Tablet landscape — floating station bar, log rail, wide band #16
Labels
No labels
needs-info
needs-triage
ready-for-agent
ready-for-human
v2
wayfinder:grilling
wayfinder:map
wayfinder:prototype
wayfinder:research
wayfinder:task
wayfinder:ticket
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
aiko/denpa#16
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What to build
Card 1c, at 1024×640. The same chrome as mobile with one band added and one changed — collapse by adding/removing bands, not by reflowing.
34ch, with wider keys and a right-aligned hands-free/VAD readout.Design reference:
docs/design/denpa-receiver/README.md(card ids1a,4a… index intoDenpa Receiver.dc.html, openable in a browser).Acceptance criteria
Blocked by
Branch
issue/denpa-16-tablet-landscape.What landed. 1a and 1c are one component set over a plan table.
chrome-plan.tsmaps a viewport to aChromePlan— which bands exist, where the keys live, the station bar's placement and meter size, the transmission band's type scale and measure.ReceiverChromeassembles the bands;StationBar,TransmissionBand/KeyRowandLogRaildraw them. Nothing is forked per breakpoint: the tablet plan addsrail, dropsdock, and moves the keys into the wide band.scrim.stationgradient at 1c, banded under a hairline on mobile. Callsign 900 14px / .2em, meter 5 bars 4x18 gap 3 at 1s.ChatHistoryContext'sMessage[].letter-spacing: -.025em, capped at34ch, keys 56px with PTT at 230px and the常時受信 ON · VAD 0.62 / 0.28readout right-aligned.Tests (
npm test: 81 passed in 10 files, up from 60 in 8; nothing skipped or deleted). 13 cases on the plan: which layout each viewport picks including the rotated 640x1024 tablet and an 800x420 phone on its side, that the band lists differ only by adding and removing bands, the 704px free stage, the 34ch cap, the PTT width, and the hands-free readout formatter. 8 on the render: the rail draws the running log and grows with it, the rail is a sized flex sibling while the band is inside theflex:1 min-width:0stage column so their boxes cannot overlap, the 34ch/34px body on tablet against 21px uncapped on mobile, and that the four chrome files contain nouseState/useRef— which is what makes rotation lossless, since the transmission, log, hands-free state and AI state all arrive as props.Not verified. I could not render this. Every visual claim above is a reading of the design doc, not an observation of the UI. In particular "the rail does not overlap the band" is established structurally — the band is absolutely positioned inside the stage column, which is the rail's flex sibling — and not by looking at 1024x640. Type sizes, paddings and the scrim gradients are transcribed from § 1c and unchecked against the mock.
Deliberately left out. Nothing mounts
ReceiverChrome: #12 owns the App.tsx mount point and was still unmerged, so wiring it here would have ripped out the surviving sidebar/footer and guaranteed a conflict. TheuseViewporthook ships alongside; the mount is<ReceiverChrome viewport={useViewport()} ... />. Also out: the mobile 記録 log overlay (#12/#17), the unprompted break-in band, the scanline sweep, and the rail's 思考 rows and chain summary from 4a — the rail draws message entries and relay rows only.Assumed. 1c applies to any landscape viewport at least 900px wide, desktop included, since it is the only card with a rail and "desktop pal = bar only" is a window mode rather than a breakpoint. Both assumptions are in
.denpa-work/QUESTIONS.md.Shared files touched, minimally:
carrier-bars.tsgained an optionaldurationMsso 1c's meter can run at 1s;faceplate-spec.tsgainedkeyTreatmentStylesso the dock and the wide band cannot disagree about a lit PTT key;denpa-tokens.ts/system.tsgainedscrim.*,hairline.ruleand two transmission text-shadows rather than local constants.npm run typecheckreports no errors in any file this branch touches.npm run lintis still broken on main for unrelated reasons.Review-findings pass on
issue/denpa-16-tablet-landscape(86913b8).Addressed
✓on 完了, magenta✕on 失敗, and the live cyan mini-meter on 実行中. Under reduced motion the mini-meter holds at a single static half-height bar insignal, per 4b — done through the same pure-motion-plan mechanism #10 used, not a CSS media block. The mini-meter's geometry lives incarrier-bars.tsbeside the station/faceplate/pal meters. A row whosestatusnever arrived draws no glyph rather than a✓it cannot support.hairline.railat.24and pointed the rail'sborder-leftat it; the station bar keeps.22.Skipped, with reason
ProactiveSpeakContextcarries onlyallowProactiveSpeak/idleSecondsToSpeak/allowButtonTrigger— no per-turn trigger, no per-message attribution — so anisProactivefield onMessagewould have no producer and the acid branch would be dead.faceplate-spec.tsalready records the same decision for the 自発 faceplate row. Logged in the questions ledger with the five-line change to make once the trigger exists.#f4ecf6body; only the speaker label differs.Tests — 5 new assertions, each confirmed red against the pre-fix code (stashed the source, ran, unstashed): the two glyphs and their colours, the running row's meter + empty duration slot, the rail's border token, and both branches of
relayMeterBars.npm run typecheckadds no errors in any touched file (grepped forreceiver,theme/,log-rail,carrier-bars,denpa-tokens,system.ts— no hits).Branch:
issue/denpa-16-tablet-landscape(pushed, no PR, not merged).What was built
The 1c tablet-landscape receiver chrome, as components plus a viewport hook — but nothing mounts it (see below).
chrome-plan.tsis a pure table mapping a viewport to a layout decision: which layout, whether the log rail is present, and whether the keys live in the dock or in the transmission band.receiver-chrome.tsxrenders either the mobile-portrait shape or the 1c shape from that plan;station-bar.tsx,log-rail.tsxandtransmission-band.tsxare the pieces.use-viewport.tsreports width/height/orientation.carrier-bars.tsgainedrelayMeterBars().denpa-tokens.tsandsystem.tsgained ahairline.railcolour (rgba(255,45,149,.24)) for the rail's left border; the station bar keeps.22.The four chrome files contain no
useStateand nouseRef— the chrome is a pure render of props.Tests
npm test: 11 files, 87 passed.npm run typecheckadds no new errors in the paths touched (the 585 pre-existing errors onmainare untouched).Coverage is over the plan table and the emitted markup: which layout each viewport selects, that the rail is present at 1c and absent at mobile, the 704px free-stage arithmetic, that the transmission band sits in a
flex:1; min-width:0; position:relativestage column that is the 320px rail's flex sibling,max-width:34chon the large body, the VAD-threshold formatter's three cases (both thresholds / positive only / omitted entirely when VAD has reported none), and the relay-row status glyphs including the reduced-motion branch. The newlog-rail.test.tsx(4 cases) and therelayMeterBarsblock incarrier-bars.test.ts(2 cases) were confirmed red against the pre-fix source by stashing the four source files and re-running: 5 failures, all in the new assertions.Visual claims that were never observed rendering
I never rendered any of this. No browser, no
.dc.htmlmock, no device. Every size, padding, gradient and colour is transcribed fromdocs/design/denpa-receiver/README.md§1a and §1c and cross-checked against the token file. Specifically:max-width:34chin the emitted markup. No line length was measured.Anything that depends on how it actually looks needs a human with eyes on it before merge.
Deliberately left out
Nothing mounts
ReceiverChrome. #12 (mobile chrome) has not landed onmaindespite being listed as this issue's blocker — its branch is still atmain's tip — andApp.tsxstill carries the pre-Denpa sidebar/footer layout. Wiring the chrome in would have torn that out and guaranteed a conflict with the sibling working #12, so this branch ships the components and the hook and leaves the mount point to #12 or to the merge. The mount is one line:<ReceiverChrome viewport={useViewport()} ... />.Also out of scope and absent: the mobile 記録 log overlay, the unprompted break-in band, the scanline sweep, and 4a's 思考 rows and chain summary (the rail draws message entries and one-line relay rows only). Screen-reader and keyboard behaviour was not extended beyond
aria-pressedon the hands-free key and the faceplate live region that already existed.Assumptions
App.tsxmount point for the receiver chrome, so #16 mounts nothing. If wrong, it is a one-line change at the mount point.TABLET_LANDSCAPE_MIN_WIDTH.keys: 'dock' | 'transmission'field rather than duplicating the key definitions..24as the design specifies, rather than reusing the existing.22hairline or leaving the mismatch.NaNwhen VAD has reported none.Review
No code-review agent ran — this subagent context has no Task/subagent-spawn tool, so that gate is unmet. Assume one pair of eyes on this, not two. I self-reviewed the diff and found four things; three were fixed, one was skipped.
Fixed: the missing 4b relay glyphs (acid ✓ on completed, magenta ✕ on error, live cyan mini-meter on running, with reduced motion collapsing the meter to one static half-height bar in
signalvia a pure branch ofrelayMeterBars()rather than a CSS media block, matching #10'suseReducedMotion→ motion-plan mechanism); the.24vs.22hairline mismatch; and a test gap. Also fixed: a relay row's data attribute defaulted an absent status torunning, contradicting how the row actually drew — a row whose status never arrived now draws no glyph rather than a ✓ it cannot support.Skipped —
isProactiveonMessageand acid#eaff3c自発 speaker labels in the rail. Design 4a does specify an acid speaker label for a 自発-triggered agent message, but the design's own build-status table defers all of 自発送信 ("the adapter that carries a pushed turn does not exist yet"), and the governing rule is "leave it out entirely — no placeholder" / "Building UI that does nothing is not [acceptable]".ProactiveSpeakContext(src/renderer/src/context/proactive-speak-context.tsx) carries onlyallowProactiveSpeak/idleSecondsToSpeak/allowButtonTrigger— no per-turn trigger and no per-message attribution — so anisProactivefield onMessagewould have no producer and the acid branch would be unreachable dead code.faceplate-spec.tsalready records the identical decision for the 自発 faceplate row. The finding also asked for a different body colour; 4a gives both agent variants the same#f4ecf6body, so only the speaker label would ever change. The exact five-line change to make once the trigger exists is logged in.denpa-work/QUESTIONS.md.Shipped. Merged to
mainatd73f4ed(phase B2i — client chrome integration).Closed as part of reconciling the board with
main: the autonomous run's policy was not to auto-close, so shipped work stayed open and the tracker read as though nothing had landed. That policy is now reversed — issues close when they land.