Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
222 changes: 82 additions & 140 deletions src/components/run/InterpretationLog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,14 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
const [editingField, setEditingField] = useState<{listId: number, fieldKey: string} | null>(null);
const [editingValue, setEditingValue] = useState<string>('');

const [editingListName, setEditingListName] = useState<number | null>(null);
const [editingListNameValue, setEditingListNameValue] = useState<string>('');

const [editingTextGroupName, setEditingTextGroupName] = useState<boolean>(false);
const [editingTextGroupNameValue, setEditingTextGroupNameValue] = useState<string>('Text Data');

const [editingTextLabel, setEditingTextLabel] = useState<number | null>(null);
const [editingTextLabelValue, setEditingTextLabelValue] = useState<string>('');

const [editingScreenshotName, setEditingScreenshotName] = useState<number | null>(null);
const [editingScreenshotNameValue, setEditingScreenshotNameValue] = useState<string>('');
const [editing, setEditing] = useState<{
stepId: number | null;
type: 'list' | 'text' | 'screenshot' | null;
value: string;
}>({ stepId: null, type: null, value: '' });

const logEndRef = useRef<HTMLDivElement | null>(null);
const autoFocusedListIds = useRef<Set<number>>(new Set());
Expand Down Expand Up @@ -125,30 +122,6 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
}
};

const handleStartEditListName = (listId: number, currentName: string) => {
setEditingListName(listId);
setEditingListNameValue(currentName);
};

const handleSaveListName = () => {
if (editingListName !== null) {
const trimmedName = editingListNameValue.trim();
const finalName = trimmedName || `List Data ${captureListData.findIndex(l => l.id === editingListName) + 1}`;

updateListStepName(editingListName, finalName);

// Use ref-synced version of browserSteps via emitForStepId
const listStep = browserSteps.find(step => step.id === editingListName);
if (listStep?.actionId) {
// small async delay ensures React state commit
setTimeout(() => emitForStepId(listStep.actionId!), 0);
}

setEditingListName(null);
setEditingListNameValue('');
}
};

const handleStartEditTextGroupName = () => {
setEditingTextGroupName(true);
setEditingTextGroupNameValue(currentTextGroupName);
Expand All @@ -158,7 +131,6 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
const trimmedName = editingTextGroupNameValue.trim();
const finalName = trimmedName || 'Text Data';

console.log("SAVING TEXT GROUP NAME:", finalName);
setCurrentTextGroupName(finalName);
setEditingTextGroupName(false);

Expand All @@ -169,34 +141,6 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
}, 0);
};


const handleStartEditTextLabel = (textId: number, currentLabel: string) => {
setEditingTextLabel(textId);
setEditingTextLabelValue(currentLabel);
};

const handleSaveTextLabel = () => {
if (editingTextLabel !== null && editingTextLabelValue.trim()) {
const textStep = browserSteps.find(step => step.id === editingTextLabel);
const actionId = textStep?.actionId;

updateBrowserTextStepLabel(editingTextLabel, editingTextLabelValue.trim());

// Emit updated action to backend after state update completes
if (actionId) {
setTimeout(() => emitForStepId(actionId), 0);
}

setEditingTextLabel(null);
setEditingTextLabelValue('');
}
};

const handleCancelTextLabel = () => {
setEditingTextLabel(null);
setEditingTextLabelValue('');
};

const handleDeleteTextStep = (textId: number) => {
const textStep = browserSteps.find(step => step.id === textId);
const actionId = textStep?.actionId;
Expand All @@ -210,36 +154,36 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
}
};

const handleStartEditScreenshotName = (screenshotStepId: number, currentName: string) => {
setEditingScreenshotName(screenshotStepId);
setEditingScreenshotNameValue(currentName);
const startEdit = (stepId: number, type: 'list' | 'text' | 'screenshot', currentValue: string) => {
setEditing({ stepId, type, value: currentValue });
};

const handleSaveScreenshotName = () => {
if (editingScreenshotName !== null) {
const trimmedName = editingScreenshotNameValue.trim();
const screenshotSteps = browserSteps.filter(step => step.type === 'screenshot');
const screenshotIndex = screenshotSteps.findIndex(s => s.id === editingScreenshotName);
const finalName = trimmedName || `Screenshot ${screenshotIndex + 1}`;

updateScreenshotStepName(editingScreenshotName, finalName);

const screenshotStep = browserSteps.find(step => step.id === editingScreenshotName);
if (screenshotStep?.actionId) {
const originalName = screenshotStep.name?.trim() || "";
const trimmedName = editingScreenshotNameValue.trim();

// 🚫 Only emit if name actually changed
if (trimmedName && trimmedName !== originalName) {
setTimeout(() => emitForStepId(screenshotStep.actionId!), 500);
} else {
console.log("🧠 Skipping emit — screenshot name unchanged.");
}
}
const saveEdit = () => {
const { stepId, type, value } = editing;
if (stepId == null || !type) return;

setEditingScreenshotName(null);
setEditingScreenshotNameValue('');
const finalValue = value.trim();
if (!finalValue) {
setEditing({ stepId: null, type: null, value: '' });
return;
}

if (type === 'list') {
updateListStepName(stepId, finalValue);
} else if (type === 'text') {
updateBrowserTextStepLabel(stepId, finalValue);
} else if (type === 'screenshot') {
updateScreenshotStepName(stepId, finalValue);
}

const step = browserSteps.find(s => s.id === stepId);
if (step?.actionId) setTimeout(() => emitForStepId(step.actionId!), 0);

setEditing({ stepId: null, type: null, value: '' });
};

const cancelEdit = () => {
setEditing({ stepId: null, type: null, value: '' });
};


Expand Down Expand Up @@ -354,8 +298,6 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se

useEffect(() => {
let shouldOpenDrawer = false;
let switchToTextTab = false;
let switchToScreenshotTab = false;

if (hasScrapeListAction && captureListData.length > 0 && captureListData[0]?.data?.length > 0) {
setShowPreviewData(true);
Expand All @@ -371,7 +313,6 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
if (captureTextData.length > lastTextDataLength.current) {
userClosedDrawer.current = false;
shouldOpenDrawer = true;
switchToTextTab = true;
}
lastTextDataLength.current = captureTextData.length;
}
Expand All @@ -381,23 +322,35 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
if (screenshotData.length > lastScreenshotDataLength.current) {
userClosedDrawer.current = false;
shouldOpenDrawer = true;
switchToScreenshotTab = true;
}
lastScreenshotDataLength.current = screenshotData.length;
}

const getLatestCaptureType = () => {
for (let i = browserSteps.length - 1; i >= 0; i--) {
const type = browserSteps[i].type;
if (type === "list" || type === "text" || type === "screenshot") {
return type;
}
}
return null;
};

if (shouldOpenDrawer) {
setIsOpen(true);
if (switchToTextTab) {
setTimeout(() => {
const textTabIndex = getAvailableTabs().findIndex(tab => tab.id === 'captureText');
if (textTabIndex !== -1) {
setActiveTab(textTabIndex);
}
}, 100);
} else if (switchToScreenshotTab) {
setTimeout(() => {
const screenshotTabIndex = getAvailableTabs().findIndex(tab => tab.id === 'captureScreenshot');
const latestType = getLatestCaptureType();

setTimeout(() => {
if (latestType === "text") {
const idx = getAvailableTabs().findIndex(t => t.id === "captureText");
if (idx !== -1) setActiveTab(idx);

} else if (latestType === "list") {
const idx = getAvailableTabs().findIndex(t => t.id === "captureList");
if (idx !== -1) setActiveTab(idx);

} else if (latestType === "screenshot") {
const screenshotTabIndex = getAvailableTabs().findIndex(tab => tab.id === "captureScreenshot");
if (screenshotTabIndex !== -1) {
setActiveTab(screenshotTabIndex);
const latestIndex = screenshotData.length - 1;
Expand All @@ -406,25 +359,25 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
if (!autoFocusedScreenshotIndices.current.has(latestIndex)) {
autoFocusedScreenshotIndices.current.add(latestIndex);
setTimeout(() => {
const screenshotSteps = browserSteps.filter(step => step.type === 'screenshot') as Array<{ id: number; name?: string; type: 'screenshot' }>;
const screenshotSteps = browserSteps.filter(step => step.type === "screenshot");
const latestScreenshotStep = screenshotSteps[latestIndex];
if (latestScreenshotStep) {
const screenshotName = latestScreenshotStep.name || `Screenshot ${latestIndex + 1}`;
handleStartEditScreenshotName(latestScreenshotStep.id, screenshotName);
startEdit(latestScreenshotStep.id, 'screenshot', screenshotName);
}
Comment on lines +362 to 367
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix screenshot auto-edit targeting.

latestIndex is derived from screenshotData, which only includes steps where step.screenshotData exists. However, the follow-up lookup builds screenshotSteps from all screenshot steps (including ones without data), so the indexes can drift and we end up renaming the wrong step—or failing to rename at all—whenever a screenshot action is recorded before its payload arrives. Filter the steps the same way you populated screenshotData before indexing.

Apply this diff to keep the arrays aligned:

-                const screenshotSteps = browserSteps.filter(step => step.type === "screenshot");
-                const latestScreenshotStep = screenshotSteps[latestIndex];
+                const screenshotStepsWithData = browserSteps.filter(
+                  (step): step is { id: number; name?: string; type: 'screenshot'; screenshotData?: string } =>
+                    step.type === "screenshot" && step.screenshotData
+                );
+                const latestScreenshotStep = screenshotStepsWithData[latestIndex];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const screenshotSteps = browserSteps.filter(step => step.type === "screenshot");
const latestScreenshotStep = screenshotSteps[latestIndex];
if (latestScreenshotStep) {
const screenshotName = latestScreenshotStep.name || `Screenshot ${latestIndex + 1}`;
handleStartEditScreenshotName(latestScreenshotStep.id, screenshotName);
startEdit(latestScreenshotStep.id, 'screenshot', screenshotName);
}
const screenshotStepsWithData = browserSteps.filter(
(step): step is { id: number; name?: string; type: 'screenshot'; screenshotData?: string } =>
step.type === "screenshot" && step.screenshotData
);
const latestScreenshotStep = screenshotStepsWithData[latestIndex];
if (latestScreenshotStep) {
const screenshotName = latestScreenshotStep.name || `Screenshot ${latestIndex + 1}`;
startEdit(latestScreenshotStep.id, 'screenshot', screenshotName);
}
🤖 Prompt for AI Agents
In src/components/run/InterpretationLog.tsx around lines 362 to 367, the code
builds screenshotSteps from all steps but latestIndex was computed from
screenshotData (only steps with step.screenshotData), causing index drift; fix
by constructing screenshotSteps using the same filter used for screenshotData
(i.e., only include steps with step.screenshotData) so latestIndex maps to the
correct element, then use that filtered array to pick latestScreenshotStep and
call startEdit as before.

}, 300);
}
}
}, 100);
}
}
}, 100);
}
}, [hasScrapeListAction, hasScrapeSchemaAction, hasScreenshotAction, captureListData, captureTextData, screenshotData, setIsOpen, getText]);

useEffect(() => {
if (captureListData.length > 0 && isOpen && captureStage === 'initial') {
const latestListIndex = captureListData.length - 1;
const latestList = captureListData[latestListIndex];
if (latestList && latestList.data && latestList.data.length > 0 && !editingListName) {
if (latestList && latestList.data && latestList.data.length > 0 && editing.type !== 'list') {
const previousLength = previousDataLengths.current.get(latestList.id) || 0;
const currentLength = latestList.data.length;

Expand All @@ -433,7 +386,7 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
autoFocusedListIds.current.add(latestList.id);
setActiveListTab(latestListIndex);
setTimeout(() => {
handleStartEditListName(latestList.id, latestList.name || `List Data ${latestListIndex + 1}`);
startEdit(latestList.id, 'list', latestList.name || `List Data ${latestListIndex + 1}`);
}, 300);
}
}
Expand Down Expand Up @@ -579,7 +532,7 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
}}
>
{captureListData.map((listItem, index) => {
const isEditing = editingListName === listItem.id;
const isEditing = editing.stepId === listItem.id && editing.type === 'list';
const isActive = activeListTab === index;

return (
Expand All @@ -597,10 +550,7 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
}
}}
onDoubleClick={() => {
handleStartEditListName(
listItem.id,
listItem.name || `List Data ${index + 1}`
);
startEdit(listItem.id, 'list', listItem.name || `List Data ${index + 1}`)
}}
sx={{
px: 3,
Expand Down Expand Up @@ -638,15 +588,12 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
>
{isEditing ? (
<TextField
value={editingListNameValue}
onChange={(e) => setEditingListNameValue(e.target.value)}
onBlur={handleSaveListName}
value={editing.value}
onChange={(e) => setEditing({ ...editing, value: e.target.value })}
onBlur={saveEdit}
onKeyDown={(e) => {
if (e.key === 'Enter') handleSaveListName();
if (e.key === 'Escape') {
setEditingListName(null);
setEditingListNameValue('');
}
if (e.key === 'Enter') saveEdit();
if (e.key === 'Escape') cancelEdit();
}}
autoFocus
size="small"
Expand Down Expand Up @@ -842,7 +789,7 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
if (!screenshotStep) return null;

const isActive = activeScreenshotTab === index;
const isEditing = editingScreenshotName === screenshotStep.id;
const isEditing = editing.stepId === screenshotStep.id && editing.type === 'screenshot';
const screenshotName = screenshotStep.name || `Screenshot ${index + 1}`;

return (
Expand All @@ -858,9 +805,7 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
setActiveScreenshotTab(index);
}
}}
onDoubleClick={() => {
handleStartEditScreenshotName(screenshotStep.id, screenshotName);
}}
onDoubleClick={() => startEdit(screenshotStep.id, 'screenshot', screenshotName)}
sx={{
px: 3,
py: 1.25,
Expand Down Expand Up @@ -895,15 +840,12 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
>
{isEditing ? (
<TextField
value={editingScreenshotNameValue}
onChange={(e) => setEditingScreenshotNameValue(e.target.value)}
onBlur={handleSaveScreenshotName}
value={editing.value}
onChange={(e) => setEditing({ ...editing, value: e.target.value })}
onBlur={saveEdit}
onKeyDown={(e) => {
if (e.key === 'Enter') handleSaveScreenshotName();
if (e.key === 'Escape') {
setEditingScreenshotName(null);
setEditingScreenshotNameValue('');
}
if (e.key === 'Enter') saveEdit();
if (e.key === 'Escape') cancelEdit();
}}
autoFocus
size="small"
Expand Down Expand Up @@ -1059,7 +1001,7 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
</TableHead>
<TableBody>
{captureTextData.map((textStep: any, index) => {
const isEditing = editingTextLabel === textStep.id;
const isEditing = editing.stepId === textStep.id && editing.type === 'text';

return (
<TableRow
Expand All @@ -1083,12 +1025,12 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
{isEditing ? (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, minWidth: '200px' }}>
<TextField
value={editingTextLabelValue}
onChange={(e) => setEditingTextLabelValue(e.target.value)}
onBlur={handleSaveTextLabel}
value={editing.value}
onChange={(e) => setEditing({ ...editing, value: e.target.value })}
onBlur={saveEdit}
onKeyDown={(e) => {
if (e.key === 'Enter') handleSaveTextLabel();
if (e.key === 'Escape') handleCancelTextLabel();
if (e.key === 'Enter') saveEdit();
if (e.key === 'Escape') cancelEdit();
}}
autoFocus
size="small"
Expand All @@ -1102,7 +1044,7 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
/>
<IconButton
size="small"
onClick={handleSaveTextLabel}
onClick={saveEdit}
sx={{
color: '#4caf50',
padding: '4px'
Expand All @@ -1124,7 +1066,7 @@ export const InterpretationLog: React.FC<InterpretationLogProps> = ({ isOpen, se
textDecoration: 'underline'
}
}}
onClick={() => handleStartEditTextLabel(textStep.id, textStep.label)}
onClick={() => startEdit(textStep.id, 'text', textStep.label)}
>
{textStep.label}
</Typography>
Expand Down