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>
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
'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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user