Added Text Upload & Display Nodes
This commit is contained in:
parent
5098e5f040
commit
d3b3258dbf
@ -1,5 +1,4 @@
|
||||
////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/Data/WebUI/src/nodes/Data Analysis/Node_Regex_Search.jsx
|
||||
////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/Data/WebUI/src/nodes/Data Manipulation/Node_Regex_Search.jsx
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { Handle, Position, useReactFlow, useStore } from "reactflow";
|
||||
|
||||
|
@ -0,0 +1,190 @@
|
||||
////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/nodes/Data Analysis/Node_TextArray_Display.jsx
|
||||
|
||||
/**
|
||||
* Display Multi-Line Array Node
|
||||
* --------------------------------------------------
|
||||
* A node to display upstream multi-line text arrays.
|
||||
* Has one input edge on left and passthrough output on right.
|
||||
* Custom drag-resize handle for width & height adjustments.
|
||||
* Inner textarea scrolls vertically; container overflow visible.
|
||||
*/
|
||||
import React, { useEffect, useState, useRef, useCallback } from "react";
|
||||
import { Handle, Position, useReactFlow, useStore } from "reactflow";
|
||||
|
||||
const TextArrayDisplayNode = ({ id, data }) => {
|
||||
const { setNodes } = useReactFlow();
|
||||
const edges = useStore((state) => state.edges);
|
||||
const containerRef = useRef(null);
|
||||
const resizingRef = useRef(false);
|
||||
const startPosRef = useRef({ x: 0, y: 0 });
|
||||
const startDimRef = useRef({ width: 0, height: 0 });
|
||||
|
||||
// Initialize lines and dimensions
|
||||
const [lines, setLines] = useState(data?.lines || []);
|
||||
const linesRef = useRef(lines);
|
||||
const initW = parseInt(data?.width || "300", 10);
|
||||
const initH = parseInt(data?.height || "150", 10);
|
||||
const [dimensions, setDimensions] = useState({ width: initW, height: initH });
|
||||
|
||||
// Persist dimensions to node data
|
||||
const persistDimensions = useCallback(() => {
|
||||
const w = `${Math.round(dimensions.width)}px`;
|
||||
const h = `${Math.round(dimensions.height)}px`;
|
||||
setNodes((nds) =>
|
||||
nds.map((n) =>
|
||||
n.id === id
|
||||
? { ...n, data: { ...n.data, width: w, height: h } }
|
||||
: n
|
||||
)
|
||||
);
|
||||
}, [dimensions, id, setNodes]);
|
||||
|
||||
// Mouse handlers for custom resize
|
||||
useEffect(() => {
|
||||
const onMouseMove = (e) => {
|
||||
if (!resizingRef.current) return;
|
||||
const dx = e.clientX - startPosRef.current.x;
|
||||
const dy = e.clientY - startPosRef.current.y;
|
||||
setDimensions({
|
||||
width: Math.max(100, startDimRef.current.width + dx),
|
||||
height: Math.max(60, startDimRef.current.height + dy)
|
||||
});
|
||||
};
|
||||
const onMouseUp = () => {
|
||||
if (resizingRef.current) {
|
||||
resizingRef.current = false;
|
||||
persistDimensions();
|
||||
}
|
||||
};
|
||||
window.addEventListener("mousemove", onMouseMove);
|
||||
window.addEventListener("mouseup", onMouseUp);
|
||||
return () => {
|
||||
window.removeEventListener("mousemove", onMouseMove);
|
||||
window.removeEventListener("mouseup", onMouseUp);
|
||||
};
|
||||
}, [persistDimensions]);
|
||||
|
||||
// Start drag
|
||||
const onResizeMouseDown = (e) => {
|
||||
e.stopPropagation();
|
||||
resizingRef.current = true;
|
||||
startPosRef.current = { x: e.clientX, y: e.clientY };
|
||||
startDimRef.current = { ...dimensions };
|
||||
};
|
||||
|
||||
// Polling for upstream data
|
||||
useEffect(() => {
|
||||
let rate = window.BorealisUpdateRate;
|
||||
const tick = () => {
|
||||
const edge = edges.find((e) => e.target === id);
|
||||
if (edge && edge.source) {
|
||||
const arr = window.BorealisValueBus[edge.source] || [];
|
||||
if (JSON.stringify(arr) !== JSON.stringify(linesRef.current)) {
|
||||
linesRef.current = arr;
|
||||
setLines(arr);
|
||||
window.BorealisValueBus[id] = arr;
|
||||
setNodes((nds) =>
|
||||
nds.map((n) =>
|
||||
n.id === id ? { ...n, data: { ...n.data, lines: arr } } : n
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
window.BorealisValueBus[id] = linesRef.current;
|
||||
}
|
||||
};
|
||||
const iv = setInterval(tick, rate);
|
||||
const monitor = setInterval(() => {
|
||||
if (window.BorealisUpdateRate !== rate) {
|
||||
clearInterval(iv);
|
||||
clearInterval(monitor);
|
||||
}
|
||||
}, 200);
|
||||
return () => { clearInterval(iv); clearInterval(monitor); };
|
||||
}, [id, edges, setNodes]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="borealis-node"
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: dimensions.width,
|
||||
height: dimensions.height,
|
||||
overflow: "visible",
|
||||
position: "relative",
|
||||
boxSizing: "border-box"
|
||||
}}
|
||||
>
|
||||
{/* Connectors */}
|
||||
<Handle type="target" position={Position.Left} className="borealis-handle" />
|
||||
<Handle type="source" position={Position.Right} className="borealis-handle" />
|
||||
|
||||
{/* Header */}
|
||||
<div className="borealis-node-header">
|
||||
{data?.label || "Display Multi-Line Array"}
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div
|
||||
className="borealis-node-content"
|
||||
style={{
|
||||
flex: 1,
|
||||
padding: "4px",
|
||||
fontSize: "9px",
|
||||
color: "#ccc",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
overflow: "hidden"
|
||||
}}
|
||||
>
|
||||
<div style={{ marginBottom: "4px" }}>
|
||||
{data?.content || "Display upstream multi-line text arrays."}
|
||||
</div>
|
||||
<label style={{ marginBottom: "4px" }}>Upstream Text Data:</label>
|
||||
<textarea
|
||||
value={lines.join("\n")}
|
||||
readOnly
|
||||
style={{
|
||||
flex: 1,
|
||||
width: "100%",
|
||||
fontSize: "9px",
|
||||
background: "#1e1e1e",
|
||||
color: "#ccc",
|
||||
border: "1px solid #444",
|
||||
borderRadius: "2px",
|
||||
padding: "4px",
|
||||
resize: "none",
|
||||
overflowY: "auto",
|
||||
boxSizing: "border-box"
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Invisible drag-resize handle */}
|
||||
<div
|
||||
onMouseDown={onResizeMouseDown}
|
||||
style={{
|
||||
position: "absolute",
|
||||
width: "20px",
|
||||
height: "20px",
|
||||
right: "-4px",
|
||||
bottom: "-4px",
|
||||
cursor: "nwse-resize",
|
||||
background: "transparent",
|
||||
zIndex: 10
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Export node metadata
|
||||
export default {
|
||||
type: "Node_TextArray_Display",
|
||||
label: "Display Multi-Line Array",
|
||||
description: "Display upstream multi-line text arrays.",
|
||||
content: "Display upstream multi-line text arrays.",
|
||||
component: TextArrayDisplayNode
|
||||
};
|
123
Data/Server/WebUI/src/nodes/Data Processing/Node_Upload_Text.jsx
Normal file
123
Data/Server/WebUI/src/nodes/Data Processing/Node_Upload_Text.jsx
Normal file
@ -0,0 +1,123 @@
|
||||
////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/Data/WebUI/src/nodes/Data/Node_Upload_Text.jsx
|
||||
|
||||
/**
|
||||
* Upload Text File Node
|
||||
* --------------------------------------------------
|
||||
* A node to upload a text file (TXT/LOG/INI/ETC) and store it as a multi-line text array.
|
||||
* No input edges. Outputs an array of text lines via the shared value bus.
|
||||
*/
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { Handle, Position, useReactFlow, useStore } from "reactflow";
|
||||
|
||||
const UploadTextFileNode = ({ id, data }) => {
|
||||
const { setNodes } = useReactFlow();
|
||||
const edges = useStore((state) => state.edges);
|
||||
|
||||
// Initialize lines from persisted data or empty
|
||||
const initialLines = data?.lines || [];
|
||||
const [lines, setLines] = useState(initialLines);
|
||||
const linesRef = useRef(initialLines);
|
||||
|
||||
const fileInputRef = useRef(null);
|
||||
|
||||
// Handle file selection and reading
|
||||
const handleFileChange = (e) => {
|
||||
const file = e.target.files && e.target.files[0];
|
||||
if (!file) return;
|
||||
file.text().then((text) => {
|
||||
const arr = text.split(/\r\n|\n/);
|
||||
linesRef.current = arr;
|
||||
setLines(arr);
|
||||
// Broadcast to shared bus
|
||||
window.BorealisValueBus[id] = arr;
|
||||
// Persist data for workflow serialization
|
||||
setNodes((nds) =>
|
||||
nds.map((n) =>
|
||||
n.id === id
|
||||
? { ...n, data: { ...n.data, lines: arr } }
|
||||
: n
|
||||
)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// Trigger file input click
|
||||
const handleUploadClick = () => {
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
// Periodically broadcast current lines to bus
|
||||
useEffect(() => {
|
||||
let currentRate = window.BorealisUpdateRate;
|
||||
const intervalId = setInterval(() => {
|
||||
window.BorealisValueBus[id] = linesRef.current;
|
||||
}, currentRate);
|
||||
|
||||
// Monitor for rate changes
|
||||
const monitorId = setInterval(() => {
|
||||
const newRate = window.BorealisUpdateRate;
|
||||
if (newRate !== currentRate) {
|
||||
clearInterval(intervalId);
|
||||
clearInterval(monitorId);
|
||||
}
|
||||
}, 250);
|
||||
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
clearInterval(monitorId);
|
||||
};
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<div className="borealis-node">
|
||||
{/* No input handle for this node */}
|
||||
<div className="borealis-node-header">
|
||||
{data?.label || "Upload Text File"}
|
||||
</div>
|
||||
<div className="borealis-node-content">
|
||||
<div style={{ marginBottom: "8px", fontSize: "9px", color: "#ccc" }}>
|
||||
{data?.content ||
|
||||
"Upload a text-based file, output a multi-line string array."}
|
||||
</div>
|
||||
<button
|
||||
onClick={handleUploadClick}
|
||||
style={{
|
||||
width: "100%",
|
||||
padding: "6px",
|
||||
fontSize: "9px",
|
||||
background: "#1e1e1e",
|
||||
color: "#ccc",
|
||||
border: "1px solid #444",
|
||||
borderRadius: "2px",
|
||||
cursor: "pointer"
|
||||
}}
|
||||
>
|
||||
Select File...
|
||||
</button>
|
||||
<input
|
||||
type="file"
|
||||
accept=".txt,.log,.ini,text/*"
|
||||
style={{ display: "none" }}
|
||||
ref={fileInputRef}
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Output connector on right */}
|
||||
<Handle
|
||||
type="source"
|
||||
position={Position.Right}
|
||||
className="borealis-handle"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Export node metadata for Borealis
|
||||
export default {
|
||||
type: "Upload_Text_File",
|
||||
label: "Upload Text File",
|
||||
description: "A node to upload a text file (TXT/LOG/INI/ETC) and store it as a multi-line text array.",
|
||||
content: "Upload a text-based file, output a multi-line string array.",
|
||||
component: UploadTextFileNode
|
||||
};
|
@ -26,16 +26,6 @@
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { Handle, Position, useReactFlow, useStore } from "reactflow";
|
||||
|
||||
// Ensure the global shared bus exists for inter-node communication
|
||||
if (!window.BorealisValueBus) {
|
||||
window.BorealisValueBus = {};
|
||||
}
|
||||
|
||||
// Ensure a default global update rate (ms) for polling loops
|
||||
if (!window.BorealisUpdateRate) {
|
||||
window.BorealisUpdateRate = 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* TemplateNode Component
|
||||
* ----------------------
|
||||
|
Loading…
x
Reference in New Issue
Block a user