@@ -1,7 +1,75 @@
import Head from 'next/head' ;
import Link from 'next/link' ;
import { useEffect , useState } from 'react ' ;
import { useRouter } from 'next/router ' ;
import { useEffect , useState , type FormEvent } from 'react' ;
import GuidanceShell from '@/components/GuidanceShell' ;
import { guides } from '@/content/guides/catalog' ;
import s from '@/styles/guidance.module.css' ;
export default function Home ( ) { const [ published , setPublished ] = useState < { id :string ; revision :number ; title :string ; summary :string ; kind :string } [ ] > ( [ ] ) , [ loadError , setLoadError ] = useState ( '' ) ; useEffect ( ( ) = > { fetch ( '/api/documents' ) . then ( async r = > { const b = await r . json ( ) ; if ( ! r . ok ) throw Error ( b . error ) ; setPublished ( b . documents ) ; } ) . catch ( e = > setLoadError ( e . message ) ) ; } , [ ] ) ; const [ q , setQ ] = useState ( '' ) , [ kind , setKind ] = useState ( 'all' ) ; const items = guides . filter ( g = > ( kind === 'all' || g . kind === kind ) && ` ${ g . title } ${ g . summary } ${ g . tags . join ( ' ' ) } ` . toLowerCase ( ) . includes ( q . toLowerCase ( ) ) ) ; return < GuidanceShell > < Head > < title > 织 页 · 为 Agent 找 到 下 一 页 < / title > < / Head > < section className = { s . hero } > < span className = { s . eyebrow } > A WEB OF USEFUL KNOWLEDGE < / span > < h1 > 一 个 起 点 , < br / > 连 接 下 一 种 可 能 。 < / h1 > < p > 每 一 篇 文 档 , 都 能 独 立 帮 助 你 的 Agent 。 < br / > 从 当 前 问 题 出 发 , 沿 着 有 用 的 链 接 继 续 探 索 。 < / p > < label className = { s . search } > ⌕ < input aria-label = "搜索文档" placeholder = "搜索文档、任务或一种想法…" value = { q } onChange = { e = > setQ ( e . target . value ) } / > < kbd > 探 索 < / kbd > < / label > < div className = { s . topics } > { [ '代码审查' , '规划' , '验证' ] . map ( t = > < button key = { t } onClick = { ( ) = > setQ ( t ) } > { t } ↗ < / button > ) } < / div > < / section > < main className = { s . layout } > < aside className = { s . sidebar } > < h2 > 文 档 目 录 < / h2 > { [ [ 'all' , '全部文档' ] , [ 'guide' , '操作指导' ] , [ 'context' , '背景知识' ] , [ 'evidence' , '事实依据' ] ] . map ( ( [ key , label ] ) = > < button key = { key } aria-pressed = { kind === key } onClick = { ( ) = > setKind ( key ) } > { label } < span > { guides . filter ( g = > key === 'all' || g . kind === key ) . length } < / span > < / button > ) } < p > 一 个 文 档 就 是 一 个 入 口 。 < br / > 集 合 是 组 织 方 式 , < br / > 无 需 整 套 安 装 。 < / p > < / aside > < section > < div className = { s . sectionTitle } > < h2 > 用 户 发 布 < / h2 > < Link href = "/write" > 发 布 一 篇 文 档 ↗ < / Link > < / div > { loadError && < p > { loadError } < / p > } < div className = { s . list } > { published . filter ( g = > ( kind === 'all' || g . kind === kind ) && ` ${ g . title } ${ g . summary } ` . toLowerCase ( ) . includes ( q . toLowerCase ( ) ) ) . map ( g = > < article className = { s . row } key = { g . id } > < div className = { s . icon } > ↗ < / div > < div > < div className = { s . meta } > { g . id } · { g . kind } · v { g . revision } < / div > < h3 > < Link href = { ` /documents/ ${ g . id } ?revision= ${ g . revision } ` } > { g . title } < / Link > < / h3 > < p > { g . summary } < / p > < / div > < / article > ) } < / div > < div className = { s . sectionTitle } > < h2 > 从 这 里 开 始 < small > { items . length } 篇 < / small > < / h2 > < span > 协 议 样 例 · v0 . 1 < / span > < / div > < div className = { s . list } > { items . map ( g = > < article className = { s . row } key = { g . id } > < div className = { s . icon } > { g . kind === 'guide' ? '↗' : '≋' } < / div > < div > < div className = { s . meta } > 织 页 / protocol - examples · r { g . revision } < / div > < h3 > < Link href = { ` /guide/ ${ g . id } ` } > { g . title } < / Link > < / h3 > < p > { g . summary } < / p > < div className = { s . tags } > { g . tags . map ( t = > < span key = { t } > { t } < / span > ) } < span > ↗ { g . links . length } 个 关 联 < / span > < / div > < / div > < / article > ) } { ! items . length && < p > 没 有 匹 配 文 档 , 请 尝 试 其 他 关 键 词 或 分 类 。 < / p > } < / div > < / section > < aside className = { s . right } > < div className = { s . manifest } > < span className = { s . eyebrow } > DOCUMENTS , CONNECTED . < / span > < h2 > 知 识 不 必 < br / > 打 包 带 走 。 < / h2 > < p > 读 一 页 , 解 决 眼 前 的 问 题 。 < br / > 需 要 更 多 时 , 再 打 开 下 一 页 。 < / p > < Link href = "/protocol" > 了 解 文 档 协 议 ↗ < / Link > < / div > < div className = { s . note } > < h3 > 为 两 种 Agent 而 写 < / h3 > < p > Coding agent 的 工 作 方 法 , Personal agent 的 日 常 指 导 , 都 可 以 从 一 篇 文 档 开 始 。 < / p > < h3 > 这 是 一 个 早 期 原 型 < / h3 > < p > 已 验 证 邮 箱 用 户 可 以 发 布 独 立 文 档 , 更 新 会 创 建 不 可 变 版 本 。 下 方 协 议 样 例 与 真 实 用 户 文 档 分 别 展 示 。 < / p > < / div > < / aside > < / main > < / GuidanceShell > }
type PublishedDocument = { id :string ; revision :number ; title :string ; summary :string ; kind :string } ;
const categories = [ [ 'all' , '全部文档' ] , [ 'guide' , '操作指导' ] , [ 'context' , '背景知识' ] , [ 'evidence' , '事实依据' ] ] ;
export default function Home ( ) {
const router = useRouter ( ) ;
const [ published , setPublished ] = useState < PublishedDocument [ ] > ( [ ] ) ;
const [ loadError , setLoadError ] = useState ( '' ) ;
const [ loading , setLoading ] = useState ( true ) ;
const [ q , setQ ] = useState ( '' ) ;
const [ kind , setKind ] = useState ( 'all' ) ;
useEffect ( ( ) = > {
let active = true ;
fetch ( '/api/documents' ) . then ( async r = > {
const b = await r . json ( ) ; if ( ! r . ok ) throw Error ( b . error ) ;
if ( active ) setPublished ( b . documents ) ;
} ) . catch ( e = > { if ( active ) setLoadError ( e . message ) ; } ) . finally ( ( ) = > { if ( active ) setLoading ( false ) ; } ) ;
return ( ) = > { active = false ; } ;
} , [ ] ) ;
useEffect ( ( ) = > {
if ( ! router . isReady ) return ;
setQ ( typeof router . query . q === 'string' ? router . query . q : '' ) ;
setKind ( typeof router . query . kind === 'string' && categories . some ( ( [ key ] ) = > key === router . query . kind ) ? router . query . kind : 'all' ) ;
} , [ router . isReady , router . query . q , router . query . kind ] ) ;
function rememberFilters ( query :string , category :string ) {
const next : { q? :string ; kind? :string } = { } ;
if ( query ) next . q = query ; if ( category !== 'all' ) next . kind = category ;
void router . push ( { pathname : '/' , query :next } , undefined , { shallow :true , scroll :false } ) ;
}
function search ( e :FormEvent ) { e . preventDefault ( ) ; rememberFilters ( q , kind ) ; }
function chooseKind ( value :string ) { setKind ( value ) ; rememberFilters ( q , value ) ; }
function clearFilters ( ) { setQ ( '' ) ; setKind ( 'all' ) ; rememberFilters ( '' , 'all' ) ; }
const matches = ( g : { title :string ; summary :string ; kind :string ; tags? :string [ ] } ) = > ( kind === 'all' || g . kind === kind ) && ` ${ g . title } ${ g . summary } ${ g . tags ? . join ( ' ' ) || '' } ` . toLowerCase ( ) . includes ( q . trim ( ) . toLowerCase ( ) ) ;
const items = guides . filter ( matches ) , userItems = published . filter ( matches ) ;
const total = items . length + userItems . length ;
return < GuidanceShell >
< Head > < title > 织 页 · 为 Agent 找 到 下 一 页 < / title > < / Head >
< section className = { s . hero } >
< span className = { s . eyebrow } > A WEB OF USEFUL KNOWLEDGE < / span >
< h1 > 一 个 起 点 , < br / > 连 接 下 一 种 可 能 。 < / h1 >
< p > 每 一 篇 文 档 , 都 能 独 立 帮 助 你 的 Agent 。 < br / > 从 当 前 问 题 出 发 , 沿 着 有 用 的 链 接 继 续 探 索 。 < / p >
< form className = { s . search } role = "search" onSubmit = { search } >
< span aria-hidden = "true" > ⌕ < / span > < input aria-label = "搜索文档" placeholder = "搜索文档、任务或一种想法…" value = { q } onChange = { e = > setQ ( e . target . value ) } / > < button type = "submit" > 搜 索 < / button >
< / form >
< div className = { s . topics } > { [ '代码审查' , '规划' , '验证' ] . map ( t = > < button key = { t } onClick = { ( ) = > { setQ ( t ) ; rememberFilters ( t , kind ) ; } } > { t } ↗ < / button > ) } < / div >
< / section >
< main className = { s . layout } >
< aside className = { s . sidebar } >
< h2 > 文 档 目 录 < / h2 >
{ categories . map ( ( [ key , label ] ) = > < button key = { key } aria-pressed = { kind === key } onClick = { ( ) = > chooseKind ( key ) } > { label } < span > { [ . . . guides , . . . published ] . filter ( g = > key === 'all' || g . kind === key ) . length } < / span > < / button > ) }
< p > 一 个 文 档 就 是 一 个 入 口 。 < br / > 集 合 是 组 织 方 式 , < br / > 无 需 整 套 安 装 。 < / p >
< / aside >
< section aria-label = "文档搜索结果" >
< div className = { s . sectionTitle } > < p role = "status" > { loading ? '正在加载用户文档…' : loadError ? ` 协议样例 ${ items . length } 篇;用户文档加载失败 ` : ` 找到 ${ total } 篇文档 ` } < / p > { ( q || kind !== 'all' ) && < button type = "button" onClick = { clearFilters } > 清 除 所 有 筛 选 < / button > } < / div >
{ loadError && < p role = "alert" > 用 户 文 档 暂 未 加 载 : { loadError } 。 下 方 仍 可 阅 读 协 议 样 例 。 < / p > }
{ ! loading && ! loadError && total === 0 && < p > 没 有 匹 配 文 档 , 请 尝 试 其 他 关 键 词 或 清 除 筛 选 。 < / p > }
< div className = { s . sectionTitle } > < h2 > 用 户 发 布 < small > { userItems . length } 篇 < / small > < / h2 > < Link href = "/write" > 发 布 一 篇 文 档 ↗ < / Link > < / div >
< div className = { s . list } > { userItems . map ( g = > < article className = { s . row } key = { g . id } > < div className = { s . icon } > ↗ < / div > < div > < div className = { s . meta } > { g . id } · { g . kind } · v { g . revision } < / div > < h3 > < Link href = { ` /documents/ ${ g . id } ?revision= ${ g . revision } ` } > { g . title } < / Link > < / h3 > < p > { g . summary } < / p > < / div > < / article > ) } < / div >
{ ! loading && ! loadError && ! published . length && < p > 还 没 有 用 户 文 档 。 你 可 以 发 布 第 一 篇 , 或 先 阅 读 下 方 样 例 。 < / p > }
< div className = { s . sectionTitle } > < h2 > 从 这 里 开 始 < small > { items . length } 篇 < / small > < / h2 > < span > 协 议 样 例 · v0 . 1 < / span > < / div >
< div className = { s . list } > { items . map ( g = > < article className = { s . row } key = { g . id } > < div className = { s . icon } > { g . kind === 'guide' ? '↗' : '≋' } < / div > < div > < div className = { s . meta } > 织 页 / protocol - examples · r { g . revision } < / div > < h3 > < Link href = { ` /guide/ ${ g . id } ` } > { g . title } < / Link > < / h3 > < p > { g . summary } < / p > < div className = { s . tags } > { g . tags . map ( t = > < span key = { t } > { t } < / span > ) } < span > ↗ { g . links . length } 个 关 联 < / span > < / div > < / div > < / article > ) } < / div >
< / section >
< aside className = { s . right } >
< div className = { s . manifest } > < span className = { s . eyebrow } > DOCUMENTS , CONNECTED . < / span > < h2 > 知 识 不 必 < br / > 打 包 带 走 。 < / h2 > < p > 读 一 页 , 解 决 眼 前 的 问 题 。 < br / > 需 要 更 多 时 , 再 打 开 下 一 页 。 < / p > < Link href = "/protocol" > 了 解 文 档 协 议 ↗ < / Link > < / div >
< div className = { s . note } > < h3 > 为 两 种 Agent 而 写 < / h3 > < p > Coding agent 的 工 作 方 法 , Personal agent 的 日 常 指 导 , 都 可 以 从 一 篇 文 档 开 始 。 < / p > < h3 > 这 是 一 个 早 期 原 型 < / h3 > < p > 已 验 证 邮 箱 用 户 可 以 发 布 独 立 文 档 , 更 新 会 创 建 不 可 变 版 本 。 下 方 协 议 样 例 与 真 实 用 户 文 档 分 别 展 示 。 < / p > < / div >
< / aside >
< / main >
< / GuidanceShell > ;
}