feat: Add Gemini AI settings and inline action buttons

- Introduced GeminiSettings component for configuring Gemini AI settings including API key, model selection, and inline suggestions.
- Updated App.vue to include GeminiSettings in the view management.
- Enhanced MonacoEditor with AI action buttons for code fixing, explaining, refactoring, and optimizing.
- Implemented responsive design for GeminiSettings and MonacoEditor components.
- Added sidebar button to toggle Gemini settings.
- Integrated API calls for saving and testing Gemini configuration.
This commit is contained in:
Shni
2025-11-04 04:40:39 -06:00
parent 38046e4df8
commit 4cf99a6f91
8 changed files with 1618 additions and 35 deletions

View File

@@ -14,12 +14,15 @@ version = "0.1.0"
dependencies = [
"discord-rich-presence",
"regex",
"reqwest 0.11.27",
"serde",
"serde_json",
"tauri",
"tauri-build",
"tauri-plugin-dialog",
"tauri-plugin-opener",
"tokio",
"uuid 1.18.1",
]
[[package]]
@@ -526,6 +529,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"
@@ -549,9 +562,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1"
dependencies = [
"bitflags 2.10.0",
"core-foundation",
"core-foundation 0.10.1",
"core-graphics-types",
"foreign-types",
"foreign-types 0.5.0",
"libc",
]
@@ -562,7 +575,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
dependencies = [
"bitflags 2.10.0",
"core-foundation",
"core-foundation 0.10.1",
"libc",
]
@@ -862,7 +875,7 @@ dependencies = [
"rustc_version",
"toml 0.9.8",
"vswhom",
"winreg",
"winreg 0.55.0",
]
[[package]]
@@ -871,6 +884,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.0"
@@ -993,6 +1015,15 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[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"
@@ -1000,7 +1031,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]]
@@ -1014,6 +1045,12 @@ dependencies = [
"syn 2.0.108",
]
[[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"
@@ -1424,6 +1461,25 @@ dependencies = [
"syn 2.0.108",
]
[[package]]
name = "h2"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http 0.2.12",
"indexmap 2.12.0",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@@ -1472,6 +1528,17 @@ dependencies = [
"match_token",
]
[[package]]
name = "http"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http"
version = "1.3.1"
@@ -1483,6 +1550,17 @@ dependencies = [
"itoa",
]
[[package]]
name = "http-body"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
dependencies = [
"bytes",
"http 0.2.12",
"pin-project-lite",
]
[[package]]
name = "http-body"
version = "1.0.1"
@@ -1490,7 +1568,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [
"bytes",
"http",
"http 1.3.1",
]
[[package]]
@@ -1501,8 +1579,8 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
dependencies = [
"bytes",
"futures-core",
"http",
"http-body",
"http 1.3.1",
"http-body 1.0.1",
"pin-project-lite",
]
@@ -1512,6 +1590,36 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "0.14.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7"
dependencies = [
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"h2",
"http 0.2.12",
"http-body 0.4.6",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"socket2 0.5.10",
"tokio",
"tower-service",
"tracing",
"want",
]
[[package]]
name = "hyper"
version = "1.7.0"
@@ -1522,8 +1630,8 @@ dependencies = [
"bytes",
"futures-channel",
"futures-core",
"http",
"http-body",
"http 1.3.1",
"http-body 1.0.1",
"httparse",
"itoa",
"pin-project-lite",
@@ -1533,6 +1641,19 @@ dependencies = [
"want",
]
[[package]]
name = "hyper-tls"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes",
"hyper 0.14.32",
"native-tls",
"tokio",
"tokio-native-tls",
]
[[package]]
name = "hyper-util"
version = "0.1.17"
@@ -1544,14 +1665,14 @@ dependencies = [
"futures-channel",
"futures-core",
"futures-util",
"http",
"http-body",
"hyper",
"http 1.3.1",
"http-body 1.0.1",
"hyper 1.7.0",
"ipnet",
"libc",
"percent-encoding",
"pin-project-lite",
"socket2",
"socket2 0.6.1",
"tokio",
"tower-service",
"tracing",
@@ -2055,6 +2176,23 @@ dependencies = [
"windows-sys 0.60.2",
]
[[package]]
name = "native-tls"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
dependencies = [
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]]
name = "ndk"
version = "0.9.0"
@@ -2429,6 +2567,50 @@ dependencies = [
"pathdiff",
]
[[package]]
name = "openssl"
version = "0.10.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654"
dependencies = [
"bitflags 2.10.0",
"cfg-if",
"foreign-types 0.3.2",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.108",
]
[[package]]
name = "openssl-probe"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-sys"
version = "0.9.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "option-ext"
version = "0.2.0"
@@ -3030,6 +3212,46 @@ version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "reqwest"
version = "0.11.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
dependencies = [
"base64 0.21.7",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http 0.2.12",
"http-body 0.4.6",
"hyper 0.14.32",
"hyper-tls",
"ipnet",
"js-sys",
"log",
"mime",
"native-tls",
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls-pemfile",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper 0.1.2",
"system-configuration",
"tokio",
"tokio-native-tls",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"winreg 0.50.0",
]
[[package]]
name = "reqwest"
version = "0.12.24"
@@ -3040,10 +3262,10 @@ dependencies = [
"bytes",
"futures-core",
"futures-util",
"http",
"http-body",
"http 1.3.1",
"http-body 1.0.1",
"http-body-util",
"hyper",
"hyper 1.7.0",
"hyper-util",
"js-sys",
"log",
@@ -3052,7 +3274,7 @@ dependencies = [
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
"sync_wrapper 1.0.2",
"tokio",
"tokio-util",
"tower",
@@ -3112,6 +3334,15 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "rustls-pemfile"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
dependencies = [
"base64 0.21.7",
]
[[package]]
name = "rustversion"
version = "1.0.22"
@@ -3133,6 +3364,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"
@@ -3196,6 +3436,29 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "security-framework"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags 2.10.0",
"core-foundation 0.9.4",
"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.24.0"
@@ -3450,6 +3713,16 @@ version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "socket2"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "socket2"
version = "0.6.1"
@@ -3469,7 +3742,7 @@ dependencies = [
"bytemuck",
"cfg_aliases",
"core-graphics",
"foreign-types",
"foreign-types 0.5.0",
"js-sys",
"log",
"objc2 0.5.2",
@@ -3584,6 +3857,12 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "sync_wrapper"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "sync_wrapper"
version = "1.0.2"
@@ -3604,6 +3883,27 @@ dependencies = [
"syn 2.0.108",
]
[[package]]
name = "system-configuration"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
dependencies = [
"bitflags 1.3.2",
"core-foundation 0.9.4",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "system-deps"
version = "6.2.2"
@@ -3625,7 +3925,7 @@ checksum = "f3a753bdc39c07b192151523a3f77cd0394aa75413802c883a0f6f6a0e5ee2e7"
dependencies = [
"bitflags 2.10.0",
"block2 0.6.2",
"core-foundation",
"core-foundation 0.10.1",
"core-graphics",
"crossbeam-channel",
"dispatch",
@@ -3690,7 +3990,7 @@ dependencies = [
"glob",
"gtk",
"heck 0.5.0",
"http",
"http 1.3.1",
"jni",
"libc",
"log",
@@ -3704,7 +4004,7 @@ dependencies = [
"percent-encoding",
"plist",
"raw-window-handle",
"reqwest",
"reqwest 0.12.24",
"serde",
"serde_json",
"serde_repr",
@@ -3876,7 +4176,7 @@ dependencies = [
"cookie",
"dpi",
"gtk",
"http",
"http 1.3.1",
"jni",
"objc2 0.6.3",
"objc2-ui-kit",
@@ -3899,7 +4199,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "929f5df216f5c02a9e894554401bcdab6eec3e39ec6a4a7731c7067fc8688a93"
dependencies = [
"gtk",
"http",
"http 1.3.1",
"jni",
"log",
"objc2 0.6.3",
@@ -3932,7 +4232,7 @@ dependencies = [
"dunce",
"glob",
"html5ever",
"http",
"http 1.3.1",
"infer",
"json-patch",
"kuchikiki",
@@ -4081,13 +4381,36 @@ dependencies = [
"bytes",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"socket2 0.6.1",
"tokio-macros",
"tracing",
"windows-sys 0.61.2",
]
[[package]]
name = "tokio-macros"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.108",
]
[[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-util"
version = "0.7.17"
@@ -4206,7 +4529,7 @@ dependencies = [
"futures-core",
"futures-util",
"pin-project-lite",
"sync_wrapper",
"sync_wrapper 1.0.2",
"tokio",
"tower-layer",
"tower-service",
@@ -4221,8 +4544,8 @@ dependencies = [
"bitflags 2.10.0",
"bytes",
"futures-util",
"http",
"http-body",
"http 1.3.1",
"http-body 1.0.1",
"iri-string",
"pin-project-lite",
"tower",
@@ -4434,6 +4757,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version-compare"
version = "0.2.1"
@@ -4921,6 +5250,24 @@ dependencies = [
"windows-targets 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
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"
@@ -4963,6 +5310,21 @@ dependencies = [
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
@@ -5020,6 +5382,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
@@ -5038,6 +5406,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
@@ -5056,6 +5430,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
@@ -5086,6 +5466,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
@@ -5104,6 +5490,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
@@ -5122,6 +5514,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
@@ -5140,6 +5538,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
@@ -5170,6 +5574,16 @@ dependencies = [
"memchr",
]
[[package]]
name = "winreg"
version = "0.50.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
dependencies = [
"cfg-if",
"windows-sys 0.48.0",
]
[[package]]
name = "winreg"
version = "0.55.0"
@@ -5208,7 +5622,7 @@ dependencies = [
"gdkx11",
"gtk",
"html5ever",
"http",
"http 1.3.1",
"javascriptcore-rs",
"jni",
"kuchikiki",

View File

@@ -25,4 +25,7 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1"
discord-rich-presence = "0.2"
regex = "1"
reqwest = { version = "0.11", features = ["json", "blocking"] }
tokio = { version = "1", features = ["full"] }
uuid = { version = "1", features = ["v4"] }

View File

@@ -8,6 +8,29 @@ use discord_rich_presence::{activity, DiscordIpc, DiscordIpcClient};
// Cliente Discord RPC global
static DISCORD_CLIENT: Mutex<Option<DiscordIpcClient>> = Mutex::new(None);
// Structs para Codeium API
#[derive(Debug, Serialize, Deserialize)]
struct CodeiumCompletionRequest {
text: String,
cursor_position: usize,
language: String,
file_path: String,
}
#[derive(Debug, Serialize, Deserialize)]
struct CodeiumCompletion {
text: String,
range: CodeiumRange,
}
#[derive(Debug, Serialize, Deserialize)]
struct CodeiumRange {
start_line: usize,
start_column: usize,
end_line: usize,
end_column: usize,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct FileInfo {
@@ -749,6 +772,397 @@ fn get_package_scripts(project_root: String) -> Result<Vec<(String, String)>, St
Ok(scripts)
}
// ============================================
// GEMINI API INTEGRATION (Code Completion)
// ============================================
#[derive(serde::Serialize)]
struct GeminiRequest {
contents: Vec<GeminiContent>,
#[serde(rename = "generationConfig")]
generation_config: GeminiGenerationConfig,
}
#[derive(serde::Serialize)]
struct GeminiContent {
parts: Vec<GeminiPart>,
}
#[derive(serde::Serialize)]
struct GeminiPart {
text: String,
}
#[derive(serde::Serialize)]
struct GeminiGenerationConfig {
temperature: f32,
#[serde(rename = "maxOutputTokens")]
max_output_tokens: i32,
#[serde(rename = "candidateCount")]
candidate_count: i32,
#[serde(rename = "thinkingConfig", skip_serializing_if = "Option::is_none")]
thinking_config: Option<GeminiThinkingConfig>,
}
#[derive(serde::Serialize)]
struct GeminiThinkingConfig {
#[serde(rename = "thinkingBudget")]
thinking_budget: i32,
#[serde(rename = "includeThoughts")]
include_thoughts: bool,
}
#[derive(serde::Deserialize)]
struct GeminiResponse {
candidates: Option<Vec<GeminiCandidate>>,
}
#[derive(serde::Deserialize)]
struct GeminiCandidate {
content: GeminiResponseContent,
}
#[derive(serde::Deserialize)]
struct GeminiResponseContent {
parts: Vec<GeminiResponsePart>,
}
#[derive(serde::Deserialize)]
struct GeminiResponsePart {
text: String,
}
#[tauri::command]
async fn get_gemini_completion(
text: String,
cursor_position: usize,
language: String,
file_path: String,
api_key: String,
model: String,
agent_mode: Option<bool>,
) -> Result<Vec<String>, String> {
use reqwest::Client;
if api_key.is_empty() {
return Ok(vec![]);
}
// Extraer contexto antes y después del cursor
let before = text.chars().take(cursor_position).collect::<String>();
let after = text.chars().skip(cursor_position).take(200).collect::<String>();
// Tomar últimas 15 líneas de contexto
let context_lines: Vec<&str> = before.lines().rev().take(15).collect();
let context = context_lines.into_iter().rev().collect::<Vec<&str>>().join("\n");
// Crear prompt optimizado para autocompletado
let prompt = format!(
"You are a code completion AI. Complete the {} code at the cursor position.
File: {}
Code:
```{}
{}[CURSOR]{}
```
Complete ONLY what comes immediately after [CURSOR]. Output raw code only, no markdown, no explanations:",
language, file_path, language, context, after
);
// Configurar thinking mode basado en agent_mode
let (thinking_config, max_tokens) = if agent_mode.unwrap_or(false) {
println!("🤖 Modo Agent activado con thinking dinámico");
(
Some(GeminiThinkingConfig {
thinking_budget: -1, // Dinámico: el modelo decide cuánto "pensar"
include_thoughts: true, // Incluir resumen de pensamientos
}),
512 // Más tokens cuando thinking está activado
)
} else {
println!("⚡ Modo rápido: thinking desactivado");
(
Some(GeminiThinkingConfig {
thinking_budget: 0, // Sin thinking para completions rápidos
include_thoughts: false,
}),
120 // Tokens normales para autocompletado rápido
)
};
let request_body = GeminiRequest {
contents: vec![GeminiContent {
parts: vec![GeminiPart { text: prompt }],
}],
generation_config: GeminiGenerationConfig {
temperature: 0.2,
max_output_tokens: max_tokens,
candidate_count: 1,
thinking_config,
},
};
let url = format!(
"https://generativelanguage.googleapis.com/v1beta/models/{}:generateContent?key={}",
model, api_key
);
println!("🚀 Llamando a Gemini API...");
println!(" Modelo: {}", model);
println!(" URL: {}", url.split("?key=").next().unwrap_or(""));
let client = Client::new();
match client
.post(&url)
.header("Content-Type", "application/json")
.json(&request_body)
.timeout(std::time::Duration::from_secs(8))
.send()
.await
{
Ok(response) => {
let status = response.status();
println!("📡 Status: {}", status);
if response.status().is_success() {
// Primero obtener el texto para debuggear
let response_text = response.text().await.map_err(|e| e.to_string())?;
println!("📦 Respuesta completa: {}", response_text);
// Intentar parsear manualmente
match serde_json::from_str::<serde_json::Value>(&response_text) {
Ok(json) => {
println!("✅ JSON parseado correctamente");
let mut suggestions = Vec::new();
// Navegar por la estructura real de Gemini
if let Some(candidates) = json["candidates"].as_array() {
println!("✅ Candidatos encontrados: {}", candidates.len());
for (i, candidate) in candidates.iter().enumerate() {
println!(" 📋 Procesando candidato #{}", i + 1);
// Ver el finishReason
if let Some(finish_reason) = candidate["finishReason"].as_str() {
println!(" 🏁 Finish reason: {}", finish_reason);
}
// Intentar extraer el texto de diferentes formas
if let Some(content) = candidate.get("content") {
if let Some(parts) = content["parts"].as_array() {
println!(" ✅ Parts encontrados: {}", parts.len());
for part in parts {
// Detectar si es un "thought" (pensamiento del modelo)
let is_thought = part["thought"].as_bool().unwrap_or(false);
if let Some(text) = part["text"].as_str() {
if is_thought {
println!(" 💭 Pensamiento del modelo: {}", text);
// Los pensamientos NO se agregan como sugerencias
continue;
}
let cleaned = text.trim()
.trim_start_matches("```")
.trim_start_matches(&language)
.trim_end_matches("```")
.trim();
if !cleaned.is_empty() {
println!(" ✨ Sugerencia: {}", cleaned);
suggestions.push(cleaned.to_string());
}
}
}
} else {
println!(" ⚠️ No hay 'parts' en content");
}
} else {
println!(" ⚠️ No hay 'content' en candidate");
}
}
}
if suggestions.is_empty() {
println!("⚠️ No se pudieron extraer sugerencias del JSON");
}
Ok(suggestions)
}
Err(e) => {
eprintln!("❌ Error parsing JSON: {:?}", e);
Ok(vec![])
}
}
} else {
let error_text = response.text().await.unwrap_or_default();
eprintln!("❌ Gemini API error {}: {}", status, error_text);
Ok(vec![])
}
}
Err(e) => {
eprintln!("❌ Error calling Gemini: {:?}", e);
Ok(vec![])
}
}
}
// Nueva función para procesar prompts directos (Fix, Explain, etc.)
#[tauri::command]
async fn ask_gemini(
prompt: String,
api_key: String,
model: String,
use_thinking: bool,
) -> Result<String, String> {
use reqwest::Client;
if api_key.is_empty() {
return Err("No API key provided".to_string());
}
// Configurar thinking mode
let (thinking_config, max_tokens) = if use_thinking {
println!("🤖 ask_gemini con thinking activado");
(
Some(GeminiThinkingConfig {
thinking_budget: -1,
include_thoughts: true,
}),
2048 // Más tokens para respuestas completas
)
} else {
println!("⚡ ask_gemini modo rápido");
(
Some(GeminiThinkingConfig {
thinking_budget: 0,
include_thoughts: false,
}),
1024
)
};
let request_body = GeminiRequest {
contents: vec![GeminiContent {
parts: vec![GeminiPart { text: prompt }],
}],
generation_config: GeminiGenerationConfig {
temperature: 0.3,
max_output_tokens: max_tokens,
candidate_count: 1,
thinking_config,
},
};
let url = format!(
"https://generativelanguage.googleapis.com/v1beta/models/{}:generateContent?key={}",
model, api_key
);
println!("🚀 ask_gemini llamando a Gemini API...");
println!(" Modelo: {}", model);
let client = Client::new();
match client
.post(&url)
.header("Content-Type", "application/json")
.json(&request_body)
.timeout(std::time::Duration::from_secs(15))
.send()
.await
{
Ok(response) => {
let status = response.status();
println!("📡 Status: {}", status);
if response.status().is_success() {
let response_text = response.text().await.map_err(|e| e.to_string())?;
match serde_json::from_str::<serde_json::Value>(&response_text) {
Ok(json) => {
if let Some(candidates) = json["candidates"].as_array() {
if let Some(candidate) = candidates.first() {
if let Some(content) = candidate.get("content") {
if let Some(parts) = content["parts"].as_array() {
// Concatenar todas las partes de texto (ignorando thoughts)
let mut result = String::new();
for part in parts {
let is_thought = part["thought"].as_bool().unwrap_or(false);
if !is_thought {
if let Some(text) = part["text"].as_str() {
result.push_str(text);
}
}
}
if !result.is_empty() {
println!("✅ Respuesta obtenida: {} caracteres", result.len());
return Ok(result);
}
}
}
}
}
Err("No se encontró contenido en la respuesta".to_string())
}
Err(e) => {
eprintln!("❌ Error parsing JSON: {:?}", e);
Err(format!("Error parsing response: {}", e))
}
}
} else {
let error_text = response.text().await.unwrap_or_default();
eprintln!("❌ Gemini API error {}: {}", status, error_text);
Err(format!("API error: {}", error_text))
}
}
Err(e) => {
eprintln!("❌ Error calling Gemini: {:?}", e);
Err(format!("Network error: {}", e))
}
}
}
// Guardar configuración de Gemini
#[tauri::command]
fn save_gemini_config(api_key: String, model: String, app_data_dir: String, agent_mode: Option<bool>, inline_suggestions_enabled: Option<bool>) -> Result<(), String> {
// Crear el directorio si no existe
let dir_path = Path::new(&app_data_dir);
if !dir_path.exists() {
fs::create_dir_all(dir_path).map_err(|e| format!("Error creando directorio: {}", e))?;
}
let config_path = dir_path.join("gemini_config.json");
let config = serde_json::json!({
"api_key": api_key,
"model": model,
"enabled": true,
"agent_mode": agent_mode.unwrap_or(false),
"inline_suggestions_enabled": inline_suggestions_enabled.unwrap_or(false)
});
fs::write(&config_path, serde_json::to_string_pretty(&config).unwrap())
.map_err(|e| format!("Error guardando archivo: {}", e))?;
Ok(())
}
// Leer configuración de Gemini
#[tauri::command]
fn load_gemini_config(app_data_dir: String) -> Result<String, String> {
let config_path = Path::new(&app_data_dir).join("gemini_config.json");
if !config_path.exists() {
return Err("No hay API key configurada".to_string());
}
let content = fs::read_to_string(&config_path).map_err(|e| e.to_string())?;
Ok(content)
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
@@ -778,7 +1192,11 @@ pub fn run() {
write_env_file,
scan_env_variables,
scan_env_variables_with_locations,
get_package_scripts
get_package_scripts,
get_gemini_completion,
ask_gemini,
save_gemini_config,
load_gemini_config
])
.run(tauri::generate_context!())
.expect("error while running tauri application");