Icon Studio
Designs application icons from the house palettes: pick a Material Symbols glyph or some text, a foreground colour and a background treatment, and see every combination your choices allow side by side.

Provenance
The original icon studio was lost in the data-loss incident, and unlike the Android clients there was no artefact to decompile — an egui binary was not sitting on a phone somewhere. So this is a rebuild from two things that did survive:
- its inputs — the palettes, which are still live (see below);
- its outputs — the icons it produced, which ship inside the five
cbz*Android apps.
That second point is what keeps this honest. tests/reproduce.rs renders a
background with the reconstructed code and compares it against
ic_icon_background.png taken from the installed cbzauth APK. The originals
turn out to be an even two-tone diagonal split of the Casabeza brand colours
with a hairline boundary — roughly one blended pixel per row — and the tests
assert exactly that, so a change that turns the split into a soft gradient
fails rather than quietly drifting.
What is attested, and what is not
| Feature | Evidence |
|---|---|
| Diagonal split background | Attested — all five shipped cbz icons use it |
| Material Symbols foreground | Attested — shipped icons are Symbols Rounded, outlined |
| Text foreground | Attested — cbznumerics ships “1234” |
| Casabeza + neiam palettes | Attested — recovered from the APK and live in dms |
| Solid, gradients, stripes, checks, dots, rays, wedge | Invented — described as “gradients, patterns and other things”, but no shipped icon uses them |
| Sub-banner | Invented — described, but no shipped icon has one, so the layout is a guess |
The invented parts are reasonable, not recovered. Treat them as a starting point rather than as the tool you remember.
Documents
Configurations save as TOML, so they are readable and hand-editable:
version = 1
palette = "Casabeza"
[foreground]
kind = "icon"
codepoint = "e899"
name = "lock" # informational; the codepoint wins
[colors.background_a]
role = "primary"
rgb = [0, 201, 212]
[background]
style = "diagonal-split"
angle = 45.0
Colours carry both a palette role and literal RGB. The role wins where the
named palette still defines it, so retuning a theme in dms updates every
document that references it; the RGB is the fallback that keeps old documents
openable when a role is gone. A document from a newer version is refused
rather than half-read, and an unknown background.style is an error rather
than a silent default — but an unknown grid preference is dropped, since
that should not block opening a file.
New / Open / Save / Save as are on the left panel, with Ctrl+N, Ctrl+O,
Ctrl+S and Ctrl+Shift+S. The title bar shows the filename and a bullet for
unsaved changes, and New/Open prompt before discarding them.
The cbz icon set
presets/ holds a document per shipped app — cbz-auth, cbz-lists,
cbz-numerics, cbz-scanner, cbz-inventory — so each icon opens as an
editable configuration instead of having to be rebuilt by hand.
These are reconstructions. The palette, the diagonal split and its orientation
are measured from the shipped PNGs, and the scales are taken from each one’s
ink extents; the glyph identifications are mine by eye, so cbz-scanner
being satellite_alt is a judgement, not a recovered fact. A test loads all
five and asserts they still reproduce the shipped background orientation.
Palettes
Nothing is retyped by hand. tools/regen_palettes.py writes
src/palette/generated.rs from:
- neiam — the daisyUI themes carried by the sites, converted from OKLCH to
sRGB with the same transform the Android clients use. Several sites ship the
same set, so more than one stylesheet is read and the results merged by name:
dmsprovidesher,afterdark,forest,sky,clays,stones,darkandlight;waxxadditionally providesblueprint. The shared themes are identical between sources — where they ever diverge, the earlier source wins and the script says so rather than resolving it quietly; - Casabeza — the exact values recovered from the cbzauth APK after the loss, carried in the script.
tools/regen_palettes.py # refresh after a site's themes change
tools/regen_palettes.py --source path.css # read a specific stylesheet, repeatable
tools/regen_palettes.py --check # non-zero if stale, for CI
The Android themes and the dms themes agree exactly on primary and accent;
Android’s backgrounds are deliberately darker (L 0.18 against 0.35). Both are
offered.
Fonts
Vendored in assets/:
- Material Symbols Rounded, outlined and filled, from
egui_material_icons(MIT), with the.codepointstable parsed at startup so the icon list and the rasteriser cannot disagree about what a name means; - B612 regular and bold, the face the cbz apps ship in
res/font, used for text marks and sub-banners. Rendering text through the Symbols font lands on.notdef, which is why text needs its own face.
Installing
Packages are published to this forge’s registries on every push to master, x86_64 only.
# Debian / Ubuntu
curl -fsSL $FORGE/pkg/_keys/deb/neiam | sudo tee /etc/apt/keyrings/neiam.asc >/dev/null
echo "deb [signed-by=/etc/apt/keyrings/neiam.asc] $FORGE/pkg/deb/neiam/_all stable main" \
| sudo tee /etc/apt/sources.list.d/neiam.list
sudo apt update && sudo apt install neiam-icon-studio
For Fedora / RHEL / openSUSE the workflow prints a matching .repo stanza at
the end of each run.
The package installs the binary, a desktop entry with the application icon,
and the preset documents under /usr/share/neiam-icon-studio/presets/.
Builds off a tag publish the bare Cargo version; builds off master publish
<version>+dev.<timestamp>, which sorts after the release — so a machine
tracking this repository gets the latest build, and two runs never collide on
one filename.
Running
cargo run # the studio
cargo test # including the comparison against shipped icons
cargo run --example reproduce_cbz # re-render the five cbz icons
cargo run --example showcase # every background generator
cargo run --example make_presets # regenerate presets/
cargo run --example make_app_icon # regenerate the desktop icon
The application’s own icon is made by the application, from
presets/icon-studio.toml — change it in the studio and regenerate.
Export writes ic_icon, ic_icon_background and ic_icon_foreground across
mipmap-mdpi through mipmap-xxxhdpi (108–432px), matching the layout the
cbz apps use.
Design notes
Rendering is tiny-skia and ab_glyph, deliberately independent of egui: the
preview, the grid and the exported PNG all go through render::render_composed,
so what you see is what lands on disk, and the renderer can be tested
headlessly. Grid cells are cached by content hash — the cartesian product gets
large quickly, and it is capped at 240 cells for that reason.