feat: auto-refresh FileNav on write_file, replace_file_content, and run_command

Backend emits terminal events for write_file, replace_file_content,
and run_command. Frontend showFileNavDir subscriber uses startsWith
path matching to smartly refresh only when the event is relevant:
- write_file/replace_file_content: refresh if path is in current view
- run_command: always refresh (uses root '/' which matches everything)
- Also adds copy-to-clipboard button and code preview full-height fix
This commit is contained in:
Timothy Jaeryang Baek
2026-03-05 14:41:18 -06:00
parent 3b97c8d89b
commit 828656b35f
3 changed files with 32 additions and 15 deletions
+16 -6
View File
@@ -1053,16 +1053,16 @@ async def terminal_event_handler(
"""Emit terminal:* events for Open Terminal tools. """Emit terminal:* events for Open Terminal tools.
- display_file → emits 'terminal:display_file' to open the file preview. - display_file → emits 'terminal:display_file' to open the file preview.
- write_file → emits 'terminal:write_file' to silently refresh the file browser. - write_file / replace_file_content → emits 'terminal:write_file' to refresh.
- run_command → emits 'terminal:run_command' with cwd to refresh if relevant.
""" """
if not event_emitter: if not event_emitter:
return return
path = tool_function_params.get("path", "")
if not path:
return
if tool_function_name == "display_file": if tool_function_name == "display_file":
path = tool_function_params.get("path", "")
if not path:
return
# Only emit if the file actually exists # Only emit if the file actually exists
parsed = tool_result parsed = tool_result
if isinstance(parsed, str): if isinstance(parsed, str):
@@ -1079,13 +1079,23 @@ async def terminal_event_handler(
"data": {"path": path}, "data": {"path": path},
} }
) )
elif tool_function_name == "write_file": elif tool_function_name in ("write_file", "replace_file_content"):
path = tool_function_params.get("path", "")
if not path:
return
await event_emitter( await event_emitter(
{ {
"type": f"terminal:{tool_function_name}", "type": f"terminal:{tool_function_name}",
"data": {"path": path}, "data": {"path": path},
} }
) )
elif tool_function_name == "run_command":
await event_emitter(
{
"type": "terminal:run_command",
"data": {},
}
)
async def chat_completion_tools_handler( async def chat_completion_tools_handler(
+5 -2
View File
@@ -406,11 +406,14 @@
}; };
const terminalEventHandler = (type: string, data: any) => { const terminalEventHandler = (type: string, data: any) => {
if (!data?.path) return;
if (type === 'terminal:display_file') { if (type === 'terminal:display_file') {
if (!data?.path) return;
displayFileHandler(data.path, { showControls, showFileNavPath }); displayFileHandler(data.path, { showControls, showFileNavPath });
} else if (type === 'terminal:write_file') { } else if (type === 'terminal:write_file' || type === 'terminal:replace_file_content') {
if (!data?.path) return;
showFileNavDir.set(data.path); showFileNavDir.set(data.path);
} else if (type === 'terminal:run_command') {
showFileNavDir.set('/');
} }
}; };
+11 -7
View File
@@ -511,13 +511,15 @@
const lastSlash = filePath.lastIndexOf('/'); const lastSlash = filePath.lastIndexOf('/');
const dir = lastSlash > 0 ? filePath.substring(0, lastSlash + 1) : '/'; const dir = lastSlash > 0 ? filePath.substring(0, lastSlash + 1) : '/';
if (dir === currentPath) { if (selectedFile) {
await loadDir(currentPath); if (selectedFile === filePath || currentPath.startsWith(dir)) {
} const fileName = selectedFile.split('/').pop() ?? '';
if (filePath === selectedFile) { await openEntry({ name: fileName, type: 'file', size: 0 });
const fileName = filePath.substring(lastSlash + 1); }
const entry = entries.find((e) => e.name === fileName); } else {
if (entry) await openEntry(entry); if (currentPath.startsWith(dir) || dir.startsWith(currentPath)) {
await loadDir(currentPath);
}
} }
}); });
@@ -549,6 +551,8 @@
document.addEventListener('visibilitychange', onVisibilityChange); document.addEventListener('visibilitychange', onVisibilityChange);
return () => { return () => {
unsubFileNav();
unsubFileNavDir();
window.removeEventListener('keydown', onKeyDown); window.removeEventListener('keydown', onKeyDown);
window.removeEventListener('keyup', onKeyUp); window.removeEventListener('keyup', onKeyUp);
window.removeEventListener('blur', onBlur); window.removeEventListener('blur', onBlur);