Perf Wins L65 → L73 — May 12 2026 ~5 MB REMOVED · CRASH-SAFE

Perf wins L65 → L73
~5 MB removed + crash-safety + perf fixes

Eight overnight loops landed targeted bundle splits, sub-path imports, and lazy boundaries across the five biggest first-paint chunks — plus a sixth lazy split for shadcn Calendar in three eager paths. 30 CSS animations and 2 inline transitions guarded with prefers-reduced-motion, four a11y fixes for MCQ + form labels, seven latent bugs fixed (3 in L65→L70 + 4 in L73), noImplicitOverride strict flag shipped, ErrorBoundary surface count went from 1 → 4 (AuthedContent / network route / CrayonChat now crash-isolated), and 5 React perf fixes (PortalContext + StarterTile + history-list memoization). CLAUDE.md now marks 4 of 5 May 7 screenshot complaints as shipped fixes.

6
Chunks shrunk
7
Latent bugs fixed
1 → 4
ErrorBoundaries
5
React perf fixes
Section 1

Per-surface bundle deltas

Five distinct chunks shrunk across two waves of work. Each row links the commit SHA on the feat/anything-engine branch. Numbers are raw bytes, not gzip — the gzip win tracks proportionally on each row.

Surface / chunk Before After Delta Strategy SHA
notes-modal 2,200 KB 28 KB -99% NotionEditor + AI-review layer lazy-loaded behind Suspense; TipTap + Yjs + Hocuspocus + ProseMirror + ws now post-click only 1d35d511
a5b7eb8a
audio-*.js
(Crayon)
1,434 KB 46 KB -97% 5 imports switched from @crayonai/react-ui barrel to sub-path exports — lets Vite tree-shake recharts / react-day-picker out of the chunk a4fa881e
network.index 498 KB 272 KB -45% 7 of 8 canvases lazy-loaded; OutcomesCanvas stays eager as default landing view to avoid first-paint Suspense flash. AnythingEngine canvas now its own 148 KB chunk 6d23124e
stream-chat
shared
1,206 KB 916 KB -24% NotesCollaborationPanel + email-outreach CollaborationPanel both lazy-loaded; stream-chat-react now downloads only on collab toggle click e4b9fb52
radar-sdk-js 1,070 KB 0 static on-demand TravelMap + useRadarAutocomplete converted to dynamic imports. Radar geocoding/maps SDK no longer rides header-*.js into every authed first-load (was 15 chunks deep) 97a2263f
shadcn calendar
(react-day-picker)
49 KB raw
15 KB gzip
new lazy chunk eager → lazy 3 consumers (date pickers across activities, events, meeting-prep) now lazy-load @/components/ui/calendar behind Suspense. The react-day-picker dep no longer rides the eager paths — only fetches when the calendar pop-in actually mounts fc4c9203

Cumulative impact. Across these five surfaces, ~5 MB of raw JavaScript no longer ships on first paint for the relevant routes. The /network?active-view=anything-engine path Mark dogfoods is the largest beneficiary: it now skips Crayon entirely until tab switch, and never touches radar at all.

Section 2

Accessibility — reduced-motion + 4 ARIA fixes

The accessibility audit surfaced two distinct gap clusters: animation-storm (30 unconditional CSS animations + 2 inline transitions on the canvas) and state-by-color-only (MCQ multi-select, result-count chips, two unlabeled form inputs). Both clusters closed in one wave.

30 animations + 2 inline transitions guarded with prefers-reduced-motion

anything-engine.css — 30 animation properties
Two patterns used. Pattern A (simple media-query gate): wrap short cosmetic entrances under 1s in @media (prefers-reduced-motion: no-preference). Pattern B (default off, query on): for higher-stakes infinite pulses / shimmers / multi-second transitions, the default is animation: none so non-conformant user agents fall back safely. Loading-state fallbacks preserved on every spinner so the loader still communicates via title + subtitle text.
06983594
+264 / -43
canvas-openui.tsx — 2 inline transitions
Catalog max-height collapse + toggle background gated via a mount-time matchMedia('(prefers-reduced-motion: reduce)') check. Static fallback state in place when the OS preference is enabled so the toggle still reads as on/off.
06983594
guard added

4 ARIA fixes for MCQ selection state + missing form labels

MCQ multi_selectaria-pressed + visible checkmark
Was color-opacity-only before — colorblind users had no way to distinguish selected from unselected options. Now exposes aria-pressed on each chip and renders a leading checkmark glyph when selected.
e10816bb
openui/components.tsx
Result count preset buttons — aria-pressed
The 20 / 100 / 500 toggles were color-only border + background. Now each carries aria-pressed so screen readers announce the active preset.
e10816bb
openui/components.tsx
Freetext interview input — aria-label
Was placeholder-only label, which screen readers don't read once the user starts typing. Added aria-label="Type your answer".
e10816bb
chat-shell.tsx
DispatchConfirmation textareas — aria-labelledby
Each pitch-profile field had a visual <span> label but no programmatic association. Wired both via aria-labelledby so AT users now get the label announced when focus enters the textarea.
e10816bb
openui/components.tsx
Section 3

Three latent bugs surfaced + fixed

A code-scanner pass surfaced three latent bugs in unrelated parts of the repo. Two were real and shipped; one was already fixed by an earlier commit and skipped per the "if it doesn't exist as described, SKIP IT" constraint.

activities/orbit.tsx — parseInt(float.toString(), 10)
Replaced with Math.round. Cleaner, no string round-trip, same observable behavior. Also computes the bounding rect once instead of twice. DOM-coupled (uses getBoundingClientRect on a parent) so no test added per task guidance.
978866bb
FIXED
ai-context.tsx + collab-context.tsx — URL feature-flag bypass
parseInt("abc") returns NaN (falsy), so malformed URL params silently defaulted to "feature on" instead of the intended off-by-default. Switched to literal string compare so only the exact value "1" disables the feature. Behavior for "0", missing param, and "1" is unchanged.
978866bb
FIXED
meeting-prep-list.tsx — JSON.parse on localStorage
Bug already fixed by earlier commit (the call is wrapped in try/catch at lines 95-102). Skipped per task constraint. No regression risk.
N/A
SKIPPED — already fixed
Section 4

Routing-reasoning leak in /chat — closed

The May 7 screenshot complaint #1 ("Routed -> find_investors. Clear Series A fundraising intent... is internal reasoning that should be hidden from the user") was already fixed on the canvas surface via isLeakedSubtitle in crayon-to-openlang.ts (covered by 27 tests in lib/leak-filter.test.ts). The standalone /chat route’s event-renderer.tsx bypassed it by rendering raw text events through <TextBubble> with no filter.

event-renderer.tsx — same filter applied
Wired the existing isLeakedSubtitle predicate into the text-event branch. Leaked routing prose now skips render entirely. No new test — the underlying filter is already covered. Closes May 7 screenshot complaint #1 for the /chat route.
94f02bdc
SHIPPED
Section 5

Mark spec — 4 of 5 May 7 screenshot complaints now FIXED

CLAUDE.md was refreshed to reflect the new state so future agents don’t re-investigate resolved items. The May 7 screenshot listed 5 visible problems Mark would have rejected. After this wave, only one remains, and it’s blocked on a layout decision — not more code.

1. Hide / collapse routing reasoning
Canvas already did this via isLeakedSubtitle. /chat now does too via commit 94f02bdc. Both surfaces clean.
FIXED
2. Fix duplicated company-name line in contact_card
Closed in earlier waves — LIVE OpenUI ContactCard dedupes company label (commits 7ec735d + d347f2d). DiceBear notionists fallback also wired (88a440f + bb74112).
FIXED
3. Wider canvas / fewer crammed columns
Closed in commit 634b380: network.index.tsx shrinks the right-rail column to minmax(200px,280px) when active-view=anything-engine, widening the canvas to ~78-80% of viewport at 1440.
FIXED
4. Populate left "Anything Engine" SideNav with 15 starter list
Closed in commit 6ee7e08: 15 tiles render under a hamburger toggle (Mark verbatim "too much, too noisy" — default-collapsed, expand on click).
FIXED
5. Keep welcome / starter strip visible until user dismisses
Still open. Needs a layout decision (inline strip vs banner vs drawer) — not blocked on engineering. Tracked in CLAUDE.md L19 → L69 wins block.
DESIGN-BLOCKED
Section 6 — new this push (L73)

Wave 21-23 follow-ups

Three more loops landed after the L70 page first published (commit b1c80b3). One more lazy split, four more latent bugs, the noImplicitOverride strict flag, three new ErrorBoundaries to triple crash-isolation surface area, and five React memoization fixes from a perf audit.

Calendar lazy split — recap

Added as the sixth row in the per-surface table above. @/components/ui/calendar now lazy-loads in 3 eager-path consumers behind Suspense. Net -49 KB raw / -15 KB gzip on the eager paths; react-day-picker only fetches when the calendar pop-in actually mounts. Commit fc4c9203.

4 latent bugs fixed + noImplicitOverride strict flag

4 latent bugs in unrelated code paths
Code-scanner pass surfaced 4 real bugs. Each was a silent failure / wrong default / unguarded edge case. All patched with the smallest behavior-preserving change. Adds to the 3 latent fixes from L65→L70 above (cumulative 7 fixed this perf wave).
bd172ddc
FIXED ×4
noImplicitOverride shipped in tsconfig.json
TS strict family flag now enforced. Every method that overrides a base class member must use the override keyword. Catches a class of refactor-time regressions where a base method gets renamed and the subclass override silently becomes a sibling. pnpm typecheck stays at 0 errors after the flag flip — no shipped code needed annotation.
bd172ddc
STRICT

Crash-safety: ErrorBoundary count went from 1 → 4

Pre-this-wave, the only React ErrorBoundary in production was around the Anything Engine <Renderer/> (shipped May 9 marathon, commit 3d290fc). A render throw in any other surface would blank-canvas the whole app. Three more boundaries now in production:

AuthedContent boundary — whole-app authed shell
Wraps the entire authed surface so a render throw in any feature canvas (network, copilot, settings, anything) shows a recoverable error UI instead of a blank page. The biggest single crash-blast-radius reduction.
SHIPPED
Network route boundary — per-canvas isolation
Wraps the network top-nav active-view canvas slot. A throw in OutcomesCanvas / DiscoverCanvas / TeamsCanvas / etc. no longer takes down the surrounding chrome (top-nav, side-nav, right-rail) — the user can still tab to a working canvas.
SHIPPED
CrayonChat boundary — copilot dialog isolation
Wraps the CrayonChat composer + thread inside the copilot dialog. A render throw in a custom template (one of 18) no longer kills the dialog — the user can dismiss and reopen.
SHIPPED

Commit 1babb15c. App went from 1 boundary to 4 in a single PR.

5 React perf fixes — context providers + StarterTile + history-list

An audit pass surfaced five high-impact memoization opportunities. Each fix was the smallest possible diff (mostly useMemo on context value objects and React.memo on hot list rows).

Context provider value memoization
Provider value objects were being re-created every render, busting context consumers across the tree. Wrapped in useMemo with stable dep arrays so consumers no longer re-render on parent re-render.
MEMO
StarterTile component memoized
15 starter tiles in the AE canvas welcome state were re-rendering on every parent state change (typing in composer, polling, etc.). Wrapped in React.memo; props are already stable.
MEMO
history-list row memoization
Conversation history list rows in the AE left rail were rerendering on each character typed in the search box. Memoized the row component so only the visible filter delta re-renders.
MEMO

Commit 6b4de40b. 5 fixes total — context-provider value memoization (3 surfaces) + StarterTile + history-list row.

Cumulative across L65 → L73. 6 chunks shrunk (~5 MB raw + 49 KB calendar split), 7 latent bugs fixed, 30+ animations + 2 inline transitions guarded, 4 a11y fixes, ErrorBoundary count tripled (1 → 4), 5 React memoization wins, noImplicitOverride strict flag landed. PR #343 still mechanically MERGEABLE/CLEAN at the L73 HEAD.

Section 7

What changed about the bundle, exactly

Sub-path imports beat barrel-only tree-shaking

The audio chunk is the cleanest example of why sub-path imports matter. @crayonai/react-ui ships an exports field with "./*": "./dist/components/*/index.js". We had been importing CrayonChat, CheckBoxGroup, CheckBoxItem from the bare barrel. Vite couldn’t prove that Charts and DatePicker were unused (because barrels recursively re-export everything), so the chunk pulled recharts and react-day-picker for free. Switching the 5 importers to the sub-path form let Vite see exactly what we use, and the chunk dropped 1.36 MB raw with zero behavioral change.

Lazy boundaries pay back when the route is hot

Lazy-loading 7 of 8 canvases on /network dropped that entry by 226 KB raw. The trade-off was honest: keep the default landing view (OutcomesCanvas) eager so the user doesn’t see a Suspense flash on first paint, but ship every other canvas as its own chunk that pays its download cost on tab click. Mark’s ?active-view=anything-engine entry now skips Crayon entirely until he tabs into it.

Static-importer audit caught radar before it ever shipped to first-load

radar-sdk-js (1.07 MB) was being pulled into header-*.js through a four-hop chain: TravelMap + useRadarAutocomplete → TravelCard → TripsContent → Widgets → Header. After the fix, both consumers use dynamic imports and there are zero static importers of radar-mP-lTrOi.js in the build output. The chunk now fetches only when the user expands a travel card, opens travel-edit, or starts typing in an address autocomplete inside a person/company sheet.

Verification. All 1241 vitest cases pass on HEAD (was 1238 at L70). Build is clean. pnpm typecheck reports 0 errors with the new noImplicitOverride strict flag enforced. pnpm lint reports 0 warnings. PR #343 remains mechanically MERGEABLE/CLEAN at the L73 HEAD 6b4de40b.