Add admin debug trace and fix the grid/dialog jank it found - #517
fredrivett wants to merge 15 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
There was a problem hiding this comment.
All reported issues were addressed across 36 files
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 12 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Bounded in-memory event timeline (no-op unless tracing) plus instrumenters for URL writes, React Query cache events, layout shifts, slow frames, masonry reflows, and component lifecycle. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Gated to admins (anyone in local dev) behind a per-browser flag, toggled via ?debug=1 or the admin account menu; the session is code-split. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Capture cold loads opened with ?debug=1, and strip the param once applied so it can't override a later menu toggle - Log :present when tracing starts after a component mounted; skip lifecycle diffing while tracing is off - Pause also stops highlights and grid reflow reporting; a new session starts unpaused - Trim the buffer after inserting so an over-old backdated event is dropped - Distinguish the grid's first render from an empty list; include the item id on closeItem Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign-out/sign-in are soft navigations, so the user store (incl. isAdmin), the root query cache, and the debug trace outlived the session. Key the user store by user id (a different user replaces the whole profile), clear it on sign-out, and drop cached queries + the trace whenever the signed-in user goes away or changes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…test Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
44e925f to
afdbf07
Compare
The balanced masonry assigns each row's frames to columns relative to the row above, so a partial last row is balanced among just its own frames. Appending the next page (or its loading skeletons) filled that row and rebalanced it across every column, jumping cards at the bottom of the screen sideways right as the load triggered. Render only complete rows while more pages are coming; the remainder lands with the next page. Found with the debug trace: a bottom-row card moved ~2 columns on most page loads; after this, no existing card moves. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Lets automation/agents read the trace without the clipboard. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
useSearch re-parsed the URL on every query-string change, including the item dialog's ?item=, producing a fresh state (new filter ids) that re-ran the search and reset its pagination. Opening an item from a later page of a filtered view dropped it from the results, so the dialog lost its item, unmounted, fetched it by id and remounted; closing reset the results again and lost the scroll depth. Only sync on changes to the search-owned params. Debounced writes now also keep params they don't own, so a typed query landing after an item opens can't drop ?item= and close the dialog; immediate writes (a chip click) still replace the whole query. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
TipTap's setEditable emits an update by default, and the editability sync effect runs on mount, so every note open fired onChange: a PATCH of the unchanged content plus an items-cache patch on close (re-rendering every card). Pass emitUpdate: false. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Detail views were next/dynamic (React.lazy) chunks, and a lazy component suspends on its first render even when its chunk is already loaded; React then holds the committed fallback for its ~300ms throttle. So the first open of each item kind flashed a Loading placeholder before the real view. Add preloadableView: once loaded it renders directly with no Suspense boundary (each instance keeps the path it mounted with, so nothing swaps mid-life), and expose preload(). The grid preloads a card's detail view on pointerover (fires just before a tap on touch), so opening renders the real view in the first frame. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
All reported issues were addressed across 14 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
React.lazy caches its promise for good, including a rejection, so a transient chunk failure broke that detail view until reload. Recreate the lazy wrapper after a failed load so a later render retries. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
What & why
UX jank (the grid jumping as pages load, the item dialog mounting → closing → remounting) was hard to diagnose because each case is a chain across URL writes, React Query refetches, re-renders, and masonry reflows. This adds an admin-only debug trace — a floating panel (enable with
?debug=1or the admin account menu; anyone in local dev) recording URL writes with call stacks, query fetches/invalidations, masonry reflows (visible moved cards flashed blue), layout shifts, slow frames, and grid/dialog lifecycle + input changes, exportable as JSON (orwindow.__abodeDebugTracefor automation) — then uses it to find and fix the reported jank:useSearchre-parsed the URL on any change, so the dialog's?item=re-ran the active search and reset its pagination; an item opened from a later page dropped out of the list, the dialog unmounted, fetched it by id and remounted (and closing reset the results again). It now only syncs on search-owned params; debounced writes keep params they don't own.preloadableViewrenders loaded views without Suspense, and the grid preloads a card's view on pointerover.setEditableemits an update by default, so every open PATCHed the unchanged note and patched the items cache on close.Also fixes client state outliving a session: sign-out/sign-in are soft navigations, so the user store (incl.
isAdmin), the root React Query cache, and the trace survived into the next user's session in the same tab. The user store is now keyed by user id and cleared on sign-out, andSessionStateResetdrops cached queries + the trace whenever the signed-in user goes away or changes.Checklist
captureServerException/ the error boundary, or N/A — N/Alearnskill, or N/A — N/A (each fix has a regression test)Notes
aria-hiddenhydration mismatch on the command palette (RadixhideOthersruns before the Suspense-wrapped palette hydrates); room pages use the same masonry with a "Load more" button and could adopt the complete-rows hold-back; react-scan / React Query Devtools behind the debug flag.🤖 Generated with Claude Code