Skip to content

Proposal: draw the tree in a pane-grid cell - #71

Open
hwanyong wants to merge 3 commits into
data-goblin:release/0.9.0from
hwanyong:pane-grid-cell
Open

hwanyong wants to merge 3 commits into
data-goblin:release/0.9.0from
hwanyong:pane-grid-cell

Conversation

@hwanyong

@hwanyong hwanyong commented Nov 7, 2026 •

Copy link
Copy Markdown

Hi, and thank you for filetree.

I maintain pane-grid, a Claude Code mod that splits one pane into a tmux-style grid of cells and places other mods in them. filetree is the mod I most wanted in a cell, so I'd like to propose this integration. It is entirely optional. Without pane-grid installed, filetree behaves exactly as it does today. If the approach doesn't fit how you want filetree to grow, I'm happy to rework it or close this.

What

In the terminal, with pane-grid installed:

  • Empty grid cells list filetree as a choice, and /grid place filetree draws the tree in a cell
  • While placed, the Files tab closes. It opens again, with the tree state kept, when the cell is emptied (/grid place -)
  • Clicks, keys and the wheel work in the cell. With pane-grid 0.4.0 the wheel scrolls the tree under its header, as it does in the Files tab
  • /filetree skips the fullscreen and 110-column checks while placed, and names the cell in its reply

The Desktop app is unchanged. A grid cell there keeps drawing what pane-grid draws, and filetree stays in its own pane.

Why

pane-grid publishes where each placed mod goes in $.state (pane-grid.layout). A mod reads that value and draws a keyed Box, so neither side imports the other. Some people keep several mods open at once (a tree, a usage gauge, a question list). A grid lets them share one sidebar instead of taking turns as tabs.

How

  • hooks/pane-grid.ts is pane-grid's reading module (source). I removed its comments so scripts/ci/greps.sh passes. Every value it reads is checked at run time, so a missing or unexpected pane-grid.layout reads as "not placed"
  • hooks/site.ts names where the tree draws (Site: the pane id, columns and rows). It is either the Files pane, or the grid pane of filetree's cell with that cell's size. openPane and focusKey skip the Files pane while placed
  • The views read site.columns and site.rows instead of e.props.bodyColumns and e.props.scroll.bodyRows, so one renderPane draws both. The treemap blit targets site.id
  • register.tsx adds three hooks:
    • a ui.render hook on /^grid-/ panes that fills the cell or adds the choice
    • a state.set hook on pane-grid.layout that closes or reopens the Files pane
    • /^grid-/ added to the ui.focus and ui.scroll matchers, through isSite and isScrollIn
  • Keys in a cell: a Button hotkey drawn in a grid cell acts for the whole grid pane, and of two Buttons with one hotkey the engine presses the later drawn. So the hidden hotkeys and the autoFocus Button are drawn only while the cell is active (placement.isActive), and a cell leaves out the letters of pane-grid's toolbar (TOOLBAR_KEYS: v s o z x w b c k q). takesKey in hooks/site.ts decides this. The Files pane is always active, so nothing changes there
  • The inline-pane check moved from renderPane into the Files pane's own render hook, so it does not run for a grid cell
  • README.md lists the feature, and docs/sidebar.md has a new "In a pane-grid cell" section with its limits

Limits

  • Terminal only, as above
  • While placed, alt+l and the other keys that move the keyboard to the tree do nothing. $.ui.focus acts only on a plugin's own sites, and the cell belongs to pane-grid's pane. Clicking the cell gives it the keyboard
  • In a cell, filetree's own keys on those ten toolbar letters (for example c and z) do nothing; pane-grid's toolbar keeps them
  • With pane-grid 0.3.x the cell works, but the wheel moves the whole cell instead of the tree

Checks

  • claude plugin test . passes: 309 tests in 4 files.
  • tsc -p tsconfig.json --noEmit passes with TypeScript 5.9.3.
  • oxlint passes with 1.88.0 and tsgolint 7.0.2004.
  • sh scripts/ci/greps.sh passes.
  • sh scripts/ci/version.sh and sh scripts/ci/version-test.sh pass.
  • claude plugin validate . passes. It lists state reads: … pane-grid.layout and state.set{plugin=pane-grid, key=layout}.
  • scripts/ci/jsx-sizes.ts not run locally (no bun on this machine). I'm relying on CI for it.
  • Live checks in tmux (200x50), in both --plugin-dir load orders:
    • placing, moving and emptying the cell
    • clicks, Right to expand, j/k, /filetree while placed
    • text split preview
    • the wheel over the tree, over the cell's title row and over another cell
    • no hook failures in the debug log
  • Targets release/0.9.0, since this is a feature. The version is unchanged.
  • ACKNOWLEDGEMENTS.md line added. The PR link will follow in a follow-up commit, once this PR's number is known.

Thank you for taking a look. Any feedback on naming, file layout or scope is very welcome.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BQMofoXjiZ7a2qbMz3g1o1

pane-grid (https://github.com/hwanyong/pane-grid) splits a Claude Code pane
into cells and places grid-ready mods in them. filetree now joins it in the
terminal:

- hooks/pane-grid.ts is pane-grid's reading module, comments removed for
  scripts/ci/greps.sh
- hooks/site.ts names where the tree draws: its own pane, or the grid pane of
  its cell, with that cell's columns and rows
- the views read columns and rows from the site, so one renderPane draws both
- a ui.render hook on grid panes fills the cell and lists filetree in empty
  cells; a state.set hook on pane-grid's layout closes the Files tab while
  placed and opens it again when the cell is emptied
- ui.message, ui.focus and ui.scroll accept the grid pane of the cell; with
  pane-grid 0.4.0 the wheel scrolls the tree under its header
- /filetree skips the fullscreen and width checks while placed and names the
  cell in its reply

Without pane-grid installed every read is undefined and filetree behaves as
before. The Desktop app keeps the tree in its own pane.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BQMofoXjiZ7a2qbMz3g1o1
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-07T20:19:58.517022Z 1524c8b Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

hwanyong and others added 2 commits November 7, 2026 03:54
…id's toolbar letters

A Button hotkey drawn in a grid cell acts for the whole grid pane, and of
two Buttons with one hotkey the engine presses the later drawn. The tree's
hidden hotkeys took pane-grid's toolbar keys (c for a new tab, z for zoom),
and the autoFocus Button of an inactive filetree cell sent digit keys to
the prompt.

- hooks/pane-grid.ts: TOOLBAR_KEYS from pane-grid 0.4.1's reading module
- hooks/site.ts: Site.isActive, and takesKey() for the keys a site draws
- hooks/footer.tsx: the hidden hotkeys and the autoFocus Button are drawn
  only while the site is active, and a cell leaves out TOOLBAR_KEYS
- hooks/register.tsx: the Files pane is always active; a cell reads
  placement.isActive (true with a pane-grid that does not write it)
- docs/sidebar.md: the key limits in a cell

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BQMofoXjiZ7a2qbMz3g1o1
@data-goblin

Copy link
Copy Markdown
Owner

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1524c8b0a8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread hooks/previewpane.tsx
Comment on lines +115 to 117
<Box flexDirection="column" minHeight={Math.max(1, site.rows ?? 1)} backgroundColor={theme.bg || undefined}>
<Box flexDirection="row">
<Button key="back" plain hotkey="h" autoFocus={settings.editWithSetting || machine.inTmux || machine.inHerdr ? undefined : true} label={unicode ? '← files' : '\u{ea9b} files'} onPress={() => void closePreview($)} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Apply the grid keyboard policy to full-preview controls

When a placed tree shows a full-file preview or diff, renderPane returns previewPane before reaching footerOf's takesKey filtering. The terminal preview consequently retains its hotkeys and autofocus controls even when site.isActive is false. Since hotkeys are pane-wide, keys intended for another active cell can still scroll or close the preview, and reserved k/b can override pane-grid's toolbar actions. Apply takesKey to the preview hotkeys and gate its autofocus controls with site.isActive, as the normal tree view does.

Useful? React with 👍 / 👎.

Comment thread hooks/site.ts
export async function placeOwnPane($: EngineInterface, root: string, layout: unknown, previous: unknown): Promise<void> {
const isPlaced = placementIn(layout, PLUGIN) !== undefined
const isOpen = (await $.ui.panes()).some(pane => pane.id === PANE)
if (isPlaced && isOpen) return $.ui.close({ id: PANE })

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the standalone pane for Desktop surfaces

When a session with a terminal grid placement also draws on Desktop, pane-grid.layout is shared session state, so this closes the Files pane for Desktop too. openPane also refuses to reopen it while the placement exists, but the grid renderer explicitly returns below on non-terminal surfaces (hooks/register.tsx:364), leaving Desktop with no filetree at all. Keep the standalone pane available on unsupported surfaces instead of treating a terminal placement as a global replacement.

Useful? React with 👍 / 👎.

@data-goblin

Copy link
Copy Markdown
Owner

Thanks - I will take a look in more detail and get back to you in the coming days before releasing 0.9.0. Appreciate the PR!

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.

2 participants