diff --git a/backend/open_webui/utils/middleware.py b/backend/open_webui/utils/middleware.py index 9c11c96447..53d1a54859 100644 --- a/backend/open_webui/utils/middleware.py +++ b/backend/open_webui/utils/middleware.py @@ -1053,16 +1053,16 @@ async def terminal_event_handler( """Emit terminal:* events for Open Terminal tools. - 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: return - path = tool_function_params.get("path", "") - if not path: - return - if tool_function_name == "display_file": + path = tool_function_params.get("path", "") + if not path: + return # Only emit if the file actually exists parsed = tool_result if isinstance(parsed, str): @@ -1079,13 +1079,23 @@ async def terminal_event_handler( "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( { "type": f"terminal:{tool_function_name}", "data": {"path": path}, } ) + elif tool_function_name == "run_command": + await event_emitter( + { + "type": "terminal:run_command", + "data": {}, + } + ) async def chat_completion_tools_handler( diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 50b9a0294d..5f68320452 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -406,11 +406,14 @@ }; const terminalEventHandler = (type: string, data: any) => { - if (!data?.path) return; if (type === 'terminal:display_file') { + if (!data?.path) return; 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); + } else if (type === 'terminal:run_command') { + showFileNavDir.set('/'); } }; diff --git a/src/lib/components/chat/FileNav.svelte b/src/lib/components/chat/FileNav.svelte index 141f4c68bc..7e9e977e67 100644 --- a/src/lib/components/chat/FileNav.svelte +++ b/src/lib/components/chat/FileNav.svelte @@ -511,13 +511,15 @@ const lastSlash = filePath.lastIndexOf('/'); const dir = lastSlash > 0 ? filePath.substring(0, lastSlash + 1) : '/'; - if (dir === currentPath) { - await loadDir(currentPath); - } - if (filePath === selectedFile) { - const fileName = filePath.substring(lastSlash + 1); - const entry = entries.find((e) => e.name === fileName); - if (entry) await openEntry(entry); + if (selectedFile) { + if (selectedFile === filePath || currentPath.startsWith(dir)) { + const fileName = selectedFile.split('/').pop() ?? ''; + await openEntry({ name: fileName, type: 'file', size: 0 }); + } + } else { + if (currentPath.startsWith(dir) || dir.startsWith(currentPath)) { + await loadDir(currentPath); + } } }); @@ -549,6 +551,8 @@ document.addEventListener('visibilitychange', onVisibilityChange); return () => { + unsubFileNav(); + unsubFileNavDir(); window.removeEventListener('keydown', onKeyDown); window.removeEventListener('keyup', onKeyUp); window.removeEventListener('blur', onBlur);