const {useState,useEffect,useRef}=React;
const paths={terminal:'M4 4h16v16H4ZM7 8l3 3-3 3m6 1h4',automation:'M12 8v5l3 2 M20 13a8 8 0 1 1-16 0 8 8 0 0 1 16 0 M9 2h6 M12 2v3 M18 5l2 2',panel:'M9 4v16 M5 4h14a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2Z',plus:'M12 5v14M5 12h14',compose:'M20 13v5a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V7a3 3 0 0 1 3-3h5 M15 4l5 5 M9 15l1-5 8-8 4 4-8 8Z',search:'M16 16l5 5 M17 10a7 7 0 1 1-14 0 7 7 0 0 1 14 0',chat:'M5 4h14a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H9l-6 4V6a2 2 0 0 1 2-2Z',folder:'M3 7V5a2 2 0 0 1 2-2h5l3 3h6a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7h18',grid:'M3 3h7v7H3ZM14 3h7v7h-7ZM3 14h7v7H3ZM14 14h7v7h-7Z',plug:'M8 3v5m8-5v5M6 8h12v4a6 6 0 0 1-12 0V8Zm6 10v4',down:'m7 10 5 5 5-5',right:'m9 6 6 6-6 6',arrow:'M12 20V4m-6 6 6-6 6 6',enter:'M19 5v9H5m5-5-5 5 5 5',monitor:'M5 4h14a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2ZM8 21h8m-4-4v4',cloud:'M7 18a5 5 0 1 1 .3-10A6 6 0 0 1 19 9a4.5 4.5 0 0 1-.5 9H7Z',server:'M4 3h16v7H4ZM4 14h16v7H4ZM7 6.5h.1M7 17.5h.1m4-11h5m-5 11h5',check:'m5 12 4.5 4.5L19 7',close:'m6 6 12 12M6 18 18 6',more:'M4 12h.2M12 12h.2M20 12h.2',shield:'m12 3 8 3v6c0 5-8 9-8 9s-8-4-8-9V6l8-3ZM8 11l3 3 5-5',model:'M5 5h14v14H5ZM9 2v3m6-3v3M9 19v3m6-3v3M2 9h3m-3 6h3m14-6h3m-3 6h3M9 9h6v6H9Z',doc:'M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9l-6-6ZM14 3v6h6M8 13h8m-8 4h5',tools:'M14 4a5 5 0 0 0-6 6L3 16a3 3 0 0 0 5 4l5-6a5 5 0 0 0 7-6l-4 3-3-3 3-4Z',calendar:'M5 5h14a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2ZM3 10h18M8 3v4m8-4v4M7 14h3m4 0h3m-10 3h3',briefcase:'M5 7h14a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2ZM8 7V4h8v3M3 12c5 4 13 4 18 0m-9 0v4',refresh:'M20 8a8 8 0 0 0-14-3L3 8m0-5v5h5M4 16a8 8 0 0 0 14 3l3-3m0 5v-5h-5',settings:'m9 3-1 3-3 1-2 4 2 2v4l4 3 3-1 3 1 4-3v-4l2-2-2-4-3-1-1-3H9ZM15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0',github:'M9 20c-5 1-5-2-7-2m15 4v-4a3 3 0 0 0-1-2c4-.5 6-2 6-6a5 5 0 0 0-1-3 5 5 0 0 0-.2-4S19 2.7 17 4a14 14 0 0 0-8 0C7 2.7 5.2 3 5.2 3A5 5 0 0 0 5 7a5 5 0 0 0-1 3c0 4 2 5.5 6 6a3 3 0 0 0-1 2v4',diagram:'M9 2h6v5H9ZM2 16h6v5H2ZM16 16h6v5h-6ZM12 7v5H5v4m7-4h7v4',paperclip:'m8 12 6-6a3 3 0 0 1 4 4l-8 8a5 5 0 0 1-7-7l8-8a6 6 0 0 1 9 9l-8 8',style:'M4 5h3m4 0h9M4 12h9m4 0h3M4 19h3m4 0h9M7 3h4v4H7ZM13 10h4v4h-4ZM7 17h4v4H7Z'};
function Icon({name,size='',className=''}){return <svg className={'icon '+size+' '+className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d={paths[name]||paths.chat}/></svg>;}
function IB({icon,label,onClick}){return <button type="button" className="icon-button" title={label} aria-label={label} onClick={onClick}><Icon name={icon}/></button>;}
const initialProjects=[{id:'p1',name:'产品设计',scope:'team',description:'从想法到界面，让每一个细节更好。',env:'local'},{id:'p2',name:'个人网站',scope:'personal',description:'设计、开发和完善我的个人网站。',env:'local'},{id:'p3',name:'工作资料',scope:'personal',description:'整理文档、沉淀知识与日常工作记录。',env:'cloud'}];
const initialChats=[['c1','整理本周的工作周报',null,'今天'],['c2','帮我分析这份用户反馈',null,'今天'],['c3','检查一下我的开发环境',null,'昨天'],['c4','把这些材料汇总成一页',null,'昨天'],['c5','规划下周的学习安排',null,'昨天'],['c6','设计新的对话首页','p1','今天'],['c7','梳理项目管理的交互流程','p1','昨天'],['c8','整理界面设计规范','p1','昨天'],['c9','完善首页的响应式布局','p2','昨天']].map(([id,title,project,time])=>({id,title,project,time,messages:[]}));
const envs=[{id:'local',name:'本机',icon:'monitor',detail:'在这台电脑上处理文件和运行任务'},{id:'cloud',name:'云端工作区',icon:'cloud',detail:'在独立的云端环境中处理任务'},{id:'remote',name:'远程环境',icon:'server',detail:'尚未连接远程设备',disabled:true}];
const plugins=[{id:'feishu',name:'飞书',icon:'doc',color:'blue',desc:'查找文档、整理会议与工作资料'},{id:'github',name:'GitHub',icon:'github',color:'',desc:'查看代码、Issue 和 Pull Request'},{id:'mermaid',name:'Mermaid',icon:'diagram',color:'purple',desc:'绘制流程图、架构图和思维导图'}];
const suggestionSets=[[
{icon:'briefcase',text:'整理本周的工作进展，帮我写一份周报',prompt:'帮我整理本周的工作进展，按已完成、进行中、风险和下周计划，写一份简洁的周报。'},
{icon:'tools',text:'检查一下我的开发环境，看看有没有需要修复的',prompt:'帮我检查一下当前开发环境，找出配置、依赖或版本上的问题，先列出检查结果和修复建议。'},
{icon:'doc',text:'把这些材料汇总成一页，提炼重点与待办',prompt:'帮我把接下来提供的材料汇总成一页，提炼核心结论、关键信息与待办事项。'},
{icon:'calendar',text:'规划今天的工作，优先处理最重要的事情',prompt:'帮我规划今天的工作。先了解我的任务和时间安排，再按重要性与紧急程度排序，给出可执行的计划。'}],[
{icon:'search',text:'分析用户反馈，找到最值得改进的问题',prompt:'帮我分析接下来提供的用户反馈，按主题分类，找出高频问题，并建议优先改进的三件事。'},
{icon:'diagram',text:'把复杂的流程整理成一张清晰的流程图',prompt:'帮我把接下来描述的业务流程整理成一张清晰的流程图，标明角色、操作与关键判断节点。'},
{icon:'folder',text:'盘点项目进展，整理下一步行动清单',prompt:'帮我盘点当前项目的进展，明确已经完成的内容、存在的阻碍和下一步行动清单。'},
{icon:'doc',text:'读一份长文档，提炼出值得关注的结论',prompt:'帮我阅读接下来提供的文档，用简洁的语言提炼核心结论、依据和需要进一步确认的问题。'}]];
function loadSaved(){try{const d=JSON.parse(localStorage.getItem('conversation-prototype-v1'));return d&&Array.isArray(d.projects)&&Array.isArray(d.chats)?d:null;}catch{return null;}}
const harnessOptions=[{id:'codex',name:'Codex',models:['GPT 6.0 Astra','GPT 5.6 Sol','GPT 5.6 Terra']},{id:'claude-code',name:'Claude Code',models:['Claude Sonnet 4.6','Claude Opus 4.6']}];
const saved=loadSaved();
function App(){
const [extensionState,setExtensionState]=useState(ExtensionData.load);
const plugins=extensionState.plugins.filter(p=>p.installed&&p.enabled&&(p.auth==='none'||p.connected));
useEffect(()=>{try{localStorage.setItem('workspace-extensions-v1',JSON.stringify(extensionState));}catch{}},[extensionState]);
const [t,setTweak]=useTweaks(window.TWEAK_DEFAULTS);
const [projects,setProjects]=useState(()=>(saved?.projects||initialProjects).map(p=>({...p,scope:p.scope||initialProjects.find(seed=>seed.id===p.id)?.scope||'personal'}))),[chats,setChats]=useState(saved?.chats||initialChats);
const [view,setView]=useState('home'),[project,setProject]=useState(null),[activeChat,setActiveChat]=useState(null);
const [harness,setHarness]=useState('codex');
const [env,setEnv]=useState('local'),[enabled,setEnabled]=useState(['feishu','github']),[model,setModel]=useState('GPT 6.0 Astra'),[approval,setApproval]=useState('自动批准');
useEffect(()=>{setEnabled(ids=>{const next=ids.filter(id=>plugins.some(p=>p.id===id));return next.length===ids.length?ids:next;});},[extensionState.plugins,enabled]);
const [draft,setDraft]=useState(''),[files,setFiles]=useState([]),[pop,setPop]=useState(null),[modal,setModal]=useState(null),[query,setQuery]=useState(''),[filter,setFilter]=useState('');
const [popUp,setPopUp]=useState(false);
const [popMaxHeight,setPopMaxHeight]=useState(420);
const [expanded,setExpanded]=useState(['p1']),[personalOpen,setPersonalOpen]=useState(true),[projectsOpen,setProjectsOpen]=useState(true);
const [sidebar,setSidebar]=useState(window.innerWidth>760),[suggestionSet,setSuggestionSet]=useState(0),[toast,setToast]=useState(''),[pending,setPending]=useState(null);
const textarea=useRef(null),composerRef=useRef(null),messagesRef=useRef(null),uploadRef=useRef(null),modalRef=useRef(null),toastTimer=useRef(null),replyTimers=useRef([]);
const currentHarness=harnessOptions.find(h=>h.id===harness)||harnessOptions[0];
const currentProject=projects.find(p=>p.id===project),currentChat=chats.find(c=>c.id===activeChat),currentEnv=envs.find(e=>e.id===env);
useEffect(()=>{try{localStorage.setItem('conversation-prototype-v1',JSON.stringify({projects,chats}));}catch{}},[projects,chats]);
useEffect(()=>{if(activeChat)setChats(all=>all.map(c=>c.id===activeChat?{...c,env,plugins:enabled,harness,model}:c));},[activeChat,env,enabled,harness,model]);
useEffect(()=>{const outside=e=>{if(composerRef.current&&!composerRef.current.contains(e.target))setPop(null);};document.addEventListener('pointerdown',outside);return()=>document.removeEventListener('pointerdown',outside);},[]);
useEffect(()=>{const key=e=>{if(e.key==='Escape'){setPop(null);setModal(null);if(window.innerWidth<761)setSidebar(false);}if((e.metaKey||e.ctrlKey)&&e.key.toLowerCase()==='k'){e.preventDefault();setQuery('');setModal({type:'search'});}if((e.metaKey||e.ctrlKey)&&e.key.toLowerCase()==='n'){e.preventDefault();newChat();}};window.addEventListener('keydown',key);return()=>window.removeEventListener('keydown',key);},[]);
useEffect(()=>{if(messagesRef.current)messagesRef.current.scrollTop=messagesRef.current.scrollHeight;},[currentChat?.messages?.length,pending]);
useEffect(()=>{if(!modal)return;const previous=document.activeElement,el=modalRef.current;const list=()=>Array.from(el?.querySelectorAll('button:not(:disabled),input,textarea,select')||[]);(el?.querySelector('input')||list()[0])?.focus();const trap=e=>{if(e.key!=='Tab')return;const items=list();if(e.shiftKey&&document.activeElement===items[0]){e.preventDefault();items.at(-1)?.focus();}else if(!e.shiftKey&&document.activeElement===items.at(-1)){e.preventDefault();items[0]?.focus();}};document.addEventListener('keydown',trap);return()=>{document.removeEventListener('keydown',trap);previous?.focus();};},[modal?.type]);
useEffect(()=>()=>{replyTimers.current.forEach(clearTimeout);clearTimeout(toastTimer.current);},[]);
function notify(text){setToast(text);clearTimeout(toastTimer.current);toastTimer.current=setTimeout(()=>setToast(''),2800);}
function closeMobile(){if(window.innerWidth<761)setSidebar(false);}
function newChat(pid=null){setActiveChat(null);setProject(pid);setView('home');setDraft('');setFiles([]);setPop(null);if(pid){setEnv(projects.find(x=>x.id===pid)?.env||'local');setExpanded(e=>[...new Set([...e,pid])]);}closeMobile();}
function openChat(c){setActiveChat(c.id);setProject(c.project);setEnv(c.env||projects.find(p=>p.id===c.project)?.env||'local');setEnabled(c.plugins||['feishu','github']);const selected=harnessOptions.find(h=>h.id===c.harness)||harnessOptions[0];setHarness(selected.id);setModel(selected.models.includes(c.model)?c.model:selected.models[0]);setView('conversation');setDraft('');setFiles([]);setPop(null);setModal(null);closeMobile();}
function openProject(pid){setProject(pid);setView('project');setActiveChat(null);setPop(null);setExpanded(e=>[...new Set([...e,pid])]);closeMobile();}
function navigate(v){setView(v);setPop(null);setFilter('');closeMobile();}
function chooseProject(pid){setProject(pid);setPop(null);if(pid)setExpanded(e=>[...new Set([...e,pid])]);if(activeChat){setChats(all=>all.map(c=>c.id===activeChat?{...c,project:pid}:c));notify(pid?'已移入「'+projects.find(p=>p.id===pid)?.name+'」':'已移入个人对话');}}
function togglePlugin(id){setEnabled(all=>all.includes(id)?all.filter(x=>x!==id):[...all,id]);}
function togglePop(name){const rect=composerRef.current?.querySelector('.context-bar')?.getBoundingClientRect();const stage=document.querySelector('.main-stage')?.getBoundingClientRect();if(rect&&stage){const above=rect.top-stage.top-12,below=stage.bottom-rect.bottom-12;const up=view==='conversation'||(below<420&&above>below);setPopUp(up);setPopMaxHeight(Math.max(140,Math.min(420,up?above:below)));}setPop(x=>x===name?null:name);setQuery('');}
function send(){
if(!draft.trim()||(activeChat&&pending===activeChat))return;
const id=activeChat||'chat-'+Date.now(),body=draft.trim();
const context={harness:currentHarness.name,model,env:currentEnv.name,project:currentProject?.name||'个人对话',plugins:plugins.filter(p=>enabled.includes(p.id)).map(p=>p.name).join('、')||'未启用插件'};
const msg={role:'user',text:body,files:files.map(f=>f.name),context};
setChats(all=>activeChat?all.map(c=>c.id===activeChat?{...c,env,plugins:enabled,harness,model,messages:[...c.messages,msg],time:'刚刚'}:c):[{id,title:body.slice(0,28),project,env,plugins:enabled,harness,model,time:'刚刚',messages:[msg]},...all]);
setActiveChat(id);setView('conversation');setDraft('');setFiles([]);setPop(null);setPending(id);if(project)setExpanded(e=>[...new Set([...e,project])]);
replyTimers.current.push(setTimeout(()=>{setChats(all=>all.map(c=>c.id===id?{...c,messages:[...c.messages,{role:'assistant',text:'好的，我们从这件事开始。\n\n这是一条交互演示回复。正式产品会在「'+context.env+'」中，结合'+(project?'「'+context.project+'」的项目上下文':'本次个人对话的内容')+'处理你的请求。\n\n你可以继续输入，也可以在下方调整环境、项目和插件。',context}]}:c));setPending(p=>p===id?null:p);},850));
}
function chatRow(c,nested=false){return <div key={c.id} className={'chat-row '+(activeChat===c.id&&view==='conversation'?'selected':'')}><button onClick={()=>openChat(c)} title={c.title}>{!nested&&<Icon name="chat"/>}<span className="chat-name">{c.title}</span></button><button className="icon-button row-menu" aria-label={'管理对话：'+c.title} onClick={()=>setModal({type:'chat-edit',chat:c})}><Icon name="more"/></button></div>;}
function selectorPopover(){
if(pop==='environment')return <div className="popover context-pop" role="dialog" aria-label="选择环境"><div className="popover-title">选择环境</div><div className="popover-sub">决定任务在哪里运行</div>{envs.map(e=><button className={'pop-option '+(e.disabled?'disabled':'')} key={e.id} disabled={e.disabled} onClick={()=>{setEnv(e.id);setPop(null);}} aria-pressed={env===e.id}><span className="option-icon"><Icon name={e.icon}/></span><span className="option-info"><strong>{e.name}</strong><small>{e.detail}</small></span>{env===e.id&&<Icon name="check" className="checkmark"/>}</button>)}<div className="pop-bottom-note">当前为演示环境，不会访问真实设备。</div></div>;
if(pop==='project')return <div className="popover context-pop project-pop" role="dialog" aria-label="选择项目"><div className="popover-title">选择项目</div><div className="popover-sub">将对话与相关工作放在一起</div><label className="pop-search"><Icon name="search" size="small"/><input aria-label="搜索项目" placeholder="搜索项目…" value={query} onChange={e=>setQuery(e.target.value)}/></label><button className="pop-option" onClick={()=>chooseProject(null)}><span className="option-icon"><Icon name="chat"/></span><span className="option-info"><strong>个人对话</strong><small>不归入任何项目</small></span>{!project&&<Icon name="check" className="checkmark"/>}</button>{projects.filter(p=>p.name.toLowerCase().includes(query.toLowerCase())).map(p=><button className="pop-option" key={p.id} onClick={()=>chooseProject(p.id)}><span className="option-icon"><Icon name="folder"/></span><span className="option-info"><strong>{p.name}</strong><small>{chats.filter(c=>c.project===p.id).length} 个对话</small></span>{project===p.id&&<Icon name="check" className="checkmark"/>}</button>)}{query&&!projects.some(p=>p.name.toLowerCase().includes(query.toLowerCase()))&&<div className="pop-bottom-note">没有找到匹配项目，可以新建一个。</div>}<hr className="pop-divider"/><button className="pop-foot" onClick={()=>{setModal({type:'project-form',fromComposer:true});setPop(null);}}><Icon name="plus" size="small"/>新建项目</button></div>;
if(pop==='plugins')return <div className="popover context-pop plugins-pop" role="dialog" aria-label="选择插件"><div className="popover-title">本次对话的插件 <small>已启用 {enabled.length} 个</small></div><div className="popover-sub">按需要启用，为对话添加工作能力</div>{plugins.map(p=><button key={p.id} className="pop-option" role="switch" aria-checked={enabled.includes(p.id)} aria-label={p.name} onClick={()=>togglePlugin(p.id)}><span className={'option-icon '+p.color}><Icon name={p.icon}/></span><span className="option-info"><strong>{p.name}</strong><small>{p.desc}</small></span><span className={'toggle '+(enabled.includes(p.id)?'on':'')}></span></button>)}<hr className="pop-divider"/><button className="pop-foot" onClick={()=>navigate('plugins')}><Icon name="grid" size="small"/>管理所有插件<Icon name="right" size="small"/></button><div className="pop-bottom-note">启用后可由助手按需调用，不会立即执行。</div></div>;
return null;
}
function composer(){return <div className={'composer-wrap '+(popUp?'pop-up':'')} style={{'--popover-max-height':popMaxHeight+'px'}} ref={composerRef}>
<div className="composer">{files.length>0&&<div className="attachment-strip">{files.map((f,i)=><div className="attachment-chip" key={i}><Icon name="doc" size="small"/><span>{f.name}</span><IB icon="close" label={'移除 '+f.name} onClick={()=>setFiles(all=>all.filter((_,j)=>i!==j))}/></div>)}</div>}
<textarea aria-label="描述你的工作或问题" ref={textarea} rows="3" value={draft} onChange={e=>setDraft(e.target.value)} placeholder={view==='conversation'?'继续对话…':'描述你的工作，或从一个想法开始…'} onKeyDown={e=>{if(e.key==='Enter'&&!e.shiftKey&&!e.nativeEvent.isComposing){e.preventDefault();send();}}}></textarea>
<div className="composer-tools"><div className="composer-left"><IB icon="plus" label="添加文件" onClick={()=>uploadRef.current?.click()}/><button className="text-control approval" onClick={()=>togglePop('approval')} aria-expanded={pop==='approval'}><Icon name="shield"/>{approval}<Icon name="down" size="small"/></button></div><div className="composer-right"><div className="harness-selector"><button className="text-control harness-button" title="切换 Harness" aria-label={'选择 Harness：'+currentHarness.name} aria-haspopup="listbox" aria-expanded={pop==='harness'} onClick={()=>togglePop('harness')}><Icon name="terminal"/><span>{currentHarness.name}</span><Icon name="down" size="small"/></button>{pop==='harness'&&<div className="popover harness-pop" role="listbox" aria-label="选择 Harness"><div className="popover-title">选择 Harness <small>演示选项</small></div>{harnessOptions.map(h=><button key={h.id} className="pop-option" role="option" aria-selected={harness===h.id} onClick={()=>{setHarness(h.id);if(!h.models.includes(model))setModel(h.models[0]);setPop(null);}}><span className="option-icon"><Icon name="terminal"/></span><span className="option-info"><strong>{h.name}</strong></span>{harness===h.id&&<Icon name="check" className="checkmark"/>}</button>)}</div>}</div><button className="text-control model-button" aria-label={'选择模型：'+model} onClick={()=>togglePop('model')} aria-expanded={pop==='model'}><Icon name="model" className="model-icon"/>{model}<span style={{color:'#a1a1ac'}}>高</span><Icon name="down" size="small"/></button><button className="send-button" disabled={!draft.trim()||!!activeChat&&pending===activeChat} onClick={send} aria-label="发送消息" title="发送消息"><Icon name="arrow"/></button></div></div>
{pop==='model'&&<div className="popover tools-pop" role="dialog" aria-label="选择模型"><div className="popover-title">选择模型 <small>演示选项</small></div>{currentHarness.models.map(m=><button className="pop-option" key={m} onClick={()=>{setModel(m);setPop(null);}}><Icon name="model"/><span className="option-info"><strong>{m}</strong></span>{model===m&&<Icon name="check" className="checkmark"/>}</button>)}</div>}
{pop==='approval'&&<div className="popover approval-pop" role="dialog" aria-label="批准方式"><div className="popover-title">批准方式</div>{['自动批准','执行前询问'].map(a=><button className="pop-option" key={a} onClick={()=>{setApproval(a);setPop(null);}}><Icon name="shield"/><span className="option-info"><strong>{a}</strong><small>{a==='自动批准'?'按已有授权继续处理工作':'执行操作前先确认'}</small></span>{approval===a&&<Icon name="check" className="checkmark"/>}</button>)}</div>}
</div>
<div className="context-bar"><button className={'context-select '+(pop==='environment'?'open':'')} onClick={()=>togglePop('environment')} aria-label={'选择环境：'+currentEnv.name} aria-expanded={pop==='environment'}><Icon name={currentEnv.icon}/>{currentEnv.name}<Icon name="down" size="small"/></button><span className="context-divider"></span><button className={'context-select '+(project?'selected ':'')+(pop==='project'?'open':'')} onClick={()=>togglePop('project')} aria-label={'选择项目：'+(currentProject?.name||'未选择')} aria-expanded={pop==='project'}><Icon name="folder"/><span style={{overflow:'hidden',textOverflow:'ellipsis'}}>{currentProject?.name||'选择项目'}</span><Icon name="down" size="small"/></button><span className="context-divider"></span><button className={'context-select '+(pop==='plugins'?'open':'')} onClick={()=>togglePop('plugins')} aria-label="选择插件" aria-expanded={pop==='plugins'}><Icon name="plug"/>插件{enabled.length>0&&<span className="plugin-count">{enabled.length}</span>}<Icon name="down" size="small"/></button><span className="context-spacer"></span><span className="connection"><span className="status-dot"></span>准备就绪</span>{selectorPopover()}</div>
</div>;}
return <div className="app" style={{'--sidebar-width':t.sidebarWidth+'px'}} data-screen-label={view==='home'?'缺省对话首页':view==='conversation'?'对话进行中':view==='projects'?'项目管理':view==='project'?'项目详情':view==='automation'?'自动化':'插件管理'}>
{sidebar&&<div className="mobile-shade" onClick={()=>setSidebar(false)}></div>}
<aside className={'sidebar '+(!sidebar?'closed':'')} aria-label="侧边栏" inert={!sidebar?'':undefined}><div className="side-inner">
<div className="windowbar"><div className="traffic" aria-hidden="true"><i></i><i></i><i></i></div><IB icon="panel" label="收起侧边栏" onClick={()=>setSidebar(false)}/></div>
<div className="workspace-title"><h2>工作台</h2><IB icon="search" label="搜索对话与项目" onClick={()=>{setQuery('');setModal({type:'search'});}}/></div>
<nav className="side-nav" aria-label="主要导航"><button className={'nav-button '+(view==='home'&&!project?'active':'')} onClick={()=>newChat()}><Icon name="compose"/>新对话<span className="shortcut">⌘ N</span></button><button className={'nav-button '+(view==='automation'?'active':'')} onClick={()=>navigate('automation')}><Icon name="automation"/>自动化</button><button className={'nav-button '+(view==='plugins'?'active':'')} onClick={()=>navigate('plugins')}><Icon name="plug"/>插件</button></nav>
<div className="side-scroll"><section className="side-section"><div className="section-label"><button className="label-button" onClick={()=>setPersonalOpen(x=>!x)} aria-expanded={personalOpen}>个人对话<Icon name={personalOpen?'down':'right'} size="small"/></button><IB icon="plus" label="新建个人对话" onClick={()=>newChat()}/></div>{personalOpen&&<><div className="side-subtle">最近</div>{chats.filter(c=>!c.project).slice(0,7).map(c=>chatRow(c))}{chats.filter(c=>!c.project).length>7&&<button className="all-projects" onClick={()=>{setQuery('');setModal({type:'search',onlyPersonal:true});}}>查看全部对话<Icon name="right" size="small"/></button>}</>}</section>
<section className="side-section"><div className="section-label"><button className="label-button" onClick={()=>setProjectsOpen(x=>!x)} aria-expanded={projectsOpen}>项目<Icon name={projectsOpen?'down':'right'} size="small"/></button><IB icon="plus" label="新建项目" onClick={()=>setModal({type:'project-form'})}/></div>{projectsOpen&&<>{projects.map(p=><div key={p.id}><div className={'project-row '+(project===p.id?'selected':'')}><button className="expander" aria-label={(expanded.includes(p.id)?'收起':'展开')+p.name} aria-expanded={expanded.includes(p.id)} onClick={()=>setExpanded(all=>all.includes(p.id)?all.filter(x=>x!==p.id):[...all,p.id])}><Icon name={expanded.includes(p.id)?'down':'right'} size="small"/></button><button className="project-main" onClick={()=>openProject(p.id)}><Icon name="folder"/><span className="project-name" title={p.name}>{p.name}</span><span className={"project-scope "+p.scope}>{p.scope==='team'?'团队':'个人'}</span></button><span className="count">{chats.filter(c=>c.project===p.id).length}</span></div>{expanded.includes(p.id)&&<div className="project-children">{chats.filter(c=>c.project===p.id).map(c=>chatRow(c,true))}{!chats.some(c=>c.project===p.id)&&<div className="project-empty">还没有对话</div>}</div>}</div>)}<button className="all-projects" onClick={()=>navigate('projects')}>查看全部项目<Icon name="right" size="small"/></button></>}</section></div>
<div className="side-footer"><div className="avatar">我</div><div className="account"><div className="account-name">个人空间</div><div className="account-sub">让想法开始发生</div></div><IB icon="settings" label="设置" onClick={()=>setModal({type:'settings'})}/></div>
</div></aside>
<main className="main"><header className="topbar">{!sidebar&&<IB icon="panel" label="展开侧边栏" onClick={()=>setSidebar(true)}/>}<div className="breadcrumb"><span>{view==='projects'||view==='project'?'项目':view==='plugins'||view==='automation'?'工作台':currentProject?.name||'个人对话'}</span><span className="slash">/</span><span className="current">{view==='home'?'新对话':view==='conversation'?currentChat?.title:view==='projects'?'全部项目':view==='project'?currentProject?.name:view==='automation'?'自动化':'插件'}</span></div><div className="topbar-right"><span className="prototype-badge">交互原型</span><IB icon="style" label="调整设计风格" onClick={()=>window.postMessage({type:'miaoda:tweaks:activate'},'*')}/></div></header>
<div className="main-stage">
{view==='home'&&<div className="home-content"><div className="hero"><h1>{currentProject?'继续推进「'+currentProject.name+'」':t.headline}</h1><p>{currentProject?'带上项目上下文，从下一件事开始。':'想清楚一件事，完成一项工作。'}</p></div>{composer()}<section className="suggestions" aria-label="工作内容建议"><div className="suggestion-heading"><span>{currentProject?'从这个项目开始':'可以从这些工作开始'}</span><button onClick={()=>setSuggestionSet(s=>(s+1)%suggestionSets.length)}><Icon name="refresh" size="small"/>换一组</button></div><div className={'suggestion-list '+(t.suggestionStyle==='双列卡片'?'cards':'')}>{(currentProject?[{icon:'folder',text:'梳理这个项目的进展与下一步行动',prompt:'帮我梳理「'+currentProject.name+'」项目的当前进展，明确下一步行动和优先级。'},...suggestionSets[suggestionSet].slice(0,3)]:suggestionSets[suggestionSet]).map((s,i)=><button className="suggestion" key={i} onClick={()=>{setDraft(s.prompt);textarea.current?.focus();}}><Icon name={s.icon}/><span>{s.text}</span><Icon name="enter" className="insert-arrow"/></button>)}</div></section></div>}
{view==='conversation'&&currentChat&&<div className="conversation-content"><div className="message-list" ref={messagesRef} role="log" aria-live="polite">{currentChat.messages.length===0&&<><div className="message user">{currentChat.title}</div><div className="message assistant"><div className="assistant-label"><Icon name="chat" size="small"/>助手 · 演示回复</div>这是「{currentChat.title}」的示例历史对话。你可以在下方继续输入，体验对话归档与上下文选择。</div></>}{currentChat.messages.map((m,i)=><div className={'message '+m.role} key={i}>{m.role==='assistant'&&<div className="assistant-label"><Icon name="chat" size="small"/>助手 · 演示回复</div>}{m.text}{m.files?.length>0&&<div className="message-context"><Icon name="paperclip" size="small"/>{m.files.join('、')}</div>}{m.role==='assistant'&&m.context&&<div className="message-context">{m.context.harness&&<span>{m.context.harness} · {m.context.model}</span>}<span>{m.context.env}</span><span>{m.context.project}</span><span>{m.context.plugins}</span></div>}</div>)}{pending===activeChat&&<div className="typing-dots" aria-label="正在生成演示回复"><i></i><i></i><i></i></div>}</div>{composer()}</div>}
{view==='projects'&&<div className="management"><div className="management-header"><div><h1>把相关工作，放在一起。</h1><p>以项目组织对话，让每次开始都接得上。</p></div><button className="primary-button" onClick={()=>setModal({type:'project-form'})}><Icon name="plus" size="small"/>新建项目</button></div><label className="pop-search management-search"><Icon name="search" size="small"/><input aria-label="查找项目" value={filter} onChange={e=>setFilter(e.target.value)} placeholder="查找项目…"/></label><div className="project-grid">{projects.filter(p=>(p.name+p.description).toLowerCase().includes(filter.toLowerCase())).map(p=><div className="project-card" key={p.id} role="button" tabIndex="0" aria-label={'打开项目 '+p.name} onClick={()=>openProject(p.id)} onKeyDown={e=>{if(e.target===e.currentTarget&&(e.key==='Enter'||e.key===' ')){e.preventDefault();openProject(p.id);}}}><div className="project-card-icon"><Icon name="folder"/></div><h3>{p.name}</h3><p>{p.description||'把相关的对话与工作整理在这里。'}</p><footer><span>{chats.filter(c=>c.project===p.id).length} 个对话</span><span>·</span><span>{envs.find(e=>e.id===p.env)?.name}</span></footer><div className="card-menu"><IB icon="more" label={'编辑项目 '+p.name} onClick={e=>{e.stopPropagation();setModal({type:'project-form',project:p});}}/></div></div>)}{!filter&&<button className="project-card project-add" onClick={()=>setModal({type:'project-form'})}><Icon name="plus" size="large"/><span>开始一个新项目</span></button>}</div>{filter&&!projects.some(p=>(p.name+p.description).toLowerCase().includes(filter.toLowerCase()))&&<div className="empty-state"><Icon name="search"/><p>没有找到「{filter}」相关的项目</p><button className="secondary-button" onClick={()=>setFilter('')}>清空搜索</button></div>}</div>}
{view==='project'&&currentProject&&<div className="management"><div className="management-header"><div><h1>{currentProject.name}</h1><p>{currentProject.description||'在这里继续你的项目工作。'}</p></div><button className="primary-button" onClick={()=>newChat(project)}><Icon name="plus" size="small"/>新对话</button></div><div className="project-detail-summary"><span><Icon name="chat" size="small"/>{chats.filter(c=>c.project===project).length} 个对话</span><span><Icon name="monitor" size="small"/>默认环境：{envs.find(e=>e.id===currentProject.env)?.name}</span><button onClick={()=>setModal({type:'project-form',project:currentProject})}>编辑项目</button></div><div className="heading-small">项目对话</div>{chats.filter(c=>c.project===project).map(c=><div className="detail-chat" key={c.id}><button onClick={()=>openChat(c)}><Icon name="chat"/><span><strong>{c.title}</strong><small>{c.time} · {c.messages.length>0?c.messages.length+' 条消息':'示例对话'}</small></span></button><IB icon="more" label={'管理对话：'+c.title} onClick={()=>setModal({type:'chat-edit',chat:c})}/></div>)}{!chats.some(c=>c.project===project)&&<div className="empty-state"><Icon name="chat" size="large"/><p>项目准备好了，开始第一个对话吧。</p><button className="secondary-button" onClick={()=>newChat(project)}>开始对话</button></div>}</div>}
{view==='automation'&&<div className="management"><div className="management-header"><div><h1>自动化</h1><p>让重复的工作按计划完成。</p></div></div><div className="empty-state"><Icon name="automation" size="large"/><p>暂无自动化任务</p><span style={{fontSize:11,color:'#aaaab5'}}>定时任务与自动执行的工作会显示在这里。</span></div></div>}
{view==='plugins'&&<ExtensionsManager state={extensionState} setState={setExtensionState} notify={notify} onCompose={prompt=>{newChat();setDraft(prompt);setTimeout(()=>textarea.current?.focus(),50);}}/>}
</div><footer className="bottom-hint">{view==='home'||view==='conversation'?<><kbd>Enter</kbd> 发送 · <kbd>Shift + Enter</kbd> 换行<span style={{padding:'0 8px',color:'#d2d2d8'}}>·</span>演示模式</>:'交互原型 · 数据仅保存在当前浏览器'}</footer>
</main>
<input type="file" multiple ref={uploadRef} style={{display:'none'}} onChange={e=>{setFiles(all=>[...all,...Array.from(e.target.files)]);e.target.value='';notify('已添加文件，仅展示文件名，不会上传');}}/>
{modal&&<div className="modal-backdrop" onClick={e=>{if(e.target===e.currentTarget)setModal(null);}}><div className={'modal '+(modal.type==='search'?'search-modal':'')} role="dialog" aria-modal="true" aria-label={modal.type==='search'?'搜索对话与项目':modal.type==='project-form'?'项目设置':modal.type==='chat-edit'?'管理对话':'设置'} ref={modalRef}>
{modal.type==='project-form'&&<ProjectForm key={modal.project?.id||'new'} data={modal.project} projects={projects} close={()=>setModal(null)} onSave={data=>{const id=data.id||'project-'+Date.now();setProjects(all=>data.id?all.map(p=>p.id===id?{...data,id}:p):[...all,{...data,id}]);setModal(null);if(!data.id){setProject(id);setExpanded(all=>[...all,id]);if(modal.fromComposer){if(activeChat)setChats(all=>all.map(c=>c.id===activeChat?{...c,project:id}:c));}else{setView('project');setActiveChat(null);}closeMobile();}notify(data.id?'项目已更新':'项目已创建');}}/>}
{modal.type==='search'&&<><div className="global-search-input"><Icon name="search"/><input autoFocus aria-label="搜索对话与项目" placeholder="搜索对话与项目…" value={query} onChange={e=>setQuery(e.target.value)}/><IB icon="close" label="关闭搜索" onClick={()=>setModal(null)}/></div><div className="search-results">{!modal.onlyPersonal&&projects.filter(p=>p.name.toLowerCase().includes(query.toLowerCase())).map(p=><button key={p.id} className="search-result" onClick={()=>{openProject(p.id);setModal(null);}}><Icon name="folder"/><span>{p.name}<small>项目</small></span></button>)}{chats.filter(c=>(!modal.onlyPersonal||!c.project)&&c.title.toLowerCase().includes(query.toLowerCase())).map(c=><button key={c.id} className="search-result" onClick={()=>openChat(c)}><Icon name="chat"/><span>{c.title}<small>{projects.find(p=>p.id===c.project)?.name||'个人对话'} · {c.time}</small></span></button>)}{!chats.some(c=>(!modal.onlyPersonal||!c.project)&&c.title.toLowerCase().includes(query.toLowerCase()))&&(!projects.some(p=>p.name.toLowerCase().includes(query.toLowerCase()))||modal.onlyPersonal)&&<div className="empty-state"><p>没有找到相关对话或项目</p></div>}</div></>}
{modal.type==='chat-edit'&&<ChatEdit chat={modal.chat} projects={projects} close={()=>setModal(null)} onSave={data=>{setChats(all=>all.map(c=>c.id===data.id?{...c,...data}:c));if(activeChat===data.id)setProject(data.project);setModal(null);notify('对话已更新');}} onDelete={()=>{setChats(all=>all.filter(c=>c.id!==modal.chat.id));if(activeChat===modal.chat.id)newChat();setModal(null);notify('示例对话已删除');}}/>}
{modal.type==='settings'&&<><div className="modal-header"><h2>原型设置</h2><IB icon="close" label="关闭设置" onClick={()=>setModal(null)}/></div><p className="modal-description">这是交互设计演示，环境、模型与插件均为示例。</p><div className="settings-line"><span>数据保存</span><small>当前浏览器</small></div><div className="settings-line"><span>界面风格</span><button className="secondary-button" onClick={()=>{setModal(null);window.postMessage({type:'miaoda:tweaks:activate'},'*');}}>调整风格</button></div><div className="settings-line"><span>键盘快捷键</span><small>⌘ K 搜索 · ⌘ N 新对话</small></div><p className="modal-description" style={{margin:'20px 0 0'}}>可体验项目管理、上下文选择与模拟对话。不会连接真实 AI、执行工作或上传文件。</p></>}
</div></div>}
{toast&&<div className="toast" role="status"><Icon name="check" size="small"/>{toast}</div>}
<TweaksPanel title="风格"><TweakSection label="布局"/><TweakSlider label="侧边栏宽度" value={t.sidebarWidth} min={235} max={300} unit="px" onChange={v=>setTweak('sidebarWidth',v)}/><TweakRadio label="工作建议" value={t.suggestionStyle} options={['轻量列表','双列卡片']} onChange={v=>setTweak('suggestionStyle',v)}/><TweakSection label="首页文案"/><TweakText label="欢迎语" value={t.headline} onChange={v=>setTweak('headline',v)}/></TweaksPanel>
</div>;
}
function ProjectForm({data,projects,close,onSave}){
const [name,setName]=useState(data?.name||''),[description,setDescription]=useState(data?.description||''),[env,setEnv]=useState(data?.env||'local'),[scope,setScope]=useState(data?.scope||'personal'),[error,setError]=useState('');
return <form onSubmit={e=>{e.preventDefault();if(!name.trim()){setError('请输入项目名称');return;}if(projects.some(p=>p.id!==data?.id&&p.name===name.trim())){setError('已有同名项目，请换一个名称');return;}onSave({id:data?.id,name:name.trim(),description:description.trim(),env,scope});}}><div className="modal-header"><h2>{data?'编辑项目':'新建项目'}</h2><IB icon="close" label="关闭项目设置" onClick={close}/></div><p className="modal-description">把相关对话放在一起，持续推进一项工作。</p><label className="field">项目名称<input autoFocus value={name} maxLength="24" placeholder="例如：产品设计、个人网站" onChange={e=>{setName(e.target.value);setError('');}}/></label><label className="field">描述（选填）<textarea rows="2" value={description} maxLength="100" placeholder="这个项目主要做什么？" onChange={e=>setDescription(e.target.value)}></textarea></label><label className="field">项目归属<select value={scope} onChange={e=>setScope(e.target.value)}><option value="personal">个人</option><option value="team">团队</option></select></label><label className="field">默认环境<select value={env} onChange={e=>setEnv(e.target.value)}><option value="local">本机</option><option value="cloud">云端工作区</option></select><small>从项目中新建对话时使用，也可以在对话中调整。</small></label>{error&&<p className="form-error" role="alert">{error}</p>}<div className="modal-actions"><button type="button" className="secondary-button" onClick={close}>取消</button><button type="submit" className="primary-button" disabled={!name.trim()}>{data?'保存修改':'创建项目'}</button></div></form>;
}
function ChatEdit({chat,projects,close,onSave,onDelete}){
const [title,setTitle]=useState(chat.title),[project,setProject]=useState(chat.project||''),[confirm,setConfirm]=useState(false);
return <form onSubmit={e=>{e.preventDefault();if(title.trim())onSave({id:chat.id,title:title.trim(),project:project||null});}}><div className="modal-header"><h2>管理对话</h2><IB icon="close" label="关闭对话管理" onClick={close}/></div><p className="modal-description">修改名称，或把对话整理到相关项目。</p><label className="field">对话名称<input value={title} autoFocus maxLength="60" onChange={e=>setTitle(e.target.value)}/></label><label className="field">所属项目<select value={project} onChange={e=>setProject(e.target.value)}><option value="">个人对话</option>{projects.map(p=><option key={p.id} value={p.id}>{p.name}</option>)}</select></label>{confirm&&<p className="form-error">删除后无法恢复此示例对话。确定删除？</p>}<div className="modal-actions"><button type="button" className={confirm?'primary-button danger-button':'secondary-button'} style={{marginRight:'auto'}} onClick={()=>confirm?onDelete():setConfirm(true)}>{confirm?'确认删除':'删除对话'}</button><button type="button" className="secondary-button" onClick={close}>取消</button><button type="submit" className="primary-button" disabled={!title.trim()}>保存</button></div></form>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
