| 28 | ]; |
| 29 | |
| 30 | export function TopHeader() { |
| 31 | const t = useT(); |
| 32 | const pathname = usePathname(); |
| 33 | |
| 34 | // active 判定:首页严格相等("/" 会前缀匹配所有路由,需特判);其余按前缀匹配子路由 |
| 35 | const isActive = (href: string) => |
| 36 | href === "/" ? pathname === "/" : pathname === href || pathname.startsWith(href + "/"); |
| 37 | |
| 38 | return ( |
| 39 | <header className="border-b px-4 py-2 flex items-center justify-between bg-background shrink-0"> |
| 40 | <Link href="/" className="text-lg font-semibold"> |
| 41 | {t("app.name")} |
| 42 | </Link> |
| 43 | <div className="flex items-center gap-4"> |
| 44 | <nav className="flex gap-4 text-sm"> |
| 45 | {NAV_ITEMS.map(({ href, labelKey }) => { |
| 46 | const active = isActive(href); |
| 47 | return ( |
| 48 | <Link |
| 49 | key={href} |
| 50 | href={href} |
| 51 | aria-current={active ? "page" : undefined} |
| 52 | className={ |
| 53 | active |
| 54 | ? "font-semibold text-foreground border-b-2 border-amber-600/70 dark:border-amber-400/70 -mb-[2px] pb-[2px]" |
| 55 | : "text-muted-foreground hover:text-foreground" |
| 56 | } |
| 57 | > |
| 58 | {t(labelKey)} |
| 59 | </Link> |
| 60 | ); |
| 61 | })} |
| 62 | </nav> |
| 63 | <WorkspaceSwitcher /> |
| 64 | <ConsoleLink /> |
| 65 | <SettingsMenu /> |
| 66 | </div> |
| 67 | </header> |
| 68 | ); |
| 69 | } |