feat: use @pierre/trees for Studio file tree#424
Conversation
jrusso1020
left a comment
There was a problem hiding this comment.
Verdict: approve on code merit — do not un-draft or merge until the rebase + CI fixes below land
Clean wrapper architecture. The Studio-side surface (FileTreeProps, create/rename/delete/duplicate/move/import callbacks) is preserved intact, and the internal mechanics match @pierre/trees' documented extension points (shadow DOM via host.shadowRoot, unsafeCSS injection for the drag-target highlight, renderContextMenu slot). Staff-level skim finds no behavioral regressions in the preserved API.
What the tests actually prove
buildStudioTreePaths / createPlaceholderPath / getDropPathData / buildMoveDestinationPath / isPendingCreateCleared are all pure functions extracted from the component — and the test suite hits the one semantic this PR is really about: isPendingCreateCleared("keeps pending creates alive across rename moves"). That's the exact race the PR body calls out (inline-rename of a newly created placeholder used to fall through to the Studio rename API instead of the create API). The test locks that in, and the component's renaming.onRename handler correctly branches on pending.placeholderPath === event.sourcePath before deciding which callback to fire. Fix is surgical.
Expansion persistence across displayPaths changes (via collectExpandedPaths(host) + model.resetPaths(…, {initialExpandedPaths})) is the right shape — without it, any backend file refresh would collapse all open folders. Nice detail.
Things worth noting (non-blocking)
useEffect(() => { hostRef.current = getHostElement(...); })with no deps runs on every render. The next effect with[displayPaths, model]also refresheshostRef.current, so the unkeyed version is defensive chain on top. Cheap, but if you ever profile render work this is a free effect-callback you can drop.canDrag: (paths) => paths.length === 1— multi-select drag is disabled. Consistent with existing Studio behavior as far as I can tell, but worth noting: if the old tree supported multi-drag anywhere (even unintentionally), this is a silent removal.@pierre/treesbundle-size impact wasn't in the PR body. A one-liner in the description with thebun run build --filter @hyperframes/studiobefore/after bundle sizes would close the loop for the ops-side concern.- Keyboard-nav / accessibility coverage is effectively "we trust
@pierre/trees" — the PR body's browser verification is scoped to inline-rename, not focus management / arrow keys / roving tabindex. Worth a focused agent-browser pass on: tab into tree → arrow up/down → enter to open → shift+F10 for context menu → escape to close. Those are the failure modes that wouldn't show up in the FileTree.test.ts suite.
🚨 Before un-drafting (blockers)
- Stale base. PR was opened 2026-04-22 on
7800a9ff;mainhas moved substantially since (multiple merges today: #458 / #461 / #353 / #463). Needs a rebase onto currentmainbefore merge — and CI reruns on the rebased head. Formatcheck was FAILURE on the current head —bun run formatshould clean that.Semantic PR titlecheck was FAILURE on the current head — the titlefeat: use @pierre/trees for Studio file treeis structurally valid conventional-commit, so this is likely a scope-requirement config; verify what the check wants (maybefeat(studio): …) and update accordingly.
Browser-test ask — honest capability gap
My session doesn't have Playwright / Puppeteer / a Studio dev-server + headed-browser setup wired in. I can't run the agent-browser verification pass myself without the tooling you used locally. Your own verification is captured in the PR body (file-create + folder-create inline rename + persistence across reload); for the drag/drop behavior you flagged as wanting "a final manual browser pass", that still needs to happen before un-drafting — either you locally, or routed to someone on the team with the Studio dev-server + browser-automation setup.
On code merit, stamp is clean. The draft status plus the above blockers are the right gate for un-drafting.
Review by hyperframes
What changed
@pierre/trees-based wrapper while keeping the existing Studio actions and affordances wired through it.gitkeephandlingWhy
Studio needed the richer tree viewer from
@pierre/trees, but the first integration attempt exposed a wrapper bug: inline renames for newly created items could lose pending-create state and fall through to a rename request instead of the Studio create API. This patch fixes that race so new files and folders persist correctly after the inline rename flow.Impact
Validation
bunx oxfmt packages/studio/src/App.tsx packages/studio/src/components/editor/FileTree.tsx packages/studio/src/components/editor/FileTree.test.tsbunx oxlint packages/studio/src/App.tsx packages/studio/src/components/editor/FileTree.tsx packages/studio/src/components/editor/FileTree.test.tsbun run --cwd packages/studio test src/components/editor/FileTree.test.tsbun run --cwd packages/studio typecheckagent-browserbrowser verification on local Studio for file-create and folder-create inline rename flows, including persistence after reloadNotes
.thumbnails/artifact out of the PR scope