diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..929057a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1514 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "cc" +version = "1.2.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "claude-code-usage-bubble" +version = "0.1.0" +dependencies = [ + "dirs", + "embed-resource", + "log", + "native-tls", + "serde", + "serde_json", + "simplelog", + "thiserror", + "tiny-skia", + "toml 0.8.23", + "ureq", + "windows", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "embed-resource" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31a88c8d26de40ed18fe748c547845aa39de1db3afd958f8cb91579f3644bcb" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 1.1.2+spec-1.1.0", + "vswhom", + "winreg", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libredox" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +dependencies = [ + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-conv" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "openssl" +version = "0.10.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf0b434746ee2832f4f0baf10137e1cabb18cbe6912c69e2e33263c45250f542" +dependencies = [ + "bitflags 2.11.1", + "cfg-if", + "foreign-types", + "libc", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "openssl-sys" +version = "0.9.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "158fe5b292746440aa6e7a7e690e55aeb72d41505e2804c23c6973ad0e9c9781" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.11.1", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "simplelog" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16257adbfaef1ee58b1363bdc0664c9b8e1e30aed86049635fb5f147d065a9c0" +dependencies = [ + "log", + "termcolor", + "time", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.3", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_write", + "winnow 0.7.15", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.3", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64", + "log", + "native-tls", + "once_cell", + "serde", + "serde_json", + "url", +] + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.1", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core", + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" + +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.1", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/src/app.rs b/src/app.rs index 9178edf..12769bf 100644 --- a/src/app.rs +++ b/src/app.rs @@ -249,11 +249,18 @@ fn create_initial_bubbles() { } fn spawn_bubble(kind: TrayIconKind, settings: &Settings, is_dark: bool) { + // "…" matches the in-flight/transient-error placeholder used by + // `apply_results`, so the bubble has visible feedback during the first + // poll rather than rendering with two empty grey tracks. + let placeholder = "…".to_string(); let hwnd = bubble::create(bubble::BubbleConfig { model: kind, size_logical: settings.bubble_size_logical, position: settings.bubble_positions.get(kind), - percent: None, + session_pct: None, + session_text: placeholder.clone(), + weekly_pct: None, + weekly_text: placeholder, is_dark, }); if hwnd != HWND::default() { @@ -492,11 +499,18 @@ fn propagate_to_ui() { for (kind, hwnd) in snap.bubbles.iter() { let id = kind_to_provider(*kind); - let pct = snap - .snapshots - .get(&id) - .map(|s| s.windows.primary.utilization); - bubble::update_percentage(hwnd.to_hwnd(), pct); + let entry = snap.snapshots.get(&id); + let session_pct = entry.map(|s| s.windows.primary.utilization); + let weekly_pct = entry.map(|s| s.windows.secondary.utilization); + let session_text = entry.map(|s| s.primary_text.clone()).unwrap_or_default(); + let weekly_text = entry.map(|s| s.secondary_text.clone()).unwrap_or_default(); + bubble::update_data( + hwnd.to_hwnd(), + session_pct, + session_text, + weekly_pct, + weekly_text, + ); } refresh_tray_icons_with(&snap); diff --git a/src/bubble.rs b/src/bubble.rs index 972faa1..cd4ae95 100644 --- a/src/bubble.rs +++ b/src/bubble.rs @@ -1,10 +1,15 @@ -// Floating circular bubble window. +// Floating rounded-rectangle bubble window. // // Top-level window with WS_POPUP + WS_EX_LAYERED + WS_EX_TOPMOST + WS_EX_NOACTIVATE. -// Shape is achieved via per-pixel alpha (alpha=0 outside the circle) and confirmed -// via WM_NCHITTEST returning HTCAPTION inside the circle, HTTRANSPARENT outside. -// The OS handles drag automatically because HTCAPTION inside the circle puts the -// click into the system move loop. +// Shape is achieved via per-pixel alpha (alpha=0 outside the rounded rect) and +// confirmed via WM_NCHITTEST returning HTCAPTION inside the rect, HTTRANSPARENT +// outside. The OS handles drag automatically because HTCAPTION inside the +// rect puts the click into the system move loop. +// +// Layout: two horizontal bars stacked vertically — top = session (5h), bottom = +// weekly (7d) — each followed by right-aligned "X% · Yh Zm" text. The aspect +// ratio is fixed at BUBBLE_ASPECT (3:1) so `size_logical` is interpreted as +// width and the height is derived. use std::collections::HashMap; use std::ffi::c_void; @@ -13,7 +18,7 @@ use std::sync::{Mutex, MutexGuard, OnceLock}; use windows::core::PCWSTR; use windows::Win32::Foundation::*; use windows::Win32::Graphics::Gdi::*; -use windows::Win32::System::LibraryLoader::GetModuleHandleW; +use windows::Win32::System::LibraryLoader::{GetModuleFileNameW, GetModuleHandleW}; use windows::Win32::UI::HiDpi::*; use windows::Win32::UI::Shell::ExtractIconExW; use windows::Win32::UI::WindowsAndMessaging::*; @@ -26,10 +31,12 @@ type TrayIconKind = ProviderId; // ---------- Public types & API ---------- -pub const MIN_BUBBLE_SIZE: i32 = 32; -pub const MAX_BUBBLE_SIZE: i32 = 128; -pub const DEFAULT_BUBBLE_SIZE: i32 = 56; -const RESIZE_STEP: i32 = 8; +// Width clamps in logical pixels (height = width / BUBBLE_ASPECT). +pub const MIN_BUBBLE_SIZE: i32 = 120; +pub const MAX_BUBBLE_SIZE: i32 = 360; +pub const DEFAULT_BUBBLE_SIZE: i32 = 180; +const RESIZE_STEP: i32 = 20; +const BUBBLE_ASPECT: i32 = 3; // width : height = 3 : 1 const SNAP_ZONE_LOGICAL: i32 = 12; const CLASS_NAME: &str = "ClaudeCodeUsageBubble"; const FULLSCREEN_POLL_MS: u32 = 1500; @@ -38,10 +45,17 @@ pub struct BubbleConfig { pub model: TrayIconKind, pub size_logical: i32, pub position: Option<(i32, i32)>, - pub percent: Option, + pub session_pct: Option, + pub session_text: String, + pub weekly_pct: Option, + pub weekly_text: String, pub is_dark: bool, } +fn bubble_height_logical(width_logical: i32) -> i32 { + (width_logical / BUBBLE_ASPECT).max(20) +} + /// Register the bubble window class. Idempotent; safe to call before the first /// `create()` from the UI thread. pub fn register_class() { @@ -77,11 +91,11 @@ pub fn create(config: BubbleConfig) -> HWND { let title_w = wide_str("Claude Code Usage Bubble"); let hinstance = GetModuleHandleW(PCWSTR::null()).unwrap_or_default(); let dpi = primary_dpi(); - let size_px = scale_to_dpi(initial_size_logical, dpi); - let (x, y) = - config - .position - .unwrap_or_else(|| default_position(size_px, config.model)); + let width_px = scale_to_dpi(initial_size_logical, dpi); + let height_px = scale_to_dpi(bubble_height_logical(initial_size_logical), dpi); + let (x, y) = config + .position + .unwrap_or_else(|| default_position(width_px, height_px, config.model)); CreateWindowExW( WS_EX_TOOLWINDOW | WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_NOACTIVATE, PCWSTR::from_raw(class_w.as_ptr()), @@ -89,8 +103,8 @@ pub fn create(config: BubbleConfig) -> HWND { WS_POPUP, x, y, - size_px, - size_px, + width_px, + height_px, HWND::default(), HMENU::default(), hinstance, @@ -143,7 +157,10 @@ pub fn create(config: BubbleConfig) -> HWND { model: config.model, size_logical: initial_size_logical, dpi, - percent: config.percent, + session_pct: config.session_pct, + session_text: config.session_text, + weekly_pct: config.weekly_pct, + weekly_text: config.weekly_text, is_dark: config.is_dark, drag_start_pos: None, hidden_by_fullscreen: false, @@ -168,13 +185,22 @@ pub fn destroy(hwnd: HWND) { } } -pub fn update_percentage(hwnd: HWND, percent: Option) { +pub fn update_data( + hwnd: HWND, + session_pct: Option, + session_text: String, + weekly_pct: Option, + weekly_text: String, +) { { let mut bubbles = lock_bubbles(); let Some(b) = bubbles.get_mut(&(hwnd.0 as isize)) else { return; }; - b.percent = percent; + b.session_pct = session_pct; + b.session_text = session_text; + b.weekly_pct = weekly_pct; + b.weekly_text = weekly_text; } render(hwnd); } @@ -232,7 +258,10 @@ struct BubbleState { model: TrayIconKind, size_logical: i32, dpi: u32, - percent: Option, + session_pct: Option, + session_text: String, + weekly_pct: Option, + weekly_text: String, is_dark: bool, drag_start_pos: Option<(i32, i32)>, hidden_by_fullscreen: bool, @@ -363,18 +392,41 @@ fn hit_test(hwnd: HWND, lparam: LPARAM) -> LRESULT { return LRESULT(HTNOWHERE as isize); } } - let cx = (r.left + r.right) / 2; - let cy = (r.top + r.bottom) / 2; - let radius = ((r.right - r.left) / 2).max(1); - let dx = pt.x - cx; - let dy = pt.y - cy; - if dx * dx + dy * dy <= radius * radius { + let w = r.right - r.left; + let h = r.bottom - r.top; + let radius = corner_radius_px(h); + // Local coordinates relative to top-left of the bubble. + let lx = pt.x - r.left; + let ly = pt.y - r.top; + if point_in_rounded_rect(lx, ly, w, h, radius) { LRESULT(HTCAPTION as isize) } else { LRESULT(HTTRANSPARENT as isize) } } +fn corner_radius_px(height_px: i32) -> i32 { + (height_px / 3).max(4) +} + +fn point_in_rounded_rect(x: i32, y: i32, w: i32, h: i32, r: i32) -> bool { + if x < 0 || y < 0 || x >= w || y >= h { + return false; + } + // The straight horizontal and vertical strips are always inside; only the + // four corner squares need the circular falloff check. + let in_x_strip = x >= r && x < w - r; + let in_y_strip = y >= r && y < h - r; + if in_x_strip || in_y_strip { + return true; + } + let cx = if x < r { r } else { w - 1 - r }; + let cy = if y < r { r } else { h - 1 - r }; + let dx = x - cx; + let dy = y - cy; + dx * dx + dy * dy <= r * r +} + fn lparam_to_point(lparam: LPARAM) -> POINT { let lo = (lparam.0 & 0xFFFF) as i16 as i32; let hi = ((lparam.0 >> 16) & 0xFFFF) as i16 as i32; @@ -396,22 +448,23 @@ fn resize_step(hwnd: HWND, delta: i32) { b.size_logical = new_logical; (new_logical, b.dpi) }; - let size_px = scale_to_dpi(new_logical, dpi); + let width_px = scale_to_dpi(new_logical, dpi); + let height_px = scale_to_dpi(bubble_height_logical(new_logical), dpi); let mut r = RECT::default(); unsafe { let _ = GetWindowRect(hwnd, &mut r); // Resize centered on existing center. let cx = (r.left + r.right) / 2; let cy = (r.top + r.bottom) / 2; - let new_x = cx - size_px / 2; - let new_y = cy - size_px / 2; + let new_x = cx - width_px / 2; + let new_y = cy - height_px / 2; let _ = SetWindowPos( hwnd, HWND::default(), new_x, new_y, - size_px, - size_px, + width_px, + height_px, SWP_NOZORDER | SWP_NOACTIVATE, ); } @@ -541,15 +594,72 @@ fn check_fullscreen(bubble_hwnd: HWND) { // ---------- Painting ---------- +struct BarLayout { + bar_left: i32, + bar_right: i32, + bar_h: i32, + right_text_left: i32, + right_text_w: i32, + row1_y: i32, + row2_y: i32, +} + +fn compute_layout(width_px: i32, height_px: i32) -> BarLayout { + let pad_x = (height_px / 6).max(3); + // Bar height is capped — the previous formula filled all the vertical space + // and produced a too-tall font that overflowed the right-text column. Cap + // at ~1/4 of the bubble height so the rows have visible padding around them. + let bar_h = (height_px / 4).clamp(6, 18); + let row_gap = (bar_h / 2).max(3); + let pad_y = ((height_px - bar_h * 2 - row_gap) / 2).max(2); + // Right-side text needs ~6× the bar height to fit "100% · 23h" comfortably + // at the font size derived below (bar_h * 0.75). Floor in px for the + // smallest legible width. + let right_text_w = (bar_h * 6).max(56); + let bar_left = pad_x; + let bar_right = (width_px - pad_x - right_text_w - bar_h / 2).max(bar_left + 8); + let right_text_left = bar_right + (bar_h / 2).max(2); + let row1_y = pad_y; + let row2_y = pad_y + bar_h + row_gap; + BarLayout { + bar_left, + bar_right, + bar_h, + right_text_left, + right_text_w, + row1_y, + row2_y, + } +} + +/// Pack an `Rgb` for direct write into a 32-bpp `BI_RGB` DIB. The DIB stores +/// bytes B,G,R,X in memory, so a little-endian u32 read is `(b) | (g<<8) | (r<<16)`. +/// Note this is the OPPOSITE byte order from a GDI `COLORREF` (which is +/// `(r) | (g<<8) | (b<<16)`) — don't confuse the two. +fn rgb_to_dib(c: Color) -> u32 { + (c.b as u32) | ((c.g as u32) << 8) | ((c.r as u32) << 16) +} + fn render(hwnd: HWND) { - let (size_logical, dpi, percent, is_dark) = { + let (size_logical, dpi, session_pct, session_text, weekly_pct, weekly_text, is_dark) = { let bubbles = lock_bubbles(); let Some(b) = bubbles.get(&(hwnd.0 as isize)) else { return; }; - (b.size_logical, b.dpi, b.percent, b.is_dark) + ( + b.size_logical, + b.dpi, + b.session_pct, + b.session_text.clone(), + b.weekly_pct, + b.weekly_text.clone(), + b.is_dark, + ) }; - let size_px = scale_to_dpi(size_logical, dpi); + let width_px = scale_to_dpi(size_logical, dpi); + let height_px = scale_to_dpi(bubble_height_logical(size_logical), dpi); + let radius = corner_radius_px(height_px); + let layout = compute_layout(width_px, height_px); unsafe { let screen_dc = GetDC(hwnd); @@ -557,8 +667,8 @@ fn render(hwnd: HWND) { let bmi = BITMAPINFO { bmiHeader: BITMAPINFOHEADER { biSize: std::mem::size_of::() as u32, - biWidth: size_px, - biHeight: -size_px, + biWidth: width_px, + biHeight: -height_px, biPlanes: 1, biBitCount: 32, biCompression: 0, @@ -576,30 +686,25 @@ fn render(hwnd: HWND) { } let old_bmp = SelectObject(mem_dc, dib); - let pixel_count = (size_px * size_px) as usize; + let pixel_count = (width_px * height_px) as usize; let pixels = std::slice::from_raw_parts_mut(bits as *mut u32, pixel_count); - paint_background(pixels, size_px, is_dark); - paint_ring(pixels, size_px, percent.unwrap_or(0.0), is_dark); - paint_text(mem_dc, size_px, percent, is_dark, dpi); + // Everything outside the rounded rect stays 0 (fully transparent). + pixels.fill(0); - // Final alpha pass: set alpha=255 inside circle, 0 outside. - let cx = (size_px - 1) as f64 / 2.0; - let cy = cx; - let radius = (size_px / 2) as f64 - 1.0; - let r_sq = radius * radius; - for y in 0..size_px { - for x in 0..size_px { - let dx = x as f64 - cx; - let dy = y as f64 - cy; - let idx = (y * size_px + x) as usize; - if dx * dx + dy * dy <= r_sq { - pixels[idx] |= 0xFF000000; - } else { - pixels[idx] = 0; - } - } - } + paint_background(pixels, width_px, height_px, radius, is_dark); + paint_bars(pixels, width_px, &layout, session_pct, weekly_pct, is_dark); + paint_bar_texts( + mem_dc, + &layout, + &session_text, + &weekly_text, + is_dark, + ); + + // Final alpha pass: alpha=255 inside the rounded rect, 0 outside. This + // also lifts GDI-drawn text (which leaves alpha=0) into the visible plane. + apply_alpha_mask(pixels, width_px, height_px, radius); let mut wr = RECT::default(); let _ = GetWindowRect(hwnd, &mut wr); @@ -609,8 +714,8 @@ fn render(hwnd: HWND) { }; let pt_src = POINT { x: 0, y: 0 }; let sz = SIZE { - cx: size_px, - cy: size_px, + cx: width_px, + cy: height_px, }; let blend = BLENDFUNCTION { BlendOp: 0, @@ -637,84 +742,96 @@ fn render(hwnd: HWND) { } } -fn paint_background(pixels: &mut [u32], size_px: i32, is_dark: bool) { +fn paint_background(pixels: &mut [u32], w: i32, h: i32, radius: i32, is_dark: bool) { let bg = if is_dark { Color::from_hex("#1F1F1F") } else { Color::from_hex("#F3F3F3") }; - let bg_bgr = bg.to_colorref(); - let cx = (size_px - 1) as f64 / 2.0; - let cy = cx; - let radius = (size_px / 2) as f64 - 1.0; - let r_sq = radius * radius; - for y in 0..size_px { - for x in 0..size_px { - let dx = x as f64 - cx; - let dy = y as f64 - cy; - let idx = (y * size_px + x) as usize; - if dx * dx + dy * dy <= r_sq { - pixels[idx] = bg_bgr; - } else { - pixels[idx] = 0; + let bg_packed = rgb_to_dib(bg); + for y in 0..h { + for x in 0..w { + if point_in_rounded_rect(x, y, w, h, radius) { + pixels[(y * w + x) as usize] = bg_packed; } } } } -fn paint_ring(pixels: &mut [u32], size_px: i32, percent: f64, is_dark: bool) { - let ring = ring_color_for_percent(percent).to_colorref(); +fn paint_bars( + pixels: &mut [u32], + width_px: i32, + layout: &BarLayout, + session_pct: Option, + weekly_pct: Option, + is_dark: bool, +) { let track = if is_dark { - Color::from_hex("#3A3A3A").to_colorref() + rgb_to_dib(Color::from_hex("#3A3A3A")) } else { - Color::from_hex("#D6D6D6").to_colorref() + rgb_to_dib(Color::from_hex("#D6D6D6")) }; - let cx = (size_px - 1) as f64 / 2.0; - let cy = cx; - let outer = (size_px / 2) as f64 - 1.0; - let thickness = ((size_px as f64) * 0.12).max(3.0); - let inner = outer - thickness; - let inner_sq = inner * inner; - let outer_sq = outer * outer; - let sweep = (percent.clamp(0.0, 100.0) / 100.0) * 2.0 * std::f64::consts::PI; - for y in 0..size_px { - for x in 0..size_px { - let dx = x as f64 - cx; - let dy = y as f64 - cy; - let d_sq = dx * dx + dy * dy; - if d_sq <= outer_sq && d_sq >= inner_sq { - // Angle from 12 o'clock, clockwise. - let mut a = dx.atan2(-dy); - if a < 0.0 { - a += 2.0 * std::f64::consts::PI; - } - let idx = (y * size_px + x) as usize; - pixels[idx] = if a <= sweep { ring } else { track }; - } + paint_bar(pixels, width_px, layout, layout.row1_y, session_pct, track); + paint_bar(pixels, width_px, layout, layout.row2_y, weekly_pct, track); +} + +fn paint_bar( + pixels: &mut [u32], + width_px: i32, + layout: &BarLayout, + top: i32, + pct: Option, + track: u32, +) { + let bar_w = layout.bar_right - layout.bar_left; + if bar_w <= 0 { + return; + } + // Track first. + for y in top..top + layout.bar_h { + for x in layout.bar_left..layout.bar_right { + pixels[(y * width_px + x) as usize] = track; + } + } + let Some(p) = pct else { + return; + }; + let fill_w = ((p.clamp(0.0, 100.0) / 100.0) * bar_w as f64).round() as i32; + if fill_w <= 0 { + return; + } + let accent = rgb_to_dib(ring_color_for_percent(p)); + let end_x = (layout.bar_left + fill_w).min(layout.bar_right); + for y in top..top + layout.bar_h { + for x in layout.bar_left..end_x { + pixels[(y * width_px + x) as usize] = accent; } } } -fn paint_text(hdc: HDC, size_px: i32, percent: Option, is_dark: bool, _dpi: u32) { - let text = match percent { - Some(p) => format!("{:.0}%", p), - None => "—".to_string(), - }; - let mut text_w = wide_str(&text); +fn paint_bar_texts( + hdc: HDC, + layout: &BarLayout, + session_text: &str, + weekly_text: &str, + is_dark: bool, +) { let text_color = if is_dark { - Color::from_hex("#F5F5F5") + Color::from_hex("#EAEAEA") } else { Color::from_hex("#1F1F1F") }; - let font_height = -(size_px / 4).max(8); + // Match font height to ~75% of the bar height — leaves room above/below + // for descenders and keeps "100% · 23h" within `right_text_w`. + let font_size = (layout.bar_h as f64 * 0.75).round() as i32; let font_name = wide_str("Segoe UI"); unsafe { let font = CreateFontW( - font_height, + -font_size.max(8), 0, 0, 0, - FW_SEMIBOLD.0 as i32, + FW_NORMAL.0 as i32, 0, 0, 0, @@ -726,24 +843,50 @@ fn paint_text(hdc: HDC, size_px: i32, percent: Option, is_dark: bool, _dpi: PCWSTR::from_raw(font_name.as_ptr()), ); let old_font = SelectObject(hdc, font); - SetTextColor(hdc, COLORREF(text_color.to_colorref())); + SetTextColor(hdc, COLORREF(text_color.into_colorref())); SetBkMode(hdc, TRANSPARENT); - let mut rect = RECT { - left: 0, - top: 0, - right: size_px, - bottom: size_px, - }; - // Trim the trailing NUL — DrawTextW reads slice length as count. - let len_no_nul = text_w.len().saturating_sub(1); + + draw_right_text(hdc, layout, layout.row1_y, session_text); + draw_right_text(hdc, layout, layout.row2_y, weekly_text); + + SelectObject(hdc, old_font); + let _ = DeleteObject(font); + } +} + +fn draw_right_text(hdc: HDC, layout: &BarLayout, row_top: i32, text: &str) { + if text.is_empty() { + return; + } + let mut text_w = wide_str(text); + let len_no_nul = text_w.len().saturating_sub(1); + // Vertically center within the bar row. + let mut rect = RECT { + left: layout.right_text_left, + top: row_top - 2, + right: layout.right_text_left + layout.right_text_w, + bottom: row_top + layout.bar_h + 2, + }; + unsafe { let _ = DrawTextW( hdc, &mut text_w[..len_no_nul], &mut rect, - DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOCLIP, + DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOCLIP | DT_END_ELLIPSIS, ); - SelectObject(hdc, old_font); - let _ = DeleteObject(font); + } +} + +fn apply_alpha_mask(pixels: &mut [u32], w: i32, h: i32, radius: i32) { + for y in 0..h { + for x in 0..w { + let idx = (y * w + x) as usize; + if point_in_rounded_rect(x, y, w, h, radius) { + pixels[idx] |= 0xFF00_0000; + } else { + pixels[idx] = 0; + } + } } } @@ -788,7 +931,7 @@ fn scale_to_dpi(logical: i32, dpi: u32) -> i32 { ((logical as i64) * (dpi as i64) / 96) as i32 } -fn default_position(size_px: i32, model: TrayIconKind) -> (i32, i32) { +fn default_position(width_px: i32, height_px: i32, model: TrayIconKind) -> (i32, i32) { // Bottom-right of primary work area, with a 24-pixel gap from the edges. // Stagger the Codex bubble above the Claude one if both are enabled. unsafe { @@ -810,10 +953,10 @@ fn default_position(size_px: i32, model: TrayIconKind) -> (i32, i32) { let gap = 24; let stagger = match model { ProviderId::Claude => 0, - ProviderId::ChatGpt => size_px + gap, + ProviderId::ChatGpt => height_px + gap, }; - let x = wa.right - size_px - gap; - let y = wa.bottom - size_px - gap - stagger; + let x = wa.right - width_px - gap; + let y = wa.bottom - height_px - gap - stagger; (x, y) } } diff --git a/src/main.rs b/src/main.rs index 197856b..d7fae51 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,8 @@ #![windows_subsystem = "windows"] +// Several modules (creds, usage, update, os::dpi, …) expose API that the +// app surface doesn't fully call yet — they're scaffolding for in-progress +// port phases. Allow at the crate root rather than scattering attributes. +#![allow(dead_code)] // Original infrastructure. mod creds; diff --git a/src/os/color.rs b/src/os/color.rs index 3df53ee..45d8f1f 100644 --- a/src/os/color.rs +++ b/src/os/color.rs @@ -16,6 +16,12 @@ impl Rgb { Self { r, g, b } } + /// Parse `#RRGGBB` or `RRGGBB`. Panics on malformed input — meant for + /// hardcoded design-system literals at compile-known call sites. + pub fn from_hex(hex: &str) -> Self { + Self::parse_hex(hex).unwrap_or_else(|| panic!("invalid hex color literal: {hex:?}")) + } + /// Parse `#RRGGBB` or `RRGGBB`. Returns `None` on malformed input. pub fn parse_hex(hex: &str) -> Option { let s = hex.trim_start_matches('#'); diff --git a/src/panel.rs b/src/panel.rs index 1d2829e..4ec9388 100644 --- a/src/panel.rs +++ b/src/panel.rs @@ -41,6 +41,11 @@ struct PanelState { data: PanelData, } +// HWND wraps `*mut c_void` which is `!Send`, but the panel state is only ever +// accessed from the UI thread — the Mutex exists to satisfy the `OnceLock` +// static contract, not because of real cross-thread sharing. +unsafe impl Send for PanelState {} + fn state() -> &'static Mutex> { static S: OnceLock>> = OnceLock::new(); S.get_or_init(|| Mutex::new(None)) @@ -267,7 +272,7 @@ fn paint(hwnd: HWND, hdc: HDC) { let accent = bar_color_for(data.session_pct.max(data.weekly_pct), data.is_dark); unsafe { - let bg_brush = CreateSolidBrush(COLORREF(bg.to_colorref())); + let bg_brush = CreateSolidBrush(COLORREF(bg.into_colorref())); FillRect(hdc, &rc, bg_brush); let _ = DeleteObject(bg_brush); @@ -362,7 +367,7 @@ fn draw_row( ); unsafe { - let track_brush = CreateSolidBrush(COLORREF(track.to_colorref())); + let track_brush = CreateSolidBrush(COLORREF(track.into_colorref())); let bar_rect = RECT { left: bar_x, top: row_y, @@ -374,7 +379,7 @@ fn draw_row( let fill_w = ((pct.clamp(0.0, 100.0) / 100.0) * bar_w as f64).round() as i32; if fill_w > 0 { - let accent_brush = CreateSolidBrush(COLORREF(accent.to_colorref())); + let accent_brush = CreateSolidBrush(COLORREF(accent.into_colorref())); let fill_rect = RECT { left: bar_x, top: row_y, @@ -439,7 +444,7 @@ fn draw_text( PCWSTR::from_raw(font_name.as_ptr()), ); let old_font = SelectObject(hdc, font); - SetTextColor(hdc, COLORREF(color.to_colorref())); + SetTextColor(hdc, COLORREF(color.into_colorref())); SetBkMode(hdc, TRANSPARENT); let mut rect = RECT { left: x, diff --git a/src/update/mod.rs b/src/update/mod.rs index 752d2a3..f0b6955 100644 --- a/src/update/mod.rs +++ b/src/update/mod.rs @@ -23,8 +23,8 @@ pub enum Error { } pub use channel::{current as current_channel, Channel}; -pub use install::{begin, run_cli}; -pub use release::{fetch_latest, Release, Version}; +pub use install::run_cli; +pub use release::Release; /// Result of a release-check call. #[derive(Debug)] diff --git a/src/usage/anthropic.rs b/src/usage/anthropic.rs index 513077f..8c8edfa 100644 --- a/src/usage/anthropic.rs +++ b/src/usage/anthropic.rs @@ -8,7 +8,7 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use serde::Deserialize; -use crate::creds::{CredentialSource, Locator}; +use crate::creds::Locator; use crate::net::Client; use crate::usage::{headers, Error, ProviderId, UsageProvider, UsageWindows, Window}; diff --git a/src/usage/chatgpt.rs b/src/usage/chatgpt.rs index ceddfaa..12bbd3d 100644 --- a/src/usage/chatgpt.rs +++ b/src/usage/chatgpt.rs @@ -7,7 +7,7 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use serde::Deserialize; -use crate::creds::{CredentialSource, Locator}; +use crate::creds::Locator; use crate::net::Client; use crate::usage::{Error, ProviderId, UsageProvider, UsageWindows, Window}; @@ -67,16 +67,16 @@ impl UsageProvider for ChatGptProvider { } fn envelope_to_windows(envelope: Envelope) -> Option { - let rl = envelope.rate_limit.flatten()?; + let rl = envelope.rate_limit.flatten_box()?; Some(UsageWindows { primary: rl .primary_window - .flatten() + .flatten_box() .map(window_from) .unwrap_or_default(), secondary: rl .secondary_window - .flatten() + .flatten_box() .map(window_from) .unwrap_or_default(), }) @@ -114,12 +114,14 @@ struct ApiWindow { reset_at: i64, } -// Helpers used to make `Option>>` flatten cleanly. +// Helpers used to make `Option>>` flatten cleanly. We can't +// reuse the std `Option::flatten` name — the inherent method (which returns +// `Option>`) would shadow this trait method. trait FlattenBoxed { - fn flatten(self) -> Option; + fn flatten_box(self) -> Option; } impl FlattenBoxed for Option>> { - fn flatten(self) -> Option { + fn flatten_box(self) -> Option { self.and_then(|inner| inner.map(|b| *b)) } } diff --git a/src/usage/mod.rs b/src/usage/mod.rs index c65e47d..4f7257a 100644 --- a/src/usage/mod.rs +++ b/src/usage/mod.rs @@ -12,7 +12,7 @@ pub mod registry; pub mod types; pub use registry::Registry; -pub use types::{ProviderId, ProviderSnapshot, UsageWindows, Window}; +pub use types::{ProviderId, UsageWindows, Window}; #[derive(Debug, thiserror::Error)] pub enum Error { @@ -24,6 +24,8 @@ pub enum Error { TokenExpired, #[error("network: {0}")] Network(#[from] crate::net::Error), + #[error("credentials: {0}")] + Creds(#[from] crate::creds::Error), #[error("unexpected response shape: {0}")] BadResponse(String), }