Skip to content

feat: add frontend inspector for mapping DOM nodes to Python source#6411

Open
FarhanAliRaza wants to merge 4 commits intoreflex-dev:mainfrom
FarhanAliRaza:inspector
Open

feat: add frontend inspector for mapping DOM nodes to Python source#6411
FarhanAliRaza wants to merge 4 commits intoreflex-dev:mainfrom
FarhanAliRaza:inspector

Conversation

@FarhanAliRaza
Copy link
Copy Markdown
Contributor

@FarhanAliRaza FarhanAliRaza commented Apr 28, 2026

  • Add frontend_inspector, frontend_inspector_shortcut, and frontend_inspector_editor config options; "dev" mode fails the prod build (validated at Config init and re-checked at compile time).
  • New reflex_base.inspector package: state flag, capture registry that records source location per component, and an integration module owning compile-time wiring.
  • Component.create stamps emitted tags with a data-rx-source id when capture is enabled (Fragments skipped).
  • Vite config and package.json now react to inspector state and are re-rendered each compile; launch-editor pulled in as a dev dep on demand.
  • Extract framework-frame walking from console.py into shared reflex_base.utils.frames, reused by the inspector.
  • Exclude _inspector_id from generated pyi stubs.
  • Docs: new advanced_onboarding/frontend_inspector.md page, sidebar entry, and pointer from the configuration guide.
  • Tests for config validation, runtime gating, capture lifecycle, frame walking, browser contract, and the component data-attribute.

All Submissions:

  • Have you followed the guidelines stated in CONTRIBUTING.md file?
  • Have you checked to ensure there aren't any other open Pull Requests for the desired changed?

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

New Feature Submission:

  • Does your submission pass the tests?
  • Have you linted your code locally prior to submission?

Changes To Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally?

Alt-click actually opens the file in the editor.

image

- Add `frontend_inspector`, `frontend_inspector_shortcut`, and
  `frontend_inspector_editor` config options; "dev" mode fails the prod
  build (validated at Config init and re-checked at compile time).
- New `reflex_base.inspector` package: state flag, capture registry that
  records source location per component, and an integration module
  owning compile-time wiring.
- `Component.create` stamps emitted tags with a `data-rx-source` id when
  capture is enabled (Fragments skipped).
- Vite config and package.json now react to inspector state and are
  re-rendered each compile; `launch-editor` pulled in as a dev dep on
  demand.
- Extract framework-frame walking from `console.py` into shared
  `reflex_base.utils.frames`, reused by the inspector.
- Exclude `_inspector_id` from generated pyi stubs.
- Docs: new `advanced_onboarding/frontend_inspector.md` page, sidebar
  entry, and pointer from the configuration guide.
- Tests for config validation, runtime gating, capture lifecycle, frame
  walking, browser contract, and the component data-attribute.
…n alt

- Build inspector script src and runtime URLs through prepend_frontend_path
  so the script and its assets load when the app is hosted under a base
  path (e.g. docs at /docs/). Inspector JS now reads the URLs from the
  window config instead of hardcoding /__reflex/...
- Editor middleware matches /__open-in-editor as a substring so it still
  fires when the request comes in prefixed.
- Dedupe capture entries by (file, line, column, component); identical
  call sites now share an id, cutting the docs source-map from ~200MB to
  ~600KB.
- Require alt at click time and add a 350ms post-focus guard so
  re-focusing the browser window doesn't hijack a click into 'open in
  editor'; hide the overlay on blur to avoid stale snap on return.
- Update the inspector docs to reflect alt+click.
@FarhanAliRaza FarhanAliRaza requested review from a team and Alek99 as code owners April 28, 2026 20:03
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 28, 2026

Merging this PR will not alter performance

✅ 9 untouched benchmarks


Comparing FarhanAliRaza:inspector (3f7ad4d) with main (21406ab)

Open in CodSpeed

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 28, 2026

Greptile Summary

This PR adds a dev-only frontend inspector that maps rendered DOM nodes back to their Python Component.create call sites. It introduces three new config options (frontend_inspector, frontend_inspector_shortcut, frontend_inspector_editor), a new reflex_base.inspector package for capture/emit/state management, Vite plugin wiring, and browser-side JS/CSS assets.

One P1 bug was found in the browser script: onClick, onKeyDown, and onKeyUp all hardcode event.altKey checks rather than consulting the configured shortcut modifier, so any non-alt shortcut configuration (e.g. ctrl+x) will silently fail for click-to-open and hover-mode. There is also a P2 UX gap where the in-memory source map is never invalidated after HMR rebuilds.

Confidence Score: 3/5

Not safe to merge as-is: click-to-open-editor is broken for any non-default shortcut modifier configuration due to hardcoded altKey checks in the browser script.

A P1 logic bug means onClick, onKeyDown (modifier-held preload), and onKeyUp all hardcode event.altKey instead of checking the configured shortcut modifier. This breaks the primary interaction for any user who configures a non-alt modifier. Since the default shortcut is alt+x this passed basic testing but is broken for all other configurations.

packages/reflex-base/src/reflex_base/assets/inspector/inspector.js — the onClick, onKeyDown, and onKeyUp handlers need to consult the configured shortcut object instead of hardcoding event.altKey.

Important Files Changed

Filename Overview
packages/reflex-base/src/reflex_base/assets/inspector/inspector.js New browser-side inspector script; contains a P1 bug where click-to-open-editor is hardcoded to check event.altKey ignoring the configured shortcut modifier, and the hover/modifierHeld flow has the same issue. Source map is also never invalidated after HMR.
packages/reflex-base/src/reflex_base/inspector/capture.py New capture module that walks the call stack to record Python source locations; hardcodes column=1 since CPython frames don't expose column info, which is a known limitation. Logic is otherwise clean.
packages/reflex-base/src/reflex_base/inspector/integration.py Compile-time coordinator for the inspector; validates config vs env mode, manages capture lifecycle, emits Vite plugin and package.json deps. Looks correct.
packages/reflex-base/src/reflex_base/components/component.py Stamps data-rx attribute on rendered tags when inspector is enabled; skips Fragments. Correctly deduplicates via setdefault.
reflex/utils/frontend_skeleton.py Adds inspector asset copy and Vite plugin emission; correctly guards on is_active() and hooks into existing compile pipeline.
reflex/app.py Integrates prepare_for_compile and write_source_map into the compile pipeline; also moves RouterData import to correct submodule path.

Sequence Diagram

sequenceDiagram
    participant User as Developer
    participant Config as rx.Config
    participant Integration as inspector.integration
    participant Capture as inspector.capture
    participant Compile as App._compile
    participant Emit as inspector.emit
    participant Browser as inspector.js

    User->>Config: frontend_inspector="dev"
    Config->>Integration: validate(config)
    Integration-->>Config: ok (or ConfigError if prod)

    User->>Compile: reflex run
    Compile->>Integration: prepare_for_compile(config)
    Integration->>Capture: reset()
    Integration-->>Compile: state enabled

    loop For each Component.create()
        Compile->>Capture: capture(cls.__name__)
        Capture-->>Compile: cid (int)
        Compile->>Compile: stamp data-rx=cid on tag
    end

    Compile->>Emit: write_source_map(public_dir)
    Emit-->>Compile: /__reflex/source-map.json written

    Browser->>Browser: Alt+X toggles inspector
    Browser->>Browser: fetch(SOURCE_MAP_URL)
    Browser-->>Browser: sourceMap loaded
    Browser->>Browser: hover shows outline+label
    Browser->>Browser: Alt+click GET /__open-in-editor?file=…
    Browser->>Browser: dev_server_middleware launches editor
Loading

Reviews (1): Last reviewed commit: "fix(inspector): respect frontend_path, d..." | Re-trigger Greptile

Comment thread packages/reflex-base/src/reflex_base/assets/inspector/inspector.js Outdated
Comment thread packages/reflex-base/src/reflex_base/assets/inspector/inspector.js Outdated
…prod

frontend_inspector="dev" is already inert under REFLEX_ENV_MODE=prod
(is_active gates every emission site), so the validate() raise just
broke shared rxconfigs that get reused across run modes — including
the docs CI which runs reflex run --env prod against a config that
keeps the inspector on for local dev.
Cache the source map per modifier-press/click so edits are picked up without reloading the page, and gate clicks on the configured shortcut (alt/ctrl/meta/shift) instead of hard-coding altKey.
Comment on lines +263 to +268
frontend_inspector: FrontendInspectorMode = "off"

frontend_inspector_shortcut: str = "alt+x"

frontend_inspector_editor: str = ""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would rather this be implemented as a plugin, and the config options belong in that plugin, similar to sitemap

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.

2 participants