17 lines
4.4 KiB
TypeScript
17 lines
4.4 KiB
TypeScript
import Head from 'next/head';
|
|
import Link from 'next/link';
|
|
import {useState,useEffect,useRef} from 'react';
|
|
import {useRouter} from 'next/router';
|
|
import {streetPeople} from "@/lib/street-people";
|
|
import {X,PanelRight,Copy,ExternalLink} from 'lucide-react';
|
|
import {Shell} from './Shell';
|
|
import {skillDocuments,skillResources,documentMarkdown,type Resource} from '@/content/skills/catalog';
|
|
export function SkillDocs({documentId='index'}:{documentId?:string}){
|
|
const router=useRouter();const doc=skillDocuments.find(d=>d.id===documentId)!;const [open,setOpen]=useState(false);const[selected,setSelected]=useState<Resource|null>(null);const[copied,setCopied]=useState(false);const panel=useRef<HTMLElement>(null);
|
|
useEffect(()=>{setOpen(window.innerWidth>760)},[]);
|
|
useEffect(()=>{setSelected(null);setCopied(false)},[documentId]);
|
|
useEffect(()=>{if(!open)return;const handler=(e:KeyboardEvent)=>{if(e.key==='Escape')setOpen(false)};window.addEventListener('keydown',handler);return()=>window.removeEventListener('keydown',handler)},[open]);
|
|
const resource=(id:string)=>skillResources.find(r=>r.id===id)!;
|
|
return <Shell><Head><title>{`${doc.title} · 技能文档 · PROMPT`}</title></Head><div className="docs-top"><Link href="/tools/skills">← 全部技能</Link><button onClick={()=>setOpen(v=>!v)} aria-expanded={open}><PanelRight size={16}/>文档与来源 · {skillResources.length}</button></div><div className={`docs-layout ${open?'with-sources':''}`}><article className="docs-body"><div className="docs-eyebrow">街头小人 / SKILL DOCUMENTS</div><h1>{doc.title}</h1><div className="docs-actions"><button onClick={async()=>{try{const value=location.origin+'/skills/street-people/SKILL.md';if(navigator.clipboard)await navigator.clipboard.writeText(value);else{const input=document.createElement('textarea');input.value=value;document.body.appendChild(input);input.select();const ok=document.execCommand('copy');input.remove();if(!ok)throw Error('Copy unavailable');}setCopied(true)}catch{setCopied(false)}}}><Copy size={14}/>{copied?'已复制入口文档':'复制入口文档给 Agent'}</button><a href={resource(documentId).href} target="_blank" rel="noreferrer">纯文本版本 ↗</a></div><p className="docs-hint">网页与 Agent 纯文本版本来自同一份文档。参考图、子文档与接口均通过链接连接。</p>{doc.blocks.map((b,i)=>b.type==='heading'?<h2 key={i}>{b.text}</h2>:b.type==='code'?<pre key={i}>{b.text}</pre>:b.type==='links'?<div className="docs-inline-links" key={i}>{b.refs!.map(id=>{const r=resource(id);return <a href={r.human||r.href} key={id} onClick={e=>{e.preventDefault();setSelected(r);setOpen(true);panel.current?.focus()}}>{r.kind==='document'?'文档':r.kind==='image'?'图片':r.method} · {r.title}<ExternalLink size={12}/></a>})}</div>:<p key={i}>{b.text}</p>)}</article>{open&&<aside className="docs-sources" ref={panel} tabIndex={-1} aria-label="技能所有文档与来源"><header><div><b>文档与来源</b><small>当前技能的完整资源索引</small></div><button aria-label="关闭文档与来源" onClick={()=>setOpen(false)}><X size={18}/></button></header>{(['document','image','api'] as const).map(kind=><section key={kind}><h3>{kind==='document'?'文档':kind==='image'?'参考与示意图':'API 接口'}</h3>{skillResources.filter(r=>r.kind===kind).map(r=><button key={r.id} className={selected?.id===r.id?'selected':''} onClick={()=>setSelected(r)}><span>{r.method|| (r.kind==='image'?'IMG':'MD')}</span><div>{r.title}<small>{r.href}</small></div></button>)}</section>)}{selected&&<div className="docs-resource-preview"><h3>{selected.title}</h3>{selected.kind==='image'?<img src={selected.href} alt={selected.title}/>:selected.kind==='document'?<pre>{documentMarkdown(selected.id)}</pre>:<><code>{selected.method} {selected.href}</code><pre>{JSON.stringify(selected.method==='POST'?streetPeople.upload:streetPeople,null,2)}</pre><p>{selected.method==='POST'?'上传接口。阅读上传文档后,由 Agent 使用 multipart/form-data 提交文件。此处仅展示说明,不会发送请求。':'返回 JSON 规范。入口文档负责引导流程,JSON 用于查阅接口结构。'}</p></>}<div className="docs-resource-actions">{selected.human&&<Link href={selected.human}>打开文档网页 ↗</Link>}{selected.method!=='POST'&&<a href={selected.href} target="_blank" rel="noreferrer">{selected.kind==='document'?'打开纯文本':'打开资源'} ↗</a>}</div></div>}</aside>}</div></Shell>;
|
|
}
|