feat: enhance discovery, vibe sessions, and library enrichment
Typecheck / typecheck (backend) (push) Has been cancelled
Typecheck / typecheck (workers) (push) Has been cancelled

This commit is contained in:
kami
2026-08-01 14:40:48 +04:00
parent a0c9f42a89
commit 4c48d11e9d
54 changed files with 4136 additions and 521 deletions
+78 -4
View File
@@ -1,10 +1,11 @@
import { Link } from '@tanstack/react-router';
import { useEffect, useRef, useState } from 'react';
import type { LucideIcon } from 'lucide-react';
import {
Home, Music, Disc3, Users, Tag, Compass,
Terminal, ShieldAlert,
Zap,
Settings, Sparkles,
Settings, Sparkles, X,
} from 'lucide-react';
interface NavItem {
@@ -59,15 +60,86 @@ 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() {
interface NavRailProps {
open: boolean;
onClose: () => void;
}
export function NavRail({ open, onClose }: NavRailProps) {
const [desktop, setDesktop] = useState(false);
const drawerRef = useRef<HTMLElement>(null);
const closeRef = useRef<HTMLButtonElement>(null);
useEffect(() => {
const query = window.matchMedia('(min-width: 1024px)');
const update = () => setDesktop(query.matches);
update();
query.addEventListener('change', update);
return () => query.removeEventListener('change', update);
}, []);
useEffect(() => {
if (open && !desktop) closeRef.current?.focus();
}, [open, desktop]);
useEffect(() => {
if (!open || desktop || !drawerRef.current) return;
const previous = document.activeElement as HTMLElement | null;
const trap = (event: KeyboardEvent) => {
if (event.key !== 'Tab' || !drawerRef.current) return;
const focusable = [...drawerRef.current.querySelectorAll<HTMLElement>(
'a[href], button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])'
)].filter((element) => !element.hasAttribute('hidden'));
if (focusable.length === 0) return;
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (event.shiftKey && document.activeElement === first) {
event.preventDefault(); last.focus();
} else if (!event.shiftKey && document.activeElement === last) {
event.preventDefault(); first.focus();
}
};
document.addEventListener('keydown', trap);
return () => {
document.removeEventListener('keydown', trap);
previous?.focus();
};
}, [open, desktop]);
// Do not leave an off-screen mobile drawer in the tab order. The desktop
// rail remains mounted independently of the drawer state.
if (!desktop && !open) return null;
return (
<aside className="w-48 flex flex-col bg-bg1 border-r border-border shrink-0 overflow-y-auto">
<>
{open && (
<button
type="button"
className="absolute inset-0 z-30 bg-black/60 lg:hidden"
aria-label="Close navigation"
onClick={onClose}
/>
)}
<aside
ref={drawerRef}
aria-label="Main navigation"
aria-modal={!desktop || undefined}
role={desktop ? undefined : 'dialog'}
className={`absolute inset-y-0 left-0 z-40 flex w-72 max-w-[85vw] flex-col overflow-y-auto border-r border-border bg-bg1 shadow-2xl transition-transform duration-200 lg:relative lg:z-auto lg:w-48 lg:max-w-none lg:translate-x-0 lg:shadow-none ${
open ? 'translate-x-0' : '-translate-x-full'
}`}
>
{/* 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>
<button
ref={closeRef}
type="button"
onClick={onClose}
className="ml-auto rounded-md p-2 text-muted hover:bg-surface0 hover:text-text lg:hidden"
aria-label="Close navigation"
>
<X size={18} />
</button>
</div>
{/* Navigation */}
@@ -85,6 +157,7 @@ export function NavRail() {
activeOptions={{ exact: exact ?? false }}
activeProps={{ className: `${base} ${active}` }}
inactiveProps={{ className: `${base} ${inactive}` }}
onClick={onClose}
>
<Icon size={15} className="flex-none transition-transform group-hover:scale-110" />
<span className="truncate">{label}</span>
@@ -100,6 +173,7 @@ export function NavRail() {
<div className="px-3 py-2 text-[10px] text-disabled border-t border-border">
muzick · v0.1
</div>
</aside>
</aside>
</>
);
}