Unify content under ID-based note dialogs with lightweight previews and Studio proxy
wiki CI and deployment / verify-and-deploy (push) Has been cancelled
studio CI and deployment / verify-and-deploy (push) Has been cancelled

This commit is contained in:
2026-10-25 22:06:05 +08:00
parent 7946aae568
commit b943951bed
44 changed files with 265 additions and 75 deletions
+3 -8
View File
@@ -2,19 +2,14 @@ import Link from "next/link";
import { useRouter } from "next/router";
import { Grid2X2,Image,Film,AudioLines,BookOpen,Clapperboard,Sparkles,ArrowUpRight,Layers,Tags,Upload,Menu,X,Search } from "lucide-react";
import { useEffect,useState,type ReactNode } from "react";
const groups=[
{label:'',items:[{href:'/',label:'发现',icon:Grid2X2}]},
{label:'素材',items:[{href:'/images',label:'图片',icon:Image},{href:'/videos',label:'视频',icon:Film},{href:'/audio',label:'声音',icon:AudioLines}]},
{label:'作品',items:[{href:'/articles',label:'图文',icon:BookOpen},{href:'/motion',label:'动态',icon:Clapperboard}]},
{label:'工具',items:[{href:'/skills',label:'指导文档',icon:BookOpen},{href:'/tools/doodle',label:'线条工具',icon:Sparkles}]},
];
const groups=[{label:'',items:[{href:'/',label:'首页',icon:Grid2X2},{href:'/categories',label:'分类',icon:Tags}]}];
export function Shell({children}:{children:ReactNode}){
const router=useRouter();const [open,setOpen]=useState(false);const [query,setQuery]=useState(String(router.query.q||''));
useEffect(()=>setQuery(String(router.query.q||'')),[router.query.q]);
return <div className="app-shell"><button className="mobile-menu icon-button" aria-label={open?"关闭导航":"展开导航"} aria-expanded={open} onClick={()=>setOpen(!open)}>{open?<X size={22}/>:<Menu size={22}/>}</button>
<aside className={`sidebar ${open?'is-open':''}`}><Link href="/" className="brand" aria-label="PROMPT 首页" onClick={()=>setOpen(false)}><img className="brand-mark" src="/brand-mark.svg" width={40} height={40} alt=""/><strong>PROMPT</strong></Link>
<nav>{groups.map((g,i)=><div className="nav-group" key={i}>{g.label&&<p>{g.label}</p>}{g.items.map(item=>{const active=item.href==='/'?router.asPath.split('?')[0]==='/':router.asPath.split('?')[0].startsWith(item.href);return <Link key={item.href} href={item.href} title={item.label} className={`nav-item ${active?'active':''}`} onClick={()=>setOpen(false)}><item.icon size={24} strokeWidth={active?2.2:1.8}/><span>{item.label}</span></Link>;})}</div>)}</nav>
<div className="sidebar-bottom"><Link href="/library/import" className="import-link"><Upload size={22}/><span>导入素材</span><ArrowUpRight size={17}/></Link></div>
</aside><main className="main-content"><div className="global-topbar"><form className="global-search" role="search" onSubmit={e=>{e.preventDefault();const path=router.asPath.split('?')[0];const destination=/^\/(images|videos|audio|articles|motion)(\/|$)/.test(path)?path:'/';void router.push({pathname:destination,query:query?{q:query}:{}});}}><Search size={24}/><input aria-label="搜索素材包" placeholder="搜索知识、灵感和素材" value={query} onChange={e=>setQuery(e.target.value)}/>{query&&<button type="button" aria-label="清空搜索" onClick={()=>setQuery('')}><X size={20}/></button>}</form></div>{children}</main>{open&&<button className="nav-scrim" aria-label="关闭导航" onClick={()=>setOpen(false)}/>}
</aside><main className="main-content"><div className="global-topbar"><form className="global-search" role="search" onSubmit={e=>{e.preventDefault();const path=router.asPath.split('?')[0];const destination=path==='/categories'?path:'/';void router.push({pathname:destination,query:query?{q:query}:{}});}}><Search size={24}/><input aria-label="搜索素材包" placeholder="搜索知识、灵感和素材" value={query} onChange={e=>setQuery(e.target.value)}/>{query&&<button type="button" aria-label="清空搜索" onClick={()=>setQuery('')}><X size={20}/></button>}</form></div>{children}</main>{open&&<button className="nav-scrim" aria-label="关闭导航" onClick={()=>setOpen(false)}/>}
</div>;
}