Merge branch 'develop' into perfect-ui
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -98,6 +98,7 @@ interface RRWebDOMBrowserRendererProps {
|
||||
getList?: boolean;
|
||||
getText?: boolean;
|
||||
listSelector?: string | null;
|
||||
cachedChildSelectors?: string[];
|
||||
paginationMode?: boolean;
|
||||
paginationType?: string;
|
||||
limitMode?: boolean;
|
||||
@@ -106,12 +107,14 @@ interface RRWebDOMBrowserRendererProps {
|
||||
selector: string;
|
||||
elementInfo: ElementInfo | null;
|
||||
childSelectors?: string[];
|
||||
groupInfo?: any;
|
||||
}) => void;
|
||||
onElementSelect?: (data: {
|
||||
rect: DOMRect;
|
||||
selector: string;
|
||||
elementInfo: ElementInfo | null;
|
||||
childSelectors?: string[];
|
||||
groupInfo?: any;
|
||||
}) => void;
|
||||
onShowDatePicker?: (info: {
|
||||
coordinates: { x: number; y: number };
|
||||
@@ -144,6 +147,7 @@ export const DOMBrowserRenderer: React.FC<RRWebDOMBrowserRendererProps> = ({
|
||||
getList = false,
|
||||
getText = false,
|
||||
listSelector = null,
|
||||
cachedChildSelectors = [],
|
||||
paginationMode = false,
|
||||
paginationType = "",
|
||||
limitMode = false,
|
||||
@@ -205,11 +209,24 @@ export const DOMBrowserRenderer: React.FC<RRWebDOMBrowserRendererProps> = ({
|
||||
const handleDOMHighlighting = useCallback(
|
||||
(x: number, y: number, iframeDoc: Document) => {
|
||||
try {
|
||||
if (!getText && !getList) {
|
||||
setCurrentHighlight(null);
|
||||
if (onHighlight) {
|
||||
onHighlight({
|
||||
rect: new DOMRect(0, 0, 0, 0),
|
||||
selector: "",
|
||||
elementInfo: null,
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const highlighterData =
|
||||
clientSelectorGenerator.generateDataForHighlighter(
|
||||
{ x, y },
|
||||
iframeDoc,
|
||||
true
|
||||
true,
|
||||
cachedChildSelectors
|
||||
);
|
||||
|
||||
if (!highlighterData) {
|
||||
@@ -224,70 +241,40 @@ export const DOMBrowserRenderer: React.FC<RRWebDOMBrowserRendererProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const { rect, selector, elementInfo, childSelectors } = highlighterData;
|
||||
const { rect, selector, elementInfo, childSelectors, groupInfo } =
|
||||
highlighterData;
|
||||
|
||||
let shouldHighlight = false;
|
||||
|
||||
if (getList) {
|
||||
if (listSelector) {
|
||||
const hasValidChildSelectors =
|
||||
Array.isArray(childSelectors) && childSelectors.length > 0;
|
||||
|
||||
// First phase: Allow any group to be highlighted for selection
|
||||
if (!listSelector && groupInfo?.isGroupElement) {
|
||||
shouldHighlight = true;
|
||||
}
|
||||
// Second phase: Show valid children within selected group
|
||||
else if (listSelector) {
|
||||
if (limitMode) {
|
||||
shouldHighlight = false;
|
||||
} else if (paginationMode) {
|
||||
if (
|
||||
paginationType !== "" &&
|
||||
!["none", "scrollDown", "scrollUp"].includes(paginationType)
|
||||
) {
|
||||
shouldHighlight = true;
|
||||
} else {
|
||||
shouldHighlight = false;
|
||||
}
|
||||
} else if (childSelectors && childSelectors.includes(selector)) {
|
||||
} else if (
|
||||
paginationMode &&
|
||||
paginationType !== "" &&
|
||||
!["none", "scrollDown", "scrollUp"].includes(paginationType)
|
||||
) {
|
||||
shouldHighlight = true;
|
||||
} else if (childSelectors && childSelectors.length > 0) {
|
||||
console.log("✅ Child selectors present, highlighting enabled");
|
||||
shouldHighlight = true;
|
||||
} else if (elementInfo?.isIframeContent && childSelectors) {
|
||||
const isIframeChild = childSelectors.some(
|
||||
(childSelector: string) =>
|
||||
selector.includes(":>>") &&
|
||||
childSelector
|
||||
.split(":>>")
|
||||
.some((part) => selector.includes(part.trim()))
|
||||
);
|
||||
shouldHighlight = isIframeChild;
|
||||
} else if (selector.includes(":>>") && hasValidChildSelectors) {
|
||||
const selectorParts = selector
|
||||
.split(":>>")
|
||||
.map((part: string) => part.trim());
|
||||
const isValidMixedSelector = selectorParts.some((part: any) =>
|
||||
childSelectors!.some((childSelector) =>
|
||||
childSelector.includes(part)
|
||||
)
|
||||
);
|
||||
} else if (elementInfo?.isShadowRoot && childSelectors) {
|
||||
const isShadowChild = childSelectors.some(
|
||||
(childSelector: string) =>
|
||||
selector.includes(">>") &&
|
||||
childSelector
|
||||
.split(">>")
|
||||
.some((part) => selector.includes(part.trim()))
|
||||
);
|
||||
} else if (selector.includes(">>") && hasValidChildSelectors) {
|
||||
const selectorParts = selector
|
||||
.split(">>")
|
||||
.map((part: string) => part.trim());
|
||||
const isValidMixedSelector = selectorParts.some((part: any) =>
|
||||
childSelectors!.some((childSelector) =>
|
||||
childSelector.includes(part)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
console.log("❌ No child selectors available");
|
||||
shouldHighlight = false;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
// No list selector - show regular highlighting
|
||||
else {
|
||||
shouldHighlight = true;
|
||||
}
|
||||
} else {
|
||||
// getText mode - always highlight
|
||||
shouldHighlight = true;
|
||||
}
|
||||
|
||||
@@ -316,6 +303,7 @@ export const DOMBrowserRenderer: React.FC<RRWebDOMBrowserRendererProps> = ({
|
||||
},
|
||||
selector,
|
||||
childSelectors,
|
||||
groupInfo,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -335,9 +323,11 @@ export const DOMBrowserRenderer: React.FC<RRWebDOMBrowserRendererProps> = ({
|
||||
}
|
||||
},
|
||||
[
|
||||
getText,
|
||||
getList,
|
||||
listSelector,
|
||||
paginationMode,
|
||||
cachedChildSelectors,
|
||||
paginationType,
|
||||
limitMode,
|
||||
onHighlight,
|
||||
@@ -363,6 +353,10 @@ export const DOMBrowserRenderer: React.FC<RRWebDOMBrowserRendererProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isInCaptureMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const now = performance.now();
|
||||
if (now - lastMouseMoveTime.current < MOUSE_MOVE_THROTTLE) {
|
||||
return;
|
||||
@@ -401,11 +395,24 @@ export const DOMBrowserRenderer: React.FC<RRWebDOMBrowserRendererProps> = ({
|
||||
e.stopPropagation();
|
||||
|
||||
if (currentHighlight && onElementSelect) {
|
||||
// Get the group info for the current highlight
|
||||
const highlighterData =
|
||||
clientSelectorGenerator.generateDataForHighlighter(
|
||||
{ x: iframeX, y: iframeY },
|
||||
iframeDoc,
|
||||
true,
|
||||
cachedChildSelectors
|
||||
);
|
||||
|
||||
onElementSelect({
|
||||
rect: currentHighlight.rect,
|
||||
selector: currentHighlight.selector,
|
||||
elementInfo: currentHighlight.elementInfo,
|
||||
childSelectors: currentHighlight.childSelectors || [],
|
||||
childSelectors:
|
||||
cachedChildSelectors.length > 0
|
||||
? cachedChildSelectors
|
||||
: highlighterData?.childSelectors || [],
|
||||
groupInfo: highlighterData?.groupInfo,
|
||||
});
|
||||
}
|
||||
notifyLastAction("select element");
|
||||
|
||||
@@ -22,6 +22,7 @@ import { useThemeMode } from '../../context/theme-provider';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useBrowserDimensionsStore } from '../../context/browserDimensions';
|
||||
import { clientListExtractor } from '../../helpers/clientListExtractor';
|
||||
import { clientSelectorGenerator } from '../../helpers/clientSelectorGenerator';
|
||||
|
||||
const fetchWorkflow = (id: string, callback: (response: WorkflowFile) => void) => {
|
||||
getActiveWorkflow(id).then(
|
||||
@@ -52,10 +53,8 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture
|
||||
const [isCaptureTextConfirmed, setIsCaptureTextConfirmed] = useState(false);
|
||||
const [isCaptureListConfirmed, setIsCaptureListConfirmed] = useState(false);
|
||||
const { panelHeight } = useBrowserDimensionsStore();
|
||||
const [isDOMMode, setIsDOMMode] = useState(false);
|
||||
const [currentSnapshot, setCurrentSnapshot] = useState<any>(null);
|
||||
|
||||
const { lastAction, notify, currentWorkflowActionsState, setCurrentWorkflowActionsState, resetInterpretationLog, currentListActionId, setCurrentListActionId, currentTextActionId, setCurrentTextActionId, currentScreenshotActionId, setCurrentScreenshotActionId } = useGlobalInfoStore();
|
||||
const { lastAction, notify, currentWorkflowActionsState, setCurrentWorkflowActionsState, resetInterpretationLog, currentListActionId, setCurrentListActionId, currentTextActionId, setCurrentTextActionId, currentScreenshotActionId, setCurrentScreenshotActionId, updateDOMMode, currentSnapshot, isDOMMode } = useGlobalInfoStore();
|
||||
const {
|
||||
getText, startGetText, stopGetText,
|
||||
getList, startGetList, stopGetList,
|
||||
@@ -86,22 +85,20 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture
|
||||
if (socket) {
|
||||
const domModeHandler = (data: any) => {
|
||||
if (!data.userId || data.userId === id) {
|
||||
setIsDOMMode(true);
|
||||
updateDOMMode(true);
|
||||
}
|
||||
};
|
||||
|
||||
const screenshotModeHandler = (data: any) => {
|
||||
if (!data.userId || data.userId === id) {
|
||||
setIsDOMMode(false);
|
||||
setCurrentSnapshot(null);
|
||||
updateDOMMode(false);
|
||||
}
|
||||
};
|
||||
|
||||
const domcastHandler = (data: any) => {
|
||||
if (!data.userId || data.userId === id) {
|
||||
if (data.snapshotData && data.snapshotData.snapshot) {
|
||||
setCurrentSnapshot(data.snapshotData);
|
||||
setIsDOMMode(true);
|
||||
updateDOMMode(true, data.snapshotData);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -116,7 +113,7 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture
|
||||
socket.off("domcast", domcastHandler);
|
||||
};
|
||||
}
|
||||
}, [socket, id]);
|
||||
}, [socket, id, updateDOMMode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (socket) {
|
||||
@@ -214,7 +211,6 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture
|
||||
) => {
|
||||
if (isDOMMode && currentSnapshot) {
|
||||
try {
|
||||
// Find the DOM iframe element
|
||||
let iframeElement = document.querySelector(
|
||||
"#dom-browser-iframe"
|
||||
) as HTMLIFrameElement;
|
||||
@@ -247,22 +243,42 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture
|
||||
return;
|
||||
}
|
||||
|
||||
// Use client-side extraction
|
||||
Object.entries(fields).forEach(([key, field]) => {
|
||||
if (field.selectorObj?.selector) {
|
||||
const isFieldXPath =
|
||||
field.selectorObj.selector.startsWith("//") ||
|
||||
field.selectorObj.selector.startsWith("/");
|
||||
console.log(
|
||||
`Field "${key}" selector:`,
|
||||
field.selectorObj.selector,
|
||||
`(XPath: ${isFieldXPath})`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const extractedData = clientListExtractor.extractListData(
|
||||
iframeDoc,
|
||||
listSelector,
|
||||
fields,
|
||||
5 // limit for preview
|
||||
5
|
||||
);
|
||||
|
||||
updateListStepData(currentListId, extractedData);
|
||||
console.log("✅ UI extraction completed:");
|
||||
|
||||
if (extractedData.length === 0) {
|
||||
console.warn(
|
||||
"⚠️ No data extracted - this might indicate selector issues"
|
||||
);
|
||||
notify(
|
||||
"warning",
|
||||
"No data was extracted. Please verify your selections."
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in client-side data extraction:", error);
|
||||
notify("error", "Failed to extract data client-side");
|
||||
}
|
||||
} else {
|
||||
// Fallback to socket-based extraction for screenshot mode
|
||||
if (!socket) {
|
||||
console.error("Socket not available for backend extraction");
|
||||
return;
|
||||
@@ -275,8 +291,6 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture
|
||||
currentListId,
|
||||
pagination: { type: "", selector: "" },
|
||||
});
|
||||
|
||||
console.log("📤 Sent extraction request to server");
|
||||
} catch (error) {
|
||||
console.error("Error in backend data extraction:", error);
|
||||
}
|
||||
@@ -443,6 +457,7 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture
|
||||
resetInterpretationLog();
|
||||
finishAction('text');
|
||||
onFinishCapture();
|
||||
clientSelectorGenerator.cleanup();
|
||||
}, [stopGetText, getTextSettingsObject, socket, browserSteps, confirmedTextSteps, resetInterpretationLog, finishAction, notify, onFinishCapture, t]);
|
||||
|
||||
const getListSettingsObject = useCallback(() => {
|
||||
@@ -494,6 +509,8 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture
|
||||
|
||||
const stopCaptureAndEmitGetListSettings = useCallback(() => {
|
||||
const settings = getListSettingsObject();
|
||||
|
||||
console.log("rrwebSnapshotHandler", settings);
|
||||
|
||||
const latestListStep = getLatestListStep(browserSteps);
|
||||
if (latestListStep && settings) {
|
||||
@@ -509,6 +526,7 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture
|
||||
resetInterpretationLog();
|
||||
finishAction('list');
|
||||
onFinishCapture();
|
||||
clientSelectorGenerator.cleanup();
|
||||
}, [getListSettingsObject, socket, notify, handleStopGetList, resetInterpretationLog, finishAction, onFinishCapture, t, browserSteps, extractDataClientSide]);
|
||||
|
||||
const hasUnconfirmedListTextFields = browserSteps.some(step =>
|
||||
@@ -638,6 +656,7 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture
|
||||
|
||||
setCurrentTextActionId('');
|
||||
setIsCaptureTextConfirmed(false);
|
||||
clientSelectorGenerator.cleanup();
|
||||
notify('error', t('right_panel.errors.capture_text_discarded'));
|
||||
}, [currentTextActionId, browserSteps, stopGetText, deleteStepsByActionId, notify, t]);
|
||||
|
||||
@@ -668,6 +687,7 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture
|
||||
setCaptureStage('initial');
|
||||
setCurrentListActionId('');
|
||||
setIsCaptureListConfirmed(false);
|
||||
clientSelectorGenerator.cleanup();
|
||||
notify('error', t('right_panel.errors.capture_list_discarded'));
|
||||
}, [currentListActionId, browserSteps, stopGetList, deleteStepsByActionId, resetListState, setShowPaginationOptions, setShowLimitOptions, setCaptureStage, notify, t]);
|
||||
|
||||
@@ -686,6 +706,7 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture
|
||||
stopGetScreenshot();
|
||||
resetInterpretationLog();
|
||||
finishAction('screenshot');
|
||||
clientSelectorGenerator.cleanup();
|
||||
onFinishCapture();
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user