////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/Node_Configuration_Sidebar.jsx import { Box, Typography, Tabs, Tab, TextField, MenuItem, IconButton, Dialog, DialogTitle, DialogContent, DialogActions, Button, Tooltip } from "@mui/material"; import React, { useState } from "react"; import { useReactFlow } from "reactflow"; import ReactMarkdown from "react-markdown"; // Used for Node Usage Documentation import EditIcon from "@mui/icons-material/Edit"; import PaletteIcon from "@mui/icons-material/Palette"; import { SketchPicker } from "react-color"; // ---- NEW: Brightness utility for gradient ---- function darkenColor(hex, percent = 0.7) { if (!/^#[0-9A-Fa-f]{6}$/.test(hex)) return hex; let r = parseInt(hex.slice(1, 3), 16); let g = parseInt(hex.slice(3, 5), 16); let b = parseInt(hex.slice(5, 7), 16); r = Math.round(r * percent); g = Math.round(g * percent); b = Math.round(b * percent); return `#${r.toString(16).padStart(2,"0")}${g.toString(16).padStart(2,"0")}${b.toString(16).padStart(2,"0")}`; } // -------------------------------------------- export default function NodeConfigurationSidebar({ drawerOpen, setDrawerOpen, title, nodeData, setNodes, selectedNode }) { const [activeTab, setActiveTab] = useState(0); const contextSetNodes = useReactFlow().setNodes; // Use setNodes from props if provided, else fallback to context (for backward compatibility) const effectiveSetNodes = setNodes || contextSetNodes; const handleTabChange = (_, newValue) => setActiveTab(newValue); // Rename dialog state const [renameOpen, setRenameOpen] = useState(false); const [renameValue, setRenameValue] = useState(title || ""); // ---- NEW: Accent Color Picker ---- const [colorDialogOpen, setColorDialogOpen] = useState(false); const accentColor = selectedNode?.data?.accentColor || "#58a6ff"; // ---------------------------------- const renderConfigFields = () => { const config = nodeData?.config || []; const nodeId = nodeData?.nodeId; return config.map((field, index) => { const value = nodeData?.[field.key] || ""; // ---- DYNAMIC DROPDOWN SUPPORT ---- if (field.type === "select") { let options = field.options || []; // Handle dynamic options for things like Target Window if (field.dynamicOptions && nodeData?.windowList && Array.isArray(nodeData.windowList)) { options = nodeData.windowList .map(win => ({ value: String(win.handle), label: `${win.title} (${win.handle})` })) .sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: "base" })); } else { options = options.map(opt => ({ value: opt, label: opt })); } return ( {field.label || field.key} { const newValue = e.target.value; if (!nodeId) return; effectiveSetNodes((nds) => nds.map((n) => n.id === nodeId ? { ...n, data: { ...n.data, [field.key]: newValue } } : n ) ); window.BorealisValueBus[nodeId] = newValue; }} SelectProps={{ MenuProps: { PaperProps: { sx: { bgcolor: "#1e1e1e", color: "#ccc", border: "1px solid #58a6ff", "& .MuiMenuItem-root": { color: "#ccc", fontSize: "0.85rem", "&:hover": { backgroundColor: "#2a2a2a" }, "&.Mui-selected": { backgroundColor: "#2c2c2c !important", color: "#58a6ff" }, "&.Mui-selected:hover": { backgroundColor: "#2a2a2a !important" } } } } } }} sx={{ "& .MuiOutlinedInput-root": { backgroundColor: "#1e1e1e", color: "#ccc", fontSize: "0.85rem", "& fieldset": { borderColor: "#444" }, "&:hover fieldset": { borderColor: "#58a6ff" }, "&.Mui-focused fieldset": { borderColor: "#58a6ff" } }, "& .MuiSelect-select": { backgroundColor: "#1e1e1e" } }} > {options.length === 0 ? ( {field.label === "Target Window" ? "No windows detected" : "No options"} ) : ( options.map((opt, idx) => ( {opt.label} )) )} ); } // ---- END DYNAMIC DROPDOWN SUPPORT ---- return ( {field.label || field.key} { const newValue = e.target.value; if (!nodeId) return; effectiveSetNodes((nds) => nds.map((n) => n.id === nodeId ? { ...n, data: { ...n.data, [field.key]: newValue } } : n ) ); window.BorealisValueBus[nodeId] = newValue; }} InputProps={{ sx: { backgroundColor: "#1e1e1e", color: "#ccc", "& fieldset": { borderColor: "#444" }, "&:hover fieldset": { borderColor: "#666" }, "&.Mui-focused fieldset": { borderColor: "#58a6ff" } } }} /> ); }); }; // ---- NEW: Accent Color Button ---- const renderAccentColorButton = () => ( setColorDialogOpen(true)} sx={{ ml: 1, border: "1px solid #58a6ff", background: accentColor, color: "#222", width: 28, height: 28, p: 0 }} > ); // ---------------------------------- return ( <> setDrawerOpen(false)} sx={{ position: "absolute", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: "rgba(0, 0, 0, 0.3)", opacity: drawerOpen ? 1 : 0, pointerEvents: drawerOpen ? "auto" : "none", transition: "opacity 0.6s ease", zIndex: 10 }} /> e.stopPropagation()} > {"Edit " + (title || "Node")} { setRenameValue(title || ""); setRenameOpen(true); }} sx={{ ml: 1, color: "#58a6ff" }} > {/* ---- NEW: Accent Color Picker button next to pencil ---- */} {renderAccentColorButton()} {/* ------------------------------------------------------ */} {activeTab === 0 && renderConfigFields()} {activeTab === 1 && ( ( ), p: ({ node, ...props }) => ( ), ul: ({ node, ...props }) => (
    ), li: ({ node, ...props }) => (
  • ) }} /> )} {/* Rename Node Dialog */} setRenameOpen(false)} PaperProps={{ sx: { bgcolor: "#232323" } }} > Rename Node setRenameValue(e.target.value)} sx={{ mt: 1, bgcolor: "#1e1e1e", "& .MuiOutlinedInput-root": { color: "#ccc", backgroundColor: "#1e1e1e", "& fieldset": { borderColor: "#444" } }, label: { color: "#aaa" } }} /> {/* ---- NEW: Accent Color Picker Dialog ---- */} setColorDialogOpen(false)} PaperProps={{ sx: { bgcolor: "#232323" } }} > Pick Node Header/Accent Color { const nodeId = selectedNode?.id || nodeData?.nodeId; if (!nodeId) return; const accent = color.hex; const accentDark = darkenColor(accent, 0.7); effectiveSetNodes((nds) => nds.map((n) => n.id === nodeId ? { ...n, data: { ...n.data, accentColor: accent }, style: { ...n.style, "--borealis-accent": accent, "--borealis-accent-dark": accentDark, "--borealis-title": accent, }, } : n ) ); }} disableAlpha presetColors={[ "#58a6ff", "#0475c2", "#00d18c", "#ff4f4f", "#ff8c00", "#6b21a8", "#0e7490", "#888", "#fff", "#000" ]} /> The node's header text and accent gradient will use your selected color.
    The accent gradient fades to a slightly darker version.
    {accentColor}
    {/* ---- END ACCENT COLOR PICKER DIALOG ---- */} ); }