Dev Inspector
Introduction
The dev inspector is a click-to-source overlay that runs only under the dev server. Hold a modifier chord and hover any rendered element to highlight the source it came from; click to act on it - edit a translation string in place, edit a markdown/markdown-translation value in a dialog, edit an element's class, or open the source file in your editor at the exact line.
It is injected into every page the dev server renders, alongside the live-reload client, and is absent from static site generation (bun ssg). Nothing it does - the highlight overlay, the data-ree/data-md source stamps, the edit dialogs - ever reaches generated output.
The Two Chords
The inspector has two independent activation chords. Which one is held decides what a click does:
| Chord | Tier | What a click does |
|---|---|---|
| Meta + Shift + hover/click | Content | Edit a translation string, edit markdown, or open the source in the editor |
| Alt + Shift + hover/click | Class | Edit the nearest element's class attribute |
Meta is Cmd on macOS and the Win key on Windows/Linux, so the content chord is Cmd+Shift / Win+Shift. Win+Shift+click does not collide with the reserved Win+Shift+<key> OS shortcuts (screenshot, move-window, ...) because those all need a third keyboard key. Plain Alt+click is avoided (PowerToys reserves it on Windows); Alt+Shift is not.
Hovering with a chord held draws a coloured overlay on the target and a label naming the action, so you always see what a click will do before you commit to it.
Content Tier (Meta + Shift)
Clicking with Meta+Shift held picks the action from what you clicked:
- Edit text (green) - a plain
{_ path }translation. Editable in place with a contenteditable outline. Enter saves; Esc or clicking away discards. A save only ever happens on an explicit Enter. - Edit markup (purple) - a
{- path }(raw HTML) or{@ path }(markdown) translation. Opens a dialog with the raw source fetched over the socket; edit it and Save (or Ctrl+Enter). Esc or Cancel discards. - Open code (grey) - anything else stamped from a
.reesource (a block element or a component). Opens the source file in your editor at the line the element came from. See Editor Configuration. - Open markdown (grey) - a rendered block stamped from a
.mdsource. Same open-in-editor behavior, labeled distinctly so you know which file will open.
Translation edits are written back to the route's {lang}.json file; the inspector resolves the language and namespace from the page you are viewing. Because the dev server live-reloads on source changes, the page re-renders with your edit immediately.
Class Tier (Alt + Shift)
Clicking with Alt+Shift held edits the nearest element's class attribute (amber highlight). Hovering shows the "edit class" label next to the element's current class value, in light text on black, so you can see what you're about to change before clicking. A small input opens prefilled with the current class value:
- Enter or Save writes the new class into the
.reesource and the page live-reloads. Esc, Cancel, or clicking away discards. - Clearing the value (empty input) removes the
classattribute entirely.
The class tier targets both block-level tags (div, section, h1-h6, p, ul, ...) and interactive/form elements (a, button, label, img, input, select, textarea), so a button or link inside a wrapper is directly reachable. Pure text-inline tags (span, strong, em, code, ...) are not targeted - a click on one resolves up to the nearest targetable ancestor.
Two safety limits:
- Literal classes only. A dynamic class such as
class="{= badge_color(x) }"is refused - the rendered value cannot be mapped back to the expression that produced it. Only the class value's own dynamism matters: a tag with a dynamichrefbut a literalclassis still editable. .reesource only. Class editing patches.reetemplates. Blocks rendered from.mdfiles are not class-editable (their source is markdown, which has no class attribute).
Editor Configuration
The Open code action launches an editor via the OPEN_IDE environment variable (set it in .env). It is strict - there is no default; if OPEN_IDE is unset or set to an unknown key, the open action reports a clear error rather than guessing.
OPEN_IDE=vscode
OPEN_IDE | Editor | Launch command |
|---|---|---|
vscode | VS Code | code --goto <file>:<line> |
zed | Zed | zed <file>:<line> |
nvim | Neovim | nvim +<line> <file> |
sublime | Sublime Text | subl <file>:<line> |
idea | IntelliJ IDEA | idea --line <line> <file> |
The editor's CLI launcher must be on your PATH (e.g. VS Code's code command, installed via its "Shell Command: Install 'code' command in PATH" action). To add another editor, extend the IDE_COMMANDS map in scripts/dev/open_in_editor.ts - each entry is an argv template where {file} and {line} are substituted, so editors with different flag ordering (like Neovim's +<line> before the file) are handled by their own row.
See also Configuration for the full environment-variable list.
How It Works
The inspector is stitched together from a few dev-only pieces, none of which run during generation:
- Source stamps. As each
.reetemplate is loaded, block and interactive tags are stamped withdata-ree="<file>:<line>"; rendered markdown blocks are stamped withdata-md="<file>:<line>".{_ }/{- }/{@ }lookups are wrapped in a marker span carrying their key. The browser resolves a click by walking up to the nearest stamped ancestor. - Browser client.
scripts/dev/inspector-client.jsis injected next to the live-reload client by the dev server's</body>rewrite. It draws the overlay, runs the edit dialogs, and talks to the server over the same/__reloadWebSocket. - Server handlers. Editor opens go to the
/__ree_openHTTP endpoint; translation and class edits ride the WebSocket. Every file path a request supplies is validated against the project root before any read or write, so a click can only ever touch a file inside the project.
Because all of this lives in scripts/dev/ and the dev-only template engine, bun ssg generation carries no stamps, no client script, and no endpoints.