From 1d8d0bd8cf9d3373958dd908501e7a7b6ebce21d Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sun, 21 Jul 2024 23:51:55 +0530 Subject: [PATCH 001/255] feat: introduce modal --- src/components/organisms/BrowserWindow.tsx | 78 ++++++++++++++++++++-- 1 file changed, 72 insertions(+), 6 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index d81ff967..f0d21902 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -3,12 +3,42 @@ import { useSocketStore } from '../../context/socket'; import Canvas from "../atoms/canvas"; import { useBrowserDimensionsStore } from "../../context/browserDimensions"; import { Highlighter } from "../atoms/Highlighter"; +import { GenericModal } from '../atoms/GenericModal'; +import { Button, Typography, Box } from '@mui/material'; + +interface ConfirmationBoxProps { + selector: string; + onYes: () => void; + onNo: () => void; +} + +// New component for the confirmation box inside the modal +const ConfirmationBox = ({ selector, onYes, onNo }: ConfirmationBoxProps) => { + return ( + + + Confirmation + + + Do you want to interact with the element: {selector}? + + + + + + + ); +}; export const BrowserWindow = () => { - const [canvasRef, setCanvasReference] = useState | undefined>(undefined); const [screenShot, setScreenShot] = useState(""); const [highlighterData, setHighlighterData] = useState<{ rect: DOMRect, selector: string } | null>(null); + const [showConfirmation, setShowConfirmation] = useState(false); const { socket } = useSocketStore(); const { width, height } = useBrowserDimensionsStore(); @@ -46,10 +76,8 @@ export const BrowserWindow = () => { return () => { socket?.off("screencast", screencastHandler); } - }, [screenShot, canvasRef, socket, screencastHandler]); - const highlighterHandler = useCallback((data: { rect: DOMRect, selector: string }) => { setHighlighterData(data); console.log('Highlighter Rect via socket:', data.rect) @@ -60,15 +88,53 @@ export const BrowserWindow = () => { if (socket) { socket.on("highlighter", highlighterHandler); } - //cleaning function return () => { document.removeEventListener('mousemove', onMouseMove); socket?.off("highlighter", highlighterHandler); }; }, [socket, onMouseMove]); + const handleClick = (e: React.MouseEvent) => { + if (highlighterData && canvasRef?.current) { + const canvasRect = canvasRef.current.getBoundingClientRect(); + const clickX = e.clientX - canvasRect.left; + const clickY = e.clientY - canvasRect.top; + + const highlightRect = highlighterData.rect; + if ( + clickX >= highlightRect.left && + clickX <= highlightRect.right && + clickY >= highlightRect.top && + clickY <= highlightRect.bottom + ) { + setShowConfirmation(true); + } + } + }; + + const handleConfirmation = (confirmed: boolean) => { + if (confirmed) { + console.log(`User confirmed interaction with: ${highlighterData?.selector}`); + // Here you can add logic to interact with the element + } else { + console.log('User declined interaction'); + } + setShowConfirmation(false); + }; + return ( - <> +
+ setShowConfirmation(false)} + canBeClosed={false} + > + handleConfirmation(true)} + onNo={() => handleConfirmation(false)} + /> + {(highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? { width={width} height={height} /> - +
); }; From 57f2b8139c02815d4405121ff9e722693be95b54 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sun, 21 Jul 2024 23:52:37 +0530 Subject: [PATCH 002/255] feat: set z-index greater than Highlighter z-index --- src/components/atoms/GenericModal.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/atoms/GenericModal.tsx b/src/components/atoms/GenericModal.tsx index 30ba7a68..63aa6900 100644 --- a/src/components/atoms/GenericModal.tsx +++ b/src/components/atoms/GenericModal.tsx @@ -41,4 +41,5 @@ const defaultModalStyle = { display: 'block', overflow: 'scroll', padding: '5px 25px 10px 25px', + zIndex: 3147483647, }; From c3f45b35f33bb0bc47d11e819fe542b97d56e6e7 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sun, 21 Jul 2024 23:59:35 +0530 Subject: [PATCH 003/255] feat: rm Highlighter from DOM when modal open --- src/components/organisms/BrowserWindow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index f0d21902..f63ecd38 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -135,7 +135,7 @@ export const BrowserWindow = () => { onNo={() => handleConfirmation(false)} /> - {(highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? + {(!showConfirmation && highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? Date: Mon, 22 Jul 2024 16:49:39 +0530 Subject: [PATCH 004/255] chore: cleanup --- src/components/organisms/BrowserWindow.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index f63ecd38..a0bcda07 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -12,7 +12,6 @@ interface ConfirmationBoxProps { onNo: () => void; } -// New component for the confirmation box inside the modal const ConfirmationBox = ({ selector, onYes, onNo }: ConfirmationBoxProps) => { return ( From f2a1fb4d7650469275e2be8bc4d797057cc43011 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 22 Jul 2024 16:50:36 +0530 Subject: [PATCH 005/255] chore: comment logs --- src/components/organisms/BrowserWindow.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index a0bcda07..5d4578d5 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -42,7 +42,7 @@ export const BrowserWindow = () => { const { socket } = useSocketStore(); const { width, height } = useBrowserDimensionsStore(); - console.log('Use browser dimensions:', width, height) + // console.log('Use browser dimensions:', width, height) const onMouseMove = (e: MouseEvent) => { if (canvasRef && canvasRef.current && highlighterData) { @@ -79,7 +79,7 @@ export const BrowserWindow = () => { const highlighterHandler = useCallback((data: { rect: DOMRect, selector: string }) => { setHighlighterData(data); - console.log('Highlighter Rect via socket:', data.rect) + // console.log('Highlighter Rect via socket:', data.rect) }, [highlighterData]) useEffect(() => { @@ -162,8 +162,8 @@ const drawImage = (image: string, canvas: HTMLCanvasElement): void => { img.onload = () => { URL.revokeObjectURL(img.src); ctx?.drawImage(img, 0, 0, 1280, 720); - console.log('Image drawn on canvas:', img.width, img.height); - console.log('Image drawn on canvas:', canvas.width, canvas.height); + //console.log('Image drawn on canvas:', img.width, img.height); + //console.log('Image drawn on canvas:', canvas.width, canvas.height); }; }; \ No newline at end of file From 11fdc05eebd5b384e3903eae0e87e1f699d455f3 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 22 Jul 2024 16:54:59 +0530 Subject: [PATCH 006/255] chore: comment logs --- src/components/atoms/Highlighter.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/atoms/Highlighter.tsx b/src/components/atoms/Highlighter.tsx index 4949277d..34fb8f47 100644 --- a/src/components/atoms/Highlighter.tsx +++ b/src/components/atoms/Highlighter.tsx @@ -24,9 +24,9 @@ export const Highlighter = ({ unmodifiedRect, displayedSelector = '', width, hei }; - console.log('unmodifiedRect:', unmodifiedRect) - console.log('rectangle:', rect) - console.log('canvas rectangle:', canvasRect) + //console.log('unmodifiedRect:', unmodifiedRect) + //console.log('rectangle:', rect) + //console.log('canvas rectangle:', canvasRect) return (
From 0d69e7e776d3060fab8be9a71d88004d1bbd152a Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 22 Jul 2024 17:02:20 +0530 Subject: [PATCH 007/255] chore: lint --- src/components/organisms/BrowserWindow.tsx | 42 +++++++++++----------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 5d4578d5..6b8f8843 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -13,24 +13,24 @@ interface ConfirmationBoxProps { } const ConfirmationBox = ({ selector, onYes, onNo }: ConfirmationBoxProps) => { - return ( - - - Confirmation - - - Do you want to interact with the element: {selector}? - - - - - - - ); + return ( + + + Confirmation + + + Do you want to interact with the element: {selector}? + + + + + + + ); }; export const BrowserWindow = () => { @@ -98,7 +98,7 @@ export const BrowserWindow = () => { const canvasRect = canvasRef.current.getBoundingClientRect(); const clickX = e.clientX - canvasRect.left; const clickY = e.clientY - canvasRect.top; - + const highlightRect = highlighterData.rect; if ( clickX >= highlightRect.left && @@ -123,8 +123,8 @@ export const BrowserWindow = () => { return (
- setShowConfirmation(false)} canBeClosed={false} > From 137029cc9a9aa1a30abef6dec0e6284be18f9a61 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 07:40:09 +0530 Subject: [PATCH 008/255] feat: state for click confirmation --- src/components/organisms/BrowserWindow.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 6b8f8843..90e9ea9e 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -38,6 +38,7 @@ export const BrowserWindow = () => { const [screenShot, setScreenShot] = useState(""); const [highlighterData, setHighlighterData] = useState<{ rect: DOMRect, selector: string } | null>(null); const [showConfirmation, setShowConfirmation] = useState(false); + const [isClickConfirmed, setIsClickConfirmed] = useState(false); const { socket } = useSocketStore(); const { width, height } = useBrowserDimensionsStore(); From 32e759367ff09badbd900ca18d55571579e4614f Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 08:37:50 +0530 Subject: [PATCH 009/255] feat: conditional set for click --- src/components/organisms/BrowserWindow.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 90e9ea9e..c943e975 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -116,8 +116,10 @@ export const BrowserWindow = () => { if (confirmed) { console.log(`User confirmed interaction with: ${highlighterData?.selector}`); // Here you can add logic to interact with the element + setIsClickConfirmed(true); } else { console.log('User declined interaction'); + setIsClickConfirmed(true); } setShowConfirmation(false); }; From 843fbc6a75a670c18c931d500240b15eb80259eb Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 08:38:48 +0530 Subject: [PATCH 010/255] feat: isCLickCOnfirmed prop --- src/components/atoms/canvas.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 9a879106..03845186 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -11,6 +11,7 @@ interface CanvasProps { width: number; height: number; onCreateRef: CreateRefCallback; + isClickConfirmed: boolean; } /** @@ -45,7 +46,8 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { switch (event.type) { case 'mousedown': const clickCoordinates = getMappedCoordinates(event, canvasRef.current, width, height); - socket.emit('input:mousedown', clickCoordinates); + //socket.emit('input:mousedown', clickCoordinates); + console.log('you clicked on:', clickCoordinates); notifyLastAction('click'); break; case 'mousemove': From 74a9cf9f622d273e52115ba4cb74b8c993cd43b1 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 08:39:13 +0530 Subject: [PATCH 011/255] feat: resetClickConfirmation prop --- src/components/atoms/canvas.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 03845186..a41b47dd 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -12,6 +12,7 @@ interface CanvasProps { height: number; onCreateRef: CreateRefCallback; isClickConfirmed: boolean; + resetClickConfirmation: () => void; } /** From 3c5fadc5bb3537c9deee426548d9b9845616b205 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 08:39:33 +0530 Subject: [PATCH 012/255] feat: pass missing props --- src/components/atoms/canvas.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index a41b47dd..1e7f0fb6 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -23,7 +23,7 @@ export interface Coordinates { y: number; }; -const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { +const Canvas = ({ width, height, onCreateRef, isClickConfirmed, resetClickConfirmation }: CanvasProps) => { const canvasRef = useRef(null); const { socket } = useSocketStore(); From 032fabc76b25341a0ff83a17edc4933d875c3271 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 08:40:44 +0530 Subject: [PATCH 013/255] feat: emit mousedown event on click confirmation --- src/components/atoms/canvas.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 1e7f0fb6..9ddcc628 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -47,9 +47,10 @@ const Canvas = ({ width, height, onCreateRef, isClickConfirmed, resetClickConfir switch (event.type) { case 'mousedown': const clickCoordinates = getMappedCoordinates(event, canvasRef.current, width, height); - //socket.emit('input:mousedown', clickCoordinates); - console.log('you clicked on:', clickCoordinates); - notifyLastAction('click'); + if (isClickConfirmed) { + socket.emit('input:mousedown', clickCoordinates); + notifyLastAction('click'); + } break; case 'mousemove': const coordinates = getMappedCoordinates(event, canvasRef.current, width, height); From 6ade89aeaa3f89ff721b67e35bd1c2dfe415a398 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 08:41:20 +0530 Subject: [PATCH 014/255] feat: reset click confirmation --- src/components/atoms/canvas.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 9ddcc628..ff438669 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -50,6 +50,7 @@ const Canvas = ({ width, height, onCreateRef, isClickConfirmed, resetClickConfir if (isClickConfirmed) { socket.emit('input:mousedown', clickCoordinates); notifyLastAction('click'); + resetClickConfirmation(); } break; case 'mousemove': From ef30ec47c7e7ac3c1d2aec0c9f12ef28fa3a170d Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 08:42:21 +0530 Subject: [PATCH 015/255] feat: pass isClickConfirmed --- src/components/organisms/BrowserWindow.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index c943e975..4145cf57 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -119,7 +119,7 @@ export const BrowserWindow = () => { setIsClickConfirmed(true); } else { console.log('User declined interaction'); - setIsClickConfirmed(true); + setIsClickConfirmed(false); } setShowConfirmation(false); }; @@ -150,6 +150,7 @@ export const BrowserWindow = () => { onCreateRef={setCanvasReference} width={width} height={height} + isClickConfirmed={isClickConfirmed} />
); From a28d0bd977360d7fcf9f0a4eba036dc9a8f2ecd5 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 08:42:53 +0530 Subject: [PATCH 016/255] feat: pass resetClickConfirmation --- src/components/organisms/BrowserWindow.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 4145cf57..a483201d 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -151,6 +151,7 @@ export const BrowserWindow = () => { width={width} height={height} isClickConfirmed={isClickConfirmed} + resetClickConfirmation={() => setIsClickConfirmed(false)} />
); From 0f1a8b9823f66d6cf82667fff256fc9aef50b01e Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 10:18:38 +0530 Subject: [PATCH 017/255] refactor: move click logic to Canvas --- src/components/organisms/BrowserWindow.tsx | 95 ++-------------------- 1 file changed, 5 insertions(+), 90 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index a483201d..c516a776 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -3,52 +3,18 @@ import { useSocketStore } from '../../context/socket'; import Canvas from "../atoms/canvas"; import { useBrowserDimensionsStore } from "../../context/browserDimensions"; import { Highlighter } from "../atoms/Highlighter"; -import { GenericModal } from '../atoms/GenericModal'; -import { Button, Typography, Box } from '@mui/material'; - -interface ConfirmationBoxProps { - selector: string; - onYes: () => void; - onNo: () => void; -} - -const ConfirmationBox = ({ selector, onYes, onNo }: ConfirmationBoxProps) => { - return ( - - - Confirmation - - - Do you want to interact with the element: {selector}? - - - - - - - ); -}; export const BrowserWindow = () => { const [canvasRef, setCanvasReference] = useState | undefined>(undefined); const [screenShot, setScreenShot] = useState(""); const [highlighterData, setHighlighterData] = useState<{ rect: DOMRect, selector: string } | null>(null); - const [showConfirmation, setShowConfirmation] = useState(false); - const [isClickConfirmed, setIsClickConfirmed] = useState(false); const { socket } = useSocketStore(); const { width, height } = useBrowserDimensionsStore(); - // console.log('Use browser dimensions:', width, height) - const onMouseMove = (e: MouseEvent) => { if (canvasRef && canvasRef.current && highlighterData) { const canvasRect = canvasRef.current.getBoundingClientRect(); - // mousemove outside the browser window if ( e.pageX < canvasRect.left || e.pageX > canvasRect.right @@ -62,7 +28,7 @@ export const BrowserWindow = () => { const screencastHandler = useCallback((data: string) => { setScreenShot(data); - }, [screenShot]); + }, []); useEffect(() => { if (socket) { @@ -70,8 +36,6 @@ export const BrowserWindow = () => { } if (canvasRef?.current) { drawImage(screenShot, canvasRef.current); - } else { - console.log('Canvas is not initialized'); } return () => { socket?.off("screencast", screencastHandler); @@ -80,8 +44,7 @@ export const BrowserWindow = () => { const highlighterHandler = useCallback((data: { rect: DOMRect, selector: string }) => { setHighlighterData(data); - // console.log('Highlighter Rect via socket:', data.rect) - }, [highlighterData]) + }, []) useEffect(() => { document.addEventListener('mousemove', onMouseMove, false); @@ -94,50 +57,9 @@ export const BrowserWindow = () => { }; }, [socket, onMouseMove]); - const handleClick = (e: React.MouseEvent) => { - if (highlighterData && canvasRef?.current) { - const canvasRect = canvasRef.current.getBoundingClientRect(); - const clickX = e.clientX - canvasRect.left; - const clickY = e.clientY - canvasRect.top; - - const highlightRect = highlighterData.rect; - if ( - clickX >= highlightRect.left && - clickX <= highlightRect.right && - clickY >= highlightRect.top && - clickY <= highlightRect.bottom - ) { - setShowConfirmation(true); - } - } - }; - - const handleConfirmation = (confirmed: boolean) => { - if (confirmed) { - console.log(`User confirmed interaction with: ${highlighterData?.selector}`); - // Here you can add logic to interact with the element - setIsClickConfirmed(true); - } else { - console.log('User declined interaction'); - setIsClickConfirmed(false); - } - setShowConfirmation(false); - }; - return ( -
- setShowConfirmation(false)} - canBeClosed={false} - > - handleConfirmation(true)} - onNo={() => handleConfirmation(false)} - /> - - {(!showConfirmation && highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? +
+ {(highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? { onCreateRef={setCanvasReference} width={width} height={height} - isClickConfirmed={isClickConfirmed} - resetClickConfirmation={() => setIsClickConfirmed(false)} + highlighterData={highlighterData} />
); }; const drawImage = (image: string, canvas: HTMLCanvasElement): void => { - const ctx = canvas.getContext('2d'); - const img = new Image(); - img.src = image; img.onload = () => { URL.revokeObjectURL(img.src); ctx?.drawImage(img, 0, 0, 1280, 720); - //console.log('Image drawn on canvas:', img.width, img.height); - //console.log('Image drawn on canvas:', canvas.width, canvas.height); }; - }; \ No newline at end of file From 05807809aa23ea2fa566f19f081cdfe2c2b5da69 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 10:19:11 +0530 Subject: [PATCH 018/255] refactor: click & confirmation logic --- src/components/atoms/canvas.tsx | 155 +++++++++++++++++++------------- 1 file changed, 92 insertions(+), 63 deletions(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index ff438669..c9a9ca5a 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -1,7 +1,9 @@ -import React, { useCallback, useEffect, useRef } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useSocketStore } from '../../context/socket'; import { getMappedCoordinates } from "../../helpers/inputHelpers"; import { useGlobalInfoStore } from "../../context/globalInfo"; +import { GenericModal } from '../atoms/GenericModal'; +import { Box, Button, Typography } from '@mui/material'; interface CreateRefCallback { (ref: React.RefObject): void; @@ -11,61 +13,54 @@ interface CanvasProps { width: number; height: number; onCreateRef: CreateRefCallback; - isClickConfirmed: boolean; - resetClickConfirmation: () => void; + highlighterData: { rect: DOMRect, selector: string } | null; } -/** - * Interface for mouse's x,y coordinates - */ export interface Coordinates { x: number; y: number; +} + +const ConfirmationBox = ({ selector, onYes, onNo }: { selector: string; onYes: () => void; onNo: () => void }) => { + return ( + + + Confirmation + + + Do you want to interact with the element: {selector}? + + + + + + + ); }; -const Canvas = ({ width, height, onCreateRef, isClickConfirmed, resetClickConfirmation }: CanvasProps) => { - +const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => { const canvasRef = useRef(null); const { socket } = useSocketStore(); - const { setLastAction, lastAction } = useGlobalInfoStore(); - - const notifyLastAction = (action: string) => { - if (lastAction !== action) { - setLastAction(action); - } - }; + const { setLastAction } = useGlobalInfoStore(); + const [showConfirmation, setShowConfirmation] = useState(false); + const [pendingClick, setPendingClick] = useState(null); const lastMousePosition = useRef({ x: 0, y: 0 }); - //const lastWheelPosition = useRef({ deltaX: 0, deltaY: 0 }); const onMouseEvent = useCallback((event: MouseEvent) => { if (socket) { - const coordinates = { - x: event.clientX, - y: event.clientY, - } + const coordinates = getMappedCoordinates(event, canvasRef.current, width, height); switch (event.type) { - case 'mousedown': - const clickCoordinates = getMappedCoordinates(event, canvasRef.current, width, height); - if (isClickConfirmed) { - socket.emit('input:mousedown', clickCoordinates); - notifyLastAction('click'); - resetClickConfirmation(); - } - break; case 'mousemove': - const coordinates = getMappedCoordinates(event, canvasRef.current, width, height); if (lastMousePosition.current.x !== coordinates.x || lastMousePosition.current.y !== coordinates.y) { - lastMousePosition.current = { - x: coordinates.x, - y: coordinates.y, - }; - socket.emit('input:mousemove', { - x: coordinates.x, - y: coordinates.y, - }); - notifyLastAction('move'); + lastMousePosition.current = coordinates; + socket.emit('input:mousemove', coordinates); + setLastAction('move'); } break; case 'wheel': @@ -75,37 +70,64 @@ const Canvas = ({ width, height, onCreateRef, isClickConfirmed, resetClickConfir deltaY: Math.round(wheelEvent.deltaY), }; socket.emit('input:wheel', deltas); - notifyLastAction('scroll'); + setLastAction('scroll'); break; - default: - console.log('Default mouseEvent registered'); - return; } } - }, [socket]); + }, [socket, width, height, setLastAction]); const onKeyboardEvent = useCallback((event: KeyboardEvent) => { if (socket) { switch (event.type) { case 'keydown': socket.emit('input:keydown', { key: event.key, coordinates: lastMousePosition.current }); - notifyLastAction(`${event.key} pressed`); + setLastAction(`${event.key} pressed`); break; case 'keyup': socket.emit('input:keyup', event.key); break; - default: - console.log('Default keyEvent registered'); - return; } } - }, [socket]); + }, [socket, setLastAction]); + const handleCanvasClick = useCallback((event: MouseEvent) => { + if (canvasRef.current && highlighterData) { + const canvasRect = canvasRef.current.getBoundingClientRect(); + const clickX = event.clientX - canvasRect.left; + const clickY = event.clientY - canvasRect.top; + + const highlightRect = highlighterData.rect; + if ( + clickX >= highlightRect.left && + clickX <= highlightRect.right && + clickY >= highlightRect.top && + clickY <= highlightRect.bottom + ) { + setPendingClick({ x: clickX, y: clickY }); + setShowConfirmation(true); + } + } + }, [highlighterData]); + + const handleConfirmation = (confirmed: boolean) => { + if (confirmed && pendingClick && socket && canvasRef.current) { + const mappedCoordinates = getMappedCoordinates( + { clientX: pendingClick.x, clientY: pendingClick.y } as MouseEvent, + canvasRef.current, + width, + height + ); + socket.emit('input:mousedown', mappedCoordinates); + setLastAction('click'); + } + setShowConfirmation(false); + setPendingClick(null); + }; useEffect(() => { if (canvasRef.current) { onCreateRef(canvasRef); - canvasRef.current.addEventListener('mousedown', onMouseEvent); + canvasRef.current.addEventListener('click', handleCanvasClick); canvasRef.current.addEventListener('mousemove', onMouseEvent); canvasRef.current.addEventListener('wheel', onMouseEvent, { passive: true }); canvasRef.current.addEventListener('keydown', onKeyboardEvent); @@ -113,30 +135,37 @@ const Canvas = ({ width, height, onCreateRef, isClickConfirmed, resetClickConfir return () => { if (canvasRef.current) { - canvasRef.current.removeEventListener('mousedown', onMouseEvent); + canvasRef.current.removeEventListener('click', handleCanvasClick); canvasRef.current.removeEventListener('mousemove', onMouseEvent); canvasRef.current.removeEventListener('wheel', onMouseEvent); canvasRef.current.removeEventListener('keydown', onKeyboardEvent); canvasRef.current.removeEventListener('keyup', onKeyboardEvent); } - }; - } else { - console.log('Canvas not initialized'); } - - }, [onMouseEvent]); + }, [onMouseEvent, onKeyboardEvent, handleCanvasClick, onCreateRef]); return ( - + <> + setShowConfirmation(false)} + canBeClosed={false} + > + handleConfirmation(true)} + onNo={() => handleConfirmation(false)} + /> + + + ); - }; - export default Canvas; \ No newline at end of file From 065fa69d729557f62aa73d17beb3c520ef1ec7a3 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 10:20:37 +0530 Subject: [PATCH 019/255] fix: pass proper co-ordinates to lastMousePosition.current --- src/components/atoms/canvas.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index c9a9ca5a..f3848c66 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -58,7 +58,10 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => case 'mousemove': if (lastMousePosition.current.x !== coordinates.x || lastMousePosition.current.y !== coordinates.y) { - lastMousePosition.current = coordinates; + lastMousePosition.current = { + x: coordinates.x, + y: coordinates.y, + }; socket.emit('input:mousemove', coordinates); setLastAction('move'); } From c080c881b90daa0b809ef9f958814518a86abc08 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 10:21:42 +0530 Subject: [PATCH 020/255] fix: emit proper co-ordinates on mouse move --- src/components/atoms/canvas.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index f3848c66..b4679291 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -62,7 +62,10 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => x: coordinates.x, y: coordinates.y, }; - socket.emit('input:mousemove', coordinates); + socket.emit('input:mousemove', { + x: coordinates.x, + y: coordinates.y, + }); setLastAction('move'); } break; From 7574d0aaa856d50dacf9e93c39b689f462f78359 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 10:22:40 +0530 Subject: [PATCH 021/255] feat: default statement --- src/components/atoms/canvas.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index b4679291..44b09367 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -78,6 +78,9 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => socket.emit('input:wheel', deltas); setLastAction('scroll'); break; + default: + console.log('Default mouseEvent registered'); + return; } } }, [socket, width, height, setLastAction]); From a2821b404e3adc13cc54f3dc54b59185114826cd Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 17:08:55 +0530 Subject: [PATCH 022/255] feat: notify last action --- src/components/atoms/canvas.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 44b09367..32aadaba 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -45,10 +45,16 @@ const ConfirmationBox = ({ selector, onYes, onNo }: { selector: string; onYes: ( const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => { const canvasRef = useRef(null); const { socket } = useSocketStore(); - const { setLastAction } = useGlobalInfoStore(); + const { setLastAction, lastAction } = useGlobalInfoStore(); const [showConfirmation, setShowConfirmation] = useState(false); const [pendingClick, setPendingClick] = useState(null); + const notifyLastAction = (action: string) => { + if (lastAction !== action) { + setLastAction(action); + } + }; + const lastMousePosition = useRef({ x: 0, y: 0 }); const onMouseEvent = useCallback((event: MouseEvent) => { From bae513f5618bc5cf829ad12a27f58c395ee0dfcb Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 17:10:28 +0530 Subject: [PATCH 023/255] feat: use notify last action --- src/components/atoms/canvas.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 32aadaba..0f32d4a6 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -72,7 +72,7 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => x: coordinates.x, y: coordinates.y, }); - setLastAction('move'); + notifyLastAction('move'); } break; case 'wheel': @@ -82,7 +82,7 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => deltaY: Math.round(wheelEvent.deltaY), }; socket.emit('input:wheel', deltas); - setLastAction('scroll'); + notifyLastAction('scroll'); break; default: console.log('Default mouseEvent registered'); @@ -96,7 +96,7 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => switch (event.type) { case 'keydown': socket.emit('input:keydown', { key: event.key, coordinates: lastMousePosition.current }); - setLastAction(`${event.key} pressed`); + notifyLastAction(`${event.key} pressed`); break; case 'keyup': socket.emit('input:keyup', event.key); @@ -133,7 +133,7 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => height ); socket.emit('input:mousedown', mappedCoordinates); - setLastAction('click'); + notifyLastAction('click'); } setShowConfirmation(false); setPendingClick(null); From 402234000dace867c97368aef4fc14309d547586 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 20:06:14 +0530 Subject: [PATCH 024/255] feat: use mousedown instead of click --- src/components/atoms/canvas.tsx | 89 +++++++++++++-------------------- 1 file changed, 35 insertions(+), 54 deletions(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 0f32d4a6..729e10a0 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -45,34 +45,43 @@ const ConfirmationBox = ({ selector, onYes, onNo }: { selector: string; onYes: ( const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => { const canvasRef = useRef(null); const { socket } = useSocketStore(); - const { setLastAction, lastAction } = useGlobalInfoStore(); + const { setLastAction } = useGlobalInfoStore(); const [showConfirmation, setShowConfirmation] = useState(false); const [pendingClick, setPendingClick] = useState(null); - const notifyLastAction = (action: string) => { - if (lastAction !== action) { - setLastAction(action); - } - }; - const lastMousePosition = useRef({ x: 0, y: 0 }); const onMouseEvent = useCallback((event: MouseEvent) => { - if (socket) { + if (socket && canvasRef.current) { const coordinates = getMappedCoordinates(event, canvasRef.current, width, height); + switch (event.type) { case 'mousemove': if (lastMousePosition.current.x !== coordinates.x || lastMousePosition.current.y !== coordinates.y) { - lastMousePosition.current = { - x: coordinates.x, - y: coordinates.y, - }; - socket.emit('input:mousemove', { - x: coordinates.x, - y: coordinates.y, - }); - notifyLastAction('move'); + lastMousePosition.current = coordinates; + socket.emit('input:mousemove', coordinates); + setLastAction('move'); + } + break; + case 'mousedown': + if (highlighterData) { + const highlightRect = highlighterData.rect; + if ( + coordinates.x >= highlightRect.left && + coordinates.x <= highlightRect.right && + coordinates.y >= highlightRect.top && + coordinates.y <= highlightRect.bottom + ) { + setPendingClick(coordinates); + setShowConfirmation(true); + } else { + socket.emit('input:mousedown', coordinates); + setLastAction('click'); + } + } else { + socket.emit('input:mousedown', coordinates); + setLastAction('click'); } break; case 'wheel': @@ -82,21 +91,18 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => deltaY: Math.round(wheelEvent.deltaY), }; socket.emit('input:wheel', deltas); - notifyLastAction('scroll'); + setLastAction('scroll'); break; - default: - console.log('Default mouseEvent registered'); - return; } } - }, [socket, width, height, setLastAction]); + }, [socket, width, height, setLastAction, highlighterData]); const onKeyboardEvent = useCallback((event: KeyboardEvent) => { if (socket) { switch (event.type) { case 'keydown': socket.emit('input:keydown', { key: event.key, coordinates: lastMousePosition.current }); - notifyLastAction(`${event.key} pressed`); + setLastAction(`${event.key} pressed`); break; case 'keyup': socket.emit('input:keyup', event.key); @@ -105,35 +111,10 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => } }, [socket, setLastAction]); - const handleCanvasClick = useCallback((event: MouseEvent) => { - if (canvasRef.current && highlighterData) { - const canvasRect = canvasRef.current.getBoundingClientRect(); - const clickX = event.clientX - canvasRect.left; - const clickY = event.clientY - canvasRect.top; - - const highlightRect = highlighterData.rect; - if ( - clickX >= highlightRect.left && - clickX <= highlightRect.right && - clickY >= highlightRect.top && - clickY <= highlightRect.bottom - ) { - setPendingClick({ x: clickX, y: clickY }); - setShowConfirmation(true); - } - } - }, [highlighterData]); - const handleConfirmation = (confirmed: boolean) => { - if (confirmed && pendingClick && socket && canvasRef.current) { - const mappedCoordinates = getMappedCoordinates( - { clientX: pendingClick.x, clientY: pendingClick.y } as MouseEvent, - canvasRef.current, - width, - height - ); - socket.emit('input:mousedown', mappedCoordinates); - notifyLastAction('click'); + if (confirmed && pendingClick && socket) { + socket.emit('input:mousedown', pendingClick); + setLastAction('click'); } setShowConfirmation(false); setPendingClick(null); @@ -142,7 +123,7 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => useEffect(() => { if (canvasRef.current) { onCreateRef(canvasRef); - canvasRef.current.addEventListener('click', handleCanvasClick); + canvasRef.current.addEventListener('mousedown', onMouseEvent); canvasRef.current.addEventListener('mousemove', onMouseEvent); canvasRef.current.addEventListener('wheel', onMouseEvent, { passive: true }); canvasRef.current.addEventListener('keydown', onKeyboardEvent); @@ -150,7 +131,7 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => return () => { if (canvasRef.current) { - canvasRef.current.removeEventListener('click', handleCanvasClick); + canvasRef.current.removeEventListener('mousedown', onMouseEvent); canvasRef.current.removeEventListener('mousemove', onMouseEvent); canvasRef.current.removeEventListener('wheel', onMouseEvent); canvasRef.current.removeEventListener('keydown', onKeyboardEvent); @@ -158,7 +139,7 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => } }; } - }, [onMouseEvent, onKeyboardEvent, handleCanvasClick, onCreateRef]); + }, [onMouseEvent, onKeyboardEvent, onCreateRef]); return ( <> From 9861f06dbbdde24f019d026adbaa707a96459f6f Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 20:07:45 +0530 Subject: [PATCH 025/255] chore: docs --- src/components/atoms/canvas.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 729e10a0..b0a65fba 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -8,7 +8,9 @@ import { Box, Button, Typography } from '@mui/material'; interface CreateRefCallback { (ref: React.RefObject): void; } - +/** + * Interface for mouse's x,y coordinates + */ interface CanvasProps { width: number; height: number; From e575e3733bd890cb0cb909f4488999ae8e4e9d28 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 20:08:40 +0530 Subject: [PATCH 026/255] feat: notify last action --- src/components/atoms/canvas.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index b0a65fba..520c6d0b 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -47,12 +47,18 @@ const ConfirmationBox = ({ selector, onYes, onNo }: { selector: string; onYes: ( const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => { const canvasRef = useRef(null); const { socket } = useSocketStore(); - const { setLastAction } = useGlobalInfoStore(); + const { setLastAction, lastAction } = useGlobalInfoStore(); const [showConfirmation, setShowConfirmation] = useState(false); const [pendingClick, setPendingClick] = useState(null); const lastMousePosition = useRef({ x: 0, y: 0 }); + const notifyLastAction = (action: string) => { + if (lastAction !== action) { + setLastAction(action); + } + }; + const onMouseEvent = useCallback((event: MouseEvent) => { if (socket && canvasRef.current) { const coordinates = getMappedCoordinates(event, canvasRef.current, width, height); From 152c5501b35677a6ac9846fe2384d71dcbd9d430 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 20:09:30 +0530 Subject: [PATCH 027/255] feat: use notify last action --- src/components/atoms/canvas.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 520c6d0b..2663d10f 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -69,7 +69,7 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => lastMousePosition.current.y !== coordinates.y) { lastMousePosition.current = coordinates; socket.emit('input:mousemove', coordinates); - setLastAction('move'); + notifyLastAction('move'); } break; case 'mousedown': @@ -85,11 +85,11 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => setShowConfirmation(true); } else { socket.emit('input:mousedown', coordinates); - setLastAction('click'); + notifyLastAction('click'); } } else { socket.emit('input:mousedown', coordinates); - setLastAction('click'); + notifyLastAction('click'); } break; case 'wheel': @@ -99,7 +99,7 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => deltaY: Math.round(wheelEvent.deltaY), }; socket.emit('input:wheel', deltas); - setLastAction('scroll'); + notifyLastAction('scroll'); break; } } @@ -110,7 +110,7 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => switch (event.type) { case 'keydown': socket.emit('input:keydown', { key: event.key, coordinates: lastMousePosition.current }); - setLastAction(`${event.key} pressed`); + notifyLastAction(`${event.key} pressed`); break; case 'keyup': socket.emit('input:keyup', event.key); @@ -122,7 +122,7 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => const handleConfirmation = (confirmed: boolean) => { if (confirmed && pendingClick && socket) { socket.emit('input:mousedown', pendingClick); - setLastAction('click'); + notifyLastAction('click'); } setShowConfirmation(false); setPendingClick(null); From 90bf664ca108a941f9b9df4488a3f660fac9becd Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 20:11:05 +0530 Subject: [PATCH 028/255] feat: use proper co-ordinates for mousemove --- src/components/atoms/canvas.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 2663d10f..e3b83f54 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -67,8 +67,14 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => case 'mousemove': if (lastMousePosition.current.x !== coordinates.x || lastMousePosition.current.y !== coordinates.y) { - lastMousePosition.current = coordinates; - socket.emit('input:mousemove', coordinates); + lastMousePosition.current = { + x: coordinates.x, + y: coordinates.y, + }; + socket.emit('input:mousemove', { + x: coordinates.x, + y: coordinates.y, + }); notifyLastAction('move'); } break; From 14c78997b5497569f68f7c0bafb03e0d02f5e5ca Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 20:22:01 +0530 Subject: [PATCH 029/255] chore: lint --- src/components/atoms/canvas.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index e3b83f54..7351621e 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -62,7 +62,7 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => const onMouseEvent = useCallback((event: MouseEvent) => { if (socket && canvasRef.current) { const coordinates = getMappedCoordinates(event, canvasRef.current, width, height); - + switch (event.type) { case 'mousemove': if (lastMousePosition.current.x !== coordinates.x || From 897397c8993049acda6c26868b5aaf7756827379 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 20:37:41 +0530 Subject: [PATCH 030/255] feat: move Highlighter to Canvas --- src/components/atoms/canvas.tsx | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 7351621e..b0b779d8 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -4,6 +4,7 @@ import { getMappedCoordinates } from "../../helpers/inputHelpers"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { GenericModal } from '../atoms/GenericModal'; import { Box, Button, Typography } from '@mui/material'; +import { Highlighter } from "../atoms/Highlighter" interface CreateRefCallback { (ref: React.RefObject): void; @@ -157,6 +158,21 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => return ( <> + + {!showConfirmation && highlighterData && canvasRef.current && ( + + )} setShowConfirmation(false)} @@ -168,12 +184,6 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => onNo={() => handleConfirmation(false)} /> - ); }; From 6815f1617b06e0ca0e35d3beaaf4eed152df125a Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 20:38:27 +0530 Subject: [PATCH 031/255] refactor: only handle highlighter state --- src/components/organisms/BrowserWindow.tsx | 30 ++-------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index c516a776..18fec604 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -2,7 +2,6 @@ import React, { useCallback, useEffect, useState } from 'react'; import { useSocketStore } from '../../context/socket'; import Canvas from "../atoms/canvas"; import { useBrowserDimensionsStore } from "../../context/browserDimensions"; -import { Highlighter } from "../atoms/Highlighter"; export const BrowserWindow = () => { const [canvasRef, setCanvasReference] = useState | undefined>(undefined); @@ -12,20 +11,6 @@ export const BrowserWindow = () => { const { socket } = useSocketStore(); const { width, height } = useBrowserDimensionsStore(); - const onMouseMove = (e: MouseEvent) => { - if (canvasRef && canvasRef.current && highlighterData) { - const canvasRect = canvasRef.current.getBoundingClientRect(); - if ( - e.pageX < canvasRect.left - || e.pageX > canvasRect.right - || e.pageY < canvasRect.top - || e.pageY > canvasRect.bottom - ) { - setHighlighterData(null); - } - } - }; - const screencastHandler = useCallback((data: string) => { setScreenShot(data); }, []); @@ -47,27 +32,16 @@ export const BrowserWindow = () => { }, []) useEffect(() => { - document.addEventListener('mousemove', onMouseMove, false); if (socket) { socket.on("highlighter", highlighterHandler); } return () => { - document.removeEventListener('mousemove', onMouseMove); socket?.off("highlighter", highlighterHandler); }; - }, [socket, onMouseMove]); + }, [socket, highlighterHandler]); return ( -
- {(highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? - - : null} +
Date: Tue, 23 Jul 2024 20:46:12 +0530 Subject: [PATCH 032/255] refactor: rm highlighter data check --- src/components/atoms/canvas.tsx | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index b0b779d8..f8c906f4 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -81,19 +81,8 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => break; case 'mousedown': if (highlighterData) { - const highlightRect = highlighterData.rect; - if ( - coordinates.x >= highlightRect.left && - coordinates.x <= highlightRect.right && - coordinates.y >= highlightRect.top && - coordinates.y <= highlightRect.bottom - ) { - setPendingClick(coordinates); - setShowConfirmation(true); - } else { - socket.emit('input:mousedown', coordinates); - notifyLastAction('click'); - } + setPendingClick(coordinates); + setShowConfirmation(true); } else { socket.emit('input:mousedown', coordinates); notifyLastAction('click'); From 23b724ecb494dc695c42d064ee669a2e4cac3b2d Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 20:46:34 +0530 Subject: [PATCH 033/255] chore: lint --- src/components/atoms/canvas.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index f8c906f4..f8f172d5 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -147,7 +147,7 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => return ( <> - Date: Tue, 23 Jul 2024 21:57:49 +0530 Subject: [PATCH 034/255] refactor: rever highlighter --- src/components/atoms/canvas.tsx | 37 +++++++++++++++++---------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index f8f172d5..7351621e 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -4,7 +4,6 @@ import { getMappedCoordinates } from "../../helpers/inputHelpers"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { GenericModal } from '../atoms/GenericModal'; import { Box, Button, Typography } from '@mui/material'; -import { Highlighter } from "../atoms/Highlighter" interface CreateRefCallback { (ref: React.RefObject): void; @@ -81,8 +80,19 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => break; case 'mousedown': if (highlighterData) { - setPendingClick(coordinates); - setShowConfirmation(true); + const highlightRect = highlighterData.rect; + if ( + coordinates.x >= highlightRect.left && + coordinates.x <= highlightRect.right && + coordinates.y >= highlightRect.top && + coordinates.y <= highlightRect.bottom + ) { + setPendingClick(coordinates); + setShowConfirmation(true); + } else { + socket.emit('input:mousedown', coordinates); + notifyLastAction('click'); + } } else { socket.emit('input:mousedown', coordinates); notifyLastAction('click'); @@ -147,21 +157,6 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => return ( <> - - {!showConfirmation && highlighterData && canvasRef.current && ( - - )} setShowConfirmation(false)} @@ -173,6 +168,12 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => onNo={() => handleConfirmation(false)} /> + ); }; From 2eb049d3190de8f8584117021eea3b805eadee4b Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 21:58:28 +0530 Subject: [PATCH 035/255] refactor: reintroduce highlighter --- src/components/organisms/BrowserWindow.tsx | 30 ++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 18fec604..c516a776 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { useSocketStore } from '../../context/socket'; import Canvas from "../atoms/canvas"; import { useBrowserDimensionsStore } from "../../context/browserDimensions"; +import { Highlighter } from "../atoms/Highlighter"; export const BrowserWindow = () => { const [canvasRef, setCanvasReference] = useState | undefined>(undefined); @@ -11,6 +12,20 @@ export const BrowserWindow = () => { const { socket } = useSocketStore(); const { width, height } = useBrowserDimensionsStore(); + const onMouseMove = (e: MouseEvent) => { + if (canvasRef && canvasRef.current && highlighterData) { + const canvasRect = canvasRef.current.getBoundingClientRect(); + if ( + e.pageX < canvasRect.left + || e.pageX > canvasRect.right + || e.pageY < canvasRect.top + || e.pageY > canvasRect.bottom + ) { + setHighlighterData(null); + } + } + }; + const screencastHandler = useCallback((data: string) => { setScreenShot(data); }, []); @@ -32,16 +47,27 @@ export const BrowserWindow = () => { }, []) useEffect(() => { + document.addEventListener('mousemove', onMouseMove, false); if (socket) { socket.on("highlighter", highlighterHandler); } return () => { + document.removeEventListener('mousemove', onMouseMove); socket?.off("highlighter", highlighterHandler); }; - }, [socket, highlighterHandler]); + }, [socket, onMouseMove]); return ( -
+
+ {(highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? + + : null} Date: Tue, 23 Jul 2024 21:59:20 +0530 Subject: [PATCH 036/255] feat: log canvas !initialized --- src/components/organisms/BrowserWindow.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index c516a776..a2ca5568 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -36,6 +36,8 @@ export const BrowserWindow = () => { } if (canvasRef?.current) { drawImage(screenShot, canvasRef.current); + } else { + console.log('Canvas is not initialized'); } return () => { socket?.off("screencast", screencastHandler); From 123666c9461660d434b5e82c279f842ba8692cca Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 21:59:57 +0530 Subject: [PATCH 037/255] feat: pass highligherData as dependency --- src/components/organisms/BrowserWindow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index a2ca5568..ba842854 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -46,7 +46,7 @@ export const BrowserWindow = () => { const highlighterHandler = useCallback((data: { rect: DOMRect, selector: string }) => { setHighlighterData(data); - }, []) + }, [highlighterData]) useEffect(() => { document.addEventListener('mousemove', onMouseMove, false); From 6e55f9e650fed266e56d677dff3f1509e0efc9ea Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 23 Jul 2024 22:28:32 +0530 Subject: [PATCH 038/255] fix: revert to !conditional mousedown emit --- src/components/atoms/canvas.tsx | 128 +++++++-------------- src/components/organisms/BrowserWindow.tsx | 87 +++++++++++++- 2 files changed, 125 insertions(+), 90 deletions(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 7351621e..9a879106 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -1,57 +1,31 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useRef } from 'react'; import { useSocketStore } from '../../context/socket'; import { getMappedCoordinates } from "../../helpers/inputHelpers"; import { useGlobalInfoStore } from "../../context/globalInfo"; -import { GenericModal } from '../atoms/GenericModal'; -import { Box, Button, Typography } from '@mui/material'; interface CreateRefCallback { (ref: React.RefObject): void; } -/** - * Interface for mouse's x,y coordinates - */ + interface CanvasProps { width: number; height: number; onCreateRef: CreateRefCallback; - highlighterData: { rect: DOMRect, selector: string } | null; } +/** + * Interface for mouse's x,y coordinates + */ export interface Coordinates { x: number; y: number; -} - -const ConfirmationBox = ({ selector, onYes, onNo }: { selector: string; onYes: () => void; onNo: () => void }) => { - return ( - - - Confirmation - - - Do you want to interact with the element: {selector}? - - - - - - - ); }; -const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => { +const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { + const canvasRef = useRef(null); const { socket } = useSocketStore(); const { setLastAction, lastAction } = useGlobalInfoStore(); - const [showConfirmation, setShowConfirmation] = useState(false); - const [pendingClick, setPendingClick] = useState(null); - - const lastMousePosition = useRef({ x: 0, y: 0 }); const notifyLastAction = (action: string) => { if (lastAction !== action) { @@ -59,12 +33,23 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => } }; - const onMouseEvent = useCallback((event: MouseEvent) => { - if (socket && canvasRef.current) { - const coordinates = getMappedCoordinates(event, canvasRef.current, width, height); + const lastMousePosition = useRef({ x: 0, y: 0 }); + //const lastWheelPosition = useRef({ deltaX: 0, deltaY: 0 }); + const onMouseEvent = useCallback((event: MouseEvent) => { + if (socket) { + const coordinates = { + x: event.clientX, + y: event.clientY, + } switch (event.type) { + case 'mousedown': + const clickCoordinates = getMappedCoordinates(event, canvasRef.current, width, height); + socket.emit('input:mousedown', clickCoordinates); + notifyLastAction('click'); + break; case 'mousemove': + const coordinates = getMappedCoordinates(event, canvasRef.current, width, height); if (lastMousePosition.current.x !== coordinates.x || lastMousePosition.current.y !== coordinates.y) { lastMousePosition.current = { @@ -78,26 +63,6 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => notifyLastAction('move'); } break; - case 'mousedown': - if (highlighterData) { - const highlightRect = highlighterData.rect; - if ( - coordinates.x >= highlightRect.left && - coordinates.x <= highlightRect.right && - coordinates.y >= highlightRect.top && - coordinates.y <= highlightRect.bottom - ) { - setPendingClick(coordinates); - setShowConfirmation(true); - } else { - socket.emit('input:mousedown', coordinates); - notifyLastAction('click'); - } - } else { - socket.emit('input:mousedown', coordinates); - notifyLastAction('click'); - } - break; case 'wheel': const wheelEvent = event as WheelEvent; const deltas = { @@ -107,9 +72,12 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => socket.emit('input:wheel', deltas); notifyLastAction('scroll'); break; + default: + console.log('Default mouseEvent registered'); + return; } } - }, [socket, width, height, setLastAction, highlighterData]); + }, [socket]); const onKeyboardEvent = useCallback((event: KeyboardEvent) => { if (socket) { @@ -121,18 +89,13 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => case 'keyup': socket.emit('input:keyup', event.key); break; + default: + console.log('Default keyEvent registered'); + return; } } - }, [socket, setLastAction]); + }, [socket]); - const handleConfirmation = (confirmed: boolean) => { - if (confirmed && pendingClick && socket) { - socket.emit('input:mousedown', pendingClick); - notifyLastAction('click'); - } - setShowConfirmation(false); - setPendingClick(null); - }; useEffect(() => { if (canvasRef.current) { @@ -151,31 +114,24 @@ const Canvas = ({ width, height, onCreateRef, highlighterData }: CanvasProps) => canvasRef.current.removeEventListener('keydown', onKeyboardEvent); canvasRef.current.removeEventListener('keyup', onKeyboardEvent); } + }; + } else { + console.log('Canvas not initialized'); } - }, [onMouseEvent, onKeyboardEvent, onCreateRef]); + + }, [onMouseEvent]); return ( - <> - setShowConfirmation(false)} - canBeClosed={false} - > - handleConfirmation(true)} - onNo={() => handleConfirmation(false)} - /> - - - + ); + }; + export default Canvas; \ No newline at end of file diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index ba842854..f63ecd38 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -3,18 +3,52 @@ import { useSocketStore } from '../../context/socket'; import Canvas from "../atoms/canvas"; import { useBrowserDimensionsStore } from "../../context/browserDimensions"; import { Highlighter } from "../atoms/Highlighter"; +import { GenericModal } from '../atoms/GenericModal'; +import { Button, Typography, Box } from '@mui/material'; + +interface ConfirmationBoxProps { + selector: string; + onYes: () => void; + onNo: () => void; +} + +// New component for the confirmation box inside the modal +const ConfirmationBox = ({ selector, onYes, onNo }: ConfirmationBoxProps) => { + return ( + + + Confirmation + + + Do you want to interact with the element: {selector}? + + + + + + + ); +}; export const BrowserWindow = () => { const [canvasRef, setCanvasReference] = useState | undefined>(undefined); const [screenShot, setScreenShot] = useState(""); const [highlighterData, setHighlighterData] = useState<{ rect: DOMRect, selector: string } | null>(null); + const [showConfirmation, setShowConfirmation] = useState(false); const { socket } = useSocketStore(); const { width, height } = useBrowserDimensionsStore(); + console.log('Use browser dimensions:', width, height) + const onMouseMove = (e: MouseEvent) => { if (canvasRef && canvasRef.current && highlighterData) { const canvasRect = canvasRef.current.getBoundingClientRect(); + // mousemove outside the browser window if ( e.pageX < canvasRect.left || e.pageX > canvasRect.right @@ -28,7 +62,7 @@ export const BrowserWindow = () => { const screencastHandler = useCallback((data: string) => { setScreenShot(data); - }, []); + }, [screenShot]); useEffect(() => { if (socket) { @@ -46,6 +80,7 @@ export const BrowserWindow = () => { const highlighterHandler = useCallback((data: { rect: DOMRect, selector: string }) => { setHighlighterData(data); + console.log('Highlighter Rect via socket:', data.rect) }, [highlighterData]) useEffect(() => { @@ -59,9 +94,48 @@ export const BrowserWindow = () => { }; }, [socket, onMouseMove]); + const handleClick = (e: React.MouseEvent) => { + if (highlighterData && canvasRef?.current) { + const canvasRect = canvasRef.current.getBoundingClientRect(); + const clickX = e.clientX - canvasRect.left; + const clickY = e.clientY - canvasRect.top; + + const highlightRect = highlighterData.rect; + if ( + clickX >= highlightRect.left && + clickX <= highlightRect.right && + clickY >= highlightRect.top && + clickY <= highlightRect.bottom + ) { + setShowConfirmation(true); + } + } + }; + + const handleConfirmation = (confirmed: boolean) => { + if (confirmed) { + console.log(`User confirmed interaction with: ${highlighterData?.selector}`); + // Here you can add logic to interact with the element + } else { + console.log('User declined interaction'); + } + setShowConfirmation(false); + }; + return ( -
- {(highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? +
+ setShowConfirmation(false)} + canBeClosed={false} + > + handleConfirmation(true)} + onNo={() => handleConfirmation(false)} + /> + + {(!showConfirmation && highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? { onCreateRef={setCanvasReference} width={width} height={height} - highlighterData={highlighterData} />
); }; const drawImage = (image: string, canvas: HTMLCanvasElement): void => { + const ctx = canvas.getContext('2d'); + const img = new Image(); + img.src = image; img.onload = () => { URL.revokeObjectURL(img.src); ctx?.drawImage(img, 0, 0, 1280, 720); + console.log('Image drawn on canvas:', img.width, img.height); + console.log('Image drawn on canvas:', canvas.width, canvas.height); }; + }; \ No newline at end of file From fb7dedbc0f127b58a8ea0ead99f069d965471f2f Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 08:33:09 +0530 Subject: [PATCH 039/255] chore: navigation reminder??? --- src/components/atoms/canvas.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 9a879106..139abc7e 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -45,6 +45,7 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { switch (event.type) { case 'mousedown': const clickCoordinates = getMappedCoordinates(event, canvasRef.current, width, height); + // needed for navigation socket.emit('input:mousedown', clickCoordinates); notifyLastAction('click'); break; From f313d5dabebcafdea6f85775c0124eaa06e92f27 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 15:54:31 +0530 Subject: [PATCH 040/255] feat: page wrapper --- src/pages/PageWrappper.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/PageWrappper.tsx b/src/pages/PageWrappper.tsx index 00e83a39..e788ad50 100644 --- a/src/pages/PageWrappper.tsx +++ b/src/pages/PageWrappper.tsx @@ -54,7 +54,7 @@ export const PageWrapper = () => { - + ) From d8f9bd98255fc5862d702799f468b8e98a4f2a20 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 17:09:50 +0530 Subject: [PATCH 041/255] chore: lint --- src/pages/PageWrappper.tsx | 42 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/pages/PageWrappper.tsx b/src/pages/PageWrappper.tsx index e788ad50..686d4cd4 100644 --- a/src/pages/PageWrappper.tsx +++ b/src/pages/PageWrappper.tsx @@ -15,7 +15,7 @@ export const PageWrapper = () => { const [recordingName, setRecordingName] = useState(''); const [open, setOpen] = useState(false); - const { browserId, setBrowserId, notification } = useGlobalInfoStore(); + const { browserId, setBrowserId, notification } = useGlobalInfoStore(); const handleNewRecording = () => { setBrowserId('new-recording'); @@ -27,15 +27,15 @@ export const PageWrapper = () => { setBrowserId('new-recording'); } - const isNotification = (): boolean=> { - if (notification.isOpen && !open){ + const isNotification = (): boolean => { + if (notification.isOpen && !open) { setOpen(true); } return notification.isOpen; } useEffect(() => { - const isRecordingInProgress = async() => { + const isRecordingInProgress = async () => { const id = await getActiveBrowserId(); if (id) { setBrowserId(id); @@ -48,26 +48,26 @@ export const PageWrapper = () => {
- - {browserId - ? ( - - - - - - - ) - : - } + + {browserId + ? ( + + + + + + + ) + : + } - { isNotification() ? + {isNotification() ? + message={notification.message} + isOpen={notification.isOpen} /> : null }
From 5636fb93a1a54b1d15f353b4222cc3b9b4f47458 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 17:13:16 +0530 Subject: [PATCH 042/255] feat: browser content --- src/components/organisms/BrowserContent.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/components/organisms/BrowserContent.tsx b/src/components/organisms/BrowserContent.tsx index 189af6ee..e72af44d 100644 --- a/src/components/organisms/BrowserContent.tsx +++ b/src/components/organisms/BrowserContent.tsx @@ -134,3 +134,14 @@ export const BrowserContent = () => { const BrowserContentWrapper = styled.div` grid-area: browser; `; + +// const BrowserContentWrapper = styled.div` +// position: fixed; +// top: 0; +// left: 0; +// width: 100%; +// height: 100%; +// overflow: hidden; /* To ensure no scrollbars appear */ +// display: flex; +// flex-direction: column; +// `; From 0b3a340725a21dc2e349279d74bf3d38220d7ff2 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 18:13:12 +0530 Subject: [PATCH 043/255] feat: show confirmation modal only if confirmation tru --- src/components/organisms/BrowserWindow.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index f63ecd38..b87afaca 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -124,7 +124,9 @@ export const BrowserWindow = () => { return (
- setShowConfirmation(false)} canBeClosed={false} @@ -135,6 +137,8 @@ export const BrowserWindow = () => { onNo={() => handleConfirmation(false)} /> + ) : null + } {(!showConfirmation && highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? Date: Wed, 24 Jul 2024 18:13:53 +0530 Subject: [PATCH 044/255] chore: lint --- src/components/organisms/BrowserWindow.tsx | 60 +++++++++++----------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index b87afaca..a606326e 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -14,24 +14,24 @@ interface ConfirmationBoxProps { // New component for the confirmation box inside the modal const ConfirmationBox = ({ selector, onYes, onNo }: ConfirmationBoxProps) => { - return ( - - - Confirmation - - - Do you want to interact with the element: {selector}? - - - - - - - ); + return ( + + + Confirmation + + + Do you want to interact with the element: {selector}? + + + + + + + ); }; export const BrowserWindow = () => { @@ -99,7 +99,7 @@ export const BrowserWindow = () => { const canvasRect = canvasRef.current.getBoundingClientRect(); const clickX = e.clientX - canvasRect.left; const clickY = e.clientY - canvasRect.top; - + const highlightRect = highlighterData.rect; if ( clickX >= highlightRect.left && @@ -126,17 +126,17 @@ export const BrowserWindow = () => {
{ showConfirmation ? ( - setShowConfirmation(false)} - canBeClosed={false} - > - handleConfirmation(true)} - onNo={() => handleConfirmation(false)} - /> - + setShowConfirmation(false)} + canBeClosed={false} + > + handleConfirmation(true)} + onNo={() => handleConfirmation(false)} + /> + ) : null } {(!showConfirmation && highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? From 8716c8fc16610d9fe9896897711f1f749ed0455a Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 19:29:52 +0530 Subject: [PATCH 045/255] feat: action context type --- src/context/browserActions.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/context/browserActions.tsx diff --git a/src/context/browserActions.tsx b/src/context/browserActions.tsx new file mode 100644 index 00000000..46d2d936 --- /dev/null +++ b/src/context/browserActions.tsx @@ -0,0 +1,10 @@ +import React, { createContext, useState, useContext, ReactNode } from 'react'; + +interface ActionContextType { + getText: boolean; + getScreenshot: boolean; + handleGetText: () => void; + handleGetScreenshot: () => void; + resetActions: () => void; +} + From 5576aa26b032c62f07f4d429ffec866269c9a9b8 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 19:30:22 +0530 Subject: [PATCH 046/255] feat: action context & hook --- src/context/browserActions.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/context/browserActions.tsx b/src/context/browserActions.tsx index 46d2d936..7afc5337 100644 --- a/src/context/browserActions.tsx +++ b/src/context/browserActions.tsx @@ -8,3 +8,13 @@ interface ActionContextType { resetActions: () => void; } +const ActionContext = createContext(undefined); + +export const useActionContext = (): ActionContextType => { + const context = useContext(ActionContext); + if (context === undefined) { + throw new Error('useActionContext must be used within an ActionProvider'); + } + return context; +}; + From 450e45736d171f33337e75f2753b7a32bedee27e Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 19:30:42 +0530 Subject: [PATCH 047/255] feat: action provider --- src/context/browserActions.tsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/context/browserActions.tsx b/src/context/browserActions.tsx index 7afc5337..3320647a 100644 --- a/src/context/browserActions.tsx +++ b/src/context/browserActions.tsx @@ -18,3 +18,21 @@ export const useActionContext = (): ActionContextType => { return context; }; +interface ActionProviderProps { + children: ReactNode; +} + +export const ActionProvider: React.FC = ({ children }) => { + const [getText, setGetText] = useState(false); + const [getScreenshot, setGetScreenshot] = useState(false); + + const handleGetText = () => setGetText(true); + const handleGetScreenshot = () => setGetScreenshot(true); + + + return ( + + {children} + + ); +}; From 07a370e05463459b8a6c8b296cf5bef93f25ffa3 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 19:30:55 +0530 Subject: [PATCH 048/255] feat: reset actio --- src/context/browserActions.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/context/browserActions.tsx b/src/context/browserActions.tsx index 3320647a..0a22c9c0 100644 --- a/src/context/browserActions.tsx +++ b/src/context/browserActions.tsx @@ -29,6 +29,11 @@ export const ActionProvider: React.FC = ({ children }) => { const handleGetText = () => setGetText(true); const handleGetScreenshot = () => setGetScreenshot(true); + // Reset actions (optional) + const resetActions = () => { + setGetText(false); + setGetScreenshot(false); + }; return ( From 09433ed4b41c1376c4ea2f5afdb6d1c52385279b Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 19:31:20 +0530 Subject: [PATCH 049/255] chore: lint --- 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 0a22c9c0..c9ac784c 100644 --- a/src/context/browserActions.tsx +++ b/src/context/browserActions.tsx @@ -40,4 +40,4 @@ export const ActionProvider: React.FC = ({ children }) => { {children} ); -}; +}; \ No newline at end of file From 53157b44627f07ba1cd1adabcb8e4e6723c66e27 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 19:47:43 +0530 Subject: [PATCH 050/255] feat: wrap w ActionProvider --- src/pages/RecordingPage.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pages/RecordingPage.tsx b/src/pages/RecordingPage.tsx index b3bcedea..91ccb49b 100644 --- a/src/pages/RecordingPage.tsx +++ b/src/pages/RecordingPage.tsx @@ -7,6 +7,7 @@ import { RightSidePanel } from "../components/organisms/RightSidePanel"; import { Loader } from "../components/atoms/Loader"; import { useSocketStore } from "../context/socket"; import { useBrowserDimensionsStore } from "../context/browserDimensions"; +import { ActionProvider } from "../context/browserActions" import { useGlobalInfoStore } from "../context/globalInfo"; import { editRecordingFromStorage } from "../api/storage"; import { WhereWhatPair } from "@wbr-project/wbr-interpret"; @@ -104,6 +105,7 @@ export const RecordingPage = ({ recordingName }: RecordingPageProps) => { }, [socket, handleLoaded]); return ( +
{isLoaded ? @@ -124,6 +126,7 @@ export const RecordingPage = ({ recordingName }: RecordingPageProps) => { : }
+
); }; From 03a92d8f0d06ff6a6d205cbde776234e186d75c6 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 19:48:02 +0530 Subject: [PATCH 051/255] chore: lint --- src/pages/RecordingPage.tsx | 38 ++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/pages/RecordingPage.tsx b/src/pages/RecordingPage.tsx index 91ccb49b..dac908ed 100644 --- a/src/pages/RecordingPage.tsx +++ b/src/pages/RecordingPage.tsx @@ -106,26 +106,26 @@ export const RecordingPage = ({ recordingName }: RecordingPageProps) => { return ( -
- {isLoaded ? - - - - - - +
+ {isLoaded ? + + + + + + + + + + - - - - - : } -
+ : } +
); }; From 2521a1a25c4ec6108e1d3ebef393cf965b4f0a63 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 20:25:22 +0530 Subject: [PATCH 052/255] feat: text & ss button --- src/components/organisms/RightSidePanel.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 2769c0f1..1e9de422 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -8,6 +8,7 @@ import { SimpleBox } from "../atoms/Box"; import Typography from "@mui/material/Typography"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { PairForEdit } from "../../pages/RecordingPage"; +import { useActionContext } from '../../context/browserActions'; interface RightSidePanelProps { pairForEdit: PairForEdit; @@ -79,6 +80,9 @@ export const RightSidePanel = ({pairForEdit}: RightSidePanelProps) => { ) : null } + + + ); }; From ccc9708cf5f6aa733971afe495f11f859353c387 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 20:26:58 +0530 Subject: [PATCH 053/255] feat: use getText & getScreenshot action context --- src/components/organisms/RightSidePanel.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 1e9de422..0531e939 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -21,6 +21,7 @@ export const RightSidePanel = ({pairForEdit}: RightSidePanelProps) => { const [isSettingsDisplayed, setIsSettingsDisplayed] = React.useState(false); const { lastAction } = useGlobalInfoStore(); + const { handleGetText, handleGetScreenshot } = useActionContext(); const handleChange = (event: React.SyntheticEvent, newValue: string) => { setContent(newValue); @@ -81,8 +82,8 @@ export const RightSidePanel = ({pairForEdit}: RightSidePanelProps) => { : null } - - + + ); }; From 0c99e7fd6189df2aa4dae702d39cb1e5c855274c Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 20:54:13 +0530 Subject: [PATCH 054/255] feat: render Highlighter if getText --- src/components/organisms/BrowserWindow.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index a606326e..3a91f4de 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -5,6 +5,7 @@ import { useBrowserDimensionsStore } from "../../context/browserDimensions"; import { Highlighter } from "../atoms/Highlighter"; import { GenericModal } from '../atoms/GenericModal'; import { Button, Typography, Box } from '@mui/material'; +import { useActionContext } from '../../context/browserActions'; interface ConfirmationBoxProps { selector: string; @@ -40,8 +41,11 @@ export const BrowserWindow = () => { const [highlighterData, setHighlighterData] = useState<{ rect: DOMRect, selector: string } | null>(null); const [showConfirmation, setShowConfirmation] = useState(false); + const { socket } = useSocketStore(); const { width, height } = useBrowserDimensionsStore(); + const { getText, getScreenshot, resetActions } = useActionContext(); + console.log('Use browser dimensions:', width, height) @@ -139,7 +143,7 @@ export const BrowserWindow = () => { ) : null } - {(!showConfirmation && highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? + {(getText && !showConfirmation && highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? Date: Wed, 24 Jul 2024 21:25:13 +0530 Subject: [PATCH 055/255] chore: lint --- src/components/organisms/RightSidePanel.tsx | 54 +++++++++++---------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 0531e939..76cdb238 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -14,7 +14,7 @@ interface RightSidePanelProps { pairForEdit: PairForEdit; } -export const RightSidePanel = ({pairForEdit}: RightSidePanelProps) => { +export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { const [content, setContent] = useState('action'); const [action, setAction] = React.useState(''); @@ -56,34 +56,38 @@ export const RightSidePanel = ({pairForEdit}: RightSidePanelProps) => { {content === 'action' ? ( - - Type of action: - - - click on coordinates - enqueueLinks - scrape - scrapeSchema - screenshot - script - scroll - - + + Type of action: + + + click on coordinates + enqueueLinks + scrape + scrapeSchema + screenshot + script + scroll + + - {isSettingsDisplayed && - - } - - ) + {isSettingsDisplayed && + + } + + ) : null } - - + + ); }; From d0442381c997ae191fd81ee63c84ced4d086d61b Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 21:34:28 +0530 Subject: [PATCH 056/255] feat: stop browser actions context --- src/context/browserActions.tsx | 56 +++++++++++++++------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/src/context/browserActions.tsx b/src/context/browserActions.tsx index c9ac784c..ca08b816 100644 --- a/src/context/browserActions.tsx +++ b/src/context/browserActions.tsx @@ -1,43 +1,37 @@ -import React, { createContext, useState, useContext, ReactNode } from 'react'; +import React, { createContext, useContext, useState, ReactNode } from 'react'; -interface ActionContextType { +interface ActionContextProps { getText: boolean; getScreenshot: boolean; - handleGetText: () => void; - handleGetScreenshot: () => void; - resetActions: () => void; + startGetText: () => void; + stopGetText: () => void; + startGetScreenshot: () => void; + stopGetScreenshot: () => void; } -const ActionContext = createContext(undefined); +const ActionContext = createContext(undefined); -export const useActionContext = (): ActionContextType => { +export const ActionProvider = ({ children }: { children: ReactNode }) => { + const [getText, setGetText] = useState(false); + const [getScreenshot, setGetScreenshot] = useState(false); + + const startGetText = () => setGetText(true); + const stopGetText = () => setGetText(false); + + const startGetScreenshot = () => setGetScreenshot(true); + const stopGetScreenshot = () => setGetScreenshot(false); + + return ( + + {children} + + ); +}; + +export const useActionContext = () => { const context = useContext(ActionContext); if (context === undefined) { throw new Error('useActionContext must be used within an ActionProvider'); } return context; }; - -interface ActionProviderProps { - children: ReactNode; -} - -export const ActionProvider: React.FC = ({ children }) => { - const [getText, setGetText] = useState(false); - const [getScreenshot, setGetScreenshot] = useState(false); - - const handleGetText = () => setGetText(true); - const handleGetScreenshot = () => setGetScreenshot(true); - - // Reset actions (optional) - const resetActions = () => { - setGetText(false); - setGetScreenshot(false); - }; - - return ( - - {children} - - ); -}; \ No newline at end of file From 223cb01d8f4eef58be20e7945180ef693506d9e0 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 21:34:58 +0530 Subject: [PATCH 057/255] chore: lint --- 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 ca08b816..50d86777 100644 --- a/src/context/browserActions.tsx +++ b/src/context/browserActions.tsx @@ -34,4 +34,4 @@ export const useActionContext = () => { throw new Error('useActionContext must be used within an ActionProvider'); } return context; -}; +}; \ No newline at end of file From ea2ead1749249f65817939c86cf2d889f78fa077 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 21:35:41 +0530 Subject: [PATCH 058/255] fix: remove resetAction() --- src/components/organisms/BrowserWindow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 3a91f4de..6c0488a9 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -44,7 +44,7 @@ export const BrowserWindow = () => { const { socket } = useSocketStore(); const { width, height } = useBrowserDimensionsStore(); - const { getText, getScreenshot, resetActions } = useActionContext(); + const { getText, getScreenshot } = useActionContext(); console.log('Use browser dimensions:', width, height) From 1ce7e8841741384146e7aa29cbc98e8ac9933b69 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 21:37:26 +0530 Subject: [PATCH 059/255] feat: start/stop bw actions --- src/components/organisms/RightSidePanel.tsx | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 76cdb238..124f76cb 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -21,7 +21,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { const [isSettingsDisplayed, setIsSettingsDisplayed] = React.useState(false); const { lastAction } = useGlobalInfoStore(); - const { handleGetText, handleGetScreenshot } = useActionContext(); + const { getText, getScreenshot, startGetText, stopGetText, startGetScreenshot, stopGetScreenshot } = useActionContext(); const handleChange = (event: React.SyntheticEvent, newValue: string) => { setContent(newValue); @@ -39,6 +39,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { } }, [pairForEdit]) + return ( { : null } - - + + + + ); }; From ef8411208b2786783167f5fbf2144a978de9a1aa Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 21:37:48 +0530 Subject: [PATCH 060/255] chore: lint --- src/components/organisms/RightSidePanel.tsx | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 124f76cb..c4f234a2 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -83,18 +83,18 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { : null } - - - - + + + + ); }; From 6c11f5afee5f486957738477f94e0fe6fc89882a Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 23:28:15 +0530 Subject: [PATCH 061/255] chore: remove unwanted code --- src/components/organisms/BrowserWindow.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 6c0488a9..8191b05f 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -13,7 +13,6 @@ interface ConfirmationBoxProps { onNo: () => void; } -// New component for the confirmation box inside the modal const ConfirmationBox = ({ selector, onYes, onNo }: ConfirmationBoxProps) => { return ( From 034cdd635deb4edabac0318c83c3a5129bb2629f Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 23:28:48 +0530 Subject: [PATCH 062/255] chore: lint --- src/components/organisms/BrowserWindow.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 8191b05f..948c4f01 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -40,11 +40,9 @@ export const BrowserWindow = () => { const [highlighterData, setHighlighterData] = useState<{ rect: DOMRect, selector: string } | null>(null); const [showConfirmation, setShowConfirmation] = useState(false); - const { socket } = useSocketStore(); const { width, height } = useBrowserDimensionsStore(); - const { getText, getScreenshot } = useActionContext(); - + const { getText, getScreenshot } = useActionContext(); console.log('Use browser dimensions:', width, height) From df0d1eec3d7be7c3d92b61b102ce1e783a7bc5f3 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 24 Jul 2024 23:29:56 +0530 Subject: [PATCH 063/255] chore: remove unwanted code --- src/components/organisms/BrowserWindow.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 948c4f01..34359936 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -44,8 +44,6 @@ export const BrowserWindow = () => { const { width, height } = useBrowserDimensionsStore(); const { getText, getScreenshot } = useActionContext(); - console.log('Use browser dimensions:', width, height) - const onMouseMove = (e: MouseEvent) => { if (canvasRef && canvasRef.current && highlighterData) { const canvasRect = canvasRef.current.getBoundingClientRect(); @@ -81,7 +79,6 @@ export const BrowserWindow = () => { const highlighterHandler = useCallback((data: { rect: DOMRect, selector: string }) => { setHighlighterData(data); - console.log('Highlighter Rect via socket:', data.rect) }, [highlighterData]) useEffect(() => { @@ -116,7 +113,6 @@ export const BrowserWindow = () => { const handleConfirmation = (confirmed: boolean) => { if (confirmed) { console.log(`User confirmed interaction with: ${highlighterData?.selector}`); - // Here you can add logic to interact with the element } else { console.log('User declined interaction'); } @@ -168,8 +164,6 @@ const drawImage = (image: string, canvas: HTMLCanvasElement): void => { img.onload = () => { URL.revokeObjectURL(img.src); ctx?.drawImage(img, 0, 0, 1280, 720); - console.log('Image drawn on canvas:', img.width, img.height); - console.log('Image drawn on canvas:', canvas.width, canvas.height); }; }; \ No newline at end of file From 1c3eb50ef552216e4eb47cb15391064c3f9344fa Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 25 Jul 2024 00:10:42 +0530 Subject: [PATCH 064/255] fix: remove BrowserContentWrapper --- src/components/organisms/BrowserContent.tsx | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/components/organisms/BrowserContent.tsx b/src/components/organisms/BrowserContent.tsx index e72af44d..3b489968 100644 --- a/src/components/organisms/BrowserContent.tsx +++ b/src/components/organisms/BrowserContent.tsx @@ -133,15 +133,4 @@ export const BrowserContent = () => { const BrowserContentWrapper = styled.div` grid-area: browser; -`; - -// const BrowserContentWrapper = styled.div` -// position: fixed; -// top: 0; -// left: 0; -// width: 100%; -// height: 100%; -// overflow: hidden; /* To ensure no scrollbars appear */ -// display: flex; -// flex-direction: column; -// `; +`; \ No newline at end of file From 1374f2395016ddbd1f9c99a2ef31f5cd78de5051 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 25 Jul 2024 00:53:07 +0530 Subject: [PATCH 065/255] feat: show modal if getText --- src/components/organisms/BrowserWindow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 34359936..e402dbfc 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -122,7 +122,7 @@ export const BrowserWindow = () => { return (
{ - showConfirmation ? ( + getText && showConfirmation ? ( setShowConfirmation(false)} From bc332e55180d6cf8dacd295defab2730ae25999a Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 25 Jul 2024 00:58:08 +0530 Subject: [PATCH 066/255] feat: conditionally render buttons --- src/components/organisms/RightSidePanel.tsx | 33 +++++++++++++-------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index c4f234a2..f466c0b2 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -83,18 +83,27 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { : null } - - - - + {!getText && ( + + )} + {getText && ( + + )} + + {!getScreenshot && ( + + )} + {getScreenshot && ( + + )} ); }; From f0c285eabca819ded9780189b4ee25c21404a717 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 25 Jul 2024 01:01:26 +0530 Subject: [PATCH 067/255] feat: text or ss --- src/components/organisms/RightSidePanel.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index f466c0b2..5befe5e1 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -83,7 +83,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { : null } - {!getText && ( + {!getText && !getScreenshot && ( @@ -94,7 +94,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { )} - {!getScreenshot && ( + {!getText && !getScreenshot && ( From 4101b4f2e2d2ea52111f378f6d7609613a4fd397 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 25 Jul 2024 01:56:31 +0530 Subject: [PATCH 068/255] feat: vertical align --- src/components/organisms/RightSidePanel.tsx | 44 +++++++++++---------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 5befe5e1..68679a42 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { Button, MenuItem, Paper, Stack, Tabs, Tab } from "@mui/material"; +import { Button, MenuItem, Paper, Stack, Tabs, Tab, Box } from "@mui/material"; import { Dropdown as MuiDropdown } from '../atoms/DropdownMui'; import styled from "styled-components"; import { ActionSettings } from "../molecules/ActionSettings"; @@ -83,27 +83,29 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { : null } - {!getText && !getScreenshot && ( - - )} - {getText && ( - - )} + + {!getText && !getScreenshot && ( + + )} + {getText && ( + + )} - {!getText && !getScreenshot && ( - - )} - {getScreenshot && ( - - )} + {!getText && !getScreenshot && ( + + )} + {getScreenshot && ( + + )} + ); }; From 1572db6076d0fac087432b7dae7ac1b7b847f170 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 25 Jul 2024 01:56:45 +0530 Subject: [PATCH 069/255] chore: lint --- src/components/organisms/RightSidePanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 68679a42..3c766520 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -83,7 +83,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { : null } - + {!getText && !getScreenshot && ( + + - ); -}; - -const ConfirmationBoxContainer = styled.div` - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background: white; - border: 1px solid #ccc; - padding: 20px; - box-shadow: 0 4px 8px rgba(0,0,0,0.1); - z-index: 2147483648; /* Ensure it's above the highlighter */ - text-align: center; -`; - -export default ConfirmationBox; +}; \ No newline at end of file From d586ef85ac8eb6689ce8529f5f9ac3234c4d8658 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 25 Jul 2024 22:49:51 +0530 Subject: [PATCH 076/255] chore: lint --- src/components/atoms/ConfirmationBox.tsx | 30 ++++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/components/atoms/ConfirmationBox.tsx b/src/components/atoms/ConfirmationBox.tsx index b317c191..a1c6e869 100644 --- a/src/components/atoms/ConfirmationBox.tsx +++ b/src/components/atoms/ConfirmationBox.tsx @@ -10,21 +10,21 @@ interface ConfirmationBoxProps { export const ConfirmationBox = ({ selector, onYes, onNo }: ConfirmationBoxProps) => { return ( - - - Confirmation - - - Do you want to interact with the element: {selector}? - - - - - + + + Confirmation + + + Do you want to interact with the element: {selector}? + + + + + ); }; \ No newline at end of file From 43912840aba21a16a309dde6b4b6da4a400a79d0 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 25 Jul 2024 22:50:07 +0530 Subject: [PATCH 077/255] chore: -rm styled import --- src/components/atoms/ConfirmationBox.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/atoms/ConfirmationBox.tsx b/src/components/atoms/ConfirmationBox.tsx index a1c6e869..b3eb10c2 100644 --- a/src/components/atoms/ConfirmationBox.tsx +++ b/src/components/atoms/ConfirmationBox.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import styled from 'styled-components'; import { Box, Button, IconButton, Stack, Typography } from "@mui/material"; interface ConfirmationBoxProps { From e53cf242a38128d81c6f7acaf50cdd03bb121e8d Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 25 Jul 2024 22:50:45 +0530 Subject: [PATCH 078/255] feat: import confirmation box --- src/components/organisms/BrowserWindow.tsx | 27 +--------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index e402dbfc..08abce9e 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -6,33 +6,8 @@ import { Highlighter } from "../atoms/Highlighter"; import { GenericModal } from '../atoms/GenericModal'; import { Button, Typography, Box } from '@mui/material'; import { useActionContext } from '../../context/browserActions'; +import { ConfirmationBox } from "../atoms/ConfirmationBox"; -interface ConfirmationBoxProps { - selector: string; - onYes: () => void; - onNo: () => void; -} - -const ConfirmationBox = ({ selector, onYes, onNo }: ConfirmationBoxProps) => { - return ( - - - Confirmation - - - Do you want to interact with the element: {selector}? - - - - - - - ); -}; export const BrowserWindow = () => { const [canvasRef, setCanvasReference] = useState | undefined>(undefined); From 8f44f1174d40e127c65f991929f9af5490eef7c3 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 25 Jul 2024 22:51:12 +0530 Subject: [PATCH 079/255] chore: -rm whitespace --- src/components/organisms/BrowserWindow.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 08abce9e..13c15185 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -8,7 +8,6 @@ import { Button, Typography, Box } from '@mui/material'; import { useActionContext } from '../../context/browserActions'; import { ConfirmationBox } from "../atoms/ConfirmationBox"; - export const BrowserWindow = () => { const [canvasRef, setCanvasReference] = useState | undefined>(undefined); const [screenShot, setScreenShot] = useState(""); From 6ce28cb8b76902006173192728748a8a9a8214c5 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 25 Jul 2024 22:51:30 +0530 Subject: [PATCH 080/255] chore: -rm MUI imports --- src/components/organisms/BrowserWindow.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 13c15185..2cc394d6 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -4,7 +4,6 @@ import Canvas from "../atoms/canvas"; import { useBrowserDimensionsStore } from "../../context/browserDimensions"; import { Highlighter } from "../atoms/Highlighter"; import { GenericModal } from '../atoms/GenericModal'; -import { Button, Typography, Box } from '@mui/material'; import { useActionContext } from '../../context/browserActions'; import { ConfirmationBox } from "../atoms/ConfirmationBox"; From 0956a1df2ad2ff6254e9c3ae4ed7344296235179 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 02:23:32 +0530 Subject: [PATCH 081/255] feat: add check for getText === true --- src/components/organisms/BrowserWindow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 2cc394d6..f7eb3ead 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -109,7 +109,7 @@ export const BrowserWindow = () => { ) : null } - {(getText && !showConfirmation && highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? + {(getText === true && !showConfirmation && highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? Date: Fri, 26 Jul 2024 02:23:52 +0530 Subject: [PATCH 082/255] feat: add check for getText === true --- src/components/organisms/BrowserWindow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index f7eb3ead..3abde59d 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -95,7 +95,7 @@ export const BrowserWindow = () => { return (
{ - getText && showConfirmation ? ( + getText === true && showConfirmation ? ( setShowConfirmation(false)} From d788b0942884ff9f0bc7f2ec31ad6e998736cf43 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 03:42:33 +0530 Subject: [PATCH 083/255] fix: -rm getText as dep. array --- src/components/atoms/canvas.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 2e262ba1..4e9afaa3 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -36,6 +36,7 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { }; const lastMousePosition = useRef({ x: 0, y: 0 }); + //const lastWheelPosition = useRef({ deltaX: 0, deltaY: 0 }); const onMouseEvent = useCallback((event: MouseEvent) => { if (socket) { @@ -51,7 +52,6 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { } else { socket.emit('input:mousedown', clickCoordinates); } - notifyLastAction('click'); break; case 'mousemove': const coordinates = getMappedCoordinates(event, canvasRef.current, width, height); @@ -82,7 +82,7 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { return; } } - }, [socket, getText]); + }, [socket]); const onKeyboardEvent = useCallback((event: KeyboardEvent) => { if (socket) { From 58345600b022b84f07f0314f9f872440989dbea5 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 03:43:26 +0530 Subject: [PATCH 084/255] fix: -rm getText logic from mousedown --- src/components/atoms/canvas.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 4e9afaa3..8e07ad21 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -47,11 +47,8 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { switch (event.type) { case 'mousedown': const clickCoordinates = getMappedCoordinates(event, canvasRef.current, width, height); - if (getText === true) { - console.log('get text') - } else { socket.emit('input:mousedown', clickCoordinates); - } + notifyLastAction('click'); break; case 'mousemove': const coordinates = getMappedCoordinates(event, canvasRef.current, width, height); From f6d2b1dc31f1dab6e3a63ae9f4d6f497cdcc03a4 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 03:44:58 +0530 Subject: [PATCH 085/255] feat: handleMouseEventForGetText --- src/components/atoms/canvas.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 8e07ad21..7642db6b 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -38,6 +38,18 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { const lastMousePosition = useRef({ x: 0, y: 0 }); //const lastWheelPosition = useRef({ deltaX: 0, deltaY: 0 }); + const handleMouseEventForGetText = useCallback((event: MouseEvent) => { + if (socket && getText) { + switch (event.type) { + case 'mousedown': + console.log('Handling text selection logic'); + break; + default: + return; + } + } + }, [socket, getText]); + const onMouseEvent = useCallback((event: MouseEvent) => { if (socket) { const coordinates = { From e184f24145a9fa028a469e50d357cfa44e9938bb Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 03:46:07 +0530 Subject: [PATCH 086/255] feat: create combined mouse event handler --- src/components/atoms/canvas.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 7642db6b..53edb75c 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -114,6 +114,12 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { useEffect(() => { if (canvasRef.current) { onCreateRef(canvasRef); + + const combinedMouseEventHandler = (event: MouseEvent) => { + onMouseEvent(event); + handleMouseEventForGetText(event); + }; + canvasRef.current.addEventListener('mousedown', onMouseEvent); canvasRef.current.addEventListener('mousemove', onMouseEvent); canvasRef.current.addEventListener('wheel', onMouseEvent, { passive: true }); From bec8a6efb07dbc71c9a0d245fee763e05553af23 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 03:46:44 +0530 Subject: [PATCH 087/255] feat: use combined mouse event handler --- src/components/atoms/canvas.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 53edb75c..effe24c7 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -120,7 +120,7 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { handleMouseEventForGetText(event); }; - canvasRef.current.addEventListener('mousedown', onMouseEvent); + canvasRef.current.addEventListener('mousedown', combinedMouseEventHandler); canvasRef.current.addEventListener('mousemove', onMouseEvent); canvasRef.current.addEventListener('wheel', onMouseEvent, { passive: true }); canvasRef.current.addEventListener('keydown', onKeyboardEvent); @@ -128,7 +128,7 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { return () => { if (canvasRef.current) { - canvasRef.current.removeEventListener('mousedown', onMouseEvent); + canvasRef.current.removeEventListener('mousedown', combinedMouseEventHandler); canvasRef.current.removeEventListener('mousemove', onMouseEvent); canvasRef.current.removeEventListener('wheel', onMouseEvent); canvasRef.current.removeEventListener('keydown', onKeyboardEvent); From c6bcec9ce16c17ab7b2c358ea873eb4ecde0887b Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 04:09:41 +0530 Subject: [PATCH 088/255] fix: revert separate event handler mousedown --- src/components/atoms/canvas.tsx | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index effe24c7..c6509c50 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -38,18 +38,6 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { const lastMousePosition = useRef({ x: 0, y: 0 }); //const lastWheelPosition = useRef({ deltaX: 0, deltaY: 0 }); - const handleMouseEventForGetText = useCallback((event: MouseEvent) => { - if (socket && getText) { - switch (event.type) { - case 'mousedown': - console.log('Handling text selection logic'); - break; - default: - return; - } - } - }, [socket, getText]); - const onMouseEvent = useCallback((event: MouseEvent) => { if (socket) { const coordinates = { @@ -59,8 +47,11 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { switch (event.type) { case 'mousedown': const clickCoordinates = getMappedCoordinates(event, canvasRef.current, width, height); + if (getText === true) { + console.log('get text') + } else { socket.emit('input:mousedown', clickCoordinates); - notifyLastAction('click'); + } break; case 'mousemove': const coordinates = getMappedCoordinates(event, canvasRef.current, width, height); @@ -91,7 +82,7 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { return; } } - }, [socket]); + }, [socket, getText]); const onKeyboardEvent = useCallback((event: KeyboardEvent) => { if (socket) { @@ -114,13 +105,7 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { useEffect(() => { if (canvasRef.current) { onCreateRef(canvasRef); - - const combinedMouseEventHandler = (event: MouseEvent) => { - onMouseEvent(event); - handleMouseEventForGetText(event); - }; - - canvasRef.current.addEventListener('mousedown', combinedMouseEventHandler); + canvasRef.current.addEventListener('mousedown', onMouseEvent); canvasRef.current.addEventListener('mousemove', onMouseEvent); canvasRef.current.addEventListener('wheel', onMouseEvent, { passive: true }); canvasRef.current.addEventListener('keydown', onKeyboardEvent); @@ -128,7 +113,7 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { return () => { if (canvasRef.current) { - canvasRef.current.removeEventListener('mousedown', combinedMouseEventHandler); + canvasRef.current.removeEventListener('mousedown', onMouseEvent); canvasRef.current.removeEventListener('mousemove', onMouseEvent); canvasRef.current.removeEventListener('wheel', onMouseEvent); canvasRef.current.removeEventListener('keydown', onKeyboardEvent); From 99e92797b8f9d10c5e8967e7883422af99f804b9 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 04:19:19 +0530 Subject: [PATCH 089/255] feat: use ref for getText (it works finally!) --- src/components/atoms/canvas.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index c6509c50..2cfbf043 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -28,6 +28,7 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { const { socket } = useSocketStore(); const { setLastAction, lastAction } = useGlobalInfoStore(); const { getText, getScreenshot } = useActionContext(); + const getTextRef = useRef(getText); const notifyLastAction = (action: string) => { if (lastAction !== action) { @@ -38,6 +39,10 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { const lastMousePosition = useRef({ x: 0, y: 0 }); //const lastWheelPosition = useRef({ deltaX: 0, deltaY: 0 }); + useEffect(() => { + getTextRef.current = getText; + }, [getText]); + const onMouseEvent = useCallback((event: MouseEvent) => { if (socket) { const coordinates = { @@ -47,7 +52,7 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { switch (event.type) { case 'mousedown': const clickCoordinates = getMappedCoordinates(event, canvasRef.current, width, height); - if (getText === true) { + if (getTextRef.current === true) { console.log('get text') } else { socket.emit('input:mousedown', clickCoordinates); @@ -82,7 +87,7 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { return; } } - }, [socket, getText]); + }, [socket]); const onKeyboardEvent = useCallback((event: KeyboardEvent) => { if (socket) { From cdf6ce42e5cca25bcdfedd1af8f9b469613b70af Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 04:19:43 +0530 Subject: [PATCH 090/255] chore: cleanup --- src/components/atoms/canvas.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 2cfbf043..444d6b0b 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -37,7 +37,6 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { }; const lastMousePosition = useRef({ x: 0, y: 0 }); - //const lastWheelPosition = useRef({ deltaX: 0, deltaY: 0 }); useEffect(() => { getTextRef.current = getText; From 3d6fac9ea7ad442a4a7c35649d97930c8b2b1969 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 04:26:07 +0530 Subject: [PATCH 091/255] feat: notify click action --- src/components/atoms/canvas.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/atoms/canvas.tsx b/src/components/atoms/canvas.tsx index 444d6b0b..f6e6fb1c 100644 --- a/src/components/atoms/canvas.tsx +++ b/src/components/atoms/canvas.tsx @@ -56,6 +56,7 @@ const Canvas = ({ width, height, onCreateRef }: CanvasProps) => { } else { socket.emit('input:mousedown', clickCoordinates); } + notifyLastAction('click'); break; case 'mousemove': const coordinates = getMappedCoordinates(event, canvasRef.current, width, height); From db98639f67083c490e3d45bd6d15327385f5c97d Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 09:29:14 +0530 Subject: [PATCH 092/255] feat(wip): browser steps --- src/components/organisms/RightSidePanel.tsx | 58 +++++++++++++++++---- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 3c766520..b4fe8795 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { Button, MenuItem, Paper, Stack, Tabs, Tab, Box } from "@mui/material"; +import { Button, MenuItem, Paper, Box, TextField } from "@mui/material"; import { Dropdown as MuiDropdown } from '../atoms/DropdownMui'; import styled from "styled-components"; import { ActionSettings } from "../molecules/ActionSettings"; @@ -14,11 +14,18 @@ interface RightSidePanelProps { pairForEdit: PairForEdit; } -export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { +interface BrowserStep { + id: number; + label: string; + description: string; +} +export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { const [content, setContent] = useState('action'); - const [action, setAction] = React.useState(''); - const [isSettingsDisplayed, setIsSettingsDisplayed] = React.useState(false); + const [action, setAction] = useState(''); + const [isSettingsDisplayed, setIsSettingsDisplayed] = useState(false); + const [browserSteps, setBrowserSteps] = useState([]); + const [stepLabel, setStepLabel] = useState(''); const { lastAction } = useGlobalInfoStore(); const { getText, getScreenshot, startGetText, stopGetText, startGetScreenshot, stopGetScreenshot } = useActionContext(); @@ -37,8 +44,21 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { if (content !== 'detail' && pairForEdit.pair !== null) { setContent('detail'); } - }, [pairForEdit]) + }, [pairForEdit]); + const addBrowserStep = () => { + setBrowserSteps([...browserSteps, { id: Date.now(), label: stepLabel, description: 'Description of the step' }]); + setStepLabel(''); + }; + + const confirmStep = (id: number) => { + console.log(`Step with ID ${id} confirmed.`); + // Implement your logic here + }; + + const discardStep = (id: number) => { + setBrowserSteps(browserSteps.filter(step => step.id !== id)); + }; return ( { } - ) - : null - } + ) : null} {!getText && !getScreenshot && ( @@ -106,6 +124,28 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { )} + + + setStepLabel(e.target.value)} + /> + + + + + {browserSteps.map(step => ( + + {step.label} + {step.description} + + + + ))} + ); }; @@ -120,4 +160,4 @@ const ActionTypeWrapper = styled.div` export const ActionDescription = styled.p` margin-left: 15px; -`; +`; \ No newline at end of file From 3c91a49ecb664c667248987a0f3498d0f1dcdf0a Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 18:09:13 +0530 Subject: [PATCH 093/255] feat(wip): browser steps --- src/components/organisms/RightSidePanel.tsx | 49 +++++++++++---------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index b4fe8795..27dffaea 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import { Button, MenuItem, Paper, Box, TextField } from "@mui/material"; import { Dropdown as MuiDropdown } from '../atoms/DropdownMui'; import styled from "styled-components"; @@ -26,6 +26,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { const [isSettingsDisplayed, setIsSettingsDisplayed] = useState(false); const [browserSteps, setBrowserSteps] = useState([]); const [stepLabel, setStepLabel] = useState(''); + const [stepDescription, setStepDescription] = useState(''); const { lastAction } = useGlobalInfoStore(); const { getText, getScreenshot, startGetText, stopGetText, startGetScreenshot, stopGetScreenshot } = useActionContext(); @@ -40,24 +41,18 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { setIsSettingsDisplayed(true); }; - useEffect(() => { - if (content !== 'detail' && pairForEdit.pair !== null) { - setContent('detail'); - } - }, [pairForEdit]); - - const addBrowserStep = () => { - setBrowserSteps([...browserSteps, { id: Date.now(), label: stepLabel, description: 'Description of the step' }]); + const confirmStep = () => { + setBrowserSteps([ + ...browserSteps, + { id: Date.now(), label: stepLabel, description: stepDescription } + ]); setStepLabel(''); + setStepDescription(''); }; - const confirmStep = (id: number) => { - console.log(`Step with ID ${id} confirmed.`); - // Implement your logic here - }; - - const discardStep = (id: number) => { - setBrowserSteps(browserSteps.filter(step => step.id !== id)); + const discardStep = () => { + setStepLabel(''); + setStepDescription(''); }; return ( @@ -127,22 +122,30 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { setStepLabel(e.target.value)} /> - + setStepDescription(e.target.value)} + /> + + + + {browserSteps.map(step => ( - {step.label} + {step.label} {step.description} - - ))} From 826ec272364fcd483803a2538ef9c8e6cc451707 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 20:20:11 +0530 Subject: [PATCH 094/255] feat: browser steps context --- src/context/browserSteps.tsx | 53 ++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/context/browserSteps.tsx diff --git a/src/context/browserSteps.tsx b/src/context/browserSteps.tsx new file mode 100644 index 00000000..c1606c0d --- /dev/null +++ b/src/context/browserSteps.tsx @@ -0,0 +1,53 @@ +import React, { createContext, useContext, useState } from 'react'; + +interface BrowserStep { + id: number; + label: string; + description: string; +} + +interface BrowserStepsContextType { + browserSteps: BrowserStep[]; + addBrowserStep: (label: string, description: string) => void; + deleteBrowserStep: (id: number) => void; + updateBrowserStepLabel: (id: number, newLabel: string) => void; +} + +const BrowserStepsContext = createContext(undefined); + +export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const [browserSteps, setBrowserSteps] = useState([]); + + const addBrowserStep = (label: string, description: string) => { + setBrowserSteps(prevSteps => [ + ...prevSteps, + { id: Date.now(), label, description } + ]); + }; + + const deleteBrowserStep = (id: number) => { + setBrowserSteps(prevSteps => prevSteps.filter(step => step.id !== id)); + }; + + const updateBrowserStepLabel = (id: number, newLabel: string) => { + setBrowserSteps(prevSteps => + prevSteps.map(step => + step.id === id ? { ...step, label: newLabel } : step + ) + ); + }; + + return ( + + {children} + + ); +}; + +export const useBrowserSteps = () => { + const context = useContext(BrowserStepsContext); + if (!context) { + throw new Error('useBrowserSteps must be used within a BrowserStepsProvider'); + } + return context; +}; From 24df2c73e3b02c51890d9ebc73fe0d46139bfa5f Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 20:21:56 +0530 Subject: [PATCH 095/255] refactor: rename description to value (just felt makes more sense?) --- src/context/browserSteps.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/context/browserSteps.tsx b/src/context/browserSteps.tsx index c1606c0d..cd414dae 100644 --- a/src/context/browserSteps.tsx +++ b/src/context/browserSteps.tsx @@ -3,12 +3,12 @@ import React, { createContext, useContext, useState } from 'react'; interface BrowserStep { id: number; label: string; - description: string; + value: string; } interface BrowserStepsContextType { browserSteps: BrowserStep[]; - addBrowserStep: (label: string, description: string) => void; + addBrowserStep: (label: string, value: string) => void; deleteBrowserStep: (id: number) => void; updateBrowserStepLabel: (id: number, newLabel: string) => void; } @@ -18,10 +18,10 @@ const BrowserStepsContext = createContext(u export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [browserSteps, setBrowserSteps] = useState([]); - const addBrowserStep = (label: string, description: string) => { + const addBrowserStep = (label: string, value: string) => { setBrowserSteps(prevSteps => [ ...prevSteps, - { id: Date.now(), label, description } + { id: Date.now(), label, value } ]); }; From 37359a75e6848cc043fa938ac1b1a22749c26d02 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 20:22:20 +0530 Subject: [PATCH 096/255] chore: lint --- src/context/browserSteps.tsx | 68 ++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/context/browserSteps.tsx b/src/context/browserSteps.tsx index cd414dae..26a6b8c0 100644 --- a/src/context/browserSteps.tsx +++ b/src/context/browserSteps.tsx @@ -1,53 +1,53 @@ import React, { createContext, useContext, useState } from 'react'; interface BrowserStep { - id: number; - label: string; - value: string; + id: number; + label: string; + value: string; } interface BrowserStepsContextType { - browserSteps: BrowserStep[]; - addBrowserStep: (label: string, value: string) => void; - deleteBrowserStep: (id: number) => void; - updateBrowserStepLabel: (id: number, newLabel: string) => void; + browserSteps: BrowserStep[]; + addBrowserStep: (label: string, value: string) => void; + deleteBrowserStep: (id: number) => void; + updateBrowserStepLabel: (id: number, newLabel: string) => void; } const BrowserStepsContext = createContext(undefined); export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { - const [browserSteps, setBrowserSteps] = useState([]); + const [browserSteps, setBrowserSteps] = useState([]); - const addBrowserStep = (label: string, value: string) => { - setBrowserSteps(prevSteps => [ - ...prevSteps, - { id: Date.now(), label, value } - ]); - }; + const addBrowserStep = (label: string, value: string) => { + setBrowserSteps(prevSteps => [ + ...prevSteps, + { id: Date.now(), label, value } + ]); + }; - const deleteBrowserStep = (id: number) => { - setBrowserSteps(prevSteps => prevSteps.filter(step => step.id !== id)); - }; + const deleteBrowserStep = (id: number) => { + setBrowserSteps(prevSteps => prevSteps.filter(step => step.id !== id)); + }; - const updateBrowserStepLabel = (id: number, newLabel: string) => { - setBrowserSteps(prevSteps => - prevSteps.map(step => - step.id === id ? { ...step, label: newLabel } : step - ) + const updateBrowserStepLabel = (id: number, newLabel: string) => { + setBrowserSteps(prevSteps => + prevSteps.map(step => + step.id === id ? { ...step, label: newLabel } : step + ) + ); + }; + + return ( + + {children} + ); - }; - - return ( - - {children} - - ); }; export const useBrowserSteps = () => { - const context = useContext(BrowserStepsContext); - if (!context) { - throw new Error('useBrowserSteps must be used within a BrowserStepsProvider'); - } - return context; + const context = useContext(BrowserStepsContext); + if (!context) { + throw new Error('useBrowserSteps must be used within a BrowserStepsProvider'); + } + return context; }; From a4e65b08b949b1b195c882ae8fe74452599ffb5b Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 20:25:23 +0530 Subject: [PATCH 097/255] feat: wrap w BrowserStepsProvider --- src/pages/RecordingPage.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pages/RecordingPage.tsx b/src/pages/RecordingPage.tsx index dac908ed..8bfeb11d 100644 --- a/src/pages/RecordingPage.tsx +++ b/src/pages/RecordingPage.tsx @@ -8,6 +8,7 @@ import { Loader } from "../components/atoms/Loader"; import { useSocketStore } from "../context/socket"; import { useBrowserDimensionsStore } from "../context/browserDimensions"; import { ActionProvider } from "../context/browserActions" +import { BrowserStepsProvider } from '../context/browserSteps'; import { useGlobalInfoStore } from "../context/globalInfo"; import { editRecordingFromStorage } from "../api/storage"; import { WhereWhatPair } from "@wbr-project/wbr-interpret"; @@ -106,6 +107,7 @@ export const RecordingPage = ({ recordingName }: RecordingPageProps) => { return ( +
{isLoaded ? @@ -126,6 +128,7 @@ export const RecordingPage = ({ recordingName }: RecordingPageProps) => { : }
+
); }; From be1500b7695d57e85f07182ca39561acbb141cd1 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 20:26:06 +0530 Subject: [PATCH 098/255] chore: lint --- src/pages/RecordingPage.tsx | 38 ++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/pages/RecordingPage.tsx b/src/pages/RecordingPage.tsx index 8bfeb11d..26b12ede 100644 --- a/src/pages/RecordingPage.tsx +++ b/src/pages/RecordingPage.tsx @@ -108,26 +108,26 @@ export const RecordingPage = ({ recordingName }: RecordingPageProps) => { return ( -
- {isLoaded ? - - - +
+ {isLoaded ? + + + + + + + + + + - - - - - - - - : } -
+ : } +
); From 1f15857fefc6907c980171f0945c39ff43491ca8 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 22:53:02 +0530 Subject: [PATCH 099/255] feat(wip): addBrowserSteps for highlighter --- src/components/organisms/BrowserWindow.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 3abde59d..0fff4db9 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -5,6 +5,7 @@ import { useBrowserDimensionsStore } from "../../context/browserDimensions"; import { Highlighter } from "../atoms/Highlighter"; import { GenericModal } from '../atoms/GenericModal'; import { useActionContext } from '../../context/browserActions'; +import { useBrowserSteps } from '../../context/browserSteps'; import { ConfirmationBox } from "../atoms/ConfirmationBox"; export const BrowserWindow = () => { @@ -16,6 +17,7 @@ export const BrowserWindow = () => { const { socket } = useSocketStore(); const { width, height } = useBrowserDimensionsStore(); const { getText, getScreenshot } = useActionContext(); + const { addBrowserStep } = useBrowserSteps(); const onMouseMove = (e: MouseEvent) => { if (canvasRef && canvasRef.current && highlighterData) { @@ -78,7 +80,7 @@ export const BrowserWindow = () => { clickY >= highlightRect.top && clickY <= highlightRect.bottom ) { - setShowConfirmation(true); + addBrowserStep('Label 1', highlighterData.selector); } } }; From 6f2fb330cf9680b4716b056aee9f3d91450e6d31 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 22:53:36 +0530 Subject: [PATCH 100/255] feat(wip): use browser steps context --- src/components/organisms/RightSidePanel.tsx | 62 +++++++-------------- 1 file changed, 21 insertions(+), 41 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 27dffaea..d73c0eae 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -9,6 +9,7 @@ import Typography from "@mui/material/Typography"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { PairForEdit } from "../../pages/RecordingPage"; import { useActionContext } from '../../context/browserActions'; +import { useBrowserSteps } from '../../context/browserSteps'; interface RightSidePanelProps { pairForEdit: PairForEdit; @@ -24,12 +25,12 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { const [content, setContent] = useState('action'); const [action, setAction] = useState(''); const [isSettingsDisplayed, setIsSettingsDisplayed] = useState(false); - const [browserSteps, setBrowserSteps] = useState([]); const [stepLabel, setStepLabel] = useState(''); const [stepDescription, setStepDescription] = useState(''); const { lastAction } = useGlobalInfoStore(); const { getText, getScreenshot, startGetText, stopGetText, startGetScreenshot, stopGetScreenshot } = useActionContext(); + const { browserSteps } = useBrowserSteps(); const handleChange = (event: React.SyntheticEvent, newValue: string) => { setContent(newValue); @@ -41,19 +42,19 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { setIsSettingsDisplayed(true); }; - const confirmStep = () => { - setBrowserSteps([ - ...browserSteps, - { id: Date.now(), label: stepLabel, description: stepDescription } - ]); - setStepLabel(''); - setStepDescription(''); - }; + // const confirmStep = () => { + // setBrowserSteps([ + // ...browserSteps, + // { id: Date.now(), label: stepLabel, description: stepDescription } + // ]); + // setStepLabel(''); + // setStepDescription(''); + // }; - const discardStep = () => { - setStepLabel(''); - setStepDescription(''); - }; + // const discardStep = () => { + // setStepLabel(''); + // setStepDescription(''); + // }; return ( { )} - - setStepLabel(e.target.value)} - /> - setStepDescription(e.target.value)} - /> - - - + + {browserSteps.map(step => ( + + {step.label} + {step.value} + + ))} - - - - {browserSteps.map(step => ( - - {step.label} - {step.description} - - ))} - ); }; From ecd27f99471fd16c32f4ea8b0bdc9045bf24f7aa Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 22:56:02 +0530 Subject: [PATCH 101/255] chore: lint --- src/components/organisms/RightSidePanel.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index d73c0eae..c1e02675 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -122,13 +122,13 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { - {browserSteps.map(step => ( - - {step.label} - {step.value} - - ))} - + {browserSteps.map(step => ( + + {step.label} + {step.value} + + ))} + ); }; From 66de868e3f47e04af97d9b4b6d16fb7aa2bf8ef9 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 22:57:49 +0530 Subject: [PATCH 102/255] fix: change label --- src/components/organisms/BrowserWindow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 0fff4db9..a9f4877f 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -80,7 +80,7 @@ export const BrowserWindow = () => { clickY >= highlightRect.top && clickY <= highlightRect.bottom ) { - addBrowserStep('Label 1', highlighterData.selector); + addBrowserStep('Enter Label Here...', highlighterData.selector); } } }; From 80a502980493cda8760e0232963970ab8f2f86d3 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 23:07:17 +0530 Subject: [PATCH 103/255] fix: empty label for add browser step --- src/components/organisms/BrowserWindow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index a9f4877f..9f671689 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -80,7 +80,7 @@ export const BrowserWindow = () => { clickY >= highlightRect.top && clickY <= highlightRect.bottom ) { - addBrowserStep('Enter Label Here...', highlighterData.selector); + addBrowserStep('', highlighterData.selector); } } }; From fe9b695b8bf3e818407dca9cfe749f1b2547f11c Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 23:07:54 +0530 Subject: [PATCH 104/255] feat: user input for label --- src/components/organisms/RightSidePanel.tsx | 47 +++++++++++++++++---- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index c1e02675..a74f6fce 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -25,12 +25,13 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { const [content, setContent] = useState('action'); const [action, setAction] = useState(''); const [isSettingsDisplayed, setIsSettingsDisplayed] = useState(false); - const [stepLabel, setStepLabel] = useState(''); + const [labels, setLabels] = useState<{ [id: number]: string }>({}); const [stepDescription, setStepDescription] = useState(''); + const { lastAction } = useGlobalInfoStore(); const { getText, getScreenshot, startGetText, stopGetText, startGetScreenshot, stopGetScreenshot } = useActionContext(); - const { browserSteps } = useBrowserSteps(); + const { browserSteps, updateBrowserStepLabel, deleteBrowserStep } = useBrowserSteps(); const handleChange = (event: React.SyntheticEvent, newValue: string) => { setContent(newValue); @@ -56,6 +57,20 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { // setStepDescription(''); // }; + const handleLabelChange = (id: number, label: string) => { + setLabels(prevLabels => ({ ...prevLabels, [id]: label })); +}; + +const handleConfirm = (id: number) => { + if (labels[id]) { + updateBrowserStepLabel(id, labels[id]); + } +}; + +const handleDiscard = (id: number) => { + deleteBrowserStep(id); +}; + return ( { - {browserSteps.map(step => ( - - {step.label} - {step.value} - - ))} - + {browserSteps.map(step => ( + + handleLabelChange(step.id, e.target.value)} + fullWidth + margin="normal" + /> + Description: {step.value} + + + + + + ))} + ); }; From 03a23d8839fd1a34c9de1ef9ff76af5ae0e5b031 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 23:15:03 +0530 Subject: [PATCH 105/255] chore: remove unused state --- src/components/organisms/RightSidePanel.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index a74f6fce..df74c306 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -26,7 +26,6 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { const [action, setAction] = useState(''); const [isSettingsDisplayed, setIsSettingsDisplayed] = useState(false); const [labels, setLabels] = useState<{ [id: number]: string }>({}); - const [stepDescription, setStepDescription] = useState(''); const { lastAction } = useGlobalInfoStore(); From 605eeb5d76a87b6b328a1291915261d85a716c6b Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 23:15:34 +0530 Subject: [PATCH 106/255] chore: remove unused code --- src/components/organisms/RightSidePanel.tsx | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index df74c306..7020b0b0 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -42,20 +42,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { setIsSettingsDisplayed(true); }; - // const confirmStep = () => { - // setBrowserSteps([ - // ...browserSteps, - // { id: Date.now(), label: stepLabel, description: stepDescription } - // ]); - // setStepLabel(''); - // setStepDescription(''); - // }; - - // const discardStep = () => { - // setStepLabel(''); - // setStepDescription(''); - // }; - + const handleLabelChange = (id: number, label: string) => { setLabels(prevLabels => ({ ...prevLabels, [id]: label })); }; From 6d2553465d9bf4f550ed5a4740cd1f7183dced0e Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 23:24:41 +0530 Subject: [PATCH 107/255] feat: handle user input --- src/components/organisms/RightSidePanel.tsx | 52 ++++++++++++++++----- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 7020b0b0..67a9429f 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -26,6 +26,8 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { const [action, setAction] = useState(''); const [isSettingsDisplayed, setIsSettingsDisplayed] = useState(false); const [labels, setLabels] = useState<{ [id: number]: string }>({}); + const [errors, setErrors] = useState<{ [id: number]: string }>({}); + const [confirmedSteps, setConfirmedSteps] = useState<{ [id: number]: boolean }>({}); const { lastAction } = useGlobalInfoStore(); @@ -42,19 +44,36 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { setIsSettingsDisplayed(true); }; - + const handleLabelChange = (id: number, label: string) => { setLabels(prevLabels => ({ ...prevLabels, [id]: label })); + if (!label.trim()) { + setErrors(prevErrors => ({ ...prevErrors, [id]: 'Label cannot be empty' })); + } else { + setErrors(prevErrors => ({ ...prevErrors, [id]: '' })); + } }; const handleConfirm = (id: number) => { - if (labels[id]) { - updateBrowserStepLabel(id, labels[id]); + const label = labels[id]?.trim(); + if (label) { + updateBrowserStepLabel(id, label); + setConfirmedSteps(prev => ({ ...prev, [id]: true })); + } else { + setErrors(prevErrors => ({ ...prevErrors, [id]: 'Label cannot be empty' })); } }; const handleDiscard = (id: number) => { deleteBrowserStep(id); + setLabels(prevLabels => { + const { [id]: _, ...rest } = prevLabels; + return rest; + }); + setErrors(prevErrors => { + const { [id]: _, ...rest } = prevErrors; + return rest; + }); }; return ( @@ -131,16 +150,25 @@ const handleDiscard = (id: number) => { onChange={(e) => handleLabelChange(step.id, e.target.value)} fullWidth margin="normal" + error={!!errors[step.id]} + helperText={errors[step.id]} + disabled={confirmedSteps[step.id]} /> - Description: {step.value} - - - - + Description: {step.value} + {!confirmedSteps[step.id] && ( + + + + + )} ))} From 8e70b4f01954a21e06713c0deb211a6faa229d41 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 23:25:03 +0530 Subject: [PATCH 108/255] chore: lint --- src/components/organisms/RightSidePanel.tsx | 92 ++++++++++----------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 67a9429f..3e4d5d28 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -27,8 +27,8 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { const [isSettingsDisplayed, setIsSettingsDisplayed] = useState(false); const [labels, setLabels] = useState<{ [id: number]: string }>({}); const [errors, setErrors] = useState<{ [id: number]: string }>({}); - const [confirmedSteps, setConfirmedSteps] = useState<{ [id: number]: boolean }>({}); - + const [confirmedSteps, setConfirmedSteps] = useState<{ [id: number]: boolean }>({}); + const { lastAction } = useGlobalInfoStore(); const { getText, getScreenshot, startGetText, stopGetText, startGetScreenshot, stopGetScreenshot } = useActionContext(); @@ -48,33 +48,33 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { const handleLabelChange = (id: number, label: string) => { setLabels(prevLabels => ({ ...prevLabels, [id]: label })); if (!label.trim()) { - setErrors(prevErrors => ({ ...prevErrors, [id]: 'Label cannot be empty' })); + setErrors(prevErrors => ({ ...prevErrors, [id]: 'Label cannot be empty' })); } else { - setErrors(prevErrors => ({ ...prevErrors, [id]: '' })); + setErrors(prevErrors => ({ ...prevErrors, [id]: '' })); } -}; + }; -const handleConfirm = (id: number) => { + const handleConfirm = (id: number) => { const label = labels[id]?.trim(); if (label) { - updateBrowserStepLabel(id, label); - setConfirmedSteps(prev => ({ ...prev, [id]: true })); + updateBrowserStepLabel(id, label); + setConfirmedSteps(prev => ({ ...prev, [id]: true })); } else { - setErrors(prevErrors => ({ ...prevErrors, [id]: 'Label cannot be empty' })); + setErrors(prevErrors => ({ ...prevErrors, [id]: 'Label cannot be empty' })); } -}; + }; -const handleDiscard = (id: number) => { + const handleDiscard = (id: number) => { deleteBrowserStep(id); setLabels(prevLabels => { - const { [id]: _, ...rest } = prevLabels; - return rest; + const { [id]: _, ...rest } = prevLabels; + return rest; }); setErrors(prevErrors => { - const { [id]: _, ...rest } = prevErrors; - return rest; + const { [id]: _, ...rest } = prevErrors; + return rest; }); -}; + }; return ( { - {browserSteps.map(step => ( - - handleLabelChange(step.id, e.target.value)} - fullWidth - margin="normal" - error={!!errors[step.id]} - helperText={errors[step.id]} - disabled={confirmedSteps[step.id]} - /> - Description: {step.value} - {!confirmedSteps[step.id] && ( - - - - - )} - - ))} - + {browserSteps.map(step => ( + + handleLabelChange(step.id, e.target.value)} + fullWidth + margin="normal" + error={!!errors[step.id]} + helperText={errors[step.id]} + disabled={confirmedSteps[step.id]} + /> + Description: {step.value} + {!confirmedSteps[step.id] && ( + + + + + )} + + ))} + ); }; From 48c87d60a83044174071e1dd489eeeb9e2b5b8a0 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 26 Jul 2024 23:38:22 +0530 Subject: [PATCH 109/255] fix: remove description --- src/components/organisms/RightSidePanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 3e4d5d28..ea9802a6 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -154,7 +154,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { helperText={errors[step.id]} disabled={confirmedSteps[step.id]} /> - Description: {step.value} + {step.value} {!confirmedSteps[step.id] && ( - )} - {getText && ( - - )} - - {!getText && !getScreenshot && ( - - )} - {getScreenshot && ( - - )} + {!getText && !getScreenshot && } + {getText && } + {!getText && !getScreenshot && } + {getScreenshot && } {browserSteps.map(step => ( - + { margin="normal" error={!!errors[step.id]} helperText={errors[step.id]} - InputProps={{ - readOnly: confirmedSteps[step.id] - }} + InputProps={{ readOnly: confirmedSteps[step.id] }} /> {!confirmedSteps[step.id] && ( - - + + )} @@ -201,4 +164,4 @@ const ActionTypeWrapper = styled.div` export const ActionDescription = styled.p` margin-left: 15px; -`; \ No newline at end of file +`; From 020b5f4d0a6469236742e361a9f6e1443cc73cf4 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 04:47:38 +0530 Subject: [PATCH 134/255] fix: set action to scrapeSchema --- src/components/organisms/RightSidePanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index e0a6167d..167bc068 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -86,7 +86,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { const stopCaptureAndEmitSettings = useCallback(() => { stopGetText(); const settings = createSettingsObject(); - socket?.emit('action', { action: 'settings', settings }); + socket?.emit('action', { action: 'scrapeSchema', settings }); }, [stopGetText, createSettingsObject, socket]); return ( From f608ef58959b2bf2edcc85a8f0c4079c05f14200 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 04:53:28 +0530 Subject: [PATCH 135/255] feat: accept selector prop --- src/context/browserSteps.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/context/browserSteps.tsx b/src/context/browserSteps.tsx index 26a6b8c0..8f4ed959 100644 --- a/src/context/browserSteps.tsx +++ b/src/context/browserSteps.tsx @@ -4,11 +4,12 @@ interface BrowserStep { id: number; label: string; value: string; + selector: string; } interface BrowserStepsContextType { browserSteps: BrowserStep[]; - addBrowserStep: (label: string, value: string) => void; + addBrowserStep: (label: string, value: string, selector: string) => void; deleteBrowserStep: (id: number) => void; updateBrowserStepLabel: (id: number, newLabel: string) => void; } @@ -18,10 +19,10 @@ const BrowserStepsContext = createContext(u export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [browserSteps, setBrowserSteps] = useState([]); - const addBrowserStep = (label: string, value: string) => { + const addBrowserStep = (label: string, value: string, selector: string) => { setBrowserSteps(prevSteps => [ ...prevSteps, - { id: Date.now(), label, value } + { id: Date.now(), label, value, selector } ]); }; From 1872c3cc502366d449edb1c0cc1ccc3a94cc572a Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 04:55:09 +0530 Subject: [PATCH 136/255] feat: pass highlighter selector for selector prop --- src/components/organisms/BrowserWindow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 8ea3a977..34f80d3d 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -89,7 +89,7 @@ export const BrowserWindow = () => { clickY >= highlightRect.top && clickY <= highlightRect.bottom ) { - addBrowserStep('', highlighterData.elementInfo?.innerText || ''); + addBrowserStep('', highlighterData.elementInfo?.innerText || '', highlighterData.selector); } } }; From 25042d4eeb642d68df3c4b630736cd3d3fa48892 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 05:01:22 +0530 Subject: [PATCH 137/255] feat: use selector instead of value for key-value pair --- src/components/organisms/RightSidePanel.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 167bc068..8df10b46 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -74,8 +74,8 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { const createSettingsObject = useCallback(() => { const settings: Record = {}; browserSteps.forEach(step => { - if (step.label && step.value) { - settings[step.label] = step.value; + if (step.label && step.selector) { + settings[step.label] = step.selector; } }); console.log(`settings from getText:`, settings); From a0c781a89d72c600ee0b48c2d11c9710c9050c1a Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 05:01:42 +0530 Subject: [PATCH 138/255] chore: remove comments --- src/components/organisms/RightSidePanel.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 8df10b46..cb7644e5 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -70,7 +70,6 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { }); }; - // Create settings object when stopping text capture const createSettingsObject = useCallback(() => { const settings: Record = {}; browserSteps.forEach(step => { From 0a95080412f84e934631258389efd56585f6d9a9 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 05:02:07 +0530 Subject: [PATCH 139/255] chore: -rm unused code --- src/components/organisms/RightSidePanel.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index cb7644e5..7321f50e 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -77,11 +77,9 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { settings[step.label] = step.selector; } }); - console.log(`settings from getText:`, settings); return settings; }, [browserSteps]); - // Stop text capture and emit settings object const stopCaptureAndEmitSettings = useCallback(() => { stopGetText(); const settings = createSettingsObject(); From 2335226495a30bc626e8a6fb8507ef67a9ded18e Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 05:12:28 +0530 Subject: [PATCH 140/255] feat: -rm scrape action --- src/components/molecules/ActionSettings.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/molecules/ActionSettings.tsx b/src/components/molecules/ActionSettings.tsx index af366838..4a994d2b 100644 --- a/src/components/molecules/ActionSettings.tsx +++ b/src/components/molecules/ActionSettings.tsx @@ -20,8 +20,6 @@ export const ActionSettings = ({ action }: ActionSettingsProps) => { return ; case 'scroll': return ; - case 'scrape': - return ; case 'scrapeSchema': return ; default: From f6cf2591e3661581ecfd50845336fd5edec11f21 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 05:35:59 +0530 Subject: [PATCH 141/255] feat(core): preprocessor schema & workflow validation --- mx-interpreter/preprocessor.ts | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 mx-interpreter/preprocessor.ts diff --git a/mx-interpreter/preprocessor.ts b/mx-interpreter/preprocessor.ts new file mode 100644 index 00000000..95fae90c --- /dev/null +++ b/mx-interpreter/preprocessor.ts @@ -0,0 +1,50 @@ +import Joi from 'joi'; +import { + Workflow, WorkflowFile, ParamType, SelectorArray, Where, +} from './types/workflow'; +import { operators } from './types/logic'; + +/** +* Class for static processing the workflow files/objects. +*/ +export default class Preprocessor { + static validateWorkflow(workflow: WorkflowFile) : any { + const regex = Joi.object({ + $regex: Joi.string().required(), + }); + + const whereSchema = Joi.object({ + url: [Joi.string().uri(), regex], + selectors: Joi.array().items(Joi.string()), + cookies: Joi.object({}).pattern(Joi.string(), Joi.string()), + $after: [Joi.string(), regex], + $before: [Joi.string(), regex], + $and: Joi.array().items(Joi.link('#whereSchema')), + $or: Joi.array().items(Joi.link('#whereSchema')), + $not: Joi.link('#whereSchema'), + }).id('whereSchema'); + + const schema = Joi.object({ + meta: Joi.object({ + name: Joi.string(), + desc: Joi.string(), + }), + workflow: Joi.array().items( + Joi.object({ + id: Joi.string(), + where: whereSchema.required(), + what: Joi.array().items({ + action: Joi.string().required(), + args: Joi.array().items(Joi.any()), + }).required(), + }), + ).required(), + }); + + const { error } = schema.validate(workflow); + + return error; + } + + +} \ No newline at end of file From 29b365b7143cd5eeb588285e6fe46a851775dce2 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 05:36:34 +0530 Subject: [PATCH 142/255] feat(core): extract params from workflow --- mx-interpreter/preprocessor.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/mx-interpreter/preprocessor.ts b/mx-interpreter/preprocessor.ts index 95fae90c..7905c41a 100644 --- a/mx-interpreter/preprocessor.ts +++ b/mx-interpreter/preprocessor.ts @@ -46,5 +46,28 @@ export default class Preprocessor { return error; } + /** +* Extracts parameter names from the workflow. +* @param {WorkflowFile} workflow The given workflow +* @returns {String[]} List of parameters' names. +*/ + static getParams(workflow: WorkflowFile) : string[] { + const getParamsRecurse = (object : any) : string[] => { + if (typeof object === 'object') { + // Recursion base case + if (object.$param) { + return [object.$param]; + } + + // Recursion general case + return Object.values(object) + .reduce((p: string[], v : any) : string[] => [...p, ...getParamsRecurse(v)], []); + } + return []; + }; + + return getParamsRecurse(workflow.workflow); + } + } \ No newline at end of file From 13d042b5c913de1703b5893c6ce3def884f939ba Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 05:37:09 +0530 Subject: [PATCH 143/255] feat(core): extract selectors --- mx-interpreter/preprocessor.ts | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/mx-interpreter/preprocessor.ts b/mx-interpreter/preprocessor.ts index 7905c41a..80d77326 100644 --- a/mx-interpreter/preprocessor.ts +++ b/mx-interpreter/preprocessor.ts @@ -69,5 +69,43 @@ export default class Preprocessor { return getParamsRecurse(workflow.workflow); } + /** +* List all the selectors used in the given workflow (only literal "selector" +* field in WHERE clauses so far) +*/ + // TODO : add recursive selector search (also in click/fill etc. events?) + static extractSelectors(workflow: Workflow) : SelectorArray { + /** +* Given a Where condition, this function extracts +* all the existing selectors from it (recursively). +*/ + const selectorsFromCondition = (where: Where) : SelectorArray => { + // the `selectors` field is either on the top level + let out = where.selectors ?? []; + if (!Array.isArray(out)) { + out = [out]; + } + + // or nested in the "operator" array + operators.forEach((op) => { + let condWhere = where[op]; + if (condWhere) { + condWhere = Array.isArray(condWhere) ? condWhere : [condWhere]; + (condWhere).forEach((subWhere) => { + out = [...out, ...selectorsFromCondition(subWhere)]; + }); + } + }); + + return out; + }; + + // Iterate through all the steps and extract the selectors from all of them. + return workflow.reduce((p: SelectorArray, step) => [ + ...p, + ...selectorsFromCondition(step.where).filter((x) => !p.includes(x)), + ], []); + } + } \ No newline at end of file From 67c4bbaf3e8d3f1e0a40e29b0cc3b46b2fb48484 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 05:37:37 +0530 Subject: [PATCH 144/255] feat(core): workflow initialization --- mx-interpreter/preprocessor.ts | 70 +++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/mx-interpreter/preprocessor.ts b/mx-interpreter/preprocessor.ts index 80d77326..dab0076e 100644 --- a/mx-interpreter/preprocessor.ts +++ b/mx-interpreter/preprocessor.ts @@ -107,5 +107,73 @@ export default class Preprocessor { ], []); } - + /** +* Recursively crawl `object` and initializes params - replaces the `{$param : paramName}` objects +* with the defined value. +* @returns {Workflow} Copy of the given workflow, modified (the initial workflow is left untouched). +*/ + static initWorkflow(workflow: Workflow, params?: ParamType) : Workflow { + const paramNames = this.getParams({ workflow }); + + if (Object.keys(params ?? {}).sort().join(',') !== paramNames.sort().join(',')) { + throw new Error(`Provided parameters do not match the workflow parameters + provided: ${Object.keys(params ?? {}).sort().join(',')}, + expected: ${paramNames.sort().join(',')} + `); + } + /** + * A recursive method for initializing special `{key: value}` syntax objects in the workflow. + * @param object Workflow to initialize (or a part of it). + * @param k key to look for ($regex, $param) + * @param f function mutating the special `{}` syntax into + * its true representation (RegExp...) + * @returns Updated object + */ + const initSpecialRecurse = ( + object: unknown, + k: string, + f: (value: string) => unknown, + ) : unknown => { + if (!object || typeof object !== 'object') { + return object; + } + + const out = object; + // for every key (child) of the object + Object.keys(object!).forEach((key) => { + // if the field has only one key, which is `k` + if (Object.keys((object)[key]).length === 1 && (object)[key][k]) { + // process the current special tag (init param, hydrate regex...) + (out)[key] = f((object)[key][k]); + } else { + initSpecialRecurse((object)[key], k, f); + } + }); + return out; + }; + + // TODO: do better deep copy, this is hideous. + let workflowCopy = JSON.parse(JSON.stringify(workflow)); + + if (params) { + workflowCopy = initSpecialRecurse( + workflowCopy, + '$param', + (paramName) => { + if (params && params[paramName]) { + return params[paramName]; + } + throw new SyntaxError(`Unspecified parameter found ${paramName}.`); + }, + ); + } + + workflowCopy = initSpecialRecurse( + workflowCopy, + '$regex', + (regex) => new RegExp(regex), + ); + + return workflowCopy; + } } \ No newline at end of file From 014f6e6bece76aec43d4a6dca7581929f6a1b7bc Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 05:37:57 +0530 Subject: [PATCH 145/255] chore(core): lint --- mx-interpreter/preprocessor.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/mx-interpreter/preprocessor.ts b/mx-interpreter/preprocessor.ts index dab0076e..e7227a9f 100644 --- a/mx-interpreter/preprocessor.ts +++ b/mx-interpreter/preprocessor.ts @@ -8,7 +8,7 @@ import { operators } from './types/logic'; * Class for static processing the workflow files/objects. */ export default class Preprocessor { - static validateWorkflow(workflow: WorkflowFile) : any { + static validateWorkflow(workflow: WorkflowFile): any { const regex = Joi.object({ $regex: Joi.string().required(), }); @@ -51,8 +51,8 @@ export default class Preprocessor { * @param {WorkflowFile} workflow The given workflow * @returns {String[]} List of parameters' names. */ - static getParams(workflow: WorkflowFile) : string[] { - const getParamsRecurse = (object : any) : string[] => { + static getParams(workflow: WorkflowFile): string[] { + const getParamsRecurse = (object: any): string[] => { if (typeof object === 'object') { // Recursion base case if (object.$param) { @@ -61,7 +61,7 @@ export default class Preprocessor { // Recursion general case return Object.values(object) - .reduce((p: string[], v : any) : string[] => [...p, ...getParamsRecurse(v)], []); + .reduce((p: string[], v: any): string[] => [...p, ...getParamsRecurse(v)], []); } return []; }; @@ -74,12 +74,12 @@ export default class Preprocessor { * field in WHERE clauses so far) */ // TODO : add recursive selector search (also in click/fill etc. events?) - static extractSelectors(workflow: Workflow) : SelectorArray { + static extractSelectors(workflow: Workflow): SelectorArray { /** * Given a Where condition, this function extracts * all the existing selectors from it (recursively). */ - const selectorsFromCondition = (where: Where) : SelectorArray => { + const selectorsFromCondition = (where: Where): SelectorArray => { // the `selectors` field is either on the top level let out = where.selectors ?? []; if (!Array.isArray(out)) { @@ -112,7 +112,7 @@ export default class Preprocessor { * with the defined value. * @returns {Workflow} Copy of the given workflow, modified (the initial workflow is left untouched). */ - static initWorkflow(workflow: Workflow, params?: ParamType) : Workflow { + static initWorkflow(workflow: Workflow, params?: ParamType): Workflow { const paramNames = this.getParams({ workflow }); if (Object.keys(params ?? {}).sort().join(',') !== paramNames.sort().join(',')) { @@ -133,7 +133,7 @@ export default class Preprocessor { object: unknown, k: string, f: (value: string) => unknown, - ) : unknown => { + ): unknown => { if (!object || typeof object !== 'object') { return object; } @@ -174,6 +174,6 @@ export default class Preprocessor { (regex) => new RegExp(regex), ); - return workflowCopy; + return workflowCopy; } } \ No newline at end of file From be539e25248dfd0e7daadde811a9d339d37e862c Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 05:38:31 +0530 Subject: [PATCH 146/255] fix(core): format --- mx-interpreter/preprocessor.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mx-interpreter/preprocessor.ts b/mx-interpreter/preprocessor.ts index e7227a9f..9ad15c2a 100644 --- a/mx-interpreter/preprocessor.ts +++ b/mx-interpreter/preprocessor.ts @@ -46,7 +46,7 @@ export default class Preprocessor { return error; } - /** +/** * Extracts parameter names from the workflow. * @param {WorkflowFile} workflow The given workflow * @returns {String[]} List of parameters' names. @@ -69,7 +69,7 @@ export default class Preprocessor { return getParamsRecurse(workflow.workflow); } - /** +/** * List all the selectors used in the given workflow (only literal "selector" * field in WHERE clauses so far) */ @@ -107,7 +107,7 @@ export default class Preprocessor { ], []); } - /** +/** * Recursively crawl `object` and initializes params - replaces the `{$param : paramName}` objects * with the defined value. * @returns {Workflow} Copy of the given workflow, modified (the initial workflow is left untouched). From c7b9cbd0badad7d3ca18591db6bd23a3ebd1dddb Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 05:39:13 +0530 Subject: [PATCH 147/255] feat(core): interpreter options --- mx-interpreter/interpret.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 mx-interpreter/interpret.ts diff --git a/mx-interpreter/interpret.ts b/mx-interpreter/interpret.ts new file mode 100644 index 00000000..fc9cc9d1 --- /dev/null +++ b/mx-interpreter/interpret.ts @@ -0,0 +1,31 @@ +/* eslint-disable no-await-in-loop, no-restricted-syntax */ +import { Page, PageScreenshotOptions } from 'playwright'; +import path from 'path'; + +import { EventEmitter } from 'events'; +import { + Where, What, PageState, Workflow, WorkflowFile, + ParamType, SelectorArray, CustomFunctions, +} from './types/workflow'; + +import { operators, meta } from './types/logic'; +import { arrayToObject } from './utils/utils'; +import Concurrency from './utils/concurrency'; +import Preprocessor from './preprocessor'; +import log, { Level } from './utils/logger'; + +/** + * Defines optional intepreter options (passed in constructor) + */ +interface InterpreterOptions { + maxRepeats: number; + maxConcurrency: number; + serializableCallback: (output: any) => (void | Promise); + binaryCallback: (output: any, mimeType: string) => (void | Promise); + debug: boolean; + debugChannel: Partial<{ + activeId: Function, + debugMessage: Function, + }> +} + From 71d1b66399a3f3469945bd55d5d811a48d3c1504 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 05:39:55 +0530 Subject: [PATCH 148/255] feat(core): init interpreter class + constructor --- mx-interpreter/interpret.ts | 51 +++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/mx-interpreter/interpret.ts b/mx-interpreter/interpret.ts index fc9cc9d1..55581085 100644 --- a/mx-interpreter/interpret.ts +++ b/mx-interpreter/interpret.ts @@ -29,3 +29,54 @@ interface InterpreterOptions { }> } +/** + * Class for running the Smart Workflows. + */ +export default class Interpreter extends EventEmitter { + private workflow: Workflow; + + private initializedWorkflow: Workflow | null; + + private options: InterpreterOptions; + + private concurrency : Concurrency; + + private stopper : Function | null = null; + + private log : typeof log; + + constructor(workflow: WorkflowFile, options?: Partial) { + super(); + this.workflow = workflow.workflow; + this.initializedWorkflow = null; + this.options = { + maxRepeats: 5, + maxConcurrency: 5, + serializableCallback: (data) => { log(JSON.stringify(data), Level.WARN); }, + binaryCallback: () => { log('Received binary data, thrashing them.', Level.WARN); }, + debug: false, + debugChannel: {}, + ...options, + }; + this.concurrency = new Concurrency(this.options.maxConcurrency); + this.log = (...args) => log(...args); + + const error = Preprocessor.validateWorkflow(workflow); + if (error) { + throw (error); + } + + if (this.options.debugChannel?.debugMessage) { + const oldLog = this.log; + // @ts-ignore + this.log = (...args: Parameters) => { + if (args[1] !== Level.LOG) { + this.options.debugChannel.debugMessage!(typeof args[0] === 'string' ? args[0] : args[0].message); + } + oldLog(...args); + }; + } + } + + +} \ No newline at end of file From 4349d18041d2aa0b703570da7e8f18e3fe4f2d2f Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 05:41:03 +0530 Subject: [PATCH 149/255] feat(core): context obj from page & current workflow --- mx-interpreter/interpret.ts | 58 +++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/mx-interpreter/interpret.ts b/mx-interpreter/interpret.ts index 55581085..96501625 100644 --- a/mx-interpreter/interpret.ts +++ b/mx-interpreter/interpret.ts @@ -78,5 +78,63 @@ export default class Interpreter extends EventEmitter { } } + /** + * Returns the context object from given Page and the current workflow.\ + * \ + * `workflow` is used for selector extraction - function searches for used selectors to + * look for later in the page's context. + * @param page Playwright Page object + * @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 { + /** + * All the selectors present in the current Workflow + */ + const selectors = Preprocessor.extractSelectors(workflow); + + /** + * 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 }), + ]; + + 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()); + + return { + url: page.url(), + cookies: (await page.context().cookies([page.url()])) + .reduce((p, cookie) => ( + { + ...p, + [cookie.name]: cookie.value, + }), {}), + selectors: presentSelectors, + }; + } + } \ No newline at end of file From 5efbac7c6f381eb4fda2348cb923eeb94725f438 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 05:41:49 +0530 Subject: [PATCH 150/255] feat(core): action application for given context --- mx-interpreter/interpret.ts | 89 +++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/mx-interpreter/interpret.ts b/mx-interpreter/interpret.ts index 96501625..99dd7505 100644 --- a/mx-interpreter/interpret.ts +++ b/mx-interpreter/interpret.ts @@ -136,5 +136,94 @@ export default class Interpreter extends EventEmitter { }; } + /** + * Tests if the given action is applicable with the given context. + * @param where Tested *where* condition + * @param context Current browser context. + * @returns True if `where` is applicable in the given context, false otherwise + */ + private applicable(where: Where, context: PageState, usedActions : string[] = []) : boolean { + /** + * Given two arbitrary objects, determines whether `subset` is a subset of `superset`.\ + * \ + * For every key in `subset`, there must be a corresponding key with equal scalar + * value in `superset`, or `inclusive(subset[key], superset[key])` must hold. + * @param subset Arbitrary non-cyclic JS object (where clause) + * @param superset Arbitrary non-cyclic JS object (browser context) + * @returns `true` if `subset <= superset`, `false` otherwise. + */ + const inclusive = (subset: Record, superset: Record) + : boolean => ( + Object.entries(subset).every( + ([key, value]) => { + /** + * Arrays are compared without order (are transformed into objects before comparison). + */ + const parsedValue = Array.isArray(value) ? arrayToObject(value) : value; + + const parsedSuperset : Record = {}; + parsedSuperset[key] = Array.isArray(superset[key]) + ? arrayToObject(superset[key]) + : superset[key]; + + // Every `subset` key must exist in the `superset` and + // have the same value (strict equality), or subset[key] <= superset[key] + return parsedSuperset[key] + && ( + (parsedSuperset[key] === parsedValue) + || ((parsedValue).constructor.name === 'RegExp' && (parsedValue).test(parsedSuperset[key])) + || ( + (parsedValue).constructor.name !== 'RegExp' + && typeof parsedValue === 'object' && inclusive(parsedValue, parsedSuperset[key]) + ) + ); + }, + ) + ); + + // Every value in the "where" object should be compliant to the current state. + return Object.entries(where).every( + ([key, value]) => { + if (operators.includes(key)) { + const array = Array.isArray(value) + ? value as Where[] + : Object.entries(value).map((a) => Object.fromEntries([a])); + // every condition is treated as a single context + + switch (key as keyof typeof operators) { + case '$and': + return array?.every((x) => this.applicable(x, context)); + case '$or': + return array?.some((x) => this.applicable(x, context)); + case '$not': + return !this.applicable(value, context); // $not should be a unary operator + default: + throw new Error('Undefined logic operator.'); + } + } else if (meta.includes(key)) { + const testRegexString = (x: string) => { + if (typeof value === 'string') { + return x === value; + } + + return (value).test(x); + }; + + switch (key as keyof typeof meta) { + case '$before': + return !usedActions.find(testRegexString); + case '$after': + return !!usedActions.find(testRegexString); + default: + throw new Error('Undefined meta operator.'); + } + } else { + // Current key is a base condition (url, cookies, selectors) + return inclusive({ [key]: value }, context); + } + }, + ); + } + } \ No newline at end of file From 20bf646656ae1615891e2a158c80de448a05be91 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 05:42:27 +0530 Subject: [PATCH 151/255] feat(core): perform workflow steps --- mx-interpreter/interpret.ts | 119 ++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/mx-interpreter/interpret.ts b/mx-interpreter/interpret.ts index 99dd7505..83bd3b85 100644 --- a/mx-interpreter/interpret.ts +++ b/mx-interpreter/interpret.ts @@ -225,5 +225,124 @@ export default class Interpreter extends EventEmitter { ); } + /** + * Given a Playwright's page object and a "declarative" list of actions, this function + * calls all mentioned functions on the Page object.\ + * \ + * Manipulates the iterator indexes (experimental feature, likely to be removed in + * the following versions of waw-interpreter) + * @param page Playwright Page object + * @param steps Array of actions. + */ + private async carryOutSteps(page: Page, steps: What[]) : Promise { + /** + * Defines overloaded (or added) methods/actions usable in the workflow. + * If a method overloads any existing method of the Page class, it accepts the same set + * of parameters *(but can override some!)*\ + * \ + * Also, following piece of code defines functions to be run in the browser's context. + * Beware of false linter errors - here, we know better! + */ + const wawActions : Record void> = { + screenshot: async (params: PageScreenshotOptions) => { + const screenshotBuffer = await page.screenshot({ + ...params, path: undefined, + }); + await this.options.binaryCallback(screenshotBuffer, 'image/png'); + }, + enqueueLinks: async (selector : string) => { + const links : string[] = await page.locator(selector) + .evaluateAll( + // @ts-ignore + (elements) => elements.map((a) => a.href).filter((x) => x), + ); + const context = page.context(); + + for (const link of links) { + // eslint-disable-next-line + this.concurrency.addJob(async () => { + try { + const newPage = await context.newPage(); + await newPage.goto(link); + await newPage.waitForLoadState('networkidle'); + await this.runLoop(newPage, this.initializedWorkflow!); + } catch (e) { + // `runLoop` uses soft mode, so it recovers from it's own exceptions + // but newPage(), goto() and waitForLoadState() don't (and will kill + // the interpreter by throwing). + this.log(e, Level.ERROR); + } + }); + } + await page.close(); + }, + scrape: async (selector?: string) => { + const scrapeResults : Record[] = await page + // eslint-disable-next-line + // @ts-ignore + .evaluate((s) => scrape(s ?? null), selector); + await this.options.serializableCallback(scrapeResults); + }, + scrapeSchema: async (schema: Record) => { + const handleLists = await Promise.all( + Object.values(schema).map((selector) => page.$$(selector)), + ); + + const namedHandleLists = Object.fromEntries( + Object.keys(schema).map((key, i) => [key, handleLists[i]]), + ); + + const scrapeResult = await page.evaluate((n) => scrapeSchema(n), namedHandleLists); + + this.options.serializableCallback(scrapeResult); + }, + scroll: async (pages? : number) => { + await page.evaluate(async (pagesInternal) => { + for (let i = 1; i <= (pagesInternal ?? 1); i += 1) { + // @ts-ignore + window.scrollTo(0, window.scrollY + window.innerHeight); + } + }, pages ?? 1); + }, + script: async (code : string) => { + const AsyncFunction : FunctionConstructor = Object.getPrototypeOf( + async () => {}, + ).constructor; + const x = new AsyncFunction('page', 'log', code); + await x(page, this.log); + }, + flag: async () => new Promise((res) => { + this.emit('flag', page, res); + }), + }; + + for (const step of steps) { + this.log(`Launching ${step.action}`, Level.LOG); + + if (step.action in wawActions) { + // "Arrayifying" here should not be needed (TS + syntax checker - only arrays; but why not) + const params = !step.args || Array.isArray(step.args) ? step.args : [step.args]; + await wawActions[step.action as CustomFunctions](...(params ?? [])); + } else { + // Implements the dot notation for the "method name" in the workflow + const levels = step.action.split('.'); + const methodName = levels[levels.length - 1]; + + let invokee : any = page; + for (const level of levels.splice(0, levels.length - 1)) { + invokee = invokee[level]; + } + + if (!step.args || Array.isArray(step.args)) { + await (invokee[methodName])(...(step.args ?? [])); + } else { + await (invokee[methodName])(step.args); + } + } + + await new Promise((res) => { setTimeout(res, 500); }); + } + } + } \ No newline at end of file From 2f435e565526e446bca4fd19e083d8830bb1bf95 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 05:42:53 +0530 Subject: [PATCH 152/255] feat(core): handle popups --- mx-interpreter/interpret.ts | 71 +++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/mx-interpreter/interpret.ts b/mx-interpreter/interpret.ts index 83bd3b85..fc715008 100644 --- a/mx-interpreter/interpret.ts +++ b/mx-interpreter/interpret.ts @@ -344,5 +344,76 @@ export default class Interpreter extends EventEmitter { } } + private async runLoop(p : Page, workflow: Workflow) { + const usedActions : string[] = []; + let lastAction = null; + let repeatCount = 0; + + /** + * Enables the interpreter functionality for popup windows. + * User-requested concurrency should be entirely managed by the concurrency manager, + * e.g. via `enqueueLinks`. + */ + p.on('popup', (popup) => { + this.concurrency.addJob(() => this.runLoop(popup, workflow)); + }); + + /* eslint no-constant-condition: ["warn", { "checkLoops": false }] */ + while (true) { + // Checks whether the page was closed from outside, + // or the workflow execution has been stopped via `interpreter.stop()` + if (p.isClosed() || !this.stopper) { + return; + } + + try { + await p.waitForLoadState(); + } catch (e) { + await p.close(); + return; + } + + let pageState = {}; + try { + pageState = await this.getState(p, workflow); + } catch (e: any) { + this.log('The browser has been closed.'); + return; + } + + if (this.options.debug) { + this.log(`Current state is: \n${JSON.stringify(pageState, null, 2)}`, Level.WARN); + } + const actionId = workflow.findIndex( + (step) => this.applicable(step.where, pageState, usedActions), + ); + + const action = workflow[actionId]; + + this.log(`Matched ${JSON.stringify(action?.where)}`, Level.LOG); + + if (action) { // action is matched + if (this.options.debugChannel?.activeId) { + this.options.debugChannel.activeId(actionId); + } + + repeatCount = action === lastAction ? repeatCount + 1 : 0; + if (this.options.maxRepeats && repeatCount >= this.options.maxRepeats) { + return; + } + lastAction = action; + + try { + await this.carryOutSteps(p, action.what); + usedActions.push(action.id ?? 'undefined'); + } catch (e) { + this.log(e, Level.ERROR); + } + } else { + return; + } + } + } + } \ No newline at end of file From 040c7a9ce64160c442afb08fa2cbce1fe6a37773 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 05:43:14 +0530 Subject: [PATCH 153/255] feat(core): spawn browser context & run workflow --- mx-interpreter/interpret.ts | 42 ++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/mx-interpreter/interpret.ts b/mx-interpreter/interpret.ts index fc715008..d465312f 100644 --- a/mx-interpreter/interpret.ts +++ b/mx-interpreter/interpret.ts @@ -415,5 +415,45 @@ export default class Interpreter extends EventEmitter { } } - + /** + * Spawns a browser context and runs given workflow. + * \ + * Resolves after the playback is finished. + * @param {Page} [page] Page to run the workflow on. + * @param {ParamType} params Workflow specific, set of parameters + * for the `{$param: nameofparam}` fields. + */ + public async run(page: Page, params? : ParamType) : Promise { + if (this.stopper) { + throw new Error('This Interpreter is already running a workflow. To run another workflow, please, spawn another Interpreter.'); + } + /** + * `this.workflow` with the parameters initialized. + */ + this.initializedWorkflow = Preprocessor.initWorkflow(this.workflow, params); + + // @ts-ignore + if (await page.evaluate(() => !window.scrape)) { + page.context().addInitScript({ path: path.join(__dirname, 'browserSide', 'scraper.js') }); + } + + this.stopper = () => { + this.stopper = null; + }; + + this.concurrency.addJob(() => this.runLoop(page, this.initializedWorkflow!)); + + await this.concurrency.waitForCompletion(); + + this.stopper = null; + } + + public async stop() : Promise { + if (this.stopper) { + await this.stopper(); + this.stopper = null; + } else { + throw new Error('Cannot stop, there is no running workflow!'); + } + } } \ No newline at end of file From b628aca0c6d776d8adbf0dd7f303ef8d1cb609a7 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 05:43:32 +0530 Subject: [PATCH 154/255] chore(core): lint --- mx-interpreter/interpret.ts | 82 ++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/mx-interpreter/interpret.ts b/mx-interpreter/interpret.ts index d465312f..9ac32b0e 100644 --- a/mx-interpreter/interpret.ts +++ b/mx-interpreter/interpret.ts @@ -39,11 +39,11 @@ export default class Interpreter extends EventEmitter { private options: InterpreterOptions; - private concurrency : Concurrency; + private concurrency: Concurrency; - private stopper : Function | null = null; + private stopper: Function | null = null; - private log : typeof log; + private log: typeof log; constructor(workflow: WorkflowFile, options?: Partial) { super(); @@ -87,7 +87,7 @@ 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): Promise { /** * All the selectors present in the current Workflow */ @@ -98,7 +98,7 @@ export default class Interpreter extends EventEmitter { * @param selector Selector to be queried * @returns True if the targetted element is actionable, false otherwise. */ - const actionable = async (selector: string) : Promise => { + const actionable = async (selector: string): Promise => { try { const proms = [ page.isEnabled(selector, { timeout: 500 }), @@ -115,7 +115,7 @@ export default class Interpreter extends EventEmitter { /** * Object of selectors present in the current page. */ - const presentSelectors : SelectorArray = await Promise.all( + const presentSelectors: SelectorArray = await Promise.all( selectors.map(async (selector) => { if (await actionable(selector)) { return [selector]; @@ -142,7 +142,7 @@ export default class Interpreter extends EventEmitter { * @param context Current browser context. * @returns True if `where` is applicable in the given context, false otherwise */ - private applicable(where: Where, context: PageState, usedActions : string[] = []) : boolean { + private applicable(where: Where, context: PageState, usedActions: string[] = []): boolean { /** * Given two arbitrary objects, determines whether `subset` is a subset of `superset`.\ * \ @@ -153,7 +153,7 @@ export default class Interpreter extends EventEmitter { * @returns `true` if `subset <= superset`, `false` otherwise. */ const inclusive = (subset: Record, superset: Record) - : boolean => ( + : boolean => ( Object.entries(subset).every( ([key, value]) => { /** @@ -161,7 +161,7 @@ export default class Interpreter extends EventEmitter { */ const parsedValue = Array.isArray(value) ? arrayToObject(value) : value; - const parsedSuperset : Record = {}; + const parsedSuperset: Record = {}; parsedSuperset[key] = Array.isArray(superset[key]) ? arrayToObject(superset[key]) : superset[key]; @@ -169,14 +169,14 @@ export default class Interpreter extends EventEmitter { // Every `subset` key must exist in the `superset` and // have the same value (strict equality), or subset[key] <= superset[key] return parsedSuperset[key] - && ( - (parsedSuperset[key] === parsedValue) - || ((parsedValue).constructor.name === 'RegExp' && (parsedValue).test(parsedSuperset[key])) - || ( - (parsedValue).constructor.name !== 'RegExp' - && typeof parsedValue === 'object' && inclusive(parsedValue, parsedSuperset[key]) - ) - ); + && ( + (parsedSuperset[key] === parsedValue) + || ((parsedValue).constructor.name === 'RegExp' && (parsedValue).test(parsedSuperset[key])) + || ( + (parsedValue).constructor.name !== 'RegExp' + && typeof parsedValue === 'object' && inclusive(parsedValue, parsedSuperset[key]) + ) + ); }, ) ); @@ -188,7 +188,7 @@ export default class Interpreter extends EventEmitter { const array = Array.isArray(value) ? value as Where[] : Object.entries(value).map((a) => Object.fromEntries([a])); - // every condition is treated as a single context + // every condition is treated as a single context switch (key as keyof typeof operators) { case '$and': @@ -234,24 +234,24 @@ export default class Interpreter extends EventEmitter { * @param page Playwright Page object * @param steps Array of actions. */ - private async carryOutSteps(page: Page, steps: What[]) : Promise { - /** - * Defines overloaded (or added) methods/actions usable in the workflow. - * If a method overloads any existing method of the Page class, it accepts the same set - * of parameters *(but can override some!)*\ - * \ - * Also, following piece of code defines functions to be run in the browser's context. - * Beware of false linter errors - here, we know better! - */ - const wawActions : Record void> = { + private async carryOutSteps(page: Page, steps: What[]): Promise { + /** + * Defines overloaded (or added) methods/actions usable in the workflow. + * If a method overloads any existing method of the Page class, it accepts the same set + * of parameters *(but can override some!)*\ + * \ + * Also, following piece of code defines functions to be run in the browser's context. + * Beware of false linter errors - here, we know better! + */ + const wawActions: Record void> = { screenshot: async (params: PageScreenshotOptions) => { const screenshotBuffer = await page.screenshot({ ...params, path: undefined, }); await this.options.binaryCallback(screenshotBuffer, 'image/png'); }, - enqueueLinks: async (selector : string) => { - const links : string[] = await page.locator(selector) + enqueueLinks: async (selector: string) => { + const links: string[] = await page.locator(selector) .evaluateAll( // @ts-ignore (elements) => elements.map((a) => a.href).filter((x) => x), @@ -277,7 +277,7 @@ export default class Interpreter extends EventEmitter { await page.close(); }, scrape: async (selector?: string) => { - const scrapeResults : Record[] = await page + const scrapeResults: Record[] = await page // eslint-disable-next-line // @ts-ignore .evaluate((s) => scrape(s ?? null), selector); @@ -296,7 +296,7 @@ export default class Interpreter extends EventEmitter { this.options.serializableCallback(scrapeResult); }, - scroll: async (pages? : number) => { + scroll: async (pages?: number) => { await page.evaluate(async (pagesInternal) => { for (let i = 1; i <= (pagesInternal ?? 1); i += 1) { // @ts-ignore @@ -304,9 +304,9 @@ export default class Interpreter extends EventEmitter { } }, pages ?? 1); }, - script: async (code : string) => { - const AsyncFunction : FunctionConstructor = Object.getPrototypeOf( - async () => {}, + script: async (code: string) => { + const AsyncFunction: FunctionConstructor = Object.getPrototypeOf( + async () => { }, ).constructor; const x = new AsyncFunction('page', 'log', code); await x(page, this.log); @@ -324,11 +324,11 @@ export default class Interpreter extends EventEmitter { const params = !step.args || Array.isArray(step.args) ? step.args : [step.args]; await wawActions[step.action as CustomFunctions](...(params ?? [])); } else { - // Implements the dot notation for the "method name" in the workflow + // Implements the dot notation for the "method name" in the workflow const levels = step.action.split('.'); const methodName = levels[levels.length - 1]; - let invokee : any = page; + let invokee: any = page; for (const level of levels.splice(0, levels.length - 1)) { invokee = invokee[level]; } @@ -344,8 +344,8 @@ export default class Interpreter extends EventEmitter { } } - private async runLoop(p : Page, workflow: Workflow) { - const usedActions : string[] = []; + private async runLoop(p: Page, workflow: Workflow) { + const usedActions: string[] = []; let lastAction = null; let repeatCount = 0; @@ -423,7 +423,7 @@ export default class Interpreter extends EventEmitter { * @param {ParamType} params Workflow specific, set of parameters * for the `{$param: nameofparam}` fields. */ - public async run(page: Page, params? : ParamType) : Promise { + public async run(page: Page, params?: ParamType): Promise { if (this.stopper) { throw new Error('This Interpreter is already running a workflow. To run another workflow, please, spawn another Interpreter.'); } @@ -448,7 +448,7 @@ export default class Interpreter extends EventEmitter { this.stopper = null; } - public async stop() : Promise { + public async stop(): Promise { if (this.stopper) { await this.stopper(); this.stopper = null; From 30bfe509061b968d6aab3e8ccf85c6437dcb1a63 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 19:47:52 +0530 Subject: [PATCH 155/255] feat: body css --- src/api/storage.ts | 120 +++++++++++++++++++++++++++++++++++++++++++++ src/index.css | 6 +++ 2 files changed, 126 insertions(+) create mode 100644 src/api/storage.ts diff --git a/src/api/storage.ts b/src/api/storage.ts new file mode 100644 index 00000000..acae2121 --- /dev/null +++ b/src/api/storage.ts @@ -0,0 +1,120 @@ +import { default as axios } from "axios"; +import { WorkflowFile } from "@wbr-project/wbr-interpret"; +import { RunSettings } from "../components/molecules/RunSettings"; +import { CreateRunResponse } from "../pages/MainPage"; + +export const getStoredRecordings = async (): Promise => { + try { + const response = await axios.get('http://localhost:8080/storage/recordings'); + if (response.status === 200) { + return response.data; + } else { + throw new Error('Couldn\'t retrieve stored recordings'); + } + } catch(error: any) { + console.log(error); + return null; + } +}; + +export const getStoredRuns = async (): Promise => { + try { + const response = await axios.get('http://localhost:8080/storage/runs'); + if (response.status === 200) { + return response.data; + } else { + throw new Error('Couldn\'t retrieve stored recordings'); + } + } catch(error: any) { + console.log(error); + return null; + } +}; + +export const deleteRecordingFromStorage = async (fileName: string): Promise => { + try { + const response = await axios.delete(`http://localhost:8080/storage/recordings/${fileName}`); + if (response.status === 200) { + return response.data; + } else { + throw new Error(`Couldn't delete stored recording ${fileName}`); + } + } catch(error: any) { + console.log(error); + return false; + } +}; + +export const deleteRunFromStorage = async (fileName: string): Promise => { + try { + const response = await axios.delete(`http://localhost:8080/storage/runs/${fileName}`); + if (response.status === 200) { + return response.data; + } else { + throw new Error(`Couldn't delete stored recording ${fileName}`); + } + } catch(error: any) { + console.log(error); + return false; + } +}; + +export const editRecordingFromStorage = async (browserId: string, fileName: string): Promise => { + try { + const response = await axios.put(`http://localhost:8080/workflow/${browserId}/${fileName}`); + if (response.status === 200) { + return response.data; + } else { + throw new Error(`Couldn't edit stored recording ${fileName}`); + } + } catch(error: any) { + console.log(error); + return null; + } +}; + +export const createRunForStoredRecording = async (fileName: string, settings: RunSettings): Promise => { + try { + const response = await axios.put( + `http://localhost:8080/storage/runs/${fileName}`, + {...settings}); + if (response.status === 200) { + return response.data; + } else { + throw new Error(`Couldn't create a run for a recording ${fileName}`); + } + } catch(error: any) { + console.log(error); + return {browserId: '', runId: ''}; + } +} + +export const interpretStoredRecording = async (fileName: string, runId: string): Promise => { + try { + const response = await axios.post(`http://localhost:8080/storage/runs/run/${fileName}/${runId}`); + if (response.status === 200) { + return response.data; + } else { + throw new Error(`Couldn't run a recording ${fileName}`); + } + } catch(error: any) { + console.log(error); + return false; + } +} + +export const notifyAboutAbort = async (fileName: string, runId:string): Promise => { + try { + const response = await axios.post(`http://localhost:8080/storage/runs/abort/${fileName}/${runId}`); + if (response.status === 200) { + return response.data; + } else { + throw new Error(`Couldn't abort a running recording ${fileName} with id ${runId}`); + } + } catch(error: any) { + console.log(error); + return false; + } +} + + diff --git a/src/index.css b/src/index.css index 21808ea5..1d4c3871 100644 --- a/src/index.css +++ b/src/index.css @@ -5,6 +5,12 @@ body { sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + scrollbar-gutter: stable; + overflow: hidden; } code { From 30edc9a6ac2ef87b759dd6f9b0f08bf4b92024cb Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 20:40:14 +0530 Subject: [PATCH 156/255] refactor: rename to maxun-core from mx-interpreter --- maxun-core/browserSide/scraper.js | 226 +++++++++++++++ maxun-core/index.ts | 8 + maxun-core/interpret.ts | 459 ++++++++++++++++++++++++++++++ maxun-core/preprocessor.ts | 179 ++++++++++++ maxun-core/types/logic.ts | 5 + maxun-core/types/workflow.ts | 58 ++++ maxun-core/utils/concurrency.ts | 85 ++++++ maxun-core/utils/logger.ts | 30 ++ maxun-core/utils/utils.ts | 13 + 9 files changed, 1063 insertions(+) create mode 100644 maxun-core/browserSide/scraper.js create mode 100644 maxun-core/index.ts create mode 100644 maxun-core/interpret.ts create mode 100644 maxun-core/preprocessor.ts create mode 100644 maxun-core/types/logic.ts create mode 100644 maxun-core/types/workflow.ts create mode 100644 maxun-core/utils/concurrency.ts create mode 100644 maxun-core/utils/logger.ts create mode 100644 maxun-core/utils/utils.ts diff --git a/maxun-core/browserSide/scraper.js b/maxun-core/browserSide/scraper.js new file mode 100644 index 00000000..c411f642 --- /dev/null +++ b/maxun-core/browserSide/scraper.js @@ -0,0 +1,226 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ + +const area = (element) => element.offsetHeight * element.offsetWidth; + +function getBiggestElement(selector) { + const elements = Array.from(document.querySelectorAll(selector)); + const biggest = elements.reduce( + (max, elem) => ( + area(elem) > area(max) ? elem : max), + { offsetHeight: 0, offsetWidth: 0 }, + ); + return biggest; +} + +/** + * Generates structural selector (describing element by its DOM tree location). + * + * **The generated selector is not guaranteed to be unique!** (In fact, this is + * the desired behaviour in here.) + * @param {HTMLElement} element Element being described. + * @returns {string} CSS-compliant selector describing the element's location in the DOM tree. + */ +function GetSelectorStructural(element) { + // Base conditions for the recursive approach. + if (element.tagName === 'BODY') { + return 'BODY'; + } + const selector = element.tagName; + if (element.parentElement) { + return `${GetSelectorStructural(element.parentElement)} > ${selector}`; + } + + return selector; +} + +/** + * Heuristic method to find collections of "interesting" items on the page. + * @returns {Array} A collection of interesting DOM nodes + * (online store products, plane tickets, list items... and many more?) + */ +function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, metricType = 'size_deviation') { + const restoreScroll = (() => { + const { scrollX, scrollY } = window; + return () => { + window.scrollTo(scrollX, scrollY); + }; + })(); + + /** +* @typedef {Array<{x: number, y: number}>} Grid +*/ + + /** + * Returns an array of grid-aligned {x,y} points. + * @param {number} [granularity=0.005] sets the number of generated points + * (the higher the granularity, the more points). + * @returns {Grid} Array of {x, y} objects. + */ + function getGrid(startX = 0, startY = 0, granularity = 0.005) { + const width = window.innerWidth; + const height = window.innerHeight; + + const out = []; + for (let x = 0; x < width; x += 1 / granularity) { + for (let y = 0; y < height; y += 1 / granularity) { + out.push({ x: startX + x, y: startY + y }); + } + } + return out; + } + + let maxSelector = { selector: 'body', metric: 0 }; + + const updateMaximumWithPoint = (point) => { + const currentElement = document.elementFromPoint(point.x, point.y); + const selector = GetSelectorStructural(currentElement); + + const elements = Array.from(document.querySelectorAll(selector)) + .filter((element) => area(element) > minArea); + + // If the current selector targets less than three elements, + // we consider it not interesting (would be a very underwhelming scraper) + if (elements.length < 3) { + return; + } + + let metric = null; + + if (metricType === 'total_area') { + metric = elements + .reduce((p, x) => p + area(x), 0); + } else if (metricType === 'size_deviation') { + // This could use a proper "statistics" approach... but meh, so far so good! + const sizes = elements + .map((element) => area(element)); + + metric = (1 - (Math.max(...sizes) - Math.min(...sizes)) / Math.max(...sizes)); + } + + // console.debug(`Total ${metricType} is ${metric}.`) + if (metric > maxSelector.metric && elements.length < maxCountPerPage) { + maxSelector = { selector, metric }; + } + }; + + for (let scroll = 0; scroll < scrolls; scroll += 1) { + window.scrollTo(0, scroll * window.innerHeight); + + const grid = getGrid(); + + grid.forEach(updateMaximumWithPoint); + } + + restoreScroll(); + + let out = Array.from(document.querySelectorAll(maxSelector.selector)); + + const different = (x, i, a) => a.findIndex((e) => e === x) === i; + // as long as we don't merge any two elements by substituing them for their parents, + // we substitute. + while (out.map((x) => x.parentElement).every(different) + && out.forEach((x) => x.parentElement !== null)) { + out = out.map((x) => x.parentElement ?? x); + } + + return out; +} + +/** + * Returns a "scrape" result from the current page. + * @returns {Array} *Curated* array of scraped information (with sparse rows removed) + */ +function scrape(selector = null) { + /** + * **crudeRecords** contains uncurated rundowns of "scrapable" elements + * @type {Array} + */ + const crudeRecords = (selector + ? Array.from(document.querySelectorAll(selector)) + : scrapableHeuristics()) + .map((record) => ({ + ...Array.from(record.querySelectorAll('img')) + .reduce((p, x, i) => { + let url = null; + if (x.srcset) { + const urls = x.srcset.split(', '); + [url] = urls[urls.length - 1].split(' '); + } + + /** + * Contains the largest elements from `srcset` - if `srcset` is not present, contains + * URL from the `src` attribute + * + * If the `src` attribute contains a data url, imgUrl contains `undefined`. + */ + let imgUrl; + if (x.srcset) { + imgUrl = url; + } else if (x.src.indexOf('data:') === -1) { + imgUrl = x.src; + } + + return ({ + ...p, + ...(imgUrl ? { [`img_${i}`]: imgUrl } : {}), + }); + }, {}), + ...record.innerText.split('\n') + .reduce((p, x, i) => ({ + ...p, + [`record_${String(i).padStart(4, '0')}`]: x.trim(), + }), {}), + })); + + return crudeRecords; +} + +/** + * Given an object with named lists of elements, + * groups the elements by their distance in the DOM tree. + * @param {Object.} lists The named lists of HTML elements. + * @returns {Array.>} + */ +function scrapeSchema(lists) { + function omap(object, f, kf = (x) => x) { + return Object.fromEntries( + Object.entries(object) + .map(([k, v]) => [kf(k), f(v)]), + ); + } + + function ofilter(object, f) { + return Object.fromEntries( + Object.entries(object) + .filter(([k, v]) => f(k, v)), + ); + } + + function getSeedKey(listObj) { + const maxLength = Math.max(...Object.values(omap(listObj, (x) => x.length))); + return Object.keys(ofilter(listObj, (_, v) => v.length === maxLength))[0]; + } + + function getMBEs(elements) { + return elements.map((element) => { + let candidate = element; + const isUniqueChild = (e) => elements + .filter((elem) => e.parentNode?.contains(elem)) + .length === 1; + + while (candidate && isUniqueChild(candidate)) { + candidate = candidate.parentNode; + } + + return candidate; + }); + } + + const seedName = getSeedKey(lists); + const MBEs = getMBEs(lists[seedName]); + + return MBEs.map((mbe) => omap( + lists, + (listOfElements) => listOfElements.find((elem) => mbe.contains(elem))?.innerText, + )); +} \ No newline at end of file diff --git a/maxun-core/index.ts b/maxun-core/index.ts new file mode 100644 index 00000000..571f5781 --- /dev/null +++ b/maxun-core/index.ts @@ -0,0 +1,8 @@ +import Interpreter from './interpret'; + +export default Interpreter; +export { default as Preprocessor } from './preprocessor'; +export type { + WorkflowFile, WhereWhatPair, Where, What, +} from './types/workflow'; +export { unaryOperators, naryOperators, meta as metaOperators } from './types/logic'; \ No newline at end of file diff --git a/maxun-core/interpret.ts b/maxun-core/interpret.ts new file mode 100644 index 00000000..9ac32b0e --- /dev/null +++ b/maxun-core/interpret.ts @@ -0,0 +1,459 @@ +/* eslint-disable no-await-in-loop, no-restricted-syntax */ +import { Page, PageScreenshotOptions } from 'playwright'; +import path from 'path'; + +import { EventEmitter } from 'events'; +import { + Where, What, PageState, Workflow, WorkflowFile, + ParamType, SelectorArray, CustomFunctions, +} from './types/workflow'; + +import { operators, meta } from './types/logic'; +import { arrayToObject } from './utils/utils'; +import Concurrency from './utils/concurrency'; +import Preprocessor from './preprocessor'; +import log, { Level } from './utils/logger'; + +/** + * Defines optional intepreter options (passed in constructor) + */ +interface InterpreterOptions { + maxRepeats: number; + maxConcurrency: number; + serializableCallback: (output: any) => (void | Promise); + binaryCallback: (output: any, mimeType: string) => (void | Promise); + debug: boolean; + debugChannel: Partial<{ + activeId: Function, + debugMessage: Function, + }> +} + +/** + * Class for running the Smart Workflows. + */ +export default class Interpreter extends EventEmitter { + private workflow: Workflow; + + private initializedWorkflow: Workflow | null; + + private options: InterpreterOptions; + + private concurrency: Concurrency; + + private stopper: Function | null = null; + + private log: typeof log; + + constructor(workflow: WorkflowFile, options?: Partial) { + super(); + this.workflow = workflow.workflow; + this.initializedWorkflow = null; + this.options = { + maxRepeats: 5, + maxConcurrency: 5, + serializableCallback: (data) => { log(JSON.stringify(data), Level.WARN); }, + binaryCallback: () => { log('Received binary data, thrashing them.', Level.WARN); }, + debug: false, + debugChannel: {}, + ...options, + }; + this.concurrency = new Concurrency(this.options.maxConcurrency); + this.log = (...args) => log(...args); + + const error = Preprocessor.validateWorkflow(workflow); + if (error) { + throw (error); + } + + if (this.options.debugChannel?.debugMessage) { + const oldLog = this.log; + // @ts-ignore + this.log = (...args: Parameters) => { + if (args[1] !== Level.LOG) { + this.options.debugChannel.debugMessage!(typeof args[0] === 'string' ? args[0] : args[0].message); + } + oldLog(...args); + }; + } + } + + /** + * Returns the context object from given Page and the current workflow.\ + * \ + * `workflow` is used for selector extraction - function searches for used selectors to + * look for later in the page's context. + * @param page Playwright Page object + * @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 { + /** + * All the selectors present in the current Workflow + */ + const selectors = Preprocessor.extractSelectors(workflow); + + /** + * 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 }), + ]; + + 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()); + + return { + url: page.url(), + cookies: (await page.context().cookies([page.url()])) + .reduce((p, cookie) => ( + { + ...p, + [cookie.name]: cookie.value, + }), {}), + selectors: presentSelectors, + }; + } + + /** + * Tests if the given action is applicable with the given context. + * @param where Tested *where* condition + * @param context Current browser context. + * @returns True if `where` is applicable in the given context, false otherwise + */ + private applicable(where: Where, context: PageState, usedActions: string[] = []): boolean { + /** + * Given two arbitrary objects, determines whether `subset` is a subset of `superset`.\ + * \ + * For every key in `subset`, there must be a corresponding key with equal scalar + * value in `superset`, or `inclusive(subset[key], superset[key])` must hold. + * @param subset Arbitrary non-cyclic JS object (where clause) + * @param superset Arbitrary non-cyclic JS object (browser context) + * @returns `true` if `subset <= superset`, `false` otherwise. + */ + const inclusive = (subset: Record, superset: Record) + : boolean => ( + Object.entries(subset).every( + ([key, value]) => { + /** + * Arrays are compared without order (are transformed into objects before comparison). + */ + const parsedValue = Array.isArray(value) ? arrayToObject(value) : value; + + const parsedSuperset: Record = {}; + parsedSuperset[key] = Array.isArray(superset[key]) + ? arrayToObject(superset[key]) + : superset[key]; + + // Every `subset` key must exist in the `superset` and + // have the same value (strict equality), or subset[key] <= superset[key] + return parsedSuperset[key] + && ( + (parsedSuperset[key] === parsedValue) + || ((parsedValue).constructor.name === 'RegExp' && (parsedValue).test(parsedSuperset[key])) + || ( + (parsedValue).constructor.name !== 'RegExp' + && typeof parsedValue === 'object' && inclusive(parsedValue, parsedSuperset[key]) + ) + ); + }, + ) + ); + + // Every value in the "where" object should be compliant to the current state. + return Object.entries(where).every( + ([key, value]) => { + if (operators.includes(key)) { + const array = Array.isArray(value) + ? value as Where[] + : Object.entries(value).map((a) => Object.fromEntries([a])); + // every condition is treated as a single context + + switch (key as keyof typeof operators) { + case '$and': + return array?.every((x) => this.applicable(x, context)); + case '$or': + return array?.some((x) => this.applicable(x, context)); + case '$not': + return !this.applicable(value, context); // $not should be a unary operator + default: + throw new Error('Undefined logic operator.'); + } + } else if (meta.includes(key)) { + const testRegexString = (x: string) => { + if (typeof value === 'string') { + return x === value; + } + + return (value).test(x); + }; + + switch (key as keyof typeof meta) { + case '$before': + return !usedActions.find(testRegexString); + case '$after': + return !!usedActions.find(testRegexString); + default: + throw new Error('Undefined meta operator.'); + } + } else { + // Current key is a base condition (url, cookies, selectors) + return inclusive({ [key]: value }, context); + } + }, + ); + } + + /** + * Given a Playwright's page object and a "declarative" list of actions, this function + * calls all mentioned functions on the Page object.\ + * \ + * Manipulates the iterator indexes (experimental feature, likely to be removed in + * the following versions of waw-interpreter) + * @param page Playwright Page object + * @param steps Array of actions. + */ + private async carryOutSteps(page: Page, steps: What[]): Promise { + /** + * Defines overloaded (or added) methods/actions usable in the workflow. + * If a method overloads any existing method of the Page class, it accepts the same set + * of parameters *(but can override some!)*\ + * \ + * Also, following piece of code defines functions to be run in the browser's context. + * Beware of false linter errors - here, we know better! + */ + const wawActions: Record void> = { + screenshot: async (params: PageScreenshotOptions) => { + const screenshotBuffer = await page.screenshot({ + ...params, path: undefined, + }); + await this.options.binaryCallback(screenshotBuffer, 'image/png'); + }, + enqueueLinks: async (selector: string) => { + const links: string[] = await page.locator(selector) + .evaluateAll( + // @ts-ignore + (elements) => elements.map((a) => a.href).filter((x) => x), + ); + const context = page.context(); + + for (const link of links) { + // eslint-disable-next-line + this.concurrency.addJob(async () => { + try { + const newPage = await context.newPage(); + await newPage.goto(link); + await newPage.waitForLoadState('networkidle'); + await this.runLoop(newPage, this.initializedWorkflow!); + } catch (e) { + // `runLoop` uses soft mode, so it recovers from it's own exceptions + // but newPage(), goto() and waitForLoadState() don't (and will kill + // the interpreter by throwing). + this.log(e, Level.ERROR); + } + }); + } + await page.close(); + }, + scrape: async (selector?: string) => { + const scrapeResults: Record[] = await page + // eslint-disable-next-line + // @ts-ignore + .evaluate((s) => scrape(s ?? null), selector); + await this.options.serializableCallback(scrapeResults); + }, + scrapeSchema: async (schema: Record) => { + const handleLists = await Promise.all( + Object.values(schema).map((selector) => page.$$(selector)), + ); + + const namedHandleLists = Object.fromEntries( + Object.keys(schema).map((key, i) => [key, handleLists[i]]), + ); + + const scrapeResult = await page.evaluate((n) => scrapeSchema(n), namedHandleLists); + + this.options.serializableCallback(scrapeResult); + }, + scroll: async (pages?: number) => { + await page.evaluate(async (pagesInternal) => { + for (let i = 1; i <= (pagesInternal ?? 1); i += 1) { + // @ts-ignore + window.scrollTo(0, window.scrollY + window.innerHeight); + } + }, pages ?? 1); + }, + script: async (code: string) => { + const AsyncFunction: FunctionConstructor = Object.getPrototypeOf( + async () => { }, + ).constructor; + const x = new AsyncFunction('page', 'log', code); + await x(page, this.log); + }, + flag: async () => new Promise((res) => { + this.emit('flag', page, res); + }), + }; + + for (const step of steps) { + this.log(`Launching ${step.action}`, Level.LOG); + + if (step.action in wawActions) { + // "Arrayifying" here should not be needed (TS + syntax checker - only arrays; but why not) + const params = !step.args || Array.isArray(step.args) ? step.args : [step.args]; + await wawActions[step.action as CustomFunctions](...(params ?? [])); + } else { + // Implements the dot notation for the "method name" in the workflow + const levels = step.action.split('.'); + const methodName = levels[levels.length - 1]; + + let invokee: any = page; + for (const level of levels.splice(0, levels.length - 1)) { + invokee = invokee[level]; + } + + if (!step.args || Array.isArray(step.args)) { + await (invokee[methodName])(...(step.args ?? [])); + } else { + await (invokee[methodName])(step.args); + } + } + + await new Promise((res) => { setTimeout(res, 500); }); + } + } + + private async runLoop(p: Page, workflow: Workflow) { + const usedActions: string[] = []; + let lastAction = null; + let repeatCount = 0; + + /** + * Enables the interpreter functionality for popup windows. + * User-requested concurrency should be entirely managed by the concurrency manager, + * e.g. via `enqueueLinks`. + */ + p.on('popup', (popup) => { + this.concurrency.addJob(() => this.runLoop(popup, workflow)); + }); + + /* eslint no-constant-condition: ["warn", { "checkLoops": false }] */ + while (true) { + // Checks whether the page was closed from outside, + // or the workflow execution has been stopped via `interpreter.stop()` + if (p.isClosed() || !this.stopper) { + return; + } + + try { + await p.waitForLoadState(); + } catch (e) { + await p.close(); + return; + } + + let pageState = {}; + try { + pageState = await this.getState(p, workflow); + } catch (e: any) { + this.log('The browser has been closed.'); + return; + } + + if (this.options.debug) { + this.log(`Current state is: \n${JSON.stringify(pageState, null, 2)}`, Level.WARN); + } + const actionId = workflow.findIndex( + (step) => this.applicable(step.where, pageState, usedActions), + ); + + const action = workflow[actionId]; + + this.log(`Matched ${JSON.stringify(action?.where)}`, Level.LOG); + + if (action) { // action is matched + if (this.options.debugChannel?.activeId) { + this.options.debugChannel.activeId(actionId); + } + + repeatCount = action === lastAction ? repeatCount + 1 : 0; + if (this.options.maxRepeats && repeatCount >= this.options.maxRepeats) { + return; + } + lastAction = action; + + try { + await this.carryOutSteps(p, action.what); + usedActions.push(action.id ?? 'undefined'); + } catch (e) { + this.log(e, Level.ERROR); + } + } else { + return; + } + } + } + + /** + * Spawns a browser context and runs given workflow. + * \ + * Resolves after the playback is finished. + * @param {Page} [page] Page to run the workflow on. + * @param {ParamType} params Workflow specific, set of parameters + * for the `{$param: nameofparam}` fields. + */ + public async run(page: Page, params?: ParamType): Promise { + if (this.stopper) { + throw new Error('This Interpreter is already running a workflow. To run another workflow, please, spawn another Interpreter.'); + } + /** + * `this.workflow` with the parameters initialized. + */ + this.initializedWorkflow = Preprocessor.initWorkflow(this.workflow, params); + + // @ts-ignore + if (await page.evaluate(() => !window.scrape)) { + page.context().addInitScript({ path: path.join(__dirname, 'browserSide', 'scraper.js') }); + } + + this.stopper = () => { + this.stopper = null; + }; + + this.concurrency.addJob(() => this.runLoop(page, this.initializedWorkflow!)); + + await this.concurrency.waitForCompletion(); + + this.stopper = null; + } + + public async stop(): Promise { + if (this.stopper) { + await this.stopper(); + this.stopper = null; + } else { + throw new Error('Cannot stop, there is no running workflow!'); + } + } +} \ No newline at end of file diff --git a/maxun-core/preprocessor.ts b/maxun-core/preprocessor.ts new file mode 100644 index 00000000..9ad15c2a --- /dev/null +++ b/maxun-core/preprocessor.ts @@ -0,0 +1,179 @@ +import Joi from 'joi'; +import { + Workflow, WorkflowFile, ParamType, SelectorArray, Where, +} from './types/workflow'; +import { operators } from './types/logic'; + +/** +* Class for static processing the workflow files/objects. +*/ +export default class Preprocessor { + static validateWorkflow(workflow: WorkflowFile): any { + const regex = Joi.object({ + $regex: Joi.string().required(), + }); + + const whereSchema = Joi.object({ + url: [Joi.string().uri(), regex], + selectors: Joi.array().items(Joi.string()), + cookies: Joi.object({}).pattern(Joi.string(), Joi.string()), + $after: [Joi.string(), regex], + $before: [Joi.string(), regex], + $and: Joi.array().items(Joi.link('#whereSchema')), + $or: Joi.array().items(Joi.link('#whereSchema')), + $not: Joi.link('#whereSchema'), + }).id('whereSchema'); + + const schema = Joi.object({ + meta: Joi.object({ + name: Joi.string(), + desc: Joi.string(), + }), + workflow: Joi.array().items( + Joi.object({ + id: Joi.string(), + where: whereSchema.required(), + what: Joi.array().items({ + action: Joi.string().required(), + args: Joi.array().items(Joi.any()), + }).required(), + }), + ).required(), + }); + + const { error } = schema.validate(workflow); + + return error; + } + +/** +* Extracts parameter names from the workflow. +* @param {WorkflowFile} workflow The given workflow +* @returns {String[]} List of parameters' names. +*/ + static getParams(workflow: WorkflowFile): string[] { + const getParamsRecurse = (object: any): string[] => { + if (typeof object === 'object') { + // Recursion base case + if (object.$param) { + return [object.$param]; + } + + // Recursion general case + return Object.values(object) + .reduce((p: string[], v: any): string[] => [...p, ...getParamsRecurse(v)], []); + } + return []; + }; + + return getParamsRecurse(workflow.workflow); + } + +/** +* List all the selectors used in the given workflow (only literal "selector" +* field in WHERE clauses so far) +*/ + // TODO : add recursive selector search (also in click/fill etc. events?) + static extractSelectors(workflow: Workflow): SelectorArray { + /** +* Given a Where condition, this function extracts +* all the existing selectors from it (recursively). +*/ + const selectorsFromCondition = (where: Where): SelectorArray => { + // the `selectors` field is either on the top level + let out = where.selectors ?? []; + if (!Array.isArray(out)) { + out = [out]; + } + + // or nested in the "operator" array + operators.forEach((op) => { + let condWhere = where[op]; + if (condWhere) { + condWhere = Array.isArray(condWhere) ? condWhere : [condWhere]; + (condWhere).forEach((subWhere) => { + out = [...out, ...selectorsFromCondition(subWhere)]; + }); + } + }); + + return out; + }; + + // Iterate through all the steps and extract the selectors from all of them. + return workflow.reduce((p: SelectorArray, step) => [ + ...p, + ...selectorsFromCondition(step.where).filter((x) => !p.includes(x)), + ], []); + } + +/** +* Recursively crawl `object` and initializes params - replaces the `{$param : paramName}` objects +* with the defined value. +* @returns {Workflow} Copy of the given workflow, modified (the initial workflow is left untouched). +*/ + static initWorkflow(workflow: Workflow, params?: ParamType): Workflow { + const paramNames = this.getParams({ workflow }); + + if (Object.keys(params ?? {}).sort().join(',') !== paramNames.sort().join(',')) { + throw new Error(`Provided parameters do not match the workflow parameters + provided: ${Object.keys(params ?? {}).sort().join(',')}, + expected: ${paramNames.sort().join(',')} + `); + } + /** + * A recursive method for initializing special `{key: value}` syntax objects in the workflow. + * @param object Workflow to initialize (or a part of it). + * @param k key to look for ($regex, $param) + * @param f function mutating the special `{}` syntax into + * its true representation (RegExp...) + * @returns Updated object + */ + const initSpecialRecurse = ( + object: unknown, + k: string, + f: (value: string) => unknown, + ): unknown => { + if (!object || typeof object !== 'object') { + return object; + } + + const out = object; + // for every key (child) of the object + Object.keys(object!).forEach((key) => { + // if the field has only one key, which is `k` + if (Object.keys((object)[key]).length === 1 && (object)[key][k]) { + // process the current special tag (init param, hydrate regex...) + (out)[key] = f((object)[key][k]); + } else { + initSpecialRecurse((object)[key], k, f); + } + }); + return out; + }; + + // TODO: do better deep copy, this is hideous. + let workflowCopy = JSON.parse(JSON.stringify(workflow)); + + if (params) { + workflowCopy = initSpecialRecurse( + workflowCopy, + '$param', + (paramName) => { + if (params && params[paramName]) { + return params[paramName]; + } + throw new SyntaxError(`Unspecified parameter found ${paramName}.`); + }, + ); + } + + workflowCopy = initSpecialRecurse( + workflowCopy, + '$regex', + (regex) => new RegExp(regex), + ); + + return workflowCopy; + } +} \ No newline at end of file diff --git a/maxun-core/types/logic.ts b/maxun-core/types/logic.ts new file mode 100644 index 00000000..5d06abbe --- /dev/null +++ b/maxun-core/types/logic.ts @@ -0,0 +1,5 @@ +export const unaryOperators = ['$not'] as const; +export const naryOperators = ['$and', '$or'] as const; + +export const operators = [...unaryOperators, ...naryOperators] as const; +export const meta = ['$before', '$after'] as const; \ No newline at end of file diff --git a/maxun-core/types/workflow.ts b/maxun-core/types/workflow.ts new file mode 100644 index 00000000..36c6d14d --- /dev/null +++ b/maxun-core/types/workflow.ts @@ -0,0 +1,58 @@ +import { Page } from 'playwright'; +import { + naryOperators, unaryOperators, operators, meta, +} from './logic'; + +export type Operator = typeof operators[number]; +export type UnaryOperator = typeof unaryOperators[number]; +export type NAryOperator = typeof naryOperators[number]; + +export type Meta = typeof meta[number]; + +export type SelectorArray = string[]; + +type RegexableString = string | { '$regex': string }; + +type BaseConditions = { + 'url': RegexableString, + 'cookies': Record, + 'selectors': SelectorArray, // (CSS/Playwright) selectors use their own logic, there is no reason (and several technical difficulties) to allow regular expression notation +} & Record; + +export type Where = + Partial<{ [key in NAryOperator]: Where[] }> & // either a logic operator (arity N) + Partial<{ [key in UnaryOperator]: Where }> & // or an unary operator + Partial; // or one of the base conditions + +type MethodNames = { + [K in keyof T]: T[K] extends Function ? K : never; +}[keyof T]; + +export type CustomFunctions = 'scrape' | 'scrapeSchema' | 'scroll' | 'screenshot' | 'script' | 'enqueueLinks' | 'flag'; + +export type What = { + action: MethodNames | CustomFunctions, + args?: any[] +}; + +export type PageState = Partial; + +export type ParamType = Record; + +export type MetaData = { + name?: string, + desc?: string, +}; + +export interface WhereWhatPair { + id?: string + where: Where + what: What[] +} + +export type Workflow = WhereWhatPair[]; + +export type WorkflowFile = { + meta?: MetaData, + workflow: Workflow +}; \ No newline at end of file diff --git a/maxun-core/utils/concurrency.ts b/maxun-core/utils/concurrency.ts new file mode 100644 index 00000000..eec7eb33 --- /dev/null +++ b/maxun-core/utils/concurrency.ts @@ -0,0 +1,85 @@ +/** + * Concurrency class for running concurrent tasks while managing a limited amount of resources. + */ +export default class Concurrency { + /** + * Maximum number of workers running in parallel. If set to `null`, there is no limit. + */ + maxConcurrency: number = 1; + + /** + * Number of currently active workers. + */ + activeWorkers: number = 0; + + /** + * Queue of jobs waiting to be completed. + */ + private jobQueue: Function[] = []; + + /** + * "Resolve" callbacks of the waitForCompletion() promises. + */ + private waiting: Function[] = []; + + /** + * Constructs a new instance of concurrency manager. + * @param {number} maxConcurrency Maximum number of workers running in parallel. + */ + constructor(maxConcurrency: number) { + this.maxConcurrency = maxConcurrency; + } + + /** + * Takes a waiting job out of the queue and runs it. + */ + private runNextJob(): void { + const job = this.jobQueue.pop(); + + if (job) { + // console.debug("Running a job..."); + job().then(() => { + // console.debug("Job finished, running the next waiting job..."); + this.runNextJob(); + }); + } else { + // console.debug("No waiting job found!"); + this.activeWorkers -= 1; + if (this.activeWorkers === 0) { + // console.debug("This concurrency manager is idle!"); + this.waiting.forEach((x) => x()); + } + } + } + + /** + * Pass a job (a time-demanding async function) to the concurrency manager. \ + * The time of the job's execution depends on the concurrency manager itself + * (given a generous enough `maxConcurrency` value, it might be immediate, + * but this is not guaranteed). + * @param worker Async function to be executed (job to be processed). + */ + addJob(job: () => Promise): void { + // console.debug("Adding a worker!"); + this.jobQueue.push(job); + + if (!this.maxConcurrency || this.activeWorkers < this.maxConcurrency) { + this.runNextJob(); + this.activeWorkers += 1; + } else { + // console.debug("No capacity to run a worker now, waiting!"); + } + } + + /** + * Waits until there is no running nor waiting job. \ + * If the concurrency manager is idle at the time of calling this function, + * it waits until at least one job is compeleted (can be "presubscribed"). + * @returns Promise, resolved after there is no running/waiting worker. + */ + waitForCompletion(): Promise { + return new Promise((res) => { + this.waiting.push(res); + }); + } +} \ No newline at end of file diff --git a/maxun-core/utils/logger.ts b/maxun-core/utils/logger.ts new file mode 100644 index 00000000..e57421aa --- /dev/null +++ b/maxun-core/utils/logger.ts @@ -0,0 +1,30 @@ +/* +* Logger class for more detailed and comprehensible logs (with colors and timestamps) +*/ + +export enum Level { + DATE = 36, + LOG = 0, + WARN = 93, + ERROR = 31, + DEBUG = 95, + RESET = 0, + } + + export default function logger( + message: string | Error, + level: (Level.LOG | Level.WARN | Level.ERROR | Level.DEBUG) = Level.LOG, + ) { + let m = message; + if (message.constructor.name.includes('Error') && typeof message !== 'string') { + m = (message).message; + } + process.stdout.write(`\x1b[${Level.DATE}m[${(new Date()).toLocaleString()}]\x1b[0m `); + process.stdout.write(`\x1b[${level}m`); + if (level === Level.ERROR || level === Level.WARN) { + process.stderr.write(m); + } else { + process.stdout.write(m); + } + process.stdout.write(`\x1b[${Level.RESET}m\n`); + } \ No newline at end of file diff --git a/maxun-core/utils/utils.ts b/maxun-core/utils/utils.ts new file mode 100644 index 00000000..48883dcf --- /dev/null +++ b/maxun-core/utils/utils.ts @@ -0,0 +1,13 @@ +/** + * ESLint rule in case there is only one util function + * (it still does not represent the "utils" file) +*/ + +/* eslint-disable import/prefer-default-export */ + +/** + * Converts an array of scalars to an object with **items** of the array **for keys**. + */ +export function arrayToObject(array : any[]) { + return array.reduce((p, x) => ({ ...p, [x]: [] }), {}); + } \ No newline at end of file From c6bbd27c717f03f1db87b9b1c2dcd3e5867b8f62 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 20:42:19 +0530 Subject: [PATCH 157/255] refactor: create src dir --- maxun-core/{ => src}/browserSide/scraper.js | 0 maxun-core/{ => src}/index.ts | 0 maxun-core/{ => src}/interpret.ts | 0 maxun-core/{ => src}/preprocessor.ts | 0 maxun-core/{ => src}/types/logic.ts | 0 maxun-core/{ => src}/types/workflow.ts | 0 maxun-core/{ => src}/utils/concurrency.ts | 0 maxun-core/{ => src}/utils/logger.ts | 0 maxun-core/{ => src}/utils/utils.ts | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename maxun-core/{ => src}/browserSide/scraper.js (100%) rename maxun-core/{ => src}/index.ts (100%) rename maxun-core/{ => src}/interpret.ts (100%) rename maxun-core/{ => src}/preprocessor.ts (100%) rename maxun-core/{ => src}/types/logic.ts (100%) rename maxun-core/{ => src}/types/workflow.ts (100%) rename maxun-core/{ => src}/utils/concurrency.ts (100%) rename maxun-core/{ => src}/utils/logger.ts (100%) rename maxun-core/{ => src}/utils/utils.ts (100%) diff --git a/maxun-core/browserSide/scraper.js b/maxun-core/src/browserSide/scraper.js similarity index 100% rename from maxun-core/browserSide/scraper.js rename to maxun-core/src/browserSide/scraper.js diff --git a/maxun-core/index.ts b/maxun-core/src/index.ts similarity index 100% rename from maxun-core/index.ts rename to maxun-core/src/index.ts diff --git a/maxun-core/interpret.ts b/maxun-core/src/interpret.ts similarity index 100% rename from maxun-core/interpret.ts rename to maxun-core/src/interpret.ts diff --git a/maxun-core/preprocessor.ts b/maxun-core/src/preprocessor.ts similarity index 100% rename from maxun-core/preprocessor.ts rename to maxun-core/src/preprocessor.ts diff --git a/maxun-core/types/logic.ts b/maxun-core/src/types/logic.ts similarity index 100% rename from maxun-core/types/logic.ts rename to maxun-core/src/types/logic.ts diff --git a/maxun-core/types/workflow.ts b/maxun-core/src/types/workflow.ts similarity index 100% rename from maxun-core/types/workflow.ts rename to maxun-core/src/types/workflow.ts diff --git a/maxun-core/utils/concurrency.ts b/maxun-core/src/utils/concurrency.ts similarity index 100% rename from maxun-core/utils/concurrency.ts rename to maxun-core/src/utils/concurrency.ts diff --git a/maxun-core/utils/logger.ts b/maxun-core/src/utils/logger.ts similarity index 100% rename from maxun-core/utils/logger.ts rename to maxun-core/src/utils/logger.ts diff --git a/maxun-core/utils/utils.ts b/maxun-core/src/utils/utils.ts similarity index 100% rename from maxun-core/utils/utils.ts rename to maxun-core/src/utils/utils.ts From d91e88c30e13f123c0b72d49e46f23a301590a89 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 20:45:07 +0530 Subject: [PATCH 158/255] feat(core): ts config --- maxun-core/jest.config.js | 10 ++++++++++ maxun-core/tsconfig.json | 8 ++++++++ 2 files changed, 18 insertions(+) create mode 100644 maxun-core/jest.config.js create mode 100644 maxun-core/tsconfig.json diff --git a/maxun-core/jest.config.js b/maxun-core/jest.config.js new file mode 100644 index 00000000..e4e978ec --- /dev/null +++ b/maxun-core/jest.config.js @@ -0,0 +1,10 @@ +/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + globals: { + 'ts-jest': { + isolatedModules: true + } + } +}; \ No newline at end of file diff --git a/maxun-core/tsconfig.json b/maxun-core/tsconfig.json new file mode 100644 index 00000000..a3813d95 --- /dev/null +++ b/maxun-core/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "outDir": "./build", + "declaration": true, + "allowJs": true + }, + "include": ["src"] +} From 99ba2629cf4fdc153b1e4105bacd946a371dfcc3 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 20:48:25 +0530 Subject: [PATCH 159/255] chore(core): install joi & playwright --- maxun-core/package.json | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 maxun-core/package.json diff --git a/maxun-core/package.json b/maxun-core/package.json new file mode 100644 index 00000000..2fa29f22 --- /dev/null +++ b/maxun-core/package.json @@ -0,0 +1,29 @@ +{ + "name": "maxun-core", + "version": "0.0.1", + "description": "Smart Workflow interpreter", + "main": "build/index.js", + "typings": "build/index.d.ts", + "scripts": { + "test": "jest", + "build": "npm run clean && tsc", + "lint": "eslint .", + "clean": "rimraf ./build" + }, + "files": [ + "build/*" + ], + "keywords": [ + "web", + "automation", + "workflow", + "interpret", + "scraping" + ], + "author": "Karishma Shukla", + "license": "MIT", + "dependencies": { + "joi": "^17.6.0", + "playwright": "^1.20.1" + } +} From 0ee54bf1f0912b5ac317516ba5c7668f9b963e96 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 21:10:25 +0530 Subject: [PATCH 160/255] feat: use maxun-core --- .../src/browser-management/inputHandlers.ts | 1 + server/src/routes/storage.ts | 235 ++++++++++++++++++ server/src/workflow-management/selector.ts | 2 +- 3 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 server/src/routes/storage.ts diff --git a/server/src/browser-management/inputHandlers.ts b/server/src/browser-management/inputHandlers.ts index d6902b3f..2e43a4c5 100644 --- a/server/src/browser-management/inputHandlers.ts +++ b/server/src/browser-management/inputHandlers.ts @@ -271,6 +271,7 @@ const handleChangeUrl = async (generator: WorkflowGenerator, page: Page, url: st try { await page.goto(url); logger.log('debug', `Went to ${url}`); + console.log(`Went to ${url}`) } catch (e) { const { message } = e as Error; logger.log('error', message); diff --git a/server/src/routes/storage.ts b/server/src/routes/storage.ts new file mode 100644 index 00000000..beed25bb --- /dev/null +++ b/server/src/routes/storage.ts @@ -0,0 +1,235 @@ +/** + * RESTful API endpoints handling the recording storage. + */ + +import { Router } from 'express'; +import logger from "../logger"; +import { deleteFile, readFile, readFiles, saveFile } from "../workflow-management/storage"; +import { createRemoteBrowserForRun, destroyRemoteBrowser } from "../browser-management/controller"; +import { chromium } from "playwright"; +import { browserPool } from "../server"; +import fs from "fs"; +import { uuid } from "uuidv4"; + +export const router = Router(); + +/** + * Logs information about recordings API. + */ +router.all('/', (req, res, next) => { + logger.log('debug',`The recordings API was invoked: ${req.url}`) + next() // pass control to the next handler +}) + +/** + * GET endpoint for getting an array of all stored recordings. + */ +router.get('/recordings', async (req, res) => { + try { + const data = await readFiles('./../storage/recordings/'); + return res.send(data); + } catch (e) { + logger.log('info', 'Error while reading recordings'); + return res.send(null); + } +}); + +/** + * DELETE endpoint for deleting a recording from the storage. + */ +router.delete('/recordings/:fileName', async (req, res) => { + try { + await deleteFile(`./../storage/recordings/${req.params.fileName}.waw.json`); + return res.send(true); + } catch (e) { + const {message} = e as Error; + logger.log('info', `Error while deleting a recording with name: ${req.params.fileName}.waw.json`); + return res.send(false); + } +}); + +/** + * GET endpoint for getting an array of runs from the storage. + */ +router.get('/runs', async (req, res) => { + try { + const data = await readFiles('./../storage/runs/'); + return res.send(data); + } catch (e) { + logger.log('info', 'Error while reading runs'); + return res.send(null); + } +}); + +/** + * DELETE endpoint for deleting a run from the storage. + */ +router.delete('/runs/:fileName', async (req, res) => { + try { + await deleteFile(`./../storage/runs/${req.params.fileName}.json`); + return res.send(true); + } catch (e) { + const {message} = e as Error; + logger.log('info', `Error while deleting a run with name: ${req.params.fileName}.json`); + return res.send(false); + } +}); + +/** + * PUT endpoint for starting a remote browser instance and saving run metadata to the storage. + * Making it ready for interpretation and returning a runId. + */ +router.put('/runs/:fileName', async (req, res) => { + try { + const id = createRemoteBrowserForRun({ + browser: chromium, + launchOptions: { headless: true } + }); + + const runId = uuid(); + + const run_meta = { + status: 'RUNNING', + name: req.params.fileName, + startedAt: new Date().toLocaleString(), + finishedAt: '', + duration: '', + task: req.body.params ? 'task' : '', + browserId: id, + interpreterSettings: req.body, + log: '', + runId, + }; + fs.mkdirSync('../storage/runs', { recursive: true }) + await saveFile( + `../storage/runs/${req.params.fileName}_${runId}.json`, + JSON.stringify({ ...run_meta }, null, 2) + ); + logger.log('debug', `Created run with name: ${req.params.fileName}.json`); + return res.send({ + browserId: id, + runId: runId, + }); + } catch (e) { + const {message} = e as Error; + logger.log('info', `Error while creating a run with name: ${req.params.fileName}.json`); + return res.send(''); + } +}); + +/** + * GET endpoint for getting a run from the storage. + */ +router.get('/runs/run/:fileName/:runId', async (req, res) => { + try { + // read the run from storage + const run = await readFile(`./../storage/runs/${req.params.fileName}_${req.params.runId}.json`) + const parsedRun = JSON.parse(run); + return res.send(parsedRun); + } catch (e) { + const { message } = e as Error; + logger.log('error', `Error ${message} while reading a run with name: ${req.params.fileName}_${req.params.runId}.json`); + return res.send(null); + } +}); + +/** + * PUT endpoint for finishing a run and saving it to the storage. + */ +router.post('/runs/run/:fileName/:runId', async (req, res) => { + try { + // read the recording from storage + const recording = await readFile(`./../storage/recordings/${req.params.fileName}.waw.json`) + const parsedRecording = JSON.parse(recording); + // read the run from storage + const run = await readFile(`./../storage/runs/${req.params.fileName}_${req.params.runId}.json`) + const parsedRun = JSON.parse(run); + + // interpret the run in active browser + const browser = browserPool.getRemoteBrowser(parsedRun.browserId); + const currentPage = browser?.getCurrentPage(); + if (browser && currentPage) { + const interpretationInfo = await browser.interpreter.InterpretRecording( + parsedRecording.recording, currentPage, parsedRun.interpreterSettings); + const duration = Math.round((new Date().getTime() - new Date(parsedRun.startedAt).getTime()) / 1000); + const durString = (() => { + if (duration < 60) { + return `${duration} s`; + } + else { + const minAndS = (duration / 60).toString().split('.'); + return `${minAndS[0]} m ${minAndS[1]} s`; + } + })(); + await destroyRemoteBrowser(parsedRun.browserId); + const run_meta = { + ...parsedRun, + status: interpretationInfo.result, + finishedAt: new Date().toLocaleString(), + duration: durString, + browserId: null, + log: interpretationInfo.log.join('\n'), + serializableOutput: interpretationInfo.serializableOutput, + binaryOutput: interpretationInfo.binaryOutput, + }; + fs.mkdirSync('../storage/runs', { recursive: true }) + await saveFile( + `../storage/runs/${parsedRun.name}_${req.params.runId}.json`, + JSON.stringify(run_meta, null, 2) + ); + return res.send(true); + } else { + throw new Error('Could not destroy browser'); + } + } catch (e) { + const {message} = e as Error; + logger.log('info', `Error while running a recording with name: ${req.params.fileName}_${req.params.runId}.json`); + return res.send(false); + } +}); + +/** + * POST endpoint for aborting a current interpretation of the run. + */ +router.post('/runs/abort/:fileName/:runId', async (req, res) => { + try { + // read the run from storage + const run = await readFile(`./../storage/runs/${req.params.fileName}_${req.params.runId}.json`) + const parsedRun = JSON.parse(run); + + //get current log + const browser = browserPool.getRemoteBrowser(parsedRun.browserId); + const currentLog = browser?.interpreter.debugMessages.join('/n'); + const serializableOutput = browser?.interpreter.serializableData.reduce((reducedObject, item, index) => { + return { + [`item-${index}`]: item, + ...reducedObject, + } + }, {}); + const binaryOutput = browser?.interpreter.binaryData.reduce((reducedObject, item, index) => { + return { + [`item-${index}`]: item, + ...reducedObject, + } + }, {}); + const run_meta = { + ...parsedRun, + status: 'ABORTED', + finishedAt: null, + duration: '', + browserId: null, + log: currentLog, + }; + + fs.mkdirSync('../storage/runs', { recursive: true }) + await saveFile( + `../storage/runs/${parsedRun.name}_${req.params.runId}.json`, + JSON.stringify({ ...run_meta, serializableOutput, binaryOutput }, null, 2) + ); + return res.send(true); + } catch (e) { + const {message} = e as Error; + logger.log('info', `Error while running a recording with name: ${req.params.fileName}_${req.params.runId}.json`); + return res.send(false); + } +}); diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 0290ee9f..9c8f1b8c 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -111,7 +111,7 @@ export const getElementInformation = async ( console.log(`Element innerText: ${elementInfo.innerText}`); } } - + return elementInfo; } catch (error) { const { message, stack } = error as Error; From 820939ec78d4f670c93a0fde493bc7ec29c96ce2 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 21:10:52 +0530 Subject: [PATCH 161/255] feat: use maxun-core --- src/api/storage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/storage.ts b/src/api/storage.ts index acae2121..93e99212 100644 --- a/src/api/storage.ts +++ b/src/api/storage.ts @@ -1,5 +1,5 @@ import { default as axios } from "axios"; -import { WorkflowFile } from "@wbr-project/wbr-interpret"; +import { WorkflowFile } from "maxun-core"; import { RunSettings } from "../components/molecules/RunSettings"; import { CreateRunResponse } from "../pages/MainPage"; From 19ca0fa517b1a86e0291b138a02d6a69ed7ddd2a Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 22:46:19 +0530 Subject: [PATCH 162/255] fix: use maxun-core --- server/src/workflow-management/classes/Generator.ts | 2 +- server/src/workflow-management/classes/Interpreter.ts | 6 +++--- server/src/workflow-management/selector.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/server/src/workflow-management/classes/Generator.ts b/server/src/workflow-management/classes/Generator.ts index 4b66b54b..ae2d2016 100644 --- a/server/src/workflow-management/classes/Generator.ts +++ b/server/src/workflow-management/classes/Generator.ts @@ -1,5 +1,5 @@ import { Action, ActionType, Coordinates, TagName } from "../../types"; -import { WhereWhatPair, WorkflowFile } from '@wbr-project/wbr-interpret'; +import { WhereWhatPair, WorkflowFile } from 'maxun-core'; import logger from "../../logger"; import { Socket } from "socket.io"; import { Page } from "playwright"; diff --git a/server/src/workflow-management/classes/Interpreter.ts b/server/src/workflow-management/classes/Interpreter.ts index ff3d8c08..fa5e9332 100644 --- a/server/src/workflow-management/classes/Interpreter.ts +++ b/server/src/workflow-management/classes/Interpreter.ts @@ -1,4 +1,4 @@ -import Interpreter, { WorkflowFile } from "@wbr-project/wbr-interpret"; +import Interpreter, { WorkflowFile } from "maxun-core"; import logger from "../../logger"; import { Socket } from "socket.io"; import { Page } from "playwright"; @@ -8,7 +8,7 @@ import { InterpreterSettings } from "../../types"; * This class implements the main interpretation functions. * It holds some information about the current interpretation process and * registers to some events to allow the client (frontend) to interact with the interpreter. - * It uses the [@wbr-project/wbr-interpret](https://www.npmjs.com/package/@wbr-project/wbr-interpret) + * It uses the [maxun-core](https://www.npmjs.com/package/maxun-core) * library to interpret the workflow. * @category WorkflowManagement */ @@ -26,7 +26,7 @@ export class WorkflowInterpreter { /** * The instance of the {@link Interpreter} class used to interpret the workflow. - * From @wbr-project/wbr-interpret. + * From maxun-core. * @private */ private interpreter: Interpreter | null = null; diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 9c8f1b8c..b94ed2d1 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -1,6 +1,6 @@ import { Page } from "playwright"; import { Action, ActionType, Coordinates, TagName } from "../types"; -import { WhereWhatPair, WorkflowFile } from "@wbr-project/wbr-interpret"; +import { WhereWhatPair, WorkflowFile } from "maxun-core"; import logger from "../logger"; import { getBestSelectorForAction } from "./utils"; From 7d57e5adc068688029dbf9d54e77e53fad6e5ccb Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Wed, 31 Jul 2024 22:46:38 +0530 Subject: [PATCH 163/255] fix: use maxun-core --- src/api/workflow.ts | 2 +- src/components/atoms/PairDisplayDiv.tsx | 2 +- src/components/molecules/AddWhatCondModal.tsx | 2 +- src/components/molecules/AddWhereCondModal.tsx | 2 +- src/components/molecules/InterpretationButtons.tsx | 2 +- src/components/molecules/LeftSidePanelContent.tsx | 2 +- src/components/molecules/Pair.tsx | 2 +- src/components/molecules/PairDetail.tsx | 2 +- src/components/molecules/PairEditForm.tsx | 2 +- src/components/molecules/RecordingsTable.tsx | 2 +- src/components/molecules/SidePanelHeader.tsx | 2 +- src/components/organisms/LeftSidePanel.tsx | 2 +- src/pages/RecordingPage.tsx | 2 +- src/shared/constants.ts | 2 +- src/shared/types.ts | 4 ++-- 15 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/api/workflow.ts b/src/api/workflow.ts index 212737cb..dd76d9ac 100644 --- a/src/api/workflow.ts +++ b/src/api/workflow.ts @@ -1,4 +1,4 @@ -import { WhereWhatPair, WorkflowFile } from "@wbr-project/wbr-interpret"; +import { WhereWhatPair, WorkflowFile } from "maxun-core"; import { emptyWorkflow } from "../shared/constants"; const axios = require('axios').default; diff --git a/src/components/atoms/PairDisplayDiv.tsx b/src/components/atoms/PairDisplayDiv.tsx index 3611e135..c7c4447c 100644 --- a/src/components/atoms/PairDisplayDiv.tsx +++ b/src/components/atoms/PairDisplayDiv.tsx @@ -1,6 +1,6 @@ import React, { FC } from 'react'; import Typography from '@mui/material/Typography'; -import { WhereWhatPair } from "@wbr-project/wbr-interpret"; +import { WhereWhatPair } from "maxun-core"; import styled from "styled-components"; interface PairDisplayDivProps { diff --git a/src/components/molecules/AddWhatCondModal.tsx b/src/components/molecules/AddWhatCondModal.tsx index 714c0fef..5fadfaa6 100644 --- a/src/components/molecules/AddWhatCondModal.tsx +++ b/src/components/molecules/AddWhatCondModal.tsx @@ -1,4 +1,4 @@ -import { WhereWhatPair } from "@wbr-project/wbr-interpret"; +import { WhereWhatPair } from "maxun-core"; import { GenericModal } from "../atoms/GenericModal"; import { modalStyle } from "./AddWhereCondModal"; import { Button, MenuItem, TextField, Typography } from "@mui/material"; diff --git a/src/components/molecules/AddWhereCondModal.tsx b/src/components/molecules/AddWhereCondModal.tsx index 182a8dd6..e5c34015 100644 --- a/src/components/molecules/AddWhereCondModal.tsx +++ b/src/components/molecules/AddWhereCondModal.tsx @@ -6,7 +6,7 @@ import { } from "@mui/material"; import React, { useRef } from "react"; import { GenericModal } from "../atoms/GenericModal"; -import { WhereWhatPair } from "@wbr-project/wbr-interpret"; +import { WhereWhatPair } from "maxun-core"; import { SelectChangeEvent } from "@mui/material/Select/Select"; import { DisplayConditionSettings } from "./DisplayWhereConditionSettings"; import { useSocketStore } from "../../context/socket"; diff --git a/src/components/molecules/InterpretationButtons.tsx b/src/components/molecules/InterpretationButtons.tsx index bedb85af..7e0633af 100644 --- a/src/components/molecules/InterpretationButtons.tsx +++ b/src/components/molecules/InterpretationButtons.tsx @@ -5,7 +5,7 @@ import { interpretCurrentRecording, stopCurrentInterpretation } from "../../api/ import { useSocketStore } from "../../context/socket"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { GenericModal } from "../atoms/GenericModal"; -import { WhereWhatPair } from "@wbr-project/wbr-interpret"; +import { WhereWhatPair } from "maxun-core"; import HelpIcon from '@mui/icons-material/Help'; interface InterpretationButtonsProps { diff --git a/src/components/molecules/LeftSidePanelContent.tsx b/src/components/molecules/LeftSidePanelContent.tsx index 5c19996e..79750d8f 100644 --- a/src/components/molecules/LeftSidePanelContent.tsx +++ b/src/components/molecules/LeftSidePanelContent.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import Box from "@mui/material/Box"; import { Pair } from "./Pair"; -import { WhereWhatPair, WorkflowFile } from "@wbr-project/wbr-interpret"; +import { WhereWhatPair, WorkflowFile } from "maxun-core"; import { useSocketStore } from "../../context/socket"; import { Add } from "@mui/icons-material"; import { Socket } from "socket.io-client"; diff --git a/src/components/molecules/Pair.tsx b/src/components/molecules/Pair.tsx index 7f501719..b05b912d 100644 --- a/src/components/molecules/Pair.tsx +++ b/src/components/molecules/Pair.tsx @@ -1,7 +1,7 @@ import React, { FC, useState } from 'react'; import { Stack, Button, IconButton, Tooltip, Chip, Badge } from "@mui/material"; import { AddPair, deletePair, UpdatePair } from "../../api/workflow"; -import { WorkflowFile } from "@wbr-project/wbr-interpret"; +import { WorkflowFile } from "maxun-core"; import { ClearButton } from "../atoms/buttons/ClearButton"; import { GenericModal } from "../atoms/GenericModal"; import { PairEditForm } from "./PairEditForm"; diff --git a/src/components/molecules/PairDetail.tsx b/src/components/molecules/PairDetail.tsx index 9d37bb6d..aea6191b 100644 --- a/src/components/molecules/PairDetail.tsx +++ b/src/components/molecules/PairDetail.tsx @@ -1,5 +1,5 @@ import React, { useLayoutEffect, useRef, useState } from 'react'; -import { WhereWhatPair } from "@wbr-project/wbr-interpret"; +import { WhereWhatPair } from "maxun-core"; import { Box, Button, IconButton, MenuItem, Stack, TextField, Tooltip, Typography } from "@mui/material"; import { Close, KeyboardArrowDown, KeyboardArrowUp } from "@mui/icons-material"; import TreeView from '@mui/lab/TreeView'; diff --git a/src/components/molecules/PairEditForm.tsx b/src/components/molecules/PairEditForm.tsx index 815acfdd..7ab9c381 100644 --- a/src/components/molecules/PairEditForm.tsx +++ b/src/components/molecules/PairEditForm.tsx @@ -1,6 +1,6 @@ import { Button, TextField, Typography } from "@mui/material"; import React, { FC } from "react"; -import { Preprocessor, WhereWhatPair } from "@wbr-project/wbr-interpret"; +import { Preprocessor, WhereWhatPair } from "maxun-core"; interface PairProps { index: string; diff --git a/src/components/molecules/RecordingsTable.tsx b/src/components/molecules/RecordingsTable.tsx index df389a41..ee510761 100644 --- a/src/components/molecules/RecordingsTable.tsx +++ b/src/components/molecules/RecordingsTable.tsx @@ -8,7 +8,7 @@ import TableHead from '@mui/material/TableHead'; import TablePagination from '@mui/material/TablePagination'; import TableRow from '@mui/material/TableRow'; import { useEffect } from "react"; -import { WorkflowFile } from "@wbr-project/wbr-interpret"; +import { WorkflowFile } from "maxun-core"; import { IconButton } from "@mui/material"; import { Assignment, DeleteForever, Edit, PlayCircle } from "@mui/icons-material"; import { useGlobalInfoStore } from "../../context/globalInfo"; diff --git a/src/components/molecules/SidePanelHeader.tsx b/src/components/molecules/SidePanelHeader.tsx index 7743230e..54ae3060 100644 --- a/src/components/molecules/SidePanelHeader.tsx +++ b/src/components/molecules/SidePanelHeader.tsx @@ -3,7 +3,7 @@ import { InterpretationButtons } from "./InterpretationButtons"; import { AddButton } from "../atoms/buttons/AddButton"; import { GenericModal } from "../atoms/GenericModal"; import { PairEditForm } from "./PairEditForm"; -import { WhereWhatPair, WorkflowFile } from "@wbr-project/wbr-interpret"; +import { WhereWhatPair, WorkflowFile } from "maxun-core"; import { AddPair } from "../../api/workflow"; import { Button, Stack } from "@mui/material"; import { FastForward } from "@mui/icons-material"; diff --git a/src/components/organisms/LeftSidePanel.tsx b/src/components/organisms/LeftSidePanel.tsx index 3319e536..164dc38c 100644 --- a/src/components/organisms/LeftSidePanel.tsx +++ b/src/components/organisms/LeftSidePanel.tsx @@ -2,7 +2,7 @@ import { Box, Paper, Tab, Tabs } from "@mui/material"; import React, { useCallback, useEffect, useState } from "react"; import { getActiveWorkflow, getParamsOfActiveWorkflow } from "../../api/workflow"; import { useSocketStore } from '../../context/socket'; -import { WhereWhatPair, WorkflowFile } from "@wbr-project/wbr-interpret"; +import { WhereWhatPair, WorkflowFile } from "maxun-core"; import { SidePanelHeader } from "../molecules/SidePanelHeader"; import { emptyWorkflow } from "../../shared/constants"; import { LeftSidePanelContent } from "../molecules/LeftSidePanelContent"; diff --git a/src/pages/RecordingPage.tsx b/src/pages/RecordingPage.tsx index 26b12ede..cc229fd9 100644 --- a/src/pages/RecordingPage.tsx +++ b/src/pages/RecordingPage.tsx @@ -11,7 +11,7 @@ import { ActionProvider } from "../context/browserActions" import { BrowserStepsProvider } from '../context/browserSteps'; import { useGlobalInfoStore } from "../context/globalInfo"; import { editRecordingFromStorage } from "../api/storage"; -import { WhereWhatPair } from "@wbr-project/wbr-interpret"; +import { WhereWhatPair } from "maxun-core"; import styled from "styled-components"; interface RecordingPageProps { diff --git a/src/shared/constants.ts b/src/shared/constants.ts index d7ce0bd0..de805266 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -1,3 +1,3 @@ -import { WorkflowFile } from "@wbr-project/wbr-interpret"; +import { WorkflowFile } from "maxun-core"; export const emptyWorkflow: WorkflowFile = { workflow: [] }; diff --git a/src/shared/types.ts b/src/shared/types.ts index 72e86a0e..aa5f254e 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -1,4 +1,4 @@ -import { WorkflowFile } from "@wbr-project/wbr-interpret"; +import { WorkflowFile } from "maxun-core"; import { Locator } from "playwright"; export type Workflow = WorkflowFile["workflow"]; @@ -15,7 +15,7 @@ export interface ScreenshotSettings { fullPage?: boolean; mask?: Locator[]; omitBackground?: boolean; - // is this still needed? - @wbr-project/wbr-interpret outputs to a binary output + // is this still needed? - maxun-core outputs to a binary output path?: string; quality?: number; scale?: "css" | "device"; From 92bb30d8b7edf7234d9c8a913ad77ad26de4bcc5 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Thu, 1 Aug 2024 19:16:13 +0530 Subject: [PATCH 164/255] fix(core): script injection for scrape | scrapeSchema --- maxun-core/src/interpret.ts | 48 ++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index 9ac32b0e..c7cedc02 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -277,25 +277,39 @@ export default class Interpreter extends EventEmitter { await page.close(); }, scrape: async (selector?: string) => { - const scrapeResults: Record[] = await page - // eslint-disable-next-line - // @ts-ignore - .evaluate((s) => scrape(s ?? null), selector); + // Check if 'scrape' function is available in the page context + const isScrapeAvailable = await page.evaluate(() => typeof window.scrape === 'function'); + + if (!isScrapeAvailable) { + // Inject the script that defines the 'scrape' function + await page.addScriptTag({ path: path.join(__dirname, 'browserSide', 'scraper.js') }); + } + + const scrapeResults: Record[] = await page.evaluate((s) => window.scrape(s ?? null), selector); await this.options.serializableCallback(scrapeResults); - }, - scrapeSchema: async (schema: Record) => { + }, + + scrapeSchema: async (schema: Record) => { + // Check if 'scrapeSchema' function is available in the page context + const isScrapeSchemaAvailable = await page.evaluate(() => typeof window.scrapeSchema === 'function'); + + if (!isScrapeSchemaAvailable) { + // Inject the script that defines the 'scrapeSchema' function + await page.addScriptTag({ path: path.join(__dirname, 'browserSide', 'scraper.js') }); + } + const handleLists = await Promise.all( - Object.values(schema).map((selector) => page.$$(selector)), + Object.values(schema).map((selector) => page.$$(selector)), ); - + const namedHandleLists = Object.fromEntries( - Object.keys(schema).map((key, i) => [key, handleLists[i]]), + Object.keys(schema).map((key, i) => [key, handleLists[i]]), ); - - const scrapeResult = await page.evaluate((n) => scrapeSchema(n), namedHandleLists); - - this.options.serializableCallback(scrapeResult); - }, + + const scrapeResult = await page.evaluate((n) => window.scrapeSchema(n), namedHandleLists); + await this.options.serializableCallback(scrapeResult); + }, + scroll: async (pages?: number) => { await page.evaluate(async (pagesInternal) => { for (let i = 1; i <= (pagesInternal ?? 1); i += 1) { @@ -433,9 +447,9 @@ export default class Interpreter extends EventEmitter { this.initializedWorkflow = Preprocessor.initWorkflow(this.workflow, params); // @ts-ignore - if (await page.evaluate(() => !window.scrape)) { - page.context().addInitScript({ path: path.join(__dirname, 'browserSide', 'scraper.js') }); - } + // if (await page.evaluate(() => !window.scrape)) { + // page.context().addInitScript({ path: path.join(__dirname, 'browserSide', 'scraper.js') }); + // } this.stopper = () => { this.stopper = null; From 12e40f089d56d8402c9f1cc00d368cca0b9e6a9e Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 2 Aug 2024 13:10:26 +0530 Subject: [PATCH 165/255] feat: bypass CSP by setting extra HTTP headers for page context --- maxun-core/src/interpret.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index c7cedc02..bac0150b 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -451,6 +451,11 @@ export default class Interpreter extends EventEmitter { // page.context().addInitScript({ path: path.join(__dirname, 'browserSide', 'scraper.js') }); // } + // Enable CSP bypass for the page + await page.context().setExtraHTTPHeaders({ + 'Content-Security-Policy': '' + }); + this.stopper = () => { this.stopper = null; }; From 2aa7cdc4bc1a15b6638c57c6bd220baab1b659c0 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 2 Aug 2024 19:05:41 +0530 Subject: [PATCH 166/255] fix: use addInitScript THE RIGHT WAY!!! --- maxun-core/src/interpret.ts | 40 +++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index bac0150b..a3f3b1cc 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -1,5 +1,5 @@ /* eslint-disable no-await-in-loop, no-restricted-syntax */ -import { Page, PageScreenshotOptions } from 'playwright'; +import { Page, PageScreenshotOptions, BrowserContextOptions } from 'playwright'; import path from 'path'; import { EventEmitter } from 'events'; @@ -277,26 +277,28 @@ export default class Interpreter extends EventEmitter { await page.close(); }, scrape: async (selector?: string) => { + await this.ensureScriptsLoaded(page); // Check if 'scrape' function is available in the page context - const isScrapeAvailable = await page.evaluate(() => typeof window.scrape === 'function'); + // const isScrapeAvailable = await page.evaluate(() => typeof window.scrape === 'function'); - if (!isScrapeAvailable) { - // Inject the script that defines the 'scrape' function - await page.addScriptTag({ path: path.join(__dirname, 'browserSide', 'scraper.js') }); - } + // if (!isScrapeAvailable) { + // // Inject the script that defines the 'scrape' function + // await page.addScriptTag({ path: path.join(__dirname, 'browserSide', 'scraper.js') }); + // } const scrapeResults: Record[] = await page.evaluate((s) => window.scrape(s ?? null), selector); await this.options.serializableCallback(scrapeResults); }, scrapeSchema: async (schema: Record) => { + await this.ensureScriptsLoaded(page); // Check if 'scrapeSchema' function is available in the page context - const isScrapeSchemaAvailable = await page.evaluate(() => typeof window.scrapeSchema === 'function'); + // const isScrapeSchemaAvailable = await page.evaluate(() => typeof window.scrapeSchema === 'function'); - if (!isScrapeSchemaAvailable) { - // Inject the script that defines the 'scrapeSchema' function - await page.addScriptTag({ path: path.join(__dirname, 'browserSide', 'scraper.js') }); - } + // if (!isScrapeSchemaAvailable) { + // // Inject the script that defines the 'scrapeSchema' function + // await page.addScriptTag({ path: path.join(__dirname, 'browserSide', 'scraper.js') }); + // } const handleLists = await Promise.all( Object.values(schema).map((selector) => page.$$(selector)), @@ -429,6 +431,13 @@ export default class Interpreter extends EventEmitter { } } + private async ensureScriptsLoaded(page: Page) { + const isScriptLoaded = await page.evaluate(() => typeof window.scrape === 'function' && typeof window.scrapeSchema === 'function'); + if (!isScriptLoaded) { + await page.addInitScript({ path: path.join(__dirname, 'browserSide', 'scraper.js') }); + } + } + /** * Spawns a browser context and runs given workflow. * \ @@ -446,15 +455,16 @@ export default class Interpreter extends EventEmitter { */ this.initializedWorkflow = Preprocessor.initWorkflow(this.workflow, params); + await this.ensureScriptsLoaded(page); + // @ts-ignore // if (await page.evaluate(() => !window.scrape)) { // page.context().addInitScript({ path: path.join(__dirname, 'browserSide', 'scraper.js') }); // } - // Enable CSP bypass for the page - await page.context().setExtraHTTPHeaders({ - 'Content-Security-Policy': '' - }); + // await page.context({ + // bypassCSP: true, + // }) this.stopper = () => { this.stopper = null; From 85409051922d58e97e9e0bd3d713896ada81a9e0 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 2 Aug 2024 19:07:18 +0530 Subject: [PATCH 167/255] feat: wrap scrape & scrapeSchema in IIFE --- maxun-core/src/browserSide/scraper.js | 92 +++++++++++++++------------ 1 file changed, 51 insertions(+), 41 deletions(-) diff --git a/maxun-core/src/browserSide/scraper.js b/maxun-core/src/browserSide/scraper.js index c411f642..d67c425a 100644 --- a/maxun-core/src/browserSide/scraper.js +++ b/maxun-core/src/browserSide/scraper.js @@ -130,58 +130,65 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, * Returns a "scrape" result from the current page. * @returns {Array} *Curated* array of scraped information (with sparse rows removed) */ -function scrape(selector = null) { +// Wrap the entire function in an IIFE (Immediately Invoked Function Expression) +// and attach it to the window object +(function(window) { /** - * **crudeRecords** contains uncurated rundowns of "scrapable" elements - * @type {Array} + * Returns a "scrape" result from the current page. + * @returns {Array} *Curated* array of scraped information (with sparse rows removed) */ - const crudeRecords = (selector - ? Array.from(document.querySelectorAll(selector)) - : scrapableHeuristics()) - .map((record) => ({ - ...Array.from(record.querySelectorAll('img')) - .reduce((p, x, i) => { - let url = null; - if (x.srcset) { - const urls = x.srcset.split(', '); - [url] = urls[urls.length - 1].split(' '); - } + window.scrape = function(selector = null) { + /** + * **crudeRecords** contains uncurated rundowns of "scrapable" elements + * @type {Array} + */ + const crudeRecords = (selector + ? Array.from(document.querySelectorAll(selector)) + : scrapableHeuristics()) + .map((record) => ({ + ...Array.from(record.querySelectorAll('img')) + .reduce((p, x, i) => { + let url = null; + if (x.srcset) { + const urls = x.srcset.split(', '); + [url] = urls[urls.length - 1].split(' '); + } - /** - * Contains the largest elements from `srcset` - if `srcset` is not present, contains - * URL from the `src` attribute - * - * If the `src` attribute contains a data url, imgUrl contains `undefined`. - */ - let imgUrl; - if (x.srcset) { - imgUrl = url; - } else if (x.src.indexOf('data:') === -1) { - imgUrl = x.src; - } + /** + * Contains the largest elements from `srcset` - if `srcset` is not present, contains + * URL from the `src` attribute + * + * If the `src` attribute contains a data url, imgUrl contains `undefined`. + */ + let imgUrl; + if (x.srcset) { + imgUrl = url; + } else if (x.src.indexOf('data:') === -1) { + imgUrl = x.src; + } - return ({ + return ({ + ...p, + ...(imgUrl ? { [`img_${i}`]: imgUrl } : {}), + }); + }, {}), + ...record.innerText.split('\n') + .reduce((p, x, i) => ({ ...p, - ...(imgUrl ? { [`img_${i}`]: imgUrl } : {}), - }); - }, {}), - ...record.innerText.split('\n') - .reduce((p, x, i) => ({ - ...p, - [`record_${String(i).padStart(4, '0')}`]: x.trim(), - }), {}), - })); + [`record_${String(i).padStart(4, '0')}`]: x.trim(), + }), {}), + })); - return crudeRecords; -} + return crudeRecords; + }; -/** + /** * Given an object with named lists of elements, * groups the elements by their distance in the DOM tree. * @param {Object.} lists The named lists of HTML elements. * @returns {Array.>} */ -function scrapeSchema(lists) { +window.scrapeSchema = function (lists) { function omap(object, f, kf = (x) => x) { return Object.fromEntries( Object.entries(object) @@ -223,4 +230,7 @@ function scrapeSchema(lists) { lists, (listOfElements) => listOfElements.find((elem) => mbe.contains(elem))?.innerText, )); -} \ No newline at end of file +} + + +})(window); \ No newline at end of file From 9b182936431541861f85b409b53523b3c38212d9 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 2 Aug 2024 19:07:58 +0530 Subject: [PATCH 168/255] chore: lint --- maxun-core/src/browserSide/scraper.js | 89 +++++++++++++-------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/maxun-core/src/browserSide/scraper.js b/maxun-core/src/browserSide/scraper.js index d67c425a..6bb5e61a 100644 --- a/maxun-core/src/browserSide/scraper.js +++ b/maxun-core/src/browserSide/scraper.js @@ -132,12 +132,12 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, */ // Wrap the entire function in an IIFE (Immediately Invoked Function Expression) // and attach it to the window object -(function(window) { +(function (window) { /** * Returns a "scrape" result from the current page. * @returns {Array} *Curated* array of scraped information (with sparse rows removed) */ - window.scrape = function(selector = null) { + window.scrape = function (selector = null) { /** * **crudeRecords** contains uncurated rundowns of "scrapable" elements * @type {Array} @@ -188,49 +188,48 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, * @param {Object.} lists The named lists of HTML elements. * @returns {Array.>} */ -window.scrapeSchema = function (lists) { - function omap(object, f, kf = (x) => x) { - return Object.fromEntries( - Object.entries(object) - .map(([k, v]) => [kf(k), f(v)]), - ); + window.scrapeSchema = function (lists) { + function omap(object, f, kf = (x) => x) { + return Object.fromEntries( + Object.entries(object) + .map(([k, v]) => [kf(k), f(v)]), + ); + } + + function ofilter(object, f) { + return Object.fromEntries( + Object.entries(object) + .filter(([k, v]) => f(k, v)), + ); + } + + function getSeedKey(listObj) { + const maxLength = Math.max(...Object.values(omap(listObj, (x) => x.length))); + return Object.keys(ofilter(listObj, (_, v) => v.length === maxLength))[0]; + } + + function getMBEs(elements) { + return elements.map((element) => { + let candidate = element; + const isUniqueChild = (e) => elements + .filter((elem) => e.parentNode?.contains(elem)) + .length === 1; + + while (candidate && isUniqueChild(candidate)) { + candidate = candidate.parentNode; + } + + return candidate; + }); + } + + const seedName = getSeedKey(lists); + const MBEs = getMBEs(lists[seedName]); + + return MBEs.map((mbe) => omap( + lists, + (listOfElements) => listOfElements.find((elem) => mbe.contains(elem))?.innerText, + )); } - function ofilter(object, f) { - return Object.fromEntries( - Object.entries(object) - .filter(([k, v]) => f(k, v)), - ); - } - - function getSeedKey(listObj) { - const maxLength = Math.max(...Object.values(omap(listObj, (x) => x.length))); - return Object.keys(ofilter(listObj, (_, v) => v.length === maxLength))[0]; - } - - function getMBEs(elements) { - return elements.map((element) => { - let candidate = element; - const isUniqueChild = (e) => elements - .filter((elem) => e.parentNode?.contains(elem)) - .length === 1; - - while (candidate && isUniqueChild(candidate)) { - candidate = candidate.parentNode; - } - - return candidate; - }); - } - - const seedName = getSeedKey(lists); - const MBEs = getMBEs(lists[seedName]); - - return MBEs.map((mbe) => omap( - lists, - (listOfElements) => listOfElements.find((elem) => mbe.contains(elem))?.innerText, - )); -} - - })(window); \ No newline at end of file From 7967b686f36f775b263d2852aca9bde8f3e831cb Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 2 Aug 2024 19:09:16 +0530 Subject: [PATCH 169/255] chore: remove unwanted comment --- maxun-core/src/browserSide/scraper.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/maxun-core/src/browserSide/scraper.js b/maxun-core/src/browserSide/scraper.js index 6bb5e61a..994bd96d 100644 --- a/maxun-core/src/browserSide/scraper.js +++ b/maxun-core/src/browserSide/scraper.js @@ -126,12 +126,7 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, return out; } -/** - * Returns a "scrape" result from the current page. - * @returns {Array} *Curated* array of scraped information (with sparse rows removed) - */ -// Wrap the entire function in an IIFE (Immediately Invoked Function Expression) -// and attach it to the window object +// wrap inside an IIFE to avoid polluting the global scope (function (window) { /** * Returns a "scrape" result from the current page. From 142bdaa3a6ad585c2e6fffa79939fe4dfb9b15b6 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Fri, 2 Aug 2024 19:44:15 +0530 Subject: [PATCH 170/255] docs: link gh issue --- 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 994bd96d..a2c32bf9 100644 --- a/maxun-core/src/browserSide/scraper.js +++ b/maxun-core/src/browserSide/scraper.js @@ -126,7 +126,7 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, return out; } -// wrap inside an IIFE to avoid polluting the global scope +// wrap inside an IIFE to avoid polluting the global scope: https://github.com/microsoft/playwright/issues/31864 (function (window) { /** * Returns a "scrape" result from the current page. From 420725a8c38e643e8572ce7d185df19fcd0a4499 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sat, 3 Aug 2024 00:28:54 +0530 Subject: [PATCH 171/255] chore: remove unused import --- 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 a3f3b1cc..00fa4aa7 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -1,5 +1,5 @@ /* eslint-disable no-await-in-loop, no-restricted-syntax */ -import { Page, PageScreenshotOptions, BrowserContextOptions } from 'playwright'; +import { Page, PageScreenshotOptions } from 'playwright'; import path from 'path'; import { EventEmitter } from 'events'; From 92db9e5b93249f67c0a84576a033497a163a2ca4 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sat, 3 Aug 2024 02:20:16 +0530 Subject: [PATCH 172/255] fix: -rm bypassCSP --- maxun-core/src/interpret.ts | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index 00fa4aa7..bbf930c4 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -278,13 +278,6 @@ export default class Interpreter extends EventEmitter { }, scrape: async (selector?: string) => { await this.ensureScriptsLoaded(page); - // Check if 'scrape' function is available in the page context - // const isScrapeAvailable = await page.evaluate(() => typeof window.scrape === 'function'); - - // if (!isScrapeAvailable) { - // // Inject the script that defines the 'scrape' function - // await page.addScriptTag({ path: path.join(__dirname, 'browserSide', 'scraper.js') }); - // } const scrapeResults: Record[] = await page.evaluate((s) => window.scrape(s ?? null), selector); await this.options.serializableCallback(scrapeResults); @@ -292,13 +285,6 @@ export default class Interpreter extends EventEmitter { scrapeSchema: async (schema: Record) => { await this.ensureScriptsLoaded(page); - // Check if 'scrapeSchema' function is available in the page context - // const isScrapeSchemaAvailable = await page.evaluate(() => typeof window.scrapeSchema === 'function'); - - // if (!isScrapeSchemaAvailable) { - // // Inject the script that defines the 'scrapeSchema' function - // await page.addScriptTag({ path: path.join(__dirname, 'browserSide', 'scraper.js') }); - // } const handleLists = await Promise.all( Object.values(schema).map((selector) => page.$$(selector)), @@ -457,15 +443,6 @@ export default class Interpreter extends EventEmitter { await this.ensureScriptsLoaded(page); - // @ts-ignore - // if (await page.evaluate(() => !window.scrape)) { - // page.context().addInitScript({ path: path.join(__dirname, 'browserSide', 'scraper.js') }); - // } - - // await page.context({ - // bypassCSP: true, - // }) - this.stopper = () => { this.stopper = null; }; From 25eb58945ecc9cab594a97b0e3fba4530cd51cde Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sat, 3 Aug 2024 02:20:59 +0530 Subject: [PATCH 173/255] chore: lint --- maxun-core/src/interpret.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index bbf930c4..fec1770f 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -278,26 +278,26 @@ export default class Interpreter extends EventEmitter { }, scrape: async (selector?: string) => { await this.ensureScriptsLoaded(page); - + const scrapeResults: Record[] = await page.evaluate((s) => window.scrape(s ?? null), selector); await this.options.serializableCallback(scrapeResults); - }, - - scrapeSchema: async (schema: Record) => { - await this.ensureScriptsLoaded(page); - + }, + + scrapeSchema: async (schema: Record) => { + await this.ensureScriptsLoaded(page); + const handleLists = await Promise.all( - Object.values(schema).map((selector) => page.$$(selector)), + Object.values(schema).map((selector) => page.$$(selector)), ); - + const namedHandleLists = Object.fromEntries( - Object.keys(schema).map((key, i) => [key, handleLists[i]]), + Object.keys(schema).map((key, i) => [key, handleLists[i]]), ); - + const scrapeResult = await page.evaluate((n) => window.scrapeSchema(n), namedHandleLists); await this.options.serializableCallback(scrapeResult); - }, - + }, + scroll: async (pages?: number) => { await page.evaluate(async (pagesInternal) => { for (let i = 1; i <= (pagesInternal ?? 1); i += 1) { From cf06bbe7033cc51f8b8848f46072f4721de2d30b Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sat, 3 Aug 2024 20:31:55 +0530 Subject: [PATCH 174/255] feat: pass object instead of str for key-val pair --- maxun-core/src/browserSide/scraper.js | 104 ++++++++++++++------------ 1 file changed, 57 insertions(+), 47 deletions(-) diff --git a/maxun-core/src/browserSide/scraper.js b/maxun-core/src/browserSide/scraper.js index a2c32bf9..3911565f 100644 --- a/maxun-core/src/browserSide/scraper.js +++ b/maxun-core/src/browserSide/scraper.js @@ -126,13 +126,18 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, return out; } -// wrap inside an IIFE to avoid polluting the global scope: https://github.com/microsoft/playwright/issues/31864 -(function (window) { +/** + * Returns a "scrape" result from the current page. + * @returns {Array} *Curated* array of scraped information (with sparse rows removed) + */ +// Wrap the entire function in an IIFE (Immediately Invoked Function Expression) +// and attach it to the window object +(function(window) { /** * Returns a "scrape" result from the current page. * @returns {Array} *Curated* array of scraped information (with sparse rows removed) */ - window.scrape = function (selector = null) { + window.scrape = function(selector = null) { /** * **crudeRecords** contains uncurated rundowns of "scrapable" elements * @type {Array} @@ -177,54 +182,59 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, return crudeRecords; }; - /** + /** * Given an object with named lists of elements, * groups the elements by their distance in the DOM tree. - * @param {Object.} lists The named lists of HTML elements. + * @param {Object.} lists The named lists of HTML elements. * @returns {Array.>} */ - window.scrapeSchema = function (lists) { - function omap(object, f, kf = (x) => x) { - return Object.fromEntries( - Object.entries(object) - .map(([k, v]) => [kf(k), f(v)]), - ); - } - - function ofilter(object, f) { - return Object.fromEntries( - Object.entries(object) - .filter(([k, v]) => f(k, v)), - ); - } - - function getSeedKey(listObj) { - const maxLength = Math.max(...Object.values(omap(listObj, (x) => x.length))); - return Object.keys(ofilter(listObj, (_, v) => v.length === maxLength))[0]; - } - - function getMBEs(elements) { - return elements.map((element) => { - let candidate = element; - const isUniqueChild = (e) => elements - .filter((elem) => e.parentNode?.contains(elem)) - .length === 1; - - while (candidate && isUniqueChild(candidate)) { - candidate = candidate.parentNode; - } - - return candidate; - }); - } - - const seedName = getSeedKey(lists); - const MBEs = getMBEs(lists[seedName]); - - return MBEs.map((mbe) => omap( - lists, - (listOfElements) => listOfElements.find((elem) => mbe.contains(elem))?.innerText, - )); +window.scrapeSchema = function (lists) { + function omap(object, f, kf = (x) => x) { + return Object.fromEntries( + Object.entries(object) + .map(([k, v]) => [kf(k), f(v)]), + ); } + function ofilter(object, f) { + return Object.fromEntries( + Object.entries(object) + .filter(([k, v]) => f(k, v)), + ); + } + + function getSeedKey(listObj) { + const maxLength = Math.max(...Object.values(omap(listObj, (x) => document.querySelectorAll(x.selector).length))); + return Object.keys(ofilter(listObj, (_, v) => document.querySelectorAll(v.selector).length === maxLength))[0]; + } + + function getMBEs(elements) { + return elements.map((element) => { + let candidate = element; + const isUniqueChild = (e) => elements + .filter((elem) => e.parentNode?.contains(elem)) + .length === 1; + + while (candidate && isUniqueChild(candidate)) { + candidate = candidate.parentNode; + } + + return candidate; + }); + } + + const seedName = getSeedKey(lists); + const seedElements = Array.from(document.querySelectorAll(lists[seedName].selector)); + const MBEs = getMBEs(seedElements); + + return MBEs.map((mbe) => omap( + lists, + ({ selector }) => { + const elem = Array.from(document.querySelectorAll(selector)).find((elem) => mbe.contains(elem)); + return elem ? elem.innerText : undefined; + }, + (key) => lists[key].selector // Use the selector as the key in the output + )); +} + })(window); \ No newline at end of file From 39b8cdfbb46705edbf6a08b510713ad81c58590b Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sat, 3 Aug 2024 20:32:12 +0530 Subject: [PATCH 175/255] feat: handle object instead of str for key-val pair --- maxun-core/src/interpret.ts | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index fec1770f..bb511631 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -283,18 +283,10 @@ export default class Interpreter extends EventEmitter { await this.options.serializableCallback(scrapeResults); }, - scrapeSchema: async (schema: Record) => { + scrapeSchema: async (schema: Record) => { await this.ensureScriptsLoaded(page); - - const handleLists = await Promise.all( - Object.values(schema).map((selector) => page.$$(selector)), - ); - - const namedHandleLists = Object.fromEntries( - Object.keys(schema).map((key, i) => [key, handleLists[i]]), - ); - - const scrapeResult = await page.evaluate((n) => window.scrapeSchema(n), namedHandleLists); + + const scrapeResult = await page.evaluate((schemaObj) => window.scrapeSchema(schemaObj), schema); await this.options.serializableCallback(scrapeResult); }, From eabe62fff7b05b5d7543d1aca842c9020d1b7ad7 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sat, 3 Aug 2024 20:45:37 +0530 Subject: [PATCH 176/255] fix: preserve original key structure --- maxun-core/src/browserSide/scraper.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/maxun-core/src/browserSide/scraper.js b/maxun-core/src/browserSide/scraper.js index 3911565f..9c9182d5 100644 --- a/maxun-core/src/browserSide/scraper.js +++ b/maxun-core/src/browserSide/scraper.js @@ -182,7 +182,7 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, return crudeRecords; }; - /** +/** * Given an object with named lists of elements, * groups the elements by their distance in the DOM tree. * @param {Object.} lists The named lists of HTML elements. @@ -229,11 +229,11 @@ window.scrapeSchema = function (lists) { return MBEs.map((mbe) => omap( lists, - ({ selector }) => { + ({ selector }, key) => { const elem = Array.from(document.querySelectorAll(selector)).find((elem) => mbe.contains(elem)); return elem ? elem.innerText : undefined; }, - (key) => lists[key].selector // Use the selector as the key in the output + (key) => key // Use the original key in the output )); } From 99f17421db7b375432b36dfd8effe7a197a2f036 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sat, 3 Aug 2024 20:46:05 +0530 Subject: [PATCH 177/255] chore: lint --- maxun-core/src/browserSide/scraper.js | 110 +++++++++++++------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/maxun-core/src/browserSide/scraper.js b/maxun-core/src/browserSide/scraper.js index 9c9182d5..83d196fe 100644 --- a/maxun-core/src/browserSide/scraper.js +++ b/maxun-core/src/browserSide/scraper.js @@ -132,12 +132,12 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, */ // Wrap the entire function in an IIFE (Immediately Invoked Function Expression) // and attach it to the window object -(function(window) { +(function (window) { /** * Returns a "scrape" result from the current page. * @returns {Array} *Curated* array of scraped information (with sparse rows removed) */ - window.scrape = function(selector = null) { + window.scrape = function (selector = null) { /** * **crudeRecords** contains uncurated rundowns of "scrapable" elements * @type {Array} @@ -182,59 +182,59 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, return crudeRecords; }; -/** - * Given an object with named lists of elements, - * groups the elements by their distance in the DOM tree. - * @param {Object.} lists The named lists of HTML elements. - * @returns {Array.>} - */ -window.scrapeSchema = function (lists) { - function omap(object, f, kf = (x) => x) { - return Object.fromEntries( - Object.entries(object) - .map(([k, v]) => [kf(k), f(v)]), - ); + /** + * Given an object with named lists of elements, + * groups the elements by their distance in the DOM tree. + * @param {Object.} lists The named lists of HTML elements. + * @returns {Array.>} + */ + window.scrapeSchema = function (lists) { + function omap(object, f, kf = (x) => x) { + return Object.fromEntries( + Object.entries(object) + .map(([k, v]) => [kf(k), f(v)]), + ); + } + + function ofilter(object, f) { + return Object.fromEntries( + Object.entries(object) + .filter(([k, v]) => f(k, v)), + ); + } + + function getSeedKey(listObj) { + const maxLength = Math.max(...Object.values(omap(listObj, (x) => document.querySelectorAll(x.selector).length))); + return Object.keys(ofilter(listObj, (_, v) => document.querySelectorAll(v.selector).length === maxLength))[0]; + } + + function getMBEs(elements) { + return elements.map((element) => { + let candidate = element; + const isUniqueChild = (e) => elements + .filter((elem) => e.parentNode?.contains(elem)) + .length === 1; + + while (candidate && isUniqueChild(candidate)) { + candidate = candidate.parentNode; + } + + return candidate; + }); + } + + const seedName = getSeedKey(lists); + const seedElements = Array.from(document.querySelectorAll(lists[seedName].selector)); + const MBEs = getMBEs(seedElements); + + return MBEs.map((mbe) => omap( + lists, + ({ selector }, key) => { + const elem = Array.from(document.querySelectorAll(selector)).find((elem) => mbe.contains(elem)); + return elem ? elem.innerText : undefined; + }, + (key) => key // Use the original key in the output + )); } - function ofilter(object, f) { - return Object.fromEntries( - Object.entries(object) - .filter(([k, v]) => f(k, v)), - ); - } - - function getSeedKey(listObj) { - const maxLength = Math.max(...Object.values(omap(listObj, (x) => document.querySelectorAll(x.selector).length))); - return Object.keys(ofilter(listObj, (_, v) => document.querySelectorAll(v.selector).length === maxLength))[0]; - } - - function getMBEs(elements) { - return elements.map((element) => { - let candidate = element; - const isUniqueChild = (e) => elements - .filter((elem) => e.parentNode?.contains(elem)) - .length === 1; - - while (candidate && isUniqueChild(candidate)) { - candidate = candidate.parentNode; - } - - return candidate; - }); - } - - const seedName = getSeedKey(lists); - const seedElements = Array.from(document.querySelectorAll(lists[seedName].selector)); - const MBEs = getMBEs(seedElements); - - return MBEs.map((mbe) => omap( - lists, - ({ selector }, key) => { - const elem = Array.from(document.querySelectorAll(selector)).find((elem) => mbe.contains(elem)); - return elem ? elem.innerText : undefined; - }, - (key) => key // Use the original key in the output - )); -} - })(window); \ No newline at end of file From a0acb65391a20adea01480bf5fd666cd62f400e2 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sat, 3 Aug 2024 21:22:01 +0530 Subject: [PATCH 178/255] feat(ts): set target & module --- maxun-core/tsconfig.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/maxun-core/tsconfig.json b/maxun-core/tsconfig.json index a3813d95..4a1cf18b 100644 --- a/maxun-core/tsconfig.json +++ b/maxun-core/tsconfig.json @@ -2,7 +2,10 @@ "compilerOptions": { "outDir": "./build", "declaration": true, - "allowJs": true + "allowJs": true, + "target": "es5", + "module": "commonjs", + "esModuleInterop": true }, "include": ["src"] } From 1ebfbe91734fb9a1e31de433d9b8e7507c897476 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sat, 3 Aug 2024 21:24:58 +0530 Subject: [PATCH 179/255] refactor: rename value to data --- src/context/browserSteps.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/context/browserSteps.tsx b/src/context/browserSteps.tsx index 8f4ed959..1d3ba00b 100644 --- a/src/context/browserSteps.tsx +++ b/src/context/browserSteps.tsx @@ -3,13 +3,13 @@ import React, { createContext, useContext, useState } from 'react'; interface BrowserStep { id: number; label: string; - value: string; + data: string; selector: string; } interface BrowserStepsContextType { browserSteps: BrowserStep[]; - addBrowserStep: (label: string, value: string, selector: string) => void; + addBrowserStep: (label: string, data: string, selector: string) => void; deleteBrowserStep: (id: number) => void; updateBrowserStepLabel: (id: number, newLabel: string) => void; } @@ -19,10 +19,10 @@ const BrowserStepsContext = createContext(u export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [browserSteps, setBrowserSteps] = useState([]); - const addBrowserStep = (label: string, value: string, selector: string) => { + const addBrowserStep = (label: string, data: string, selector: string) => { setBrowserSteps(prevSteps => [ ...prevSteps, - { id: Date.now(), label, value, selector } + { id: Date.now(), label, data, selector } ]); }; From e044f92406881545e68bf12d00cd1c27c6925886 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sat, 3 Aug 2024 21:27:03 +0530 Subject: [PATCH 180/255] fix: use step.data instead of step.value --- src/components/organisms/RightSidePanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 7321f50e..c64d963e 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -133,7 +133,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { /> Date: Sat, 3 Aug 2024 21:33:33 +0530 Subject: [PATCH 181/255] feat: use selector object instead of str --- src/context/browserSteps.tsx | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/context/browserSteps.tsx b/src/context/browserSteps.tsx index 1d3ba00b..fb21e02c 100644 --- a/src/context/browserSteps.tsx +++ b/src/context/browserSteps.tsx @@ -1,15 +1,22 @@ import React, { createContext, useContext, useState } from 'react'; +interface SelectorObject { + selector: string; + tag?: string; + // todo: allow for additional properties like what did user select for a, img, etc. + [key: string]: any; +} + interface BrowserStep { id: number; label: string; data: string; - selector: string; + selectorObj: SelectorObject; } interface BrowserStepsContextType { browserSteps: BrowserStep[]; - addBrowserStep: (label: string, data: string, selector: string) => void; + addBrowserStep: (label: string, data: string, selectorObj: SelectorObject) => void; deleteBrowserStep: (id: number) => void; updateBrowserStepLabel: (id: number, newLabel: string) => void; } @@ -19,10 +26,10 @@ const BrowserStepsContext = createContext(u export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [browserSteps, setBrowserSteps] = useState([]); - const addBrowserStep = (label: string, data: string, selector: string) => { + const addBrowserStep = (label: string, data: string, selectorObj: SelectorObject) => { setBrowserSteps(prevSteps => [ ...prevSteps, - { id: Date.now(), label, data, selector } + { id: Date.now(), label, data, selectorObj } ]); }; @@ -38,8 +45,14 @@ export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ ); }; + return ( - + {children} ); @@ -51,4 +64,4 @@ export const useBrowserSteps = () => { throw new Error('useBrowserSteps must be used within a BrowserStepsProvider'); } return context; -}; +}; \ No newline at end of file From a4142497a7a6d199a3c40dfcd298e7cd126d08fa Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sat, 3 Aug 2024 21:34:56 +0530 Subject: [PATCH 182/255] feat: pass highligherData selector & tag --- src/components/organisms/BrowserWindow.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 34f80d3d..f45e7030 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -89,7 +89,10 @@ export const BrowserWindow = () => { clickY >= highlightRect.top && clickY <= highlightRect.bottom ) { - addBrowserStep('', highlighterData.elementInfo?.innerText || '', highlighterData.selector); + addBrowserStep('', highlighterData.elementInfo?.innerText || '', { + selector: highlighterData.selector, + tag: highlighterData.elementInfo?.tagName + }); } } }; From c3d9e0ddcb1f80a898320523ae6e9aa1adab1557 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sat, 3 Aug 2024 21:55:58 +0530 Subject: [PATCH 183/255] feat: use strObj for selector & tag --- src/components/organisms/RightSidePanel.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index c64d963e..8eea414c 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -71,15 +71,16 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { }; const createSettingsObject = useCallback(() => { - const settings: Record = {}; + const settings: Record = {}; browserSteps.forEach(step => { - if (step.label && step.selector) { - settings[step.label] = step.selector; + if (step.label && step.selectorObj && step.selectorObj.selector) { + settings[step.label] = step.selectorObj; } }); return settings; }, [browserSteps]); + const stopCaptureAndEmitSettings = useCallback(() => { stopGetText(); const settings = createSettingsObject(); From 09bfe1d82d4251925ac04d9a0212f9b0b2c549c8 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sat, 3 Aug 2024 21:56:21 +0530 Subject: [PATCH 184/255] chore: lint --- src/components/organisms/RightSidePanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 8eea414c..3e2aa731 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -71,7 +71,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { }; const createSettingsObject = useCallback(() => { - const settings: Record = {}; + const settings: Record = {}; browserSteps.forEach(step => { if (step.label && step.selectorObj && step.selectorObj.selector) { settings[step.label] = step.selectorObj; From 5370bf99493aa402bcafc805ee73b310b324fd7c Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sun, 4 Aug 2024 02:54:06 +0530 Subject: [PATCH 185/255] feat: attribute options --- src/components/organisms/BrowserWindow.tsx | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index f45e7030..86820a80 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -16,6 +16,30 @@ interface ElementInfo { imageUrl?: string; } +interface AttributeOption { + label: string; + value: string; +} + + +const getAttributeOptions = (tagName: string): AttributeOption[] => { + switch (tagName.toLowerCase()) { + case 'a': + return [ + { label: 'Text', value: 'innerText' }, + { label: 'URL', value: 'href' } + ]; + case 'img': + return [ + { label: 'Alt Text', value: 'alt' }, + { label: 'Source URL', value: 'src' } + ]; + default: + return [{ label: 'Text', value: 'innerText' }]; + } +}; + + export const BrowserWindow = () => { const [canvasRef, setCanvasReference] = useState | undefined>(undefined); From cee15d737957b698e0f168051bcbeba128f6c9bd Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sun, 4 Aug 2024 03:07:15 +0530 Subject: [PATCH 186/255] feat: attribute prop --- src/context/browserSteps.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/context/browserSteps.tsx b/src/context/browserSteps.tsx index fb21e02c..d323e154 100644 --- a/src/context/browserSteps.tsx +++ b/src/context/browserSteps.tsx @@ -3,6 +3,7 @@ import React, { createContext, useContext, useState } from 'react'; interface SelectorObject { selector: string; tag?: string; + attribute?: string; // todo: allow for additional properties like what did user select for a, img, etc. [key: string]: any; } From a4a2760d4055cfb5c10e17fc51f33dc7a6a1a06d Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sun, 4 Aug 2024 03:15:20 +0530 Subject: [PATCH 187/255] chore: remove todo --- src/context/browserSteps.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/context/browserSteps.tsx b/src/context/browserSteps.tsx index d323e154..eeabd05f 100644 --- a/src/context/browserSteps.tsx +++ b/src/context/browserSteps.tsx @@ -4,7 +4,6 @@ interface SelectorObject { selector: string; tag?: string; attribute?: string; - // todo: allow for additional properties like what did user select for a, img, etc. [key: string]: any; } From 4c20ce7e1fa1370f33500668945911cdfca961ad Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sun, 4 Aug 2024 03:15:50 +0530 Subject: [PATCH 188/255] chore: lint --- src/context/browserSteps.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/context/browserSteps.tsx b/src/context/browserSteps.tsx index eeabd05f..04ebbc28 100644 --- a/src/context/browserSteps.tsx +++ b/src/context/browserSteps.tsx @@ -4,7 +4,7 @@ interface SelectorObject { selector: string; tag?: string; attribute?: string; - [key: string]: any; + [key: string]: any; } interface BrowserStep { @@ -45,12 +45,11 @@ export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ ); }; - return ( - {children} From 50c086acee4f948a602cdb66d9d2f1d8c126726f Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sun, 4 Aug 2024 03:18:41 +0530 Subject: [PATCH 189/255] feat: handle attribute selection --- src/components/organisms/BrowserWindow.tsx | 70 ++++++++++++++++++---- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 86820a80..c042d026 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -40,12 +40,15 @@ const getAttributeOptions = (tagName: string): AttributeOption[] => { }; - export const BrowserWindow = () => { const [canvasRef, setCanvasReference] = useState | undefined>(undefined); const [screenShot, setScreenShot] = useState(""); const [highlighterData, setHighlighterData] = useState<{ rect: DOMRect, selector: string, elementInfo: ElementInfo | null; } | null>(null); const [showConfirmation, setShowConfirmation] = useState(false); + const [showAttributeModal, setShowAttributeModal] = useState(false); + const [attributeOptions, setAttributeOptions] = useState([]); + const [selectedElement, setSelectedElement] = useState<{selector: string, info: ElementInfo | null} | null>(null); + const { socket } = useSocketStore(); const { width, height } = useBrowserDimensionsStore(); @@ -113,14 +116,50 @@ export const BrowserWindow = () => { clickY >= highlightRect.top && clickY <= highlightRect.bottom ) { - addBrowserStep('', highlighterData.elementInfo?.innerText || '', { - selector: highlighterData.selector, - tag: highlighterData.elementInfo?.tagName - }); + const options = getAttributeOptions(highlighterData.elementInfo?.tagName || ''); + if (options.length > 1) { + setAttributeOptions(options); + setSelectedElement({ + selector: highlighterData.selector, + info: highlighterData.elementInfo + }); + setShowAttributeModal(true); + } else { + addBrowserStep('', highlighterData.elementInfo?.innerText || '', { + selector: highlighterData.selector, + tag: highlighterData.elementInfo?.tagName, + attribute: 'innerText' + }); + } } } }; + const handleAttributeSelection = (attribute: string) => { + if (selectedElement) { + let data = ''; + switch (attribute) { + case 'href': + data = selectedElement.info?.url || ''; + break; + case 'src': + data = selectedElement.info?.imageUrl || ''; + break; + default: + data = selectedElement.info?.innerText || ''; + } + + addBrowserStep('', data, { + selector: selectedElement.selector, + tag: selectedElement.info?.tagName, + attribute: attribute + }); + } + setShowAttributeModal(false); + }; + + + const handleConfirmation = (confirmed: boolean) => { if (confirmed) { console.log(`User confirmed interaction with: ${highlighterData?.selector}`); @@ -133,21 +172,30 @@ export const BrowserWindow = () => { return (
{ - getText === true && showConfirmation ? ( + getText === true ? ( setShowConfirmation(false)} + isOpen={showAttributeModal} + onClose={() => {}} canBeClosed={false} > - handleConfirmation(true)} onNo={() => handleConfirmation(false)} - /> + /> */} +
+

Select Attribute

+ {attributeOptions.map((option) => ( + + ))} +
+
) : null } - {(getText === true && !showConfirmation && highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? + {(getText === true && highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? Date: Sun, 4 Aug 2024 03:19:57 +0530 Subject: [PATCH 190/255] fix: -rm ConfirmationBox --- src/components/organisms/BrowserWindow.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index c042d026..f84e3d58 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -178,11 +178,6 @@ export const BrowserWindow = () => { onClose={() => {}} canBeClosed={false} > - {/* handleConfirmation(true)} - onNo={() => handleConfirmation(false)} - /> */}

Select Attribute

{attributeOptions.map((option) => ( From 891486f98e138dfcd8d885e5eff9878e18b5b893 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sun, 4 Aug 2024 03:21:14 +0530 Subject: [PATCH 191/255] chore: remove handleConfirmation --- src/components/organisms/BrowserWindow.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index f84e3d58..49d31c98 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -160,15 +160,6 @@ export const BrowserWindow = () => { - const handleConfirmation = (confirmed: boolean) => { - if (confirmed) { - console.log(`User confirmed interaction with: ${highlighterData?.selector}`); - } else { - console.log('User declined interaction'); - } - setShowConfirmation(false); - }; - return (
{ From 409678ef111e4c56e7990f994a04c78092ccc5e8 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sun, 4 Aug 2024 03:21:55 +0530 Subject: [PATCH 192/255] chore: ConfirmationBox cleanup --- src/components/organisms/BrowserWindow.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 49d31c98..41c422ed 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -6,7 +6,6 @@ import { Highlighter } from "../atoms/Highlighter"; import { GenericModal } from '../atoms/GenericModal'; import { useActionContext } from '../../context/browserActions'; import { useBrowserSteps } from '../../context/browserSteps'; -import { ConfirmationBox } from "../atoms/ConfirmationBox"; interface ElementInfo { tagName: string; @@ -44,7 +43,6 @@ export const BrowserWindow = () => { const [canvasRef, setCanvasReference] = useState | undefined>(undefined); const [screenShot, setScreenShot] = useState(""); const [highlighterData, setHighlighterData] = useState<{ rect: DOMRect, selector: string, elementInfo: ElementInfo | null; } | null>(null); - const [showConfirmation, setShowConfirmation] = useState(false); const [showAttributeModal, setShowAttributeModal] = useState(false); const [attributeOptions, setAttributeOptions] = useState([]); const [selectedElement, setSelectedElement] = useState<{selector: string, info: ElementInfo | null} | null>(null); From 195d3a74bad169016d74909f9d1d6ada0d8bd9e1 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sun, 4 Aug 2024 03:23:02 +0530 Subject: [PATCH 193/255] chore: lint --- src/components/organisms/BrowserWindow.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 41c422ed..070dd17c 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -45,7 +45,7 @@ export const BrowserWindow = () => { const [highlighterData, setHighlighterData] = useState<{ rect: DOMRect, selector: string, elementInfo: ElementInfo | null; } | null>(null); const [showAttributeModal, setShowAttributeModal] = useState(false); const [attributeOptions, setAttributeOptions] = useState([]); - const [selectedElement, setSelectedElement] = useState<{selector: string, info: ElementInfo | null} | null>(null); + const [selectedElement, setSelectedElement] = useState<{ selector: string, info: ElementInfo | null } | null>(null); const { socket } = useSocketStore(); @@ -164,17 +164,17 @@ export const BrowserWindow = () => { getText === true ? ( {}} + onClose={() => { }} canBeClosed={false} >
-

Select Attribute

- {attributeOptions.map((option) => ( - - ))} -
+

Select Attribute

+ {attributeOptions.map((option) => ( + + ))} +
) : null From 79b8ad9952dbc74580f43a8f53cc1619a87d3e48 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sun, 4 Aug 2024 03:41:39 +0530 Subject: [PATCH 194/255] feat: show highlighter if !showAttributeModal --- src/components/molecules/ActionSettings.tsx | 2 ++ src/components/organisms/BrowserWindow.tsx | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/molecules/ActionSettings.tsx b/src/components/molecules/ActionSettings.tsx index 4a994d2b..dcf2ba5c 100644 --- a/src/components/molecules/ActionSettings.tsx +++ b/src/components/molecules/ActionSettings.tsx @@ -20,6 +20,8 @@ export const ActionSettings = ({ action }: ActionSettingsProps) => { return ; case 'scroll': return ; + case 'scrape': + return ; case 'scrapeSchema': return ; default: diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 070dd17c..df7fe5cf 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -179,7 +179,7 @@ export const BrowserWindow = () => { ) : null } - {(getText === true && highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? + {(getText === true && !showAttributeModal && highlighterData?.rect != null && highlighterData?.rect.top != null) && canvasRef?.current ? Date: Sun, 4 Aug 2024 03:42:21 +0530 Subject: [PATCH 195/255] chore: lint --- src/components/organisms/BrowserWindow.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index df7fe5cf..8359ba91 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -20,7 +20,6 @@ interface AttributeOption { value: string; } - const getAttributeOptions = (tagName: string): AttributeOption[] => { switch (tagName.toLowerCase()) { case 'a': @@ -38,7 +37,6 @@ const getAttributeOptions = (tagName: string): AttributeOption[] => { } }; - export const BrowserWindow = () => { const [canvasRef, setCanvasReference] = useState | undefined>(undefined); const [screenShot, setScreenShot] = useState(""); @@ -47,7 +45,6 @@ export const BrowserWindow = () => { const [attributeOptions, setAttributeOptions] = useState([]); const [selectedElement, setSelectedElement] = useState<{ selector: string, info: ElementInfo | null } | null>(null); - const { socket } = useSocketStore(); const { width, height } = useBrowserDimensionsStore(); const { getText, getScreenshot } = useActionContext(); @@ -156,8 +153,6 @@ export const BrowserWindow = () => { setShowAttributeModal(false); }; - - return (
{ From 062ded7c01be72e67ba19028ef278b5e3ac6e952 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sun, 4 Aug 2024 03:50:03 +0530 Subject: [PATCH 196/255] feat(core): accept attribute --- 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 bb511631..4860d2fd 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -283,7 +283,7 @@ export default class Interpreter extends EventEmitter { await this.options.serializableCallback(scrapeResults); }, - scrapeSchema: async (schema: Record) => { + scrapeSchema: async (schema: Record) => { await this.ensureScriptsLoaded(page); const scrapeResult = await page.evaluate((schemaObj) => window.scrapeSchema(schemaObj), schema); From e943a8c253cf6742f082ee2c148218897bafb0ad Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sun, 4 Aug 2024 03:53:59 +0530 Subject: [PATCH 197/255] feat(core): extraction based on attribute --- maxun-core/src/browserSide/scraper.js | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/maxun-core/src/browserSide/scraper.js b/maxun-core/src/browserSide/scraper.js index 83d196fe..009cf9da 100644 --- a/maxun-core/src/browserSide/scraper.js +++ b/maxun-core/src/browserSide/scraper.js @@ -229,12 +229,25 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, return MBEs.map((mbe) => omap( lists, - ({ selector }, key) => { - const elem = Array.from(document.querySelectorAll(selector)).find((elem) => mbe.contains(elem)); - return elem ? elem.innerText : undefined; + ({ selector, attribute }, key) => { + const elem = Array.from(document.querySelectorAll(selector)).find((elem) => mbe.contains(elem)); + if (!elem) return undefined; + + switch (attribute) { + case 'href': + return elem.getAttribute('href'); + case 'src': + return elem.getAttribute('src'); + case 'innerText': + return elem.innerText; + case 'textContent': + return elem.textContent; + default: + return elem.innerText; + } }, (key) => key // Use the original key in the output - )); + )); } })(window); \ No newline at end of file From 68ac09f0db07b705b11c4407d70326362a0ad324 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sun, 4 Aug 2024 04:00:51 +0530 Subject: [PATCH 198/255] chore: lint --- maxun-core/src/browserSide/scraper.js | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/maxun-core/src/browserSide/scraper.js b/maxun-core/src/browserSide/scraper.js index 009cf9da..262e63ec 100644 --- a/maxun-core/src/browserSide/scraper.js +++ b/maxun-core/src/browserSide/scraper.js @@ -230,24 +230,24 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, return MBEs.map((mbe) => omap( lists, ({ selector, attribute }, key) => { - const elem = Array.from(document.querySelectorAll(selector)).find((elem) => mbe.contains(elem)); - if (!elem) return undefined; + const elem = Array.from(document.querySelectorAll(selector)).find((elem) => mbe.contains(elem)); + if (!elem) return undefined; - switch (attribute) { - case 'href': - return elem.getAttribute('href'); - case 'src': - return elem.getAttribute('src'); - case 'innerText': - return elem.innerText; - case 'textContent': - return elem.textContent; - default: - return elem.innerText; - } + switch (attribute) { + case 'href': + return elem.getAttribute('href'); + case 'src': + return elem.getAttribute('src'); + case 'innerText': + return elem.innerText; + case 'textContent': + return elem.textContent; + default: + return elem.innerText; + } }, (key) => key // Use the original key in the output - )); + )); } })(window); \ No newline at end of file From 2d3437d6e6b23b64a1cc904dc584e1b84e0e532d Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Sun, 4 Aug 2024 20:57:40 +0530 Subject: [PATCH 199/255] feat: emit scrapeSchema action if browserSteps are not 0 --- src/components/organisms/RightSidePanel.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 3e2aa731..f57bb01f 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -84,7 +84,9 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { const stopCaptureAndEmitSettings = useCallback(() => { stopGetText(); const settings = createSettingsObject(); - socket?.emit('action', { action: 'scrapeSchema', settings }); + if (browserSteps.length > 0) { + socket?.emit('action', { action: 'scrapeSchema', settings }); + } }, [stopGetText, createSettingsObject, socket]); return ( From b0e6a45c7ccfbb03905748e60746abae71556716 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 5 Aug 2024 22:44:03 +0530 Subject: [PATCH 200/255] feat: remove Action box --- src/components/organisms/RightSidePanel.tsx | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index f57bb01f..616bd0dd 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -95,25 +95,6 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { Last action: {` ${lastAction}`} - {content === 'action' && ( - <> - Type of action: - - - click on coordinates - enqueueLinks - scrape - scrapeSchema - screenshot - script - scroll - - - - {isSettingsDisplayed && } - - )} - {!getText && !getScreenshot && } {getText && } From 5d25789471f55691f46c8b84368d0156dca7fd3f Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 5 Aug 2024 22:44:41 +0530 Subject: [PATCH 201/255] chore: remove ActionTypeWrapper --- src/components/organisms/RightSidePanel.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 616bd0dd..e863b9a8 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -135,13 +135,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { ); }; -const ActionTypeWrapper = styled.div` - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - margin-top: 20px; -`; + export const ActionDescription = styled.p` margin-left: 15px; From d0e8d90769a2792863f0f28b7f69b46770dc45f3 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 5 Aug 2024 22:45:49 +0530 Subject: [PATCH 202/255] fix: remove action handlers --- src/components/organisms/RightSidePanel.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index e863b9a8..a790675c 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -29,15 +29,6 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { const { browserSteps, updateBrowserStepLabel, deleteBrowserStep } = useBrowserSteps(); const { socket } = useSocketStore(); - const handleChange = (event: React.SyntheticEvent, newValue: string) => { - setContent(newValue); - }; - - const handleActionSelect = (event: SelectChangeEvent) => { - const { value } = event.target; - setAction(value); - setIsSettingsDisplayed(true); - }; const handleLabelChange = (id: number, label: string) => { setLabels(prevLabels => ({ ...prevLabels, [id]: label })); From a67d9624f01a686273f0788621c15939c018dd00 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 5 Aug 2024 22:46:16 +0530 Subject: [PATCH 203/255] chore: remove unused action states --- src/components/organisms/RightSidePanel.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index a790675c..72b309f4 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -17,9 +17,6 @@ interface RightSidePanelProps { } export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { - const [content, setContent] = useState('action'); - const [action, setAction] = useState(''); - const [isSettingsDisplayed, setIsSettingsDisplayed] = useState(false); const [labels, setLabels] = useState<{ [id: number]: string }>({}); const [errors, setErrors] = useState<{ [id: number]: string }>({}); const [confirmedSteps, setConfirmedSteps] = useState<{ [id: number]: boolean }>({}); From a6665016ae1cf1a89a01ed5360df0bc3cf5b4c2c Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 5 Aug 2024 22:46:47 +0530 Subject: [PATCH 204/255] chore: remove inputs for action handlers --- src/components/organisms/RightSidePanel.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 72b309f4..b93303c0 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -2,8 +2,6 @@ import React, { useState, useEffect, useCallback } from 'react'; import { Button, MenuItem, Paper, Box, TextField } from "@mui/material"; import { Dropdown as MuiDropdown } from '../atoms/DropdownMui'; import styled from "styled-components"; -import { ActionSettings } from "../molecules/ActionSettings"; -import { SelectChangeEvent } from "@mui/material/Select/Select"; import { SimpleBox } from "../atoms/Box"; import Typography from "@mui/material/Typography"; import { useGlobalInfoStore } from "../../context/globalInfo"; From 28c09a016f791ab4cdc08959c4466f3692dae983 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 5 Aug 2024 23:02:01 +0530 Subject: [PATCH 205/255] feat: capture full page & visible part buttons --- src/components/organisms/RightSidePanel.tsx | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index b93303c0..298f6dfc 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -24,7 +24,6 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { const { browserSteps, updateBrowserStepLabel, deleteBrowserStep } = useBrowserSteps(); const { socket } = useSocketStore(); - const handleLabelChange = (id: number, label: string) => { setLabels(prevLabels => ({ ...prevLabels, [id]: label })); if (!label.trim()) { @@ -57,7 +56,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { }; const createSettingsObject = useCallback(() => { - const settings: Record = {}; + const settings: Record = {}; browserSteps.forEach(step => { if (step.label && step.selectorObj && step.selectorObj.selector) { settings[step.label] = step.selectorObj; @@ -66,7 +65,6 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { return settings; }, [browserSteps]); - const stopCaptureAndEmitSettings = useCallback(() => { stopGetText(); const settings = createSettingsObject(); @@ -75,6 +73,12 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { } }, [stopGetText, createSettingsObject, socket]); + const handleCaptureFullpage = () => { + }; + + const handleCaptureVisiblePart = () => { + }; + return ( @@ -85,7 +89,13 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { {!getText && !getScreenshot && } {getText && } {!getText && !getScreenshot && } - {getScreenshot && } + {getScreenshot && ( + + + + + + )} @@ -121,8 +131,6 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { ); }; - - export const ActionDescription = styled.p` margin-left: 15px; `; From ca3b8d5b7656f495009305472e6fb29184618281 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 5 Aug 2024 23:03:17 +0530 Subject: [PATCH 206/255] feat: import screenshot settings --- src/components/organisms/RightSidePanel.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 298f6dfc..de7a3c64 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -9,6 +9,7 @@ import { PairForEdit } from "../../pages/RecordingPage"; import { useActionContext } from '../../context/browserActions'; import { useBrowserSteps } from '../../context/browserSteps'; import { useSocketStore } from '../../context/socket'; +import { ScreenshotSettings } from "../../shared/types"; interface RightSidePanelProps { pairForEdit: PairForEdit; From 0c03c54eae84327fff93232247ffdc0a626ffdc0 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 5 Aug 2024 23:17:11 +0530 Subject: [PATCH 207/255] feat: handle capture full page and visible part ss --- src/components/organisms/RightSidePanel.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index de7a3c64..459ead49 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -9,7 +9,7 @@ import { PairForEdit } from "../../pages/RecordingPage"; import { useActionContext } from '../../context/browserActions'; import { useBrowserSteps } from '../../context/browserSteps'; import { useSocketStore } from '../../context/socket'; -import { ScreenshotSettings } from "../../shared/types"; +import { ScreenshotSettings } from '../../shared/types'; interface RightSidePanelProps { pairForEdit: PairForEdit; @@ -74,10 +74,21 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { } }, [stopGetText, createSettingsObject, socket]); + // Function to handle screenshot settings based on capture type + const captureScreenshot = (fullPage: boolean) => { + const screenshotSettings: ScreenshotSettings = { + fullPage, + // Add other settings as required + }; + socket?.emit('action', { action: 'takeScreenshot', settings: screenshotSettings }); + }; + const handleCaptureFullpage = () => { + captureScreenshot(true); }; const handleCaptureVisiblePart = () => { + captureScreenshot(false); }; return ( @@ -134,4 +145,3 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { export const ActionDescription = styled.p` margin-left: 15px; -`; From 05ac2066f1b9312536185d5da7afb19013d46c9d Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 5 Aug 2024 23:17:56 +0530 Subject: [PATCH 208/255] fix: add missing in ActionDescription --- src/components/organisms/RightSidePanel.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 459ead49..ab57fe7b 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -145,3 +145,4 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { export const ActionDescription = styled.p` margin-left: 15px; +`; From c578c68282471defa1d181658777b1706f5f4f88 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 5 Aug 2024 23:24:11 +0530 Subject: [PATCH 209/255] feat: pass all screenshot options --- src/components/organisms/RightSidePanel.tsx | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index ab57fe7b..b9271a0a 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -74,21 +74,16 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { } }, [stopGetText, createSettingsObject, socket]); - // Function to handle screenshot settings based on capture type const captureScreenshot = (fullPage: boolean) => { const screenshotSettings: ScreenshotSettings = { fullPage, - // Add other settings as required + type: 'png', + timeout: 30000, + animations: 'allow', + caret: 'hide', + scale: 'device', }; - socket?.emit('action', { action: 'takeScreenshot', settings: screenshotSettings }); - }; - - const handleCaptureFullpage = () => { - captureScreenshot(true); - }; - - const handleCaptureVisiblePart = () => { - captureScreenshot(false); + socket?.emit('action', { action: 'screenshot', settings: screenshotSettings }); }; return ( @@ -103,8 +98,8 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { {!getText && !getScreenshot && } {getScreenshot && ( - - + + )} From f3c5c36a6cc354d815b7815a410cf2cf133e9ae3 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 5 Aug 2024 23:24:39 +0530 Subject: [PATCH 210/255] chore: lint --- src/components/organisms/RightSidePanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index b9271a0a..f48702c3 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -57,7 +57,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { }; const createSettingsObject = useCallback(() => { - const settings: Record = {}; + const settings: Record = {}; browserSteps.forEach(step => { if (step.label && step.selectorObj && step.selectorObj.selector) { settings[step.label] = step.selectorObj; From 269d00bb3a025bb6a905b22d4f1248103669bebe Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 5 Aug 2024 23:32:00 +0530 Subject: [PATCH 211/255] chore: delete script.tsx --- .../molecules/action-settings/script.tsx | 63 ------------------- 1 file changed, 63 deletions(-) delete mode 100644 src/components/molecules/action-settings/script.tsx diff --git a/src/components/molecules/action-settings/script.tsx b/src/components/molecules/action-settings/script.tsx deleted file mode 100644 index 2cece263..00000000 --- a/src/components/molecules/action-settings/script.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, { forwardRef, useImperativeHandle } from 'react'; -import Editor from 'react-simple-code-editor'; -// @ts-ignore -import { highlight, languages } from 'prismjs/components/prism-core'; -import 'prismjs/components/prism-clike'; -import 'prismjs/components/prism-javascript'; -import 'prismjs/themes/prism.css'; -import styled from "styled-components"; -import InfoIcon from '@mui/icons-material/Info'; -import { WarningText } from "../../atoms/texts"; - -export const ScriptSettings = forwardRef((props, ref) => { - const [code, setCode] = React.useState(''); - - useImperativeHandle(ref, () => ({ - getSettings() { - return code; - } - })); - - return ( - - - - Allows to run an arbitrary asynchronous function evaluated at the server - side accepting the current page instance argument. - - setCode(code)} - highlight={code => highlight(code, languages.js)} - padding={10} - style={{ - fontFamily: '"Fira code", "Fira Mono", monospace', - fontSize: 12, - background: '#f0f0f0', - }} - /> - - ); -}); - -const EditorWrapper = styled.div` - flex: 1; - overflow: auto; - /** hard-coded height */ - height: 100%; - width: 100%; -`; - -const StyledEditor = styled(Editor)` - white-space: pre; - caret-color: #fff; - min-width: 100%; - min-height: 100%; - float: left; - & > textarea, - & > pre { - outline: none; - white-space: pre !important; - } -`; From 451a0675652b3e66b98de43fc45c917b21aa08ce Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 5 Aug 2024 23:32:24 +0530 Subject: [PATCH 212/255] chore: delete clickOnCoordinates.tsx --- .../action-settings/clickOnCoordinates.tsx | 39 ------------------- 1 file changed, 39 deletions(-) delete mode 100644 src/components/molecules/action-settings/clickOnCoordinates.tsx diff --git a/src/components/molecules/action-settings/clickOnCoordinates.tsx b/src/components/molecules/action-settings/clickOnCoordinates.tsx deleted file mode 100644 index c68a53b0..00000000 --- a/src/components/molecules/action-settings/clickOnCoordinates.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { forwardRef, useImperativeHandle } from 'react'; -import { Stack, TextField } from "@mui/material"; -import { WarningText } from '../../atoms/texts'; -import InfoIcon from "@mui/icons-material/Info"; - -export const ClickOnCoordinatesSettings = forwardRef((props, ref) => { - const [settings, setSettings] = React.useState([0, 0]); - useImperativeHandle(ref, () => ({ - getSettings() { - return settings; - } - })); - - return ( - - setSettings(prevState => ([Number(e.target.value), prevState[1]]))} - required - defaultValue={settings[0]} - /> - setSettings(prevState => ([prevState[0], Number(e.target.value)]))} - required - defaultValue={settings[1]} - /> - - - The click function will click on the given coordinates. - You need to put the coordinates by yourself. - - - ); -}); From 5e0aa8bd045995f7ea6d55dd621d56026589b13a Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 5 Aug 2024 23:32:51 +0530 Subject: [PATCH 213/255] chore: delete enqueueLinks.tsx --- .../action-settings/enqueueLinks.tsx | 32 ------------------- 1 file changed, 32 deletions(-) delete mode 100644 src/components/molecules/action-settings/enqueueLinks.tsx diff --git a/src/components/molecules/action-settings/enqueueLinks.tsx b/src/components/molecules/action-settings/enqueueLinks.tsx deleted file mode 100644 index 2c383d47..00000000 --- a/src/components/molecules/action-settings/enqueueLinks.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React, { forwardRef, useImperativeHandle } from 'react'; -import { Stack, TextField } from "@mui/material"; -import { WarningText } from "../../atoms/texts"; -import WarningIcon from "@mui/icons-material/Warning"; -import InfoIcon from "@mui/icons-material/Info"; - -export const EnqueueLinksSettings = forwardRef((props, ref) => { - const [settings, setSettings] = React.useState(''); - useImperativeHandle(ref, () => ({ - getSettings() { - return settings; - } - })); - - return ( - - setSettings(e.target.value)} - /> - - - Reads elements targeted by the selector and stores their links in a queue. - Those pages are then processed using the same workflow as the initial page - (in parallel if the maxConcurrency parameter is greater than 1). - - - ); -}); From fe97858ad8df19b1592bc74608aed48be12d3d4a Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 5 Aug 2024 23:33:33 +0530 Subject: [PATCH 214/255] fix: remove enqueueLinks import --- src/components/molecules/action-settings/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/molecules/action-settings/index.ts b/src/components/molecules/action-settings/index.ts index 2674bee4..42113d76 100644 --- a/src/components/molecules/action-settings/index.ts +++ b/src/components/molecules/action-settings/index.ts @@ -2,12 +2,11 @@ import { ScrollSettings } from './scroll'; import { ScreenshotSettings } from "./screenshot"; import { ScrapeSettings } from "./scrape"; import { ScrapeSchemaSettings } from "./scrapeSchema"; -import { EnqueueLinksSettings } from "./enqueueLinks"; export { ScrollSettings, ScreenshotSettings, ScrapeSettings, ScrapeSchemaSettings, - EnqueueLinksSettings, + , }; From 2bc911edcb2306d2757d5d54858ac08e72917b46 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Mon, 5 Aug 2024 23:33:56 +0530 Subject: [PATCH 215/255] fix: remove , --- src/components/molecules/action-settings/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/molecules/action-settings/index.ts b/src/components/molecules/action-settings/index.ts index 42113d76..58e3f3c4 100644 --- a/src/components/molecules/action-settings/index.ts +++ b/src/components/molecules/action-settings/index.ts @@ -8,5 +8,4 @@ export { ScreenshotSettings, ScrapeSettings, ScrapeSchemaSettings, - , }; From 34df84e867ef219a56c974b3648837ba9d529f84 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 01:39:49 +0530 Subject: [PATCH 216/255] feat: set variant and color --- src/components/organisms/RightSidePanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index f48702c3..c970d9a5 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -94,7 +94,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { {!getText && !getScreenshot && } - {getText && } + {getText && } {!getText && !getScreenshot && } {getScreenshot && ( From ceb250c533f2b6ff8570ff5bb238411a0923744b Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 01:41:43 +0530 Subject: [PATCH 217/255] feat: set variant and color --- src/components/organisms/RightSidePanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index c970d9a5..00f5342e 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -100,7 +100,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { - + )} From 49be673d6d4fa3dcb6e0a09b22ca4a443237b7ec Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 01:56:28 +0530 Subject: [PATCH 218/255] feat: rename button to Discard --- src/components/organisms/RightSidePanel.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 00f5342e..68de73de 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -94,13 +94,13 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { {!getText && !getScreenshot && } - {getText && } + {getText && } {!getText && !getScreenshot && } {getScreenshot && ( - + )} From 959a498cf81f31a0859b71895d5e8495371c5870 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 02:11:52 +0530 Subject: [PATCH 219/255] fix: display browserSteps only if getText is true --- src/components/organisms/RightSidePanel.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 68de73de..2f728743 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -104,8 +104,10 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { )} - - + + { + getText ? ( + {browserSteps.map(step => ( { ))} + ) : null + } ); }; From 864d4e1ad2c49bb12e6999968894ac114f29fb7a Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 02:16:53 +0530 Subject: [PATCH 220/255] fix: add browser step iff getText true --- src/components/organisms/BrowserWindow.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 8359ba91..a0d58906 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -111,6 +111,7 @@ export const BrowserWindow = () => { clickY >= highlightRect.top && clickY <= highlightRect.bottom ) { + if (getText === true) { const options = getAttributeOptions(highlighterData.elementInfo?.tagName || ''); if (options.length > 1) { setAttributeOptions(options); @@ -127,6 +128,7 @@ export const BrowserWindow = () => { }); } } + } } }; @@ -144,11 +146,15 @@ export const BrowserWindow = () => { data = selectedElement.info?.innerText || ''; } - addBrowserStep('', data, { - selector: selectedElement.selector, - tag: selectedElement.info?.tagName, - attribute: attribute - }); + { + if (getText === true) { + addBrowserStep('', data, { + selector: selectedElement.selector, + tag: selectedElement.info?.tagName, + attribute: attribute + }); + } + } } setShowAttributeModal(false); }; From 39e3ddd593d1bd74e7e2e8ca06107f3bfae9f507 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 02:17:28 +0530 Subject: [PATCH 221/255] chore: lint --- src/components/organisms/BrowserWindow.tsx | 31 +++++++++++----------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index a0d58906..812abd84 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -112,23 +112,23 @@ export const BrowserWindow = () => { clickY <= highlightRect.bottom ) { if (getText === true) { - const options = getAttributeOptions(highlighterData.elementInfo?.tagName || ''); - if (options.length > 1) { - setAttributeOptions(options); - setSelectedElement({ - selector: highlighterData.selector, - info: highlighterData.elementInfo - }); - setShowAttributeModal(true); - } else { - addBrowserStep('', highlighterData.elementInfo?.innerText || '', { - selector: highlighterData.selector, - tag: highlighterData.elementInfo?.tagName, - attribute: 'innerText' - }); + const options = getAttributeOptions(highlighterData.elementInfo?.tagName || ''); + if (options.length > 1) { + setAttributeOptions(options); + setSelectedElement({ + selector: highlighterData.selector, + info: highlighterData.elementInfo + }); + setShowAttributeModal(true); + } else { + addBrowserStep('', highlighterData.elementInfo?.innerText || '', { + selector: highlighterData.selector, + tag: highlighterData.elementInfo?.tagName, + attribute: 'innerText' + }); + } } } - } } }; @@ -145,7 +145,6 @@ export const BrowserWindow = () => { default: data = selectedElement.info?.innerText || ''; } - { if (getText === true) { addBrowserStep('', data, { From 558d629ab1a638a238368379b801d55b8dd85a8e Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 02:23:33 +0530 Subject: [PATCH 222/255] chore: lint --- src/components/organisms/RightSidePanel.tsx | 54 ++++++++++----------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 2f728743..961f17a1 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -104,38 +104,38 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { )} - + { getText ? ( - {browserSteps.map(step => ( - - handleLabelChange(step.id, e.target.value)} - fullWidth - margin="normal" - error={!!errors[step.id]} - helperText={errors[step.id]} - InputProps={{ readOnly: confirmedSteps[step.id] }} - /> - - {!confirmedSteps[step.id] && ( - - - + {browserSteps.map(step => ( + + handleLabelChange(step.id, e.target.value)} + fullWidth + margin="normal" + error={!!errors[step.id]} + helperText={errors[step.id]} + InputProps={{ readOnly: confirmedSteps[step.id] }} + /> + + {!confirmedSteps[step.id] && ( + + + + + )} - )} + ))} - ))} - ) : null } From 202ce3a229459be914713aae00690eb1483d6335 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 02:26:22 +0530 Subject: [PATCH 223/255] feat: rename labels state to textLabels --- src/components/organisms/RightSidePanel.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 961f17a1..c117458f 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -16,7 +16,7 @@ interface RightSidePanelProps { } export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { - const [labels, setLabels] = useState<{ [id: number]: string }>({}); + const [textLabels, setTextLabels] = useState<{ [id: number]: string }>({}); const [errors, setErrors] = useState<{ [id: number]: string }>({}); const [confirmedSteps, setConfirmedSteps] = useState<{ [id: number]: boolean }>({}); @@ -26,7 +26,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { const { socket } = useSocketStore(); const handleLabelChange = (id: number, label: string) => { - setLabels(prevLabels => ({ ...prevLabels, [id]: label })); + setTextLabels(prevLabels => ({ ...prevLabels, [id]: label })); if (!label.trim()) { setErrors(prevErrors => ({ ...prevErrors, [id]: 'Label cannot be empty' })); } else { @@ -35,7 +35,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { }; const handleConfirm = (id: number) => { - const label = labels[id]?.trim(); + const label = textLabels[id]?.trim(); if (label) { updateBrowserStepLabel(id, label); setConfirmedSteps(prev => ({ ...prev, [id]: true })); @@ -46,7 +46,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { const handleDiscard = (id: number) => { deleteBrowserStep(id); - setLabels(prevLabels => { + setTextLabels(prevLabels => { const { [id]: _, ...rest } = prevLabels; return rest; }); @@ -112,7 +112,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { handleLabelChange(step.id, e.target.value)} fullWidth margin="normal" @@ -129,7 +129,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { /> {!confirmedSteps[step.id] && ( - + )} From fc53dc3f1d30b4468f5b24df2763394536a5cad2 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 02:30:07 +0530 Subject: [PATCH 224/255] refactor: more specfic variable names for getText --- src/components/organisms/RightSidePanel.tsx | 32 ++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index c117458f..911fb156 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -18,14 +18,14 @@ interface RightSidePanelProps { export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { const [textLabels, setTextLabels] = useState<{ [id: number]: string }>({}); const [errors, setErrors] = useState<{ [id: number]: string }>({}); - const [confirmedSteps, setConfirmedSteps] = useState<{ [id: number]: boolean }>({}); + const [confirmedTextSteps, setConfirmedTextSteps] = useState<{ [id: number]: boolean }>({}); const { lastAction } = useGlobalInfoStore(); const { getText, getScreenshot, startGetText, stopGetText, startGetScreenshot, stopGetScreenshot } = useActionContext(); const { browserSteps, updateBrowserStepLabel, deleteBrowserStep } = useBrowserSteps(); const { socket } = useSocketStore(); - const handleLabelChange = (id: number, label: string) => { + const handleTextLabelChange = (id: number, label: string) => { setTextLabels(prevLabels => ({ ...prevLabels, [id]: label })); if (!label.trim()) { setErrors(prevErrors => ({ ...prevErrors, [id]: 'Label cannot be empty' })); @@ -34,17 +34,17 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { } }; - const handleConfirm = (id: number) => { + const handleTextStepConfirm = (id: number) => { const label = textLabels[id]?.trim(); if (label) { updateBrowserStepLabel(id, label); - setConfirmedSteps(prev => ({ ...prev, [id]: true })); + setConfirmedTextSteps(prev => ({ ...prev, [id]: true })); } else { setErrors(prevErrors => ({ ...prevErrors, [id]: 'Label cannot be empty' })); } }; - const handleDiscard = (id: number) => { + const handleTextStepDiscard = (id: number) => { deleteBrowserStep(id); setTextLabels(prevLabels => { const { [id]: _, ...rest } = prevLabels; @@ -56,7 +56,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { }); }; - const createSettingsObject = useCallback(() => { + const getTextSettingsObject = useCallback(() => { const settings: Record = {}; browserSteps.forEach(step => { if (step.label && step.selectorObj && step.selectorObj.selector) { @@ -66,13 +66,13 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { return settings; }, [browserSteps]); - const stopCaptureAndEmitSettings = useCallback(() => { + const stopCaptureAndEmitGetTextSettings = useCallback(() => { stopGetText(); - const settings = createSettingsObject(); + const settings = getTextSettingsObject(); if (browserSteps.length > 0) { socket?.emit('action', { action: 'scrapeSchema', settings }); } - }, [stopGetText, createSettingsObject, socket]); + }, [stopGetText, getTextSettingsObject, socket]); const captureScreenshot = (fullPage: boolean) => { const screenshotSettings: ScreenshotSettings = { @@ -94,7 +94,7 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { {!getText && !getScreenshot && } - {getText && } + {getText && } {!getText && !getScreenshot && } {getScreenshot && ( @@ -113,24 +113,24 @@ export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { handleLabelChange(step.id, e.target.value)} + onChange={(e) => handleTextLabelChange(step.id, e.target.value)} fullWidth margin="normal" error={!!errors[step.id]} helperText={errors[step.id]} - InputProps={{ readOnly: confirmedSteps[step.id] }} + InputProps={{ readOnly: confirmedTextSteps[step.id] }} /> - {!confirmedSteps[step.id] && ( + {!confirmedTextSteps[step.id] && ( - - + + )} From 00e81a811f1eb17827fde48d41f1f6c8faa0da79 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 02:31:36 +0530 Subject: [PATCH 225/255] fix: remove pairForEdit prop --- src/components/organisms/RightSidePanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 911fb156..d3afb4d2 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -15,7 +15,7 @@ interface RightSidePanelProps { pairForEdit: PairForEdit; } -export const RightSidePanel = ({ pairForEdit }: RightSidePanelProps) => { +export const RightSidePanel = () => { const [textLabels, setTextLabels] = useState<{ [id: number]: string }>({}); const [errors, setErrors] = useState<{ [id: number]: string }>({}); const [confirmedTextSteps, setConfirmedTextSteps] = useState<{ [id: number]: boolean }>({}); From cd759843881e4b2b6f8a05418a993e9cf84c03b7 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 02:31:57 +0530 Subject: [PATCH 226/255] fix: remove pairForEdit prop from RightSidePanel --- src/pages/RecordingPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/RecordingPage.tsx b/src/pages/RecordingPage.tsx index cc229fd9..45b422b2 100644 --- a/src/pages/RecordingPage.tsx +++ b/src/pages/RecordingPage.tsx @@ -123,7 +123,7 @@ export const RecordingPage = ({ recordingName }: RecordingPageProps) => { - + : } From 7e6345dffe86feba1e5ccd01e630cfbff0da8940 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 02:32:26 +0530 Subject: [PATCH 227/255] fix: remove pairForEdit prop interface --- src/components/organisms/RightSidePanel.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index d3afb4d2..8d14f36c 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -11,10 +11,6 @@ import { useBrowserSteps } from '../../context/browserSteps'; import { useSocketStore } from '../../context/socket'; import { ScreenshotSettings } from '../../shared/types'; -interface RightSidePanelProps { - pairForEdit: PairForEdit; -} - export const RightSidePanel = () => { const [textLabels, setTextLabels] = useState<{ [id: number]: string }>({}); const [errors, setErrors] = useState<{ [id: number]: string }>({}); From 2b8b81c349d8e33a39809fc75c7f8babdd7ce62c Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 02:33:39 +0530 Subject: [PATCH 228/255] chore: remove unwanted imports --- src/components/organisms/RightSidePanel.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 8d14f36c..8e88801d 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -1,11 +1,9 @@ -import React, { useState, useEffect, useCallback } from 'react'; -import { Button, MenuItem, Paper, Box, TextField } from "@mui/material"; -import { Dropdown as MuiDropdown } from '../atoms/DropdownMui'; +import React, { useState, useCallback } from 'react'; +import { Button, Paper, Box, TextField } from "@mui/material"; import styled from "styled-components"; import { SimpleBox } from "../atoms/Box"; import Typography from "@mui/material/Typography"; import { useGlobalInfoStore } from "../../context/globalInfo"; -import { PairForEdit } from "../../pages/RecordingPage"; import { useActionContext } from '../../context/browserActions'; import { useBrowserSteps } from '../../context/browserSteps'; import { useSocketStore } from '../../context/socket'; From 265018cada6ad1fa79fee2cff568dc2c8b75d0ad Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 02:48:10 +0530 Subject: [PATCH 229/255] fix: remove getScreenshot --- src/components/organisms/BrowserWindow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index 812abd84..f582046b 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -47,7 +47,7 @@ export const BrowserWindow = () => { const { socket } = useSocketStore(); const { width, height } = useBrowserDimensionsStore(); - const { getText, getScreenshot } = useActionContext(); + const { getText } = useActionContext(); const { addBrowserStep } = useBrowserSteps(); const onMouseMove = (e: MouseEvent) => { From a8b91102600d327de1e4fc694daac93a21885ff7 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 03:10:54 +0530 Subject: [PATCH 230/255] feat: screenshot & text steps --- src/context/browserSteps.tsx | 49 +++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/src/context/browserSteps.tsx b/src/context/browserSteps.tsx index 04ebbc28..79113b38 100644 --- a/src/context/browserSteps.tsx +++ b/src/context/browserSteps.tsx @@ -1,5 +1,22 @@ import React, { createContext, useContext, useState } from 'react'; +interface TextStep { + id: number; + type: 'text'; + label: string; + data: string; + selectorObj: SelectorObject; +} + +interface ScreenshotStep { + id: number; + type: 'screenshot'; + fullPage: boolean; +} + + +type BrowserStep = TextStep | ScreenshotStep; + interface SelectorObject { selector: string; tag?: string; @@ -7,18 +24,12 @@ interface SelectorObject { [key: string]: any; } -interface BrowserStep { - id: number; - label: string; - data: string; - selectorObj: SelectorObject; -} - interface BrowserStepsContextType { browserSteps: BrowserStep[]; - addBrowserStep: (label: string, data: string, selectorObj: SelectorObject) => void; + addTextStep: (label: string, data: string, selectorObj: SelectorObject) => void; + addScreenshotStep: (label: string, fullPage: boolean) => void; deleteBrowserStep: (id: number) => void; - updateBrowserStepLabel: (id: number, newLabel: string) => void; + updateBrowserTextStepLabel: (id: number, newLabel: string) => void; } const BrowserStepsContext = createContext(undefined); @@ -26,10 +37,17 @@ const BrowserStepsContext = createContext(u export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [browserSteps, setBrowserSteps] = useState([]); - const addBrowserStep = (label: string, data: string, selectorObj: SelectorObject) => { + const addTextStep = (label: string, data: string, selectorObj: SelectorObject) => { setBrowserSteps(prevSteps => [ ...prevSteps, - { id: Date.now(), label, data, selectorObj } + { id: Date.now(), type: 'text', label, data, selectorObj } + ]); + }; + + const addScreenshotStep = (label: string, fullPage: boolean) => { + setBrowserSteps(prevSteps => [ + ...prevSteps, + { id: Date.now(), type: 'screenshot', label, fullPage } ]); }; @@ -37,7 +55,7 @@ export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ setBrowserSteps(prevSteps => prevSteps.filter(step => step.id !== id)); }; - const updateBrowserStepLabel = (id: number, newLabel: string) => { + const updateBrowserTextStepLabel = (id: number, newLabel: string) => { setBrowserSteps(prevSteps => prevSteps.map(step => step.id === id ? { ...step, label: newLabel } : step @@ -48,9 +66,10 @@ export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ return ( {children} @@ -63,4 +82,4 @@ export const useBrowserSteps = () => { throw new Error('useBrowserSteps must be used within a BrowserStepsProvider'); } return context; -}; \ No newline at end of file +}; From e9b200f87c54fcd69e17cd9c8a36b829146133dc Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 03:11:28 +0530 Subject: [PATCH 231/255] feat: use addTextStep --- src/components/organisms/BrowserWindow.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/organisms/BrowserWindow.tsx b/src/components/organisms/BrowserWindow.tsx index f582046b..fd1589da 100644 --- a/src/components/organisms/BrowserWindow.tsx +++ b/src/components/organisms/BrowserWindow.tsx @@ -48,7 +48,7 @@ export const BrowserWindow = () => { const { socket } = useSocketStore(); const { width, height } = useBrowserDimensionsStore(); const { getText } = useActionContext(); - const { addBrowserStep } = useBrowserSteps(); + const { addTextStep } = useBrowserSteps(); const onMouseMove = (e: MouseEvent) => { if (canvasRef && canvasRef.current && highlighterData) { @@ -121,7 +121,7 @@ export const BrowserWindow = () => { }); setShowAttributeModal(true); } else { - addBrowserStep('', highlighterData.elementInfo?.innerText || '', { + addTextStep('', highlighterData.elementInfo?.innerText || '', { selector: highlighterData.selector, tag: highlighterData.elementInfo?.tagName, attribute: 'innerText' @@ -147,7 +147,7 @@ export const BrowserWindow = () => { } { if (getText === true) { - addBrowserStep('', data, { + addTextStep('', data, { selector: selectedElement.selector, tag: selectedElement.info?.tagName, attribute: attribute From e58002c65585b3127ed61e722273928f80645167 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 03:12:07 +0530 Subject: [PATCH 232/255] feat: use text & screenshot steps --- src/components/organisms/RightSidePanel.tsx | 23 ++++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 8e88801d..95509cce 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -16,7 +16,7 @@ export const RightSidePanel = () => { const { lastAction } = useGlobalInfoStore(); const { getText, getScreenshot, startGetText, stopGetText, startGetScreenshot, stopGetScreenshot } = useActionContext(); - const { browserSteps, updateBrowserStepLabel, deleteBrowserStep } = useBrowserSteps(); + const { browserSteps, updateBrowserTextStepLabel, deleteBrowserStep } = useBrowserSteps(); const { socket } = useSocketStore(); const handleTextLabelChange = (id: number, label: string) => { @@ -31,7 +31,7 @@ export const RightSidePanel = () => { const handleTextStepConfirm = (id: number) => { const label = textLabels[id]?.trim(); if (label) { - updateBrowserStepLabel(id, label); + updateBrowserTextStepLabel(id, label); setConfirmedTextSteps(prev => ({ ...prev, [id]: true })); } else { setErrors(prevErrors => ({ ...prevErrors, [id]: 'Label cannot be empty' })); @@ -51,14 +51,15 @@ export const RightSidePanel = () => { }; const getTextSettingsObject = useCallback(() => { - const settings: Record = {}; + const settings: Record = {}; browserSteps.forEach(step => { - if (step.label && step.selectorObj && step.selectorObj.selector) { - settings[step.label] = step.selectorObj; - } + if (step.type === 'text' && step.label && step.selectorObj?.selector) { + settings[step.label] = step.selectorObj; + } }); return settings; - }, [browserSteps]); +}, [browserSteps]); + const stopCaptureAndEmitGetTextSettings = useCallback(() => { stopGetText(); @@ -104,7 +105,10 @@ export const RightSidePanel = () => { {browserSteps.map(step => ( - + handleTextLabelChange(step.id, e.target.value)} @@ -121,6 +125,9 @@ export const RightSidePanel = () => { margin="normal" InputProps={{ readOnly: confirmedTextSteps[step.id] }} /> + + ) : null + } {!confirmedTextSteps[step.id] && ( From 7d06e5f2524e7e1fa1aa1eef745c6e8a19622a15 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 03:12:58 +0530 Subject: [PATCH 233/255] fix: remove label from addScreenshotStep --- src/context/browserSteps.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/context/browserSteps.tsx b/src/context/browserSteps.tsx index 79113b38..6e531e61 100644 --- a/src/context/browserSteps.tsx +++ b/src/context/browserSteps.tsx @@ -47,7 +47,7 @@ export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ const addScreenshotStep = (label: string, fullPage: boolean) => { setBrowserSteps(prevSteps => [ ...prevSteps, - { id: Date.now(), type: 'screenshot', label, fullPage } + { id: Date.now(), type: 'screenshot', fullPage } ]); }; From 13524ec40d37c605888562a9b7dfbba6522c681f Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 03:13:37 +0530 Subject: [PATCH 234/255] fix: remove label from addScreenshotStep --- src/context/browserSteps.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/context/browserSteps.tsx b/src/context/browserSteps.tsx index 6e531e61..de51a38c 100644 --- a/src/context/browserSteps.tsx +++ b/src/context/browserSteps.tsx @@ -27,7 +27,7 @@ interface SelectorObject { interface BrowserStepsContextType { browserSteps: BrowserStep[]; addTextStep: (label: string, data: string, selectorObj: SelectorObject) => void; - addScreenshotStep: (label: string, fullPage: boolean) => void; + addScreenshotStep: (fullPage: boolean) => void; deleteBrowserStep: (id: number) => void; updateBrowserTextStepLabel: (id: number, newLabel: string) => void; } @@ -44,7 +44,7 @@ export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ ]); }; - const addScreenshotStep = (label: string, fullPage: boolean) => { + const addScreenshotStep = (fullPage: boolean) => { setBrowserSteps(prevSteps => [ ...prevSteps, { id: Date.now(), type: 'screenshot', fullPage } From 379f603ede0095fe5ff801ff79dbb99609e7f39c Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 03:14:03 +0530 Subject: [PATCH 235/255] chore: lint --- src/context/browserSteps.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/context/browserSteps.tsx b/src/context/browserSteps.tsx index de51a38c..e2984e53 100644 --- a/src/context/browserSteps.tsx +++ b/src/context/browserSteps.tsx @@ -11,7 +11,7 @@ interface TextStep { interface ScreenshotStep { id: number; type: 'screenshot'; - fullPage: boolean; + fullPage: boolean; } From eb56c5025c1ff949313f3403e3581c288603b22c Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 03:16:09 +0530 Subject: [PATCH 236/255] feat: add ss step on capture ss --- src/components/organisms/RightSidePanel.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 95509cce..2c78e941 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -16,7 +16,7 @@ export const RightSidePanel = () => { const { lastAction } = useGlobalInfoStore(); const { getText, getScreenshot, startGetText, stopGetText, startGetScreenshot, stopGetScreenshot } = useActionContext(); - const { browserSteps, updateBrowserTextStepLabel, deleteBrowserStep } = useBrowserSteps(); + const { browserSteps, updateBrowserTextStepLabel, deleteBrowserStep, addScreenshotStep } = useBrowserSteps(); const { socket } = useSocketStore(); const handleTextLabelChange = (id: number, label: string) => { @@ -79,6 +79,7 @@ export const RightSidePanel = () => { scale: 'device', }; socket?.emit('action', { action: 'screenshot', settings: screenshotSettings }); + addScreenshotStep(fullPage); }; return ( From d46befb1a6081d491e29634e4ca41950a5d2a398 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 03:24:21 +0530 Subject: [PATCH 237/255] feat: emit getText action only if text steps present --- src/components/organisms/RightSidePanel.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 2c78e941..90709f7b 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -61,13 +61,15 @@ export const RightSidePanel = () => { }, [browserSteps]); - const stopCaptureAndEmitGetTextSettings = useCallback(() => { - stopGetText(); - const settings = getTextSettingsObject(); - if (browserSteps.length > 0) { +const stopCaptureAndEmitGetTextSettings = useCallback(() => { + stopGetText(); + const settings = getTextSettingsObject(); + const hasTextSteps = browserSteps.some(step => step.type === 'text'); + if (hasTextSteps) { socket?.emit('action', { action: 'scrapeSchema', settings }); - } - }, [stopGetText, getTextSettingsObject, socket]); + } +}, [stopGetText, getTextSettingsObject, socket, browserSteps]); + const captureScreenshot = (fullPage: boolean) => { const screenshotSettings: ScreenshotSettings = { From 03de77dba166e4baf57f4a5f8e8d164ac02607d9 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 03:25:36 +0530 Subject: [PATCH 238/255] fix: move confirm & discard buttons w. text steps logic --- src/components/organisms/RightSidePanel.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 90709f7b..960090d4 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -128,15 +128,15 @@ const stopCaptureAndEmitGetTextSettings = useCallback(() => { margin="normal" InputProps={{ readOnly: confirmedTextSteps[step.id] }} /> - - ) : null - } {!confirmedTextSteps[step.id] && ( )} + + ) : null + } ))} From 06c0bf9ec8bb69342883eef01142c6a54c52cd38 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 03:25:52 +0530 Subject: [PATCH 239/255] chore: lint --- src/components/organisms/RightSidePanel.tsx | 82 ++++++++++----------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 960090d4..7c66de25 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -51,24 +51,24 @@ export const RightSidePanel = () => { }; const getTextSettingsObject = useCallback(() => { - const settings: Record = {}; + const settings: Record = {}; browserSteps.forEach(step => { - if (step.type === 'text' && step.label && step.selectorObj?.selector) { - settings[step.label] = step.selectorObj; - } + if (step.type === 'text' && step.label && step.selectorObj?.selector) { + settings[step.label] = step.selectorObj; + } }); return settings; -}, [browserSteps]); + }, [browserSteps]); -const stopCaptureAndEmitGetTextSettings = useCallback(() => { - stopGetText(); - const settings = getTextSettingsObject(); - const hasTextSteps = browserSteps.some(step => step.type === 'text'); - if (hasTextSteps) { + const stopCaptureAndEmitGetTextSettings = useCallback(() => { + stopGetText(); + const settings = getTextSettingsObject(); + const hasTextSteps = browserSteps.some(step => step.type === 'text'); + if (hasTextSteps) { socket?.emit('action', { action: 'scrapeSchema', settings }); - } -}, [stopGetText, getTextSettingsObject, socket, browserSteps]); + } + }, [stopGetText, getTextSettingsObject, socket, browserSteps]); const captureScreenshot = (fullPage: boolean) => { @@ -108,35 +108,35 @@ const stopCaptureAndEmitGetTextSettings = useCallback(() => { {browserSteps.map(step => ( - { - step.type === 'text' ? ( - <> - handleTextLabelChange(step.id, e.target.value)} - fullWidth - margin="normal" - error={!!errors[step.id]} - helperText={errors[step.id]} - InputProps={{ readOnly: confirmedTextSteps[step.id] }} - /> - - {!confirmedTextSteps[step.id] && ( - - - - - )} - - ) : null - } + { + step.type === 'text' ? ( + <> + handleTextLabelChange(step.id, e.target.value)} + fullWidth + margin="normal" + error={!!errors[step.id]} + helperText={errors[step.id]} + InputProps={{ readOnly: confirmedTextSteps[step.id] }} + /> + + {!confirmedTextSteps[step.id] && ( + + + + + )} + + ) : null + } ))} From bbe6d5373a40891083705ee059381d89ce33880e Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 03:28:21 +0530 Subject: [PATCH 240/255] feat: show browser steps if getText || getScreenshot --- src/components/organisms/RightSidePanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 7c66de25..472ffacf 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -104,7 +104,7 @@ export const RightSidePanel = () => { { - getText ? ( + getText || getScreenshot ? ( {browserSteps.map(step => ( From 6892f1d54d76a450ceac7befc2a1558b88814a0d Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 03:37:28 +0530 Subject: [PATCH 241/255] feat: screenshot browser steps --- src/components/organisms/RightSidePanel.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 472ffacf..0f776bad 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -135,7 +135,11 @@ export const RightSidePanel = () => { )} - ) : null + ) : ( + step.type === 'screenshot' && ( + {`Take ${step.fullPage ? 'Fullpage' : 'Visible Part'} Screenshot`} + ) + ) } ))} From eb4b71db2c5568269dd12c1b7981e3329903349f Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 03:37:41 +0530 Subject: [PATCH 242/255] chore: lint --- src/components/organisms/RightSidePanel.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 0f776bad..46202ac3 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -136,9 +136,9 @@ export const RightSidePanel = () => { )} ) : ( - step.type === 'screenshot' && ( - {`Take ${step.fullPage ? 'Fullpage' : 'Visible Part'} Screenshot`} - ) + step.type === 'screenshot' && ( + {`Take ${step.fullPage ? 'Fullpage' : 'Visible Part'} Screenshot`} + ) ) } From 742c65455350be3d6ea5c53babf25c1f95c26610 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 03:45:11 +0530 Subject: [PATCH 243/255] feat: back & discard buttons --- src/components/organisms/RightSidePanel.tsx | 101 +++++++++++--------- 1 file changed, 54 insertions(+), 47 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 46202ac3..5242cbc3 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -13,7 +13,7 @@ export const RightSidePanel = () => { const [textLabels, setTextLabels] = useState<{ [id: number]: string }>({}); const [errors, setErrors] = useState<{ [id: number]: string }>({}); const [confirmedTextSteps, setConfirmedTextSteps] = useState<{ [id: number]: boolean }>({}); - + const { lastAction } = useGlobalInfoStore(); const { getText, getScreenshot, startGetText, stopGetText, startGetScreenshot, stopGetScreenshot } = useActionContext(); const { browserSteps, updateBrowserTextStepLabel, deleteBrowserStep, addScreenshotStep } = useBrowserSteps(); @@ -21,11 +21,10 @@ export const RightSidePanel = () => { const handleTextLabelChange = (id: number, label: string) => { setTextLabels(prevLabels => ({ ...prevLabels, [id]: label })); - if (!label.trim()) { - setErrors(prevErrors => ({ ...prevErrors, [id]: 'Label cannot be empty' })); - } else { - setErrors(prevErrors => ({ ...prevErrors, [id]: '' })); - } + setErrors(prevErrors => ({ + ...prevErrors, + [id]: label.trim() ? '' : 'Label cannot be empty' + })); }; const handleTextStepConfirm = (id: number) => { @@ -51,7 +50,7 @@ export const RightSidePanel = () => { }; const getTextSettingsObject = useCallback(() => { - const settings: Record = {}; + const settings: Record = {}; browserSteps.forEach(step => { if (step.type === 'text' && step.label && step.selectorObj?.selector) { settings[step.label] = step.selectorObj; @@ -60,7 +59,6 @@ export const RightSidePanel = () => { return settings; }, [browserSteps]); - const stopCaptureAndEmitGetTextSettings = useCallback(() => { stopGetText(); const settings = getTextSettingsObject(); @@ -70,7 +68,6 @@ export const RightSidePanel = () => { } }, [stopGetText, getTextSettingsObject, socket, browserSteps]); - const captureScreenshot = (fullPage: boolean) => { const screenshotSettings: ScreenshotSettings = { fullPage, @@ -84,6 +81,14 @@ export const RightSidePanel = () => { addScreenshotStep(fullPage); }; + const handleBack = () => { + if (getText) { + stopGetText(); + } else if (getScreenshot) { + stopGetScreenshot(); + } + }; + return ( @@ -92,60 +97,62 @@ export const RightSidePanel = () => { {!getText && !getScreenshot && } - {getText && } + {getText && } {!getText && !getScreenshot && } {getScreenshot && ( - )} - { - getText || getScreenshot ? ( + {(getText || getScreenshot) && ( + + + + + + {browserSteps.map(step => ( - { - step.type === 'text' ? ( - <> - handleTextLabelChange(step.id, e.target.value)} - fullWidth - margin="normal" - error={!!errors[step.id]} - helperText={errors[step.id]} - InputProps={{ readOnly: confirmedTextSteps[step.id] }} - /> - - {!confirmedTextSteps[step.id] && ( - - - - - )} - - ) : ( - step.type === 'screenshot' && ( - {`Take ${step.fullPage ? 'Fullpage' : 'Visible Part'} Screenshot`} - ) + {step.type === 'text' ? ( + <> + handleTextLabelChange(step.id, e.target.value)} + fullWidth + margin="normal" + error={!!errors[step.id]} + helperText={errors[step.id]} + InputProps={{ readOnly: confirmedTextSteps[step.id] }} + /> + + {!confirmedTextSteps[step.id] && ( + + + + + )} + + ) : ( + step.type === 'screenshot' && ( + {`Take ${step.fullPage ? 'Fullpage' : 'Visible Part'} Screenshot`} ) - } + )} ))} - ) : null - } + + )} ); }; From a3c9debe16fa5ddaf9993f8655898fc8914aeb8f Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 03:50:55 +0530 Subject: [PATCH 244/255] fix: show browser steps always --- src/components/organisms/RightSidePanel.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 5242cbc3..9168f6a8 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -107,7 +107,6 @@ export const RightSidePanel = () => { )} - {(getText || getScreenshot) && ( @@ -152,7 +151,6 @@ export const RightSidePanel = () => { ))} - )} ); }; From 24c1d0cf5761192398e24f9d1003fccb481c68ea Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 05:05:50 +0530 Subject: [PATCH 245/255] revert: only discard button --- src/components/organisms/RightSidePanel.tsx | 95 ++++++++++----------- 1 file changed, 43 insertions(+), 52 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 9168f6a8..c7ba1b9f 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -13,7 +13,7 @@ export const RightSidePanel = () => { const [textLabels, setTextLabels] = useState<{ [id: number]: string }>({}); const [errors, setErrors] = useState<{ [id: number]: string }>({}); const [confirmedTextSteps, setConfirmedTextSteps] = useState<{ [id: number]: boolean }>({}); - + const { lastAction } = useGlobalInfoStore(); const { getText, getScreenshot, startGetText, stopGetText, startGetScreenshot, stopGetScreenshot } = useActionContext(); const { browserSteps, updateBrowserTextStepLabel, deleteBrowserStep, addScreenshotStep } = useBrowserSteps(); @@ -21,10 +21,11 @@ export const RightSidePanel = () => { const handleTextLabelChange = (id: number, label: string) => { setTextLabels(prevLabels => ({ ...prevLabels, [id]: label })); - setErrors(prevErrors => ({ - ...prevErrors, - [id]: label.trim() ? '' : 'Label cannot be empty' - })); + if (!label.trim()) { + setErrors(prevErrors => ({ ...prevErrors, [id]: 'Label cannot be empty' })); + } else { + setErrors(prevErrors => ({ ...prevErrors, [id]: '' })); + } }; const handleTextStepConfirm = (id: number) => { @@ -50,7 +51,7 @@ export const RightSidePanel = () => { }; const getTextSettingsObject = useCallback(() => { - const settings: Record = {}; + const settings: Record = {}; browserSteps.forEach(step => { if (step.type === 'text' && step.label && step.selectorObj?.selector) { settings[step.label] = step.selectorObj; @@ -59,6 +60,7 @@ export const RightSidePanel = () => { return settings; }, [browserSteps]); + const stopCaptureAndEmitGetTextSettings = useCallback(() => { stopGetText(); const settings = getTextSettingsObject(); @@ -68,6 +70,7 @@ export const RightSidePanel = () => { } }, [stopGetText, getTextSettingsObject, socket, browserSteps]); + const captureScreenshot = (fullPage: boolean) => { const screenshotSettings: ScreenshotSettings = { fullPage, @@ -81,14 +84,6 @@ export const RightSidePanel = () => { addScreenshotStep(fullPage); }; - const handleBack = () => { - if (getText) { - stopGetText(); - } else if (getScreenshot) { - stopGetScreenshot(); - } - }; - return ( @@ -97,60 +92,56 @@ export const RightSidePanel = () => { {!getText && !getScreenshot && } - {getText && } + {getText && } {!getText && !getScreenshot && } {getScreenshot && ( + )} - - - - - - {browserSteps.map(step => ( - {step.type === 'text' ? ( - <> - handleTextLabelChange(step.id, e.target.value)} - fullWidth - margin="normal" - error={!!errors[step.id]} - helperText={errors[step.id]} - InputProps={{ readOnly: confirmedTextSteps[step.id] }} - /> - - {!confirmedTextSteps[step.id] && ( - - - - - )} - - ) : ( - step.type === 'screenshot' && ( - {`Take ${step.fullPage ? 'Fullpage' : 'Visible Part'} Screenshot`} + { + step.type === 'text' ? ( + <> + handleTextLabelChange(step.id, e.target.value)} + fullWidth + margin="normal" + error={!!errors[step.id]} + helperText={errors[step.id]} + InputProps={{ readOnly: confirmedTextSteps[step.id] }} + /> + + {!confirmedTextSteps[step.id] && ( + + + + + )} + + ) : ( + step.type === 'screenshot' && ( + {`Take ${step.fullPage ? 'Fullpage' : 'Visible Part'} Screenshot`} + ) ) - )} + } ))} - ); }; From de21f701a25fec5e1299ea1881994033d72996e6 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 05:06:04 +0530 Subject: [PATCH 246/255] chore: lint --- src/components/organisms/RightSidePanel.tsx | 76 ++++++++++----------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index c7ba1b9f..4a73287f 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -103,45 +103,45 @@ export const RightSidePanel = () => { )} - - {browserSteps.map(step => ( - - { - step.type === 'text' ? ( - <> - handleTextLabelChange(step.id, e.target.value)} - fullWidth - margin="normal" - error={!!errors[step.id]} - helperText={errors[step.id]} - InputProps={{ readOnly: confirmedTextSteps[step.id] }} - /> - - {!confirmedTextSteps[step.id] && ( - - - - - )} - - ) : ( - step.type === 'screenshot' && ( - {`Take ${step.fullPage ? 'Fullpage' : 'Visible Part'} Screenshot`} - ) - ) - } - - ))} + + {browserSteps.map(step => ( + + { + step.type === 'text' ? ( + <> + handleTextLabelChange(step.id, e.target.value)} + fullWidth + margin="normal" + error={!!errors[step.id]} + helperText={errors[step.id]} + InputProps={{ readOnly: confirmedTextSteps[step.id] }} + /> + + {!confirmedTextSteps[step.id] && ( + + + + + )} + + ) : ( + step.type === 'screenshot' && ( + {`Take ${step.fullPage ? 'Fullpage' : 'Visible Part'} Screenshot`} + ) + ) + } + ))} + ); }; From 18bd8051992b82f77fb1ae439b7bec33f18a9d58 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 05:12:08 +0530 Subject: [PATCH 247/255] feat: emit capture text setting if no labels are empty in text steps --- src/components/organisms/RightSidePanel.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 4a73287f..230a2a74 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -62,13 +62,19 @@ export const RightSidePanel = () => { const stopCaptureAndEmitGetTextSettings = useCallback(() => { + const hasUnconfirmedTextSteps = browserSteps.some(step => step.type === 'text' && !confirmedTextSteps[step.id]); + if (hasUnconfirmedTextSteps) { + alert('Please confirm all text labels before proceeding.'); + return; + } stopGetText(); const settings = getTextSettingsObject(); const hasTextSteps = browserSteps.some(step => step.type === 'text'); if (hasTextSteps) { socket?.emit('action', { action: 'scrapeSchema', settings }); } - }, [stopGetText, getTextSettingsObject, socket, browserSteps]); + }, [stopGetText, getTextSettingsObject, socket, browserSteps, confirmedTextSteps]); + const captureScreenshot = (fullPage: boolean) => { From e994342e74e2a453d689e00b8ff7e30ecb54595c Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 05:16:12 +0530 Subject: [PATCH 248/255] feat: use notify from global info store --- src/components/organisms/RightSidePanel.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 230a2a74..1b2dafb9 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -14,7 +14,7 @@ export const RightSidePanel = () => { const [errors, setErrors] = useState<{ [id: number]: string }>({}); const [confirmedTextSteps, setConfirmedTextSteps] = useState<{ [id: number]: boolean }>({}); - const { lastAction } = useGlobalInfoStore(); + const { lastAction, notify } = useGlobalInfoStore(); const { getText, getScreenshot, startGetText, stopGetText, startGetScreenshot, stopGetScreenshot } = useActionContext(); const { browserSteps, updateBrowserTextStepLabel, deleteBrowserStep, addScreenshotStep } = useBrowserSteps(); const { socket } = useSocketStore(); @@ -64,7 +64,7 @@ export const RightSidePanel = () => { const stopCaptureAndEmitGetTextSettings = useCallback(() => { const hasUnconfirmedTextSteps = browserSteps.some(step => step.type === 'text' && !confirmedTextSteps[step.id]); if (hasUnconfirmedTextSteps) { - alert('Please confirm all text labels before proceeding.'); + notify('error', 'Please confirm no labels are empty'); return; } stopGetText(); From 0da1ef5ffba6beec0e076d97895e034b2ad25437 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 05:16:33 +0530 Subject: [PATCH 249/255] chore: lint --- src/components/organisms/RightSidePanel.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 1b2dafb9..b770d76f 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -75,8 +75,6 @@ export const RightSidePanel = () => { } }, [stopGetText, getTextSettingsObject, socket, browserSteps, confirmedTextSteps]); - - const captureScreenshot = (fullPage: boolean) => { const screenshotSettings: ScreenshotSettings = { fullPage, From 3a792e4921ea78024805b60d0a9c0731e05048d7 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 05:22:47 +0530 Subject: [PATCH 250/255] feat: capture text confirm & discard buttons --- src/components/organisms/RightSidePanel.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index b770d76f..21ba5e1c 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -96,7 +96,15 @@ export const RightSidePanel = () => { {!getText && !getScreenshot && } - {getText && } + {getText && + <> + + + + + + } + {!getText && !getScreenshot && } {getScreenshot && ( From 56fd9c8b1bd333b40a63a6f3c076866ee34d6ed2 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 05:32:05 +0530 Subject: [PATCH 251/255] feat: call stopGetScreenshot after capture screenshot --- src/components/organisms/RightSidePanel.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 21ba5e1c..955670bd 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -86,6 +86,7 @@ export const RightSidePanel = () => { }; socket?.emit('action', { action: 'screenshot', settings: screenshotSettings }); addScreenshotStep(fullPage); + stopGetScreenshot(); }; return ( From 3efd0a8e9e32797f9529c678f54ef1c2570b98ca Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 05:32:45 +0530 Subject: [PATCH 252/255] refactor: rearrange imports as per text or ss --- src/components/organisms/RightSidePanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 955670bd..04e57983 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -15,7 +15,7 @@ export const RightSidePanel = () => { const [confirmedTextSteps, setConfirmedTextSteps] = useState<{ [id: number]: boolean }>({}); const { lastAction, notify } = useGlobalInfoStore(); - const { getText, getScreenshot, startGetText, stopGetText, startGetScreenshot, stopGetScreenshot } = useActionContext(); + const { getText, startGetText, stopGetText, getScreenshot, startGetScreenshot, stopGetScreenshot } = useActionContext(); const { browserSteps, updateBrowserTextStepLabel, deleteBrowserStep, addScreenshotStep } = useBrowserSteps(); const { socket } = useSocketStore(); From cc734f86d6351935a07c16dfdfcbabab0d1e1a5e Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 06:04:22 +0530 Subject: [PATCH 253/255] feat: icons for capture text --- src/components/organisms/RightSidePanel.tsx | 36 +++++++++++++++------ 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 04e57983..38c633b6 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -1,5 +1,7 @@ import React, { useState, useCallback } from 'react'; import { Button, Paper, Box, TextField } from "@mui/material"; +import EditIcon from '@mui/icons-material/Edit'; +import TextFieldsIcon from '@mui/icons-material/TextFields'; import styled from "styled-components"; import { SimpleBox } from "../atoms/Box"; import Typography from "@mui/material/Typography"; @@ -8,6 +10,8 @@ import { useActionContext } from '../../context/browserActions'; import { useBrowserSteps } from '../../context/browserSteps'; import { useSocketStore } from '../../context/socket'; import { ScreenshotSettings } from '../../shared/types'; +import InputAdornment from '@mui/material/InputAdornment'; + export const RightSidePanel = () => { const [textLabels, setTextLabels] = useState<{ [id: number]: string }>({}); @@ -97,13 +101,13 @@ export const RightSidePanel = () => { {!getText && !getScreenshot && } - {getText && - <> - - - - - + {getText && + <> + + + + + } {!getText && !getScreenshot && } @@ -130,14 +134,28 @@ export const RightSidePanel = () => { margin="normal" error={!!errors[step.id]} helperText={errors[step.id]} - InputProps={{ readOnly: confirmedTextSteps[step.id] }} + InputProps={{ + readOnly: confirmedTextSteps[step.id], + startAdornment: ( + + + + ) + }} /> + + + ) + }} /> {!confirmedTextSteps[step.id] && ( From 26a51c3aeea82a16c5f2bf05b84c1852476c437d Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 06:13:13 +0530 Subject: [PATCH 254/255] feat: icons for capture screenshot --- src/components/organisms/RightSidePanel.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index 38c633b6..de3fddff 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -2,6 +2,7 @@ import React, { useState, useCallback } from 'react'; import { Button, Paper, Box, TextField } from "@mui/material"; import EditIcon from '@mui/icons-material/Edit'; import TextFieldsIcon from '@mui/icons-material/TextFields'; +import DocumentScannerIcon from '@mui/icons-material/DocumentScanner'; import styled from "styled-components"; import { SimpleBox } from "../atoms/Box"; import Typography from "@mui/material/Typography"; @@ -166,7 +167,12 @@ export const RightSidePanel = () => { ) : ( step.type === 'screenshot' && ( - {`Take ${step.fullPage ? 'Fullpage' : 'Visible Part'} Screenshot`} + + + + {`Take ${step.fullPage ? 'Fullpage' : 'Visible Part'} Screenshot`} + + ) ) } From 63cd694d85d42053d755860b8f068e43ad9ed072 Mon Sep 17 00:00:00 2001 From: karishmas6 Date: Tue, 6 Aug 2024 06:13:24 +0530 Subject: [PATCH 255/255] chore: lint --- src/components/organisms/RightSidePanel.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/organisms/RightSidePanel.tsx b/src/components/organisms/RightSidePanel.tsx index de3fddff..514db44f 100644 --- a/src/components/organisms/RightSidePanel.tsx +++ b/src/components/organisms/RightSidePanel.tsx @@ -135,7 +135,7 @@ export const RightSidePanel = () => { margin="normal" error={!!errors[step.id]} helperText={errors[step.id]} - InputProps={{ + InputProps={{ readOnly: confirmedTextSteps[step.id], startAdornment: ( @@ -149,7 +149,7 @@ export const RightSidePanel = () => { value={step.data} fullWidth margin="normal" - InputProps={{ + InputProps={{ readOnly: confirmedTextSteps[step.id], startAdornment: ( @@ -168,11 +168,11 @@ export const RightSidePanel = () => { ) : ( step.type === 'screenshot' && ( - - - {`Take ${step.fullPage ? 'Fullpage' : 'Visible Part'} Screenshot`} - - + + + {`Take ${step.fullPage ? 'Fullpage' : 'Visible Part'} Screenshot`} + + ) ) }