Merge branch 'develop' into v1-dark-mode

This commit is contained in:
Karishma
2025-10-21 19:25:06 +05:30
committed by GitHub
10 changed files with 1435 additions and 65 deletions

View File

@@ -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;
export default ActionDescriptionBox;

View File

@@ -89,17 +89,39 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp
orientation="vertical"
sx={{ alignItems: 'flex-start' }}
>
<Tab value="robots" label={t('mainmenu.recordings')} icon={<AutoAwesome />} iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} onClick={handleRobotsClick} />
<Tab value="runs" label={t('mainmenu.runs')} icon={<FormatListBulleted />} iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} />
<Tab value="proxy" label={t('mainmenu.proxy')} icon={<Usb />} iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} />
<Tab value="apikey" label={t('mainmenu.apikey')} icon={<VpnKey />} iconPosition="start" sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} />
<Tab
value="robots"
label={t('mainmenu.recordings')}
icon={<AutoAwesome />}
iconPosition="start"
disableRipple={true}
sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }}
onClick={handleRobotsClick} />
<Tab value="runs"
label={t('mainmenu.runs')}
icon={<FormatListBulleted />}
iconPosition="start"
disableRipple={true}
sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} />
<Tab value="proxy"
label={t('mainmenu.proxy')}
icon={<Usb />}
iconPosition="start"
disableRipple={true}
sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} />
<Tab value="apikey"
label={t('mainmenu.apikey')}
icon={<VpnKey />}
iconPosition="start"
disableRipple={true}
sx={{ justifyContent: 'flex-start', textAlign: 'left', fontSize: 'medium' }} />
</Tabs>
<Divider sx={{ borderColor: theme.palette.mode === 'dark' ? "#080808ff" : "" }} />
<Box sx={{ display: 'flex', flexDirection: 'column', textAlign: 'left' }}>
<Button
onClick={() => setDocModalOpen(true)}
sx={buttonStyles}
startIcon={<Description />}
startIcon={<Description sx={{ fontSize: 20 }} />}
>
Documentation
</Button>
@@ -133,10 +155,10 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp
href='https://app.maxun.dev/'
target="_blank"
rel="noopener noreferrer"
sx={buttonStyles} startIcon={<CloudQueue />}>
sx={buttonStyles} startIcon={<CloudQueue sx={{ fontSize: 20 }} />}>
Join Maxun Cloud
</Button>
<Button onClick={() => setSponsorModalOpen(true)} sx={buttonStyles} startIcon={<Favorite />}>
<Button onClick={() => setSponsorModalOpen(true)} sx={buttonStyles} startIcon={<Favorite sx={{ fontSize: 20 }} />}>
Sponsor Us
</Button>
</Box>
@@ -150,15 +172,11 @@ export const MainMenu = ({ value = 'robots', handleChangeContent }: MainMenuProp
<Typography variant="body1" gutterBottom>
Maxun is built by a small, full-time team. Your donations directly contribute to making it better.
<br />
<br />
Thank you for your support! 💙
Thank you for your support! 🩷
</Typography>
<Stack direction="row" spacing={2} mt={2}>
<Button href="https://checkout.dodopayments.com/buy/pdt_1Bdstszcg9VY8WYGwNBPM?quantity=1" target="_blank" rel="noopener noreferrer" variant="outlined" fullWidth>
Sponsor $5 One-Time
</Button>
<Button href="https://checkout.dodopayments.com/buy/pdt_HDalaYf8hEGVG7hXcfNBj?quantity=1" target="_blank" rel="noopener noreferrer" variant="outlined" fullWidth>
Sponsor $5 Monthly
<Stack direction="row" spacing={2} mt={4}>
<Button href="https://github.com/sponsors/amhsirak" target="_blank" rel="noopener noreferrer" variant="outlined" fullWidth>
Sponsor Maxun on GitHub Sponsors
</Button>
</Stack>
</Box>

View File

@@ -158,14 +158,11 @@ export const NavBar: React.FC<NavBarProps> = ({
};
const renderThemeToggle = () => (
<Tooltip title="Toggle Mode">
<Tooltip title="Change Mode">
<IconButton
onClick={toggleTheme}
sx={{
color: darkMode ? '#ffffff' : '#0000008A',
'&:hover': {
background: 'inherit'
}
}}
>
{darkMode ? <LightMode /> : <DarkMode />}

View File

@@ -908,6 +908,7 @@ export const DOMBrowserRenderer: React.FC<RRWebDOMBrowserRendererProps> = ({
rebuild(snapshotData.snapshot, {
doc: iframeDoc,
mirror: mirror,
hackCss: false,
cache: { stylesWithHoverClass: new Map() },
afterAppend: (node) => {
if (node.nodeType === Node.TEXT_NODE && node.textContent) {

View File

@@ -188,8 +188,8 @@ export const ScheduleSettingsPage = ({
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
padding: "20px",
"& > *": { marginBottom: "20px" },
marginTop: "-20px",
}}
>
<>

View File

@@ -555,38 +555,25 @@ 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)) {
if (tagName === "img") {
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");
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")
);
}
return false;
}
@@ -2561,12 +2548,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 +2560,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 +2572,7 @@ class ClientSelectorGenerator {
meaningfulDescendants.push(element);
}
if (currentDepth >= adjustedMaxDepth) {
if (currentDepth >= MAX_DEPTH) {
continue;
}
@@ -2607,7 +2587,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 +2696,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 +2746,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 +2918,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 +3989,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<HTMLElement>();
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
*/