Skip to content

feat(t27f): renderers draw the UI Kit frame in TH_T27 + Outfit (Refs #13296 #13297 #13298) - #13324

Merged
cursor[bot] merged 5 commits into
claude/t27f-renderersfrom
claude/t27f-style-uikit
Nov 9, 2026
Merged

cursor[bot] merged 5 commits into
claude/t27f-renderersfrom
claude/t27f-style-uikit

Conversation

@gHashTag

@gHashTag gHashTag commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

Refs #13296 #13297 #13298 #13214

Style lane, stacked on #13315 (renderers). Merge order: #13254, then #13315, then this PR, then the uikit lane.

What changed

The renderers now draw the UI Kit frame that already exists. They carry no palette or glyph table of their own. No new style spec was added: theme, tokens, type and painter were already specs, so this PR wires them in.

Source What the renderers take from it
vf_theme.t27 (TH_T27) Ground 0x000000, text 0xFFFFFF, accent 0x00FF88, outline 0x333333, control fill 0x141414
vf_card_tok.t27 vct_render: the token style (26 Bold heading, 16 Regular body, padding, border) in one call
vf_type.t27 Outfit glyphs
vf_card.t27 The painter rule, now public: vc_box / vc_text return what vc_node paints, and vc_fill / vc_off_m are pub
  • web: the draw list (FILL + TEXT ops, 6 words each) is built from vc_box / vc_text. Replaying it equals the raster byte for byte.
  • native: t27os_blit gets the vct_render raster, and only on VC_OK.
  • fpga: the AXI4-Lite write plan is FB_W, FB_H, then 8 box fills (6 writes each, 50 writes so far). Text follows as 1-px-high fill spans. No glyph ROM is needed. The fill-engine model replays the plan to the vct_render frame in every byte.
  • main: VF_TARGET=web|native|fpga ./m > out.ppm.

Evidence

Tests (t27b):

Spec Result
vf_render_web 7/7
vf_render_native 2/2
vf_render_fpga 4/4, with an n-1 negative control and a refused out-of-window address
vf_render_main 2/2: three targets give one frame, with a one-byte negative control
vf_card_tok 11/17, including the new vct_render_is_the_token_style (heading-size negative control)
vf_card 10/21

Artifact:

  • web.ppm, native.ppm and fpga.ppm are byte-identical: 121515 bytes each, sha256 36d330f03a1c50462d0bb047c87be030475f9fa74cce0c51e3898e1337769963.
  • Pixel (0,0) = 0x333333 (outline); pixel (17,53) = 0x00FF88 (accent).

Gates: tools/dupe_scan.py reports no new or grown duplicate body. The lefthook own-language and secret-gate checks pass.

Known gap, not fixed here

Six tests in vf_card_tok and eleven in vf_card fail. They fail at the same asserts on the HEAD copy of vf_card.t27, before this PR. The cause is layout drift: card heights, a title top of 20, and a min-target check that follow vf_layout text wrap, last touched on the #13254 lane. These belong to #13254 or a follow-up, not to the style lane.

Not done: no seal (none was asked for), no window has shown the native frame yet, and no board has run the FPGA plan.

A session merges only a PR it opened itself, and only when its brief or the owner says to. It never merges, approves, closes, retargets or force-pushes a PR or branch that another session, bee or person opened, even when that PR is the one holding its own gate.

🤖 Generated with Claude Code


Note

Medium Risk
Large cross-cutting renderer refactor with new public painter APIs and FPGA span encoding; risk is drift or performance in the text-span plan, mitigated by byte-for-byte tests across targets.

Overview
Unifies web, native, and FPGA on one UI Kit frame: theme TH_T27, token metrics via vct_render, and Outfit text—no per-target palettes or 5×7 glyph ROM.

vf_card now exposes a single placement rule: public vc_box, vc_text, VcBox, VcText, and vc_fill. vc_node only replays those records into the raster so vector paths cannot drift.

vf_card_tok adds vct_render / vct_render_style() as the shared raster entry for all three renderers.

vf_menu takes padding and gaps from vct_metrics(), height 108 (was 84), and Browser alone is PRIMARY; Desktop/FPGA are SECONDARY.

Web: vf_draw_ops is built from vc_box / vc_text; vf_ops_replay must match vct_render byte-for-byte. Canvas text uses Q8 pen, baseline, px, and weight.

Native: blits the vct_render raster; returns VC_* instead of VF_R_*.

FPGA: drops GLYPH commands; boxes from the draw list, text as 1px-high FILL spans after diffing vct_render vs a base replay. The engine model uses vc_fill; tests pin equality to the full raster (121500 bytes).

vf_render_main: all VF_TARGET values emit the same 375×108 PPM from that unified pipeline.

Reviewed by Cursor Bugbot for commit 75f8206. Bugbot is set up for automated code reviews on this repo. Configure here.

…13296 #13297 #13298 #13214)

The three renderers no longer carry their own palette or glyphs. Every target
draws the existing UI Kit frame: vct_render (vf_card_tok) in theme TH_T27 with
Outfit type (vf_type), boxes and text from one public rule in vf_card.

- vf_card: the painter rule is public. vc_box / vc_text return what vc_node
  paints (VcBox, VcText); vc_fill and vc_off_m are pub. No new spec.
- vf_card_tok: vct_render / vct_render_style are the token style as one call;
  test vct_render_is_the_token_style compares it with vc_render_s, with a
  heading-size negative control.
- vf_render_web: the draw list (FILL + TEXT ops) is built from vc_box / vc_text;
  its replay equals the raster byte for byte.
- vf_render_native: blits the vct_render raster, only on VC_OK.
- vf_render_fpga: AXI4-Lite plan = 8 box fills (50 writes) plus 1-px text
  spans; the fill-engine model replays it to the vct_render frame in every
  byte, with an n-1 negative control and an out-of-window address refusal.
- vf_render_main: VF_TARGET=web|native|fpga writes the frame as a PPM.

Tests: vf_render_web 7/7, vf_render_native 2/2, vf_render_fpga 4/4,
vf_render_main 2/2. vf_card 10/21 and vf_card_tok 11/17: the failing asserts
fail the same way on the HEAD copy (layout drift on the #13254 lane), not
fixed here.
Artifact: web.ppm, native.ppm and fpga.ppm are byte-identical (121515 bytes,
sha256 36d330f0...69963).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…3296 #13297 #13298 #13214)

The Verilog widths gate (tools/check_verilog_widths.py) refused
vf_render_main: its 162000-byte RGBA local lowered to a 1296000-bit
signal, over the 2^20-bit absurd-width limit. The web route in main now
builds the canvas draw list (vf_draw_ops) and replays it into RGB
(vf_ops_replay), the same ops canvas_fill_rect and canvas_draw_text get.
The RGBA pixel path stays tested in vf_render_web.

- widest gen-verilog range now 1048575 (the 32768 x u32 fpga buffers)
- t27b test vf_render_main: 2 passed, 0 failed
- web.ppm, native.ppm, fpga.ppm still byte-identical, 121515 bytes,
  sha256 36d330f0...69963, the same frame as before

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (Refs #13296 #13297 #13298 #13214)

spec-guards refused specs/course/lesson_01_style_main.t27 (head 1000000,
base 0). t27c keeps one name set for str-typed names (string_names,
is_string_typed), and the new str field VcText.s put `s` in it, so
`s == SCENE_HOOK` in lesson_01_fpga_intro's scene(s: u8), bundled into the
same Zig, lowered to std.mem.eql and Zig refused it ("expected type
'[]const u8', found 'u8'"). The field is now run_s; vf_card and
vf_render_web follow.

- t27c test-report on the 10 specs spec-guards selects: every count equals
  the base (lesson 0, vf_card 11, vf_card_fit 1, vf_card_tok 6,
  vf_card_main 1, vf_menu and the four renderers 0), none BLOCKED
- t27b test vf_render_web 7/7, vf_render_main 2/2

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 9, 2026
@cursor
cursor Bot marked this pull request as ready for review November 9, 2026 19:51
@cursor
cursor Bot merged commit d04f1ce into claude/t27f-renderers Nov 9, 2026
41 of 45 checks passed
@cursor
cursor Bot deleted the claude/t27f-style-uikit branch November 9, 2026 19:52
cursor Bot pushed a commit that referenced this pull request Nov 9, 2026
…13297 #13298) (#13315)

* feat(t27f): menu graph + web, fpga and native renderers (Refs #13296 #13297 #13298)

vf_menu.t27: the shared 3-button graph (Browser / Desktop / FPGA) with
route tokens from ui_targets; pointer and key pick a route. 6/6 tests.

vf_render_web.t27: canvas route. A 6-word draw list (13 FILL + 4 TEXT
for the menu) for canvas_fill_rect / canvas_draw_text, plus the RGBA
plan for canvas_put_pixels. Vector fills equal the raster. 7/7 tests.

vf_render_fpga.t27: AX7203 AXI4-Lite route. Register map at
0x44A00000 (CTRL, X, Y, W, H, RGB, GLYPH_LO/HI, FB_W/H); the menu is
13 fills + 25 glyphs = 230 writes. A software model of the fill engine
replays the writes and equals vf_render_rgb in all 94500 bytes; one
write fewer does not (negative control). 6/6 tests.

vf_render_native.t27: T27 OS route. One t27os_blit of the 24-bit
raster on success, nothing on a refused frame. 2/2 tests.

vf_render_main.t27: artifact driver. VF_TARGET=web|native|fpga writes
the menu frame as a binary PPM; the three outputs are byte-identical
(sha256 30d08352...). 2/2 tests.

Not here: the WASM lowering and the JS canvas shim, the FPGA engine RTL
and bus bridge, and the T27 OS host symbol t27os_blit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(t27f): renderers draw the UI Kit frame in TH_T27 + Outfit (Refs #13296 #13297 #13298) (#13324)

* feat(t27f): renderers draw the UI Kit frame in TH_T27 + Outfit (Refs #13296 #13297 #13298 #13214)

The three renderers no longer carry their own palette or glyphs. Every target
draws the existing UI Kit frame: vct_render (vf_card_tok) in theme TH_T27 with
Outfit type (vf_type), boxes and text from one public rule in vf_card.

- vf_card: the painter rule is public. vc_box / vc_text return what vc_node
  paints (VcBox, VcText); vc_fill and vc_off_m are pub. No new spec.
- vf_card_tok: vct_render / vct_render_style are the token style as one call;
  test vct_render_is_the_token_style compares it with vc_render_s, with a
  heading-size negative control.
- vf_render_web: the draw list (FILL + TEXT ops) is built from vc_box / vc_text;
  its replay equals the raster byte for byte.
- vf_render_native: blits the vct_render raster, only on VC_OK.
- vf_render_fpga: AXI4-Lite plan = 8 box fills (50 writes) plus 1-px text
  spans; the fill-engine model replays it to the vct_render frame in every
  byte, with an n-1 negative control and an out-of-window address refusal.
- vf_render_main: VF_TARGET=web|native|fpga writes the frame as a PPM.

Tests: vf_render_web 7/7, vf_render_native 2/2, vf_render_fpga 4/4,
vf_render_main 2/2. vf_card 10/21 and vf_card_tok 11/17: the failing asserts
fail the same way on the HEAD copy (layout drift on the #13254 lane), not
fixed here.
Artifact: web.ppm, native.ppm and fpga.ppm are byte-identical (121515 bytes,
sha256 36d330f0...69963).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(t27f): web route in vf_render_main replays the draw list (Refs #13296 #13297 #13298 #13214)

The Verilog widths gate (tools/check_verilog_widths.py) refused
vf_render_main: its 162000-byte RGBA local lowered to a 1296000-bit
signal, over the 2^20-bit absurd-width limit. The web route in main now
builds the canvas draw list (vf_draw_ops) and replays it into RGB
(vf_ops_replay), the same ops canvas_fill_rect and canvas_draw_text get.
The RGBA pixel path stays tested in vf_render_web.

- widest gen-verilog range now 1048575 (the 32768 x u32 fpga buffers)
- t27b test vf_render_main: 2 passed, 0 failed
- web.ppm, native.ppm, fpga.ppm still byte-identical, 121515 bytes,
  sha256 36d330f0...69963, the same frame as before

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(t27f): VcText field s -> run_s, lesson_01_style_main builds again (Refs #13296 #13297 #13298 #13214)

spec-guards refused specs/course/lesson_01_style_main.t27 (head 1000000,
base 0). t27c keeps one name set for str-typed names (string_names,
is_string_typed), and the new str field VcText.s put `s` in it, so
`s == SCENE_HOOK` in lesson_01_fpga_intro's scene(s: u8), bundled into the
same Zig, lowered to std.mem.eql and Zig refused it ("expected type
'[]const u8', found 'u8'"). The field is now run_s; vf_card and
vf_render_web follow.

- t27c test-report on the 10 specs spec-guards selects: every count equals
  the base (lesson 0, vf_card 11, vf_card_fit 1, vf_card_tok 6,
  vf_card_main 1, vf_menu and the four renderers 0), none BLOCKED
- t27b test vf_render_web 7/7, vf_render_main 2/2

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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