[Air #1440] feat: dedicated send-queue/open-terminal Stream Deck action icons - #1443
Conversation
…dered from face.ts glyphs Render the send-queue (comment glyph) and open-terminal (terminal glyph) manifest action icons from the same GLYPHS vectors the runtime key face uses, so the action-picker icon and the hardware key agree by construction. Repoint the manifest Icon + States[].Image off the shared 'action' asset. Remove six verified-dead PNGs (approve-gate-empty, approve-gate-pending, gate-nav, each +@2x). A committed one-off render script (scripts/render-action-icons.mjs) parses the glyph vector out of src/face.ts and rasterizes via system rsvg-convert (no new npm dep), keeping the PNGs derived from a single source without editing face.ts.
Claude's PR review (verified against the PNGs) caught the first-pass list icons filling ~45% of the frame vs the ~95% every other list/* asset uses: listSvg reused the key-frame padding, and the glyphs don't fill their authored 24x24 box. Rewrite the render pipeline to rasterize the glyph, trim to its true bbox, then fit it to the convention's fill fraction (list 0.94, key 0.56) via rsvg-convert + magick, with a build-time self-check that fails below 80% list coverage. Re-measured against siblings: list @2x 38x36 / 38x30 (sibling 38x34), key @2x 81x77 / 81x65 (sibling 80x68). Also from the review: friendly ENOENT for both system tools, quoted-key-safe extractGlyph regex, and a zero-dep PNG-dimension test guard.
|
Integration review (streamdeck architect) — APPROVE. Low-risk tier (assets + manifest), verified by looking at the rendered PNGs, not just reading the diff — the right check for an image change. Verified:
Board was degraded (codex unavailable — OpenAI billing, raised to the owner), so this ran 2-way + architect verification per the interim policy. Residual, non-blocking — the builder flagged it and it is real. I compared the renders side by side: the new |
Summary
Give the
send-queue(Send Feedback) andopen-terminal(Open Terminal) Stream Deck actions their own icons instead of borrowing the sharedactionasset, and drop six verified-dead PNGs. Icons are rendered from the glyph vectors already insrc/face.ts— the samecomment/terminalvectors the runtime key face draws — so the action-picker icon and the hardware key agree by construction.Closes #1440
What Changed
icons/<name>.png72,@2x144,icons/list/<name>.png20,@2x40):send-queue← thecommentglyphopen-terminal← theterminalglyphmanifest.json: repointed both actions'IconandStates[].Imageofficons/(list/)actiononto their dedicated assets. Elgatostreamdeck validatepasses.approve-gate-empty,approve-gate-pending,gate-nav, each+@2x. Liveapprove-gate.*/list/approve-gate.*kept.scripts/render-action-icons.mjs: a committed one-off render script (see decisions below).manifest-icons.test.ts(asset-integrity, dedicated-wiring, and committed-dimension guards) andrender-action-icons.test.ts(single-source extraction contract).Key Decisions
face.tsat build time. The render script readssrc/face.ts, extracts thecomment/terminalentries from theGLYPHSmap (and reproduces thestroked()wrapper), and rasterizes from that. The PNGs therefore derive from the exact vector the runtime face uses — no re-drawn duplicate, andface.tsis not edited (bugfix-1431 is in flight there; this PR's source diff ismanifest.json+ the new test/script files only).list/*≈ 95%, key images ≈ 56%), with a build-time self-check that fails if a list icon drops below 80% coverage. (This corrects a first-pass defect the CMAP round caught — see below.)rsvg-convert(librsvg) +magick(ImageMagick), no new npm dependency. Per the issue scope, a one-time asset build prefers repo-available tooling over adding a dependency solely to turn SVG into PNG. The script is committed for reproducibility; re-run withnode scripts/render-action-icons.mjs(needslibrsvg+imagemagick, both checked with a friendly error). Alternatives considered: adding@resvg/resvg-jsorsharpas a devDep — rejected as a dependency for a one-off.action/approve-gateassets: key image = rounded rectrx=12, fill#1C2128, white glyph; list icon = white glyph on transparent.CMAP Review (AIR / pr)
extractGlyphregex, committed-dimension test guard.open-terminal's glyph resembles the still-sharedicons/actionused by the catch-all "Codev Action"; re-glyphing Codev Action (e.g. tobolt) would fully resolve the picker ambiguity. Theterminal → open-terminalmapping itself is baked scope.Test Plan
manifest-icons.test.ts,render-action-icons.test.ts)pnpm --filter @cluesmith/codev-streamdeck build)streamdeck validatepassesReview Notes
GLYPHSout offace.tsvia regex rather than importing it (the map is module-private, andface.tsis off-limits while bugfix-1431 is in flight). If that declaration shape drifts,extractGlyphthrows — a loud build failure, covered by a test — rather than silently shipping a stale icon.