From 1ea27f9c716f27d84565380965579f25228584ac Mon Sep 17 00:00:00 2001 From: Szymon0C Date: Thu, 2 Apr 2026 10:26:06 +0200 Subject: [PATCH 01/18] feat: add Rust backend for sidecar, AI streaming, and hardware detection Co-Authored-By: Claude Opus 4.6 --- src-tauri/Cargo.lock | 772 ++++++++++++++-------------- src-tauri/Cargo.toml | 5 + src-tauri/capabilities/default.json | 4 +- src-tauri/src/ai.rs | 108 ++++ src-tauri/src/hardware.rs | 102 ++++ src-tauri/src/lib.rs | 13 + src-tauri/src/sidecar.rs | 146 ++++++ 7 files changed, 766 insertions(+), 384 deletions(-) create mode 100644 src-tauri/src/ai.rs create mode 100644 src-tauri/src/hardware.rs create mode 100644 src-tauri/src/sidecar.rs diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 2091435..f3a2d2e 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -53,137 +53,6 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" -[[package]] -name = "async-broadcast" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" -dependencies = [ - "event-listener", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "pin-project-lite", - "slab", -] - -[[package]] -name = "async-io" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" -dependencies = [ - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite", - "parking", - "polling", - "rustix", - "slab", - "windows-sys 0.61.2", -] - -[[package]] -name = "async-lock" -version = "3.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-process" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" -dependencies = [ - "async-channel", - "async-io", - "async-lock", - "async-signal", - "async-task", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "rustix", -] - -[[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "async-signal" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" -dependencies = [ - "async-io", - "async-lock", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix", - "signal-hook-registry", - "slab", - "windows-sys 0.61.2", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "atk" version = "0.18.2" @@ -294,19 +163,6 @@ dependencies = [ "objc2", ] -[[package]] -name = "blocking" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" -dependencies = [ - "async-channel", - "async-task", - "futures-io", - "futures-lite", - "piper", -] - [[package]] name = "brotli" version = "8.0.2" @@ -518,6 +374,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation" version = "0.10.1" @@ -541,9 +407,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" dependencies = [ "bitflags 2.11.0", - "core-foundation", + "core-foundation 0.10.1", "core-graphics-types", - "foreign-types", + "foreign-types 0.5.0", "libc", ] @@ -554,7 +420,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ "bitflags 2.11.0", - "core-foundation", + "core-foundation 0.10.1", "libc", ] @@ -938,33 +804,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "endi" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" - -[[package]] -name = "enumflags2" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" -dependencies = [ - "enumflags2_derive", - "serde", -] - -[[package]] -name = "enumflags2_derive" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "equivalent" version = "1.0.2" @@ -1014,16 +853,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener", - "pin-project-lite", -] - [[package]] name = "fastrand" version = "2.3.0" @@ -1094,6 +923,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + [[package]] name = "foreign-types" version = "0.5.0" @@ -1101,7 +939,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared", + "foreign-types-shared 0.3.1", ] [[package]] @@ -1115,6 +953,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -1184,19 +1028,6 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - [[package]] name = "futures-macro" version = "0.3.32" @@ -1549,6 +1380,25 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.13.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1593,12 +1443,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - [[package]] name = "hex" version = "0.4.3" @@ -1703,6 +1547,7 @@ dependencies = [ "bytes", "futures-channel", "futures-core", + "h2", "http", "http-body", "httparse", @@ -1713,6 +1558,38 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.20" @@ -1731,9 +1608,11 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -2314,6 +2193,23 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ndk" version = "0.9.0" @@ -2356,6 +2252,15 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "ntapi" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" +dependencies = [ + "winapi", +] + [[package]] name = "num-bigint-dig" version = "0.8.6" @@ -2504,6 +2409,16 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "objc2-io-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +dependencies = [ + "libc", + "objc2-core-foundation", +] + [[package]] name = "objc2-io-surface" version = "0.3.2" @@ -2557,13 +2472,16 @@ dependencies = [ name = "offpage" version = "0.1.0" dependencies = [ + "futures-util", + "reqwest 0.12.28", "serde", "serde_json", + "sysinfo", "tauri", "tauri-build", - "tauri-plugin-opener", "tauri-plugin-shell", "tauri-plugin-sql", + "tokio", ] [[package]] @@ -2585,21 +2503,55 @@ dependencies = [ ] [[package]] -name = "option-ext" -version = "0.2.0" +name = "openssl" +version = "0.10.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" +dependencies = [ + "bitflags 2.11.0", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] [[package]] -name = "ordered-stream" -version = "0.2.0" +name = "openssl-macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "futures-core", - "pin-project-lite", + "proc-macro2", + "quote", + "syn 2.0.117", ] +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "openssl-sys" +version = "0.9.112" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "os_pipe" version = "1.2.3" @@ -2872,22 +2824,11 @@ dependencies = [ "siphasher 1.0.2", ] -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "piper" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" -dependencies = [ - "atomic-waker", - "fastrand", - "futures-io", -] +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pkcs1" @@ -2948,20 +2889,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "polling" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi", - "pin-project-lite", - "rustix", - "windows-sys 0.61.2", -] - [[package]] name = "potential_utf" version = "0.1.5" @@ -3265,6 +3192,49 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams 0.4.2", + "web-sys", +] + [[package]] name = "reqwest" version = "0.13.2" @@ -3295,10 +3265,24 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", + "wasm-streams 0.5.0", "web-sys", ] +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rsa" version = "0.9.10" @@ -3347,6 +3331,39 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustls" +version = "0.23.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -3368,6 +3385,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "schemars" version = "0.8.22" @@ -3425,6 +3451,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "selectors" version = "0.24.0" @@ -4168,6 +4217,41 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "sysinfo" +version = "0.35.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3ffa3e4ff2b324a57f7aeb3c349656c7b127c3c189520251a648102a92496e" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "windows", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.11.0", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "system-deps" version = "6.2.2" @@ -4189,7 +4273,7 @@ checksum = "9103edf55f2da3c82aea4c7fab7c4241032bfeea0e71fa557d98e00e7ce7cc20" dependencies = [ "bitflags 2.11.0", "block2", - "core-foundation", + "core-foundation 0.10.1", "core-graphics", "crossbeam-channel", "dispatch2", @@ -4266,7 +4350,7 @@ dependencies = [ "percent-encoding", "plist", "raw-window-handle", - "reqwest", + "reqwest 0.13.2", "serde", "serde_json", "serde_repr", @@ -4367,28 +4451,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "tauri-plugin-opener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc624469b06f59f5a29f874bbc61a2ed737c0f9c23ef09855a292c389c42e83f" -dependencies = [ - "dunce", - "glob", - "objc2-app-kit", - "objc2-foundation", - "open", - "schemars 0.8.22", - "serde", - "serde_json", - "tauri", - "tauri-plugin", - "thiserror 2.0.18", - "url", - "windows", - "zbus", -] - [[package]] name = "tauri-plugin-shell" version = "2.3.5" @@ -4674,6 +4736,26 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.18" @@ -4920,17 +5002,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" -[[package]] -name = "uds_windows" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" -dependencies = [ - "memoffset", - "tempfile", - "windows-sys 0.61.2", -] - [[package]] name = "unic-char-property" version = "0.9.0" @@ -5011,6 +5082,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.8" @@ -5230,6 +5307,19 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasm-streams" version = "0.5.0" @@ -5516,6 +5606,17 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + [[package]] name = "windows-result" version = "0.3.4" @@ -5570,6 +5671,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -5872,9 +5982,6 @@ name = "winnow" version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" -dependencies = [ - "memchr", -] [[package]] name = "winnow" @@ -6077,67 +6184,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zbus" -version = "5.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca82f95dbd3943a40a53cfded6c2d0a2ca26192011846a1810c4256ef92c60bc" -dependencies = [ - "async-broadcast", - "async-executor", - "async-io", - "async-lock", - "async-process", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "enumflags2", - "event-listener", - "futures-core", - "futures-lite", - "hex", - "libc", - "ordered-stream", - "rustix", - "serde", - "serde_repr", - "tracing", - "uds_windows", - "uuid", - "windows-sys 0.61.2", - "winnow 0.7.15", - "zbus_macros", - "zbus_names", - "zvariant", -] - -[[package]] -name = "zbus_macros" -version = "5.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897e79616e84aac4b2c46e9132a4f63b93105d54fe8c0e8f6bffc21fa8d49222" -dependencies = [ - "proc-macro-crate 3.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", - "zbus_names", - "zvariant", - "zvariant_utils", -] - -[[package]] -name = "zbus_names" -version = "4.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" -dependencies = [ - "serde", - "winnow 0.7.15", - "zvariant", -] - [[package]] name = "zerocopy" version = "0.8.48" @@ -6223,43 +6269,3 @@ name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" - -[[package]] -name = "zvariant" -version = "5.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" -dependencies = [ - "endi", - "enumflags2", - "serde", - "winnow 0.7.15", - "zvariant_derive", - "zvariant_utils", -] - -[[package]] -name = "zvariant_derive" -version = "5.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" -dependencies = [ - "proc-macro-crate 3.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", - "zvariant_utils", -] - -[[package]] -name = "zvariant_utils" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "syn 2.0.117", - "winnow 0.7.15", -] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 3ee597b..8a74be2 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -20,6 +20,11 @@ tauri-build = { version = "2", features = [] } [dependencies] tauri = { version = "2", features = [] } tauri-plugin-sql = { version = "2", features = ["sqlite"] } +tauri-plugin-shell = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" +reqwest = { version = "0.12", features = ["json", "stream"] } +futures-util = "0.3" +sysinfo = "0.35" +tokio = { version = "1", features = ["time"] } diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 181e100..7471bb5 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -7,6 +7,8 @@ "core:default", "sql:default", "sql:allow-execute", - "sql:allow-select" + "sql:allow-select", + "shell:allow-spawn", + "shell:allow-execute" ] } diff --git a/src-tauri/src/ai.rs b/src-tauri/src/ai.rs new file mode 100644 index 0000000..3726e82 --- /dev/null +++ b/src-tauri/src/ai.rs @@ -0,0 +1,108 @@ +use futures_util::StreamExt; +use serde::{Deserialize, Serialize}; +use tauri::{AppHandle, Emitter, Runtime}; + +use crate::sidecar::SidecarState; + +#[derive(Debug, Clone, Serialize)] +pub struct AiChunk { + pub token: String, + pub done: bool, +} + +#[derive(Deserialize)] +struct ChatDelta { + content: Option, +} + +#[derive(Deserialize)] +struct ChatChoice { + delta: ChatDelta, + finish_reason: Option, +} + +#[derive(Deserialize)] +struct ChatChunkResponse { + choices: Vec, +} + +#[tauri::command] +pub async fn stream_generate( + app: AppHandle, + state: tauri::State<'_, SidecarState>, + messages: Vec, +) -> Result { + let port = { + let port_lock = state.port.lock().map_err(|e| e.to_string())?; + *port_lock + }; + + let url = format!("http://127.0.0.1:{}/v1/chat/completions", port); + + let body = serde_json::json!({ + "messages": messages, + "stream": true, + }); + + let client = reqwest::Client::new(); + let response = client + .post(&url) + .json(&body) + .send() + .await + .map_err(|e| format!("Request failed: {}", e))?; + + if !response.status().is_success() { + return Err(format!("Server returned status: {}", response.status())); + } + + let mut stream = response.bytes_stream(); + let mut full_response = String::new(); + let mut buffer = String::new(); + + while let Some(chunk) = stream.next().await { + let chunk = chunk.map_err(|e| format!("Stream error: {}", e))?; + let text = String::from_utf8_lossy(&chunk); + buffer.push_str(&text); + + // Process complete SSE lines + while let Some(newline_pos) = buffer.find('\n') { + let line = buffer[..newline_pos].trim().to_string(); + buffer = buffer[newline_pos + 1..].to_string(); + + if line.is_empty() || !line.starts_with("data: ") { + continue; + } + + let data = &line[6..]; + + if data == "[DONE]" { + let _ = app.emit( + "ai-chunk", + AiChunk { + token: String::new(), + done: true, + }, + ); + return Ok(full_response); + } + + if let Ok(parsed) = serde_json::from_str::(data) { + for choice in &parsed.choices { + if let Some(content) = &choice.delta.content { + full_response.push_str(content); + let _ = app.emit( + "ai-chunk", + AiChunk { + token: content.clone(), + done: choice.finish_reason.is_some(), + }, + ); + } + } + } + } + } + + Ok(full_response) +} diff --git a/src-tauri/src/hardware.rs b/src-tauri/src/hardware.rs new file mode 100644 index 0000000..11f2ff0 --- /dev/null +++ b/src-tauri/src/hardware.rs @@ -0,0 +1,102 @@ +use serde::Serialize; +use sysinfo::System; +use tauri::Runtime; + +#[derive(Debug, Clone, Serialize)] +pub enum GpuType { + Metal, + Nvidia { vram_gb: f64 }, + Cpu, +} + +#[derive(Debug, Clone, Serialize)] +pub enum HardwareTier { + Minimum, + Recommended, + Optimal, +} + +#[derive(Debug, Clone, Serialize)] +pub struct HardwareInfo { + pub total_ram_gb: f64, + pub cpu_cores: usize, + pub gpu_type: GpuType, + pub tier: HardwareTier, + pub recommended_quantization: String, +} + +fn detect_gpu() -> GpuType { + // Check for Apple Silicon via sysctl + #[cfg(target_os = "macos")] + { + if let Ok(output) = std::process::Command::new("sysctl") + .args(["-n", "machdep.cpu.brand_string"]) + .output() + { + let brand = String::from_utf8_lossy(&output.stdout); + if brand.contains("Apple") { + return GpuType::Metal; + } + } + } + + // Check for NVIDIA GPU via nvidia-smi + if let Ok(output) = std::process::Command::new("nvidia-smi") + .args(["--query-gpu=memory.total", "--format=csv,noheader,nounits"]) + .output() + { + if output.status.success() { + let vram_str = String::from_utf8_lossy(&output.stdout); + if let Ok(vram_mb) = vram_str.trim().parse::() { + return GpuType::Nvidia { + vram_gb: vram_mb / 1024.0, + }; + } + } + } + + GpuType::Cpu +} + +fn classify_tier(ram_gb: f64, gpu: &GpuType) -> HardwareTier { + match gpu { + GpuType::Metal if ram_gb >= 32.0 => HardwareTier::Optimal, + GpuType::Nvidia { vram_gb } if ram_gb >= 32.0 && *vram_gb >= 8.0 => HardwareTier::Optimal, + GpuType::Metal if ram_gb >= 16.0 => HardwareTier::Recommended, + GpuType::Nvidia { vram_gb } if *vram_gb >= 6.0 => HardwareTier::Recommended, + _ if ram_gb >= 8.0 => HardwareTier::Minimum, + _ => HardwareTier::Minimum, + } +} + +fn recommended_quantization(tier: &HardwareTier) -> String { + match tier { + HardwareTier::Optimal => "Q8_0".to_string(), + HardwareTier::Recommended => "Q5_K_M".to_string(), + HardwareTier::Minimum => "Q4_K_M".to_string(), + } +} + +pub fn detect_hardware() -> HardwareInfo { + let mut sys = System::new_all(); + sys.refresh_all(); + + let total_ram_gb = sys.total_memory() as f64 / (1024.0 * 1024.0 * 1024.0); + let cpu_cores = sys.cpus().len(); + let gpu_type = detect_gpu(); + let tier = classify_tier(total_ram_gb, &gpu_type); + let quantization = recommended_quantization(&tier); + + HardwareInfo { + total_ram_gb, + cpu_cores, + gpu_type, + tier, + recommended_quantization: quantization, + } +} + +#[tauri::command] +pub fn get_hardware_info(_app: tauri::AppHandle) -> Result { + Ok(detect_hardware()) +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 634a820..278d85b 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,7 +1,20 @@ +mod ai; +mod hardware; +mod sidecar; + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() .plugin(tauri_plugin_sql::Builder::default().build()) + .plugin(tauri_plugin_shell::init()) + .manage(sidecar::SidecarState::default()) + .invoke_handler(tauri::generate_handler![ + sidecar::start_sidecar, + sidecar::stop_sidecar, + sidecar::sidecar_status, + ai::stream_generate, + hardware::get_hardware_info, + ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/src-tauri/src/sidecar.rs b/src-tauri/src/sidecar.rs new file mode 100644 index 0000000..99dee15 --- /dev/null +++ b/src-tauri/src/sidecar.rs @@ -0,0 +1,146 @@ +use serde::Serialize; +use std::sync::Mutex; +use tauri::{AppHandle, Emitter, Runtime}; +use tauri_plugin_shell::process::{CommandChild, CommandEvent}; +use tauri_plugin_shell::ShellExt; + +pub struct SidecarState { + child: Mutex>, + pub port: Mutex, +} + +impl Default for SidecarState { + fn default() -> Self { + Self { + child: Mutex::new(None), + port: Mutex::new(8080), + } + } +} + +#[derive(Debug, Clone, Serialize)] +struct SidecarEvent { + message: String, + source: String, +} + +#[tauri::command] +pub async fn start_sidecar( + app: AppHandle, + state: tauri::State<'_, SidecarState>, + model_path: String, + port: Option, +) -> Result { + // Check if already running + { + let child = state.child.lock().map_err(|e| e.to_string())?; + if child.is_some() { + return Err("Sidecar is already running".to_string()); + } + } + + let port = port.unwrap_or(8080); + + let (mut rx, child) = app + .shell() + .sidecar("llama-server") + .map_err(|e| format!("Failed to create sidecar command: {}", e))? + .args([ + "-m", + &model_path, + "--port", + &port.to_string(), + "--host", + "127.0.0.1", + "-ngl", + "99", + ]) + .spawn() + .map_err(|e| format!("Failed to spawn sidecar: {}", e))?; + + // Store child process and port + { + let mut child_lock = state.child.lock().map_err(|e| e.to_string())?; + *child_lock = Some(child); + } + { + let mut port_lock = state.port.lock().map_err(|e| e.to_string())?; + *port_lock = port; + } + + // Monitor stdout/stderr via Tauri events + let app_clone = app.clone(); + tauri::async_runtime::spawn(async move { + while let Some(event) = rx.recv().await { + match event { + CommandEvent::Stdout(line) => { + let msg = String::from_utf8_lossy(&line).to_string(); + let _ = app_clone.emit( + "sidecar-output", + SidecarEvent { + message: msg, + source: "stdout".to_string(), + }, + ); + } + CommandEvent::Stderr(line) => { + let msg = String::from_utf8_lossy(&line).to_string(); + let _ = app_clone.emit( + "sidecar-output", + SidecarEvent { + message: msg, + source: "stderr".to_string(), + }, + ); + } + CommandEvent::Terminated(status) => { + let _ = app_clone.emit( + "sidecar-output", + SidecarEvent { + message: format!("Process terminated with status: {:?}", status), + source: "system".to_string(), + }, + ); + break; + } + _ => {} + } + } + }); + + // Poll /health endpoint for readiness (30s timeout) + let health_url = format!("http://127.0.0.1:{}/health", port); + let client = reqwest::Client::new(); + let start = std::time::Instant::now(); + let timeout = std::time::Duration::from_secs(30); + + loop { + if start.elapsed() > timeout { + return Err("Sidecar health check timed out after 30 seconds".to_string()); + } + + match client.get(&health_url).send().await { + Ok(resp) if resp.status().is_success() => break, + _ => { + tokio::time::sleep(std::time::Duration::from_millis(500)).await; + } + } + } + + Ok(port) +} + +#[tauri::command] +pub fn stop_sidecar(state: tauri::State<'_, SidecarState>) -> Result<(), String> { + let mut child = state.child.lock().map_err(|e| e.to_string())?; + if let Some(child_process) = child.take() { + child_process.kill().map_err(|e| e.to_string())?; + } + Ok(()) +} + +#[tauri::command] +pub fn sidecar_status(state: tauri::State<'_, SidecarState>) -> Result { + let child = state.child.lock().map_err(|e| e.to_string())?; + Ok(child.is_some()) +} From ef4b558dc2cf3a203aeb04edf78b7ea572fc8967 Mon Sep 17 00:00:00 2001 From: Szymon0C Date: Thu, 2 Apr 2026 10:28:09 +0200 Subject: [PATCH 02/18] feat: add system prompts for generation and editing --- src/lib/prompts.ts | 78 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/lib/prompts.ts diff --git a/src/lib/prompts.ts b/src/lib/prompts.ts new file mode 100644 index 0000000..3571e06 --- /dev/null +++ b/src/lib/prompts.ts @@ -0,0 +1,78 @@ +export const SYSTEM_PROMPTS = { + generate: `You are a web developer AI. Generate a complete, single-file HTML page with inline CSS and JavaScript. + +Rules: +- Output ONLY valid HTML. No markdown, no explanation, no code fences. +- Start with and end with . +- All CSS must be in a