From 9cce64a31e66169148dd347727a19d08b14a0367 Mon Sep 17 00:00:00 2001 From: Rohit Rajan Date: Mon, 11 May 2026 22:00:22 +0530 Subject: [PATCH 01/10] fix: screenshot tab focus flickering --- src/components/run/InterpretationLog.tsx | 59 +++++++++++------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/src/components/run/InterpretationLog.tsx b/src/components/run/InterpretationLog.tsx index 79cf40b1..6d6b988e 100644 --- a/src/components/run/InterpretationLog.tsx +++ b/src/components/run/InterpretationLog.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import SwipeableDrawer from '@mui/material/SwipeableDrawer'; import Typography from '@mui/material/Typography'; import { Button, Grid, Box, TextField, IconButton, Tooltip } from '@mui/material'; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react"; import { useBrowserDimensionsStore } from "../../context/browserDimensions"; import Table from '@mui/material/Table'; import TableBody from '@mui/material/TableBody'; @@ -58,7 +58,7 @@ export const InterpretationLog: React.FC = ({ isOpen, se const previousDataLengths = useRef>(new Map()); const hasAutoFocusedTextTab = useRef(false); const previousGetText = useRef(false); - const autoFocusedScreenshotIndices = useRef>(new Set()); + const autoFocusedScreenshotIds = useRef>(new Set()); const [showPreviewData, setShowPreviewData] = useState(false); const userClosedDrawer = useRef(false); @@ -78,13 +78,15 @@ export const InterpretationLog: React.FC = ({ isOpen, se ) , [browserSteps, getText, currentTextActionId]); - const screenshotData = React.useMemo(() => { - const screenshotSteps = browserSteps.filter((step): step is ScreenshotStep => - step.type === 'screenshot' - ); + const screenshotSteps = React.useMemo(() => + browserSteps.filter((step): step is ScreenshotStep => + step.type === 'screenshot' && Boolean(step.screenshotData) + ) + , [browserSteps]); - return screenshotSteps.filter(step => step.screenshotData).map(step => step.screenshotData!); - }, [browserSteps]); + const screenshotData = React.useMemo(() => + screenshotSteps.map(step => step.screenshotData!) + , [screenshotSteps]); const toggleDrawer = (newOpen: boolean) => (event: React.KeyboardEvent | React.MouseEvent) => { if ( @@ -281,7 +283,6 @@ export const InterpretationLog: React.FC = ({ isOpen, se const handleRemoveScreenshotAction = (screenshotId: number, actionId: string | undefined) => { if (!actionId) return; - const screenshotSteps = browserSteps.filter(step => step.type === 'screenshot' && step.screenshotData); const screenshotIndex = screenshotSteps.findIndex(step => step.id === screenshotId); const screenshotStep = screenshotSteps[screenshotIndex]; const screenshotName = screenshotStep?.name || `Screenshot ${screenshotIndex + 1}`; @@ -343,7 +344,6 @@ export const InterpretationLog: React.FC = ({ isOpen, se setActiveTab(availableTabs.findIndex(tab => tab.id === 'captureText')); } else if (hasNewScreenshotData && availableTabs.findIndex(tab => tab.id === 'captureScreenshot') !== -1) { setActiveTab(availableTabs.findIndex(tab => tab.id === 'captureScreenshot')); - setActiveScreenshotTab(screenshotData.length - 1); } }, [captureListData.length, captureTextData.length, screenshotData.length]); @@ -351,7 +351,7 @@ export const InterpretationLog: React.FC = ({ isOpen, se browserStepsRef.current = browserSteps; }, [browserSteps]); - useEffect(() => { + useLayoutEffect(() => { if (captureListData.length > 0 || captureTextData.length > 0 || screenshotData.length > 0) { setShowPreviewData(true); } else { @@ -377,7 +377,7 @@ export const InterpretationLog: React.FC = ({ isOpen, se setShowPreviewData(false); autoFocusedListIds.current.clear(); previousDataLengths.current.clear(); - autoFocusedScreenshotIndices.current.clear(); + autoFocusedScreenshotIds.current.clear(); userClosedDrawer.current = false; lastListDataLength.current = 0; lastTextDataLength.current = 0; @@ -480,20 +480,6 @@ export const InterpretationLog: React.FC = ({ isOpen, se const screenshotTabIndex = getAvailableTabs().findIndex(tab => tab.id === "captureScreenshot"); if (screenshotTabIndex !== -1) { setActiveTab(screenshotTabIndex); - const latestIndex = screenshotData.length - 1; - setActiveScreenshotTab(latestIndex); - - if (!autoFocusedScreenshotIndices.current.has(latestIndex)) { - autoFocusedScreenshotIndices.current.add(latestIndex); - setTimeout(() => { - const screenshotSteps = browserSteps.filter(step => step.type === "screenshot"); - const latestScreenshotStep = screenshotSteps[latestIndex]; - if (latestScreenshotStep) { - const screenshotName = latestScreenshotStep.name || `Screenshot ${latestIndex + 1}`; - startEdit(latestScreenshotStep.id, 'screenshot', screenshotName); - } - }, 300); - } } } }, 100); @@ -523,12 +509,22 @@ export const InterpretationLog: React.FC = ({ isOpen, se } }, [captureListData.length, isOpen, captureStage]); - useEffect(() => { - if (screenshotData.length > 0 && isOpen) { - const latestScreenshotIndex = screenshotData.length - 1; - setActiveScreenshotTab(latestScreenshotIndex); + useLayoutEffect(() => { + if (screenshotSteps.length > 0 && isOpen) { + const latestScreenshotIndex = screenshotSteps.length - 1; + const latestScreenshot = screenshotSteps[latestScreenshotIndex]; + + if (latestScreenshot && !autoFocusedScreenshotIds.current.has(latestScreenshot.id)) { + autoFocusedScreenshotIds.current.add(latestScreenshot.id); + setActiveScreenshotTab(latestScreenshotIndex); + cancelEdit(); + + setTimeout(() => { + startEdit(latestScreenshot.id, 'screenshot', latestScreenshot.name || `Screenshot ${latestScreenshotIndex + 1}`); + }, 300); + } } - }, [screenshotData.length, isOpen]); + }, [screenshotSteps.length, isOpen]); const { darkMode } = useThemeMode(); @@ -940,7 +936,6 @@ export const InterpretationLog: React.FC = ({ isOpen, se }} > {(() => { - const screenshotSteps = browserSteps.filter(step => step.type === 'screenshot' && step.screenshotData) as Array<{ id: number; name?: string; type: 'screenshot'; fullPage: boolean; actionId?: string; screenshotData?: string }>; return screenshotData.map((screenshot, index) => { const screenshotStep = screenshotSteps[index]; if (!screenshotStep) return null; From c8b1244488d0d79848c4ed45133984039f92e9f1 Mon Sep 17 00:00:00 2001 From: Rohit Rajan Date: Tue, 12 May 2026 11:26:48 +0530 Subject: [PATCH 02/10] feat: enhance output data ui and format ordering --- src/components/run/RunContent.tsx | 689 ++++++++++++++++-------------- 1 file changed, 360 insertions(+), 329 deletions(-) diff --git a/src/components/run/RunContent.tsx b/src/components/run/RunContent.tsx index 99af097a..ce722834 100644 --- a/src/components/run/RunContent.tsx +++ b/src/components/run/RunContent.tsx @@ -8,6 +8,8 @@ import { AccordionSummary, AccordionDetails, Link, + Tooltip, + IconButton, Tabs, Tab } from "@mui/material"; @@ -15,7 +17,19 @@ import * as React from "react"; import { Data } from "./RunsTable"; import { TabPanel, TabContext } from "@mui/lab"; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import ArticleIcon from '@mui/icons-material/Article'; +import ImageIcon from '@mui/icons-material/Image'; +import CodeIcon from '@mui/icons-material/Code'; +import DescriptionIcon from '@mui/icons-material/Description'; +import SubjectIcon from '@mui/icons-material/Subject'; +import LinkIcon from '@mui/icons-material/Link'; +import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; +import TextFieldsIcon from '@mui/icons-material/TextFields'; +import ViewListIcon from '@mui/icons-material/ViewList'; +import TravelExploreIcon from '@mui/icons-material/TravelExplore'; +import SearchIcon from '@mui/icons-material/Search'; +import PsychologyIcon from '@mui/icons-material/Psychology'; +import StorageIcon from '@mui/icons-material/Storage'; +import { ContentCopy } from "@mui/icons-material"; import { useEffect, useState } from "react"; import JSZip from "jszip"; import Table from '@mui/material/Table'; @@ -195,11 +209,14 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe if (!row.serializableOutput) return; + const modernKeys = ['scrapeSchema', 'scrapeList', 'crawl', 'search', 'markdown', 'html', 'textContent', 'text', 'links', 'promptResult']; + const hasModernData = modernKeys.some(key => row.serializableOutput[key]); + const hasLegacyDataInOutput = Object.keys(row.serializableOutput).some(key => !modernKeys.includes(key)); + const hasLegacySchema = row.serializableOutput.scrapeSchema && Array.isArray(row.serializableOutput.scrapeSchema); const hasLegacyList = row.serializableOutput.scrapeList && Array.isArray(row.serializableOutput.scrapeList); - const hasOldFormat = !row.serializableOutput.scrapeSchema && !row.serializableOutput.scrapeList && !row.serializableOutput.crawl && !row.serializableOutput.search && Object.keys(row.serializableOutput).length > 0; - if (hasLegacySchema || hasLegacyList || hasOldFormat) { + if (hasLegacySchema || hasLegacyList || (hasLegacyDataInOutput && !hasModernData)) { processLegacyData(row.serializableOutput); setIsLegacyData(false); return; @@ -794,6 +811,7 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe }> + {title} @@ -882,77 +900,79 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe if (!title || title.trim() === '') { return ( <> - - - - - - {shouldShowAsKeyValue ? ( - <> - theme.palette.mode === 'dark' ? '#11111' : '#f8f9fa' - }} - > - Label - - theme.palette.mode === 'dark' ? '#11111' : '#f8f9fa' - }} - > - Value - - - ) : ( - columns.map((column) => ( - theme.palette.mode === 'dark' ? '#11111' : '#f8f9fa' - }} - > - {column} - - )) - )} - - - + + +
+ + {shouldShowAsKeyValue ? ( - columns.map((column) => ( - - - {column} - - - {data[0][column] === undefined || data[0][column] === "" - ? "-" - : (typeof data[0][column] === 'object' - ? JSON.stringify(data[0][column]) - : String(data[0][column]))} - - - )) + <> + + Label + + + Value + + ) : ( - data.map((row, index) => ( - - {columns.map((column) => ( - - {row[column] === undefined || row[column] === "" - ? "-" - : (typeof row[column] === 'object' - ? JSON.stringify(row[column]) - : String(row[column]))} - - ))} - + columns.map((column) => ( + + {column} + )) )} - -
-
-
+ + + + {shouldShowAsKeyValue ? ( + columns.map((column) => ( + + + {column} + + + {data[0][column] === undefined || data[0][column] === "" + ? "-" + : (typeof data[0][column] === 'object' + ? JSON.stringify(data[0][column]) + : String(data[0][column]))} + + + )) + ) : ( + data.map((row, index) => ( + + {columns.map((column) => ( + + {row[column] === undefined || row[column] === "" + ? "-" + : (typeof row[column] === 'object' + ? JSON.stringify(row[column]) + : String(row[column]))} + + ))} + + )) + )} + + + + - - - - @@ -1061,14 +1040,16 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe <> theme.palette.mode === 'dark' ? '#11111' : '#f8f9fa' + backgroundColor: darkMode ? '#11111' : '#f8f9fa', + minWidth: '100px', + whiteSpace: 'nowrap' }} > Label theme.palette.mode === 'dark' ? '#11111' : '#f8f9fa' + backgroundColor: darkMode ? '#11111' : '#f8f9fa' }} > Value @@ -1079,7 +1060,7 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe theme.palette.mode === 'dark' ? '#11111' : '#f8f9fa' + backgroundColor: darkMode ? '#11111' : '#f8f9fa' }} > {column} @@ -1092,7 +1073,7 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe {shouldShowAsKeyValue ? ( columns.map((column) => ( - + {column} @@ -1122,6 +1103,45 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe
+ + + + + +
); @@ -1142,30 +1162,80 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe return ( - - {hasMarkdown || hasHTML || hasTextFormat || hasLinks ? ( + + {row.status === 'running' || row.status === 'queued' ? ( + <> + + {workflowProgress ? ( + <> + + {getProgressMessage(workflowProgress.percentage)} + + ) : ( + <> + + {t('run_content.loading')} + + )} + + + + ) : (!hasData && !hasScreenshots && !hasMarkdown && !hasHTML && !hasTextFormat && !hasLinks && !hasPromptResult ? ( + + + {t('run_content.no_data_found', 'No data found. Need help?')}{" "} + + {t('run_content.contact_support', 'Contact Support')} + {" "} + {t('run_content.or', 'or')}{" "} + + {t('run_content.open_github_issue', 'Open a GitHub Issue')} + . + + + ) : ( - {hasMarkdown && ( + {hasTextFormat && ( }> - - Markdown + + Text Content - - {markdownContent} - + + {textContent} + - - @@ -1176,28 +1246,29 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe }> - + HTML - + {htmlContent} - + - - @@ -1245,7 +1308,7 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe }> - + Links ({linksContent.length}) @@ -1282,46 +1345,44 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe )} - {showCapturedScreenshotSection && renderCapturedScreenshotsAccordion( - t('run_content.captured_screenshot.title', 'Captured Screenshots'), - screenshotKeys.map((label, index) => ({ key: label, label, value: rawScreenshotKeys[index] })).filter(tab => tab.value), - currentScreenshotIndex, - setCurrentScreenshotIndex, - 'global-screenshots-primary' + {hasPromptResult && promptResultData && ( + + }> + + + Smart Queries + + + + + + {promptResultData} + + + + + + + )} - - ) : ( - <> - {row.status === 'running' || row.status === 'queued' ? ( - <> - - {workflowProgress ? ( - <> - - {getProgressMessage(workflowProgress.percentage)} - - ) : ( - <> - - {t('run_content.loading')} - - )} - - - - ) : (!hasData && !hasScreenshots && !hasMarkdown && !hasHTML && !hasTextFormat && !hasLinks && !hasPromptResult - ? (['failed', 'aborted', 'aborting'].includes(row.status) - ? {t('run_content.failed_no_output', 'Run failed before generating output. Check run logs for details.')} - : {t('run_content.empty_output')}) - : null)} - {hasData && ( - + {(schemaData.length > 0 || listData.length > 0 || legacyData.length > 0) && ( + <> {isLegacyData && ( renderDataTable( legacyData, @@ -1338,6 +1399,7 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe }> + {t('run_content.captured_data.schema_title', 'Captured Texts')} @@ -1363,12 +1425,12 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe cursor: 'pointer', backgroundColor: currentSchemaIndex === idx - ? (theme) => theme.palette.mode === 'dark' + ? darkMode ? '#121111ff' : '#e9ecef' : 'transparent', borderBottom: currentSchemaIndex === idx ? '3px solid #FF00C3' : 'none', - color: (theme) => theme.palette.mode === 'dark' ? '#fff' : '#000', + color: darkMode ? '#fff' : '#000', }} > {key} @@ -1393,6 +1455,7 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe }> + {t('run_content.captured_data.list_title', 'Captured Lists')} @@ -1417,12 +1480,12 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe cursor: 'pointer', backgroundColor: currentListIndex === idx - ? (theme) => theme.palette.mode === 'dark' + ? darkMode ? '#121111ff' : '#e9ecef' : 'transparent', borderBottom: currentListIndex === idx ? '3px solid #FF00C3' : 'none', - color: (theme) => theme.palette.mode === 'dark' ? '#fff' : '#000', + color: darkMode ? '#fff' : '#000', }} > {key} @@ -1438,7 +1501,7 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe theme.palette.mode === 'dark' ? '#11111' : '#f8f9fa' + backgroundColor: darkMode ? '#11111' : '#f8f9fa' }} > {column} @@ -1531,11 +1594,14 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe )} )} + + )} {crawlData.length > 0 && crawlData[0] && crawlData[0].length > 0 && ( }> + Crawl Results @@ -1600,6 +1666,7 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe }> + Metadata @@ -1648,7 +1715,7 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe }> - Text Content + Text Content @@ -1667,7 +1734,8 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe whiteSpace: 'pre-wrap', wordBreak: 'break-word', fontFamily: 'monospace', - fontSize: '0.75rem', + fontSize: '14px', + lineHeight: 1.6, m: 0 }} > @@ -1698,7 +1766,7 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe }> - HTML + HTML @@ -1717,7 +1785,8 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe whiteSpace: 'pre-wrap', wordBreak: 'break-word', fontFamily: 'monospace', - fontSize: '0.65rem', + fontSize: '14px', + lineHeight: 1.6, m: 0 }} > @@ -1748,7 +1817,7 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe }> - Markdown + Markdown @@ -1767,7 +1836,8 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe whiteSpace: 'pre-wrap', wordBreak: 'break-word', fontFamily: 'monospace', - fontSize: '0.875rem', + fontSize: '14px', + lineHeight: 1.6, m: 0 }} > @@ -1793,18 +1863,6 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe )} - {renderCapturedScreenshotsAccordion( - t('run_content.captured_screenshot.title', 'Captured Screenshots'), - [ - ...(crawlData[0][currentCrawlIndex].screenshotVisible ? [{ key: 'visible', label: 'Screenshot (Visible)', value: crawlData[0][currentCrawlIndex].screenshotVisible }] : []), - ...(crawlData[0][currentCrawlIndex].screenshotFullpage ? [{ key: 'fullpage', label: 'Screenshot (Full Page)', value: crawlData[0][currentCrawlIndex].screenshotFullpage }] : []), - ], - currentCrawlScreenshotTab, - setCurrentCrawlScreenshotTab, - `crawl-${currentCrawlIndex}`, - false - )} - {(() => { const validLinks = crawlData[0][currentCrawlIndex].links?.filter((link: any) => typeof link === 'string' && link.trim() !== '' @@ -1815,7 +1873,7 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe }> - Links ({validLinks.length}) + Links ({validLinks.length}) @@ -1848,6 +1906,18 @@ export const RunContent = ({ row, currentLog, interpretationInProgress, logEndRe ); })()} + {renderCapturedScreenshotsAccordion( + t('run_content.captured_screenshot.title', 'Captured Screenshots'), + [ + ...(crawlData[0][currentCrawlIndex].screenshotVisible ? [{ key: 'visible', label: 'Screenshot (Visible)', value: crawlData[0][currentCrawlIndex].screenshotVisible }] : []), + ...(crawlData[0][currentCrawlIndex].screenshotFullpage ? [{ key: 'fullpage', label: 'Screenshot (Full Page)', value: crawlData[0][currentCrawlIndex].screenshotFullpage }] : []), + ], + currentCrawlScreenshotTab, + setCurrentCrawlScreenshotTab, + `crawl-${currentCrawlIndex}`, + false + )} +