visual-adjust
Visually iterate on UI layout by comparing screenshots against ground truth
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Visually iterate on UI layout by comparing screenshots against ground truth
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
You MUST use this before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation.
Picks up after /build to publish a GitHub release. Pre-flights the staged artifacts, drafts release_notes.md from commits since the last tag, tags master, then hands the gh release create upload command to the user. Stops short of running the upload — that's user-driven.
Spawn a fresh-context Opus 4.7 agent to give an independent take when Claude and the user are talking past each other on a contested logic interpretation, OR to validate behavioral self-observations from /session-recap Step 0. User-triggered or claude self-proposed at impasse.
End-of-session recap — codify learnings synchronously into their canonical homes; daily logs hold narrative continuity only.
Review dirty code and fix iteratively using Ralph loop pattern. When user say to "loop to fix dirty" or "review+fix"
Review dirty code changes using Claude Code Agent tool. When user say to "review" or "review changes" or "review dirty code"
| name | visual-adjust |
| description | Visually iterate on UI layout by comparing screenshots against ground truth |
| triggers | ["/visual-adjust","adjust ui","visual adjust","fix layout"] |
You are adjusting pygame display layout by comparing rendered screenshots against a real-world ground truth photo provided by the user.
preview_display.py supports --screenshot, --mode, --stop, --pa flagsupper_lcd.py)When the task spans more than one element, start wide before going deep:
Ask the user for a reference screenshot if they haven't provided one. Study it carefully — note font sizes, spacing, alignment, colors, margins.
uv run preview_display.py --screenshot screenshot_<name>.png --mode english --stop 0 --pa 2
Read the PNG to see the current rendering. Compare against ground truth.
Call out specific differences:
Edit the renderer code. Change one thing at a time:
Take a new screenshot, read it, compare against ground truth. Present to user for feedback.
For non-trivial size/position/font work, build a side-by-side composite that stacks the reference photo above the cropped render, height-aligned — see "Side-by-side composites" below.
rm screenshot_v<N-1>_*.png; the freshly-rendered
screenshot_v<N>_*.png stays. Never end a turn with no recent screenshot presentcd prefix to avoid path issuesWhen iterating on size, vertical placement, weight, or font choice, eyeballing two separate images side-by-side is unreliable. Build a composite that:
REFERENCE: <name> on top, RENDER (<label>): <name> belowcompare_<label>_<station>.png_dev_scripts/compare_fonts.py is a working implementation. Pattern:
# 1. render variants under a label
uv run preview_display.py --screenshot screenshot_v1_bold_tokyo.png --mode english --stop 0 --pa 2
# ... one per reference station
# 2. build composites
uv run _dev_scripts/compare_fonts.py v1_bold
For A/B/C comparison of multiple variants at once, see _dev_scripts/compare_grid.py —
stacks reference + N candidate renders for each station.
Use this whenever there's a static reference photo and you're iterating — much more reliable than mental overlay.
Your eyes are not precise enough — write code so the user can fine-tune it.
When writing or adjusting layout code:
Extract all tuneable values into a labeled params block at the top of the
method (or __init__ for fonts). Group them visually so they're easy to find:
# --- Badge params (adjust freely) ---
badge_x = 222
badge_w = 68
ring_black = 7
text_gap = -10
# -------------------------------------
All positioning must derive from those params — no magic numbers scattered
below. If the user changes badge_w, interior width, centering, and text
positions should all recompute automatically.
Font sizes live in __init__ (must be preloaded), but add a comment pointing
to the method so the user knows where to look. Layout in the draw method should
use font.get_size() / surface.get_size() to react to whatever size was set.
Do not add overflow guards (e.g. max(0, ...)). If the user sets a font too
large for the container, they should see the overflow — that's their signal to
tune the value.
Confirm each param's reactivity before coding — including coupling to OTHER elements: before writing a new element or wiring a tuneable, describe the intended reactive behaviour (e.g. "text group centered vertically in the white interior") AND name what derives from what across elements — "digit size = ratio × circle radius (changes when you resize the circle)" vs "digit size = its own param". Confirm with the user per relationship. A silently-coupled param surprises the user mid-tuning; a silently-independent one forces them to eyeball N copies of the same proportion. (2026-06-12: 5-station countdown digit was hard-derived from circle radius at a guessed ratio — one circle fit, another broke; the derive-or-independent question belonged up front.) Saves back-and-forth on the wrong layout model.
# Flags
--screenshot <file.png> # Save one frame and exit
--mode <kanji|furigana|english> # Force display mode
--stop <index> # Station index (0-based)
--pa <0|1|2> # 0=次は/Next, 1=まもなく/Arriving at, 2=ただいま/Now stopping at
--route <name|path> # Route shorthand (e.g. yamanote, sobu/1217F) or path; default _mock/main
--lower-view <full|eight|cycle> # Force lower LCD view; default 'cycle' (slot rotation on the beat schedule).
# 'eight' or 'full' freezes the view-cycler for deterministic frames.
--debug-grid # Tint each upper-LCD region's clear rect with a unique color
# Examples
uv run preview_display.py --screenshot out.png --mode english --stop 0 --pa 2
uv run preview_display.py --screenshot out.png --mode kanji --stop 3 --pa 0
uv run preview_display.py --screenshot 8sta.png --route sobu/1217F --stop 7 --lower-view eight
uv run preview_display.py --debug-grid --route sobu/1217F --mode english --stop 18 --pa 2
When the work is about region territory (where an element's drawing should and should not land) rather than glyph appearance, use --debug-grid. Each region's clear rect paints in a distinct color (red=dest, blue=prefix, yellow=clock, magenta=station, orange=pa_hint, gray=upper_bg). What it surfaces:
The principle: anything a region draws (bg fill, glyphs, decorations) must visually stay inside that region's confinement. Clear rect is not special — it's just one of the things drawn. Same rule for all of them. See DISPLAY.md "Element Clear-Background Convention" for the full statement.
Pygame font surfaces include leading — empty (transparent) pixels above visible glyph caps, ~10–15px for big fonts. So:
blit_y ≥ confinement.top. Pure analytical, no probing. If it passes → containment guaranteed by construction. If it fails → signal to probe, not auto-violation.D2 is the rule that ships. D1 is a useful pre-check. If the user requires a specific font size for IRL accuracy and D1 would forbid it, that's not actually a violation — probe D2 to confirm what visible pixels do.
When pixel-probing for containment, isolate the region so neighbors' content doesn't masquerade as the target's:
.ttf Helvetica has macron artifacts at large sizes → use .otf variants
(fonts/HelveticaNeue-Bold.otf is the canonical English-station font; the
old HelveticaNeueBold.ttf has been removed)_bg("<region>") as the first step of its draw method — not just the current
glyph footprint. All three mode renderers (Japanese / Furigana / English) share the
same territory for the same element. Adding a new region: register it in
_DEBUG_COLORS AND the Region Map comment block at the top of the LCD module.
See DISPLAY.md "Element Clear-Background Convention" for full rules,
including the band-bottom clamp that prevents the station's tall 2-line variant
from clobbering the prefix/clock above.SDL_VIDEODRIVER=dummy automatically — no window neededroute.json at audio/_mock/main/route.json
(not in code). Default for preview_display.py. Edit that file directly to add
test cases. Reference station indices for compare scripts: see
audio/_mock/main/README.md.