Page template renderer: HERO1 and GRID6, Phase 1b (#59) #120

Merged
robert merged 1 commit from area/page-template-hero1-grid6 into main 2026-09-06 00:17:08 +02:00
Owner

Completes the part of #59 that PR #87 deliberately deferred, per that issue's own 2026-09-01 update (D32): HERO1 and GRID6, on top of the emery/HERO2/QUAD Phase 1 renderer #87 already shipped. Read the issue's full body (including the 2026-09-01 drawn-digits/emphasised-slot/narrowing update and the 2026-09-02 stale-vs-unavailable update) and PR #87's own description before starting — this PR reuses everything #87 built and does not touch it.

What this ships

page_render_geometry.h/.c — same bounds-in/rects-out contract as #87 (zero <pebble.h>, zero SDK calls, D34):

  • page_render_hero1_hero_rect(content) — one field, the whole content area (DESIGN.md section 3: "full 200x208" on emery). Named to match the established page_render_hero2_hero_rect()/page_render_quad_cell_rect() convention. Returns content unchanged, but exists as a real, host-testable function anyway so a future round variant (#62) has a call site to grow a PBL_IF_ROUND_ELSE branch into.

  • page_render_grid6_cell_rect(content, slot) — 2x3 grid, same last-row/last-column-absorbs-the-remainder tiling rule as page_render_quad_cell_rect().

  • page_render_digit_height() extended:

    • HERO1 slot 0 = 140px, DESIGN.md's own "~140px digits" figure, used exactly.
    • GRID6 ordinary slots (1-5) = 40px, DESIGN.md's own figure (also D29/D24's stated 5.0mm floor).
    • GRID6 slot 0 (PAGE_EMPHASISED_SLOT, D29) = 46px — a reasoned choice mirroring QUAD's 56/76 pattern (a size lookup keyed on slot index, not a separate layout path), but with much less headroom: GRID6's cell (100x69) leaves only 69-20=49px of value area versus QUAD's 84px, so the margin above the ordinary height is smaller (3px clearance vs QUAD's 8px). Verified in the emulator screenshot below, same as QUAD's own 76 was.
  • page_render_resolve_template(requested, content) — new function. GRID6 falls back to QUAD when content, divided into a 2x3 grid, produces a cell smaller than GRID6's own DESIGN.md reference cell (100x69) in either dimension. This is a pixel comparison against DESIGN.md's own numbers, not a PBL_PLATFORM_* check — it stays platform-blind by construction (this file takes no SDK dependency, same as everything else in it) and correctly falls back on basalt's real content (144x148 → 72x49 cell) while leaving GRID6 untouched on emery (200x208 → 100x69, exactly at the reference size).

    Deliberately does not special-case gabbro. A round screen's usable area is smaller than its rectangular bounding box (a 260x260 circle only inscribes ~184x184, D24), but nothing in this file — Phase 1 or 1b — has taught page_render_content_rect() that a screen can be round; it still returns a plain rectangle. Called with gabbro's raw rectangular content (260x240), this function reports GRID6 "fits" (cell 130x80), which is wrong for the physical screen. That gap is real, confirmed by an actual gabbro screenshot below, and is explicitly not fixed here — building the round-aware content rect that would make this call correct is #62's job, a separate open issue, not a side effect of this one.

page_render.c/.h — prv_draw_hero1()/prv_draw_grid6() added, following the exact same one-cell-at-a-time pattern as prv_draw_hero2()/prv_draw_quad(). page_render_draw() now resolves the template through page_render_resolve_template() before its dispatch switch; a GRID6 that resolves to QUAD draws through the existing, unchanged prv_draw_quad(), which means it only shows desc->fields[0..3] — silently dropping fields 4 and 5 is the expected degrade the issue's own acceptance criterion ("GRID6 may fall back to QUAD") asks for, documented at the call site.

Stale-vs-unavailable (D44) and label overflow handling (GTextOverflowModeTrailingEllipsis) are entirely unchanged, reused as-is — prv_draw_cell() doesn't know or care which template called it, so HERO1/GRID6 get D44's hollow+strike-vs-solid-dashes distinction and #87's ellipsis-based overflow protection for free, with no new code.

watchapp/tests/test_page_render_geometry.c — 10 new tests (38 total, up from 28): HERO1's hero rect, GRID6's cell rects (DESIGN.md numbers + exact tiling/no-gap/no-overlap, at emery's real 200x228), GRID6's emphasised-slot lookup, HERO1's digit height, and page_render_resolve_template() at both emery's and basalt's real dimensions (144x168 — DESIGN.md's own basalt figure). Hand-compiled and run directly, no cmake binary in this environment:

gcc -std=c11 -Wall -Wextra -Werror -I src/c -I tests \
  tests/test_page_render_geometry.c src/c/page_render_geometry.c -o test_page_render_geometry
./test_page_render_geometry
# 38 test(s) run, 0 failure(s)

Also re-ran all four other existing host suites (fields, page, state, backlight) the same way — 126 tests, zero regressions, 164 total across the whole suite.

Two real findings, flagged in code rather than silently patched

Both surfaced by this PR's own emulator verification (not assumed), and both are pre-existing, cross-cutting limitations of the fixed-digit-height mechanism (page_render_digit_height(template, slot) doesn't know content's actual size) rather than something a two-template Phase 1b PR should fix as a side effect:

  1. HERO1 at the literal 140px clips. DESIGN.md's own single most natural HERO1 value — FIELD_SPEED's "28.4" (3 digits + 1 dot) — needs ~322px of run width at 140px digit height (the fixed 0.6x-height-per-digit ratio every template uses). Emery's content is only 200px wide; basalt's is 144. Confirmed via real screenshots on both (below) — the last digit is visibly sliced off-screen on emery, and HERO1 is nearly unusable on basalt. The largest height that keeps that same value on-screen on emery is ~88px — smaller than HERO2's own 96px hero, which would make HERO1 the smaller of the two "one dominant number" shapes, contradicting the physical-size table the 140 figure came from. Fixing this properly means making digit height content-width-aware, which is a mechanism change touching every template, not a HERO1-specific number swap — flagged in page_render_geometry.c and left at the literal DESIGN.md figure rather than quietly substituting a smaller number that buries the conflict.
  2. QUAD itself (unchanged, already-shipped Phase 1 code) has never been rendered on basalt until this PR's own basalt verification work. It also overflows there: basalt's real QUAD cell is 72x74, well under emery's 100x104 that QUAD's 56/76px digit heights were sized against. A 3-digit-plus-dot value visibly overlaps the neighbouring cell (screenshot below). This means DESIGN.md rule 7's "GRID6 drops to QUAD on basalt" doesn't yet fully deliver "degrades without clipping", because the fallback target itself wasn't basalt-safe to begin with. This affects every QUAD page on basalt, including the two default pages (Effort, Progress) that already ship — not something GRID6's fallback introduces, just something it was the first to expose visually. Same root cause as (1); same reason it isn't fixed here.

Both are documented in code comments at the relevant constant/function, and reported here for a decision rather than resolved unilaterally (D48: a checked fact, not a better argument).

Render cost — measured, not assumed

Same carousel.c timing wrapper #87 used (time_ms() bracketing page_render_draw()), same emery emulator. Real log lines:

page_render_draw(Ride, template=0):   25-46 ms   (HERO1)
page_render_draw(Progress, template=2): 28-47 ms (QUAD, unchanged)
page_render_draw(Effort, template=3):  61-69 ms  (GRID6 — six cells, more glyphs, costs more)

All comfortably inside the 1Hz (1000ms) budget — GRID6's worst case is 6.9% of it. heap_bytes_free() stayed flat across every transition in the same log (99784 -> 99784) — no allocation anywhere in the new code, digit drawing included.

Also measured on basalt and gabbro, since this PR is the first to actually look at either for these templates:

  • gabbro: 28-67ms — essentially identical to emery (same-generation hardware).
  • basalt: 150-360ms — 3-5x emery's cost (older/slower hardware), but still comfortably under the 1000ms budget (worst case 36%). Flagging the magnitude honestly rather than only reporting the comfortable emery number.

Verification

Host tests: see above — 38/38 in this suite, 164/164 across the whole host suite, hand-compiled with gcc -std=c11 -Wall -Wextra -Werror.

Builds: pebble build clean for all three platforms (emery, gabbro, basalt) — zero warnings from any changed file, both before and after the emulator-verification temp seeding was reverted. Final emery/basalt memory-usage reports are byte-for-byte identical to the pre-change build (14384 bytes RAM footprint) — nothing here costs any static RAM.

Emulator, live, real screenshots — temporarily seeded fields.c's store and swapped two default pages' templates to HERO1/GRID6 in main.c/page.c (same technique #87 used, marked _TEMP, fully reverted — git diff on both files is empty in this PR) purely to have real digit values and reachable pages to screenshot:

  • emery, HERO1 (FIELD_SPEED, "28.4"): renders at 140px, confirms finding (1) above — last digit clipped off the right edge.
  • emery, GRID6 (DESIGN.md's own worked mockup fields: SPEED/AVG SPEED/DIST/TIME/HEART/POWER): renders cleanly, all six cells tile with no gaps, slot 0 (SPEED) visibly and unmistakably larger than the other five — direct confirmation of D29. Matches DESIGN.md's section-3 diagram values exactly (28.4 / 24.1 / 42.7 / 1:47 / 148 / 213).
  • emery, GRID6 with a worst-case German-length label (AVG SPEED temporarily replaced with DURCHSCHNITTSGESCHWINDIGKEIT, reverted): truncates cleanly via the existing ellipsis mechanism, no clipping, digit rendering completely unaffected — confirms the same #87 mechanism covers HERO1/GRID6 with no new code needed.
  • basalt, HERO1: severe overflow, confirms finding (1) is not emery-specific.
  • basalt, GRID6-descriptor page: correctly resolves to QUAD (only 4 of the 6 configured fields shown — SPEED/AVG SPEED/DIST/TIME, HEART/POWER dropped as expected) — confirms page_render_resolve_template() works, but also visibly shows finding (2): the QUAD cells themselves overlap.
  • gabbro, unmodified QUAD (Progress) and GRID6-descriptor (Effort) pages: both visibly clipped by the round bezel (labels and a value corner cut off) — confirms, as expected and as documented in code, that this file's rectangular math does not yet know about round screens. Not fixed here; #62's job.

Not in this PR

  • Round (gabbro) geometry variants — #62, still open. Nothing round-specific is built here; page_render_resolve_template() explicitly does not special-case gabbro (see its own comment) rather than half-building #62 as a side effect.
  • Internationalisation infrastructure — #51, still open. Only checked that the existing ellipsis mechanism holds up against a worst-case German-length label; no German strings were added.
  • The two findings above (HERO1's literal-140 clipping, QUAD-on-basalt's pre-existing overflow) — flagged, not fixed, per D48.
  • The on-watch nav view (#35) and icon set (#34) — unchanged, out of scope, same as #87.

https://claude.ai/code/session_01DAoXbRmJUf2uxNYBfdAXPt

Completes the part of #59 that PR #87 deliberately deferred, per that issue's own 2026-09-01 update (D32): **`HERO1` and `GRID6`**, on top of the `emery`/`HERO2`/`QUAD` Phase 1 renderer #87 already shipped. Read the issue's full body (including the 2026-09-01 drawn-digits/emphasised-slot/narrowing update and the 2026-09-02 stale-vs-unavailable update) and PR #87's own description before starting — this PR reuses everything #87 built and does not touch it. ## What this ships **`page_render_geometry.h`/`.c`** — same bounds-in/rects-out contract as #87 (zero `<pebble.h>`, zero SDK calls, D34): - `page_render_hero1_hero_rect(content)` — one field, the whole content area (DESIGN.md section 3: "full 200x208" on emery). Named to match the established `page_render_hero2_hero_rect()`/`page_render_quad_cell_rect()` convention. Returns `content` unchanged, but exists as a real, host-testable function anyway so a future round variant (#62) has a call site to grow a `PBL_IF_ROUND_ELSE` branch into. - `page_render_grid6_cell_rect(content, slot)` — 2x3 grid, same last-row/last-column-absorbs-the-remainder tiling rule as `page_render_quad_cell_rect()`. - `page_render_digit_height()` extended: - `HERO1` slot 0 = **140px**, DESIGN.md's own "~140px digits" figure, used exactly. - `GRID6` ordinary slots (1-5) = **40px**, DESIGN.md's own figure (also D29/D24's stated 5.0mm floor). - `GRID6` slot 0 (`PAGE_EMPHASISED_SLOT`, D29) = **46px** — a reasoned choice mirroring QUAD's 56/76 pattern (a size lookup keyed on slot index, not a separate layout path), but with much less headroom: GRID6's cell (100x69) leaves only 69-20=49px of value area versus QUAD's 84px, so the margin above the ordinary height is smaller (3px clearance vs QUAD's 8px). Verified in the emulator screenshot below, same as QUAD's own 76 was. - `page_render_resolve_template(requested, content)` — new function. GRID6 falls back to QUAD when `content`, divided into a 2x3 grid, produces a cell smaller than GRID6's own DESIGN.md reference cell (100x69) in either dimension. This is a **pixel comparison against DESIGN.md's own numbers, not a `PBL_PLATFORM_*` check** — it stays platform-blind by construction (this file takes no SDK dependency, same as everything else in it) and correctly falls back on basalt's real content (144x148 → 72x49 cell) while leaving GRID6 untouched on emery (200x208 → 100x69, exactly at the reference size). **Deliberately does not special-case gabbro.** A round screen's usable area is smaller than its rectangular bounding box (a 260x260 circle only inscribes ~184x184, D24), but nothing in this file — Phase 1 or 1b — has taught `page_render_content_rect()` that a screen can be round; it still returns a plain rectangle. Called with gabbro's raw rectangular content (260x240), this function reports GRID6 "fits" (cell 130x80), which is wrong for the physical screen. That gap is real, confirmed by an actual gabbro screenshot below, and is explicitly **not** fixed here — building the round-aware content rect that would make this call correct is #62's job, a separate open issue, not a side effect of this one. **`page_render.c`/`.h`** — `prv_draw_hero1()`/`prv_draw_grid6()` added, following the exact same one-cell-at-a-time pattern as `prv_draw_hero2()`/`prv_draw_quad()`. `page_render_draw()` now resolves the template through `page_render_resolve_template()` before its dispatch switch; a GRID6 that resolves to QUAD draws through the *existing, unchanged* `prv_draw_quad()`, which means it only shows `desc->fields[0..3]` — silently dropping fields 4 and 5 is the expected degrade the issue's own acceptance criterion ("GRID6 may fall back to QUAD") asks for, documented at the call site. Stale-vs-unavailable (D44) and label overflow handling (`GTextOverflowModeTrailingEllipsis`) are **entirely unchanged, reused as-is** — `prv_draw_cell()` doesn't know or care which template called it, so HERO1/GRID6 get D44's hollow+strike-vs-solid-dashes distinction and #87's ellipsis-based overflow protection for free, with no new code. **`watchapp/tests/test_page_render_geometry.c`** — 10 new tests (38 total, up from 28): `HERO1`'s hero rect, `GRID6`'s cell rects (DESIGN.md numbers + exact tiling/no-gap/no-overlap, at emery's real 200x228), `GRID6`'s emphasised-slot lookup, `HERO1`'s digit height, and `page_render_resolve_template()` at both emery's and basalt's real dimensions (144x168 — DESIGN.md's own basalt figure). Hand-compiled and run directly, no cmake binary in this environment: ``` gcc -std=c11 -Wall -Wextra -Werror -I src/c -I tests \ tests/test_page_render_geometry.c src/c/page_render_geometry.c -o test_page_render_geometry ./test_page_render_geometry # 38 test(s) run, 0 failure(s) ``` Also re-ran all four other existing host suites (`fields`, `page`, `state`, `backlight`) the same way — 126 tests, zero regressions, 164 total across the whole suite. ## Two real findings, flagged in code rather than silently patched Both surfaced by this PR's own emulator verification (not assumed), and both are pre-existing, cross-cutting limitations of the fixed-digit-height mechanism (`page_render_digit_height(template, slot)` doesn't know `content`'s actual size) rather than something a two-template Phase 1b PR should fix as a side effect: 1. **HERO1 at the literal 140px clips.** DESIGN.md's own single most natural HERO1 value — `FIELD_SPEED`'s "28.4" (3 digits + 1 dot) — needs ~322px of run width at 140px digit height (the fixed 0.6x-height-per-digit ratio every template uses). Emery's content is only 200px wide; basalt's is 144. Confirmed via real screenshots on both (below) — the last digit is visibly sliced off-screen on emery, and HERO1 is nearly unusable on basalt. The largest height that keeps that same value on-screen on emery is ~88px — *smaller* than HERO2's own 96px hero, which would make HERO1 the smaller of the two "one dominant number" shapes, contradicting the physical-size table the 140 figure came from. Fixing this properly means making digit height content-width-aware, which is a mechanism change touching every template, not a HERO1-specific number swap — flagged in `page_render_geometry.c` and left at the literal DESIGN.md figure rather than quietly substituting a smaller number that buries the conflict. 2. **QUAD itself (unchanged, already-shipped Phase 1 code) has never been rendered on basalt until this PR's own basalt verification work.** It also overflows there: basalt's real QUAD cell is 72x74, well under emery's 100x104 that QUAD's 56/76px digit heights were sized against. A 3-digit-plus-dot value visibly overlaps the neighbouring cell (screenshot below). This means DESIGN.md rule 7's "GRID6 drops to QUAD on basalt" doesn't yet fully deliver "degrades without clipping", because the fallback target itself wasn't basalt-safe to begin with. This affects every QUAD page on basalt, including the two default pages (Effort, Progress) that already ship — not something GRID6's fallback introduces, just something it was the first to expose visually. Same root cause as (1); same reason it isn't fixed here. Both are documented in code comments at the relevant constant/function, and reported here for a decision rather than resolved unilaterally (D48: a checked fact, not a better argument). ## Render cost — measured, not assumed Same `carousel.c` timing wrapper #87 used (`time_ms()` bracketing `page_render_draw()`), same emery emulator. Real log lines: ``` page_render_draw(Ride, template=0): 25-46 ms (HERO1) page_render_draw(Progress, template=2): 28-47 ms (QUAD, unchanged) page_render_draw(Effort, template=3): 61-69 ms (GRID6 — six cells, more glyphs, costs more) ``` All comfortably inside the 1Hz (1000ms) budget — GRID6's worst case is 6.9% of it. `heap_bytes_free()` stayed flat across every transition in the same log (99784 -> 99784) — no allocation anywhere in the new code, digit drawing included. **Also measured on basalt and gabbro**, since this PR is the first to actually look at either for these templates: - gabbro: 28-67ms — essentially identical to emery (same-generation hardware). - basalt: **150-360ms** — 3-5x emery's cost (older/slower hardware), but still comfortably under the 1000ms budget (worst case 36%). Flagging the magnitude honestly rather than only reporting the comfortable emery number. ## Verification **Host tests**: see above — 38/38 in this suite, 164/164 across the whole host suite, hand-compiled with `gcc -std=c11 -Wall -Wextra -Werror`. **Builds**: `pebble build` clean for all three platforms (`emery`, `gabbro`, `basalt`) — zero warnings from any changed file, both before and after the emulator-verification temp seeding was reverted. Final `emery`/`basalt` memory-usage reports are byte-for-byte identical to the pre-change build (14384 bytes RAM footprint) — nothing here costs any static RAM. **Emulator, live, real screenshots** — temporarily seeded `fields.c`'s store and swapped two default pages' templates to HERO1/GRID6 in `main.c`/`page.c` (same technique #87 used, marked `_TEMP`, fully reverted — `git diff` on both files is empty in this PR) purely to have real digit values and reachable pages to screenshot: - *emery, HERO1* (`FIELD_SPEED`, "28.4"): renders at 140px, confirms finding (1) above — last digit clipped off the right edge. - *emery, GRID6* (DESIGN.md's own worked mockup fields: SPEED/AVG SPEED/DIST/TIME/HEART/POWER): renders cleanly, all six cells tile with no gaps, slot 0 (SPEED) visibly and unmistakably larger than the other five — direct confirmation of D29. Matches DESIGN.md's section-3 diagram values exactly (28.4 / 24.1 / 42.7 / 1:47 / 148 / 213). - *emery, GRID6 with a worst-case German-length label* (`AVG SPEED` temporarily replaced with `DURCHSCHNITTSGESCHWINDIGKEIT`, reverted): truncates cleanly via the existing ellipsis mechanism, no clipping, digit rendering completely unaffected — confirms the same #87 mechanism covers HERO1/GRID6 with no new code needed. - *basalt, HERO1*: severe overflow, confirms finding (1) is not emery-specific. - *basalt, GRID6-descriptor page*: correctly resolves to QUAD (only 4 of the 6 configured fields shown — SPEED/AVG SPEED/DIST/TIME, HEART/POWER dropped as expected) — confirms `page_render_resolve_template()` works, but also visibly shows finding (2): the QUAD cells themselves overlap. - *gabbro, unmodified QUAD (Progress) and GRID6-descriptor (Effort) pages*: both visibly clipped by the round bezel (labels and a value corner cut off) — confirms, as expected and as documented in code, that this file's rectangular math does not yet know about round screens. Not fixed here; #62's job. ## Not in this PR - Round (`gabbro`) geometry variants — #62, still open. Nothing round-specific is built here; `page_render_resolve_template()` explicitly does not special-case gabbro (see its own comment) rather than half-building #62 as a side effect. - Internationalisation infrastructure — #51, still open. Only checked that the existing ellipsis mechanism holds up against a worst-case German-length label; no German strings were added. - The two findings above (HERO1's literal-140 clipping, QUAD-on-basalt's pre-existing overflow) — flagged, not fixed, per D48. - The on-watch nav view (#35) and icon set (#34) — unchanged, out of scope, same as #87. https://claude.ai/code/session_01DAoXbRmJUf2uxNYBfdAXPt
Page template renderer: HERO1 and GRID6, Phase 1b (#59)
Some checks failed
dev-artifact / build-pbw (push) Failing after 0s
dev-artifact / build-apk (push) Failing after 0s
dev-artifact / publish (push) Has been skipped
fast-lane / host-c-tests (push) Failing after 0s
fast-lane / jvm-tests (push) Failing after 0s
fast-lane / pebble-build (push) Failing after 0s
fast-lane / lint-and-secrets (push) Failing after 0s
fast-lane / meta-declares-required-jobs (push) Failing after 0s
fast-lane / host-c-tests (pull_request) Failing after 0s
fast-lane / jvm-tests (pull_request) Failing after 0s
fast-lane / pebble-build (pull_request) Failing after 0s
fast-lane / lint-and-secrets (pull_request) Failing after 0s
fast-lane / meta-declares-required-jobs (pull_request) Failing after 0s
521f464f23
Completes the part of #59 that PR #87 deliberately deferred per D32:
HERO1 and GRID6 templates, on top of the emery/HERO2/QUAD Phase 1
renderer that already shipped.

- page_render_hero1_hero_rect(): one field, the whole content area
  (DESIGN.md section 3, 200x208 on emery), matching the naming
  convention page_render_hero2_hero_rect()/page_render_quad_cell_rect()
  already established.
- page_render_grid6_cell_rect(): 2x3 grid, same tiling/remainder rule
  as page_render_quad_cell_rect().
- page_render_digit_height() extended: HERO1 slot 0 = 140px (DESIGN.md's
  own '~140px' figure). GRID6 ordinary slots = 40px (DESIGN.md), slot 0
  (PAGE_EMPHASISED_SLOT, D29) = 46px, a reasoned choice mirroring QUAD's
  56/76 pattern but with less headroom (GRID6's cell is shorter).
- page_render_resolve_template(): GRID6 falls back to QUAD when the
  content rect is smaller than GRID6's own DESIGN.md reference cell
  (100x69) in either dimension. Threshold is a pixel comparison, not a
  PBL_PLATFORM_* check, so it stays platform-blind by construction and
  correctly does not special-case gabbro (whose round screen this file
  cannot yet reason about safely - that's #62's job).
- 10 new host tests in test_page_render_geometry.c (38 total, up from
  28), hand-compiled and run directly (no cmake in this environment).

Two real, checked-fact findings surfaced by this PR's own emulator
verification, flagged in code comments rather than silently patched
(both are cross-cutting fixed-digit-height limitations bigger than
this PR's two-template scope):
- HERO1 at the literal 140px figure clips a perfectly ordinary decimal
  value (FIELD_SPEED's own '28.4') on both emery and basalt - confirmed
  via emulator screenshot, not assumed.
- QUAD itself (unchanged, already-shipped Phase 1 code) was never
  actually rendered on basalt before this PR's own basalt verification
  work; it also overflows there, which means 'GRID6 falls back to QUAD
  on basalt' does not yet fully satisfy 'no clipping' the way DESIGN.md
  rule 7 assumes.

Not in this PR, per its own scope: gabbro/round geometry (#62, still
open - confirmed nothing round-specific is built here, and the fallback
threshold explicitly does not know about gabbro's usable area), German
label strings (#51, still open - only the existing ellipsis-based
overflow mechanism was checked against a worst-case German-length
label, not implemented).

Claude-Session: https://claude.ai/code/session_01DAoXbRmJUf2uxNYBfdAXPt
robert merged commit 5fb97a3b08 into main 2026-09-06 00:17:08 +02:00
Sign in to join this conversation.
No description provided.