diff --git a/src/App.tsx b/src/App.tsx
new file mode 100644
index 00000000..7f0715a9
--- /dev/null
+++ b/src/App.tsx
@@ -0,0 +1,15 @@
+import React from 'react';
+
+import { GlobalInfoProvider } from "./context/globalInfo";
+import { PageWrapper } from "./pages/PageWrappper";
+
+function App() {
+
+ return (
+
+
+
+ );
+}
+
+export default App;
diff --git a/src/components/atoms/Highlighter.tsx b/src/components/atoms/Highlighter.tsx
index b72cac06..3199b083 100644
--- a/src/components/atoms/Highlighter.tsx
+++ b/src/components/atoms/Highlighter.tsx
@@ -36,8 +36,6 @@ export const Highlighter = ({ unmodifiedRect, displayedSelector = '', width, hei
height: unmodifiedRect.height,
};
- const adjustedWidth = Math.min(rect.width, width - rect.left); // Adjust width if it extends beyond canvas boundary
- const adjustedHeight = Math.min(rect.height, height - rect.top); // Adjust height if it extends beyond canvas boundary
console.log('unmodifiedRect:', unmodifiedRect)
console.log('rectangle:', rect)
diff --git a/src/constants/const.ts b/src/constants/const.ts
new file mode 100644
index 00000000..8aec049e
--- /dev/null
+++ b/src/constants/const.ts
@@ -0,0 +1,5 @@
+export const VIEWPORT_W = 1280;
+export const VIEWPORT_H = 720;
+
+export const ONE_PERCENT_OF_VIEWPORT_W = VIEWPORT_W / 100;
+export const ONE_PERCENT_OF_VIEWPORT_H = VIEWPORT_H / 100;
diff --git a/src/context/globalInfo.tsx b/src/context/globalInfo.tsx
new file mode 100644
index 00000000..e7eb2ed2
--- /dev/null
+++ b/src/context/globalInfo.tsx
@@ -0,0 +1,83 @@
+import React, { createContext, useContext, useState } from "react";
+import { AlertSnackbarProps } from "../components/atoms/AlertSnackbar";
+
+
+interface GlobalInfo {
+ browserId: string | null;
+ setBrowserId: (newId: string | null) => void;
+ lastAction: string;
+ setLastAction: (action: string) => void;
+ notification: AlertSnackbarProps;
+ notify: (severity: 'error' | 'warning' | 'info' | 'success', message: string) => void;
+ closeNotify: () => void;
+ recordings: string[];
+ setRecordings: (recordings: string[]) => void;
+ rerenderRuns: boolean;
+ setRerenderRuns: (rerenderRuns: boolean) => void;
+ recordingLength: number;
+ setRecordingLength: (recordingLength: number) => void;
+};
+
+class GlobalInfoStore implements Partial {
+ browserId = null;
+ lastAction = '';
+ recordingLength = 0;
+ notification: AlertSnackbarProps = {
+ severity: 'info',
+ message: '',
+ isOpen: false,
+ };
+ recordings: string[] = [];
+ rerenderRuns = false;
+};
+
+const globalInfoStore = new GlobalInfoStore();
+const globalInfoContext = createContext(globalInfoStore as GlobalInfo);
+
+export const useGlobalInfoStore = () => useContext(globalInfoContext);
+
+export const GlobalInfoProvider = ({ children }: { children: JSX.Element }) => {
+ const [browserId, setBrowserId] = useState(globalInfoStore.browserId);
+ const [lastAction, setLastAction] = useState(globalInfoStore.lastAction);
+ const [notification, setNotification] = useState(globalInfoStore.notification);
+ const [recordings, setRecordings] = useState(globalInfoStore.recordings);
+ const [rerenderRuns, setRerenderRuns] = useState(globalInfoStore.rerenderRuns);
+ const [recordingLength, setRecordingLength] = useState(globalInfoStore.recordingLength);
+
+ const notify = (severity: 'error' | 'warning' | 'info' | 'success', message: string) => {
+ setNotification({ severity, message, isOpen: true });
+ }
+
+ const closeNotify = () => {
+ setNotification(globalInfoStore.notification);
+ }
+
+ const setBrowserIdWithValidation = (browserId: string | null) => {
+ setBrowserId(browserId);
+ if (!browserId) {
+ setRecordingLength(0);
+ }
+ }
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/src/context/socket.tsx b/src/context/socket.tsx
new file mode 100644
index 00000000..678f5520
--- /dev/null
+++ b/src/context/socket.tsx
@@ -0,0 +1,52 @@
+import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';
+import { io, Socket } from 'socket.io-client';
+
+const SERVER_ENDPOINT = 'http://localhost:8080';
+
+interface SocketState {
+ socket: Socket | null;
+ id: string;
+ setId: (id: string) => void;
+};
+
+class SocketStore implements Partial {
+ socket = null;
+ id = '';
+};
+
+const socketStore = new SocketStore();
+const socketStoreContext = createContext(socketStore as SocketState);
+
+export const useSocketStore = () => useContext(socketStoreContext);
+
+export const SocketProvider = ({ children }: { children: JSX.Element }) => {
+ const [socket, setSocket] = useState(socketStore.socket);
+ const [id, setActiveId] = useState(socketStore.id);
+
+ const setId = useCallback((id: string) => {
+ // the socket client connection is recomputed whenever id changes -> the new browser has been initialized
+ const socket =
+ io(`${SERVER_ENDPOINT}/${id}`, {
+ transports: ["websocket"],
+ rejectUnauthorized: false
+ });
+
+ socket.on('connect', () => console.log('connected to socket'));
+ socket.on("connect_error", (err) => console.log(`connect_error due to ${err.message}`));
+
+ setSocket(socket);
+ setActiveId(id);
+ }, [setSocket]);
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/src/index.css b/src/index.css
new file mode 100644
index 00000000..21808ea5
--- /dev/null
+++ b/src/index.css
@@ -0,0 +1,17 @@
+body {
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+ sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+ monospace;
+}
+
+html {
+ overflow-y:scroll;
+}
diff --git a/src/index.tsx b/src/index.tsx
new file mode 100644
index 00000000..b7688442
--- /dev/null
+++ b/src/index.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import './index.css';
+import App from './App';
+
+const root = ReactDOM.createRoot(
+ document.getElementById('root') as HTMLElement
+);
+root.render(
+
+
+
+);
+
diff --git a/src/shared/constants.ts b/src/shared/constants.ts
new file mode 100644
index 00000000..d7ce0bd0
--- /dev/null
+++ b/src/shared/constants.ts
@@ -0,0 +1,3 @@
+import { WorkflowFile } from "@wbr-project/wbr-interpret";
+
+export const emptyWorkflow: WorkflowFile = { workflow: [] };
diff --git a/src/shared/types.ts b/src/shared/types.ts
new file mode 100644
index 00000000..72e86a0e
--- /dev/null
+++ b/src/shared/types.ts
@@ -0,0 +1,26 @@
+import { WorkflowFile } from "@wbr-project/wbr-interpret";
+import { Locator } from "playwright";
+
+export type Workflow = WorkflowFile["workflow"];
+
+export interface ScreenshotSettings {
+ animations?: "disabled" | "allow";
+ caret?: "hide" | "initial";
+ clip?: {
+ x: number;
+ y: number;
+ width: number;
+ height: number;
+ };
+ fullPage?: boolean;
+ mask?: Locator[];
+ omitBackground?: boolean;
+ // is this still needed? - @wbr-project/wbr-interpret outputs to a binary output
+ path?: string;
+ quality?: number;
+ scale?: "css" | "device";
+ timeout?: number;
+ type?: "jpeg" | "png";
+};
+
+export declare type CustomActions = 'scrape' | 'scrapeSchema' | 'scroll' | 'screenshot' | 'script' | 'enqueueLinks' | 'flag';