Repository navigation
fix(remote): apply the audit fixes to remote, graphs and the plate - #5
Merged
Merged
Conversation
… size
"This device controls the size" re-sent the local xterm's grid, which is
always the host's own size (the terminal is opened at the snapshot size
and scaled to fit), so turning it on changed nothing.
The toggle now makes this device the size owner: it measures the cell off
the open terminal (screen / grid, shown at the face's base scale times the
zoom), fits whole cells into its panel (cols 20-500, rows 5-200), resizes
the local terminal in place to that grid so it renders at scale 1, and
sends resize{id,cols,rows} through the existing path. While on it
re-asserts, debounced 150 ms, on panel resize, orientation change,
fullscreen and zoom; the remembered toggle re-asserts the computed size on
every mount; turning it off stops asserting and fits the host's size again.
The owner's fit box takes a fixed 40dvh so the rows never feed back into
the height they are measured from.
…keover
The host now announces a session's PTY size change as
resized{id,cols,rows} (another device asserted its size, or the local TUI
resized it). The client parses it with the host's size bounds and routes
it for the attached session only; older hosts never send it and anything
malformed is dropped like an unknown frame.
A follower resizes its xterm in place and refits, so the picture matches
the host without waiting for a snapshot (the guest view follows too). A
size owner that sees a size it never asserted lets go instead of fighting
back: the toggle turns off and is remembered off, and the size row's note
says "Size taken over by another device.". A slow echo of one of its own
older sizes is not a takeover.
The dev fake host tracks each session's size, answers resize with
resized, snapshots at the held size and can resize from elsewhere, and a
new e2e spec drives the toggle and both resized paths at 390x844.
… them centred 6a7bc4b moved the graphs and remote banners onto the landing's wizard greens; the intent was the other way round (the landing moves to the teal). Only the colour goes back: BANNER_GRADIENT is the TUI's five legible Canopy teal stops again, bannerColor is again an exact port of harness-canopy src/shared/banner.rs gradient_rgb (round(index / max(1, count) * 4), clamped, #52fff6 fallback), and the app word is #52fff6. The stacked, centred header from 6a7bc4b stays as it is. The look tests, the graphs page snapshots and the e2e ink checks (graphs visual, remote serve) expect the teal again.
At 390 px the Keys and latest labels ran past their 44 px buttons (the latest word wrapped under its arrow, stretching the whole row to 69 px), and at 360 px the latest button ran past the viewport: eight 44 px targets do not fit a phone's row once fullscreen and both attach buttons are up. Keys gains a keyboard icon and, like Builder, both Keys and latest go icon-only under 640 px, named by their aria-label and a title tooltip; on desktop the words stay beside the icons. Toolbar buttons keep the 44 px floor without shrinking or stacking their content, and the row wraps instead of clipping, so the latest button drops to a second row. The control-size checkbox (13x13 px) is now a switch in the latched-key look, role="switch", with the global focus ring and Space to toggle. The size row is laid out like a settings row: the label spans the switch's words and the explainer beneath them, so the whole 44 px row is the target and the row is no taller than before at 360x640. A touch-targets spec checks the row at 360 and 390 px with latest up: no clipped label, no overflow, every target at least 44 px.
The horizontal layout measured each arc's rise against mainTop in design coordinates, bounded the viewBox with it there, then shifted every point into the padded viewBox but handed edgePath the unshifted rise. Every arc that loops above the flow (the fail and back edges into Designer, Implementer and Reviewer on canopy-builder) therefore topped out `dy` units above the bounds: outside the viewBox, clipped flat by the svg edge, and the token's fail legs rode off the plate with them. The rise now shifts with the drawing, and the bounds read the curve the arc actually draws (cubicExtent, from its start through the rise to the shortened arrowhead end) instead of its control points, so the plate loses the quarter-height of empty band the controls left above the apex. The vertical orientation was already consistent and is unchanged. A bounds test parses every edge and walk path of every fixture and bundled pattern, in both orientations, and holds it inside the viewBox; it fails on the old layout. The snapshots and the handler-free digest move with the shifted drawing, and the remote's bundle gets a 512 B allowance for the bounds code (+237 B gzip measured).
Below the datasheet breakpoint the live viewport stood a fixed 60svh, min 320 px, so a wide horizontal plate fitted to a phone's width filled barely a fifth of it: canopy-builder at 390 px drew 110 px of plate between ~195 px black bands above and below. The client now publishes the drawing's proportions as --plate-ratio, and the one-column live viewport takes them through aspect-ratio, between 200 px and 60svh: the band shrinks to what the controls need, and a tall plate still stops at 60svh. Beside the datasheet (1024 px and up) the viewport stretches to the sheet as before, and without scripting it keeps its 60svh scroll fallback. The plate spec now checks each edge path's own box, not the svg's, against the viewport at the initial fit and after Fit (an arc cut off by the viewBox never showed in the svg box), asserts the new phone height, and that the page never scrolls sideways at 390 px.
Since the relay gated the guest door with invite codes (5f264ee), it answers 403 bad_invite to any /ws/device?guest_of=<uid> without a live `invite=<code>`, but the guest page never sent one: guestRelayUrl built `?guest_of=<uid>` only and no web code knew about invites, so a guest could never connect. The guest page now takes the code from the link's fragment, `/g/<owner_uid>/<host_id>#invite=<code>` (the form `canopy remote invite` prints), so it never reaches the relay's logs or a Referer. The fragment is read before anything else runs and stripped with history.replaceState; the code is kept in this tab's sessionStorage only, per link, so a reload or a reconnect in the same tab still has it and it survives the GitHub sign-in round trip. guestRelayUrl(ownerUid, code) appends `&invite=<encoded code>`. The relay hashes exactly what it receives and the host publishes the SHA-256 of the canonical CNR-XXXX-XXXX-XXXX-XXXX form, so the page sends only that form: canonicalInviteCode mirrors the host's normalize_code_input (dashes and spaces ignored, ASCII case folded, I/L read as 1 and O as 0, the CNR prefix optional). A link without a code, or with a malformed one, shows a small invite form in the sign-in card: one labelled field tied to its hint and its error (aria-describedby, aria-invalid, role="alert"), checked against the code format on submit. The relay refuses a bad door with an HTTP status the browser never shows the page, so a guest socket that closes before it ever opened, while online, is read as the invite being refused: the code leaves the tab and the form comes back prefilled, saying the code may be mistyped, expired, revoked or used, or, once the host had let this tab in, that the single-use code is spent. Offline, the code is kept and the relay is reported unreachable instead. The fake host gains `?screen=guest&invite=ask|refuse` to drive the form, and the guest chunk an allowance of 1.5 KB gzip (measured +1 390 B).
A guest who opened /g/<owner_uid>/<host_id> signed out clicked "Sign in with GitHub", and the OAuth callback redirected to `/`: the guest landed on the owner's pairing form with the link gone, and only an already signed-in guest could ever reach a host. The guest sign-in button now remembers the link's pathname (never the invite code, which keeps its own tab-only slot) in sessionStorage, and main.ts, before routing, resumes it in place with history.replaceState when the tab comes back to `/` within 15 minutes, so the guest chunk mounts with the code it saved before leaving. Only a well-formed guest path is ever resumed, once; a stale or malformed entry is dropped. The main bundle gains a 512 B allowance (measured +226 B gzip), since this step has to run before the guest chunk loads.
At 360 and 390 px the latest button, the toolbar's eighth 44 px target, only fit by wrapping onto a row of its own whenever the view was scrolled up: the toolbar grew from 44 to 90 px and the terminal lost a row. latest now floats over the terminal card's bottom-right corner at every width, inside a new .term-stage around #term-pane, so the toolbar holds seven targets on one row. It stays a 44x44 round target in the key look, icon-only everywhere (its aria-label and title name it), slightly see-through until pointed at or focused, and inset by the pane's padding. It shows only when the cursor line is off screen: on the shell's buffer while the view no longer follows the bottom, as before, and in a fullscreen app, where follow is always off and the button would sit over the app's own input line, only while the pane is scrolled away from the frame's top, which a tap restores. The touch-targets spec now checks at 360 and 390 px that the toolbar is one row, that latest sits inside the pane's bottom-right corner rather than in the toolbar, and the 44 px floor; the alternate-screen spec expects the button hidden while the whole frame fits.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Nine fixes from the audit: PTY sizing from the device panel, following the host's resized frame, the Canopy teal header banners, the phone toolbar, fail arcs inside the plate's viewBox, the phone plate viewport, the guest invite code and sign-in return, and the floating latest button. Merging does not deploy.