Repository navigation
feat: pretty-print JSON on arrival and make headers selectable - #10
Merged
Merged
Conversation
The pretty-print toggle was one click away on every response, and every API preman is pointed at returns minified JSON. A body the toggle would format is now formatted when it arrives, bounded by the toggle's own two-megabyte limit rather than a second threshold that would leave the control offered, enabled and silent. ADR 053 records the repaint that costs and what it narrows in 013. Response header rows take a hover band, because a name and its value sit at opposite ends of a wide pane and a wrapped value leaves nothing tying them together, and `select-text`, because a header value is a string the reader wants in their clipboard.
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.
1. Purpose & Motivation
Description
Two things in the response pane cost the reader a step they should not have to take.
Every API preman is pointed at returns minified JSON, and the pretty-print toggle was one click away on every single response. A default in the wrong place. A body the toggle would have formatted is now formatted when it arrives.
Response header rows were also unreadable in two ways: a name and its value sit at opposite ends of a wide pane with nothing tying them together once the value wraps, and the app-wide
select-noneinapp.cssmeant a header value could not be copied at all.Context & Flow
2. Key Changes
Description
BodyViewergains one effect that requests the formatted body on mount whenformatAvailabilityallows it, guarded by a ref so it fires once per response. The viewer is already keyed onbody.handle, so a new response is a new decision, and turning the toggle back off does not hand the old one a second chance.The toggle's tooltip is now state-dependent. It is lit before anyone has touched it, so while pretty it reads "Show the raw body" rather than "Pretty-print".
Response header rows in
ResponsePanetakehover:bg-hoverandselect-text. The band is what the eye follows from a name across the gap to a value that wraps to three lines, whichaccess-control-allow-headersusually does.Technical Decisions
The automatic pass reuses the toggle's own predicate and its own
BODY_FORMAT_LIMIT_BYTESof two megabytes rather than a second, lower limit. A lower one is defensible on cost, since two megabytes now gets formatted, sent over the port and given a syntax tree without anyone asking. It was rejected for what it looks like from the outside: between the two numbers the reader finds a toggle that is offered, enabled, and did not fire, and no tooltip explains that state.A JSON body therefore paints twice, raw then formatted a round trip later. Gating the first frame on a round trip would have cost every response, including every response that turns out not to be JSON, to avoid a repaint the
body-enterfade mostly covers.The automatic pass swallows its own errors where the click reports. The reader did not ask for it, so a red line under the toolbar would be the app complaining about something they did not do.
Rejected alternatives, recorded in full in ADR 053: a preference in
Preferences, which holds things where two people genuinely want different answers and this is not one; and formatting in the renderer, which blocks the main thread past the interaction budgets and puts a second formatter next to the engine's.select-textis a deliberate exception to the app-wideselect-none.docs/design-system.mdenumerated exactly two such exceptions, so that count is updated rather than left stale.Critical Modules/Files
packages/desktop/src/renderer/panes/BodyViewer.tsx— the new effect. The ref guard and thehandlekey are what stop it looping or fighting the reader's toggle. Worth checking that a body arriving after the pane moved on cannot set state on the wrong response.docs/decisions/053-a-json-body-arrives-pretty-printed.md— this narrows ADR 013 by name. The pane's "it will not hold the body" is now true with a stated exception rather than absolutely.packages/desktop/src/renderer/panes/ResponsePane.tsx— small, but it changes an app-wide interaction rule for one table.Recommended Review Order
docs/decisions/053-a-json-body-arrives-pretty-printed.md— the decision and what it costpackages/desktop/src/renderer/panes/BodyViewer.tsx— the effect and the toggle labelpackages/desktop/src/renderer/model/body.ts—formatAvailabilitynow answers two questionspackages/desktop/src/renderer/panes/ResponsePane.tsx— the header rowstest/renderer/response.test.ts— the truncated-body preconditiondocs/design-system.md,docs/decisions/README.md3. Verification & Testing
Provide reproducible inputs or steps for each test case, followed by the result and supporting evidence.
Driven against a throwaway HTTP server on localhost returning minified JSON, HTML, and a 2,556,900-byte JSON body, from a scratch workspace in a temp directory. Nothing in these images is a real service or a real credential.
A JSON response arrives pretty-printed without clicking the format toggle, with line numbers and no range strip
A 515-byte minified body, sent once and not touched afterwards. It is indented, the gutter is numbering real lines, and the format toggle is already lit.
The format toggle turns a pretty body back to the raw bytes the server sent, and its tooltip reads "Show the raw body" while lit
The same response after one click on the lit toggle: one line, no gutter, exactly what was on the wire. The accessibility tree reports the button as
"Show the raw body" [pressed]while pretty and"Pretty-print"after the click.A non-JSON response (HTML or plain text) arrives raw with the toggle disabled and a tooltip naming the reason
text/html, left exactly as sent. The toggle'saria-labelisOnly JSON is pretty-printed.and it carriesdisabled.A JSON body above the 2MB limit arrives raw with the toggle disabled and a tooltip naming the size
2.4 MB of JSON. Nothing was formatted, the range strip is back, and the windowing still reads
0 B – 64.0 KB of 2.4 MB. The disabled toggle saysToo large to pretty-print (2.4 MB). The limit is 2.0 MB - search it instead.Response header rows show a hover band across the full row width under the pointer
The pointer is on
access-control-allow-headers, which is the row the band is for: the value is long and the name is far away on the left.A header name and value can be drag-selected and copied
A drag across the
strict-transport-securityvalue. The highlight is visible, andwindow.getSelection().toString()returnedmax-age=15724800; includeSubDomains, max-age=31536000— before this change the app-wideselect-nonemade that string empty.bun run typecheck,lint,format:checkandtestare clean (1954 passed)All four clean. One caveat worth knowing:
givenThousandRequestWorkspace_whenBuildCatalog_thenUnderFourHundredMsis a wall-clock budget and fails in a full run if anything else is loading the machine — it passed once the Electron app used for these screenshots was closed. It passes in isolation in 7.5s.4. Impact and Scope
Renderer only. Nothing under
packages/core/orpackages/cli/changes, so the CLI's output is untouched and no engine contract moves —body-formatwas already in the protocol and is now called on mount as well as on click.Performance: the renderer now holds up to two megabytes of a body it was not asked to hold, on every JSON response, where before it held at most
VIEWER_RETAINED_BYTES. The cap is the same cap the reader could always have reached with one click. Each JSON response adds one IPC round trip and one repaint.A truncated JSON body under the limit costs one wasted 64KB window: the format request and window zero go out in the same tick and the format wins. Suppressing it would mean the window loader knowing what the formatter is about to do, and that coupling costs more than the bytes.
One test added, covering the precondition the effect depends on — that a truncated JSON body is decided at seed time, before any bytes arrive, because
languageForreads the content type rather than sniffing an empty sample.No API, schema, configuration or security surface is affected.