From 41ae15d453d58b2aa8c9c83a4a7cd7f2a9a05781 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 6 Oct 2025 18:04:51 +0530 Subject: [PATCH 01/36] chore: move duplicate modal to legacy --- legacy/src/RobotDuplicate.tsx | 172 ++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 legacy/src/RobotDuplicate.tsx diff --git a/legacy/src/RobotDuplicate.tsx b/legacy/src/RobotDuplicate.tsx new file mode 100644 index 00000000..bee1ef5b --- /dev/null +++ b/legacy/src/RobotDuplicate.tsx @@ -0,0 +1,172 @@ +import React, { useState, useEffect } from 'react'; +import { GenericModal } from "../ui/GenericModal"; +import { TextField, Typography, Box, Button } from "@mui/material"; +import { modalStyle } from "../recorder/AddWhereCondModal"; +import { useGlobalInfoStore } from '../../context/globalInfo'; +import { duplicateRecording, getStoredRecording } from '../../api/storage'; +import { WhereWhatPair } from 'maxun-core'; +import { useTranslation } from 'react-i18next'; + +interface RobotMeta { + name: string; + id: string; + createdAt: string; + pairs: number; + updatedAt: string; + params: any[]; +} + +interface RobotWorkflow { + workflow: WhereWhatPair[]; +} + +interface ScheduleConfig { + runEvery: number; + runEveryUnit: 'MINUTES' | 'HOURS' | 'DAYS' | 'WEEKS' | 'MONTHS'; + startFrom: 'SUNDAY' | 'MONDAY' | 'TUESDAY' | 'WEDNESDAY' | 'THURSDAY' | 'FRIDAY' | 'SATURDAY'; + atTimeStart?: string; + atTimeEnd?: string; + timezone: string; + lastRunAt?: Date; + nextRunAt?: Date; + cronExpression?: string; +} + +export interface RobotSettings { + id: string; + userId?: number; + recording_meta: RobotMeta; + recording: RobotWorkflow; + google_sheet_email?: string | null; + google_sheet_name?: string | null; + google_sheet_id?: string | null; + google_access_token?: string | null; + google_refresh_token?: string | null; + schedule?: ScheduleConfig | null; +} + +interface RobotSettingsProps { + isOpen: boolean; + handleStart: (settings: RobotSettings) => void; + handleClose: () => void; + initialSettings?: RobotSettings | null; + +} + +export const RobotDuplicationModal = ({ isOpen, handleStart, handleClose, initialSettings }: RobotSettingsProps) => { + const { t } = useTranslation(); + const [targetUrl, setTargetUrl] = useState(''); + const [robot, setRobot] = useState(null); + const { recordingId, notify, setRerenderRobots } = useGlobalInfoStore(); + + useEffect(() => { + if (isOpen) { + getRobot(); + } + }, [isOpen]); + + useEffect(() => { + if (robot) { + const lastPair = robot?.recording.workflow[robot?.recording.workflow.length - 1]; + const url = lastPair?.what.find(action => action.action === "goto")?.args?.[0]; + setTargetUrl(url); + } + }, [robot]); + + const getRobot = async () => { + if (recordingId) { + const robot = await getStoredRecording(recordingId); + setRobot(robot); + } else { + notify('error', t('robot_duplication.notifications.robot_not_found')); + } + } + + const handleTargetUrlChange = (e: React.ChangeEvent) => { + setTargetUrl(e.target.value); + }; + + const handleSave = async () => { + if (!robot || !targetUrl) { + notify('error', t('robot_duplication.notifications.url_required')); + return; + } + + try { + const success = await duplicateRecording(robot.recording_meta.id, targetUrl); + + if (success) { + setRerenderRobots(true); + + notify('success', t('robot_duplication.notifications.duplicate_success')); + handleStart(robot); + handleClose(); + } else { + notify('error', t('robot_duplication.notifications.duplicate_error')); + } + } catch (error) { + notify('error', t('robot_duplication.notifications.unknown_error')); + console.error('Error updating Target URL:', error); + } + }; + + return ( + + <> + + {t('robot_duplication.title')} + + + { + robot && ( + <> + + {t('robot_duplication.descriptions.purpose')} + +
+ producthunt.com/topics/api', + url2: 'producthunt.com/topics/database' + }) + }} /> +
+ + {t('robot_duplication.descriptions.warning')} + + + + + + + + ) + } +
+ +
+ ); +}; From 259a2e9caa05cf43ea0d2d4f3abcee932b964668 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 6 Oct 2025 18:05:13 +0530 Subject: [PATCH 02/36] chore: move duplicate modal to legacy --- legacy/src/RobotEdit.tsx | 586 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 586 insertions(+) create mode 100644 legacy/src/RobotEdit.tsx diff --git a/legacy/src/RobotEdit.tsx b/legacy/src/RobotEdit.tsx new file mode 100644 index 00000000..3b110ba1 --- /dev/null +++ b/legacy/src/RobotEdit.tsx @@ -0,0 +1,586 @@ +import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { GenericModal } from "../ui/GenericModal"; +import { TextField, Typography, Box, Button, IconButton, InputAdornment } from "@mui/material"; +import { Visibility, VisibilityOff } from '@mui/icons-material'; +import { modalStyle } from "../recorder/AddWhereCondModal"; +import { useGlobalInfoStore } from '../../context/globalInfo'; +import { getStoredRecording, updateRecording } from '../../api/storage'; +import { WhereWhatPair } from 'maxun-core'; + +interface RobotMeta { + name: string; + id: string; + createdAt: string; + pairs: number; + updatedAt: string; + params: any[]; +} + +interface RobotWorkflow { + workflow: WhereWhatPair[]; +} + +interface ScheduleConfig { + runEvery: number; + runEveryUnit: 'MINUTES' | 'HOURS' | 'DAYS' | 'WEEKS' | 'MONTHS'; + startFrom: 'SUNDAY' | 'MONDAY' | 'TUESDAY' | 'WEDNESDAY' | 'THURSDAY' | 'FRIDAY' | 'SATURDAY'; + atTimeStart?: string; + atTimeEnd?: string; + timezone: string; + lastRunAt?: Date; + nextRunAt?: Date; + cronExpression?: string; +} + +export interface RobotSettings { + id: string; + userId?: number; + recording_meta: RobotMeta; + recording: RobotWorkflow; + google_sheet_email?: string | null; + google_sheet_name?: string | null; + google_sheet_id?: string | null; + google_access_token?: string | null; + google_refresh_token?: string | null; + schedule?: ScheduleConfig | null; +} + +interface RobotSettingsProps { + isOpen: boolean; + handleStart: (settings: RobotSettings) => void; + handleClose: () => void; + initialSettings?: RobotSettings | null; +} + +interface CredentialInfo { + value: string; + type: string; +} + +interface Credentials { + [key: string]: CredentialInfo; +} + +interface CredentialVisibility { + [key: string]: boolean; +} + +interface GroupedCredentials { + passwords: string[]; + emails: string[]; + usernames: string[]; + others: string[]; +} + +interface ScrapeListLimit { + pairIndex: number; + actionIndex: number; + argIndex: number; + currentLimit: number; +} + +export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettings }: RobotSettingsProps) => { + const { t } = useTranslation(); + const [credentials, setCredentials] = useState({}); + const { recordingId, notify, setRerenderRobots } = useGlobalInfoStore(); + const [robot, setRobot] = useState(null); + const [credentialGroups, setCredentialGroups] = useState({ + passwords: [], + emails: [], + usernames: [], + others: [] + }); + const [showPasswords, setShowPasswords] = useState({}); + const [scrapeListLimits, setScrapeListLimits] = useState([]); + + const isEmailPattern = (value: string): boolean => { + return value.includes('@'); + }; + + const isUsernameSelector = (selector: string): boolean => { + return selector.toLowerCase().includes('username') || + selector.toLowerCase().includes('user') || + selector.toLowerCase().includes('email'); + }; + + const determineCredentialType = (selector: string, info: CredentialInfo): 'password' | 'email' | 'username' | 'other' => { + if (info.type === 'password' || selector.toLowerCase().includes('password')) { + return 'password'; + } + if (isEmailPattern(info.value) || selector.toLowerCase().includes('email')) { + return 'email'; + } + if (isUsernameSelector(selector)) { + return 'username'; + } + return 'other'; + }; + + useEffect(() => { + if (isOpen) { + getRobot(); + } + }, [isOpen]); + + useEffect(() => { + if (robot?.recording?.workflow) { + const extractedCredentials = extractInitialCredentials(robot.recording.workflow); + setCredentials(extractedCredentials); + setCredentialGroups(groupCredentialsByType(extractedCredentials)); + + findScrapeListLimits(robot.recording.workflow); + } + }, [robot]); + + const findScrapeListLimits = (workflow: WhereWhatPair[]) => { + const limits: ScrapeListLimit[] = []; + + workflow.forEach((pair, pairIndex) => { + if (!pair.what) return; + + pair.what.forEach((action, actionIndex) => { + if (action.action === 'scrapeList' && action.args && action.args.length > 0) { + // Check if first argument has a limit property + const arg = action.args[0]; + if (arg && typeof arg === 'object' && 'limit' in arg) { + limits.push({ + pairIndex, + actionIndex, + argIndex: 0, + currentLimit: arg.limit + }); + } + } + }); + }); + + setScrapeListLimits(limits); + }; + + function extractInitialCredentials(workflow: any[]): Credentials { + const credentials: Credentials = {}; + + const isPrintableCharacter = (char: string): boolean => { + return char.length === 1 && !!char.match(/^[\x20-\x7E]$/); + }; + + workflow.forEach(step => { + if (!step.what) return; + + let currentSelector = ''; + let currentValue = ''; + let currentType = ''; + let i = 0; + + while (i < step.what.length) { + const action = step.what[i]; + + if (!action.action || !action.args?.[0]) { + i++; + continue; + } + + const selector = action.args[0]; + + // Handle full word type actions first + if (action.action === 'type' && + action.args?.length >= 2 && + typeof action.args[1] === 'string' && + action.args[1].length > 1) { + + if (!credentials[selector]) { + credentials[selector] = { + value: action.args[1], + type: action.args[2] || 'text' + }; + } + i++; + continue; + } + + // Handle character-by-character sequences (both type and press) + if ((action.action === 'type' || action.action === 'press') && + action.args?.length >= 2 && + typeof action.args[1] === 'string') { + + if (selector !== currentSelector) { + if (currentSelector && currentValue) { + credentials[currentSelector] = { + value: currentValue, + type: currentType || 'text' + }; + } + currentSelector = selector; + currentValue = credentials[selector]?.value || ''; + currentType = action.args[2] || credentials[selector]?.type || 'text'; + } + + const character = action.args[1]; + + if (isPrintableCharacter(character)) { + currentValue += character; + } else if (character === 'Backspace') { + currentValue = currentValue.slice(0, -1); + } + + if (!currentType && action.args[2]?.toLowerCase() === 'password') { + currentType = 'password'; + } + + let j = i + 1; + while (j < step.what.length) { + const nextAction = step.what[j]; + if (!nextAction.action || !nextAction.args?.[0] || + nextAction.args[0] !== selector || + (nextAction.action !== 'type' && nextAction.action !== 'press')) { + break; + } + if (nextAction.args[1] === 'Backspace') { + currentValue = currentValue.slice(0, -1); + } else if (isPrintableCharacter(nextAction.args[1])) { + currentValue += nextAction.args[1]; + } + j++; + } + + credentials[currentSelector] = { + value: currentValue, + type: currentType + }; + + i = j; + } else { + i++; + } + } + + if (currentSelector && currentValue) { + credentials[currentSelector] = { + value: currentValue, + type: currentType || 'text' + }; + } + }); + + return credentials; + } + + const groupCredentialsByType = (credentials: Credentials): GroupedCredentials => { + return Object.entries(credentials).reduce((acc: GroupedCredentials, [selector, info]) => { + const credentialType = determineCredentialType(selector, info); + + switch (credentialType) { + case 'password': + acc.passwords.push(selector); + break; + case 'email': + acc.emails.push(selector); + break; + case 'username': + acc.usernames.push(selector); + break; + default: + acc.others.push(selector); + } + + return acc; + }, { passwords: [], emails: [], usernames: [], others: [] }); + }; + + const getRobot = async () => { + if (recordingId) { + const robot = await getStoredRecording(recordingId); + setRobot(robot); + } else { + notify('error', t('robot_edit.notifications.update_failed')); + } + }; + + const handleClickShowPassword = (selector: string) => { + setShowPasswords(prev => ({ + ...prev, + [selector]: !prev[selector] + })); + }; + + const handleRobotNameChange = (newName: string) => { + setRobot((prev) => + prev ? { ...prev, recording_meta: { ...prev.recording_meta, name: newName } } : prev + ); + }; + + const handleCredentialChange = (selector: string, value: string) => { + setCredentials(prev => ({ + ...prev, + [selector]: { + ...prev[selector], + value + } + })); + }; + + const handleLimitChange = (pairIndex: number, actionIndex: number, argIndex: number, newLimit: number) => { + setRobot((prev) => { + if (!prev) return prev; + + const updatedWorkflow = [...prev.recording.workflow]; + if ( + updatedWorkflow.length > pairIndex && + updatedWorkflow[pairIndex]?.what && + updatedWorkflow[pairIndex].what.length > actionIndex && + updatedWorkflow[pairIndex].what[actionIndex].args && + updatedWorkflow[pairIndex].what[actionIndex].args.length > argIndex + ) { + updatedWorkflow[pairIndex].what[actionIndex].args[argIndex].limit = newLimit; + + setScrapeListLimits(prev => { + return prev.map(item => { + if (item.pairIndex === pairIndex && + item.actionIndex === actionIndex && + item.argIndex === argIndex) { + return { ...item, currentLimit: newLimit }; + } + return item; + }); + }); + } + + return { ...prev, recording: { ...prev.recording, workflow: updatedWorkflow } }; + }); + }; + + const handleTargetUrlChange = (newUrl: string) => { + setRobot((prev) => { + if (!prev) return prev; + + const updatedWorkflow = [...prev.recording.workflow]; + const lastPairIndex = updatedWorkflow.length - 1; + + if (lastPairIndex >= 0) { + const gotoAction = updatedWorkflow[lastPairIndex]?.what?.find(action => action.action === "goto"); + if (gotoAction && gotoAction.args && gotoAction.args.length > 0) { + gotoAction.args[0] = newUrl; + } + } + + return { ...prev, recording: { ...prev.recording, workflow: updatedWorkflow } }; + }); + }; + + const renderAllCredentialFields = () => { + return ( + <> + {renderCredentialFields( + credentialGroups.usernames, + t('Username'), + 'text' + )} + + {renderCredentialFields( + credentialGroups.emails, + t('Email'), + 'text' + )} + + {renderCredentialFields( + credentialGroups.passwords, + t('Password'), + 'password' + )} + + {renderCredentialFields( + credentialGroups.others, + t('Other'), + 'text' + )} + + ); + }; + + const renderCredentialFields = (selectors: string[], headerText: string, defaultType: 'text' | 'password' = 'text') => { + if (selectors.length === 0) return null; + + return ( + <> + {selectors.map((selector, index) => { + const isVisible = showPasswords[selector]; + + return ( + handleCredentialChange(selector, e.target.value)} + style={{ marginBottom: '20px' }} + InputProps={{ + endAdornment: ( + + handleClickShowPassword(selector)} + edge="end" + disabled={!credentials[selector]?.value} + > + {isVisible ? : } + + + ), + }} + /> + ); + })} + + ); + }; + + const renderScrapeListLimitFields = () => { + if (scrapeListLimits.length === 0) return null; + + return ( + <> + + {t('List Limits')} + + + {scrapeListLimits.map((limitInfo, index) => ( + { + const value = parseInt(e.target.value, 10); + if (value >= 1) { + handleLimitChange( + limitInfo.pairIndex, + limitInfo.actionIndex, + limitInfo.argIndex, + value + ); + } + }} + inputProps={{ min: 1 }} + style={{ marginBottom: '20px' }} + /> + ))} + + ); + }; + + const handleSave = async () => { + if (!robot) return; + + try { + const credentialsForPayload = Object.entries(credentials).reduce((acc, [selector, info]) => { + const enforceType = info.type === 'password' ? 'password' : 'text'; + + acc[selector] = { + value: info.value, + type: enforceType + }; + return acc; + }, {} as Record); + + const lastPair = robot.recording.workflow[robot.recording.workflow.length - 1]; + const targetUrl = lastPair?.what.find(action => action.action === "goto")?.args?.[0]; + + const payload = { + name: robot.recording_meta.name, + limits: scrapeListLimits.map(limit => ({ + pairIndex: limit.pairIndex, + actionIndex: limit.actionIndex, + argIndex: limit.argIndex, + limit: limit.currentLimit + })), + credentials: credentialsForPayload, + targetUrl: targetUrl, + }; + + const success = await updateRecording(robot.recording_meta.id, payload); + + if (success) { + setRerenderRobots(true); + + notify('success', t('robot_edit.notifications.update_success')); + handleStart(robot); + handleClose(); + } else { + notify('error', t('robot_edit.notifications.update_failed')); + } + } catch (error) { + notify('error', t('robot_edit.notifications.update_error')); + console.error('Error updating robot:', error); + } + }; + + const lastPair = robot?.recording.workflow[robot?.recording.workflow.length - 1]; + const targetUrl = lastPair?.what.find(action => action.action === "goto")?.args?.[0]; + + return ( + + <> + + {t('robot_edit.title')} + + + {robot && ( + <> + handleRobotNameChange(e.target.value)} + style={{ marginBottom: '20px' }} + /> + + handleTargetUrlChange(e.target.value)} + style={{ marginBottom: '20px' }} + /> + + {renderScrapeListLimitFields()} + + {(Object.keys(credentials).length > 0) && ( + <> + + {t('Input Texts')} + + {renderAllCredentialFields()} + + )} + + + + + + + )} + + + + ); +}; \ No newline at end of file From 8cb7c52693cc3a471afe7dd316b022acc520654c Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 6 Oct 2025 18:05:30 +0530 Subject: [PATCH 03/36] chore: move duplicate modal to legacy --- legacy/src/RobotSettings.tsx | 173 +++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 legacy/src/RobotSettings.tsx diff --git a/legacy/src/RobotSettings.tsx b/legacy/src/RobotSettings.tsx new file mode 100644 index 00000000..c91b4f11 --- /dev/null +++ b/legacy/src/RobotSettings.tsx @@ -0,0 +1,173 @@ +import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { GenericModal } from "../ui/GenericModal"; +import { TextField, Typography, Box } from "@mui/material"; +import { useGlobalInfoStore } from '../../context/globalInfo'; +import { getStoredRecording } from '../../api/storage'; +import { WhereWhatPair } from 'maxun-core'; +import { getUserById } from "../../api/auth"; + +interface RobotMeta { + name: string; + id: string; + createdAt: string; + pairs: number; + updatedAt: string; + params: any[]; +} + +interface RobotWorkflow { + workflow: WhereWhatPair[]; +} + +interface ScheduleConfig { + runEvery: number; + runEveryUnit: 'MINUTES' | 'HOURS' | 'DAYS' | 'WEEKS' | 'MONTHS'; + startFrom: 'SUNDAY' | 'MONDAY' | 'TUESDAY' | 'WEDNESDAY' | 'THURSDAY' | 'FRIDAY' | 'SATURDAY'; + atTimeStart?: string; + atTimeEnd?: string; + timezone: string; + lastRunAt?: Date; + nextRunAt?: Date; + cronExpression?: string; +} + +export interface RobotSettings { + id: string; + userId?: number; + recording_meta: RobotMeta; + recording: RobotWorkflow; + google_sheet_email?: string | null; + google_sheet_name?: string | null; + google_sheet_id?: string | null; + google_access_token?: string | null; + google_refresh_token?: string | null; + schedule?: ScheduleConfig | null; +} + +interface RobotSettingsProps { + isOpen: boolean; + handleStart: (settings: RobotSettings) => void; + handleClose: () => void; + initialSettings?: RobotSettings | null; +} + +export const RobotSettingsModal = ({ isOpen, handleStart, handleClose, initialSettings }: RobotSettingsProps) => { + const { t } = useTranslation(); + const [userEmail, setUserEmail] = useState(null); + const [robot, setRobot] = useState(null); + const { recordingId, notify } = useGlobalInfoStore(); + + useEffect(() => { + if (isOpen) { + getRobot(); + } + }, [isOpen]); + + const getRobot = async () => { + if (recordingId) { + const robot = await getStoredRecording(recordingId); + setRobot(robot); + } else { + notify('error', t('robot_settings.errors.robot_not_found')); + } + } + + const lastPair = robot?.recording.workflow[robot?.recording.workflow.length - 1]; + + // Find the `goto` action in `what` and retrieve its arguments + const targetUrl = lastPair?.what.find(action => action.action === "goto")?.args?.[0]; + + useEffect(() => { + const fetchUserEmail = async () => { + if (robot && robot.userId) { + const userData = await getUserById(robot.userId.toString()); + if (userData && userData.user) { + setUserEmail(userData.user.email); + } + } + }; + fetchUserEmail(); + }, [robot?.userId]); + + return ( + + <> + + {t('robot_settings.title')} + + + { + robot && ( + <> + + + {robot.recording.workflow?.[0]?.what?.[0]?.args?.[0]?.limit !== undefined && ( + + )} + + + + ) + } + + + + ); +}; + +export const modalStyle = { +top: "50%", +left: "50%", +transform: "translate(-50%, -50%)", +width: "30%", +backgroundColor: "background.paper", +p: 4, +height: "fit-content", +display: "block", +padding: "20px", +}; From 9441545b525d4c8be0ca45ab7571c47c892e583e Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 6 Oct 2025 18:16:02 +0530 Subject: [PATCH 04/36] fix: remove box padding --- src/components/robot/pages/ScheduleSettingsPage.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/robot/pages/ScheduleSettingsPage.tsx b/src/components/robot/pages/ScheduleSettingsPage.tsx index 5108d485..1835d123 100644 --- a/src/components/robot/pages/ScheduleSettingsPage.tsx +++ b/src/components/robot/pages/ScheduleSettingsPage.tsx @@ -188,7 +188,6 @@ export const ScheduleSettingsPage = ({ display: "flex", flexDirection: "column", alignItems: "flex-start", - padding: "20px", "& > *": { marginBottom: "20px" }, }} > From 9e06d14214e16a7227ffc007e4c371c54767e359 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 6 Oct 2025 18:17:29 +0530 Subject: [PATCH 05/36] feat: set margin top --- src/components/robot/pages/ScheduleSettingsPage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/robot/pages/ScheduleSettingsPage.tsx b/src/components/robot/pages/ScheduleSettingsPage.tsx index 1835d123..e386cfec 100644 --- a/src/components/robot/pages/ScheduleSettingsPage.tsx +++ b/src/components/robot/pages/ScheduleSettingsPage.tsx @@ -189,6 +189,7 @@ export const ScheduleSettingsPage = ({ flexDirection: "column", alignItems: "flex-start", "& > *": { marginBottom: "20px" }, + marginTop: "-20px", }} > <> From df7aadbec045be61fb5b56ced69c9fb338fd7498 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 6 Oct 2025 18:23:06 +0530 Subject: [PATCH 06/36] chore: move schedule modal to legacy --- legacy/src/ScheduleSettings.tsx | 314 ++++++++++++++++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 legacy/src/ScheduleSettings.tsx diff --git a/legacy/src/ScheduleSettings.tsx b/legacy/src/ScheduleSettings.tsx new file mode 100644 index 00000000..7a28d2dc --- /dev/null +++ b/legacy/src/ScheduleSettings.tsx @@ -0,0 +1,314 @@ +import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { GenericModal } from "../ui/GenericModal"; +import { MenuItem, TextField, Typography, Box } from "@mui/material"; +import { Dropdown } from "../ui/DropdownMui"; +import Button from "@mui/material/Button"; +import { validMomentTimezones } from '../../constants/const'; +import { useGlobalInfoStore } from '../../context/globalInfo'; +import { getSchedule, deleteSchedule } from '../../api/storage'; + +interface ScheduleSettingsProps { + isOpen: boolean; + handleStart: (settings: ScheduleSettings) => Promise; + handleClose: () => void; + initialSettings?: ScheduleSettings | null; +} + +export interface ScheduleSettings { + runEvery: number; + runEveryUnit: string; + startFrom: string; + dayOfMonth?: string; + atTimeStart?: string; + atTimeEnd?: string; + timezone: string; +} + +export const ScheduleSettingsModal = ({ isOpen, handleStart, handleClose, initialSettings }: ScheduleSettingsProps) => { + const { t } = useTranslation(); + const [schedule, setSchedule] = useState(null); + const [settings, setSettings] = useState({ + runEvery: 1, + runEveryUnit: 'HOURS', + startFrom: 'MONDAY', + dayOfMonth: '1', + atTimeStart: '00:00', + atTimeEnd: '01:00', + timezone: 'UTC' + }); + + useEffect(() => { + if (initialSettings) { + setSettings(initialSettings); + } + }, [initialSettings]); + + const handleChange = (field: keyof ScheduleSettings, value: string | number | boolean) => { + setSettings(prev => ({ ...prev, [field]: value })); + }; + + const textStyle = { + width: '150px', + height: '52px', + marginRight: '10px', + }; + + const dropDownStyle = { + marginTop: '2px', + width: '150px', + height: '59px', + marginRight: '10px', + }; + + const units = [ + 'MINUTES', + 'HOURS', + 'DAYS', + 'WEEKS', + 'MONTHS' + ]; + + const days = [ + 'MONDAY', + 'TUESDAY', + 'WEDNESDAY', + 'THURSDAY', + 'FRIDAY', + 'SATURDAY', + 'SUNDAY' + ]; + + const { recordingId, notify } = useGlobalInfoStore(); + + const deleteRobotSchedule = () => { + if (recordingId) { + deleteSchedule(recordingId); + setSchedule(null); + notify('success', t('Schedule deleted successfully')); + } else { + console.error('No recording id provided'); + } + + setSettings({ + runEvery: 1, + runEveryUnit: 'HOURS', + startFrom: 'MONDAY', + dayOfMonth: '', + atTimeStart: '00:00', + atTimeEnd: '01:00', + timezone: 'UTC' + }); + }; + + const getRobotSchedule = async () => { + if (recordingId) { + const scheduleData = await getSchedule(recordingId); + setSchedule(scheduleData); + } else { + console.error('No recording id provided'); + } + } + + useEffect(() => { + if (isOpen) { + const fetchSchedule = async () => { + await getRobotSchedule(); + }; + fetchSchedule(); + } + }, [isOpen]); + + const getDayOrdinal = (day: string | undefined) => { + if (!day) return ''; + const lastDigit = day.slice(-1); + const lastTwoDigits = day.slice(-2); + + // Special cases for 11, 12, 13 + if (['11', '12', '13'].includes(lastTwoDigits)) { + return t('schedule_settings.labels.on_day.th'); + } + + // Other cases + switch (lastDigit) { + case '1': return t('schedule_settings.labels.on_day.st'); + case '2': return t('schedule_settings.labels.on_day.nd'); + case '3': return t('schedule_settings.labels.on_day.rd'); + default: return t('schedule_settings.labels.on_day.th'); + } + }; + + return ( + + *': { marginBottom: '20px' }, + }}> + {t('schedule_settings.title')} + <> + {schedule !== null ? ( + <> + {t('schedule_settings.run_every')}: {schedule.runEvery} {schedule.runEveryUnit.toLowerCase()} + {['MONTHS', 'WEEKS'].includes(settings.runEveryUnit) ? t('schedule_settings.start_from') : t('schedule_settings.start_from')}: {schedule.startFrom.charAt(0).toUpperCase() + schedule.startFrom.slice(1).toLowerCase()} + {schedule.runEveryUnit === 'MONTHS' && ( + {t('schedule_settings.on_day')}: {schedule.dayOfMonth}{getDayOrdinal(schedule.dayOfMonth)} of the month + )} + {t('schedule_settings.at_around')}: {schedule.atTimeStart}, {schedule.timezone} {t('schedule_settings.timezone')} + + + + + ) : ( + <> + + {t('schedule_settings.labels.run_once_every')} + handleChange('runEvery', parseInt(e.target.value))} + sx={textStyle} + inputProps={{ min: 1 }} + /> + handleChange('runEveryUnit', e.target.value)} + sx={dropDownStyle} + > + {units.map((unit) => ( + {unit.charAt(0).toUpperCase() + unit.slice(1).toLowerCase()} + ))} + + + + + + {['MONTHS', 'WEEKS'].includes(settings.runEveryUnit) ? t('schedule_settings.labels.start_from_label') : t('schedule_settings.labels.start_from_label')} + + handleChange('startFrom', e.target.value)} + sx={dropDownStyle} + > + {days.map((day) => ( + + {day.charAt(0).toUpperCase() + day.slice(1).toLowerCase()} + + ))} + + + + {settings.runEveryUnit === 'MONTHS' && ( + + {t('schedule_settings.labels.on_day_of_month')} + handleChange('dayOfMonth', e.target.value)} + sx={textStyle} + inputProps={{ min: 1, max: 31 }} + /> + + )} + + {['MINUTES', 'HOURS'].includes(settings.runEveryUnit) ? ( + + + {t('schedule_settings.labels.in_between')} + handleChange('atTimeStart', e.target.value)} + sx={textStyle} + /> + handleChange('atTimeEnd', e.target.value)} + sx={textStyle} + /> + + + ) : ( + + {t('schedule_settings.at_around')} + handleChange('atTimeStart', e.target.value)} + sx={textStyle} + /> + + )} + + + {t('schedule_settings.timezone')} + handleChange('timezone', e.target.value)} + sx={dropDownStyle} + > + {validMomentTimezones.map((tz) => ( + {tz.charAt(0).toUpperCase() + tz.slice(1).toLowerCase()} + ))} + + + + + + + + )} + + + + ); +}; + +const modalStyle = { + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + width: '40%', + backgroundColor: 'background.paper', + p: 4, + height: 'fit-content', + display: 'block', + padding: '20px', +}; \ No newline at end of file From b6456641ca1121210713fc5557a7503cb6074cde Mon Sep 17 00:00:00 2001 From: Rohit Rajan Date: Wed, 8 Oct 2025 22:17:05 +0530 Subject: [PATCH 07/36] feat: filter atomic child elements --- src/helpers/clientSelectorGenerator.ts | 192 +++++++++++++++++++------ 1 file changed, 151 insertions(+), 41 deletions(-) diff --git a/src/helpers/clientSelectorGenerator.ts b/src/helpers/clientSelectorGenerator.ts index 385aa8ec..0ecb148a 100644 --- a/src/helpers/clientSelectorGenerator.ts +++ b/src/helpers/clientSelectorGenerator.ts @@ -555,36 +555,24 @@ class ClientSelectorGenerator { */ private isMeaningfulElement(element: HTMLElement): boolean { const tagName = element.tagName.toLowerCase(); - - // Fast path for common meaningful elements - if (["a", "img", "input", "button", "select"].includes(tagName)) { - return true; + + if (tagName === "img") { + return element.hasAttribute("src"); + } + + if (element.children.length > 0) { + return false; } const text = (element.textContent || "").trim(); const hasHref = element.hasAttribute("href"); - const hasSrc = element.hasAttribute("src"); - - // Quick checks first - if (text.length > 0 || hasHref || hasSrc) { + + if (text.length > 0) { return true; } - const isCustomElement = tagName.includes("-"); - - // For custom elements, be more lenient about what's considered meaningful - if (isCustomElement) { - const hasChildren = element.children.length > 0; - const hasSignificantAttributes = Array.from(element.attributes).some( - (attr) => !["class", "style", "id"].includes(attr.name.toLowerCase()) - ); - - return ( - hasChildren || - hasSignificantAttributes || - element.hasAttribute("role") || - element.hasAttribute("aria-label") - ); + if (tagName === "a" && hasHref) { + return true; } return false; @@ -2561,12 +2549,9 @@ class ClientSelectorGenerator { const MAX_MEANINGFUL_ELEMENTS = 300; const MAX_NODES_TO_CHECK = 1200; - const MAX_DEPTH = 12; + const MAX_DEPTH = 20; let nodesChecked = 0; - let adjustedMaxDepth = MAX_DEPTH; - const elementDensityThreshold = 50; - const depths: number[] = [0]; let queueIndex = 0; @@ -2576,14 +2561,10 @@ class ClientSelectorGenerator { queueIndex++; nodesChecked++; - if (currentDepth <= 3 && meaningfulDescendants.length > elementDensityThreshold) { - adjustedMaxDepth = Math.max(6, adjustedMaxDepth - 2); - } - if ( nodesChecked > MAX_NODES_TO_CHECK || meaningfulDescendants.length >= MAX_MEANINGFUL_ELEMENTS || - currentDepth > adjustedMaxDepth + currentDepth > MAX_DEPTH ) { break; } @@ -2592,7 +2573,7 @@ class ClientSelectorGenerator { meaningfulDescendants.push(element); } - if (currentDepth >= adjustedMaxDepth) { + if (currentDepth >= MAX_DEPTH) { continue; } @@ -2607,7 +2588,7 @@ class ClientSelectorGenerator { } } - if (element.shadowRoot && currentDepth < adjustedMaxDepth - 1) { + if (element.shadowRoot && currentDepth < MAX_DEPTH - 1) { const shadowChildren = element.shadowRoot.children; const shadowLimit = Math.min(shadowChildren.length, 20); for (let i = 0; i < shadowLimit; i++) { @@ -2716,22 +2697,46 @@ class ClientSelectorGenerator { } if (!addPositionToAll) { - const meaningfulAttrs = ["role", "type", "name", "src", "aria-label"]; + const meaningfulAttrs = ["role", "type"]; for (const attrName of meaningfulAttrs) { if (element.hasAttribute(attrName)) { const value = element.getAttribute(attrName)!.replace(/'/g, "\\'"); - return `${tagName}[@${attrName}='${value}']`; + const isCommonAttribute = this.isAttributeCommonAcrossLists( + element, + attrName, + value, + otherListElements + ); + if (isCommonAttribute) { + return `${tagName}[@${attrName}='${value}']`; + } } } } const testId = element.getAttribute("data-testid"); if (testId && !addPositionToAll) { - return `${tagName}[@data-testid='${testId}']`; + const isCommon = this.isAttributeCommonAcrossLists( + element, + "data-testid", + testId, + otherListElements + ); + if (isCommon) { + return `${tagName}[@data-testid='${testId}']`; + } } if (element.id && !element.id.match(/^\d/) && !addPositionToAll) { - return `${tagName}[@id='${element.id}']`; + const isCommon = this.isAttributeCommonAcrossLists( + element, + "id", + element.id, + otherListElements + ); + if (isCommon) { + return `${tagName}[@id='${element.id}']`; + } } if (!addPositionToAll) { @@ -2742,7 +2747,15 @@ class ClientSelectorGenerator { attr.name !== "data-mx-id" && attr.value ) { - return `${tagName}[@${attr.name}='${attr.value}']`; + const isCommon = this.isAttributeCommonAcrossLists( + element, + attr.name, + attr.value, + otherListElements + ); + if (isCommon) { + return `${tagName}[@${attr.name}='${attr.value}']`; + } } } } @@ -2906,12 +2919,70 @@ class ClientSelectorGenerator { const result = pathParts.length > 0 ? "/" + pathParts.join("/") : null; this.pathCache.set(targetElement, result); - + return result; } + private isAttributeCommonAcrossLists( + targetElement: HTMLElement, + attrName: string, + attrValue: string, + otherListElements: HTMLElement[] + ): boolean { + if (otherListElements.length === 0) { + return true; + } + + const targetPath = this.getElementPath(targetElement); + + for (const otherListElement of otherListElements) { + const correspondingElement = this.findCorrespondingElement( + otherListElement, + targetPath + ); + if (correspondingElement) { + const otherValue = correspondingElement.getAttribute(attrName); + if (otherValue !== attrValue) { + return false; + } + } + } + + return true; + } + + private getElementPath(element: HTMLElement): number[] { + const path: number[] = []; + let current: HTMLElement | null = element; + + while (current && current.parentElement) { + const siblings = Array.from(current.parentElement.children); + path.unshift(siblings.indexOf(current)); + current = current.parentElement; + } + + return path; + } + + private findCorrespondingElement( + rootElement: HTMLElement, + path: number[] + ): HTMLElement | null { + let current: HTMLElement = rootElement; + + for (const index of path) { + const children = Array.from(current.children); + if (index >= children.length) { + return null; + } + current = children[index] as HTMLElement; + } + + return current; + } + private getCommonClassesAcrossLists( - targetElement: HTMLElement, + targetElement: HTMLElement, otherListElements: HTMLElement[] ): string[] { if (otherListElements.length === 0) { @@ -3919,9 +3990,48 @@ class ClientSelectorGenerator { ); if (!deepestElement) return null; + if (!this.isMeaningfulElementCached(deepestElement)) { + const atomicChild = this.findAtomicChildAtPoint(deepestElement, x, y); + if (atomicChild) { + return atomicChild; + } + } + return deepestElement; } + private findAtomicChildAtPoint( + parent: HTMLElement, + x: number, + y: number + ): HTMLElement | null { + const stack: HTMLElement[] = [parent]; + const visited = new Set(); + + while (stack.length > 0) { + const element = stack.pop()!; + if (visited.has(element)) continue; + visited.add(element); + + if (element !== parent && this.isMeaningfulElementCached(element)) { + const rect = element.getBoundingClientRect(); + if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) { + return element; + } + } + + for (let i = element.children.length - 1; i >= 0; i--) { + const child = element.children[i] as HTMLElement; + const rect = child.getBoundingClientRect(); + if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) { + stack.push(child); + } + } + } + + return null; + } + /** * Helper methods used by the unified getDeepestElementFromPoint */ From ede319f6ac392bf5d9b441e84b3229484539c580 Mon Sep 17 00:00:00 2001 From: Karishma Date: Wed, 8 Oct 2025 23:48:20 +0530 Subject: [PATCH 08/36] fix: match theme on checking action description --- src/components/action/ActionDescriptionBox.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/action/ActionDescriptionBox.tsx b/src/components/action/ActionDescriptionBox.tsx index d36db407..e0a7efbb 100644 --- a/src/components/action/ActionDescriptionBox.tsx +++ b/src/components/action/ActionDescriptionBox.tsx @@ -102,7 +102,7 @@ const ActionDescriptionBox = ({ isDarkMode }: { isDarkMode: boolean }) => { sx={{ color: isDarkMode ? 'white' : 'default', '&.Mui-checked': { - color: '#ff33cc', + color: '#ff00c3', }, }} /> @@ -138,4 +138,4 @@ const ActionDescriptionBox = ({ isDarkMode }: { isDarkMode: boolean }) => { ); }; -export default ActionDescriptionBox; \ No newline at end of file +export default ActionDescriptionBox; From 3a8950b0dc6006886e6a8fc1b5409ba9adad09ae Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 8 Oct 2025 23:56:49 +0530 Subject: [PATCH 09/36] feat: use PlayArrow icon for runs --- src/components/dashboard/MainMenu.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index 05846b75..dd921d7c 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -4,7 +4,7 @@ import Tab from '@mui/material/Tab'; import Box from '@mui/material/Box'; import { useNavigate, useLocation } from 'react-router-dom'; import { Paper, Button, useTheme, Modal, Typography, Stack, TextField, InputAdornment, IconButton } from "@mui/material"; -import { AutoAwesome, FormatListBulleted, VpnKey, Usb, CloudQueue, Description, Favorite, ContentCopy, SlowMotionVideo } from "@mui/icons-material"; +import { AutoAwesome, FormatListBulleted, VpnKey, Usb, CloudQueue, Description, Favorite, ContentCopy, SlowMotionVideo, PlayArrow } from "@mui/icons-material"; import { useTranslation } from 'react-i18next'; import { useGlobalInfoStore } from "../../context/globalInfo"; @@ -90,7 +90,7 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp sx={{ alignItems: 'flex-start' }} > } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} onClick={handleRobotsClick} /> - } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> + } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> From 33a9e34f3004b553508156fd06191203aef367cb Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 9 Oct 2025 00:00:01 +0530 Subject: [PATCH 10/36] feat: set icon size to 20 --- src/components/dashboard/MainMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index dd921d7c..f4a82756 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -89,7 +89,7 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp orientation="vertical" sx={{ alignItems: 'flex-start' }} > - } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} onClick={handleRobotsClick} /> + } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} onClick={handleRobotsClick} /> } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> From 9cfded79e6019a68fc2e73bd1866cc2034663953 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 9 Oct 2025 00:00:15 +0530 Subject: [PATCH 11/36] feat: set icon size to 20 --- src/components/dashboard/MainMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index f4a82756..d4b87eac 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -90,7 +90,7 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp sx={{ alignItems: 'flex-start' }} > } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} onClick={handleRobotsClick} /> - } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> + } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> From 8c448e47d473d304a7c9e88e3c1afdd115ae9b43 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 9 Oct 2025 00:00:24 +0530 Subject: [PATCH 12/36] feat: set icon size to 20 --- src/components/dashboard/MainMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index d4b87eac..b930bad5 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -91,7 +91,7 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp > } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} onClick={handleRobotsClick} /> } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> - } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> + } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} />
From 08bb44ffa9684f3a5e2e9337cedbd192922bc9a3 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 9 Oct 2025 00:00:32 +0530 Subject: [PATCH 13/36] feat: set icon size to 20 --- src/components/dashboard/MainMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index b930bad5..a42491b6 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -92,7 +92,7 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} onClick={handleRobotsClick} /> } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> - } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> + } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} />
From 8fc992baa92c663fb24b02a2dbf4f1db0676f533 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 9 Oct 2025 00:01:25 +0530 Subject: [PATCH 14/36] feat: set icon size to 20 --- src/components/dashboard/MainMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index a42491b6..2b184718 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -111,7 +111,7 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp target="_blank" rel="noopener noreferrer" variant="outlined" - startIcon={} + startIcon={} fullWidth > Documentation From 5f9d7d8671d41f15023334a5c19474d0180a0c2d Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 9 Oct 2025 00:02:18 +0530 Subject: [PATCH 15/36] fix: revert --- src/components/dashboard/MainMenu.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index 2b184718..83b6a853 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -99,7 +99,7 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp @@ -111,7 +111,7 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp target="_blank" rel="noopener noreferrer" variant="outlined" - startIcon={} + startIcon={} fullWidth > Documentation From b31b3c9c7fc79e6f1e06c95deb551b268e71c20c Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 9 Oct 2025 00:02:28 +0530 Subject: [PATCH 16/36] feat: set icon size to 20 --- src/components/dashboard/MainMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index 83b6a853..0746287f 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -99,7 +99,7 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp From 6219e4f601d7a499dfdf274cf5d899b21d105d26 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 9 Oct 2025 00:02:46 +0530 Subject: [PATCH 17/36] feat: set icon size to 20 --- src/components/dashboard/MainMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index 0746287f..3282747a 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -133,7 +133,7 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp href='https://app.maxun.dev/' target="_blank" rel="noopener noreferrer" - sx={buttonStyles} startIcon={}> + sx={buttonStyles} startIcon={}> Join Maxun Cloud - From ef65fb3e27fa7bd4e6ef791166efa58745d21068 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 9 Oct 2025 00:03:52 +0530 Subject: [PATCH 19/36] chore: remove unused imports --- src/components/dashboard/MainMenu.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index 5e58069c..26abd6f7 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -3,8 +3,8 @@ import Tabs from '@mui/material/Tabs'; import Tab from '@mui/material/Tab'; import Box from '@mui/material/Box'; import { useNavigate, useLocation } from 'react-router-dom'; -import { Paper, Button, useTheme, Modal, Typography, Stack, TextField, InputAdornment, IconButton } from "@mui/material"; -import { AutoAwesome, FormatListBulleted, VpnKey, Usb, CloudQueue, Description, Favorite, ContentCopy, SlowMotionVideo, PlayArrow } from "@mui/icons-material"; +import { Paper, Button, useTheme, Modal, Typography, Stack } from "@mui/material"; +import { AutoAwesome, VpnKey, Usb, CloudQueue, Description, Favorite, SlowMotionVideo, PlayArrow } from "@mui/icons-material"; import { useTranslation } from 'react-i18next'; import { useGlobalInfoStore } from "../../context/globalInfo"; From 76459832a269358fcb89c738560495b854aca11b Mon Sep 17 00:00:00 2001 From: Rohit Rajan Date: Sat, 11 Oct 2025 11:44:14 +0530 Subject: [PATCH 20/36] fix: disable postCSS processing --- src/components/recorder/DOMBrowserRenderer.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/recorder/DOMBrowserRenderer.tsx b/src/components/recorder/DOMBrowserRenderer.tsx index 7fcafdeb..a212f14b 100644 --- a/src/components/recorder/DOMBrowserRenderer.tsx +++ b/src/components/recorder/DOMBrowserRenderer.tsx @@ -908,6 +908,7 @@ export const DOMBrowserRenderer: React.FC = ({ rebuild(snapshotData.snapshot, { doc: iframeDoc, mirror: mirror, + hackCss: false, cache: { stylesWithHoverClass: new Map() }, afterAppend: (node) => { if (node.nodeType === Node.TEXT_NODE && node.textContent) { From ce2e28aea13d386196ebd0dea6fce6c370f15672 Mon Sep 17 00:00:00 2001 From: Rohit Rajan Date: Mon, 13 Oct 2025 10:47:45 +0530 Subject: [PATCH 21/36] fix: prioritize link elements --- src/helpers/clientSelectorGenerator.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/helpers/clientSelectorGenerator.ts b/src/helpers/clientSelectorGenerator.ts index 0ecb148a..d4e5051c 100644 --- a/src/helpers/clientSelectorGenerator.ts +++ b/src/helpers/clientSelectorGenerator.ts @@ -560,21 +560,20 @@ class ClientSelectorGenerator { return element.hasAttribute("src"); } + if (tagName === "a" && element.hasAttribute("href")) { + return true; + } + if (element.children.length > 0) { return false; } const text = (element.textContent || "").trim(); - const hasHref = element.hasAttribute("href"); if (text.length > 0) { return true; } - if (tagName === "a" && hasHref) { - return true; - } - return false; } From 511e1c92060a0c62fea1ed80fa66851172afc078 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 13 Oct 2025 16:40:07 +0530 Subject: [PATCH 22/36] fix: change mode --- src/components/dashboard/NavBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dashboard/NavBar.tsx b/src/components/dashboard/NavBar.tsx index 24e11d09..2d10a3ab 100644 --- a/src/components/dashboard/NavBar.tsx +++ b/src/components/dashboard/NavBar.tsx @@ -158,7 +158,7 @@ export const NavBar: React.FC = ({ }; const renderThemeToggle = () => ( - + Date: Mon, 13 Oct 2025 16:41:24 +0530 Subject: [PATCH 23/36] feat: no hover on mode toggle --- src/components/dashboard/NavBar.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/dashboard/NavBar.tsx b/src/components/dashboard/NavBar.tsx index 2d10a3ab..a8770466 100644 --- a/src/components/dashboard/NavBar.tsx +++ b/src/components/dashboard/NavBar.tsx @@ -163,9 +163,6 @@ export const NavBar: React.FC = ({ onClick={toggleTheme} sx={{ color: darkMode ? '#ffffff' : '#0000008A', - '&:hover': { - background: 'inherit' - } }} > {darkMode ? : } From 73ee83a9a0aec0bc2068fafebf1e535e197b826e Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 13 Oct 2025 16:42:32 +0530 Subject: [PATCH 24/36] feat: disable hover styles for icon button --- src/context/theme-provider.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/context/theme-provider.tsx b/src/context/theme-provider.tsx index 75dd4b8b..b7fe5902 100644 --- a/src/context/theme-provider.tsx +++ b/src/context/theme-provider.tsx @@ -160,9 +160,9 @@ const darkTheme = createTheme({ }, '&.MuiIconButton-colorError': { color: '#f44336', - "&:hover": { - backgroundColor: 'rgba(244, 67, 54, 0.08)', - }, + // "&:hover": { + // backgroundColor: 'rgba(244, 67, 54, 0.08)', + // }, }, }, }, From a091df4c17ca394c26eb10b08a8ec6ea1ad714c1 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 13 Oct 2025 16:43:04 +0530 Subject: [PATCH 25/36] feat: disable hover styles for icon button --- src/context/theme-provider.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/context/theme-provider.tsx b/src/context/theme-provider.tsx index b7fe5902..d9a6d745 100644 --- a/src/context/theme-provider.tsx +++ b/src/context/theme-provider.tsx @@ -155,9 +155,9 @@ const darkTheme = createTheme({ styleOverrides: { root: { color: '#ffffff', - "&:hover": { - backgroundColor: 'rgba(255, 0, 195, 0.08)', - }, + // "&:hover": { + // backgroundColor: 'rgba(255, 0, 195, 0.08)', + // }, '&.MuiIconButton-colorError': { color: '#f44336', // "&:hover": { From 995cfba23447b39b84d62b7827bb13cbad2c77e8 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 13 Oct 2025 16:52:51 +0530 Subject: [PATCH 26/36] chore: lint --- src/components/dashboard/MainMenu.tsx | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index 05846b75..7cfb2ff7 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -89,10 +89,28 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp orientation="vertical" sx={{ alignItems: 'flex-start' }} > - } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} onClick={handleRobotsClick} /> - } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> - } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> - } iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> + } + iconPosition="start" + sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} + onClick={handleRobotsClick} /> + } + iconPosition="start" + sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> + } + iconPosition="start" + sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> + } + iconPosition="start" + sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} />
From 7f998e79080cbc09beac7393072c7c7b5fe7be08 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 13 Oct 2025 16:53:16 +0530 Subject: [PATCH 27/36] feat: disable ripple --- src/components/dashboard/MainMenu.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index 7cfb2ff7..7797a65c 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -94,6 +94,7 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp label={t('mainmenu.recordings')} icon={} iconPosition="start" + disableRipple={true} sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} onClick={handleRobotsClick} /> Date: Mon, 13 Oct 2025 16:53:24 +0530 Subject: [PATCH 28/36] feat: disable ripple --- src/components/dashboard/MainMenu.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index 7797a65c..8a7e2849 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -101,6 +101,7 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp label={t('mainmenu.runs')} icon={} iconPosition="start" + disableRipple={true} sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> Date: Mon, 13 Oct 2025 16:53:33 +0530 Subject: [PATCH 29/36] feat: disable ripple --- src/components/dashboard/MainMenu.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index 8a7e2849..2151753f 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -107,6 +107,7 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp label={t('mainmenu.proxy')} icon={} iconPosition="start" + disableRipple={true} sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} /> Date: Mon, 13 Oct 2025 16:53:44 +0530 Subject: [PATCH 30/36] feat: disable ripple --- src/components/dashboard/MainMenu.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index 2151753f..3f499dc1 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -113,6 +113,7 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp label={t('mainmenu.apikey')} icon={} iconPosition="start" + disableRipple={true} sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} />
From a8d449ffb5fd0e9d035696d7c293058fd1bb843f Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 13 Oct 2025 19:32:22 +0530 Subject: [PATCH 31/36] feat: -rm unused btn --- src/components/dashboard/MainMenu.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index 05846b75..8e5614c6 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -157,9 +157,6 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp -
From 47a2b87252badda199828f1a139933be3c1ed185 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 13 Oct 2025 19:32:45 +0530 Subject: [PATCH 32/36] feat: gh sponsors --- src/components/dashboard/MainMenu.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index 8e5614c6..44ce3132 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -154,8 +154,8 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp Thank you for your support! 💙 - From d29d8e5b7dbe2a3c951c30a68e829ca65d4de703 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 13 Oct 2025 19:33:50 +0530 Subject: [PATCH 33/36] feat: gh sponsors link --- src/components/dashboard/MainMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index 44ce3132..d3127752 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -154,7 +154,7 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp Thank you for your support! 💙 - From 7f117eede99a9aca1d05eb0f0d2a457d60b836ab Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 13 Oct 2025 19:34:43 +0530 Subject: [PATCH 34/36] feat: change emoji --- src/components/dashboard/MainMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dashboard/MainMenu.tsx b/src/components/dashboard/MainMenu.tsx index d3127752..f5009042 100644 --- a/src/components/dashboard/MainMenu.tsx +++ b/src/components/dashboard/MainMenu.tsx @@ -151,7 +151,7 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp Maxun is built by a small, full-time team. Your donations directly contribute to making it better.

- Thank you for your support! 💙 + Thank you for your support! 🩷