feat(network-activity-plugin): virtualize large code blocks#279
Merged
V3RON merged 3 commits intoMay 19, 2026
Merged
Conversation
9210b04 to
9d9983e
Compare
`CodeBlock` grows a threshold-aware rendering path. When `children`
is a string longer than 50,000 characters, the body renders inside
a `react-virtuoso` `<Virtuoso>` so only the rows visible in the
500px viewport are kept in the DOM — large pretty-printed JSON,
minified bundles served as text, oversized logs, and similar
multi-megabyte payloads no longer pin a `<pre>` node containing
hundreds of thousands of lines.
The dispatch is a runtime branch on `typeof children === 'string'`.
React-element children (the `<JsonTree>` and `<XmlTree>` trees
wrapped in `<CodeBlock>` to inherit the monospace-on-dark frame)
flow through the existing `<pre>` unchanged regardless of nested
content size — the type-check on the children prop itself takes
precedence over anything the wrapped element contains. String
children below the threshold also continue to use `<pre>`, so the
vast majority of responses see no behavior change.
Zero changes are required at any existing `CodeBlock` call site:
the 11+ consumers in the plugin (json / xml / svg / text-fallback
renderers, malformed-fallback paths, RequestBody) pass strings or
trees today and pick up virtualization automatically when a body
is large enough to need it.
Virtuoso configuration:
- `style={{ height: 500 }}` — matches HexView and the iframe in
other parts of the response panel for visual consistency.
- `<div>` rows (not `<span>`) because Virtuoso positions rows
absolutely and needs block-level items.
- `whitespace-pre-wrap wrap-anywhere` per row preserves today's
`<pre>` wrapping UX — long URLs, base64, minified-on-one-line
content wrap visually instead of forcing horizontal scroll.
Rows have variable height; Virtuoso measures them.
- Outer styling via the existing `codeBlockClassNames` so the
dark bg / monospace / border / padding are identical between
the two branches.
Known shape limitation noted in code: a single 50KB-line body
with no newlines virtualizes to one row containing the entire
payload. Browser wrapping keeps layout sane but there's no real
virtualization benefit — the size win shows up once the body has
many lines. Programmatic line-wrapping (choosing a column width
to split very long lines into multiple rows) is out of scope.
UX trade-off worth knowing: today's `<pre>` expands to content
(height grows with body length). Post-change, content above 50KB
scrolls within a 500px window. Deliberate, for snappy rendering
on multi-megabyte responses.
Adds `react-virtuoso ^4.6.0` to the plugin's `dependencies`.
Tests:
- `vitest.setup.ts` adds a `vi.mock('react-virtuoso')` passthrough
that renders every row regardless of viewport. jsdom can't drive
Virtuoso's resize observation, so without the stub any test
importing a virtualizing component fails to render rows. The
passthrough lets RTL queries assert content correctness; the
production build uses real Virtuoso unchanged.
- `__tests__/CodeBlock.test.tsx` covers the branch (small → `<pre>`,
large → `data-testid="virtuoso-mock"`), inclusive-boundary
semantics at exactly 50,000 chars, the 50,001-char trigger,
content preservation across the threshold, newline-based row
splitting, React-element children staying on the `<pre>` path
regardless of nested size, and `className` forwarding on both
branches.
A new "Large text" main-tab on NetworkTestScreen, modeled after the existing Images tab. Two buttons that straddle the 50 KB virtualization threshold so reviewers can see both branches of CodeBlock without leaving the playground: - "Alice in Wonderland (~174 KB)" — https://www.gutenberg.org/cache/epub/11/pg11.txt (200 OK, text/plain; charset=utf-8, 174 KB). Stable URL, routes via text-fallback, exercises the virtualized 500 px scrollable window. - "GPL-3 license (~35 KB)" — https://www.gnu.org/licenses/ gpl-3.0.txt (200 OK, text/plain, 35 KB). Stays under the threshold, renders as the flat <pre> for direct comparison.
Minor bump for @rozenite/network-activity-plugin. The Response Viewing section in network-activity.mdx gains a single paragraph explaining that text response bodies above 50 KB render inside a virtualized 500 px scrollable window, while smaller bodies stay on the flat-block path. Wording emphasises the no-truncation guarantee.
9d9983e to
6728d85
Compare
V3RON
approved these changes
May 19, 2026
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.
Part of #244 — "Improve response viewing in Network Activity." Makes the response panel's CodeBlock threshold-aware so multi-megabyte text bodies don't pin hundreds of thousands of DOM nodes.
Summary
CodeBlockbecomes threshold-aware. Whenchildrenis a string longer than 50,000 characters, the body renders inside areact-virtuoso<Virtuoso>and only the rows currently visible inside a 500 px window stay in the DOM. Below the threshold (and for any non-string children —<JsonTree>,<XmlTree>wrapped in CodeBlock for the monospace-on-dark frame), rendering is unchanged: a flat<pre>. The dispatch is a runtimetypeof children === 'string'branch — zero changes required at any of the 11+ existing CodeBlock call sites (json / xml / svg / text-fallback renderers, malformed-fallback paths, RequestBody).style={{ height: 500 }}matches the visual scale of HexView and the iframe elsewhere in the response panel.<div>rows (Virtuoso positions rows absolutely and needs block-level items). Per-rowwhitespace-pre-wrap wrap-anywherepreserves today's wrapping UX so long URLs / base64 / minified-on-one-line content wrap visually instead of forcing horizontal scroll. Outer styling reuses the existingcodeBlockClassNamesconstant so the dark bg / monospace / border / padding are identical between the two branches.<pre>expands to content (height grows with body length). After this change, content above 50 KB scrolls inside a 500 px window. Deliberate, for snappy rendering on multi-megabyte responses.react-virtuoso ^4.6.0to@rozenite/network-activity-plugin'sdependencies.NetworkTestScreenwith two pre-verified URLs straddling the threshold so reviewers can see both branches: Alice in Wonderland from Gutenberg (~174 KB, virtualizes) and the GPL-3 license text (~35 KB, stays flat).Test plan
pnpm --filter @rozenite/network-activity-plugin test— all tests pass (8 new inCodeBlock.test.tsx).pnpm --filter @rozenite/network-activity-plugin typecheck— clean.pnpm --filter @rozenite/network-activity-plugin lint— clean.npx prettier --checkon changed files — clean.pnpm --filter @rozenite/playground iosor:android) → Network Test → Large text tab:<pre>. Scrolling is smooth at the end of the ~3300-line file. Dark bg / monospace / border look identical to a small<pre>.<pre>, height grows with content, no internal scrolling. (Control: confirms the threshold gating works.)<pre>with SVG source (SVG bodies are well under threshold).New test surface
components/__tests__/CodeBlock.test.tsx(8 tests):<pre>with no virtuoso-mock present.>, not>=).<pre>path even when the wrapped element contains a 50,001-char string internally (thetypeof children === 'string'check takes precedence).classNameforwarding on both the flat and virtualized branches.vitest.setup.tsadds avi.mock('react-virtuoso')passthrough so jsdom-based tests can assert content correctness. jsdom can't drive Virtuoso's resize observation otherwise — without the stub any test importing a virtualizing component fails to render rows. Production code is unaffected.Followups (deliberately not in this PR)
JsonTree,XmlTree) — would require forkingreact-json-treeor rewriting from scratch.Coordination notes
react-virtuosodep +vi.mock('react-virtuoso')test stub are also added by feat(network-activity-plugin): binary hex viewer + metadata card #274 (binary hex viewer, ready for review). The lines are identical on both branches; whichever lands second will see a clean auto-merge (or a one-line trivial conflict on the dependencies block).network-activity.mdxResponse Viewing section is also edited by feat(network-activity-plugin): HTML response renderer with sandboxed iframe #275 (HTML) and feat(network-activity-plugin): XML response renderer + JSON Raw view #277 (XML + JSON Raw). This PR appends a separate paragraph at the end of the section rather than touching their bullets — additive, no overlap.