Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 27 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,9 @@ public/ # static files copied verbatim into out/:
repositories.txt # synced from the repo root (sync-repositories.mjs)
assets/ # mustache template, logo
templates/ # workflow-sample-*.html fragments
widgets/ # module bridges: vizdom WASM, three-spritetext,
widgets/ # module bridges: vizdom WASM, force-graph
# (3D graph + sprite labels sharing one
# three build),
# sentry (runtime CDN imports cannot be bundled)
templates/ # user CI samples (user-refactorfirst-*.yml) for the docs
ci/process-submissions.sh # shared submission validator for GH/Gl/BB CI
Expand Down Expand Up @@ -256,14 +258,30 @@ coverage there when introducing new markup patterns.
lib/renderer.js applies **filter → sort → paginate** per table and injects
`tableUi` blocks the mustache template renders; `enhanceTables` in
lib/table-enhancer.js binds the controls and reports state changes back to
components/report-view.jsx, which re-renders (widgets only gate the first
render; the search input's focus/caret is restored after each re-render).
The expensive `enhanceReport` pipeline (Chart.js charts, WASM DOT layout)
runs only when the payload changes — table-state re-renders stash the live
chart canvases / graph containers before the innerHTML swap and graft them
back into the fresh DOM (`statefulElementIds`/`stashStatefulDom`/
`graftStatefulDom` in lib/report-view.js), re-binding only the cheap popup
handlers.
components/report-view.jsx, which re-renders (widgets never gate the
render — charts/graphs enhance progressively via `onWidgetReady`
callbacks when their widget reports ready; the search input's focus/caret
is restored after each re-render). The expensive `enhanceReport` pipeline
(Chart.js charts, WASM DOT layout) runs only when the payload changes —
it receives `{charts, graphs}` readiness flags so still-loading widgets
are skipped and picked up by the widget-readiness effect instead — and
table-state re-renders stash the live chart canvases / graph containers
before the innerHTML swap and graft them back into the fresh DOM
(`statefulElementIds`/`stashStatefulDom`/`graftStatefulDom` in
lib/report-view.js), re-binding only the cheap popup handlers.
- Initial render performance (LCP/CLS): the report markup renders as soon
as the parallel template + report-JSON fetches resolve, never waiting on
the CDN widgets or the ~4.8MB vizdom wasm. The report page shells
(`[username]/[repository]/page.jsx` + `[branch]/page.jsx`) preconnect to
raw.githubusercontent.com (the report JSON host; CORS fetch) and
cdnjs.cloudflare.com (classic widget scripts) and render the loading
placeholder inside `#report-container` — the same id the hydrated
ReportView renders — with a full-viewport `min-height: 100svh` (vh
fallback) in app/globals.css, so the footer starts below the fold and
the report's arrival causes no layout shift. The vizdom bridge
(`public/widgets/vizdom-bridge.js`) serves the wasm from Cache Storage
(versioned cache name) so repeat visits never re-download it regardless
of the host's HTTP cache headers.
- Search `<input>`s are injected by table-enhancer — `<input>` is FORBID in
the renderer's sanitization allow-list, so it must never appear in the
mustache template.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ lib/ # Shared logic consumed by client components and
public/ # Published verbatim: repositories.txt, assets/
# (mustache template, logo), templates/
# (workflow samples), widgets/ (WASM + ESM
# bridges for vizdom, three-spritetext, Sentry)
# bridges for vizdom, force-graph, Sentry)
repositories.txt # Listed repositories, one "user/repo" per line
ci/process-submissions.sh # Shared submission validator used by GitHub
# Actions, GitLab CI and Bitbucket Pipelines
Expand Down Expand Up @@ -436,7 +436,7 @@ in
| URL routes | `lib/routes.js` (+ `tests/unit/routes.test.js`) |
| Raw fetching / branch fallback (platform-aware) | `lib/fetcher.js` |
| Mustache rendering | `lib/renderer.js`, `public/assets/refactor-first-report.mustache` (port of the RefactorFirst viewer template) |
| Interactive report widgets | `lib/report-view.js` + CDN widgets loaded via `next/script` in `components/report-view.jsx` (Chart.js, sigma/graphology, graphlib-dot, svg-pan-zoom, 3d-force-graph) and the ES-module bridges in `public/widgets/` (vizdom WASM, three-spritetext) |
| Interactive report widgets | `lib/report-view.js` + CDN widgets loaded via `next/script` in `components/report-view.jsx` (Chart.js, sigma/graphology, graphlib-dot, svg-pan-zoom) and the ES-module bridges in `public/widgets/` (vizdom WASM; force-graph: 3D graph + sprite labels sharing one pinned Three.js build) |
| Search / type-ahead | `lib/search.js` + `components/{search-combobox,hero-search,menu-search}.jsx` |
| Submission flow | `lib/repo-submission.js`, `components/repo-submission-form.jsx` |
| Submission validation (CI) | `ci/process-submissions.sh`, `.github/workflows/add-repository.yml`, `.gitlab-ci.yml`, `bitbucket-pipelines.yml` |
Expand Down
24 changes: 21 additions & 3 deletions app/[username]/[repository]/[branch]/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,26 @@ export default async function BranchPage({ params }) {
notFound();
}
return (
<Suspense fallback={<p className="loading" role="status">Loading report&hellip;</p>}>
<ReportView username={username} repository={repository} branch={branch} />
</Suspense>
<>
{/* Warm the connections the client-side report fetch and the lazy CDN
widgets will use: raw.githubusercontent.com serves the report JSON
(a CORS fetch, so the preconnect is anonymous too) and cdnjs the
2D-popup scripts (classic scripts, no CORS). Self-managed
GitLab/Bitbucket deployments fetch from their own platform host
instead, where these hints are simply unused. React hoists both
links into <head>. */}
<link rel="preconnect" href="https://raw.githubusercontent.com" crossOrigin="anonymous" />
<link rel="preconnect" href="https://cdnjs.cloudflare.com" />
{/* The fallback mirrors the hydrated container (same id + reserved
height, app/globals.css) so the footer starts below the fold before
hydration too and the report's arrival causes no layout shift. */}
<Suspense fallback={(
<div id="report-container">
<p className="loading" role="status">Loading report&hellip;</p>
</div>
)}>
<ReportView username={username} repository={repository} branch={branch} />
</Suspense>
</>
);
}
24 changes: 21 additions & 3 deletions app/[username]/[repository]/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,26 @@ export default async function RepositoryPage({ params }) {
notFound();
}
return (
<Suspense fallback={<p className="loading" role="status">Loading report&hellip;</p>}>
<ReportView username={username} repository={repository} />
</Suspense>
<>
{/* Warm the connections the client-side report fetch and the lazy CDN
widgets will use: raw.githubusercontent.com serves the report JSON
(a CORS fetch, so the preconnect is anonymous too) and cdnjs the
2D-popup scripts (classic scripts, no CORS). Self-managed
GitLab/Bitbucket deployments fetch from their own platform host
instead, where these hints are simply unused. React hoists both
links into <head>. */}
<link rel="preconnect" href="https://raw.githubusercontent.com" crossOrigin="anonymous" />
<link rel="preconnect" href="https://cdnjs.cloudflare.com" />
{/* The fallback mirrors the hydrated container (same id + reserved
height, app/globals.css) so the footer starts below the fold before
hydration too and the report's arrival causes no layout shift. */}
<Suspense fallback={(
<div id="report-container">
<p className="loading" role="status">Loading report&hellip;</p>
</div>
)}>
<ReportView username={username} repository={repository} />
</Suspense>
</>
);
}
15 changes: 15 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,21 @@ main section {
padding: 3rem 0;
}

/* Report pages fetch and render their report client-side; both the
prerendered loading fallback and the hydrated container carry
#report-container, so reserving a full viewport here keeps the footer
below the fold from the very first paint — whatever the header's
height. The report's late arrival then grows the page instead of
shoving the visible footer down the screen (layout shift / CLS).
Rendered reports are always far taller than this, so the reservation
adds no space once content lands. svh is the guaranteed-visible
viewport height on mobile (dynamic browser chrome); vh is the
fallback for engines without viewport units. */
#report-container {
min-height: 100vh;
min-height: 100svh;
}

.site-footer {
text-align: center;
padding: 1.5rem 1rem;
Expand Down
Loading
Loading