From 8b2160f2f7a253f4b75fb45062fa2831f6a9cf49 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 25 Feb 2026 16:13:18 -0600 Subject: [PATCH] refac --- src/lib/apis/terminal/index.ts | 10 +- src/lib/components/chat/FileNav.svelte | 52 +++++--- .../components/common/FileItemModal.svelte | 8 +- src/lib/components/common/PDFViewer.svelte | 117 ++++++++++++++++++ 4 files changed, 160 insertions(+), 27 deletions(-) create mode 100644 src/lib/components/common/PDFViewer.svelte diff --git a/src/lib/apis/terminal/index.ts b/src/lib/apis/terminal/index.ts index 34920e2d5..94ee064b5 100644 --- a/src/lib/apis/terminal/index.ts +++ b/src/lib/apis/terminal/index.ts @@ -68,22 +68,14 @@ export const downloadFileBlob = async ( apiKey: string, path: string ): Promise<{ blob: Blob; filename: string } | null> => { - const url = `${baseUrl.replace(/\/$/, '')}/files/read?path=${encodeURIComponent(path)}`; + const url = `${baseUrl.replace(/\/$/, '')}/files/view?path=${encodeURIComponent(path)}`; const res = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } }).catch(() => null); if (!res || !res.ok) return null; - const contentType = res.headers.get('content-type') ?? ''; const filename = path.split('/').pop() ?? 'file'; - - if (contentType.includes('application/json')) { - const json = await res.json().catch(() => null); - const blob = new Blob([json?.content ?? ''], { type: 'text/plain' }); - return { blob, filename }; - } - const blob = await res.blob(); return { blob, filename }; }; diff --git a/src/lib/components/chat/FileNav.svelte b/src/lib/components/chat/FileNav.svelte index c0c8c2c87..2d7775bb6 100644 --- a/src/lib/components/chat/FileNav.svelte +++ b/src/lib/components/chat/FileNav.svelte @@ -26,6 +26,7 @@ import GarbageBin from '../icons/GarbageBin.svelte'; import Spinner from '../common/Spinner.svelte'; import Tooltip from '../common/Tooltip.svelte'; + import PDFViewer from '../common/PDFViewer.svelte'; import ConfirmDialog from '../common/ConfirmDialog.svelte'; const i18n = getContext('i18n'); @@ -40,7 +41,7 @@ let selectedFile: string | null = null; let fileContent: string | null = null; let fileImageUrl: string | null = null; - let filePdfUrl: string | null = null; + let filePdfData: ArrayBuffer | null = null; let fileLoading = false; const IMAGE_EXTS = new Set(['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico', 'avif']); @@ -88,10 +89,7 @@ URL.revokeObjectURL(fileImageUrl); fileImageUrl = null; } - if (filePdfUrl) { - URL.revokeObjectURL(filePdfUrl); - filePdfUrl = null; - } + filePdfData = null; }; const loadDir = async (path: string) => { @@ -132,10 +130,7 @@ if (result) fileImageUrl = URL.createObjectURL(result.blob); } else if (isPdf(filePath)) { const result = await downloadFileBlob(terminalUrl, terminalKey, filePath); - if (result) - filePdfUrl = URL.createObjectURL( - new Blob([await result.blob.arrayBuffer()], { type: 'application/pdf' }) - ); + if (result) filePdfData = await result.blob.arrayBuffer(); } else { fileContent = await readFile(terminalUrl, terminalKey, filePath); } @@ -236,20 +231,29 @@ }; const onBlur = () => (shiftKey = false); + // Auto-reload directory when the browser tab regains focus + const onVisibilityChange = () => { + if (document.visibilityState === 'visible' && !selectedFile && configured && !loading) { + loadDir(currentPath); + } + }; + window.addEventListener('keydown', onKeyDown); window.addEventListener('keyup', onKeyUp); window.addEventListener('blur', onBlur); + document.addEventListener('visibilitychange', onVisibilityChange); return () => { window.removeEventListener('keydown', onKeyDown); window.removeEventListener('keyup', onKeyUp); window.removeEventListener('blur', onBlur); + document.removeEventListener('visibilitychange', onVisibilityChange); }; }); onDestroy(() => { if (fileImageUrl) URL.revokeObjectURL(fileImageUrl); - if (filePdfUrl) URL.revokeObjectURL(filePdfUrl); + }); @@ -338,6 +342,26 @@ {#if !selectedFile} + + +