redlining

Guide

The overlay lives in a shadow root on top of your page. While it is inactive it registers only the hotkey and captures no pointer events; your app behaves exactly as before.

Toolbar and keys

Key Action
Alt+R Toggle the overlay (hotkey option). Matched on the physical key, so it works on macOS where Option+R types ®.
S / D / M / T Select / Draw / Move / Tweak mode
[ / ], +scroll Walk the hovered selection up / down the ancestor chain
+click Add another element to the open note
Handles, drag, ⌥⇧ drag Tweak: resize, set padding, set margin on that edge
Arrows, arrows, drag Tweak: nudge by 1px / 10px / freely (exported as a visual nudge)
+Z Tweak: undo the last change
hover Tweak: measure the distance to another element
L Annotation list panel
Width select (375 / 768 / 1280) Open the page in a device frame of that width; annotations made inside sync into this session
Move button Cycle the toolbar and the pen button through the four corners; remembered per browser
Camera button Include a screenshot with pins when saving (on by default)
++C Copy the Markdown prompt to the clipboard
+ Save to .redlining/
Trash button Clear the session, from the list panel's header (asks first)
? In-package help: keys, the loop, links to these pages
Esc Close the popover, then the panel, then the overlay

On Windows and Linux Ctrl stands in for .

Select — change or remove something that exists

Hover to outline the smallest host element under the cursor. The badge shows the innermost component that rendered it, the file and line, and the tag. Often you mean the whole card rather than the <span> inside it: press [ until the outline covers what you mean. Click to drop a pin and open the note.

The note popover offers Change and Remove. Write what should be different; the anchor already says where. saves, + inserts a newline, Esc cancels.

Draw — add something new in a region

Press D, then drag a rectangle where the new thing belongs. On release Redlining resolves the container: the deepest decorated element whose box covers at least 60 % of what you drew, otherwise the nearest decorated ancestor of the centre point. The popover says Add inside FilterPanel and the export records a position such as after child 2 · full width · ≈ 220 px tall.

The component-hint chips (Table, Form, Button, Card, Modal, Nav, List, Chart) prepend a keyword to your note; free text remains the primary instruction. The box is stored relative to the container, so it survives scrolling and resizing.

Move — put something somewhere else

Press M. Click the element to move; it gets a dashed outline. Click the destination; the popover asks before, after or inside and takes a note. The pin sits on the source and a →N marker on the target. The export writes a From and a To line, so nobody has to describe DOM positions in prose.

Multi-select — one note, several elements

With a select note open, +click other elements to add them to the same annotation ("make all of these the same height"). The popover shows +N, every element gets a pin with the same number, and the export lists the anchors under one note.

Tweak — change values directly, export the numbers

Press T and click an element. The page updates live while you work; nothing is written to your code. When you press Done, the annotation records every change as before → after in computed pixels, next to the element's class list, so the agent can translate "16px" into text-base or a token rather than an inline style.

Previews survive a reload and hot reload; deleting the annotation or clearing the session restores the element. The note is optional for a tweak, the numbers usually say enough.

List panel

L opens a panel on the right with every annotation: number, action, component and file, and the note clamped to two lines. The chevron expands a row to the full anchor (file and line, owner chain, usage site, element text), the target of a move or the box of an add, and the complete note. Click the note to edit it inline ( saves, Esc cancels); the × deletes and renumbers the rest. The toolbar moves aside while the panel is open. Sessions are kept per route; when other routes in this browser have annotations, the panel lists them at the bottom and Save and Copy include them, each under its own # Redlining — /route heading, so one /redline run covers the whole pass. The × next to a route clears it.

Settings

The gear in the toolbar opens the settings. Styling shows which idiom was detected from the page (Tailwind 4 from its theme tokens, Tailwind 3 from the preflight variables, CSS Modules from hashed class names, otherwise plain CSS) and lets you override it; the framework prop sets it in code. It decides how tweak values map to classes: under Tailwind, padding-left 12 → 24 exports as add class pl-6 even if pl-6 is not used anywhere yet, off-scale values become pl-[13px], colours use their --color-* token (bg-brand-500), and the steppers snap to the theme scale. The export header states the idiom and the footer tells the agent how to apply it. Snap to scale sets the Inspector's default, and Include other routes controls the multi-route export.

Copy and Save

Copy prompt puts the Markdown spec on the clipboard, for pasting into any agent. Save posts the session to the endpoint, which writes annotations.md, annotations.json and, with the camera on, screenshot.png with the pins burned in. Pins in the screenshot carry the same numbers as the Markdown headings, so "pin 3" is unambiguous. With the camera on, every annotation also gets crop-N.png, the element as it looks now with a small margin, so the agent sees exactly the thing being discussed.

Reference images. Paste or drop a mockup into the note (up to three per note; shrunk to 1600px on the long edge). It shows as a thumbnail in the popover and the panel, survives reloads with the session, and is saved as ref-N-i.jpg; the Markdown line Reference: .redlining/ref-3-1.jpg — match this tells the agent to look at it. "Copy prompt" cannot carry images, so it only counts them.

The screenshot is rendered in the browser and skipped with a toast if it would exceed 7 MB; the route accepts bodies up to 16 MB.

Verify — did the agent's edit land?

The /redline command ends by writing .redlining/reply.md, one line per annotation: ## 3 · done — text-sm → text-lg (components/toolbar.tsx:31) (or partial, skipped). When you open the overlay after that, it reads the reply through the route's GET, removes its previews for a moment, and checks every annotation against the page as it is now: each tweak's target values against the computed styles, edited text against the DOM, a hidden element against its display, a removed element against its absence. The list opens with a verdict per row — applied, differs · font-size is 15px, expected 16px, missing, or check by eye for notes and nudges — beside what the agent reported. Remove applied drops what landed and keeps the rest for a second round; the magnifier in the panel header runs the check again at any time. Routes created before 0.6.0 export only POST; add GET (npx redlining init says so) or the reply is simply not read.

Sessions

A session is all annotations for one route. It is stored in localStorage per route, so a reload or a /redline run does not lose it; pins re-attach to their elements after hot reload by the stamped file and line, then by CSS selector. Clear it with the trash button when the batch has landed.

Sessions are capped at 15 annotations, with a warning at 10. Larger batches produce worse results from the agent than two smaller ones.

How anchors resolve

Situation Anchor What the export says
The element carries data-rl exact file:line of the opening tag
Only an ancestor carries it ancestor the ancestor's file:line plus locate the child by selector
Nothing decorated (third-party UI, portals) selector-only unresolved — locate by selector and text

Anchors from a reusable component also carry their usage site: the nearest decorated ancestor in another file and the branch's position among its children, so "instance 2 of 3 in <section> · app/page.tsx:41" tells the second card apart from the first and third even though all three are written at card.tsx:33.

Components are never decorated, only host elements like <nav> and <button>: an unknown prop on your components would leak into prop spreads and warnings. Component names come from React's owner chain at runtime instead, which is why server-only subtrees show an empty owner list but still resolve exactly.