Repository: tiiuae/ghaf-ctrl-panel
Pull request #13: Fix crash on bug report submission
Author: login: slakkala
Created: 2025-03-26T12:17:37Z | Merged: 2025-04-23T11:52:48Z

Move tokio runtime needed for octocrab usage into it's own thread, so it does not block UI; further refinements into bug reporting code to allow authentication cancellation and remove reliance on external dialog program.


Diff:
diff --git a/Cargo.lock b/Cargo.lock
index 0b4cce8..90a2eee 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -93,9 +93,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.95"
+version = "1.0.98"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
+checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
 
 [[package]]
 name = "arc-swap"
@@ -103,12 +103,6 @@ version = "1.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
 
-[[package]]
-name = "arraydeque"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
-
 [[package]]
 name = "async-broadcast"
 version = "0.7.2"
@@ -164,13 +158,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059"
 dependencies = [
  "async-lock",
- "cfg-if 1.0.0",
+ "cfg-if",
  "concurrent-queue",
  "futures-io",
  "futures-lite",
  "parking",
  "polling",
- "rustix",
+ "rustix 0.38.44",
  "slab",
  "tracing",
  "windows-sys 0.59.0",
@@ -199,10 +193,10 @@ dependencies = [
  "async-signal",
  "async-task",
  "blocking",
- "cfg-if 1.0.0",
+ "cfg-if",
  "event-listener",
  "futures-lite",
- "rustix",
+ "rustix 0.38.44",
  "tracing",
 ]
 
@@ -226,10 +220,10 @@ dependencies = [
  "async-io",
  "async-lock",
  "atomic-waker",
- "cfg-if 1.0.0",
+ "cfg-if",
  "futures-core",
  "futures-io",
- "rustix",
+ "rustix 0.38.44",
  "signal-hook-registry",
  "slab",
  "windows-sys 0.59.0",
@@ -265,9 +259,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
 
 [[package]]
 name = "async-trait"
-version = "0.1.86"
+version = "0.1.88"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d"
+checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -288,16 +282,15 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
 
 [[package]]
 name = "axum"
-version = "0.7.9"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
+checksum = "de45108900e1f9b9242f7f2e254aa3e2c029c921c258fe9e6b4217eeebd54288"
 dependencies = [
- "async-trait",
  "axum-core",
  "bytes",
  "futures-util",
- "http 1.2.0",
- "http-body 1.0.1",
+ "http",
+ "http-body",
  "http-body-util",
  "itoa",
  "matchit",
@@ -308,22 +301,21 @@ dependencies = [
  "rustversion",
  "serde",
  "sync_wrapper",
- "tower 0.5.2",
+ "tower",
  "tower-layer",
  "tower-service",
 ]
 
 [[package]]
 name = "axum-core"
-version = "0.4.5"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
+checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6"
 dependencies = [
- "async-trait",
  "bytes",
- "futures-util",
- "http 1.2.0",
- "http-body 1.0.1",
+ "futures-core",
+ "http",
+ "http-body",
  "http-body-util",
  "mime",
  "pin-project-lite",
@@ -340,7 +332,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
 dependencies = [
  "addr2line",
- "cfg-if 1.0.0",
+ "cfg-if",
  "libc",
  "miniz_oxide",
  "object",
@@ -348,12 +340,6 @@ dependencies = [
  "windows-targets",
 ]
 
-[[package]]
-name = "base64"
-version = "0.21.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
-
 [[package]]
 name = "base64"
 version = "0.22.1"
@@ -362,27 +348,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
 
 [[package]]
 name = "bitflags"
-version = "2.8.0"
+version = "2.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "block"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
-
-[[package]]
-name = "block-buffer"
-version = "0.10.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
-dependencies = [
- "generic-array",
-]
+checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
 
 [[package]]
 name = "blocking"
@@ -403,17 +371,11 @@ version = "3.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
 
-[[package]]
-name = "byteorder"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
-
 [[package]]
 name = "bytes"
-version = "1.10.0"
+version = "1.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
+checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
 
 [[package]]
 name = "cairo-rs"
@@ -440,9 +402,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.2.14"
+version = "1.2.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9"
+checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362"
 dependencies = [
  "shlex",
 ]
@@ -457,12 +419,6 @@ dependencies = [
  "target-lexicon",
 ]
 
-[[package]]
-name = "cfg-if"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
-
 [[package]]
 name = "cfg-if"
 version = "1.0.0"
@@ -545,65 +501,20 @@ dependencies = [
  "crossbeam-utils",
 ]
 
-[[package]]
-name = "config"
-version = "0.15.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "595aae20e65c3be792d05818e8c63025294ac3cb7e200f11459063a352a6ef80"
-dependencies = [
- "async-trait",
- "convert_case",
- "json5",
- "pathdiff",
- "ron",
- "rust-ini",
- "serde",
- "serde_json",
- "toml",
- "winnow",
- "yaml-rust2",
-]
-
-[[package]]
-name = "const-random"
-version = "0.1.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
-dependencies = [
- "const-random-macro",
-]
-
-[[package]]
-name = "const-random-macro"
-version = "0.1.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
-dependencies = [
- "getrandom 0.2.15",
- "once_cell",
- "tiny-keccak",
-]
-
 [[package]]
 name = "control_panel_gui"
 version = "0.1.0"
 dependencies = [
  "async-channel",
- "base64 0.22.1",
  "chrono",
  "clap",
- "config",
- "dbus",
- "dialog",
  "env_logger",
  "futures",
- "gettext-rs",
  "givc-client",
  "givc-common",
  "glib-build-tools",
  "gtk4",
- "http 1.2.0",
- "hyper 0.14.32",
+ "http",
  "libadwaita",
  "log",
  "octocrab",
@@ -612,20 +523,12 @@ dependencies = [
  "serde",
  "serde_json",
  "syslog",
+ "thiserror",
  "tokio",
- "toml_edit",
+ "toml",
  "zbus",
 ]
 
-[[package]]
-name = "convert_case"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
-dependencies = [
- "unicode-segmentation",
-]
-
 [[package]]
 name = "core-foundation"
 version = "0.10.0"
@@ -642,98 +545,21 @@ version = "0.8.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
 
-[[package]]
-name = "cpufeatures"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
-dependencies = [
- "libc",
-]
-
 [[package]]
 name = "crossbeam-utils"
 version = "0.8.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
 
-[[package]]
-name = "crunchy"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
-
-[[package]]
-name = "crypto-common"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
-dependencies = [
- "generic-array",
- "typenum",
-]
-
-[[package]]
-name = "dbus"
-version = "0.9.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b"
-dependencies = [
- "libc",
- "libdbus-sys",
- "winapi 0.3.9",
-]
-
 [[package]]
 name = "deranged"
-version = "0.3.11"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
 dependencies = [
  "powerfmt",
 ]
 
-[[package]]
-name = "dialog"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "736bab36d647d14c985725a57a4110a1182c6852104536cd42f1c97e96d29bf0"
-dependencies = [
- "dirs",
- "rpassword",
-]
-
-[[package]]
-name = "digest"
-version = "0.10.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
-dependencies = [
- "block-buffer",
- "crypto-common",
-]
-
-[[package]]
-name = "dirs"
-version = "2.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
-dependencies = [
- "cfg-if 0.1.10",
- "dirs-sys",
-]
-
-[[package]]
-name = "dirs-sys"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
-dependencies = [
- "libc",
- "redox_users",
- "winapi 0.3.9",
-]
-
 [[package]]
 name = "displaydoc"
 version = "0.2.5"
@@ -745,29 +571,11 @@ dependencies = [
  "syn",
 ]
 
-[[package]]
-name = "dlv-list"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f"
-dependencies = [
- "const-random",
-]
-
 [[package]]
 name = "either"
-version = "1.13.0"
+version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
-
-[[package]]
-name = "encoding_rs"
-version = "0.8.35"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
-dependencies = [
- "cfg-if 1.0.0",
-]
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
 
 [[package]]
 name = "endi"
@@ -827,9 +635,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
 
 [[package]]
 name = "errno"
-version = "0.3.10"
+version = "0.3.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
+checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
 dependencies = [
  "libc",
  "windows-sys 0.59.0",
@@ -848,9 +656,9 @@ dependencies = [
 
 [[package]]
 name = "event-listener-strategy"
-version = "0.5.3"
+version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2"
+checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
 dependencies = [
  "event-listener",
  "pin-project-lite",
@@ -884,12 +692,6 @@ version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
-[[package]]
-name = "foldhash"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
-
 [[package]]
 name = "form_urlencoded"
 version = "1.2.1"
@@ -1058,23 +860,13 @@ dependencies = [
  "system-deps",
 ]
 
-[[package]]
-name = "generic-array"
-version = "0.14.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
-dependencies = [
- "typenum",
- "version_check",
-]
-
 [[package]]
 name = "getrandom"
 version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "js-sys",
  "libc",
  "wasi 0.11.0+wasi-snapshot-preview1",
@@ -1083,34 +875,14 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.3.1"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
+checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "libc",
- "wasi 0.13.3+wasi-0.2.2",
- "windows-targets",
-]
-
-[[package]]
-name = "gettext-rs"
-version = "0.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a44e92f7dc08430aca7ed55de161253a22276dfd69c5526e5c5e95d1f7cf338a"
-dependencies = [
- "gettext-sys",
- "locale_config",
-]
-
-[[package]]
-name = "gettext-sys"
-version = "0.22.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb45773f5b8945f12aecd04558f545964f943dacda1b1155b3d738f5469ef661"
-dependencies = [
- "cc",
- "temp-dir",
+ "r-efi",
+ "wasi 0.14.2+wasi-0.2.4",
 ]
 
 [[package]]
@@ -1152,7 +924,7 @@ dependencies = [
 [[package]]
 name = "givc-client"
 version = "0.0.1"
-source = "git+https://github.com/tiiuae/ghaf-givc?branch=main#07a4cce5f6d2479c053ef82c21d156f353ed815f"
+source = "git+https://github.com/tiiuae/ghaf-givc?branch=main#129ed30717176ecf9eab833b94358de25eb780e6"
 dependencies = [
  "anyhow",
  "async-channel",
@@ -1165,14 +937,14 @@ dependencies = [
  "tokio-vsock",
  "tonic",
  "tonic-types",
- "tower 0.4.13",
+ "tower",
  "tracing",
 ]
 
 [[package]]
 name = "givc-common"
 version = "0.0.1"
-source = "git+https://github.com/tiiuae/ghaf-givc?branch=main#07a4cce5f6d2479c053ef82c21d156f353ed815f"
+source = "git+https://github.com/tiiuae/ghaf-givc?branch=main#129ed30717176ecf9eab833b94358de25eb780e6"
 dependencies = [
  "anyhow",
  "async-stream",
@@ -1361,71 +1133,28 @@ dependencies = [
 
 [[package]]
 name = "h2"
-version = "0.3.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
-dependencies = [
- "bytes",
- "fnv",
- "futures-core",
- "futures-sink",
- "futures-util",
- "http 0.2.12",
- "indexmap 2.7.1",
- "slab",
- "tokio",
- "tokio-util",
- "tracing",
-]
-
-[[package]]
-name = "h2"
-version = "0.4.8"
+version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2"
+checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633"
 dependencies = [
  "atomic-waker",
  "bytes",
  "fnv",
  "futures-core",
  "futures-sink",
- "http 1.2.0",
- "indexmap 2.7.1",
+ "http",
+ "indexmap",
  "slab",
  "tokio",
  "tokio-util",
  "tracing",
 ]
 
-[[package]]
-name = "hashbrown"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
-
-[[package]]
-name = "hashbrown"
-version = "0.14.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
-
 [[package]]
 name = "hashbrown"
 version = "0.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
-dependencies = [
- "foldhash",
-]
-
-[[package]]
-name = "hashlink"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
-dependencies = [
- "hashbrown 0.15.2",
-]
 
 [[package]]
 name = "heck"
@@ -1447,48 +1176,26 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
 
 [[package]]
 name = "hostname"
-version = "0.4.0"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba"
+checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "libc",
- "windows",
-]
-
-[[package]]
-name = "http"
-version = "0.2.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
-dependencies = [
- "bytes",
- "fnv",
- "itoa",
+ "windows-link",
 ]
 
 [[package]]
 name = "http"
-version = "1.2.0"
+version = "1.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
+checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
 dependencies = [
  "bytes",
  "fnv",
  "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"
@@ -1496,27 +1203,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
 dependencies = [
  "bytes",
- "http 1.2.0",
+ "http",
 ]
 
 [[package]]
 name = "http-body-util"
-version = "0.1.2"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
+checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
 dependencies = [
  "bytes",
- "futures-util",
- "http 1.2.0",
- "http-body 1.0.1",
+ "futures-core",
+ "http",
+ "http-body",
  "pin-project-lite",
 ]
 
 [[package]]
 name = "httparse"
-version = "1.10.0"
+version = "1.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a"
+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
 
 [[package]]
 name = "httpdate"
@@ -1524,30 +1231,6 @@ 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 0.3.26",
- "http 0.2.12",
- "http-body 0.4.6",
- "httparse",
- "httpdate",
- "itoa",
- "pin-project-lite",
- "socket2",
- "tokio",
- "tower-service",
- "tracing",
- "want",
-]
-
 [[package]]
 name = "hyper"
 version = "1.6.0"
@@ -1557,9 +1240,9 @@ dependencies = [
  "bytes",
  "futures-channel",
  "futures-util",
- "h2 0.4.8",
- "http 1.2.0",
- "http-body 1.0.1",
+ "h2",
+ "http",
+ "http-body",
  "httparse",
  "httpdate",
  "itoa",
@@ -1576,8 +1259,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
 dependencies = [
  "futures-util",
- "http 1.2.0",
- "hyper 1.6.0",
+ "http",
+ "hyper",
  "hyper-util",
  "log",
  "rustls",
@@ -1594,7 +1277,7 @@ version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
 dependencies = [
- "hyper 1.6.0",
+ "hyper",
  "hyper-util",
  "pin-project-lite",
  "tokio",
@@ -1603,16 +1286,17 @@ dependencies = [
 
 [[package]]
 name = "hyper-util"
-version = "0.1.10"
+version = "0.1.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
+checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2"
 dependencies = [
  "bytes",
  "futures-channel",
  "futures-util",
- "http 1.2.0",
- "http-body 1.0.1",
- "hyper 1.6.0",
+ "http",
+ "http-body",
+ "hyper",
+ "libc",
  "pin-project-lite",
  "socket2",
  "tokio",
@@ -1622,14 +1306,15 @@ dependencies = [
 
 [[package]]
 name = "iana-time-zone"
-version = "0.1.61"
+version = "0.1.63"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
+checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
 dependencies = [
  "android_system_properties",
  "core-foundation-sys",
  "iana-time-zone-haiku",
  "js-sys",
+ "log",
  "wasm-bindgen",
  "windows-core",
 ]
@@ -1684,9 +1369,9 @@ dependencies = [
 
 [[package]]
 name = "icu_locid_transform_data"
-version = "1.5.0"
+version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
+checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d"
 
 [[package]]
 name = "icu_normalizer"
@@ -1708,9 +1393,9 @@ dependencies = [
 
 [[package]]
 name = "icu_normalizer_data"
-version = "1.5.0"
+version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
+checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7"
 
 [[package]]
 name = "icu_properties"
@@ -1729,9 +1414,9 @@ dependencies = [
 
 [[package]]
 name = "icu_properties_data"
-version = "1.5.0"
+version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
+checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2"
 
 [[package]]
 name = "icu_provider"
@@ -1784,29 +1469,19 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "1.9.3"
+version = "2.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
-dependencies = [
- "autocfg",
- "hashbrown 0.12.3",
-]
-
-[[package]]
-name = "indexmap"
-version = "2.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
+checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
 dependencies = [
  "equivalent",
- "hashbrown 0.15.2",
+ "hashbrown",
 ]
 
 [[package]]
 name = "iri-string"
-version = "0.7.7"
+version = "0.7.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc0f0a572e8ffe56e2ff4f769f32ffe919282c3916799f8b68688b6030063bea"
+checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2"
 dependencies = [
  "memchr",
  "serde",
@@ -1829,15 +1504,15 @@ dependencies = [
 
 [[package]]
 name = "itoa"
-version = "1.0.14"
+version = "1.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
+checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
 
 [[package]]
 name = "jiff"
-version = "0.2.5"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260"
+checksum = "5a064218214dc6a10fbae5ec5fa888d80c45d611aba169222fc272072bf7aef6"
 dependencies = [
  "jiff-static",
  "log",
@@ -1848,9 +1523,9 @@ dependencies = [
 
 [[package]]
 name = "jiff-static"
-version = "0.2.5"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c"
+checksum = "199b7932d97e325aff3a7030e141eafe7f2c6268e1d1b24859b753a627f45254"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1867,24 +1542,13 @@ dependencies = [
  "wasm-bindgen",
 ]
 
-[[package]]
-name = "json5"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1"
-dependencies = [
- "pest",
- "pest_derive",
- "serde",
-]
-
 [[package]]
 name = "jsonwebtoken"
 version = "9.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde"
 dependencies = [
- "base64 0.22.1",
+ "base64",
  "js-sys",
  "pem",
  "ring",
@@ -1893,16 +1557,6 @@ dependencies = [
  "simple_asn1",
 ]
 
-[[package]]
-name = "kernel32-sys"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
-dependencies = [
- "winapi 0.2.8",
- "winapi-build",
-]
-
 [[package]]
 name = "lazy_static"
 version = "1.5.0"
@@ -1942,28 +1596,9 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.169"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
-
-[[package]]
-name = "libdbus-sys"
-version = "0.2.5"
+version = "0.2.172"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72"
-dependencies = [
- "pkg-config",
-]
-
-[[package]]
-name = "libredox"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
-dependencies = [
- "bitflags",
- "libc",
-]
+checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
 
 [[package]]
 name = "linux-raw-sys"
@@ -1972,44 +1607,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
 
 [[package]]
-name = "litemap"
-version = "0.7.4"
+name = "linux-raw-sys"
+version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
+checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
 
 [[package]]
-name = "locale_config"
-version = "0.3.0"
+name = "litemap"
+version = "0.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08d2c35b16f4483f6c26f0e4e9550717a2f6575bcd6f12a53ff0c490a94a6934"
-dependencies = [
- "lazy_static",
- "objc",
- "objc-foundation",
- "regex",
- "winapi 0.3.9",
-]
+checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
 
 [[package]]
 name = "log"
 version = "0.4.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
-
-[[package]]
-name = "malloc_buf"
-version = "0.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
-dependencies = [
- "libc",
-]
+checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
 
 [[package]]
 name = "matchit"
-version = "0.7.3"
+version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
+checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
 
 [[package]]
 name = "memchr"
@@ -2034,9 +1653,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
 
 [[package]]
 name = "miniz_oxide"
-version = "0.8.4"
+version = "0.8.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b"
+checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
 dependencies = [
  "adler2",
 ]
@@ -2065,7 +1684,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
 dependencies = [
  "bitflags",
- "cfg-if 1.0.0",
+ "cfg-if",
  "cfg_aliases",
  "libc",
  "memoffset",
@@ -2078,7 +1697,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
 dependencies = [
  "overload",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
@@ -2124,35 +1743,6 @@ dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "objc"
-version = "0.2.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
-dependencies = [
- "malloc_buf",
-]
-
-[[package]]
-name = "objc-foundation"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
-dependencies = [
- "block",
- "objc",
- "objc_id",
-]
-
-[[package]]
-name = "objc_id"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
-dependencies = [
- "objc",
-]
-
 [[package]]
 name = "object"
 version = "0.36.7"
@@ -2164,23 +1754,23 @@ dependencies = [
 
 [[package]]
 name = "octocrab"
-version = "0.43.0"
+version = "0.44.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "27527d68322f4c603319f7958973db8f9fa4be62c0e3fafe084f5562cf6353df"
+checksum = "aaf799a9982a4d0b4b3fa15b4c1ff7daf5bd0597f46456744dcbb6ddc2e4c827"
 dependencies = [
  "arc-swap",
  "async-trait",
- "base64 0.22.1",
+ "base64",
  "bytes",
- "cfg-if 1.0.0",
+ "cfg-if",
  "chrono",
  "either",
  "futures",
  "futures-util",
- "http 1.2.0",
- "http-body 1.0.1",
+ "http",
+ "http-body",
  "http-body-util",
- "hyper 1.6.0",
+ "hyper",
  "hyper-rustls",
  "hyper-timeout",
  "hyper-util",
@@ -2195,7 +1785,7 @@ dependencies = [
  "serde_urlencoded",
  "snafu",
  "tokio",
- "tower 0.5.2",
+ "tower",
  "tower-http",
  "tracing",
  "url",
@@ -2204,9 +1794,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.20.3"
+version = "1.21.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
 
 [[package]]
 name = "openssl-probe"
@@ -2214,16 +1804,6 @@ version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
 
-[[package]]
-name = "ordered-multimap"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79"
-dependencies = [
- "dlv-list",
- "hashbrown 0.14.5",
-]
-
 [[package]]
 name = "ordered-stream"
 version = "0.2.0"
@@ -2270,19 +1850,13 @@ version = "2.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
 
-[[package]]
-name = "pathdiff"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
-
 [[package]]
 name = "pem"
-version = "3.0.4"
+version = "3.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae"
+checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3"
 dependencies = [
- "base64 0.22.1",
+ "base64",
  "serde",
 ]
 
@@ -2292,51 +1866,6 @@ version = "2.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
 
-[[package]]
-name = "pest"
-version = "2.7.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
-dependencies = [
- "memchr",
- "thiserror 2.0.11",
- "ucd-trie",
-]
-
-[[package]]
-name = "pest_derive"
-version = "2.7.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e"
-dependencies = [
- "pest",
- "pest_generator",
-]
-
-[[package]]
-name = "pest_generator"
-version = "2.7.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b"
-dependencies = [
- "pest",
- "pest_meta",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "pest_meta"
-version = "2.7.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea"
-dependencies = [
- "once_cell",
- "pest",
- "sha2",
-]
-
 [[package]]
 name = "petgraph"
 version = "0.7.1"
@@ -2344,23 +1873,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
 dependencies = [
  "fixedbitset",
- "indexmap 2.7.1",
+ "indexmap",
 ]
 
 [[package]]
 name = "pin-project"
-version = "1.1.9"
+version = "1.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d"
+checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
 dependencies = [
  "pin-project-internal",
 ]
 
 [[package]]
 name = "pin-project-internal"
-version = "1.1.9"
+version = "1.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67"
+checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2392,9 +1921,9 @@ dependencies = [
 
 [[package]]
 name = "pkg-config"
-version = "0.3.31"
+version = "0.3.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
 
 [[package]]
 name = "polling"
@@ -2402,11 +1931,11 @@ version = "3.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "concurrent-queue",
  "hermit-abi",
  "pin-project-lite",
- "rustix",
+ "rustix 0.38.44",
  "tracing",
  "windows-sys 0.59.0",
 ]
@@ -2432,20 +1961,11 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
 
-[[package]]
-name = "ppv-lite86"
-version = "0.2.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
-dependencies = [
- "zerocopy",
-]
-
 [[package]]
 name = "prettyplease"
-version = "0.2.29"
+version = "0.2.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac"
+checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6"
 dependencies = [
  "proc-macro2",
  "syn",
@@ -2462,9 +1982,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.93"
+version = "1.0.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
 dependencies = [
  "unicode-ident",
 ]
@@ -2523,53 +2043,18 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.38"
+version = "1.0.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
 dependencies = [
  "proc-macro2",
 ]
 
 [[package]]
-name = "rand"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
-dependencies = [
- "libc",
- "rand_chacha",
- "rand_core",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
-dependencies = [
- "getrandom 0.2.15",
-]
-
-[[package]]
-name = "redox_users"
-version = "0.4.6"
+name = "r-efi"
+version = "5.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
-dependencies = [
- "getrandom 0.2.15",
- "libredox",
- "thiserror 1.0.69",
-]
+checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
 
 [[package]]
 name = "regex"
@@ -2607,47 +2092,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
 dependencies = [
  "cc",
- "cfg-if 1.0.0",
+ "cfg-if",
  "getrandom 0.2.15",
  "libc",
  "untrusted",
  "windows-sys 0.52.0",
 ]
 
-[[package]]
-name = "ron"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
-dependencies = [
- "base64 0.21.7",
- "bitflags",
- "serde",
- "serde_derive",
-]
-
-[[package]]
-name = "rpassword"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d37473170aedbe66ffa3ad3726939ba677d83c646ad4fd99e5b4bc38712f45ec"
-dependencies = [
- "kernel32-sys",
- "libc",
- "winapi 0.2.8",
-]
-
-[[package]]
-name = "rust-ini"
-version = "0.21.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f"
-dependencies = [
- "cfg-if 1.0.0",
- "ordered-multimap",
- "trim-in-place",
-]
-
 [[package]]
 name = "rustc-demangle"
 version = "0.1.24"
@@ -2672,15 +2123,28 @@ dependencies = [
  "bitflags",
  "errno",
  "libc",
- "linux-raw-sys",
+ "linux-raw-sys 0.4.15",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "rustix"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.9.4",
  "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "rustls"
-version = "0.23.23"
+version = "0.23.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395"
+checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0"
 dependencies = [
  "log",
  "once_cell",
@@ -2703,15 +2167,6 @@ dependencies = [
  "security-framework",
 ]
 
-[[package]]
-name = "rustls-pemfile"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
-dependencies = [
- "rustls-pki-types",
-]
-
 [[package]]
 name = "rustls-pki-types"
 version = "1.11.0"
@@ -2720,9 +2175,9 @@ checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c"
 
 [[package]]
 name = "rustls-webpki"
-version = "0.102.8"
+version = "0.103.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
+checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03"
 dependencies = [
  "ring",
  "rustls-pki-types",
@@ -2731,15 +2186,15 @@ dependencies = [
 
 [[package]]
 name = "rustversion"
-version = "1.0.19"
+version = "1.0.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
+checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
 
 [[package]]
 name = "ryu"
-version = "1.0.19"
+version = "1.0.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
+checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
 
 [[package]]
 name = "schannel"
@@ -2756,6 +2211,7 @@ version = "0.10.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a"
 dependencies = [
+ "serde",
  "zeroize",
 ]
 
@@ -2784,9 +2240,9 @@ dependencies = [
 
 [[package]]
 name = "semver"
-version = "1.0.25"
+version = "1.0.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03"
+checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
 
 [[package]]
 name = "serde"
@@ -2822,9 +2278,9 @@ dependencies = [
 
 [[package]]
 name = "serde_path_to_error"
-version = "0.1.16"
+version = "0.1.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
+checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a"
 dependencies = [
  "itoa",
  "serde",
@@ -2832,9 +2288,9 @@ dependencies = [
 
 [[package]]
 name = "serde_repr"
-version = "0.1.19"
+version = "0.1.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
+checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2862,17 +2318,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "sha2"
-version = "0.10.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
-dependencies = [
- "cfg-if 1.0.0",
- "cpufeatures",
- "digest",
-]
-
 [[package]]
 name = "sharded-slab"
 version = "0.1.7"
@@ -2890,9 +2335,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
 
 [[package]]
 name = "signal-hook-registry"
-version = "1.4.2"
+version = "1.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
+checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
 dependencies = [
  "libc",
 ]
@@ -2905,7 +2350,7 @@ checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb"
 dependencies = [
  "num-bigint",
  "num-traits",
- "thiserror 2.0.11",
+ "thiserror",
  "time",
 ]
 
@@ -2920,9 +2365,9 @@ dependencies = [
 
 [[package]]
 name = "smallvec"
-version = "1.14.0"
+version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
+checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
 
 [[package]]
 name = "snafu"
@@ -2947,9 +2392,9 @@ dependencies = [
 
 [[package]]
 name = "socket2"
-version = "0.5.8"
+version = "0.5.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
+checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
 dependencies = [
  "libc",
  "windows-sys 0.52.0",
@@ -2975,18 +2420,18 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
 [[package]]
 name = "strum"
-version = "0.26.3"
+version = "0.27.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
+checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32"
 dependencies = [
  "strum_macros",
 ]
 
 [[package]]
 name = "strum_macros"
-version = "0.26.4"
+version = "0.27.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
+checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8"
 dependencies = [
  "heck",
  "proc-macro2",
@@ -3003,9 +2448,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
 
 [[package]]
 name = "syn"
-version = "2.0.98"
+version = "2.0.100"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
+checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3060,60 +2505,33 @@ version = "0.12.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
 
-[[package]]
-name = "temp-dir"
-version = "0.1.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc1ee6eef34f12f765cb94725905c6312b6610ab2b0940889cfe58dae7bc3c72"
-
 [[package]]
 name = "tempfile"
-version = "3.17.1"
+version = "3.19.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230"
+checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
 dependencies = [
- "cfg-if 1.0.0",
  "fastrand",
- "getrandom 0.3.1",
+ "getrandom 0.3.2",
  "once_cell",
- "rustix",
+ "rustix 1.0.5",
  "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "thiserror"
-version = "1.0.69"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
-dependencies = [
- "thiserror-impl 1.0.69",
-]
-
-[[package]]
-name = "thiserror"
-version = "2.0.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
-dependencies = [
- "thiserror-impl 2.0.11",
-]
-
-[[package]]
-name = "thiserror-impl"
-version = "1.0.69"
+version = "2.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
 dependencies = [
- "proc-macro2",
- "quote",
- "syn",
+ "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "2.0.11"
+version = "2.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
+checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3126,15 +2544,15 @@ version = "1.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "once_cell",
 ]
 
 [[package]]
 name = "time"
-version = "0.3.37"
+version = "0.3.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
+checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
 dependencies = [
  "deranged",
  "itoa",
@@ -3149,29 +2567,20 @@ dependencies = [
 
 [[package]]
 name = "time-core"
-version = "0.1.2"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
 
 [[package]]
 name = "time-macros"
-version = "0.2.19"
+version = "0.2.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
+checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
 dependencies = [
  "num-conv",
  "time-core",
 ]
 
-[[package]]
-name = "tiny-keccak"
-version = "2.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
-dependencies = [
- "crunchy",
-]
-
 [[package]]
 name = "tinystr"
 version = "0.7.6"
@@ -3211,9 +2620,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-rustls"
-version = "0.26.1"
+version = "0.26.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
+checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
 dependencies = [
  "rustls",
  "tokio",
@@ -3232,9 +2641,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-util"
-version = "0.7.13"
+version = "0.7.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
+checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034"
 dependencies = [
  "bytes",
  "futures-core",
@@ -3245,9 +2654,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-vsock"
-version = "0.7.0"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "074885a713a0e1e8f2cc6855a004c7c882572d980d4f8262523dc2b094c96da8"
+checksum = "1824fc0300433f400df6b6264a9ab00ba93f39d38c3157fb5f05183476c4af10"
 dependencies = [
  "bytes",
  "futures",
@@ -3283,7 +2692,7 @@ version = "0.22.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
 dependencies = [
- "indexmap 2.7.1",
+ "indexmap",
  "serde",
  "serde_spanned",
  "toml_datetime",
@@ -3292,31 +2701,29 @@ dependencies = [
 
 [[package]]
 name = "tonic"
-version = "0.12.3"
+version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52"
+checksum = "85839f0b32fd242bb3209262371d07feda6d780d16ee9d2bc88581b89da1549b"
 dependencies = [
- "async-stream",
  "async-trait",
  "axum",
- "base64 0.22.1",
+ "base64",
  "bytes",
- "h2 0.4.8",
- "http 1.2.0",
- "http-body 1.0.1",
+ "h2",
+ "http",
+ "http-body",
  "http-body-util",
- "hyper 1.6.0",
+ "hyper",
  "hyper-timeout",
  "hyper-util",
  "percent-encoding",
  "pin-project",
  "prost",
- "rustls-pemfile",
  "socket2",
  "tokio",
  "tokio-rustls",
  "tokio-stream",
- "tower 0.4.13",
+ "tower",
  "tower-layer",
  "tower-service",
  "tracing",
@@ -3324,9 +2731,9 @@ dependencies = [
 
 [[package]]
 name = "tonic-build"
-version = "0.12.3"
+version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11"
+checksum = "d85f0383fadd15609306383a90e85eaed44169f931a5d2be1b42c76ceff1825e"
 dependencies = [
  "prettyplease",
  "proc-macro2",
@@ -3338,35 +2745,15 @@ dependencies = [
 
 [[package]]
 name = "tonic-types"
-version = "0.12.3"
+version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0081d8ee0847d01271392a5aebe960a4600f5d4da6c67648a6382a0940f8b367"
+checksum = "9e9d18873cc637bb6e435125318958423f7b79a6c62544799e4ab34a74e74352"
 dependencies = [
  "prost",
  "prost-types",
  "tonic",
 ]
 
-[[package]]
-name = "tower"
-version = "0.4.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
-dependencies = [
- "futures-core",
- "futures-util",
- "indexmap 1.9.3",
- "pin-project",
- "pin-project-lite",
- "rand",
- "slab",
- "tokio",
- "tokio-util",
- "tower-layer",
- "tower-service",
- "tracing",
-]
-
 [[package]]
 name = "tower"
 version = "0.5.2"
@@ -3375,7 +2762,9 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
 dependencies = [
  "futures-core",
  "futures-util",
+ "indexmap",
  "pin-project-lite",
+ "slab",
  "sync_wrapper",
  "tokio",
  "tokio-util",
@@ -3393,11 +2782,11 @@ dependencies = [
  "bitflags",
  "bytes",
  "futures-util",
- "http 1.2.0",
- "http-body 1.0.1",
+ "http",
+ "http-body",
  "iri-string",
  "pin-project-lite",
- "tower 0.5.2",
+ "tower",
  "tower-layer",
  "tower-service",
  "tracing",
@@ -3473,30 +2862,12 @@ dependencies = [
  "tracing-log",
 ]
 
-[[package]]
-name = "trim-in-place"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc"
-
 [[package]]
 name = "try-lock"
 version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
 
-[[package]]
-name = "typenum"
-version = "1.18.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
-
-[[package]]
-name = "ucd-trie"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
-
 [[package]]
 name = "uds_windows"
 version = "1.1.0"
@@ -3505,20 +2876,14 @@ checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9"
 dependencies = [
  "memoffset",
  "tempfile",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
-
-[[package]]
-name = "unicode-segmentation"
-version = "1.12.0"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
 
 [[package]]
 name = "untrusted"
@@ -3568,12 +2933,6 @@ version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
 
-[[package]]
-name = "version_check"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
-
 [[package]]
 name = "vsock"
 version = "0.5.1"
@@ -3601,9 +2960,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasi"
-version = "0.13.3+wasi-0.2.2"
+version = "0.14.2+wasi-0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
+checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
 dependencies = [
  "wit-bindgen-rt",
 ]
@@ -3614,7 +2973,7 @@ version = "0.2.100"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "once_cell",
  "rustversion",
  "wasm-bindgen-macro",
@@ -3677,12 +3036,6 @@ dependencies = [
  "wasm-bindgen",
 ]
 
-[[package]]
-name = "winapi"
-version = "0.2.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
-
 [[package]]
 name = "winapi"
 version = "0.3.9"
@@ -3693,12 +3046,6 @@ dependencies = [
  "winapi-x86_64-pc-windows-gnu",
 ]
 
-[[package]]
-name = "winapi-build"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
-
 [[package]]
 name = "winapi-i686-pc-windows-gnu"
 version = "0.4.0"
@@ -3712,22 +3059,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
-name = "windows"
-version = "0.52.0"
+name = "windows-core"
+version = "0.61.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
+checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
 dependencies = [
- "windows-core",
- "windows-targets",
+ "windows-implement",
+ "windows-interface",
+ "windows-link",
+ "windows-result",
+ "windows-strings",
 ]
 
 [[package]]
-name = "windows-core"
-version = "0.52.0"
+name = "windows-implement"
+version = "0.60.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
 dependencies = [
- "windows-targets",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
 ]
 
 [[package]]
@@ -3736,6 +3099,24 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
 
+[[package]]
+name = "windows-result"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
+dependencies = [
+ "windows-link",
+]
+
 [[package]]
 name = "windows-sys"
 version = "0.52.0"
@@ -3820,18 +3201,18 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
 
 [[package]]
 name = "winnow"
-version = "0.7.2"
+version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603"
+checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
 name = "wit-bindgen-rt"
-version = "0.33.0"
+version = "0.39.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
+checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
 dependencies = [
  "bitflags",
 ]
@@ -3858,17 +3239,6 @@ dependencies = [
  "windows-sys 0.59.0",
 ]
 
-[[package]]
-name = "yaml-rust2"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "818913695e83ece1f8d2a1c52d54484b7b46d0f9c06beeb2649b9da50d9b512d"
-dependencies = [
- "arraydeque",
- "encoding_rs",
- "hashlink",
-]
-
 [[package]]
 name = "yoke"
 version = "0.7.5"
@@ -3956,41 +3326,20 @@ dependencies = [
  "zvariant",
 ]
 
-[[package]]
-name = "zerocopy"
-version = "0.7.35"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
-dependencies = [
- "byteorder",
- "zerocopy-derive",
-]
-
-[[package]]
-name = "zerocopy-derive"
-version = "0.7.35"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
 [[package]]
 name = "zerofrom"
-version = "0.1.5"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
+checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
 dependencies = [
  "zerofrom-derive",
 ]
 
 [[package]]
 name = "zerofrom-derive"
-version = "0.1.5"
+version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
+checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/Cargo.toml b/Cargo.toml
index 5723b02..9e2fec2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,32 +5,25 @@ edition = "2021"
 default-run = "ctrl-panel"
 
 [dependencies]
-gettext-rs = { version = "0.7", features = ["gettext-system"] }
-gtk = { version = "0.9.6", package = "gtk4" }
 adw = { version = "0.7.2", package = "libadwaita", features = ["v1_4"] }
-clap = {version = "4.5.37", features = ["derive", "env"]}
-regex = "1"
-dbus = "0.9"
-zbus = "5.5"
+async-channel = "2.3.1"
+chrono = "0.4"
+clap = {version = "4.5.37", features = ["derive", "env"] }
+env_logger = "0.11.8"
 futures = "0.3"
+gtk = { version = "0.9.6", package = "gtk4" }
+http = "1.0.0"
 log = "0.4.27"
-syslog = "7.0.0"
-
-env_logger = "0.11.8"
-tokio = {version = "1.44", features = ["rt", "time", "macros"]}
-async-channel = "2.3.1"
-
+octocrab = "0.44"
+regex = "1"
+secrecy = { version = "0.10.3", features = ["serde"] }
 serde_json = "1.0"
-base64 = "0.22"
-chrono = "0.4"
-octocrab = "0.43"
-hyper = { version = "0.14", features = ["full"] }
 serde = { version = "1.0", features = ["derive"] }
-config = "0.15"
-http = "1.0.0"
-secrecy = "0.10.3"
-dialog = "0.3.0"
-toml_edit = "0.22.24"
+syslog = "7.0.0"
+thiserror = "2.0.12"
+tokio = { version = "1.44", features = ["rt", "time", "macros"] }
+toml = "0.8.20"
+zbus = "5.5"
 
 givc-client = { git = "https://github.com/tiiuae/ghaf-givc", branch = "main" }
 givc-common = { git = "https://github.com/tiiuae/ghaf-givc", branch = "main" }
diff --git a/src/audio_settings.rs b/src/audio_settings.rs
index 46eaa09..ce5c291 100644
--- a/src/audio_settings.rs
+++ b/src/audio_settings.rs
@@ -261,9 +261,14 @@ impl AudioSettings {
                 }
             }),
         );*/
-        output_model.connect_items_changed(
-            glib::clone!(#[strong(rename_to = widget)] self, move |selection_model, position, removed, added| {
-                println!("Output model: Items changed at position {}, removed: {}, added: {}", position, removed, added);
+        output_model.connect_items_changed(glib::clone!(
+            #[strong(rename_to = widget)]
+            self,
+            move |selection_model, position, removed, added| {
+                println!(
+                    "Output model: Items changed at position {}, removed: {}, added: {}",
+                    position, removed, added
+                );
                 if let Some(selected_item) = selection_model.selected_item() {
                     if let Some(obj) = selected_item.downcast_ref::<AudioDeviceGObject>() {
                         widget.bind_speaker_volume_property(obj);
@@ -271,8 +276,8 @@ impl AudioSettings {
                 } else {
                     println!("No item selected");
                 }
-            })
-        );
+            }
+        ));
         /*input_model.connect_selection_changed(
             glib::clone!(@strong self as widget => move |selection_model, _, _| {
                 if let Some(selected_item) = selection_model.selected_item() {
@@ -285,9 +290,14 @@ impl AudioSettings {
                 }
             }),
         );*/
-        input_model.connect_items_changed(
-            glib::clone!(#[strong(rename_to = widget)] self, move |selection_model, position, removed, added| {
-                println!("Input model: Items changed at position {}, removed: {}, added: {}", position, removed, added);
+        input_model.connect_items_changed(glib::clone!(
+            #[strong(rename_to = widget)]
+            self,
+            move |selection_model, position, removed, added| {
+                println!(
+                    "Input model: Items changed at position {}, removed: {}, added: {}",
+                    position, removed, added
+                );
                 if let Some(selected_item) = selection_model.selected_item() {
                     if let Some(obj) = selected_item.downcast_ref::<AudioDeviceGObject>() {
                         widget.bind_mic_volume_property(obj);
@@ -295,8 +305,8 @@ impl AudioSettings {
                 } else {
                     println!("No item selected");
                 }
-            })
-        );
+            }
+        ));
 
         self.imp().speaker_switch.set_model(Some(&output_model));
         self.imp().mic_switch.set_model(Some(&input_model));
diff --git a/src/bug_report_settings_page.rs b/src/bug_report_settings_page.rs
index e691043..6dc9a36 100644
--- a/src/bug_report_settings_page.rs
+++ b/src/bug_report_settings_page.rs
@@ -4,7 +4,9 @@ use glib::subclass::Signal;
 use glib::{Binding, Properties};
 use gtk::prelude::*;
 use gtk::subclass::prelude::*;
-use gtk::{glib, Box, CheckButton, CompositeTemplate, Entry, Label, TextBuffer, TextView};
+use gtk::{
+    glib, Box, CheckButton, CompositeTemplate, Entry, Label, StateFlags, TextBuffer, TextView,
+};
 use std::cell::RefCell;
 use std::fs;
 use std::process::{Command, Stdio};
@@ -43,6 +45,8 @@ mod imp {
         #[template_child]
         pub description_textbuffer: TemplateChild<TextBuffer>,
         #[template_child]
+        pub placeholder_textbuffer: TemplateChild<TextBuffer>,
+        #[template_child]
         pub other_1: TemplateChild<CheckButton>,
         #[template_child]
         pub other_2: TemplateChild<CheckButton>,
@@ -66,7 +70,6 @@ mod imp {
         #[property(name = "issue", get, set, type = String, member = issue)]
         #[property(name = "related", get, set, type = String, member = related)]
         #[property(name = "app", get, set, type = String, member = app)]
-        #[property(name = "descriptionempty", get, set, type = bool, member = description_empty)]
         pub answers: RefCell<Answers>,
     }
 
@@ -169,14 +172,19 @@ mod imp {
         }
 
         #[template_callback]
-        fn on_description_changed(&self) {
-            const DEFAULT_TEXT: &'static str = "Enter your answer";
-            let desc = self.get_description_text();
-            if desc.trim() == DEFAULT_TEXT {
+        fn on_description_focus_changed(&self) {
+            if self.description.has_focus() {
+                self.description
+                    .set_buffer(Some(&self.description_textbuffer.get()));
                 self.description.remove_css_class("description-deactive");
                 self.description.add_css_class("description-active");
-                self.description_textbuffer.set_text("");
-                self.obj().set_property("descriptionempty", true);
+            } else if self.description_textbuffer.start_iter()
+                == self.description_textbuffer.end_iter()
+            {
+                self.description
+                    .set_buffer(Some(&self.placeholder_textbuffer.get()));
+                self.description.remove_css_class("description-active");
+                self.description.add_css_class("description-deactive");
             }
         }
 
@@ -185,11 +193,8 @@ mod imp {
             let mut enable = true;
             let device_id_path = "/etc/common/device-id";
             let title = self.title.text().to_string();
-            let description = if self.obj().property::<bool>("descriptionempty") {
-                self.get_description_text()
-            } else {
-                String::new()
-            };
+            let this = self.obj().clone();
+            let description = self.get_description_text();
 
             let time = Utc::now().to_string();
 
@@ -273,29 +278,41 @@ mod imp {
                 email_body.push_str(&format!("Description:\n{}\n", description));
 
                 let email_title = format!("{}: {}", issue, title);
-
-                let rt = tokio::runtime::Runtime::new().unwrap();
-                match rt.block_on(create_github_issue(&email_title, &email_body)) {
-                    Ok(_) => {
-                        self.summary.set_label("Report sent successfully");
-                        self.summary.remove_css_class("required-text");
-                        self.summary.add_css_class("success-text");
-                        self.summary.set_visible(true);
-                    }
-                    Err(e) => {
-                        if e.downcast_ref::<octocrab::Error>().is_none() {
-                            self.summary
-                                .set_label(format!("Error when sending report: {}", e).as_str());
-                        } else {
-                            self.summary.set_label("Error when sending report");
+                let (tx, rx) = async_channel::bounded(1);
+
+                std::thread::spawn(move || {
+                    let rt = tokio::runtime::Runtime::new().unwrap();
+                    let _ =
+                        tx.send_blocking(rt.block_on(create_github_issue(email_title, email_body)));
+                });
+
+                gtk::glib::spawn_future_local(async move {
+                    let this = this.imp();
+
+                    match rx.recv().await {
+                        Ok(Ok(issue)) => {
+                            eprintln!("Issue {} created", issue.url);
+                            this.summary.set_label("Report sent successfully");
+                            this.summary.remove_css_class("required-text");
+                            this.summary.add_css_class("success-text");
+                            this.summary.set_visible(true);
+                        }
+                        e => {
+                            eprintln!("Issue sending failed: {e:?}");
+                            if let Ok(Err(e)) = e {
+                                this.summary
+                                    .set_label(&format!("Error when sending report{e}"));
+                            } else {
+                                this.summary
+                                    .set_label(&format!("Error when sending report"));
+                            }
+
+                            this.summary.remove_css_class("success-text");
+                            this.summary.add_css_class("required-text");
+                            this.summary.set_visible(true);
                         }
-
-                        self.summary.remove_css_class("success-text");
-                        self.summary.add_css_class("required-text");
-                        self.summary.set_visible(true);
-                        return;
                     }
-                }
+                });
             }
         }
 
diff --git a/src/github.rs b/src/github.rs
index 8c90e3d..c7e2fe9 100644
--- a/src/github.rs
+++ b/src/github.rs
@@ -2,33 +2,63 @@
  * Based on https://github.com/vadika/rust-bugreporter
  */
 
-use dialog::DialogBox;
+use adw::prelude::*;
+use futures::FutureExt;
 use http::header::ACCEPT;
-use octocrab::Octocrab;
-use secrecy::{ExposeSecret, SecretString};
-use serde::Deserialize;
-use std::io::Write;
-use std::path::PathBuf;
+use octocrab::{models::issues::Issue, Octocrab};
+use secrecy::{ExposeSecret, SecretBox, SecretString};
+use serde::{Deserialize, Serialize, Serializer};
+use std::path::{Path, PathBuf};
 use std::sync::Mutex;
 use std::{env, time::Duration};
-use toml_edit::{value, DocumentMut};
+use thiserror::Error as ThisError;
+
+#[derive(ThisError, Debug)]
+pub enum Error {
+    Cancelled,
+    TimedOut,
+    NotAuthenticated,
+    #[error(transparent)]
+    Io(#[from] std::io::Error),
+    #[error(transparent)]
+    Octocrab(#[from] octocrab::Error),
+    #[error(transparent)]
+    Var(#[from] std::env::VarError),
+    #[error(transparent)]
+    Channel(#[from] async_channel::RecvError),
+    #[error(transparent)]
+    TomlDe(#[from] toml::de::Error),
+    #[error(transparent)]
+    TomlSer(#[from] toml::ser::Error),
+}
+
+impl std::fmt::Display for Error {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+        match self {
+            Error::Cancelled => write!(f, ": Authentication cancelled"),
+            Error::TimedOut => write!(f, ": Authentication timed out"),
+            _ => Ok(()),
+        }
+    }
+}
 
-#[derive(Debug, Deserialize, Clone)]
+fn expose<S>(t: &Option<SecretString>, s: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    s.serialize_str(t.as_ref().unwrap().expose_secret())
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
 pub struct GithubConfig {
-    pub token: String,
+    #[serde(skip_serializing_if = "Option::is_none", serialize_with = "expose")]
+    pub token: Option<SecretString>,
     pub owner: String,
     pub repo: String,
 }
 
-pub static CONFIG: Mutex<GithubConfig> = Mutex::new(GithubConfig {
-    token: String::new(),
-    owner: String::new(),
-    repo: String::new(),
-});
-
-pub async fn auth() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
-    let client_id = std::env::var("GITHUB_CLIENT_ID")?.to_string();
-    let secret_id = SecretString::from(client_id);
+pub async fn auth(config: &mut GithubConfig) -> Result<(), Error> {
+    let client_id = std::env::var("GITHUB_CLIENT_ID")?.into();
     let timeout = Duration::from_secs(60);
 
     let crab = octocrab::Octocrab::builder()
@@ -37,132 +67,114 @@ pub async fn auth() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
         .build()?;
 
     let codes = crab
-        .authenticate_as_device(&secret_id, ["public_repo"])
+        .authenticate_as_device(&client_id, ["public_repo"])
         .await?;
 
     // Set message box text
     let message = format!(
-        "{}\n{}",
-        codes.verification_uri.clone(),
-        codes.user_code.clone()
+        "<a href=\"{0}\">{0}</a>\n{1}",
+        codes.verification_uri, codes.user_code
     );
 
-    let mut backend = dialog::backends::Zenity::new();
-    backend.set_width(250);
-    dialog::Message::new(message)
-        .title("Github Login")
-        .show_with(backend)
-        .expect("Could not display Github dialog box");
+    // rx.recv() will resolve when _tx is dropped
+    let (_tx, rx) = async_channel::bounded::<()>(1);
+    let (cancel_tx, cancel_rx) = async_channel::bounded::<()>(1);
+
+    // GObjects are not Send + Sync, hence cannot be held across await. First create a future that
+    // is run in main thread, and use the local variant from there.
+    gtk::glib::spawn_future(async move {
+        gtk::glib::spawn_future_local(async move {
+            let dlg = adw::MessageDialog::new(gtk::Window::NONE, Some("Github Login"), None);
+            let (cncl_tx, cncl_rx) = async_channel::bounded::<()>(1);
+            let _cancel_tx = cancel_tx;
+            dlg.set_body(&message);
+            dlg.add_response("cancel", "Cancel");
+            dlg.set_body_use_markup(true);
+            dlg.connect_response(None, move |_dlg, _ers| {
+                let _ = cncl_tx.send_blocking(());
+            });
+            dlg.show();
+            futures::select! {
+                _ = rx.recv().fuse() => (),
+                _ = cncl_rx.recv().fuse() => (),
+            };
+            dlg.destroy();
+        });
+    });
 
     // Atuhentication with timeout
-    let auth = tokio::time::timeout(timeout, codes.poll_until_available(&crab, &secret_id))
-        .await?
-        .unwrap();
-    // Write key to config file
-    set_key(auth.access_token.expose_secret()).unwrap();
+
+    let auth = tokio::select! {
+        e = codes.poll_until_available(&crab, &client_id) => e?,
+        _ = tokio::time::sleep(timeout) => Err(Error::TimedOut)?,
+        _ = cancel_rx.recv() => Err(Error::Cancelled)?,
+    };
+    set_key(config, auth.access_token)?;
+
     Ok(())
 }
 
-pub fn get_config_path() -> String {
+pub fn get_config_path() -> PathBuf {
     let variable_name = "GITHUB_CONFIG";
-    let variable = env::var(variable_name);
-    let path = match variable {
-        Ok(ref val) => val,
-        Err(e) => {
-            println!("Missing environment variable: {}, {}", variable_name, e);
-            "/home/ghaf/.config/ctrl-panel/config.toml"
+    let variable = env::var_os(variable_name);
+    match variable {
+        Some(val) => PathBuf::from(val),
+        None => {
+            println!("Missing environment variable: {variable_name}");
+            Path::new(&env::var_os("HOME").unwrap_or_else(|| "/home/ghaf".into()))
+                .join(".config/ctrl-panel/config.toml")
         }
-    };
-    path.to_string()
+    }
 }
 
-pub fn load_config() -> Result<GithubConfig, String> {
+pub fn load_config() -> Result<GithubConfig, Error> {
     let path = get_config_path();
 
-    let config = match config::Config::builder()
-        .add_source(config::File::from(PathBuf::from(path)))
-        .build()
-    {
-        Ok(c) => c,
-        Err(_e) => return Err("Failed to load config".to_string()),
-    };
-
-    let result = match config.try_deserialize::<GithubConfig>() {
-        Ok(r) => r,
-        Err(_e) => return Err("Failed to parse config".to_string()),
-    };
-
-    Ok(result)
-}
-
-pub fn set_config() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
-    let _ = match load_config() {
-        Ok(c) => *CONFIG.lock().unwrap() = c,
-        Err(e) => return Err(e.into()),
-    };
-    Ok(())
-}
-
-pub fn get_config() -> GithubConfig {
-    let config = CONFIG.lock().unwrap();
-    config.clone()
+    Ok(toml::from_str(&std::fs::read_to_string(path)?)?)
 }
 
-pub async fn create_github_issue(
-    title: &str,
-    content: &str,
-) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
-    let _ = set_config();
+pub async fn create_github_issue(title: String, content: String) -> Result<Issue, Error> {
+    let mut config = load_config()?;
 
-    let config = get_config().clone();
+    let issue_body = content
+        .split_once("\n\nAttachment:")
+        .map(|(a, _)| a)
+        .unwrap_or(&content);
 
-    let parts: Vec<&str> = content.split("\n\nAttachment:").collect();
-    let (issue_body, _attachment_info) = (parts[0], parts.get(1));
-
-    let issue_sent = send_issue(&config, title, issue_body);
-    match issue_sent.await {
-        Ok(issue) => issue,
+    match send_issue(&config, &title, &issue_body).await {
         Err(_e) => {
-            auth().await?;
-            let _ = set_config();
-            let config = get_config().clone();
-            send_issue(&config, title, issue_body).await?
+            auth(&mut config).await?;
+            let config = load_config()?;
+            send_issue(&config, &title, &issue_body).await
         }
-    };
-
-    Ok(())
+        ok => ok,
+    }
 }
 
-#[inline]
-async fn send_issue(
-    config: &GithubConfig,
-    title: &str,
-    body: &str,
-) -> octocrab::Result<octocrab::models::issues::Issue> {
+async fn send_issue(config: &GithubConfig, title: &str, body: &str) -> Result<Issue, Error> {
     let octocrab = Octocrab::builder()
-        .personal_token(config.token.clone())
+        .personal_token(
+            config
+                .token
+                .as_ref()
+                .ok_or(Error::NotAuthenticated)?
+                .clone(),
+        )
         .build()?;
-    octocrab
+    Ok(octocrab
         .issues(&config.owner, &config.repo)
         .create(title)
         .body(body.to_string())
         .send()
-        .await
+        .await?)
 }
 
 #[inline]
-fn set_key(token: &str) -> Result<(), std::io::Error> {
-    let token_key = String::from("token");
+fn set_key(config: &mut GithubConfig, token: SecretString) -> Result<(), Error> {
+    config.token = Some(token);
     let path = get_config_path();
-    let contents = std::fs::read_to_string(&path)?;
-    let mut doc = contents.parse::<DocumentMut>().unwrap();
-    doc[&token_key] = value(token);
-
-    let mut file = std::fs::OpenOptions::new()
-        .write(true)
-        .truncate(true)
-        .open(&path)?;
-    file.write(doc.to_string().as_bytes())?;
+
+    std::fs::write(&path, toml::to_string(config)?.as_bytes())?;
 
     Ok(())
 }
diff --git a/src/ui/bug_report_settings_page.ui b/src/ui/bug_report_settings_page.ui
index b129ce6..d43fae7 100644
--- a/src/ui/bug_report_settings_page.ui
+++ b/src/ui/bug_report_settings_page.ui
@@ -612,9 +612,9 @@
                       </object>
                     </child>
                     <child>
-                      <object class="GtkTextBuffer" id="description_textbuffer">
+                      <object class="GtkTextBuffer" id="description_textbuffer"></object>
+                      <object class="GtkTextBuffer" id="placeholder_textbuffer">
                         <property name="text" translatable="yes">Enter your answer</property>
-                        <signal name="insert-text" handler="on_description_changed" swapped="yes"/>
                       </object>
                     </child>
                     <child>
@@ -625,10 +625,11 @@
                         <property name="pixels-inside-wrap">5</property>
                         <property name="right-margin">5</property>
                         <property name="top-margin">5</property>
-                        <property name="buffer">description_textbuffer</property>
+                        <property name="buffer">placeholder_textbuffer</property>
                         <style>
                           <class name="description-deactive"/>
                         </style>
+			<signal name="notify::has-focus" handler="on_description_focus_changed" after="yes" swapped="yes"/>
                       </object>
                     </child>
                     <child>
diff --git a/src/window.rs b/src/window.rs
index 1841e07..950851a 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -143,14 +143,20 @@ impl ControlPanelGuiWindow {
     fn init(&self) {
         self.set_destroy_with_parent(true);
 
-        self.connect_close_request(glib::clone!(#[strong(rename_to = window)] self, move |_| {
-            println!("Close window request");
-            let app = window.get_app_ref();
-            app.clean_n_quit();
-            glib::Propagation::Stop // Returning Stop allows the window to be destroyed
-        }));
+        self.connect_close_request(glib::clone!(
+            #[strong(rename_to = window)]
+            self,
+            move |_| {
+                println!("Close window request");
+                let app = window.get_app_ref();
+                app.clean_n_quit();
+                glib::Propagation::Stop // Returning Stop allows the window to be destroyed
+            }
+        ));
 
-        self.connect_destroy(|_| {println!("Destroy window"); });
+        self.connect_destroy(|_| {
+            println!("Destroy window");
+        });
 
         //get application reference
         let app = self.get_app_ref();
@@ -207,11 +213,14 @@ impl ControlPanelGuiWindow {
         // Wrap model with selection and pass it to the list view
         let selection_model = SingleSelection::new(Some(filter_model.clone()));
         // Connect to the selection-changed and items-changed signals
-        selection_model.connect_selection_changed(
-            glib::clone!(#[strong(rename_to = window)] self, move |selection_model, _, _| {
+        selection_model.connect_selection_changed(glib::clone!(
+            #[strong(rename_to = window)]
+            self,
+            move |selection_model, _, _| {
                 if let Some(selected_item) = selection_model.selected_item() {
                     println!("Selected: {}", selection_model.selected());
-                    if let Some(obj) = selected_item.downcast_ref::<ServiceGObject>() {//???
+                    if let Some(obj) = selected_item.downcast_ref::<ServiceGObject>() {
+                        //???
                         let title = obj.name();
                         let subtitle = obj.details();
                         println!("Property {title}, {subtitle}");
@@ -220,11 +229,16 @@ impl ControlPanelGuiWindow {
                 } else {
                     println!("No item selected");
                 }
-            }),
-        );
-        selection_model.connect_items_changed(
-            glib::clone!(#[strong(rename_to = window)] self, move |selection_model, position, removed, added| {
-                println!("Items changed at position {}, removed: {}, added: {}", position, removed, added);
+            }
+        ));
+        selection_model.connect_items_changed(glib::clone!(
+            #[strong(rename_to = window)]
+            self,
+            move |selection_model, position, removed, added| {
+                println!(
+                    "Items changed at position {}, removed: {}, added: {}",
+                    position, removed, added
+                );
                 if let Some(selected_item) = selection_model.selected_item() {
                     if let Some(obj) = selected_item.downcast_ref::<ServiceGObject>() {
                         window.set_vm_details(&obj);
@@ -232,8 +246,8 @@ impl ControlPanelGuiWindow {
                 } else {
                     println!("No item selected");
                 }
-            })
-        );
+            }
+        ));
 
         self.imp()
             .services_list_view


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: 

Copilot reviewed 5 out of 6 changed files in this pull request and generated 1 comment.

<details>
<summary>Files not reviewed (1)</summary>

* **src/ui/bug_report_settings_page.ui**: Language not supported
</details>

<details>
<summary>Comments suppressed due to low confidence (1)</summary>

**src/bug_report_settings_page.rs:304**
* [nitpick] The error message format is missing clear punctuation between the static text and the error value. Consider using a colon and space (e.g., 'Error when sending report: {e}') to improve clarity.
```
this.summary.set_label(&format!("Error when sending report{e}"));
```
</details>


state: COMMENTED
author_association: NONE
submitted_at: 2025-04-04T01:34:11Z
user: login: avnik
body: Generally looks as good as it could be.
I didn't try to build/run although.
Add some assorted notes, all of them should not block you
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T08:41:55Z
user: login: slakkala
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-23T08:50:36Z
user: login: slakkala
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-23T09:05:33Z

Review comments:
diff_hunk: @@ -37,132 +69,129 @@ pub async fn auth() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
         .build()?;
 
     let codes = crab
-        .authenticate_as_device(&secret_id, ["public_repo"])
+        .authenticate_as_device(&client_id, ["public_repo"])
         .await?;
 
     // Set message box text
     let message = format!(
-        "{}\n{}",
-        codes.verification_uri.clone(),
-        codes.user_code.clone()
+        "<a href=\"{0}\">{0}</a>\n{1}",
+        codes.verification_uri, codes.user_code
     );
 
-    let mut backend = dialog::backends::Zenity::new();
-    backend.set_width(250);
-    dialog::Message::new(message)
-        .title("Github Login")
-        .show_with(backend)
-        .expect("Could not display Github dialog box");
+    // rx.recv() will resolve when _tx is dropped
+    let (_tx, rx) = async_channel::bounded::<()>(1);
+    let (cancel_tx, cancel_rx) = async_channel::bounded::<()>(1);
+
+    // GObjects are not Send + Sync, hence cannot be held across await. First create a future that
+    // is run in main thread, and use the local variant from there.
+    gtk::glib::spawn_future(async move {
+        gtk::glib::spawn_future_local(async move {
+            let dlg = adw::MessageDialog::new(gtk::Window::NONE, Some("Github Login"), None);
+            let (cncl_tx, cncl_rx) = async_channel::bounded::<()>(1);
+            let _cancel_tx = cancel_tx;
+            dlg.set_body(&message);
+            dlg.add_response("cancel", "Cancel");
+            dlg.set_body_use_markup(true);
+            dlg.connect_response(None, move |_dlg, _ers| {
+                let _ = cncl_tx.send_blocking(());
+            });
+            dlg.show();
+            futures::select! {
+                _ = rx.recv().fuse() => (),
+                _ = cncl_rx.recv().fuse() => (),
+            };
+            dlg.destroy();
+        });
+    });
 
     // Atuhentication with timeout
-    let auth = tokio::time::timeout(timeout, codes.poll_until_available(&crab, &secret_id))
-        .await?
-        .unwrap();
-    // Write key to config file
-    set_key(auth.access_token.expose_secret()).unwrap();
+
+    let auth = tokio::select! {
+        e = codes.poll_until_available(&crab, &client_id) => e?,
+        _ = tokio::time::sleep(timeout) => Err(Error::TimedOut)?,
+        _ = cancel_rx.recv() => Err(Error::Cancelled)?,
+    };
+    set_key(auth.access_token)?;
+
     Ok(())
 }
 
-pub fn get_config_path() -> String {
+pub fn get_config_path() -> PathBuf {
     let variable_name = "GITHUB_CONFIG";
-    let variable = env::var(variable_name);
-    let path = match variable {
-        Ok(ref val) => val,
-        Err(e) => {
-            println!("Missing environment variable: {}, {}", variable_name, e);
-            "/home/ghaf/.config/ctrl-panel/config.toml"
+    let variable = env::var_os(variable_name);
+    match variable {
+        Some(ref val) => PathBuf::from(val),
+        None => {
+            println!("Missing environment variable: {variable_name}");
+            PathBuf::from("/home/ghaf/.config/ctrl-panel/config.toml")
         }
-    };
-    path.to_string()
+    }
 }
 
-pub fn load_config() -> Result<GithubConfig, String> {
+pub fn load_config() -> Result<GithubConfig, Error> {
     let path = get_config_path();
 
-    let config = match config::Config::builder()
-        .add_source(config::File::from(PathBuf::from(path)))
-        .build()
-    {
-        Ok(c) => c,
-        Err(_e) => return Err("Failed to load config".to_string()),
-    };
-
-    let result = match config.try_deserialize::<GithubConfig>() {
-        Ok(r) => r,
-        Err(_e) => return Err("Failed to parse config".to_string()),
-    };
-
-    Ok(result)
+    Ok(toml::from_str(&std::fs::read_to_string(path)?)?)
 }
 
-pub fn set_config() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
+pub fn set_config() -> Result<(), Error> {
     let _ = match load_config() {
-        Ok(c) => *CONFIG.lock().unwrap() = c,
+        Ok(c) => *CONFIG.lock().unwrap() = Some(c),
         Err(e) => return Err(e.into()),
     };
     Ok(())
 }
 
 pub fn get_config() -> GithubConfig {
     let config = CONFIG.lock().unwrap();
-    config.clone()
+    config.as_ref().cloned().unwrap()
 }
 
-pub async fn create_github_issue(
-    title: &str,
-    content: &str,
-) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
+pub async fn create_github_issue(title: String, content: String) -> Result<Issue, Error> {
     let _ = set_config();
-
     let config = get_config().clone();
 
-    let parts: Vec<&str> = content.split("\n\nAttachment:").collect();
-    let (issue_body, _attachment_info) = (parts[0], parts.get(1));
+    let issue_body = content
+        .split_once("\n\nAttachment:")
+        .map(|(a, _)| a)
+        .unwrap_or(&content);
 
-    let issue_sent = send_issue(&config, title, issue_body);
-    match issue_sent.await {
-        Ok(issue) => issue,
+    match send_issue(&config, &title, &issue_body).await {
         Err(_e) => {
             auth().await?;
             let _ = set_config();
-            let config = get_config().clone();
-            send_issue(&config, title, issue_body).await?
+            let config = get_config();
+            send_issue(&config, &title, &issue_body).await
         }
-    };
-
-    Ok(())
+        ok => ok,
+    }
 }
 
-#[inline]
-async fn send_issue(
-    config: &GithubConfig,
-    title: &str,
-    body: &str,
-) -> octocrab::Result<octocrab::models::issues::Issue> {
+async fn send_issue(config: &GithubConfig, title: &str, body: &str) -> Result<Issue, Error> {
     let octocrab = Octocrab::builder()
-        .personal_token(config.token.clone())
+        .personal_token(
+            config
+                .token
+                .as_ref()
+                .ok_or(Error::NotAuthenticated)?
+                .clone(),
+        )
         .build()?;
-    octocrab
+    Ok(octocrab
         .issues(&config.owner, &config.repo)
         .create(title)
         .body(body.to_string())
         .send()
-        .await
+        .await?)
 }
 
 #[inline]
-fn set_key(token: &str) -> Result<(), std::io::Error> {
-    let token_key = String::from("token");
+fn set_key(token: SecretString) -> Result<(), Error> {
+    let mut guard = CONFIG.lock().unwrap();
+    guard.as_mut().unwrap().token = Some(token);
     let path = get_config_path();
-    let contents = std::fs::read_to_string(&path)?;
-    let mut doc = contents.parse::<DocumentMut>().unwrap();
-    doc[&token_key] = value(token);
-
-    let mut file = std::fs::OpenOptions::new()
-        .write(true)
-        .truncate(true)
-        .open(&path)?;
-    file.write(doc.to_string().as_bytes())?;
+
+    std::fs::write(&path, toml::to_string(guard.as_ref().unwrap())?.as_bytes())?;
 
     Ok(())
path: src/github.rs
user: login: Copilot
body: Using unwrap() on CONFIG may lead to a panic if the configuration is not initialized. Consider checking if CONFIG is set before unwrapping and returning a descriptive error if not.
created_at: 2025-04-04T01:34:11Z
author_association: NONE
diff_hunk: @@ -37,132 +69,129 @@ pub async fn auth() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
         .build()?;
 
     let codes = crab
-        .authenticate_as_device(&secret_id, ["public_repo"])
+        .authenticate_as_device(&client_id, ["public_repo"])
         .await?;
 
     // Set message box text
     let message = format!(
-        "{}\n{}",
-        codes.verification_uri.clone(),
-        codes.user_code.clone()
+        "<a href=\"{0}\">{0}</a>\n{1}",
+        codes.verification_uri, codes.user_code
     );
 
-    let mut backend = dialog::backends::Zenity::new();
-    backend.set_width(250);
-    dialog::Message::new(message)
-        .title("Github Login")
-        .show_with(backend)
-        .expect("Could not display Github dialog box");
+    // rx.recv() will resolve when _tx is dropped
+    let (_tx, rx) = async_channel::bounded::<()>(1);
+    let (cancel_tx, cancel_rx) = async_channel::bounded::<()>(1);
+
+    // GObjects are not Send + Sync, hence cannot be held across await. First create a future that
+    // is run in main thread, and use the local variant from there.
+    gtk::glib::spawn_future(async move {
+        gtk::glib::spawn_future_local(async move {
+            let dlg = adw::MessageDialog::new(gtk::Window::NONE, Some("Github Login"), None);
+            let (cncl_tx, cncl_rx) = async_channel::bounded::<()>(1);
+            let _cancel_tx = cancel_tx;
+            dlg.set_body(&message);
+            dlg.add_response("cancel", "Cancel");
+            dlg.set_body_use_markup(true);
+            dlg.connect_response(None, move |_dlg, _ers| {
+                let _ = cncl_tx.send_blocking(());
+            });
+            dlg.show();
+            futures::select! {
+                _ = rx.recv().fuse() => (),
+                _ = cncl_rx.recv().fuse() => (),
+            };
+            dlg.destroy();
+        });
+    });
 
     // Atuhentication with timeout
-    let auth = tokio::time::timeout(timeout, codes.poll_until_available(&crab, &secret_id))
-        .await?
-        .unwrap();
-    // Write key to config file
-    set_key(auth.access_token.expose_secret()).unwrap();
+
+    let auth = tokio::select! {
+        e = codes.poll_until_available(&crab, &client_id) => e?,
+        _ = tokio::time::sleep(timeout) => Err(Error::TimedOut)?,
+        _ = cancel_rx.recv() => Err(Error::Cancelled)?,
+    };
+    set_key(auth.access_token)?;
+
     Ok(())
 }
 
-pub fn get_config_path() -> String {
+pub fn get_config_path() -> PathBuf {
     let variable_name = "GITHUB_CONFIG";
-    let variable = env::var(variable_name);
-    let path = match variable {
-        Ok(ref val) => val,
-        Err(e) => {
-            println!("Missing environment variable: {}, {}", variable_name, e);
-            "/home/ghaf/.config/ctrl-panel/config.toml"
+    let variable = env::var_os(variable_name);
+    match variable {
+        Some(ref val) => PathBuf::from(val),
+        None => {
+            println!("Missing environment variable: {variable_name}");
+            PathBuf::from("/home/ghaf/.config/ctrl-panel/config.toml")
path: src/github.rs
user: login: avnik
body: May be worth co build a path using `${HOME}/.config/....`
(non-blocking comment)
created_at: 2025-04-23T08:33:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -273,29 +278,41 @@ mod imp {
                 email_body.push_str(&format!("Description:\n{}\n", description));
 
                 let email_title = format!("{}: {}", issue, title);
-
-                let rt = tokio::runtime::Runtime::new().unwrap();
-                match rt.block_on(create_github_issue(&email_title, &email_body)) {
-                    Ok(_) => {
-                        self.summary.set_label("Report sent successfully");
-                        self.summary.remove_css_class("required-text");
-                        self.summary.add_css_class("success-text");
-                        self.summary.set_visible(true);
-                    }
-                    Err(e) => {
-                        if e.downcast_ref::<octocrab::Error>().is_none() {
-                            self.summary
-                                .set_label(format!("Error when sending report: {}", e).as_str());
-                        } else {
-                            self.summary.set_label("Error when sending report");
+                let (tx, rx) = async_channel::bounded(1);
path: src/bug_report_settings_page.rs
user: login: avnik
body: Consider using `oneshot` channel, and probably move this "spawn into tokio" to own function.
(non blocking, but may be worth to add FIXME to code)
created_at: 2025-04-23T08:36:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -37,132 +69,129 @@ pub async fn auth() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
         .build()?;
 
     let codes = crab
-        .authenticate_as_device(&secret_id, ["public_repo"])
+        .authenticate_as_device(&client_id, ["public_repo"])
         .await?;
 
     // Set message box text
     let message = format!(
-        "{}\n{}",
-        codes.verification_uri.clone(),
-        codes.user_code.clone()
+        "<a href=\"{0}\">{0}</a>\n{1}",
+        codes.verification_uri, codes.user_code
     );
 
-    let mut backend = dialog::backends::Zenity::new();
-    backend.set_width(250);
-    dialog::Message::new(message)
-        .title("Github Login")
-        .show_with(backend)
-        .expect("Could not display Github dialog box");
+    // rx.recv() will resolve when _tx is dropped
+    let (_tx, rx) = async_channel::bounded::<()>(1);
+    let (cancel_tx, cancel_rx) = async_channel::bounded::<()>(1);
+
+    // GObjects are not Send + Sync, hence cannot be held across await. First create a future that
+    // is run in main thread, and use the local variant from there.
+    gtk::glib::spawn_future(async move {
+        gtk::glib::spawn_future_local(async move {
+            let dlg = adw::MessageDialog::new(gtk::Window::NONE, Some("Github Login"), None);
+            let (cncl_tx, cncl_rx) = async_channel::bounded::<()>(1);
+            let _cancel_tx = cancel_tx;
+            dlg.set_body(&message);
+            dlg.add_response("cancel", "Cancel");
+            dlg.set_body_use_markup(true);
+            dlg.connect_response(None, move |_dlg, _ers| {
+                let _ = cncl_tx.send_blocking(());
+            });
+            dlg.show();
+            futures::select! {
+                _ = rx.recv().fuse() => (),
+                _ = cncl_rx.recv().fuse() => (),
+            };
+            dlg.destroy();
+        });
+    });
 
     // Atuhentication with timeout
-    let auth = tokio::time::timeout(timeout, codes.poll_until_available(&crab, &secret_id))
-        .await?
-        .unwrap();
-    // Write key to config file
-    set_key(auth.access_token.expose_secret()).unwrap();
+
+    let auth = tokio::select! {
+        e = codes.poll_until_available(&crab, &client_id) => e?,
+        _ = tokio::time::sleep(timeout) => Err(Error::TimedOut)?,
+        _ = cancel_rx.recv() => Err(Error::Cancelled)?,
+    };
+    set_key(auth.access_token)?;
+
     Ok(())
 }
 
-pub fn get_config_path() -> String {
+pub fn get_config_path() -> PathBuf {
     let variable_name = "GITHUB_CONFIG";
-    let variable = env::var(variable_name);
-    let path = match variable {
-        Ok(ref val) => val,
-        Err(e) => {
-            println!("Missing environment variable: {}, {}", variable_name, e);
-            "/home/ghaf/.config/ctrl-panel/config.toml"
+    let variable = env::var_os(variable_name);
+    match variable {
+        Some(ref val) => PathBuf::from(val),
+        None => {
+            println!("Missing environment variable: {variable_name}");
+            PathBuf::from("/home/ghaf/.config/ctrl-panel/config.toml")
         }
-    };
-    path.to_string()
+    }
 }
 
-pub fn load_config() -> Result<GithubConfig, String> {
+pub fn load_config() -> Result<GithubConfig, Error> {
     let path = get_config_path();
 
-    let config = match config::Config::builder()
-        .add_source(config::File::from(PathBuf::from(path)))
-        .build()
-    {
-        Ok(c) => c,
-        Err(_e) => return Err("Failed to load config".to_string()),
-    };
-
-    let result = match config.try_deserialize::<GithubConfig>() {
-        Ok(r) => r,
-        Err(_e) => return Err("Failed to parse config".to_string()),
-    };
-
-    Ok(result)
+    Ok(toml::from_str(&std::fs::read_to_string(path)?)?)
 }
 
-pub fn set_config() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
+pub fn set_config() -> Result<(), Error> {
     let _ = match load_config() {
-        Ok(c) => *CONFIG.lock().unwrap() = c,
+        Ok(c) => *CONFIG.lock().unwrap() = Some(c),
         Err(e) => return Err(e.into()),
     };
     Ok(())
 }
 
 pub fn get_config() -> GithubConfig {
     let config = CONFIG.lock().unwrap();
-    config.clone()
+    config.as_ref().cloned().unwrap()
path: src/github.rs
user: login: avnik
body: Maybe worth to put config into `Arc`?
created_at: 2025-04-23T08:39:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -273,29 +278,41 @@ mod imp {
                 email_body.push_str(&format!("Description:\n{}\n", description));
 
                 let email_title = format!("{}: {}", issue, title);
-
-                let rt = tokio::runtime::Runtime::new().unwrap();
-                match rt.block_on(create_github_issue(&email_title, &email_body)) {
-                    Ok(_) => {
-                        self.summary.set_label("Report sent successfully");
-                        self.summary.remove_css_class("required-text");
-                        self.summary.add_css_class("success-text");
-                        self.summary.set_visible(true);
-                    }
-                    Err(e) => {
-                        if e.downcast_ref::<octocrab::Error>().is_none() {
-                            self.summary
-                                .set_label(format!("Error when sending report: {}", e).as_str());
-                        } else {
-                            self.summary.set_label("Error when sending report");
+                let (tx, rx) = async_channel::bounded(1);
path: src/bug_report_settings_page.rs
user: login: slakkala
body: `oneshot` would be semantically better, but not quite sure if it is worth adding `async-oneshot-channel` crate, since we already need the `async-channel` elsewhere. (`tokio` one won't work, as the receiving end needs to support `glib` executor).
created_at: 2025-04-23T08:50:36Z
author_association: COLLABORATOR
diff_hunk: @@ -37,132 +69,129 @@ pub async fn auth() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
         .build()?;
 
     let codes = crab
-        .authenticate_as_device(&secret_id, ["public_repo"])
+        .authenticate_as_device(&client_id, ["public_repo"])
         .await?;
 
     // Set message box text
     let message = format!(
-        "{}\n{}",
-        codes.verification_uri.clone(),
-        codes.user_code.clone()
+        "<a href=\"{0}\">{0}</a>\n{1}",
+        codes.verification_uri, codes.user_code
     );
 
-    let mut backend = dialog::backends::Zenity::new();
-    backend.set_width(250);
-    dialog::Message::new(message)
-        .title("Github Login")
-        .show_with(backend)
-        .expect("Could not display Github dialog box");
+    // rx.recv() will resolve when _tx is dropped
+    let (_tx, rx) = async_channel::bounded::<()>(1);
+    let (cancel_tx, cancel_rx) = async_channel::bounded::<()>(1);
+
+    // GObjects are not Send + Sync, hence cannot be held across await. First create a future that
+    // is run in main thread, and use the local variant from there.
+    gtk::glib::spawn_future(async move {
+        gtk::glib::spawn_future_local(async move {
+            let dlg = adw::MessageDialog::new(gtk::Window::NONE, Some("Github Login"), None);
+            let (cncl_tx, cncl_rx) = async_channel::bounded::<()>(1);
+            let _cancel_tx = cancel_tx;
+            dlg.set_body(&message);
+            dlg.add_response("cancel", "Cancel");
+            dlg.set_body_use_markup(true);
+            dlg.connect_response(None, move |_dlg, _ers| {
+                let _ = cncl_tx.send_blocking(());
+            });
+            dlg.show();
+            futures::select! {
+                _ = rx.recv().fuse() => (),
+                _ = cncl_rx.recv().fuse() => (),
+            };
+            dlg.destroy();
+        });
+    });
 
     // Atuhentication with timeout
-    let auth = tokio::time::timeout(timeout, codes.poll_until_available(&crab, &secret_id))
-        .await?
-        .unwrap();
-    // Write key to config file
-    set_key(auth.access_token.expose_secret()).unwrap();
+
+    let auth = tokio::select! {
+        e = codes.poll_until_available(&crab, &client_id) => e?,
+        _ = tokio::time::sleep(timeout) => Err(Error::TimedOut)?,
+        _ = cancel_rx.recv() => Err(Error::Cancelled)?,
+    };
+    set_key(auth.access_token)?;
+
     Ok(())
 }
 
-pub fn get_config_path() -> String {
+pub fn get_config_path() -> PathBuf {
     let variable_name = "GITHUB_CONFIG";
-    let variable = env::var(variable_name);
-    let path = match variable {
-        Ok(ref val) => val,
-        Err(e) => {
-            println!("Missing environment variable: {}, {}", variable_name, e);
-            "/home/ghaf/.config/ctrl-panel/config.toml"
+    let variable = env::var_os(variable_name);
+    match variable {
+        Some(ref val) => PathBuf::from(val),
+        None => {
+            println!("Missing environment variable: {variable_name}");
+            PathBuf::from("/home/ghaf/.config/ctrl-panel/config.toml")
         }
-    };
-    path.to_string()
+    }
 }
 
-pub fn load_config() -> Result<GithubConfig, String> {
+pub fn load_config() -> Result<GithubConfig, Error> {
     let path = get_config_path();
 
-    let config = match config::Config::builder()
-        .add_source(config::File::from(PathBuf::from(path)))
-        .build()
-    {
-        Ok(c) => c,
-        Err(_e) => return Err("Failed to load config".to_string()),
-    };
-
-    let result = match config.try_deserialize::<GithubConfig>() {
-        Ok(r) => r,
-        Err(_e) => return Err("Failed to parse config".to_string()),
-    };
-
-    Ok(result)
+    Ok(toml::from_str(&std::fs::read_to_string(path)?)?)
 }
 
-pub fn set_config() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
+pub fn set_config() -> Result<(), Error> {
     let _ = match load_config() {
-        Ok(c) => *CONFIG.lock().unwrap() = c,
+        Ok(c) => *CONFIG.lock().unwrap() = Some(c),
         Err(e) => return Err(e.into()),
     };
     Ok(())
 }
 
 pub fn get_config() -> GithubConfig {
     let config = CONFIG.lock().unwrap();
-    config.clone()
+    config.as_ref().cloned().unwrap()
path: src/github.rs
user: login: slakkala
body: I removed the global altogether; the config was reloaded every time anyway, so just load it on demand.
created_at: 2025-04-23T09:05:33Z
author_association: COLLABORATOR

PR comments:
user: login: enesoztrk
created_at: 2025-04-23T10:12:31Z
body: Would it be better to use logger functions instead of `println!/eprintln!` ? If we use it, it will be easy to switch between stdout/syslog.
author_association: CONTRIBUTOR
user: login: slakkala
created_at: 2025-04-23T11:52:39Z
body: > Would it be better to use logger functions instead of `println!/eprintln!` ? If we use it, it will be easy to switch between stdout/syslog.

Agreed, will change in a later PR.
author_association: COLLABORATOR