# @pierre/trees & Path Store — Virtualized Trees

> The engine behind trees.software: prepared input + density presets, FileTree renderer, and the sophisticated @pierre/path-store (PathStore + createVisibleTreeProjection) that handles async directory loading, visible-row projection, mutations, and cleanup for massive trees (AOSP, Linux) while keeping O(n) behavior. Includes React and web-component surfaces plus Playwright E2E only for true browser-only invariants.

- Repository: pierrecomputer/pierre
- GitHub: https://github.com/pierrecomputer/pierre
- Human wiki: https://grok-wiki.com/public/wiki/pierrecomputer-pierre-fac2c554b845
- Complete Markdown: https://grok-wiki.com/public/wiki/pierrecomputer-pierre-fac2c554b845/llms-full.txt

## Source Files

- `packages/trees/package.json`
- `packages/trees/src/index.ts`
- `packages/trees/src/render/FileTree.ts`
- `packages/trees/src/model/virtualization.ts`
- `packages/path-store/src/index.ts`
- `packages/path-store/src/visible-tree-projection.ts`
- `packages/path-store/src/store.ts`
- `packages/tree-test-data/index.js`
- `packages/trees/test/rename-file-tree-paths.test.ts`

---

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [packages/trees/package.json](packages/trees/package.json)
- [packages/trees/src/index.ts](packages/trees/src/index.ts)
- [packages/trees/src/render/FileTree.ts](packages/trees/src/render/FileTree.ts)
- [packages/trees/src/model/virtualization.ts](packages/trees/src/model/virtualization.ts)
- [packages/trees/src/preparedInput.ts](packages/trees/src/preparedInput.ts)
- [packages/trees/src/model/FileTreeController.ts](packages/trees/src/model/FileTreeController.ts)
- [packages/path-store/src/index.ts](packages/path-store/src/index.ts)
- [packages/path-store/src/visible-tree-projection.ts](packages/path-store/src/visible-tree-projection.ts)
- [packages/path-store/src/store.ts](packages/path-store/src/store.ts)
- [packages/path-store/src/projection.ts](packages/path-store/src/projection.ts)
- [packages/path-store/package.json](packages/path-store/package.json)
- [packages/tree-test-data/index.js](packages/tree-test-data/index.js)
</details>

# @pierre/trees & Path Store — Virtualized Trees

The `@pierre/trees` and `@pierre/path-store` packages together power the high-performance file tree used by trees.software. They deliver a complete virtualized tree solution that ingests prepared path lists, applies density presets, renders rows via the `FileTree` component (and its React/web-component surfaces), and delegates the hard work of visible-row management, async directory loading, mutations, and cleanup to `PathStore` plus `createVisibleTreeProjection`.

The design targets massive trees—Linux kernel snapshots, AOSP-scale repositories, and 10× replicas—while preserving predictable O(n) behavior through single-pass DFS walks, typed-array bookkeeping, presorted fast paths, and chunked child-index sums. Unit and integration tests cover the model; Playwright E2E tests exist only for browser-only invariants such as sticky-scroll drift and shadow-DOM style isolation.

## Prepared Input and Density Presets

Callers avoid repeated parsing by supplying either raw paths or a precomputed `FileTreePreparedInput` (or `PathStorePreparedInput`). Two helpers exist:

```ts
// packages/trees/src/preparedInput.ts:15-30
export function prepareFileTreeInput(paths, options)
export function preparePresortedFileTreeInput(paths)
```

`preparePresortedFileTreeInput` marks input as already in canonical order, letting both the store and renderer skip sorting and duplicate detection. The store constructor detects this shape and takes the presorted fast path, including a specialized reverse post-order count walk that never re-visits the builder's work.

Density is expressed with presets or a numeric factor:

```ts
// packages/trees/src/model/density.ts:10-17
export const FILE_TREE_DENSITY_PRESETS = {
  compact: { itemHeight: 24, factor: 0.8 },
  default: { itemHeight: 30, factor: 1 },
  relaxed: { itemHeight: 36, factor: 1.2 },
};
```

`resolveFileTreeDensity` (and the exported `FILE_TREE_DEFAULT_ITEM_HEIGHT`) feed the virtualization layer so row height, overscan, and sticky-window math stay consistent between SSR and client hydration.

Sources: [packages/trees/src/preparedInput.ts:15-30](), [packages/trees/package.json:52-70]()

## FileTree Renderer and Virtualization Model

`FileTree` (the primary class) and its React counterpart accept `paths`, `preparedInput`, density, `initialExpansion`, `stickyFolders`, `overscan`, and a rich set of composition, drag-and-drop, search, and renaming options. Internally it owns a `FileTreeController` that wraps a `PathStore` instance and maintains a cached visible projection for focus, selection, and ARIA attributes.

The core virtualization primitives live in one small module:

```ts
// packages/trees/src/model/virtualization.ts:34-54
export function computeVisibleRange({ itemCount, itemHeight, scrollTop, viewportHeight })
export function computeWindowRange(metrics, currentRange)
export function computeStickyWindowLayout(...)
```

`computeWindowRange` expands the visible range by `overscan` (default 10) only when the current window does not already cover the viewport. `computeStickyWindowLayout` produces the CSS offsets and random inset that keep folder headers visually stable during fast scrolling without snapping to a grid.

The controller subscribes to store events and calls `#rebuildVisibleProjection`, which requests either a bounded slice (`INITIAL_PROJECTION_ROW_LIMIT`) or the full projection data depending on whether the caller needs the complete ARIA parent/posInSet/setSize tables.

Sources: [packages/trees/src/model/virtualization.ts:34-94](), [packages/trees/src/model/FileTreeController.ts:1947-1979]()

## Path Store — The Engine

`@pierre/path-store` is the private but fully public-API core (re-exported via trees). Its surface is intentionally small:

- `PathStore` (constructor + `add`/`remove`/`move`/`batch`)
- `getVisibleCount`, `getVisibleSlice(start, end)`, `getVisibleRowContext(index)`
- `getVisibleTreeProjection()`, `getVisibleTreeProjectionData(maxRows?)`
- `getVisibleIndex(path)`, `getPathInfo(path)`
- Async directory loading: `beginChildLoad`, `completeChildLoad`, `failChildLoad`, `markDirectoryUnloaded`
- `cleanup(options)`

Construction is heavily instrumented for benchmarks and chooses among several fast paths:

- presorted prepared input
- explicit “all directories open” initialization that skips the generic recompute walk
- detection of “every directory is already expanded” so visible counts can be written in one reverse-order pass

```ts
// packages/path-store/src/store.ts:234-341
export class PathStore {
  public constructor(options: PathStoreConstructorOptions = {})
  // chooses initializeOpenVisibleCounts or recomputeCountsRecursive
  // ...
}
```

The same class exposes the three child-load lifecycle methods that power on-demand expansion for trees too large to materialize entirely up front.

Sources: [packages/path-store/src/store.ts:234-341](), [packages/path-store/src/store.ts:604-786]()

## Visible Tree Projection

`createVisibleTreeProjection` (and the internal `buildVisibleTreeProjectionDataDFS`) turn a list of visible rows into the metadata React and web-component renderers need for `aria-posinset`/`aria-setsize` and parent-chain computation.

Implementation highlights:

- Uses `Int32Array` tables (`parentRowIndex`, `childCount`, `lastRowAtDepth`) instead of per-path Maps.
- Grows depth capacity by doubling; never allocates a row object per visible item when only the compact `PathStoreVisibleTreeProjectionData` form is requested.
- The `visibleIndexByPath` Map is built lazily on first access.
- Single DFS preorder walk over the visible subtree (bounded by `maxRows`) keeps cost linear in the number of rows actually rendered.

```ts
// packages/path-store/src/visible-tree-projection.ts:19-94
export function createVisibleTreeProjection(
  rows: readonly Pick<PathStoreVisibleRow, 'depth' | 'path'>[]
): PathStoreVisibleTreeProjection
```

The projection is deliberately decoupled from the store’s internal node IDs so that consumers can derive stable ARIA values without retaining the full canonical tree.

Sources: [packages/path-store/src/visible-tree-projection.ts:19-94](), [packages/path-store/src/projection.ts:740-790]()

## Async Directory Loading, Mutations, and Cleanup

Directories that are not yet expanded start with `loadState: 'unloaded'`. The host application calls:

1. `beginChildLoad(path)` → receives a `{ nodeId, attemptId }` token
2. performs its own I/O (network, FS, etc.)
3. `completeChildLoad(attempt)` or `failChildLoad(attempt, message)`

The store emits the corresponding `beginChildLoad` / `completeChildLoad` events, updates visible counts, and the controller rebuilds only the affected projection slice. `markDirectoryUnloaded` lets the host drop an entire subtree while preserving the placeholder row.

Mutations (`add`, `remove`, `move`, `batch`) record the visible count before and after, then emit a single coalesced event. The controller’s `#applyMutationState` walks the event to keep renaming state, focus, and search results consistent.

Cleanup (`cleanup({ mode: 'aggressive' | 'stable' })`) removes unreachable nodes and shrinks internal tables. It refuses to run while any directory load is in flight.

Sources: [packages/path-store/src/store.ts:823-849]()

## Rendering Surfaces

| Surface | Entry | Notes |
|---------|-------|-------|
| React | `export * from './react'` | `FileTree`, `useFileTree`, `useFileTreeSelection`, `useFileTreeSearch`, selector hooks |
| Web Components | `export * from './web-components'` | declarative shadow DOM, slot composition, custom element registration |
| SSR | `serializeFileTreeSsrPayload`, `preloadFileTree` | produces `outerStart` + `shadowHtml` + `outerEnd`; hydration re-uses the same controller |
| Vanilla / SSR consumers | `FileTree` class + `renderFileTreeRoot` | no React required |

All surfaces ultimately delegate to the identical `FileTreeController` + `PathStore` core, guaranteeing identical visible-row math and event ordering.

Sources: [packages/trees/src/index.ts:36-46](), [packages/trees/src/render/FileTree.ts:173-200]()

## Test Data and Massive-Tree Workloads

`@pierre/tree-test-data` supplies canonical Linux-kernel and Pierre-snapshot fixtures plus reproducible 5× and 10× replicas. Workloads expose both the raw file list and a presorted variant so benchmarks exercise the exact fast path used in production.

```js
// packages/tree-test-data/index.js:231-246
'linux-5x':  ... 5× replica
'linux-10x': ... 10× replica
```

Benchmarks in both packages (and the dedicated `benchmark:visible-tree-projection` script) measure cold-ingest, visible-slice, projection construction, and sticky-scroll window math against these data sets.

Sources: [packages/tree-test-data/index.js:180-246]()

## Testing Strategy

- Unit/integration tests (`bun test`) live beside each package and cover the model exhaustively (ingest, mutations, projection, async loading, cleanup, search, renaming).
- Playwright E2E is intentionally narrow and gated behind `@diagnostic` or explicit sticky-drift/style-isolation suites; the contract is that true browser-only behavior (layout, scroll anchoring, shadow-DOM isolation) is the only thing that justifies a real browser.

Sources: [packages/trees/package.json:61-65](), packages/trees/test/e2e/ (structure verified via listing)

## Summary

`@pierre/trees` + `@pierre/path-store` give you a drop-in virtualized tree that stays fast from a few dozen nodes to multi-million-node repositories by combining prepared-input fast paths, density-driven virtualization, a linear-cost visible projection, async child loading, and explicit cleanup. Start with the package `index.ts` files, then read `FileTreeController` and the store constructor to see how the layers compose. Every performance-sensitive path has been measured against the Linux-scale fixtures in `tree-test-data`.

Sources: [packages/path-store/src/store.ts:234-341](), [packages/trees/src/render/FileTree.ts:837-850]()
