Files
zed/crates/inspector_ui
Conrad IrwinandBen Kunkle 72dc742101 Support building with corgi (#63396)
[corgi](https://github.com/ConradIrwin/corgi) is a cargo-compatible
build tool that runs `build.rs` scripts and proc-macros in a sandbox (no
network, no ambient env, reads limited to a package's own `.rs` sources
unless declared). This makes Zed buildable under it. Cargo builds (dev
and release) are unaffected.

### Changes

- **`scratch` patch**: patch `scratch` to a small local copy
(`corgi-patches/scratch`) that reads `OUT_DIR` at runtime instead of
baking it in at compile time. `cxx-build` writes its shared cxxbridge
headers into `scratch::path(...)`; upstream returns a single global dir
(outside any action's `OUT_DIR`) that the sandbox can't grant, while
this patch gives each build script a private, writable dir in its own
entry. webrtc-sys is self-contained, so per-crate scratch dirs are
sufficient. Kept local rather than a cxx git fork because a git checkout
of cxx needs symlink support that Windows cargo CI lacks.
- **Dev asset loading**: add `util::dev_fs_embed!` and switch dev-mode
`rust_embed` asset sources (`assets`, `settings`, `grammars`, `agent`,
`edit_prediction_cli`) to read from the checkout at runtime, locating
the repo root by walking from the executable up to the enclosing `.git`.
This avoids baking `CARGO_MANIFEST_DIR` into the binary (corgi rejects
artifacts that embed the build-time checkout path, and it's wrong in any
other worktree). Release builds still embed via `#[derive(RustEmbed)]`.
- **Stop baking checkout paths into artifacts**: drop
`gpui::GPUI_MANIFEST_DIR`; resolve `../gpui` from `gpui_apple`'s build
script; stage the metal shader into `OUT_DIR` before compiling so the
`.metallib` records the output dir, not the checkout; resolve the repo
root at runtime in `remote` and `inspector_ui` (removing
`inspector_ui`'s `build.rs` / `ZED_REPO_DIR`).
- **corgi.toml**: declare the pinned tools (`cmake`, prebuilt libwebrtc
via `LK_CUSTOM_WEBRTC`), a release-only `ZED_COMMIT_SHA` probe, and the
non-`.rs` / cross-package reads each package performs.

### Validation

- `corgi check` + `corgi build`, dev and release, for `zed` (plus `cli`,
`remote_server`, `ep`) with a warm C++ cache — clean; built binaries
embed no checkout path.
- `corgi fmt --check`, `corgi clippy`, and `corgi test` on the changed
crates (922 tests pass).
- `cargo check -p util` locally; relying on CI for the full cargo
build/test matrix.

### Notes for the reviewer

- Per the repo rule, the first two lines of `README.md` are the
review-confirmation marker; remove them before merging (this is what
Danger is failing on).
- `remote_server` cross-compilation (linux-musl via zigbuild) is not yet
covered by `corgi.toml` — host (macOS arm64) only for now.

### Suggested .rules additions

Only if the team wants corgi guidance in `.rules` (it recurred several
times here): dev builds must not bake the checkout path into artifacts —
no `env!("CARGO_MANIFEST_DIR")` or `file!()`-derived absolute paths in
library/binary output. Resolve the repo root at runtime via
`util::dev_repo_root()` (walks to `.git`), and use `util::dev_fs_embed!`
for dev-mode `rust_embed` sources.

Release Notes:

- N/A

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
2026-08-31 17:37:00 +00:00
..
2026-08-31 17:37:00 +00:00
2026-08-20 22:09:43 +00:00

Inspector

This is a tool for inspecting and manipulating rendered elements in Zed. It is only available in debug builds. Use the dev::ToggleInspector action to toggle inspector mode and click on UI elements to inspect them.

Current features

  • Picking of elements via the mouse, with scroll wheel to inspect occluded elements.

  • Temporary manipulation of the selected element.

  • Layout info for Div.

  • Both Rust and JSON-based style manipulation of Div style. The rust style editor only supports argumentless Styled and StyledExt method calls.

  • Navigation to code that constructed the element.

Known bugs

JSON style editor undo history doesn't get reset

The JSON style editor appends to its undo stack on every change of the active inspected element.

I attempted to fix it by creating a new buffer and setting the buffer associated with the json_style_buffer entity. Unfortunately this doesn't work because the language server uses the version: clock::Global to figure out the changes, so would need some way to start the new buffer's text at that version.

        json_style_buffer.update(cx, |json_style_buffer, cx| {
            let language = json_style_buffer.language().cloned();
            let file = json_style_buffer.file().cloned();

            *json_style_buffer = Buffer::local("", cx);

            json_style_buffer.set_language(language, cx);
            if let Some(file) = file {
                json_style_buffer.file_updated(file, cx);
            }
        });

Future features

  • Action and keybinding for entering pick mode.

  • Ability to highlight current element after it's been picked.

  • Info and manipulation of element types other than Div.

  • Indicate when the picked element has disappeared.

  • To inspect elements that disappear, it would be helpful to be able to pause the UI.

  • Hierarchy view?

Methods that take arguments in Rust style editor

Could use TreeSitter to parse out the fluent style method chain and arguments. Tricky part of this is completions - ideally the Rust Analyzer already being used by the developer's Zed would be used.

Edit original code in Rust style editor

Two approaches:

  1. Open an excerpt of the original file.

  2. Communicate with the Zed process that has the repo open - it would send the code for the element. This seems like a lot of work, but would be very nice for rapid development, and it would allow use of rust analyzer.

With both approaches, would need to record the buffer version and use that when referring to source locations, since editing elements can cause code layout shift.

Source location UI improvements

  • Mode to navigate to source code on every element change while picking.

  • Tracking of more source locations - currently the source location is often in a ui component. Ideally this would have a way for the components to indicate that they are probably not the source location the user is looking for.

    • Could have InspectorElementId be Vec<(ElementId, Option<Location>)>, but if there are multiple code paths that construct the same element this would cause them to be considered different.

    • Probably better to have a separate Vec<Option<Location>> that uses the same indices as GlobalElementId.

Persistent modification

Currently, element modifications disappear when picker mode is started. Handling this well is tricky. Potential features:

  • Support modifying multiple elements at once. This requires a way to specify which elements are modified - possibly wildcards in a match of the InspectorElementId path. This might default to ignoring all numeric parts and just matching on the names.

  • Show a list of active modifications in the UI.

  • Support for modifications being partial overrides instead of snapshots. A trickiness here is that multiple modifications may apply to the same element.

  • The code should probably distinguish the data that is provided by the element and the modifications from the inspector. Currently these are conflated in element states.

If support is added for editing original code, then the logical selector in this case would be just matches of the source path.

Code cleanups

Consider removing special side pane rendering

Currently the inspector has special rendering in the UI, but maybe it could just be a workspace item.

Pull more inspector logic out of GPUI

Currently crates/gpui/inspector.rs and crates/inspector_ui/inspector.rs are quite entangled. It seems cleaner to pull as much logic a possible out of GPUI.

Cleaner lifecycle for inspector state viewers / editors

Currently element state inspectors are just called on render. Ideally instead they would be implementors of some trait like:

trait StateInspector: Render {
    fn new(cx: &mut App) -> Task<Self>;
    fn element_changed(inspector_id: &InspectorElementId, window: &mut Window, cx: &mut App);
}

See div_inspector.rs - it needs to initialize itself, keep track of its own loading state, and keep track of the last inspected ID in its render function.