From 27069033530d71da4300c0e680d344f047740f98 Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Fri, 22 May 2026 08:13:03 -0400 Subject: [PATCH] =?UTF-8?q?feat(ui):=20Dragonflight=20redesign=20=E2=80=94?= =?UTF-8?q?=20foundation=20JSX=20files:=20app.jsx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/web-ui/public/app.jsx | 164 +++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 services/web-ui/public/app.jsx diff --git a/services/web-ui/public/app.jsx b/services/web-ui/public/app.jsx new file mode 100644 index 0000000..ab56b7d --- /dev/null +++ b/services/web-ui/public/app.jsx @@ -0,0 +1,164 @@ +// app.jsx — main shell wiring all screens together + tweaks + +const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ + "accent": "#5B7CFA", + "density": "comfortable", + "gridSize": "md", + "showSearch": true, + "sidebarMode": "expanded" +}/*EDITMODE-END*/; + +function App() { + const [route, setRoute] = React.useState("home"); + const [openAsset, setOpenAsset] = React.useState(null); + const [openProject, setOpenProject] = React.useState(null); + const [showNewRecorder, setShowNewRecorder] = React.useState(false); + const [t, setTweak] = (window.useTweaks ? window.useTweaks(TWEAK_DEFAULTS) : [TWEAK_DEFAULTS, () => {}]); + + React.useEffect(() => { + document.documentElement.style.setProperty("--accent", t.accent); + document.documentElement.style.setProperty("--accent-soft", hexToRgba(t.accent, 0.14)); + document.documentElement.style.setProperty("--accent-soft-2", hexToRgba(t.accent, 0.22)); + document.documentElement.style.setProperty("--accent-text", lighten(t.accent, 0.25)); + document.documentElement.style.setProperty("--accent-hover", lighten(t.accent, 0.08)); + }, [t.accent]); + + const navigate = (id) => { + setOpenAsset(null); + setRoute(id); + }; + + const crumbs = React.useMemo(() => { + if (openAsset) return [ + { label: "Library", to: "library" }, + { label: openAsset.project, to: "library" }, + { label: openAsset.name }, + ]; + if (openProject) return [ + { label: "Projects", to: "projects" }, + { label: openProject.name }, + ]; + const labels = { + home: ["Home"], + library: ["Library", "Protour 2026"], + projects: ["Projects"], + upload: ["Ingest", "Upload"], + recorders: ["Ingest", "Recorders"], + capture: ["Ingest", "Capture"], + monitors: ["Ingest", "Monitors"], + jobs: ["Jobs"], + editor: ["Editor"], + users: ["Admin", "Users & Groups"], + tokens: ["Admin", "Tokens"], + containers: ["Admin", "Containers"], + cluster: ["Admin", "Cluster"], + settings: ["Admin", "Settings"], + }; + return (labels[route] || ["Home"]).map((label, i, arr) => i < arr.length - 1 ? { label } : { label }); + }, [route, openAsset, openProject]); + + let content; + if (openAsset) { + content = setOpenAsset(null)} />; + } else { + switch (route) { + case "home": content = ; break; + case "library": content = ; break; + case "projects": content = { setOpenProject(p); setRoute("library"); }} />; break; + case "upload": content = ; break; + case "recorders": content = setShowNewRecorder(true)} />; break; + case "capture": content = ; break; + case "monitors": content = ; break; + case "jobs": content = ; break; + case "editor": content = ; break; + case "users": content = ; break; + case "tokens": content = ; break; + case "containers": content = ; break; + case "cluster": content = ; break; + case "settings": content = ; break; + default: content = ; + } + } + + return ( +
+ +
+ {!openAsset && } + {content} +
+ {showNewRecorder && setShowNewRecorder(false)} />} + {window.TweaksPanel && ( + + + setTweak("accent", v)} + options={["#5B7CFA", "#7C5CFF", "#2DD4A8", "#FF5B5B", "#F5A623", "#E8E8E8"]} + /> + + + setTweak("density", v)} + options={[{ value: "comfortable", label: "Comfy" }, { value: "compact", label: "Compact" }]} + /> + setTweak("gridSize", v)} + options={[{ value: "sm", label: "S" }, { value: "md", label: "M" }, { value: "lg", label: "L" }]} + /> + + + setTweak("sidebarMode", v)} + options={[{ value: "expanded", label: "Expanded" }, { value: "collapsed", label: "Icons" }]} + /> + + +
+ {[ + ["home", "Home"], ["library", "Library"], ["recorders", "Recorders"], + ["capture", "Capture"], ["monitors", "Monitors"], ["jobs", "Jobs"], + ["editor", "Editor"], ["cluster", "Cluster"], + ].map(([k, l]) => ( + + ))} + + +
+
+
+ )} +
+ ); +} + +function hexToRgba(hex, a) { + const h = hex.replace("#", ""); + const r = parseInt(h.slice(0, 2), 16); + const g = parseInt(h.slice(2, 4), 16); + const b = parseInt(h.slice(4, 6), 16); + return `rgba(${r}, ${g}, ${b}, ${a})`; +} +function lighten(hex, amt) { + const h = hex.replace("#", ""); + const r = Math.min(255, parseInt(h.slice(0, 2), 16) + Math.round(amt * 255)); + const g = Math.min(255, parseInt(h.slice(2, 4), 16) + Math.round(amt * 255)); + const b = Math.min(255, parseInt(h.slice(4, 6), 16) + Math.round(amt * 255)); + return `rgb(${r}, ${g}, ${b})`; +} + +const root = ReactDOM.createRoot(document.getElementById("root")); +root.render();