From be0b0560ffb56032339bb9bd955e2d31554f715e Mon Sep 17 00:00:00 2001 From: Rohit Rajan Date: Thu, 16 Apr 2026 13:25:18 +0530 Subject: [PATCH] fix: back and forth pagination state capture --- src/components/browser/BrowserWindow.tsx | 168 ++++++++++++++++++--- src/components/recorder/RightSidePanel.tsx | 81 ++++------ src/context/browserActions.tsx | 5 +- 3 files changed, 187 insertions(+), 67 deletions(-) diff --git a/src/components/browser/BrowserWindow.tsx b/src/components/browser/BrowserWindow.tsx index 4290564c..972ca75b 100644 --- a/src/components/browser/BrowserWindow.tsx +++ b/src/components/browser/BrowserWindow.tsx @@ -1,10 +1,10 @@ -import React, { useCallback, useContext, useEffect, useState } from 'react'; +import React, { useCallback, useContext, useEffect, useRef, useState } from 'react'; import { generateUUID } from '../../helpers/uuid'; import { useSocketStore } from '../../context/socket'; import { Button } from '@mui/material'; import { GenericModal } from '../ui/GenericModal'; import { useActionContext } from '../../context/browserActions'; -import { useBrowserSteps, TextStep, ListStep } from '../../context/browserSteps'; +import { useBrowserSteps, TextStep, ListStep, BrowserStep } from '../../context/browserSteps'; import { useGlobalInfoStore } from '../../context/globalInfo'; import { useTranslation } from 'react-i18next'; import { AuthContext } from '../../context/auth'; @@ -86,6 +86,7 @@ export const BrowserWindow = () => { const [listSelector, setListSelector] = useState(null); const [fields, setFields] = useState>({}); const [paginationSelector, setPaginationSelector] = useState(''); + const manualHighlightedSelectorRef = useRef(""); const [isCachingChildSelectors, setIsCachingChildSelectors] = useState(false); const [cachedListSelector, setCachedListSelector] = useState( @@ -1158,6 +1159,7 @@ export const BrowserWindow = () => { } setPaginationSelector(highlighterData.selector); + applyManualPaginationHighlight(highlighterData.selector); notify( `info`, t( @@ -1402,6 +1404,7 @@ export const BrowserWindow = () => { } setPaginationSelector(highlighterData.selector); + applyManualPaginationHighlight(highlighterData.selector); notify( `info`, t( @@ -1574,9 +1577,127 @@ export const BrowserWindow = () => { setAttributeOptions([]); }; + const manualHighlightedElementsRef = useRef([]); + + const applyManualPaginationHighlight = useCallback((selector: string) => { + if (!selector) return; + let iframeElement = document.querySelector("#dom-browser-iframe") as HTMLIFrameElement; + if (!iframeElement) { + iframeElement = document.querySelector("#browser-window iframe") as HTMLIFrameElement; + } + if (!iframeElement?.contentDocument) return; + const doc = iframeElement.contentDocument; + + if (manualHighlightedElementsRef.current.length > 0) { + manualHighlightedElementsRef.current.forEach((el) => { + if (el) { + el.style.outline = ""; + el.style.outlineOffset = ""; + el.style.zIndex = ""; + } + }); + manualHighlightedElementsRef.current = []; + } + + const prev = manualHighlightedSelectorRef.current; + if (prev) { + try { + const prevEls = + prev.startsWith("//") || prev.startsWith("(//") + ? (() => { + const r = doc.evaluate(prev, doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + const arr: Element[] = []; + for (let i = 0; i < r.snapshotLength; i++) { + const n = r.snapshotItem(i); + if (n && n.nodeType === Node.ELEMENT_NODE) arr.push(n as Element); + } + return arr; + })() + : Array.from(doc.querySelectorAll(prev)); + prevEls.forEach((el) => { + (el as HTMLElement).style.outline = ""; + (el as HTMLElement).style.outlineOffset = ""; + (el as HTMLElement).style.zIndex = ""; + }); + } catch (e) {} + } + + try { + const elements: HTMLElement[] = ( + selector.startsWith("//") || selector.startsWith("(//") + ? (() => { + const r = doc.evaluate(selector, doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + const arr: HTMLElement[] = []; + for (let i = 0; i < r.snapshotLength; i++) { + const n = r.snapshotItem(i); + if (n && n.nodeType === Node.ELEMENT_NODE) arr.push(n as HTMLElement); + } + return arr; + })() + : Array.from(doc.querySelectorAll(selector)) + ) as HTMLElement[]; + + elements.forEach((el) => { + el.style.outline = "3px dashed #ff00c3"; + el.style.outlineOffset = "2px"; + el.style.zIndex = "9999"; + }); + + manualHighlightedElementsRef.current = elements; + manualHighlightedSelectorRef.current = selector; + } catch (error) { + console.error("Error applying manual pagination highlight:", error); + } + }, []); + + const clearManualPaginationHighlight = useCallback(() => { + if (manualHighlightedElementsRef.current.length > 0) { + manualHighlightedElementsRef.current.forEach((el) => { + if (el) { + el.style.outline = ""; + el.style.outlineOffset = ""; + el.style.zIndex = ""; + } + }); + manualHighlightedElementsRef.current = []; + } + + const selector = manualHighlightedSelectorRef.current; + if (!selector) return; + let iframeElement = document.querySelector("#dom-browser-iframe") as HTMLIFrameElement; + if (!iframeElement) { + iframeElement = document.querySelector("#browser-window iframe") as HTMLIFrameElement; + } + if (!iframeElement?.contentDocument) return; + const doc = iframeElement.contentDocument; + try { + const elements: Element[] = + selector.startsWith("//") || selector.startsWith("(//") + ? (() => { + const r = doc.evaluate(selector, doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + const arr: Element[] = []; + for (let i = 0; i < r.snapshotLength; i++) { + const n = r.snapshotItem(i); + if (n && n.nodeType === Node.ELEMENT_NODE) arr.push(n as Element); + } + return arr; + })() + : Array.from(doc.querySelectorAll(selector)); + elements.forEach((el) => { + (el as HTMLElement).style.outline = ""; + (el as HTMLElement).style.outlineOffset = ""; + (el as HTMLElement).style.zIndex = ""; + }); + } catch (e) { + console.error("Error clearing manual pagination highlight:", e); + } + manualHighlightedSelectorRef.current = ""; + }, []); + const resetPaginationSelector = useCallback(() => { - setPaginationSelector(''); - }, []); + clearManualPaginationHighlight(); + setPaginationSelector(''); + }, [clearManualPaginationHighlight]); useEffect(() => { if (!paginationMode) { @@ -1593,27 +1714,40 @@ export const BrowserWindow = () => { useEffect(() => { if (paginationMode && currentListActionId) { const currentListStep = browserSteps.find( - step => step.type === 'list' && step.actionId === currentListActionId - ) as (ListStep & { type: 'list' }) | undefined; + (step) => step.type === "list" && step.actionId === currentListActionId + ) as (BrowserStep & { type: "list" }) | undefined; - const currentSelector = currentListStep?.pagination?.selector; + const stepSelector = currentListStep?.pagination?.selector; const currentType = currentListStep?.pagination?.type; - if (['clickNext', 'clickLoadMore'].includes(paginationType)) { - if (!currentSelector || (currentType && currentType !== paginationType)) { - setPaginationSelector(''); + if (["clickNext", "clickLoadMore"].includes(paginationType)) { + if (!stepSelector || (currentType && currentType !== paginationType)) { + clearManualPaginationHighlight(); + setPaginationSelector(""); } } - const stepSelector = currentListStep?.pagination?.selector; - - if (stepSelector && !paginationSelector) { - setPaginationSelector(stepSelector); - } else if (!stepSelector && paginationSelector) { - setPaginationSelector(''); + if (stepSelector) { + if (paginationSelector !== stepSelector) { + setPaginationSelector(stepSelector); + } + if (manualHighlightedSelectorRef.current !== stepSelector) { + applyManualPaginationHighlight(stepSelector); + } + } else if (paginationSelector) { + clearManualPaginationHighlight(); + setPaginationSelector(""); } } - }, [browserSteps, paginationMode, currentListActionId, paginationSelector]); + }, [ + browserSteps, + paginationMode, + currentListActionId, + paginationSelector, + clearManualPaginationHighlight, + applyManualPaginationHighlight, + paginationType, + ]); return (
= ({ onFinishCapture handleStopGetList(); setCurrentListActionId(''); resetInterpretationLog(); + setAutoDetectedPagination(null); finishAction('list'); onFinishCapture(); clientSelectorGenerator.cleanup(); @@ -445,6 +446,7 @@ export const RightSidePanel: React.FC = ({ onFinishCapture handleStopGetList(); setCurrentListActionId(''); resetInterpretationLog(); + setAutoDetectedPagination(null); finishAction('list'); onFinishCapture(); clientSelectorGenerator.cleanup(); @@ -598,12 +600,6 @@ export const RightSidePanel: React.FC = ({ onFinishCapture const elements = evaluateSelector(detectionResult.selector, iframeDoc); if (elements.length > 0) { - elements.forEach((el: Element) => { - (el as HTMLElement).style.outline = '3px dashed #ff00c3'; - (el as HTMLElement).style.outlineOffset = '2px'; - (el as HTMLElement).style.zIndex = '9999'; - }); - const firstElement = elements[0] as HTMLElement; const elementRect = firstElement.getBoundingClientRect(); const iframeWindow = iframeElement.contentWindow; @@ -721,14 +717,41 @@ export const RightSidePanel: React.FC = ({ onFinishCapture setShowPaginationOptions(true); setCaptureStage('pagination'); break; - case 'pagination': + case "pagination": + if (autoDetectedPagination) { + setAutoDetectedPagination(null); + } + + if (currentListActionId) { + const currentListStep = browserSteps.find( + (step) => step.type === "list" && step.actionId === currentListActionId + ) as (BrowserStep & { type: "list" }) | undefined; + + if (currentListStep?.pagination?.selector) { + updateListStepPagination(currentListStep.id, { + type: "", + selector: null, + }); + } + } + stopPaginationMode(); setShowPaginationOptions(false); setAutoDetectedPagination(null); - setCaptureStage('initial'); + updatePaginationType(""); + setCaptureStage("initial"); break; } - }, [captureStage, stopLimitMode, startPaginationMode, stopPaginationMode]); + }, [captureStage, + stopLimitMode, + startPaginationMode, + stopPaginationMode, + autoDetectedPagination, + setAutoDetectedPagination, + currentListActionId, + browserSteps, + updateListStepPagination, + updatePaginationType]); const handlePaginationSettingSelect = (option: PaginationType) => { updatePaginationType(option); @@ -761,46 +784,6 @@ export const RightSidePanel: React.FC = ({ onFinishCapture } } - if (autoDetectedPagination?.selector) { - const iframeElement = document.querySelector('#browser-window iframe') as HTMLIFrameElement; - if (iframeElement?.contentDocument) { - try { - function evaluateSelector(selector: string, doc: Document): Element[] { - if (selector.startsWith('//') || selector.startsWith('(//')) { - try { - const result = doc.evaluate(selector, doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); - const elements: Element[] = []; - for (let i = 0; i < result.snapshotLength; i++) { - const node = result.snapshotItem(i); - if (node && node.nodeType === Node.ELEMENT_NODE) { - elements.push(node as Element); - } - } - return elements; - } catch (err) { - return []; - } - } else { - try { - return Array.from(doc.querySelectorAll(selector)); - } catch (err) { - return []; - } - } - } - - const elements = evaluateSelector(autoDetectedPagination.selector, iframeElement.contentDocument); - elements.forEach((el: Element) => { - (el as HTMLElement).style.outline = ''; - (el as HTMLElement).style.outlineOffset = ''; - (el as HTMLElement).style.zIndex = ''; - }); - } catch (error) { - console.error('Error removing pagination highlight on discard:', error); - } - } - } - resetListState(); stopPaginationMode(); stopLimitMode(); diff --git a/src/context/browserActions.tsx b/src/context/browserActions.tsx index 1a8b8518..8e5b2f8a 100644 --- a/src/context/browserActions.tsx +++ b/src/context/browserActions.tsx @@ -110,8 +110,11 @@ export const ActionProvider = ({ children }: { children: ReactNode }) => { }; const stopPaginationMode = () => { - setPaginationMode(false), + setPaginationMode(false); socket?.emit('setPaginationMode', { pagination: false }); + if (getList) { + socket?.emit('setGetList', { getList: true }); + } }; const startLimitMode = () => {