Regenerate the icons

Edit the icon master sheet, then export it into libres with three qbs commands

Regenerate the icons

content/design/icons.svg is the icon master. Every icon under src/libs/libres/resources/icons/ is cut out of that one file by the icon-export tool. The folder is generated output.

Do not hand-edit a file under src/libs/libres/resources/icons/. The next export overwrites it. Edit the master instead.

The whole routine

Run these three commands from the repository root:

qbs run -p icon-export -- content/design/icons.svg --auto-tag --retag-drift --apply
qbs run -p icon-export -- content/design/icons.svg --align-grid --apply
qbs run -p icon-export -- content/design/icons.svg src/libs/libres/resources/icons

Then review the result and build:

git status --short
qbs resolve --force-probe-execution
qbs build

The forced probe is necessary. The icon bundle is a glob, so a new file stays invisible to the build until the probe runs again.

What each command does

  1. Auto-tag. Each icon on the sheet is a 64x64 rect. The tool reads the column header above it and the title box beside it, then writes the id cutout--<column>--<title>. That id becomes the file path. --retag-drift also rewrites ids whose sheet text changed.
  2. Align-grid. The tool snaps every cutout box, every title box and their content to the 64px grid. Content moves with its box, so art never separates from its frame.
  3. Export. The tool writes one standalone SVG per cutout. It also deletes the files that earlier runs made and this run does not, so a renamed icon leaves no old copy behind.

Read the output

The export prints one summary line:

Exported: 373, Removed: 6, Errors: 0

If Errors is not 0, read the error lines above the summary. The tool refuses to write an icon that would render blank, and it keeps the previous file in that case. A blank icon is the failure this checks for. It has two causes:

  • The cutout is a scaffold and nobody pasted art into it yet.
  • The art references an element the tool cannot find.

If Removed is not 0, the sheet stopped making those icons. Search the code for their old paths and repoint every reference in the same commit.

The build checks this for you

libres runs icon-export --check as a build rule on every native build. The check writes nothing. It fails the build when an icon differs from the sheet, is missing, or is left over from a cutout the sheet stopped making:

DRIFT: app/reload.svg differs from the sheet
Checked: 373, Drifted: 1, Errors: 0

The failure names every file and prints the export command. Run it. Do not edit the icon to make the message go away — the sheet decides the content, and the next export overwrites the edit.

A cross build prints one line saying the guard did not run. That is correct: the in-graph tool is a target-architecture binary there, and the check reads repository content that is the same for every target. One native build proves it for all of them.

Add an icon

  1. Open content/design/icons.svg in Inkscape.
  2. Copy an existing cutout unit. A unit is the 64x64 cutout rect plus the 256x64 title box beside it.
  3. Draw the art inside the cutout rect.
  4. Type the icon name into the title box.
  5. Save, then run the three commands above.

The column header names the folder and the title names the file. A title can carry slashes, and the two compound: header llm with title Providers/Subscription/Groq writes opal/providers/subscription/groq.svg. A title must never repeat its column name.

Add a mark that is not an icon

The grid holds 64x64 icons. A wordmark, a banner or a plate does not fit it, so the sheet has a second kind of cutout for art of any size at any position.

  1. Draw the art anywhere in content/design/icons.svg. It does not need a lane and it does not need a column banner.

  2. Draw a cutout rect around it. Any size.

  3. Draw a label box to the right of the cutout, touching it or within 64 units of it, with the same vertical span. Put one text inside the box.

  4. Select the cutout rect, press Ctrl+Shift+X, and set its id by hand:

    cutout-standalone--brand--octomy-logo-dark
    
  5. Save, then run the export command.

The id decides the path, exactly as it does for a grid icon: -- separates folders and a single hyphen is part of a name, so the id above writes brand/octomy-logo-dark.svg.

The label names the file for a person reading the sheet. A label with no slash is checked against the last part of the id, so OctoMY Logo Dark matches the id above. A label with a slash states the whole path, so Brand/OctoMY Logo Dark also matches.

The tool never creates one of these for you. A grid cutout is identified by three facts — its size, its lane and its banner — and a standalone cutout has none of them, so a rule that guessed would name random sheet furniture as icons. You write the id once. --auto-tag then checks every standalone cutout and reports what is wrong:

Standalone cutouts: 2 checked, 1 with something wrong
  STANDALONE cutout-standalone--brand--octomy-logo-dark: the label 'OctoMY Logo Light' says cutout-standalone--brand--octomy-logo-light

--align-grid never moves a standalone unit, and never moves its art.

Rules

  • Never author an icon file by hand. The set is curated by the owner. See AGENTS.md.
  • Revert in Inkscape after every --apply. The tool writes the master in place. If Inkscape holds a stale buffer and saves it, the tool's changes disappear. This happened once and cost a full re-run.
  • Never delete a file under the icons folder by hand. Delete the cutout from the sheet and export. The export removes the file.
  • window/app/<name>.svg is a packaging contract. App.qbs builds that path from the application name to make the Windows .ico, and integration/editions/octomy/edition.json maps the same files. A rename there breaks packaging with no error.
  • Five files in the icons folder do not come from the sheet: app/view_grid.png, app/view_list.png, app/view_tree.png, octomy_logo_dark.svg and octomy_logo_light.svg. The tool never generated them, so it never touches them. The list is the difference between the tracked files and .icon-export-manifest. Measure it, do not trust this line:
comm -23 <(git ls-files src/libs/libres/resources/icons | sed 's|.*/icons/||' \
    | grep -E '\.(svg|png)$' | sort) \
  <(grep -v '^#' src/libs/libres/resources/icons/.icon-export-manifest | sort)

Check every reference

The export cannot know which paths the code uses. After a rename, verify that every icon reference still resolves:

grep -rho ':/icons/[A-Za-z0-9_/.-]*\.\(svg\|png\)' src/ test/ \
  | sort -u | sed 's|:/icons/||' \
  | while read -r p; do
      test -e "src/libs/libres/resources/icons/$p" || echo "MISSING: $p"
    done

Five names are expected in the output of this check. Anything else is a real break:

  • arrow.png and groupbox.png — libstyle keeps its own private :/icons/ bundle with those two files in it.
  • swarm/chip/no-such-asset.svg and opal/providers/api/no-such-brand.svg — two tests ask for a missing icon on purpose. One proves the fallback works, the other is a negative control that stops its own checks from being vacuous. Both names are spelled in test source, so re-read them from the suites rather than trusting this list: it already went stale once, when the opal lane retired a duplicate guard and its no-such-icon.svg with it.
  • fixo/fallback-template.svg — named inside a comment that explains the path never existed.
In this section
Topics
howto development icons resources design
See also