diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f417792
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.vs/
+Borealis-Workflow-Automation-Tool/
\ No newline at end of file
diff --git a/Data/WebUI/public/index.html b/Data/WebUI/public/index.html
new file mode 100644
index 0000000..da117a9
--- /dev/null
+++ b/Data/WebUI/public/index.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Borealis
+
+
+
+
+
+
+
diff --git a/Data/WebUI/src/App.js b/Data/WebUI/src/App.js
new file mode 100644
index 0000000..a26f26b
--- /dev/null
+++ b/Data/WebUI/src/App.js
@@ -0,0 +1,139 @@
+import React from "react";
+import FlowEditor from "./components/FlowEditor";
+import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
+import {
+ AppBar,
+ Toolbar,
+ Typography,
+ Box,
+ Menu,
+ MenuItem,
+ Button,
+ CssBaseline,
+ ThemeProvider,
+ createTheme
+} from "@mui/material";
+
+const darkTheme = createTheme({
+ palette: {
+ mode: "dark",
+ background: {
+ default: "#121212",
+ paper: "#1e1e1e"
+ },
+ text: {
+ primary: "#ffffff"
+ }
+ }
+});
+
+export default function App() {
+ const [workflowsAnchorEl, setWorkflowsAnchorEl] = React.useState(null);
+ const [aboutAnchorEl, setAboutAnchorEl] = React.useState(null);
+
+ const handleWorkflowsMenuOpen = (event) => {
+ setWorkflowsAnchorEl(event.currentTarget);
+ };
+
+ const handleAboutMenuOpen = (event) => {
+ setAboutAnchorEl(event.currentTarget);
+ };
+
+ const handleWorkflowsMenuClose = () => {
+ setWorkflowsAnchorEl(null);
+ };
+
+ const handleAboutMenuClose = () => {
+ setAboutAnchorEl(null);
+ };
+
+ return (
+
+
+ {/*
+ Main container that:
+ - fills 100% viewport height
+ - organizes content with flexbox (vertical)
+ */}
+
+ {/* --- TOP BAR --- */}
+
+
+
+ Borealis - Workflow Automation Tool
+
+
+ {/* Workflows Menu */}
+ }
+ >
+ Workflows
+
+
+
+ {/* About Menu */}
+ }
+ >
+ About
+
+
+
+
+
+ {/* --- REACT FLOW EDITOR --- */}
+ {/*
+ flexGrow={1} ⇒ This box expands to fill remaining vertical space
+ overflow="hidden" ⇒ No scroll bars, so React Flow does internal panning
+ mt: 1 ⇒ Add top margin so the gradient starts closer to the AppBar.
+ */}
+
+ {
+ document.getElementById("nodeCount").innerText = count;
+ }}
+ />
+
+
+ {/* --- STATUS BAR at BOTTOM --- */}
+
+ Nodes: 0 | Update Rate: 500ms | Flask API Server:{" "}
+
+ http://127.0.0.1:5000/data/api/nodes
+
+
+
+
+ );
+}
diff --git a/Data/WebUI/src/components/FlowEditor.css b/Data/WebUI/src/components/FlowEditor.css
new file mode 100644
index 0000000..4990c4f
--- /dev/null
+++ b/Data/WebUI/src/components/FlowEditor.css
@@ -0,0 +1,23 @@
+/* FlowEditor background container */
+.flow-editor-container {
+ position: relative;
+ width: 100vw;
+ height: 100vh;
+}
+
+ /* Blue Gradient Overlay */
+ .flow-editor-container::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none; /* Ensures grid and nodes remain fully interactive */
+ background: linear-gradient( to bottom, rgba(9, 44, 68, 0.9) 0%, /* Deep blue at the top */
+ rgba(30, 30, 30, 0) 45%, /* Fade out towards center */
+ rgba(30, 30, 30, 0) 75%, /* No gradient in the middle */
+ rgba(9, 44, 68, 0.7) 100% /* Deep blue at the bottom */
+ );
+ z-index: -1; /* Ensures it stays behind the React Flow elements */
+ }
diff --git a/Data/WebUI/src/components/FlowEditor.jsx b/Data/WebUI/src/components/FlowEditor.jsx
new file mode 100644
index 0000000..4c72a61
--- /dev/null
+++ b/Data/WebUI/src/components/FlowEditor.jsx
@@ -0,0 +1,68 @@
+import React, { useState, useEffect, useCallback } from "react";
+import ReactFlow, {
+ addEdge,
+ Controls,
+ Background,
+} from "reactflow";
+import "reactflow/dist/style.css";
+import "./FlowEditor.css";
+
+const fetchNodes = async () => {
+ const response = await fetch("/api/workflow");
+ return response.json();
+};
+
+const saveWorkflow = async (workflow) => {
+ await fetch("/api/workflow", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(workflow),
+ });
+};
+
+export default function FlowEditor() {
+ const [elements, setElements] = useState([]);
+
+ useEffect(() => {
+ fetchNodes().then((data) => {
+ // Data should contain nodes and edges arrays
+ const newElements = [...data.nodes, ...data.edges];
+ setElements(newElements);
+ });
+ }, []);
+
+ const onConnect = useCallback(
+ (params) => {
+ const newEdge = { id: `e${params.source}-${params.target}`, ...params };
+ setElements((els) => [...els, newEdge]);
+
+ // Separate nodes/edges for saving:
+ const nodes = elements.filter((el) => el.type);
+ const edges = elements.filter((el) => !el.type);
+
+ saveWorkflow({
+ nodes,
+ edges: [...edges, newEdge],
+ });
+ },
+ [elements]
+ );
+
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/Data/server.py b/Data/server.py
new file mode 100644
index 0000000..377227e
--- /dev/null
+++ b/Data/server.py
@@ -0,0 +1,131 @@
+from flask import Flask, send_from_directory, jsonify, request, abort
+import os
+import importlib
+import inspect
+import uuid
+from OdenGraphQt import BaseNode
+
+# Determine the absolute path for the React build folder
+build_folder = os.path.join(os.getcwd(), "web-interface", "build")
+if not os.path.exists(build_folder):
+ print("WARNING: web-interface build folder not found. Please build your React app.")
+
+app = Flask(__name__, static_folder=build_folder, static_url_path="/")
+
+# Directory where nodes are stored
+NODES_PACKAGE = "Nodes"
+
+# In-memory workflow storage
+workflow_data = {
+ "nodes": [],
+ "edges": [] # Store connections separately
+}
+
+def import_nodes_from_folder(package_name):
+ """Dynamically import node classes from the given package and list them."""
+ nodes_by_category = {}
+ package = importlib.import_module(package_name)
+ package_path = package.__path__[0]
+
+ for root, _, files in os.walk(package_path):
+ rel_path = os.path.relpath(root, package_path).replace(os.sep, ".")
+ module_prefix = f"{package_name}.{rel_path}" if rel_path != "." else package_name
+ category_name = os.path.basename(root)
+
+ for file in files:
+ if file.endswith(".py") and file != "__init__.py":
+ module_name = f"{module_prefix}.{file[:-3]}"
+ try:
+ module = importlib.import_module(module_name)
+ for name, obj in inspect.getmembers(module, inspect.isclass):
+ if issubclass(obj, BaseNode) and obj.__module__ == module.__name__:
+ if category_name not in nodes_by_category:
+ nodes_by_category[category_name] = []
+ nodes_by_category[category_name].append(obj.NODE_NAME)
+ except Exception as e:
+ print(f"Failed to import {module_name}: {e}")
+
+ return nodes_by_category
+
+@app.route("/")
+def serve_frontend():
+ """Serve the React app."""
+ index_path = os.path.join(build_folder, "index.html")
+ if os.path.exists(index_path):
+ return send_from_directory(app.static_folder, "index.html")
+ return "Borealis React App Code Not Found
Please re-deploy Borealis Workflow Automation Tool
", 404
+
+@app.route("/api/nodes", methods=["GET"])
+def get_available_nodes():
+ """Return available node types."""
+ nodes = import_nodes_from_folder(NODES_PACKAGE)
+ return jsonify(nodes)
+
+@app.route("/api/workflow", methods=["GET", "POST"])
+def handle_workflow():
+ """Retrieve or update the workflow."""
+ global workflow_data
+ if request.method == "GET":
+ return jsonify(workflow_data)
+ elif request.method == "POST":
+ data = request.get_json()
+ if not data:
+ abort(400, "Invalid workflow data")
+ workflow_data = data
+ return jsonify({"status": "success", "workflow": workflow_data})
+
+@app.route("/api/node", methods=["POST"])
+def create_node():
+ """Create a new node with a unique UUID."""
+ data = request.get_json()
+ if not data or "nodeType" not in data:
+ abort(400, "Invalid node data")
+
+ node_id = str(uuid.uuid4()) # Generate a unique ID
+ node = {
+ "id": node_id,
+ "type": data["nodeType"],
+ "position": data.get("position", {"x": 100, "y": 100}),
+ "properties": data.get("properties", {})
+ }
+ workflow_data["nodes"].append(node)
+ return jsonify({"status": "success", "node": node})
+
+@app.route("/api/node/", methods=["PUT", "DELETE"])
+def modify_node(node_id):
+ """Update or delete a node."""
+ global workflow_data
+ if request.method == "PUT":
+ data = request.get_json()
+ for node in workflow_data["nodes"]:
+ if node["id"] == node_id:
+ node["position"] = data.get("position", node["position"])
+ node["properties"] = data.get("properties", node["properties"])
+ return jsonify({"status": "success", "node": node})
+ abort(404, "Node not found")
+
+ elif request.method == "DELETE":
+ workflow_data["nodes"] = [n for n in workflow_data["nodes"] if n["id"] != node_id]
+ return jsonify({"status": "success", "deletedNode": node_id})
+
+@app.route("/api/edge", methods=["POST"])
+def create_edge():
+ """Create a new connection (edge) between nodes."""
+ data = request.get_json()
+ if not data or "source" not in data or "target" not in data:
+ abort(400, "Invalid edge data")
+
+ edge_id = str(uuid.uuid4())
+ edge = {"id": edge_id, "source": data["source"], "target": data["target"]}
+ workflow_data["edges"].append(edge)
+ return jsonify({"status": "success", "edge": edge})
+
+@app.route("/api/edge/", methods=["DELETE"])
+def delete_edge(edge_id):
+ """Delete an edge by ID."""
+ global workflow_data
+ workflow_data["edges"] = [e for e in workflow_data["edges"] if e["id"] != edge_id]
+ return jsonify({"status": "success", "deletedEdge": edge_id})
+
+if __name__ == "__main__":
+ app.run(host="0.0.0.0", port=5000, debug=False)
diff --git a/Launch-Borealis.ps1 b/Launch-Borealis.ps1
new file mode 100644
index 0000000..36a0b1b
--- /dev/null
+++ b/Launch-Borealis.ps1
@@ -0,0 +1,149 @@
+# Start_Windows - WebServer.ps1
+# Run this script with:
+# Set-ExecutionPolicy Unrestricted -Scope Process; .\Start_Windows -WebServer.ps1
+
+# ---------------------- Initialization & Visuals ----------------------
+$symbols = @{
+ Success = [char]0x2705
+ Running = [char]0x23F3
+ Fail = [char]0x274C
+ Info = [char]0x2139
+}
+
+function Write-ProgressStep {
+ param (
+ [string]$Message,
+ [string]$Status = $symbols["Info"] # Ensure proper lookup
+ )
+ Write-Host "`r$Status $Message... " -NoNewline
+}
+
+function Run-Step {
+ param (
+ [string]$Message,
+ [scriptblock]$Script
+ )
+ Write-ProgressStep -Message $Message -Status "$($symbols.Running)"
+ try {
+ & $Script
+ if ($LASTEXITCODE -eq 0 -or $?) {
+ Write-Host "`r$($symbols.Success) $Message " # Fix symbol lookup
+ } else {
+ throw "Non-zero exit code"
+ }
+ } catch {
+ Write-Host "`r$($symbols.Fail) $Message - Failed: $_ " -ForegroundColor Red
+ exit 1
+ }
+}
+
+Clear-Host
+Write-Host "Deploying Borealis - Workflow Automation Tool..." -ForegroundColor Green
+Write-Host "===================================================================================="
+
+# ---------------------- Node.js Check ----------------------
+if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
+ Write-Host "`r$($symbols.Fail) Node.js is not installed. Please install Node.js and try again." -ForegroundColor Red
+ exit 1
+}
+
+# ---------------------- Path Definitions ----------------------
+$venvFolder = "Borealis-Workflow-Automation-Tool"
+$dataSource = "Data"
+$dataDestination = "$venvFolder\Borealis"
+$customUIPath = "$dataSource\WebUI"
+$webUIDestination = "$venvFolder\web-interface"
+
+# ---------------------- Create Python Virtual Environment ----------------------
+Run-Step "Create Virtual Python Environment" {
+ if (!(Test-Path "$venvFolder\Scripts\Activate")) {
+ python -m venv $venvFolder | Out-Null
+ }
+}
+
+# ---------------------- Copy Server Data ----------------------
+Run-Step "Copy Borealis Server Data into Virtual Python Environment" {
+ if (Test-Path $dataSource) {
+ if (Test-Path $dataDestination) {
+ Remove-Item -Recurse -Force $dataDestination | Out-Null
+ }
+ New-Item -Path $dataDestination -ItemType Directory -Force | Out-Null
+ Copy-Item -Path "$dataSource\*" -Destination $dataDestination -Recurse
+ } else {
+ Write-Host "`r$($symbols.Info) Warning: Data folder not found, skipping copy." -ForegroundColor Yellow
+ }
+}
+
+# ---------------------- React UI Deployment ----------------------
+Run-Step "Create a new ReactJS App in $webUIDestination" {
+ if (-not (Test-Path $webUIDestination)) {
+ npx create-react-app $webUIDestination | Out-Null
+ }
+}
+
+Run-Step "Overwrite ReactJS App Files with Borealis ReactJS Files" {
+ if (Test-Path $customUIPath) {
+ Copy-Item -Path "$customUIPath\*" -Destination $webUIDestination -Recurse -Force
+ } else {
+ Write-Host "`r$($symbols.Info) No custom UI found, using default React app." -ForegroundColor Yellow
+ }
+}
+
+Run-Step "Remove Existing ReactJS Build Folder (If Exists)" {
+ if (Test-Path "$webUIDestination\build") {
+ Remove-Item -Path "$webUIDestination\build" -Recurse -Force
+ }
+}
+
+# ---------------------- Activate Python Virtual Environment ----------------------
+Run-Step "Activate Virtual Python Environment" {
+ . "$venvFolder\Scripts\Activate"
+}
+
+# ---------------------- Install Python Dependencies ----------------------
+Run-Step "Install Python Dependencies into Virtual Python Environment" {
+ if (Test-Path "requirements.txt") {
+ pip install -q -r requirements.txt 2>&1 | Out-Null
+ } else {
+ Write-Host "`r$($symbols.Info) No requirements.txt found, skipping Python packages." -ForegroundColor Yellow
+ }
+}
+
+# ---------------------- Build React App ----------------------
+Run-Step "Install NPM into ReactJS App" {
+ $packageJsonPath = Join-Path $webUIDestination "package.json"
+ if (Test-Path $packageJsonPath) {
+ Push-Location $webUIDestination
+ $env:npm_config_loglevel = "silent"
+ npm install --silent --no-fund --audit=false 2>&1 | Out-Null
+ Pop-Location
+ }
+}
+
+Run-Step "Install React Flow into ReactJS App" {
+ Push-Location $webUIDestination
+ npm install reactflow --no-fund --audit=false | Out-Null
+ Pop-Location
+}
+
+Run-Step "Install Material UI Libraries into ReactJS App" {
+ Push-Location $webUIDestination
+ $env:npm_config_loglevel = "silent" # Force NPM to be completely silent
+ npm install --silent @mui/material @mui/icons-material @emotion/react @emotion/styled --no-fund --audit=false 2>&1 | Out-Null
+ Pop-Location
+}
+
+Run-Step "Build ReactJS App" {
+ Push-Location $webUIDestination
+ npm run build | Out-Null
+ Pop-Location
+}
+
+# ---------------------- Launch Flask Server ----------------------
+Push-Location $venvFolder
+Write-Host "`nLaunching Borealis..." -ForegroundColor Green
+Write-Host "===================================================================================="
+Write-Host "$($symbols.Running) Starting the Python Flask server..." -NoNewline
+python "Borealis\server.py"
+Write-Host "`r$($symbols.Success) Borealis Launched Successfully!"
+Pop-Location
diff --git a/Launch-Borealis.sh b/Launch-Borealis.sh
new file mode 100644
index 0000000..d6eb4c2
--- /dev/null
+++ b/Launch-Borealis.sh
@@ -0,0 +1,185 @@
+#!/usr/bin/env bash
+# --------------------------------------------------------------------
+# Deploying Borealis - Workflow Automation Tool
+#
+# This script deploys the Borealis Workflow Automation Tool by:
+# - Detecting the Linux distro and installing required system dependencies.
+# - Creating a Python virtual environment.
+# - Copying server data.
+# - Setting up a React UI application.
+# - Installing Python and Node dependencies.
+# - Building the React app.
+# - Launching the Flask server.
+#
+# Usage:
+# chmod +x deploy_borealis.sh
+# ./deploy_borealis.sh
+# --------------------------------------------------------------------
+
+# ---------------------- Initialization & Visuals ----------------------
+GREEN="\033[0;32m"
+YELLOW="\033[1;33m"
+RED="\033[0;31m"
+RESET="\033[0m"
+CHECKMARK="✅"
+HOURGLASS="⏳"
+CROSSMARK="❌"
+INFO="ℹ️"
+
+# Function to run a step with progress visuals and error checking
+run_step() {
+ local message="$1"
+ shift
+ echo -ne "${HOURGLASS} ${message}... "
+ if "$@"; then
+ echo -e "\r${CHECKMARK} ${message}"
+ else
+ echo -e "\r${CROSSMARK} ${message} - Failed${RESET}"
+ exit 1
+ fi
+}
+
+echo -e "${GREEN}Deploying Borealis - Workflow Automation Tool...${RESET}"
+echo "===================================================================================="
+
+# ---------------------- Detect Linux Distribution ----------------------
+detect_distro() {
+ # This function detects the Linux distribution by sourcing /etc/os-release.
+ if [ -f /etc/os-release ]; then
+ . /etc/os-release
+ DISTRO_ID=$ID
+ else
+ DISTRO_ID="unknown"
+ fi
+ echo -e "${INFO} Detected OS: ${DISTRO_ID}"
+}
+detect_distro
+
+# ---------------------- Install System Dependencies ----------------------
+install_core_dependencies() {
+ # Install required packages based on detected Linux distribution.
+ case "$DISTRO_ID" in
+ ubuntu|debian)
+ sudo apt update -qq
+ sudo apt install -y python3 python3-venv python3-pip nodejs npm git curl
+ ;;
+ rhel|centos|fedora|rocky)
+ # For Fedora and similar distributions, the venv module is built-in so we omit python3-venv.
+ sudo dnf install -y python3 python3-pip nodejs npm git curl
+ ;;
+ arch)
+ sudo pacman -Sy --noconfirm python python-venv python-pip nodejs npm git curl
+ ;;
+ *)
+ echo -e "${RED}${CROSSMARK} Unsupported Linux distribution: ${DISTRO_ID}${RESET}"
+ exit 1
+ ;;
+ esac
+}
+run_step "Install System Dependencies" install_core_dependencies
+
+# ---------------------- Path Setup ----------------------
+# Variables and path definitions
+venvFolder="Borealis-Workflow-Automation-Tool"
+dataSource="Data"
+dataDestination="${venvFolder}/Borealis"
+customUIPath="${dataSource}/WebUI"
+webUIDestination="${venvFolder}/web-interface"
+
+# ---------------------- Create Python Virtual Environment ----------------------
+run_step "Create Virtual Python Environment" bash -c "
+ # Check if virtual environment already exists; if not, create one.
+ if [ ! -f '${venvFolder}/bin/activate' ]; then
+ python3 -m venv '${venvFolder}'
+ fi
+"
+
+# ---------------------- Copy Borealis Data ----------------------
+run_step "Copy Borealis Server Data into Virtual Python Environment" bash -c "
+ # If the Data folder exists, remove any existing server data folder and copy fresh data.
+ if [ -d \"$dataSource\" ]; then
+ rm -rf \"$dataDestination\"
+ mkdir -p \"$dataDestination\"
+ cp -r \"$dataSource/\"* \"$dataDestination\"
+ else
+ echo -e \"\r${INFO} Warning: Data folder not found, skipping copy.${RESET}\"
+ fi
+ true
+"
+
+# ---------------------- React UI Setup ----------------------
+run_step "Create a new ReactJS App in ${webUIDestination}" bash -c "
+ # Create a React app if the destination folder does not exist.
+ if [ ! -d \"$webUIDestination\" ]; then
+ # Set CI=true and add --loglevel=error to suppress funding and audit messages
+ CI=true npx create-react-app \"$webUIDestination\" --silent --use-npm --loglevel=error
+ fi
+"
+
+run_step "Overwrite React App with Custom Files" bash -c "
+ # If custom UI files exist, copy them into the React app folder.
+ if [ -d \"$customUIPath\" ]; then
+ cp -r \"$customUIPath/\"* \"$webUIDestination\"
+ else
+ echo -e \"\r${INFO} No custom UI found, using default React app.${RESET}\"
+ fi
+ true
+"
+
+run_step "Remove Existing React Build (if any)" bash -c "
+ # Remove the build folder if it exists to ensure a fresh build.
+ if [ -d \"$webUIDestination/build\" ]; then
+ rm -rf \"$webUIDestination/build\"
+ fi
+ true
+"
+
+# ---------------------- Activate Python Virtual Environment ----------------------
+# Activate the Python virtual environment for subsequent commands.
+source "${venvFolder}/bin/activate"
+
+# ---------------------- Install Python Dependencies ----------------------
+run_step "Install Python Dependencies into Virtual Python Environment" bash -c "
+ # Install Python packages if a requirements.txt file is present.
+ if [ -f \"requirements.txt\" ]; then
+ pip install -q -r requirements.txt
+ else
+ echo -e \"\r${INFO} No requirements.txt found, skipping Python packages.${RESET}\"
+ fi
+ true
+"
+
+# ---------------------- Install Node Dependencies & Build React UI ----------------------
+run_step "Install React App Dependencies" bash -c "
+ # Install npm dependencies if package.json exists.
+ if [ -f \"$webUIDestination/package.json\" ]; then
+ cd \"$webUIDestination\"
+ # Add --loglevel=error to suppress npm's funding and audit messages
+ npm install --silent --no-fund --audit=false --loglevel=error
+ cd -
+ fi
+"
+
+run_step "Install React Flow and UI Libraries" bash -c "
+ # Install additional React libraries.
+ cd \"$webUIDestination\"
+ npm install reactflow --silent --no-fund --audit=false --loglevel=error
+ npm install --silent @mui/material @mui/icons-material @emotion/react @emotion/styled --no-fund --audit=false --loglevel=error
+ cd -
+"
+
+run_step "Build React App" bash -c "
+ # Build the React app to create production-ready files.
+ cd \"$webUIDestination\"
+ npm run build --silent --loglevel=error
+ cd -
+"
+
+# ---------------------- Launch Flask Server ----------------------
+cd "${venvFolder}"
+echo -e "\n${GREEN}Launching Borealis...${RESET}"
+echo "===================================================================================="
+echo -ne "${HOURGLASS} Starting Flask server... "
+python3 Borealis/server.py
+echo -e "\r${CHECKMARK} Borealis Launched Successfully!"
+
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..f0b52a9
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,21 @@
+# PyTorch and related deep learning libraries (GPU Supported Functionality)
+torch --index-url https://download.pytorch.org/whl/cu121
+torchvision --index-url https://download.pytorch.org/whl/cu121
+torchaudio --index-url https://download.pytorch.org/whl/cu121
+
+# Flask for API handling
+Flask
+requests
+
+# GUI-related dependencies (Qt for GUI components)
+Qt.py
+qtpy
+OdenGraphQt
+PyQt5
+
+# Computer Vision & OCR dependencies
+numpy # Numerical operations
+opencv-python # Computer vision processing
+pytesseract # OCR engine
+easyocr # Deep-learning-based OCR
+Pillow # Image processing
\ No newline at end of file