Skip to content

[diffs] CodeView: Ctrl+F#833

Draft
amadeus wants to merge 77 commits into
beta-1.3from
amadeus/codeview-find
Draft

[diffs] CodeView: Ctrl+F#833
amadeus wants to merge 77 commits into
beta-1.3from
amadeus/codeview-find

Conversation

@amadeus

@amadeus amadeus commented Jun 18, 2026

Copy link
Copy Markdown
Member

A WIP vibe coded ctrl+f for CodeView.

@vercel

vercel Bot commented Jun 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pierre-docs-diffs Ready Ready Preview Jun 24, 2026 9:59pm
pierre-docs-diffshub Ready Ready Preview Jun 24, 2026 9:59pm
pierre-docs-trees Ready Ready Preview Jun 24, 2026 9:59pm
pierrejs-diff-demo Ready Ready Preview Jun 24, 2026 9:59pm

Request Review

amadeus and others added 6 commits June 24, 2026 13:30
* Remove history coalesce

* Fix selction/crate not updated when do "redo" command

* Remove visualColumns.ts

* Move editor ts files

* Refactor textarea buffer

* Rename `EditSnippet` type to `TextareaSnapshot`

* Remove `Editor` component, introduce the `Editor` class for `File` component

* Update demo

* Update editor constants to set text and background color to transparent

* Rewrite rerender logic

* Format

* Remove dead code

* Fix caret postion on empty line

* Improve `renderSelectionRange` performance by using cached DOM elements

* Support range selection in textarea

* Improve rerender performance

* Use piece table data sturcture for the text document

* refactor

* Add public `setSelection` method for the `Editor` class

* Add `FileContentsWithLineOffsets` interface and update related components to support line offsets and line count. Refactor file handling to utilize computed line offsets for rendering and iteration.

* Add `updateRenderCacheAt` method to `FileRenderer` and `File` classes for improved rendering. Refactor theme handling in `Editor` to utilize a dedicated method for color map retrieval.

* Refactor file iteration logic by removing `iterateOverFile` utility and replacing it with direct loops in `VirtualizedFile` and `FileRenderer` components. Update line offset computation to exclude trailing newlines in multi-line files while maintaining correct line counts. Enhance tests to validate line counting behavior.

* Remove EOF field

* Remove text length fields from HistoryEntry and related test cases in EditHistory

* Rename class `EditHistory` to `EditStack`

* Refactor EditStack and PieceTable to use a unified text slice interface.

* Refactor PieceTable and TextDocument to improve line offset handling and remove unnecessary EOL trimming logic.

* Refactor `Editor` to utilize new dirty line resolution logic, enhancing performance and accuracy in line tracking.

* Fix multi-cursor textarea sync

* Refactor Editor rendering logic for improved performance and reduce direct DOM manipulation.

* Add grammer cache

* Enhance line position caching in Editor for improved performance and accuracy.

* Refactor indentation handling in Editor and remove unused utility function for improved clarity and performance.

* Fix testing types

* Improve performance of the `getCharacterX` method

* Improve caching mechanism for enhanced performance.

* Add maxEntries feature to EditStack for managing undo history size

* Refactor

* Enhance PieceTable and TextDocument to trim line endings in getLineText method, improving text handling consistency. Update related tests for accuracy.

* Refactor

* Add `BackgroundTokenzier` class

* Improve performance

* Fix hightlight bug

* Add `--diffs-bg-caret` css property

* Fix input

* Fix selection range rendering

* Fix prebuildStateStackCache funciton

* Update `TOKENIZE_MAX_LINE_LENGTH` to 10,000

* Add `DiffsEditor` interface

* Fix `lineAnnotations` argument on `triggerEdit` invoke

* Refactor editor edit method to accept onChange callback directly and update demo to log file changes

* Clean up

* typo

* Refactor BackgroundTokenizer to use message-based scheduling.

* Refactor editor focus handling by removing redundant event listeners and updating CSS selectors for caret visibility.

* Refactor

* Fix `toTextareaSelectionDirection` function

* Refactor

* Update `DiffsEditor` types

* Add line annotation handling

* Add documentation for `hasVisibleLineAnnotation` function.

* Get rid of enum

* Clean up

* Refactor

* Update editor CSS

* Support text wrap

* Clean up

* Fix line y/wrap cache

* Fix line cache

* Copies leading indentation onto the new line after Enter

* Focus textare after undo/redo

* Move multi-selection functions to editorSelection module

* Add support for handling leading indentation deletion in applyTextChangeToSelections

* Fix selection glitch bug

* Add extendSelection command

* Fix `focusTextare` function

* Fix `resolveTextareaChange` function

* Remove unnecessary target check in mouseup event listener in Editor class

* Fix textarea selction direction

* Fix selection bg color for safair

* Clean up

* Fix shift select

* Refactor

* Refactor

* Fix shift select delay

* Coalesce edit stack entries for simple typing or backspace operations.

* Add Support forward-delete coalescing for edit history

* docs: add docs for editStack module

* Refctor

* Fix 'documentStart' and 'documentEnd' commands

* Rewrite selection handle logic

* Fix shouldCoalesceEditStackEntry function

* Update demo

* Add `removeEditor` for File component

* Add react api

* Clean up

* Update demo app

* Refactor useFileInstance to remove redundant editor cleanup logic

* Fix `computeLineOffsets` function

* typo

* Update editor style

* Fix `getOrCreateLineOffSets` method

* Refactor line count and annotation handling in File component; remove hasVisibleLineAnnotation utility

* Fix lines deletion crocss virtul viewport

* Remove `normalizeSelectionsForDocument` function

* Fix `edit` function

* Add editor sub-module

* Use `contenteditable` model

* Fix line wrap

* Fix wrap line

* Fix selection on mobile

* Update editor style

* Fix resize handling

* Add editor overlay layer

* Cleanup

* Add `DiffsEditableComponent` types

* Fix `VirtualizedFile` component

* Update `DiffsEditableComponent` type

* Add editor demo

* Fix slection rendering

* Update editor demo app

* Fix VirualizedFile component

* Update editor demo app

* Fix some selection bugs

* Update demo app

* Refactor findNextNonOverlappingSubstring method into PieceTable and TextDocument

* Refactor

* feat: Implement line jump

* Fix selection rendering when scrolling

* Improve tokenzier performance

* feat: simple search pannel

* Update editor demo app

* Fix jump

* Update search UI

* Add lag radar

* Fix virtualizer

* Fix render range after typing

* Fix editor tokenzier cache

* Fix search input focus

* Update log rader position

* Improve piece table performance

* Refactor

* Add lag radar

* Fix line count for empty documents

* Fix offscreen lines flush

* Introduce gutter width tracking

* refactor

* Refactor

* fix import

* Add 'expandSelectionDocStart' and 'expandSelectionDocEnd' commands

* Fix buffer height

* Add matches text for the search pancel

* Disable preious/next icon when no matches

* Update style.css

* feat: Support `quiteEdit` action

* Update edtior demo app

* Refactor

* Update demo app

* Update demo app

* Fix girdRow when render quick edit UI

* Move testing files

* Clean up

* Add searchPanel.ts

* Fix expandCollapsedSelectionToWord to match when the cursor is immediately touching one of the word's boundaries

* clean up

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Fix typo

* Clean up searchPanel and quickEdit when swith file

* Rebase to beta-1.2

* Fix selection after clean up quick edit widget

* Fix virtual buffer

* Fix `updateWindowSelection` method of Editor class

* Fix render range when typing new line at the end of the file

* Fix buffer when adding large lines

* [editor] Support 'deleteHardLineForward' input

* Add `insertTranspose` input

* Move `change` handler to options

* Update css

* Merge beta-1.2 changes

* Fix emply line rendering

* Add search settings UI

* Merge branch 'main' into editor

* Support FileDiff component

* Update `DiffsEditableComponent` interface

* Fix `getSelectionAnchor` function

* Fix text measurement for emoji

* Increase delay for diff rendering in FileDiff component

* Update types

* Add unit testings for text measue functions

* Clean up dirty render cache

* Fix `lineAnnotations` re-rendering

* Disable gutter utility when editing

* Add global css

* Fix scrollToLine method

* Refactor selection handling in Editor class to initialize selections properly and streamline rendering logic

* Fix diffs components

* Allow to create selection from gutter interaction

* Fix focus

* Fix browser compatibility

* Support dual themes

* Fix selection bugs

* refactor

* Add `Metrics` class

* Clean up

* Fix wrap selection rendering on safari

* Add `QuickEditContext` types

* Fix caret scroll margin when search panel is on

* Refactor search panel widget

* Fix selection position

* Update react components

* Update search panel CSS

* Fix quick edit

* Add editor docs

* Fix react hooks for editor

* Update editor demo component

* Update Quick Edit docs

* Update `diffStyle` and `expandUnchanged` options when editing

* demo: remove editor route

* Update docs

* Update docs

* Update examples

* Reset selection when 'Esc' key pressed

* Fix selection focus

* Add 'enable edit' shortcut('e')

* Handle the arrow key events to scroll to the cursor position manually

* Merge of overlapping selections

* Handle cursor moving events

* Fix scroll margin top

* Add debug logging option to Editor class

* Fix selection bugs

* Fix selection renering for unified `FileDiff`

* Reset ignore selection change flag on mouse up event

* Clean up

* fix bun.lock

* Add editor theme style

* Refactor

* Fix react types

* Fix last line index calculation

* Update condition for marking DOM dirty in VirtualizedFile component

* Throw if someone is trying to edit with no editor instance

* Update `mergeFileDiffOptions` function

* `lineOffsets` -> `lines`

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
ije and others added 15 commits June 24, 2026 13:51
…d for the `DiffsEditor` (#766)

* feat(editor): add pause and resume functionality for background tokenization

* pref(editor): Introduce `postponeBackgroundTokenizeToNextFrame` method for the `DiffsEditor`

* fix

* Add debug option for the tokenzier

* Update types

* Refactor

* typo

* Reduce requestAnimationFrame calls
* chore: empty commit for beta branch

* Homepage FileDiff editor demo

* Style kbd elements, add beta badge to docs content, redo table for keyboards, few edits

* docs(editor): add MultiFileDiff React example

Document editing with MultiFileDiff alongside File and FileDiff in the React integration tabs.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(editor): use parseDiffFromFile in FileDiff React example

Align the editor FileDiff tab with the pre-parsed fileDiff prop API.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Update editor react examples

* docs(editor): document worker pool usage with useTokenTransformer

Add a Worker Pool section with tabbed vanilla/React examples, and enable
useTokenTransformer on the docs site worker pool so editing works off-thread.

Co-authored-by: Cursor <cursoragent@cursor.com>

* format

* Remove toolbar, put reset into header metadata

* add link

* little copy editing

* Update homepage example to include file and diff

* redo reset

---------

Co-authored-by: Amadeus Demarzi <amadeusdemarzi@gmail.com>
Co-authored-by: Je Xia <i@jex.me>
Co-authored-by: Cursor <cursoragent@cursor.com>
* Rounded selection boundaries
* Search panel refactor/redesign
* Introduce `postponeBackgroundTokenizeToNextFrame` method for the `DiffsEditor`
Removing BETA badge from Virtualization
* [diffs/editor] refactor editor API

* Refactor

* refactor

* fix

* Refactor

* refactor

* refactor

* Add blur method to Editor class for improved focus management

* Update docs

* Refactor
* Includes editor code refactor
* Find/Replace functionality
necolas and others added 18 commits June 24, 2026 13:52
The editor renders its active-line highlight by calling the
component setSelectedLines, which commits a line-selection range
and fires the host onLineSelected callback. A caret or text
selection in the editor is not a gutter line selection, so an
editable File/FileDiff with a line-selection handler received a
bogus notification on every selection update.

Pass notify: false from the editor so the highlight renders
without committing a line selection, and widen the component
setSelectedLines type to accept the option.

Add a regression test asserting onLineSelected stays silent for a
multi-line editor selection.
Steps to reproduce:
1. Open edit mode with the selection action enabled.
2. Drag to select several characters inside a word.
3. Click the gutter lightning-bolt icon, then the wrap button.
4. Only the first (or last) character is wrapped, not the
   whole selection.

The gutter icon is a cached DOM element reused across renders for
the same line, but its click handler closed over the selection
captured when the icon was first created. During a drag that is
the first single-character selection (the first letter for a
forward drag, the last for a backward drag), so the action ran
against a stale range instead of the user's final selection.

Read the current primary selection at click time so the action
always operates on what the user has selected. Add a regression
test covering the forward and backward drag cases.
Repro (diffs /playground, edit mode):
1. Select all text in the editor.
2. Press delete/backspace.

Before this fix the editor breaks: split mode collapses to a
single uneditable view and undo does nothing; unified mode keeps
the view but you can no longer type.

The editor's text document always keeps one (empty) line, but
splitFileContents('') returns [], so emptying the editable side
recomputed the diff with zero addition lines. The additions
column then rendered no line elements, leaving the attached
editor with nothing to host its caret.

Now an emptied document is represented as one empty editable
line: diff the unchanged deletions against a single empty line
and store the addition as [''] so it still joins back to the
editor's empty document. Covered by model- and DOM-level
regression tests in both split and unified modes.
When the editor is emptied, the recompute diffs the deletions
against a single empty line to place one editable row. If the
old side was itself a single blank line, that diff was a no-op
(zero hunks), so iterateOverDiff emitted nothing and the row was
still missing.

Pick a sentinel that always differs from the deletion side so a
hunk is produced; its text is discarded by the [''] override.
Covered by the empty-document regression tests.
Turn on word wrap in an editable diff and type in a line until it
grows long enough to wrap onto another row. Now click a line below
it, or select text there: the caret and the selection highlight land
a row too high, sitting on the wrapped line's extra row instead of
the line you clicked.

The editor caches each line's vertical position and only refreshed it
when the number of lines changed. Wrapping a line adds a visual row
without changing the line count, so the cached positions of the lines
below it stayed stale, and the caret and selection drawn from them
rendered a row too high. Refresh the cached positions after an edit
whenever wrap is on, matching how the wrap offsets are already
invalidated.

This corrects only the vertical position of overlays on the lines
below a wrap. Selecting a word on the wrapped line itself still
mispaints the highlight horizontally; that is a separate bug.
Steps to reproduce:
1. Scroll a long file so the visible lines sit inside an unclosed
   block comment or template literal.
2. Delete the line directly above the viewport.
3. The visible lines lose their comment color and render as if they
   were plain code.

The tokenizer read the loop's grammar state before the offscreen
flush rebuilt the cached state stack up to the viewport's first line,
so it captured INITIAL. The visible lines were then tokenized as if
outside the construct — corrected by a later background pass, or never
when the viewport reaches the end of the document and no background
pass is scheduled.

Seed the loop state after the offscreen flush instead. This reuses the
state the flush already computed, so it adds no tokenization work and
only changes the delete-reaches-viewport case; far-above edits with a
gap stay seeded from INITIAL and are corrected by the background pass
as before.
Paste inserted clipboard text verbatim, so a Windows clipboard
(CRLF or CR) left mixed line endings in a file that otherwise uses
one style, showing up as spurious diff noise. Rewrite clipboard line
breaks to the document's detected EOL before inserting, matching what
copy already does.

Expose the line ending as TextDocument.eol with a normalizeEol()
helper and move the generic endsWithLineBreak() predicate into editor
utils, so this logic no longer sits among the selection helpers.
Repro:
1. Open the editor on a large file, or make many scattered edits so the
   document accumulates many internal fragments.
2. Keep typing; each keystroke gets progressively slower.

Every edit (typing, deleting, applyEdits) rebuilt the editor's whole
text structure from scratch, so a single edit cost time proportional to
how fragmented the document had become; in a heavily edited file each
keystroke paid for the entire document.

That structure (a piece table) keeps text as a list of "pieces" indexed
by a tree. The tree is now a treap: a binary search tree that also
keeps each node's random priority in heap order, so it stays balanced
without an explicit rebalancing pass. An edit now splits the tree at
the edit, drops the removed part, and merges the new text in, touching
one root-to-leaf path (O(log P)) instead of rebuilding all P pieces.
Reads and editor behavior are unchanged.
Steps to reproduce:
1. Scroll a long file so the editor virtualizes (only a window of
   lines is rendered).
2. With a caret near the window bottom, insert lines there - press
   Enter on the last rendered line, or paste a few lines.
3. The just-typed line, and its caret, are missing from the window
   until the next scroll.

#applyChange widened the render range only when the caret was exactly
at the window's end, and never persisted the widened range, so a
following edit read a stale renderRangeEndLine, treated the caret as
past the window, and #rerender (clamped to the range) never built the
new row; #isLineVisible/#renderCaret read the stale range and dropped
the caret too. (Only edits that carry a caret reach this path - it is
guarded on `selections` - so a bare programmatic applyEdits with no
active selection is unaffected; the real triggers are typing and paste
at the window bottom.)

Widen the range to cover the caret line for inserts that reach the
window's bottom edge and persist it to #renderRange so consecutive
edits stay accurate and the caret draws.

Bound that widening two ways so it can't defeat virtualization or
render a gap. Cap it at twice the bounded window the virtualizer last
synced (captured in __syncRenderView as #viewportWindowLines): a large
insert at the caret - most often a big multi-line paste - can drop the
caret far below the window, and widening to reach it would build a row
per inserted line synchronously, risking a freeze. And skip widening
when the edit starts below the window, since #rerender only builds rows
from change.startLine - widening there would leave the intervening rows
unbuilt while reporting them visible, mispositioning the new rows. Past
either limit, keep the bounded window and only recompute the buffer,
leaving the far region for the scroll that follows a focused edit (or
the next user scroll) to render. Capturing the window at sync time also
stops consecutive edits from ratcheting the cap up.

Add editorVirtualizedEdit.test.ts: consecutive newlines and a small
multi-line insert at the window bottom render; a 1000-line insert, a
run of consecutive inserts, and an edit starting below the window all
keep the rendered window bounded and contiguous.
Open a split diff with word wrap on and edit a line so it wraps onto
several rows. Double-click a word on a wrapped row: the word is
selected and the caret lands correctly, but the highlight is drawn at
the row's left edge instead of over the word. Selecting whole lines
across a multi-line range shows the same shift.

In a split diff the editable panel sits to the right of the deletion
panel. The caret math adds this horizontal panel offset, but the
selection-highlight math did not, so every highlight was pulled left
by the offset (its width stayed correct). Add the same content offset
when computing the highlight's left edge, for both wrapped rows and
selections that start at the beginning of a line.
In a split diff with word wrap on, edit a line so it wraps, then turn
word wrap off. Select a whole line from its start: the highlight either
vanishes or jumps far to the right of the text.

#contentOffset caches the split panel's horizontal shift but is only
set in a split + wrap diff and never cleared, so toggling wrap off
leaves a stale value on the same editor. The selection start added that
stale offset while the end (from #getCharX) did not, giving a negative
width. Read the offset through a getter that returns it only while the
live layout still applies, so caret, selection, and line-Y math all
ignore a stale value.
* Phase 1: Setup the new slot architecture

* Phase 2: Vanilla API shenanery

* Phase 3: React implementation

* Phase 4: Add CodeView support

* Add demo-ability

* Phase 5: Adding misc tests

* Phase 6: Adding docs

Both for the new API and also some forgotten docs for header prefix
[diffs/editor] Updated `editor.css` for marker popups to improve layout and responsiveness
Steps to reproduce:
1. Open a diff in edit mode and edit a line (e.g. rename a symbol).
2. Toggle any display option - word wrap, theme, diff style, line
   numbers.
3. The edit vanishes and the line shows its original text again; it
   only reappears on the next keystroke.

When an editor is attached, its document is the source of truth for the
content, but the host passes a static fileDiff. A display-option change
forces a full re-render that rebuilds the diff rows from that fileDiff,
so the in-progress edits are painted over with the original content -
and inserted or deleted lines are lost.

After a full re-render, when the editor's document survived it and the
rendered rows no longer match it, re-render the diff from the document:
rerenderFromDocument re-derives the diff and clears the render cache so
the rebuild re-highlights from the edited contents. One pass restores
text, syntax colors, and line count, with no per-row reconciliation.
The diff is mutated in place so its cacheKey is kept and the editor's
document and undo history survive.

Gated to full re-renders (a scroll's partial render reuses the existing
rows) and to components with a document-backed re-render (FileDiff; the
plain File has no such path yet). Host-agnostic: this holds whether or
not the host sets a cacheKey. Adds a regression test covering the
toggle, further typing, an inserted line via both render APIs, and
downstream block-comment highlighting - all with no cacheKey.

Co-authored-by: Je Xia <i@jex.me>
* chore: empty commit for beta branch

* Prevent transparent borders

* Fix the editing example

* fix cursor jumping in history demo

* wip

* updates

* button fixes, history improvement

* refactor(diffs): show the selection action in a floating popover

Selecting text in an editable surface used to surface a lightning icon
in the gutter; clicking it inserted the consumer's action element as a
new inline row, which reflowed the document and took two interactions.

Replace that with a floating popover that appears automatically once a
ranged selection settles, anchored just below the selection's head and
mounted in the overlay layer so it never reflows the content. The
`renderSelectionAction` API is unchanged, and its handlers now read the
live primary selection so keyboard-extending a selection keeps acting on
the current range. Drop the now-unused gutter icon, its `quick` sprite,
and the inline-row styling.

* docs(docs): rebuild the selection action demo as "Add to chat"

The edit page's selection demo wrapped the selection in t() or shouted
it in caps from a toolbar. Rebuild it around the new popover: selecting
code pops an indigo "Add to chat" action (plus a secondary copy) that
sends the snippet to a mock chat panel beside the editor, complete with
an inert composer mirroring the homepage agent UI.

Snippets render with a read-only File. The page's shared worker pool is
wired for the editable surface and doesn't highlight a dynamically
mounted read-only File, so opt these out with disableWorkerPool and
highlight on the main thread. The panel is height-matched to the editor
so the list scrolls while the header and composer stay put.

* docs(docs): describe the selection action popover

Update the editor docs and copy to match the new behavior: the action
appears in a floating popover anchored to the selection instead of a
gutter icon you click, and it can hold any number of actions. Refresh
the edit-page blurb, the Editor guide prose, and the option comment, and
drop the selection action from the standalone EditorDemo so it stays
focused on plain editing.

* chore(demo): match the renamed selection action popover hook

The selection action now renders as a popover, so target the new
[data-selection-action-popover] selector and drop the inline margin that
spaced the old inline action row.

* test(diffs): cover selection action popover lifecycle

Add cases for the popover's auto show/teardown: it disappears once the
selection collapses, and it never renders (nor calls the consumer's
callback) when enabledSelectionAction is left off.

* feature updates

* New replace and replaceall icons

* better search shortcuts

* Update keyboard shorcuts table

* refactor search, fix some nits, redo icons in search, combine marker severity styles

* remove labels

* use claude to restore the delta between X's and my changes, then reapply to markers and popovers

* improve focus, rearrange

* localize the padding/margin for now

---------

Co-authored-by: Amadeus Demarzi <amadeusdemarzi@gmail.com>
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.

4 participants