From dea38c0c449e7f221fc8e9c6cb757414644fdc6b Mon Sep 17 00:00:00 2001 From: paisley <8197966+su8su@users.noreply.github.com> Date: Mon, 18 May 2026 13:42:38 +0800 Subject: [PATCH] feat: add rendered HTML file preview (#1029) --- electron/main/ipc-handlers.ts | 1 + src/components/file-preview/ArtifactPanel.tsx | 55 ++++++--- .../file-preview/FilePreviewBody.tsx | 17 ++- .../file-preview/GeneratedFilesPanel.tsx | 3 +- src/components/file-preview/HtmlPreview.tsx | 69 +++++++++++ src/lib/generated-files.ts | 10 +- src/pages/Chat/ChatMessage.tsx | 9 +- src/pages/Chat/index.tsx | 11 +- src/stores/chat/helpers.ts | 4 +- tests/e2e/chat-file-changes.spec.ts | 108 ++++++++++++++++++ tests/unit/artifact-panel.test.tsx | 44 ++++++- tests/unit/file-preview-body.test.tsx | 40 ++++++- tests/unit/generated-files.test.ts | 6 + 13 files changed, 341 insertions(+), 36 deletions(-) create mode 100644 src/components/file-preview/HtmlPreview.tsx diff --git a/electron/main/ipc-handlers.ts b/electron/main/ipc-handlers.ts index 0a1a6c7..680a05e 100644 --- a/electron/main/ipc-handlers.ts +++ b/electron/main/ipc-handlers.ts @@ -2356,6 +2356,7 @@ const EXT_MIME_MAP: Record = { '.txt': 'text/plain', '.md': 'text/markdown', '.html': 'text/html', + '.htm': 'text/html', '.css': 'text/css', '.js': 'text/javascript', '.ts': 'text/typescript', diff --git a/src/components/file-preview/ArtifactPanel.tsx b/src/components/file-preview/ArtifactPanel.tsx index 02ce725..9db56af 100644 --- a/src/components/file-preview/ArtifactPanel.tsx +++ b/src/components/file-preview/ArtifactPanel.tsx @@ -16,7 +16,7 @@ * `useArtifactPanel` zustand store so any part of the page (file cards, * toolbar buttons, "View file changes →" links) can drive it. */ -import { useLayoutEffect, useMemo } from 'react'; +import { useLayoutEffect, useMemo, useRef } from 'react'; import { Eye, FileEdit, FolderOpen, FolderTree, X } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { toast } from 'sonner'; @@ -62,7 +62,7 @@ export function ArtifactPanel({ files, agent, runStartedAt, refreshSignal }: Art return (
-
+
{richFocusedFile ? (
-
- {visibleTab === 'changes' && ( +
+
setFocusedFile(f)} + active={visibleTab === 'changes'} /> - )} - {visibleTab === 'preview' && } - {WORKSPACE_BROWSER_ENABLED && visibleTab === 'browser' && ( - +
+
+ +
+ {WORKSPACE_BROWSER_ENABLED && ( +
+ +
)}
@@ -141,13 +146,27 @@ interface PanelTabButtonProps { } function PanelTabButton({ testId, icon, label, active, onClick }: PanelTabButtonProps) { + const pointerActivated = useRef(false); + return (