Skip to content

Editor#601

Merged
amadeus merged 261 commits into
pierrecomputer:beta-1.3from
ije:editor
May 29, 2026
Merged

Editor#601
amadeus merged 261 commits into
pierrecomputer:beta-1.3from
ije:editor

Conversation

@ije
Copy link
Copy Markdown
Collaborator

@ije ije commented Apr 23, 2026

Architecture

The editor is a plugable system for the File component. It provides:

  • Text Editing
  • Selection management
  • History (undo, redo)
  • Search
  • Quick Edit
  • Automatic Indentation

The editor adds the contenteditable property to the code line element, that supports native web selection and editing features on mobile phone.

const contentEl = fileContainer.shadowRoot.querySelector("[data-content]")
contentEl.contentEditable = true
contentEl.addEventListener("beforeInput", handleInput)

Selections are created via the native web selection API, multiple cursors has been supported.

document.addEventListener('selectionchange', () => {
  const selectionRaw = document.getSelection();
  const composedRanges = selectionRaw?.getComposedRanges({
    shadowRoots: [fileContainerShadowRoot],
  });
  const selection = convertSelection(composedRanges);
  if (selection) {
    // Text in the editor has been selected.
    // You can now get the `start.line`, `start.column`, `end.line` and `end.column` from the selection
  }
})

API

Vanilla JS:

import { VirtualizedFile } from "@pierre/diffs"
import { Editor } from "@pierre/diffs/editor"

// render a file
const fileInstance = new VirtualizedFile({ ... });
fileInstance.render({ ... });

// make the file editable
const editor = new Editor({
  onChange: (file, lineAnnotations) => console.log("change", file.name, file.contents)
})
const dispose  = editor.edit(fileInstance)
dispose() // or call `editor.cleanUp()`

lazy import (recommended):

import { VirtualizedFile } from "@pierre/diffs"

// render a file
const fileInstance = new VirtualizedFile({ ... });
fileInstance.render({ ... });

const edit = async (file: File) => {
  const { Editor } = await import("@pierre/diffs/editor")
  const editor = new Editor({
    onChange: (file, lineAnnotations) => console.log("change", file.name, file.contents)
  })
  return editor.edit(file)
}

// click to edit (lazy importing the editor.js)
button.addEventListener("click", () => void edit(fileInstance))

React:

import { Editor } from "@pierre/diffs"
import {
  type FileContents,
  File,
  EditorProvider,
} from '@pierre/diffs/react';

const file: FileContents = {
  name: 'example.ts',
  contents: `function greet(name: string) {
  console.log(\`Hello, \${name}!\`);
}

export { greet };`,
};

const editor = new Editor({
  onChange: (file, lineAnnotations) => console.log("change", file.name, file.contents)
})

export function EditCodeFile() {
  const [editable, setEditable] = useState(true)
  return (
    <EditorProvider editor={editor}>
      <File
        file={file}
        options={{
          theme: { dark: 'pierre-dark', light: 'pierre-light' },
        }}
        editable={editable}
      />
    </EditorProvider>
  );
}

TODOS

  • Basic editing
  • Selection & cursor
  • Multiple cursors (Pressing ⌘)
  • History(undo/redo)
  • Search
  • Quick Edit
  • Automatic indentation
  • Shortcuts
    • 'selectAll' ⌘ + A
    • 'copy' ⌘ + C
    • 'cut' ⌘ + X
    • 'paste' ⌘ + V/Y
    • 'findNextMatch' ⌘ + D
    • 'indent' Tab
    • 'outdent' Shift + Tab
    • 'moveCursorToDocStart' ⌘ + ↑
    • 'moveCursorToDocEnd' ⌘ + ↓
    • 'expandSelectionDocStart' ⌘ + Shift + ↑
    • 'expandSelectionDocEnd' ⌘ + Shift + ↓
    • 'undo' ⌘ + Z
    • 'redo' ⌘ + Shift + Z
  • Browser compatibility
    • Chrome
    • Firefox
    • Safari
    • Android Chrome
    • iOS Safari (has input bug)
  • LSP next version

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 23, 2026

@ije is attempting to deploy a commit to the Pierre Computer Company Team on Vercel.

A member of the Team first needs to authorize it.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 23, 2026

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

Project Deployment Actions Updated (UTC)
pierre-docs-diffshub Ready Ready Preview May 29, 2026 7:39pm
pierre-docs-trees Ready Ready Preview May 29, 2026 7:39pm
pierrejs-diff-demo Ready Ready Preview May 29, 2026 7:39pm
pierrejs-docs Ready Ready Preview May 29, 2026 7:39pm

Request Review

ije added 8 commits April 28, 2026 11:21
…ents to support line offsets and line count. Refactor file handling to utilize computed line offsets for rendering and iteration.
… for improved rendering. Refactor theme handling in `Editor` to utilize a dedicated method for color map retrieval.
…nd 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.
Comment thread packages/diffs/src/types.ts Outdated
totalLines?: number;
// Pre-split lines for caching in windowing scenarios
lines?: string[];
lineOffsets?: number[];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah i think this is slower to initialize and read, but can reduce memory usage. this aligns to the text buffer. however i can change it back it string[].

Comment thread packages/diffs/src/react/FileDiff.tsx
Comment thread packages/diffs/src/components/FileDiff.ts
Comment thread packages/diffs/src/components/VirtualizedFile.ts Outdated
Comment thread packages/diffs/src/components/VirtualizedFile.ts
Comment thread packages/diffs/src/components/VirtualizedFile.ts
options: RenderFileOptions;
result: ThemedFileResult | undefined;
renderRange: RenderRange | undefined;
isDirty?: boolean;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain a bit about the architecture for isDirty? What does it serve, how is it used?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when editing a file, the editor will update the code of the render cache. mark it as dirty to purge the render cache in workerpool when call cleanUp. this avoid file.render({...}) gets polluted code lines.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps just leave a quick comment on it there for future context?

Copy link
Copy Markdown
Member

@amadeus amadeus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I've mostly just looked over the changes to existing diffs library. I do have a couple concerns about some potential behavior changes, but there also might be some prior context I am not aware of on how things have changed.

Also chatted a bit with the team, what I will do here shortly is create a new specialized branch for the beta/alpha of editor, and then you can target that branch with this PR, and we can cut beta releases from that

let lineIndex = startingLineIndex;
lineIndex < lineCount;
lineIndex++
) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come this iteration change?

Copy link
Copy Markdown
Collaborator Author

@ije ije May 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, related to lines to lineOffsets change. maybe i should not touch this part lol we don't use iterateOverFile since the editor could change the line count of the file

@@ -1,6 +1,8 @@
import { DEFAULT_VIRTUAL_FILE_METRICS } from '../constants';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So there's a bunch of changes to VirtualizedFile, but no changes to VirtualizedFileDiff. Is that just work that still needs to happen? or was there something that needed to change about VirtualizedFile that wasn't applicable to VirtualizedFileDiff?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when editing a file instance, add/delete lines will cause the scroll height change. for file diff instance we just call rerender

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if i fully understand, couldn't diffs also change scroll height too?

Comment thread packages/diffs/src/components/VirtualizedFile.ts
Comment thread packages/diffs/src/editor/css.ts
*/
export function computeLineOffsets(contents: string): number[] {
const offsets: number[] = [0];
for (let i = 0; i < contents.length; i++) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit worried about the performance of this, iterating over every character vs before when we were just doing splits on lines

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, lineOffsets is slower than lines: []string on init/read. this is come from the pieceTable. for large file, store lineOffsets can reduce memory. I can revert the change on File component, but the pieceTable still needs this funtion.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted to lines

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, not necessarily opposed to this stuff, just trying to be diligent about tradeoffs. It's probably fine to get something merged for beta, and I/we can conduct some tests with stuff to really understand impacts.

Comment thread packages/diffs/src/react/types.ts Outdated
}

export function useEditor<LAnnotation>(): Editor<LAnnotation> | undefined {
return useContext(EditorContext) as Editor<LAnnotation> | undefined;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way we can fix the types here so we don't need to use as?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because useContext(EditorContext) returns Editor<unknown>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah i get that, but i'm wondering if we can fix the context itself to take a type we can pass through is all

Comment thread packages/diffs/src/react/utils/useFileInstance.ts Outdated
Comment thread packages/diffs/src/react/utils/useFileDiffInstance.ts
Comment thread packages/diffs/src/react/utils/useFileDiffInstance.ts Outdated
@amadeus
Copy link
Copy Markdown
Member

amadeus commented May 29, 2026

Created the new target branch for this stuff:

#749

beta-1.3

@ije ije changed the base branch from main to beta-1.3 May 29, 2026 17:42
@ije
Copy link
Copy Markdown
Collaborator Author

ije commented May 29, 2026

@amadeus lineOffsets has been change back to lines, but the iterateOverFile.ts file is not reverted, it can be replaced with for loop safely. we do not use it now since the editor could change the line count of the file.

can you please take a look again thanks 💚

@ije ije changed the base branch from beta-1.3 to main May 29, 2026 18:30
@amadeus amadeus changed the base branch from main to beta-1.3 May 29, 2026 21:22
@amadeus amadeus merged commit e6183ab into pierrecomputer:beta-1.3 May 29, 2026
12 checks passed
amadeus pushed a commit that referenced this pull request Jun 2, 2026
* 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>
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.

3 participants