From 4d7cbdb6f63ce45b69e59b611f4bbca2e6c9c79e Mon Sep 17 00:00:00 2001 From: Karstein Phobic Nyvold Kvistad Date: Tue, 28 Apr 2026 23:17:34 +0200 Subject: [PATCH] tui(browser): r re-scans mcp-mirror/ r calls onRescan() which re-walks the project root and rerenders the Browser with the new tree. Optional prop so tests don't have to wire it. --- src/tui/browser/Browser.tsx | 7 +++++-- src/tui/index.tsx | 17 +++++++++++++++++ tests/tui/Browser.test.tsx | 17 +++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/tui/browser/Browser.tsx b/src/tui/browser/Browser.tsx index 0dc24ab..5d2ee4a 100644 --- a/src/tui/browser/Browser.tsx +++ b/src/tui/browser/Browser.tsx @@ -10,6 +10,7 @@ export interface BrowserProps { readPou: (pou: POU) => Promise; writeSelection: (s: Selection) => void; onQuit: () => void; + onRescan?: () => void; } interface FlatRow { @@ -31,7 +32,7 @@ function flatten(project: Project, expanded: Set): FlatRow[] { return rows; } -export function Browser({ project, readPou, writeSelection, onQuit }: BrowserProps): React.ReactElement { +export function Browser({ project, readPou, writeSelection, onQuit, onRescan }: BrowserProps): React.ReactElement { const [expanded, setExpanded] = React.useState>(new Set()); const [cursorIdx, setCursorIdx] = React.useState(0); const [text, setText] = React.useState(null); @@ -73,6 +74,7 @@ export function Browser({ project, readPou, writeSelection, onQuit }: BrowserPro return; } if (input === 'q') return onQuit(); + if (input === 'r' && onRescan) return onRescan(); if (input === 'j' || key.downArrow) { setCursorIdx((i) => Math.min(i + 1, rows.length - 1)); } else if (input === 'k' || key.upArrow) { @@ -116,7 +118,7 @@ export function Browser({ project, readPou, writeSelection, onQuit }: BrowserPro - j/k nav l expand h collapse ? help q quit + j/k nav l expand h collapse r rescan ? help q quit ); } @@ -129,6 +131,7 @@ function HelpOverlay(): React.ReactElement { k / ↑ move cursor up l / → expand device h / ← collapse device + r re-scan mcp-mirror/ ? toggle this help Esc close help q quit diff --git a/src/tui/index.tsx b/src/tui/index.tsx index a3d81f9..e21f44c 100644 --- a/src/tui/index.tsx +++ b/src/tui/index.tsx @@ -51,12 +51,29 @@ async function runBrowser(maybeRoot: string | undefined): Promise { resolve(0); }; const readPou = (pou: { absPath: string }) => fs.readFile(pou.absPath, 'utf8'); + const onRescan = async () => { + try { + const next = await walk(root); + app.rerender( + + ); + } catch (err) { + process.stderr.write(`phobiCS-tui: rescan failed: ${(err as Error).message}\n`); + } + }; const app = render( ); }); diff --git a/tests/tui/Browser.test.tsx b/tests/tui/Browser.test.tsx index e7382d1..dd53974 100644 --- a/tests/tui/Browser.test.tsx +++ b/tests/tui/Browser.test.tsx @@ -99,6 +99,23 @@ describe('', () => { expect(lastFrame()).not.toContain('Keybindings'); }); + it('calls onRescan on r', async () => { + const onRescan = vi.fn(); + const { stdin } = render( + ''} + writeSelection={() => {}} + onQuit={() => {}} + onRescan={onRescan} + /> + ); + await flush(); + stdin.write('r'); + await flush(); + expect(onRescan).toHaveBeenCalled(); + }); + it('closes the help overlay on Esc', async () => { const { stdin, lastFrame } = render(