Skip to content

0.7.2: reused-component hint and source-map fallback, verified on real builds - #30

Merged
gowtham012 merged 5 commits into
mainfrom
feat/0.7.2
Oct 21, 2026
Merged

gowtham012 merged 5 commits into
mainfrom
feat/0.7.2

Conversation

@gowtham012

Copy link
Copy Markdown
Owner

Both 0.7.2 features in one branch: #28 (reused-component hint) and #29 (source-map fallback) merged together. On top of that, four fixes for things that only broke on real production builds.

What it does

  • Reused-component hint: " appears 6 times on this page", with Just this one / Everywhere. The choice goes into element.reuse, and the agent's markdown gets a "Reused:" line.
  • Source-map fallback: a minified build has no _debugSource or __file. The clicked component's own code is found in the page's scripts and followed through its source map back to the original file:line and component name (method: "sourcemap").

Real-app check (headless Chromium + the extension from this branch, bridge + MCP)

Picks per app: .card-title (3rd) with Everywhere, .site-title, .price (2nd). "popover" is the meta line and reuse text after the lookup. "note" is the stored annotation. "md" is the agent's markdown.

App Reuse hint Source hint Arrow component (Price)
Vite + React 18 prod PASS: popover <ProductCard> appears 6 times; element.reuse ProductCard×6, scope component; md Reused: <ProductCard> is rendered 6 times PASS: src/components/ProductCard.jsx:3, Header.jsx:1, method sourcemap PASS: <Price> appears 6 times, src/components/Price.jsx:1, name Price
Vite + React 19 prod PASS: <ProductCard> ×6, same note and md PASS: ProductCard.jsx:3 / Header.jsx:1, sourcemap n/a (function decl.): Price.jsx:1, Price
Next 15 prod PASS: <ProductCard> ×5 PASS: components/ProductCard.jsx:4, components/Header.jsx:2, sourcemap n/a (price is inline in ProductCard)
webpack 5 prod PASS: <ProductCard> ×6, <Price> ×6 PASS: ProductCard.jsx:3 / Header.jsx:1 / Price.jsx:1, sourcemap n/a (function decl.)
Vite + Vue 3.5 prod PASS: popover <PriceCard> appears 4 times; element.reuse PriceCard×4, estimate false; md Reused line PASS: src/components/PriceCard.vue:3, SiteHeader.vue:6, framework vue, sourcemap n/a
Vite + React 18 dev PASS: <ProductCard> ×6, <Price> ×6 PASS: _debugSource, no map lookup PASS
Vite + React 19 dev PASS: <ProductCard> ×6 PASS: _debugStack (components/ProductCard.jsx:20) n/a
Vite + Vue 3.5 dev PASS: <PriceCard> ×4 PASS: __file n/a

The only console errors were favicon 404s.

The four fixes

  1. React prod: the reuse hint showed the minified name (<Vf>) in the popover, in element.reuse.component and in the markdown. applyMapSource now also renames element.reuse.component when it matches the minified name. paintMeta waits for the lookup, then repaints the meta line and the reuse text with the original name, as long as the same element is still picked.
  2. Vue 3 prod: nothing was detected. el.__vueParentComponent and app._instance exist only in dev builds. A new vueOwner(el) uses __vueParentComponent when it is there. Otherwise it finds the nearest container with __vue_app__, walks container._vnode.component's vnode tree (bounded at 50k), maps each element to the instance that owns it, and returns the nearest owner. vueHint, reuseHint and mapProbe all use it.
  3. Vue prod: the source map pointed to the wrong file. The snippet starts at setup(e){, and Vue's generated setup wrapper has no mapping segment. Greatest-lower-bound then picked the previous module's segment. originalPosition now uses the first segment inside the snippet (within 120 columns) when the found one starts before it, or when none is found.
  4. Arrow-function components kept their minified name. names[] puts "Price" on the const Af= binding just before the function text, and the name search only looked forward. It now also looks back up to 40 columns in the same source file, taking only the nearest named segment.

Tests

  • New test/e2e-real.test.mjs runs against real Vite production bundles that are committed in test/site/real/{react,vue} (React 18.3.1, Vue 3.5.44). They are rebuilt by test/fixtures/real/build.sh; CI does not build them.
    • React: a ProductCard rendered 3× shows <ProductCard> appears 3 times in the popover after the lookup, and the note and markdown say the same. Source is src/components/ProductCard.jsx, method sourcemap.
    • React arrow component: Price resolves to src/components/Price.jsx under its real name.
    • Vue: <script setup> SFC rendered 3× shows <PriceCard> appears 3 times (estimate false), with source src/components/PriceCard.vue.
  • test/ui-sourcemap.test.mjs: unit tests on those same real maps. The Vue setup() segment fallback (the test also asserts that the segment before it belongs to another module), and the arrow look-back name. A function declaration still takes the name after function.
  • All of these failed before the fixes: 3/3 e2e timed out still showing the minified name or no hint, and 3 unit tests failed.
  • Full suite: 242 tests, 241 pass, 0 fail, 1 skipped (as before).
  • Firefox: web-ext lint gives 0 errors and 1 warning, the existing Firefox-for-Android min-version warning.
  • Manifest: no new permission.

🤖 Generated with Claude Code

gowtham012 and others added 5 commits October 20, 2026 23:12
When the picked element belongs to a component rendered more than once,
the comment box says "<Card> appears 3 times on this page" and offers
"Just this one" / "Everywhere (the component)" (nothing chosen by
default). The note carries element.reuse = {component, count, estimate,
scope?}; the agent's markdown gets a "Reused:" line, and when no scope
was chosen it is told to ask with reply_annotation before editing the
shared component. The MCP instructions say the same.

Detection (inspector.js, MAIN world, one new function + one call):
React counts fibers of the nearest component's type in the committed
tree from the HostRoot; Vue 3 walks the app root's vnode/instance tree;
plain HTML estimates from tag + classes + parent tag/classes. Capped at
500 matches / 50k nodes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When a minified build has stripped React's _debugSource and Vue's __file,
inspector.js hands the nearest component function's own source text and the
page's script URLs to the worker, which finds the script that holds it,
follows its sourceMappingURL (or SourceMap header; data: and indexed maps
too), decodes the VLQ mappings and puts the original file:line and component
name into the source hint with method "sourcemap". Same origin (or loopback
from a loopback page) only, redirects refused, no cookies, 8 MB per file,
1.5 s total; any failure leaves no hint. No new permission or dependency.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	CHANGELOG.md
#	extension/content.js
Verified against Vite + React 18/19, Next 15, webpack 5 and Vite + Vue 3.5
production builds; four things only showed up there:

- React: the reuse hint said <Vf>. The map's original name now also renames
  element.reuse, and the popover repaints its meta line and reuse text once
  the lookup answers.
- Vue 3 production sets no __vueParentComponent: the owner is found through
  the mount container's __vue_app__ vnode tree (vueOwner), used by the
  framework hint, the reuse count and the map probe.
- Vue's generated setup() has no mapping segment of its own, so the segment
  before it (another module) was taken; the first segment inside the
  function's head is used instead.
- Arrow components carry their name on the `const` binding before the
  function: look back up to 40 columns in the same source.

Tests: e2e-real.test.mjs against committed Vite production bundles of React
18 and Vue 3.5 (test/site/real, rebuilt by test/fixtures/real/build.sh), and
unit tests for the segment fallback and the look-back name on those maps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t; Windows-safe test server

captureRect and buildAnnotation now run together: the crop is still measured at the moment of Send,
but a source-map lookup can no longer hold the note back, so navigating right after Send keeps it
(Bug 6 failed on macOS CI). The lookup budget is 4 s: 1.5 s was too tight on a slow runner. The
e2e-real test server checked the trailing slash after path.normalize, which uses \\ on Windows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gowtham012
gowtham012 merged commit 429c4c7 into main Oct 21, 2026
3 checks passed
@gowtham012
gowtham012 deleted the feat/0.7.2 branch October 21, 2026 13:36
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