From f4c38e6001dd9d4853ed923e0bc5e790c4fd9941 Mon Sep 17 00:00:00 2001 From: Timothy Jaeryang Baek Date: Wed, 4 Mar 2026 16:10:15 -0600 Subject: [PATCH] feat: add JSON collapsible tree view, SVG rendered preview, and source toggle - New JsonTreeView component with recursive collapsible nodes, auto-expand depth, and GitHub-themed dark mode colors - JSON/JSONC/JSON5 files show tree view by default, toggle to Shiki-highlighted source - SVG files show rendered preview (DOMPurify-sanitized) by default, toggle to Shiki-highlighted XML source - SVG removed from IMAGE_EXTS to enable text-based preview - YAML/TOML already covered by Shiki bundled languages --- src/lib/components/chat/FileNav.svelte | 6 +- .../chat/FileNav/FilePreview.svelte | 50 +++++- .../chat/FileNav/JsonTreeView.svelte | 147 ++++++++++++++++++ 3 files changed, 198 insertions(+), 5 deletions(-) create mode 100644 src/lib/components/chat/FileNav/JsonTreeView.svelte diff --git a/src/lib/components/chat/FileNav.svelte b/src/lib/components/chat/FileNav.svelte index 5b34f7dce..46df84b48 100644 --- a/src/lib/components/chat/FileNav.svelte +++ b/src/lib/components/chat/FileNav.svelte @@ -119,6 +119,8 @@ $: isMarkdown = MD_EXTS.has(getFileExt(selectedFile)); $: isCsv = CSV_EXTS.has(getFileExt(selectedFile)); $: isHtml = HTML_EXTS.has(getFileExt(selectedFile)); + $: isJson = ['json', 'jsonc', 'jsonl', 'json5'].includes(getFileExt(selectedFile)); + $: isSvg = getFileExt(selectedFile) === 'svg'; $: isCode = isCodeFile(selectedFile); $: isOfficeFile = OFFICE_EXTS.has(getFileExt(selectedFile)); $: isTextFile = @@ -182,7 +184,7 @@ } // ── Helpers ────────────────────────────────────────────────────────── - const IMAGE_EXTS = new Set(['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico', 'avif']); + const IMAGE_EXTS = new Set(['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'ico', 'avif']); const VIDEO_EXTS = new Set(['mp4', 'webm', 'mov', 'ogv', 'avi', 'mkv']); const AUDIO_EXTS = new Set(['mp3', 'wav', 'ogg', 'oga', 'flac', 'm4a', 'aac', 'wma', 'opus']); const isImage = (path: string) => IMAGE_EXTS.has(path.split('.').pop()?.toLowerCase() ?? ''); @@ -634,7 +636,7 @@ {/if} - {#if (isMarkdown || isCsv || isHtml || isCode) && fileContent !== null && !editing} + {#if (isMarkdown || isCsv || isHtml || isCode || isJson || isSvg) && fileContent !== null && !editing}