Files
Arcrun/landing/app/mira/_shared/MiraSidebar.tsx
uncle6me-web 5d00e71275 chore: D22 落地——docs/SDD/wiki/CLAUDE.md 進 repo(Gitea private 預設全 push)
頂層 D22 決策(leo 2026-07-03 拍板):推什麼由開發環境歸屬決定,
Gitea private=除機敏值/build 產物/.github 外全 push。
解 T1.5 卡點:雲端工人 clone 拿得到 credential-store-migration.md,可就地改寫 SDD。
機敏掃描兩輪通過(新增 189 檔約 2.1MB,node_modules/dist/wasm 照舊排除)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 07:13:33 +08:00

100 lines
3.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
// Mira 側邊欄(桌機左固定欄 / 手機底部 tab bar)
// SDD: polaris/mira/.agents/specs/mira-app/design.md §5.2 v2.1 + §3.7.4
// 對應 task: 10B0.1 / 10B0.2 / 10B0.4
// 現階段入口手寫;detector framework#8)上線後改讀 detector view 規格動態生成
import { useState } from 'react';
import Link from 'next/link';
import { usePathname, useRouter } from 'next/navigation';
type NavItem = {
href: string;
icon: string;
label: string;
status: 'live' | 'planned';
};
// 側邊欄入口 = 各 detector 的 view(§3.7.4)。河道是固定輸入入口,其餘是 detector 產物頁。
const NAV_ITEMS: NavItem[] = [
{ href: '/mira/feed', icon: '🌊', label: '河道', status: 'live' },
{ href: '/mira/chat', icon: '💬', label: '對話', status: 'live' },
{ href: '/mira/wiki', icon: '📚', label: 'Wiki', status: 'live' },
{ href: '/mira/projects', icon: '📋', label: '專案', status: 'live' },
{ href: '/mira/dissent', icon: '⚔️', label: '異見牆', status: 'planned' },
];
function isActive(pathname: string | null, href: string): boolean {
if (!pathname) return false;
if (href === '/mira/feed') return pathname === '/mira/feed' || pathname === '/mira';
return pathname === href || pathname.startsWith(href + '/');
}
export default function MiraSidebar() {
const pathname = usePathname();
const router = useRouter();
const [q, setQ] = useState('');
const submitSearch = (e: React.FormEvent) => {
e.preventDefault();
const query = q.trim();
if (!query) return;
router.push(`/mira/search?q=${encodeURIComponent(query)}`);
};
return (
<nav className="mira-sidebar" aria-label="Mira 導覽">
<Link href="/mira/feed" className="mira-sidebar-logo">
<span className="mira-sidebar-logo-icon">🦔</span>
<span className="mira-sidebar-logo-text">Mira</span>
</Link>
{/* 桌機:側欄輸入框 */}
<form className="mira-sidebar-search" onSubmit={submitSearch}>
<input
value={q}
onChange={e => setQ(e.target.value)}
placeholder="🔍 搜尋 wiki…"
aria-label="搜尋"
/>
</form>
{/* 手機:底部 bar 已滿,搜尋收成一個放大鏡 icon → 去搜尋頁輸入 */}
<Link
href="/mira/search"
className={`mira-sidebar-search-icon${isActive(pathname, '/mira/search') ? ' is-active' : ''}`}
aria-label="搜尋"
title="搜尋"
>
🔍
</Link>
<ul className="mira-sidebar-list">
{NAV_ITEMS.map(item => {
const active = isActive(pathname, item.href);
const planned = item.status === 'planned';
const className = `mira-sidebar-item${active ? ' is-active' : ''}${planned ? ' is-planned' : ''}`;
const inner = (
<>
<span className="mira-sidebar-icon">{item.icon}</span>
<span className="mira-sidebar-label">{item.label}</span>
{planned && <span className="mira-sidebar-badge">即將</span>}
</>
);
return (
<li key={item.href}>
{planned ? (
<span className={className} aria-disabled="true" title="即將開放">
{inner}
</span>
) : (
<Link href={item.href} className={className} aria-current={active ? 'page' : undefined}>
{inner}
</Link>
)}
</li>
);
})}
</ul>
</nav>
);
}