From 37f7ee6e15faa0037558a3a0027344aea7efe7fd Mon Sep 17 00:00:00 2001 From: huangjianwu Date: Thu, 14 May 2026 19:01:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(desktop):=20=E5=90=8E=E7=AB=AF=E5=81=A5?= =?UTF-8?q?=E5=BA=B7=E7=9B=91=E6=8E=A7=E9=9F=A7=E6=80=A7=20+=20onboarding?= =?UTF-8?q?=20=E4=BF=AE=E5=A4=8D=20+=20=E5=85=A8=E5=B1=80=E4=BB=A3?= =?UTF-8?q?=E7=90=86=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - useCheckBackend 重写:60s 总超时取代 while(true) 死轮询,订阅 Tauri backend-ready/terminated/startup-timeout 事件,裸 fetch 探测避免 启动期 toast 叠堆 - Tauri lib.rs:spawn 后 HTTP 探针轮询 /api/sys_check 拿 200 才算就绪 (之前 TCP connect 会被孤儿进程误判);RunEvent::Exit 钩子退出前 kill sidecar,修孤儿进程占端口;restart 前发 backend-restarting 让前端忽略主动 kill 引发的 terminated - BackendInitDialog:失败态展示原因 + 最近 stderr + 重启/复制日志按钮 - StartupBanner:收到 restarted/ready 自动清「已退出」横幅 - BackendHealthIndicator:修 /api/api/sys_health 双前缀 404 - Onboarding:step1 后端连通改自动重试 + 事件触发 + 手动按钮;step2 撞预置供应商名时改为更新已存在供应商;errText 统一错误文案 - 全局代理 UI:下载配置页新增代理卡片(services/proxy.ts + ProxyConfig) - request.ts 加 suppressToast 配置位,预期失败不弹全局红 toast - NoteForm/taskStore:捕获就绪门禁错误,引导去音频转写配置页下载 - providerCard:整行可点切换(之前只有 icon 区域响应) - Monitor 页 Whisper 卡显示模型本地下载状态 - tauri/api 升级对齐 2.11,修 vite build 版本不匹配 Co-Authored-By: Claude Opus 4.7 (1M context) --- BillNote_frontend/package.json | 4 +- BillNote_frontend/pnpm-lock.yaml | 22 +- BillNote_frontend/src-tauri/Cargo.lock | 961 ++++++++---------- BillNote_frontend/src-tauri/Cargo.toml | 8 +- BillNote_frontend/src-tauri/src/lib.rs | 127 ++- BillNote_frontend/src/App.tsx | 11 +- .../BackendHealth/BackendHealthIndicator.tsx | 8 +- .../BackendHealth/useBackendEvents.ts | 21 +- .../src/components/BackendInitDialog.tsx | 141 ++- .../Form/DownloaderForm/ProxyConfig.tsx | 89 ++ .../modelForm/components/providerCard.tsx | 13 +- .../SystemDiagnostic/StartupBanner.tsx | 11 +- .../src/hooks/useCheckBackend.ts | 190 +++- .../pages/HomePage/components/NoteForm.tsx | 22 +- .../src/pages/Onboarding/index.tsx | 158 ++- .../src/pages/SettingPage/Downloader.tsx | 5 +- .../src/pages/SettingPage/Monitor.tsx | 14 +- BillNote_frontend/src/services/model.ts | 28 +- BillNote_frontend/src/services/proxy.ts | 19 + BillNote_frontend/src/services/system.ts | 26 +- .../src/store/taskStore/index.ts | 23 +- BillNote_frontend/src/utils/request.ts | 23 +- 22 files changed, 1273 insertions(+), 651 deletions(-) create mode 100644 BillNote_frontend/src/components/Form/DownloaderForm/ProxyConfig.tsx create mode 100644 BillNote_frontend/src/services/proxy.ts diff --git a/BillNote_frontend/package.json b/BillNote_frontend/package.json index 8435b87..b2c9ee8 100644 --- a/BillNote_frontend/package.json +++ b/BillNote_frontend/package.json @@ -25,8 +25,8 @@ "@radix-ui/react-tabs": "^1.1.9", "@radix-ui/react-tooltip": "^1.1.8", "@tailwindcss/vite": "^4.1.3", - "@tauri-apps/api": "^2.10.1", - "@tauri-apps/plugin-shell": "~2.2.2", + "@tauri-apps/api": "^2.11.0", + "@tauri-apps/plugin-shell": "~2.3.5", "@uiw/react-markdown-preview": "^5.1.3", "antd": "^5.24.8", "axios": "^1.8.4", diff --git a/BillNote_frontend/pnpm-lock.yaml b/BillNote_frontend/pnpm-lock.yaml index 6162429..9c272ec 100644 --- a/BillNote_frontend/pnpm-lock.yaml +++ b/BillNote_frontend/pnpm-lock.yaml @@ -54,11 +54,11 @@ importers: specifier: ^4.1.3 version: 4.2.2(vite@6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3)) '@tauri-apps/api': - specifier: ^2.10.1 - version: 2.10.1 + specifier: ^2.11.0 + version: 2.11.0 '@tauri-apps/plugin-shell': - specifier: ~2.2.2 - version: 2.2.2 + specifier: ~2.3.5 + version: 2.3.5 '@uiw/react-markdown-preview': specifier: ^5.1.3 version: 5.1.5(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -1590,8 +1590,8 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 || ^8 - '@tauri-apps/api@2.10.1': - resolution: {integrity: sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw==} + '@tauri-apps/api@2.11.0': + resolution: {integrity: sha512-7CinYODhky9lmO23xHnUFv0Xt43fbtWMyxZcLcRBlFkcgXKuEirBvHpmtJ89YMhyeGcq20Wuc47Fa4XjyniywA==} '@tauri-apps/cli-darwin-arm64@2.10.1': resolution: {integrity: sha512-Z2OjCXiZ+fbYZy7PmP3WRnOpM9+Fy+oonKDEmUE6MwN4IGaYqgceTjwHucc/kEEYZos5GICve35f7ZiizgqEnQ==} @@ -1669,8 +1669,8 @@ packages: engines: {node: '>= 10'} hasBin: true - '@tauri-apps/plugin-shell@2.2.2': - resolution: {integrity: sha512-fg9XKWfzRQsN8p+Zrk82WeHvXFvGVnG0/mTlujQdLWNnO5cM6WD9qCrHbFytScVS+WhmRAkuypQPcxeKKl3VBg==} + '@tauri-apps/plugin-shell@2.3.5': + resolution: {integrity: sha512-jewtULhiQ7lI7+owCKAjc8tYLJr92U16bPOeAa472LHJdgaibLP83NcfAF2e+wkEcA53FxKQAZ7byDzs2eeizg==} '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -6463,7 +6463,7 @@ snapshots: tailwindcss: 4.2.2 vite: 6.4.1(@types/node@22.19.15)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.3) - '@tauri-apps/api@2.10.1': {} + '@tauri-apps/api@2.11.0': {} '@tauri-apps/cli-darwin-arm64@2.10.1': optional: true @@ -6512,9 +6512,9 @@ snapshots: '@tauri-apps/cli-win32-ia32-msvc': 2.10.1 '@tauri-apps/cli-win32-x64-msvc': 2.10.1 - '@tauri-apps/plugin-shell@2.2.2': + '@tauri-apps/plugin-shell@2.3.5': dependencies: - '@tauri-apps/api': 2.10.1 + '@tauri-apps/api': 2.11.0 '@types/babel__core@7.20.5': dependencies: diff --git a/BillNote_frontend/src-tauri/Cargo.lock b/BillNote_frontend/src-tauri/Cargo.lock index 43f5aae..84a0834 100644 --- a/BillNote_frontend/src-tauri/Cargo.lock +++ b/BillNote_frontend/src-tauri/Cargo.lock @@ -165,6 +165,21 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[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" @@ -216,7 +231,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" dependencies = [ - "objc2 0.6.1", + "objc2 0.6.4", ] [[package]] @@ -244,9 +259,9 @@ dependencies = [ [[package]] name = "brotli" -version = "7.0.0" +version = "8.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -255,9 +270,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.3" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -387,7 +402,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02260d489095346e5cafd04dea8e8cb54d1d74fcd759022a9b72986ebe9a1257" dependencies = [ "serde", - "toml", + "toml 0.8.23", ] [[package]] @@ -448,7 +463,7 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -461,12 +476,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "cookie" version = "0.18.1" @@ -506,6 +515,19 @@ dependencies = [ "libc", ] +[[package]] +name = "core-graphics" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +dependencies = [ + "bitflags 2.9.1", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + [[package]] name = "core-graphics-types" version = "0.2.0" @@ -562,19 +584,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]] @@ -589,14 +607,20 @@ dependencies = [ [[package]] name = "ctor" -version = "0.2.9" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98" dependencies = [ - "quote", - "syn 2.0.103", + "ctor-proc-macro", + "dtor", ] +[[package]] +name = "ctor-proc-macro" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" + [[package]] name = "darling" version = "0.20.11" @@ -632,6 +656,17 @@ dependencies = [ "syn 2.0.103", ] +[[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.4.0" @@ -644,11 +679,19 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.20" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" +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 = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "convert_case", "proc-macro2", "quote", "rustc_version", @@ -686,12 +729,6 @@ dependencies = [ "windows-sys 0.60.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" @@ -699,7 +736,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ "bitflags 2.9.1", - "objc2 0.6.1", + "block2 0.6.1", + "libc", + "objc2 0.6.4", ] [[package]] @@ -715,9 +754,9 @@ dependencies = [ [[package]] name = "dlopen2" -version = "0.7.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" dependencies = [ "dlopen2_derive", "libc", @@ -736,6 +775,21 @@ dependencies = [ "syn 2.0.103", ] +[[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", + "html5ever", + "precomputed-hash", + "selectors", + "tendril", +] + [[package]] name = "dpi" version = "0.1.2" @@ -760,6 +814,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" @@ -781,7 +850,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml", + "toml 0.8.23", "vswhom", "winreg", ] @@ -827,6 +896,12 @@ dependencies = [ "typeid", ] +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + [[package]] name = "fdeflate" version = "0.3.7" @@ -871,6 +946,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[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.5.0" @@ -913,16 +994,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" -[[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" @@ -995,15 +1066,6 @@ dependencies = [ "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.18.2" @@ -1113,17 +1175,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.16" @@ -1336,16 +1387,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]] @@ -1356,7 +1403,7 @@ checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", - "itoa 1.0.15", + "itoa", ] [[package]] @@ -1400,7 +1447,7 @@ dependencies = [ "http", "http-body", "httparse", - "itoa 1.0.15", + "itoa", "pin-project-lite", "smallvec", "tokio", @@ -1457,12 +1504,12 @@ 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", + "png 0.17.16", ] [[package]] @@ -1615,16 +1662,6 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" -[[package]] -name = "iri-string" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "is-docker" version = "0.2.0" @@ -1644,12 +1681,6 @@ dependencies = [ "once_cell", ] -[[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.15" @@ -1703,10 +1734,12 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -1744,25 +1777,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "kuchikiki" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" -dependencies = [ - "cssparser", - "html5ever", - "indexmap 1.9.3", - "matches", - "selectors", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - [[package]] name = "libappindicator" version = "0.9.0" @@ -1793,6 +1807,15 @@ version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +[[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" @@ -1838,32 +1861,17 @@ dependencies = [ "value-bag", ] -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - [[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 = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - [[package]] name = "memchr" version = "2.7.5" @@ -1908,23 +1916,23 @@ dependencies = [ [[package]] name = "muda" -version = "0.16.1" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de14a9b5d569ca68d7c891d613b390cf5ab4f851c77aaa2f9e435555d3d9492" +checksum = "0ae8844f63b5b118e334e205585b8c5c17b984121dbdb179d44aeb087ffad3cb" dependencies = [ "crossbeam-channel", "dpi", "gtk", "keyboard-types", - "objc2 0.6.1", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", "objc2-foundation 0.3.1", "once_cell", - "png", + "png 0.18.1", "serde", "thiserror 2.0.12", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1942,12 +1950,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "ndk-context" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" - [[package]] name = "ndk-sys" version = "0.6.0+11769913" @@ -1963,12 +1965,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - [[package]] name = "num-conv" version = "0.1.0" @@ -2032,9 +2028,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" dependencies = [ "objc2-encode", "objc2-exception-helper", @@ -2048,15 +2044,9 @@ checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" dependencies = [ "bitflags 2.9.1", "block2 0.6.1", - "libc", - "objc2 0.6.1", - "objc2-cloud-kit", - "objc2-core-data", + "objc2 0.6.4", "objc2-core-foundation", - "objc2-core-graphics", - "objc2-core-image", "objc2-foundation 0.3.1", - "objc2-quartz-core 0.3.1", ] [[package]] @@ -2066,7 +2056,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17614fdcd9b411e6ff1117dfb1d0150f908ba83a7df81b1f118005fe0a8ea15d" dependencies = [ "bitflags 2.9.1", - "objc2 0.6.1", + "objc2 0.6.4", "objc2-foundation 0.3.1", ] @@ -2076,8 +2066,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291fbbf7d29287518e8686417cf7239c74700fd4b607623140a7d4a3c834329d" dependencies = [ - "bitflags 2.9.1", - "objc2 0.6.1", + "objc2 0.6.4", "objc2-foundation 0.3.1", ] @@ -2089,7 +2078,7 @@ checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ "bitflags 2.9.1", "dispatch2", - "objc2 0.6.1", + "objc2 0.6.4", ] [[package]] @@ -2100,7 +2089,7 @@ checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" dependencies = [ "bitflags 2.9.1", "dispatch2", - "objc2 0.6.1", + "objc2 0.6.4", "objc2-core-foundation", "objc2-io-surface", ] @@ -2111,7 +2100,17 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79b3dc0cc4386b6ccf21c157591b34a7f44c8e75b064f85502901ab2188c007e" dependencies = [ - "objc2 0.6.1", + "objc2 0.6.4", + "objc2-foundation 0.3.1", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac0f75792558aa9d618443bbb5db7426a7a0b6fddf96903f86ef9ad02e135740" +dependencies = [ + "objc2 0.6.4", "objc2-foundation 0.3.1", ] @@ -2150,8 +2149,7 @@ checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ "bitflags 2.9.1", "block2 0.6.1", - "libc", - "objc2 0.6.1", + "objc2 0.6.4", "objc2-core-foundation", ] @@ -2162,7 +2160,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" dependencies = [ "bitflags 2.9.1", - "objc2 0.6.1", + "objc2 0.6.4", "objc2-core-foundation", ] @@ -2198,7 +2196,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5" dependencies = [ "bitflags 2.9.1", - "objc2 0.6.1", + "objc2 0.6.4", + "objc2-core-foundation", "objc2-foundation 0.3.1", ] @@ -2209,8 +2208,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25b1312ad7bc8a0e92adae17aa10f90aae1fb618832f9b993b022b591027daed" dependencies = [ "bitflags 2.9.1", - "objc2 0.6.1", + "block2 0.6.1", + "objc2 0.6.4", + "objc2-cloud-kit", + "objc2-core-data", "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation 0.3.1", + "objc2-quartz-core 0.3.1", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a3f5ec77a81d9e0c5a0b32159b0cb143d7086165e79708351e02bf37dfc65cd" +dependencies = [ + "objc2 0.6.4", "objc2-foundation 0.3.1", ] @@ -2222,7 +2239,7 @@ checksum = "91672909de8b1ce1c2252e95bbee8c1649c9ad9d14b9248b3d7b4c47903c47ad" dependencies = [ "bitflags 2.9.1", "block2 0.6.1", - "objc2 0.6.1", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", "objc2-foundation 0.3.1", @@ -2333,106 +2350,43 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "phf" -version = "0.8.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" 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", -] - -[[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", + "phf_macros", + "phf_shared", + "serde", ] [[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 = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", -] - -[[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", + "phf_generator", + "phf_shared", ] [[package]] name = "phf_generator" -version = "0.8.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" dependencies = [ - "phf_shared 0.8.0", - "rand 0.7.3", -] - -[[package]] -name = "phf_generator" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" -dependencies = [ - "phf_shared 0.10.0", - "rand 0.8.5", -] - -[[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", + "fastrand", + "phf_shared", ] [[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 = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "phf_macros" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" -dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", + "phf_generator", + "phf_shared", "proc-macro2", "quote", "syn 2.0.103", @@ -2440,29 +2394,11 @@ dependencies = [ [[package]] name = "phf_shared" -version = "0.8.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" dependencies = [ - "siphasher 0.3.11", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher 1.0.1", + "siphasher", ] [[package]] @@ -2509,6 +2445,19 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.9.1", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "potential_utf" version = "0.1.2" @@ -2591,12 +2540,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.95" @@ -2656,20 +2599,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "rand" -version = "0.7.3" -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", -] - [[package]] name = "rand" version = "0.8.5" @@ -2677,18 +2606,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -2698,16 +2617,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -2719,24 +2629,6 @@ dependencies = [ "getrandom 0.2.16", ] -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - [[package]] name = "raw-window-handle" version = "0.6.2" @@ -2823,9 +2715,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.20" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" +checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0" dependencies = [ "base64 0.22.1", "bytes", @@ -2842,7 +2734,6 @@ dependencies = [ "pin-project-lite", "serde", "serde_json", - "serde_urlencoded", "sync_wrapper", "tokio", "tokio-util", @@ -2895,7 +2786,7 @@ dependencies = [ "borsh", "bytes", "num-traits", - "rand 0.8.5", + "rand", "rkyv", "serde", "serde_json", @@ -2907,6 +2798,12 @@ version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +[[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" @@ -2990,22 +2887,21 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[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.9.1", "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]] @@ -3019,10 +2915,11 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] @@ -3038,10 +2935,19 @@ dependencies = [ ] [[package]] -name = "serde_derive" -version = "1.0.219" +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -3065,7 +2971,7 @@ version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "itoa 1.0.15", + "itoa", "memchr", "ryu", "serde", @@ -3092,15 +2998,12 @@ dependencies = [ ] [[package]] -name = "serde_urlencoded" -version = "0.7.1" +name = "serde_spanned" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ - "form_urlencoded", - "itoa 1.0.15", - "ryu", - "serde", + "serde_core", ] [[package]] @@ -3136,9 +3039,9 @@ dependencies = [ [[package]] name = "serialize-to-javascript" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb" +checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5" dependencies = [ "serde", "serde_json", @@ -3147,22 +3050,21 @@ dependencies = [ [[package]] name = "serialize-to-javascript-impl" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" +checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.103", ] [[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", ] @@ -3236,12 +3138,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - [[package]] name = "siphasher" version = "1.0.1" @@ -3278,7 +3174,7 @@ checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" dependencies = [ "bytemuck", "cfg_aliases", - "core-graphics", + "core-graphics 0.24.0", "foreign-types", "js-sys", "log", @@ -3326,25 +3222,24 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[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", ] @@ -3417,40 +3312,41 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml", + "toml 0.8.23", "version-compare", ] [[package]] name = "tao" -version = "0.33.0" +version = "0.35.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e59c1f38e657351a2e822eadf40d6a2ad4627b9c25557bc1180ec1b3295ef82" +checksum = "a33f7f9e486ade65fcf1e45c440f9236c904f5c1002cdc7fc6ae582777345ce4" dependencies = [ "bitflags 2.9.1", + "block2 0.6.1", "core-foundation", - "core-graphics", + "core-graphics 0.25.0", "crossbeam-channel", - "dispatch", + "dbus", + "dispatch2", "dlopen2", "dpi", "gdkwayland-sys", "gdkx11-sys", "gtk", "jni", - "lazy_static", "libc", "log", "ndk", - "ndk-context", "ndk-sys", - "objc2 0.6.1", + "objc2 0.6.4", "objc2-app-kit", "objc2-foundation 0.3.1", + "objc2-ui-kit", "once_cell", "parking_lot", + "percent-encoding", "raw-window-handle", - "scopeguard", "tao-macros", "unicode-segmentation", "url", @@ -3485,17 +3381,17 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.5.1" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7b0bc1aec81bda6bc455ea98fcaed26b3c98c1648c627ad6ff1c704e8bf8cbc" +checksum = "b93bd86d231f0a8138f11a02a584769fe4b703dc36ae133d783228dbc4801405" dependencies = [ "anyhow", "bytes", + "cookie", "dirs", "dunce", "embed_plist", - "futures-util", - "getrandom 0.2.16", + "getrandom 0.3.3", "glob", "gtk", "heck 0.5.0", @@ -3505,10 +3401,11 @@ dependencies = [ "log", "mime", "muda", - "objc2 0.6.1", + "objc2 0.6.4", "objc2-app-kit", "objc2-foundation 0.3.1", "objc2-ui-kit", + "objc2-web-kit", "percent-encoding", "plist", "raw-window-handle", @@ -3527,7 +3424,6 @@ dependencies = [ "tokio", "tray-icon", "url", - "urlpattern", "webkit2gtk", "webview2-com", "window-vibrancy", @@ -3536,9 +3432,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.2.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a0350f0df1db385ca5c02888a83e0e66655c245b7443db8b78a70da7d7f8fc" +checksum = "3a318b234cc2dea65f575467bafcfb76286bce228ebc3778e337d61d03213007" dependencies = [ "anyhow", "cargo_toml", @@ -3552,22 +3448,21 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "toml", "walkdir", ] [[package]] name = "tauri-codegen" -version = "2.2.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93f035551bf7b11b3f51ad9bc231ebbe5e085565527991c16cf326aa38cdf47" +checksum = "6bd11644962add2549a60b7e7c6800f17d7020156e02f516021d8103e80cc528" dependencies = [ "base64 0.22.1", "brotli", "ico", "json-patch", "plist", - "png", + "png 0.17.16", "proc-macro2", "quote", "semver", @@ -3585,9 +3480,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.2.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8db4df25e2d9d45de0c4c910da61cd5500190da14ae4830749fee3466dddd112" +checksum = "fed9d3742a37a355d2e47c9af924e9fbc112abb76f9835d35d4780e318419502" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -3599,9 +3494,9 @@ dependencies = [ [[package]] name = "tauri-plugin" -version = "2.2.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a5ebe6a610d1b78a94650896e6f7c9796323f408800cef436e0fa0539de601" +checksum = "eefb2c18e8a605c23edb48fc56bb77381199e1a1e7f6ff0c9b970afe7b3cb8ee" dependencies = [ "anyhow", "glob", @@ -3610,7 +3505,6 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "toml", "walkdir", ] @@ -3624,7 +3518,7 @@ dependencies = [ "byte-unit", "fern", "log", - "objc2 0.6.1", + "objc2 0.6.4", "objc2-foundation 0.3.1", "serde", "serde_json", @@ -3638,9 +3532,9 @@ dependencies = [ [[package]] name = "tauri-plugin-shell" -version = "2.2.2" +version = "2.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34e525a448b80ad5d906fcbd93838ac3ba37985b29ac699a045b5da9b0a1a22" +checksum = "8457dbf9e2bab1edd8df22bb2c20857a59a9868e79cb3eac5ed639eec4d0c73b" dependencies = [ "encoding_rs", "log", @@ -3659,39 +3553,41 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.6.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f004905d549854069e6774533d742b03cacfd6f03deb08940a8677586cbe39" +checksum = "8fef478ba1d2ac21c2d528740b24d0cb315e1e8b1111aae53fafac34804371fc" dependencies = [ "cookie", "dpi", "gtk", "http", "jni", - "objc2 0.6.1", + "objc2 0.6.4", "objc2-ui-kit", + "objc2-web-kit", "raw-window-handle", "serde", "serde_json", "tauri-utils", "thiserror 2.0.12", "url", + "webkit2gtk", + "webview2-com", "windows", ] [[package]] name = "tauri-runtime-wry" -version = "2.6.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f85d056f4d4b014fe874814034f3416d57114b617a493a4fe552580851a3f3a2" +checksum = "a3989df2ae1c476404fe0a2e8ffc4cfbde97e51efd613c2bb5355fbc9ab52cf0" dependencies = [ "gtk", "http", "jni", "log", - "objc2 0.6.1", + "objc2 0.6.4", "objc2-app-kit", - "objc2-foundation 0.3.1", "once_cell", "percent-encoding", "raw-window-handle", @@ -3708,24 +3604,24 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.4.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2900399c239a471bcff7f15c4399eb1a8c4fe511ba2853e07c996d771a5e0a4" +checksum = "d57200389a2f82b4b0a40ae29ca19b6978116e8f4d4e974c3234ce40c0ffbdec" dependencies = [ "anyhow", "brotli", "cargo_metadata", "ctor", + "dom_query", "dunce", "glob", - "html5ever", "http", "infer", "json-patch", - "kuchikiki", "log", "memchr", - "phf 0.11.3", + "phf", + "plist", "proc-macro2", "quote", "regex", @@ -3737,7 +3633,7 @@ dependencies = [ "serde_with", "swift-rs", "thiserror 2.0.12", - "toml", + "toml 0.9.6", "url", "urlpattern", "uuid", @@ -3752,26 +3648,19 @@ checksum = "e8d321dbc6f998d825ab3f0d62673e810c861aac2d0de2cc2c395328f1d113b4" dependencies = [ "embed-resource", "indexmap 2.9.0", - "toml", + "toml 0.8.23", ] [[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", ] -[[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" @@ -3819,7 +3708,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", - "itoa 1.0.15", + "itoa", "libc", "num-conv", "num_threads", @@ -3905,11 +3794,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_edit 0.22.27", ] +[[package]] +name = "toml" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae2a4cf385da23d1d53bc15cdfa5c2109e93d8d362393c801e87da2f72f0e201" +dependencies = [ + "indexmap 2.9.0", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 0.7.11", +] + [[package]] name = "toml_datetime" version = "0.6.11" @@ -3919,6 +3823,15 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.19.15" @@ -3926,7 +3839,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap 2.9.0", - "toml_datetime", + "toml_datetime 0.6.11", "winnow 0.5.40", ] @@ -3937,7 +3850,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" dependencies = [ "indexmap 2.9.0", - "toml_datetime", + "toml_datetime 0.6.11", "winnow 0.5.40", ] @@ -3949,18 +3862,33 @@ checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap 2.9.0", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_write", "winnow 0.7.11", ] +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.2", +] + [[package]] name = "toml_write" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +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.2" @@ -3978,20 +3906,20 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "68d6fdd9f81c2819c9a8b0e0cd91660e7746a8e6ea2ba7c6b2b057985f6bcb51" dependencies = [ "bitflags 2.9.1", "bytes", "futures-util", "http", "http-body", - "iri-string", "pin-project-lite", "tower", "tower-layer", "tower-service", + "url", ] [[package]] @@ -4027,24 +3955,24 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.20.1" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7eee98ec5c90daf179d55c20a49d8c0d043054ce7c26336c09a24d31f14fa0" +checksum = "15edbb0d80583e85ee8df283410038e17314df5cba30da2087a54a85216c0773" dependencies = [ "crossbeam-channel", "dirs", "libappindicator", "muda", - "objc2 0.6.1", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", "objc2-core-graphics", "objc2-foundation 0.3.1", "once_cell", - "png", + "png 0.18.1", "serde", "thiserror 2.0.12", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -4229,12 +4157,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" @@ -4252,48 +4174,32 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.103", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" dependencies = [ - "cfg-if", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4301,31 +4207,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn 2.0.103", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" 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", @@ -4336,19 +4242,31 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] -name = "webkit2gtk" -version = "2.0.1" +name = "web_atoms" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +checksum = "d7cff6eef815df1834fd250e3a2ff436044d82a9f1bc1980ca1dbdf07effc538" +dependencies = [ + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" dependencies = [ "bitflags 1.3.2", "cairo-rs", @@ -4370,9 +4288,9 @@ dependencies = [ [[package]] name = "webkit2gtk-sys" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" dependencies = [ "bitflags 1.3.2", "cairo-sys-rs", @@ -4390,9 +4308,9 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.37.0" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b542b5cfbd9618c46c2784e4d41ba218c336ac70d44c55e47b251033e7d85601" +checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" dependencies = [ "webview2-com-macros", "webview2-com-sys", @@ -4404,9 +4322,9 @@ dependencies = [ [[package]] name = "webview2-com-macros" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" +checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" dependencies = [ "proc-macro2", "quote", @@ -4415,9 +4333,9 @@ dependencies = [ [[package]] name = "webview2-com-sys" -version = "0.37.0" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae2d11c4a686e4409659d7891791254cf9286d3cfe0eef54df1523533d22295" +checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" dependencies = [ "thiserror 2.0.12", "windows", @@ -4461,7 +4379,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" dependencies = [ - "objc2 0.6.1", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", "objc2-foundation 0.3.1", @@ -4479,7 +4397,7 @@ dependencies = [ "windows-collections", "windows-core", "windows-future", - "windows-link", + "windows-link 0.1.3", "windows-numerics", ] @@ -4500,7 +4418,7 @@ checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", - "windows-link", + "windows-link 0.1.3", "windows-result", "windows-strings", ] @@ -4512,7 +4430,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core", - "windows-link", + "windows-link 0.1.3", "windows-threading", ] @@ -4544,6 +4462,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-numerics" version = "0.2.0" @@ -4551,7 +4475,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ "windows-core", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -4560,7 +4484,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -4569,7 +4493,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -4608,6 +4532,15 @@ dependencies = [ "windows-targets 0.53.2", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -4661,7 +4594,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -4670,7 +4603,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -4829,6 +4762,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" + [[package]] name = "winreg" version = "0.55.0" @@ -4856,26 +4795,26 @@ checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "wry" -version = "0.51.2" +version = "0.55.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c886a0a9d2a94fd90cfa1d929629b79cfefb1546e2c7430c63a47f0664c0e4e2" +checksum = "186f9871daa55fd9c016578b810d149de58367113db7fb72b462d2323ce19514" dependencies = [ "base64 0.22.1", "block2 0.6.1", "cookie", "crossbeam-channel", + "dirs", + "dom_query", "dpi", "dunce", "gdkx11", "gtk", - "html5ever", "http", "javascriptcore-rs", "jni", - "kuchikiki", "libc", "ndk", - "objc2 0.6.1", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", "objc2-foundation 0.3.1", diff --git a/BillNote_frontend/src-tauri/Cargo.toml b/BillNote_frontend/src-tauri/Cargo.toml index 3aed5bb..612cc61 100644 --- a/BillNote_frontend/src-tauri/Cargo.toml +++ b/BillNote_frontend/src-tauri/Cargo.toml @@ -15,14 +15,16 @@ name = "app_lib" crate-type = ["staticlib", "cdylib", "rlib"] [build-dependencies] -tauri-build = { version = "2.2.0", features = [] } +# tauri-build / tauri crate 与 @tauri-apps/api 大版本必须对齐(CLI 在 build 前会校验)。 +# @tauri-apps/api 已升 2.10(commit bb9a70e),这里同步到 2.x 最新让 cargo 解析到匹配版本。 +tauri-build = { version = "2", features = [] } [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } log = "0.4" -tauri = { version = "2.5.0", features = ["devtools"] } -tauri-plugin-log = "2.0.0-rc" +tauri = { version = "2", features = ["devtools"] } +tauri-plugin-log = "2" tauri-plugin-shell = "2" [package.metadata.tauri.bundle.macOS] diff --git a/BillNote_frontend/src-tauri/src/lib.rs b/BillNote_frontend/src-tauri/src/lib.rs index 2c8be15..a623437 100644 --- a/BillNote_frontend/src-tauri/src/lib.rs +++ b/BillNote_frontend/src-tauri/src/lib.rs @@ -3,10 +3,19 @@ use tauri_plugin_shell::ShellExt; use tauri_plugin_shell::process::{CommandChild, CommandEvent}; use std::env; use std::collections::HashMap; +use std::io::{Read, Write}; +use std::net::{SocketAddr, TcpStream}; use std::path::Path; use std::sync::Mutex; +use std::time::{Duration, Instant}; use serde::Serialize; +// Sidecar 启动期内前端不该看到「加载中」无限转。 +// 总等待上限 = 启动期 PyInstaller 解压 + uvicorn bind 时间的最坏估计, +// 实测 macOS / Windows 慢盘大概 5-20s,设 45s 留余量但不至于让用户绝望。 +const BACKEND_STARTUP_TIMEOUT_SECS: u64 = 45; +const BACKEND_DEFAULT_PORT: u16 = 8483; + // Sidecar 子进程句柄,用 Mutex 包裹方便 restart 时杀旧进程 struct SidecarHandle(Mutex>); @@ -50,6 +59,10 @@ pub fn run() { })?; app.manage(SidecarHandle(Mutex::new(Some(child)))); + // 启动 ready probe:异步轮询本地 BACKEND_PORT 是否在监听, + // 解决前端 useCheckBackend 在 PyInstaller 解压期瞎猜后端起没起的问题。 + spawn_backend_ready_probe(app.handle().clone()); + Ok(()) }) .invoke_handler(tauri::generate_handler![ @@ -60,8 +73,33 @@ pub fn run() { get_install_path_diagnostics, restart_backend_sidecar ]) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); + .build(tauri::generate_context!()) + .expect("error while building tauri application") + // 用 build()+run() 拿到 RunEvent 流,关键诉求:app 退出前必须 kill 掉 PyInstaller + // sidecar,否则它会变成持有 8483 端口的孤儿进程,下次启动 BiliNote 直接 bind 失败。 + // 之前漏掉这一步导致用户 PID 96739 那种「上次没关干净 → 这次起不来」的死循环。 + .run(|app_handle, event| { + match event { + // ExitRequested 在用户 Cmd-Q / 点关闭 / Dock 退出时触发,先于实际进程结束。 + // Exit 是兜底——任何走到 Tauri 主循环结束的路径都会经过它。 + tauri::RunEvent::ExitRequested { .. } | tauri::RunEvent::Exit => { + kill_backend_sidecar(app_handle); + } + _ => {} + } + }); +} + +// 关闭期统一杀 sidecar,take() 把 child 从 state 拿走避免重复 kill。 +fn kill_backend_sidecar(app_handle: &tauri::AppHandle) { + if let Some(state) = app_handle.try_state::() { + if let Ok(mut guard) = state.0.lock() { + if let Some(child) = guard.take() { + eprintln!("[shutdown] killing backend sidecar before app exit"); + let _ = child.kill(); + } + } + } } // 获取额外的二进制路径 @@ -306,6 +344,12 @@ fn restart_backend_sidecar( state: State<'_, SidecarHandle>, app: tauri::AppHandle, ) -> Result<(), String> { + // 0. 先告诉前端「我们要重启了」。前端可以借此忽略接下来 N 秒内的 backend-terminated + // 事件——那是我们主动 kill 老 sidecar 的副作用,不是真异常。否则会出现: + // terminated 事件延迟到达 → 覆盖掉 'running' 状态 → 面板永远显示「已退出」。 + if let Some(window) = app.get_webview_window("main") { + let _ = window.emit("backend-restarting", ()); + } // 1. 拿出旧 child 并 kill(kill 失败也继续,可能进程已经退了) { let mut guard = state.0.lock().map_err(|e| format!("锁 sidecar state 失败: {}", e))?; @@ -323,9 +367,88 @@ fn restart_backend_sidecar( if let Some(window) = app.get_webview_window("main") { let _ = window.emit("backend-restarted", ()); } + // 4. 重启后同样起一次 ready probe,让前端能及时退出失败态 + spawn_backend_ready_probe(app); Ok(()) } +// 后端就绪探测:异步轮询 GET /api/sys_check,要求 HTTP 200 才算就绪。 +// +// 旧实现只做 TcpStream::connect_timeout——但端口被另一个孤儿 sidecar 占着时也会 +// 连得通,导致 emit('backend-ready') 误判:前端进入主界面,但真正的新 sidecar +// 没 bind 上立刻就死,banner 永远停在「后端进程已退出」。 +// +// 真发一个 HTTP 请求拿 200 才算「这是我们的后端在响应」。 +fn spawn_backend_ready_probe(app: tauri::AppHandle) { + let port: u16 = env::var("BACKEND_PORT") + .ok() + .and_then(|v| v.parse().ok()) + .unwrap_or(BACKEND_DEFAULT_PORT); + let addr: SocketAddr = format!("127.0.0.1:{}", port).parse().expect("invalid backend addr"); + let timeout = Duration::from_secs(BACKEND_STARTUP_TIMEOUT_SECS); + + std::thread::spawn(move || { + let start = Instant::now(); + let probe_interval = Duration::from_millis(500); + loop { + if probe_sys_check(&addr) { + if let Some(window) = app.get_webview_window("main") { + let _ = window.emit("backend-ready", port); + println!("Backend ready on port {} after {:?}", port, start.elapsed()); + } + return; + } + if start.elapsed() >= timeout { + if let Some(window) = app.get_webview_window("main") { + let payload = format!( + "后端在 {}s 内 /api/sys_check 未返回 200,疑似启动失败或端口 {} 被其他进程占用", + timeout.as_secs(), + port + ); + let _ = window.emit("backend-startup-timeout", payload); + eprintln!( + "Backend startup timeout: /api/sys_check did not return 200 on 127.0.0.1:{} after {:?}", + port, + start.elapsed() + ); + } + return; + } + std::thread::sleep(probe_interval); + } + }); +} + +// 极简 HTTP/1.0 GET /api/sys_check —— 用 std::net 手写避免引 reqwest/ureq 的重依赖。 +// 任何错都视为「还没就绪」,下次 tick 再试。 +fn probe_sys_check(addr: &SocketAddr) -> bool { + let connect_timeout = Duration::from_millis(800); + let rw_timeout = Duration::from_millis(1500); + let mut stream = match TcpStream::connect_timeout(addr, connect_timeout) { + Ok(s) => s, + Err(_) => return false, + }; + let _ = stream.set_read_timeout(Some(rw_timeout)); + let _ = stream.set_write_timeout(Some(rw_timeout)); + // HTTP/1.0 + Connection: close 让服务端发完响应就关,免去 chunked / keep-alive 解析 + let req = format!( + "GET /api/sys_check HTTP/1.0\r\nHost: 127.0.0.1:{}\r\nConnection: close\r\n\r\n", + addr.port() + ); + if stream.write_all(req.as_bytes()).is_err() { + return false; + } + // 只要 status line,64 字节够了 + let mut buf = [0u8; 64]; + let n = match stream.read(&mut buf) { + Ok(n) => n, + Err(_) => return false, + }; + let head = std::str::from_utf8(&buf[..n]).unwrap_or(""); + // 兼容 HTTP/1.0 / 1.1 起始行 + head.starts_with("HTTP/1.1 200") || head.starts_with("HTTP/1.0 200") +} + // 安装路径诊断:PyInstaller 在含非 ASCII / 空格的路径下加载 _internal/* 经常炸; // 父目录不可写时模型 / 配置 / 日志也无法落盘 #[derive(Serialize, Clone)] diff --git a/BillNote_frontend/src/App.tsx b/BillNote_frontend/src/App.tsx index 682a75a..d871506 100644 --- a/BillNote_frontend/src/App.tsx +++ b/BillNote_frontend/src/App.tsx @@ -33,7 +33,7 @@ const NotFoundPage = lazy(() => import('@/pages/NotFoundPage')) function App() { useTaskPolling(3000) // 每 3 秒轮询一次 - const { loading, initialized } = useCheckBackend() + const { loading, initialized, failed, lastError, retry } = useCheckBackend() // 在后端初始化完成后执行系统检查 useEffect(() => { @@ -42,12 +42,17 @@ function App() { } }, [initialized]) - // 如果后端还未初始化,显示初始化对话框 + // 如果后端还未初始化,显示初始化对话框(loading 或 failed 都展示,由 dialog 内部决定渲染哪一态) if (!initialized) { return ( <> - + ) } diff --git a/BillNote_frontend/src/components/BackendHealth/BackendHealthIndicator.tsx b/BillNote_frontend/src/components/BackendHealth/BackendHealthIndicator.tsx index ce8217e..cddc5c1 100644 --- a/BillNote_frontend/src/components/BackendHealth/BackendHealthIndicator.tsx +++ b/BillNote_frontend/src/components/BackendHealth/BackendHealthIndicator.tsx @@ -10,12 +10,14 @@ import BackendLogPanel from './BackendLogPanel' type Health = 'green' | 'yellow' | 'red' | 'unknown' const HEALTH_POLL_MS = 5000 -const SYS_HEALTH_PATH = '/api/sys_health' +// 路径不带 /api/,因为 backendBase() 已经把它包进 baseURL 了(同 axios 实例的语义)。 +// 之前写 '/api/sys_health' + base='http://host/api' = 双 /api → 一直 404。 +const SYS_HEALTH_PATH = '/sys_health' function backendBase(): string { - // 与 services/request.ts 用的一致 + // 与 utils/request.ts 的 baseURL 计算保持一致:env 没设走 '/api' 兜底。 const fromEnv = (import.meta as any).env?.VITE_API_BASE_URL as string | undefined - return (fromEnv ?? '').replace(/\/$/, '') + return ((fromEnv && fromEnv.length > 0) ? fromEnv : '/api').replace(/\/$/, '') } const BackendHealthIndicator = () => { diff --git a/BillNote_frontend/src/components/BackendHealth/useBackendEvents.ts b/BillNote_frontend/src/components/BackendHealth/useBackendEvents.ts index cd2a5d2..ca423f7 100644 --- a/BillNote_frontend/src/components/BackendHealth/useBackendEvents.ts +++ b/BillNote_frontend/src/components/BackendHealth/useBackendEvents.ts @@ -35,6 +35,9 @@ export function useBackendEvents(): BackendEvents { const [logs, setLogs] = useState([]) // 用 ref 持有最新 logs 数组,append 时不被闭包陷阱卡到旧值 const logsRef = useRef([]) + // 主动重启期:Rust 在 kill 老 sidecar 前会 emit 'backend-restarting'。 + // 期间到达的 'backend-terminated' 是我们自己造成的,不要污染状态。 + const ignoreNextTerminatedRef = useRef(false) function append(entry: LogEntry) { const next = logsRef.current.concat(entry) @@ -58,7 +61,23 @@ export function useBackendEvents(): BackendEvents { const offErr = await listen('backend-error', event => { append({ level: 'error', text: stripQuotes(event.payload), ts: Date.now() }) }) + const offRestarting = await listen('backend-restarting', () => { + // 紧接着到达的 terminated 是我们主动 kill 老 sidecar 引发的,跳过 3s + ignoreNextTerminatedRef.current = true + setTimeout(() => { ignoreNextTerminatedRef.current = false }, 3000) + append({ level: 'info', text: '[Backend restarting]', ts: Date.now() }) + }) const offTerm = await listen('backend-terminated', event => { + // 主动重启窗口内的 terminated 是预期副作用,仅记日志、不改状态 + if (ignoreNextTerminatedRef.current) { + ignoreNextTerminatedRef.current = false + append({ + level: 'info', + text: `[Backend terminated, restart in progress] code=${event.payload ?? 'unknown'}`, + ts: Date.now(), + }) + return + } setStatus('terminated') setExitCode(event.payload ?? null) append({ @@ -73,7 +92,7 @@ export function useBackendEvents(): BackendEvents { append({ level: 'info', text: '[Backend restarted]', ts: Date.now() }) }) - unlisteners = [offMsg, offErr, offTerm, offRestart] + unlisteners = [offMsg, offErr, offRestarting, offTerm, offRestart] })() return () => { diff --git a/BillNote_frontend/src/components/BackendInitDialog.tsx b/BillNote_frontend/src/components/BackendInitDialog.tsx index 989888f..079ca4d 100644 --- a/BillNote_frontend/src/components/BackendInitDialog.tsx +++ b/BillNote_frontend/src/components/BackendInitDialog.tsx @@ -1,13 +1,141 @@ +import { useMemo, useState } from 'react' import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog' -import { Loader2 } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { Loader2, AlertTriangle, RotateCcw, Clipboard } from 'lucide-react' +import { useBackendEvents } from '@/components/BackendHealth/useBackendEvents' + +// 失败态预览里最多展示几行 stderr。比这还多就请用户去 copyLogs() 拷出来。 +const STDERR_PREVIEW_LINES = 6 interface Props { + /** 加载中:显示转圈对话框 */ open: boolean + /** 启动失败:显示错误 + 重启/复制日志按钮 */ + failed?: boolean + /** 失败原因(来自 useCheckBackend.lastError 或 Tauri 事件 payload) */ + lastError?: string | null + /** 重新走一遍 useCheckBackend 的轮询(不重启 sidecar) */ + onRetry?: () => void } - function BackendInitDialog({ open }: Props) { +// 加载中 + 启动失败两个状态合并在一个 dialog 里。 +// 失败态比加载态更紧急:用户能看到具体原因 + 一键重启 + 一键复制日志去 issue, +// 而不是面对一个永远转圈的对话框。 +function BackendInitDialog({ open, failed = false, lastError = null, onRetry }: Props) { + const { isTauri, restart, copyLogs, logs } = useBackendEvents() + const [restarting, setRestarting] = useState(false) + const [copyResult, setCopyResult] = useState<'idle' | 'ok' | 'fail'>('idle') + + // 从 ring buffer 里挑最后几行 stderr —— 它们比 lastError(hook 自己总结的那句)信息密度更高, + // 通常 Python traceback 的最后一行就是真正的错误类型 + 消息 + const stderrPreview = useMemo(() => { + if (!failed || !logs?.length) return [] + return logs + .filter((l) => l.level === 'error') + .slice(-STDERR_PREVIEW_LINES) + .map((l) => l.text) + }, [failed, logs]) + + // 任一态需要展示就保持 dialog 开着,关掉只在两个 flag 都熄灭时发生 + const isOpen = open || failed + + const handleRestart = async () => { + setRestarting(true) + try { + if (isTauri) await restart() + onRetry?.() + } catch { + // restart 内部已经 append 到 log,这里不再 toast + } finally { + setRestarting(false) + } + } + + const handleCopy = async () => { + const ok = await copyLogs() + setCopyResult(ok ? 'ok' : 'fail') + setTimeout(() => setCopyResult('idle'), 2000) + } + + if (failed) { + return ( + + + + + + 后端启动失败 + + +
+

+ {lastError || '后端在预计时间内未就绪。'} +

+ {stderrPreview.length > 0 && ( +
+

+ 后端最近 {stderrPreview.length} 行错误日志 + (完整日志请用「复制启动日志」): +

+
+                  {stderrPreview.join('\n')}
+                
+
+ )} +
+

常见原因:

+
    +
  • 安装路径含中文 / 空格(PyInstaller 在这种路径下经常起不来)
  • +
  • 没装 ffmpeg / 端口 8483 被占用
  • +
  • 首次启动时 whisper 模型下载未完成
  • +
+
+
+ + {isTauri && ( + + )} +
+

+ 仍然无法解决?复制日志去  + + GitHub Issues + +  反馈。 +

+
+
+
+ ) + } + + // 默认加载态 return ( - + @@ -15,9 +143,12 @@ interface Props { 后端正在初始化中… -

请稍候,系统正在启动后端服务,出现报错属于正常现象

+

+ 请稍候,系统正在启动后端服务。首次启动可能需要 10-30 秒解压依赖。 +

) } -export default BackendInitDialog \ No newline at end of file + +export default BackendInitDialog diff --git a/BillNote_frontend/src/components/Form/DownloaderForm/ProxyConfig.tsx b/BillNote_frontend/src/components/Form/DownloaderForm/ProxyConfig.tsx new file mode 100644 index 0000000..c256c27 --- /dev/null +++ b/BillNote_frontend/src/components/Form/DownloaderForm/ProxyConfig.tsx @@ -0,0 +1,89 @@ +import { useEffect, useState } from 'react' +import toast from 'react-hot-toast' +import { Switch } from '@/components/ui/switch' +import { Input } from '@/components/ui/input' +import { Button } from '@/components/ui/button' +import { getProxyConfig, updateProxyConfig } from '@/services/proxy' + +// 全局代理配置:作用于 LLM API + 转写 API(Groq 等)+ yt-dlp 视频下载。 +// 国内访问 OpenAI / Groq / YouTube 基本都要靠它。 +const ProxyConfig = () => { + const [enabled, setEnabled] = useState(false) + const [url, setUrl] = useState('') + const [effective, setEffective] = useState('') + const [loading, setLoading] = useState(true) + const [saving, setSaving] = useState(false) + + useEffect(() => { + ;(async () => { + try { + const cfg = await getProxyConfig() + setEnabled(cfg.enabled) + setUrl(cfg.url) + setEffective(cfg.effective) + } catch { + /* 拦截器已 toast */ + } finally { + setLoading(false) + } + })() + }, []) + + const handleSave = async () => { + if (enabled && !url.trim()) { + toast.error('请填写代理地址,或关闭代理开关') + return + } + setSaving(true) + try { + const cfg = await updateProxyConfig({ enabled, url: url.trim() }) + setEnabled(cfg.enabled) + setUrl(cfg.url) + setEffective(cfg.effective) + toast.success('代理配置已保存') + } catch { + /* 拦截器已 toast */ + } finally { + setSaving(false) + } + } + + if (loading) { + return
加载代理配置…
+ } + + // env 兜底:配置没开但 effective 有值,说明来自 HTTP_PROXY 环境变量 + const fromEnv = !enabled && !!effective + + return ( +
+
+ 全局代理 + +
+

+ 作用于 AI 模型接口、转写接口(Groq 等)、YouTube 下载。 +

+ setUrl(e.target.value)} + className="text-sm" + /> + {fromEnv && ( +

+ 当前生效(来自环境变量):{effective} +

+ )} + {enabled && effective && ( +

当前生效:{effective}

+ )} + +
+ ) +} + +export default ProxyConfig diff --git a/BillNote_frontend/src/components/Form/modelForm/components/providerCard.tsx b/BillNote_frontend/src/components/Form/modelForm/components/providerCard.tsx index 4092af8..93f8dae 100644 --- a/BillNote_frontend/src/components/Form/modelForm/components/providerCard.tsx +++ b/BillNote_frontend/src/components/Form/modelForm/components/providerCard.tsx @@ -41,21 +41,22 @@ const ProviderCard: FC = ({
navigate(`/settings/model/${id}`)} > -
navigate(`/settings/model/${id}`)} - > +
{providerName}
-
+ {/* Switch 自己的点击不应该冒泡触发整行跳转 */} +
e.stopPropagation()}> { }) }) + // 后端被「重启后端」按钮拉起来后 / Rust ready-probe 检测到新 sidecar 真的就绪后, + // 自动清掉 terminated 横幅。之前 dismissible:false + 没自动清逻辑 = banner 永远卡。 + const offRestarted = await listen('backend-restarted', () => { + setBanner(b => (b?.severity === 'error' ? null : b)) + }) + const offReady = await listen('backend-ready', () => { + setBanner(b => (b?.severity === 'error' ? null : b)) + }) + // backend-error 是 sidecar stderr,量大噪音多,这里不直接展示,留给 P2 的日志面板。 - unlisteners = [offWarning, offTerminated] + unlisteners = [offWarning, offTerminated, offRestarted, offReady] })() return () => { diff --git a/BillNote_frontend/src/hooks/useCheckBackend.ts b/BillNote_frontend/src/hooks/useCheckBackend.ts index f05ce70..8b8ee50 100644 --- a/BillNote_frontend/src/hooks/useCheckBackend.ts +++ b/BillNote_frontend/src/hooks/useCheckBackend.ts @@ -1,52 +1,156 @@ -import { useEffect, useState } from 'react' -import request from '@/utils/request' +import { useCallback, useEffect, useRef, useState } from 'react' -const MAX_RETRIES = 3 -const RETRY_INTERVAL = 10000 // 10秒 +// 后端就绪检测的几个时间常量 +// - 总等待上限 60s:超过这个时间没就绪就切「启动失败」UI, +// 不再像旧实现 while(true) 无限转 +// - 轮询间隔 2s:比旧的 10s 更敏感,桌面端 sidecar 5-15s 解压期内能尽快感知就绪 +// - 单次请求超时 5s,避免连接 hang 拖到下一轮 +const TOTAL_TIMEOUT_MS = 60_000 +const POLL_INTERVAL_MS = 2_000 +const PROBE_TIMEOUT_MS = 5_000 -export const useCheckBackend = () => { - const [loading, setLoading] = useState(false) - const [initialized, setInitialized] = useState(false) +const isTauri = typeof window !== 'undefined' && '__TAURI_INTERNALS__' in window - useEffect(() => { - let retries = 0 +// 直接用 fetch 而非 utils/request 的共享 axios:那个 axios 装了全局 toast 拦截器, +// 启动期每次 /sys_check 失败都会弹一个红色 toast,2s 一次轮询会叠出十几个。 +function getBackendBase(): string { + const fromEnv = (import.meta as any).env?.VITE_API_BASE_URL as string | undefined + return ((fromEnv && fromEnv.length > 0) ? fromEnv : '/api').replace(/\/$/, '') +} - const check = async () => { - try { - await request.get('/sys_check') - setInitialized(true) - setLoading(false) - } catch { - if (retries === 0) { - // 第一次失败时开始显示加载状态 - setLoading(true) - } +async function probeSysCheck(): Promise { + const url = `${getBackendBase()}/sys_check` + const ctrl = new AbortController() + const t = setTimeout(() => ctrl.abort(), PROBE_TIMEOUT_MS) + try { + const res = await fetch(url, { signal: ctrl.signal }) + if (!res.ok) return false + const json = await res.json().catch(() => null) + return json?.code === 0 + } + catch { + return false + } + finally { + clearTimeout(t) + } +} - if (retries < MAX_RETRIES) { - retries++ - setTimeout(check, RETRY_INTERVAL) - } else { - // 达到重试上限,继续轮询直到后端就绪 - waitUntilBackendReady() - } - } - } +interface Status { + loading: boolean + initialized: boolean + failed: boolean + lastError: string | null +} - const waitUntilBackendReady = async () => { - while (true) { - try { - await request.get('/sys_health') - setInitialized(true) - setLoading(false) - break - } catch { - await new Promise(res => setTimeout(res, RETRY_INTERVAL)) - } - } - } +interface BackendCheck extends Status { + retry: () => void +} - check() +const initialStatus: Status = { + loading: true, + initialized: false, + failed: false, + lastError: null, +} + +/** + * 后端就绪检测。 + * + * 三路信号汇聚: + * 1. HTTP 轮询 /sys_check —— 所有平台通用 + * 2. Tauri 'backend-ready' 事件 —— 桌面端 sidecar 探测器先于 HTTP 一步触达 + * 3. Tauri 'backend-terminated' / 'backend-startup-timeout' 事件 —— sidecar 死了或超时 + * 立即进失败态,不再继续轮询(旧实现的 while(true) 就是死在这里) + * + * 任何一路报「ready」即成功;任何一路报「失败」立即停掉所有轮询。 + */ +export const useCheckBackend = (): BackendCheck => { + const [status, setStatus] = useState(initialStatus) + // tick 用来强制重启 useEffect(retry 时 +1),不引入 ref 互斥逻辑的复杂性 + const [tick, setTick] = useState(0) + // 标记当前 effect 是否已 settle(避免后到的事件覆盖已确定的成功/失败态) + const settledRef = useRef(false) + + const retry = useCallback(() => { + settledRef.current = false + setStatus(initialStatus) + setTick((t) => t + 1) }, []) - return { loading, initialized } -} \ No newline at end of file + useEffect(() => { + let timeoutId: ReturnType | null = null + let pollTimerId: ReturnType | null = null + let cancelled = false + const tauriUnsubs: Array<() => void> = [] + + const markReady = () => { + if (cancelled || settledRef.current) return + settledRef.current = true + setStatus({ loading: false, initialized: true, failed: false, lastError: null }) + } + + const markFailed = (msg: string) => { + if (cancelled || settledRef.current) return + settledRef.current = true + setStatus({ loading: false, initialized: false, failed: true, lastError: msg }) + } + + const poll = async () => { + if (cancelled || settledRef.current) return + const ok = await probeSysCheck() + if (cancelled || settledRef.current) return + if (ok) { + markReady() + return + } + // 单次失败不报 toast、不抛错,继续轮询 + setStatus((s) => ({ ...s, lastError: '后端尚未响应' })) + pollTimerId = setTimeout(poll, POLL_INTERVAL_MS) + } + + // 总超时兜底 + timeoutId = setTimeout(() => { + markFailed(`后端 ${TOTAL_TIMEOUT_MS / 1000}s 内未就绪,请检查后端日志或重启`) + }, TOTAL_TIMEOUT_MS) + + // 桌面端订阅 Tauri 事件(动态 import 避免 web 端打包报错) + if (isTauri) { + import('@tauri-apps/api/event') + .then(async ({ listen }) => { + if (cancelled) return + const offReady = await listen('backend-ready', () => markReady()) + const offTimeout = await listen('backend-startup-timeout', (e) => { + markFailed(typeof e.payload === 'string' ? e.payload : '后端启动超时') + }) + const offTerm = await listen('backend-terminated', (e) => { + const code = e.payload + markFailed(`后端进程已退出 (code=${code ?? 'unknown'})`) + }) + tauriUnsubs.push(offReady, offTimeout, offTerm) + }) + .catch((err) => { + // 拿不到 @tauri-apps/api/event 不致命,继续走 HTTP 轮询 + console.warn('[useCheckBackend] 无法订阅 Tauri 事件:', err) + }) + } + + // 立刻开始第一轮轮询 + poll() + + return () => { + cancelled = true + if (timeoutId) clearTimeout(timeoutId) + if (pollTimerId) clearTimeout(pollTimerId) + tauriUnsubs.forEach((off) => { + try { + off() + } catch { + /* noop */ + } + }) + } + }, [tick]) + + return { ...status, retry } +} diff --git a/BillNote_frontend/src/pages/HomePage/components/NoteForm.tsx b/BillNote_frontend/src/pages/HomePage/components/NoteForm.tsx index 1a1f6e5..9e9cbfa 100644 --- a/BillNote_frontend/src/pages/HomePage/components/NoteForm.tsx +++ b/BillNote_frontend/src/pages/HomePage/components/NoteForm.tsx @@ -39,6 +39,7 @@ import { Textarea } from '@/components/ui/textarea.tsx' import { noteStyles, noteFormats, videoPlatforms } from '@/constant/note.ts' import { fetchModels } from '@/services/model.ts' import { useNavigate } from 'react-router-dom' +import toast from 'react-hot-toast' /* -------------------- 校验 Schema -------------------- */ const formSchema = z @@ -229,8 +230,25 @@ const NoteForm = () => { } // message.success('已提交任务') - const data = await generateNote(payload) - addPendingTask(data.task_id, values.platform, payload) + try { + const data = await generateNote(payload) + addPendingTask(data.task_id, values.platform, payload) + } catch (e: any) { + // 就绪门禁:本地转写模型还没下载好。后端返回 reason='transcriber_model_not_ready', + // 引导用户去「设置 → 音频转写配置」下载,而不是留一个静默失败的任务。 + if (e?.data?.reason === 'transcriber_model_not_ready') { + const downloading = e?.data?.downloading + toast.error( + downloading + ? '转写模型正在下载中,请稍候再提交' + : '转写模型尚未下载,请先去「音频转写配置」页下载', + ) + if (!downloading) navigate('/settings/transcriber') + return + } + // 其余错误:axios 拦截器已经弹过 toast,这里只兜底不让 promise 变成未处理 rejection + console.error('提交任务失败:', e) + } } const onInvalid = (errors: FieldErrors) => { console.warn('表单校验失败:', errors) diff --git a/BillNote_frontend/src/pages/Onboarding/index.tsx b/BillNote_frontend/src/pages/Onboarding/index.tsx index d60a6c1..7cebcb2 100644 --- a/BillNote_frontend/src/pages/Onboarding/index.tsx +++ b/BillNote_frontend/src/pages/Onboarding/index.tsx @@ -1,9 +1,36 @@ -import { useEffect, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' -import { addProvider, addModel, getProviderList, testConnection } from '@/services/model' +import { addProvider, addModel, testConnection, getProviderList, updateProviderById } from '@/services/model' import { getTranscriberConfig, updateTranscriberConfig } from '@/services/transcriber' import logo from '@/assets/icon.svg' +// 后端 R.error / ProviderError 的形状是 { code, msg, data },没有 .message。 +// 直接 ${e} 会渲染成 [object Object],这里统一抽取可读文案。 +function errText(e: any): string { + if (!e) return '未知错误' + if (typeof e === 'string') return e + return e.msg || e.message || JSON.stringify(e) +} + +const isTauri = typeof window !== 'undefined' && '__TAURI_INTERNALS__' in window + +// 后端连通性自检不走共享 axios(会弹 toast),用裸 fetch 避免启动期 toast 叠堆 +function getBackendBase(): string { + const fromEnv = (import.meta as any).env?.VITE_API_BASE_URL as string | undefined + return ((fromEnv && fromEnv.length > 0) ? fromEnv : '/api').replace(/\/$/, '') +} +async function pingBackend(): Promise { + try { + const res = await fetch(`${getBackendBase()}/sys_check`) + if (!res.ok) return false + const json = await res.json().catch(() => null) + return json?.code === 0 + } + catch { + return false + } +} + // 桌面端首启 4 步引导。完成后写 localStorage('bilinote-onboarded') = '1',路由守卫不再拦。 // // 1. 后端连通性自检 @@ -52,24 +79,52 @@ const Onboarding = () => { } // step 1: ping 后端 + // 关键点:旧实现 useEffect 只在 step===1 时 ping 一次。失败后 backendOk=false 永远卡死, + // 即便后端随后就绪了也不会刷新。现在改成: + // - 手动重试按钮调用 doPing + // - Tauri backend-ready / backend-restarted 事件触发 doPing + // - 初次失败后 2s 自动再 ping 一次(覆盖 sidecar 慢热场景) + const doPing = useCallback(async () => { + setPinging(true) + const ok = await pingBackend() + setBackendOk(ok) + setPinging(false) + return ok + }, []) + useEffect(() => { if (step !== 1) return let cancelled = false + let timerId: ReturnType | null = null + let offReady: (() => void) | null = null + let offRestarted: (() => void) | null = null + ;(async () => { - setPinging(true) - try { - await getProviderList() - if (!cancelled) setBackendOk(true) + const ok = await doPing() + if (cancelled) return + if (!ok) { + // 后端可能正在解压/启动,2s 后再试一次 + timerId = setTimeout(() => { if (!cancelled) doPing() }, 2000) } - catch { - if (!cancelled) setBackendOk(false) - } - finally { - if (!cancelled) setPinging(false) + + // 桌面端订阅 Tauri 事件:后端真正就绪 / 重启完成时立刻再检查一次 + if (isTauri) { + try { + const { listen } = await import('@tauri-apps/api/event') + offReady = await listen('backend-ready', () => { if (!cancelled) doPing() }) + offRestarted = await listen('backend-restarted', () => { if (!cancelled) doPing() }) + } + catch { /* 拿不到事件 API 不致命 */ } } })() - return () => { cancelled = true } - }, [step]) + + return () => { + cancelled = true + if (timerId) clearTimeout(timerId) + offReady?.() + offRestarted?.() + } + }, [step, doPing]) async function saveProvider() { setError('') @@ -79,31 +134,61 @@ const Onboarding = () => { if (!modelName.trim()) { setError('请填模型名'); return } setSavingProvider(true) try { - // 复用桌面 web 端的 add_provider;type 必须是 'custom'(backend 强制) - const res: any = await addProvider({ - name: providerName.trim(), - api_key: apiKey.trim(), - base_url: baseUrl.trim(), - type: 'custom', - logo: 'custom', - }) - const newId = (res?.data ?? res) as string | undefined - if (!newId) throw new Error('后端未返回 provider id') - setProviderId(newId) + const name = providerName.trim() + let pid: string | undefined - // 加一个默认 model - await addModel({ provider_id: newId, model_name: modelName.trim() }) + // 后端 seed_default_providers() 会预置 OpenAI / DeepSeek / Qwen 等同名供应商, + // 直接 add_provider 撞名会报「供应商名称已存在」。所以:撞名时改为 + // 「找到那个已存在的同名供应商 → 更新它的 key / base_url」而不是新建。 + // 这些调用都带 silent:true —— 撞名是预期内的,不弹全局红 toast。 + try { + const res: any = await addProvider({ + name, + api_key: apiKey.trim(), + base_url: baseUrl.trim(), + type: 'custom', + logo: 'custom', + }, { silent: true }) + pid = (res?.data ?? res) as string | undefined + if (!pid) throw new Error('后端未返回 provider id') + } + catch (addErr: any) { + const msg = errText(addErr) + if (!msg.includes('已存在')) throw addErr + // 撞名:复用已存在的同名供应商 + const list: any[] = (await getProviderList({ silent: true })) || [] + const existing = list.find(p => p?.name === name) + if (!existing?.id) throw new Error(`供应商「${name}」已存在但无法定位,请换个名字`) + pid = existing.id + await updateProviderById({ + id: pid, + api_key: apiKey.trim(), + base_url: baseUrl.trim(), + enabled: 1, + }, { silent: true }) + } - // 测试连通 - try { await testConnection({ id: newId }) } + setProviderId(pid!) + + // 加一个默认 model(同名 model 已存在时后端会报错,这里也容错) + try { + await addModel({ provider_id: pid!, model_name: modelName.trim() }, { silent: true }) + } + catch (modelErr: any) { + const msg = errText(modelErr) + if (!msg.includes('已存在')) throw modelErr + // 模型已存在,直接继续 + } + + // 测试连通(失败不阻断流程,让用户自己决定继续) + try { await testConnection({ id: pid!, model: modelName.trim() }, { silent: true }) } catch (e: any) { - // 测试失败不阻断流程,让用户自己决定继续 - console.warn('测试连接失败:', e?.message ?? e) + console.warn('测试连接失败:', errText(e)) } next() } catch (e: any) { - setError(`保存失败:${e?.message ?? e}`) + setError(`保存失败:${errText(e)}`) } finally { setSavingProvider(false) @@ -123,7 +208,7 @@ const Onboarding = () => { next() } catch (e: any) { - setError(`保存失败:${e?.message ?? e}`) + setError(`保存失败:${errText(e)}`) } finally { setSavingTranscriber(false) @@ -171,6 +256,15 @@ const Onboarding = () => {
)}
+ {backendOk !== true && ( + + )} diff --git a/BillNote_frontend/src/pages/SettingPage/Downloader.tsx b/BillNote_frontend/src/pages/SettingPage/Downloader.tsx index cb80a8c..dfa58c1 100644 --- a/BillNote_frontend/src/pages/SettingPage/Downloader.tsx +++ b/BillNote_frontend/src/pages/SettingPage/Downloader.tsx @@ -1,10 +1,11 @@ -import Provider from '@/components/Form/modelForm/Provider.tsx' import { Outlet } from 'react-router-dom' import Options from '@/components/Form/DownloaderForm/Options.tsx' +import ProxyConfig from '@/components/Form/DownloaderForm/ProxyConfig.tsx' const Downloader = () => { return (
-
+
+
diff --git a/BillNote_frontend/src/pages/SettingPage/Monitor.tsx b/BillNote_frontend/src/pages/SettingPage/Monitor.tsx index f806606..76d74b8 100644 --- a/BillNote_frontend/src/pages/SettingPage/Monitor.tsx +++ b/BillNote_frontend/src/pages/SettingPage/Monitor.tsx @@ -174,7 +174,11 @@ export default function Monitor() { Whisper 模型 - {status && } + {status && (() => { + const isLocal = status.whisper.transcriber_type === 'fast-whisper' || status.whisper.transcriber_type === 'mlx-whisper' + if (!isLocal) return + return + })()} {loading && !status ? ( @@ -192,6 +196,14 @@ export default function Monitor() { 转写引擎: {status.whisper.transcriber_type}
+ {(status.whisper.transcriber_type === 'fast-whisper' || status.whisper.transcriber_type === 'mlx-whisper') && ( +
+ 本地下载: + + {status.whisper.downloaded ? '已就绪' : '未下载(首次转写会触发下载)'} + +
+ )}
) : null} diff --git a/BillNote_frontend/src/services/model.ts b/BillNote_frontend/src/services/model.ts index ec91d94..0a00199 100644 --- a/BillNote_frontend/src/services/model.ts +++ b/BillNote_frontend/src/services/model.ts @@ -1,21 +1,26 @@ import request from '@/utils/request.ts' -export const getProviderList = async () => { - return await request.get('/get_all_providers') +// opts.silent: 让本次请求失败时不弹全局红 toast(调用方自行 catch 处理, +// 比如 onboarding 撞名重试这种预期内失败) +interface CallOpts { silent?: boolean } +const cfg = (o?: CallOpts) => (o?.silent ? { suppressToast: true } : undefined) + +export const getProviderList = async (opts?: CallOpts) => { + return await request.get('/get_all_providers', cfg(opts)) } export const getProviderById = async (id: string) => { return await request.get(`/get_provider_by_id/${id}`) } -export const updateProviderById = async (data: any) => { - return await request.post('/update_provider', data) +export const updateProviderById = async (data: any, opts?: CallOpts) => { + return await request.post('/update_provider', data, cfg(opts)) } -export const addProvider = async (data: any) => { - return await request.post('/add_provider', data) +export const addProvider = async (data: any, opts?: CallOpts) => { + return await request.post('/add_provider', data, cfg(opts)) } -export const testConnection = async (data: any) => { - return await request.post('/connect_test', data) +export const testConnection = async (data: any, opts?: CallOpts) => { + return await request.post('/connect_test', data, cfg(opts)) } export const fetchModels = async (providerId: string) => { @@ -26,8 +31,11 @@ export const fetchEnableModelById = async (id: string) => { return await request.get('/model_enable/' + id) } -export async function addModel(data: { provider_id: string; model_name: string }) { - return request.post('/models', data) +export async function addModel( + data: { provider_id: string; model_name: string }, + opts?: CallOpts, +) { + return request.post('/models', data, cfg(opts)) } export const fetchEnableModels = async () => { diff --git a/BillNote_frontend/src/services/proxy.ts b/BillNote_frontend/src/services/proxy.ts new file mode 100644 index 0000000..8911b5a --- /dev/null +++ b/BillNote_frontend/src/services/proxy.ts @@ -0,0 +1,19 @@ +import request from '@/utils/request' + +export interface ProxyConfig { + enabled: boolean + url: string + /** 后端实际生效的代理(可能来自配置,也可能来自 HTTP_PROXY 环境变量兜底) */ + effective: string +} + +export const getProxyConfig = async (): Promise => { + return await request.get('/proxy_config') +} + +export const updateProxyConfig = async (data: { + enabled: boolean + url?: string +}): Promise => { + return await request.post('/proxy_config', data) +} diff --git a/BillNote_frontend/src/services/system.ts b/BillNote_frontend/src/services/system.ts index 4f54c97..f51d295 100644 --- a/BillNote_frontend/src/services/system.ts +++ b/BillNote_frontend/src/services/system.ts @@ -1,9 +1,29 @@ import request from '@/utils/request' -export const systemCheck = async () => { +export interface SysHealth { + backend: 'ok' | 'error' + ffmpeg: 'ok' | 'missing' + db: 'ok' | 'error' + whisper_model: { + /** 当前选中的模型 size,例如 'tiny' / 'base' / 'large-v3' */ + size: string | null + /** 转写器类型 */ + type: string | null + /** 是否已完整下载到本地(仅本地引擎有意义) */ + downloaded: boolean + /** 是否实际检查过 —— 在线引擎跳过检查时为 false */ + checked: boolean + } +} + +/** 详细健康状态:用于设置页 / 启动诊断。后端始终返回 200,按字段判断各项。 */ +export const getSysHealth = async (): Promise => { return await request.get('/sys_health') } +/** 保留旧 systemCheck 函数名(App.tsx 启动时仍调用),返回值同 getSysHealth。 */ +export const systemCheck = getSysHealth + export interface DeployStatus { backend: { status: string @@ -11,12 +31,16 @@ export interface DeployStatus { } cuda: { available: boolean + /** 新增:torch 是否安装。轻量部署没装 torch 时为 false,避免误判为 CUDA 故障 */ + torch_installed?: boolean version: string | null gpu_name: string | null } whisper: { model_size: string transcriber_type: string + /** 新增:模型是否已完整下载(fast-whisper 看 model.bin / mlx 看 config.json) */ + downloaded: boolean } ffmpeg: { available: boolean diff --git a/BillNote_frontend/src/store/taskStore/index.ts b/BillNote_frontend/src/store/taskStore/index.ts index 678b0c5..f511f78 100644 --- a/BillNote_frontend/src/store/taskStore/index.ts +++ b/BillNote_frontend/src/store/taskStore/index.ts @@ -169,10 +169,25 @@ export const useTaskStore = create()( if (!task) return const newFormData = payload || task.formData - await generateNote({ - ...newFormData, - task_id: id, - }) + try { + await generateNote({ + ...newFormData, + task_id: id, + }) + } catch (e: any) { + // 就绪门禁:转写模型未下载好。不要把任务标成 PENDING(会一直转), + // 给提示让用户先去下载。 + if (e?.data?.reason === 'transcriber_model_not_ready') { + toast.error( + e?.data?.downloading + ? '转写模型正在下载中,请稍候再重试' + : '转写模型尚未下载,请先去「设置 → 音频转写配置」页下载', + ) + return + } + console.error('重试任务失败:', e) + return + } set(state => ({ tasks: state.tasks.map(t => diff --git a/BillNote_frontend/src/utils/request.ts b/BillNote_frontend/src/utils/request.ts index ccaed46..c44693c 100644 --- a/BillNote_frontend/src/utils/request.ts +++ b/BillNote_frontend/src/utils/request.ts @@ -8,6 +8,14 @@ export interface IResponse { data: T; } +// 允许调用方在 axios 配置里带 suppressToast: true,让拦截器对【预期内的失败】 +// 不弹全局红 toast(例如 onboarding 撞名重试、轮询健康检查)。业务代码自己 catch 处理。 +declare module 'axios' { + export interface AxiosRequestConfig { + suppressToast?: boolean + } +} + // 模拟一个消息提示函数 (实际项目中会使用UI库的组件,如 Ant Design 的 message 或 Element UI 的 ElMessage) // This function simulates a message display (in real projects, you'd use a UI library's component) @@ -28,25 +36,24 @@ request.interceptors.response.use( // showMessage('success', res.msg || '操作成功'); // 如果需要显示成功消息 return res.data; // 返回data部分,简化后续业务代码 } else { - // 业务错误,统一显示后端返回的错误消息 - // Business error, uniformly display the error message returned from the backend - toast.error(res.msg || '操作失败,请稍后再试'); + // 业务错误,统一显示后端返回的错误消息(除非调用方显式 suppressToast) + if (!response.config?.suppressToast) { + toast.error(res.msg || '操作失败,请稍后再试'); + } return Promise.reject(res); // 拒绝Promise,让业务代码可以捕获并处理 } }, (error) => { + const suppress = error?.config?.suppressToast === true // 网络/服务器错误 const res = error?.response?.data as IResponse | undefined; if (res) { // 如果后端有返回错误信息,则显示后端信息 - // If the backend returns an error message, display it - - toast.error(res.msg || '服务器错误,请稍后再试'); + if (!suppress) toast.error(res.msg || '服务器错误,请稍后再试'); return Promise.reject(res); } else { // 没有响应数据(如网络中断),显示通用网络错误 - // No response data (e.g., network disconnected), display generic network error - toast.error( '请求失败,请检查网络连接或稍后再试') + if (!suppress) toast.error('请求失败,请检查网络连接或稍后再试') return Promise.reject({ code: -1, msg: '请求失败,请检查网络连接',