From 741fea5c8915ebb87d2aed358c8d462e5a84ea4d Mon Sep 17 00:00:00 2001 From: ciregenz Date: Mon, 11 May 2026 10:19:36 -0700 Subject: [PATCH] [eric] reset history button (replaces dead View Changes) + thumbnail capture path fix --- .../src/app/pages/AgentChat/AgentChat.tsx | 24 ++- .../src/app/pages/AgentChat/DiffViewer.tsx | 145 ------------------ .../app/pages/Views/captureViewThumbnail.ts | 20 ++- 3 files changed, 39 insertions(+), 150 deletions(-) delete mode 100644 frontend/src/app/pages/AgentChat/DiffViewer.tsx diff --git a/frontend/src/app/pages/AgentChat/AgentChat.tsx b/frontend/src/app/pages/AgentChat/AgentChat.tsx index 5b794940..caccc138 100644 --- a/frontend/src/app/pages/AgentChat/AgentChat.tsx +++ b/frontend/src/app/pages/AgentChat/AgentChat.tsx @@ -15,6 +15,7 @@ import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; import CheckIcon from '@mui/icons-material/Check'; import DragIndicatorIcon from '@mui/icons-material/DragIndicator'; +import RestartAltIcon from '@mui/icons-material/RestartAlt'; import { useAppDispatch, useAppSelector } from '@/shared/hooks'; import { openSettingsModal } from '@/shared/state/settingsSlice'; import { API_BASE, getAuthToken } from '@/shared/config'; @@ -35,6 +36,7 @@ import { updateThinkingLevel, fetchSession, AgentMessage, + clearSessionMessages, } from '@/shared/state/agentsSlice'; import { fetchModes } from '@/shared/state/modesSlice'; import { createSessionWs } from '@/shared/ws/WebSocketManager'; @@ -48,7 +50,6 @@ import ChatInput, { ChatInputHandle } from './ChatInput'; import ContextDrawer from './ContextDrawer'; import { ErrorSlime } from '@/app/components/ErrorSlime'; import { ContextPath } from '@/app/components/DirectoryBrowser'; -import DiffViewer from './DiffViewer'; import { setGlowingBrowserCards, fadeGlowingBrowserCards, clearGlowingBrowserCards } from '@/shared/state/dashboardLayoutSlice'; import { useClaudeTokens } from '@/shared/styles/ThemeContext'; @@ -911,7 +912,26 @@ const AgentChat: React.FC = ({ sessionId: sessionIdProp, onClose )} - {!isDraft && id && } + {!isDraft && id && ( + + { + const sid = id; + try { + const tok = (() => { try { return getAuthToken(); } catch { return ''; } })(); + const headers: Record = { 'Content-Type': 'application/json' }; + if (tok) headers['Authorization'] = `Bearer ${tok}`; + await fetch(`${API_BASE}/agents/sessions/${sid}/clear`, { method: 'POST', headers }); + } catch { /* surfaced via context_status */ } + dispatch(clearSessionMessages(sid)); + }} + sx={{ color: c.text.tertiary, '&:hover': { color: c.text.primary } }} + > + + + + )} {onClose && ( diff --git a/frontend/src/app/pages/AgentChat/DiffViewer.tsx b/frontend/src/app/pages/AgentChat/DiffViewer.tsx deleted file mode 100644 index 4c07bd0c..00000000 --- a/frontend/src/app/pages/AgentChat/DiffViewer.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import Box from '@mui/material/Box'; -import Typography from '@mui/material/Typography'; -import IconButton from '@mui/material/IconButton'; -import Tooltip from '@mui/material/Tooltip'; -import RefreshIcon from '@mui/icons-material/Refresh'; -import DifferenceIcon from '@mui/icons-material/Difference'; -import { useClaudeTokens } from '@/shared/styles/ThemeContext'; -import { API_BASE } from '@/shared/config'; -import { Skeleton } from '@/app/components/Loading'; - -const AGENTS_API = `${API_BASE}/agents`; - -interface Props { - sessionId: string; -} - -const DiffViewer: React.FC = ({ sessionId }) => { - const c = useClaudeTokens(); - const [diff, setDiff] = useState(''); - const [loading, setLoading] = useState(false); - const [open, setOpen] = useState(false); - - const fetchDiff = async () => { - setLoading(true); - try { - const res = await fetch(`${AGENTS_API}/sessions/${sessionId}/diff`); - const data = await res.json(); - setDiff(data.diff || ''); - } catch { - setDiff('Failed to fetch diff'); - } - setLoading(false); - }; - - useEffect(() => { - if (open) fetchDiff(); - }, [open, sessionId]); - - if (!open) { - return ( - - setOpen(true)} sx={{ color: c.text.tertiary }}> - - - - ); - } - - return ( - - - - Worktree Changes - - - - - - - - setOpen(false)} sx={{ color: c.text.tertiary }}> - × - - - - - {loading ? ( - - {[0, 1, 2, 3, 4, 5, 6].map((i) => ( - - ))} - - ) : diff ? ( -
-            {diff.split('\n').map((line, i) => {
-              let color = c.text.muted;
-              if (line.startsWith('+') && !line.startsWith('+++')) color = c.status.success;
-              else if (line.startsWith('-') && !line.startsWith('---')) color = c.status.error;
-              else if (line.startsWith('@@')) color = c.accent.primary;
-              else if (line.startsWith('diff ') || line.startsWith('index ')) color = c.text.tertiary;
-
-              return (
-                
-                  {line}
-                  {'\n'}
-                
-              );
-            })}
-          
- ) : ( - - No changes detected in the worktree. - - )} -
-
- ); -}; - -export default DiffViewer; diff --git a/frontend/src/app/pages/Views/captureViewThumbnail.ts b/frontend/src/app/pages/Views/captureViewThumbnail.ts index 430af1e8..07ff6891 100644 --- a/frontend/src/app/pages/Views/captureViewThumbnail.ts +++ b/frontend/src/app/pages/Views/captureViewThumbnail.ts @@ -3,7 +3,21 @@ import { toJpeg } from 'html-to-image'; const CAPTURE_WIDTH = 1280; const CAPTURE_HEIGHT = 800; const JPEG_QUALITY = 0.7; -const LOAD_TIMEOUT_MS = 3000; +const LOAD_TIMEOUT_MS = 4000; + +// Workspace file keys are stored relative to the workspace root with no +// leading `./` or `/` — but agent-written HTML routinely references its +// siblings as `./style.css` or `/style.css`. Without normalizing here, +// `files[href]` lookup misses and the iframe renders unstyled, producing +// the broken thumbnails (text-only Markdown Editor, layoutless Calculator, +// etc.) you'd otherwise see on the Apps page. +function lookupFile(href: string, files: Record): string | null { + const candidates = [href, href.replace(/^\.\//, ''), href.replace(/^\//, '')]; + for (const k of candidates) { + if (k in files) return files[k]; + } + return null; +} /** * Inline local CSS/JS references so multi-file views render in a single srcdoc. @@ -19,7 +33,7 @@ function inlineResources(html: string, files: Record): string { if (!hrefMatch) return match; const href = hrefMatch[1]; if (/^(https?:)?\/\//.test(href)) return match; - const content = files[href]; + const content = lookupFile(href, files); if (content == null) return match; return ``; }, @@ -32,7 +46,7 @@ function inlineResources(html: string, files: Record): string { if (!srcMatch) return match; const src = srcMatch[1]; if (/^(https?:)?\/\//.test(src)) return match; - const content = files[src]; + const content = lookupFile(src, files); if (content == null) return match; const typeMatch = attrs.match(/type=["']([^"']+)["']/); const typeAttr = typeMatch ? ` type="${typeMatch[1]}"` : '';