Root cause: the React DevTools browser extension injects a hook stub at
document_start (before HTML parsing), so hook.inject can be undefined
when React Fast Refresh runs. React Refresh destructures inject as
undefined and later calls undefined.apply() -> TypeError. This prevents
React from ever setting injectedHook, so onCommitFiberRoot is never called.
Fix: unconditionally replace hook.inject with a wrapper that tries the
captured previous inject (with try/catch) then falls back to our own
renderer ID allocation. Works regardless of script execution order.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
React's injectInternals() calls hook.inject(renderer) before it will ever
invoke onCommitFiberRoot. When inject is missing, React's try/catch silently
bails and injectedHook stays null — our handler is never reached regardless
of how correctly onCommitFiberRoot is set up.
Fix: define inject() on the hook we create (returns a renderer ID and stores
the renderer in hook.renderers, matching the React DevTools spec). Also
patches inject onto any existing stub hook that lacks it, so third-party
minimal hooks don't silently block registration.
This is the root cause of the 'Static HTML page' false positive on apps
that don't have the React DevTools browser extension installed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Next.js 13+ App Router calls hydrateRoot on <html> or <body> directly
(not on a #__next div), so __reactFiber$ annotations land on
document.documentElement or document.body.
Changes:
- Add document.documentElement to the candidate list (covers App Router)
- Extract getFiber() helper to remove repeated key-scan loop
- Add DOM-wide fallback scan (querySelectorAll('*')) so any React app
is found regardless of its root container convention
- Remove the early !tree bail-out — post FIBER_TREE_UPDATE even with a
sparse tree, matching onCommitFiberRoot's behaviour
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three related fixes for the canvas live-render panel:
1. captureExistingTree() in fiber-hook — retroactively walks the already-
mounted React fiber tree via __reactFiber$ DOM annotations. Called
immediately on READY (handles post-hydration race) and again at 2 s
(handles deferred hydration / Suspense). Prevents the 'Static HTML page'
false positive on React apps that mounted before the hook script ran.
2. Static-page timer bumped from 4 s → 8 s in LiveArtboard to give the
2-second captureExistingTree safety-net enough headroom before the
no-React verdict fires.
3. Route discovery same-origin fix applied to both renderer/fiber-hook.ts
and live-sdk/hook.ts — root-relative hrefs (starting with '/') are now
accepted regardless of origin so CLI-proxied pages (where links still
point to the original domain) expose their navigation routes correctly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>