Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
135 commits
Select commit Hold shift + click to select a range
434ea2a
First implement
ije Apr 23, 2026
364e3b2
Fix paste
ije Apr 23, 2026
b915a8c
Fix character X
ije Apr 23, 2026
f05e492
Add 'indent' and 'outdent' commands
ije Apr 23, 2026
b1edaee
Add typing buffer
ije Apr 23, 2026
3779a55
Type alias migration from `ISelection`/`IEditorSelection` to `EditorS…
ije Apr 23, 2026
fefbac3
Fix indent command for multiline selection
ije Apr 23, 2026
1d081f5
Refactor
ije Apr 24, 2026
73623fb
Improve shiki theme/garmmar loading
ije Apr 25, 2026
fc88eb8
Add `minNumberColumnWidth` option
ije Apr 25, 2026
0113543
Refactor
ije Apr 25, 2026
79177f5
Remove history coalesce
ije Apr 25, 2026
4cb7b41
Fix selction/crate not updated when do "redo" command
ije Apr 25, 2026
57cfbe6
Remove visualColumns.ts
ije Apr 25, 2026
78ec876
Move editor ts files
ije Apr 25, 2026
a0934f8
Refactor textarea buffer
ije Apr 25, 2026
84d5df6
Rename `EditSnippet` type to `TextareaSnapshot`
ije Apr 25, 2026
996db61
Remove `Editor` component, introduce the `Editor` class for `File` co…
ije Apr 27, 2026
f95df57
Update demo
ije Apr 27, 2026
18f5422
Update editor constants to set text and background color to transparent
ije Apr 27, 2026
0194e41
Rewrite rerender logic
ije Apr 27, 2026
ef525f1
Format
ije Apr 27, 2026
dd85b08
Remove dead code
ije Apr 27, 2026
1ae5008
Fix caret postion on empty line
ije Apr 28, 2026
a8b41bc
Improve `renderSelectionRange` performance by using cached DOM elements
ije Apr 28, 2026
e647f74
Support range selection in textarea
ije Apr 28, 2026
d08abe3
Improve rerender performance
ije Apr 28, 2026
b9495e7
Use piece table data sturcture for the text document
ije Apr 29, 2026
6ea0708
refactor
ije Apr 29, 2026
e42e502
Add public `setSelection` method for the `Editor` class
ije Apr 29, 2026
9a3eb6a
Add `FileContentsWithLineOffsets` interface and update related compon…
ije Apr 29, 2026
3b4d3b4
Add `updateRenderCacheAt` method to `FileRenderer` and `File` classes…
ije Apr 29, 2026
fc6a47e
Refactor file iteration logic by removing `iterateOverFile` utility a…
ije Apr 29, 2026
a6ec728
Remove EOF field
ije Apr 30, 2026
44d631f
Remove text length fields from HistoryEntry and related test cases in…
ije Apr 30, 2026
59f379e
Rename class `EditHistory` to `EditStack`
ije Apr 30, 2026
0eceba2
Refactor EditStack and PieceTable to use a unified text slice interface.
ije Apr 30, 2026
43247d8
Refactor PieceTable and TextDocument to improve line offset handling …
ije May 1, 2026
1b90426
Refactor `Editor` to utilize new dirty line resolution logic, enhanci…
ije May 1, 2026
ec78938
Fix multi-cursor textarea sync
ije May 1, 2026
c56c600
Refactor Editor rendering logic for improved performance and reduce d…
ije May 1, 2026
2a74aaf
Add grammer cache
ije May 1, 2026
d456fa9
Enhance line position caching in Editor for improved performance and …
ije May 1, 2026
c446ad9
Refactor indentation handling in Editor and remove unused utility fun…
ije May 1, 2026
3e14a92
Fix testing types
ije May 1, 2026
d656e4a
Improve performance of the `getCharacterX` method
ije May 1, 2026
a8961f4
Improve caching mechanism for enhanced performance.
ije May 1, 2026
738e2b7
Add maxEntries feature to EditStack for managing undo history size
ije May 1, 2026
aaedff4
Refactor
ije May 1, 2026
fe150a0
Enhance PieceTable and TextDocument to trim line endings in getLineTe…
ije May 3, 2026
45b71a1
Refactor
ije May 3, 2026
a685b75
Add `BackgroundTokenzier` class
ije May 3, 2026
acbde05
Improve performance
ije May 4, 2026
209999f
Fix hightlight bug
ije May 4, 2026
2b8cb09
Add `--diffs-bg-caret` css property
ije May 4, 2026
6f3659a
Fix input
ije May 4, 2026
0686fac
Fix selection range rendering
ije May 4, 2026
cefee50
Fix prebuildStateStackCache funciton
ije May 4, 2026
af0e508
Update `TOKENIZE_MAX_LINE_LENGTH` to 10,000
ije May 4, 2026
d9da211
Add `DiffsEditor` interface
ije May 4, 2026
092486e
Fix `lineAnnotations` argument on `triggerEdit` invoke
ije May 4, 2026
4dcc108
Refactor editor edit method to accept onChange callback directly and …
ije May 4, 2026
a2cf5cc
Clean up
ije May 4, 2026
dbbc2b1
typo
ije May 4, 2026
a187ff8
Refactor BackgroundTokenizer to use message-based scheduling.
ije May 5, 2026
d69e59b
Refactor editor focus handling by removing redundant event listeners …
ije May 5, 2026
24f899a
Refactor
ije May 5, 2026
4634c3d
Fix `toTextareaSelectionDirection` function
ije May 5, 2026
229e21e
Refactor
ije May 5, 2026
febadf7
Update `DiffsEditor` types
ije May 5, 2026
1002da8
Add line annotation handling
ije May 5, 2026
a9b7106
Add documentation for `hasVisibleLineAnnotation` function.
ije May 5, 2026
264a4ed
Get rid of enum
ije May 6, 2026
5521967
Clean up
ije May 6, 2026
d83eaa6
Refactor
ije May 6, 2026
c69028b
Update editor CSS
ije May 6, 2026
4b26cba
Support text wrap
ije May 7, 2026
7fc5f61
Clean up
ije May 7, 2026
c3a1fa8
Fix line y/wrap cache
ije May 7, 2026
e567b0b
Fix line cache
ije May 7, 2026
dd12a8a
Copies leading indentation onto the new line after Enter
ije May 7, 2026
415b6df
Focus textare after undo/redo
ije May 7, 2026
c5ad096
Move multi-selection functions to editorSelection module
ije May 8, 2026
8a387b3
Add support for handling leading indentation deletion in applyTextCha…
ije May 8, 2026
47e57d4
Fix selection glitch bug
ije May 8, 2026
2609f22
Add extendSelection command
ije May 8, 2026
23dc842
Fix `focusTextare` function
ije May 8, 2026
18487ed
Fix `resolveTextareaChange` function
ije May 8, 2026
754958f
Remove unnecessary target check in mouseup event listener in Editor c…
ije May 8, 2026
fc26f73
Fix textarea selction direction
ije May 8, 2026
f16e618
Fix selection bg color for safair
ije May 8, 2026
779a50a
Clean up
ije May 8, 2026
d9651ff
Fix shift select
ije May 8, 2026
262f8f9
Refactor
ije May 8, 2026
2bb2e4b
Refactor
ije May 9, 2026
f114396
Fix shift select delay
ije May 9, 2026
f847f63
Coalesce edit stack entries for simple typing or backspace operations.
ije May 9, 2026
2967161
Add Support forward-delete coalescing for edit history
ije May 9, 2026
f63f2fd
docs: add docs for editStack module
ije May 9, 2026
d4d8dec
Refctor
ije May 9, 2026
fe5ab6d
Fix 'documentStart' and 'documentEnd' commands
ije May 9, 2026
5ff2b42
Rewrite selection handle logic
ije May 9, 2026
76a2fb4
Fix shouldCoalesceEditStackEntry function
ije May 9, 2026
b23ad56
Update demo
ije May 9, 2026
1e76276
Add `removeEditor` for File component
ije May 9, 2026
3bb44ee
Add react api
ije May 9, 2026
ff1d452
Clean up
ije May 9, 2026
0ff08c9
Update demo app
ije May 9, 2026
3b40cf8
Refactor useFileInstance to remove redundant editor cleanup logic
ije May 9, 2026
d3289c7
Fix `computeLineOffsets` function
ije May 10, 2026
2346eef
typo
ije May 10, 2026
1a7797c
Update editor style
ije May 10, 2026
99720ae
Fix `getOrCreateLineOffSets` method
ije May 10, 2026
1abf83a
Refactor line count and annotation handling in File component; remove…
ije May 10, 2026
e174585
Fix lines deletion crocss virtul viewport
ije May 10, 2026
9083774
Remove `normalizeSelectionsForDocument` function
ije May 10, 2026
2e6580e
Fix `edit` function
ije May 10, 2026
3951793
Add editor sub-module
ije May 10, 2026
3219737
Use `contenteditable` model
ije May 11, 2026
3fbcba6
Fix line wrap
ije May 11, 2026
7c979b1
Fix wrap line
ije May 11, 2026
60a98b6
Fix selection on mobile
ije May 11, 2026
583adb3
Update editor style
ije May 11, 2026
942cba9
Fix resize handling
ije May 11, 2026
2095c1d
Add editor overlay layer
ije May 12, 2026
3ce394a
Cleanup
ije May 12, 2026
a8b674d
Add `DiffsEditableComponent` types
ije May 12, 2026
1f3a448
Fix `VirtualizedFile` component
ije May 12, 2026
c18a373
Update `DiffsEditableComponent` type
ije May 12, 2026
0d9ea91
Add editor demo
ije May 12, 2026
c76b51b
Merge branch 'main' into editor
ije May 12, 2026
b1c50ef
Fix slection rendering
ije May 12, 2026
c9ef7e8
Update editor demo app
ije May 12, 2026
6dea24a
Fix VirualizedFile component
ije May 12, 2026
8140e15
Update editor demo app
ije May 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions apps/demo/editor.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"
/>
<title>PierreJS R&amp;D</title>
</head>
<body>
<div id="editor">
<div id="file-tree">
<header id="file-tree-header">
<h1>
<svg
fill="none"
height="32"
viewBox="0 0 32 32"
width="32"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m16 0c-13.176 0-16 2.824-16 16s2.824 16 16 16 16-2.824 16-16-2.824-16-16-16z"
fill="#000"
/>
<g fill="#fff">
<path
d="m17.5001 8.59222c0-.82842-.6716-1.5-1.5-1.5-.8285 0-1.5.67158-1.5 1.5v3.90778h-4.0001c-.82843 0-1.5.6716-1.5 1.5s.67157 1.5 1.5 1.5h4.0001v3h3v-3h3.9999c.8284 0 1.5-.6716 1.5-1.5s-.6716-1.5-1.5-1.5h-3.9999z"
/>
<path
d="m10.5 20c-.82843 0-1.5.6716-1.5 1.5s.67157 1.5 1.5 1.5h11c.8284 0 1.5-.6716 1.5-1.5s-.6716-1.5-1.5-1.5z"
/>
</g>
</svg>
@pierre/diffs
</h1>
</header>
<div id="file-tree-container"></div>
</div>
<div id="editor-container"></div>
<!-- <div id="sidebar"></div> -->
</div>
<script type="module" src="/src/editor.ts"></script>
</body>
</html>
4 changes: 3 additions & 1 deletion apps/demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
"build:deps": "bun run build:deps:diffs",
"build:deps:diffs": "output=$(cd ../../packages/diffs && bun run build 2>&1) && echo '[diffs] Successfully cleaned and built.' || (echo \"$output\" >&2 && exit 1)",
"build-types": "bun run build:deps && tsgo --build",
"dev": "bun run build:deps && concurrently \"bun run dev:deps:diffs\" \"bun run dev:vite\" --names \"diffs,vite\" --prefix-colors \"blue,green\"",
"dev": "bun run build:deps && concurrently \"bun run dev:deps:diffs\" \"bun run dev:deps:trees\" \"bun run dev:vite\" --names \"diffs,trees,vite\" --prefix-colors \"blue,green,gray\"",
"dev:deps:diffs": "(cd ../../packages/diffs && bun run dev)",
"dev:deps:trees": "(cd ../../packages/trees && bun run dev)",
"dev:vite": "vite --host --clearScreen=false",
"preview": "vite preview",
"start": "vite preview",
Expand All @@ -18,6 +19,7 @@
},
"dependencies": {
"@pierre/diffs": "workspace:*",
"@pierre/trees": "workspace:*",
"react": "catalog:",
"react-dom": "catalog:",
"shiki": "catalog:"
Expand Down
86 changes: 86 additions & 0 deletions apps/demo/src/editor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
Editor,
type FileContents,
VirtualizedFile,
Virtualizer,
} from '@pierre/diffs';
import { FileTree, type GitStatusEntry } from '@pierre/trees';

import './style.css';

const API = {
// get git status
getGitStatus: () =>
fetch(`/git-status/packages/diffs`).then(
(res) => res.json() as unknown as GitStatusEntry[]
),

// get paths
getPaths: () =>
fetch('/fs/packages/diffs').then(
(res) => res.json() as unknown as string[]
),

// read file from disk
readFile: (path: string) =>
fetch(`/fs/packages/diffs/${path}`).then((res) => res.text()),

// write file to disk
writeFile: (path: string, contents: string) =>
fetch(`/fs/packages/diffs/${path}`, { method: 'POST', body: contents }),
};

const fileTreeContainer = document.getElementById('file-tree-container')!;
const editorContainer = document.getElementById('editor-container')!;
const editor = new Editor<undefined>();
const virtualizer = new Virtualizer();
const fileInstance = new VirtualizedFile<undefined>(
{
unsafeCSS: /* CSS */ `
[data-diffs-header] {
position: sticky;
top: 0;
z-index: 100;
}
`,
},
virtualizer
);
const [paths, gitStatus] = await Promise.all([
API.getPaths(),
API.getGitStatus(),
]);
const fileTree = new FileTree({
paths,
gitStatus,
search: true,
onSelectionChange: (selectedPaths) => {
if (selectedPaths.length === 1) {
const filename = selectedPaths[0];
if (!filename.endsWith('/')) {
void openDocument(filename);
}
}
},
});

async function openDocument(filename: string) {
const file: FileContents = {
name: filename,
contents: await API.readFile(filename),
};
fileInstance.render({
file,
containerWrapper: editorContainer,
});
editorContainer.scrollTo({ left: 0, top: 0 });
}

async function onFileChange(file: FileContents) {
await API.writeFile(file.name, file.contents);
fileTree.setGitStatus(await API.getGitStatus());
}

virtualizer.setup(editorContainer);
fileTree.render({ fileTreeContainer });
editor.edit(fileInstance, () => void onFileChange);
40 changes: 36 additions & 4 deletions apps/demo/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
DEFAULT_THEMES,
DIFFS_TAG_NAME,
type DiffsThemeNames,
Editor,
File,
type FileContents,
FileDiff,
Expand Down Expand Up @@ -206,7 +207,8 @@ function renderDiff(parsedPatches: ParsedPatch[], manager?: WorkerPoolManager) {
overflow: wrap ? 'wrap' : 'scroll',
renderAnnotation: renderDiffAnnotation,
renderHeaderMetadata() {
return createCollapsedToggle(
return createToggle(
'Collapse',
instance?.options.collapsed ?? false,
(checked) => {
instance?.setOptions({
Expand Down Expand Up @@ -673,6 +675,7 @@ if (renderFileButton != null) {

virtualizer?.setup(globalThis.document);
const wrap = getWrapped();
const editor = new Editor<LineCommentMetadata>();
const fileContainer = document.createElement(DIFFS_TAG_NAME);
wrapper.appendChild(fileContainer);
let instance:
Expand All @@ -684,7 +687,8 @@ if (renderFileButton != null) {
themeType: getThemeType(),
renderAnnotation,
renderCustomMetadata() {
return createCollapsedToggle(
const collapsedToggle = createToggle(
'Collapse',
instance?.options.collapsed ?? false,
(checked) => {
instance?.setOptions({
Expand All @@ -696,6 +700,33 @@ if (renderFileButton != null) {
}
}
);
const editableToggle = createToggle('Editable', false, (checked) => {
if (checked) {
editor.edit(instance, (file, lineAnnotations) => {
console.log('change', file, lineAnnotations);
});
editor.setSelections([
{
start: {
line: 0,
character: 1000, // will be normalized to the end of the line(< 1000 chars)
},
end: {
line: 0,
character: 1000, // will be normalized to the end of the line(< 1000 chars)
},
direction: 'none',
},
]);
} else {
editor.cleanUp();
}
});
const div = document.createElement('div');
div.style.display = 'flex';
div.style.gap = '8px';
div.append(collapsedToggle, editableToggle);
return div;
},

// Line selection stuff
Expand Down Expand Up @@ -883,7 +914,8 @@ cleanButton?.addEventListener('click', () => {
cleanupInstances(container);
});

function createCollapsedToggle(
function createToggle(
labelText: string,
checked: boolean,
onChange: (checked: boolean) => void
): HTMLElement {
Expand All @@ -896,7 +928,7 @@ function createCollapsedToggle(
});
label.dataset.collapser = '';
label.appendChild(input);
label.append(' Collapse');
label.appendChild(document.createTextNode(` ${labelText}`));
return label;
}

Expand Down
41 changes: 41 additions & 0 deletions apps/demo/src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,44 @@ diffs-container {
align-items: center;
gap: 4px;
}

#editor {
display: grid;
grid-template-columns: 280px 1fr;
grid-template-rows: 1fr;
gap: 10px;
background-color: light-dark(white, black);
height: 100vh;
width: 100vw;
overflow: hidden;
}

#editor[data-show-sidebar] {
grid-template-columns: 280px 1fr 280px;
}

#file-tree {
background-color: light-dark(#f8f8f8, #141415);
}

#file-tree h1 {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
padding: 16px;
}

#file-tree h1 svg {
width: 20px;
height: 20px;
}

#editor-container {
overflow-y: auto;
overscroll-behavior: none;
}

#editor-container diffs-container {
margin-top: 0;
}
Loading