mirror of
https://github.com/ruvnet/RuView
synced 2026-08-08 20:11:43 +00:00
f49c722764
The Rust port lived two directories deep (rust-port/wifi-densepose-rs/) without any sibling under rust-port/ that warranted the extra level. Move the whole workspace up to v2/ to match v1/ (Python) at the same depth and shorten every cd / build command across the repo. git mv preserves history for all tracked files. 60 files updated for path references (CI workflows, ADRs, docs, scripts, READMEs, internal .claude-flow state). Two manual fixes for relative-cd paths in CLAUDE.md and ADR-043 that became wrong after the depth change (cd ../.. → cd ..). Validated: - cargo check --workspace --no-default-features → clean (after target/ nuke; the gitignored target/ was carried by the OS rename and had hard-coded old paths in build scripts) - cargo test --workspace --no-default-features → 1,539 passed, 0 failed, 8 ignored (same totals as pre-rename) - ESP32-S3 on COM7 → still streaming live CSI (cb #40300, RSSI -64 dBm) After-merge follow-up: contributors should `rm -rf v2/target` once and let cargo regenerate from the new path.
153 lines
5.0 KiB
TypeScript
153 lines
5.0 KiB
TypeScript
import { type ReactNode } from "react";
|
|
|
|
export interface NavItem {
|
|
id: string;
|
|
label: string;
|
|
icon: ReactNode;
|
|
}
|
|
|
|
interface SidebarProps {
|
|
items: NavItem[];
|
|
activeId: string;
|
|
onNavigate: (id: string) => void;
|
|
}
|
|
|
|
// Minimal SVG icons to avoid external dependency
|
|
const ICONS: Record<string, ReactNode> = {
|
|
dashboard: (
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<rect x="3" y="3" width="7" height="9" rx="1" />
|
|
<rect x="14" y="3" width="7" height="5" rx="1" />
|
|
<rect x="14" y="12" width="7" height="9" rx="1" />
|
|
<rect x="3" y="16" width="7" height="5" rx="1" />
|
|
</svg>
|
|
),
|
|
nodes: (
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<circle cx="12" cy="5" r="3" />
|
|
<circle cx="5" cy="19" r="3" />
|
|
<circle cx="19" cy="19" r="3" />
|
|
<line x1="12" y1="8" x2="5" y2="16" />
|
|
<line x1="12" y1="8" x2="19" y2="16" />
|
|
</svg>
|
|
),
|
|
flash: (
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />
|
|
</svg>
|
|
),
|
|
server: (
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<rect x="2" y="2" width="20" height="8" rx="2" />
|
|
<rect x="2" y="14" width="20" height="8" rx="2" />
|
|
<line x1="6" y1="6" x2="6.01" y2="6" />
|
|
<line x1="6" y1="18" x2="6.01" y2="18" />
|
|
</svg>
|
|
),
|
|
settings: (
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
<circle cx="12" cy="12" r="3" />
|
|
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42" />
|
|
</svg>
|
|
),
|
|
};
|
|
|
|
export const DEFAULT_NAV_ITEMS: NavItem[] = [
|
|
{ id: "dashboard", label: "Dashboard", icon: ICONS.dashboard },
|
|
{ id: "nodes", label: "Nodes", icon: ICONS.nodes },
|
|
{ id: "flash", label: "Flash", icon: ICONS.flash },
|
|
{ id: "server", label: "Server", icon: ICONS.server },
|
|
{ id: "settings", label: "Settings", icon: ICONS.settings },
|
|
];
|
|
|
|
export function Sidebar({ items, activeId, onNavigate }: SidebarProps) {
|
|
return (
|
|
<nav
|
|
style={{
|
|
width: "200px",
|
|
minWidth: "200px",
|
|
height: "100%",
|
|
background: "var(--sidebar-bg, #12121a)",
|
|
borderRight: "1px solid var(--border, #2e2e3e)",
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
padding: "16px 0",
|
|
}}
|
|
>
|
|
{/* App title */}
|
|
<div
|
|
style={{
|
|
padding: "0 20px 20px",
|
|
fontSize: "18px",
|
|
fontWeight: 800,
|
|
color: "var(--text-primary, #e2e8f0)",
|
|
letterSpacing: "-0.02em",
|
|
}}
|
|
>
|
|
RuView
|
|
</div>
|
|
|
|
{/* Nav items */}
|
|
<div style={{ display: "flex", flexDirection: "column", gap: "2px", flex: 1 }}>
|
|
{items.map((item) => {
|
|
const isActive = item.id === activeId;
|
|
return (
|
|
<button
|
|
key={item.id}
|
|
onClick={() => onNavigate(item.id)}
|
|
style={{
|
|
display: "flex",
|
|
alignItems: "center",
|
|
gap: "10px",
|
|
padding: "10px 20px",
|
|
border: "none",
|
|
background: isActive
|
|
? "var(--accent-muted, rgba(99, 102, 241, 0.12))"
|
|
: "transparent",
|
|
color: isActive
|
|
? "var(--accent, #6366f1)"
|
|
: "var(--text-secondary, #94a3b8)",
|
|
cursor: "pointer",
|
|
fontSize: "13px",
|
|
fontWeight: isActive ? 600 : 400,
|
|
textAlign: "left",
|
|
borderLeft: isActive
|
|
? "3px solid var(--accent, #6366f1)"
|
|
: "3px solid transparent",
|
|
transition: "background 0.1s, color 0.1s",
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
if (!isActive) {
|
|
e.currentTarget.style.background =
|
|
"var(--hover-bg, rgba(255,255,255,0.04))";
|
|
e.currentTarget.style.color = "var(--text-primary, #e2e8f0)";
|
|
}
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
if (!isActive) {
|
|
e.currentTarget.style.background = "transparent";
|
|
e.currentTarget.style.color = "var(--text-secondary, #94a3b8)";
|
|
}
|
|
}}
|
|
>
|
|
{item.icon}
|
|
{item.label}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
{/* Version footer */}
|
|
<div
|
|
style={{
|
|
padding: "12px 20px",
|
|
fontSize: "10px",
|
|
color: "var(--text-muted, #64748b)",
|
|
}}
|
|
>
|
|
v0.3.0
|
|
</div>
|
|
</nav>
|
|
);
|
|
}
|