From de4e4ed95360a8731f9b3f1ac9cfc4f04901a265 Mon Sep 17 00:00:00 2001 From: Nicole Rappe Date: Mon, 14 Apr 2025 19:08:45 -0600 Subject: [PATCH] Fixed BW Threshold Node Import Behavior --- Data/WebUI/src/Borealis.css | 44 +++ .../Image Processing/Node_BW_Threshold.jsx | 39 ++- .../Macro Automation/Node_Macro_KeyPress.jsx | 295 ++++++++++++++++++ Launch-Borealis.ps1 | 4 +- 4 files changed, 375 insertions(+), 7 deletions(-) create mode 100644 Data/WebUI/src/nodes/Macro Automation/Node_Macro_KeyPress.jsx diff --git a/Data/WebUI/src/Borealis.css b/Data/WebUI/src/Borealis.css index 45b4a8a..abe4fde 100644 --- a/Data/WebUI/src/Borealis.css +++ b/Data/WebUI/src/Borealis.css @@ -93,3 +93,47 @@ label { } /* We rely on the TabIndicatorProps to show the underline highlight for active tabs. */ + +/* keyboard-dark-theme.css */ + +/* react-simple-keyboard dark theming */ +.hg-theme-dark { + background-color: #1e1e1e; + border: 1px solid #444; +} + +.hg-button { + background: #2c2c2c; + color: #ccc; + border: 1px solid #444; + font-size: 11px; +} + +.hg-button:hover { + background: #58a6ff; + color: #000; + border-color: #58a6ff; +} + +.borealis-keyboard .hg-button.hg-standardBtn { + border-radius: 3px; +} + +/* Force rows to appear horizontally */ +.simple-keyboard-main .hg-row { + display: flex !important; + flex-flow: row wrap; + justify-content: center; + } + +/* Slight spacing around each key (optional) */ +.simple-keyboard-main .hg-row .hg-button { + margin: 3px !important; + } + + /* Keep the entire keyboard from shrinking or going vertical */ + .simple-keyboard-main { + display: inline-block !important; + width: auto !important; + max-width: 1000px; /* or whatever max width you like */ + } \ No newline at end of file diff --git a/Data/WebUI/src/nodes/Image Processing/Node_BW_Threshold.jsx b/Data/WebUI/src/nodes/Image Processing/Node_BW_Threshold.jsx index a09d0f9..b2f983d 100644 --- a/Data/WebUI/src/nodes/Image Processing/Node_BW_Threshold.jsx +++ b/Data/WebUI/src/nodes/Image Processing/Node_BW_Threshold.jsx @@ -12,22 +12,45 @@ if (!window.BorealisUpdateRate) { const BWThresholdNode = ({ id, data }) => { const edges = useStore((state) => state.edges); - const [threshold, setThreshold] = useState(() => parseInt(data?.value, 10) || 128); + + // Attempt to parse threshold from data.value (if present), + // otherwise default to 128. + const initial = parseInt(data?.value, 10); + const [threshold, setThreshold] = useState( + isNaN(initial) ? 128 : initial + ); + const [renderValue, setRenderValue] = useState(""); const valueRef = useRef(""); const lastUpstreamRef = useRef(""); + // If the node is reimported and data.value changes externally, + // update the threshold accordingly. + useEffect(() => { + const newVal = parseInt(data?.value, 10); + if (!isNaN(newVal)) { + setThreshold(newVal); + } + }, [data?.value]); + const handleThresholdInput = (e) => { let val = parseInt(e.target.value, 10); - if (isNaN(val)) val = 128; + if (isNaN(val)) { + val = 128; + } val = Math.max(0, Math.min(255, val)); + // Keep the Node's data.value updated + data.value = val; + setThreshold(val); window.BorealisValueBus[id] = val; }; const applyThreshold = async (base64Data, cutoff) => { - if (!base64Data || typeof base64Data !== "string") return ""; + if (!base64Data || typeof base64Data !== "string") { + return ""; + } return new Promise((resolve) => { const img = new Image(); @@ -56,7 +79,7 @@ const BWThresholdNode = ({ id, data }) => { }; img.onerror = () => resolve(base64Data); - img.src = `data:image/png;base64,${base64Data}`; + img.src = "data:image/png;base64," + base64Data; }); }; @@ -105,10 +128,14 @@ const BWThresholdNode = ({ id, data }) => { // Reapply when threshold changes (even if image didn't) useEffect(() => { const inputEdge = edges.find(e => e.target === id); - if (!inputEdge?.source) return; + if (!inputEdge?.source) { + return; + } const upstreamValue = window.BorealisValueBus[inputEdge.source] ?? ""; - if (!upstreamValue) return; + if (!upstreamValue) { + return; + } applyThreshold(upstreamValue, threshold).then((result) => { valueRef.current = result; diff --git a/Data/WebUI/src/nodes/Macro Automation/Node_Macro_KeyPress.jsx b/Data/WebUI/src/nodes/Macro Automation/Node_Macro_KeyPress.jsx new file mode 100644 index 0000000..00a958a --- /dev/null +++ b/Data/WebUI/src/nodes/Macro Automation/Node_Macro_KeyPress.jsx @@ -0,0 +1,295 @@ +import React, { useState, useRef } from "react"; +import { Handle, Position } from "reactflow"; +import Keyboard from "react-simple-keyboard"; +import "react-simple-keyboard/build/css/index.css"; +import "../../Borealis.css"; + +if (!window.BorealisValueBus) window.BorealisValueBus = {}; +if (!window.BorealisUpdateRate) window.BorealisUpdateRate = 100; + +/** + * KeyPressNode: + * - Full keyboard with SHIFT toggling + * - Press F-keys, digits, letters, or symbols + * - Single key stored, overlay closes + * - SHIFT or CAPS toggles "default" <-> "shift" + */ + +const KeyPressNode = ({ id, data }) => { + const [selectedWindow, setSelectedWindow] = useState(data?.selectedWindow || ""); + const [keyPressed, setKeyPressed] = useState(data?.keyPressed || ""); + const [intervalMs, setIntervalMs] = useState(data?.intervalMs || 1000); + const [randomRangeEnabled, setRandomRangeEnabled] = useState(false); + const [randomMin, setRandomMin] = useState(750); + const [randomMax, setRandomMax] = useState(950); + + // Keyboard overlay + const [showKeyboard, setShowKeyboard] = useState(false); + const [layoutName, setLayoutName] = useState("default"); + + // A simple set of Windows for demonstration + const fakeWindows = ["Notepad", "Chrome", "Discord", "Visual Studio Code"]; + + // This function is triggered whenever the user taps a key on the virtual keyboard + const onKeyPress = (button) => { + // SHIFT or CAPS toggling: + if (button === "{shift}" || button === "{lock}") { + handleShift(); + return; + } + + // Example skip list: these won't be stored as final single key + const skipKeys = [ + "{bksp}", "{space}", "{tab}", "{enter}", "{escape}", + "{f1}", "{f2}", "{f3}", "{f4}", "{f5}", "{f6}", + "{f7}", "{f8}", "{f9}", "{f10}", "{f11}", "{f12}", + "{shift}", "{lock}" + ]; + + // If the pressed button is not in skipKeys, let's store it and close + if (!skipKeys.includes(button)) { + setKeyPressed(button); + setShowKeyboard(false); + } + }; + + // Toggle between "default" layout and "shift" layout + const handleShift = () => { + setLayoutName((prev) => (prev === "default" ? "shift" : "default")); + }; + + return ( +
+ {/* React Flow Handles */} + + + + {/* Node Header */} +
+ Key Press +
+
+ + {/* Node Content */} +
+
+ Sends keypress to selected window on trigger. +
+ + {/* Window Selector */} + + + + {/* Key: "Select Key" button & readOnly input */} + +
+ + +
+ + {/* Interval Configuration */} + + setIntervalMs(Number(e.target.value))} + disabled={randomRangeEnabled} + style={{ + ...inputStyle, + backgroundColor: randomRangeEnabled ? "#2a2a2a" : "#1e1e1e" + }} + /> + + {/* Random Interval */} + + + {randomRangeEnabled && ( +
+ setRandomMin(Number(e.target.value))} + style={{ ...inputStyle, flex: 1 }} + /> + setRandomMax(Number(e.target.value))} + style={{ ...inputStyle, flex: 1 }} + /> +
+ )} +
+ + {/* Keyboard Overlay */} + {showKeyboard && ( +
+
+
+ Full Keyboard +
+ ? {shift}", + "{space}" + ] + }} + display={{ + "{bksp}": "⌫", + "{escape}": "esc", + "{tab}": "tab", + "{lock}": "caps", + "{enter}": "enter", + "{shift}": "shift", + "{space}": "space", + "{f1}": "F1", + "{f2}": "F2", + "{f3}": "F3", + "{f4}": "F4", + "{f5}": "F5", + "{f6}": "F6", + "{f7}": "F7", + "{f8}": "F8", + "{f9}": "F9", + "{f10}": "F10", + "{f11}": "F11", + "{f12}": "F12" + }} + /> +
+ +
+
+
+ )} +
+ ); +}; + +/* Basic styling objects */ +const inputStyle = { + width: "100%", + fontSize: "9px", + padding: "4px", + color: "#ccc", + border: "1px solid #444", + borderRadius: "2px", + marginBottom: "6px" +}; + +const buttonStyle = { + fontSize: "9px", + padding: "4px 8px", + backgroundColor: "#1e1e1e", + color: "#ccc", + border: "1px solid #444", + borderRadius: "2px", + cursor: "pointer" +}; + +const keyboardOverlay = { + position: "fixed", + top: 0, + left: 0, + width: "100vw", + height: "100vh", + zIndex: 1000, + backgroundColor: "rgba(0, 0, 0, 0.8)", + display: "flex", + justifyContent: "center", + alignItems: "center" +}; + +const keyboardContainer = { + backgroundColor: "#1e1e1e", + padding: "16px", + borderRadius: "6px", + border: "1px solid #444", + zIndex: 1001, + maxWidth: "650px" +}; + +export default { + type: "Macro_KeyPress", + label: "Key Press (GUI-ONLY)", + description: ` +Press a single character or function key on a full keyboard overlay. +Shift/caps toggles uppercase/symbols. +F-keys are included, but pressing them won't store that value unless you remove them from "skip" logic, if desired. +`, + content: "Send Key Press to Foreground Window via Borealis Agent", + component: KeyPressNode +}; diff --git a/Launch-Borealis.ps1 b/Launch-Borealis.ps1 index f950e51..32134bf 100644 --- a/Launch-Borealis.ps1 +++ b/Launch-Borealis.ps1 @@ -137,7 +137,9 @@ switch ($choice) { npm install --silent react-resizable --no-fund --audit=false | Out-Null npm install --silent reactflow --no-fund --audit=false | Out-Null npm install --silent @mui/material @mui/icons-material @emotion/react @emotion/styled --no-fund --audit=false 2>&1 | Out-Null - npm install --silent socket.io-client --no-fund --audit=false | Out-Null + npm install --silent socket.io-client --no-fund --audit=false | Out-Null + npm install --silent react-simple-keyboard --no-fund --audit=false | Out-Null + Pop-Location } }