Compare commits

...

13 commits
v0.1.2 ... main

Author SHA1 Message Date
Breadway
aa34aa1319 Bump bread-theme to v0.2.8 (live-reload fix)
Some checks failed
Build and publish package / package (push) Failing after 3m57s
Mirror to GitHub / mirror (push) Successful in 56s
2026-06-17 12:55:47 +08:00
Breadway
0893724e5f Fix illegible text on light pywal palettes + hot-reload
Use bread-theme 0.2.7's luminance-picked ink (@on-*) for text on coloured
backgrounds: the active workspace pill and notification cards previously kept the
pywal foreground, which vanished when those slots came out light. Drop the
blanket label colour rule (it overrode the per-surface ink on child labels).

Switch to bread_theme::gtk::apply_app_css so the bar recolours live on
`bread-theme reload` instead of only at startup.
2026-06-17 12:40:52 +08:00
Breadway
37b9a342e1 Release 0.1.7: shared bread-theme stylesheet
Some checks failed
Mirror to GitHub / mirror (push) Successful in 27s
Build and publish package / package (push) Failing after 4m7s
Pin bread-theme v0.2.6 and load the shared ecosystem stylesheet.
2026-06-16 18:31:18 +08:00
Breadway
0cb27ec1c4 theme: load the shared bread-theme stylesheet
Call bread_theme::gtk::apply_shared() before breadbar's own rules so fonts,
palette, and generic widgets come from the one ecosystem stylesheet (and
recolour live). Keep only breadbar-specific CSS (bar window, workspace
buttons, stats, notifications, OSD). Bump bread-theme dep to v0.2.6.
2026-06-16 16:56:52 +08:00
Breadway
1959a86157 Default workspace-button rounding to 0
Some checks failed
Mirror to GitHub / mirror (push) Successful in 31s
Build and publish package / package (push) Failing after 3m55s
2026-06-15 18:51:42 +08:00
Breadway
b098178058 Embed SVG assets and rasterise with resvg
Some checks failed
Mirror to GitHub / mirror (push) Successful in 33s
Build and publish package / package (push) Failing after 3m10s
The packaged binary panicked on startup ("svg load: Unrecognized image file
format"): asset SVGs were referenced by their build-time CARGO_MANIFEST_DIR
path (absent on an installed system, so read_to_string returned empty bytes),
and gdk::Texture::from_bytes can no longer decode SVG since librsvg dropped its
gdk-pixbuf loader.

- include_str! the SVGs into the binary (no runtime asset files)
- rasterise via resvg/tiny-skia into a gdk::MemoryTexture (no system loader)
2026-06-14 19:36:38 +08:00
Breadway
ec24ed6371 Disable debug package so the main package publishes correctly
Some checks failed
Mirror to GitHub / mirror (push) Successful in 46s
Build and publish package / package (push) Failing after 3m11s
makepkg's debug split produced a -debug pkg; the upload's head -1 could
grab it instead of the main package. !debug yields a single package.
2026-06-13 23:00:49 +08:00
Breadway
ea441a2de3 Use REGISTRY_TOKEN (scoped write:package) for registry publish
Some checks failed
Mirror to GitHub / mirror (push) Successful in 36s
Build and publish package / package (push) Failing after 3m21s
2026-06-13 22:55:40 +08:00
Breadway
289fc1c827 Disable LTO in PKGBUILD (vendored ring/mlua static libs vs makepkg -flto) 2026-06-13 17:06:54 +08:00
Breadway
4b4d222784 Clone from public URL, not GITHUB_SERVER_URL (resolves to localhost in runner)
The Forgejo runner injects GITHUB_SERVER_URL as http://localhost:3002, which
is unreachable from inside the job container. Use the public URL instead.
2026-06-13 16:14:13 +08:00
Breadway
7c23265eab Rename mirror secret to MIRROR_TOKEN (GITHUB_ prefix is reserved)
Forgejo/gitea rejects user secret names starting with GITHUB_.
2026-06-13 16:10:49 +08:00
Breadway
00dbb1df5f Fix Forgejo workflows for the actual server capabilities
- package.yml: correct Arch registry upload (octet-stream + binary body),
  drop --privileged, manual shell clone (archlinux image has no Node),
  built-in Actions token, --nocheck
- mirror.yml: clone --mirror + explicit refs push with --prune
2026-06-13 16:02:07 +08:00
Breadway
50bb249b3a Add packaging/arch PKGBUILD and Forgejo Actions workflows
- packaging/arch/PKGBUILD: builds and publishes breadbar to [breadway] repo
- .forgejo/workflows/mirror.yml: mirrors every push/tag to GitHub
- .forgejo/workflows/package.yml: builds on tag, publishes to Forgejo registry

Requires FORGEJO_TOKEN and GITHUB_MIRROR_TOKEN secrets in Forgejo.
2026-06-13 12:12:41 +08:00
8 changed files with 454 additions and 57 deletions

View file

@ -0,0 +1,21 @@
name: Mirror to GitHub
on:
push:
branches: ['**']
tags: ['**']
jobs:
mirror:
runs-on: [self-hosted, hestia]
steps:
- name: Mirror to GitHub
run: |
set -euo pipefail
git clone --mirror "https://git.breadway.dev/${GITHUB_REPOSITORY}.git" repo.git
cd repo.git
# Mirror only branches and tags (not refs/pull/*, which GitHub rejects);
# --prune deletes GitHub refs that no longer exist on Forgejo.
git push --prune \
"https://x-access-token:${{ secrets.MIRROR_TOKEN }}@github.com/Breadway/breadbar.git" \
'+refs/heads/*:refs/heads/*' '+refs/tags/*:refs/tags/*'

View file

@ -0,0 +1,40 @@
name: Build and publish package
on:
push:
tags: ['v*']
jobs:
package:
runs-on: [self-hosted, hestia]
container:
image: archlinux:latest
steps:
# Note: no actions/checkout — the archlinux image has no Node, which JS
# actions require. Everything runs as shell steps and clones manually.
- name: Build and publish
env:
PUBLISH_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: |
set -euo pipefail
VERSION="${GITHUB_REF_NAME#v}"
pacman -Syu --noconfirm base-devel git rust cargo gtk4 gtk4-layer-shell libpulse iw
useradd -m builder
git config --global --add safe.directory '*'
git clone --branch "${GITHUB_REF_NAME}" --depth 1 \
"https://git.breadway.dev/${GITHUB_REPOSITORY}.git" /home/builder/src
cd /home/builder/src
git archive --format=tar.gz --prefix="breadbar-${VERSION}/" HEAD \
> packaging/arch/breadbar-${VERSION}.tar.gz
SHA=$(sha256sum packaging/arch/breadbar-${VERSION}.tar.gz | awk '{print $1}')
sed -i "s/^pkgver=.*/pkgver=${VERSION}/" packaging/arch/PKGBUILD
sed -i "s/^sha256sums=.*/sha256sums=('${SHA}')/" packaging/arch/PKGBUILD
chown -R builder:builder /home/builder/src
# --nocheck: packaging builds the artifact; tests belong in a CI job.
su builder -c "cd /home/builder/src/packaging/arch && makepkg -f --noconfirm --nocheck"
PKG=$(find /home/builder/src/packaging/arch -name '*.pkg.tar.zst' | head -1)
curl -fsS -X PUT \
-H "Authorization: token ${PUBLISH_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary "@${PKG}" \
"https://git.breadway.dev/api/packages/Breadway/arch/os"

282
Cargo.lock generated
View file

@ -2,12 +2,30 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.102" version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" 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]] [[package]]
name = "async-broadcast" name = "async-broadcast"
version = "0.7.2" version = "0.7.2"
@ -70,6 +88,18 @@ version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[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]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.13.0" version = "2.13.0"
@ -78,8 +108,8 @@ checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
[[package]] [[package]]
name = "bread-theme" name = "bread-theme"
version = "0.1.0" version = "0.2.3"
source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.1.0#6b5f4f475f66a645b08cb865e6dda8228d23679b" source = "git+https://github.com/Breadway/bread-ecosystem?tag=v0.2.8#77417d552130281ff787e07d52541eb25e9d533b"
dependencies = [ dependencies = [
"dirs", "dirs",
"gtk4", "gtk4",
@ -89,7 +119,7 @@ dependencies = [
[[package]] [[package]]
name = "breadbar" name = "breadbar"
version = "0.1.2" version = "0.1.7"
dependencies = [ dependencies = [
"bread-theme", "bread-theme",
"futures-lite", "futures-lite",
@ -97,6 +127,7 @@ dependencies = [
"gtk4-layer-shell", "gtk4-layer-shell",
"hyprland", "hyprland",
"relm4", "relm4",
"resvg",
"serde", "serde",
"serde_json", "serde_json",
"tokio", "tokio",
@ -109,6 +140,12 @@ version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "bytemuck"
version = "1.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.11.1" version = "1.11.1"
@ -121,7 +158,7 @@ version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cc8d9aa793480744cd9a0524fef1a2e197d9eaa0f739cde19d16aba530dcb95" checksum = "5cc8d9aa793480744cd9a0524fef1a2e197d9eaa0f739cde19d16aba530dcb95"
dependencies = [ dependencies = [
"bitflags", "bitflags 2.13.0",
"cairo-sys-rs", "cairo-sys-rs",
"glib", "glib",
"libc", "libc",
@ -172,12 +209,27 @@ dependencies = [
"unicode-segmentation", "unicode-segmentation",
] ]
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.21" version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "data-url"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
[[package]] [[package]]
name = "derive_more" name = "derive_more"
version = "2.1.1" version = "2.1.1"
@ -271,6 +323,15 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "euclid"
version = "0.22.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "event-listener" name = "event-listener"
version = "5.4.1" version = "5.4.1"
@ -301,6 +362,15 @@ dependencies = [
"getrandom 0.3.4", "getrandom 0.3.4",
] ]
[[package]]
name = "fdeflate"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
dependencies = [
"simd-adler32",
]
[[package]] [[package]]
name = "field-offset" name = "field-offset"
version = "0.3.6" version = "0.3.6"
@ -311,6 +381,22 @@ dependencies = [
"rustc_version", "rustc_version",
] ]
[[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 = "float-cmp"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
[[package]] [[package]]
name = "flume" name = "flume"
version = "0.12.0" version = "0.12.0"
@ -591,7 +677,7 @@ version = "0.22.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c207e04e51605dcf7b2924c41591b3a10e1438eaac5bcf448fb91f325381104a" checksum = "c207e04e51605dcf7b2924c41591b3a10e1438eaac5bcf448fb91f325381104a"
dependencies = [ dependencies = [
"bitflags", "bitflags 2.13.0",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-executor", "futures-executor",
@ -720,7 +806,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4069987ff4793699511a251028cc336b438e46565b463f111250148d574752a" checksum = "a4069987ff4793699511a251028cc336b438e46565b463f111250148d574752a"
dependencies = [ dependencies = [
"bitflags", "bitflags 2.13.0",
"gdk4", "gdk4",
"glib", "glib",
"glib-sys", "glib-sys",
@ -835,6 +921,12 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "imagesize"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.14.0" version = "2.14.0"
@ -871,6 +963,17 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "kurbo"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62"
dependencies = [
"arrayvec",
"euclid",
"smallvec",
]
[[package]] [[package]]
name = "leb128fmt" name = "leb128fmt"
version = "0.1.0" version = "0.1.0"
@ -928,6 +1031,16 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[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]] [[package]]
name = "mio" name = "mio"
version = "1.2.1" version = "1.2.1"
@ -939,6 +1052,15 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.21.4" version = "1.21.4"
@ -997,6 +1119,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec"
[[package]]
name = "pico-args"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.17" version = "0.2.17"
@ -1009,6 +1137,19 @@ version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" 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]] [[package]]
name = "prettyplease" name = "prettyplease"
version = "0.2.37" version = "0.2.37"
@ -1103,6 +1244,35 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "resvg"
version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a325d5e8d1cebddd070b13f44cec8071594ab67d1012797c121f27a669b7958"
dependencies = [
"log",
"pico-args",
"rgb",
"svgtypes",
"tiny-skia",
"usvg",
]
[[package]]
name = "rgb"
version = "0.8.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4"
dependencies = [
"bytemuck",
]
[[package]]
name = "roxmltree"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
[[package]] [[package]]
name = "rustc_version" name = "rustc_version"
version = "0.4.1" version = "0.4.1"
@ -1118,7 +1288,7 @@ version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [ dependencies = [
"bitflags", "bitflags 2.13.0",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
@ -1216,6 +1386,27 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "simd-adler32"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
[[package]]
name = "simplecss"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c"
dependencies = [
"log",
]
[[package]]
name = "siphasher"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.12" version = "0.4.12"
@ -1247,6 +1438,25 @@ dependencies = [
"lock_api", "lock_api",
] ]
[[package]]
name = "strict-num"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
dependencies = [
"float-cmp",
]
[[package]]
name = "svgtypes"
version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc"
dependencies = [
"kurbo",
"siphasher",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.117" version = "2.0.117"
@ -1310,6 +1520,32 @@ dependencies = [
"syn", "syn",
] ]
[[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]] [[package]]
name = "tokio" name = "tokio"
version = "1.52.3" version = "1.52.3"
@ -1449,6 +1685,28 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "usvg"
version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7447e703d7223b067607655e625e0dbca80822880248937da65966194c4864e6"
dependencies = [
"base64",
"data-url",
"flate2",
"imagesize",
"kurbo",
"log",
"pico-args",
"roxmltree",
"simplecss",
"siphasher",
"strict-num",
"svgtypes",
"tiny-skia-path",
"xmlwriter",
]
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.23.2" version = "1.23.2"
@ -1563,7 +1821,7 @@ version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [ dependencies = [
"bitflags", "bitflags 2.13.0",
"hashbrown 0.15.5", "hashbrown 0.15.5",
"indexmap", "indexmap",
"semver", "semver",
@ -1723,7 +1981,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bitflags", "bitflags 2.13.0",
"indexmap", "indexmap",
"log", "log",
"serde", "serde",
@ -1759,6 +2017,12 @@ version = "0.8.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
[[package]]
name = "xmlwriter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]] [[package]]
name = "zbus" name = "zbus"
version = "5.16.0" version = "5.16.0"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "breadbar" name = "breadbar"
version = "0.1.2" version = "0.1.7"
edition = "2021" edition = "2021"
description = "Minimal status bar and notification daemon for Hyprland on Wayland" description = "Minimal status bar and notification daemon for Hyprland on Wayland"
license = "MIT" license = "MIT"
@ -10,7 +10,7 @@ keywords = ["wayland", "hyprland", "bar", "status-bar", "gtk4"]
categories = ["gui"] categories = ["gui"]
[dependencies] [dependencies]
bread-theme = { git = "https://github.com/Breadway/bread-ecosystem", tag = "v0.1.0", features = ["gtk"] } bread-theme = { git = "https://github.com/Breadway/bread-ecosystem", tag = "v0.2.8", features = ["gtk"] }
gtk4 = { version = "0.11", features = ["v4_12"] } gtk4 = { version = "0.11", features = ["v4_12"] }
gtk4-layer-shell = "0.8" gtk4-layer-shell = "0.8"
relm4 = { version = "0.11", features = ["macros"] } relm4 = { version = "0.11", features = ["macros"] }
@ -20,6 +20,9 @@ zbus = { version = "5", default-features = false, features = ["tokio"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "time", "process", "signal", "sync"] } tokio = { version = "1", features = ["rt-multi-thread", "macros", "time", "process", "signal", "sync"] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
# Pure-Rust SVG rasteriser (default features off → no text/font deps; the icons
# are vector-only). Needed because librsvg dropped its gdk-pixbuf SVG loader.
resvg = { version = "0.44", default-features = false }
[profile.release] [profile.release]
lto = "thin" lto = "thin"

36
packaging/arch/PKGBUILD Normal file
View file

@ -0,0 +1,36 @@
# Maintainer: Breadway <rileyhorsham@gmail.com>
pkgname=breadbar
pkgver=0.1.0
pkgrel=1
pkgdesc="Minimal status bar and notification daemon for Hyprland"
arch=('x86_64')
url="https://github.com/Breadway/breadbar"
license=('MIT')
# Some Rust deps (ring/mlua) build vendored C/asm into static archives; makepkg's
# default -flto=auto emits GCC LTO bitcode the Rust (lld) link cannot read,
# causing undefined-symbol errors. Disable LTO.
options=(!lto !debug)
depends=('gtk4' 'gtk4-layer-shell' 'libpulse' 'iw')
optdepends=(
'hyprland: workspace and window data integration'
)
makedepends=('rust' 'cargo')
source=("${pkgname}-${pkgver}.tar.gz")
sha256sums=('SKIP')
build() {
cd "${srcdir}/${pkgname}-${pkgver}"
cargo build --release --locked
}
check() {
cd "${srcdir}/${pkgname}-${pkgver}"
cargo test --release --locked
}
package() {
cd "${srcdir}/${pkgname}-${pkgver}"
install -Dm755 target/release/breadbar "${pkgdir}/usr/bin/breadbar"
install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
}

View file

@ -15,22 +15,25 @@ static BT_CONN: AsyncOnce<zbus::Connection> = AsyncOnce::const_new();
static BT_CACHE: LazyLock<Mutex<&'static str>> = LazyLock::new(|| Mutex::new(BT_OFF)); static BT_CACHE: LazyLock<Mutex<&'static str>> = LazyLock::new(|| Mutex::new(BT_OFF));
static BT_TICK: AtomicU8 = AtomicU8::new(0); static BT_TICK: AtomicU8 = AtomicU8::new(0);
pub const WIFI_STRONG: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/WiFi Strong.svg"); // Embedded SVG contents (not paths). These &str constants double as stable
pub const WIFI_MEDIUM: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/WiFi Medium.svg"); // HashMap keys via their .as_ptr(); include_str! keeps each one a single
pub const WIFI_WEAK: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/WiFi Weak.svg"); // 'static literal, so pointer identity still holds.
pub const WIFI_OFF: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/WiFi Disconnect.svg"); pub const WIFI_STRONG: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/WiFi Strong.svg"));
pub const WIFI_MEDIUM: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/WiFi Medium.svg"));
pub const WIFI_WEAK: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/WiFi Weak.svg"));
pub const WIFI_OFF: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/WiFi Disconnect.svg"));
pub const BAT_HIGH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/Battery 3 Bars.svg"); pub const BAT_HIGH: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/Battery 3 Bars.svg"));
pub const BAT_MID: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/Battery 2 Bars.svg"); pub const BAT_MID: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/Battery 2 Bars.svg"));
pub const BAT_LOW: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/Battery 1 Bar.svg"); pub const BAT_LOW: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/Battery 1 Bar.svg"));
pub const AC_POWER: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/AC Power.svg"); pub const AC_POWER: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/AC Power.svg"));
pub const BT_OFF: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/Bluetooth Off.svg"); pub const BT_OFF: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/Bluetooth Off.svg"));
pub const BT_ON: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/assets/Bluetooth.svg"); pub const BT_ON: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/Bluetooth.svg"));
pub const BT_CONNECTED: &str = concat!( pub const BT_CONNECTED: &str = include_str!(concat!(
env!("CARGO_MANIFEST_DIR"), env!("CARGO_MANIFEST_DIR"),
"/assets/Bluetooth Connected.svg" "/assets/Bluetooth Connected.svg"
); ));
#[derive(Debug)] #[derive(Debug)]
pub struct Stats { pub struct Stats {

View file

@ -1,6 +1,10 @@
// Embed asset SVGs into the binary at compile time. Previously these were
// referenced by their build-time filesystem path (CARGO_MANIFEST_DIR), which
// does not exist on an installed system — so the packaged binary loaded empty
// bytes and panicked. include_str! bakes the contents in instead.
macro_rules! asset { macro_rules! asset {
($n:literal) => { ($n:literal) => {
concat!(env!("CARGO_MANIFEST_DIR"), "/assets/", $n) include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/", $n))
}; };
} }
@ -274,24 +278,39 @@ impl App {
} }
} }
fn stat_pair(icon_path: &str, label: &gtk4::Label) -> gtk4::Box { fn stat_pair(icon_svg: &str, label: &gtk4::Label) -> gtk4::Box {
let pair = gtk4::Box::new(gtk4::Orientation::Horizontal, 0); let pair = gtk4::Box::new(gtk4::Orientation::Horizontal, 0);
pair.add_css_class("stat-pair"); pair.add_css_class("stat-pair");
let img = gtk4::Image::from_paintable(Some(&svg_texture(icon_path))); let img = gtk4::Image::from_paintable(Some(&svg_texture(icon_svg)));
img.add_css_class("stat-icon"); img.add_css_class("stat-icon");
pair.append(&img); pair.append(&img);
pair.append(label); pair.append(label);
pair pair
} }
fn svg_texture(path: &str) -> gtk4::gdk::Texture { // Rasterise an (embedded) SVG to a texture. Done in pure Rust with resvg
// because librsvg dropped its gdk-pixbuf SVG loader, so gdk::Texture::from_bytes
// can no longer decode SVG on a stock system.
fn svg_texture(svg_src: &str) -> gtk4::gdk::Texture {
use resvg::{tiny_skia, usvg};
let fg = theme::fg_color(); let fg = theme::fg_color();
let svg = std::fs::read_to_string(path) let svg = svg_src
.unwrap_or_default()
.replace("currentColor", &fg) .replace("currentColor", &fg)
.replace(r#"width="24" height="24""#, r#"width="16" height="16""#); .replace(r#"width="24" height="24""#, r#"width="16" height="16""#);
let bytes = gtk4::glib::Bytes::from_owned(svg.into_bytes()); let tree = usvg::Tree::from_str(&svg, &usvg::Options::default()).expect("parse svg");
gtk4::gdk::Texture::from_bytes(&bytes).expect("svg load") let size = tree.size().to_int_size();
let (w, h) = (size.width(), size.height());
let mut pixmap = tiny_skia::Pixmap::new(w, h).expect("alloc pixmap");
resvg::render(&tree, tiny_skia::Transform::identity(), &mut pixmap.as_mut());
let bytes = gtk4::glib::Bytes::from_owned(pixmap.take());
gtk4::gdk::MemoryTexture::new(
w as i32,
h as i32,
gtk4::gdk::MemoryFormat::R8g8b8a8Premultiplied,
&bytes,
(w * 4) as usize,
)
.upcast()
} }
fn stat_label() -> gtk4::Label { fn stat_label() -> gtk4::Label {

View file

@ -1,57 +1,68 @@
use bread_theme::{gtk as bgtk, hex_to_rgba, load_palette}; use bread_theme::{gtk as bgtk, hex_to_rgba, ink_on, load_palette};
use gtk4::CssProvider; use gtk4::CssProvider;
use std::cell::RefCell; use std::cell::RefCell;
thread_local! { thread_local! {
static PROVIDER: RefCell<Option<CssProvider>> = const { RefCell::new(None) };
static USER_PROVIDER: RefCell<Option<CssProvider>> = const { RefCell::new(None) }; static USER_PROVIDER: RefCell<Option<CssProvider>> = const { RefCell::new(None) };
} }
fn load_css() -> String { fn load_css() -> String {
let p = load_palette(); let p = load_palette();
// breadbar-specific rules only — fonts, base colours, and generic widgets
// come from the shared ecosystem stylesheet (applied first in `apply()`).
// Colour is set on each surface (bar, active workspace pill, notification
// card) and child labels inherit it, so text stays legible whatever lightness
// pywal hands a given slot. `on_*` are luminance-picked ink (black/white) for
// that background — the pywal hues themselves are untouched.
format!( format!(
"* {{ font-family: 'Varela Round', sans-serif; font-size: 14px; }}\ "window.breadbar {{ background-color: {bg_rgba}; color: {on_bg}; border-radius: 0; }}\
window.breadbar {{ background-color: {bg_rgba}; border-radius: 0; }}\ .workspace-btn {{ background: transparent; opacity: 0.45;\
label {{ color: {fg}; }}\ border-radius: 0; border: none; outline: none; box-shadow: none;\
.workspace-btn {{ background: transparent; color: {fg}; opacity: 0.45;\
border-radius: 0 0 8px 8px; border: none; outline: none; box-shadow: none;\
min-width: 24px; padding: 4px 8px; }}\ min-width: 24px; padding: 4px 8px; }}\
.workspace-btn:hover {{ opacity: 0.8; }}\ .workspace-btn:hover {{ opacity: 0.8; }}\
.workspace-btn.active {{ background: {accent}; opacity: 1; }}\ .workspace-btn.active {{ background: {accent}; color: {on_accent}; opacity: 1; }}\
.stats-box {{ margin-right: 8px; }}\ .stats-box {{ margin-right: 8px; }}\
.stat-pair {{ margin-right: 12px; }}\ .stat-pair {{ margin-right: 12px; }}\
.stat-icon {{ margin-right: 5px; }}\ .stat-icon {{ margin-right: 5px; }}\
.bt-icon {{ margin-right: 12px; }}\ .bt-icon {{ margin-right: 12px; }}\
window.breadbar-notification {{ background-color: alpha({bg_plain}, 0.95); }}\ window.breadbar-notification {{ background-color: alpha({bg_plain}, 0.95); color: {on_bg}; }}\
.notification-card {{ background: {surface}; border-radius: 8px;\ .notification-card {{ background: {surface}; color: {on_surface}; border-radius: 8px;\
padding: 12px; margin-bottom: 8px; }}\ padding: 12px; margin-bottom: 8px; }}\
.notification-summary {{ font-weight: bold; color: {fg}; }}\ .notification-summary {{ font-weight: bold; }}\
.notification-app {{ color: {fg}; opacity: 0.6; }}\ .notification-app {{ opacity: 0.6; }}\
.notification-body {{ color: {fg}; }}\ window.breadbar-osd {{ background-color: alpha({bg_plain}, 0.95); color: {on_bg}; border-radius: 8px; }}\
window.breadbar-osd {{ background-color: alpha({bg_plain}, 0.95); border-radius: 8px; }}\ .osd-kind {{ opacity: 0.75; font-size: 12px; }}\
.osd-kind {{ color: {fg}; opacity: 0.75; font-size: 12px; }}\ .osd-pct {{ font-weight: bold; font-size: 12px; }}\
.osd-pct {{ color: {fg}; font-weight: bold; font-size: 12px; }}\
progressbar.osd-bar {{ min-height: 8px; }}\ progressbar.osd-bar {{ min-height: 8px; }}\
progressbar.osd-bar trough {{ background-image: none; background-color: {trough}; border-radius: 4px; min-height: 8px; }}\ progressbar.osd-bar trough {{ background-image: none; background-color: {trough}; border-radius: 4px; min-height: 8px; }}\
progressbar.osd-bar trough progress {{ background-image: none; background-color: {accent}; border-radius: 4px; min-height: 8px; }}", progressbar.osd-bar trough progress {{ background-image: none; background-color: {accent}; border-radius: 4px; min-height: 8px; }}",
bg_plain = p.background, bg_plain = p.background,
bg_rgba = hex_to_rgba(&p.background, 0.92), bg_rgba = hex_to_rgba(&p.background, 0.92),
surface = p.color0, surface = p.color0,
fg = p.foreground,
accent = p.color4, accent = p.color4,
on_bg = ink_on(&p.background),
on_surface = ink_on(&p.color0),
on_accent = ink_on(&p.color4),
trough = hex_to_rgba(&p.color4, 0.25), trough = hex_to_rgba(&p.color4, 0.25),
) )
} }
/// Returns the current foreground colour (used for icon tinting in the stats bar). /// Returns the ink colour for icon tinting in the stats bar — the same
/// luminance-picked colour the bar's text uses, so icons stay legible on the bar
/// whatever lightness pywal gives the background.
pub fn fg_color() -> String { pub fn fg_color() -> String {
load_palette().foreground ink_on(&load_palette().background).to_string()
} }
/// Apply (or reload) the theme CSS. Safe to call from `glib::MainContext::invoke`. /// Apply (or reload) the theme CSS. Safe to call from `glib::MainContext::invoke`.
pub fn apply() { pub fn apply() {
let css = load_css(); // Shared ecosystem base (fonts, palette, generic widgets) — applied first
PROVIDER.with(|cell| bgtk::apply_css(&css, cell)); // (and self-reloading) so breadbar's own rules below layer on top.
bgtk::apply_shared();
// breadbar's own rules, hot-reloaded on `bread-theme reload`: the closure
// re-reads the pywal palette each time so the bar recolours without restart.
bgtk::apply_app_css(load_css);
let home = std::env::var("HOME").unwrap_or_default(); let home = std::env::var("HOME").unwrap_or_default();
let user_path = std::path::PathBuf::from(format!("{home}/.config/breadbar/style.css")); let user_path = std::path::PathBuf::from(format!("{home}/.config/breadbar/style.css"));