Enable a full-featured yet lightweight editor that lazy-loads when needed on top of any File or FileDiff. All the ergonomics and customization of @pierre/diffs, with everything you need to edit in place.
Editor mode (experimental) makes any code surface—File or FileDiff—editable in place. Toggle between a read-only Review and a live Edit, switch the surface between a file and a diff, and render the diff unified or side-by-side split. Start typing in the code below and it updates as you edit.
1234567891011121314151617181920212223242526272829303132export interface DebounceOptions { waitMs: number; trailing?: boolean;}
export function debounce<Args extends unknown[]>( fn: (...args: Args) => void, options: DebounceOptions,) { let timer: ReturnType<typeof setTimeout> | undefined;
const debounced = (...args: Args) => { if (timer != null) { clearTimeout(timer); }
timer = setTimeout(() => { timer = undefined; if (options.trailing !== false) { fn(...args); } }, options.waitMs); };
debounced.cancel = () => { clearTimeout(timer); timer = undefined; };
return debounced;}
Select any text to reveal a floating popover, anchored to the selection and rendered with renderSelectionAction(). Place any number of actions inside—here, an editor-style Add to chat sends the selected snippet to the panel on the right, while a secondary action copies it.
12345678910111213141516171819const greeting = 'Welcome back'const farewell = 'See you soon'const errorText = 'Something went wrong'
type Banner = { title: string; tone: 'info' | 'error' }
function renderBanner(name: string): Banner { const title = greeting + ', ' + name + '!' return { title, tone: 'info' }}
function renderError(): Banner { return { title: errorText, tone: 'error' }}
function renderFooter(year: number) { return farewell + ' · © ' + year}
Use editor.setMarkers() to inject inline context into your code for linter, formatting, and more. Includes support for severity-aware underlines and hover popups. Hover over markers (shown with wavy, colored underlines) in the example below.
123456789101112131415161718192021// TODO: validate items and taxRate before summingfunction calculateTotal(items, taxRate) { var total = 0 for (var i = 0; i < items.length; i++) { total += items[i].price }
let tax = total * taxRate console.log('subtotal', total)
if (total == 0) { return null }
return { subtotal: total, tax, grandTotal: total + tax, }}
Find strings across files with Cmd/Ctrl-F on any File or FileDiff. Find and replace with Cmd/Ctrl-Shift-F. The example below shows the search panel pre-filled—press Enter or use its arrows to jump between matches, and toggle case, whole-word, or regex as you go.
12345678910111213141516type User = { id: string; name: string; email: string;};
function formatUser(user: User) { const name = user.name.trim(); const email = user.email.toLowerCase(); return { id: user.id, name, email };}
export function getUsers(users: User[]) { return users.map(formatUser);}
Edits land on a structure-aware undo stack out of the box. Walk it with keyboard shortcuts and the toolbar below, or drive it in code with editor.undo(), editor.redo(), and editor.applyEdits(). The example loads with a short refactor already applied across several commits.
12345678910111213141516171819function calculateCart(items) { var total = 0 for (var i = 0; i < items.length; i++) { total = total + items[i].price * items[i].qty }
var discount = 0 if (total > 100) { discount = total * 0.1 }
var shipping = 5 if (total > 50) { shipping = 0 }
return total - discount + shipping}
Edit mode ships with all the additional shortcuts your users will need out of the box. Use the example File below to try the shortcuts you see in the table. Editing the example File will not update the table.
123456789101112131415161718192021222324252627282930// The data behind the table on the right—this very page maps over it.// Editing here won't rebuild the table, but go ahead: the surface is live.// `keys` are alternatives (joined by /); `modifiers` are held together.// `mod` adds the platform key: Cmd on macOS, Ctrl everywhere else.export const shortcuts = [ // Editing { keys: ['Tab'], action: 'Indent line or selection' }, { keys: ['Tab'], action: 'Outdent line or selection', modifiers: ['Shift'] }, { keys: ['X'], action: 'Cut', mod: true }, { keys: ['C'], action: 'Copy', mod: true }, { keys: ['V'], action: 'Paste', mod: true }, // Selection & cursor { keys: ['←', '→', '↑', '↓'], action: 'Move the cursor' }, { keys: ['←', '→', '↑', '↓'], action: 'Extend the selection', modifiers: ['Shift'] }, { keys: ['←', '→'], action: 'Jump to line start / end', mod: true }, { keys: ['Home', 'End'], action: 'Jump to document start / end', mod: true }, { keys: ['A'], action: 'Select all', mod: true }, { keys: ['Esc'], action: 'Collapse to a single cursor' }, // History { keys: ['Z'], action: 'Undo', mod: true }, { keys: ['Z'], action: 'Redo', modifiers: ['Shift'], mod: true }, // Find { keys: ['F'], action: 'Open the search panel', mod: true }, { keys: ['D'], action: 'Find next match of the selection', mod: true }, { keys: ['Enter'], action: 'Next match (in search panel)' }, { keys: ['Esc'], action: 'Close the search panel' }, // Multiple cursors { keys: ['Click'], action: 'Add a cursor at the click', mod: true },];
| Key | Action |
|---|---|
| Editing | |
| Tab | Indent line or selection |
| ShiftTab | Outdent line or selection |
| CmdX | Cut |
| CmdC | Copy |
| CmdV | Paste |
| Selection & cursor | |
| ←→↑↓ | Move the cursor |
| Shift←→↑↓ | Extend the selection |
| Cmd←→ | Jump to line start / end |
| CmdHomeEnd | Jump to document start / end |
| CmdA | Select all |
| Esc | Collapse to a single cursor |
| History | |
| CmdZ | Undo |
| CmdShiftZ | Redo |
| Find | |
| CmdF | Open the search panel |
| CmdD | Find next match of the selection |
| Enter | Next match (in search panel) |
| Esc | Close the search panel |
| Multiple cursors | |
| CmdClick | Add a cursor at the click |
The demos above cover the headline features. Here's the rest of what edit mode gives you for free.
File, FileDiff, MultiFileDiff, or PatchDiff; the new-file side of a diff re-tokenizes as you type.VirtualizedFile and VirtualizedFileDiff to edit massive files; off-screen lines render on demand.contentEditable with role="textbox"; autocorrect, spellcheck, and capitalization off.@pierre/diffs/editor entry point—import it only when editing begins.Collectively, our team brings over 150 years of expertise designing, building, and scaling the world's largest distributed systems at Cloudflare, Coinbase, Discord, GitHub, Reddit, Stripe, X, and others.