'use client'; // Mira Wiki 索引頁 // SDD: polaris/mira/.agents/specs/mira-app/design.md §5.2 + §3.5.10 // 對應 task: 7C.1 // 階段 7-A 已建:mira-wiki-schema、mira-wiki-index(+4 children)、mira-wiki-log(+1 child) // 此頁列出這些 infra block 與既有 wiki-page,方便 leo 在瀏覽器確認 schema 寫得對不對 import { useEffect, useState } from 'react'; import Link from 'next/link'; import '../mira.css'; const KBDB_BASE = 'https://kbdb.finally.click'; const API_BASE = process.env.NEXT_PUBLIC_API_BASE ?? 'https://cypher.arcrun.dev'; type Block = { id: string; page_name: string; content: string; type: string; parent_id: string | null; tags_json: string | null; created_at: number; }; export default function WikiIndexPage() { const [schema, setSchema] = useState(null); const [indexChildren, setIndexChildren] = useState([]); const [logEntries, setLogEntries] = useState([]); const [otherWikiPages, setOtherWikiPages] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { let cancelled = false; async function load() { try { // 先拿 ak_ partner key(同 page.tsx pattern) const meRes = await fetch(`${API_BASE}/me`, { credentials: 'include' }); if (!meRes.ok) throw new Error('未登入'); const me = (await meRes.json()) as { api_key: string }; const headers = { Authorization: `Bearer ${me.api_key}` }; // 撈所有 type=wiki-page,再 client 端過濾 tags 含 'mira-wiki' // 原本 ?tag=mira-wiki 撞 KBDB worker D1 bug(malformed JSON),改 type filter // 待 KBDB 修 tag filter 後可改回(SDD 待開 kbdb-tag-filter-fix) const res = await fetch( `${KBDB_BASE}/blocks?type=wiki-page&limit=200`, { headers }, ); if (!res.ok) throw new Error(`KBDB ${res.status}`); const data = await res.json(); if (cancelled) return; const allWikiBlocks: Block[] = data.blocks ?? []; // Client 端過濾:只留 tags 含 'mira-wiki' const blocks: Block[] = allWikiBlocks.filter((b) => { if (!b.tags_json) return false; try { const tags = JSON.parse(b.tags_json) as string[]; return tags.includes('mira-wiki'); } catch { return false; } }); const tagsOf = (b: Block): string[] => { if (!b.tags_json) return []; try { return JSON.parse(b.tags_json) as string[]; } catch { return []; } }; const hasSubtype = (b: Block, st: string) => tagsOf(b).includes(`subtype:${st}`); const hasAnyInfraSubtype = (b: Block) => ['schema', 'index', 'index-child', 'log', 'log-child'].some((st) => hasSubtype(b, st)); const hasMetaTag = (b: Block) => tagsOf(b).some((t) => t === 'data-source-config' || t === 'source-skill'); setSchema(blocks.find((b) => hasSubtype(b, 'schema')) ?? null); setIndexChildren( blocks .filter((b) => hasSubtype(b, 'index-child')) .sort((a, b) => a.page_name.localeCompare(b.page_name)), ); setLogEntries( blocks .filter((b) => hasSubtype(b, 'log-child')) .sort((a, b) => b.page_name.localeCompare(a.page_name)), ); // 真正的 wiki-page paragraphs(排除 infra 跟 meta 配置) setOtherWikiPages( blocks .filter((b) => !hasAnyInfraSubtype(b) && !hasMetaTag(b)) .sort((a, b) => (b.created_at ?? 0) - (a.created_at ?? 0)), ); } catch (e: any) { if (!cancelled) setError(e?.message ?? 'load failed'); } finally { if (!cancelled) setLoading(false); } } load(); return () => { cancelled = true; }; }, []); return (
← Mira 首頁

📚 Mira Wiki

leo 的個人觀點累積(Karpathy LLM Wiki 風格)

{loading &&
載入中⋯
} {error && (
讀取失敗:{error}
)} {!loading && !error && ( <>
{schema ? ( ) : ( 尚未建立 schema )}
{indexChildren.length > 0 ? (
{indexChildren.map((b) => { const tags = b.tags_json ? (JSON.parse(b.tags_json) as string[]) : []; const key = tags.find((t) => t.startsWith('index-key:'))?.replace('index-key:', '') ?? '?'; return ( ); })}
) : ( index children 尚未建立 )}
{logEntries.length > 0 ? (
{logEntries.map((b) => ( ))}
) : ( 尚未有 log )}
{otherWikiPages.length > 0 ? (
{otherWikiPages.map((p) => ( ))}
) : ( 尚未有 wiki page(待 7-B ai-canon-wiki workflow 跑出第一張) )}
)}
); } function Section({ title, children }: { title: string; children: React.ReactNode }) { return (

{title}

{children}
); } function Empty({ children }: { children: React.ReactNode }) { return (
{children}
); } function WikiCardLink({ page_name, title, excerpt, }: { page_name: string; title: string; excerpt: string; }) { return (
{title}
{excerpt && (
{excerpt}
)} ); } function iconForKey(key: string): string { return ( { entities: '🧩', topics: '📂', sources: '🔗', stale: '⚠️', }[key] ?? '•' ); } function firstLineOf(content: string): string { if (!content) return ''; const firstNonHeader = content .split('\n') .map((l) => l.trim()) .find((l) => l && !l.startsWith('#') && !l.startsWith('>')); return firstNonHeader ?? ''; }