From 4f781da136511a576570679354dcf7e450c4ad79 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 4 Dec 2024 23:53:41 +0530 Subject: [PATCH 01/82] feat: upgrade backend image to v0.0.4 --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 92b69c14..ca64f644 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -43,7 +43,7 @@ services: #build: #context: . #dockerfile: server/Dockerfile - image: getmaxun/maxun-backend:v0.0.3 + image: getmaxun/maxun-backend:v0.0.4 ports: - "${BACKEND_PORT:-8080}:${BACKEND_PORT:-8080}" env_file: .env From 1a59255c76ba5b3f7342c01f277302613f3db3f3 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 5 Dec 2024 23:06:26 +0530 Subject: [PATCH 02/82] chore: v0.0.5 maxun core --- maxun-core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maxun-core/package.json b/maxun-core/package.json index faa133ec..90ee01b7 100644 --- a/maxun-core/package.json +++ b/maxun-core/package.json @@ -1,6 +1,6 @@ { "name": "maxun-core", - "version": "0.0.4", + "version": "0.0.5", "description": "Core package for Maxun, responsible for data extraction", "main": "build/index.js", "typings": "build/index.d.ts", From 71803055ab1589a9df044affcc21b6bccd3c7afe Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 5 Dec 2024 23:09:32 +0530 Subject: [PATCH 03/82] chore: use v0.0.5 maxun-core --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b6b73537..0269e404 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "jwt-decode": "^4.0.0", "loglevel": "^1.8.0", "loglevel-plugin-remote": "^0.6.8", - "maxun-core": "0.0.4", + "maxun-core": "0.0.5", "minio": "^8.0.1", "moment-timezone": "^0.5.45", "node-cron": "^3.0.3", From b1e2c30d7f7cdb4b71bdb828ca1688e978b1718d Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 5 Dec 2024 23:10:03 +0530 Subject: [PATCH 04/82] chore: use v0.0.5 maxun-core --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0269e404..006ddc8a 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "jwt-decode": "^4.0.0", "loglevel": "^1.8.0", "loglevel-plugin-remote": "^0.6.8", - "maxun-core": "0.0.5", + "maxun-core": "^0.0.5", "minio": "^8.0.1", "moment-timezone": "^0.5.45", "node-cron": "^3.0.3", @@ -110,4 +110,4 @@ "ts-node": "^10.4.0", "vite": "^5.4.10" } -} \ No newline at end of file +} From 939e7a7bc4aea280d55e6178b4c5d28d352ce2a6 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 5 Dec 2024 23:10:32 +0530 Subject: [PATCH 05/82] chore: maxun v0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 006ddc8a..977daada 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "maxun", - "version": "0.0.2", + "version": "0.0.4", "author": "Maxun", "license": "AGPL-3.0-or-later", "dependencies": { From 1fc7ddc363ee94409c487b90c9e9cedc9821644a Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 5 Dec 2024 23:19:55 +0530 Subject: [PATCH 06/82] chore: merge pagination hotfix --- .../workflow-management/classes/Generator.ts | 45 +------------------ 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/server/src/workflow-management/classes/Generator.ts b/server/src/workflow-management/classes/Generator.ts index cfef4a30..c9dc3385 100644 --- a/server/src/workflow-management/classes/Generator.ts +++ b/server/src/workflow-management/classes/Generator.ts @@ -160,41 +160,6 @@ export class WorkflowGenerator { }) }; - /** - * New function to handle actionable check for scrapeList - * @param page The current Playwright Page object. - * @param config The scrapeList configuration object. - * @returns {Promise} Array of actionable selectors. - */ - private async getSelectorsForScrapeList(page: Page, config: { - listSelector: string; - fields: any; - limit?: number; - pagination: any; - }): Promise { - const { listSelector } = config; - - // Verify if the selectors are present and actionable on the current page - const actionableSelectors: string[] = []; - if (listSelector) { - const isActionable = await page.isVisible(listSelector).catch(() => false); - if (isActionable) { - actionableSelectors.push(listSelector); - logger.log('debug', `List selector ${listSelector} is actionable.`); - } else { - logger.log('warn', `List selector ${listSelector} is not visible on the page.`); - } - } - - return actionableSelectors; - } - - /** - * New function to handle actionable check for scrapeList - * @param page The current Playwright Page object. - * @param schema The scrapeSchema configuration object. - * @returns {Promise} Array of actionable selectors. - */ private async getSelectorsForSchema(page: Page, schema: Record): Promise { const selectors = Object.values(schema).map((field) => field.selector); @@ -243,14 +208,6 @@ export class WorkflowGenerator { pair.where.selectors = [...(pair.where.selectors || []), ...additionalSelectors]; } } - - if (pair.what[0].action === 'scrapeList') { - const config = pair.what[0]?.args?.[0]; - if (config) { - const actionableSelectors = await this.getSelectorsForScrapeList(page, config); - pair.where.selectors = [...(pair.where.selectors || []), ...actionableSelectors]; - } - } // Validate if the pair is already in the workflow if (pair.where.selectors && pair.where.selectors[0]) { @@ -923,4 +880,4 @@ export class WorkflowGenerator { public clearLastIndex = () => { this.generatedData.lastIndex = null; } -} +} \ No newline at end of file From 236f3edcd47b059decb6fbf9bb52caf874dfa25e Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 5 Dec 2024 23:23:30 +0530 Subject: [PATCH 07/82] chore: merge pagination hotfix --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index ca64f644..46cc72c4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -43,7 +43,7 @@ services: #build: #context: . #dockerfile: server/Dockerfile - image: getmaxun/maxun-backend:v0.0.4 + image: getmaxun/maxun-backend:v0.0.5 ports: - "${BACKEND_PORT:-8080}:${BACKEND_PORT:-8080}" env_file: .env From 5ed8e8ae427fc47e645c7b37dfb770df96f727dc Mon Sep 17 00:00:00 2001 From: amhsirak Date: Fri, 6 Dec 2024 03:20:30 +0530 Subject: [PATCH 08/82] fix: use window.location.origin instead of base url --- maxun-core/src/browserSide/scraper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maxun-core/src/browserSide/scraper.js b/maxun-core/src/browserSide/scraper.js index 369a08be..467d0eb2 100644 --- a/maxun-core/src/browserSide/scraper.js +++ b/maxun-core/src/browserSide/scraper.js @@ -285,7 +285,7 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, } else if (attribute === 'src') { // Handle relative 'src' URLs const src = fieldElement.getAttribute('src'); - record[label] = src ? new URL(src, baseUrl).href : null; + record[label] = src ? new URL(src, window.location.origin).href : null; } else if (attribute === 'href') { // Handle relative 'href' URLs const href = fieldElement.getAttribute('href'); From 5985bc11f058c2e730b3daee180a6fa0ff710163 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Fri, 6 Dec 2024 03:20:48 +0530 Subject: [PATCH 09/82] fix: use window.location.origin instead of base url --- maxun-core/src/browserSide/scraper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maxun-core/src/browserSide/scraper.js b/maxun-core/src/browserSide/scraper.js index 467d0eb2..d3410de4 100644 --- a/maxun-core/src/browserSide/scraper.js +++ b/maxun-core/src/browserSide/scraper.js @@ -289,7 +289,7 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, } else if (attribute === 'href') { // Handle relative 'href' URLs const href = fieldElement.getAttribute('href'); - record[label] = href ? new URL(href, baseUrl).href : null; + record[label] = href ? new URL(href, window.location.origin).href : null; } else { record[label] = fieldElement.getAttribute(attribute); } From 429ddaa5719634b84f481c2360702c1bc59aa296 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Fri, 6 Dec 2024 03:21:18 +0530 Subject: [PATCH 10/82] chore: lint --- maxun-core/src/browserSide/scraper.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/maxun-core/src/browserSide/scraper.js b/maxun-core/src/browserSide/scraper.js index d3410de4..09b6578b 100644 --- a/maxun-core/src/browserSide/scraper.js +++ b/maxun-core/src/browserSide/scraper.js @@ -283,9 +283,9 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, } else if (attribute === 'innerHTML') { record[label] = fieldElement.innerHTML.trim(); } else if (attribute === 'src') { - // Handle relative 'src' URLs - const src = fieldElement.getAttribute('src'); - record[label] = src ? new URL(src, window.location.origin).href : null; + // Handle relative 'src' URLs + const src = fieldElement.getAttribute('src'); + record[label] = src ? new URL(src, window.location.origin).href : null; } else if (attribute === 'href') { // Handle relative 'href' URLs const href = fieldElement.getAttribute('href'); @@ -346,5 +346,5 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, return results; }; - + })(window); \ No newline at end of file From 079863d015c1d3883d3d4c2154239fe419117046 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Fri, 6 Dec 2024 03:39:15 +0530 Subject: [PATCH 11/82] feat: add earliest selectors logic for page state --- maxun-core/src/interpret.ts | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index a7a5de47..06586038 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -121,6 +121,26 @@ export default class Interpreter extends EventEmitter { } } + private getPreviousSelectors(workflow: Workflow, actionId: number): string[] { + const selectors: string[] = []; + let index = actionId - 1; + + while (index >= 0) { + const previousSelectors = workflow[index]?.where?.selectors; + if (previousSelectors && previousSelectors.length > 0) { + previousSelectors.forEach((selector) => { + if (!selectors.includes(selector)) { + selectors.push(selector); // Avoid duplicates + } + }); + break; // Exit the loop once valid selectors are found + } + index--; // Move further back in the workflow + } + + return selectors; + } + /** * Returns the context object from given Page and the current workflow.\ * \ @@ -130,11 +150,11 @@ export default class Interpreter extends EventEmitter { * @param workflow Current **initialized** workflow (array of where-what pairs). * @returns {PageState} State of the current page. */ - private async getState(page: Page, workflow: Workflow): Promise { + private async getState(page: Page, workflow: Workflow, selectors: string[]): Promise { /** * All the selectors present in the current Workflow */ - const selectors = Preprocessor.extractSelectors(workflow); + // const selectors = Preprocessor.extractSelectors(workflow); /** * Determines whether the element targetted by the selector is [actionable](https://playwright.dev/docs/actionability). @@ -365,6 +385,7 @@ export default class Interpreter extends EventEmitter { console.log("MERGED results:", mergedResult); await this.options.serializableCallback(mergedResult); + // await this.options.serializableCallback(scrapeResult); }, scrapeList: async (config: { listSelector: string, fields: any, limit?: number, pagination: any }) => { @@ -550,6 +571,7 @@ export default class Interpreter extends EventEmitter { // apply ad-blocker to the current page await this.applyAdBlocker(p); const usedActions: string[] = []; + const selectors: string[] = []; let lastAction = null; let repeatCount = 0; @@ -579,7 +601,7 @@ export default class Interpreter extends EventEmitter { let pageState = {}; try { - pageState = await this.getState(p, workflow); + pageState = await this.getState(p, workflow, selectors); } catch (e: any) { this.log('The browser has been closed.'); return; @@ -615,6 +637,9 @@ export default class Interpreter extends EventEmitter { try { await this.carryOutSteps(p, action.what); usedActions.push(action.id ?? 'undefined'); + + selectors.push(...this.getPreviousSelectors(workflow, actionId)); + console.log("SELECTORS", selectors); } catch (e) { this.log(e, Level.ERROR); } From 964913775e77b31061428432fcc2093e8f6f7206 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Fri, 6 Dec 2024 03:40:58 +0530 Subject: [PATCH 12/82] fix: add on load emit urlChanged --- .../browser-management/classes/RemoteBrowser.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/server/src/browser-management/classes/RemoteBrowser.ts b/server/src/browser-management/classes/RemoteBrowser.ts index 769787da..e30632c3 100644 --- a/server/src/browser-management/classes/RemoteBrowser.ts +++ b/server/src/browser-management/classes/RemoteBrowser.ts @@ -402,14 +402,14 @@ export class RemoteBrowser { await this.currentPage?.close(); this.currentPage = newPage; if (this.currentPage) { - this.currentPage.on('framenavigated', (frame) => { - if (frame === this.currentPage?.mainFrame()) { - this.socket.emit('urlChanged', this.currentPage.url()); - } - }); - // this.currentPage.on('load', (page) => { - // this.socket.emit('urlChanged', page.url()); - // }) + // this.currentPage.on('framenavigated', (frame) => { + // if (frame === this.currentPage?.mainFrame()) { + // this.socket.emit('urlChanged', this.currentPage.url()); + // } + // }); + this.currentPage.on('load', (page) => { + this.socket.emit('urlChanged', page.url()); + }) this.client = await this.currentPage.context().newCDPSession(this.currentPage); await this.subscribeToScreencast(); } else { From 95b2c508a7a3e9c6c3958e9b12c1e42a2519e657 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Fri, 6 Dec 2024 04:29:40 +0530 Subject: [PATCH 13/82] chore: proper spacing --- src/components/molecules/IntegrationSettings.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index c31605de..a8c19961 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -15,11 +15,13 @@ import { useGlobalInfoStore } from "../../context/globalInfo"; import { getStoredRecording } from "../../api/storage"; import { apiUrl } from "../../apiConfig.js"; import Cookies from 'js-cookie'; + interface IntegrationProps { isOpen: boolean; handleStart: (data: IntegrationSettings) => void; handleClose: () => void; } + export interface IntegrationSettings { spreadsheetId: string; spreadsheetName: string; From 4550718e4d6079ee46c96d4c15348d20b73e9b05 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Fri, 6 Dec 2024 04:37:39 +0530 Subject: [PATCH 14/82] chore: lint --- src/components/molecules/IntegrationSettings.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index a8c19961..b34bc0e9 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -77,8 +77,7 @@ export const IntegrationSettingsModal = ({ ); notify( "error", - `Error fetching spreadsheet files: ${ - error.response?.data?.message || error.message + `Error fetching spreadsheet files: ${error.response?.data?.message || error.message }` ); } From a6f4d0436b28298cdb6b0c388470ef1b2d4a33e2 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Fri, 6 Dec 2024 04:56:31 +0530 Subject: [PATCH 15/82] feat: show maxun version --- src/components/molecules/NavBar.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index ee8c80e8..8d195ebc 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -11,6 +11,7 @@ import { SaveRecording } from '../molecules/SaveRecording'; import DiscordIcon from '../atoms/DiscordIcon'; import { apiUrl } from '../../apiConfig'; import MaxunLogo from "../../assets/maxunlogo.png"; +import packageJson from "../../../package.json" interface NavBarProps { recordingName: string; @@ -57,7 +58,11 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => justifyContent: 'flex-start', }}> -
Maxun
+
Maxun
{ user ? ( From bbf9699dfd314021f4164794c779289d1838e470 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Fri, 6 Dec 2024 04:57:09 +0530 Subject: [PATCH 16/82] chore: lint --- src/components/molecules/NavBar.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 8d195ebc..bc373576 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -58,11 +58,13 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => justifyContent: 'flex-start', }}> -
Maxun
+
Maxun + +
{ user ? ( From 8a0f2b6ca54705bf6168bc269e4ae09161d966f9 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Fri, 6 Dec 2024 05:00:46 +0530 Subject: [PATCH 17/82] feat: format version chip --- src/components/molecules/NavBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index bc373576..6b2bfa92 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -58,14 +58,14 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => justifyContent: 'flex-start', }}> -
Maxun +
Maxun
- { user ? (
From 76255c21eaffd2eb705f234357d0be4b4ccff29a Mon Sep 17 00:00:00 2001 From: amhsirak Date: Fri, 6 Dec 2024 05:05:25 +0530 Subject: [PATCH 18/82] chore: remove unused chip import --- src/components/molecules/Pair.tsx | 2 +- src/components/molecules/RobotDuplicate.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/Pair.tsx b/src/components/molecules/Pair.tsx index b05b912d..3c332600 100644 --- a/src/components/molecules/Pair.tsx +++ b/src/components/molecules/Pair.tsx @@ -1,5 +1,5 @@ import React, { FC, useState } from 'react'; -import { Stack, Button, IconButton, Tooltip, Chip, Badge } from "@mui/material"; +import { Stack, Button, IconButton, Tooltip, Badge } from "@mui/material"; import { AddPair, deletePair, UpdatePair } from "../../api/workflow"; import { WorkflowFile } from "maxun-core"; import { ClearButton } from "../atoms/buttons/ClearButton"; diff --git a/src/components/molecules/RobotDuplicate.tsx b/src/components/molecules/RobotDuplicate.tsx index 850614b0..38b7b422 100644 --- a/src/components/molecules/RobotDuplicate.tsx +++ b/src/components/molecules/RobotDuplicate.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; import { GenericModal } from "../atoms/GenericModal"; -import { TextField, Typography, Box, Button, Chip } from "@mui/material"; +import { TextField, Typography, Box, Button } from "@mui/material"; import { modalStyle } from "./AddWhereCondModal"; import { useGlobalInfoStore } from '../../context/globalInfo'; import { duplicateRecording, getStoredRecording } from '../../api/storage'; From 0d6633130596993aded87bf71420c9374e04a13c Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Fri, 6 Dec 2024 16:57:55 +0530 Subject: [PATCH 19/82] fix: add on load socket emit --- server/src/browser-management/classes/RemoteBrowser.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/src/browser-management/classes/RemoteBrowser.ts b/server/src/browser-management/classes/RemoteBrowser.ts index e30632c3..cfcc96f8 100644 --- a/server/src/browser-management/classes/RemoteBrowser.ts +++ b/server/src/browser-management/classes/RemoteBrowser.ts @@ -370,11 +370,11 @@ export class RemoteBrowser { await this.stopScreencast(); this.currentPage = page; - this.currentPage.on('framenavigated', (frame) => { - if (frame === this.currentPage?.mainFrame()) { - this.socket.emit('urlChanged', this.currentPage.url()); - } - }); + // this.currentPage.on('framenavigated', (frame) => { + // if (frame === this.currentPage?.mainFrame()) { + // this.socket.emit('urlChanged', this.currentPage.url()); + // } + // }); //await this.currentPage.setViewportSize({ height: 400, width: 900 }) this.client = await this.currentPage.context().newCDPSession(this.currentPage); From bffe8389889d909c4913f6d68b4085100dc3bf8c Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Fri, 6 Dec 2024 17:06:20 +0530 Subject: [PATCH 20/82] feat: add earliest selectors from workflow --- maxun-core/src/interpret.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index 06586038..5c24317c 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -127,6 +127,7 @@ export default class Interpreter extends EventEmitter { while (index >= 0) { const previousSelectors = workflow[index]?.where?.selectors; + console.log("Previous Selectors:", previousSelectors); if (previousSelectors && previousSelectors.length > 0) { previousSelectors.forEach((selector) => { if (!selectors.includes(selector)) { @@ -156,6 +157,8 @@ export default class Interpreter extends EventEmitter { */ // const selectors = Preprocessor.extractSelectors(workflow); + console.log("All selectors:", selectors); + /** * Determines whether the element targetted by the selector is [actionable](https://playwright.dev/docs/actionability). * @param selector Selector to be queried @@ -164,8 +167,8 @@ export default class Interpreter extends EventEmitter { const actionable = async (selector: string): Promise => { try { const proms = [ - page.isEnabled(selector, { timeout: 500 }), - page.isVisible(selector, { timeout: 500 }), + page.isEnabled(selector, { timeout: 2000 }), + page.isVisible(selector, { timeout: 2000 }), ]; return await Promise.all(proms).then((bools) => bools.every((x) => x)); @@ -627,19 +630,26 @@ export default class Interpreter extends EventEmitter { if (this.options.debugChannel?.activeId) { this.options.debugChannel.activeId(actionId); } - + repeatCount = action === lastAction ? repeatCount + 1 : 0; - if (this.options.maxRepeats && repeatCount >= this.options.maxRepeats) { + + console.log("REPEAT COUNT", repeatCount); + if (this.options.maxRepeats && repeatCount > this.options.maxRepeats) { return; } lastAction = action; - + try { + console.log("Carrying out:", action.what); await this.carryOutSteps(p, action.what); usedActions.push(action.id ?? 'undefined'); - selectors.push(...this.getPreviousSelectors(workflow, actionId)); - console.log("SELECTORS", selectors); + const newSelectors = this.getPreviousSelectors(workflow, actionId); + newSelectors.forEach(selector => { + if (!selectors.includes(selector)) { + selectors.push(selector); + } + }); } catch (e) { this.log(e, Level.ERROR); } From 0ee50e1c26eaad4326135c76117ebe479e1606ed Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Fri, 6 Dec 2024 22:10:28 +0530 Subject: [PATCH 21/82] feat: add bottom up workflow traversal --- maxun-core/src/interpret.ts | 107 +++++++++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 25 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index 5c24317c..2457f79b 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -121,27 +121,55 @@ export default class Interpreter extends EventEmitter { } } - private getPreviousSelectors(workflow: Workflow, actionId: number): string[] { - const selectors: string[] = []; - let index = actionId - 1; + // private getPreviousSelectors(workflow: Workflow, actionId: number): string[] { + // const selectors: string[] = []; + // let index = actionId - 1; - while (index >= 0) { - const previousSelectors = workflow[index]?.where?.selectors; - console.log("Previous Selectors:", previousSelectors); - if (previousSelectors && previousSelectors.length > 0) { - previousSelectors.forEach((selector) => { + // while (index >= 0) { + // const previousSelectors = workflow[index]?.where?.selectors; + // console.log("Previous Selectors:", previousSelectors); + // if (previousSelectors && previousSelectors.length > 0) { + // previousSelectors.forEach((selector) => { + // if (!selectors.includes(selector)) { + // selectors.push(selector); // Avoid duplicates + // } + // }); + // break; // Exit the loop once valid selectors are found + // } + // index--; // Move further back in the workflow + // } + + // return selectors; + // } + + private getSelectors(workflow: Workflow, actionId: number): string[] { + const selectors: string[] = []; + + // Validate actionId + if (actionId <= 0) { + console.log("No previous selectors to collect."); + return selectors; // Empty array as there are no previous steps + } + + // Iterate from the start up to (but not including) actionId + for (let index = 0; index < actionId; index++) { + const currentSelectors = workflow[index]?.where?.selectors; + console.log(`Selectors at step ${index}:`, currentSelectors); + + if (currentSelectors && currentSelectors.length > 0) { + currentSelectors.forEach((selector) => { if (!selectors.includes(selector)) { selectors.push(selector); // Avoid duplicates } }); - break; // Exit the loop once valid selectors are found } - index--; // Move further back in the workflow } + console.log("Collected Selectors:", selectors); return selectors; } + /** * Returns the context object from given Page and the current workflow.\ * \ @@ -167,8 +195,8 @@ export default class Interpreter extends EventEmitter { const actionable = async (selector: string): Promise => { try { const proms = [ - page.isEnabled(selector, { timeout: 2000 }), - page.isVisible(selector, { timeout: 2000 }), + page.isEnabled(selector, { timeout: 500 }), + page.isVisible(selector, { timeout: 500 }), ]; return await Promise.all(proms).then((bools) => bools.every((x) => x)); @@ -198,7 +226,7 @@ export default class Interpreter extends EventEmitter { ...p, [cookie.name]: cookie.value, }), {}), - selectors: presentSelectors, + selectors: selectors, }; } @@ -570,11 +598,29 @@ export default class Interpreter extends EventEmitter { return allResults; } + private getMatchingActionId(workflow: Workflow, pageState: PageState, usedActions: string[]) { + for (let actionId = workflow.length - 1; actionId >= 0; actionId--) { + const step = workflow[actionId]; + const isApplicable = this.applicable(step.where, pageState, usedActions); + console.log("-------------------------------------------------------------"); + console.log(`Where:`, step.where); + console.log(`Page state:`, pageState); + console.log(`Match result: ${isApplicable}`); + console.log("-------------------------------------------------------------"); + + if (isApplicable) { + return actionId; + } + } + } + private async runLoop(p: Page, workflow: Workflow) { + const workflowCopy: Workflow = JSON.parse(JSON.stringify(workflow)); + // apply ad-blocker to the current page await this.applyAdBlocker(p); const usedActions: string[] = []; - const selectors: string[] = []; + let selectors: string[] = []; let lastAction = null; let repeatCount = 0; @@ -584,7 +630,7 @@ export default class Interpreter extends EventEmitter { * e.g. via `enqueueLinks`. */ p.on('popup', (popup) => { - this.concurrency.addJob(() => this.runLoop(popup, workflow)); + this.concurrency.addJob(() => this.runLoop(popup, workflowCopy)); }); /* eslint no-constant-condition: ["warn", { "checkLoops": false }] */ @@ -604,7 +650,8 @@ export default class Interpreter extends EventEmitter { let pageState = {}; try { - pageState = await this.getState(p, workflow, selectors); + pageState = await this.getState(p, workflowCopy, selectors); + selectors = []; } catch (e: any) { this.log('The browser has been closed.'); return; @@ -614,16 +661,22 @@ export default class Interpreter extends EventEmitter { this.log(`Current state is: \n${JSON.stringify(pageState, null, 2)}`, Level.WARN); } - const actionId = workflow.findIndex((step) => { - const isApplicable = this.applicable(step.where, pageState, usedActions); - console.log(`Where:`, step.where); - console.log(`Page state:`, pageState); - console.log(`Match result: ${isApplicable}`); - return isApplicable; - }); + // const actionId = workflow.findIndex((step) => { + // const isApplicable = this.applicable(step.where, pageState, usedActions); + // console.log("-------------------------------------------------------------"); + // console.log(`Where:`, step.where); + // console.log(`Page state:`, pageState); + // console.log(`Match result: ${isApplicable}`); + // console.log("-------------------------------------------------------------"); + // return isApplicable; + // }); - const action = workflow[actionId]; + const actionId = this.getMatchingActionId(workflowCopy, pageState, usedActions); + const action = workflowCopy[actionId]; + + console.log("MATCHED ACTION:", action); + console.log("MATCHED ACTION ID:", actionId); this.log(`Matched ${JSON.stringify(action?.where)}`, Level.LOG); if (action) { // action is matched @@ -643,8 +696,12 @@ export default class Interpreter extends EventEmitter { console.log("Carrying out:", action.what); await this.carryOutSteps(p, action.what); usedActions.push(action.id ?? 'undefined'); + + workflowCopy.splice(actionId, 1); + console.log(`Action with ID ${action.id} removed from the workflow copy.`); - const newSelectors = this.getPreviousSelectors(workflow, actionId); + // const newSelectors = this.getPreviousSelectors(workflow, actionId); + const newSelectors = this.getSelectors(workflowCopy, actionId); newSelectors.forEach(selector => { if (!selectors.includes(selector)) { selectors.push(selector); From d6be2683fdfc8863db235b6c679cd93eca15af45 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sat, 7 Dec 2024 21:16:58 +0530 Subject: [PATCH 22/82] feat: add check to match action url and return --- maxun-core/src/interpret.ts | 63 ++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index 2457f79b..fce67257 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -179,54 +179,62 @@ export default class Interpreter extends EventEmitter { * @param workflow Current **initialized** workflow (array of where-what pairs). * @returns {PageState} State of the current page. */ - private async getState(page: Page, workflow: Workflow, selectors: string[]): Promise { + private async getState(page: Page, workflowCopy: Workflow, selectors: string[]): Promise { /** * All the selectors present in the current Workflow */ // const selectors = Preprocessor.extractSelectors(workflow); - - console.log("All selectors:", selectors); + // console.log("Current selectors:", selectors); /** * Determines whether the element targetted by the selector is [actionable](https://playwright.dev/docs/actionability). * @param selector Selector to be queried * @returns True if the targetted element is actionable, false otherwise. */ - const actionable = async (selector: string): Promise => { - try { - const proms = [ - page.isEnabled(selector, { timeout: 500 }), - page.isVisible(selector, { timeout: 500 }), - ]; + // const actionable = async (selector: string): Promise => { + // try { + // const proms = [ + // page.isEnabled(selector, { timeout: 5000 }), + // page.isVisible(selector, { timeout: 5000 }), + // ]; - return await Promise.all(proms).then((bools) => bools.every((x) => x)); - } catch (e) { - // log(e, Level.ERROR); - return false; - } - }; + // return await Promise.all(proms).then((bools) => bools.every((x) => x)); + // } catch (e) { + // // log(e, Level.ERROR); + // return false; + // } + // }; /** * Object of selectors present in the current page. */ - const presentSelectors: SelectorArray = await Promise.all( - selectors.map(async (selector) => { - if (await actionable(selector)) { - return [selector]; - } - return []; - }), - ).then((x) => x.flat()); + // const presentSelectors: SelectorArray = await Promise.all( + // selectors.map(async (selector) => { + // if (await actionable(selector)) { + // return [selector]; + // } + // return []; + // }), + // ).then((x) => x.flat()); + const action = workflowCopy[workflowCopy.length - 1]; + + console.log("Next action:", action) + + let url: any = page.url(); + + if (action && action.where.url !== url && action.where.url !== "about:blank") { + url = action.where.url; + } return { - url: page.url(), + url, cookies: (await page.context().cookies([page.url()])) .reduce((p, cookie) => ( { ...p, [cookie.name]: cookie.value, }), {}), - selectors: selectors, + selectors, }; } @@ -622,6 +630,7 @@ export default class Interpreter extends EventEmitter { const usedActions: string[] = []; let selectors: string[] = []; let lastAction = null; + let actionId = -1 let repeatCount = 0; /** @@ -649,9 +658,11 @@ export default class Interpreter extends EventEmitter { } let pageState = {}; + let getStateTest = "Hello"; try { pageState = await this.getState(p, workflowCopy, selectors); selectors = []; + console.log("Empty selectors:", selectors) } catch (e: any) { this.log('The browser has been closed.'); return; @@ -671,7 +682,7 @@ export default class Interpreter extends EventEmitter { // return isApplicable; // }); - const actionId = this.getMatchingActionId(workflowCopy, pageState, usedActions); + actionId = this.getMatchingActionId(workflowCopy, pageState, usedActions); const action = workflowCopy[actionId]; From db37c72ce5b8a97f2fa9be6cd12c3b3f0bb07d62 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sat, 7 Dec 2024 21:18:37 +0530 Subject: [PATCH 23/82] fix: add goto frame navigation --- server/src/browser-management/classes/RemoteBrowser.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/src/browser-management/classes/RemoteBrowser.ts b/server/src/browser-management/classes/RemoteBrowser.ts index cfcc96f8..f1d18f3f 100644 --- a/server/src/browser-management/classes/RemoteBrowser.ts +++ b/server/src/browser-management/classes/RemoteBrowser.ts @@ -402,11 +402,11 @@ export class RemoteBrowser { await this.currentPage?.close(); this.currentPage = newPage; if (this.currentPage) { - // this.currentPage.on('framenavigated', (frame) => { - // if (frame === this.currentPage?.mainFrame()) { - // this.socket.emit('urlChanged', this.currentPage.url()); - // } - // }); + this.currentPage.on('framenavigated', (frame) => { + if (frame === this.currentPage?.mainFrame()) { + this.socket.emit('urlChanged', this.currentPage.url()); + } + }); this.currentPage.on('load', (page) => { this.socket.emit('urlChanged', page.url()); }) From a8e8c1de82b0ff47add118b8ab74377d4076f42c Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sat, 7 Dec 2024 21:53:55 +0530 Subject: [PATCH 24/82] fix: rm about:blank url check for action --- maxun-core/src/interpret.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index fce67257..c2a1186c 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -222,7 +222,7 @@ export default class Interpreter extends EventEmitter { let url: any = page.url(); - if (action && action.where.url !== url && action.where.url !== "about:blank") { + if (action && action.where.url !== url) { url = action.where.url; } From 342fd79588e14a5c9b66a11329b9a06d3c4980b6 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sat, 7 Dec 2024 22:34:02 +0530 Subject: [PATCH 25/82] feat: add bottom up get selectors logic --- maxun-core/src/interpret.ts | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index c2a1186c..844b46c7 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -142,31 +142,23 @@ export default class Interpreter extends EventEmitter { // return selectors; // } - private getSelectors(workflow: Workflow, actionId: number): string[] { - const selectors: string[] = []; + private getSelectors(workflow: Workflow): string[] { + const selectorsSet = new Set(); - // Validate actionId - if (actionId <= 0) { - console.log("No previous selectors to collect."); - return selectors; // Empty array as there are no previous steps + if (workflow.length === 0) { + return []; } - // Iterate from the start up to (but not including) actionId - for (let index = 0; index < actionId; index++) { + for (let index = workflow.length - 1; index >= 0; index--) { const currentSelectors = workflow[index]?.where?.selectors; - console.log(`Selectors at step ${index}:`, currentSelectors); if (currentSelectors && currentSelectors.length > 0) { - currentSelectors.forEach((selector) => { - if (!selectors.includes(selector)) { - selectors.push(selector); // Avoid duplicates - } - }); + currentSelectors.forEach((selector) => selectorsSet.add(selector)); + return Array.from(selectorsSet); } } - console.log("Collected Selectors:", selectors); - return selectors; + return []; } @@ -216,9 +208,8 @@ export default class Interpreter extends EventEmitter { // return []; // }), // ).then((x) => x.flat()); - const action = workflowCopy[workflowCopy.length - 1]; - console.log("Next action:", action) + const action = workflowCopy[workflowCopy.length - 1]; let url: any = page.url(); @@ -709,10 +700,10 @@ export default class Interpreter extends EventEmitter { usedActions.push(action.id ?? 'undefined'); workflowCopy.splice(actionId, 1); - console.log(`Action with ID ${action.id} removed from the workflow copy.`); + console.log(`Action with ID ${actionId} removed from the workflow copy.`); // const newSelectors = this.getPreviousSelectors(workflow, actionId); - const newSelectors = this.getSelectors(workflowCopy, actionId); + const newSelectors = this.getSelectors(workflowCopy); newSelectors.forEach(selector => { if (!selectors.includes(selector)) { selectors.push(selector); From ef571c4ea09ebc15b12bdbdc7398bef7c5d69d68 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 7 Dec 2024 23:28:31 +0530 Subject: [PATCH 26/82] feat: traverse dom tree for parent element selection --- server/src/workflow-management/selector.ts | 144 +++++++++++---------- 1 file changed, 78 insertions(+), 66 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 193de891..e0cd10c5 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -20,49 +20,6 @@ type Workflow = WorkflowFile["workflow"]; * @category WorkflowManagement-Selectors * @returns {Promise} */ -export const getRect = async (page: Page, coordinates: Coordinates) => { - try { - const rect = await page.evaluate( - async ({ x, y }) => { - const el = document.elementFromPoint(x, y) as HTMLElement; - if (el) { - const { parentElement } = el; - // Match the logic in recorder.ts for link clicks - const element = parentElement?.tagName === 'A' ? parentElement : el; - const rectangle = element?.getBoundingClientRect(); - // @ts-ignore - if (rectangle) { - return { - x: rectangle.x, - y: rectangle.y, - width: rectangle.width, - height: rectangle.height, - top: rectangle.top, - right: rectangle.right, - bottom: rectangle.bottom, - left: rectangle.left, - }; - } - } - }, - { x: coordinates.x, y: coordinates.y }, - ); - return rect; - } catch (error) { - const { message, stack } = error as Error; - logger.log('error', `Error while retrieving selector: ${message}`); - logger.log('error', `Stack: ${stack}`); - } -} - -/** - * Checks the basic info about an element and returns a {@link BaseActionInfo} object. - * If the element is not found, returns undefined. - * @param page The page instance. - * @param coordinates Coordinates of an element. - * @category WorkflowManagement-Selectors - * @returns {Promise} - */ export const getElementInformation = async ( page: Page, coordinates: Coordinates @@ -70,10 +27,15 @@ export const getElementInformation = async ( try { const elementInfo = await page.evaluate( async ({ x, y }) => { - const el = document.elementFromPoint(x, y) as HTMLElement; - if (el) { - const { parentElement } = el; - const element = parentElement?.tagName === 'A' ? parentElement : el; + // Find the initial element at the point + const initialElement = document.elementFromPoint(x, y) as HTMLElement; + + if (initialElement) { + // Simply use the direct parent, no complex logic + const parentElement = initialElement.parentElement; + + // Use the parent if it exists, otherwise use the initial element + const element = parentElement || initialElement; let info: { tagName: string; @@ -84,32 +46,41 @@ export const getElementInformation = async ( attributes?: Record; innerHTML?: string; outerHTML?: string; + parentTagName?: string; + parentClasses?: string[]; } = { - tagName: element?.tagName ?? '', + tagName: element.tagName, + parentTagName: element.parentElement?.tagName, + parentClasses: element.parentElement + ? Array.from(element.parentElement.classList) + : [] }; - if (element) { - info.attributes = Array.from(element.attributes).reduce( - (acc, attr) => { - acc[attr.name] = attr.value; - return acc; - }, - {} as Record - ); - } + // Collect attributes + info.attributes = Array.from(element.attributes).reduce( + (acc, attr) => { + acc[attr.name] = attr.value; + return acc; + }, + {} as Record + ); - // Gather specific information based on the tag - if (element?.tagName === 'A') { - info.url = (element as HTMLAnchorElement).href; - info.innerText = element.innerText ?? ''; - } else if (element?.tagName === 'IMG') { - info.imageUrl = (element as HTMLImageElement).src; + // Specific handling for different element types + if (element.tagName === 'A') { + const anchorElement = element as HTMLAnchorElement; + info.url = anchorElement.href; + info.innerText = anchorElement.innerText ?? ''; + } else if (element.tagName === 'IMG') { + const imgElement = element as HTMLImageElement; + info.imageUrl = imgElement.src; } else { - info.hasOnlyText = element?.children?.length === 0 && - element?.innerText?.length > 0; - info.innerText = element?.innerText ?? ''; + // Check if element contains only text + info.hasOnlyText = element.children.length === 0 && + (element.innerText?.length ?? 0) > 0; + info.innerText = element.innerText ?? ''; } + // HTML content info.innerHTML = element.innerHTML; info.outerHTML = element.outerHTML; @@ -127,6 +98,47 @@ export const getElementInformation = async ( } }; +export const getRect = async (page: Page, coordinates: Coordinates) => { + try { + const rect = await page.evaluate( + async ({ x, y }) => { + // Find the initial element at the point + const initialElement = document.elementFromPoint(x, y) as HTMLElement; + + if (initialElement) { + // Simply use the direct parent, no complex logic + const parentElement = initialElement.parentElement; + + // Use the parent if it exists, otherwise use the initial element + const element = parentElement || initialElement; + + // Get bounding rectangle + const rectangle = element?.getBoundingClientRect(); + + if (rectangle) { + return { + x: rectangle.x, + y: rectangle.y, + width: rectangle.width, + height: rectangle.height, + top: rectangle.top, + right: rectangle.right, + bottom: rectangle.bottom, + left: rectangle.left, + }; + } + } + return null; + }, + { x: coordinates.x, y: coordinates.y }, + ); + return rect; + } catch (error) { + const { message, stack } = error as Error; + console.error('Error while retrieving selector:', message); + console.error('Stack:', stack); + } +}; /** * Returns the best and unique css {@link Selectors} for the element on the page. From 9f24e0018c29efa30a7a49a6cea45ea7bc7a004a Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 7 Dec 2024 23:36:01 +0530 Subject: [PATCH 27/82] feat: !return null --- server/src/workflow-management/selector.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index e0cd10c5..aaa53a5d 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -128,7 +128,6 @@ export const getRect = async (page: Page, coordinates: Coordinates) => { }; } } - return null; }, { x: coordinates.x, y: coordinates.y }, ); From 8c4c0b734d863bf1bc78f00e72fd709a89c626bc Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 7 Dec 2024 23:45:07 +0530 Subject: [PATCH 28/82] feat: handle selector generation if no parent element --- server/src/workflow-management/selector.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index aaa53a5d..7908edc2 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -753,7 +753,6 @@ interface SelectorResult { export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates): Promise => { try { const selectors = await page.evaluate(({ x, y }: { x: number, y: number }) => { - function getNonUniqueSelector(element: HTMLElement): string { let selector = element.tagName.toLowerCase(); @@ -775,18 +774,25 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates let depth = 0; const maxDepth = 2; - while (element && element !== document.body && depth < maxDepth) { - const selector = getNonUniqueSelector(element); + // Ensure we start with a valid element + let currentElement = element; + while (currentElement && currentElement !== document.body && depth < maxDepth) { + const selector = getNonUniqueSelector(currentElement); path.unshift(selector); - element = element.parentElement; + currentElement = currentElement.parentElement; depth++; } return path.join(' > '); } - const element = document.elementFromPoint(x, y) as HTMLElement | null; - if (!element) return null; + // Find the initial element at the point + const initialElement = document.elementFromPoint(x, y) as HTMLElement; + + if (!initialElement) return null; + + // Prefer parent if exists, otherwise use initial element + const element = initialElement.parentElement || initialElement; const generalSelector = getSelectorPath(element); return { From be6d8ab249d6486e7af9266dd43b3bab53d21012 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sun, 8 Dec 2024 18:04:49 +0530 Subject: [PATCH 29/82] feat: add selectors in bottom up order --- maxun-core/src/interpret.ts | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index 844b46c7..de9a9845 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -121,24 +121,30 @@ export default class Interpreter extends EventEmitter { } } - // private getPreviousSelectors(workflow: Workflow, actionId: number): string[] { + // private getSelectors(workflow: Workflow, actionId: number): string[] { // const selectors: string[] = []; - // let index = actionId - 1; - // while (index >= 0) { - // const previousSelectors = workflow[index]?.where?.selectors; - // console.log("Previous Selectors:", previousSelectors); - // if (previousSelectors && previousSelectors.length > 0) { - // previousSelectors.forEach((selector) => { + // // Validate actionId + // if (actionId <= 0) { + // console.log("No previous selectors to collect."); + // return selectors; // Empty array as there are no previous steps + // } + + // // Iterate from the start up to (but not including) actionId + // for (let index = 0; index < actionId; index++) { + // const currentSelectors = workflow[index]?.where?.selectors; + // console.log(`Selectors at step ${index}:`, currentSelectors); + + // if (currentSelectors && currentSelectors.length > 0) { + // currentSelectors.forEach((selector) => { // if (!selectors.includes(selector)) { // selectors.push(selector); // Avoid duplicates // } // }); - // break; // Exit the loop once valid selectors are found // } - // index--; // Move further back in the workflow // } + // console.log("Collected Selectors:", selectors); // return selectors; // } @@ -208,12 +214,14 @@ export default class Interpreter extends EventEmitter { // return []; // }), // ).then((x) => x.flat()); - + const action = workflowCopy[workflowCopy.length - 1]; + // console.log("Next action:", action) + let url: any = page.url(); - if (action && action.where.url !== url) { + if (action && action.where.url !== url && action.where.url !== "about:blank") { url = action.where.url; } @@ -700,7 +708,7 @@ export default class Interpreter extends EventEmitter { usedActions.push(action.id ?? 'undefined'); workflowCopy.splice(actionId, 1); - console.log(`Action with ID ${actionId} removed from the workflow copy.`); + console.log(`Action with ID ${action.id} removed from the workflow copy.`); // const newSelectors = this.getPreviousSelectors(workflow, actionId); const newSelectors = this.getSelectors(workflowCopy); From 5259e3e386787267662755dd02ea9fc7c71d56b4 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sun, 8 Dec 2024 18:06:57 +0530 Subject: [PATCH 30/82] feat: add on flag logic for InterpretRecording --- .../classes/Interpreter.ts | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/server/src/workflow-management/classes/Interpreter.ts b/server/src/workflow-management/classes/Interpreter.ts index d53259b7..b982b172 100644 --- a/server/src/workflow-management/classes/Interpreter.ts +++ b/server/src/workflow-management/classes/Interpreter.ts @@ -244,7 +244,12 @@ export class WorkflowInterpreter { * @param page The page instance used to interact with the browser. * @param settings The settings to use for the interpretation. */ - public InterpretRecording = async (workflow: WorkflowFile, page: Page, settings: InterpreterSettings) => { + public InterpretRecording = async ( + workflow: WorkflowFile, + page: Page, + updatePageOnPause: (page: Page) => void, + settings: InterpreterSettings + ) => { const params = settings.params ? settings.params : null; delete settings.params; @@ -262,7 +267,7 @@ export class WorkflowInterpreter { this.socket.emit('debugMessage', msg) }, }, - serializableCallback: (data: string) => { + serializableCallback: (data: any) => { this.serializableData.push(data); this.socket.emit('serializableCallback', data); }, @@ -275,6 +280,23 @@ export class WorkflowInterpreter { const interpreter = new Interpreter(decryptedWorkflow, options); this.interpreter = interpreter; + interpreter.on('flag', async (page, resume) => { + if (this.activeId !== null && this.breakpoints[this.activeId]) { + logger.log('debug', `breakpoint hit id: ${this.activeId}`); + this.socket.emit('breakpointHit'); + this.interpretationIsPaused = true; + } + + if (this.interpretationIsPaused) { + this.interpretationResume = resume; + logger.log('debug', `Paused inside of flag: ${page.url()}`); + updatePageOnPause(page); + this.socket.emit('log', '----- The interpretation has been paused -----', false); + } else { + resume(); + } + }); + const status = await interpreter.run(page, params); const lastArray = this.serializableData.length > 1 From aec65d1b2232fef9a2c70ac3b43e0001dbbafe42 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sun, 8 Dec 2024 18:07:40 +0530 Subject: [PATCH 31/82] feat: add flag generation logic --- server/src/api/record.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/server/src/api/record.ts b/server/src/api/record.ts index 5b33b12f..05560487 100644 --- a/server/src/api/record.ts +++ b/server/src/api/record.ts @@ -15,6 +15,8 @@ import { io, Socket } from "socket.io-client"; import { BinaryOutputService } from "../storage/mino"; import { AuthenticatedRequest } from "../routes/record" import {capture} from "../utils/analytics"; +import { Page } from "playwright"; +import { WorkflowFile } from "maxun-core"; chromium.use(stealthPlugin()); const formatRecording = (recordingData: any) => { @@ -533,6 +535,17 @@ function resetRecordingState(browserId: string, id: string) { id = ''; } +function AddGeneratedFlags(workflow: WorkflowFile) { + const copy = JSON.parse(JSON.stringify(workflow)); + for (let i = 0; i < workflow.workflow.length; i++) { + copy.workflow[i].what.unshift({ + action: 'flag', + args: ['generated'], + }); + } + return copy; +}; + async function executeRun(id: string) { try { const run = await Run.findOne({ where: { runId: id } }); @@ -560,13 +573,14 @@ async function executeRun(id: string) { throw new Error('Could not access browser'); } - const currentPage = await browser.getCurrentPage(); + let currentPage = await browser.getCurrentPage(); if (!currentPage) { throw new Error('Could not create a new page'); } + const workflow = AddGeneratedFlags(recording.recording); const interpretationInfo = await browser.interpreter.InterpretRecording( - recording.recording, currentPage, plainRun.interpreterSettings + workflow, currentPage, (newPage: Page) => currentPage = newPage, plainRun.interpreterSettings ); const binaryOutputService = new BinaryOutputService('maxun-run-screenshots'); From 0a81292bea115446c9323bc6df5647b1b19f684d Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sun, 8 Dec 2024 18:08:05 +0530 Subject: [PATCH 32/82] feat: add flag generation logic --- server/src/routes/storage.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/server/src/routes/storage.ts b/server/src/routes/storage.ts index d1f648f8..ddadf240 100644 --- a/server/src/routes/storage.ts +++ b/server/src/routes/storage.ts @@ -18,6 +18,8 @@ import { AuthenticatedRequest } from './record'; import { computeNextRun } from '../utils/schedule'; import { capture } from "../utils/analytics"; import { tryCatch } from 'bullmq'; +import { WorkflowFile } from 'maxun-core'; +import { Page } from 'playwright'; chromium.use(stealthPlugin()); export const router = Router(); @@ -422,6 +424,17 @@ router.get('/runs/run/:id', requireSignIn, async (req, res) => { } }); +function AddGeneratedFlags(workflow: WorkflowFile) { + const copy = JSON.parse(JSON.stringify(workflow)); + for (let i = 0; i < workflow.workflow.length; i++) { + copy.workflow[i].what.unshift({ + action: 'flag', + args: ['generated'], + }); + } + return copy; +}; + /** * PUT endpoint for finishing a run and saving it to the storage. */ @@ -443,10 +456,11 @@ router.post('/runs/run/:id', requireSignIn, async (req: AuthenticatedRequest, re // interpret the run in active browser const browser = browserPool.getRemoteBrowser(plainRun.browserId); - const currentPage = browser?.getCurrentPage(); + let currentPage = browser?.getCurrentPage(); if (browser && currentPage) { + const workflow = AddGeneratedFlags(recording.recording); const interpretationInfo = await browser.interpreter.InterpretRecording( - recording.recording, currentPage, plainRun.interpreterSettings); + workflow, currentPage, (newPage: Page) => currentPage = newPage, plainRun.interpreterSettings); const binaryOutputService = new BinaryOutputService('maxun-run-screenshots'); const uploadedBinaryOutput = await binaryOutputService.uploadAndStoreBinaryOutput(run, interpretationInfo.binaryOutput); await destroyRemoteBrowser(plainRun.browserId); From 45f0c819ea05831ccc0fe20781adfebc39b48f47 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sun, 8 Dec 2024 18:10:53 +0530 Subject: [PATCH 33/82] feat: add flag generation logic --- .../workflow-management/scheduler/index.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/server/src/workflow-management/scheduler/index.ts b/server/src/workflow-management/scheduler/index.ts index 02ca905f..169b0061 100644 --- a/server/src/workflow-management/scheduler/index.ts +++ b/server/src/workflow-management/scheduler/index.ts @@ -11,6 +11,8 @@ import Run from "../../models/Run"; import { getDecryptedProxyConfig } from "../../routes/proxy"; import { BinaryOutputService } from "../../storage/mino"; import { capture } from "../../utils/analytics"; +import { WorkflowFile } from "maxun-core"; +import { Page } from "playwright"; chromium.use(stealthPlugin()); async function createWorkflowAndStoreMetadata(id: string, userId: string) { @@ -79,6 +81,17 @@ async function createWorkflowAndStoreMetadata(id: string, userId: string) { } } +function AddGeneratedFlags(workflow: WorkflowFile) { + const copy = JSON.parse(JSON.stringify(workflow)); + for (let i = 0; i < workflow.workflow.length; i++) { + copy.workflow[i].what.unshift({ + action: 'flag', + args: ['generated'], + }); + } + return copy; +}; + async function executeRun(id: string) { try { const run = await Run.findOne({ where: { runId: id } }); @@ -106,13 +119,15 @@ async function executeRun(id: string) { throw new Error('Could not access browser'); } - const currentPage = await browser.getCurrentPage(); + let currentPage = await browser.getCurrentPage(); if (!currentPage) { throw new Error('Could not create a new page'); } + const workflow = AddGeneratedFlags(recording.recording); const interpretationInfo = await browser.interpreter.InterpretRecording( - recording.recording, currentPage, plainRun.interpreterSettings); + workflow, currentPage, (newPage: Page) => currentPage = newPage, plainRun.interpreterSettings + ); const binaryOutputService = new BinaryOutputService('maxun-run-screenshots'); const uploadedBinaryOutput = await binaryOutputService.uploadAndStoreBinaryOutput(run, interpretationInfo.binaryOutput); From a30211de6d3ed309a5477a44091b19a4f72a9dd0 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sun, 8 Dec 2024 18:55:19 +0530 Subject: [PATCH 34/82] fix: add frame navigation logic in place of load --- server/src/browser-management/classes/RemoteBrowser.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/src/browser-management/classes/RemoteBrowser.ts b/server/src/browser-management/classes/RemoteBrowser.ts index f1d18f3f..3f5b677c 100644 --- a/server/src/browser-management/classes/RemoteBrowser.ts +++ b/server/src/browser-management/classes/RemoteBrowser.ts @@ -407,9 +407,9 @@ export class RemoteBrowser { this.socket.emit('urlChanged', this.currentPage.url()); } }); - this.currentPage.on('load', (page) => { - this.socket.emit('urlChanged', page.url()); - }) + // this.currentPage.on('load', (page) => { + // this.socket.emit('urlChanged', page.url()); + // }) this.client = await this.currentPage.context().newCDPSession(this.currentPage); await this.subscribeToScreencast(); } else { From 2ea64385c389178972e7d2a00930f55b7dfdab1e Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 20:26:34 +0530 Subject: [PATCH 35/82] feat: youtube icon --- src/components/molecules/NavBar.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 6b2bfa92..6ecf9b61 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -4,7 +4,7 @@ import styled from "styled-components"; import { stopRecording } from "../../api/recording"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { IconButton, Menu, MenuItem, Typography, Avatar, Chip, } from "@mui/material"; -import { AccountCircle, Logout, Clear } from "@mui/icons-material"; +import { AccountCircle, Logout, Clear, YouTube } from "@mui/icons-material"; import { useNavigate } from 'react-router-dom'; import { AuthContext } from '../../context/auth'; import { SaveRecording } from '../molecules/SaveRecording'; @@ -114,6 +114,9 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => { handleMenuClose(); logout(); }}> Logout + { handleMenuClose(); logout(); }}> + YouTube + ) : ( From e2cb4c7613d570b0fd4cd7f8a826fe3a6e019c85 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 20:29:53 +0530 Subject: [PATCH 36/82] feat: youtube link --- src/components/molecules/NavBar.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 6ecf9b61..9e0f6642 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -114,7 +114,9 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => { handleMenuClose(); logout(); }}> Logout - { handleMenuClose(); logout(); }}> + { + window.open('https://www.youtube.com/@MaxunOSS/videos', '_blank'); + }}> YouTube From 99d62101cb512ff675b465959391c8dfe68c80ff Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 20:30:11 +0530 Subject: [PATCH 37/82] chore: lint --- src/components/molecules/NavBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 9e0f6642..d6855184 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -114,8 +114,8 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => { handleMenuClose(); logout(); }}> Logout - { - window.open('https://www.youtube.com/@MaxunOSS/videos', '_blank'); + { + window.open('https://www.youtube.com/@MaxunOSS/videos', '_blank'); }}> YouTube From 69757050a9ac59a6ceca27a5c74a880551aba120 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 20:32:41 +0530 Subject: [PATCH 38/82] feat: x link --- src/components/molecules/NavBar.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index d6855184..28ca959a 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -4,7 +4,7 @@ import styled from "styled-components"; import { stopRecording } from "../../api/recording"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { IconButton, Menu, MenuItem, Typography, Avatar, Chip, } from "@mui/material"; -import { AccountCircle, Logout, Clear, YouTube } from "@mui/icons-material"; +import { AccountCircle, Logout, Clear, YouTube, X } from "@mui/icons-material"; import { useNavigate } from 'react-router-dom'; import { AuthContext } from '../../context/auth'; import { SaveRecording } from '../molecules/SaveRecording'; @@ -119,6 +119,11 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => }}> YouTube + { + window.open('https://x.com/maxun_io', '_blank'); + }}> + Twiiter (X) + ) : ( From 7186540ed94dbace62f45bbfd94725ffc4c1c2c2 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 20:37:48 +0530 Subject: [PATCH 39/82] feat: increase width of menu --- src/components/molecules/NavBar.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 28ca959a..68b452db 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -110,6 +110,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => vertical: 'top', horizontal: 'right', }} + PaperProps={{sx: {width: '180px'}}} > { handleMenuClose(); logout(); }}> Logout From a8ea05527b28a109709d71249a69977466921ead Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 20:42:24 +0530 Subject: [PATCH 40/82] feat: move discord icon to menu --- src/components/molecules/NavBar.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 68b452db..e8d154ef 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -115,6 +115,11 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => { handleMenuClose(); logout(); }}> Logout + { + window.open('https://discord.gg/5GbPjBUkws', '_blank'); + }}> + Discord + { window.open('https://www.youtube.com/@MaxunOSS/videos', '_blank'); }}> From 8d6b962301ae1108b409afc251bb6b61340aba72 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 20:42:54 +0530 Subject: [PATCH 41/82] feat: remove discord icon button --- src/components/molecules/NavBar.tsx | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index e8d154ef..2d1096a5 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -71,21 +71,6 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
{!isRecording ? ( <> - - - Date: Sun, 8 Dec 2024 20:55:24 +0530 Subject: [PATCH 42/82] feat: add ref to yt x links --- src/components/molecules/NavBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 2d1096a5..ef1f9878 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -106,12 +106,12 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => Discord { - window.open('https://www.youtube.com/@MaxunOSS/videos', '_blank'); + window.open('https://www.youtube.com/@MaxunOSS/videos?ref=app', '_blank'); }}> YouTube { - window.open('https://x.com/maxun_io', '_blank'); + window.open('https://x.com/maxun_io?ref=app', '_blank'); }}> Twiiter (X) From f8f1d926d99cd495e8196823e28cf5d62642ad5c Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 21:08:20 +0530 Subject: [PATCH 43/82] wip: upgrade maxun button --- src/components/molecules/NavBar.tsx | 97 ++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index ef1f9878..9b1e9472 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -3,7 +3,7 @@ import axios from 'axios'; import styled from "styled-components"; import { stopRecording } from "../../api/recording"; import { useGlobalInfoStore } from "../../context/globalInfo"; -import { IconButton, Menu, MenuItem, Typography, Avatar, Chip, } from "@mui/material"; +import { IconButton, Menu, MenuItem, Typography, Avatar, Chip, Button, Modal, Tabs, Tab, Box } from "@mui/material"; import { AccountCircle, Logout, Clear, YouTube, X } from "@mui/icons-material"; import { useNavigate } from 'react-router-dom'; import { AuthContext } from '../../context/auth'; @@ -25,6 +25,38 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => const navigate = useNavigate(); const [anchorEl, setAnchorEl] = useState(null); + const currentVersion = "0.0.3"; // Dynamically fetch from package.json + + const [open, setOpen] = useState(false); + const [latestVersion, setLatestVersion] = useState(null); + const [tab, setTab] = useState(0); + + const fetchLatestVersion = async () => { + try { + const response = await fetch("https://api.github.com/repos/getmaxun/maxun/releases/latest"); + const data = await response.json(); + const version = data.tag_name.replace(/^v/, ""); // Remove 'v' prefix + setLatestVersion(version); + } catch (error) { + console.error("Failed to fetch latest version:", error); + setLatestVersion(null); // Handle errors gracefully + } + }; + + const handleOpen = () => { + setOpen(true); + fetchLatestVersion(); + }; + + const handleClose = () => { + setOpen(false); + setTab(0); // Reset tab to the first tab + }; + + const handleTabChange = (newValue: any) => { + setTab(newValue); + }; + const handleMenuOpen = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); @@ -71,6 +103,69 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
{!isRecording ? ( <> + + + + {latestVersion === null ? ( + Checking for updates... + ) : currentVersion === latestVersion ? ( + + 🎉 You're up to date! + + ) : ( + <> + + A new version is available: {latestVersion} + + + + + + {tab === 0 && ( + + Manual Upgrade + + git pull origin main +
+ npm install +
+ npm run start +
+
+ )} + {tab === 1 && ( + + Docker Compose Upgrade + + docker pull getmaxun/maxun:latest +
+ docker-compose up -d +
+
+ )} + + )} +
+
Date: Sun, 8 Dec 2024 21:09:42 +0530 Subject: [PATCH 44/82] refactor: rename menu & tab update functions --- src/components/molecules/NavBar.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 9b1e9472..bef197fa 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -43,17 +43,17 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => } }; - const handleOpen = () => { + const handleUpdateOpen = () => { setOpen(true); fetchLatestVersion(); }; - const handleClose = () => { + const handleUpdateClose = () => { setOpen(false); setTab(0); // Reset tab to the first tab }; - const handleTabChange = (newValue: any) => { + const handleUpdateTabChange = (newValue: any) => { setTab(newValue); }; @@ -103,10 +103,10 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
{!isRecording ? ( <> - - + = ({ recordingName, isRecording }) => From 37ed5fa555ca832f854071a885ee8fd5bf3c1454 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 21:09:58 +0530 Subject: [PATCH 45/82] chore: lint --- src/components/molecules/NavBar.tsx | 128 ++++++++++++++-------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index bef197fa..96e1f07e 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -103,69 +103,69 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
{!isRecording ? ( <> - - - - {latestVersion === null ? ( - Checking for updates... - ) : currentVersion === latestVersion ? ( - - 🎉 You're up to date! - - ) : ( - <> - - A new version is available: {latestVersion} - - - - - - {tab === 0 && ( - - Manual Upgrade - - git pull origin main -
- npm install -
- npm run start -
-
- )} - {tab === 1 && ( - - Docker Compose Upgrade - - docker pull getmaxun/maxun:latest -
- docker-compose up -d -
-
- )} - - )} -
-
+ + + + {latestVersion === null ? ( + Checking for updates... + ) : currentVersion === latestVersion ? ( + + 🎉 You're up to date! + + ) : ( + <> + + A new version is available: {latestVersion} + + + + + + {tab === 0 && ( + + Manual Upgrade + + git pull origin main +
+ npm install +
+ npm run start +
+
+ )} + {tab === 1 && ( + + Docker Compose Upgrade + + docker pull getmaxun/maxun:latest +
+ docker-compose up -d +
+
+ )} + + )} +
+
= ({ recordingName, isRecording }) => vertical: 'top', horizontal: 'right', }} - PaperProps={{sx: {width: '180px'}}} + PaperProps={{ sx: { width: '180px' } }} > { handleMenuClose(); logout(); }}> Logout From 929bd91a7ef794ef0bbb55e4cd858d1c978374cd Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 21:11:58 +0530 Subject: [PATCH 46/82] feat: store package.json version in currentVersion --- src/components/molecules/NavBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 96e1f07e..4ca7cc17 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -25,7 +25,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => const navigate = useNavigate(); const [anchorEl, setAnchorEl] = useState(null); - const currentVersion = "0.0.3"; // Dynamically fetch from package.json + const currentVersion = packageJson.version; const [open, setOpen] = useState(false); const [latestVersion, setLatestVersion] = useState(null); @@ -92,7 +92,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
Maxun
Date: Sun, 8 Dec 2024 21:15:21 +0530 Subject: [PATCH 47/82] feat: margin right to 30px --- src/components/molecules/NavBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 4ca7cc17..cc6d2c6b 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -103,7 +103,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
{!isRecording ? ( <> - From 6a2222e6b924697aac38c13fb6294c784870b291 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 21:21:47 +0530 Subject: [PATCH 48/82] feat: pass event prop to handle update tab change --- src/components/molecules/NavBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index cc6d2c6b..5cdae02e 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -53,7 +53,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => setTab(0); // Reset tab to the first tab }; - const handleUpdateTabChange = (newValue: any) => { + const handleUpdateTabChange = (event: React.SyntheticEvent, newValue: number) => { setTab(newValue); }; From 06fdfbc65a0430468d1b34e05704d7be8851e0d9 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 21:22:03 +0530 Subject: [PATCH 49/82] chore: lint --- src/components/molecules/NavBar.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 5cdae02e..06393277 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -57,7 +57,6 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => setTab(newValue); }; - const handleMenuOpen = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); }; @@ -103,7 +102,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
{!isRecording ? ( <> - From b30be4b976b478f115c0048b3f7c38ac0e4982e2 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 21:23:00 +0530 Subject: [PATCH 50/82] feat: add setup --- src/components/molecules/NavBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 06393277..94676ecd 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -136,8 +136,8 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => sx={{ marginTop: 2, marginBottom: 2 }} centered > - - + + {tab === 0 && ( From 58aedacd4ffd99e8c8890a7ba6b63e52a69b6807 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 22:23:09 +0530 Subject: [PATCH 51/82] wip: updates ui --- src/components/molecules/NavBar.tsx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 94676ecd..be4818df 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -112,7 +112,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => top: "50%", left: "50%", transform: "translate(-50%, -50%)", - width: 400, + width: 500, bgcolor: "background.paper", boxShadow: 24, p: 4, @@ -136,29 +136,27 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => sx={{ marginTop: 2, marginBottom: 2 }} centered > - - + + {tab === 0 && ( - Manual Upgrade - +
git pull origin main
npm install
npm run start - +
)} {tab === 1 && ( - Docker Compose Upgrade - +
docker pull getmaxun/maxun:latest
docker-compose up -d - +
)} From ebd866bc16c1b3d6f7a97ae303238da06160edc6 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 22:31:17 +0530 Subject: [PATCH 52/82] feat: add comments for commands --- src/components/molecules/NavBar.tsx | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index be4818df..bc4222e2 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -141,19 +141,30 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => {tab === 0 && ( -
- git pull origin main +
+

Run the commands below

+ # pull latest changes +
+ git pull origin master +
+ # install dependencies
npm install
+ # start maxun +
npm run start
)} {tab === 1 && ( -
- docker pull getmaxun/maxun:latest +
+

Run the commands below

+ # pull latest docker images +
+ docker-compose pull + # start maxun
docker-compose up -d
From 0c66e86e28bad574460eddf622ff74e27a7f57b0 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 23:42:19 +0530 Subject: [PATCH 53/82] feat: format code blocks --- src/components/molecules/NavBar.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index bc4222e2..99535baf 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -140,34 +140,38 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => {tab === 0 && ( - -
+ +

Run the commands below

# pull latest changes
git pull origin master
+
# install dependencies
npm install
+
# start maxun
npm run start -
+
)} {tab === 1 && ( - -
+ +

Run the commands below

# pull latest docker images
docker-compose pull +
+
# start maxun
docker-compose up -d -
+
)} From e82863ad9c8061b072ac3bbf07ce212f983d58c6 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 23:42:29 +0530 Subject: [PATCH 54/82] chore: lint --- src/components/molecules/NavBar.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 99535baf..b048d720 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -141,7 +141,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => {tab === 0 && ( - +

Run the commands below

# pull latest changes
@@ -161,8 +161,8 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => )} {tab === 1 && ( - -

Run the commands below

+ +

Run the commands below

# pull latest docker images
docker-compose pull From f7eccd47cbce57d0b66eb8ed6b57bc2a60583d69 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 23:44:33 +0530 Subject: [PATCH 55/82] feat: box border radius --- src/components/molecules/NavBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index b048d720..60b75835 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -140,7 +140,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => {tab === 0 && ( - +

Run the commands below

# pull latest changes @@ -160,7 +160,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
)} {tab === 1 && ( - +

Run the commands below

# pull latest docker images From 885120cbb388b93e490a7479bed19c0394bb2dae Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 23:53:02 +0530 Subject: [PATCH 56/82] feat: add changelog link --- src/components/molecules/NavBar.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 60b75835..7861340f 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -127,8 +127,11 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => ) : ( <> - - A new version is available: {latestVersion} + + A new version is available: {latestVersion}. Upgrade to the latest version for bug fixes, enhancements and new features! +
+ View all the new updates + {' '}here.
Date: Sun, 8 Dec 2024 23:53:13 +0530 Subject: [PATCH 57/82] chore: lint --- src/components/molecules/NavBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 7861340f..01e002ec 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -127,11 +127,11 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
) : ( <> - + A new version is available: {latestVersion}. Upgrade to the latest version for bug fixes, enhancements and new features!
View all the new updates - {' '}here. + {' '}here.
Date: Mon, 9 Dec 2024 00:00:51 +0530 Subject: [PATCH 58/82] chore: -rm unused import --- src/components/molecules/NavBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 01e002ec..9d5cc623 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -3,7 +3,7 @@ import axios from 'axios'; import styled from "styled-components"; import { stopRecording } from "../../api/recording"; import { useGlobalInfoStore } from "../../context/globalInfo"; -import { IconButton, Menu, MenuItem, Typography, Avatar, Chip, Button, Modal, Tabs, Tab, Box } from "@mui/material"; +import { IconButton, Menu, MenuItem, Typography, Chip, Button, Modal, Tabs, Tab, Box } from "@mui/material"; import { AccountCircle, Logout, Clear, YouTube, X } from "@mui/icons-material"; import { useNavigate } from 'react-router-dom'; import { AuthContext } from '../../context/auth'; From c8b95bd27c30af9133ea9e7940ceb399b15741c7 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 9 Dec 2024 00:01:26 +0530 Subject: [PATCH 59/82] chore: -rm v --- src/components/molecules/NavBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 9d5cc623..08ab650e 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -91,7 +91,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
Maxun
Date: Mon, 9 Dec 2024 00:25:13 +0530 Subject: [PATCH 60/82] feat: rename to upgrade maxun --- src/components/molecules/NavBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 08ab650e..db7f72c1 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -102,8 +102,8 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
{!isRecording ? ( <> - Date: Mon, 9 Dec 2024 00:32:14 +0530 Subject: [PATCH 61/82] feat: use update icon --- src/components/molecules/NavBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index db7f72c1..fc638503 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -4,7 +4,7 @@ import styled from "styled-components"; import { stopRecording } from "../../api/recording"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { IconButton, Menu, MenuItem, Typography, Chip, Button, Modal, Tabs, Tab, Box } from "@mui/material"; -import { AccountCircle, Logout, Clear, YouTube, X } from "@mui/icons-material"; +import { AccountCircle, Logout, Clear, YouTube, X, Update } from "@mui/icons-material"; import { useNavigate } from 'react-router-dom'; import { AuthContext } from '../../context/auth'; import { SaveRecording } from '../molecules/SaveRecording'; @@ -103,7 +103,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => {!isRecording ? ( <> Date: Mon, 9 Dec 2024 00:39:03 +0530 Subject: [PATCH 62/82] feat: match upgrade icon style to rest of navbar elementa --- src/components/molecules/NavBar.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index fc638503..db29e516 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -102,7 +102,12 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
{!isRecording ? ( <> - From e5c045f0d567439d589f83f12162e60d2cf3b01b Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 9 Dec 2024 00:39:21 +0530 Subject: [PATCH 63/82] chore: lint --- src/components/molecules/NavBar.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index db29e516..56e43c5e 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -102,13 +102,13 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
{!isRecording ? ( <> - Date: Mon, 9 Dec 2024 00:40:17 +0530 Subject: [PATCH 64/82] feat: increase margin right --- src/components/molecules/NavBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 56e43c5e..62275078 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -103,7 +103,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => {!isRecording ? ( <> + } + /> +)}
= ({ recordingName, isRecording }) => ) : "" } + ); }; From e19094185214192cc5697c97dbb5a527f92ba656 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 9 Dec 2024 00:54:55 +0530 Subject: [PATCH 66/82] chore: lint --- src/components/molecules/NavBar.tsx | 352 ++++++++++++++-------------- 1 file changed, 176 insertions(+), 176 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 1755cb20..24fda0f0 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -96,186 +96,186 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => return ( <> - {isUpdateAvailable && ( - - Upgrade - - } - /> -)} - -
- -
Maxun
- + Upgrade + + } /> -
- { - user ? ( -
- {!isRecording ? ( - <> - - - - {latestVersion === null ? ( - Checking for updates... - ) : currentVersion === latestVersion ? ( - - 🎉 You're up to date! - - ) : ( - <> - - A new version is available: {latestVersion}. Upgrade to the latest version for bug fixes, enhancements and new features! -
- View all the new updates - {' '}here. + )} + +
+ +
Maxun
+ +
+ { + user ? ( +
+ {!isRecording ? ( + <> + + + + {latestVersion === null ? ( + Checking for updates... + ) : currentVersion === latestVersion ? ( + + 🎉 You're up to date! - - - - - {tab === 0 && ( - - -

Run the commands below

- # pull latest changes -
- git pull origin master -
-
- # install dependencies -
- npm install -
-
- # start maxun -
- npm run start -
-
- )} - {tab === 1 && ( - - -

Run the commands below

- # pull latest docker images -
- docker-compose pull -
-
- # start maxun -
- docker-compose up -d -
-
- )} - - )} -
-
- - - - {user.email} - - - { handleMenuClose(); logout(); }}> - Logout - - { - window.open('https://discord.gg/5GbPjBUkws', '_blank'); + ) : ( + <> + + A new version is available: {latestVersion}. Upgrade to the latest version for bug fixes, enhancements and new features! +
+ View all the new updates + {' '}here. +
+ + + + + {tab === 0 && ( + + +

Run the commands below

+ # pull latest changes +
+ git pull origin master +
+
+ # install dependencies +
+ npm install +
+
+ # start maxun +
+ npm run start +
+
+ )} + {tab === 1 && ( + + +

Run the commands below

+ # pull latest docker images +
+ docker-compose pull +
+
+ # start maxun +
+ docker-compose up -d +
+
+ )} + + )} + + + + - Discord -
- { - window.open('https://www.youtube.com/@MaxunOSS/videos?ref=app', '_blank'); + + {user.email} + + + { handleMenuClose(); logout(); }}> + Logout + + { + window.open('https://discord.gg/5GbPjBUkws', '_blank'); + }}> + Discord + + { + window.open('https://www.youtube.com/@MaxunOSS/videos?ref=app', '_blank'); + }}> + YouTube + + { + window.open('https://x.com/maxun_io?ref=app', '_blank'); + }}> + Twiiter (X) + + + + ) : ( + <> + - YouTube - - { - window.open('https://x.com/maxun_io?ref=app', '_blank'); - }}> - Twiiter (X) - -
- - ) : ( - <> - - - Discard - - - - )} -
- ) : "" - } -
+ + Discard + + + + )} +
+ ) : "" + } +
); }; From 612622725d8c8542e6b4666a692d0773f3255cea Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 9 Dec 2024 01:05:39 +0530 Subject: [PATCH 67/82] feat: snackbar ui --- src/components/molecules/NavBar.tsx | 47 ++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 24fda0f0..3f3907a9 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -4,7 +4,7 @@ import styled from "styled-components"; import { stopRecording } from "../../api/recording"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { IconButton, Menu, MenuItem, Typography, Chip, Button, Modal, Tabs, Tab, Box, Snackbar } from "@mui/material"; -import { AccountCircle, Logout, Clear, YouTube, X, Update } from "@mui/icons-material"; +import { AccountCircle, Logout, Clear, YouTube, X, Update, Close } from "@mui/icons-material"; import { useNavigate } from 'react-router-dom'; import { AuthContext } from '../../context/auth'; import { SaveRecording } from '../molecules/SaveRecording'; @@ -98,14 +98,47 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => <> {isUpdateAvailable && ( + open={isUpdateAvailable} + onClose={() => setIsUpdateAvailable(false)} // Close when clicking the close button + message={ + + New version {latestVersion} available! Click "Upgrade Maxun" to update. + + } + action={ + <> + - } - /> + setIsUpdateAvailable(false)} // Close Snackbar + style={{ color: 'black' }} + > + + + + } + anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} // Position of Snackbar + sx={{ + backgroundColor: 'white', + boxShadow: '0px 4px 10px rgba(0, 0, 0, 0.2)', + }} + /> + )}
Date: Mon, 9 Dec 2024 01:16:07 +0530 Subject: [PATCH 68/82] feat: snackbar ui --- src/components/molecules/NavBar.tsx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 3f3907a9..0b933b7e 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -99,24 +99,23 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => {isUpdateAvailable && ( setIsUpdateAvailable(false)} // Close when clicking the close button + onClose={() => setIsUpdateAvailable(false)} message={ - - New version {latestVersion} available! Click "Upgrade Maxun" to update. - + `New version ${latestVersion} available! Click "Upgrade" to update.` } action={ <> - setIsUpdateAvailable(false)} - style={{ color: 'black' }} - > - - - - } - ContentProps={{ - sx: { - background: "white", - color: "black", } - }} - /> - + action={ + <> + + setIsUpdateAvailable(false)} + style={{ color: 'black' }} + > + + + + } + ContentProps={{ + sx: { + background: "white", + color: "black", + } + }} + /> + )}
Date: Mon, 9 Dec 2024 05:43:51 +0530 Subject: [PATCH 70/82] feat: proper rect and element info --- server/src/workflow-management/selector.ts | 166 +++++++++++++-------- 1 file changed, 103 insertions(+), 63 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 7908edc2..9587e898 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -27,15 +27,33 @@ export const getElementInformation = async ( try { const elementInfo = await page.evaluate( async ({ x, y }) => { - // Find the initial element at the point - const initialElement = document.elementFromPoint(x, y) as HTMLElement; - - if (initialElement) { - // Simply use the direct parent, no complex logic - const parentElement = initialElement.parentElement; + const originalEl = document.elementFromPoint(x, y) as HTMLElement; + if (originalEl) { + let element = originalEl; + + // Generic parent finding logic based on visual containment + while (element.parentElement) { + const parentRect = element.parentElement.getBoundingClientRect(); + const childRect = element.getBoundingClientRect(); - // Use the parent if it exists, otherwise use the initial element - const element = parentElement || initialElement; + // Check if parent visually contains the child + const fullyContained = + parentRect.left <= childRect.left && + parentRect.right >= childRect.right && + parentRect.top <= childRect.top && + parentRect.bottom >= childRect.bottom; + + // Additional checks for more comprehensive containment + const significantOverlap = + (childRect.width * childRect.height) / + (parentRect.width * parentRect.height) > 0.5; + + if (fullyContained && significantOverlap) { + element = element.parentElement; + } else { + break; + } + } let info: { tagName: string; @@ -46,44 +64,34 @@ export const getElementInformation = async ( attributes?: Record; innerHTML?: string; outerHTML?: string; - parentTagName?: string; - parentClasses?: string[]; } = { - tagName: element.tagName, - parentTagName: element.parentElement?.tagName, - parentClasses: element.parentElement - ? Array.from(element.parentElement.classList) - : [] + tagName: element?.tagName ?? '', }; - // Collect attributes - info.attributes = Array.from(element.attributes).reduce( - (acc, attr) => { - acc[attr.name] = attr.value; - return acc; - }, - {} as Record - ); - - // Specific handling for different element types - if (element.tagName === 'A') { - const anchorElement = element as HTMLAnchorElement; - info.url = anchorElement.href; - info.innerText = anchorElement.innerText ?? ''; - } else if (element.tagName === 'IMG') { - const imgElement = element as HTMLImageElement; - info.imageUrl = imgElement.src; - } else { - // Check if element contains only text - info.hasOnlyText = element.children.length === 0 && - (element.innerText?.length ?? 0) > 0; - info.innerText = element.innerText ?? ''; + if (element) { + info.attributes = Array.from(element.attributes).reduce( + (acc, attr) => { + acc[attr.name] = attr.value; + return acc; + }, + {} as Record + ); + } + + // Existing tag-specific logic + if (element?.tagName === 'A') { + info.url = (element as HTMLAnchorElement).href; + info.innerText = element.innerText ?? ''; + } else if (element?.tagName === 'IMG') { + info.imageUrl = (element as HTMLImageElement).src; + } else { + info.hasOnlyText = element?.children?.length === 0 && + element?.innerText?.length > 0; + info.innerText = element?.innerText ?? ''; } - // HTML content info.innerHTML = element.innerHTML; info.outerHTML = element.outerHTML; - return info; } return null; @@ -102,17 +110,32 @@ export const getRect = async (page: Page, coordinates: Coordinates) => { try { const rect = await page.evaluate( async ({ x, y }) => { - // Find the initial element at the point - const initialElement = document.elementFromPoint(x, y) as HTMLElement; - - if (initialElement) { - // Simply use the direct parent, no complex logic - const parentElement = initialElement.parentElement; + const originalEl = document.elementFromPoint(x, y) as HTMLElement; + if (originalEl) { + let element = originalEl; + + // Same parent-finding logic as in getElementInformation + while (element.parentElement) { + const parentRect = element.parentElement.getBoundingClientRect(); + const childRect = element.getBoundingClientRect(); - // Use the parent if it exists, otherwise use the initial element - const element = parentElement || initialElement; + const fullyContained = + parentRect.left <= childRect.left && + parentRect.right >= childRect.right && + parentRect.top <= childRect.top && + parentRect.bottom >= childRect.bottom; + + const significantOverlap = + (childRect.width * childRect.height) / + (parentRect.width * parentRect.height) > 0.5; + + if (fullyContained && significantOverlap) { + element = element.parentElement; + } else { + break; + } + } - // Get bounding rectangle const rectangle = element?.getBoundingClientRect(); if (rectangle) { @@ -134,10 +157,11 @@ export const getRect = async (page: Page, coordinates: Coordinates) => { return rect; } catch (error) { const { message, stack } = error as Error; - console.error('Error while retrieving selector:', message); - console.error('Stack:', stack); + logger.log('error', `Error while retrieving selector: ${message}`); + logger.log('error', `Stack: ${stack}`); } -}; +} + /** * Returns the best and unique css {@link Selectors} for the element on the page. @@ -774,25 +798,42 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates let depth = 0; const maxDepth = 2; - // Ensure we start with a valid element - let currentElement = element; - while (currentElement && currentElement !== document.body && depth < maxDepth) { - const selector = getNonUniqueSelector(currentElement); + while (element && element !== document.body && depth < maxDepth) { + const selector = getNonUniqueSelector(element); path.unshift(selector); - currentElement = currentElement.parentElement; + element = element.parentElement; depth++; } return path.join(' > '); } - // Find the initial element at the point - const initialElement = document.elementFromPoint(x, y) as HTMLElement; - - if (!initialElement) return null; + const originalEl = document.elementFromPoint(x, y) as HTMLElement; + if (!originalEl) return null; - // Prefer parent if exists, otherwise use initial element - const element = initialElement.parentElement || initialElement; + let element = originalEl; + + // Find the most appropriate parent element + while (element.parentElement) { + const parentRect = element.parentElement.getBoundingClientRect(); + const childRect = element.getBoundingClientRect(); + + const fullyContained = + parentRect.left <= childRect.left && + parentRect.right >= childRect.right && + parentRect.top <= childRect.top && + parentRect.bottom >= childRect.bottom; + + const significantOverlap = + (childRect.width * childRect.height) / + (parentRect.width * parentRect.height) > 0.5; + + if (fullyContained && significantOverlap) { + element = element.parentElement; + } else { + break; + } + } const generalSelector = getSelectorPath(element); return { @@ -807,7 +848,6 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates } }; - export const getChildSelectors = async (page: Page, parentSelector: string): Promise => { try { const childSelectors = await page.evaluate((parentSelector: string) => { From 560f0ea24f8f214e48867791b64c4b3630cb94b9 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 9 Dec 2024 14:41:59 +0530 Subject: [PATCH 71/82] fix: a tags --- server/src/workflow-management/selector.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 9587e898..6a80e7a3 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -31,6 +31,11 @@ export const getElementInformation = async ( if (originalEl) { let element = originalEl; + if (originalEl.tagName === 'A') { + element = originalEl; + } else if (originalEl.parentElement?.tagName === 'A') { + element = originalEl.parentElement; + } else { // Generic parent finding logic based on visual containment while (element.parentElement) { const parentRect = element.parentElement.getBoundingClientRect(); @@ -53,7 +58,7 @@ export const getElementInformation = async ( } else { break; } - } + } } let info: { tagName: string; @@ -114,7 +119,11 @@ export const getRect = async (page: Page, coordinates: Coordinates) => { if (originalEl) { let element = originalEl; - // Same parent-finding logic as in getElementInformation + if (originalEl.tagName === 'A') { + element = originalEl; + } else if (originalEl.parentElement?.tagName === 'A') { + element = originalEl.parentElement; + } else { while (element.parentElement) { const parentRect = element.parentElement.getBoundingClientRect(); const childRect = element.getBoundingClientRect(); @@ -134,8 +143,9 @@ export const getRect = async (page: Page, coordinates: Coordinates) => { } else { break; } - } + }} + //element = element?.parentElement?.tagName === 'A' ? element?.parentElement : element; const rectangle = element?.getBoundingClientRect(); if (rectangle) { From 4da462f48bc037ee2eed4db48428b58293c44f1b Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 9 Dec 2024 15:04:45 +0530 Subject: [PATCH 72/82] fix: a tags --- server/src/workflow-management/selector.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 6a80e7a3..f3925ec9 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -823,7 +823,11 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates let element = originalEl; - // Find the most appropriate parent element + if (originalEl.tagName === 'A') { + element = originalEl; + } else if (originalEl.parentElement?.tagName === 'A') { + element = originalEl.parentElement; + } else { while (element.parentElement) { const parentRect = element.parentElement.getBoundingClientRect(); const childRect = element.getBoundingClientRect(); @@ -844,6 +848,7 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates break; } } + } const generalSelector = getSelectorPath(element); return { From 3dfe9117b0061d0a265f938f16a551f41ef03dae Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Mon, 9 Dec 2024 17:57:28 +0530 Subject: [PATCH 73/82] feat: inject cookie remover script --- server/src/browser-management/classes/RemoteBrowser.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/browser-management/classes/RemoteBrowser.ts b/server/src/browser-management/classes/RemoteBrowser.ts index 769787da..29beaa9b 100644 --- a/server/src/browser-management/classes/RemoteBrowser.ts +++ b/server/src/browser-management/classes/RemoteBrowser.ts @@ -15,6 +15,7 @@ import { InterpreterSettings, RemoteBrowserOptions } from "../../types"; import { WorkflowGenerator } from "../../workflow-management/classes/Generator"; import { WorkflowInterpreter } from "../../workflow-management/classes/Interpreter"; import { getDecryptedProxyConfig } from '../../routes/proxy'; +import { getInjectableScript } from 'idcac-playwright'; chromium.use(stealthPlugin()); @@ -168,6 +169,7 @@ export class RemoteBrowser { this.currentPage.on('framenavigated', (frame) => { if (frame === this.currentPage?.mainFrame()) { + this.currentPage.evaluate(getInjectableScript()) this.socket.emit('urlChanged', this.currentPage.url()); } }); From 549a0d35fc4c7f8f7d02b25ab632f6e4f5940b0c Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 9 Dec 2024 18:40:00 +0530 Subject: [PATCH 74/82] chore(deps): install idcac-playwright --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 977daada..a7d634f3 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "fortawesome": "^0.0.1-security", "google-auth-library": "^9.14.1", "googleapis": "^144.0.0", + "idcac-playwright": "^0.1.3", "ioredis": "^5.4.1", "joi": "^17.6.0", "jsonwebtoken": "^9.0.2", From 117dddc2ff8337320a162af350632ee69633221d Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Mon, 9 Dec 2024 18:49:23 +0530 Subject: [PATCH 75/82] feat: inject cookie remover script --- server/src/browser-management/classes/RemoteBrowser.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/browser-management/classes/RemoteBrowser.ts b/server/src/browser-management/classes/RemoteBrowser.ts index 29beaa9b..0081cb5c 100644 --- a/server/src/browser-management/classes/RemoteBrowser.ts +++ b/server/src/browser-management/classes/RemoteBrowser.ts @@ -374,6 +374,7 @@ export class RemoteBrowser { this.currentPage.on('framenavigated', (frame) => { if (frame === this.currentPage?.mainFrame()) { + this.currentPage.evaluate(getInjectableScript()); this.socket.emit('urlChanged', this.currentPage.url()); } }); @@ -406,6 +407,7 @@ export class RemoteBrowser { if (this.currentPage) { this.currentPage.on('framenavigated', (frame) => { if (frame === this.currentPage?.mainFrame()) { + this.currentPage.evaluate(getInjectableScript()); this.socket.emit('urlChanged', this.currentPage.url()); } }); From 386e7c9a98d3c519e6ad0d628318aaa355ce2134 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Mon, 9 Dec 2024 20:13:46 +0530 Subject: [PATCH 76/82] feat: add programmatic click event for clickNext --- maxun-core/src/interpret.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index a7a5de47..f0b10936 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -475,6 +475,8 @@ export default class Interpreter extends EventEmitter { case 'clickNext': const pageResults = await page.evaluate((cfg) => window.scrapeList(cfg), config); + // console.log("Page results:", pageResults); + // Filter out already scraped items const newResults = pageResults.filter(item => { const uniqueKey = JSON.stringify(item); @@ -482,9 +484,9 @@ export default class Interpreter extends EventEmitter { scrapedItems.add(uniqueKey); // Mark as scraped return true; }); - + allResults = allResults.concat(newResults); - + if (config.limit && allResults.length >= config.limit) { return allResults.slice(0, config.limit); } @@ -494,7 +496,7 @@ export default class Interpreter extends EventEmitter { return allResults; // No more pages to scrape } await Promise.all([ - nextButton.click(), + nextButton.dispatchEvent('click'), page.waitForNavigation({ waitUntil: 'networkidle' }) ]); From b84e9186b9ccf71cb05423b3923bb0c7c745b3d3 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Mon, 9 Dec 2024 22:05:50 +0530 Subject: [PATCH 77/82] fix: inject cookie script on page load --- .../browser-management/classes/RemoteBrowser.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/server/src/browser-management/classes/RemoteBrowser.ts b/server/src/browser-management/classes/RemoteBrowser.ts index 0081cb5c..7e0a7d1a 100644 --- a/server/src/browser-management/classes/RemoteBrowser.ts +++ b/server/src/browser-management/classes/RemoteBrowser.ts @@ -167,13 +167,16 @@ export class RemoteBrowser { this.context = await this.browser.newContext(contextOptions); this.currentPage = await this.context.newPage(); - this.currentPage.on('framenavigated', (frame) => { + this.currentPage.on('framenavigated', (frame) => { if (frame === this.currentPage?.mainFrame()) { - this.currentPage.evaluate(getInjectableScript()) this.socket.emit('urlChanged', this.currentPage.url()); } }); + this.currentPage.on('load', (page) => { + page.evaluate(getInjectableScript()) + }) + // await this.currentPage.setExtraHTTPHeaders({ // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3' // }); @@ -374,11 +377,14 @@ export class RemoteBrowser { this.currentPage.on('framenavigated', (frame) => { if (frame === this.currentPage?.mainFrame()) { - this.currentPage.evaluate(getInjectableScript()); this.socket.emit('urlChanged', this.currentPage.url()); } }); + this.currentPage.on('load', (page) => { + page.evaluate(getInjectableScript()) + }) + //await this.currentPage.setViewportSize({ height: 400, width: 900 }) this.client = await this.currentPage.context().newCDPSession(this.currentPage); this.socket.emit('urlChanged', this.currentPage.url()); @@ -407,10 +413,13 @@ export class RemoteBrowser { if (this.currentPage) { this.currentPage.on('framenavigated', (frame) => { if (frame === this.currentPage?.mainFrame()) { - this.currentPage.evaluate(getInjectableScript()); this.socket.emit('urlChanged', this.currentPage.url()); } }); + + this.currentPage.on('load', (page) => { + page.evaluate(getInjectableScript()) + }) // this.currentPage.on('load', (page) => { // this.socket.emit('urlChanged', page.url()); // }) From f561ef7f559dac83913ca23773723088fc1d5b07 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Tue, 10 Dec 2024 04:02:28 +0530 Subject: [PATCH 78/82] feat: apply conditional visual containment --- server/src/workflow-management/selector.ts | 49 ++++++++++++++++------ 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index f3925ec9..fa58be67 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -31,16 +31,28 @@ export const getElementInformation = async ( if (originalEl) { let element = originalEl; - if (originalEl.tagName === 'A') { - element = originalEl; - } else if (originalEl.parentElement?.tagName === 'A') { - element = originalEl.parentElement; - } else { + // if (originalEl.tagName === 'A') { + // element = originalEl; + // } else if (originalEl.parentElement?.tagName === 'A') { + // element = originalEl.parentElement; + // } else { // Generic parent finding logic based on visual containment + const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', + 'ADDRESS', 'BLOCKQUOTE', 'DETAILS', 'DIALOG', 'FIGURE', 'FIGCAPTION', 'MAIN', 'MARK', 'SUMMARY', 'TIME', + 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TH', 'TD', 'CAPTION', 'COLGROUP', 'COL', 'FORM', 'FIELDSET', + 'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT', + 'PROGRESS', 'METER', 'DETAILS', 'SUMMARY', 'MENU', 'MENUITEM', 'MENUITEM', 'APPLET', 'EMBED', 'OBJECT', + 'PARAM', 'VIDEO', 'AUDIO', 'SOURCE', 'TRACK', 'CANVAS', 'MAP', 'AREA', 'SVG', 'IFRAME', 'FRAME', 'FRAMESET', + 'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT', 'A' + ]; while (element.parentElement) { const parentRect = element.parentElement.getBoundingClientRect(); const childRect = element.getBoundingClientRect(); + if (!containerTags.includes(element.parentElement.tagName)) { + break; + } + // Check if parent visually contains the child const fullyContained = parentRect.left <= childRect.left && @@ -57,7 +69,7 @@ export const getElementInformation = async ( element = element.parentElement; } else { break; - } + // } } } let info: { @@ -119,15 +131,28 @@ export const getRect = async (page: Page, coordinates: Coordinates) => { if (originalEl) { let element = originalEl; - if (originalEl.tagName === 'A') { - element = originalEl; - } else if (originalEl.parentElement?.tagName === 'A') { - element = originalEl.parentElement; - } else { + // if (originalEl.tagName === 'A') { + // element = originalEl; + // } else if (originalEl.parentElement?.tagName === 'A') { + // element = originalEl.parentElement; + // } else { + const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', + 'ADDRESS', 'BLOCKQUOTE', 'DETAILS', 'DIALOG', 'FIGURE', 'FIGCAPTION', 'MAIN', 'MARK', 'SUMMARY', 'TIME', + 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TH', 'TD', 'CAPTION', 'COLGROUP', 'COL', 'FORM', 'FIELDSET', + 'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT', + 'PROGRESS', 'METER', 'DETAILS', 'SUMMARY', 'MENU', 'MENUITEM', 'MENUITEM', 'APPLET', 'EMBED', 'OBJECT', + 'PARAM', 'VIDEO', 'AUDIO', 'SOURCE', 'TRACK', 'CANVAS', 'MAP', 'AREA', 'SVG', 'IFRAME', 'FRAME', 'FRAMESET', + 'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT', 'A' + ]; while (element.parentElement) { const parentRect = element.parentElement.getBoundingClientRect(); const childRect = element.getBoundingClientRect(); + if (!containerTags.includes(element.parentElement.tagName)) { + break; + } + + const fullyContained = parentRect.left <= childRect.left && parentRect.right >= childRect.right && @@ -142,7 +167,7 @@ export const getRect = async (page: Page, coordinates: Coordinates) => { element = element.parentElement; } else { break; - } + // } }} //element = element?.parentElement?.tagName === 'A' ? element?.parentElement : element; From 668a67057f9542027a08f37e274a0f6b78ea5e0e Mon Sep 17 00:00:00 2001 From: amhsirak Date: Tue, 10 Dec 2024 19:24:28 +0530 Subject: [PATCH 79/82] fix: include visual containment for capture list selection --- server/src/workflow-management/selector.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index fa58be67..917ac561 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -847,16 +847,24 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates if (!originalEl) return null; let element = originalEl; + + const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', + 'ADDRESS', 'BLOCKQUOTE', 'DETAILS', 'DIALOG', 'FIGURE', 'FIGCAPTION', 'MAIN', 'MARK', 'SUMMARY', 'TIME', + 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TH', 'TD', 'CAPTION', 'COLGROUP', 'COL', 'FORM', 'FIELDSET', + 'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT', + 'PROGRESS', 'METER', 'DETAILS', 'SUMMARY', 'MENU', 'MENUITEM', 'MENUITEM', 'APPLET', 'EMBED', 'OBJECT', + 'PARAM', 'VIDEO', 'AUDIO', 'SOURCE', 'TRACK', 'CANVAS', 'MAP', 'AREA', 'SVG', 'IFRAME', 'FRAME', 'FRAMESET', + 'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT', 'A' + ]; - if (originalEl.tagName === 'A') { - element = originalEl; - } else if (originalEl.parentElement?.tagName === 'A') { - element = originalEl.parentElement; - } else { while (element.parentElement) { const parentRect = element.parentElement.getBoundingClientRect(); const childRect = element.getBoundingClientRect(); + if (!containerTags.includes(element.parentElement.tagName)) { + break; + } + const fullyContained = parentRect.left <= childRect.left && parentRect.right >= childRect.right && @@ -871,7 +879,6 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates element = element.parentElement; } else { break; - } } } From 142c90ae1c0e0fc9441bcedaff1430dd3eab7abd Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Tue, 10 Dec 2024 20:32:01 +0530 Subject: [PATCH 80/82] fix: handle context destroyed, frame navigation URL --- .../classes/RemoteBrowser.ts | 94 +++++++++++++------ 1 file changed, 64 insertions(+), 30 deletions(-) diff --git a/server/src/browser-management/classes/RemoteBrowser.ts b/server/src/browser-management/classes/RemoteBrowser.ts index 7e0a7d1a..417e8964 100644 --- a/server/src/browser-management/classes/RemoteBrowser.ts +++ b/server/src/browser-management/classes/RemoteBrowser.ts @@ -66,6 +66,8 @@ export class RemoteBrowser { maxRepeats: 1, }; + private lastEmittedUrl: string | null = null; + /** * {@link WorkflowGenerator} instance specific to the remote browser. */ @@ -88,6 +90,64 @@ export class RemoteBrowser { this.generator = new WorkflowGenerator(socket); } + /** + * Normalizes URLs to prevent navigation loops while maintaining consistent format + */ + private normalizeUrl(url: string): string { + try { + const parsedUrl = new URL(url); + // Remove trailing slashes except for root path + parsedUrl.pathname = parsedUrl.pathname.replace(/\/+$/, '') || '/'; + // Ensure consistent protocol handling + parsedUrl.protocol = parsedUrl.protocol.toLowerCase(); + return parsedUrl.toString(); + } catch { + return url; + } + } + + /** + * Determines if a URL change is significant enough to emit + */ + private shouldEmitUrlChange(newUrl: string): boolean { + if (!this.lastEmittedUrl) { + return true; + } + const normalizedNew = this.normalizeUrl(newUrl); + const normalizedLast = this.normalizeUrl(this.lastEmittedUrl); + return normalizedNew !== normalizedLast; + } + + private async setupPageEventListeners(page: Page) { + page.on('framenavigated', async (frame) => { + if (frame === page.mainFrame()) { + const currentUrl = page.url(); + if (this.shouldEmitUrlChange(currentUrl)) { + this.lastEmittedUrl = currentUrl; + this.socket.emit('urlChanged', currentUrl); + } + } + }); + + // Handle page load events with retry mechanism + page.on('load', async () => { + const injectScript = async (): Promise => { + try { + await page.waitForLoadState('networkidle', { timeout: 5000 }); + + await page.evaluate(getInjectableScript()); + return true; + } catch (error: any) { + logger.log('warn', `Script injection attempt failed: ${error.message}`); + return false; + } + }; + + const success = await injectScript(); + console.log("Script injection result:", success); + }); + } + /** * An asynchronous constructor for asynchronously initialized properties. * Must be called right after creating an instance of RemoteBrowser class. @@ -167,15 +227,7 @@ export class RemoteBrowser { this.context = await this.browser.newContext(contextOptions); this.currentPage = await this.context.newPage(); - this.currentPage.on('framenavigated', (frame) => { - if (frame === this.currentPage?.mainFrame()) { - this.socket.emit('urlChanged', this.currentPage.url()); - } - }); - - this.currentPage.on('load', (page) => { - page.evaluate(getInjectableScript()) - }) + await this.setupPageEventListeners(this.currentPage); // await this.currentPage.setExtraHTTPHeaders({ // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3' @@ -375,15 +427,7 @@ export class RemoteBrowser { await this.stopScreencast(); this.currentPage = page; - this.currentPage.on('framenavigated', (frame) => { - if (frame === this.currentPage?.mainFrame()) { - this.socket.emit('urlChanged', this.currentPage.url()); - } - }); - - this.currentPage.on('load', (page) => { - page.evaluate(getInjectableScript()) - }) + await this.setupPageEventListeners(this.currentPage); //await this.currentPage.setViewportSize({ height: 400, width: 900 }) this.client = await this.currentPage.context().newCDPSession(this.currentPage); @@ -411,18 +455,8 @@ export class RemoteBrowser { await this.currentPage?.close(); this.currentPage = newPage; if (this.currentPage) { - this.currentPage.on('framenavigated', (frame) => { - if (frame === this.currentPage?.mainFrame()) { - this.socket.emit('urlChanged', this.currentPage.url()); - } - }); - - this.currentPage.on('load', (page) => { - page.evaluate(getInjectableScript()) - }) - // this.currentPage.on('load', (page) => { - // this.socket.emit('urlChanged', page.url()); - // }) + await this.setupPageEventListeners(this.currentPage); + this.client = await this.currentPage.context().newCDPSession(this.currentPage); await this.subscribeToScreencast(); } else { From c7af54ebe061abcf1c5c5bcb26e4182fa3f8d254 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Tue, 10 Dec 2024 20:33:35 +0530 Subject: [PATCH 81/82] feat: add fallback mechanism for click and waitForLoadState action --- maxun-core/src/interpret.ts | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index bd90e40f..afea8e47 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -469,6 +469,16 @@ export default class Interpreter extends EventEmitter { }), }; + const executeAction = async (invokee: any, methodName: string, args: any) => { + console.log("Executing action:", methodName, args); + if (!args || Array.isArray(args)) { + await (invokee[methodName])(...(args ?? [])); + } else { + await (invokee[methodName])(args); + } + }; + + for (const step of steps) { this.log(`Launching ${String(step.action)}`, Level.LOG); @@ -486,10 +496,20 @@ export default class Interpreter extends EventEmitter { invokee = invokee[level]; } - if (!step.args || Array.isArray(step.args)) { - await (invokee[methodName])(...(step.args ?? [])); + if (methodName === 'waitForLoadState') { + try { + await executeAction(invokee, methodName, step.args); + } catch (error) { + await executeAction(invokee, methodName, 'domcontentloaded'); + } + } else if (methodName === 'click') { + try { + await executeAction(invokee, methodName, step.args); + } catch (error) { + await executeAction(invokee, methodName, [step.args[0], { force: true }]); + } } else { - await (invokee[methodName])(step.args); + await executeAction(invokee, methodName, step.args); } } @@ -571,7 +591,7 @@ export default class Interpreter extends EventEmitter { return allResults; } // Click the 'Load More' button to load additional items - await loadMoreButton.click(); + await loadMoreButton.dispatchEvent('click'); await page.waitForTimeout(2000); // Wait for new items to load // After clicking 'Load More', scroll down to load more items await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)); From fd16aff31c01d95b5cf8df464fba548adea55a28 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Tue, 10 Dec 2024 23:05:17 +0530 Subject: [PATCH 82/82] feat: emit setGetList:false socket event in pagination mode --- src/context/browserActions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/context/browserActions.tsx b/src/context/browserActions.tsx index ef303f82..55ca1b37 100644 --- a/src/context/browserActions.tsx +++ b/src/context/browserActions.tsx @@ -53,6 +53,7 @@ export const ActionProvider = ({ children }: { children: ReactNode }) => { const startPaginationMode = () => { setPaginationMode(true); setCaptureStage('pagination'); + socket?.emit('setGetList', { getList: false }); }; const stopPaginationMode = () => setPaginationMode(false); @@ -75,7 +76,6 @@ export const ActionProvider = ({ children }: { children: ReactNode }) => { const stopGetList = () => { setGetList(false); - socket?.emit('setGetList', { getList: false }); setPaginationType(''); setLimitType(''); setCustomLimit('');