This commit is contained in:
Timothy Jaeryang Baek
2026-03-01 12:30:03 -06:00
parent 769ef856bc
commit 18865a9fef
5 changed files with 81 additions and 32 deletions
+33 -21
View File
@@ -1043,37 +1043,49 @@ def process_tool_result(
return tool_result, tool_result_files, tool_result_embeds
async def display_file_handler(
async def terminal_event_handler(
tool_function_name: str,
tool_function_params: dict,
tool_result,
event_emitter,
):
"""Emit a display_file event if the tool is display_file and the file exists."""
if not event_emitter or tool_function_name != "display_file":
"""Emit terminal:* events for Open Terminal tools.
- display_file → emits 'terminal:display_file' to open the file preview.
- write_file / replace_file_content → emits 'terminal:write_file' or
'terminal:replace_file_content' to silently refresh the file browser.
"""
if not event_emitter:
return
path = tool_function_params.get("path", "")
if not path:
return
# Parse the result to check if the file exists
parsed = tool_result
if isinstance(parsed, str):
try:
parsed = json.loads(parsed)
except (json.JSONDecodeError, TypeError):
pass
if tool_function_name == "display_file":
# Only emit if the file actually exists
parsed = tool_result
if isinstance(parsed, str):
try:
parsed = json.loads(parsed)
except (json.JSONDecodeError, TypeError):
pass
if isinstance(parsed, dict) and parsed.get("exists") is False:
return
if isinstance(parsed, dict) and parsed.get("exists") is False:
return
await event_emitter(
{
"type": "display_file",
"data": {"path": path},
}
)
await event_emitter(
{
"type": f"terminal:{tool_function_name}",
"data": {"path": path},
}
)
elif tool_function_name in ("write_file", "replace_file_content"):
await event_emitter(
{
"type": f"terminal:{tool_function_name}",
"data": {"path": path},
}
)
async def chat_completion_tools_handler(
@@ -1230,7 +1242,7 @@ async def chat_completion_tools_handler(
)
if event_emitter:
await display_file_handler(
await terminal_event_handler(
tool_function_name,
tool_function_params,
tool_result,
@@ -4224,7 +4236,7 @@ async def streaming_chat_response_handler(response, ctx):
)
)
await display_file_handler(
await terminal_event_handler(
tool_function_name,
tool_function_params,
tool_result,
+13 -5
View File
@@ -44,7 +44,8 @@
pinnedChats,
showEmbeds,
selectedTerminalId,
showFileNavPath
showFileNavPath,
showFileNavDir
} from '$lib/stores';
import { WEBUI_API_BASE_URL } from '$lib/constants';
@@ -446,6 +447,15 @@
saveChatHandler(_chatId, history);
};
const terminalEventHandler = (type: string, data: any) => {
if (!data?.path) return;
if (type === 'terminal:display_file') {
displayFileHandler(data.path, { showControls, showFileNavPath });
} else if (type === 'terminal:write_file' || type === 'terminal:replace_file_content') {
showFileNavDir.set(data.path);
}
};
const chatEventHandler = async (event, cb) => {
console.log(event);
@@ -579,10 +589,8 @@
eventConfirmationInputPlaceholder = data.placeholder;
eventConfirmationInputValue = data?.value ?? '';
eventConfirmationInputType = data?.type ?? '';
} else if (type === 'display_file') {
if (data?.path) {
displayFileHandler(data.path, { showControls, showFileNavPath });
}
} else if (type.startsWith('terminal:')) {
terminalEventHandler(type, data);
} else {
console.log('Unknown message type', data);
}
+28 -5
View File
@@ -6,7 +6,13 @@
<script lang="ts">
import { toast } from 'svelte-sonner';
import { getContext, onMount, onDestroy, tick } from 'svelte';
import { terminalServers, settings, showFileNavPath, selectedTerminalId } from '$lib/stores';
import {
terminalServers,
settings,
showFileNavPath,
showFileNavDir,
selectedTerminalId
} from '$lib/stores';
import {
getCwd,
listFiles,
@@ -342,6 +348,23 @@
if (entry) await openEntry(entry);
});
const unsubFileNavDir = showFileNavDir.subscribe(async (filePath) => {
if (!filePath || !selectedTerminal) return;
showFileNavDir.set(null);
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 (!handledDisplayFile) {
if (savedPath === '/') {
const cwd = await getCwd(terminal.url, terminal.key);
@@ -393,12 +416,12 @@
/>
{#if !selectedTerminal}
<div class="flex-1 flex flex-col items-center justify-center p-6 text-center gap-3">
<Folder className="size-10 text-gray-300 dark:text-gray-600" />
<div class="text-sm text-gray-500 dark:text-gray-400">
<div class="flex-1 flex flex-col items-center justify-center p-6 text-center">
<Folder className="size-6 text-gray-300 dark:text-gray-600 mb-2" />
<div class="text-xs text-gray-500 dark:text-gray-400 mb-1">
{$i18n.t('No Terminal connection configured.')}
</div>
<div class="text-xs text-gray-400 dark:text-gray-500">
<div class="text-[10px] text-gray-400 dark:text-gray-500">
{$i18n.t('Add your Open Terminal URL and API key in Settings → Integrations.')}
</div>
</div>
+1
View File
@@ -93,6 +93,7 @@ export const showArtifacts = writable(false);
export const showCallOverlay = writable(false);
export const showFileNav = writable(false);
export const showFileNavPath: Writable<string | null> = writable(null);
export const showFileNavDir: Writable<string | null> = writable(null);
export const selectedTerminalId: Writable<string | null> = writable(null);
export const artifactCode = writable(null);
+6 -1
View File
@@ -33,7 +33,8 @@
channelId,
terminalServers,
showControls,
showFileNavPath
showFileNavPath,
showFileNavDir
} from '$lib/stores';
import { goto } from '$app/navigation';
import { page } from '$app/stores';
@@ -337,6 +338,10 @@
}
}
if (['write_file', 'replace_file_content'].includes(data?.name) && data?.params?.path) {
showFileNavDir.set(res?.path ?? data.params.path);
}
if (cb) {
cb(JSON.parse(JSON.stringify(res)));
}