diff --git a/README.md b/README.md index b63cf8a7..47e170b5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@
- Website |
+ Website |
Discord |
- Twitter |
+ Twitter |
Join Maxun Cloud |
- Watch Tutorials
+ Watch Tutorials
diff --git a/maxun-core/src/browserSide/scraper.js b/maxun-core/src/browserSide/scraper.js
index ff5a1938..d6489e6b 100644
--- a/maxun-core/src/browserSide/scraper.js
+++ b/maxun-core/src/browserSide/scraper.js
@@ -705,14 +705,14 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3,
if (Object.keys(record).length > 0) {
nonTableData.push(record);
}
- }
+ }
}
-
+
+ // Merge and limit the results
const scrapedData = [...tableData, ...nonTableData];
return scrapedData;
};
-
/**
* Gets all children of the elements matching the listSelector,
* returning their CSS selectors and innerText.
diff --git a/public/locales/de.json b/public/locales/de.json
index 90beaa14..b9b4185b 100644
--- a/public/locales/de.json
+++ b/public/locales/de.json
@@ -1,7 +1,7 @@
{
"login": {
"title": "Willkommen zurück!",
- "email": "E-Mail",
+ "email": "Geben Sie Ihre geschäftliche E-Mail-Adresse ein",
"password": "Passwort",
"button": "Einloggen",
"loading": "Lädt",
@@ -12,7 +12,7 @@
},
"register": {
"title": "Konto registrieren",
- "email": "E-Mail",
+ "email": "Geben Sie Ihre geschäftliche E-Mail-Adresse ein",
"password": "Passwort",
"button": "Registrieren",
"loading": "Lädt",
@@ -158,11 +158,13 @@
"confirm": "Bestätigen",
"discard": "Verwerfen",
"confirm_capture": "Erfassung bestätigen",
- "confirm_pagination": "Paginierung bestätigen",
- "confirm_limit": "Limit bestätigen",
+ "confirm_pagination": "Bestätigen",
+ "confirm_limit": "Bestätigen",
"finish_capture": "Erfassung abschließen",
+ "back": "Zurück",
"finish": "Fertig",
- "cancel": "Abbrechen"
+ "cancel": "Abbrechen",
+ "delete": "Löschen"
},
"screenshot": {
"capture_fullpage": "Vollständige Seite erfassen",
diff --git a/public/locales/en.json b/public/locales/en.json
index 9dcad514..bd8acce3 100644
--- a/public/locales/en.json
+++ b/public/locales/en.json
@@ -1,7 +1,7 @@
{
"login": {
"title": "Welcome Back!",
- "email": "Email",
+ "email": "Enter Work Email",
"password": "Password",
"button": "Login",
"loading": "Loading",
@@ -12,7 +12,7 @@
},
"register": {
"title": "Register Account",
- "email": "Email",
+ "email": "Enter Work Email",
"password": "Password",
"button": "Register",
"loading": "Loading",
@@ -159,11 +159,13 @@
"confirm": "Confirm",
"discard": "Discard",
"confirm_capture": "Confirm Capture",
- "confirm_pagination": "Confirm Pagination",
- "confirm_limit": "Confirm Limit",
+ "confirm_pagination": "Confirm",
+ "confirm_limit": "Confirm",
"finish_capture": "Finish Capture",
+ "back": "Back",
"finish": "Finish",
- "cancel": "Cancel"
+ "cancel": "Cancel",
+ "delete": "Delete"
},
"screenshot": {
"capture_fullpage": "Capture Fullpage",
diff --git a/public/locales/es.json b/public/locales/es.json
index 00fa379e..94210880 100644
--- a/public/locales/es.json
+++ b/public/locales/es.json
@@ -1,7 +1,7 @@
{
"login": {
"title": "¡Bienvenido de nuevo!",
- "email": "Correo electrónico",
+ "email": "Introducir correo electrónico de trabajo",
"password": "Contraseña",
"button": "Iniciar sesión",
"loading": "Cargando",
@@ -12,7 +12,7 @@
},
"register": {
"title": "Crear cuenta",
- "email": "Correo electrónico",
+ "email": "Introducir correo electrónico de trabajo",
"password": "Contraseña",
"button": "Registrarse",
"loading": "Cargando",
@@ -159,11 +159,13 @@
"confirm": "Confirmar",
"discard": "Descartar",
"confirm_capture": "Confirmar Captura",
- "confirm_pagination": "Confirmar Paginación",
- "confirm_limit": "Confirmar Límite",
+ "confirm_pagination": "Confirmar",
+ "confirm_limit": "Confirmar",
"finish_capture": "Finalizar Captura",
+ "back": "Atrás",
"finish": "Finalizar",
- "cancel": "Cancelar"
+ "cancel": "Cancelar",
+ "delete": "Eliminar"
},
"screenshot": {
"capture_fullpage": "Capturar Página Completa",
diff --git a/public/locales/ja.json b/public/locales/ja.json
index b444c81a..0bcba967 100644
--- a/public/locales/ja.json
+++ b/public/locales/ja.json
@@ -1,7 +1,7 @@
{
"login": {
"title": "お帰りなさい!",
- "email": "メールアドレス",
+ "email": "勤務先メールアドレスを入力",
"password": "パスワード",
"button": "ログイン",
"loading": "読み込み中",
@@ -12,7 +12,7 @@
},
"register": {
"title": "アカウントを登録する",
- "email": "メールアドレス",
+ "email": "勤務先メールアドレスを入力",
"password": "パスワード",
"button": "登録する",
"loading": "読み込み中",
@@ -159,11 +159,13 @@
"confirm": "確認",
"discard": "破棄",
"confirm_capture": "取得を確認",
- "confirm_pagination": "ページネーションを確認",
- "confirm_limit": "制限を確認",
+ "confirm_pagination": "確認",
+ "confirm_limit": "確認",
"finish_capture": "取得を完了",
+ "back": "戻る",
"finish": "完了",
- "cancel": "キャンセル"
+ "cancel": "キャンセル",
+ "delete": "削除"
},
"screenshot": {
"capture_fullpage": "フルページを取得",
diff --git a/public/locales/zh.json b/public/locales/zh.json
index 27455ebe..a19fe439 100644
--- a/public/locales/zh.json
+++ b/public/locales/zh.json
@@ -1,7 +1,7 @@
{
"login": {
"title": "欢迎回来!",
- "email": "电子邮箱",
+ "email": "输入工作电子邮箱",
"password": "密码",
"button": "登录",
"loading": "加载中",
@@ -12,7 +12,7 @@
},
"register": {
"title": "注册账号",
- "email": "电子邮箱",
+ "email": "输入工作电子邮箱",
"password": "密码",
"button": "注册",
"loading": "加载中",
@@ -159,11 +159,13 @@
"confirm": "确认",
"discard": "放弃",
"confirm_capture": "确认捕获",
- "confirm_pagination": "确认分页",
- "confirm_limit": "确认限制",
+ "confirm_pagination": "确认",
+ "confirm_limit": "确认",
"finish_capture": "完成捕获",
+ "back": "返回",
"finish": "完成",
- "cancel": "取消"
+ "cancel": "取消",
+ "delete": "删除"
},
"screenshot": {
"capture_fullpage": "捕获整页",
diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts
index 713c05bc..82e50d9f 100644
--- a/server/src/workflow-management/selector.ts
+++ b/server/src/workflow-management/selector.ts
@@ -139,26 +139,45 @@ export const getElementInformation = async (
const originalEl = getDeepestElementFromPoint(x, y);
if (originalEl) {
let element = originalEl;
-
- // Handle element hierarchy traversal for list items
- 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;
+
+ if (element.tagName === 'TD' || element.tagName === 'TH') {
+ const tableParent = element.closest('table');
+ if (tableParent) {
+ element = tableParent;
+ }
+ }
+
+ if (element.tagName !== 'TABLE') {
+ while (element.parentElement) {
+ if (element.tagName.toLowerCase() === 'body' ||
+ element.tagName.toLowerCase() === 'html') {
+ break;
+ }
+
+ 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) {
+ const nextParent = element.parentElement;
+ if (nextParent.tagName.toLowerCase() !== 'body' &&
+ nextParent.tagName.toLowerCase() !== 'html') {
+ element = nextParent;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
}
}
@@ -319,25 +338,44 @@ export const getRect = async (page: Page, coordinates: Coordinates, listSelector
if (originalEl) {
let element = originalEl;
- // Handle element hierarchy traversal for list items
- while (element.parentElement) {
- const parentRect = element.parentElement.getBoundingClientRect();
- const childRect = element.getBoundingClientRect();
+ if (element.tagName === 'TD' || element.tagName === 'TH') {
+ const tableParent = element.closest('table');
+ if (tableParent) {
+ element = tableParent;
+ }
+ }
- const fullyContained =
- parentRect.left <= childRect.left &&
- parentRect.right >= childRect.right &&
- parentRect.top <= childRect.top &&
- parentRect.bottom >= childRect.bottom;
+ if (element.tagName !== 'TABLE') {
+ while (element.parentElement) {
+ if (element.tagName.toLowerCase() === 'body' ||
+ element.tagName.toLowerCase() === 'html') {
+ break;
+ }
- const significantOverlap =
- (childRect.width * childRect.height) /
- (parentRect.width * parentRect.height) > 0.5;
+ const parentRect = element.parentElement.getBoundingClientRect();
+ const childRect = element.getBoundingClientRect();
- if (fullyContained && significantOverlap) {
- element = element.parentElement;
- } else {
- break;
+ 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) {
+ const nextParent = element.parentElement;
+ if (nextParent.tagName.toLowerCase() !== 'body' &&
+ nextParent.tagName.toLowerCase() !== 'html') {
+ element = nextParent;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
}
}
@@ -1113,17 +1151,24 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates
// Generate basic selector from element's tag and classes
function getNonUniqueSelector(element: HTMLElement): string {
let selector = element.tagName.toLowerCase();
-
- const className = typeof element.className === 'string' ? element.className : '';
- if (className) {
- const classes = className.split(/\s+/)
- .filter(cls => Boolean(cls) && !cls.startsWith('!') && !cls.includes(':'));
-
+
+ if (selector === 'td' && element.parentElement) {
+ // Find position among td siblings
+ const siblings = Array.from(element.parentElement.children);
+ const position = siblings.indexOf(element) + 1;
+ return `${selector}:nth-child(${position})`;
+ }
+
+ if (element.className) {
+ const classes = element.className.split(/\s+/).filter((cls: string) => Boolean(cls));
if (classes.length > 0) {
- selector += '.' + classes.map(cls => CSS.escape(cls)).join('.');
+ const validClasses = classes.filter((cls: string) => !cls.startsWith('!') && !cls.includes(':'));
+ if (validClasses.length > 0) {
+ selector += '.' + validClasses.map(cls => CSS.escape(cls)).join('.');
+ }
}
}
-
+
return selector;
}
@@ -1202,25 +1247,45 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates
let element = originalEl;
- // Handle parent traversal for better element targeting
- while (element.parentElement) {
- const parentRect = element.parentElement.getBoundingClientRect();
- const childRect = element.getBoundingClientRect();
+ if (element.tagName === 'TD' || element.tagName === 'TH') {
+ const tableParent = element.closest('table');
+ if (tableParent) {
+ element = tableParent;
+ }
+ }
- const fullyContained =
- parentRect.left <= childRect.left &&
- parentRect.right >= childRect.right &&
- parentRect.top <= childRect.top &&
- parentRect.bottom >= childRect.bottom;
+ // if (listSelector === '') {
+ if (element.tagName !== 'TABLE') {
+ while (element.parentElement) {
+ if (element.tagName.toLowerCase() === 'body' ||
+ element.tagName.toLowerCase() === 'html') {
+ break;
+ }
- const significantOverlap =
- (childRect.width * childRect.height) /
- (parentRect.width * parentRect.height) > 0.5;
+ const parentRect = element.parentElement.getBoundingClientRect();
+ const childRect = element.getBoundingClientRect();
- if (fullyContained && significantOverlap) {
- element = element.parentElement;
- } else {
- break;
+ 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) {
+ const nextParent = element.parentElement;
+ if (nextParent.tagName.toLowerCase() !== 'body' &&
+ nextParent.tagName.toLowerCase() !== 'html') {
+ element = nextParent;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
}
}
@@ -1260,17 +1325,23 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates
// Generate basic selector from element's tag and classes
function getNonUniqueSelector(element: HTMLElement): string {
let selector = element.tagName.toLowerCase();
-
- const className = typeof element.className === 'string' ? element.className : '';
- if (className) {
- const classes = className.split(/\s+/)
- .filter(cls => Boolean(cls) && !cls.startsWith('!') && !cls.includes(':'));
-
+
+ if (selector === 'td' && element.parentElement) {
+ const siblings = Array.from(element.parentElement.children);
+ const position = siblings.indexOf(element) + 1;
+ return `${selector}:nth-child(${position})`;
+ }
+
+ if (element.className) {
+ const classes = element.className.split(/\s+/).filter((cls: string) => Boolean(cls));
if (classes.length > 0) {
- selector += '.' + classes.map(cls => CSS.escape(cls)).join('.');
+ const validClasses = classes.filter((cls: string) => !cls.startsWith('!') && !cls.includes(':'));
+ if (validClasses.length > 0) {
+ selector += '.' + validClasses.map(cls => CSS.escape(cls)).join('.');
+ }
}
}
-
+
return selector;
}
@@ -1364,6 +1435,12 @@ export const getChildSelectors = async (page: Page, parentSelector: string): Pro
function getNonUniqueSelector(element: HTMLElement): string {
let selector = element.tagName.toLowerCase();
+ if (selector === 'td' && element.parentElement) {
+ const siblings = Array.from(element.parentElement.children);
+ const position = siblings.indexOf(element) + 1;
+ return `${selector}:nth-child(${position})`;
+ }
+
const className = typeof element.className === 'string' ? element.className : '';
if (className) {
const classes = className.split(/\s+/).filter((cls: string) => Boolean(cls));
diff --git a/src/api/storage.ts b/src/api/storage.ts
index 4b2f4e80..18c793c0 100644
--- a/src/api/storage.ts
+++ b/src/api/storage.ts
@@ -5,11 +5,6 @@ import { ScheduleSettings } from "../components/molecules/ScheduleSettings";
import { CreateRunResponse, ScheduleRunResponse } from "../pages/MainPage";
import { apiUrl } from "../apiConfig";
-
-
-
-
-
export const getStoredRecordings = async (): Promise