pierrecomputer/pierre is the monorepo behind The Pierre Computer Company's product. The top-level README is four lines and two links — everything worth knowing lives in the code.
What it does
The repo ships two npm packages: @pierre/diffs, a diff viewer and code editor built on Shiki, and @pierre/trees, a file tree UI. Both export vanilla JS, React bindings, SSR helpers, and web components from the same source, and both are Apache-2.0.
Why I starred it
Most diff viewers are a thin wrapper around a highlighting library plus some CSS for red/green gutters. @pierre/diffs includes a full text editor with its own piece-table buffer, incremental TextMate tokenization, bracket matching, and a worker pool for background highlighting. @pierre/trees is the same story on the file-tree side: it has an opt-in struct-of-arrays representation purpose-built for trees with a million nodes. These are components someone had to actually ship in a product under load, not demo-ware.
How it works
The text buffer is a piece table on a treap. packages/diffs/src/editor/pieceTable.ts implements the classic piece-table structure — a document as a sequence of references into an original buffer and an appended-edits buffer — but instead of the red-black tree Monaco uses to index pieces, it balances with random priorities:
// A node in the balanced piece tree. `priority` keeps the tree balanced as a
// treap (see PieceTable): the tree is a max-heap on priority while staying a
// binary search tree on document offset.
class PieceNode {
left: PieceNode | null = null;
right: PieceNode | null = null;
parent: PieceNode | null = null;
priority = 0;
...
}
A treap gets you the same expected O(log n) insert/delete/split as a red-black tree with far less rotation bookkeeping — random priorities do the balancing work instead of color-flip rules. Each PieceNode also caches subtree length, line-break count, and first/last char codes so line lookups and CRLF-boundary detection don't walk the whole tree. TextBuffer.append() in the same file tracks line offsets incrementally as text streams in, rather than rescanning on every edit.
Tokenizer caching invalidates state, not text. packages/diffs/src/editor/tokenizer.ts keeps a StateStack[] cache of TextMate grammar state per line. PR #1070 changed how edits invalidate that cache: previously, editing line 0 threw away every line's cached bracket metadata down to the end of the document, even when the grammar state after line 0 hadn't actually changed. The fix re-tokenizes the edited line, compares its resulting state against the cached state, and only invalidates downstream if they diverge — otherwise it keeps the existing metadata. The PR's own benchmark, primed with a 100k-line cache and edited at line 0, shows the payoff:
| Metric | Before | After | | --- | ---: | ---: | | Grammar calls for the final line | 1 | 0 | | Median benchmark time | 0.198 ms | 0.104 ms |
That's a 47% improvement specific to the state-neutral-edit path — the common case of typing a character that doesn't open a new string or bracket scope.
The file tree has two internal representations. By default, @pierre/trees' path-store package models the tree as plain node objects plus per-directory childIds: number[] arrays — good for the string-heavy render path. But packages/path-store/src/soa-node-store.ts adds an opt-in flag that mirrors a finalized snapshot into parallel Int32Arrays (parentId, nameId, depthAndFlags, subtreeNodeCount, visibleSubtreeCount) plus a flat CSR child table:
// Flat CSR child table. The children of node `id` occupy
// childIdsFlat[childStart[id] .. childStart[id] + childCount[id]).
readonly childStart: Int32Array;
readonly childCount: Int32Array;
readonly childIdsFlat: Int32Array;
The comment in the file is blunt about why: at roughly 1M nodes, the object-array layout pays "object header overhead, pointer chasing through the directories Map, and a fragmented GC tail" for pure topology sweeps like recomputing visible counts after an expand/collapse. This code path is only used for the count-only DFS sweep — the default representation stays untouched everywhere else. That's a deliberate scope decision: optimize the one hot loop that scales badly, leave the rest alone.
Public state throughout @pierre/trees is keyed by canonical path strings, not internal numeric IDs — worth noting if you've used tree components that leak implementation-detail IDs into their callback APIs.
Using it
pnpm add @pierre/trees @pierre/diffs
import { FileTree } from '@pierre/trees';
const tree = new FileTree({
flattenEmptyDirectories: true,
initialExpansion: 'open',
paths: ['README.md', 'src/index.ts', 'src/components/Button.tsx'],
search: true,
});
tree.render({ containerWrapper: document.getElementById('mount')! });
For large or frequently reloaded path lists, preparePresortedFileTreeInput(paths) does the sort/normalize work once and hands FileTree a preprocessed input instead of re-deriving structure on every mount.
Both packages ship an agent skill installable via npx skills add pierrecomputer/pierre --skill diffs (or trees) — a nod to the fact that a chunk of their users are now coding agents, not humans reading the docs page.
Rough edges
The tooling is opinionated and not trivially portable to a plain-pnpm workflow: task running goes through moon/moonx, tests run on Bun (bun test), and tool versions are pinned via proto. AGENTS.md even tells contributors to set AGENT=1 so Bun's test runner emits agent-friendly output. Fine if you're contributing upstream, friction if you just want to git clone and poke around.
@pierre/trees is still 1.0.0-beta.6 — the API surface (composition slots, context-menu trigger modes, row decoration lanes) reads like it's still settling, and @pierre/diffs at 1.3.5 is the more battle-tested of the two. Commit activity is daily as of mid-August 2026, so this isn't a repo you star and forget — expect breaking changes on the beta package.
Bottom line
If you're building a code-review UI or an in-browser editor and were about to reach for Monaco or roll your own diff renderer, @pierre/diffs is worth reading before you do — the piece-table treap and the tokenizer cache-invalidation logic are the kind of details most projects get wrong. @pierre/trees is the pick if you need a file tree that won't fall over at repo scale, though I'd wait for a stable 1.0 before betting a production surface on its API.
