Merge branch worktree-neo-surveillance-redesign into main — Neo Surveillance redesign

Full frontend redesign with glassmorphic dark theme, floating top nav,
Live2D mascot, split-pane messages, and Ops Center dashboard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com
This commit is contained in:
Developer
2026-07-28 14:32:47 +07:00
co-authored by Claude Opus 4.8 (1M context) <noreply@anthropic.com
parent 102b3bac1f
commit 3f4fa42098
25 changed files with 2102 additions and 96 deletions
+29 -12
View File
@@ -1,23 +1,26 @@
import {
Headphones,
LayoutDashboard,
type LucideIcon,
MessageSquare,
Mic,
Headphones,
Music,
Search,
Settings,
type LucideIcon,
} from "lucide-react";
export interface NavItem {
href: string;
label: string;
icon: LucideIcon;
}
export interface NavItemWithMatch extends NavItem {
/** The pathname prefix that indicates this item is active */
matchPrefix: string;
}
export const navItems: NavItemWithMatch[] = [
/**
* Primary navigation items shown in the sidebar.
*/
export const navItems: NavItem[] = [
{
href: "/dashboard",
label: "Dashboard",
@@ -36,12 +39,24 @@ export const navItems: NavItemWithMatch[] = [
icon: Mic,
matchPrefix: "/voice",
},
{
href: "/media",
label: "Media",
icon: Music,
matchPrefix: "/media",
},
{
href: "/recordings",
label: "Recordings",
icon: Headphones,
matchPrefix: "/recordings",
},
{
href: "/analysis",
label: "Search",
icon: Search,
matchPrefix: "/analysis",
},
{
href: "/settings",
label: "Settings",
@@ -50,14 +65,16 @@ export const navItems: NavItemWithMatch[] = [
},
];
export const mobileNavItems: NavItemWithMatch[] = navItems.filter((item) =>
["/dashboard", "/messages", "/voice", "/recordings"].includes(item.href),
/**
* Mobile bottom bar items (subset of primary nav).
*/
export const mobileNavItems: NavItem[] = navItems.filter((item) =>
["/dashboard", "/messages", "/voice", "/media"].includes(item.href),
);
export function isActivePath(
pathname: string,
matchPrefix: string,
): boolean {
export type NavItemId = (typeof navItems)[number]["href"];
export function isActivePath(pathname: string, matchPrefix: string): boolean {
if (matchPrefix === "/dashboard") return pathname === "/dashboard";
return pathname.startsWith(matchPrefix);
}