Repository navigation
benchmark groups start collapsed, load charts when a group opens - #130
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Bench preview for |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
Each group's summary row now ends with one line that says how Vortex
compares with Parquet, so a folded group still says something. A pure
function, groupHeadline in lib/headline.ts, reads the summary the group
already carries. It adds no query and changes no summary math.
A slower result reads in words ("4.0× slower"), not only through color.
Tone colors wait for the visual redesign. When the needed series are
missing, unmeasured or degenerate, the row shows no headline rather than
a misleading number.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Will Manning <will@willmanning.io>
Each group's summary card and a new client GroupBody now render inside the group's <details>. GroupBody server-renders as an empty div and mounts the group toolbar and the chart grid only while the disclosure is open; closing unmounts them and aborts the group's bundle. A folded landing page therefore ships no chart markup and hydrates no chart island. The loading machinery in lib/chart-store does not change. lib/group-bodies.ts seeds a group's URL series overrides once per page load, so a reopen keeps the user's changes, and lets later code wait until the open groups' bodies have mounted and their bundles settled. chartPriority names the page-wide load priority the islands already used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
The landing page's fragment now lists the open groups' anchors in page order (#tpc-h-nvme-sf-1,clickbench). An inline script after the last group opens them before the first paint. LandingState writes the fragment on every toggle with replaceState, opens the listed groups after a client navigation or a hashchange, and scrolls to the first one once its body mounts. Expand All and Collapse All write once. The group nav adds its group to the open set. A permalink copies a link to its one group and no longer rewrites the address bar; it shows "copied" only after the clipboard accepts the write. Old single-anchor and opaque-slug links still open their group. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
groupBodiesReady kept a closed group noted, so after an open and a close its `mounted` resolved at once and a waiter could keep the old, aborted bundle. GroupBody now withdraws its note when it closes or unmounts (noteGroupBodyClosed, from the noting effect's cleanup), so a wait that starts later holds for the next mount and that mount's bundle. A wait that already saw the body keeps its bundle. The cleanup never aborts, so a StrictMode replay stays safe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
The pre-paint script compared every fragment token with every group section, reading `data-group-slug` for each miss, so a crafted multi-megabyte fragment could stall the first paint. It now builds an anchor lookup and a slug lookup once (first section per key, keys prefixed so `__proto__` or `constructor` never reach Object.prototype), skips repeated decoded tokens, and opens what it finds. Behavior is unchanged: the parity test against the lib/open-groups helpers still passes, now with prototype-named tokens too. New tests load 100k-token fragments and bound the script's DOM calls by the number of sections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
An open group's cards mount only after hydration, so the browser's own scroll restoration lands against a shorter page. The landing page now sets history.scrollRestoration to 'manual' while mounted (and puts the old mode back on unmount) and restores the scroll itself (lib/scroll-restore.ts): - On pagehide, and when a chart link is clicked, it saves to sessionStorage, keyed by path and fragment, the first chart card in view below the sticky header (or else the first group section) and its offset from the viewport top. Storage that is blocked or full never throws. - On mount, planScroll chooses. A reload or Back/Forward with a saved position restores it: once the open bodies mount, and again one frame after their bundles settle. Wheel, touch, key or pointer input stops it. If the element is gone, it falls back to the fragment scroll. A fresh visit, or nothing saved, scrolls to the first listed group as before. A hashchange now also rewrites the fragment to the whole open set (a group already open fires no toggle) and ends a pending restore. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
navigationKind read the document's navigation entry, but the chart page's logo link renders `/` inside the chart page's document. After a reload or a Back to that chart page, the landing page saw 'reload' or 'back_forward' and restored an old position on what was a fresh visit. An entry now counts only when its address has the current path; another path, no address or one that does not parse reads as 'navigate'. Leaving the page before a restore applies no longer overwrites the good save with the top of the page. restoreScroll takes an optional onPendingEnd, called once when its first pass applies, it falls back, or it stops first. Until then pagehide saves the position being restored (saveScroll takes an optional position). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
The islands now mount into an open group and unmount on close, so teardown runs through effect cleanup and destroy(), not the toggle event. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
Safari throws on a document's 101st history.replaceState call in 10 seconds. Each group toggle made two calls: ours, then Next.js's own re-sync, which is not caught. About 50 fast toggles replaced the landing page with Next.js's error page. A toggle after 300 ms of quiet still writes at once. Inside that time the write waits until the toggles stop, so a burst is two writes and writes stay at least 300 ms apart. A hashchange drops a pending write before it reads the new fragment, and unmount drops it too. A chart link click makes a pending write before it saves the scroll. pagehide saves the scroll under the current URL, which a reload loads, then makes the write and saves again under the new fragment for Back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
A query or random-access headline now says "(partial)" when it leaves out an engine, or a hot or cold side, that ran Parquet but not Vortex. Before, that result was dropped without a word. An engine that ran only Vortex is still left out silently. A size reads as a multiple outside half to twice Parquet's: "2.0× larger" once the printed percentage would reach 100 (from 1.995), and "2.0× smaller" from a ratio of 0.5 down. No size reads "100% larger" or "100% smaller" any more. Tests now also cover random access with no Parquet run, an unknown engine's label and order, a vector-search tie and NaN score, and the 9.95× and 20× display boundaries. None of the 23 groups on the local production dump changes its wording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
Before hydration, a reader can already open or close groups, and scroll. LandingState overrode both: - On mount it reopened every group the fragment lists, so a group the reader closed before hydration opened again. A group opened before hydration reached the fragment only with the next toggle. - A Reload or Back restore moved a reader who had already scrolled. The pre-paint script now leaves a marker on its document, after it opens the groups. One-shot passive capture listeners (wheel, touchstart, keydown, pointerdown) set its input flag. A scroll does not set it, so the browser's jump to a single-anchor fragment is not input. LandingState takes the marker on its first mount, once per document. It then opens nothing, writes the fragment once from what is open, and skips a restore when the flag is set, as input after mount would. A client navigation to / finds no marker and opens the groups from the fragment, as before. Also: - Save the scroll position when the tab goes hidden too, through the pagehide path. A mobile browser can discard a hidden tab without a pagehide. - A scroll that no click asked for (a fresh visit, a restore's fallback, a new fragment) now jumps. Only the group nav scrolls smoothly. - Space fragment writes on performance.now(), not Date.now(). A wall clock that jumps forward let two writes come closer than 300 ms. - Drop a pending fragment write when the path changed since it was scheduled, as when the fragment changed. - Remove the wall-clock limit from the bootstrap's linear-work test. The DOM-call counts are the bound. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
A group with no charts mounts no chart island, so it has no bundle to wait for. Its body now requests nothing on open, and the scroll restorer's wait for it resolves at once. Rename a GroupBody test that claimed to pin the once-per-page-load seed of the URL's series overrides. The body stays mounted across a close, so the test passes without that guard; lib/group-bodies.test.ts pins it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
With JavaScript off, / shows only the loading card: the groups stream inside the Suspense boundary's hidden segment, which only React's inline script reveals. This predates the folded page. Comments in GroupSection, SummaryCard and GroupPermalink claimed behavior "with or without JavaScript". They now say what holds: before hydration, with JavaScript on. page.tsx says why its <noscript> hint never shows in a browser. The hint stays where it is: outside the boundary it would need a wrapper and would sit outside the page layout, above the header or below the footer. performance.md's folded-page section now: - names OpenGroupsScript and the pre-hydration marker; - says why first contentful paint rose 124 -> 184 ms: the previous build first paints its loading card, which the new page no longer needs; - says what the reader sees on Reload and Back (1-2 frames of the page top) and where the 1.4-1.8 layout-instability score comes from; - gives the conditions: local dump, no network throttling, CPU 4x, median of 5 runs (Expand All: 3), one-off scripts, and the previous build (the claude/bench-cleanup head, b4caf7c); - says Expand All mounts its islands in the toggle tasks after the click. Chart.tsx's header adds GroupBody's mount at hydration for a group that is already open. Header.tsx no longer says Expand All writes the fragment once; writes are scheduled and coalesced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
Input before hydration means the reader has taken over the scroll. The pre-paint script's input flag stopped only the Reload and Back restore. On a fresh visit to a link that lists groups, the page still jumped to the first listed group after hydration, away from where the reader had scrolled. planScroll now returns "none" whenever the flag is set: no restore, no fallback, and no fragment scroll. A scroll alone is still not input, so the browser's jump to a single-anchor fragment changes nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
The design and plan documents for the folded landing page were working notes. The code, bench/docs/architecture/performance.md and the PR record what they decided. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
The first run of LandingState's effect may rewrite the fragment, which keys the saved position and orders the listed groups. Keep that run's reads in a ref so the replay in development restores the same position. Name a click as input before hydration in the comments that list it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
CodeQL flags the tag-stripping regex as incomplete HTML sanitization. The tests now take the summary row's textContent instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
A collapsed group showed only its header row. The summary card now sits after the group's <details>, inside its section, so it shows whether the group is open or not. The toolbar and charts still mount only when the group opens. The card was already in the HTML, so the page ships the same bytes. Comments and docs now say "collapsed", not "folded". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
7ee508e to
511c316
Compare
main's #130 opens groups lazily: GroupBody now holds the group toolbar, and lib/open-groups.ts opens or closes every group. GroupSection keeps the skin's InfoIcon and drops the toolbar import, and Header keeps the skin's Material icons with main's setAllGroupsOpen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Will Manning <will@willmanning.io>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 511c3166d5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (seededGroups.has(groupSlug)) { | ||
| return; |
There was a problem hiding this comment.
Re-seed filters when the landing-page URL changes
When an App Router navigation changes the landing-page query without reloading the tab—for example, clicking the header logo from a ?group=…&hide=… permalink—the existing GroupBody receives the new empty overrides, but this module-level set causes them to be ignored forever. The chart-store therefore retains the old hidden/shown series even though the URL no longer requests them; scope this guard to close/reopen cycles or allow changed URL props to reinitialize the filter.
Useful? React with 👍 / 👎.
| 'g=document.__benchOpenGroups={input:false,stop:function(){' + | ||
| 'for(var j=0;j<n.length;j++)removeEventListener(n[j],h,true)}};' + | ||
| 'h=function(){g.input=true;g.stop()};' + | ||
| 'for(i=0;i<n.length;i++)addEventListener(n[i],h,{capture:true,passive:true})' + |
There was a problem hiding this comment.
Start capturing input before the streamed groups arrive
On a cold request, HomeLoading can remain visible for several seconds, but these takeover listeners are installed only when the delayed group chunk reaches this inline script. A visitor who wheels, touches, presses a key, or clicks the loading shell before then is recorded as input: false, so LandingState subsequently performs fragment scrolling or restoration and moves the page despite that interaction. Install the capture marker in the initial shell (or otherwise preserve input observed while the Suspense fallback is displayed) so the promised pre-hydration takeover protection covers the entire loading interval.
Useful? React with 👍 / 👎.
| window.addEventListener('pagehide', saveOnLeave); | ||
| window.addEventListener('hashchange', onHashChange); |
There was a problem hiding this comment.
Handle fragment changes made by client-side navigation
This listener only observes native hashchange events, but a same-route Next.js navigation updates the URL through the History API and keeps this client component mounted. For example, clicking the header's <Link href="/"> while groups are open removes the fragment without firing hashchange; the disclosures remain open and no fragment is rewritten, so the address bar no longer represents the open set and reloading immediately collapses those groups. Observe the router/history transition as well, or explicitly reconcile open state when the landing URL changes.
Useful? React with 👍 / 👎.
Benchmark groups on the landing page start collapsed. Each group shows a one-line result from its summary, for example "DuckDB 2.5× faster". If Vortex is slower, the text says "N× slower". A group loads its charts only when it opens. The URL fragment lists the open groups, and a link with that fragment opens them. Reload and Back open the same groups and go back to the same scroll position.
These values are from a copy of the production database, with the CPU 4× slower:
The APIs do not change. The landing page screenshots change, as expected. With JavaScript off, the page shows only a loading message, as before.
🤖 Generated with Claude Code