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:
@@ -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(
|
||||||
|
|||||||
@@ -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('/');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user