From e1dae4dd96fd8901ea699cb0612de94ec4b32a32 Mon Sep 17 00:00:00 2001 From: Nicole Rappe <nicole.rappe@bunny-lab.io> Date: Sat, 31 May 2025 01:53:05 -0600 Subject: [PATCH] Added Universal Node Accent Theming --- Data/Server/WebUI/src/Borealis.css | 10 +- .../WebUI/src/Node_Configuration_Sidebar.jsx | 154 ++++++++++++++++-- 2 files changed, 142 insertions(+), 22 deletions(-) diff --git a/Data/Server/WebUI/src/Borealis.css b/Data/Server/WebUI/src/Borealis.css index 24c28f5..aadeb8d 100644 --- a/Data/Server/WebUI/src/Borealis.css +++ b/Data/Server/WebUI/src/Borealis.css @@ -88,13 +88,13 @@ position: absolute; left: 0; top: 0; - width: 3px; /* Accent width */ + width: 3px; height: 100%; background: linear-gradient( to bottom, - #58a6ff 0%, - #0475c2 100% - ); /* Or any accent color(s) you want */ + var(--borealis-accent, #58a6ff) 0%, + var(--borealis-accent-dark, #0475c2) 100% + ); border-top-left-radius: 4px; border-bottom-left-radius: 4px; } @@ -104,7 +104,7 @@ border-top-left-radius: 4px; border-top-right-radius: 4px; font-weight: bold; - color: #58a6ff; + color: var(--borealis-title, #58a6ff); font-size: 10px; } .borealis-node-content { diff --git a/Data/Server/WebUI/src/Node_Configuration_Sidebar.jsx b/Data/Server/WebUI/src/Node_Configuration_Sidebar.jsx index 4a3936a..81d4e09 100644 --- a/Data/Server/WebUI/src/Node_Configuration_Sidebar.jsx +++ b/Data/Server/WebUI/src/Node_Configuration_Sidebar.jsx @@ -1,9 +1,24 @@ ////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/Data/WebUI/src/Node_Configuration_Sidebar.jsx -import { Box, Typography, Tabs, Tab, TextField, MenuItem, IconButton, Dialog, DialogTitle, DialogContent, DialogActions, Button } from "@mui/material"; +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); @@ -16,6 +31,11 @@ export default function NodeConfigurationSidebar({ drawerOpen, setDrawerOpen, ti 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; @@ -39,11 +59,21 @@ export default function NodeConfigurationSidebar({ drawerOpen, setDrawerOpen, ti const newValue = e.target.value; if (!nodeId) return; effectiveSetNodes((nds) => - nds.map((n) => - n.id === nodeId - ? { ...n, data: { ...n.data, [field.key]: newValue } } - : n - ) + nds.map((n) => { + if (n.id !== nodeId) return n; + const accentColor = color.hex; + const accentColorDark = darkenColor(accentColor, 0.7); + return { + ...n, + data: { ...n.data, accentColor }, + style: { + ...n.style, + "--borealis-accent": accentColor, + "--borealis-accent-dark": accentColorDark, + "--borealis-title": accentColor, + }, + }; + }) ); window.BorealisValueBus[nodeId] = newValue; }} @@ -133,6 +163,27 @@ export default function NodeConfigurationSidebar({ drawerOpen, setDrawerOpen, ti }); }; + // ---- NEW: Accent Color Button ---- + const renderAccentColorButton = () => ( + <Tooltip title="Override Node Header/Accent Color"> + <IconButton + size="small" + aria-label="Override Node Color" + onClick={() => setColorDialogOpen(true)} + sx={{ + ml: 1, + border: "1px solid #58a6ff", + background: accentColor, + color: "#222", + width: 28, height: 28, p: 0 + }} + > + <PaletteIcon fontSize="small" /> + </IconButton> + </Tooltip> + ); + // ---------------------------------- + return ( <> <Box @@ -175,17 +226,22 @@ export default function NodeConfigurationSidebar({ drawerOpen, setDrawerOpen, ti <Typography variant="h7" sx={{ color: "#0475c2", fontWeight: "bold" }}> {"Edit " + (title || "Node")} </Typography> - <IconButton - size="small" - aria-label="Rename Node" - onClick={() => { - setRenameValue(title || ""); - setRenameOpen(true); - }} - sx={{ ml: 1, color: "#58a6ff" }} - > - <EditIcon fontSize="small" /> - </IconButton> + <Box sx={{ display: "flex", alignItems: "center" }}> + <IconButton + size="small" + aria-label="Rename Node" + onClick={() => { + setRenameValue(title || ""); + setRenameOpen(true); + }} + sx={{ ml: 1, color: "#58a6ff" }} + > + <EditIcon fontSize="small" /> + </IconButton> + {/* ---- NEW: Accent Color Picker button next to pencil ---- */} + {renderAccentColorButton()} + {/* ------------------------------------------------------ */} + </Box> </Box> </Box> <Tabs @@ -305,6 +361,70 @@ export default function NodeConfigurationSidebar({ drawerOpen, setDrawerOpen, ti </Button> </DialogActions> </Dialog> + + {/* ---- NEW: Accent Color Picker Dialog ---- */} + <Dialog + open={colorDialogOpen} + onClose={() => setColorDialogOpen(false)} + PaperProps={{ sx: { bgcolor: "#232323" } }} + > + <DialogTitle>Pick Node Header/Accent Color</DialogTitle> + <DialogContent> + <SketchPicker + color={accentColor} + onChangeComplete={(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" + ]} + /> + <Box sx={{ mt: 2 }}> + <Typography variant="body2"> + The node's header text and accent gradient will use your selected color.<br /> + The accent gradient fades to a slightly darker version. + </Typography> + <Box sx={{ mt: 2, display: "flex", alignItems: "center" }}> + <span style={{ + display: "inline-block", + width: 48, + height: 22, + borderRadius: 4, + border: "1px solid #888", + background: `linear-gradient(to bottom, ${accentColor} 0%, ${darkenColor(accentColor, 0.7)} 100%)` + }} /> + <span style={{ marginLeft: 10, color: accentColor, fontWeight: "bold" }}> + {accentColor} + </span> + </Box> + </Box> + </DialogContent> + <DialogActions> + <Button onClick={() => setColorDialogOpen(false)} sx={{ color: "#aaa" }}>Close</Button> + </DialogActions> + </Dialog> + {/* ---- END ACCENT COLOR PICKER DIALOG ---- */} </> ); }