From a0d8a36ad835133c30da5a95fdef14a9d74aa4c0 Mon Sep 17 00:00:00 2001 From: MotherSphere Date: Wed, 14 Oct 2026 13:53:00 +0200 Subject: [PATCH 1/3] chore(deps): update serde_json, tokio, keyring-core, sha2, zip and winresource --- Cargo.lock | 128 +++++++++++++++++++++++++++++------------------------ Cargo.toml | 12 ++--- 2 files changed, 77 insertions(+), 63 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a57a9b7..48ad59e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -237,7 +237,7 @@ checksum = "4f8abc12baad266b1c8cec146854c195b5864b4221d4b2ca7296a7ae82d9e451" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -272,7 +272,7 @@ checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -289,9 +289,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-lc-rs" -version = "1.18.1" +version = "1.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e" +checksum = "faac5829c2b74c28f830747e7818ccfb684261b5f48a1118b1e2a13d36dfab13" dependencies = [ "aws-lc-sys", "zeroize", @@ -299,9 +299,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.45.0" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27" +checksum = "f1622d8446a2d4b2ce0c7eefc73dd43a99779028d5ee5c2dd8073a658ba8a2bc" dependencies = [ "cc", "cmake", @@ -430,7 +430,7 @@ checksum = "6a1f896587b6f2c069c73d2f0913e2d590c3990285cd2f0b6aa02b786b4c679c" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -512,9 +512,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f74872d07caf508b30a21f6836e7d7016a2eaf7d9ff4f48deaa58cd8a0407630" +checksum = "6651c9ed80effdc7db0ff72512157f901af5e3549e341e24b1dd4887d836d838" dependencies = [ "find-msvc-tools", "jobserver", @@ -635,7 +635,7 @@ dependencies = [ "semver", "serde", "serde_json", - "sha2 0.11.0", + "sha2 0.11.1", "shell-words", "tar", "tempfile", @@ -1013,7 +1013,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -1075,7 +1075,7 @@ checksum = "6ebaa1a2bf1290ab3bfe5a7b771d050ebffab2711c19a81691c683a5144a25de" dependencies = [ "curve25519-dalek", "ed25519", - "sha2 0.11.0", + "sha2 0.11.1", "subtle", ] @@ -1122,9 +1122,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826" [[package]] name = "errno" @@ -1315,7 +1315,7 @@ checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -1414,7 +1414,7 @@ checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -2274,9 +2274,9 @@ dependencies = [ [[package]] name = "keyring-core" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb1e621458ca9c51aa110bd0339d4751a056b9576bf1253aee1aa560dda0fc9d" +checksum = "29fbf4b551298c67925f38cf341a1ada797a75f1837bdf8ae1fc8e2ebc6efb59" dependencies = [ "log", ] @@ -2393,9 +2393,9 @@ checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" [[package]] name = "litrs" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" +checksum = "4744e383959f0db86ede514b809b1c53251889093803c05267acc7d4e7030d70" [[package]] name = "lock_api" @@ -2621,6 +2621,18 @@ dependencies = [ "jni-sys 0.3.1", ] +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags 2.13.2", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -3737,9 +3749,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.45" +version = "0.23.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" +checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf" dependencies = [ "aws-lc-rs", "once_cell", @@ -3751,9 +3763,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +checksum = "4a073f5dc7469f984c52ad2752b63b0807745133b6de880b7b64c1ac4c48aec4" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -3927,15 +3939,16 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] name = "serde_json" -version = "1.0.151" +version = "1.0.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" dependencies = [ + "foldhash 0.2.0", "indexmap", "itoa", "memchr", @@ -3952,7 +3965,7 @@ checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -3989,9 +4002,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +checksum = "47d7069beb7d6ac7b9acd1039986e73443f24234f41074da099d6f994ac9ad19" dependencies = [ "cfg-if", "cpufeatures 0.3.1", @@ -4094,9 +4107,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.16.2" +version = "1.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9395f0f0eee849a9b707b2f06bb92a6a422090e2123bb2ef8e87a0e61892a8e" +checksum = "5b3dc8af474f516a851ff4bd12db780f948b9250ad37211e4eec0bccea54e01b" [[package]] name = "smithay-client-toolkit" @@ -4274,9 +4287,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.6" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" dependencies = [ "proc-macro2", "quote", @@ -4300,7 +4313,7 @@ checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -4403,7 +4416,7 @@ checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -4471,9 +4484,9 @@ checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" [[package]] name = "tokio" -version = "1.53.2" +version = "1.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e95f91fcc7a621e8b030f6aa23c71fe9838ae2fb4d8118b75602a328f5144044" +checksum = "ce3335fa71841cda333a58d7615b03901380ecf09d59b3296d21f8bbac0dde4e" dependencies = [ "bytes", "libc", @@ -4717,9 +4730,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" +checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" [[package]] name = "unicode-linebreak" @@ -4875,7 +4888,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", "wasm-bindgen-shared", ] @@ -5605,9 +5618,9 @@ dependencies = [ [[package]] name = "winresource" -version = "0.1.31" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0986a8b1d586b7d3e4fe3d9ea39fb451ae22869dcea4aa109d287a374d866087" +checksum = "a2bc6d36480b829272a5bdabf08e6dca8004761c1da7e35efef800540c66221b" dependencies = [ "version_check", ] @@ -5722,7 +5735,7 @@ checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", "synstructure", ] @@ -5770,7 +5783,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", "zbus_names", "zvariant", "zvariant_utils", @@ -5804,18 +5817,18 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" -version = "0.8.62" +version = "0.8.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86502bf56ac7c77571a32e2647bb2a15894565e981fb2a48d7bde2d91c965a9d" +checksum = "e5fe1f8f1b06191a00962174c61aa5005e0bb391a6d80d07e24d115c01a92ed8" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.62" +version = "0.8.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5457206954b06561e2608c7e19cf58b1926586d999c246eebe4502f7e2039d1a" +checksum = "863ad3ac83293fb4d740aedbfdc9240dd8d1a50c1099acd76ce80ce7c7230c7f" dependencies = [ "proc-macro2", "quote", @@ -5839,7 +5852,7 @@ checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", "synstructure", ] @@ -5893,19 +5906,20 @@ checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] name = "zip" -version = "9.0.0" +version = "9.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711e8c9646791c49b7eb799a19c4ae70a9b8372ddc6a1fa4778defd1e2aa5d5e" +checksum = "c2d98561b612a84ae525331a7a094429ef8a5772514ed507ff6db5f54badc971" dependencies = [ "crc32fast", "flate2", "indexmap", "memchr", + "nix", "typed-path", "zopfli", ] @@ -5924,9 +5938,9 @@ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "zopfli" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +checksum = "aaf7fc5d30c28483d93805c4a5e12b05bbb52407fa67c5f8bd552374cd01fb11" dependencies = [ "bumpalo", "crc32fast", @@ -5958,7 +5972,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", "zvariant_utils", ] @@ -5971,6 +5985,6 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 3.0.6", + "syn 3.0.9", "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index 6175e12..1377e42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,10 +24,10 @@ colony-ui = "0.1.6" iced = { version = "0.14.0", features = ["tokio", "markdown", "advanced", "image-without-codecs"] } shell-words = "1.1.1" serde = { version = "1.0.229", features = ["derive"] } -serde_json = "1.0.151" +serde_json = "1.0.154" toml = "1.1.8" reqwest = { version = "0.13.5", default-features = false, features = ["json", "form", "stream", "rustls", "http2", "charset", "system-proxy"] } -tokio = { version = "1.53.2", features = ["sync", "time", "rt"] } +tokio = { version = "1.53.3", features = ["sync", "time", "rt"] } futures = "0.3.34" open = "5.4.4" base64 = "0.23.1" @@ -40,12 +40,12 @@ dirs = "7.0.0" # no implicit store at all. Colony never sets a default store and builds every # entry from the per-target store in src/oauth.rs, so the mock is reachable only # from the tests, and the plaintext copy goes only after the token reads back. -keyring-core = "1.0.0" +keyring-core = "1.0.1" semver = "1.0.28" -sha2 = "0.11.0" +sha2 = "0.11.1" # Pure-Rust ed25519 verification for signed launcher self-updates (no openssl). ed25519-dalek = { version = "3.0.0", default-features = false } -zip = { version = "9.0.0", default-features = false, features = ["deflate"] } +zip = { version = "9.0.3", default-features = false, features = ["deflate"] } tar = "0.4.46" flate2 = "1.1.10" # PNG-only decoder for per-app icons (no other codecs); pairs with iced's @@ -88,4 +88,4 @@ tempfile = "3.27.0" # Embeds the Windows version resource (build.rs). Without default features it # pulls in nothing but version_check, and it does nothing on other targets. [build-dependencies] -winresource = { version = "0.1.31", default-features = false } +winresource = { version = "0.1.34", default-features = false } From ae011255c4d528a9a9716e054a6679b8e5554ff4 Mon Sep 17 00:00:00 2001 From: MotherSphere Date: Wed, 14 Oct 2026 13:53:00 +0200 Subject: [PATCH 2/3] fix(ui): theme every widget from the Colony palette and localize the remaining labels Build the iced theme and the Markdown style from the active palette and the UI font, style the Back, Rescan and search widgets, draw Nerd Font glyphs in the glyph font, give the GitHub entry the sidebar selection style, localize the category chips, scale the grid's status columns with the text size, fix French glosses, give the caches section its own title key, replace em dashes and drop agent-tool entries from .gitignore. --- .github/ISSUE_TEMPLATE/feature_request.yml | 6 +- .github/workflows/aur-publish.yml | 12 ++-- .gitignore | 7 --- src/app.rs | 21 +++++-- src/download/files.rs | 10 ++-- src/download/http.rs | 2 +- src/download/install.rs | 4 +- src/download/launcher.rs | 2 +- src/github/catalog.rs | 4 +- src/github/http.rs | 2 +- src/github/releases.rs | 2 +- src/github/types.rs | 2 +- src/i18n/en.rs | 17 +++--- src/i18n/fr.rs | 21 +++---- src/i18n/mod.rs | 34 ++++++++++- src/message.rs | 2 +- src/oauth.rs | 2 +- src/persistence.rs | 10 ++-- src/scan.rs | 2 +- src/ui/app_grid.rs | 49 +++++++++++----- src/ui/detail.rs | 60 +++++++++++++++----- src/ui/github_panel.rs | 2 +- src/ui/markdown_blocks.rs | 6 +- src/ui/settings.rs | 4 +- src/ui/sidebar.rs | 65 ++++++++++------------ src/ui/theme.rs | 4 +- src/update/keyboard.rs | 2 +- src/update/mod.rs | 6 +- src/update/store.rs | 2 +- 29 files changed, 221 insertions(+), 141 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 640a6fc..8099700 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -36,9 +36,9 @@ body: attributes: label: Scope estimate (your best guess) options: - - Small — isolated UI tweak, setting, or doc change - - Medium — new panel, new config surface, extra API call - - Large — architectural change, new auth mode, new platform + - Small: isolated UI tweak, setting, or doc change + - Medium: new panel, new config surface, extra API call + - Large: architectural change, new auth mode, new platform validations: required: true diff --git a/.github/workflows/aur-publish.yml b/.github/workflows/aur-publish.yml index 261e063..d5e6bd1 100644 --- a/.github/workflows/aur-publish.yml +++ b/.github/workflows/aur-publish.yml @@ -1,13 +1,13 @@ # Bump the colony-bin AUR package to a given release tag. Uses a dedicated SSH # key (AUR_SSH_PRIVATE_KEY secret) registered as a secondary key on the -# MotherSphere AUR account — scope is limited to AUR push access, revocable by +# MotherSphere AUR account - scope is limited to AUR push access, revocable by # removing that key from the AUR profile if ever compromised. # # Invoked as a reusable workflow from release.yml after the release # assets are built, uploaded and signed. It deliberately does NOT trigger on # `release: published`: our releases are created by release-please with the # default GITHUB_TOKEN, and GitHub suppresses workflow triggers for events -# created by that token — the old trigger never fired once (colony-bin sat at +# created by that token - the old trigger never fired once (colony-bin sat at # 0.1.4 for nine releases). workflow_dispatch remains as a manual fallback. name: Publish colony-bin to AUR @@ -55,7 +55,7 @@ jobs: env: GH_TOKEN: ${{ github.token }} TAG: ${{ steps.tag.outputs.tag }} - # gh fails hard when the asset is missing — unlike the old bare curl, + # gh fails hard when the asset is missing - unlike the old bare curl, # which would happily save GitHub's 404 page and push its sha256 to # AUR, breaking installs for every colony-bin user. run: | @@ -110,7 +110,7 @@ jobs: echo "AUR currently at pkgver=${CURRENT}, target=${PKGVER}" if [ "${CURRENT}" = "${PKGVER}" ]; then echo "changed=false" >> "$GITHUB_OUTPUT" - echo "pkgver already at ${PKGVER} — nothing to do." + echo "pkgver already at ${PKGVER}, nothing to do." else echo "changed=true" >> "$GITHUB_OUTPUT" # Carried to the next step so it can rewrite every occurrence of @@ -126,7 +126,7 @@ jobs: OLDVER: ${{ steps.check.outputs.oldver }} run: | cd aur-repo - # Reset pkgrel to 1 because pkgver changed — standard AUR convention. + # Reset pkgrel to 1 because pkgver changed - standard AUR convention. sed -i "s/^pkgver=.*/pkgver=${PKGVER}/" PKGBUILD sed -i "s/^pkgrel=.*/pkgrel=1/" PKGBUILD # The binary hash lives in its own _binsha variable (the icon's @@ -170,7 +170,7 @@ jobs: cd aur-repo git add PKGBUILD .SRCINFO if git diff --cached --quiet; then - echo "No changes — colony-bin already at ${PKGVER}. Skipping push." + echo "No changes: colony-bin already at ${PKGVER}. Skipping push." exit 0 fi git commit -m "colony-bin: auto-bump to ${PKGVER}" diff --git a/.gitignore b/.gitignore index bee693b..53eaa21 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,2 @@ /target **/*.rs.bk - -# Local audit/workflow scratch state (never committed) -/.audit-tmp/ - -# Playwright MCP scratch -/.playwright-mcp/ -.claude/ diff --git a/src/app.rs b/src/app.rs index 0f24197..69f9bdd 100644 --- a/src/app.rs +++ b/src/app.rs @@ -534,12 +534,21 @@ impl App { } } + /// The iced theme built from the active Colony palette, so the widgets that + /// keep iced's default styling (scrollbars, Markdown code blocks, text + /// selection) follow the chosen theme, accent and high contrast instead of + /// iced's stock Light or Dark. pub(crate) fn theme(&self) -> Theme { - use colony_ui::ColorExt; - if ui::theme::active_palette().bg_primary.is_light() { - Theme::Light - } else { - Theme::Dark - } + Theme::custom( + "Colony", + iced::theme::Palette { + background: Palette::BG_PRIMARY(), + text: Palette::TEXT_PRIMARY(), + primary: Palette::ACCENT(), + success: Palette::SUCCESS(), + warning: Palette::WARNING(), + danger: Palette::ERROR(), + }, + ) } } diff --git a/src/download/files.rs b/src/download/files.rs index b90993a..719b619 100644 --- a/src/download/files.rs +++ b/src/download/files.rs @@ -202,7 +202,7 @@ fn extract_from_tar_gz( /// Extract a binary from an archive based on its extension. /// /// `asset_name` is the original release asset filename used only to detect the -/// archive type — `archive_path` may be a staging file (e.g. `foo.zip.part`) +/// archive type - `archive_path` may be a staging file (e.g. `foo.zip.part`) /// whose own extension must not be used for detection. pub(super) fn extract_binary_from_archive( archive_path: &std::path::Path, @@ -213,9 +213,9 @@ pub(super) fn extract_binary_from_archive( // `binary_name` is a manifest-supplied string that becomes a path component // in EVERY branch below, so the traversal guard is hoisted here, before the // dispatch. It used to live only inside the zip and tar extractors, which - // left the raw-binary branch able to write outside `dest_dir` — and, after - // the caller's chmod 0755 and desktop entry, to gain execution at next login - // — from a hostile manifest. The extractors keep their own call so they stay + // left the raw-binary branch able to write outside `dest_dir` (and, after + // the caller's chmod 0755 and desktop entry, to gain execution at next login) + // from a hostile manifest. The extractors keep their own call so they stay // safe in isolation. ensure_safe_component(binary_name)?; if asset_name.ends_with(".zip") { @@ -227,7 +227,7 @@ pub(super) fn extract_binary_from_archive( let _ = std::fs::remove_file(archive_path); result } else { - // Raw binary (e.g. .exe, no archive extension) — rename to binary_name in dest_dir + // Raw binary (e.g. .exe, no archive extension) - rename to binary_name in dest_dir let dest = dest_dir.join(binary_name); replace_file(archive_path, &dest)?; Ok(dest) diff --git a/src/download/http.rs b/src/download/http.rs index c42cca6..3d0fe7f 100644 --- a/src/download/http.rs +++ b/src/download/http.rs @@ -441,7 +441,7 @@ pub(crate) fn build_url(base: &str, segments: &[&str]) -> Result { /// /// Gate for everything handed to the desktop's URI opener: link destinations in /// remote Markdown (READMEs, release notes) reach it verbatim, and `open::that` -/// is not a browser call — it execs `xdg-open`/`gio open` on Linux and +/// is not a browser call - it execs `xdg-open`/`gio open` on Linux and /// `Start-Process` on Windows, both of which dispatch `file://`, UNC paths and /// any registered `x-scheme-handler/*`. A `file://` or custom-scheme link in a /// hostile README would otherwise be a one-click execution primitive that diff --git a/src/download/install.rs b/src/download/install.rs index ae4ae59..77b0aff 100644 --- a/src/download/install.rs +++ b/src/download/install.rs @@ -58,7 +58,7 @@ pub async fn download_release_asset( record_asset, require_signature, } = install; - // The manifest-supplied filename becomes a local path — guard it against + // The manifest-supplied filename becomes a local path - guard it against // traversal (`../`, absolute paths) before joining, mirroring the archive // `binary` guard. ensure_safe_component(&filename)?; @@ -194,7 +194,7 @@ pub async fn download_release_asset( } // Integrity check, archive extraction and the atomic promotion are - // CPU/IO-bound — run them on a blocking thread. Any failure removes the + // CPU/IO-bound - run them on a blocking thread. Any failure removes the // temp file and leaves the previous install untouched. let final_path = { let temp_path = temp_path.clone(); diff --git a/src/download/launcher.rs b/src/download/launcher.rs index 5176f70..211ba1a 100644 --- a/src/download/launcher.rs +++ b/src/download/launcher.rs @@ -132,7 +132,7 @@ pub async fn download_launcher_asset( } // Persist the signature next to the staged binary so apply_launcher_update - // can re-verify at install time — closing any window in which the staged + // can re-verify at install time - closing any window in which the staged // file could be swapped between download and apply. The metadata sidecar is // staged for the same reason. let sig_path = staged_signature_path(&dest_path); diff --git a/src/github/catalog.rs b/src/github/catalog.rs index 5a4e5e0..64b88a3 100644 --- a/src/github/catalog.rs +++ b/src/github/catalog.rs @@ -264,7 +264,7 @@ async fn fetch_readme(client: &reqwest::Client, repo_name: &str) -> Result Result> { @@ -288,7 +288,7 @@ async fn fetch_license(client: &reqwest::Client, repo_name: &str) -> Result Result { - // Not Modified — return cached body + // Not Modified - return cached body if let Ok(cache) = HTTP_CACHE.lock() { if let Some(entry) = cache.entries.get(url) { tracing::debug!("Cache hit (304) for {}", url); diff --git a/src/github/releases.rs b/src/github/releases.rs index d3cab92..001bd73 100644 --- a/src/github/releases.rs +++ b/src/github/releases.rs @@ -304,7 +304,7 @@ pub fn parse_version_tag(tag: &str) -> Option { /// `Ok(Some(tag))` = an update to `tag` is available. `Ok(None)` = the check /// RAN and there is nothing to install (either the app is not installed at all, /// or it is current). `Err` = the check could NOT run, and the caller must not -/// turn that into "up to date" — the same fail-loud contract +/// turn that into "up to date" - the same fail-loud contract /// [`check_launcher_update`] already keeps for the launcher's own check. /// /// `pinned_tag` is compared directly unless it is "latest", in which case the diff --git a/src/github/types.rs b/src/github/types.rs index 9373124..6cb1975 100644 --- a/src/github/types.rs +++ b/src/github/types.rs @@ -13,7 +13,7 @@ pub struct ReleaseFileEntry { pub file: Option, /// Substring pattern to match against release asset names (case-insensitive). /// Colony fetches the release assets list and picks the one matching this pattern. - /// Mutually exclusive with `file` — use one or the other. + /// Mutually exclusive with `file` - use one or the other. pub file_pattern: Option, /// Optional binary name inside an archive. When present, the downloaded file /// is treated as an archive (.zip / .tar.gz) and Colony extracts this binary. diff --git a/src/i18n/en.rs b/src/i18n/en.rs index e72cdac..22165cc 100644 --- a/src/i18n/en.rs +++ b/src/i18n/en.rs @@ -29,7 +29,7 @@ pub(super) fn insert_all(strings: &mut HashMap) { ); strings.insert( "github_copy_hint".into(), - "Click to copy — Waiting for authorization...".into(), + "Click to copy. Waiting for authorization...".into(), ); strings.insert("github_connecting".into(), "Connecting...".into()); strings.insert("github_connected".into(), "Connected".into()); @@ -97,7 +97,7 @@ pub(super) fn insert_all(strings: &mut HashMap) { ); strings.insert( "oauth_device_failed".into(), - "GitHub sign-in failed: {error} — {desc}".into(), + "GitHub sign-in failed: {error} ({desc})".into(), ); strings.insert("github_api_error".into(), "GitHub error: {error}".into()); strings.insert("scan_error".into(), "Error: {error}".into()); @@ -120,7 +120,7 @@ pub(super) fn insert_all(strings: &mut HashMap) { ); strings.insert( "update_check_failed".into(), - "Could not check {count} app(s) for updates — they may not be up to date".into(), + "Could not check {count} app(s) for updates, so they may not be up to date".into(), ); // Sidebar section names (localized) @@ -159,11 +159,11 @@ pub(super) fn insert_all(strings: &mut HashMap) { // Favorites strings.insert("add_favorite".into(), "Add to favorites".into()); - // First launch — carousel (3 steps) + // First launch: carousel (3 steps) strings.insert("welcome_title".into(), "Welcome to Colony".into()); strings.insert("welcome_desc".into(), "The centralized launcher for the Project-Colony ecosystem. Discover, install and launch apps in one click.".into()); - // Step 1 — interface tour - // Step 2 — GitHub + ready + // Step 1: interface tour + // Step 2: GitHub + ready // Navigation strings.insert("welcome_start".into(), "Let's go!".into()); strings.insert("welcome_next".into(), "Next".into()); @@ -174,7 +174,7 @@ pub(super) fn insert_all(strings: &mut HashMap) { // Guided tutorial (spotlight over real UI) strings.insert("tut_sidebar_title".into(), "Categories".into()); - strings.insert("tut_sidebar_desc".into(), "Filter your apps by type — games, tools, favorites — or by origin (Colony ecosystem vs. system). The sidebar stays visible at all times.".into()); + strings.insert("tut_sidebar_desc".into(), "Filter your apps by type (games, tools, favorites) or by origin (the Colony ecosystem or your system). The sidebar stays visible at all times.".into()); strings.insert("tut_search_title".into(), "Search".into()); strings.insert( "tut_search_desc".into(), @@ -367,7 +367,8 @@ pub(super) fn insert_all(strings: &mut HashMap) { strings.insert("section_security".into(), "Security".into()); strings.insert("language_changed".into(), "Language changed".into()); strings.insert("clear_caches".into(), "Clear store caches".into()); - strings.insert("clear_caches_desc".into(), "Removes cached descriptions and icons (they re-download on the next refresh). Installed applications are not touched.".into()); + strings.insert("settings_section_caches".into(), "Caches".into()); + strings.insert("settings_caches_desc".into(), "Removes cached descriptions and icons (they re-download on the next refresh). Installed applications are not touched.".into()); strings.insert("caches_cleared".into(), "{count} cache(s) removed".into()); strings.insert("launcher_update_system_managed".into(), "Update {version} is available - this install is managed by the package manager, update via 'pacman -Syu' (colony-bin)".into()); // Detail tabs diff --git a/src/i18n/fr.rs b/src/i18n/fr.rs index 569b23d..81a30b6 100644 --- a/src/i18n/fr.rs +++ b/src/i18n/fr.rs @@ -28,7 +28,7 @@ pub(super) fn insert_all(strings: &mut HashMap) { ); strings.insert( "github_copy_hint".into(), - "Cliquez pour copier — En attente d'autorisation...".into(), + "Cliquez pour copier. En attente d'autorisation...".into(), ); strings.insert("github_connecting".into(), "Connexion en cours...".into()); strings.insert("github_connected".into(), "Connecté".into()); @@ -66,7 +66,7 @@ pub(super) fn insert_all(strings: &mut HashMap) { strings.insert("offered_version".into(), "Version {version}".into()); strings.insert( "no_release_unrecognized".into(), - "Aucune version installable - cette application ne publie pas d'assets reconnus par Colony" + "Aucune version installable : cette application ne publie aucun fichier de version reconnu par Colony" .into(), ); strings.insert( @@ -109,7 +109,7 @@ pub(super) fn insert_all(strings: &mut HashMap) { ); strings.insert( "oauth_device_failed".into(), - "Échec de la connexion GitHub : {error} — {desc}".into(), + "Échec de la connexion GitHub : {error} ({desc})".into(), ); strings.insert("github_api_error".into(), "Erreur GitHub : {error}".into()); strings.insert("scan_error".into(), "Erreur : {error}".into()); @@ -135,7 +135,7 @@ pub(super) fn insert_all(strings: &mut HashMap) { ); strings.insert( "update_check_failed".into(), - "Impossible de vérifier {count} application(s) — elles ne sont peut-être pas à jour".into(), + "Impossible de vérifier {count} application(s) : elles ne sont peut-être pas à jour".into(), ); // Sidebar section names (localized) @@ -174,11 +174,11 @@ pub(super) fn insert_all(strings: &mut HashMap) { // Favorites strings.insert("add_favorite".into(), "Ajouter aux favoris".into()); - // First launch — carousel (3 steps) + // First launch: carousel (3 steps) strings.insert("welcome_title".into(), "Bienvenue dans Colony".into()); strings.insert("welcome_desc".into(), "Le lanceur centralisé de l'écosystème Project-Colony. Découvrez, installez et lancez vos apps en un clic.".into()); - // Step 1 — interface tour - // Step 2 — GitHub + ready + // Step 1: interface tour + // Step 2: GitHub + ready // Navigation strings.insert("welcome_start".into(), "C'est parti !".into()); strings.insert("welcome_next".into(), "Suivant".into()); @@ -192,7 +192,7 @@ pub(super) fn insert_all(strings: &mut HashMap) { // Guided tutorial (spotlight on the real UI) strings.insert("tut_sidebar_title".into(), "Les catégories".into()); - strings.insert("tut_sidebar_desc".into(), "Filtrez vos apps par type : jeux, outils, favoris, ou par origine (écosystème Colony vs. système). La barre latérale reste toujours visible.".into()); + strings.insert("tut_sidebar_desc".into(), "Filtrez vos apps par type (jeux, outils, favoris) ou par origine (écosystème Colony ou système). La barre latérale reste toujours visible.".into()); strings.insert("tut_search_title".into(), "La recherche".into()); strings.insert("tut_search_desc".into(), "Tapez le nom d'une app pour la retrouver instantanément, peu importe la catégorie sélectionnée.".into()); strings.insert("tut_grid_title".into(), "Vos applications".into()); @@ -402,14 +402,15 @@ pub(super) fn insert_all(strings: &mut HashMap) { "clear_caches".into(), "Vider les caches du catalogue".into(), ); - strings.insert("clear_caches_desc".into(), "Supprime les descriptions et icônes mises en cache (elles sont retéléchargées à la prochaine actualisation). Les applications installées ne sont pas touchées.".into()); + strings.insert("settings_section_caches".into(), "Données en cache".into()); + strings.insert("settings_caches_desc".into(), "Supprime les descriptions et icônes mises en cache (elles sont retéléchargées à la prochaine actualisation). Les applications installées ne sont pas touchées.".into()); strings.insert( "caches_cleared".into(), "{count} cache(s) supprimé(s)".into(), ); strings.insert("launcher_update_system_managed".into(), "Mise à jour {version} disponible - cette installation est gérée par le gestionnaire de paquets, mettez à jour via « pacman -Syu » (colony-bin)".into()); // Detail tabs - strings.insert("tab_readme".into(), "ReadMe".into()); + strings.insert("tab_readme".into(), "Présentation".into()); strings.insert("tab_license".into(), "Licence".into()); strings.insert("tab_changelog".into(), "Journal des modifications".into()); strings.insert("tab_not_available".into(), "Non disponible".into()); diff --git a/src/i18n/mod.rs b/src/i18n/mod.rs index 706f4b2..55d692f 100644 --- a/src/i18n/mod.rs +++ b/src/i18n/mod.rs @@ -146,7 +146,7 @@ fn detect_language() -> String { #[cfg(test)] mod tests { /// The active locale is process-global, so tests that swap it must not run - /// concurrently with each other — the failure is a value from whichever + /// concurrently with each other - the failure is a value from whichever /// language the other test happened to set. static LOCALE_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(()); @@ -156,7 +156,7 @@ mod tests { } /// Theme and accent labels live in colony-ui, not in en.rs / fr.rs. They - /// must still resolve through `t()`, and must follow the active locale — + /// must still resolve through `t()`, and must follow the active locale - /// otherwise the theme picker would sit in English inside a French page. #[test] fn shared_labels_resolve_through_colony_ui_and_follow_the_locale() { @@ -217,7 +217,7 @@ mod tests { let only_en: Vec<&&String> = en_keys.difference(&fr_keys).collect(); assert!( only_fr.is_empty() && only_en.is_empty(), - "Locale key mismatch — only in fr: {only_fr:?}; only in en: {only_en:?}" + "Locale key mismatch: only in fr: {only_fr:?}; only in en: {only_en:?}" ); } @@ -301,6 +301,34 @@ mod tests { ); } + /// A local app's category chip shows `section_display_name` of its + /// `AppCategory` Debug name. A variant with no section key would fall back + /// to the English enum name inside the French grid. + #[test] + fn every_app_category_chip_is_localized() { + use crate::scan::AppCategory::*; + with_locale(|| { + set_language("fr"); + for category in [ + Development, + Graphics, + Network, + Office, + Multimedia, + System, + Utility, + Security, + Game, + Other, + ] { + let raw = format!("{category:?}"); + assert_ne!(section_display_name(&raw), raw, "no French label"); + } + assert_eq!(section_display_name("utility"), "Utilitaires"); + set_language("en"); + }); + } + #[test] fn t_fmt_substitution() { with_locale(|| { diff --git a/src/message.rs b/src/message.rs index 4dffb1c..c5ad403 100644 --- a/src/message.rs +++ b/src/message.rs @@ -39,7 +39,7 @@ pub enum Message { CancelDownload, LaunchColonyApp(PathBuf), UninstallColonyApp(String), // repo_name - ConfirmUninstall(String), // repo_name — show confirmation + ConfirmUninstall(String), // repo_name - show confirmation CancelUninstall, CopyToClipboard(String), DismissNotification(u64), diff --git a/src/oauth.rs b/src/oauth.rs index 3a13be2..beef0b4 100644 --- a/src/oauth.rs +++ b/src/oauth.rs @@ -261,7 +261,7 @@ fn token_path() -> PathBuf { auth_dir.join("github_token.json") } // Only the creation failed; ask for the same path without creating it - // rather than rebuilding the layout by hand — spelling it out here with + // rather than rebuilding the layout by hand - spelling it out here with // dirs::config_dir() put the token in Roaming on Windows, which is the // one place the shared layout says it must not be. Err(_) => colony_ui::paths::locate::config_dir("Colony") diff --git a/src/persistence.rs b/src/persistence.rs index 3433f23..7a852a1 100644 --- a/src/persistence.rs +++ b/src/persistence.rs @@ -368,7 +368,7 @@ pub fn load_repo_icon(repo_name: &str) -> Option> { /// The shared install root: `/Colony/apps/`. /// -/// Deliberately a sibling of Colony's own directory rather than a child — +/// Deliberately a sibling of Colony's own directory rather than a child - /// installed programs belong to the ecosystem, not to the launcher. Does not /// create the directory; callers that write do that themselves. pub fn colony_apps_dir() -> Result { @@ -386,7 +386,7 @@ pub fn installed_app_path(repo: &ColonyRepo) -> Option { } else if let Some(ref file) = entry.file { file.clone() } else { - // filePattern was used — check saved resolved asset name + // filePattern was used - check saved resolved asset name load_installed_asset(&repo.name)? }; // The filename comes straight from the repo's own manifest: the same @@ -1430,14 +1430,14 @@ mod path_migration_tests { "{rel}" ); } - // The source survives a copy — that is the whole point of the fallback. + // The source survives a copy - that is the whole point of the fallback. assert!(from.join("a.json").exists()); let _ = std::fs::remove_dir_all(&root); } /// The unit tests above cover `relocate` and `copy_tree` in isolation. - /// This exercises `migrate_legacy_paths` itself — the thing that actually - /// runs on a user's machine — against a planted legacy layout. + /// This exercises `migrate_legacy_paths` itself - the thing that actually + /// runs on a user's machine - against a planted legacy layout. /// /// Linux only: it drives the XDG variables, and on Linux the config root is /// unchanged by the migration, so what it proves is the cache move, which diff --git a/src/scan.rs b/src/scan.rs index 755190d..31e7733 100644 --- a/src/scan.rs +++ b/src/scan.rs @@ -45,7 +45,7 @@ impl AppCategory { } } - /// FontAwesome (solid) glyph representing this category — one shared icon + /// FontAwesome (solid) glyph representing this category - one shared icon /// language for app tiles and, later, the sidebar. pub fn glyph(&self) -> &'static str { match self { diff --git a/src/ui/app_grid.rs b/src/ui/app_grid.rs index b691079..4305427 100644 --- a/src/ui/app_grid.rs +++ b/src/ui/app_grid.rs @@ -11,7 +11,7 @@ use crate::state::App; use crate::ui::glyph_label; use crate::ui::theme::{self, Palette}; -/// Filled Material-Design hexagon glyph from the embedded Nerd Font — the +/// Filled Material-Design hexagon glyph from the embedded Nerd Font - the /// Colony "cell" that is the app tile, the brand mark, and the status badge. const HEX_FILLED: &str = "\u{f02d8}"; @@ -101,13 +101,13 @@ impl App { /// The 54px hexagon "cell": a filled hexagon in the app's stable tint, with /// the category glyph centered on top. Both glyphs are centered via the text /// widget's own alignment (width/height Fill + centered) so they co-register - /// in the same box. Zero assets — both are embedded font glyphs. + /// in the same box. Zero assets - both are embedded font glyphs. fn hex_tile<'a>(&self, tint: Color, glyph: &'static str) -> Element<'a, Message> { use iced::alignment::{Horizontal, Vertical}; let fg = theme::contrast_on(tint); let hexagon = text(HEX_FILLED) .size(sz(50)) - .font(ui_font()) + .font(fonts::glyph_font()) .color(tint) .width(Fill) .height(Fill) @@ -115,10 +115,12 @@ impl App { .align_y(Vertical::Center); // Render the category glyph in the SAME font as the hexagon (the Nerd // Font carries the FontAwesome glyphs too), so the two share identical - // metrics and centre on the exact same point — no manual nudging. + // metrics and centre on the exact same point - no manual nudging. Both + // use the glyph font, never ui_font(): with the dyslexia font on, that + // is OpenDyslexic, which has none of these glyphs. let cat = text(glyph) .size(sz(15)) - .font(ui_font()) + .font(fonts::glyph_font()) .color(fg) .width(Fill) .height(Fill) @@ -135,7 +137,7 @@ impl App { /// The 54x54 card tile: the app's real decoded icon when we have one, /// otherwise the tinted category hexagon. Clones a cheap (Arc-backed) handle - /// out of state — never decodes in `view()`. + /// out of state - never decodes in `view()`. fn icon_tile<'a>( &self, repo_name: &str, @@ -211,7 +213,7 @@ impl App { let head = row![ text(HEX_FILLED) .size(sz(11)) - .font(ui_font()) + .font(fonts::glyph_font()) .color(Palette::WARNING()), text(crate::i18n::t("status_update")) .size(sz(12)) @@ -236,7 +238,7 @@ impl App { let head = row![ text(HEX_FILLED) .size(sz(11)) - .font(ui_font()) + .font(fonts::glyph_font()) .color(Palette::SUCCESS()), text(crate::i18n::t("status_installed")) .size(sz(12)) @@ -327,7 +329,26 @@ impl App { .on_input(Message::SearchChanged) .padding(12) .size(sz(16)) - .width(Fill); + .font(ui_font()) + .width(Fill) + .style(|_theme, status| { + let border = match status { + text_input::Status::Focused { .. } => Palette::ACCENT(), + _ => Palette::BORDER_SUBTLE(), + }; + text_input::Style { + background: Palette::BG_INPUT().into(), + border: iced::Border { + color: border, + width: 1.0, + radius: 8.0.into(), + }, + icon: Palette::TEXT_DIMMER(), + placeholder: Palette::TEXT_PLACEHOLDER(), + value: Palette::TEXT_PRIMARY(), + selection: Palette::BG_SELECTED(), + } + }); let search = container(search_input) .id(crate::ui::tutorial::ID_SEARCH) .width(Fill); @@ -574,7 +595,8 @@ impl App { }; // Chip row: category, language, present platforms. - let mut chips = row![self.chip(repo.manifest.category.clone())].spacing(7); + let mut chips = + row![self.chip(crate::i18n::section_display_name(&repo.manifest.category))].spacing(7); if !repo.language.is_empty() && repo.language != "Unknown" { chips = chips.push(self.chip(repo.language.clone())); } @@ -585,7 +607,7 @@ impl App { let mid = column![name, desc, chips].spacing(6).width(Fill); let status = container(self.repo_status(repo)) - .width(Length::Fixed(136.0)) + .width(Length::Fixed(136.0 * colony_ui::font_scale())) .align_x(iced::alignment::Horizontal::Right); let content = row![tile, mid, status] @@ -667,7 +689,8 @@ impl App { .font(ui_font_medium()) .color(Palette::TEXT_PRIMARY()); - let chips = row![self.chip(format!("{:?}", app.category))].spacing(7); + let category = crate::i18n::section_display_name(&format!("{:?}", app.category)); + let chips = row![self.chip(category)].spacing(7); let mid = column![name, chips].spacing(6).width(Fill); // Favorites were a dead feature for local apps: the section filtered @@ -727,7 +750,7 @@ impl App { .spacing(6) .align_y(iced::Alignment::Center), ) - .width(Length::Fixed(170.0)) + .width(Length::Fixed(170.0 * colony_ui::font_scale())) .align_x(iced::alignment::Horizontal::Right); let content = row![tile, mid, status] diff --git a/src/ui/detail.rs b/src/ui/detail.rs index 80289da..b7bb701 100644 --- a/src/ui/detail.rs +++ b/src/ui/detail.rs @@ -42,16 +42,54 @@ impl<'a> markdown::Viewer<'a, Message> for ColonyMdViewer { ) .width(Length::Fill) .padding(settings.code_size / 4) - .class(::code_block()) + .style(|_theme| container::Style { + background: Some(Palette::BG_CARD().into()), + text_color: Some(Palette::TEXT_PRIMARY()), + border: iced::Border { + color: Palette::BORDER_SUBTLE(), + width: 1.0, + radius: 6.0.into(), + }, + ..Default::default() + }) .into() } } +/// Markdown styling from the Colony palette and fonts. iced's +/// `Style::from_palette` sets white inline code on near-black and the system +/// monospace font whatever the theme, and its body font is the default font +/// captured at startup, so READMEs ignored the dyslexia font until a restart. +/// JetBrains Mono is already monospace, so code needs no fourth font. +fn md_style() -> markdown::Style { + let font = ui_font(); + markdown::Style { + font, + inline_code_highlight: markdown::Highlight { + background: Palette::BG_CARD().into(), + border: iced::border::rounded(4), + }, + inline_code_padding: iced::padding::left(1).right(1), + inline_code_color: Palette::TEXT_PRIMARY(), + inline_code_font: font, + code_block_font: font, + link_color: Palette::ACCENT(), + } +} + impl App { pub(crate) fn view_colony_detail<'a>(&'a self, repo: &'a ColonyRepo) -> Element<'a, Message> { let back_button = button(text(crate::i18n::t("back")).size(sz(13)).font(ui_font())) .on_press(Message::ColonyRepoBack) - .padding([8, 16]); + .padding([8, 16]) + .style(|_theme, status| { + crate::ui::theme::action_button_style( + status, + Palette::BTN_DEFAULT(), + Palette::BTN_HOVER(), + Palette::BTN_PRESSED(), + ) + }); let title = text(repo.display_name()) .size(sz(24)) @@ -102,10 +140,7 @@ impl App { // Parsed Markdown comes from self.detail_blocks, kept up-to-date by the // update-side helper. Placeholder text falls back to plain muted text. - let md_settings = markdown::Settings::with_text_size( - sz(14), - markdown::Style::from_palette(iced::Theme::Dark.palette()), - ); + let md_settings = markdown::Settings::with_text_size(sz(14), md_style()); let description: Element<'_, Message> = if is_placeholder { text(crate::i18n::t("tab_not_available")) .size(sz(14)) @@ -124,7 +159,7 @@ impl App { .font(ui_font()) .color(Palette::TEXT_MUTED()); - // Favorite button — FontAwesome 6 Free, solid/regular variant for filled/empty. + // Favorite button - FontAwesome 6 Free, solid/regular variant for filled/empty. // Same codepoint \u{f005} in both variants; the weight picks the glyph. let is_fav = self.is_favorite(&repo.name); let fav_color = if is_fav { @@ -254,7 +289,7 @@ impl App { let current_platform = github::current_platform_key(); let action_row = if let Some(app_path) = installed_path { - // App is installed — show Launch + Update buttons, and uninstall trash button + // App is installed - show Launch + Update buttons, and uninstall trash button let launch_label = crate::i18n::t_fmt("launch", &[("name", &repo.manifest.name)]); let launch_btn = button(glyph_label("\u{f04b}", launch_label, 14, ui_font_medium())) .on_press_maybe(if self.is_downloading { @@ -334,7 +369,7 @@ impl App { .spacing(12) .align_y(iced::Alignment::Center) } else { - // Not installed — single download button for current platform + // Not installed - single download button for current platform let spacer: Element<'_, Message> = container(text("")).width(Fill).into(); if repo.manifest.release_files.contains_key(current_platform) { let repo_name = repo.name.clone(); @@ -423,7 +458,7 @@ impl App { .align_y(iced::Alignment::Center); // Wrap markdown in a Shrink-sized container so the outer scrollable - // sees the true content height (iced #2630 — markdown rich_text can + // sees the true content height (iced #2630 - markdown rich_text can // otherwise hide overflow from the parent). let desc_container = container(description) .width(Length::Shrink) @@ -454,10 +489,7 @@ impl App { { match self.release_notes.get(&repo.name) { Some((tag, blocks)) if tag == new_tag => { - let md_settings = markdown::Settings::with_text_size( - sz(13), - markdown::Style::from_palette(iced::Theme::Dark.palette()), - ); + let md_settings = markdown::Settings::with_text_size(sz(13), md_style()); let notes_title = text(crate::i18n::t_fmt("whats_new", &[("version", new_tag)])) .size(sz(14)) diff --git a/src/ui/github_panel.rs b/src/ui/github_panel.rs index 3ee2dbd..e7c4aaa 100644 --- a/src/ui/github_panel.rs +++ b/src/ui/github_panel.rs @@ -173,7 +173,7 @@ impl App { session .username .as_ref() - .map(|u| format!(" — {u}")) + .map(|u| format!(" ({u})")) .unwrap_or_default() ); let user_label: Element<'_, Message> = if self.is_fetching_repos { diff --git a/src/ui/markdown_blocks.rs b/src/ui/markdown_blocks.rs index 4ece29f..1c67af5 100644 --- a/src/ui/markdown_blocks.rs +++ b/src/ui/markdown_blocks.rs @@ -4,9 +4,9 @@ //! two constructs can be rendered with native Colony widgets instead of the //! default iced markdown output: //! -//! - **shields.io badges** — `[![alt](img.shields.io/badge/..)](link)` lines +//! - **shields.io badges**: `[![alt](img.shields.io/badge/..)](link)` lines //! become colored pills matching our palette (no network round-trip). -//! - **GFM pipe tables** — parsed line-based and rendered via iced's `table` +//! - **GFM pipe tables**: parsed line-based and rendered via iced's `table` //! widget directly, skipping iced markdown's internal horizontal //! `scrollable` that captures vertical wheel events. //! @@ -151,7 +151,7 @@ fn try_parse_badges(lines: &[&str]) -> Option<(Vec, usize)> { } } -/// Accept either `[![alt](img)](link)` or `![alt](img)` — returns a Badge +/// Accept either `[![alt](img)](link)` or `![alt](img)` - returns a Badge /// when the image URL is a shields.io `/badge/{label}-{value}-{color}`. fn parse_badge_line(line: &str) -> Option { // Find the `![` of the image. diff --git a/src/ui/settings.rs b/src/ui/settings.rs index 644f540..e1f3cf5 100644 --- a/src/ui/settings.rs +++ b/src/ui/settings.rs @@ -217,8 +217,8 @@ impl App { let caches = self.section( typo, "caches", - "clear_caches", - "clear_caches_desc", + "settings_section_caches", + "settings_caches_desc", vec![action_button( typo, "\u{f1f8}", diff --git a/src/ui/sidebar.rs b/src/ui/sidebar.rs index 6c961bc..8e22604 100644 --- a/src/ui/sidebar.rs +++ b/src/ui/sidebar.rs @@ -39,55 +39,37 @@ impl App { .id(crate::ui::tutorial::ID_SIDEBAR_CATS) .height(Length::Fill); - // GitHub button — Nerd Font icon (U+F09B) + // GitHub button: Nerd Font icon (U+F09B). Styled like a section entry, + // with the open menu as the selected state. The texts take the button's + // text colour, except the accent that marks a connected account while + // the menu is closed (the open state's accent fill would swallow it). let github_connected = matches!(self.github_state, GitHubState::Connected { .. }); - let github_icon_color = if github_connected { - Palette::ACCENT() - } else if self.show_github_menu { - Palette::TEXT_PRIMARY() - } else { - Palette::TEXT_DIM() + let show_menu = self.show_github_menu; + let connected_mark = |t: iced::widget::Text<'static>| { + if github_connected && !show_menu { + t.color(Palette::ACCENT()) + } else { + t + } }; let mut github_btn_content = row![ - text("\u{f09b}") - .size(sz(18)) - .font(fonts::glyph_font()) - .color(github_icon_color), - text("GitHub") - .size(sz(13)) - .font(ui_font()) - .color(github_icon_color), + connected_mark(text("\u{f09b}").size(sz(18)).font(fonts::glyph_font())), + text("GitHub").size(sz(13)).font(ui_font()), ] .spacing(10) .align_y(iced::Alignment::Center); if github_connected { - github_btn_content = github_btn_content.push( - text("\u{f00c}") - .size(sz(13)) - .font(fonts::glyph_font()) - .color(github_icon_color), - ); + github_btn_content = github_btn_content.push(connected_mark( + text("\u{f00c}").size(sz(13)).font(fonts::glyph_font()), + )); } - let show_menu = self.show_github_menu; let github_btn = button(github_btn_content) .on_press(Message::ToggleGitHubMenu) .padding([10, 14]) .width(Fill) - .style(move |_theme, _status| button::Style { - background: Some( - if show_menu { - Palette::BG_SELECTED() - } else { - iced::Color::TRANSPARENT - } - .into(), - ), - text_color: Palette::TEXT_PRIMARY(), - border: iced::Border::default().rounded(6), - ..Default::default() - }); + .style(widgets::selection_style(show_menu)); let rescan_label: Element<'_, Message> = if self.is_scanning { glyph_label("\u{f110}", crate::i18n::t("scanning"), 13, ui_font()).into() @@ -97,7 +79,18 @@ impl App { .font(ui_font()) .into() }; - let rescan_btn_base = button(rescan_label).padding([8, 16]).width(Fill); + let rescan_btn_base = + button(rescan_label) + .padding([8, 16]) + .width(Fill) + .style(|_theme, status| { + crate::ui::theme::action_button_style( + status, + Palette::BTN_DEFAULT(), + Palette::BTN_HOVER(), + Palette::BTN_PRESSED(), + ) + }); let rescan_btn = if self.is_scanning { rescan_btn_base } else { diff --git a/src/ui/theme.rs b/src/ui/theme.rs index 54ffe28..f22eb8c 100644 --- a/src/ui/theme.rs +++ b/src/ui/theme.rs @@ -1,4 +1,4 @@ -//! Colony's theme — now shared with the rest of the ecosystem. +//! Colony's theme - now shared with the rest of the ecosystem. //! //! The 38-field palette, every theme palette, the `(family, variant)` //! resolver, the picker catalog, the accent overrides and the high-contrast @@ -79,7 +79,7 @@ mod doc_parity_tests { /// altogether. Adding a palette should fail here, not go unnoticed. /// /// This used to parse this file's own source for `=> ThemePalette::` arms. - /// The palettes now come from colony-ui, so it counts the catalog itself — + /// The palettes now come from colony-ui, so it counts the catalog itself - /// which is what the picker renders, and therefore what the docs describe. #[test] fn the_documented_theme_counts_match_the_code() { diff --git a/src/update/keyboard.rs b/src/update/keyboard.rs index 7f7da26..e82be8b 100644 --- a/src/update/keyboard.rs +++ b/src/update/keyboard.rs @@ -128,7 +128,7 @@ impl App { match target { Some(key) if key.starts_with("repo:") => { self.active_colony_repo = Some(key["repo:".len()..].to_string()); - // Refresh the (repo, tab) markdown cache — + // Refresh the (repo, tab) markdown cache - // the detail view reads cached blocks only. self.refresh_detail_markdown(); } diff --git a/src/update/mod.rs b/src/update/mod.rs index 9d4680b..e2e79a6 100644 --- a/src/update/mod.rs +++ b/src/update/mod.rs @@ -201,7 +201,7 @@ impl App { self.active_colony_repo = None; self.keyboard_cursor = None; // Dismiss any open overlay panel so the section change is - // actually visible — otherwise users stay stuck on the + // actually visible - otherwise users stay stuck on the // GitHub / Settings panel even though the underlying // filter just changed. self.show_github_menu = false; @@ -906,10 +906,10 @@ mod tests { fn search_matches_description_and_display_name() { let mut app = App::new_for_test(); app.colony_repo_list = vec![ - repo("Grape", "Lecteur musique en Rust"), + repo("Grape", "Music player in Rust"), repo("orCAL", "Calendar overlay"), ]; - app.search_query = "musique".into(); + app.search_query = "music".into(); let hits: Vec<&str> = app .filtered_colony_repos() .iter() diff --git a/src/update/store.rs b/src/update/store.rs index f66c99b..7fc473c 100644 --- a/src/update/store.rs +++ b/src/update/store.rs @@ -380,7 +380,7 @@ impl App { .collect(); if repos.is_empty() { - // Nothing to check — reset the guard (otherwise it stays true + // Nothing to check - reset the guard (otherwise it stays true // forever, blocking all later checks) and still run the // chained launcher self-update check. self.is_checking_updates = false; From 4f7ae52f653363f5875144a93d12da368b92f97b Mon Sep 17 00:00:00 2001 From: MotherSphere Date: Wed, 14 Oct 2026 13:53:43 +0200 Subject: [PATCH 3/3] docs: follow the README template order and report v0.11.0 as the first shared-workflow release Move Themes under What it does and Platforms under Installation, point app authors at the spec from Documentation, drop the unmeasured design claims, keep the SignPath credit commented out until SignPath signs, promote Privacy to its own section and say when a security fix ships. --- README.md | 113 ++++++++++++++++++++-------------------------------- SECURITY.md | 5 ++- 2 files changed, 46 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index 947b44a..2698187 100644 --- a/README.md +++ b/README.md @@ -28,10 +28,10 @@ to the applications already on your system. > and never an older version than the one running. CI builds and tests every > change on Linux, Windows and macOS, and checks that it still builds with Rust > 1.88. The Windows and macOS builds work but are best-effort (see -> [Platforms](#platforms)). Releases now go through the organisation's shared -> sign-and-publish workflow, which has not yet published a Colony release: the -> latest, v0.10.4, was signed by the workflow it replaced. Authenticode signing -> of the Windows build through SignPath is applied for and not active yet. Every +> [Platforms](#platforms)). v0.11.0 is the first release built, signed and +> published by the organisation's shared sign-and-publish workflow, and the +> same run bumped the `colony-bin` AUR package. Authenticode signing of the +> Windows build through SignPath is applied for and not active yet. Every > Project Colony program's release downloads the latest `colony-linux`, verifies > its signatures and runs its `validate-manifest`, so a broken Colony release > blocks the releases of every other program. @@ -80,6 +80,26 @@ Project-Colony on GitHub Colony your machine motion, English or French, keyboard navigation, and the storage and update settings. +### Themes + +Colony ships with **26 theme families and 59 palettes**, compiled into the +binary. They come from +[colony-ui](https://crates.io/crates/colony-ui) and are shared with every other +Project Colony program, so a theme looks the same wherever you meet it: + +| | | | | +|---|---|---|---| +| Catppuccin (Latte, Frappé, Macchiato, Mocha) | Gruvbox | Everblush | Kanagawa (Lotus, Wave, Journal, Dragon) | +| Nord | Dracula | Solarized | Tokyo Night | +| Rosé Pine (Main, Moon, Dawn) | One Dark | Monokai Pro (Pro, Classic, Spectrum) | Ayu (Dark, Mirage, Light) | +| Everforest | Material (Oceanic, Palenight, Deep Ocean) | Flexoki | Nightfox | +| Sonokai | Oxocarbon | Night Owl | Iceberg | +| Horizon | Mélange | Synthwave '84 | Modus (Operandi, Vivendi) | +| Stellar Blade (Eve, Tachy, Lily, Enya, Kaya; fan-made, unofficial) | Parchment | | | + +Each palette includes full semantic tokens: backgrounds, text layers, accents, +success, warning and error states, button states, and more. + New to Colony? The [tutorial](docs/guide/tutorial.md) walks through a first install. @@ -132,67 +152,7 @@ Requires Rust 1.88 or newer and a C compiler. No system libraries are needed: on Linux, the D-Bus client Colony uses for the Secret Service is compiled from source. -## Design principles - -**Single purpose, native performance.** Colony and every app in its ecosystem -follow the same philosophy: - -- **Rust-native**: built with [Iced](https://iced.rs). Startup is instant, - memory use is small, and your CPU stays cool. -- **Async everything**: network calls, file I/O and scanning run in the - background. The UI never freezes. -- **Zero configuration needed**: Colony works out of the box. Scan directories, - sidebar sections and themes can all be changed, but the defaults are sensible. - -## Theming - -Colony ships with **26 theme families and 59 palettes**, compiled into the -binary with zero runtime cost. They come from -[colony-ui](https://crates.io/crates/colony-ui) and are shared with every other -Project Colony program, so a theme looks the same wherever you meet it: - -| | | | | -|---|---|---|---| -| Catppuccin (Latte, Frappé, Macchiato, Mocha) | Gruvbox | Everblush | Kanagawa (Lotus, Wave, Journal, Dragon) | -| Nord | Dracula | Solarized | Tokyo Night | -| Rosé Pine (Main, Moon, Dawn) | One Dark | Monokai Pro (Pro, Classic, Spectrum) | Ayu (Dark, Mirage, Light) | -| Everforest | Material (Oceanic, Palenight, Deep Ocean) | Flexoki | Nightfox | -| Sonokai | Oxocarbon | Night Owl | Iceberg | -| Horizon | Mélange | Synthwave '84 | Modus (Operandi, Vivendi) | -| Stellar Blade (Eve, Tachy, Lily, Enya, Kaya; fan-made, unofficial) | Parchment | | | - -Each palette includes full semantic tokens: backgrounds, text layers, accents, -success, warning and error states, button states, and more. - -## For app developers - -Want your desktop app to appear in Colony's catalog? It takes one JSON file plus -a GitHub release with properly named assets. Colony only lists repositories of -the [Project-Colony](https://github.com/Project-Colony) GitHub organisation, so -the repository has to live there. - -**Quick version**: add `colony.json` at the root of your repository: - -```json -{ - "$schema": "https://raw.githubusercontent.com/Project-Colony/Project-Colony-Resources/main/generated/colony.schema.json", - "name": "YourApp", - "category": "utility" -} -``` - -Then publish a GitHub release with assets named `yourapp-linux`, -`yourapp-windows.exe`, `yourapp-macos` and `yourapp-macos-x86`. Colony picks -them up automatically. - -**Full walkthrough**: [CONTRIBUTING.md, Adding your app to Colony](CONTRIBUTING.md#adding-your-app-to-colony), -and the [colony.json reference](docs/guide/colony-spec.md). - -The canonical [release workflow template](https://github.com/Project-Colony/Project-Colony-Resources/blob/main/templates/sign-and-publish-caller.yml) -lives in Project-Colony-Resources, alongside the shared signing workflow it calls -and the manifest schema. - -## Platforms +### Platforms Colony is developed and tested on Linux. Windows and macOS builds are produced by the same CI, install and update apps, and are genuinely usable, but they get @@ -215,10 +175,15 @@ that is how these move up. Full documentation is in [docs/](docs/). Start at [docs/README.md](docs/README.md). -## Code signing policy +Want your app in Colony's catalog? It takes a `colony.json` at the root of a +repository of the [Project-Colony](https://github.com/Project-Colony) +organisation and a release with properly named, signed files. See +[CONTRIBUTING.md, Adding your app to Colony](CONTRIBUTING.md#adding-your-app-to-colony), +the [colony.json reference](docs/guide/colony-spec.md) and the +[release workflow template](https://github.com/Project-Colony/Project-Colony-Resources/blob/main/templates/sign-and-publish-caller.yml) +in Project-Colony-Resources. -Free code signing provided by [SignPath.io](https://signpath.io), certificate by -[SignPath Foundation](https://signpath.org). +## Code signing policy Windows builds will be Authenticode-signed once SignPath Foundation has accepted the project; until then they ship without Authenticode. Every release asset, on @@ -255,12 +220,20 @@ How releases are built, signed and published: [design/releases.md](https://github.com/Project-Colony/Project-Colony-Resources/blob/main/design/releases.md#5-signing), and for Colony's side of it, [docs/internals/release-signing.md](docs/internals/release-signing.md). -Team roles and members: + -### Privacy policy +## Privacy Colony talks to GitHub and to no other server. It has no telemetry, no analytics and no crash reporting. diff --git a/SECURITY.md b/SECURITY.md index 08f081f..94793dc 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -24,8 +24,9 @@ What to expect: - **Acknowledgement** within a few days. - A fix (or a mitigation plan) before any public disclosure, coordinated with - you. Given the project's release automation, a patched release usually ships - as soon as the fix lands. + you. A fix ships when its release pull request is merged: the release + workflow then builds, signs and publishes the patched version, and Colony + offers it as a self-update. - Credit in the release notes if you want it. ## Scope and trust model