diff --git a/frontend/bun.lock b/frontend/bun.lock index a3e08fe42..1d756c499 100644 --- a/frontend/bun.lock +++ b/frontend/bun.lock @@ -18,6 +18,7 @@ "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", "@react-router/node": "^7.9.4", @@ -292,6 +293,8 @@ "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], + "@radix-ui/react-switch": ["@radix-ui/react-switch@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ=="], + "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="], "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.2.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg=="], diff --git a/frontend/package.json b/frontend/package.json index 27e4cf7ca..4acbd4766 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -31,6 +31,7 @@ "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-separator": "^1.1.8", "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", "@react-router/node": "^7.9.4", diff --git a/frontend/src-tauri/Cargo.lock b/frontend/src-tauri/Cargo.lock index 31d205a37..a2f29ce89 100644 --- a/frontend/src-tauri/Cargo.lock +++ b/frontend/src-tauri/Cargo.lock @@ -174,7 +174,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -209,7 +209,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -346,7 +346,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -423,9 +423,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" dependencies = [ "serde", ] @@ -499,9 +499,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.44" +version = "1.2.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3" +checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" dependencies = [ "find-msvc-tools", "shlex", @@ -668,9 +668,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -700,7 +700,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -710,7 +710,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -734,7 +734,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -745,7 +745,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -768,7 +768,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -826,7 +826,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -849,7 +849,7 @@ checksum = "788160fb30de9cdd857af31c6a2675904b16ece8fc2737b2c7127ba368c9d0f4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -941,7 +941,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -962,9 +962,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erased-serde" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "259d404d09818dec19332e31d94558aeb442fea04c817006456c24b5460bbd4b" +checksum = "89e8918065695684b2b0702da20382d5ae6065cf3327bc2d6436bd49a71ce9f3" dependencies = [ "serde", "serde_core", @@ -1038,9 +1038,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "flate2" @@ -1076,7 +1076,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -1163,7 +1163,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -1305,9 +1305,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -1413,7 +1413,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -1492,7 +1492,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -1588,9 +1588,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", @@ -1609,9 +1609,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" dependencies = [ "base64 0.22.1", "bytes", @@ -1813,9 +1813,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" dependencies = [ "memchr", "serde", @@ -2060,7 +2060,7 @@ checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -2221,7 +2221,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -2505,9 +2505,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "open" -version = "5.3.2" +version = "5.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" +checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc" dependencies = [ "dunce", "is-wsl", @@ -2711,7 +2711,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -2930,18 +2930,18 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.38.3" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" dependencies = [ "memchr", ] [[package]] name = "quote" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -3082,7 +3082,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -3275,9 +3275,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.5" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1317c3bf3e7df961da95b0a56a172a02abead31276215a0497241a7624b487ce" +checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" dependencies = [ "dyn-clone", "ref-cast", @@ -3294,7 +3294,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -3376,7 +3376,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -3387,7 +3387,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -3411,7 +3411,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -3446,9 +3446,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.15.1" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa66c845eee442168b2c8134fec70ac50dc20e760769c8ba0ad1319ca1959b04" +checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1" dependencies = [ "base64 0.22.1", "chrono", @@ -3456,7 +3456,7 @@ dependencies = [ "indexmap 1.9.3", "indexmap 2.12.0", "schemars 0.9.0", - "schemars 1.0.5", + "schemars 1.1.0", "serde_core", "serde_json", "serde_with_macros", @@ -3465,14 +3465,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.15.1" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91a903660542fced4e99881aa481bdbaec1634568ee02e0b8bd57c64cb38955" +checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -3494,7 +3494,7 @@ checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -3726,9 +3726,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.108" +version = "2.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" dependencies = [ "proc-macro2", "quote", @@ -3752,7 +3752,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -3816,7 +3816,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -3833,9 +3833,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.9.2" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bceb52453e507c505b330afe3398510e87f428ea42b6e76ecb6bd63b15965b5" +checksum = "9e492485dd390b35f7497401f67694f46161a2a00ffd800938d5dd3c898fb9d8" dependencies = [ "anyhow", "bytes", @@ -3884,9 +3884,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.5.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a924b6c50fe83193f0f8b14072afa7c25b7a72752a2a73d9549b463f5fe91a38" +checksum = "87d6f8cafe6a75514ce5333f115b7b1866e8e68d9672bf4ca89fc0f35697ea9d" dependencies = [ "anyhow", "cargo_toml", @@ -3906,9 +3906,9 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c1fe64c74cc40f90848281a90058a6db931eb400b60205840e09801ee30f190" +checksum = "b7ef707148f0755110ca54377560ab891d722de4d53297595380a748026f139f" dependencies = [ "base64 0.22.1", "brotli", @@ -3922,7 +3922,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "syn 2.0.108", + "syn 2.0.110", "tauri-utils", "thiserror 2.0.17", "time", @@ -3933,14 +3933,14 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.5.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260c5d2eb036b76206b9fca20b7be3614cfd21046c5396f7959e0e64a4b07f2f" +checksum = "71664fd715ee6e382c05345ad258d6d1d50f90cf1b58c0aa726638b33c2a075d" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", "tauri-codegen", "tauri-utils", ] @@ -4119,10 +4119,11 @@ dependencies = [ [[package]] name = "tauri-winres" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd21509dd1fa9bd355dc29894a6ff10635880732396aa38c0066c1e6c1ab8074" +checksum = "1087b111fe2b005e42dbdc1990fc18593234238d47453b0c99b7de1c9ab2c1e0" dependencies = [ + "dunce", "embed-resource", "toml 0.9.8", ] @@ -4177,7 +4178,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -4188,7 +4189,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -4436,7 +4437,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -4744,7 +4745,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", "wasm-bindgen-shared", ] @@ -4846,7 +4847,7 @@ checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -4973,7 +4974,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -4984,7 +4985,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -5419,7 +5420,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", "synstructure", ] @@ -5466,7 +5467,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", "zbus_names", "zvariant", "zvariant_utils", @@ -5501,7 +5502,7 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -5521,7 +5522,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", "synstructure", ] @@ -5555,7 +5556,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", ] [[package]] @@ -5581,7 +5582,7 @@ dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.108", + "syn 2.0.110", "zvariant_utils", ] @@ -5594,6 +5595,6 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.108", + "syn 2.0.110", "winnow 0.7.13", ] diff --git a/frontend/src-tauri/src/backend.rs b/frontend/src-tauri/src/backend.rs index d2b31d242..b4a26dc6e 100644 --- a/frontend/src-tauri/src/backend.rs +++ b/frontend/src-tauri/src/backend.rs @@ -58,7 +58,6 @@ impl BackendManager { self.env_path, module_name ); - log::info!("Working directory: {:?}", self.backend_path); // Use sidecar command directly (Tauri handles platform automatically) let sidecar_command = self @@ -233,9 +232,6 @@ impl BackendManager { processes.len() ); - // Note: CommandChild doesn't have try_wait, so we just log the count - log::info!("Processes started: {}", processes.len()); - Ok(()) } diff --git a/frontend/src/api/setting.ts b/frontend/src/api/setting.ts index 23bb02c2d..819a5ae98 100644 --- a/frontend/src/api/setting.ts +++ b/frontend/src/api/setting.ts @@ -2,7 +2,12 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { API_QUERY_KEYS } from "@/constants/api"; import type { ApiResponse } from "@/lib/api-client"; import { apiClient } from "@/lib/api-client"; -import type { MemoryItem } from "@/types/setting"; +import type { + MemoryItem, + ModelProvider, + ProviderDetail, + ProviderModelInfo, +} from "@/types/setting"; export const useGetMemoryList = () => { return useQuery({ @@ -26,3 +31,136 @@ export const useRemoveMemory = () => { }, }); }; + +export const useGetModelProviders = () => { + return useQuery({ + queryKey: API_QUERY_KEYS.SETTING.modelProviders, + queryFn: () => + apiClient.get>("/models/providers"), + select: (data) => data.data, + }); +}; + +export const useGetModelProviderDetail = (provider: string | undefined) => { + return useQuery({ + enabled: !!provider, + queryKey: API_QUERY_KEYS.SETTING.modelProviderDetail( + provider ? [provider] : [], + ), + queryFn: () => + apiClient.get>( + `/models/providers/${provider}`, + ), + select: (data) => data.data, + }); +}; + +export const useUpdateProviderConfig = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (params: { + provider: string; + api_key?: string; + base_url?: string; + }) => + apiClient.put>( + `/models/providers/${params.provider}/config`, + { + api_key: params.api_key, + base_url: params.base_url, + }, + ), + onSuccess: (_data, variables) => { + queryClient.invalidateQueries({ + queryKey: API_QUERY_KEYS.SETTING.modelProviderDetail([ + variables.provider, + ]), + }); + }, + }); +}; + +export const useAddProviderModel = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (params: { + provider: string; + model_id: string; + model_name: string; + }) => + apiClient.post>( + `/models/providers/${params.provider}/models`, + { + model_id: params.model_id, + model_name: params.model_name, + }, + ), + onSuccess: (_data, variables) => { + queryClient.invalidateQueries({ + queryKey: API_QUERY_KEYS.SETTING.modelProviderDetail([ + variables.provider, + ]), + }); + }, + }); +}; + +export const useDeleteProviderModel = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (params: { provider: string; model_id: string }) => + apiClient.delete>( + `/models/providers/${params.provider}/models?model_id=${encodeURIComponent(params.model_id)}`, + ), + onSuccess: (_data, variables) => { + queryClient.invalidateQueries({ + queryKey: API_QUERY_KEYS.SETTING.modelProviderDetail([ + variables.provider, + ]), + }); + }, + }); +}; + +export const useSetDefaultProvider = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (params: { provider: string }) => + apiClient.put>("/models/providers/default", { + provider: params.provider, + }), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: API_QUERY_KEYS.SETTING.modelProviders, + }); + queryClient.invalidateQueries({ + queryKey: API_QUERY_KEYS.SETTING.modelProviderDetail([]), + }); + }, + }); +}; + +export const useSetDefaultProviderModel = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (params: { provider: string; model_id: string }) => + apiClient.put>( + `/models/providers/${params.provider}/default-model`, + { + model_id: params.model_id, + }, + ), + onSuccess: (_data, variables) => { + queryClient.invalidateQueries({ + queryKey: API_QUERY_KEYS.SETTING.modelProviderDetail([ + variables.provider, + ]), + }); + }, + }); +}; diff --git a/frontend/src/app/setting/_layout.tsx b/frontend/src/app/setting/_layout.tsx index 12a034779..182f91e07 100644 --- a/frontend/src/app/setting/_layout.tsx +++ b/frontend/src/app/setting/_layout.tsx @@ -1,4 +1,4 @@ -import { Brain, Settings } from "lucide-react"; +import { Brain, Cpu, Settings } from "lucide-react"; import { NavLink, Outlet, useLocation } from "react-router"; import { Item, @@ -22,6 +22,12 @@ const settingNavItems = [ label: "Memory", path: "/setting/memory", }, + { + id: "models", + icon: Cpu, + label: "Models", + path: "/setting/models", + }, // { // id: "language", // icon: Globe, @@ -40,9 +46,9 @@ export default function SettingLayout() { const location = useLocation(); return ( -
+
{/* Left navigation */} -
diff --git a/frontend/src/app/setting/components/index.tsx b/frontend/src/app/setting/components/memory/index.tsx similarity index 100% rename from frontend/src/app/setting/components/index.tsx rename to frontend/src/app/setting/components/memory/index.tsx diff --git a/frontend/src/app/setting/components/memory-item-card.tsx b/frontend/src/app/setting/components/memory/memory-item-card.tsx similarity index 100% rename from frontend/src/app/setting/components/memory-item-card.tsx rename to frontend/src/app/setting/components/memory/memory-item-card.tsx diff --git a/frontend/src/app/setting/components/models/model-detail.tsx b/frontend/src/app/setting/components/models/model-detail.tsx new file mode 100644 index 000000000..6334feef8 --- /dev/null +++ b/frontend/src/app/setting/components/models/model-detail.tsx @@ -0,0 +1,368 @@ +import { useForm } from "@tanstack/react-form"; +import { Eye, EyeOff, Plus, Trash2 } from "lucide-react"; +import { useEffect, useState } from "react"; + +import { z } from "zod"; +import { + useAddProviderModel, + useDeleteProviderModel, + useGetModelProviderDetail, + useSetDefaultProvider, + useSetDefaultProviderModel, + useUpdateProviderConfig, +} from "@/api/setting"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Field, + FieldError, + FieldGroup, + FieldLabel, +} from "@/components/ui/field"; +import { Input } from "@/components/ui/input"; +import { + InputGroup, + InputGroupAddon, + InputGroupButton, + InputGroupInput, +} from "@/components/ui/input-group"; +import { Switch } from "@/components/ui/switch"; +import ScrollContainer from "@/components/valuecell/scroll/scroll-container"; + +const configSchema = z.object({ + api_key: z.string(), + base_url: z.string(), +}); + +const addModelSchema = z.object({ + model_id: z.string().min(1, "Model ID is required"), + model_name: z.string().min(1, "Model name is required"), +}); + +type ModelDetailProps = { + provider: string; +}; + +export function ModelDetail({ provider }: ModelDetailProps) { + const { data: providerDetail, isLoading: detailLoading } = + useGetModelProviderDetail(provider); + + const { mutate: updateConfig, isPending: updatingConfig } = + useUpdateProviderConfig(); + const { mutate: addModel, isPending: addingModel } = useAddProviderModel(); + const { mutate: deleteModel, isPending: deletingModel } = + useDeleteProviderModel(); + const { mutate: setDefaultModel, isPending: settingDefaultModel } = + useSetDefaultProviderModel(); + const { mutate: setDefaultProvider, isPending: settingDefaultProvider } = + useSetDefaultProvider(); + + const [isAddDialogOpen, setIsAddDialogOpen] = useState(false); + const [showApiKey, setShowApiKey] = useState(false); + + const configForm = useForm({ + defaultValues: { + api_key: "", + base_url: "", + }, + validators: { + onSubmit: configSchema, + }, + onSubmit: async ({ value }) => { + if (!provider) return; + updateConfig({ + provider, + api_key: value.api_key || providerDetail?.api_key, + base_url: value.base_url || providerDetail?.base_url, + }); + }, + }); + + useEffect(() => { + if (providerDetail) { + configForm.setFieldValue("api_key", providerDetail.api_key || ""); + configForm.setFieldValue("base_url", providerDetail.base_url || ""); + } + }, [providerDetail, configForm]); + + useEffect(() => { + if (provider) setShowApiKey(false); + }, [provider]); + + const addModelForm = useForm({ + defaultValues: { + model_id: "", + model_name: "", + }, + validators: { + onSubmit: addModelSchema, + }, + onSubmit: async ({ value }) => { + if (!provider) return; + addModel({ + provider, + model_id: value.model_id, + model_name: value.model_name, + }); + addModelForm.reset(); + setIsAddDialogOpen(false); + }, + }); + + const handleSetDefaultModel = (modelId: string) => { + if (!provider) return; + setDefaultModel({ provider, model_id: modelId }); + }; + + const handleDeleteModel = (modelId: string) => { + if (!provider) return; + deleteModel({ provider, model_id: modelId }); + }; + + const isBusy = + updatingConfig || + addingModel || + deletingModel || + settingDefaultModel || + settingDefaultProvider; + + if (detailLoading) { + return ( +
Loading provider details...
+ ); + } + + if (!providerDetail) { + return null; + } + + return ( + +
+

{provider}

+
+

+ Default Provider +

+ setDefaultProvider({ provider })} + /> +
+
+ +
+
+ + + {(field) => ( + + + API key + + + field.handleChange(e.target.value)} + onBlur={() => configForm.handleSubmit()} + /> + + setShowApiKey(!showApiKey)} + aria-label={ + showApiKey ? "Hide password" : "Show password" + } + > + {showApiKey ? ( + + ) : ( + + )} + + + + + + + )} + + + {/* API Host section */} + + {(field) => ( + + + API Host + + field.handleChange(e.target.value)} + onBlur={() => configForm.handleSubmit()} + /> + + + )} + + + + {/* Models section */} +
+
+
Models
+ + + + + + { + e.preventDefault(); + addModelForm.handleSubmit(); + }} + > + + Add Model + +
+ + + {(field) => ( + + + Model ID + + + field.handleChange(e.target.value) + } + onBlur={field.handleBlur} + /> + + + )} + + + + {(field) => ( + + + Model Name + + + field.handleChange(e.target.value) + } + onBlur={field.handleBlur} + /> + + + )} + + +
+ + + + + +
+
+
+ + {providerDetail.models.length === 0 ? ( +
+ No models configured for this provider. +
+ ) : ( +
+ {providerDetail.models.map((m) => ( +
+ + {m.model_name} + + +
+ + handleSetDefaultModel(m.model_id) + } + /> + +
+
+ ))} +
+ )} +
+
+ +
+ ); +} diff --git a/frontend/src/app/setting/components/models/model-providers.tsx b/frontend/src/app/setting/components/models/model-providers.tsx new file mode 100644 index 000000000..f4f93545e --- /dev/null +++ b/frontend/src/app/setting/components/models/model-providers.tsx @@ -0,0 +1,65 @@ +import { Item, ItemGroup } from "@/components/ui/item"; +import PngIcon from "@/components/valuecell/png-icon"; +import ScrollContainer from "@/components/valuecell/scroll/scroll-container"; +import { MODEL_PROVIDER_ICONS } from "@/constants/icons"; +import { cn } from "@/lib/utils"; +import type { ModelProvider } from "@/types/setting"; + +type ModelProvidersProps = { + providers: ModelProvider[]; + selectedProvider?: string; + onSelect: (provider: string) => void; +}; + +export function ModelProviders({ + providers, + selectedProvider, + onSelect, +}: ModelProvidersProps) { + return ( +
+

Model Provider

+ + + + {providers.length === 0 ? ( +
+ No providers available. +
+ ) : ( + providers.map((provider) => { + const isActive = provider.provider === selectedProvider; + + return ( + onSelect(provider.provider)} + > + +
+ {provider.provider} + + {provider.provider} + +
+
+ ); + }) + )} +
+
+
+ ); +} diff --git a/frontend/src/app/setting/memory.tsx b/frontend/src/app/setting/memory.tsx index 9b582c08e..44db1a894 100644 --- a/frontend/src/app/setting/memory.tsx +++ b/frontend/src/app/setting/memory.tsx @@ -1,5 +1,5 @@ import { useGetMemoryList, useRemoveMemory } from "@/api/setting"; -import { MemoryItemCard } from "./components"; +import { MemoryItemCard } from "./components/memory"; export default function MemoryPage() { const { data: memories = [], isLoading } = useGetMemoryList(); diff --git a/frontend/src/app/setting/models.tsx b/frontend/src/app/setting/models.tsx new file mode 100644 index 000000000..7a4125c12 --- /dev/null +++ b/frontend/src/app/setting/models.tsx @@ -0,0 +1,28 @@ +import { useEffect, useState } from "react"; + +import { useGetModelProviders } from "@/api/setting"; +import { ModelDetail } from "./components/models/model-detail"; +import { ModelProviders } from "./components/models/model-providers"; + +export default function ModelsSettingPage() { + const { data: providers = [] } = useGetModelProviders(); + const [selectedProvider, setSelectedProvider] = useState(""); + + useEffect(() => { + if (providers.length > 0) { + setSelectedProvider(providers[0]?.provider || ""); + } + }, [providers]); + + return ( +
+ setSelectedProvider(provider)} + /> + + {selectedProvider && } +
+ ); +} diff --git a/frontend/src/assets/png/index.ts b/frontend/src/assets/png/index.ts index cad43858f..c309207dc 100644 --- a/frontend/src/assets/png/index.ts +++ b/frontend/src/assets/png/index.ts @@ -26,8 +26,14 @@ export { default as BinancePng } from "./exchanges/binance.png"; export { default as OkxPng } from "./exchanges/okx.png"; export { default as IconGroupPng } from "./icon-group.png"; export { default as MessageGroupPng } from "./message-group.png"; +export { default as AzurePng } from "./model-providers/azure.png"; +export { default as DeepSeekPng } from "./model-providers/deepseek.png"; +export { default as GooglePng } from "./model-providers/google.png"; +export { default as OpenAiPng } from "./model-providers/openai.png"; +export { default as OpenAiCompatiblePng } from "./model-providers/openai-compatible.png"; export { default as OpenRouterPng } from "./model-providers/openrouter.png"; export { default as SiliconFlowPng } from "./model-providers/siliconflow.png"; + export { default as BtcPng } from "./symbols/btc.png"; export { default as DogePng } from "./symbols/doge.png"; export { default as EthPng } from "./symbols/eth.png"; diff --git a/frontend/src/assets/png/model-providers/azure.png b/frontend/src/assets/png/model-providers/azure.png new file mode 100644 index 000000000..87cf32a7c Binary files /dev/null and b/frontend/src/assets/png/model-providers/azure.png differ diff --git a/frontend/src/assets/png/model-providers/deepseek.png b/frontend/src/assets/png/model-providers/deepseek.png new file mode 100644 index 000000000..ba44e0052 Binary files /dev/null and b/frontend/src/assets/png/model-providers/deepseek.png differ diff --git a/frontend/src/assets/png/model-providers/google.png b/frontend/src/assets/png/model-providers/google.png new file mode 100644 index 000000000..125f56926 Binary files /dev/null and b/frontend/src/assets/png/model-providers/google.png differ diff --git a/frontend/src/assets/png/model-providers/openai-compatible.png b/frontend/src/assets/png/model-providers/openai-compatible.png new file mode 100644 index 000000000..a04ad63c1 Binary files /dev/null and b/frontend/src/assets/png/model-providers/openai-compatible.png differ diff --git a/frontend/src/assets/png/model-providers/openai.png b/frontend/src/assets/png/model-providers/openai.png new file mode 100644 index 000000000..a2ba72b90 Binary files /dev/null and b/frontend/src/assets/png/model-providers/openai.png differ diff --git a/frontend/src/assets/png/model-providers/openrouter.png b/frontend/src/assets/png/model-providers/openrouter.png index 7b227034a..aaafc206e 100644 Binary files a/frontend/src/assets/png/model-providers/openrouter.png and b/frontend/src/assets/png/model-providers/openrouter.png differ diff --git a/frontend/src/assets/png/model-providers/siliconflow.png b/frontend/src/assets/png/model-providers/siliconflow.png index 2ee6aeb32..e8ace9bae 100644 Binary files a/frontend/src/assets/png/model-providers/siliconflow.png and b/frontend/src/assets/png/model-providers/siliconflow.png differ diff --git a/frontend/src/components/ui/input-group.tsx b/frontend/src/components/ui/input-group.tsx new file mode 100644 index 000000000..6db1784c2 --- /dev/null +++ b/frontend/src/components/ui/input-group.tsx @@ -0,0 +1,168 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Textarea } from "@/components/ui/textarea" + +function InputGroup({ className, ...props }: React.ComponentProps<"div">) { + return ( +
textarea]:h-auto", + + // Variants based on alignment. + "has-[>[data-align=inline-start]]:[&>input]:pl-2", + "has-[>[data-align=inline-end]]:[&>input]:pr-2", + "has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3", + "has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3", + + // Focus state. + "has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]", + + // Error state. + "has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40", + + className + )} + {...props} + /> + ) +} + +const inputGroupAddonVariants = cva( + "text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50", + { + variants: { + align: { + "inline-start": + "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]", + "inline-end": + "order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]", + "block-start": + "order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5", + "block-end": + "order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5", + }, + }, + defaultVariants: { + align: "inline-start", + }, + } +) + +function InputGroupAddon({ + className, + align = "inline-start", + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
{ + if ((e.target as HTMLElement).closest("button")) { + return + } + e.currentTarget.parentElement?.querySelector("input")?.focus() + }} + {...props} + /> + ) +} + +const inputGroupButtonVariants = cva( + "text-sm shadow-none flex gap-2 items-center", + { + variants: { + size: { + xs: "h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2", + sm: "h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5", + "icon-xs": + "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0", + "icon-sm": "size-8 p-0 has-[>svg]:p-0", + }, + }, + defaultVariants: { + size: "xs", + }, + } +) + +function InputGroupButton({ + className, + type = "button", + variant = "ghost", + size = "xs", + ...props +}: Omit, "size"> & + VariantProps) { + return ( +