Skip to content

Dev - #248

Merged
vernu merged 7 commits into
mainfrom
dev
Jul 22, 2026
Merged

Dev#248
vernu merged 7 commits into
mainfrom
dev

Conversation

@vernu

@vernu vernu commented Jul 22, 2026

Copy link
Copy Markdown
Owner

No description provided.

vernu and others added 7 commits July 22, 2026 13:49
- prefetch route-tab links so tab clicks reuse a cached payload instead
  of paying an uncached server round trip each time
- add loading.tsx boundaries (dashboard root and each section) so clicks
  paint within a frame; section-level files keep the header and tabs
  mounted while only the content area swaps
- link the sidebar Account item and the checkout page straight to
  /dashboard/account/billing. The /dashboard/account redirect stub stays
  for old links, but no internal link pays the extra hop anymore. The
  stub also dropped query params, which silently ate the plan-change
  success toast.
- set QueryClient defaults (staleTime 60s, no focus refetch, retry 1);
  mutations already invalidate their keys, so the user's own changes
  stay instant. Device messages and webhook deliveries get a 15s
  staleTime since they change from outside the tab.
- replace the axios getCachedSession TTL cache with a token seeded from
  the server session in Providers and kept in sync by a session bridge.
  Requests attach the token synchronously; /api/auth/session is only a
  deduped fallback, instead of a refetch every 2 minutes with a
  thundering herd on expiry.
- swap the billing card's 16px loading spinner for a card-shaped
  skeleton so the tab no longer looks blank while loading

Tests: interceptor seeding/dedupe/signed-out behavior, provider
defaults and token seeding, nav active-state matching, tab prefetch,
billing loading state, plus e2e coverage for direct-to-billing
navigation and an at-most-one-session-call budget guard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The messaging pages now stream behind a loading.tsx boundary, so their
HTML can paint before React hydrates. Tests that filled the To field or
dropped a CSV immediately after page.goto raced that window: the input
was there, the handlers were not, and the interaction was silently lost.
Under parallel workers the race lost often enough to fail runs at random.

Both specs now navigate through a helper that waits for the client-side
devices fetch, which only fires after hydration effects run, so the form
is proven interactive before the test touches it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The API guide imported react-syntax-highlighter's default entry, which is
the highlight.js build with every language compiled in. It also applied
Prism themes to it, and the two use different class names, so the samples
were largely uncoloured on top of being heavy. Switch to PrismLight and
register the six languages the guide actually renders.

GoogleOAuthProvider wrapped the whole app but only the login and register
pages use it, so the Google Identity SDK initialised on every dashboard
page. Move it inside LoginWithGoogle, which is where both consumers go
through.

Total client chunks drop from 3.1M to 2.3M.

Also fixes four e2e tests that were relying on page.goto returning a
fully loaded page. Adding loading.tsx introduced a short skeleton state
that did not exist before, and each test measured or interacted during
it. They now wait on real signals instead of timing:

- bulk send waits for the dropzone row cap, which comes from
  useSubscription inside that page's own hook. The previous wait on the
  devices response was wrong: the dashboard layout requests the same
  query key, so it can resolve before the page hydrates.
- the mobile overflow sweep and the API guide replace networkidle, which
  needs a 500ms window with zero connections that link prefetching keeps
  pushing out of reach.
- the footer/tab-bar check re-scrolls as the page grows, instead of
  scrolling once while the skeleton is still short.
- the overflow sweep measures the billing tab rather than
  /dashboard/account, a redirect stub with no layout of its own whose
  client-side redirect tore down the measurement.

A new test asserts the samples carry Prism token markup in all five
languages, so a revert to the highlight.js entry fails instead of
silently dropping the colours.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n refetch

The effect depended on the whole session object. SessionProvider returns a
new object on every refetch, and React compares deps by reference, so an
identical session still ran the cleanup and the body again: the widget was
destroyed and its script re-injected even though nothing about the user
had changed, closing any chat the user had open.

It now depends on the individual strings the effect actually reads, which
compare by value, so it only re-initialises when the metadata genuinely
changes.

Two related leaks fixed at the same time:

- cleanup called destroy() but left the script tag in the document, so
  every re-run added another one for the life of the page. It is now
  removed.
- a script whose src is already cached can finish loading after cleanup
  ran, and initialising then left a widget behind that nothing would
  destroy. That late load is now ignored.

Tests cover all four behaviours and fail against the previous version.

Also gives the API guide page focus before the clipboard test: writeText
rejects on an unfocused document, which is where a page sits while other
workers run, and granting clipboard permissions does not cover it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DialogContent is a CSS grid, and an implicit grid track has a min-content
floor. `break-words` does not reduce min-content size, so a long
unbreakable URL in any child pushed every child past the card's
max-width instead of wrapping. In message history that left the message
box, the delivery error panel and the action button rendered hundreds of
pixels outside the dialog, off the side of the screen.

An explicit minmax(0,1fr) column removes that floor, after which
break-words can do its job. This fixes every dialog in the app, not just
this one: any long URL, API key or gateway ID hit the same track.

Also makes the details dialog the single scroll container, matching
api-keys and the webhook payload modal, instead of capping the message
body at 224px behind a scrollbar nested in a scrollbar. And the list row
preview now wraps, so a URL-heavy message fills both clamped lines
rather than being cut off partway through the first.

Covered by an e2e regression test at 375px and 1280px that asserts the
dialog does not scroll sideways and that the action button stays inside
it. Verified failing before this change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
perf(web): make dashboard navigation fast
fix(web): keep long message text inside the details dialog
@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
textbee Ready Ready Preview, Comment Jul 22, 2026 2:31pm

Request Review

@vernu
vernu merged commit c5e5932 into main Jul 22, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant