Common Development Tasks
Adding a New Icon
- Find the Unicode codepoint from the Nerd Fonts cheat sheet.
- Add a variant to the
StaticIconenum insrc/components/icons.rsand map it to its codepoint inStaticIcon::get_str():#![allow(unused)] fn main() { StaticIcon::MyNewIcon => "\u{f0001}", } - Build —
build.rsautomatically subsets the font to include the new glyph.
Adding a New Config Option
-
Add the field to the relevant config struct in
src/config.rs:#![allow(unused)] fn main() { #[derive(Deserialize, Clone, Debug)] #[serde(default)] pub struct MyModuleConfig { pub new_option: bool, // Add this } } -
Set a default value in the
Defaultimpl:#![allow(unused)] fn main() { impl Default for MyModuleConfig { fn default() -> Self { Self { new_option: false, } } } } -
Use the option in your module.
-
If the option should be hot-reloadable, handle it in the module’s
ConfigReloadedmessage.
Adding a D-Bus Integration
-
Create proxy definitions in a
dbus.rsfile:#![allow(unused)] fn main() { #[zbus::proxy( interface = "org.example.Service1", default_service = "org.example.Service", )] trait Service1 { #[zbus(property)] fn my_property(&self) -> zbus::Result<String>; } } -
Implement the
ReadOnlyServiceorServicetrait. -
Subscribe from a module. See Writing a New Service.
Working with the Theme
To add a new style or modify existing styles, edit src/theme.rs:
#![allow(unused)]
fn main() {
// Add a new button style method
pub fn my_button_style(&self) -> impl Fn(&Theme, Status) -> button::Style {
let opacity = self.opacity;
move |theme, status| {
// Return button::Style based on status and theme
}
}
}
Adding a Translation
ashell uses Fluent via the i18n-embed crate.
Catalogs live in i18n/<lang-tag>/ashell.ftl and are baked into the binary at compile time with include_str!.
-
Copy the seed catalog to a new BCP-47 directory (e.g.
fr-FR,de-DE,ru-RU,uk-UA,it-IT):mkdir -p i18n/<lang-tag> cp i18n/en-US/ashell.ftl i18n/<lang-tag>/ashell.ftl -
Translate the right-hand side of every entry. Preserve:
- the message keys and
## Sectioncomments, - every
{ $variable }placeholder (e.g.{ $count },{ $ssid },{ $duration }), - Fluent selector keys: state selectors
[on]/*[off]and plural selectors like[one]/*[other]. The bracketed identifiers are Fluent literals — only the messages on the right-hand side get translated. Use the CLDR plural categories that apply to your language;*[other]is the required default. - A message that takes
{ $count }may need a plural selector in your language even when the en-US entry has none (e.g.notifications-group-count: French needs “1 nouvelle” / “2 nouvelles”).
- the message keys and
-
Check the wording against established translations instead of translating each string in isolation. Users of a status bar already know the terms from their desktop, so match them:
- Desktop terms (power actions, power profiles, battery states, Bluetooth pairing, idle inhibition, system monitor labels):
look up the same English string in the GNOME and KDE Plasma translations for your language.
GNOME
.pofiles are on l10n.gnome.org and in each project’spo/directory (gnome-shell,gnome-control-center,gnome-bluetooth,gnome-system-monitor). KDE catalogs are on l10n.kde.org, or in your distribution’s packages (powerdevil,plasma-workspace,bluedevil,libksysguard) as.mofiles you can turn back into text withmsgunfmt. Pay attention to term pairs: Suspend and Hibernate must not end up with each other’s usual names. - Weather conditions (the
weather-*keys follow the WMO codes used by Open-Meteo): compare withlibgweatherand with the wording of a major weather service or site in your language. Prefer how they phrase intensities (“light”, “moderate”, “heavy”) and “feels like”. - If GNOME and KDE disagree, either is fine; say which one you followed in the PR description.
- Desktop terms (power actions, power profiles, battery states, Bluetooth pairing, idle inhibition, system monitor labels):
look up the same English string in the GNOME and KDE Plasma translations for your language.
GNOME
-
Register the catalog in the
CATALOGSslice insrc/i18n.rs:#![allow(unused)] fn main() { const CATALOGS: &[(&str, &str)] = &[ ("en-US", include_str!("../i18n/en-US/ashell.ftl")), ("<lang-tag>", include_str!("../i18n/<lang-tag>/ashell.ftl")), ]; } -
Test by setting
language = "<lang-tag>"in~/.config/ashell/config.toml, or by launching withLC_MESSAGES=<lang-tag> ./target/release/ashell. POSIX locale names are normalized to BCP-47 automatically. -
Run
make checkbefore pushing.
Running Checks Before Committing
Always run the full check before pushing:
make check
This runs cargo fmt, cargo check --workspace, and cargo clippy --workspace -- -D warnings.
Debugging a Specific Module
To see debug output for a specific module:
# In config.toml
[logging]
level = "warn,ashell::modules::my_module=debug"
Testing Config Hot-Reload
- Start ashell:
make start - Edit
~/.config/ashell/config.tomlin another terminal - Save — changes should appear immediately
- Check logs if changes don’t apply:
tail -f $XDG_RUNTIME_DIR/ashell_rCURRENT.log