This commit is contained in:
Timothy Jaeryang Baek
2026-08-24 18:06:59 -04:00
parent 98ee2bdfd3
commit fd8cc2ba4a
14 changed files with 117 additions and 18 deletions
+1 -1
View File
@@ -1139,7 +1139,7 @@
const terminalEventHandler = (type: string, data: any) => {
if (type === 'terminal:display_file') {
if (!data?.path) return;
displayFileHandler(data.path, { showControls, showFileNavPath });
displayFileHandler(data.path, { showControls, showFileNavPath }, { page: data?.page });
} else if (type === 'terminal:write_file' || type === 'terminal:replace_file_content') {
if (!data?.path) return;
showFileNavDir.set(data.path);
+12 -5
View File
@@ -37,6 +37,7 @@
} from '$lib/apis/terminal';
import { isCodeFile } from '$lib/utils/codeHighlight';
import { isSavedChatId, isTemporaryChatId } from '$lib/utils/chatId';
import { normalizeDocumentTargetPage } from '$lib/utils/documentPreview';
import Spinner from '../common/Spinner.svelte';
import Tooltip from '../common/Tooltip.svelte';
@@ -302,6 +303,7 @@
let fileLoading = false;
let filePreviewRef: FilePreview;
let fileSearchTarget: FileSearchTarget | null = null;
let documentTargetPage: number | null = null;
// ── Office preview state ────────────────────────────────────────────
let fileOfficeHtml: string | null = null;
@@ -691,6 +693,7 @@
// ── File preview management ──────────────────────────────────────────
const clearFilePreview = () => {
fileSearchTarget = null;
documentTargetPage = null;
fileContent = null;
if (fileImageUrl) {
URL.revokeObjectURL(fileImageUrl);
@@ -810,7 +813,7 @@
}
};
const openEntry = async (entry: FileEntry) => {
const openEntry = async (entry: FileEntry, options: { page?: unknown } = {}) => {
const fullPath =
'fullPath' in entry ? (entry as BrowserRow).fullPath : entryPath(currentPath, entry);
const parentPath = 'parentPath' in entry ? (entry as BrowserRow).parentPath : currentPath;
@@ -832,6 +835,7 @@
selectedFile = filePath;
fileLoading = true;
clearFilePreview();
documentTargetPage = normalizeDocumentTargetPage(options.page);
if (isImage(filePath)) {
const result = await downloadFileBlob(
@@ -1317,10 +1321,12 @@
let handledDisplayFile = false;
const unsubFileNav = showFileNavPath.subscribe(async (filePath) => {
if (!filePath || !selectedTerminal) return;
const unsubFileNav = showFileNavPath.subscribe(async (request) => {
if (!request || !selectedTerminal) return;
handledDisplayFile = true;
showFileNavPath.set(null);
let filePath = typeof request === 'string' ? request : request.path;
const targetPage = typeof request === 'string' ? null : request.page;
filePath = normalizePath(filePath);
if (!isInsideFileRoot(filePath)) {
await loadDir(fileRoot?.path ?? '/');
@@ -1337,10 +1343,10 @@
const entry = entries.find((e) => e.name === fileName);
if (entry) {
await openEntry(entry);
await openEntry(entry, { page: targetPage });
} else {
// File may not be in listing; open it directly
await openEntry({ name: fileName, type: 'file', size: 0 });
await openEntry({ name: fileName, type: 'file', size: 0 }, { page: targetPage });
}
});
@@ -1741,6 +1747,7 @@
{fileContent}
{fileOfficeHtml}
{fileOfficeSlides}
targetPage={documentTargetPage}
{excelSheetNames}
{selectedExcelSheet}
searchTarget={fileSearchTarget}
@@ -39,6 +39,7 @@
export let fileOfficeHtml: string | null = null;
export let fileOfficeSlides: string[] | null = null;
export let currentSlide = 0;
export let targetPage: number | null = null;
export let excelSheetNames: string[] = [];
export let selectedExcelSheet = '';
export let onSheetChange: ((sheet: string) => void) | null = null;
@@ -320,11 +321,11 @@
</audio>
</div>
{:else if filePdfData !== null}
<PDFViewer bind:this={pdfViewerRef} data={filePdfData} className="w-full h-full" />
<PDFViewer bind:this={pdfViewerRef} data={filePdfData} {targetPage} className="w-full h-full" />
{:else if fileSqliteData !== null}
<SqliteView data={fileSqliteData} />
{:else if fileDocxData !== null}
<DocxPreview data={fileDocxData} className="w-full h-full" />
<DocxPreview data={fileDocxData} {targetPage} className="w-full h-full" />
{:else if fileOfficeHtml !== null}
<div class="flex flex-col h-full">
<div class="office-preview overflow-auto flex-1 min-h-0">
@@ -353,6 +354,7 @@
bind:this={pptxPreviewRef}
slides={fileOfficeSlides}
bind:currentSlide
{targetPage}
className="w-full h-full"
/>
{:else if fileContent !== null}
@@ -8,6 +8,7 @@
import FilePreview from '$lib/components/chat/FileNav/FilePreview.svelte';
import Icon from '$lib/components/chat/FileNav/Icon.svelte';
import { fileIconName } from '$lib/components/chat/FileNav/fileIcon';
import { normalizeDocumentTargetPage } from '$lib/utils/documentPreview';
export let item: any;
export let chatId = '';
@@ -42,6 +43,7 @@
$: path = String(item?.full_path || item?.path || '');
$: name = String(item?.name || path.split('/').filter(Boolean).at(-1) || 'file');
$: targetPage = normalizeDocumentTargetPage(item?.page);
$: selector = item?.terminal_selector;
$: terminal = resolveTerminal();
$: unavailable = !terminal;
@@ -165,7 +167,7 @@
function openInFiles() {
if (unavailable || !path) return;
showControls.set(true);
showFileNavPath.set(path);
showFileNavPath.set(targetPage ? { path, page: targetPage } : path);
}
async function downloadFile() {
@@ -249,6 +251,7 @@
{fileOfficeHtml}
{fileOfficeSlides}
{currentSlide}
{targetPage}
{excelSheetNames}
{selectedExcelSheet}
onSheetChange={loadExcelSheet}
@@ -187,7 +187,7 @@ function getInlineFileFromToolOutput(callItem?: OutputItem, resultItem?: OutputI
return null;
}
return result;
return result.page === undefined && args.page !== undefined ? { ...result, page: args.page } : result;
}
function buildToolCallToken(item: OutputItem, toolOutputByCallId: Record<string, OutputItem>) {