From 3175ea81fef2a5101eb362a4b5c56c055dbdf010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kazimierz=20Cio=C5=82ek?= Date: Tue, 7 Jul 2026 15:25:57 +0200 Subject: [PATCH] add some features --- .gitignore | 1 + package-lock.json | 10 + package.json | 1 + src-tauri/Cargo.lock | 967 ++++++++++++++++++++++++++-- src-tauri/Cargo.toml | 5 + src-tauri/capabilities/default.json | 11 +- src-tauri/src/lib.rs | 52 +- src-tauri/src/llama.rs | 328 ++++++++++ src/lib/ai.ts | 257 ++++++++ src/lib/costs.ts | 28 + src/lib/dates.ts | 17 + src/lib/db.ts | 53 +- src/lib/llama.ts | 37 ++ src/lib/types.ts | 27 + src/routes/+layout.svelte | 5 + src/routes/asystent/+page.svelte | 457 +++++++++++++ src/routes/koszty/+page.svelte | 371 +++++++++++ src/routes/raporty/+page.svelte | 54 +- src/routes/sprzatanie/+page.svelte | 249 ++++--- src/routes/ustawienia/+page.svelte | 85 ++- 20 files changed, 2859 insertions(+), 156 deletions(-) create mode 100644 src-tauri/src/llama.rs create mode 100644 src/lib/ai.ts create mode 100644 src/lib/costs.ts create mode 100644 src/lib/llama.ts create mode 100644 src/routes/asystent/+page.svelte create mode 100644 src/routes/koszty/+page.svelte diff --git a/.gitignore b/.gitignore index 8c9181b..84459eb 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ node_modules vite.config.js.timestamp-* vite.config.ts.timestamp-* /faktury +/trainhub-flutter diff --git a/package-lock.json b/package-lock.json index aa8fdb0..5c31aa1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@tauri-apps/api": "^2", "@tauri-apps/plugin-dialog": "^2.7.1", + "@tauri-apps/plugin-http": "^2.5.9", "@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-sql": "^2.4.0" }, @@ -1229,6 +1230,15 @@ "@tauri-apps/api": "^2.11.0" } }, + "node_modules/@tauri-apps/plugin-http": { + "version": "2.5.9", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-http/-/plugin-http-2.5.9.tgz", + "integrity": "sha512-lCiY0+vs4HvIUSvZrBs8TC3TiCB0MOPRmiUjTq4prW7SlcJE2jdLeT6KBsJrT9Tlplufl7W1pY6SFAO3gCWxDA==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.11.0" + } + }, "node_modules/@tauri-apps/plugin-opener": { "version": "2.5.4", "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.5.4.tgz", diff --git a/package.json b/package.json index 737d8eb..1c6965d 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "dependencies": { "@tauri-apps/api": "^2", "@tauri-apps/plugin-dialog": "^2.7.1", + "@tauri-apps/plugin-http": "^2.5.9", "@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-sql": "^2.4.0" }, diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 02a756c..e6ab588 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -8,6 +8,17 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aes" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fc76eaeac4c9164506c466d4ffdd8ec9d0c5bf57ee97177c4d8eceb3a0e138" +dependencies = [ + "cipher", + "cpubits", + "cpufeatures 0.3.0", +] + [[package]] name = "ahash" version = "0.7.8" @@ -245,6 +256,29 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "aws-lc-rs" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", + "pkg-config", +] + [[package]] name = "base64" version = "0.21.7" @@ -314,6 +348,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", + "zeroize", +] + [[package]] name = "block2" version = "0.6.2" @@ -439,6 +483,15 @@ dependencies = [ "serde", ] +[[package]] +name = "bzip2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" +dependencies = [ + "libbz2-rs-sys", +] + [[package]] name = "cairo-rs" version = "0.18.5" @@ -513,6 +566,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" dependencies = [ "find-msvc-tools", + "jobserver", + "libc", "shlex", ] @@ -555,6 +610,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + [[package]] name = "chrono" version = "0.4.45" @@ -569,6 +635,31 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "cipher" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" +dependencies = [ + "crypto-common 0.2.2", + "inout", +] + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + [[package]] name = "combine" version = "4.6.7" @@ -594,16 +685,57 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + [[package]] name = "cookie" version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" dependencies = [ + "percent-encoding", "time", "version_check", ] +[[package]] +name = "cookie_store" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" +dependencies = [ + "cookie", + "document-features", + "idna", + "log", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[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" @@ -627,7 +759,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" dependencies = [ "bitflags 2.13.0", - "core-foundation", + "core-foundation 0.10.1", "core-graphics-types", "foreign-types", "libc", @@ -640,10 +772,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ "bitflags 2.13.0", - "core-foundation", + "core-foundation 0.10.1", "libc", ] +[[package]] +name = "cpubits" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -653,6 +791,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc" version = "3.4.0" @@ -711,6 +858,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + [[package]] name = "cssparser" version = "0.36.0" @@ -750,6 +906,15 @@ version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", +] + [[package]] name = "darling" version = "0.23.0" @@ -784,6 +949,12 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "data-url" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" + [[package]] name = "dbus" version = "0.9.12" @@ -795,13 +966,19 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "deflate64" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2" + [[package]] name = "der" version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ - "const-oid", + "const-oid 0.9.6", "pem-rfc7468", "zeroize", ] @@ -842,12 +1019,25 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", + "block-buffer 0.10.4", + "const-oid 0.9.6", + "crypto-common 0.1.7", "subtle", ] +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "const-oid 0.10.2", + "crypto-common 0.2.2", + "ctutils", + "zeroize", +] + [[package]] name = "dirs" version = "6.0.0" @@ -915,6 +1105,15 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + [[package]] name = "dom_query" version = "0.27.0" @@ -1016,6 +1215,15 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "endi" version = "1.1.1" @@ -1141,6 +1349,7 @@ checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", + "zlib-rs", ] [[package]] @@ -1208,6 +1417,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "funty" version = "2.0.0" @@ -1426,8 +1641,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -1449,8 +1666,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", + "rand_core 0.10.1", + "wasm-bindgen", ] [[package]] @@ -1601,6 +1821,25 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "h2" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.14.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1666,7 +1905,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ - "hmac", + "hmac 0.12.1", ] [[package]] @@ -1675,7 +1914,16 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", +] + +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.3", ] [[package]] @@ -1736,6 +1984,15 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "hybrid-array" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" +dependencies = [ + "typenum", +] + [[package]] name = "hyper" version = "1.10.1" @@ -1746,6 +2003,7 @@ dependencies = [ "bytes", "futures-channel", "futures-core", + "h2", "http", "http-body", "httparse", @@ -1756,6 +2014,22 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + [[package]] name = "hyper-util" version = "0.1.20" @@ -1774,9 +2048,11 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -1954,6 +2230,15 @@ dependencies = [ "cfb", ] +[[package]] +name = "inout" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" +dependencies = [ + "hybrid-array", +] + [[package]] name = "ipnet" version = "2.12.0" @@ -2024,6 +2309,36 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link 0.2.1", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.118", +] + [[package]] name = "jni-sys" version = "0.3.1" @@ -2052,6 +2367,16 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + [[package]] name = "js-sys" version = "0.3.103" @@ -2129,6 +2454,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "libbz2-rs-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b357333733e8260735ba5894eb928c02ecc69c78715f01a8019e7fa7f2db4c" + [[package]] name = "libc" version = "0.2.186" @@ -2195,6 +2526,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "lock_api" version = "0.4.14" @@ -2210,6 +2547,21 @@ version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "lzma-rust2" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca93e534d1142d1d0dcca6d25fe302508a5dfb40b302802904577725ea0b695b" +dependencies = [ + "sha2 0.11.0", +] + [[package]] name = "markup5ever" version = "0.38.0" @@ -2228,7 +2580,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", - "digest", + "digest 0.10.7", ] [[package]] @@ -2335,7 +2687,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand", + "rand 0.8.6", "smallvec", "zeroize", ] @@ -2611,6 +2963,12 @@ dependencies = [ "libc", ] +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + [[package]] name = "option-ext" version = "0.2.0" @@ -2681,6 +3039,16 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "pbkdf2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112d82ceb8c5bf524d9af484d4e4970c9fd5a0cc15ba14ad93dccd28873b0629" +dependencies = [ + "digest 0.11.3", + "hmac 0.13.0", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -2867,6 +3235,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppmd-rust" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efca4c95a19a79d1c98f791f10aebd5c1363b473244630bb7dbde1dc98455a24" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -2944,6 +3318,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + [[package]] name = "ptr_meta" version = "0.1.4" @@ -2964,6 +3344,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "publicsuffix" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" +dependencies = [ + "idna", + "psl-types", +] + [[package]] name = "quick-xml" version = "0.41.0" @@ -2973,6 +3363,63 @@ dependencies = [ "memchr", ] +[[package]] +name = "quinn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.4.3", + "lru-slab", + "rand 0.10.2", + "rand_pcg", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.61.2", +] + [[package]] name = "quote" version = "1.0.46" @@ -3008,7 +3455,18 @@ checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", ] [[package]] @@ -3018,7 +3476,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -3030,6 +3488,21 @@ dependencies = [ "getrandom 0.2.17", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -3123,6 +3596,49 @@ dependencies = [ "bytecheck", ] +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "cookie", + "cookie_store", + "encoding_rs", + "futures-core", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + [[package]] name = "reqwest" version = "0.13.4" @@ -3131,21 +3647,30 @@ checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" dependencies = [ "base64 0.22.1", "bytes", + "encoding_rs", "futures-core", "futures-util", + "h2", "http", "http-body", "http-body-util", "hyper", + "hyper-rustls", "hyper-util", "js-sys", "log", + "mime", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", "serde", "serde_json", "sync_wrapper", "tokio", + "tokio-rustls", "tokio-util", "tower", "tower-http", @@ -3181,6 +3706,20 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rkyv" version = "0.7.46" @@ -3216,14 +3755,14 @@ version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" dependencies = [ - "const-oid", - "digest", + "const-oid 0.9.6", + "digest 0.10.7", "num-bigint-dig", "num-integer", "num-traits", "pkcs1", "pkcs8", - "rand_core", + "rand_core 0.6.4", "signature", "spki", "subtle", @@ -3240,7 +3779,7 @@ dependencies = [ "borsh", "bytes", "num-traits", - "rand", + "rand 0.8.6", "rkyv", "serde", "serde_json", @@ -3275,6 +3814,82 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustls" +version = "0.23.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" +dependencies = [ + "aws-lc-rs", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni 0.22.4", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -3296,6 +3911,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "schemars" version = "0.8.22" @@ -3359,6 +3983,29 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +[[package]] +name = "security-framework" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +dependencies = [ + "bitflags 2.13.0", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "selectors" version = "0.36.1" @@ -3565,8 +4212,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha1" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -3576,8 +4234,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -3602,8 +4271,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest", - "rand_core", + "digest 0.10.7", + "rand_core 0.6.4", ] [[package]] @@ -3612,6 +4281,16 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + [[package]] name = "simdutf8" version = "0.1.5" @@ -3635,13 +4314,18 @@ name = "sloneczko-menager" version = "0.1.0" dependencies = [ "chrono", + "futures-util", + "reqwest 0.13.4", "serde", "serde_json", "tauri", "tauri-build", "tauri-plugin-dialog", + "tauri-plugin-http", "tauri-plugin-opener", "tauri-plugin-sql", + "tokio", + "zip", ] [[package]] @@ -3769,7 +4453,7 @@ dependencies = [ "rust_decimal", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "smallvec", "thiserror 2.0.18", "time", @@ -3808,7 +4492,7 @@ dependencies = [ "quote", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "sqlx-core", "sqlx-mysql", "sqlx-postgres", @@ -3830,7 +4514,7 @@ dependencies = [ "byteorder", "bytes", "crc", - "digest", + "digest 0.10.7", "dotenvy", "either", "futures-channel", @@ -3840,19 +4524,19 @@ dependencies = [ "generic-array", "hex", "hkdf", - "hmac", + "hmac 0.12.1", "itoa", "log", "md-5", "memchr", "once_cell", "percent-encoding", - "rand", + "rand 0.8.6", "rsa", "rust_decimal", "serde", - "sha1", - "sha2", + "sha1 0.10.6", + "sha2 0.10.9", "smallvec", "sqlx-core", "stringprep", @@ -3881,18 +4565,18 @@ dependencies = [ "futures-util", "hex", "hkdf", - "hmac", + "hmac 0.12.1", "home", "itoa", "log", "md-5", "memchr", "once_cell", - "rand", + "rand 0.8.6", "rust_decimal", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "smallvec", "sqlx-core", "stringprep", @@ -4035,6 +4719,27 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.13.0", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[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" @@ -4056,7 +4761,7 @@ checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9" dependencies = [ "bitflags 2.13.0", "block2", - "core-foundation", + "core-foundation 0.10.1", "core-graphics", "crossbeam-channel", "dbus", @@ -4066,7 +4771,7 @@ dependencies = [ "gdkwayland-sys", "gdkx11-sys", "gtk", - "jni", + "jni 0.21.1", "libc", "log", "ndk", @@ -4128,7 +4833,7 @@ dependencies = [ "gtk", "heck 0.5.0", "http", - "jni", + "jni 0.21.1", "libc", "log", "mime", @@ -4141,7 +4846,7 @@ dependencies = [ "percent-encoding", "plist", "raw-window-handle", - "reqwest", + "reqwest 0.13.4", "serde", "serde_json", "serde_repr", @@ -4200,7 +4905,7 @@ dependencies = [ "semver", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "syn 2.0.118", "tauri-utils", "thiserror 2.0.18", @@ -4282,6 +4987,30 @@ dependencies = [ "url", ] +[[package]] +name = "tauri-plugin-http" +version = "2.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5bd512048e1985b7ec78f96d99083e2ddaf7e0d906b2b63c44ce5bb8b894067" +dependencies = [ + "bytes", + "cookie_store", + "data-url", + "http", + "regex", + "reqwest 0.12.28", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.18", + "tokio", + "url", + "urlpattern", +] + [[package]] name = "tauri-plugin-opener" version = "2.5.4" @@ -4335,7 +5064,7 @@ dependencies = [ "dpi", "gtk", "http", - "jni", + "jni 0.21.1", "objc2", "objc2-ui-kit", "objc2-web-kit", @@ -4358,7 +5087,7 @@ checksum = "4e6fac707727b7a2f48e4ded90976324267371073edbb415ffb73bb0458d203f" dependencies = [ "gtk", "http", - "jni", + "jni 0.21.1", "log", "objc2", "objc2-app-kit", @@ -4495,6 +5224,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" dependencies = [ "deranged", + "js-sys", "num-conv", "powerfmt", "serde_core", @@ -4554,9 +5284,31 @@ dependencies = [ "mio", "pin-project-lite", "socket2", + "tokio-macros", "windows-sys 0.61.2", ] +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[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" @@ -4806,6 +5558,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typed-path" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" + [[package]] name = "typeid" version = "1.0.3" @@ -4903,6 +5661,12 @@ version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" +[[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" @@ -5109,6 +5873,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "web_atoms" version = "0.2.5" @@ -5165,6 +5939,24 @@ dependencies = [ "system-deps", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webview2-com" version = "0.38.2" @@ -5360,6 +6152,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" @@ -5414,6 +6217,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" @@ -5766,7 +6578,7 @@ dependencies = [ "gtk", "http", "javascriptcore-rs", - "jni", + "jni 0.21.1", "libc", "ndk", "objc2", @@ -5778,7 +6590,7 @@ dependencies = [ "once_cell", "percent-encoding", "raw-window-handle", - "sha2", + "sha2 0.10.9", "soup3", "tao-macros", "thiserror 2.0.18", @@ -5986,12 +6798,85 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "zip" +version = "8.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b" +dependencies = [ + "aes", + "bzip2", + "constant_time_eq", + "crc32fast", + "deflate64", + "flate2", + "getrandom 0.4.3", + "hmac 0.13.0", + "indexmap 2.14.0", + "lzma-rust2", + "memchr", + "pbkdf2", + "ppmd-rust", + "sha1 0.11.0", + "time", + "typed-path", + "zeroize", + "zopfli", + "zstd", +] + +[[package]] +name = "zlib-rs" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5431d5661c32445236631278f27946e444ddafe4684cac70b185272d4f9c52d5" + [[package]] name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "zvariant" version = "5.12.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 8c3c098..9756064 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -25,4 +25,9 @@ serde_json = "1" tauri-plugin-sql = { version = "2.4.0", features = ["sqlite"] } tauri-plugin-dialog = "2.7.1" chrono = "0.4.45" +tauri-plugin-http = "2.5.9" +reqwest = { version = "0.13.4", features = ["stream"] } +zip = "8.6.0" +futures-util = "0.3.32" +tokio = { version = "1.52.3", features = ["time"] } diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 268eec3..773e13b 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -8,6 +8,15 @@ "opener:default", "sql:default", "sql:allow-execute", - "dialog:default" + "dialog:default", + { + "identifier": "http:default", + "allow": [ + { "url": "https://api.anthropic.com/*" }, + { "url": "https://api.openai.com/*" }, + { "url": "http://localhost:*/*" }, + { "url": "http://127.0.0.1:*/*" } + ] + } ] } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 61a4d5d..5aeea5a 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,3 +1,8 @@ +mod llama; + +use std::sync::Mutex; + +use tauri::Manager; use tauri_plugin_sql::{Migration, MigrationKind}; fn migrations() -> Vec { @@ -148,14 +153,35 @@ fn migrations() -> Vec { UPDATE reservations SET prepared = 1 WHERE arrival < date('now'); ", kind: MigrationKind::Up, + }, + Migration { + version: 6, + description: "add_season_costs_ai", + sql: " + ALTER TABLE settings ADD COLUMN season_start_month INTEGER NOT NULL DEFAULT 5; + ALTER TABLE settings ADD COLUMN season_end_month INTEGER NOT NULL DEFAULT 9; + ALTER TABLE settings ADD COLUMN linen_cost REAL; + ALTER TABLE settings ADD COLUMN ai_provider TEXT NOT NULL DEFAULT 'anthropic'; + ALTER TABLE settings ADD COLUMN ai_api_key TEXT; + ALTER TABLE settings ADD COLUMN ai_model TEXT; + ALTER TABLE settings ADD COLUMN ai_base_url TEXT; + + CREATE TABLE costs ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + amount REAL NOT NULL, + period TEXT NOT NULL DEFAULT 'year', + year INTEGER, + notes TEXT + ); + ", + kind: MigrationKind::Up, }] } /// Kopia bazy przy starcie (zanim plugin SQL otworzy połączenia): /// backups/sloneczko-RRRR-MM-DD.db obok pliku bazy, retencja 14 ostatnich. fn auto_backup(app: &tauri::AppHandle) -> std::io::Result<()> { - use tauri::Manager; - let candidates = [app.path().app_config_dir(), app.path().app_data_dir()]; for dir in candidates.into_iter().flatten() { let db = dir.join("sloneczko.db"); @@ -195,7 +221,14 @@ fn save_text_file(path: String, contents: String) -> Result<(), String> { #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() - .invoke_handler(tauri::generate_handler![save_text_file]) + .manage(llama::LlamaState(Mutex::new(None))) + .invoke_handler(tauri::generate_handler![ + save_text_file, + llama::llama_status, + llama::llama_download, + llama::llama_start, + llama::llama_stop + ]) .setup(|app| { if let Err(e) = auto_backup(app.handle()) { eprintln!("Nie udało się wykonać kopii zapasowej: {e}"); @@ -208,7 +241,16 @@ pub fn run() { .build(), ) .plugin(tauri_plugin_dialog::init()) + .plugin(tauri_plugin_http::init()) .plugin(tauri_plugin_opener::init()) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); + .build(tauri::generate_context!()) + .expect("error while running tauri application") + .run(|app, event| { + // llama-server nie może przeżyć aplikacji — zombie zjadałby RAM + if let tauri::RunEvent::Exit = event { + if let Some(state) = app.try_state::() { + llama::kill(&state); + } + } + }); } diff --git a/src-tauri/src/llama.rs b/src-tauri/src/llama.rs new file mode 100644 index 0000000..7f18368 --- /dev/null +++ b/src-tauri/src/llama.rs @@ -0,0 +1,328 @@ +//! Wbudowany serwer AI: zarządzanie procesem llama.cpp (llama-server) +//! na wzór AiProcessManager z trainhub-flutter — pobieranie binarki i modelu, +//! start z fallbackiem GPU -> CPU, health-poll, ubijanie przy zamknięciu. + +use std::fs; +use std::io::Write; +use std::path::{Path, PathBuf}; +use std::process::{Child, Command, Stdio}; +use std::sync::Mutex; +use std::time::{Duration, Instant}; + +use futures_util::StreamExt; +use serde::Serialize; +use tauri::{AppHandle, Emitter, Manager, State}; + +pub const PORT: u16 = 8123; +const LLAMA_BUILD: &str = "b8130"; +const SERVER_BINARY: &str = "llama-server.exe"; +const MODEL_FILE: &str = "qwen3-4b-instruct-2507-q4_k_m.gguf"; +const MODEL_URL: &str = "https://huggingface.co/bartowski/Qwen_Qwen3-4B-Instruct-2507-GGUF/resolve/main/Qwen_Qwen3-4B-Instruct-2507-Q4_K_M.gguf"; +// Plik mniejszy niż to minimum to pozostałość przerwanego pobierania. +const MODEL_MIN_BYTES: u64 = 2_000_000_000; +const BINARY_MIN_BYTES: u64 = 1_000_000; +const CONTEXT_SIZE: u32 = 8192; +const GPU_LAYERS: u32 = 99; +const GPU_TIMEOUT_SECS: u64 = 300; +const CPU_TIMEOUT_SECS: u64 = 180; + +pub struct LlamaState(pub Mutex>); + +#[derive(Serialize, Clone)] +pub struct LlamaStatus { + pub installed: bool, + pub running: bool, +} + +#[derive(Serialize, Clone)] +struct LlamaProgress { + task: String, + progress: Option, +} + +fn emit_progress(app: &AppHandle, task: &str, progress: Option) { + let _ = app.emit( + "llama-progress", + LlamaProgress { + task: task.to_string(), + progress, + }, + ); +} + +fn llama_dir(app: &AppHandle) -> Result { + let dir = app + .path() + .app_data_dir() + .map_err(|e| e.to_string())? + .join("llama"); + fs::create_dir_all(&dir).map_err(|e| e.to_string())?; + Ok(dir) +} + +fn file_complete(path: &Path, min_bytes: u64) -> bool { + fs::metadata(path).map(|m| m.len() >= min_bytes).unwrap_or(false) +} + +fn is_installed(dir: &Path) -> bool { + file_complete(&dir.join(SERVER_BINARY), BINARY_MIN_BYTES) + && file_complete(&dir.join(MODEL_FILE), MODEL_MIN_BYTES) +} + +async fn is_healthy() -> bool { + let url = format!("http://127.0.0.1:{PORT}/health"); + match reqwest::Client::new() + .get(&url) + .timeout(Duration::from_secs(2)) + .send() + .await + { + Ok(res) => res.status().is_success(), + Err(_) => false, + } +} + +/// Ubija proces serwera, jeśli działa. Bezpieczne przy braku procesu. +pub fn kill(state: &LlamaState) { + if let Ok(mut guard) = state.0.lock() { + if let Some(mut child) = guard.take() { + let _ = child.kill(); + let _ = child.wait(); + } + } +} + +/// Czy proces trzymany w stanie nadal żyje (sprząta uchwyt po padzie). +fn child_alive(state: &LlamaState) -> bool { + let mut guard = match state.0.lock() { + Ok(g) => g, + Err(_) => return false, + }; + match guard.as_mut() { + Some(child) => match child.try_wait() { + Ok(None) => true, + _ => { + *guard = None; + false + } + }, + None => false, + } +} + +#[tauri::command] +pub async fn llama_status( + app: AppHandle, + state: State<'_, LlamaState>, +) -> Result { + let dir = llama_dir(&app)?; + let installed = is_installed(&dir); + let running = child_alive(&state) && is_healthy().await; + Ok(LlamaStatus { installed, running }) +} + +async fn download_file( + app: &AppHandle, + url: &str, + dest: &Path, + label: &str, + overall_start: f64, + overall_end: f64, + min_bytes: u64, +) -> Result<(), String> { + if min_bytes > 0 && file_complete(dest, min_bytes) { + emit_progress(app, label, Some(overall_end)); + return Ok(()); + } + emit_progress(app, label, Some(overall_start)); + + // Pobieranie do pliku .part i podmiana na końcu — przerwane pobieranie + // nigdy nie udaje kompletnego pliku. + let part = dest.with_extension("part"); + let response = reqwest::Client::new() + .get(url) + .send() + .await + .map_err(|e| format!("Błąd sieci: {e}"))?; + if !response.status().is_success() { + return Err(format!("Błąd pobierania (HTTP {}): {url}", response.status())); + } + let total = response.content_length().unwrap_or(0); + let mut file = fs::File::create(&part).map_err(|e| e.to_string())?; + let mut stream = response.bytes_stream(); + let mut received: u64 = 0; + let mut last_permille: u64 = 0; + while let Some(chunk) = stream.next().await { + let chunk = chunk.map_err(|e| format!("Błąd sieci: {e}"))?; + file.write_all(&chunk).map_err(|e| e.to_string())?; + received += chunk.len() as u64; + if total > 0 { + let permille = received * 1000 / total; + if permille != last_permille { + last_permille = permille; + let progress = + overall_start + (received as f64 / total as f64) * (overall_end - overall_start); + emit_progress(app, label, Some(progress)); + } + } + } + drop(file); + if dest.exists() { + let _ = fs::remove_file(dest); + } + fs::rename(&part, dest).map_err(|e| e.to_string())?; + Ok(()) +} + +/// Wyciąga llama-server.exe i biblioteki .dll z archiwum wydania llama.cpp. +fn extract_binary(archive: &Path, dest: &Path) -> Result<(), String> { + let file = fs::File::open(archive).map_err(|e| e.to_string())?; + let mut zip = zip::ZipArchive::new(file).map_err(|e| e.to_string())?; + let mut found_server = false; + for i in 0..zip.len() { + let mut entry = zip.by_index(i).map_err(|e| e.to_string())?; + if entry.is_dir() { + continue; + } + let name = entry.name().to_string(); + let base = name + .rsplit(['/', '\\']) + .next() + .unwrap_or(&name) + .to_string(); + let lower = base.to_lowercase(); + if lower == SERVER_BINARY || lower.ends_with(".dll") { + let out_path = dest.join(&base); + let mut out = fs::File::create(&out_path).map_err(|e| e.to_string())?; + std::io::copy(&mut entry, &mut out).map_err(|e| e.to_string())?; + if lower == SERVER_BINARY { + found_server = true; + } + } + } + if found_server { + Ok(()) + } else { + Err("Nie znaleziono llama-server.exe w archiwum.".to_string()) + } +} + +#[tauri::command] +pub async fn llama_download(app: AppHandle, state: State<'_, LlamaState>) -> Result<(), String> { + kill(&state); + let dir = llama_dir(&app)?; + + if !file_complete(&dir.join(SERVER_BINARY), BINARY_MIN_BYTES) { + let zip_url = format!( + "https://github.com/ggml-org/llama.cpp/releases/download/{LLAMA_BUILD}/llama-{LLAMA_BUILD}-bin-win-vulkan-x64.zip" + ); + let archive = dir.join("llama.zip"); + download_file(&app, &zip_url, &archive, "Pobieranie llama.cpp…", 0.0, 0.06, 0).await?; + emit_progress(&app, "Rozpakowywanie llama.cpp…", Some(0.06)); + let archive_clone = archive.clone(); + let dir_clone = dir.clone(); + tauri::async_runtime::spawn_blocking(move || extract_binary(&archive_clone, &dir_clone)) + .await + .map_err(|e| e.to_string())??; + let _ = fs::remove_file(&archive); + } + + download_file( + &app, + MODEL_URL, + &dir.join(MODEL_FILE), + "Pobieranie modelu Qwen3 4B (~2,4 GB)…", + 0.06, + 1.0, + MODEL_MIN_BYTES, + ) + .await?; + emit_progress(&app, "Pobieranie zakończone.", Some(1.0)); + Ok(()) +} + +fn spawn_server(dir: &Path, gpu_layers: u32) -> Result { + let log = fs::File::create(dir.join("llama.log")).map_err(|e| e.to_string())?; + let mut cmd = Command::new(dir.join(SERVER_BINARY)); + cmd.args([ + "-m", + &dir.join(MODEL_FILE).to_string_lossy(), + "--port", + &PORT.to_string(), + "--ctx-size", + &CONTEXT_SIZE.to_string(), + "-ngl", + &gpu_layers.to_string(), + // szablon czatu wbudowany w GGUF — wymagany dla poprawnego formatu Qwen3 + "--jinja", + ]) + .current_dir(dir) + .stdout(Stdio::null()) + .stderr(Stdio::from(log)); + + // bez migającego okna konsoli na Windowsie + #[cfg(windows)] + { + use std::os::windows::process::CommandExt; + const CREATE_NO_WINDOW: u32 = 0x0800_0000; + cmd.creation_flags(CREATE_NO_WINDOW); + } + + cmd.spawn().map_err(|e| format!("Nie udało się uruchomić llama-server: {e}")) +} + +#[tauri::command] +pub async fn llama_start(app: AppHandle, state: State<'_, LlamaState>) -> Result<(), String> { + if child_alive(&state) && is_healthy().await { + return Ok(()); + } + let dir = llama_dir(&app)?; + if !is_installed(&dir) { + return Err("Model nie jest zainstalowany — najpierw pobierz pliki.".to_string()); + } + + // Próba 1: pełny offload na GPU (Vulkan). Pierwsze uruchomienie kompiluje + // pipeline'y i może trwać minuty. Próba 2: sam CPU — ładuje przez mmap. + for (gpu_layers, timeout_secs, label) in + [(GPU_LAYERS, GPU_TIMEOUT_SECS, "GPU"), (0, CPU_TIMEOUT_SECS, "CPU")] + { + kill(&state); + let child = spawn_server(&dir, gpu_layers)?; + if let Ok(mut guard) = state.0.lock() { + *guard = Some(child); + } + + let started = Instant::now(); + while started.elapsed() < Duration::from_secs(timeout_secs) { + if !child_alive(&state) { + // proces padł — od razu przechodzimy do kolejnej próby + break; + } + if is_healthy().await { + emit_progress(&app, "Model gotowy.", None); + return Ok(()); + } + emit_progress( + &app, + &format!( + "Ładowanie modelu ({label})… {}s — pierwsze uruchomienie może potrwać kilka minut", + started.elapsed().as_secs() + ), + None, + ); + tokio::time::sleep(Duration::from_millis(750)).await; + } + } + + kill(&state); + let log_hint = dir.join("llama.log").to_string_lossy().to_string(); + Err(format!( + "Serwer AI nie wystartował (próbowano GPU i CPU). Szczegóły w pliku: {log_hint}" + )) +} + +#[tauri::command] +pub async fn llama_stop(state: State<'_, LlamaState>) -> Result<(), String> { + kill(&state); + Ok(()) +} \ No newline at end of file diff --git a/src/lib/ai.ts b/src/lib/ai.ts new file mode 100644 index 0000000..4091fce --- /dev/null +++ b/src/lib/ai.ts @@ -0,0 +1,257 @@ +import { fetch } from "@tauri-apps/plugin-http"; +import { LLAMA_PORT } from "./llama"; +import * as db from "./db"; +import type { AiProvider, Settings } from "./types"; +import { daysInSeason, isoDate, MONTH_NAMES, seasonMonths, todayISO } from "./dates"; +import { formatMoney, round2 } from "./money"; +import { COST_PERIOD_LABELS, costPerYear, totalCostsForYear } from "./costs"; + +export interface ChatMessage { + role: "user" | "assistant"; + content: string; +} + +export const AI_PROVIDER_LABELS: Record = { + local: "Wbudowany (llama.cpp, offline)", + anthropic: "Anthropic (Claude)", + openai: "OpenAI", + ollama: "Ollama (lokalnie)", + lmstudio: "LM Studio (lokalnie)" +}; + +export const DEFAULT_MODELS: Record = { + local: "Qwen3 4B Instruct", + anthropic: "claude-sonnet-5", + openai: "gpt-4o-mini", + ollama: "llama3.1", + lmstudio: "local-model" +}; + +export const DEFAULT_BASE_URLS: Partial> = { + ollama: "http://localhost:11434", + lmstudio: "http://localhost:1234" +}; + +export function providerNeedsKey(p: AiProvider): boolean { + return p === "anthropic" || p === "openai"; +} + +export function isAiConfigured(s: Settings): boolean { + return !providerNeedsKey(s.ai_provider) || !!s.ai_api_key?.trim(); +} + +const SYSTEM_PROMPT = `Jesteś asystentem menedżera małego ośrodka domków letniskowych „Słoneczko" w Polsce. +Pomagasz w: planowaniu obłożenia i cen, znajdowaniu wolnych luk w sezonie, analizie przychodów i kosztów, +oraz podpowiadaniu, jak zwiększyć zarobki możliwie małym nakładem pracy i pieniędzy. +Zasady: +- Odpowiadaj po polsku, zwięźle i konkretnie; opieraj się na danych z sekcji DANE poniżej i podawaj liczby. +- Gdy proponujesz zmiany cen lub promocje, szacuj ich wpływ na przychód. +- Pisz zwykłym tekstem bez formatowania markdown (bez gwiazdek, krzyżyków i tabel); listy zapisuj od myślników. +- Jeśli danych brakuje, powiedz czego brakuje, zamiast zgadywać.`; + +/** Zwięzły obraz biznesu z bazy — doklejany do promptu systemowego przy każdej rozmowie. */ +export async function buildBusinessContext(): Promise { + const year = new Date().getFullYear(); + const from = isoDate(year, 0, 1); + const to = isoDate(year + 1, 0, 1); + const monthRanges = Array.from({ length: 12 }, (_, m) => ({ + from: isoDate(year, m, 1), + to: m === 11 ? isoDate(year + 1, 0, 1) : isoDate(year, m + 1, 1) + })); + + const [settings, cabins, priceSeasons, costs, yearStats, cabinStats, linenSets, ...months] = + await Promise.all([ + db.getSettings(), + db.listCabins(), + db.listPriceSeasons(), + db.listCosts(), + db.revenueStats(from, to), + db.cabinReports(from, to), + db.linenSetsForYear(year), + ...monthRanges.map((r) => db.revenueStats(r.from, r.to)) + ]); + + const sMonths = seasonMonths(settings.season_start_month, settings.season_end_month); + const seasonDays = daysInSeason(year, settings.season_start_month, settings.season_end_month); + const availableNights = cabins.length * seasonDays; + + const lines: string[] = []; + lines.push(`DANE OŚRODKA (stan na ${todayISO()}, rok ${year}):`); + lines.push( + `Sezon: ${MONTH_NAMES[settings.season_start_month - 1]}–${MONTH_NAMES[settings.season_end_month - 1]}` + + ` (${seasonDays} dni × ${cabins.length} domków = ${availableNights} dostępnych dób).` + ); + lines.push( + `Domki: ${cabins.map((c) => `${c.name}${c.capacity ? ` (${c.capacity} os.)` : ""}`).join(", ")}.` + ); + + if (priceSeasons.length > 0) { + lines.push("Cennik sezonowy (za dobę):"); + for (const p of priceSeasons) { + const cabinName = p.cabin_id == null + ? "wszystkie domki" + : (cabins.find((c) => c.id === p.cabin_id)?.name ?? `domek #${p.cabin_id}`); + lines.push(`- ${p.date_from} do ${p.date_to}: ${formatMoney(p.price)} zł (${cabinName})`); + } + } else { + lines.push("Cennik sezonowy: brak zdefiniowanych okresów."); + } + + lines.push(`Obłożenie i przychód ${year} wg miesięcy (doby / % obłożenia / przychód, w nawiasie potwierdzony):`); + months.forEach((m, i) => { + if (m.nights_total <= 0 && !sMonths.includes(i + 1)) return; + const daysM = new Date(year, i + 1, 0).getDate(); + const occ = cabins.length > 0 ? Math.round((m.nights_total / (cabins.length * daysM)) * 100) : 0; + const seasonTag = sMonths.includes(i + 1) ? "" : " [poza sezonem]"; + lines.push( + `- ${MONTH_NAMES[i]}: ${Math.round(m.nights_total)} dób, ${occ}%, ${formatMoney(m.revenue_total)} zł (${formatMoney(m.revenue_confirmed)} zł)${seasonTag}` + ); + }); + + lines.push("Wg domków (doby / przychód):"); + for (const c of cabins) { + const r = cabinStats.find((x) => x.cabin_id === c.id); + lines.push( + `- ${c.name}: ${Math.round(r?.nights_total ?? 0)} dób, ${formatMoney(r?.revenue_total ?? 0)} zł` + ); + } + + const costsTotal = totalCostsForYear(costs, year, sMonths.length); + const linenCost = round2(linenSets * (settings.linen_cost ?? 0)); + lines.push(`Koszty roczne (${year}):`); + for (const c of costs) { + const yearly = costPerYear(c, year, sMonths.length); + if (yearly > 0) { + lines.push(`- ${c.name}: ${formatMoney(yearly)} zł (${COST_PERIOD_LABELS[c.period]} ${formatMoney(c.amount)} zł)`); + } + } + if (settings.linen_cost != null) { + lines.push(`- Pościel: ${linenSets} kompletów × ${formatMoney(settings.linen_cost)} zł = ${formatMoney(linenCost)} zł`); + } + const totalAllCosts = round2(costsTotal + linenCost); + lines.push(`Koszty razem: ${formatMoney(totalAllCosts)} zł.`); + lines.push( + `Wynik ${year}: przychód potencjalny ${formatMoney(yearStats.revenue_total)} zł, potwierdzony ${formatMoney(yearStats.revenue_confirmed)} zł; ` + + `przewidywany zysk (potwierdzony − koszty): ${formatMoney(round2(yearStats.revenue_confirmed - totalAllCosts))} zł.` + ); + + return lines.join("\n"); +} + +// --- Streaming --- + +function handleSseLine(line: string, onData: (data: unknown) => void): void { + if (!line.startsWith("data: ")) return; + const dataStr = line.slice(6).trim(); + if (!dataStr || dataStr === "[DONE]") return; + try { + onData(JSON.parse(dataStr)); + } catch { + // uszkodzona linia — pomijamy + } +} + +/** Czyta SSE strumieniowo; gdy strumień niedostępny, parsuje całą odpowiedź na raz. */ +async function readSse(res: Response, onData: (data: unknown) => void): Promise { + const reader = res.body?.getReader(); + if (!reader) { + for (const line of (await res.text()).split("\n")) handleSseLine(line, onData); + return; + } + const decoder = new TextDecoder(); + let pending = ""; + for (;;) { + const { done, value } = await reader.read(); + if (done) break; + pending += decoder.decode(value, { stream: true }); + const lines = pending.split("\n"); + pending = lines.pop() ?? ""; + for (const line of lines) handleSseLine(line, onData); + } + if (pending) handleSseLine(pending, onData); +} + +interface AnthropicEvent { + type?: string; + delta?: { type?: string; text?: string }; + error?: { message?: string }; +} + +interface OpenAiChunk { + choices?: { delta?: { content?: string } }[]; +} + +/** + * Wysyła rozmowę do skonfigurowanego dostawcy i przekazuje fragmenty odpowiedzi do onDelta. + * Kontekst biznesowy jest budowany z bazy przy każdym wywołaniu. + */ +export async function chat( + history: ChatMessage[], + settings: Settings, + onDelta: (text: string) => void, + signal?: AbortSignal +): Promise { + const system = `${SYSTEM_PROMPT}\n\n${await buildBusinessContext()}`; + const model = settings.ai_model?.trim() || DEFAULT_MODELS[settings.ai_provider]; + const key = settings.ai_api_key?.trim() || null; + + if (settings.ai_provider === "anthropic") { + if (!key) throw new Error("Brak klucza API Anthropic — uzupełnij w Ustawieniach."); + const res = await fetch("https://api.anthropic.com/v1/messages", { + method: "POST", + signal, + headers: { + "content-type": "application/json", + "x-api-key": key, + "anthropic-version": "2023-06-01" + }, + body: JSON.stringify({ model, max_tokens: 2048, system, messages: history, stream: true }) + }); + if (!res.ok) { + throw new Error(`Błąd API (${res.status}): ${(await res.text()).slice(0, 300)}`); + } + await readSse(res, (data) => { + const ev = data as AnthropicEvent; + if (ev.type === "content_block_delta" && ev.delta?.type === "text_delta") { + onDelta(ev.delta.text ?? ""); + } else if (ev.type === "error") { + throw new Error(ev.error?.message ?? "nieznany błąd API"); + } + }); + return; + } + + if (settings.ai_provider === "openai" && !key) { + throw new Error("Brak klucza API OpenAI — uzupełnij w Ustawieniach."); + } + const isLocal = settings.ai_provider === "local"; + const base = settings.ai_base_url?.trim() || DEFAULT_BASE_URLS[settings.ai_provider] || ""; + const url = isLocal + ? `http://127.0.0.1:${LLAMA_PORT}/v1/chat/completions` + : settings.ai_provider === "openai" + ? "https://api.openai.com/v1/chat/completions" + : `${base}/v1/chat/completions`; + const res = await fetch(url, { + method: "POST", + signal, + headers: { + "content-type": "application/json", + ...(key && !isLocal ? { authorization: `Bearer ${key}` } : {}) + }, + body: JSON.stringify({ + ...(isLocal ? {} : { model }), + messages: [{ role: "system", content: system }, ...history], + // parametry jak w trainhub: llama.cpp reuse'uje KV-cache wspólnego + // prefiksu między turami — wielotury odpowiadają dużo szybciej + ...(isLocal ? { temperature: 0.7, max_tokens: 1536, cache_prompt: true } : {}), + stream: true + }) + }); + if (!res.ok) { + throw new Error(`Błąd API (${res.status}): ${(await res.text()).slice(0, 300)}`); + } + await readSse(res, (data) => { + const delta = (data as OpenAiChunk).choices?.[0]?.delta?.content; + if (delta) onDelta(delta); + }); +} \ No newline at end of file diff --git a/src/lib/costs.ts b/src/lib/costs.ts new file mode 100644 index 0000000..542defc --- /dev/null +++ b/src/lib/costs.ts @@ -0,0 +1,28 @@ +import type { Cost, CostPeriod } from "./types"; +import { round2 } from "./money"; + +export const COST_PERIOD_LABELS: Record = { + year: "rocznie", + month: "miesięcznie", + season_month: "miesięcznie w sezonie", + once: "jednorazowo" +}; + +/** Roczny wymiar pojedynczego kosztu dla danego roku. */ +export function costPerYear(cost: Cost, year: number, seasonMonthCount: number): number { + switch (cost.period) { + case "year": + return cost.amount; + case "month": + return cost.amount * 12; + case "season_month": + return cost.amount * seasonMonthCount; + case "once": + return cost.year === year ? cost.amount : 0; + } +} + +/** Suma kosztów stałych w roku (bez pościeli). */ +export function totalCostsForYear(costs: Cost[], year: number, seasonMonthCount: number): number { + return round2(costs.reduce((sum, c) => sum + costPerYear(c, year, seasonMonthCount), 0)); +} \ No newline at end of file diff --git a/src/lib/dates.ts b/src/lib/dates.ts index 37000a9..0236b23 100644 --- a/src/lib/dates.ts +++ b/src/lib/dates.ts @@ -58,6 +58,23 @@ export function formatShort(iso: string): string { return `${Number(iso.slice(8))}.${iso.slice(5, 7)}`; } +/** Miesiące sezonu (1–12) od start do end włącznie; obsługuje sezon przez przełom roku. */ +export function seasonMonths(start: number, end: number): number[] { + const months: number[] = []; + let m = start; + for (let i = 0; i < 12; i++) { + months.push(m); + if (m === end) break; + m = m === 12 ? 1 : m + 1; + } + return months; +} + +/** Liczba dni sezonu w danym roku. */ +export function daysInSeason(year: number, start: number, end: number): number { + return seasonMonths(start, end).reduce((sum, m) => sum + daysInMonth(year, m - 1), 0); +} + /** Liczba nocy między dwiema datami ISO (departure - arrival). */ export function nightsBetween(arrival: string, departure: string): number { const a = new Date(arrival + "T00:00:00"); diff --git a/src/lib/db.ts b/src/lib/db.ts index 92639ee..f84adb6 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -2,6 +2,8 @@ import Database from "@tauri-apps/plugin-sql"; import type { Cabin, CabinReport, + Cost, + CostInput, Guest, GuestListRow, Invoice, @@ -452,7 +454,9 @@ export async function updateSettings(s: Settings): Promise { `UPDATE settings SET company_name = $1, first_name = $2, last_name = $3, address = $4, nip = $5, bank_account = $6, bank_name = $7, issue_place = $8, default_pkwiu = $9, - default_vat_rate = $10, energy_price = $11 + default_vat_rate = $10, energy_price = $11, season_start_month = $12, + season_end_month = $13, linen_cost = $14, ai_provider = $15, ai_api_key = $16, + ai_model = $17, ai_base_url = $18 WHERE id = 1`, [ s.company_name, @@ -465,11 +469,56 @@ export async function updateSettings(s: Settings): Promise { s.issue_place, s.default_pkwiu, s.default_vat_rate, - s.energy_price + s.energy_price, + s.season_start_month, + s.season_end_month, + s.linen_cost, + s.ai_provider, + s.ai_api_key, + s.ai_model, + s.ai_base_url ] ); } +// --- Koszty --- + +export async function listCosts(): Promise { + const d = await getDb(); + return d.select("SELECT * FROM costs ORDER BY name"); +} + +export async function createCost(c: CostInput): Promise { + const d = await getDb(); + await d.execute( + "INSERT INTO costs (name, amount, period, year, notes) VALUES ($1, $2, $3, $4, $5)", + [c.name, c.amount, c.period, c.year, c.notes] + ); +} + +export async function updateCost(c: Cost): Promise { + const d = await getDb(); + await d.execute( + "UPDATE costs SET name = $1, amount = $2, period = $3, year = $4, notes = $5 WHERE id = $6", + [c.name, c.amount, c.period, c.year, c.notes, c.id] + ); +} + +export async function deleteCost(id: number): Promise { + const d = await getDb(); + await d.execute("DELETE FROM costs WHERE id = $1", [id]); +} + +/** Suma kompletów pościeli (osób × pobyt) dla pobytów rozpoczętych w danym roku. */ +export async function linenSetsForYear(year: number): Promise { + const d = await getDb(); + const rows = await d.select<{ sets: number | null }[]>( + "SELECT SUM(COALESCE(num_guests, 1)) AS sets FROM reservations WHERE arrival >= $1 AND arrival < $2", + [`${year}-01-01`, `${year + 1}-01-01`] + ); + return rows[0]?.sets ?? 0; +} + // --- Faktury --- export async function listInvoices(): Promise<(Invoice & { gross: number })[]> { diff --git a/src/lib/llama.ts b/src/lib/llama.ts new file mode 100644 index 0000000..0a74e1f --- /dev/null +++ b/src/lib/llama.ts @@ -0,0 +1,37 @@ +import { invoke } from "@tauri-apps/api/core"; +import { listen, type UnlistenFn } from "@tauri-apps/api/event"; + +/** Port wbudowanego llama-server — musi zgadzać się z PORT w src-tauri/src/llama.rs. */ +export const LLAMA_PORT = 8123; + +export interface LlamaStatus { + installed: boolean; + running: boolean; +} + +export interface LlamaProgress { + task: string; + progress: number | null; +} + +export function llamaStatus(): Promise { + return invoke("llama_status"); +} + +/** Pobiera binarkę llama.cpp i model (~2,5 GB); postęp przez onLlamaProgress. */ +export function llamaDownload(): Promise { + return invoke("llama_download"); +} + +/** Startuje serwer (GPU, potem fallback CPU) i czeka aż będzie gotowy. */ +export function llamaStart(): Promise { + return invoke("llama_start"); +} + +export function llamaStop(): Promise { + return invoke("llama_stop"); +} + +export function onLlamaProgress(handler: (p: LlamaProgress) => void): Promise { + return listen("llama-progress", (event) => handler(event.payload)); +} \ No newline at end of file diff --git a/src/lib/types.ts b/src/lib/types.ts index cb4d47c..6804b0b 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -47,6 +47,8 @@ export type ReservationDialogParams = // --- Faktury --- +export type AiProvider = "anthropic" | "openai" | "ollama" | "lmstudio" | "local"; + export interface Settings { id: number; company_name: string; @@ -61,8 +63,33 @@ export interface Settings { default_vat_rate: number; /** Cena energii zł/kWh do rozliczania liczników */ energy_price: number | null; + /** Sezon działalności: miesiące 1–12 włącznie */ + season_start_month: number; + season_end_month: number; + /** Koszt prania/wymiany pościeli zł za komplet na osobę (na pobyt) */ + linen_cost: number | null; + ai_provider: AiProvider; + ai_api_key: string | null; + ai_model: string | null; + ai_base_url: string | null; } +// --- Koszty --- + +export type CostPeriod = "year" | "month" | "season_month" | "once"; + +export interface Cost { + id: number; + name: string; + amount: number; + period: CostPeriod; + /** Dla period = 'once': rok, którego koszt dotyczy */ + year: number | null; + notes: string | null; +} + +export type CostInput = Omit; + export type PaymentMethod = "CASH" | "CARD" | "CARD_CASH" | "SEND"; export interface Invoice { diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 5bfa199..f3412de 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -9,7 +9,9 @@ { href: "/faktury", label: "Faktury" }, { href: "/goscie", label: "Goście" }, { href: "/sprzatanie", label: "Sprzątanie" }, + { href: "/koszty", label: "Koszty" }, { href: "/raporty", label: "Raporty" }, + { href: "/asystent", label: "Asystent" }, { href: "/ustawienia", label: "Ustawienia" } ]; @@ -95,6 +97,9 @@ :global(select:hover:not(:focus)) { border-color: var(--muted); } + :global(input[type="checkbox"]) { + accent-color: var(--accent); + } /* --- Wspólny system przycisków --- */ :global(.btn) { diff --git a/src/routes/asystent/+page.svelte b/src/routes/asystent/+page.svelte new file mode 100644 index 0000000..ab26d66 --- /dev/null +++ b/src/routes/asystent/+page.svelte @@ -0,0 +1,457 @@ + + + + Słoneczko — asystent + + +
+
+

Asystent

+ {#if settings} + {AI_PROVIDER_LABELS[settings.ai_provider]} + {/if} + + {#if messages.length > 0} + + {/if} +
+ + {#if settings && !configured} +
+

Asystent nie jest jeszcze skonfigurowany.

+

+ Wybierz dostawcę AI i podaj klucz API (albo wskaż lokalny serwer Ollama/LM Studio). +

+ Przejdź do ustawień +
+ {:else if settings} + {#if isLocal && llama} +
+ {#if !llama.installed} + Wbudowany model nie jest jeszcze pobrany (Qwen3 4B, działa offline). + + {:else if llama.running} + ● Model działa + Qwen3 4B — lokalnie, bez wysyłania danych + {:else} + Model zainstalowany. + + uruchomi się też sam przy pierwszej wiadomości + {/if} + {#if llamaTask} +
+ {llamaTask} + {#if llamaProgress != null} +
+
+
+ {/if} +
+ {/if} +
+ {/if} +
+ {#if messages.length === 0} +
+

+ Znam Twój grafik, cennik, obłożenie i koszty — przy każdym pytaniu dostaję ich aktualny + obraz. Zapytaj np.: +

+
+ {#each SUGGESTIONS as s (s)} + + {/each} +
+
+ {/if} + {#each messages as message, i (i)} +
+ {#if message.content} + {message.content} + {:else if streaming && i === messages.length - 1} + Myślę… + {/if} +
+ {/each} +
+ + {#if error} +

{error}

+ {/if} + +
{ + e.preventDefault(); + void send(); + }} + > + + {#if streaming} + + {:else} + + {/if} +
+ {/if} +
+ + diff --git a/src/routes/koszty/+page.svelte b/src/routes/koszty/+page.svelte new file mode 100644 index 0000000..b343069 --- /dev/null +++ b/src/routes/koszty/+page.svelte @@ -0,0 +1,371 @@ + + + + Słoneczko — koszty + + +
+
+

Koszty

+ + + {year} + +
+ + {#if error} +

{error}

+ {/if} + + {#if settings && yearStats} +
+
+ Przychód potwierdzony + {formatMoney(yearStats.revenue_confirmed)} zł +
+
+ Koszty razem + {formatMoney(totalCosts)} zł +
+
+ Zysk (potwierdzony − koszty) + {formatMoney(profitConfirmed ?? 0)} zł +
+
+ Zysk przy pełnym potencjale + {formatMoney(profitPotential ?? 0)} zł +
+
+ +
+

Pościel

+
+ +

+ W {year}: {linenSets} kompletów (osoby × pobyty) + {#if settings.linen_cost != null} + × {formatMoney(settings.linen_cost)} zł = {formatMoney(linenTotal)} zł + {/if} +

+
+
+ +
+

Koszty stałe

+ {#if costs.length > 0} +
+ Nazwa + Kwota (zł) + Okres + Rok + W {year} + + {#each costs as cost (cost.id)} + persistCost(cost)} aria-label="Nazwa kosztu" /> + persistCost(cost)} aria-label="Kwota" /> + + {#if cost.period === "once"} + persistCost(cost)} aria-label="Rok kosztu" /> + {:else} + + {/if} + {formatMoney(costPerYear(cost, year, seasonMonthCount))} zł + + {/each} +
+

Koszty stałe razem: {formatMoney(fixedCosts)} zł

+ {:else} +

+ Nie ma jeszcze żadnych kosztów — dodaj np. wodę, śmieci, ubezpieczenie, podatek od + nieruchomości czy naprawy. +

+ {/if} + +
+ + + + {#if newCost.period === "once"} + + {:else} + + {/if} + +
+

+ „Miesięcznie w sezonie" mnoży kwotę przez {seasonMonthCount} + {seasonMonthCount === 1 ? "miesiąc" : seasonMonthCount < 5 ? "miesiące" : "miesięcy"} sezonu. + Jednorazowe koszty (np. naprawa dachu) przypisz do konkretnego roku. +

+
+ {/if} +
+ + diff --git a/src/routes/raporty/+page.svelte b/src/routes/raporty/+page.svelte index 7c11e95..205ab96 100644 --- a/src/routes/raporty/+page.svelte +++ b/src/routes/raporty/+page.svelte @@ -1,21 +1,36 @@