Skip to content

Find the undrawable glyphs by parsing the source, not by listing them - #48

Merged
JamesKane merged 1 commit into
mainfrom
fix/ui-source-literal-glyphs
Aug 9, 2026
Merged

Find the undrawable glyphs by parsing the source, not by listing them#48
JamesKane merged 1 commit into
mainfrom
fix/ui-source-literal-glyphs

Conversation

@JamesKane

Copy link
Copy Markdown
Owner

What was wrong

The boxes on the Genealogy card were (kit-remove, MDKA-remove) and (MDKA-edit). The last round of this fixed the locale catalogs and stopped there — but icons are not translated copy, they are bare literals in the source, and ui.small_button("✕") is invisible to a scan of en.txt.

Reading the sources instead found ~20 more that no screenshot had reached yet: every clear-filter , the Y-STR agreement /, the sortable-table sort arrows /, the unread dots on threads and DMs, the match-strength meter, the STR group banner , and in a dozen status lines.

Every replacement was probed against the actual fonts rather than chosen by eye — (U+2715) sits one codepoint from (U+2716) and only the latter has a glyph, which is precisely how this recurs:

was now

Two of them were in navigator-app, not the UI crate: a consensus warning and the import summary's reference notes, both built there and drawn by egui via sources.rs and events.rs.

The part meant to outlast it

every_source_string_literal_is_renderable parses this crate's own sources with syn and checks every string literal against egui's own fonts, so a new icon is covered the moment it is typed rather than when someone remembers to extend a list.

Parsing rather than grepping is what makes that safe — comments are full of and naming the very bug they describe, and the AST has literals and no comments. Two things had to be right for it to see anything:

  • doc comments arrive as #[doc = "…"] attributes, and
  • syn hands macro bodies over as an unparsed TokenStream, which would have hidden nearly every label in the app, since they are built by format!.

The first version had that second hole and passed vacuously. It is only trustworthy because a bad glyph was injected into a format! and the test was watched failing with both codepoints named, before being reverted.

Scope, deliberately

The gate covers navigator-ui only, and the doc comment says why rather than papering over it: navigator-app also feeds the CLI and the HTML exporter, where and are correct, so which of its strings reach a window is a dataflow question rather than a syntactic one. cli.rs and #[cfg(test)] modules are excluded here for the same reason — a terminal renders in the user's own font.

Verification

cargo fmt --all clean · cargo clippy --all-targets -- -D warnings clean · cargo test --workspace all green (0 failed).

Worth a glance in the running app

/ are heavier than the / they replace, and / are chunkier than / in the match-strength meter. They were the only drawable options in that shape family.

🤖 Generated with Claude Code

…ting them

The last round of this fixed the catalogs and left the icons, because icons are
not translated copy — they are bare literals in the source. `ui.small_button("✕")`
is invisible to a scan of `en.txt`, so the Genealogy card kept shipping a box on
every kit-remove, MDKA-edit and MDKA-remove button.

Reading the sources instead found twenty-odd more that no screenshot had reached
yet: every clear-filter `✕`, the Y-STR agreement `✓`/`✗`, the sortable-table sort
arrows `▲`/`▼`, the unread dots on threads and DMs, the match-strength meter, the
STR group banner `▸`, and `→` in a dozen status lines. Each replacement was
probed against the actual fonts rather than chosen by eye — `✕`(U+2715) sits one
codepoint from `✖`(U+2716) and only the latter has a glyph, which is precisely
how this recurs:

  ✕ -> ✖   ✎ -> ✏   ✓ -> ✔   ✗ -> ✖
  ● -> ⚫   ○ -> ⚪   ▲▼ -> ⏶⏷   ▸ -> ▶   → -> ›

Two of them were in `navigator-app`, not here: a consensus warning and the import
summary's reference notes, both built there and drawn by egui via `sources.rs`
and `events.rs`.

`every_source_string_literal_is_renderable` is the part meant to outlast this.
Parsing rather than grepping is what makes it safe — comments are full of `→` and
`◆` naming the very bug they describe, and the AST has literals and no comments.
Two things had to be right for it to see anything: doc comments arrive as
`#[doc = "…"]` attributes, and syn hands macro bodies over as an unparsed
`TokenStream`, which would have hidden nearly every label in the app, since they
are built by `format!`. Verified by injecting a bad glyph into a `format!` and
watching it fail.

It is scoped to this crate, and the doc comment says why that gap is deliberate:
`navigator-app` also feeds the CLI and the HTML exporter, where `→` and `✓` are
correct, so which of its strings reach a window is a dataflow question rather
than a syntactic one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@JamesKane
JamesKane merged commit 3157412 into main Aug 9, 2026
3 checks passed
@JamesKane
JamesKane deleted the fix/ui-source-literal-glyphs branch August 9, 2026 13:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant