initial state: muzick music player + recommendation engine
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
import { Link } from '@tanstack/react-router';
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
import {
|
||||
Home, Music, Disc3, Users, Tag, Compass,
|
||||
Terminal, ShieldAlert,
|
||||
Zap,
|
||||
Settings, Sparkles,
|
||||
} from 'lucide-react';
|
||||
|
||||
interface NavItem {
|
||||
to: string;
|
||||
icon: LucideIcon;
|
||||
label: string;
|
||||
exact?: boolean;
|
||||
}
|
||||
|
||||
interface NavGroup {
|
||||
label: string;
|
||||
items: NavItem[];
|
||||
}
|
||||
|
||||
const NAV_GROUPS: NavGroup[] = [
|
||||
{
|
||||
label: 'Workspace',
|
||||
items: [
|
||||
{ to: '/', icon: Home, label: 'Home', exact: true },
|
||||
{ to: '/tracks', icon: Music, label: 'Songs' },
|
||||
{ to: '/albums', icon: Disc3, label: 'Albums' },
|
||||
{ to: '/artists', icon: Users, label: 'Artists' },
|
||||
{ to: '/genres', icon: Tag, label: 'Genres' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'AI',
|
||||
items: [
|
||||
{ to: '/vibe', icon: Zap, label: 'Vibe' },
|
||||
{ to: '/discover', icon: Compass, label: 'Discover' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Infrastructure',
|
||||
items: [
|
||||
{ to: '/jobs', icon: Terminal, label: 'Jobs' },
|
||||
{ to: '/quarantine', icon: ShieldAlert, label: 'Quarantine' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
items: [
|
||||
{ to: '/settings', icon: Settings, label: 'Settings' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const base =
|
||||
'group relative flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs w-full transition-all duration-100';
|
||||
const inactive = 'text-secondary hover:bg-surface0 hover:text-text';
|
||||
const active =
|
||||
'bg-accent/10 text-accent font-medium ' +
|
||||
"before:content-[''] before:absolute before:left-0 before:top-1 before:bottom-1 before:w-0.5 before:rounded-full before:bg-accent";
|
||||
|
||||
export function NavRail() {
|
||||
return (
|
||||
<aside className="w-48 flex flex-col bg-bg1 border-r border-border shrink-0 overflow-y-auto">
|
||||
{/* App branding */}
|
||||
<div className="flex items-center gap-2 px-3 py-2.5 border-b border-border">
|
||||
<span className="flex h-6 w-6 items-center justify-center rounded-md bg-accent text-on-accent">
|
||||
<Sparkles size={14} />
|
||||
</span>
|
||||
<span className="text-sm font-semibold text-text tracking-tight">muzick</span>
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<nav className="flex-1 px-2 space-y-4 pb-3 pt-3">
|
||||
{NAV_GROUPS.map((group) => (
|
||||
<div key={group.label}>
|
||||
<div className="px-3 mb-1 text-[10px] font-semibold uppercase tracking-[0.1em] text-disabled">
|
||||
{group.label}
|
||||
</div>
|
||||
<ul className="space-y-0.5">
|
||||
{group.items.map(({ to, icon: Icon, label, exact }) => (
|
||||
<li key={to}>
|
||||
<Link
|
||||
to={to}
|
||||
activeOptions={{ exact: exact ?? false }}
|
||||
activeProps={{ className: `${base} ${active}` }}
|
||||
inactiveProps={{ className: `${base} ${inactive}` }}
|
||||
>
|
||||
<Icon size={15} className="flex-none transition-transform group-hover:scale-110" />
|
||||
<span className="truncate">{label}</span>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="px-3 py-2 text-[10px] text-disabled border-t border-border">
|
||||
muzick · v0.1
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user