diff --git a/package-lock.json b/package-lock.json
index 249d990..462e818 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -309,9 +309,6 @@
"arm64"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -329,9 +326,6 @@
"arm64"
],
"dev": true,
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -349,9 +343,6 @@
"ppc64"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -369,9 +360,6 @@
"s390x"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -389,9 +377,6 @@
"x64"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -409,9 +394,6 @@
"x64"
],
"dev": true,
- "libc": [
- "musl"
- ],
"license": "MIT",
"optional": true,
"os": [
@@ -597,9 +579,6 @@
"arm64"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
@@ -617,9 +596,6 @@
"arm64"
],
"dev": true,
- "libc": [
- "musl"
- ],
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
@@ -637,9 +613,6 @@
"riscv64"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
@@ -657,9 +630,6 @@
"x64"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
@@ -677,9 +647,6 @@
"x64"
],
"dev": true,
- "libc": [
- "musl"
- ],
"license": "Apache-2.0 OR MIT",
"optional": true,
"os": [
@@ -1262,9 +1229,6 @@
"arm64"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -1286,9 +1250,6 @@
"arm64"
],
"dev": true,
- "libc": [
- "musl"
- ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -1310,9 +1271,6 @@
"x64"
],
"dev": true,
- "libc": [
- "glibc"
- ],
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -1334,9 +1292,6 @@
"x64"
],
"dev": true,
- "libc": [
- "musl"
- ],
"license": "MPL-2.0",
"optional": true,
"os": [
diff --git a/popup.html b/popup.html
new file mode 100644
index 0000000..ea253d6
--- /dev/null
+++ b/popup.html
@@ -0,0 +1,18 @@
+
+
+
+
+ Projekte
+
+
+
+
+
+
+
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 8253903..85462bf 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -33,6 +33,7 @@ name = "ai-control"
version = "0.1.1"
dependencies = [
"base64 0.22.1",
+ "glib",
"keyring",
"log",
"objc2",
@@ -48,6 +49,7 @@ dependencies = [
"tauri-plugin-dialog",
"tauri-plugin-log",
"uuid",
+ "zbus",
]
[[package]]
@@ -103,6 +105,137 @@ version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
+[[package]]
+name = "async-broadcast"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532"
+dependencies = [
+ "event-listener",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-channel"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
+dependencies = [
+ "concurrent-queue",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-executor"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a"
+dependencies = [
+ "async-task",
+ "concurrent-queue",
+ "fastrand",
+ "futures-lite",
+ "pin-project-lite",
+ "slab",
+]
+
+[[package]]
+name = "async-io"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "concurrent-queue",
+ "futures-io",
+ "futures-lite",
+ "parking",
+ "polling",
+ "rustix",
+ "slab",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "async-lock"
+version = "3.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
+dependencies = [
+ "event-listener",
+ "event-listener-strategy",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-process"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75"
+dependencies = [
+ "async-channel",
+ "async-io",
+ "async-lock",
+ "async-signal",
+ "async-task",
+ "blocking",
+ "cfg-if",
+ "event-listener",
+ "futures-lite",
+ "rustix",
+]
+
+[[package]]
+name = "async-recursion"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.118",
+]
+
+[[package]]
+name = "async-signal"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485"
+dependencies = [
+ "async-io",
+ "async-lock",
+ "atomic-waker",
+ "cfg-if",
+ "futures-core",
+ "futures-io",
+ "rustix",
+ "signal-hook-registry",
+ "slab",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "async-task"
+version = "4.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
+
+[[package]]
+name = "async-trait"
+version = "0.1.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.118",
+]
+
[[package]]
name = "atk"
version = "0.18.2"
@@ -221,6 +354,19 @@ dependencies = [
"objc2",
]
+[[package]]
+name = "blocking"
+version = "1.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
+dependencies = [
+ "async-channel",
+ "async-task",
+ "futures-io",
+ "futures-lite",
+ "piper",
+]
+
[[package]]
name = "borsh"
version = "1.7.0"
@@ -486,6 +632,15 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "concurrent-queue"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
+dependencies = [
+ "crossbeam-utils",
+]
+
[[package]]
name = "cookie"
version = "0.18.1"
@@ -902,6 +1057,33 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
+[[package]]
+name = "endi"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099"
+
+[[package]]
+name = "enumflags2"
+version = "0.7.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef"
+dependencies = [
+ "enumflags2_derive",
+ "serde",
+]
+
+[[package]]
+name = "enumflags2_derive"
+version = "0.7.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.118",
+]
+
[[package]]
name = "env_filter"
version = "0.1.4"
@@ -929,6 +1111,37 @@ dependencies = [
"typeid",
]
+[[package]]
+name = "errno"
+version = "0.3.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "event-listener"
+version = "5.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
+dependencies = [
+ "concurrent-queue",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener-strategy"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
+dependencies = [
+ "event-listener",
+ "pin-project-lite",
+]
+
[[package]]
name = "fastrand"
version = "2.4.1"
@@ -1076,6 +1289,19 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
+[[package]]
+name = "futures-lite"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
+dependencies = [
+ "fastrand",
+ "futures-core",
+ "futures-io",
+ "parking",
+ "pin-project-lite",
+]
+
[[package]]
name = "futures-macro"
version = "0.3.32"
@@ -1433,6 +1659,12 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+[[package]]
+name = "hermit-abi"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
+
[[package]]
name = "hex"
version = "0.4.3"
@@ -1921,6 +2153,12 @@ dependencies = [
"libc",
]
+[[package]]
+name = "linux-raw-sys"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
+
[[package]]
name = "litemap"
version = "0.8.2"
@@ -2347,6 +2585,16 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+[[package]]
+name = "ordered-stream"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+]
+
[[package]]
name = "pango"
version = "0.18.3"
@@ -2372,6 +2620,12 @@ dependencies = [
"system-deps",
]
+[[package]]
+name = "parking"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
+
[[package]]
name = "parking_lot"
version = "0.12.5"
@@ -2460,6 +2714,17 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+[[package]]
+name = "piper"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1"
+dependencies = [
+ "atomic-waker",
+ "fastrand",
+ "futures-io",
+]
+
[[package]]
name = "pkg-config"
version = "0.3.33"
@@ -2505,6 +2770,20 @@ dependencies = [
"miniz_oxide",
]
+[[package]]
+name = "polling"
+version = "3.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
+dependencies = [
+ "cfg-if",
+ "concurrent-queue",
+ "hermit-abi",
+ "pin-project-lite",
+ "rustix",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "portable-pty"
version = "0.9.0"
@@ -2924,6 +3203,19 @@ dependencies = [
"semver",
]
+[[package]]
+name = "rustix"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
+dependencies = [
+ "bitflags 2.13.0",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "rustversion"
version = "1.0.22"
@@ -3269,6 +3561,16 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
+dependencies = [
+ "errno",
+ "libc",
+]
+
[[package]]
name = "simd-adler32"
version = "0.3.9"
@@ -3830,6 +4132,19 @@ dependencies = [
"toml 1.1.2+spec-1.1.0",
]
+[[package]]
+name = "tempfile"
+version = "3.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
+dependencies = [
+ "fastrand",
+ "getrandom 0.4.3",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "tendril"
version = "0.5.0"
@@ -4136,9 +4451,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
"pin-project-lite",
+ "tracing-attributes",
"tracing-core",
]
+[[package]]
+name = "tracing-attributes"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.118",
+]
+
[[package]]
name = "tracing-core"
version = "0.1.36"
@@ -4188,6 +4515,17 @@ version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
+[[package]]
+name = "uds_windows"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
+dependencies = [
+ "memoffset",
+ "tempfile",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "unic-char-property"
version = "0.9.0"
@@ -5115,6 +5453,67 @@ dependencies = [
"synstructure",
]
+[[package]]
+name = "zbus"
+version = "5.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285"
+dependencies = [
+ "async-broadcast",
+ "async-executor",
+ "async-io",
+ "async-lock",
+ "async-process",
+ "async-recursion",
+ "async-task",
+ "async-trait",
+ "blocking",
+ "enumflags2",
+ "event-listener",
+ "futures-core",
+ "futures-lite",
+ "hex",
+ "libc",
+ "ordered-stream",
+ "rustix",
+ "serde",
+ "serde_repr",
+ "tracing",
+ "uds_windows",
+ "uuid",
+ "windows-sys 0.61.2",
+ "winnow 1.0.3",
+ "zbus_macros",
+ "zbus_names",
+ "zvariant",
+]
+
+[[package]]
+name = "zbus_macros"
+version = "5.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6"
+dependencies = [
+ "proc-macro-crate 3.5.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.118",
+ "zbus_names",
+ "zvariant",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zbus_names"
+version = "4.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d"
+dependencies = [
+ "serde",
+ "winnow 1.0.3",
+ "zvariant",
+]
+
[[package]]
name = "zerocopy"
version = "0.8.52"
@@ -5214,3 +5613,43 @@ name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+
+[[package]]
+name = "zvariant"
+version = "5.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0"
+dependencies = [
+ "endi",
+ "enumflags2",
+ "serde",
+ "winnow 1.0.3",
+ "zvariant_derive",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zvariant_derive"
+version = "5.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737"
+dependencies = [
+ "proc-macro-crate 3.5.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.118",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zvariant_utils"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde",
+ "syn 2.0.118",
+ "winnow 1.0.3",
+]
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index d1825ec..5d52386 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -31,6 +31,10 @@ portable-pty = "0.9"
uuid = { version = "1", features = ["v4"] }
keyring = { version = "3", features = ["apple-native", "sync-secret-service", "windows-native"] }
+[target.'cfg(target_os = "linux")'.dependencies]
+zbus = "5"
+glib = "0.18"
+
[target.'cfg(target_os = "macos")'.dependencies]
objc2 = "0.6"
objc2-app-kit = { version = "0.3", features = ["NSApplication", "NSImage", "NSResponder", "NSRunningApplication"] }
diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json
index 2868d06..6a6513d 100644
--- a/src-tauri/capabilities/default.json
+++ b/src-tauri/capabilities/default.json
@@ -4,11 +4,18 @@
"description": "enables the default permissions",
"windows": [
"main",
+ "popup",
"term-*"
],
"permissions": [
"core:default",
"core:window:allow-start-dragging",
+ "core:window:allow-set-size",
+ "core:window:allow-minimize",
+ "core:window:allow-toggle-maximize",
+ "core:window:allow-close",
+ "core:window:allow-hide",
+ "core:window:allow-start-resize-dragging",
"autostart:default",
"dialog:default"
]
diff --git a/src-tauri/icons/trayLinux.png b/src-tauri/icons/trayLinux.png
new file mode 100644
index 0000000..b305de0
Binary files /dev/null and b/src-tauri/icons/trayLinux.png differ
diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
index 5e09721..945c767 100644
--- a/src-tauri/src/lib.rs
+++ b/src-tauri/src/lib.rs
@@ -340,12 +340,12 @@ fn spawn_session_watcher(app: tauri::AppHandle) {
let handle = app.clone();
app
.run_on_main_thread(move || {
- let menu = tray_menu(&handle).expect("Tray-Menü nicht aufbaubar");
- handle
- .tray_by_id("main")
- .expect("Tray-Icon fehlt")
- .set_menu(Some(menu))
- .expect("Tray-Menü nicht setzbar");
+ // Nur wenn ein Tray existiert (macOS). Unter Linux pollt das Popup selbst.
+ if let Some(tray) = handle.tray_by_id("main") {
+ if let Ok(menu) = tray_menu(&handle) {
+ let _ = tray.set_menu(Some(menu));
+ }
+ }
})
.expect("Main-Thread nicht erreichbar");
state = current;
@@ -556,6 +556,8 @@ fn create_project_full_in(
if todo {
set_todo_in(paths, name, true)?;
}
+ #[cfg(target_os = "linux")]
+ write_terminal_desktop(paths, name, &cfg.terminal);
Ok(())
}
@@ -618,7 +620,15 @@ fn add_project_in(paths: &Paths, path: &str) -> Result<(), String> {
.to_string_lossy()
.into_owned();
check_name(&name)?;
- register_project(paths, &name, &dir)
+ register_project(paths, &name, &dir)?;
+ #[cfg(target_os = "linux")]
+ {
+ let cfg = read_project_config_in(paths, &name)
+ .map(|c| c.terminal)
+ .unwrap_or_default();
+ write_terminal_desktop(paths, &name, &cfg);
+ }
+ Ok(())
}
#[cfg(test)]
@@ -739,7 +749,10 @@ fn delete_project_in(paths: &Paths, name: &str, delete_work_dirs: bool) -> Resul
}
}
fs::remove_dir_all(&dir).map_err(|e| e.to_string())?;
- unregister_project(paths, name)
+ unregister_project(paths, name)?;
+ #[cfg(target_os = "linux")]
+ remove_terminal_desktop(paths, name);
+ Ok(())
}
fn read_project_config_in(paths: &Paths, project: &str) -> Result {
@@ -809,7 +822,10 @@ fn set_terminal_config_in(
}
}
cfg.terminal = terminal;
- write_project_config_in(paths, project, &cfg)
+ write_project_config_in(paths, project, &cfg)?;
+ #[cfg(target_os = "linux")]
+ write_terminal_desktop(paths, project, &cfg.terminal);
+ Ok(())
}
/// Icon-Pfad einer Projekt-Config auflösen: relative Namen liegen im
@@ -822,6 +838,78 @@ fn resolve_icon_path(paths: &Paths, icon: &str) -> PathBuf {
}
}
+/// ~/.local/share/applications — Ziel der pro-Terminal-.desktop-Dateien.
+/// paths-abhängig (nicht $HOME direkt), damit Tests in ihr tmp-home schreiben.
+#[cfg(target_os = "linux")]
+fn applications_dir(paths: &Paths) -> PathBuf {
+ paths.home.join(".local/share/applications")
+}
+
+/// Schreibt/aktualisiert die .desktop eines Projekts: dash-to-dock ordnet dem
+/// Fenster (app_id `aicontrol-`, via set_prgname) darüber das Projekt-
+/// Icon zu. NoDisplay=true hält den App-Starter sauber.
+#[cfg(target_os = "linux")]
+pub(crate) fn write_terminal_desktop(paths: &Paths, project: &str, cfg: &TerminalConfig) {
+ let dir = applications_dir(paths);
+ if fs::create_dir_all(&dir).is_err() {
+ return;
+ }
+ let exec = std::env::current_exe()
+ .ok()
+ .and_then(|p| p.to_str().map(str::to_string))
+ .unwrap_or_else(|| "ai-control".into());
+ let icon_line = cfg
+ .icon
+ .as_deref()
+ .map(|i| resolve_icon_path(paths, i))
+ .filter(|p| p.exists())
+ .and_then(|p| p.to_str().map(str::to_string))
+ .map(|p| format!("Icon={p}\n"))
+ .unwrap_or_default();
+ let content = format!(
+ "[Desktop Entry]\nType=Application\nName={project}\nExec={exec} --terminal {project}\n\
+ {icon_line}StartupWMClass=aicontrol-{project}\nNoDisplay=true\n"
+ );
+ let _ = fs::write(dir.join(format!("aicontrol-{project}.desktop")), content);
+}
+
+/// Entfernt die .desktop eines Projekts (beim Löschen).
+#[cfg(target_os = "linux")]
+pub(crate) fn remove_terminal_desktop(paths: &Paths, project: &str) {
+ let _ = fs::remove_file(applications_dir(paths).join(format!("aicontrol-{project}.desktop")));
+}
+
+/// Beim App-Start: für jedes registrierte Projekt die .desktop neu schreiben und
+/// verwaiste (kein registriertes Projekt mehr) entfernen. Dadurch sind sie immer
+/// vorhanden und aktuell, bevor überhaupt ein Terminal startet.
+#[cfg(target_os = "linux")]
+fn sync_all_desktops(paths: &Paths) {
+ let Ok(reg) = load_registry(paths) else {
+ return;
+ };
+ for project in reg.keys() {
+ let cfg = read_project_config_in(paths, project)
+ .map(|c| c.terminal)
+ .unwrap_or_default();
+ write_terminal_desktop(paths, project, &cfg);
+ }
+ let dir = applications_dir(paths);
+ if let Ok(entries) = fs::read_dir(&dir) {
+ for e in entries.flatten() {
+ let name = e.file_name();
+ let Some(name) = name.to_str() else { continue };
+ if let Some(project) = name
+ .strip_prefix("aicontrol-")
+ .and_then(|n| n.strip_suffix(".desktop"))
+ {
+ if !reg.contains_key(project) {
+ let _ = fs::remove_file(e.path());
+ }
+ }
+ }
+ }
+}
+
/// Icon eines Projekts als PNG-data-URL für die Übersicht; ICNS wird per
/// sips nach PNG konvertiert, weil der Browser ICNS nicht rendert.
#[tauri::command]
@@ -837,20 +925,7 @@ fn project_icon(project: String) -> Result