import React, { useState, useCallback, useEffect, useRef, useMemo } from 'react'; import { Button, Paper, Box, TextField, IconButton, Tooltip } from "@mui/material"; import { WorkflowFile } from "maxun-core"; import Typography from "@mui/material/Typography"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { PaginationType, useActionContext, LimitType } from '../../context/browserActions'; import { BrowserStep, useBrowserSteps } from '../../context/browserSteps'; import { useSocketStore } from '../../context/socket'; import FormControlLabel from '@mui/material/FormControlLabel'; import FormControl from '@mui/material/FormControl'; import Radio from '@mui/material/Radio'; import RadioGroup from '@mui/material/RadioGroup'; import { getActiveWorkflow } from "../../api/workflow"; import ActionDescriptionBox from '../action/ActionDescriptionBox'; 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'; import { clientPaginationDetector } from '../../helpers/clientPaginationDetector'; const fetchWorkflow = (id: string, callback: (response: WorkflowFile) => void) => { getActiveWorkflow(id).then( (response) => { if (response) { callback(response); } else { throw new Error("No workflow found"); } } ).catch((error) => { console.log(`Failed to fetch workflow:`,error.message) }) }; interface RightSidePanelProps { onFinishCapture: () => void; } export const RightSidePanel: React.FC = ({ onFinishCapture }) => { const [showCaptureList, setShowCaptureList] = useState(true); const [showCaptureScreenshot, setShowCaptureScreenshot] = useState(true); const [showCaptureText, setShowCaptureText] = useState(true); const { panelHeight } = useBrowserDimensionsStore(); const [autoDetectedPagination, setAutoDetectedPagination] = useState<{ type: PaginationType; selector: string | null; confidence: 'high' | 'medium' | 'low'; } | null>(null); const autoDetectionRunRef = useRef(null); const { notify, currentWorkflowActionsState, setCurrentWorkflowActionsState, resetInterpretationLog, currentListActionId, setCurrentListActionId, currentTextActionId, setCurrentTextActionId, currentScreenshotActionId, setCurrentScreenshotActionId, isDOMMode, updateDOMMode, currentTextGroupName } = useGlobalInfoStore(); const { getText, startGetText, stopGetText, getList, startGetList, stopGetList, getScreenshot, startGetScreenshot, stopGetScreenshot, startPaginationMode, stopPaginationMode, paginationType, updatePaginationType, limitType, customLimit, updateLimitType, updateCustomLimit, stopLimitMode, startLimitMode, captureStage, setCaptureStage, showPaginationOptions, setShowPaginationOptions, showLimitOptions, setShowLimitOptions, workflow, setWorkflow, activeAction, setActiveAction, finishAction } = useActionContext(); const { browserSteps, addScreenshotStep, updateListStepLimit, updateListStepPagination, deleteStepsByActionId, updateListStepData, updateScreenshotStepData, emitActionForStep } = useBrowserSteps(); const { id, socket } = useSocketStore(); const { t } = useTranslation(); const isAnyActionActive = activeAction !== 'none'; const workflowHandler = useCallback((data: WorkflowFile) => { setWorkflow(data); }, [setWorkflow]); useEffect(() => { if (!paginationType || !currentListActionId) return; const currentListStep = browserSteps.find( step => step.type === 'list' && step.actionId === currentListActionId ) as (BrowserStep & { type: 'list' }) | undefined; const currentSelector = currentListStep?.pagination?.selector; const currentType = currentListStep?.pagination?.type; if (['clickNext', 'clickLoadMore'].includes(paginationType)) { const needsSelector = !currentSelector && !currentType; const typeChanged = currentType && currentType !== paginationType; if (typeChanged) { const iframeElement = document.querySelector('#browser-window iframe') as HTMLIFrameElement; if (iframeElement?.contentDocument && currentSelector) { 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(currentSelector, 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:', error); } } if (currentListStep) { updateListStepPagination(currentListStep.id, { type: paginationType, selector: null, }); } startPaginationMode(); } else if (needsSelector) { startPaginationMode(); } } }, [paginationType, currentListActionId, browserSteps, updateListStepPagination, startPaginationMode]); useEffect(() => { if (socket) { const domModeHandler = (data: any) => { if (!data.userId || data.userId === id) { updateDOMMode(true); } }; socket.on("dom-mode-enabled", domModeHandler); return () => { socket.off("dom-mode-enabled", domModeHandler); }; } }, [socket, id, updateDOMMode]); useEffect(() => { if (socket) { socket.on("workflow", workflowHandler); } if (id) { fetchWorkflow(id, workflowHandler); } let interval = setInterval(() => { if (id) { fetchWorkflow(id, workflowHandler); } }, (1000 * 60 * 15)); return () => { socket?.off("workflow", workflowHandler); clearInterval(interval); }; }, [id, socket, workflowHandler]); useEffect(() => { const hasPairs = workflow.workflow.length > 0; if (!hasPairs) { setShowCaptureList(true); setShowCaptureScreenshot(true); setShowCaptureText(true); return; } const hasScrapeListAction = workflow.workflow.some(pair => pair.what.some(action => action.action === 'scrapeList') ); const hasScreenshotAction = workflow.workflow.some(pair => pair.what.some(action => action.action === 'screenshot') ); const hasScrapeSchemaAction = workflow.workflow.some(pair => pair.what.some(action => action.action === 'scrapeSchema') ); setCurrentWorkflowActionsState({ hasScrapeListAction, hasScreenshotAction, hasScrapeSchemaAction, }); setShowCaptureList(true); setShowCaptureScreenshot(true); setShowCaptureText(true); }, [workflow, setCurrentWorkflowActionsState]); useEffect(() => { if (socket) { socket.on('listDataExtracted', (response) => { if (!isDOMMode) { const { currentListId, data } = response; updateListStepData(currentListId, data); } }); } return () => { socket?.off('listDataExtracted'); }; }, [socket, updateListStepData, isDOMMode]); useEffect(() => { if (socket) { const handleDirectScreenshot = (data: any) => { const screenshotSteps = browserSteps.filter(step => step.type === 'screenshot' && step.actionId === currentScreenshotActionId ); if (screenshotSteps.length > 0) { const latestStep = screenshotSteps[screenshotSteps.length - 1]; updateScreenshotStepData(latestStep.id, data.screenshot); emitActionForStep(latestStep); } setCurrentScreenshotActionId(''); }; socket.on('directScreenshotCaptured', handleDirectScreenshot); return () => { socket.off('directScreenshotCaptured', handleDirectScreenshot); }; } }, [socket, id, notify, t, currentScreenshotActionId, updateScreenshotStepData, setCurrentScreenshotActionId, emitActionForStep, browserSteps]); const extractDataClientSide = useCallback( ( listSelector: string, fields: Record, currentListId: number ) => { if (isDOMMode) { try { let iframeElement = document.querySelector( "#dom-browser-iframe" ) as HTMLIFrameElement; if (!iframeElement) { iframeElement = document.querySelector( "#browser-window iframe" ) as HTMLIFrameElement; } if (!iframeElement) { const browserWindow = document.querySelector("#browser-window"); if (browserWindow) { iframeElement = browserWindow.querySelector( "iframe" ) as HTMLIFrameElement; } } if (!iframeElement) { console.error( "Could not find the DOM iframe element for extraction" ); return; } const iframeDoc = iframeElement.contentDocument; if (!iframeDoc) { console.error("Failed to get iframe document"); return; } const extractedData = clientListExtractor.extractListData( iframeDoc, listSelector, fields, 5 ); updateListStepData(currentListId, extractedData); 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"); } } }, [isDOMMode, updateListStepData, socket, notify, currentWorkflowActionsState] ); useEffect(() => { if (!getList) return; const currentListStep = browserSteps.find( step => step.type === 'list' && step.actionId === currentListActionId ) as (BrowserStep & { type: 'list'; listSelector?: string; fields?: Record }) | undefined; if (!currentListStep || !currentListStep.listSelector || !currentListStep.fields) return; const fieldCount = Object.keys(currentListStep.fields).length; if (fieldCount > 0) { extractDataClientSide( currentListStep.listSelector, currentListStep.fields, currentListStep.id ); setCurrentWorkflowActionsState({ ...currentWorkflowActionsState, hasScrapeListAction: true }); } }, [browserSteps, currentListActionId, getList, extractDataClientSide, setCurrentWorkflowActionsState, currentWorkflowActionsState]); const handleStartGetText = () => { const newActionId = `text-${crypto.randomUUID()}`; setCurrentTextActionId(newActionId); startGetText(); } const handleStartGetList = () => { const newActionId = `list-${crypto.randomUUID()}`; setCurrentListActionId(newActionId); startGetList(); } const handleStartGetScreenshot = () => { const newActionId = `screenshot-${crypto.randomUUID()}`; setCurrentScreenshotActionId(newActionId); startGetScreenshot(); }; const stopCaptureAndEmitGetTextSettings = useCallback(() => { const currentTextActionStep = browserSteps.find(step => step.type === 'text' && step.actionId === currentTextActionId); if (!currentTextActionStep) { notify('error', t('right_panel.errors.no_text_captured')); return; } stopGetText(); if (currentTextActionStep) { emitActionForStep(currentTextActionStep); } setCurrentTextActionId(''); resetInterpretationLog(); finishAction('text'); onFinishCapture(); clientSelectorGenerator.cleanup(); }, [stopGetText, socket, browserSteps, resetInterpretationLog, finishAction, notify, onFinishCapture, t, currentTextActionId, currentTextGroupName, emitActionForStep]); const resetListState = useCallback(() => { setShowPaginationOptions(false); updatePaginationType(''); setShowLimitOptions(false); updateLimitType(''); updateCustomLimit(''); }, [updatePaginationType, updateLimitType, updateCustomLimit]); const handleStopGetList = useCallback(() => { stopGetList(); resetListState(); }, [stopGetList, resetListState]); const stopCaptureAndEmitGetListSettings = useCallback(() => { 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 completion:', error); } } } const latestListStep = getLatestListStep(browserSteps); if (latestListStep) { extractDataClientSide(latestListStep.listSelector!, latestListStep.fields, latestListStep.id); setCurrentWorkflowActionsState({ ...currentWorkflowActionsState, hasScrapeListAction: true }); emitActionForStep(latestListStep); handleStopGetList(); setCurrentListActionId(''); resetInterpretationLog(); finishAction('list'); onFinishCapture(); clientSelectorGenerator.cleanup(); } else { notify('error', t('right_panel.errors.unable_create_settings')); handleStopGetList(); setCurrentListActionId(''); resetInterpretationLog(); finishAction('list'); onFinishCapture(); clientSelectorGenerator.cleanup(); } }, [socket, notify, handleStopGetList, resetInterpretationLog, finishAction, onFinishCapture, t, browserSteps, extractDataClientSide, setCurrentWorkflowActionsState, currentWorkflowActionsState, emitActionForStep, autoDetectedPagination]); const getLatestListStep = (steps: BrowserStep[]) => { const listSteps = steps.filter(step => step.type === 'list'); if (listSteps.length === 0) return null; return listSteps.sort((a, b) => b.id - a.id)[0]; }; const handleConfirmListCapture = useCallback(() => { switch (captureStage) { case 'initial': const hasValidListSelectorForCurrentAction = browserSteps.some(step => step.type === 'list' && step.actionId === currentListActionId && step.listSelector && Object.keys(step.fields).length > 0 ); if (!hasValidListSelectorForCurrentAction) { notify('error', t('right_panel.errors.capture_list_first')); return; } const currentListStepForAutoDetect = browserSteps.find( step => step.type === 'list' && step.actionId === currentListActionId ) as (BrowserStep & { type: 'list'; listSelector?: string }) | undefined; if (currentListStepForAutoDetect?.listSelector) { if (autoDetectionRunRef.current !== currentListActionId) { autoDetectionRunRef.current = currentListActionId; notify('info', 'Detecting pagination...'); try { socket?.emit('testPaginationScroll', { listSelector: currentListStepForAutoDetect.listSelector }); const handleScrollTestResult = (result: any) => { if (result.success && result.contentLoaded) { setAutoDetectedPagination({ type: 'scrollDown', selector: null, confidence: 'high' }); updatePaginationType('scrollDown'); const latestListStep = browserSteps.find( step => step.type === 'list' && step.actionId === currentListActionId ); if (latestListStep) { updateListStepPagination(latestListStep.id, { type: 'scrollDown', selector: null, isShadow: false }); } } else if (result.success && !result.contentLoaded) { const iframeElement = document.querySelector('#browser-window iframe') as HTMLIFrameElement; const iframeDoc = iframeElement?.contentDocument; if (iframeDoc) { const detectionResult = clientPaginationDetector.autoDetectPagination( iframeDoc, currentListStepForAutoDetect.listSelector!, clientSelectorGenerator, { disableScrollDetection: true } ); if (detectionResult.type) { setAutoDetectedPagination({ type: detectionResult.type, selector: detectionResult.selector, confidence: detectionResult.confidence }); const latestListStep = browserSteps.find( step => step.type === 'list' && step.actionId === currentListActionId ); if (latestListStep) { updateListStepPagination(latestListStep.id, { type: detectionResult.type, selector: detectionResult.selector, isShadow: false }); } updatePaginationType(detectionResult.type); if (detectionResult.selector && (detectionResult.type === 'clickNext' || detectionResult.type === 'clickLoadMore')) { try { function evaluateSelector(selector: string, doc: Document): Element[] { try { const isXPath = selector.startsWith('//') || selector.startsWith('(//'); if (isXPath) { 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; } else { try { const allElements = Array.from(doc.querySelectorAll(selector)); if (allElements.length > 0) { return allElements; } } catch (err) { console.warn('[RightSidePanel] Full chained selector failed, trying individual selectors:', err); } const selectorParts = selector.split(','); for (const part of selectorParts) { try { const elements = Array.from(doc.querySelectorAll(part.trim())); if (elements.length > 0) { return elements; } } catch (err) { console.warn('[RightSidePanel] Selector part failed:', part.trim(), err); continue; } } return []; } } catch (err) { console.error('[RightSidePanel] Selector evaluation failed:', selector, err); return []; } } 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; if (iframeWindow) { const targetY = elementRect.top + iframeWindow.scrollY - (iframeWindow.innerHeight / 2) + (elementRect.height / 2); iframeWindow.scrollTo({ top: targetY, behavior: 'smooth' }); } const paginationTypeLabel = detectionResult.type === 'clickNext' ? 'Next Button' : 'Load More Button'; notify('info', `${paginationTypeLabel} has been auto-detected and highlighted on the page`); } else { console.warn(' No elements found for selector:', detectionResult.selector); } } catch (error) { console.error('Error highlighting pagination button:', error); } } } else { setAutoDetectedPagination(null); } } } else { console.error('Scroll test failed:', result.error); setAutoDetectedPagination(null); } socket?.off('paginationScrollTestResult', handleScrollTestResult); }; socket?.on('paginationScrollTestResult', handleScrollTestResult); setTimeout(() => { socket?.off('paginationScrollTestResult', handleScrollTestResult); }, 5000); } catch (error) { console.error('Scroll test failed:', error); setAutoDetectedPagination(null); } } } const shouldSkipPaginationMode = autoDetectedPagination && ( ['scrollDown', 'scrollUp'].includes(autoDetectedPagination.type) || (['clickNext', 'clickLoadMore'].includes(autoDetectedPagination.type) && autoDetectedPagination.selector) ); if (!shouldSkipPaginationMode) { startPaginationMode(); } setShowPaginationOptions(true); setCaptureStage('pagination'); break; case 'pagination': if (!paginationType) { notify('error', t('right_panel.errors.select_pagination')); return; } const currentListStepForPagination = browserSteps.find( step => step.type === 'list' && step.actionId === currentListActionId ) as (BrowserStep & { type: 'list' }) | undefined; if (currentListStepForPagination) { const paginationSelector = currentListStepForPagination.pagination?.selector; if (['clickNext', 'clickLoadMore'].includes(paginationType) && !paginationSelector) { notify('error', t('right_panel.errors.select_pagination_element')); return; } } stopPaginationMode(); setShowPaginationOptions(false); startLimitMode(); setShowLimitOptions(true); setCaptureStage('limit'); break; case 'limit': if (!limitType || (limitType === 'custom' && !customLimit)) { notify('error', t('right_panel.errors.select_limit')); return; } const limit = limitType === 'custom' ? parseInt(customLimit) : parseInt(limitType); if (isNaN(limit) || limit <= 0) { notify('error', t('right_panel.errors.invalid_limit')); return; } const latestListStep = getLatestListStep(browserSteps); if (latestListStep) { updateListStepLimit(latestListStep.id, limit); } stopLimitMode(); setShowLimitOptions(false); stopCaptureAndEmitGetListSettings(); setCaptureStage('complete'); break; case 'complete': setCaptureStage('initial'); break; } }, [captureStage, paginationType, limitType, customLimit, startPaginationMode, setShowPaginationOptions, setCaptureStage, notify, stopPaginationMode, startLimitMode, setShowLimitOptions, stopLimitMode, stopCaptureAndEmitGetListSettings, t, browserSteps, currentListActionId, updateListStepLimit]); const handleBackCaptureList = useCallback(() => { switch (captureStage) { case 'limit': stopLimitMode(); setShowLimitOptions(false); startPaginationMode(); setShowPaginationOptions(true); setCaptureStage('pagination'); break; case 'pagination': stopPaginationMode(); setShowPaginationOptions(false); setAutoDetectedPagination(null); setCaptureStage('initial'); break; } }, [captureStage, stopLimitMode, startPaginationMode, stopPaginationMode]); const handlePaginationSettingSelect = (option: PaginationType) => { updatePaginationType(option); }; const discardGetText = useCallback(() => { stopGetText(); if (currentTextActionId) { deleteStepsByActionId(currentTextActionId); if (socket) { socket.emit('removeAction', { actionId: currentTextActionId }); } } setCurrentTextActionId(''); clientSelectorGenerator.cleanup(); notify('error', t('right_panel.errors.capture_text_discarded')); }, [currentTextActionId, browserSteps, stopGetText, deleteStepsByActionId, notify, t, socket]); const discardGetList = useCallback(() => { stopGetList(); if (currentListActionId) { deleteStepsByActionId(currentListActionId); if (socket) { socket.emit('removeAction', { actionId: currentListActionId }); } } 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(); setShowPaginationOptions(false); setShowLimitOptions(false); setAutoDetectedPagination(null); setCaptureStage('initial'); setCurrentListActionId(''); clientSelectorGenerator.cleanup(); notify('error', t('right_panel.errors.capture_list_discarded')); }, [currentListActionId, browserSteps, stopGetList, deleteStepsByActionId, resetListState, setShowPaginationOptions, setShowLimitOptions, setCaptureStage, notify, t, stopPaginationMode, stopLimitMode, socket, autoDetectedPagination]); const captureScreenshot = (fullPage: boolean) => { const screenshotCount = browserSteps.filter(s => s.type === 'screenshot').length + 1; const screenshotName = `Screenshot ${screenshotCount}`; const screenshotSettings = { fullPage, type: 'png' as const, timeout: 30000, animations: 'allow' as const, caret: 'hide' as const, scale: 'device' as const, name: screenshotName, actionId: currentScreenshotActionId }; socket?.emit('captureDirectScreenshot', screenshotSettings); addScreenshotStep(fullPage, currentScreenshotActionId); stopGetScreenshot(); resetInterpretationLog(); finishAction('screenshot'); onFinishCapture(); clientSelectorGenerator.cleanup(); }; const theme = useThemeMode(); const isDarkMode = theme.darkMode; return ( {!isAnyActionActive && ( <> {showCaptureList && ( )} {showCaptureText && ( )} {showCaptureScreenshot && ( )} )} {getList && ( {(captureStage === 'pagination' || captureStage === 'limit') && ( )} {showPaginationOptions && ( {t('right_panel.pagination.title')} {autoDetectedPagination && autoDetectedPagination.type !== '' && ( ✓ Auto-detected: { autoDetectedPagination.type === 'clickNext' ? 'Click Next' : autoDetectedPagination.type === 'clickLoadMore' ? 'Click Load More' : autoDetectedPagination.type === 'scrollDown' ? 'Scroll Down' : autoDetectedPagination.type === 'scrollUp' ? 'Scroll Up' : autoDetectedPagination.type } You can continue with this or manually select a different pagination type below. {autoDetectedPagination.selector && ['clickNext', 'clickLoadMore'].includes(autoDetectedPagination.type) && ( )} )} )} {showLimitOptions && ( {t('right_panel.limit.title')} updateLimitType(e.target.value as LimitType)} sx={{ display: 'flex', flexDirection: 'column', width: '100%', }} > } label="10" /> } label="100" />
} label={t('right_panel.limit.custom')} /> {limitType === 'custom' && ( ) => { const value = parseInt(e.target.value); if (e.target.value === '' || value >= 1) { updateCustomLimit(e.target.value); } }} inputProps={{ min: 1, onKeyPress: (e: React.KeyboardEvent) => { const value = (e.target as HTMLInputElement).value + e.key; if (parseInt(value) < 1) { e.preventDefault(); } } }} placeholder={t('right_panel.limit.enter_number')} sx={{ marginLeft: '10px', '& input': { padding: '10px', }, width: '150px', background: isDarkMode ? "#1E2124" : 'white', color: isDarkMode ? "white" : 'black', }} /> )}
)}
)} {getText && ( )} {getScreenshot && ( )}
); };