@evandu/super-pi 0.1.102 → 0.1.104

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
1
  {
2
- "hash": "800da36325ed46c54e1bfd7c3f280a527dd42baefe4912bcef71b0c75531458b",
3
- "recordedAt": "2026-10-21T07:04:30.046Z"
2
+ "hash": "4582a716a58c8faa033bdb18d408a0ec92e70f6d6f1fdf03d539047e71587d04",
3
+ "recordedAt": "2026-10-21T08:51:19.447Z"
4
4
  }
@@ -1,8 +1,8 @@
1
1
  {
2
- "hash": "71617a24a30a02b331cd6be0d75fae095a2c73c7e8f909b627ff06de444ff9ee",
2
+ "hash": "b8deff533f84ef04833217897a1670a38ace531494f44b488da818198f0e74cb",
3
3
  "files": {
4
4
  "src/client/App.tsx": "f03962cde47d8c98ec59dad6ea365ee140d7768157b5894cbc6a4d377648bae7",
5
- "src/client/FilesPanel.tsx": "17c3e7ef10914a8836c209bc47a51ad06d73025feb755ee8f68bba8f79ce6d16",
5
+ "src/client/FilesPanel.tsx": "bdee7302c732c5fe3347f6a58a4770d5e390178417add191e04205aa745935d9",
6
6
  "src/client/GitPanel.tsx": "c44f3c73936af73f436ec6a8cd9ee50f5367dd6887f881fa133ec101fb99bbac",
7
7
  "src/client/HelpWindow.tsx": "6bc90fd5dc0783b16a07b75be64cc70df21991c1f16ea1fcd894f815e7b403f1",
8
8
  "src/client/KnowledgeFilePicker.tsx": "a97ef9004088e3b7e6f90159c04fd8ff151f068e04fd35a58c435f40f46af461",
@@ -24,5 +24,5 @@
24
24
  "src/client/types.ts": "c2d8b16e056173a3951d87f3e7b63dcc53dc9178a5e73fa3e48b2d1ffd4e93c7",
25
25
  "src/client/vite-env.d.ts": "65996936fbb042915f7b74a200fcdde7e410f32a669b1ab9597cfaa4b0faddb5"
26
26
  },
27
- "recordedAt": "2026-10-21T08:26:29.793Z"
27
+ "recordedAt": "2026-10-21T15:04:31.114Z"
28
28
  }
@@ -17,12 +17,12 @@ Error generating stack: `+e.message+`
17
17
  `))});continue}if(e.includes(`|`)&&r+1<t.length&&Te.test(t[r+1])&&t[r+1].includes(`-`)){let i=Ne(e),a=Ne(t[r+1]).map(Pe),o=[];for(r+=2;r<t.length&&t[r].trim()&&t[r].includes(`|`);)o.push(Fe(Ne(t[r]),i.length).map(e=>be(e))),r+=1;n.push({type:`table`,head:i.map(e=>be(e)),align:a,rows:o});continue}if(Se.test(e)||Ce.test(e)){let e=Me(t,r);n.push(e.node),r=e.next;continue}let s=[];for(;r<t.length&&t[r].trim()&&!je(t[r]);)s.push(t[r]),r+=1;s.length||(s.push(t[r]),r+=1),n.push({type:`paragraph`,children:be(s.join(`
18
18
  `))})}return n}function je(e){return xe.test(e)||k.test(e)||A.test(e)||we.test(e)||Se.test(e)||Ce.test(e)||/^\s*\$\$/.test(e)}function Me(e,t){let n=Ce.test(e[t]),r=n?Ce.exec(e[t]):Se.exec(e[t]),i=r[1].length,a=[],o=t;for(;o<e.length;){let t=Se.exec(e[o]),r=Ce.exec(e[o]),s=n?r:t;if(!s)break;let c=s[1].length;if(c<i)break;let l=[s[s.length-1]];for(o+=1;o<e.length;){let n=e[o];if(!n.trim()){let t=e[o+1]??``;if(!t.trim()||t.search(/\S/)<=c)break;l.push(``),o+=1;continue}let i=n.search(/\S/);if(i===0&&t||i===0&&r)break;if(i>c)l.push(n.slice(Math.min(i,c+2))),o+=1;else if(i===c){if(je(n))break;l.push(n),o+=1}else break}a.push(Ae(l.join(`
19
19
  `)))}return{node:{type:`list`,ordered:n,start:n?Number(r[2]):1,items:a},next:o}}function Ne(e){let t=e.trim();t.startsWith(`|`)&&(t=t.slice(1)),t.endsWith(`|`)&&!t.endsWith(`\\|`)&&(t=t.slice(0,-1));let n=[],r=``;for(let e=0;e<t.length;e+=1)t[e]===`\\`&&t[e+1]===`|`?(r+=`|`,e+=1):t[e]===`|`?(n.push(r.trim()),r=``):r+=t[e];return n.push(r.trim()),n}function Pe(e){let t=e.startsWith(`:`),n=e.endsWith(`:`);return t&&n?`center`:n?`right`:t?`left`:null}function Fe(e,t){let n=e.slice(0,t);for(;n.length<t;)n.push(``);return n}var Ie={alpha:`α`,beta:`β`,gamma:`γ`,delta:`δ`,epsilon:`ε`,varepsilon:`ε`,zeta:`ζ`,eta:`η`,theta:`θ`,vartheta:`ϑ`,iota:`ι`,kappa:`κ`,lambda:`λ`,mu:`μ`,nu:`ν`,xi:`ξ`,pi:`π`,varpi:`ϖ`,rho:`ρ`,varrho:`ϱ`,sigma:`σ`,varsigma:`ς`,tau:`τ`,upsilon:`υ`,phi:`φ`,varphi:`ϕ`,chi:`χ`,psi:`ψ`,omega:`ω`,Gamma:`Γ`,Delta:`Δ`,Theta:`Θ`,Lambda:`Λ`,Xi:`Ξ`,Pi:`Π`,Sigma:`Σ`,Upsilon:`Υ`,Phi:`Φ`,Psi:`Ψ`,Omega:`Ω`},Le={times:`×`,div:`÷`,pm:`±`,mp:`∓`,cdot:`·`,ast:`∗`,star:`⋆`,circ:`∘`,bullet:`∙`,leq:`≤`,le:`≤`,geq:`≥`,ge:`≥`,neq:`≠`,ne:`≠`,approx:`≈`,sim:`∼`,simeq:`≃`,equiv:`≡`,propto:`∝`,ll:`≪`,gg:`≫`,in:`∈`,notin:`∉`,ni:`∋`,subset:`⊂`,subseteq:`⊆`,supset:`⊃`,supseteq:`⊇`,cup:`∪`,cap:`∩`,setminus:`∖`,emptyset:`∅`,varnothing:`∅`,aleph:`ℵ`,hbar:`ℏ`,ell:`ℓ`,Re:`ℜ`,Im:`ℑ`,sum:`∑`,prod:`∏`,coprod:`∐`,int:`∫`,iint:`∬`,iiint:`∭`,oint:`∮`,bigcup:`⋃`,bigcap:`⋂`,to:`→`,rightarrow:`→`,leftarrow:`←`,gets:`←`,leftrightarrow:`↔`,Rightarrow:`⇒`,Leftarrow:`⇐`,Leftrightarrow:`⇔`,mapsto:`↦`,longrightarrow:`⟶`,longleftarrow:`⟵`,uparrow:`↑`,downarrow:`↓`,ldots:`…`,cdots:`⋯`,vdots:`⋮`,ddots:`⋱`,dots:`…`,therefore:`∴`,because:`∵`,angle:`∠`,triangle:`△`,perp:`⊥`,parallel:`∥`,degree:`°`,prime:`′`,lbrace:`{`,rbrace:`}`,lbrack:`[`,rbrack:`]`,langle:`⟨`,rangle:`⟩`,lfloor:`⌊`,rfloor:`⌋`,lceil:`⌈`,rceil:`⌉`,vert:`|`,Vert:`‖`,backslash:`\\`,oplus:`⊕`,ominus:`⊖`,otimes:`⊗`,oslash:`⊘`,odot:`⊙`,dagger:`†`,ddagger:`‡`,quad:` `,qquad:` `,thinspace:` `,enspace:` `,negthinspace:``},Re=new Set(`arcsin.arccos.arctan.sinh.cosh.tanh.cot.sec.csc.sin.cos.tan.log.ln.lg.exp.lim.limsup.liminf.max.min.det.dim.ker.deg.gcd.hom.arg.Pr.sup.inf`.split(`.`)),ze={C:`ℂ`,H:`ℍ`,N:`ℕ`,P:`ℙ`,Q:`ℚ`,R:`ℝ`,Z:`ℤ`},Be={B:`ℬ`,E:`ℰ`,F:`ℱ`,H:`ℋ`,I:`ℐ`,L:`ℒ`,M:`ℳ`,R:`ℛ`,e:`ℯ`,g:`ℊ`,o:`ℴ`},Ve={mathbb:e=>ze[e]??e.toUpperCase(),mathcal:e=>Be[e]??e.toUpperCase(),mathscr:e=>Be[e]??e.toUpperCase()},He=new Set([`left`,`right`,`big`,`Big`,`bigg`,`Bigg`,`bigl`,`bigr`,`Bigl`,`Bigr`,`biggl`,`biggr`,`Biggl`,`Biggr`,`middle`,`displaystyle`,`textstyle`,`scriptstyle`,`limits`,`nolimits`,`nonumber`,`notag`,`label`]),Ue={hat:`^`,widehat:`^`,bar:`‾`,overline:`‾`,tilde:`~`,widetilde:`~`,vec:`→`,dot:`·`,ddot:`··`,check:`ˇ`,breve:`˘`,acute:`´`,grave:"`"},We=new Set([`(`,`)`,`[`,`]`,`|`,`/`,`<`,`>`]),Ge=new Set([`pmatrix`,`bmatrix`,`Bmatrix`,`vmatrix`,`Vmatrix`,`matrix`,`array`,`cases`,`aligned`,`align`,`alignat`,`split`,`gather`,`gathered`,`equation`,`smallmatrix`,`dcases`,`subarray`]);function Ke(e){let t=[],n=``,r=()=>{n&&=(t.push(...Xe(n)),``)},i=e=>{r(),t.push(e)};for(let a=0;a<e.length;){let o=e[a];if(o===`\\`){let t=a,r=/^\\([a-zA-Z]+|.)/.exec(e.slice(a));if(!r){n+=o,a+=1;continue}let s=r[1];if(a+=r[0].length,s===`\\`){i({type:`math-row-break`});continue}if(s===`{`||s===`}`){n+=s;continue}if(s===`,`||s===`;`||s===`:`||s===`!`||s===`&`)continue;if(s===`%`){for(;a<e.length&&e[a]!==`
20
- `;)a+=1;continue}if(He.has(s))continue;if(s===`begin`){let n=le(e,a),r=n.content,o=`\\end{${r}}`,s=e.indexOf(o,n.next),c=s===-1?e.length:s+o.length;i({type:`math-unsupported`,value:e.slice(t,c),reason:`\\begin{${r}} 不支持`}),a=c;continue}if(s===`frac`||s===`dfrac`||s===`tfrac`||s===`cfrac`){let t=Je(e,a),n=Je(e,t.next);a=n.next,i({type:`math-fraction`,numerator:Ke(t.content),denominator:Ke(n.content)});continue}if(s===`sqrt`){let t,n=a;if(e[n]===`[`){let r=qe(e,n);t=Ke(r.content),n=r.next}let r=Je(e,n);a=r.next,i({type:`math-root`,...t?{index:t}:{},radicand:Ke(r.content)});continue}if(s===`text`||s===`mathrm`||s===`textrm`||s===`mathbf`||s===`mathit`||s===`mathsf`||s===`mathtt`||s===`operatorname`||s===`textbf`){let t=le(e,a);a=t.next,i({type:`math-text`,value:t.content});continue}if(s in Ve){let t=Je(e,a);a=t.next;let n=Ve[s];i({type:`math-text`,value:[...t.content].map(n).join(``)});continue}if(s in Ue){let t=Je(e,a);a=t.next,i({type:`math-text`,value:`${Ze(Ke(t.content))}${Ue[s]}`});continue}if(s in Ie){i({type:`math-identifier`,value:Ie[s]});continue}if(s in Le){let e=Le[s];/^\d/.test(e)?i({type:`math-number`,value:e}):/^[A-Za-z]/.test(e)?i({type:`math-text`,value:e}):i({type:`math-operator`,value:e});continue}if(Re.has(s)){i({type:`math-function`,value:s});continue}if(Ge.has(s))continue;i({type:`math-unsupported`,value:`\\${s}`,reason:`\\${s} 不支持`})}else if(o===`{`){let n=le(e,a);t.push(...Ke(n.content)),a=n.next}else if(o===`}`||o===`&`||o===`$`)a+=1;else if(o===`^`||o===`_`){r();let n=Je(e,a+1);a=n.next,Ye(t,o===`^`?`sup`:`sub`,Ke(n.content))}else We.has(o)?(i({type:`math-text`,value:o}),a+=1):(n+=o,a+=1)}return r(),t}function qe(e,t){let n=e.indexOf(`]`,t+1);return n===-1?{content:``,next:e.length}:{content:e.slice(t+1,n),next:n+1}}function Je(e,t){let n=e[t];if(n===void 0)return{content:``,next:t};if(n===`{`)return le(e,t);let r=/^\\[a-zA-Z]+/.exec(e.slice(t));return r?{content:r[0],next:t+r[0].length}:{content:n,next:t+1}}function Ye(e,t,n){let r=e.pop();if(r){if(r.type===`math-script`)t===`sub`&&!r.sub?e.push({...r,sub:n}):t===`sup`&&!r.sup?e.push({...r,sup:n}):e.push(r);else if(r.type===`math-identifier`||r.type===`math-text`){let i=[...r.value],a=i.pop()??``;i.length&&e.push({type:r.type,value:i.join(``)}),e.push({type:`math-script`,base:[{type:`math-identifier`,value:a}],...t===`sub`?{sub:n}:{sup:n}})}else e.push({type:`math-script`,base:[r],...t===`sub`?{sub:n}:{sup:n}})}}function Xe(e){let t=[],n=0;for(;n<e.length;){let r=e.slice(n),i=/^\d+(?:\.\d+)?/.exec(r);if(i){t.push({type:`math-number`,value:i[0]}),n+=i[0].length;continue}let a=/^[A-Za-z]+/.exec(r);if(a){t.push(Re.has(a[0])?{type:`math-function`,value:a[0]}:{type:`math-identifier`,value:a[0]}),n+=a[0].length;continue}let o=/^[,;:!.'’|()\-+=<>/*]+/.exec(r);o?(t.push({type:`math-operator`,value:o[0]}),n+=o[0].length):(t.push({type:`math-text`,value:r[0]}),n+=1)}return t.reduce((e,t)=>{let n=e[e.length-1];return n?.type===`math-operator`&&t.type===`math-operator`?n.value+=t.value:e.push({...t}),e},[])}function Ze(e){return e.map(e=>{switch(e.type){case`math-text`:case`math-number`:case`math-identifier`:case`math-operator`:case`math-function`:return e.value;case`math-script`:return`${Ze(e.base)}${e.sub?`_{${Ze(e.sub)}}`:``}${e.sup?`^{${Ze(e.sup)}}`:``}`;case`math-fraction`:return`\\frac{${Ze(e.numerator)}}{${Ze(e.denominator)}}`;case`math-root`:return`\\sqrt{${Ze(e.radicand)}}`;case`math-row-break`:return`\\\\`;case`math-unsupported`:return e.value;default:return``}}).join(``)}function Qe({source:e}){let t=(0,p.useMemo)(()=>Ae(e),[e]);return(0,m.jsx)(`div`,{className:`md`,"data-md":`1`,children:t.map((e,t)=>tt(e,t))})}function $e({source:e}){let t=(0,p.useMemo)(()=>be(e),[e]);return(0,m.jsx)(`span`,{className:`md md-inline`,children:t.map((e,t)=>rt(e,t))})}function et(e){return ne(e).map((e,t)=>e.kind===`plain`?(0,m.jsx)(`span`,{children:e.text},t):(0,m.jsx)(`span`,{className:`code-token-${e.kind}`,children:e.text},t))}function tt(e,t){switch(e.type){case`paragraph`:return(0,m.jsx)(`p`,{className:`md-p`,children:e.children.map((e,t)=>rt(e,t))},t);case`heading`:{let n=`h${Math.min(6,Math.max(1,e.level))}`;return(0,m.jsx)(n,{className:`md-h md-h${e.level}`,children:e.children.map((e,t)=>rt(e,t))},t)}case`code-block`:return(0,m.jsx)(`pre`,{className:`md-pre`,"data-md-lang":e.lang||void 0,children:(0,m.jsx)(`code`,{className:`md-code`,children:et(e.value)})},t);case`list`:{let n=e.items.map((e,t)=>(0,m.jsx)(`li`,{className:`md-li`,children:e.map((e,t)=>tt(e,t))},t));return e.ordered?(0,m.jsx)(`ol`,{className:`md-ol`,start:e.start>1?e.start:void 0,children:n},t):(0,m.jsx)(`ul`,{className:`md-ul`,children:n},t)}case`blockquote`:return(0,m.jsx)(`blockquote`,{className:`md-quote`,children:e.children.map((e,t)=>tt(e,t))},t);case`rule`:return(0,m.jsx)(`hr`,{className:`md-rule`},t);case`table`:return(0,m.jsx)(`div`,{className:`md-table-wrap`,children:(0,m.jsxs)(`table`,{className:`md-table`,children:[(0,m.jsx)(`thead`,{children:(0,m.jsx)(`tr`,{children:e.head.map((t,n)=>(0,m.jsx)(`th`,{style:nt(e.align[n]),children:t.map((e,t)=>rt(e,t))},n))})}),(0,m.jsx)(`tbody`,{children:e.rows.map((t,n)=>(0,m.jsx)(`tr`,{children:t.map((t,n)=>(0,m.jsx)(`td`,{style:nt(e.align[n]),children:t.map((e,t)=>rt(e,t))},n))},n))})]})},t);case`math-block`:return(0,m.jsx)(it,{source:e.source},t);default:return null}}var nt=e=>e?{textAlign:e}:void 0;function rt(e,t){switch(e.type){case`text`:return(0,m.jsx)(p.Fragment,{children:e.value},t);case`code`:return(0,m.jsx)(`code`,{className:`md-code-inline`,children:e.value},t);case`strong`:return(0,m.jsx)(`strong`,{className:`md-strong`,children:e.children.map((e,t)=>rt(e,t))},t);case`em`:return(0,m.jsx)(`em`,{className:`md-em`,children:e.children.map((e,t)=>rt(e,t))},t);case`del`:return(0,m.jsx)(`del`,{className:`md-del`,children:e.children.map((e,t)=>rt(e,t))},t);case`break`:return(0,m.jsx)(`br`,{className:`md-break`},t);case`math`:return(0,m.jsx)(at,{source:e.source},t);case`link`:{let n=ae(e.href);return n?(0,m.jsx)(`a`,{className:`md-link`,href:n,title:e.title,target:`_blank`,rel:`noopener noreferrer nofollow`,children:e.children.map((e,t)=>rt(e,t))},t):(0,m.jsx)(p.Fragment,{children:e.children.map((e,t)=>rt(e,t))},t)}default:return null}}function it({source:e}){let t=(0,p.useMemo)(()=>Ke(e),[e]);return(0,m.jsx)(`div`,{className:`md-math md-math-block`,"data-md-math":`block`,"data-md-math-source":e,children:t.map((e,t)=>ot(e,t))})}function at({source:e}){let t=(0,p.useMemo)(()=>Ke(e),[e]);return(0,m.jsx)(`span`,{className:`md-math md-math-inline`,"data-md-math":`inline`,"data-md-math-source":e,children:t.map((e,t)=>ot(e,t))})}function ot(e,t){switch(e.type){case`math-text`:return(0,m.jsx)(`span`,{className:`md-m-text`,children:e.value},t);case`math-number`:return(0,m.jsx)(`span`,{className:`md-m-number`,children:e.value},t);case`math-identifier`:return(0,m.jsx)(`i`,{className:`md-m-var`,children:e.value},t);case`math-operator`:return(0,m.jsx)(`span`,{className:`md-m-op`,children:e.value},t);case`math-function`:return(0,m.jsx)(`span`,{className:`md-m-fn`,children:e.value},t);case`math-row-break`:return(0,m.jsx)(`br`,{className:`md-m-rowbreak`},t);case`math-unsupported`:return(0,m.jsx)(`span`,{className:`md-m-unsupported`,"data-md-math-unsupported":`1`,title:e.reason,children:e.value},t);case`math-script`:return(0,m.jsxs)(`span`,{className:`md-m-script`,children:[(0,m.jsx)(`span`,{className:`md-m-base`,children:e.base.map((e,t)=>ot(e,t))}),e.sup&&(0,m.jsx)(`sup`,{className:`md-m-sup`,children:e.sup.map((e,t)=>ot(e,t))}),e.sub&&(0,m.jsx)(`sub`,{className:`md-m-sub`,children:e.sub.map((e,t)=>ot(e,t))})]},t);case`math-fraction`:return(0,m.jsxs)(`span`,{className:`md-m-frac`,children:[(0,m.jsx)(`span`,{className:`md-m-num`,children:e.numerator.map((e,t)=>ot(e,t))}),(0,m.jsx)(`span`,{className:`md-m-den`,children:e.denominator.map((e,t)=>ot(e,t))})]},t);case`math-root`:return(0,m.jsxs)(`span`,{className:`md-m-root`,children:[(0,m.jsx)(`span`,{className:`md-m-radical`,children:`√`}),e.index&&(0,m.jsx)(`sup`,{className:`md-m-index`,children:e.index.map((e,t)=>ot(e,t))}),(0,m.jsx)(`span`,{className:`md-m-body`,children:e.radicand.map((e,t)=>ot(e,t))})]},t);default:return null}}var st=[[`help`,`Show help`],[`model`,`Choose model`],[`settings`,`Open settings`],[`clear`,`Clear session`],[`compact`,`Compact context`],[`reload`,`Reload agents`],[`new`,`New session`],[`fork`,`Fork session`],[`resume`,`Resume session`],[`stop`,`Stop run`],[`skill`,`Workspace skill`]].map(([e,t])=>({id:e,label:`/${e} — ${t}`,description:t})),ct=e=>e.normalize(`NFKD`).replace(/\p{M}/gu,``).toLowerCase();function lt(e,t){let n=ct(t.trim());return e.examples?.find(t=>ct(t).includes(n)&&t!==e.id)??e.id}function ut(e,t){let n=ct(t.trim());return n?e.filter(e=>ct(e.id).includes(n)||ct(e.label).includes(n)||e.examples?.some(e=>ct(e).includes(n))):e}function dt({query:e,anchorRect:t,command:n,workspaceId:r,onSelect:i,onClose:a}){let[o,s]=(0,p.useState)(0),[c,l]=(0,p.useState)(st),u=(0,p.useRef)(null),d=(0,p.useRef)([]);(0,p.useEffect)(()=>{let e=!1;if(r)return fetch(`/api/memory?workspaceId=${encodeURIComponent(r)}`).then(e=>e.ok?e.json():void 0).then(t=>{if(e||!t?.entries?.skills)return;let n=t.entries.skills.map(e=>{let t=e.content.split(/\r?\n/,1)[0].replace(/^#+\s*/,``).trim()||`unnamed-skill`,n=t.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff]+/g,`-`).replace(/^-|-$/g,``)||`unnamed-skill`;return{id:`skill:${n}`,label:t,description:`Workspace skill`,examples:[`skill:${n}`]}});l([...st.filter(e=>e.id!==`skill`),...n])}).catch(()=>{}),()=>{e=!0}},[r]);let f=ut([{id:`start`,label:`start — 打开浏览器`,description:`在浏览器中打开 Super Pi 控制台`,action:()=>n({type:`super_pi.start`})},{id:`restart`,label:`restart — 重启 Super Pi 服务`,description:`停掉并重新启动服务,加载磁盘上的新代码`,action:()=>n({type:`super_pi.restart`})},{id:`update`,label:`update — 更新 Super Pi`,description:`运行 npm install -g @evandu/super-pi 然后重启`,action:()=>n({type:`super_pi.update`})},...c],e),h=f.length===0?-1:Math.min(Math.max(o,0),f.length-1),g=e=>s(t=>f.length?Math.min(Math.max(t+e,0),f.length-1):0);(0,p.useEffect)(()=>{let e=e=>{u.current?.contains(e.target)||a()};return window.addEventListener(`mousedown`,e),()=>window.removeEventListener(`mousedown`,e)},[a]),(0,p.useEffect)(()=>{s(0)},[e]),(0,p.useLayoutEffect)(()=>{h>=0&&d.current[h]?.scrollIntoView({block:`nearest`})},[h]);let _={position:`fixed`,left:Math.max(8,Math.min(t.left,window.innerWidth-360)),bottom:Math.max(8,window.innerHeight-t.top+6),zIndex:200};return(0,m.jsx)(`div`,{ref:u,className:`slash-menu`,style:_,role:`listbox`,"aria-label":`斜杠命令`,onKeyDown:e=>{if(e.key===`Escape`)e.preventDefault(),a();else if(e.key===`ArrowDown`)e.preventDefault(),g(1);else if(e.key===`ArrowUp`)e.preventDefault(),g(-1);else if(e.key===`Home`)e.preventDefault(),s(0);else if(e.key===`End`)e.preventDefault(),s(f.length-1);else if(e.key===`Enter`){e.preventDefault();let t=f[h];if(!t)return;t.action?(t.action(),a()):i(t)}},children:f.length===0?(0,m.jsxs)(`div`,{className:`slash-menu-empty`,children:[`没有匹配「`,e,`」的命令`]}):f.map((e,t)=>(0,m.jsxs)(`button`,{ref:e=>{d.current[t]=e},type:`button`,role:`option`,"aria-selected":t===h,className:`slash-menu-item${t===h?` selected`:``}`,"data-slash-command":e.id,onClick:()=>{e.action?(e.action(),a()):i(e)},onMouseEnter:()=>s(t),children:[(0,m.jsxs)(`span`,{className:`slash-menu-item-id`,children:[`/`,e.id]}),(0,m.jsx)(`span`,{className:`slash-menu-item-sep`,"aria-hidden":`true`,children:` — `}),(0,m.jsx)(`span`,{className:`slash-menu-item-label`,children:e.label}),e.examples?.length?(0,m.jsx)(`span`,{className:`slash-menu-item-examples`,children:e.examples.slice(0,2).join(` · `)}):null]},e.id))})}var ft=4e3,pt=` `;function mt(e){return ne(e).map((e,t)=>e.kind===`plain`?(0,m.jsx)(`span`,{children:e.text},t):(0,m.jsx)(`span`,{className:`code-token-${e.kind}`,children:e.text},t))}function ht({value:e,onChange:t,onKeyDown:n,path:r}){return(0,m.jsxs)(`div`,{className:`file-textarea-highlight-wrap`,children:[(0,m.jsx)(`div`,{className:`file-textarea-highlight`,"data-file-text-highlight":r,"aria-hidden":`true`,children:mt(e)}),(0,m.jsx)(`textarea`,{className:`file-textarea`,"data-file-text":r,value:e,wrap:`off`,spellCheck:!1,autoCapitalize:`off`,autoCorrect:`off`,"aria-label":`编辑 ${r}`,onChange:e=>t(e.target.value),onKeyDown:n})]})}var gt=class extends Error{status;body;constructor(e,t){super(t.error||`HTTP ${e}`),this.status=e,this.body=t,this.name=`ApiError`}},_t=15e3,vt=e=>new gt(408,{error:`请求超时:${_t/1e3} 秒内服务端没有响应`,code:`timeout`,details:{path:e}});async function yt(e,t){let n=new AbortController,r=setTimeout(()=>n.abort(),_t);try{let r=await fetch(e,{...t,signal:n.signal}),i;try{i=await r.json()}catch{i=void 0}if(!r.ok)throw new gt(r.status,i??{error:`HTTP ${r.status}`});return i}catch(t){throw n.signal.aborted?vt(e):t}finally{clearTimeout(r)}}var bt=e=>e instanceof gt?e:new gt(0,{error:e instanceof Error?e.message:`读取失败`}),xt=e=>encodeURIComponent(e),St=(e,t)=>`/api/files?workspaceId=${xt(e)}&path=${xt(t)}`,Ct=(e,t)=>`/api/file?workspaceId=${xt(e)}&path=${xt(t)}`,wt=(e,t)=>`/api/file?workspaceId=${xt(e)}&path=${xt(t)}&meta=1`,Tt=(e,t)=>`/api/files?workspaceId=${xt(e)}&q=${xt(t)}`,Et=e=>e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(1)} MB`,Dt=e=>{let t=new Date(e);return Number.isNaN(t.getTime())?``:new Date(e).toLocaleTimeString(`zh-CN`,{hour:`2-digit`,minute:`2-digit`})};function Ot({workspaceId:e,entryPath:t,name:n,listings:r,expanded:i,toggle:a,openPaths:o,onOpen:s,onError:c,depth:l}){let u=r[t],d=i.has(t);return(0,p.useEffect)(()=>{if(!d||r[t]!==void 0)return;let n=!0;return yt(St(e,t)).then(e=>{n&&(r[t]=e)}).catch(e=>{n&&(r[t]={error:bt(e)})}),()=>{n=!1}},[d,t,e,r]),(0,m.jsxs)(`li`,{className:`file-node`,"data-file-dir":t,children:[(0,m.jsxs)(`button`,{type:`button`,className:`file-row file-row-dir`,"data-file-dir-toggle":t,"aria-expanded":d,style:{paddingLeft:`${8+l*13}px`},onClick:()=>a(t),children:[(0,m.jsx)(`span`,{className:`file-caret`,"aria-hidden":`true`,children:d?`▾`:`▸`}),(0,m.jsx)(`span`,{className:`file-name`,children:n})]}),d&&(0,m.jsx)(`ul`,{className:`file-children`,children:u===void 0?(0,m.jsx)(`li`,{className:`file-row-loading`,"data-file-loading":t,children:`读取中…`}):`error`in u?(0,m.jsx)(`li`,{className:`file-row-error`,"data-file-error":t,children:u.error.message}):u.entries.length===0?(0,m.jsx)(`li`,{className:`file-row-empty`,"data-file-empty":t,children:`空目录`}):(0,m.jsxs)(m.Fragment,{children:[u.truncated&&(0,m.jsx)(`li`,{className:`file-row-empty`,"data-file-truncated":t,children:`条目过多,只显示前一部分`}),u.entries.map(t=>t.kind===`directory`?(0,m.jsx)(Ot,{workspaceId:e,entryPath:t.path,name:t.name,listings:r,expanded:i,toggle:a,openPaths:o,onOpen:s,onError:c,depth:l+1},t.path):(0,m.jsx)(kt,{entry:t,depth:l+1,openPaths:o,onOpen:s},t.path))]})})]})}function kt({entry:e,depth:t,openPaths:n,onOpen:r}){if(e.kind===`symlink`)return(0,m.jsx)(`li`,{className:`file-node`,"data-file-entry":e.path,"data-file-outside":e.path,children:(0,m.jsxs)(`button`,{type:`button`,className:`file-row file-row-file`,disabled:!0,title:`${e.path} 是一个指向 workspace 之外的符号链接,不能打开`,style:{paddingLeft:`${8+t*13}px`},children:[(0,m.jsx)(`span`,{className:`file-caret`,"aria-hidden":`true`}),(0,m.jsx)(`span`,{className:`file-name`,children:e.name}),(0,m.jsx)(`span`,{className:`file-badge`,"data-file-outside-badge":`1`,children:`外部链接`})]})});let i=n.has(e.path);return(0,m.jsx)(`li`,{className:`file-node`,"data-file-entry":e.path,"data-file-row-open":i?`1`:`0`,children:(0,m.jsxs)(`button`,{type:`button`,className:`file-row file-row-file`,"data-file-open":e.path,disabled:!e.text,title:e.text?i?`${e.path}(已经打开,点击切换到它)`:e.path:`${e.name} 是二进制文件,无法以文本打开`,style:{paddingLeft:`${8+t*13}px`},onClick:()=>r(e),children:[(0,m.jsx)(`span`,{className:`file-caret`,"aria-hidden":`true`}),(0,m.jsx)(`span`,{className:`file-name`,children:e.name}),i&&(0,m.jsx)(`span`,{className:`file-open-mark`,"data-file-open-mark":e.path,title:`已经打开`,"aria-label":`已经打开`,children:`●`}),!e.text&&(0,m.jsx)(`span`,{className:`file-badge`,"data-file-binary":`1`,"aria-label":`不支持的文件类型`,children:`不支持`})]})})}function At({file:e,state:t,message:n,external:r,onChange:i,onSave:a,onReload:o,onOverwrite:s}){let[c,l]=(0,p.useState)(!0),u=/\.md$/i.test(e.path);return(0,m.jsxs)(`div`,{className:`file-editor`,"data-file-editor":e.path,"data-save-state":t,children:[(0,m.jsxs)(`div`,{className:`file-editor-head`,children:[(0,m.jsx)(`span`,{className:`file-editor-path`,title:e.path,children:e.path}),(0,m.jsxs)(`span`,{className:`file-editor-flags`,children:[u&&(0,m.jsx)(`button`,{type:`button`,className:`file-preview-toggle`,onClick:()=>l(e=>!e),children:c?`原文`:`预览`}),e.dirty&&(0,m.jsx)(`span`,{className:`file-dirty`,"data-file-dirty":`1`,children:`未保存`}),(0,m.jsx)(`button`,{type:`button`,className:`file-save`,"data-file-save":e.path,onClick:a,disabled:t===`saving`,children:t===`saving`?`保存中…`:`保存`})]})]}),r&&t!==`conflict`&&(0,m.jsxs)(`div`,{className:`file-conflict`,"data-file-external":e.path,role:`alert`,children:[(0,m.jsx)(`p`,{children:`这个文件在磁盘上已经被改过了,你的修改还没有覆盖它。`}),(0,m.jsxs)(`div`,{className:`file-conflict-actions`,children:[(0,m.jsx)(`button`,{type:`button`,"data-file-reload":e.path,onClick:o,children:`丢掉我的修改,读磁盘版本`}),(0,m.jsx)(`button`,{type:`button`,"data-file-overwrite":e.path,onClick:s,children:`仍然覆盖磁盘`})]})]}),t===`conflict`&&(0,m.jsxs)(`div`,{className:`file-conflict`,"data-file-conflict":e.path,role:`alert`,children:[(0,m.jsx)(`p`,{children:`保存被拒绝:这个文件在磁盘上已经被改过了,覆盖会丢掉对方的改动。`}),(0,m.jsxs)(`div`,{className:`file-conflict-actions`,children:[(0,m.jsx)(`button`,{type:`button`,"data-file-reload":e.path,onClick:o,children:`丢掉我的修改,读磁盘版本`}),(0,m.jsx)(`button`,{type:`button`,"data-file-overwrite":e.path,onClick:s,children:`仍然覆盖磁盘`})]})]}),n&&t!==`conflict`&&!r&&(0,m.jsx)(`p`,{className:`file-editor-message file-message-${t}`,"data-file-message":t,role:`status`,children:n}),u&&c?(0,m.jsx)(`div`,{className:`file-markdown-preview`,children:(0,m.jsx)(Qe,{source:e.content})}):(0,m.jsx)(ht,{value:e.content,onChange:i,onKeyDown:e=>{let t=e.currentTarget;if(e.nativeEvent.isComposing||e.keyCode===229)return;if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()===`s`){e.preventDefault(),a();return}if(e.key!==`Tab`)return;e.preventDefault();let{selectionStart:n,selectionEnd:r,value:o}=t;if(!o.slice(n,r).includes(`
20
+ `;)a+=1;continue}if(He.has(s))continue;if(s===`begin`){let n=le(e,a),r=n.content,o=`\\end{${r}}`,s=e.indexOf(o,n.next),c=s===-1?e.length:s+o.length;i({type:`math-unsupported`,value:e.slice(t,c),reason:`\\begin{${r}} 不支持`}),a=c;continue}if(s===`frac`||s===`dfrac`||s===`tfrac`||s===`cfrac`){let t=Je(e,a),n=Je(e,t.next);a=n.next,i({type:`math-fraction`,numerator:Ke(t.content),denominator:Ke(n.content)});continue}if(s===`sqrt`){let t,n=a;if(e[n]===`[`){let r=qe(e,n);t=Ke(r.content),n=r.next}let r=Je(e,n);a=r.next,i({type:`math-root`,...t?{index:t}:{},radicand:Ke(r.content)});continue}if(s===`text`||s===`mathrm`||s===`textrm`||s===`mathbf`||s===`mathit`||s===`mathsf`||s===`mathtt`||s===`operatorname`||s===`textbf`){let t=le(e,a);a=t.next,i({type:`math-text`,value:t.content});continue}if(s in Ve){let t=Je(e,a);a=t.next;let n=Ve[s];i({type:`math-text`,value:[...t.content].map(n).join(``)});continue}if(s in Ue){let t=Je(e,a);a=t.next,i({type:`math-text`,value:`${Ze(Ke(t.content))}${Ue[s]}`});continue}if(s in Ie){i({type:`math-identifier`,value:Ie[s]});continue}if(s in Le){let e=Le[s];/^\d/.test(e)?i({type:`math-number`,value:e}):/^[A-Za-z]/.test(e)?i({type:`math-text`,value:e}):i({type:`math-operator`,value:e});continue}if(Re.has(s)){i({type:`math-function`,value:s});continue}if(Ge.has(s))continue;i({type:`math-unsupported`,value:`\\${s}`,reason:`\\${s} 不支持`})}else if(o===`{`){let n=le(e,a);t.push(...Ke(n.content)),a=n.next}else if(o===`}`||o===`&`||o===`$`)a+=1;else if(o===`^`||o===`_`){r();let n=Je(e,a+1);a=n.next,Ye(t,o===`^`?`sup`:`sub`,Ke(n.content))}else We.has(o)?(i({type:`math-text`,value:o}),a+=1):(n+=o,a+=1)}return r(),t}function qe(e,t){let n=e.indexOf(`]`,t+1);return n===-1?{content:``,next:e.length}:{content:e.slice(t+1,n),next:n+1}}function Je(e,t){let n=e[t];if(n===void 0)return{content:``,next:t};if(n===`{`)return le(e,t);let r=/^\\[a-zA-Z]+/.exec(e.slice(t));return r?{content:r[0],next:t+r[0].length}:{content:n,next:t+1}}function Ye(e,t,n){let r=e.pop();if(r){if(r.type===`math-script`)t===`sub`&&!r.sub?e.push({...r,sub:n}):t===`sup`&&!r.sup?e.push({...r,sup:n}):e.push(r);else if(r.type===`math-identifier`||r.type===`math-text`){let i=[...r.value],a=i.pop()??``;i.length&&e.push({type:r.type,value:i.join(``)}),e.push({type:`math-script`,base:[{type:`math-identifier`,value:a}],...t===`sub`?{sub:n}:{sup:n}})}else e.push({type:`math-script`,base:[r],...t===`sub`?{sub:n}:{sup:n}})}}function Xe(e){let t=[],n=0;for(;n<e.length;){let r=e.slice(n),i=/^\d+(?:\.\d+)?/.exec(r);if(i){t.push({type:`math-number`,value:i[0]}),n+=i[0].length;continue}let a=/^[A-Za-z]+/.exec(r);if(a){t.push(Re.has(a[0])?{type:`math-function`,value:a[0]}:{type:`math-identifier`,value:a[0]}),n+=a[0].length;continue}let o=/^[,;:!.'’|()\-+=<>/*]+/.exec(r);o?(t.push({type:`math-operator`,value:o[0]}),n+=o[0].length):(t.push({type:`math-text`,value:r[0]}),n+=1)}return t.reduce((e,t)=>{let n=e[e.length-1];return n?.type===`math-operator`&&t.type===`math-operator`?n.value+=t.value:e.push({...t}),e},[])}function Ze(e){return e.map(e=>{switch(e.type){case`math-text`:case`math-number`:case`math-identifier`:case`math-operator`:case`math-function`:return e.value;case`math-script`:return`${Ze(e.base)}${e.sub?`_{${Ze(e.sub)}}`:``}${e.sup?`^{${Ze(e.sup)}}`:``}`;case`math-fraction`:return`\\frac{${Ze(e.numerator)}}{${Ze(e.denominator)}}`;case`math-root`:return`\\sqrt{${Ze(e.radicand)}}`;case`math-row-break`:return`\\\\`;case`math-unsupported`:return e.value;default:return``}}).join(``)}function Qe({source:e}){let t=(0,p.useMemo)(()=>Ae(e),[e]);return(0,m.jsx)(`div`,{className:`md`,"data-md":`1`,children:t.map((e,t)=>tt(e,t))})}function $e({source:e}){let t=(0,p.useMemo)(()=>be(e),[e]);return(0,m.jsx)(`span`,{className:`md md-inline`,children:t.map((e,t)=>rt(e,t))})}function et(e){return ne(e).map((e,t)=>e.kind===`plain`?(0,m.jsx)(`span`,{children:e.text},t):(0,m.jsx)(`span`,{className:`code-token-${e.kind}`,children:e.text},t))}function tt(e,t){switch(e.type){case`paragraph`:return(0,m.jsx)(`p`,{className:`md-p`,children:e.children.map((e,t)=>rt(e,t))},t);case`heading`:{let n=`h${Math.min(6,Math.max(1,e.level))}`;return(0,m.jsx)(n,{className:`md-h md-h${e.level}`,children:e.children.map((e,t)=>rt(e,t))},t)}case`code-block`:return(0,m.jsx)(`pre`,{className:`md-pre`,"data-md-lang":e.lang||void 0,children:(0,m.jsx)(`code`,{className:`md-code`,children:et(e.value)})},t);case`list`:{let n=e.items.map((e,t)=>(0,m.jsx)(`li`,{className:`md-li`,children:e.map((e,t)=>tt(e,t))},t));return e.ordered?(0,m.jsx)(`ol`,{className:`md-ol`,start:e.start>1?e.start:void 0,children:n},t):(0,m.jsx)(`ul`,{className:`md-ul`,children:n},t)}case`blockquote`:return(0,m.jsx)(`blockquote`,{className:`md-quote`,children:e.children.map((e,t)=>tt(e,t))},t);case`rule`:return(0,m.jsx)(`hr`,{className:`md-rule`},t);case`table`:return(0,m.jsx)(`div`,{className:`md-table-wrap`,children:(0,m.jsxs)(`table`,{className:`md-table`,children:[(0,m.jsx)(`thead`,{children:(0,m.jsx)(`tr`,{children:e.head.map((t,n)=>(0,m.jsx)(`th`,{style:nt(e.align[n]),children:t.map((e,t)=>rt(e,t))},n))})}),(0,m.jsx)(`tbody`,{children:e.rows.map((t,n)=>(0,m.jsx)(`tr`,{children:t.map((t,n)=>(0,m.jsx)(`td`,{style:nt(e.align[n]),children:t.map((e,t)=>rt(e,t))},n))},n))})]})},t);case`math-block`:return(0,m.jsx)(it,{source:e.source},t);default:return null}}var nt=e=>e?{textAlign:e}:void 0;function rt(e,t){switch(e.type){case`text`:return(0,m.jsx)(p.Fragment,{children:e.value},t);case`code`:return(0,m.jsx)(`code`,{className:`md-code-inline`,children:e.value},t);case`strong`:return(0,m.jsx)(`strong`,{className:`md-strong`,children:e.children.map((e,t)=>rt(e,t))},t);case`em`:return(0,m.jsx)(`em`,{className:`md-em`,children:e.children.map((e,t)=>rt(e,t))},t);case`del`:return(0,m.jsx)(`del`,{className:`md-del`,children:e.children.map((e,t)=>rt(e,t))},t);case`break`:return(0,m.jsx)(`br`,{className:`md-break`},t);case`math`:return(0,m.jsx)(at,{source:e.source},t);case`link`:{let n=ae(e.href);return n?(0,m.jsx)(`a`,{className:`md-link`,href:n,title:e.title,target:`_blank`,rel:`noopener noreferrer nofollow`,children:e.children.map((e,t)=>rt(e,t))},t):(0,m.jsx)(p.Fragment,{children:e.children.map((e,t)=>rt(e,t))},t)}default:return null}}function it({source:e}){let t=(0,p.useMemo)(()=>Ke(e),[e]);return(0,m.jsx)(`div`,{className:`md-math md-math-block`,"data-md-math":`block`,"data-md-math-source":e,children:t.map((e,t)=>ot(e,t))})}function at({source:e}){let t=(0,p.useMemo)(()=>Ke(e),[e]);return(0,m.jsx)(`span`,{className:`md-math md-math-inline`,"data-md-math":`inline`,"data-md-math-source":e,children:t.map((e,t)=>ot(e,t))})}function ot(e,t){switch(e.type){case`math-text`:return(0,m.jsx)(`span`,{className:`md-m-text`,children:e.value},t);case`math-number`:return(0,m.jsx)(`span`,{className:`md-m-number`,children:e.value},t);case`math-identifier`:return(0,m.jsx)(`i`,{className:`md-m-var`,children:e.value},t);case`math-operator`:return(0,m.jsx)(`span`,{className:`md-m-op`,children:e.value},t);case`math-function`:return(0,m.jsx)(`span`,{className:`md-m-fn`,children:e.value},t);case`math-row-break`:return(0,m.jsx)(`br`,{className:`md-m-rowbreak`},t);case`math-unsupported`:return(0,m.jsx)(`span`,{className:`md-m-unsupported`,"data-md-math-unsupported":`1`,title:e.reason,children:e.value},t);case`math-script`:return(0,m.jsxs)(`span`,{className:`md-m-script`,children:[(0,m.jsx)(`span`,{className:`md-m-base`,children:e.base.map((e,t)=>ot(e,t))}),e.sup&&(0,m.jsx)(`sup`,{className:`md-m-sup`,children:e.sup.map((e,t)=>ot(e,t))}),e.sub&&(0,m.jsx)(`sub`,{className:`md-m-sub`,children:e.sub.map((e,t)=>ot(e,t))})]},t);case`math-fraction`:return(0,m.jsxs)(`span`,{className:`md-m-frac`,children:[(0,m.jsx)(`span`,{className:`md-m-num`,children:e.numerator.map((e,t)=>ot(e,t))}),(0,m.jsx)(`span`,{className:`md-m-den`,children:e.denominator.map((e,t)=>ot(e,t))})]},t);case`math-root`:return(0,m.jsxs)(`span`,{className:`md-m-root`,children:[(0,m.jsx)(`span`,{className:`md-m-radical`,children:`√`}),e.index&&(0,m.jsx)(`sup`,{className:`md-m-index`,children:e.index.map((e,t)=>ot(e,t))}),(0,m.jsx)(`span`,{className:`md-m-body`,children:e.radicand.map((e,t)=>ot(e,t))})]},t);default:return null}}var st=[[`help`,`Show help`],[`model`,`Choose model`],[`settings`,`Open settings`],[`clear`,`Clear session`],[`compact`,`Compact context`],[`reload`,`Reload agents`],[`new`,`New session`],[`fork`,`Fork session`],[`resume`,`Resume session`],[`stop`,`Stop run`],[`skill`,`Workspace skill`]].map(([e,t])=>({id:e,label:`/${e} — ${t}`,description:t})),ct=e=>e.normalize(`NFKD`).replace(/\p{M}/gu,``).toLowerCase();function lt(e,t){let n=ct(t.trim());return e.examples?.find(t=>ct(t).includes(n)&&t!==e.id)??e.id}function ut(e,t){let n=ct(t.trim());return n?e.filter(e=>ct(e.id).includes(n)||ct(e.label).includes(n)||e.examples?.some(e=>ct(e).includes(n))):e}function dt({query:e,anchorRect:t,command:n,workspaceId:r,onSelect:i,onClose:a}){let[o,s]=(0,p.useState)(0),[c,l]=(0,p.useState)(st),u=(0,p.useRef)(null),d=(0,p.useRef)([]);(0,p.useEffect)(()=>{let e=!1;if(r)return fetch(`/api/memory?workspaceId=${encodeURIComponent(r)}`).then(e=>e.ok?e.json():void 0).then(t=>{if(e||!t?.entries?.skills)return;let n=t.entries.skills.map(e=>{let t=e.content.split(/\r?\n/,1)[0].replace(/^#+\s*/,``).trim()||`unnamed-skill`,n=t.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff]+/g,`-`).replace(/^-|-$/g,``)||`unnamed-skill`;return{id:`skill:${n}`,label:t,description:`Workspace skill`,examples:[`skill:${n}`]}});l([...st.filter(e=>e.id!==`skill`),...n])}).catch(()=>{}),()=>{e=!0}},[r]);let f=ut([{id:`start`,label:`start — 打开浏览器`,description:`在浏览器中打开 Super Pi 控制台`,action:()=>n({type:`super_pi.start`})},{id:`restart`,label:`restart — 重启 Super Pi 服务`,description:`停掉并重新启动服务,加载磁盘上的新代码`,action:()=>n({type:`super_pi.restart`})},{id:`update`,label:`update — 更新 Super Pi`,description:`运行 npm install -g @evandu/super-pi 然后重启`,action:()=>n({type:`super_pi.update`})},...c],e),h=f.length===0?-1:Math.min(Math.max(o,0),f.length-1),g=e=>s(t=>f.length?Math.min(Math.max(t+e,0),f.length-1):0);(0,p.useEffect)(()=>{let e=e=>{u.current?.contains(e.target)||a()};return window.addEventListener(`mousedown`,e),()=>window.removeEventListener(`mousedown`,e)},[a]),(0,p.useEffect)(()=>{s(0)},[e]),(0,p.useLayoutEffect)(()=>{h>=0&&d.current[h]?.scrollIntoView({block:`nearest`})},[h]);let _={position:`fixed`,left:Math.max(8,Math.min(t.left,window.innerWidth-360)),bottom:Math.max(8,window.innerHeight-t.top+6),zIndex:200};return(0,m.jsx)(`div`,{ref:u,className:`slash-menu`,style:_,role:`listbox`,"aria-label":`斜杠命令`,onKeyDown:e=>{if(e.key===`Escape`)e.preventDefault(),a();else if(e.key===`ArrowDown`)e.preventDefault(),g(1);else if(e.key===`ArrowUp`)e.preventDefault(),g(-1);else if(e.key===`Home`)e.preventDefault(),s(0);else if(e.key===`End`)e.preventDefault(),s(f.length-1);else if(e.key===`Enter`){e.preventDefault();let t=f[h];if(!t)return;t.action?(t.action(),a()):i(t)}},children:f.length===0?(0,m.jsxs)(`div`,{className:`slash-menu-empty`,children:[`没有匹配「`,e,`」的命令`]}):f.map((e,t)=>(0,m.jsxs)(`button`,{ref:e=>{d.current[t]=e},type:`button`,role:`option`,"aria-selected":t===h,className:`slash-menu-item${t===h?` selected`:``}`,"data-slash-command":e.id,onClick:()=>{e.action?(e.action(),a()):i(e)},onMouseEnter:()=>s(t),children:[(0,m.jsxs)(`span`,{className:`slash-menu-item-id`,children:[`/`,e.id]}),(0,m.jsx)(`span`,{className:`slash-menu-item-sep`,"aria-hidden":`true`,children:` — `}),(0,m.jsx)(`span`,{className:`slash-menu-item-label`,children:e.label}),e.examples?.length?(0,m.jsx)(`span`,{className:`slash-menu-item-examples`,children:e.examples.slice(0,2).join(` · `)}):null]},e.id))})}var ft=4e3,pt=` `;function mt(e){return ne(e).map((e,t)=>e.kind===`plain`?(0,m.jsx)(`span`,{children:e.text},t):(0,m.jsx)(`span`,{className:`code-token-${e.kind}`,children:e.text},t))}function ht({value:e,onChange:t,onKeyDown:n,path:r}){return(0,m.jsxs)(`div`,{className:`file-textarea-highlight-wrap`,children:[(0,m.jsx)(`div`,{className:`file-textarea-highlight`,"data-file-text-highlight":r,"aria-hidden":`true`,children:mt(e)}),(0,m.jsx)(`textarea`,{className:`file-textarea`,"data-file-text":r,value:e,wrap:`off`,spellCheck:!1,autoCapitalize:`off`,autoCorrect:`off`,"aria-label":`编辑 ${r}`,onChange:e=>t(e.target.value),onKeyDown:n})]})}var gt=class extends Error{status;body;constructor(e,t){super(t.error||`HTTP ${e}`),this.status=e,this.body=t,this.name=`ApiError`}},_t=15e3,vt=e=>new gt(408,{error:`请求超时:${_t/1e3} 秒内服务端没有响应`,code:`timeout`,details:{path:e}});async function yt(e,t){let n=new AbortController,r=setTimeout(()=>n.abort(),_t);try{let r=await fetch(e,{...t,signal:n.signal}),i;try{i=await r.json()}catch{i=void 0}if(!r.ok)throw new gt(r.status,i??{error:`HTTP ${r.status}`});return i}catch(t){throw n.signal.aborted?vt(e):t}finally{clearTimeout(r)}}var bt=e=>e instanceof gt?e:new gt(0,{error:e instanceof Error?e.message:`读取失败`}),xt=e=>encodeURIComponent(e),St=(e,t)=>`/api/files?workspaceId=${xt(e)}&path=${xt(t)}`,Ct=(e,t)=>`/api/file?workspaceId=${xt(e)}&path=${xt(t)}`,wt=(e,t)=>`/api/file?workspaceId=${xt(e)}&path=${xt(t)}&meta=1`,Tt=(e,t)=>`/api/files?workspaceId=${xt(e)}&q=${xt(t)}`,Et=e=>e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(1)} MB`,Dt=e=>{let t=new Date(e);return Number.isNaN(t.getTime())?``:new Date(e).toLocaleTimeString(`zh-CN`,{hour:`2-digit`,minute:`2-digit`})};function Ot({workspaceId:e,entryPath:t,name:n,listings:r,expanded:i,toggle:a,loadDirectory:o,openPaths:s,onOpen:c,depth:l}){let u=r[t],d=i.has(t);return(0,p.useEffect)(()=>{d&&u===void 0&&o(t)},[d,t,u,o]),(0,m.jsxs)(`li`,{className:`file-node`,"data-file-dir":t,children:[(0,m.jsxs)(`button`,{type:`button`,className:`file-row file-row-dir`,"data-file-dir-toggle":t,"aria-expanded":d,style:{paddingLeft:`${8+l*13}px`},onClick:()=>a(t),children:[(0,m.jsx)(`span`,{className:`file-caret`,"aria-hidden":`true`,children:d?`▾`:`▸`}),(0,m.jsx)(`span`,{className:`file-name`,children:n})]}),d&&(0,m.jsx)(`ul`,{className:`file-children`,children:u===void 0?(0,m.jsx)(`li`,{className:`file-row-loading`,"data-file-loading":t,children:`读取中…`}):`error`in u?(0,m.jsx)(`li`,{className:`file-row-error`,"data-file-error":t,children:u.error.message}):u.entries.length===0?(0,m.jsx)(`li`,{className:`file-row-empty`,"data-file-empty":t,children:`空目录`}):(0,m.jsxs)(m.Fragment,{children:[u.truncated&&(0,m.jsx)(`li`,{className:`file-row-empty`,"data-file-truncated":t,children:`条目过多,只显示前一部分`}),u.entries.map(t=>t.kind===`directory`?(0,m.jsx)(Ot,{workspaceId:e,entryPath:t.path,name:t.name,listings:r,expanded:i,toggle:a,loadDirectory:o,openPaths:s,onOpen:c,depth:l+1},t.path):(0,m.jsx)(kt,{entry:t,depth:l+1,openPaths:s,onOpen:c},t.path))]})})]})}function kt({entry:e,depth:t,openPaths:n,onOpen:r}){if(e.kind===`symlink`)return(0,m.jsx)(`li`,{className:`file-node`,"data-file-entry":e.path,"data-file-outside":e.path,children:(0,m.jsxs)(`button`,{type:`button`,className:`file-row file-row-file`,disabled:!0,title:`${e.path} 是一个指向 workspace 之外的符号链接,不能打开`,style:{paddingLeft:`${8+t*13}px`},children:[(0,m.jsx)(`span`,{className:`file-caret`,"aria-hidden":`true`}),(0,m.jsx)(`span`,{className:`file-name`,children:e.name}),(0,m.jsx)(`span`,{className:`file-badge`,"data-file-outside-badge":`1`,children:`外部链接`})]})});let i=n.has(e.path);return(0,m.jsx)(`li`,{className:`file-node`,"data-file-entry":e.path,"data-file-row-open":i?`1`:`0`,children:(0,m.jsxs)(`button`,{type:`button`,className:`file-row file-row-file`,"data-file-open":e.path,disabled:!e.text,title:e.text?i?`${e.path}(已经打开,点击切换到它)`:e.path:`${e.name} 是二进制文件,无法以文本打开`,style:{paddingLeft:`${8+t*13}px`},onClick:()=>r(e),children:[(0,m.jsx)(`span`,{className:`file-caret`,"aria-hidden":`true`}),(0,m.jsx)(`span`,{className:`file-name`,children:e.name}),i&&(0,m.jsx)(`span`,{className:`file-open-mark`,"data-file-open-mark":e.path,title:`已经打开`,"aria-label":`已经打开`,children:`●`}),!e.text&&(0,m.jsx)(`span`,{className:`file-badge`,"data-file-binary":`1`,"aria-label":`不支持的文件类型`,children:`不支持`})]})})}function At({file:e,state:t,message:n,external:r,onChange:i,onSave:a,onReload:o,onOverwrite:s}){let[c,l]=(0,p.useState)(!0),u=/\.md$/i.test(e.path);return(0,m.jsxs)(`div`,{className:`file-editor`,"data-file-editor":e.path,"data-save-state":t,children:[(0,m.jsxs)(`div`,{className:`file-editor-head`,children:[(0,m.jsx)(`span`,{className:`file-editor-path`,title:e.path,children:e.path}),(0,m.jsxs)(`span`,{className:`file-editor-flags`,children:[u&&(0,m.jsx)(`button`,{type:`button`,className:`file-preview-toggle`,onClick:()=>l(e=>!e),children:c?`原文`:`预览`}),e.dirty&&(0,m.jsx)(`span`,{className:`file-dirty`,"data-file-dirty":`1`,children:`未保存`}),(0,m.jsx)(`button`,{type:`button`,className:`file-save`,"data-file-save":e.path,onClick:a,disabled:t===`saving`,children:t===`saving`?`保存中…`:`保存`})]})]}),r&&t!==`conflict`&&(0,m.jsxs)(`div`,{className:`file-conflict`,"data-file-external":e.path,role:`alert`,children:[(0,m.jsx)(`p`,{children:`这个文件在磁盘上已经被改过了,你的修改还没有覆盖它。`}),(0,m.jsxs)(`div`,{className:`file-conflict-actions`,children:[(0,m.jsx)(`button`,{type:`button`,"data-file-reload":e.path,onClick:o,children:`丢掉我的修改,读磁盘版本`}),(0,m.jsx)(`button`,{type:`button`,"data-file-overwrite":e.path,onClick:s,children:`仍然覆盖磁盘`})]})]}),t===`conflict`&&(0,m.jsxs)(`div`,{className:`file-conflict`,"data-file-conflict":e.path,role:`alert`,children:[(0,m.jsx)(`p`,{children:`保存被拒绝:这个文件在磁盘上已经被改过了,覆盖会丢掉对方的改动。`}),(0,m.jsxs)(`div`,{className:`file-conflict-actions`,children:[(0,m.jsx)(`button`,{type:`button`,"data-file-reload":e.path,onClick:o,children:`丢掉我的修改,读磁盘版本`}),(0,m.jsx)(`button`,{type:`button`,"data-file-overwrite":e.path,onClick:s,children:`仍然覆盖磁盘`})]})]}),n&&t!==`conflict`&&!r&&(0,m.jsx)(`p`,{className:`file-editor-message file-message-${t}`,"data-file-message":t,role:`status`,children:n}),u&&c?(0,m.jsx)(`div`,{className:`file-markdown-preview`,children:(0,m.jsx)(Qe,{source:e.content})}):(0,m.jsx)(ht,{value:e.content,onChange:i,onKeyDown:e=>{let t=e.currentTarget;if(e.nativeEvent.isComposing||e.keyCode===229)return;if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()===`s`){e.preventDefault(),a();return}if(e.key!==`Tab`)return;e.preventDefault();let{selectionStart:n,selectionEnd:r,value:o}=t;if(!o.slice(n,r).includes(`
21
21
  `)&&!e.shiftKey){i(`${o.slice(0,n)}${pt}${o.slice(r)}`);let e=n+2;requestAnimationFrame(()=>{t.selectionStart=e,t.selectionEnd=e});return}let s=o.lastIndexOf(`
22
22
  `,n-1)+1,c=r+(o[r-1]===`
23
23
  `?0:1),l=o.slice(s,c),u=l.split(`
24
24
  `).map(t=>e.shiftKey?t.replace(RegExp(`^[ \\t]{1,2}`),``):`${pt}${t}`).join(`
25
- `);i(o.slice(0,s)+u+o.slice(c));let d=u.length-l.length;requestAnimationFrame(()=>{let e=Math.max(s,n+Math.min(d,0));t.selectionStart=e,t.selectionEnd=Math.max(e,r+d)})},path:e.path})]})}function jt({error:e}){let t=e.body.code,n=typeof e.body.details?.path==`string`?e.body.details.path:``,r=t===`workspace_missing`?`missing`:t===`timeout`?`timeout`:`failed`;return(0,m.jsxs)(`div`,{className:`rail-empty file-problem`,"data-file-root-error":`1`,"data-file-error-kind":r,role:`alert`,children:[r===`missing`&&(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(`p`,{className:`file-problem-title`,"data-file-error-title":`1`,children:`工作区路径不存在,无法读取文件。`}),(0,m.jsx)(`p`,{className:`file-problem-hint`,children:`请确认这个路径是写错了,还是目录被移动或删除了:`}),(0,m.jsx)(`code`,{className:`file-problem-path`,"data-file-error-path":`1`,children:n||e.message})]}),r===`timeout`&&(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(`p`,{className:`file-problem-title`,"data-file-error-title":`1`,children:`读取超时,服务端没有回应。`}),(0,m.jsxs)(`p`,{className:`file-problem-hint`,children:[`已经等了 `,_t/1e3,` 秒。如果这个工作区在网络盘上,请检查它是否已连接,然后点上面的 ↻ 重试。`]})]}),r===`failed`&&(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(`p`,{className:`file-problem-title`,"data-file-error-title":`1`,children:`读取失败。`}),(0,m.jsx)(`p`,{className:`file-problem-hint`,children:e.message})]})]})}function Mt(e){let[t,n]=(0,p.useState)([]),[r,i]=(0,p.useState)(),[a,o]=(0,p.useState)(``),[s,c]=(0,p.useState)({}),[l,u]=(0,p.useState)({});(0,p.useEffect)(()=>{n([]),i(void 0),o(``),c({}),u({})},[e]);let d=(0,p.useCallback)(async r=>{if(e){if(r.text===!1)o(`「${r.name}」不支持以文本打开(二进制文件)。`);else if(t.some(e=>e.path===r.path))i(r.path);else{o(``);try{let t=await yt(Ct(e,r.path));n(e=>e.some(e=>e.path===r.path)?e:[...e,{path:t.path,name:r.name,content:t.content,etag:t.meta.etag,dirty:!1}]),i(t.path),u(e=>({...e,[t.path]:!1}))}catch(e){o(`打不开「${r.name}」:${e instanceof Error?e.message:`未知错误`}`)}}}},[e,t]),f=(0,p.useCallback)(e=>{n(t=>{let n=t.find(t=>t.path===e);if(n?.dirty&&!window.confirm(`「${n.name}」有未保存的修改,关掉就丢了。仍然关闭?`))return t;let a=t.findIndex(t=>t.path===e),o=t.filter(t=>t.path!==e);return e===r&&i(o.length===0?void 0:o[Math.min(a,o.length-1)]?.path),o})},[r]),h=(0,p.useCallback)((e,t)=>{n(n=>n.map(n=>n.path===e?{...n,content:t,dirty:!0}:n))},[]);(0,p.useEffect)(()=>{if(!e||t.length===0)return;let n=!0,r=t.map(e=>e.path),i=setInterval(()=>{for(let i of r)yt(wt(e,i)).then(e=>{if(!n)return;let r=t.find(e=>e.path===i);r&&e.etag!==r.etag&&u(e=>({...e,[i]:!0}))}).catch(()=>{})},ft);return()=>{n=!1,clearInterval(i)}},[e,t]);let g=(0,p.useCallback)(async(r,i)=>{if(!e)return;let a=t.find(e=>e.path===r);if(!a)return;c(e=>({...e,[r]:{state:`saving`,message:``}}));let o=a.etag;if(i)try{o=(await yt(wt(e,r))).etag}catch(e){c(t=>({...t,[r]:{state:`error`,message:`无法读取磁盘上的最新版本:${e instanceof Error?e.message:`未知错误`}`}}));return}try{let t=await yt(Ct(e,r),{method:`PUT`,headers:{"content-type":`application/json`},body:JSON.stringify({path:r,content:a.content,baseEtag:o})});n(e=>e.map(e=>e.path===r?{...e,etag:t.meta.etag,dirty:!1}:e)),u(e=>({...e,[r]:!1})),c(e=>({...e,[r]:{state:`saved`,message:`已保存 ${Dt(t.meta.modifiedAt)}`}}))}catch(e){let t=e instanceof gt&&e.body.code===`stale`;c(n=>({...n,[r]:{state:t?`conflict`:`error`,message:e instanceof Error?e.message:`保存失败`}})),t&&u(e=>({...e,[r]:!0}))}},[e,t]),_=(0,p.useCallback)(async t=>{if(e)try{let r=await yt(Ct(e,t));n(e=>e.map(e=>e.path===t?{...e,content:r.content,etag:r.meta.etag,dirty:!1}:e)),u(e=>({...e,[t]:!1})),c(e=>({...e,[t]:{state:`saved`,message:`已重新载入磁盘版本`}}))}catch(e){c(n=>({...n,[t]:{state:`error`,message:e instanceof Error?e.message:`重新载入失败`}}))}},[e]);return{open:t,activePath:r,active:t.find(e=>e.path===r),tabItems:(0,p.useMemo)(()=>t.map(e=>({id:e.path,label:e.name,title:e.path,closable:!0,badge:e.dirty?(0,m.jsx)(`span`,{className:`file-tab-badge`,"data-file-tab-dirty":e.path,"aria-label":`未保存`,children:`●`}):void 0})),[t]),openPaths:(0,p.useMemo)(()=>new Set(t.map(e=>e.path)),[t]),notice:a,setNotice:o,saves:s,external:l,openFile:d,setActivePath:i,closeFile:f,updateBuffer:h,save:g,reload:_}}function Nt({workspaceId:e,files:t,onOpenFile:n}){let{open:r,openPaths:i,notice:a,setNotice:o,openFile:s}=t,[c,l]=(0,p.useState)({}),[u,d]=(0,p.useState)(()=>new Set),[f,h]=(0,p.useState)(``),[g,_]=(0,p.useState)(null),[v,y]=(0,p.useState)(!1),b=(0,p.useCallback)(e=>{n?n(e):s(e)},[n,s]),x=(0,p.useCallback)(e=>{d(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),S=(0,p.useCallback)(e=>{l({}),l(e=>({...e,"":void 0})),yt(St(e,``)).then(e=>l({"":e})).catch(e=>l({"":{error:bt(e)}})),d(new Set)},[]);(0,p.useEffect)(()=>{if(l({}),_(null),o(``),!e)return;let t=!0;return yt(St(e,``)).then(e=>{t&&l({"":e})}).catch(e=>{t&&l({"":{error:bt(e)}})}),()=>{t=!1}},[e]),(0,p.useEffect)(()=>{let t=f.trim();if(!e||!t){_(null),y(!1);return}let n=!0;y(!0);let r=setTimeout(()=>{yt(Tt(e,t)).then(e=>{n&&(_(e),y(!1))}).catch(e=>{n&&(_({mode:`search`,query:t,hits:[],truncated:!1,skipped:[]}),y(!1),o(`搜索失败:${e instanceof Error?e.message:`未知错误`}`))})},220);return()=>{n=!1,clearTimeout(r)}},[f,e]);let C=c[``],w=f.trim(),T=(0,p.useMemo)(()=>w?v||!g?(0,m.jsx)(`p`,{className:`rail-empty`,"data-file-searching":`1`,children:`搜索中…`}):g.hits.length===0?(0,m.jsxs)(`p`,{className:`rail-empty`,"data-file-search-empty":`1`,children:[`没有匹配「`,w,`」的文件。`]}):(0,m.jsxs)(m.Fragment,{children:[g.truncated&&(0,m.jsx)(`p`,{className:`file-search-note`,"data-file-search-partial":`1`,children:`结果不完整:搜索有上限,只列出了一部分。`}),g.skipped.length>0&&(0,m.jsxs)(`p`,{className:`file-search-note`,"data-file-search-skipped":`1`,children:[`已跳过 `,g.skipped.length,` 个目录(node_modules、.git 等)。`]}),(0,m.jsx)(`ul`,{className:`file-list`,children:g.hits.map(e=>(0,m.jsx)(`li`,{children:(0,m.jsxs)(`button`,{type:`button`,className:`file-row file-row-file`,"data-file-hit":e.path,"data-file-hit-open":i.has(e.path)?`1`:`0`,onClick:()=>b({path:e.path,name:e.name,text:!0}),children:[(0,m.jsx)(`span`,{className:`file-caret`,"aria-hidden":`true`}),(0,m.jsx)(`span`,{className:`file-name`,children:e.name}),i.has(e.path)&&(0,m.jsx)(`span`,{className:`file-open-mark`,"data-file-open-mark":e.path,title:`已经打开`,"aria-label":`已经打开`,children:`●`}),(0,m.jsx)(`span`,{className:`file-badge-path`,children:e.directory||`/`})]})},e.path))})]}):e?C===void 0?(0,m.jsx)(`p`,{className:`rail-empty`,"data-file-root-loading":`1`,children:`读取工作区…`}):`error`in C?(0,m.jsx)(jt,{error:C.error}):(0,m.jsxs)(m.Fragment,{children:[C.truncated&&(0,m.jsx)(`p`,{className:`file-search-note`,"data-file-truncated-root":`1`,children:`条目过多,只显示前一部分。`}),C.entries.length===0?(0,m.jsx)(`p`,{className:`rail-empty`,"data-file-root-empty":`1`,children:`这个工作区是空的,没有文件也没有子目录。`}):(0,m.jsx)(`ul`,{className:`file-list`,children:C.entries.map(t=>t.kind===`directory`?(0,m.jsx)(Ot,{workspaceId:e,entryPath:t.path,name:t.name,listings:c,expanded:u,toggle:x,openPaths:i,onOpen:e=>b(e),onError:o,depth:0},t.path):(0,m.jsx)(kt,{entry:t,depth:0,openPaths:i,onOpen:e=>b(e)},t.path))})]}):(0,m.jsx)(`p`,{className:`rail-empty`,"data-file-no-workspace":`1`,children:`先在左侧选一个工作区,这里会显示它的文件。`}),[w,v,g,e,C,c,u,x,b,i]);return(0,m.jsxs)(`div`,{className:`files-panel`,"data-files-panel":`1`,"data-files-open-count":r.length,children:[(0,m.jsxs)(`div`,{className:`files-search`,children:[(0,m.jsx)(`input`,{type:`search`,className:`files-search-input`,"data-file-search-input":`1`,value:f,placeholder:`按文件名搜索…`,"aria-label":`按文件名搜索`,onChange:e=>h(e.target.value)}),(0,m.jsx)(`button`,{type:`button`,className:`files-refresh`,"data-file-refresh":`1`,title:`重新读取目录`,"aria-label":`重新读取目录`,disabled:!e,onClick:()=>e&&S(e),children:`↻`})]}),a&&(0,m.jsx)(`p`,{className:`rail-empty`,"data-file-notice":`1`,children:a}),(0,m.jsx)(`div`,{className:`files-tree`,"data-file-tree":w?`search`:`browse`,children:T}),r.length===0&&(0,m.jsx)(`p`,{className:`rail-empty`,"data-file-none-open":`1`,children:`点一个文本文件,它会在上面的标签栏开一个标签,正文显示在中间主画面。`}),r.length>0&&(0,m.jsxs)(`p`,{className:`files-hint`,"data-file-open-hint":`1`,children:[`已打开 `,r.length,` 个文件,正文显示在中间主画面;点上面的标签切换,点这里的行切换。`]}),(0,m.jsxs)(`p`,{className:`files-footnote`,"data-files-footnote":`1`,children:[`只支持纯文本(md / 代码),二进制文件不会打开。写入只允许覆盖已存在的文件,不新建,不删除。`,r.length>0&&` 当前打开 ${r.length} 个文件,占 ${Et(r.reduce((e,t)=>e+t.content.length,0))}。`]})]})}function Pt(e,t,n=88,r=200){let i=Math.max(0,Math.floor(e));if(!Number.isFinite(t)||t<=0)return{perRow:Math.max(1,i),width:n};if(i===0)return{perRow:1,width:n};if(i===1)return{perRow:1,width:Math.min(r,t)};let a=t/i;if(a>=n)return{perRow:i,width:Math.min(a,r)};for(let e=2;e<=i;e+=1){let a=Math.ceil(i/e),o=t/a;if(o>=n)return{perRow:a,width:Math.min(o,r)}}return{perRow:1,width:n}}function Ft(e,t){let n=Math.max(1,Math.floor(t)),r=[];for(let t=0;t<e.length;t+=n)r.push(e.slice(t,t+n));return r}function It(e,t,n=88,r=200){let[i,a]=(0,p.useState)(()=>Pt(e,0,n,r)),[o,s]=(0,p.useState)(null),c=(0,p.useCallback)(()=>{let i=Pt(e,t,n,r);s({count:e,containerWidth:t}),a(e=>e.perRow===i.perRow&&e.width===i.width?e:i)},[e,t,n,r]),l=(0,p.useRef)(t);(0,p.useEffect)(()=>{t<=0||l.current!==t&&(l.current=t,c())},[t,c]);let u=(0,p.useRef)(null);return(0,p.useEffect)(()=>{let t=u.current;u.current=e,t!==null&&e>t&&c()},[e,c]),{layout:i,frozen:o!==null&&o.count!==e,recompute:c}}function Lt({tabs:e,activeId:t,onSelect:n,onClose:r,ariaLabel:i,minWidth:a=88,maxWidth:o=200,prefix:s=`workspace-tab`,idPrefix:c=s,className:l}){let u=(0,p.useRef)(null),[d,f]=(0,p.useState)(0),{layout:h,frozen:g,recompute:_}=It(e.length,d,a,o);(0,p.useLayoutEffect)(()=>{let e=u.current;if(!e||typeof ResizeObserver>`u`)return;let t=()=>{let t=e.clientWidth;t>0&&f(t)};t();let n=new ResizeObserver(t);return n.observe(e),()=>n.disconnect()},[]);let v=Ft(e,h.perRow),y=(e,t)=>t===void 0?{}:{[`data-${s}-${e}`]:t},b=e.filter(e=>e.kind===`file`).length,x=e.filter(e=>e.kind===`session`||!e.kind).length,S=t=>({[`data-${t}-strip`]:`1`,...g?{[`data-${t}-frozen`]:`1`}:{},[`data-${t}-count`]:t===`file-tab`?b:t===`session-tab`?x:e.length,[`data-${t}-min`]:a,[`data-${t}-max`]:o,[`data-${t}-per-row`]:h.perRow,[`data-${t}-width`]:Math.round(h.width*100)/100}),C=e=>{let t=e.relatedTarget;(!t||t.isConnected)&&_()},w=e=>{let t=e.relatedTarget;(!t||t.isConnected)&&_()};(0,p.useEffect)(()=>{if(!g)return;let e=u.current;if(!e)return;let t=t=>{let n=e.getBoundingClientRect();t.clientX>=n.left&&t.clientX<=n.right&&t.clientY>=n.top&&t.clientY<=n.bottom||_()};return document.addEventListener(`mousemove`,t,!0),()=>document.removeEventListener(`mousemove`,t,!0)},[g,_]);let T=[`${s}-strip`,`workspace-tab-strip`,`session-tab-strip`,`file-tab-strip`,l].filter(Boolean).join(` `);return(0,m.jsx)(`div`,{ref:u,className:T,role:`tablist`,"aria-label":i??`工作区标签页`,...y(`strip`,`1`),...S(`workspace-tab`),...S(`session-tab`),...S(`file-tab`),style:{"--tab-w":`${h.width}px`},onMouseEnter:C,onMouseLeave:w,children:v.map((i,a)=>(0,m.jsx)(`div`,{className:`${s}-row workspace-tab-row session-tab-row file-tab-row`,role:`presentation`,...y(`row`,a),children:i.map(i=>{let a=i.id===t||i.rawId!==void 0&&i.rawId===t,o=i.title??i.label,l=i.kind,u=i.rawId??i.id,d=e.findIndex(e=>e.id===i.id),f=[s,a?`${s}-active`:``,`workspace-tab`,a?`workspace-tab-active`:``,l?`${l}-tab`:``,l&&a?`${l}-tab-active`:``].filter(Boolean).join(` `);return(0,m.jsxs)(`div`,{role:`tab`,id:`${c}-${i.id}`,"aria-selected":a,"aria-controls":`${c}-panel-${i.id}`,tabIndex:a?0:-1,className:f,...y(`id`,u),...y(`index`,d),...a?y(`active`,`1`):{},...y(`closable`,i.closable===!1?`0`:`1`),...l?{"data-tab-kind":l}:{},...l===`session`||!l?{"data-session-tab-id":u,"data-session-tab-index":d,...a?{"data-session-tab-active":`1`}:{},"data-session-tab-closable":i.closable===!1?`0`:`1`}:{},...l===`file`?{"data-file-tab-id":u,"data-file-tab-index":d,...a?{"data-file-tab-active":`1`}:{},"data-file-tab-closable":i.closable===!1?`0`:`1`}:{},title:o,style:{width:h.width,boxSizing:`border-box`},onClick:()=>n?.(i.id),onAuxClick:e=>{e.button===1&&(e.preventDefault(),e.stopPropagation(),i.closable!==!1&&r&&r(i.id))},onMouseDown:e=>{e.button===1&&e.preventDefault()},onKeyDown:t=>{if(t.key!==`ArrowLeft`&&t.key!==`ArrowRight`)return;t.preventDefault();let r=t.key===`ArrowRight`?1:-1,a=e[(e.findIndex(e=>e.id===i.id)+r+e.length)%e.length];a&&n?.(a.id)},children:[i.icon&&(0,m.jsx)(`span`,{className:`${s}-icon workspace-tab-icon`,children:i.icon}),i.badge,(0,m.jsx)(`span`,{className:`${s}-label workspace-tab-label ${l?`${l}-tab-label`:``}`,...y(`label`,u),...l===`session`||!l?{"data-session-tab-label":u}:{},...l===`file`?{"data-file-tab-label":u}:{},children:i.label}),i.closable!==!1&&r&&(0,m.jsx)(`button`,{type:`button`,className:`${s}-close workspace-tab-close ${l?`${l}-tab-close`:``}`,...y(`close`,u),...l===`session`||!l?{"data-session-tab-close":u}:{},...l===`file`?{"data-file-tab-close":u}:{},onClick:e=>{e.stopPropagation(),r(i.id)},title:`关闭 ${o}`,"aria-label":`关闭 ${o}`,children:`×`})]},i.id)})},`row_${a}`))})}var Rt={pending:`未开始`,running:`运行中`,completed:`已完成`,failed:`失败`,skipped:`已跳过`,stopped:`已停止`},zt={running:`运行中`,completed:`全部完成`,failed:`中途失败`,stopped:`已停止`},Bt={pending:`○`,running:`◐`,completed:`✓`,failed:`✕`,skipped:`→`,stopped:`■`},Vt={stop:`失败即停`,skip:`失败跳过`,"retry:1":`失败重试 1 次`,"retry:2":`失败重试 2 次`,"retry:3":`失败重试 3 次`};function Ht(e){let t=Math.max(0,Math.round(e/1e3)),n=t%60,r=Math.floor(t/60)%60,i=Math.floor(t/3600),a=e=>String(e).padStart(2,`0`);return i>0?`${i}h ${a(r)}:${a(n)}`:`${r}:${a(n)}`}function Ut(e){let t=e.status===`running`,[n,r]=(0,p.useState)(0);(0,p.useEffect)(()=>{if(!t)return;r(Date.now());let e=window.setInterval(()=>r(Date.now()),200);return()=>window.clearInterval(e)},[t,e.startedAt]);let i=Date.parse(e.startedAt);if(Number.isFinite(i)){if(!t){let t=Date.parse(e.finishedAt??``);return Number.isFinite(t)?t-i:void 0}return n>0?n-i:void 0}}function Wt({step:e,names:t}){let n=e.attempts.length,r=e.attempts[n-1],i=t.get(e.agentId)||e.agentId,a=e.label?.trim()||i;return(0,m.jsxs)(`li`,{className:`workflow-step workflow-step-${e.status}`,"data-workflow-step":e.index+1,"data-workflow-step-status":e.status,"data-workflow-step-agent":e.agentId,"data-workflow-step-attempts":n,children:[(0,m.jsx)(`span`,{className:`workflow-step-mark`,"aria-hidden":`true`,children:Bt[e.status]}),(0,m.jsxs)(`span`,{className:`workflow-step-n`,"data-workflow-step-n":e.index+1,children:[`第 `,e.index+1,` 步`]}),(0,m.jsx)(`strong`,{className:`workflow-step-name`,"data-workflow-step-name":e.index+1,children:a}),(0,m.jsx)(`span`,{className:`workflow-step-agent`,"data-workflow-step-agent-name":e.index+1,children:i}),n>1&&(0,m.jsxs)(`span`,{className:`workflow-step-tries`,"data-workflow-step-retried":e.index+1,children:[`尝试 `,n,` 次`]}),(0,m.jsx)(`span`,{className:`workflow-step-status`,"data-workflow-step-status-text":e.index+1,children:Rt[e.status]}),e.status===`failed`&&r?.error&&(0,m.jsx)(`span`,{className:`workflow-step-why`,"data-workflow-step-error":e.index+1,children:r.error})]})}function Gt({workflow:e,names:t,onStop:n,stopping:r}){let i=e.status===`running`,a=Ut(e),o=e.steps.filter(e=>e.status===`completed`||e.status===`skipped`).length;return(0,m.jsxs)(`article`,{className:`workflow-card workflow-${e.status}`,"data-workflow-card":e.id,"data-workflow-status":e.status,"data-workflow-steps":e.steps.length,"data-workflow-current":e.currentIndex+1,children:[(0,m.jsxs)(`div`,{className:`workflow-head`,children:[(0,m.jsx)(`span`,{className:`workflow-badge`,children:`编排`}),(0,m.jsx)(`strong`,{className:`workflow-summary`,"data-workflow-summary":e.id,children:e.summary}),(0,m.jsxs)(`span`,{className:`workflow-progress`,"data-workflow-progress":e.id,children:[o,`/`,e.steps.length]}),(0,m.jsx)(`span`,{className:`workflow-status`,"data-workflow-status-text":e.id,children:zt[e.status]??e.status}),(0,m.jsx)(`span`,{className:`workflow-policy`,"data-workflow-policy":e.onFailure,children:Vt[e.onFailure]??e.onFailure}),a!==void 0&&(0,m.jsxs)(`span`,{className:`workflow-clock`,"data-workflow-elapsed":e.id,children:[i?`已运行 `:`用时 `,Ht(a)]}),i&&(0,m.jsx)(`button`,{type:`button`,className:`workflow-stop`,"data-workflow-stop":e.id,disabled:r,onClick:n,children:`停止`})]}),(0,m.jsx)(`ol`,{className:`workflow-steps`,"data-workflow-step-count":e.steps.length,children:e.steps.map(e=>(0,m.jsx)(Wt,{step:e,names:t},e.index))})]})}var j=[{id:`agents`,label:`智能体与创建`,icon:`🤖`,title:`Agent 智能体系统与创建指南`,description:`了解 Super Pi 的角色设定机制,以及如何一步步创建你自己的自定义 Agent。`},{id:`workspace`,label:`工作区与会话`,icon:`🗂️`,title:`工作区与多会话管理`,description:`高效管理本地代码仓库、独立会话标签页与多模态图文输入。`},{id:`subagents`,label:`子任务与编排`,icon:`⚡`,title:`子智能体委托与顺序编排使用指南`,description:`让主 Agent 安排分工、交接结果,并查看进度、处理失败与停止任务。`},{id:`model-policy`,label:`模型使用规则`,icon:`🔒`,title:`模型过滤规则(按工作区限制可用模型)`,description:`用正则规则限定哪些工作区只能用哪些模型,防止敏感代码被发到不该用的模型上。`},{id:`memory`,label:`主动记忆`,icon:`🧠`,title:`主动记忆与技能经验`,description:`让 Super Pi 在得到明确证据后保存长期事实、用户偏好和可复用工作方法。`},{id:`knowledge`,label:`知识库`,icon:`📚`,title:`工作区知识库`,description:`导入规范与设计文档,让 Agent 检索原文作答,而不是凭记忆编造。`},{id:`tools`,label:`侧边栏工具`,icon:`🧩`,title:`全局工具:开发、导出与导入`,description:`让 Agent 写一个工具,用一个文件分享给别人,导入前先看清它会做什么。`},{id:`cli`,label:`指令与快捷键`,icon:`⌨️`,title:`Pi 终端命令与键盘快捷操作`,description:`利用 Pi 终端斜杠指令控制服务生命周期,掌握常用快捷键提升效率。`},{id:`themes`,label:`外观与主题`,icon:`🎨`,title:`界面主题与个性化显示`,description:`内置 4 款专业高对比度主题,针对不同环境提供舒适体验。`}];function Kt({onClose:e}){let[t,n]=(0,p.useState)(`agents`);(0,p.useEffect)(()=>{let t=t=>{t.key===`Escape`&&!t.defaultPrevented&&e()};return window.addEventListener(`keydown`,t),()=>window.removeEventListener(`keydown`,t)},[e]);let r=j.find(e=>e.id===t)??j[0];return(0,m.jsx)(`div`,{className:`overlay`,onClick:e,children:(0,m.jsxs)(`div`,{className:`picker settings-window help-window`,"data-help-window":`1`,onClick:e=>e.stopPropagation(),role:`dialog`,"aria-modal":`true`,"aria-labelledby":`help-window-title`,children:[(0,m.jsxs)(`div`,{className:`settings-window-head help-window-head`,children:[(0,m.jsxs)(`div`,{className:`help-head-title`,children:[(0,m.jsx)(`h2`,{id:`help-window-title`,children:`使用指南与帮助`}),(0,m.jsx)(`span`,{className:`help-badge`,children:`v0.1.x`})]}),(0,m.jsx)(`button`,{type:`button`,className:`help-close-button`,"aria-label":`关闭指南`,onClick:e,children:`✕`})]}),(0,m.jsxs)(`div`,{className:`settings-window-body help-window-body`,children:[(0,m.jsxs)(`nav`,{className:`settings-nav help-nav`,role:`tablist`,"aria-orientation":`vertical`,"aria-label":`使用指南分类`,children:[(0,m.jsx)(`div`,{className:`settings-nav-label`,children:`目录`}),j.map(e=>(0,m.jsxs)(`button`,{type:`button`,id:`help-tab-${e.id}`,className:`settings-category help-category`,role:`tab`,"aria-selected":e.id===r.id,"aria-controls":`help-panel`,tabIndex:e.id===r.id?0:-1,onClick:()=>n(e.id),children:[(0,m.jsx)(`span`,{className:`help-tab-icon`,"aria-hidden":`true`,children:e.icon}),(0,m.jsx)(`span`,{children:e.label})]},e.id))]}),(0,m.jsxs)(`div`,{id:`help-panel`,className:`settings-panel help-panel`,role:`tabpanel`,"aria-labelledby":`help-tab-${r.id}`,children:[(0,m.jsxs)(`div`,{className:`help-panel-header`,children:[(0,m.jsx)(`h3`,{className:`help-panel-title`,children:r.title}),(0,m.jsx)(`p`,{className:`help-panel-desc`,children:r.description})]}),(0,m.jsxs)(`div`,{className:`help-panel-content`,children:[r.id===`agents`&&(0,m.jsx)(Yt,{}),r.id===`workspace`&&(0,m.jsx)(Xt,{}),r.id===`subagents`&&(0,m.jsx)(Qt,{}),r.id===`model-policy`&&(0,m.jsx)(Zt,{}),r.id===`memory`&&(0,m.jsx)(qt,{}),r.id===`knowledge`&&(0,m.jsx)(Jt,{}),r.id===`tools`&&(0,m.jsx)(M,{}),r.id===`cli`&&(0,m.jsx)($t,{}),r.id===`themes`&&(0,m.jsx)(en,{})]}),(0,m.jsx)(`div`,{className:`picker-actions help-actions`,children:(0,m.jsx)(`button`,{type:`button`,className:`picker-cancel`,onClick:e,children:`关闭`})})]})]})]})})}function qt(){return(0,m.jsxs)(`div`,{className:`help-section-group`,children:[(0,m.jsxs)(`div`,{className:`help-card highlight`,children:[(0,m.jsx)(`h4`,{children:`🧠 它会记住什么?`}),(0,m.jsx)(`p`,{children:`用户档案保存跨工作区的长期偏好;工作区事实保存环境与项目约定;技能保存已经验证过的步骤、命令和坑。临时结果、秘密和未验证的失败不会保存。`})]}),(0,m.jsxs)(`div`,{className:`help-card`,children:[(0,m.jsx)(`h4`,{children:`主动保存与后台复盘`}),(0,m.jsxs)(`ul`,{className:`help-list`,children:[(0,m.jsxs)(`li`,{children:[`主 Agent 可以使用 `,(0,m.jsx)(`code`,{children:`memory`}),` 和 `,(0,m.jsx)(`code`,{children:`skill_manage`}),` 工具主动保存;说“记住了”本身不会写入。`]}),(0,m.jsx)(`li`,{children:`每隔若干回合,系统会在独立会话中复盘最近证据。复盘不能读写工作区文件、执行命令或派发任务。`}),(0,m.jsx)(`li`,{children:`读取记忆和历史只返回带来源的参考数据,不能把记忆中的文字当作新的指令。`})]})]}),(0,m.jsxs)(`div`,{className:`help-card`,children:[(0,m.jsx)(`h4`,{children:`权限与审批`}),(0,m.jsxs)(`ul`,{className:`help-list`,children:[(0,m.jsx)(`li`,{children:`子 Agent 可以读取相关记忆,但没有写入权限。`}),(0,m.jsx)(`li`,{children:`后台复盘新增记忆可以直接保存;修改或删除旧条目会进入设置中的待审批列表。`}),(0,m.jsx)(`li`,{children:`在“设置 → 主动记忆”可以开关功能、调整复盘间隔、编辑条目和批准提案。`})]})]})]})}function Jt(){return(0,m.jsxs)(`div`,{className:`help-section-group`,children:[(0,m.jsxs)(`div`,{className:`help-card highlight`,children:[(0,m.jsx)(`h4`,{children:`📚 它解决什么问题?`}),(0,m.jsx)(`p`,{children:`Agent 默认靠训练时的记忆回答项目里的问题。知识库让你把规范、设计稿、会议纪要导入工作区,Agent 在回答前检索这些文档的原文片段,而不是凭印象编造。它与“主动记忆”不同:记忆是 Agent 自己沉淀的事实和技能,知识库只有你能写入。`})]}),(0,m.jsxs)(`div`,{className:`help-card`,children:[(0,m.jsx)(`h4`,{children:`怎么用`}),(0,m.jsxs)(`ul`,{className:`help-list`,children:[(0,m.jsx)(`li`,{children:`在“设置 → 知识库”里点“浏览工作区文件…”,在弹出的浏览器里挑一个 Markdown 或文本文件,挑完即导入。也可以直接粘贴正文,或手动填写相对路径。`}),(0,m.jsx)(`li`,{children:`同一个文件内容改了再导入就是更新,不会留下旧版本;同样的内容换个标题再导入也不会变成两篇。`}),(0,m.jsx)(`li`,{children:`面板里的“试检索”会用和 Agent 相同的检索器回答,方便你在交给 Agent 之前先确认它找得到。`}),(0,m.jsxs)(`li`,{children:[`从下一条消息开始生效。Agent 使用 `,(0,m.jsx)(`code`,{children:`knowledge_search`}),` 检索,只拿得到片段和来源,拿不到整篇文档。`]})]})]}),(0,m.jsxs)(`div`,{className:`help-card`,children:[(0,m.jsx)(`h4`,{children:`语义检索是内置的`}),(0,m.jsxs)(`ul`,{className:`help-list`,children:[(0,m.jsxs)(`li`,{children:[`关键词检索始终可用。语义检索让“撤销购买”也能找到写着“取消订单”的段落,它靠一个`,(0,m.jsx)(`strong`,{children:`随安装包一起分发`}),`的中文模型在本机运行,不需要额外下载、不需要联网、也不会把文档发到任何服务。`]}),(0,m.jsxs)(`li`,{children:[`第一次检索时加载模型(约一秒),之后一直可用。设 `,(0,m.jsx)(`code`,{children:`SUPER_PI_KNOWLEDGE_EMBEDDINGS=0`}),` 可以彻底关闭语义检索,只保留关键词检索。`]})]})]}),(0,m.jsxs)(`div`,{className:`help-card`,children:[(0,m.jsx)(`h4`,{children:`它不知道的事`}),(0,m.jsxs)(`ul`,{className:`help-list`,children:[(0,m.jsx)(`li`,{children:`知识库只会检索它有的东西。文档里没写的,它检索出来也不会有——所以导入什么,直接决定它能回答什么。`}),(0,m.jsx)(`li`,{children:`语义检索总会返回“最接近的那一段”,即使这一段其实并不回答问题。检索结果带一个分数,低分意味着这可能只是最像的一条,Agent 会被要求先核对再使用。`}),(0,m.jsx)(`li`,{children:`Agent 对知识库只读,不能写入或删除;导入和删除只有你能做。`})]})]})]})}function Yt(){return(0,m.jsxs)(`div`,{className:`help-section-group`,children:[(0,m.jsxs)(`div`,{className:`help-card highlight`,children:[(0,m.jsx)(`h4`,{children:`💡 Agent 是什么?`}),(0,m.jsxs)(`p`,{children:[`在 Super Pi 中,`,(0,m.jsx)(`strong`,{children:`Agent(智能体)`}),` 是一套独立的「角色提示词设定与能力配置」。 通过切换 Agent,模型将扮演专精工程师、系统架构师、代码审查员或规划专家等不同角色,遵循指定的原则来协助你完成任务。`]})]}),(0,m.jsxs)(`div`,{className:`help-card`,children:[(0,m.jsx)(`h4`,{children:`📁 Agent 文件存放在哪里?`}),(0,m.jsxs)(`ul`,{className:`help-list`,children:[(0,m.jsxs)(`li`,{children:[(0,m.jsx)(`strong`,{children:`内置 Agent 目录:`}),`项目根目录下的 `,(0,m.jsx)(`code`,{children:`agents/`}),` 文件夹,例如 `,(0,m.jsx)(`code`,{children:`agents/coder.md`}),`、`,(0,m.jsx)(`code`,{children:`agents/planner.md`}),`、`,(0,m.jsx)(`code`,{children:`agents/reviewer.md`}),` 等。`]}),(0,m.jsxs)(`li`,{children:[(0,m.jsx)(`strong`,{children:`用户自定义持久化目录:`}),`用户主目录下的 `,(0,m.jsx)(`code`,{children:`~/.super-pi/agents/`}),`,放入此目录的文件在所有项目和更新中均持久有效。`]})]})]}),(0,m.jsxs)(`div`,{className:`help-card`,children:[(0,m.jsx)(`h4`,{children:`🚀 如何创建一个全新的自定义 Agent?`}),(0,m.jsxs)(`div`,{className:`help-steps`,children:[(0,m.jsxs)(`div`,{className:`help-step`,children:[(0,m.jsx)(`span`,{className:`step-num`,children:`1`}),(0,m.jsxs)(`div`,{className:`step-body`,children:[(0,m.jsx)(`strong`,{children:`创建 Markdown 文件`}),(0,m.jsxs)(`p`,{children:[`在 `,(0,m.jsx)(`code`,{children:`agents/`}),` 或 `,(0,m.jsx)(`code`,{children:`~/.super-pi/agents/`}),` 目录下新建文件,例如 `,(0,m.jsx)(`code`,{children:`architect.md`}),`。`]})]})]}),(0,m.jsxs)(`div`,{className:`help-step`,children:[(0,m.jsx)(`span`,{className:`step-num`,children:`2`}),(0,m.jsxs)(`div`,{className:`step-body`,children:[(0,m.jsx)(`strong`,{children:`编写元数据与系统提示词`}),(0,m.jsx)(`p`,{children:`文件开头使用 YAML Frontmatter 声明名称和描述,正文编写设定提示词:`}),(0,m.jsx)(`pre`,{className:`help-code-block`,children:(0,m.jsx)(`code`,{children:`---
25
+ `);i(o.slice(0,s)+u+o.slice(c));let d=u.length-l.length;requestAnimationFrame(()=>{let e=Math.max(s,n+Math.min(d,0));t.selectionStart=e,t.selectionEnd=Math.max(e,r+d)})},path:e.path})]})}function jt({error:e}){let t=e.body.code,n=typeof e.body.details?.path==`string`?e.body.details.path:``,r=t===`workspace_missing`?`missing`:t===`timeout`?`timeout`:`failed`;return(0,m.jsxs)(`div`,{className:`rail-empty file-problem`,"data-file-root-error":`1`,"data-file-error-kind":r,role:`alert`,children:[r===`missing`&&(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(`p`,{className:`file-problem-title`,"data-file-error-title":`1`,children:`工作区路径不存在,无法读取文件。`}),(0,m.jsx)(`p`,{className:`file-problem-hint`,children:`请确认这个路径是写错了,还是目录被移动或删除了:`}),(0,m.jsx)(`code`,{className:`file-problem-path`,"data-file-error-path":`1`,children:n||e.message})]}),r===`timeout`&&(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(`p`,{className:`file-problem-title`,"data-file-error-title":`1`,children:`读取超时,服务端没有回应。`}),(0,m.jsxs)(`p`,{className:`file-problem-hint`,children:[`已经等了 `,_t/1e3,` 秒。如果这个工作区在网络盘上,请检查它是否已连接,然后点上面的 ↻ 重试。`]})]}),r===`failed`&&(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(`p`,{className:`file-problem-title`,"data-file-error-title":`1`,children:`读取失败。`}),(0,m.jsx)(`p`,{className:`file-problem-hint`,children:e.message})]})]})}function Mt(e){let[t,n]=(0,p.useState)([]),[r,i]=(0,p.useState)(),[a,o]=(0,p.useState)(``),[s,c]=(0,p.useState)({}),[l,u]=(0,p.useState)({}),[d,f]=(0,p.useState)({}),[h,g]=(0,p.useState)(()=>new Set),[_,v]=(0,p.useState)(``),[y,b]=(0,p.useState)(null),[x,S]=(0,p.useState)(!1);(0,p.useEffect)(()=>{if(n([]),i(void 0),o(``),c({}),u({}),f({}),g(new Set),b(null),v(``),S(!1),!e)return;let t=!0;return yt(St(e,``)).then(e=>{t&&f({"":e})}).catch(e=>{t&&f({"":{error:bt(e)}})}),()=>{t=!1}},[e]);let C=(0,p.useCallback)(t=>{e&&yt(St(e,t)).then(e=>{f(n=>({...n,[t]:e}))}).catch(e=>{f(n=>({...n,[t]:{error:bt(e)}}))})},[e]),w=(0,p.useCallback)(e=>{g(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),T=(0,p.useCallback)(()=>{e&&(f({}),g(new Set),yt(St(e,``)).then(e=>{f({"":e})}).catch(e=>{f({"":{error:bt(e)}})}))},[e]);(0,p.useEffect)(()=>{let t=_.trim();if(!e||!t){b(null),S(!1);return}let n=!0;S(!0);let r=setTimeout(()=>{yt(Tt(e,t)).then(e=>{n&&(b(e),S(!1))}).catch(e=>{n&&(b({mode:`search`,query:t,hits:[],truncated:!1,skipped:[]}),S(!1),o(`搜索失败:${e instanceof Error?e.message:`未知错误`}`))})},220);return()=>{n=!1,clearTimeout(r)}},[_,e]);let ee=(0,p.useCallback)(async r=>{if(e){if(r.text===!1)o(`「${r.name}」不支持以文本打开(二进制文件)。`);else if(t.some(e=>e.path===r.path))i(r.path);else{o(``);try{let t=await yt(Ct(e,r.path));n(e=>e.some(e=>e.path===r.path)?e:[...e,{path:t.path,name:r.name,content:t.content,etag:t.meta.etag,dirty:!1}]),i(t.path),u(e=>({...e,[t.path]:!1}))}catch(e){o(`打不开「${r.name}」:${e instanceof Error?e.message:`未知错误`}`)}}}},[e,t]),E=(0,p.useCallback)(e=>{n(t=>{let n=t.find(t=>t.path===e);if(n?.dirty&&!window.confirm(`「${n.name}」有未保存的修改,关掉就丢了。仍然关闭?`))return t;let a=t.findIndex(t=>t.path===e),o=t.filter(t=>t.path!==e);return e===r&&i(o.length===0?void 0:o[Math.min(a,o.length-1)]?.path),o})},[r]),te=(0,p.useCallback)((e,t)=>{n(n=>n.map(n=>n.path===e?{...n,content:t,dirty:!0}:n))},[]);(0,p.useEffect)(()=>{if(!e||t.length===0)return;let n=!0,r=t.map(e=>e.path),i=setInterval(()=>{for(let i of r)yt(wt(e,i)).then(e=>{if(!n)return;let r=t.find(e=>e.path===i);r&&e.etag!==r.etag&&u(e=>({...e,[i]:!0}))}).catch(()=>{})},ft);return()=>{n=!1,clearInterval(i)}},[e,t]);let D=(0,p.useCallback)(async(r,i)=>{if(!e)return;let a=t.find(e=>e.path===r);if(!a)return;c(e=>({...e,[r]:{state:`saving`,message:``}}));let o=a.etag;if(i)try{o=(await yt(wt(e,r))).etag}catch(e){c(t=>({...t,[r]:{state:`error`,message:`无法读取磁盘上的最新版本:${e instanceof Error?e.message:`未知错误`}`}}));return}try{let t=await yt(Ct(e,r),{method:`PUT`,headers:{"content-type":`application/json`},body:JSON.stringify({path:r,content:a.content,baseEtag:o})});n(e=>e.map(e=>e.path===r?{...e,etag:t.meta.etag,dirty:!1}:e)),u(e=>({...e,[r]:!1})),c(e=>({...e,[r]:{state:`saved`,message:`已保存 ${Dt(t.meta.modifiedAt)}`}}))}catch(e){let t=e instanceof gt&&e.body.code===`stale`;c(n=>({...n,[r]:{state:t?`conflict`:`error`,message:e instanceof Error?e.message:`保存失败`}})),t&&u(e=>({...e,[r]:!0}))}},[e,t]),ne=(0,p.useCallback)(async t=>{if(e)try{let r=await yt(Ct(e,t));n(e=>e.map(e=>e.path===t?{...e,content:r.content,etag:r.meta.etag,dirty:!1}:e)),u(e=>({...e,[t]:!1})),c(e=>({...e,[t]:{state:`saved`,message:`已重新载入磁盘版本`}}))}catch(e){c(n=>({...n,[t]:{state:`error`,message:e instanceof Error?e.message:`重新载入失败`}}))}},[e]);return{open:t,activePath:r,active:t.find(e=>e.path===r),tabItems:(0,p.useMemo)(()=>t.map(e=>({id:e.path,label:e.name,title:e.path,closable:!0,badge:e.dirty?(0,m.jsx)(`span`,{className:`file-tab-badge`,"data-file-tab-dirty":e.path,"aria-label":`未保存`,children:`●`}):void 0})),[t]),openPaths:(0,p.useMemo)(()=>new Set(t.map(e=>e.path)),[t]),notice:a,setNotice:o,saves:s,external:l,openFile:ee,setActivePath:i,closeFile:E,updateBuffer:te,save:D,reload:ne,listings:d,expanded:h,toggleDirectory:w,loadDirectory:C,refreshTree:T,query:_,setQuery:v,hits:y,searching:x}}function Nt({workspaceId:e,files:t,onOpenFile:n}){let{open:r,openPaths:i,notice:a,setNotice:o,openFile:s,listings:c,expanded:l,toggleDirectory:u,loadDirectory:d,refreshTree:f,query:h,setQuery:g,hits:_,searching:v}=t,y=(0,p.useCallback)(e=>{n?n(e):s(e)},[n,s]),b=c[``],x=h.trim(),S=(0,p.useMemo)(()=>x?v||!_?(0,m.jsx)(`p`,{className:`rail-empty`,"data-file-searching":`1`,children:`搜索中…`}):_.hits.length===0?(0,m.jsxs)(`p`,{className:`rail-empty`,"data-file-search-empty":`1`,children:[`没有匹配「`,x,`」的文件。`]}):(0,m.jsxs)(m.Fragment,{children:[_.truncated&&(0,m.jsx)(`p`,{className:`file-search-note`,"data-file-search-partial":`1`,children:`结果不完整:搜索有上限,只列出了一部分。`}),_.skipped.length>0&&(0,m.jsxs)(`p`,{className:`file-search-note`,"data-file-search-skipped":`1`,children:[`已跳过 `,_.skipped.length,` 个目录(node_modules、.git 等)。`]}),(0,m.jsx)(`ul`,{className:`file-list`,children:_.hits.map(e=>(0,m.jsx)(`li`,{children:(0,m.jsxs)(`button`,{type:`button`,className:`file-row file-row-file`,"data-file-hit":e.path,"data-file-hit-open":i.has(e.path)?`1`:`0`,onClick:()=>y({path:e.path,name:e.name,text:!0}),children:[(0,m.jsx)(`span`,{className:`file-caret`,"aria-hidden":`true`}),(0,m.jsx)(`span`,{className:`file-name`,children:e.name}),i.has(e.path)&&(0,m.jsx)(`span`,{className:`file-open-mark`,"data-file-open-mark":e.path,title:`已经打开`,"aria-label":`已经打开`,children:`●`}),(0,m.jsx)(`span`,{className:`file-badge-path`,children:e.directory||`/`})]})},e.path))})]}):e?b===void 0?(0,m.jsx)(`p`,{className:`rail-empty`,"data-file-root-loading":`1`,children:`读取工作区…`}):`error`in b?(0,m.jsx)(jt,{error:b.error}):(0,m.jsxs)(m.Fragment,{children:[b.truncated&&(0,m.jsx)(`p`,{className:`file-search-note`,"data-file-truncated-root":`1`,children:`条目过多,只显示前一部分。`}),b.entries.length===0?(0,m.jsx)(`p`,{className:`rail-empty`,"data-file-root-empty":`1`,children:`这个工作区是空的,没有文件也没有子目录。`}):(0,m.jsx)(`ul`,{className:`file-list`,children:b.entries.map(t=>t.kind===`directory`?(0,m.jsx)(Ot,{workspaceId:e,entryPath:t.path,name:t.name,listings:c,expanded:l,toggle:u,loadDirectory:d,openPaths:i,onOpen:e=>y(e),depth:0},t.path):(0,m.jsx)(kt,{entry:t,depth:0,openPaths:i,onOpen:e=>y(e)},t.path))})]}):(0,m.jsx)(`p`,{className:`rail-empty`,"data-file-no-workspace":`1`,children:`先在左侧选一个工作区,这里会显示它的文件。`}),[x,v,_,e,b,c,l,u,d,y,i]);return(0,m.jsxs)(`div`,{className:`files-panel`,"data-files-panel":`1`,"data-files-open-count":r.length,children:[(0,m.jsxs)(`div`,{className:`files-search`,children:[(0,m.jsx)(`input`,{type:`search`,className:`files-search-input`,"data-file-search-input":`1`,value:h,placeholder:`按文件名搜索…`,"aria-label":`按文件名搜索`,onChange:e=>g(e.target.value)}),(0,m.jsx)(`button`,{type:`button`,className:`files-refresh`,"data-file-refresh":`1`,title:`重新读取目录`,"aria-label":`重新读取目录`,disabled:!e,onClick:()=>f(),children:`↻`})]}),a&&(0,m.jsx)(`p`,{className:`rail-empty`,"data-file-notice":`1`,children:a}),(0,m.jsx)(`div`,{className:`files-tree`,"data-file-tree":x?`search`:`browse`,children:S}),r.length===0&&(0,m.jsx)(`p`,{className:`rail-empty`,"data-file-none-open":`1`,children:`点一个文本文件,它会在上面的标签栏开一个标签,正文显示在中间主画面。`}),r.length>0&&(0,m.jsxs)(`p`,{className:`files-hint`,"data-file-open-hint":`1`,children:[`已打开 `,r.length,` 个文件,正文显示在中间主画面;点上面的标签切换,点这里的行切换。`]}),(0,m.jsxs)(`p`,{className:`files-footnote`,"data-files-footnote":`1`,children:[`只支持纯文本(md / 代码),二进制文件不会打开。写入只允许覆盖已存在的文件,不新建,不删除。`,r.length>0&&` 当前打开 ${r.length} 个文件,占 ${Et(r.reduce((e,t)=>e+t.content.length,0))}。`]})]})}function Pt(e,t,n=88,r=200){let i=Math.max(0,Math.floor(e));if(!Number.isFinite(t)||t<=0)return{perRow:Math.max(1,i),width:n};if(i===0)return{perRow:1,width:n};if(i===1)return{perRow:1,width:Math.min(r,t)};let a=t/i;if(a>=n)return{perRow:i,width:Math.min(a,r)};for(let e=2;e<=i;e+=1){let a=Math.ceil(i/e),o=t/a;if(o>=n)return{perRow:a,width:Math.min(o,r)}}return{perRow:1,width:n}}function Ft(e,t){let n=Math.max(1,Math.floor(t)),r=[];for(let t=0;t<e.length;t+=n)r.push(e.slice(t,t+n));return r}function It(e,t,n=88,r=200){let[i,a]=(0,p.useState)(()=>Pt(e,0,n,r)),[o,s]=(0,p.useState)(null),c=(0,p.useCallback)(()=>{let i=Pt(e,t,n,r);s({count:e,containerWidth:t}),a(e=>e.perRow===i.perRow&&e.width===i.width?e:i)},[e,t,n,r]),l=(0,p.useRef)(t);(0,p.useEffect)(()=>{t<=0||l.current!==t&&(l.current=t,c())},[t,c]);let u=(0,p.useRef)(null);return(0,p.useEffect)(()=>{let t=u.current;u.current=e,t!==null&&e>t&&c()},[e,c]),{layout:i,frozen:o!==null&&o.count!==e,recompute:c}}function Lt({tabs:e,activeId:t,onSelect:n,onClose:r,ariaLabel:i,minWidth:a=88,maxWidth:o=200,prefix:s=`workspace-tab`,idPrefix:c=s,className:l}){let u=(0,p.useRef)(null),[d,f]=(0,p.useState)(0),{layout:h,frozen:g,recompute:_}=It(e.length,d,a,o);(0,p.useLayoutEffect)(()=>{let e=u.current;if(!e||typeof ResizeObserver>`u`)return;let t=()=>{let t=e.clientWidth;t>0&&f(t)};t();let n=new ResizeObserver(t);return n.observe(e),()=>n.disconnect()},[]);let v=Ft(e,h.perRow),y=(e,t)=>t===void 0?{}:{[`data-${s}-${e}`]:t},b=e.filter(e=>e.kind===`file`).length,x=e.filter(e=>e.kind===`session`||!e.kind).length,S=t=>({[`data-${t}-strip`]:`1`,...g?{[`data-${t}-frozen`]:`1`}:{},[`data-${t}-count`]:t===`file-tab`?b:t===`session-tab`?x:e.length,[`data-${t}-min`]:a,[`data-${t}-max`]:o,[`data-${t}-per-row`]:h.perRow,[`data-${t}-width`]:Math.round(h.width*100)/100}),C=e=>{let t=e.relatedTarget;(!t||t.isConnected)&&_()},w=e=>{let t=e.relatedTarget;(!t||t.isConnected)&&_()};(0,p.useEffect)(()=>{if(!g)return;let e=u.current;if(!e)return;let t=t=>{let n=e.getBoundingClientRect();t.clientX>=n.left&&t.clientX<=n.right&&t.clientY>=n.top&&t.clientY<=n.bottom||_()};return document.addEventListener(`mousemove`,t,!0),()=>document.removeEventListener(`mousemove`,t,!0)},[g,_]);let T=[`${s}-strip`,`workspace-tab-strip`,`session-tab-strip`,`file-tab-strip`,l].filter(Boolean).join(` `);return(0,m.jsx)(`div`,{ref:u,className:T,role:`tablist`,"aria-label":i??`工作区标签页`,...y(`strip`,`1`),...S(`workspace-tab`),...S(`session-tab`),...S(`file-tab`),style:{"--tab-w":`${h.width}px`},onMouseEnter:C,onMouseLeave:w,children:v.map((i,a)=>(0,m.jsx)(`div`,{className:`${s}-row workspace-tab-row session-tab-row file-tab-row`,role:`presentation`,...y(`row`,a),children:i.map(i=>{let a=i.id===t||i.rawId!==void 0&&i.rawId===t,o=i.title??i.label,l=i.kind,u=i.rawId??i.id,d=e.findIndex(e=>e.id===i.id),f=[s,a?`${s}-active`:``,`workspace-tab`,a?`workspace-tab-active`:``,l?`${l}-tab`:``,l&&a?`${l}-tab-active`:``].filter(Boolean).join(` `);return(0,m.jsxs)(`div`,{role:`tab`,id:`${c}-${i.id}`,"aria-selected":a,"aria-controls":`${c}-panel-${i.id}`,tabIndex:a?0:-1,className:f,...y(`id`,u),...y(`index`,d),...a?y(`active`,`1`):{},...y(`closable`,i.closable===!1?`0`:`1`),...l?{"data-tab-kind":l}:{},...l===`session`||!l?{"data-session-tab-id":u,"data-session-tab-index":d,...a?{"data-session-tab-active":`1`}:{},"data-session-tab-closable":i.closable===!1?`0`:`1`}:{},...l===`file`?{"data-file-tab-id":u,"data-file-tab-index":d,...a?{"data-file-tab-active":`1`}:{},"data-file-tab-closable":i.closable===!1?`0`:`1`}:{},title:o,style:{width:h.width,boxSizing:`border-box`},onClick:()=>n?.(i.id),onAuxClick:e=>{e.button===1&&(e.preventDefault(),e.stopPropagation(),i.closable!==!1&&r&&r(i.id))},onMouseDown:e=>{e.button===1&&e.preventDefault()},onKeyDown:t=>{if(t.key!==`ArrowLeft`&&t.key!==`ArrowRight`)return;t.preventDefault();let r=t.key===`ArrowRight`?1:-1,a=e[(e.findIndex(e=>e.id===i.id)+r+e.length)%e.length];a&&n?.(a.id)},children:[i.icon&&(0,m.jsx)(`span`,{className:`${s}-icon workspace-tab-icon`,children:i.icon}),i.badge,(0,m.jsx)(`span`,{className:`${s}-label workspace-tab-label ${l?`${l}-tab-label`:``}`,...y(`label`,u),...l===`session`||!l?{"data-session-tab-label":u}:{},...l===`file`?{"data-file-tab-label":u}:{},children:i.label}),i.closable!==!1&&r&&(0,m.jsx)(`button`,{type:`button`,className:`${s}-close workspace-tab-close ${l?`${l}-tab-close`:``}`,...y(`close`,u),...l===`session`||!l?{"data-session-tab-close":u}:{},...l===`file`?{"data-file-tab-close":u}:{},onClick:e=>{e.stopPropagation(),r(i.id)},title:`关闭 ${o}`,"aria-label":`关闭 ${o}`,children:`×`})]},i.id)})},`row_${a}`))})}var Rt={pending:`未开始`,running:`运行中`,completed:`已完成`,failed:`失败`,skipped:`已跳过`,stopped:`已停止`},zt={running:`运行中`,completed:`全部完成`,failed:`中途失败`,stopped:`已停止`},Bt={pending:`○`,running:`◐`,completed:`✓`,failed:`✕`,skipped:`→`,stopped:`■`},Vt={stop:`失败即停`,skip:`失败跳过`,"retry:1":`失败重试 1 次`,"retry:2":`失败重试 2 次`,"retry:3":`失败重试 3 次`};function Ht(e){let t=Math.max(0,Math.round(e/1e3)),n=t%60,r=Math.floor(t/60)%60,i=Math.floor(t/3600),a=e=>String(e).padStart(2,`0`);return i>0?`${i}h ${a(r)}:${a(n)}`:`${r}:${a(n)}`}function Ut(e){let t=e.status===`running`,[n,r]=(0,p.useState)(0);(0,p.useEffect)(()=>{if(!t)return;r(Date.now());let e=window.setInterval(()=>r(Date.now()),200);return()=>window.clearInterval(e)},[t,e.startedAt]);let i=Date.parse(e.startedAt);if(Number.isFinite(i)){if(!t){let t=Date.parse(e.finishedAt??``);return Number.isFinite(t)?t-i:void 0}return n>0?n-i:void 0}}function Wt({step:e,names:t}){let n=e.attempts.length,r=e.attempts[n-1],i=t.get(e.agentId)||e.agentId,a=e.label?.trim()||i;return(0,m.jsxs)(`li`,{className:`workflow-step workflow-step-${e.status}`,"data-workflow-step":e.index+1,"data-workflow-step-status":e.status,"data-workflow-step-agent":e.agentId,"data-workflow-step-attempts":n,children:[(0,m.jsx)(`span`,{className:`workflow-step-mark`,"aria-hidden":`true`,children:Bt[e.status]}),(0,m.jsxs)(`span`,{className:`workflow-step-n`,"data-workflow-step-n":e.index+1,children:[`第 `,e.index+1,` 步`]}),(0,m.jsx)(`strong`,{className:`workflow-step-name`,"data-workflow-step-name":e.index+1,children:a}),(0,m.jsx)(`span`,{className:`workflow-step-agent`,"data-workflow-step-agent-name":e.index+1,children:i}),n>1&&(0,m.jsxs)(`span`,{className:`workflow-step-tries`,"data-workflow-step-retried":e.index+1,children:[`尝试 `,n,` 次`]}),(0,m.jsx)(`span`,{className:`workflow-step-status`,"data-workflow-step-status-text":e.index+1,children:Rt[e.status]}),e.status===`failed`&&r?.error&&(0,m.jsx)(`span`,{className:`workflow-step-why`,"data-workflow-step-error":e.index+1,children:r.error})]})}function Gt({workflow:e,names:t,onStop:n,stopping:r}){let i=e.status===`running`,a=Ut(e),o=e.steps.filter(e=>e.status===`completed`||e.status===`skipped`).length;return(0,m.jsxs)(`article`,{className:`workflow-card workflow-${e.status}`,"data-workflow-card":e.id,"data-workflow-status":e.status,"data-workflow-steps":e.steps.length,"data-workflow-current":e.currentIndex+1,children:[(0,m.jsxs)(`div`,{className:`workflow-head`,children:[(0,m.jsx)(`span`,{className:`workflow-badge`,children:`编排`}),(0,m.jsx)(`strong`,{className:`workflow-summary`,"data-workflow-summary":e.id,children:e.summary}),(0,m.jsxs)(`span`,{className:`workflow-progress`,"data-workflow-progress":e.id,children:[o,`/`,e.steps.length]}),(0,m.jsx)(`span`,{className:`workflow-status`,"data-workflow-status-text":e.id,children:zt[e.status]??e.status}),(0,m.jsx)(`span`,{className:`workflow-policy`,"data-workflow-policy":e.onFailure,children:Vt[e.onFailure]??e.onFailure}),a!==void 0&&(0,m.jsxs)(`span`,{className:`workflow-clock`,"data-workflow-elapsed":e.id,children:[i?`已运行 `:`用时 `,Ht(a)]}),i&&(0,m.jsx)(`button`,{type:`button`,className:`workflow-stop`,"data-workflow-stop":e.id,disabled:r,onClick:n,children:`停止`})]}),(0,m.jsx)(`ol`,{className:`workflow-steps`,"data-workflow-step-count":e.steps.length,children:e.steps.map(e=>(0,m.jsx)(Wt,{step:e,names:t},e.index))})]})}var j=[{id:`agents`,label:`智能体与创建`,icon:`🤖`,title:`Agent 智能体系统与创建指南`,description:`了解 Super Pi 的角色设定机制,以及如何一步步创建你自己的自定义 Agent。`},{id:`workspace`,label:`工作区与会话`,icon:`🗂️`,title:`工作区与多会话管理`,description:`高效管理本地代码仓库、独立会话标签页与多模态图文输入。`},{id:`subagents`,label:`子任务与编排`,icon:`⚡`,title:`子智能体委托与顺序编排使用指南`,description:`让主 Agent 安排分工、交接结果,并查看进度、处理失败与停止任务。`},{id:`model-policy`,label:`模型使用规则`,icon:`🔒`,title:`模型过滤规则(按工作区限制可用模型)`,description:`用正则规则限定哪些工作区只能用哪些模型,防止敏感代码被发到不该用的模型上。`},{id:`memory`,label:`主动记忆`,icon:`🧠`,title:`主动记忆与技能经验`,description:`让 Super Pi 在得到明确证据后保存长期事实、用户偏好和可复用工作方法。`},{id:`knowledge`,label:`知识库`,icon:`📚`,title:`工作区知识库`,description:`导入规范与设计文档,让 Agent 检索原文作答,而不是凭记忆编造。`},{id:`tools`,label:`侧边栏工具`,icon:`🧩`,title:`全局工具:开发、导出与导入`,description:`让 Agent 写一个工具,用一个文件分享给别人,导入前先看清它会做什么。`},{id:`cli`,label:`指令与快捷键`,icon:`⌨️`,title:`Pi 终端命令与键盘快捷操作`,description:`利用 Pi 终端斜杠指令控制服务生命周期,掌握常用快捷键提升效率。`},{id:`themes`,label:`外观与主题`,icon:`🎨`,title:`界面主题与个性化显示`,description:`内置 4 款专业高对比度主题,针对不同环境提供舒适体验。`}];function Kt({onClose:e}){let[t,n]=(0,p.useState)(`agents`);(0,p.useEffect)(()=>{let t=t=>{t.key===`Escape`&&!t.defaultPrevented&&e()};return window.addEventListener(`keydown`,t),()=>window.removeEventListener(`keydown`,t)},[e]);let r=j.find(e=>e.id===t)??j[0];return(0,m.jsx)(`div`,{className:`overlay`,onClick:e,children:(0,m.jsxs)(`div`,{className:`picker settings-window help-window`,"data-help-window":`1`,onClick:e=>e.stopPropagation(),role:`dialog`,"aria-modal":`true`,"aria-labelledby":`help-window-title`,children:[(0,m.jsxs)(`div`,{className:`settings-window-head help-window-head`,children:[(0,m.jsxs)(`div`,{className:`help-head-title`,children:[(0,m.jsx)(`h2`,{id:`help-window-title`,children:`使用指南与帮助`}),(0,m.jsx)(`span`,{className:`help-badge`,children:`v0.1.x`})]}),(0,m.jsx)(`button`,{type:`button`,className:`help-close-button`,"aria-label":`关闭指南`,onClick:e,children:`✕`})]}),(0,m.jsxs)(`div`,{className:`settings-window-body help-window-body`,children:[(0,m.jsxs)(`nav`,{className:`settings-nav help-nav`,role:`tablist`,"aria-orientation":`vertical`,"aria-label":`使用指南分类`,children:[(0,m.jsx)(`div`,{className:`settings-nav-label`,children:`目录`}),j.map(e=>(0,m.jsxs)(`button`,{type:`button`,id:`help-tab-${e.id}`,className:`settings-category help-category`,role:`tab`,"aria-selected":e.id===r.id,"aria-controls":`help-panel`,tabIndex:e.id===r.id?0:-1,onClick:()=>n(e.id),children:[(0,m.jsx)(`span`,{className:`help-tab-icon`,"aria-hidden":`true`,children:e.icon}),(0,m.jsx)(`span`,{children:e.label})]},e.id))]}),(0,m.jsxs)(`div`,{id:`help-panel`,className:`settings-panel help-panel`,role:`tabpanel`,"aria-labelledby":`help-tab-${r.id}`,children:[(0,m.jsxs)(`div`,{className:`help-panel-header`,children:[(0,m.jsx)(`h3`,{className:`help-panel-title`,children:r.title}),(0,m.jsx)(`p`,{className:`help-panel-desc`,children:r.description})]}),(0,m.jsxs)(`div`,{className:`help-panel-content`,children:[r.id===`agents`&&(0,m.jsx)(Yt,{}),r.id===`workspace`&&(0,m.jsx)(Xt,{}),r.id===`subagents`&&(0,m.jsx)(Qt,{}),r.id===`model-policy`&&(0,m.jsx)(Zt,{}),r.id===`memory`&&(0,m.jsx)(qt,{}),r.id===`knowledge`&&(0,m.jsx)(Jt,{}),r.id===`tools`&&(0,m.jsx)(M,{}),r.id===`cli`&&(0,m.jsx)($t,{}),r.id===`themes`&&(0,m.jsx)(en,{})]}),(0,m.jsx)(`div`,{className:`picker-actions help-actions`,children:(0,m.jsx)(`button`,{type:`button`,className:`picker-cancel`,onClick:e,children:`关闭`})})]})]})]})})}function qt(){return(0,m.jsxs)(`div`,{className:`help-section-group`,children:[(0,m.jsxs)(`div`,{className:`help-card highlight`,children:[(0,m.jsx)(`h4`,{children:`🧠 它会记住什么?`}),(0,m.jsx)(`p`,{children:`用户档案保存跨工作区的长期偏好;工作区事实保存环境与项目约定;技能保存已经验证过的步骤、命令和坑。临时结果、秘密和未验证的失败不会保存。`})]}),(0,m.jsxs)(`div`,{className:`help-card`,children:[(0,m.jsx)(`h4`,{children:`主动保存与后台复盘`}),(0,m.jsxs)(`ul`,{className:`help-list`,children:[(0,m.jsxs)(`li`,{children:[`主 Agent 可以使用 `,(0,m.jsx)(`code`,{children:`memory`}),` 和 `,(0,m.jsx)(`code`,{children:`skill_manage`}),` 工具主动保存;说“记住了”本身不会写入。`]}),(0,m.jsx)(`li`,{children:`每隔若干回合,系统会在独立会话中复盘最近证据。复盘不能读写工作区文件、执行命令或派发任务。`}),(0,m.jsx)(`li`,{children:`读取记忆和历史只返回带来源的参考数据,不能把记忆中的文字当作新的指令。`})]})]}),(0,m.jsxs)(`div`,{className:`help-card`,children:[(0,m.jsx)(`h4`,{children:`权限与审批`}),(0,m.jsxs)(`ul`,{className:`help-list`,children:[(0,m.jsx)(`li`,{children:`子 Agent 可以读取相关记忆,但没有写入权限。`}),(0,m.jsx)(`li`,{children:`后台复盘新增记忆可以直接保存;修改或删除旧条目会进入设置中的待审批列表。`}),(0,m.jsx)(`li`,{children:`在“设置 → 主动记忆”可以开关功能、调整复盘间隔、编辑条目和批准提案。`})]})]})]})}function Jt(){return(0,m.jsxs)(`div`,{className:`help-section-group`,children:[(0,m.jsxs)(`div`,{className:`help-card highlight`,children:[(0,m.jsx)(`h4`,{children:`📚 它解决什么问题?`}),(0,m.jsx)(`p`,{children:`Agent 默认靠训练时的记忆回答项目里的问题。知识库让你把规范、设计稿、会议纪要导入工作区,Agent 在回答前检索这些文档的原文片段,而不是凭印象编造。它与“主动记忆”不同:记忆是 Agent 自己沉淀的事实和技能,知识库只有你能写入。`})]}),(0,m.jsxs)(`div`,{className:`help-card`,children:[(0,m.jsx)(`h4`,{children:`怎么用`}),(0,m.jsxs)(`ul`,{className:`help-list`,children:[(0,m.jsx)(`li`,{children:`在“设置 → 知识库”里点“浏览工作区文件…”,在弹出的浏览器里挑一个 Markdown 或文本文件,挑完即导入。也可以直接粘贴正文,或手动填写相对路径。`}),(0,m.jsx)(`li`,{children:`同一个文件内容改了再导入就是更新,不会留下旧版本;同样的内容换个标题再导入也不会变成两篇。`}),(0,m.jsx)(`li`,{children:`面板里的“试检索”会用和 Agent 相同的检索器回答,方便你在交给 Agent 之前先确认它找得到。`}),(0,m.jsxs)(`li`,{children:[`从下一条消息开始生效。Agent 使用 `,(0,m.jsx)(`code`,{children:`knowledge_search`}),` 检索,只拿得到片段和来源,拿不到整篇文档。`]})]})]}),(0,m.jsxs)(`div`,{className:`help-card`,children:[(0,m.jsx)(`h4`,{children:`语义检索是内置的`}),(0,m.jsxs)(`ul`,{className:`help-list`,children:[(0,m.jsxs)(`li`,{children:[`关键词检索始终可用。语义检索让“撤销购买”也能找到写着“取消订单”的段落,它靠一个`,(0,m.jsx)(`strong`,{children:`随安装包一起分发`}),`的中文模型在本机运行,不需要额外下载、不需要联网、也不会把文档发到任何服务。`]}),(0,m.jsxs)(`li`,{children:[`第一次检索时加载模型(约一秒),之后一直可用。设 `,(0,m.jsx)(`code`,{children:`SUPER_PI_KNOWLEDGE_EMBEDDINGS=0`}),` 可以彻底关闭语义检索,只保留关键词检索。`]})]})]}),(0,m.jsxs)(`div`,{className:`help-card`,children:[(0,m.jsx)(`h4`,{children:`它不知道的事`}),(0,m.jsxs)(`ul`,{className:`help-list`,children:[(0,m.jsx)(`li`,{children:`知识库只会检索它有的东西。文档里没写的,它检索出来也不会有——所以导入什么,直接决定它能回答什么。`}),(0,m.jsx)(`li`,{children:`语义检索总会返回“最接近的那一段”,即使这一段其实并不回答问题。检索结果带一个分数,低分意味着这可能只是最像的一条,Agent 会被要求先核对再使用。`}),(0,m.jsx)(`li`,{children:`Agent 对知识库只读,不能写入或删除;导入和删除只有你能做。`})]})]})]})}function Yt(){return(0,m.jsxs)(`div`,{className:`help-section-group`,children:[(0,m.jsxs)(`div`,{className:`help-card highlight`,children:[(0,m.jsx)(`h4`,{children:`💡 Agent 是什么?`}),(0,m.jsxs)(`p`,{children:[`在 Super Pi 中,`,(0,m.jsx)(`strong`,{children:`Agent(智能体)`}),` 是一套独立的「角色提示词设定与能力配置」。 通过切换 Agent,模型将扮演专精工程师、系统架构师、代码审查员或规划专家等不同角色,遵循指定的原则来协助你完成任务。`]})]}),(0,m.jsxs)(`div`,{className:`help-card`,children:[(0,m.jsx)(`h4`,{children:`📁 Agent 文件存放在哪里?`}),(0,m.jsxs)(`ul`,{className:`help-list`,children:[(0,m.jsxs)(`li`,{children:[(0,m.jsx)(`strong`,{children:`内置 Agent 目录:`}),`项目根目录下的 `,(0,m.jsx)(`code`,{children:`agents/`}),` 文件夹,例如 `,(0,m.jsx)(`code`,{children:`agents/coder.md`}),`、`,(0,m.jsx)(`code`,{children:`agents/planner.md`}),`、`,(0,m.jsx)(`code`,{children:`agents/reviewer.md`}),` 等。`]}),(0,m.jsxs)(`li`,{children:[(0,m.jsx)(`strong`,{children:`用户自定义持久化目录:`}),`用户主目录下的 `,(0,m.jsx)(`code`,{children:`~/.super-pi/agents/`}),`,放入此目录的文件在所有项目和更新中均持久有效。`]})]})]}),(0,m.jsxs)(`div`,{className:`help-card`,children:[(0,m.jsx)(`h4`,{children:`🚀 如何创建一个全新的自定义 Agent?`}),(0,m.jsxs)(`div`,{className:`help-steps`,children:[(0,m.jsxs)(`div`,{className:`help-step`,children:[(0,m.jsx)(`span`,{className:`step-num`,children:`1`}),(0,m.jsxs)(`div`,{className:`step-body`,children:[(0,m.jsx)(`strong`,{children:`创建 Markdown 文件`}),(0,m.jsxs)(`p`,{children:[`在 `,(0,m.jsx)(`code`,{children:`agents/`}),` 或 `,(0,m.jsx)(`code`,{children:`~/.super-pi/agents/`}),` 目录下新建文件,例如 `,(0,m.jsx)(`code`,{children:`architect.md`}),`。`]})]})]}),(0,m.jsxs)(`div`,{className:`help-step`,children:[(0,m.jsx)(`span`,{className:`step-num`,children:`2`}),(0,m.jsxs)(`div`,{className:`step-body`,children:[(0,m.jsx)(`strong`,{children:`编写元数据与系统提示词`}),(0,m.jsx)(`p`,{children:`文件开头使用 YAML Frontmatter 声明名称和描述,正文编写设定提示词:`}),(0,m.jsx)(`pre`,{className:`help-code-block`,children:(0,m.jsx)(`code`,{children:`---
26
26
  name: 系统架构师
27
27
  description: 专注于系统架构拆解、模块解耦与技术选型分析
28
28
  ---
@@ -8,7 +8,7 @@
8
8
  <link rel="alternate icon" href="/favicon.ico" />
9
9
  <link rel="apple-touch-icon" href="/favicon.svg" />
10
10
  <title>Super Pi</title>
11
- <script type="module" crossorigin src="/assets/index-pD4FaDWr.js"></script>
11
+ <script type="module" crossorigin src="/assets/index-mAu39Bra.js"></script>
12
12
  <link rel="stylesheet" crossorigin href="/assets/index-DDkbPYLz.css">
13
13
  </head>
14
14
  <body>
@@ -1,6 +1,7 @@
1
1
  import { type Store } from "../store.js";
2
2
  import type { AgentRuntime } from "../runtime.js";
3
3
  import type { SubagentRunRecorder } from "../subagent-runs.js";
4
+ import type { ModelSelection } from "../types.js";
4
5
  import type { EventBroadcaster } from "../server-context.js";
5
6
  import type { CatalogService } from "./catalog-service.js";
6
7
  import type { RunService } from "./run-service.js";
@@ -45,6 +46,14 @@ export declare class ConversationService {
45
46
  * a log and in a retry.
46
47
  */
47
48
  autoRenameConversation(conversationId: string): Promise<string>;
49
+ /**
50
+ * The model a conversation has selected, or undefined when it follows Pi's default.
51
+ *
52
+ * THIS IS WHAT THE CATALOG'S THINKING LEVELS DEPEND ON. Pi computes the levels from the model
53
+ * itself, so the levels of the model in force are the levels that conversation gets. A catalog read
54
+ * without this is a catalog of whatever Pi would default to.
55
+ */
56
+ modelSelectionFor(conversationId: string | undefined): ModelSelection | undefined;
48
57
  /**
49
58
  * Selects a conversation and broadcasts the catalog for that conversation's model.
50
59
  */
@@ -107,6 +107,19 @@ export class ConversationService {
107
107
  throw new Error("当前没有可用的 Pi 进程,无法生成标题");
108
108
  return this.runtime.summarizeConversation(conversation, workspace.path);
109
109
  }
110
+ /**
111
+ * The model a conversation has selected, or undefined when it follows Pi's default.
112
+ *
113
+ * THIS IS WHAT THE CATALOG'S THINKING LEVELS DEPEND ON. Pi computes the levels from the model
114
+ * itself, so the levels of the model in force are the levels that conversation gets. A catalog read
115
+ * without this is a catalog of whatever Pi would default to.
116
+ */
117
+ modelSelectionFor(conversationId) {
118
+ if (!conversationId)
119
+ return undefined;
120
+ const conversation = this.store.snapshot().conversations.find((item) => item.id === conversationId);
121
+ return conversation?.model;
122
+ }
110
123
  /**
111
124
  * Selects a conversation and broadcasts the catalog for that conversation's model.
112
125
  */
@@ -219,6 +219,57 @@ export declare class RunService {
219
219
  * the moment Pi holds it.
220
220
  */
221
221
  onPiQueue(conversationId: string, snapshot: PiQueueSnapshot): void;
222
+ /**
223
+ * The message a delivery is about, resolved by the text Pi announced.
224
+ *
225
+ * WHY THE TEXT AND NOT THE POSITION. A message that was STEERED is `sending` here - claimed, so the
226
+ * user cannot withdraw it - and the oldest `sending` message is the one Pi picked up, because Pi's
227
+ * steering queue is FIFO. That is the old rule and it still holds. A message that was handed to Pi's
228
+ * FOLLOW-UP queue is NOT `sending`: Super Pi keeps it in its own list so the user can still withdraw
229
+ * it, and a conversation can hold a `waiting` message and a `sending` one at the same time. For that
230
+ * one, position is the wrong question and the text is the right one.
231
+ *
232
+ * THE TEXT IS THE SAME TEXT ON BOTH SIDES. Pi matches its own queue by `indexOf` on the message text
233
+ * (`agent-session.ts:1123`), so two messages with identical words are ONE entry to Pi and the first of
234
+ * them to Super Pi as well. Resolving by text is therefore not a guess that could pick a different
235
+ * message than Pi did - it is the same comparison Pi itself made.
236
+ *
237
+ * A `sending` message wins over a `waiting` one with the same text, because Pi drains its steering
238
+ * queue before its follow-up queue, so the steering message is the one that was delivered first.
239
+ */
240
+ private matchDeliveredMessage;
241
+ /**
242
+ * Hands a waiting message to Pi's follow-up queue, so the run that is already going answers it.
243
+ *
244
+ * WHY THIS EXISTS. A message typed while the agent was busy used to wait for the current turn to END
245
+ * and then be answered by a turn of its own - a fresh `prompt()`, which runs Pi's `_prepareRun`, which
246
+ * emits `before_agent_start`. Super Pi's memory extension answers that hook with an HTTP search and
247
+ * patches the system prompt with what came back, so every queued message paid for a round trip to the
248
+ * memory endpoint and for a prompt larger than the one before it. A follow-up is delivered by Pi's
249
+ * agent loop inside the running turn, so it pays for neither.
250
+ *
251
+ * IT IS AN OPTIMISATION, NOT A COMMITMENT. The message stays in Super Pi's own waiting list, so it is
252
+ * still the user's to withdraw and still the user's to interrupt, and if Pi does not deliver it - the
253
+ * run was aborted, the queues were cleared - the message falls back to being answered by a turn of its
254
+ * own. Nothing is lost either way, which is what makes it safe to try.
255
+ */
256
+ private submitToPiQueue;
257
+ /**
258
+ * Empties Pi's steering and follow-up queues for a conversation.
259
+ *
260
+ * WHY A HOST HAS TO DO THIS. Pi delivers whatever is left in those queues when the run ends, and it
261
+ * cannot take back a single message - `clear_queue` is the only queue command it has. So whenever
262
+ * Super Pi takes a message back, the queues have to be emptied, or Pi answers words the user believes
263
+ * they took back.
264
+ *
265
+ * NOTHING IS LOST. Every message Pi was holding is still in Super Pi's own waiting list, and the run's
266
+ * loop answers those as turns of their own.
267
+ *
268
+ * THE STEERS COME BACK. Emptying the queues also empties the steering queue, and a steer is a message
269
+ * the user asked to be answered NOW - so the ones Pi had not delivered go back in, and clearing the
270
+ * queues for one message does not silently un-steer another.
271
+ */
272
+ private clearPiQueues;
222
273
  /**
223
274
  * Opens a turn on a message that is ALREADY IN THE TRANSCRIPT.
224
275
  *
@@ -285,6 +285,11 @@ export class RunService {
285
285
  console.warn(`Super Pi: auto-trigger for background exec result failed: ${error instanceof Error ? error.message : "unknown error"}`);
286
286
  });
287
287
  }
288
+ else {
289
+ // THE CONVERSATION IS BUSY, so the run that is going answers this result itself - through
290
+ // Pi's own follow-up queue, so the result does not have to wait for a turn of its own.
291
+ await this.submitToPiQueue(task.conversationId, queued);
292
+ }
288
293
  }
289
294
  catch (error) {
290
295
  console.warn(`Super Pi: exec result could not be appended as a tool message: ${error instanceof Error ? error.message : "unknown error"}`);
@@ -541,6 +546,118 @@ export class RunService {
541
546
  onPiQueue(conversationId, snapshot) {
542
547
  this.broadcaster.broadcast({ type: "pi.queue", conversationId, ...snapshot });
543
548
  }
549
+ /**
550
+ * The message a delivery is about, resolved by the text Pi announced.
551
+ *
552
+ * WHY THE TEXT AND NOT THE POSITION. A message that was STEERED is `sending` here - claimed, so the
553
+ * user cannot withdraw it - and the oldest `sending` message is the one Pi picked up, because Pi's
554
+ * steering queue is FIFO. That is the old rule and it still holds. A message that was handed to Pi's
555
+ * FOLLOW-UP queue is NOT `sending`: Super Pi keeps it in its own list so the user can still withdraw
556
+ * it, and a conversation can hold a `waiting` message and a `sending` one at the same time. For that
557
+ * one, position is the wrong question and the text is the right one.
558
+ *
559
+ * THE TEXT IS THE SAME TEXT ON BOTH SIDES. Pi matches its own queue by `indexOf` on the message text
560
+ * (`agent-session.ts:1123`), so two messages with identical words are ONE entry to Pi and the first of
561
+ * them to Super Pi as well. Resolving by text is therefore not a guess that could pick a different
562
+ * message than Pi did - it is the same comparison Pi itself made.
563
+ *
564
+ * A `sending` message wins over a `waiting` one with the same text, because Pi drains its steering
565
+ * queue before its follow-up queue, so the steering message is the one that was delivered first.
566
+ */
567
+ matchDeliveredMessage(conversationId, text) {
568
+ if (!text)
569
+ return undefined;
570
+ const waiting = this.store.listWaitingMessages(conversationId);
571
+ const match = waiting.find((item) => item.delivery === "sending" && composePrompt(item) === text)
572
+ ?? waiting.find((item) => item.delivery === "waiting" && composePrompt(item) === text);
573
+ return match?.id;
574
+ }
575
+ /**
576
+ * Hands a waiting message to Pi's follow-up queue, so the run that is already going answers it.
577
+ *
578
+ * WHY THIS EXISTS. A message typed while the agent was busy used to wait for the current turn to END
579
+ * and then be answered by a turn of its own - a fresh `prompt()`, which runs Pi's `_prepareRun`, which
580
+ * emits `before_agent_start`. Super Pi's memory extension answers that hook with an HTTP search and
581
+ * patches the system prompt with what came back, so every queued message paid for a round trip to the
582
+ * memory endpoint and for a prompt larger than the one before it. A follow-up is delivered by Pi's
583
+ * agent loop inside the running turn, so it pays for neither.
584
+ *
585
+ * IT IS AN OPTIMISATION, NOT A COMMITMENT. The message stays in Super Pi's own waiting list, so it is
586
+ * still the user's to withdraw and still the user's to interrupt, and if Pi does not deliver it - the
587
+ * run was aborted, the queues were cleared - the message falls back to being answered by a turn of its
588
+ * own. Nothing is lost either way, which is what makes it safe to try.
589
+ */
590
+ async submitToPiQueue(conversationId, message) {
591
+ if (!this.runtime.followUp)
592
+ return;
593
+ // NOTHING TO FOLLOW UP ON. With no turn running, Pi would hold the message until the NEXT one,
594
+ // which is a later answer than the one the message is waiting for.
595
+ if (!this.activeRuns.has(conversationId))
596
+ return;
597
+ // AN INTERRUPT IS ALREADY STARTING. It aborts the run and empties Pi's queues, so a message handed
598
+ // to Pi now would be taken back a moment later. It waits in the list for the next run, exactly as it
599
+ // did before.
600
+ if (this.interruptTargets.has(conversationId))
601
+ return;
602
+ // A MESSAGE THAT IS A COMMAND IS NOT QUEUED. Pi expands a skill command or a prompt template before
603
+ // it queues the text (`agent-session.ts:2221-2222`), and it REFUSES an extension command outright.
604
+ // An expanded text is not the text Super Pi has, so the delivery could not be resolved back to its
605
+ // message - the answer would land under the wrong question. Such a message waits for a turn of its
606
+ // own, where it is the prompt and the question and the answer are the same thing.
607
+ if (composePrompt(message).startsWith("/"))
608
+ return;
609
+ try {
610
+ await this.runtime.followUp(conversationId, composePrompt(message), message.images);
611
+ }
612
+ catch (error) {
613
+ // THE FALLBACK IS THE OLD BEHAVIOUR, so a refusal here is not a failure: the message stays in the
614
+ // waiting list and the run's loop answers it with a turn of its own.
615
+ console.warn(`Super Pi: handing a queued message to Pi's follow-up queue failed: ${error instanceof Error ? error.message : "unknown error"}`);
616
+ }
617
+ }
618
+ /**
619
+ * Empties Pi's steering and follow-up queues for a conversation.
620
+ *
621
+ * WHY A HOST HAS TO DO THIS. Pi delivers whatever is left in those queues when the run ends, and it
622
+ * cannot take back a single message - `clear_queue` is the only queue command it has. So whenever
623
+ * Super Pi takes a message back, the queues have to be emptied, or Pi answers words the user believes
624
+ * they took back.
625
+ *
626
+ * NOTHING IS LOST. Every message Pi was holding is still in Super Pi's own waiting list, and the run's
627
+ * loop answers those as turns of their own.
628
+ *
629
+ * THE STEERS COME BACK. Emptying the queues also empties the steering queue, and a steer is a message
630
+ * the user asked to be answered NOW - so the ones Pi had not delivered go back in, and clearing the
631
+ * queues for one message does not silently un-steer another.
632
+ */
633
+ async clearPiQueues(conversationId) {
634
+ if (!this.runtime.clearQueues)
635
+ return;
636
+ let cleared;
637
+ try {
638
+ cleared = await this.runtime.clearQueues(conversationId);
639
+ }
640
+ catch (error) {
641
+ console.warn(`Super Pi: clearing Pi's queues for ${conversationId} failed: ${error instanceof Error ? error.message : "unknown error"}`);
642
+ return;
643
+ }
644
+ for (const text of cleared.steering ?? []) {
645
+ // A STEER NEEDS A TURN TO GO INTO. With no turn running there is nothing to steer, and the message
646
+ // is released back to `waiting` when the run ends anyway - so it is answered as a turn of its own.
647
+ if (this.runtime.canSteer && !this.runtime.canSteer(conversationId))
648
+ continue;
649
+ const message = this.store.listWaitingMessages(conversationId)
650
+ .find((item) => item.delivery === "sending" && composePrompt(item) === text);
651
+ if (!message)
652
+ continue;
653
+ try {
654
+ await this.runtime.steer?.(conversationId, text, message.images);
655
+ }
656
+ catch (error) {
657
+ console.warn(`Super Pi: putting a steered message back failed: ${error instanceof Error ? error.message : "unknown error"}`);
658
+ }
659
+ }
660
+ }
544
661
  /**
545
662
  * Opens a turn on a message that is ALREADY IN THE TRANSCRIPT.
546
663
  *
@@ -825,18 +942,28 @@ export class RunService {
825
942
  * cleared rather than reassigned for the same reason. `thinkingBlock` continues numbering on purpose:
826
943
  * it is a global fold sequence, and restarting it would make the browser render two folds numbered 0.
827
944
  */
828
- const onSteerDelivered = () => {
829
- enqueueTools("a steer being delivered", async () => {
945
+ const onSteerDelivered = (text) => {
946
+ enqueueTools("a queued message being delivered", async () => {
830
947
  const previous = assistant;
831
- // Checkpoint first: the message being left behind is complete - Pi drains steering at a turn
948
+ // Checkpoint first: the message being left behind is complete - Pi drains its queues at a turn
832
949
  // boundary - and this is the last chance to write it durably with the lane it belongs to.
833
950
  await this.store.checkpointMessage(conversation.id, previous.id);
834
- const delivered = await this.store.deliverWaitingMessage(conversation.id, agentIdentity);
951
+ // WHICH MESSAGE THIS DELIVERY IS ABOUT. Pi announces the text it picked up, and Pi itself matches
952
+ // its own queue by that text (`agent-session.ts:1123`), so the same text resolves to the same
953
+ // message on both sides - including when two messages carry identical words, where both sides
954
+ // pick the first. A message handed to Pi's FOLLOW-UP queue was never claimed here: it is still
955
+ // `waiting`, so the user can still withdraw it, and a conversation can hold a `waiting` and a
956
+ // `sending` message at the same time. Position cannot answer that one; the text can.
957
+ //
958
+ // WHEN THE TEXT MATCHES NOTHING the old position-based answer is used, which is the steer path's
959
+ // proven behaviour: the message Pi is holding is the one Pi picked up. A text that matches
960
+ // nothing AND has no `sending` message is a delivery of a message Super Pi no longer has - one
961
+ // the user withdrew - and there is then nothing to open an answer to.
962
+ const target = this.matchDeliveredMessage(conversation.id, text);
963
+ const delivered = await this.store.deliverWaitingMessage(conversation.id, agentIdentity, target);
835
964
  // NOTHING TO DELIVER MEANS NOTHING TO SPLIT. Pi may announce a user `message_start` that this
836
965
  // server never handed it - the prompt that opened the turn is the obvious case - and inventing an
837
- // assistant message for one of those would open an empty bubble before every single turn. The
838
- // question "was anything `sending`?" is the server's own state, not something the event decides,
839
- // which is the same reason the delivery is matched by position rather than by text.
966
+ // assistant message for one of those would open an empty bubble before every single turn.
840
967
  if (!delivered)
841
968
  return [];
842
969
  const next = delivered.assistant;
@@ -978,6 +1105,13 @@ export class RunService {
978
1105
  for (;;) {
979
1106
  if (controller.signal.aborted)
980
1107
  break;
1108
+ // PI'S QUEUES ARE EMPTIED BEFORE THE NEXT MESSAGE IS TAKEN. A message handed to Pi's follow-up
1109
+ // queue is delivered by the agent loop inside the run that has just ended - Pi drains that queue
1110
+ // before it settles - so whatever is STILL in the queue when the turn ended was not delivered.
1111
+ // Emptying it gives those messages back: they are still in the waiting list, and the loop below
1112
+ // answers them with a turn of their own, which is the behaviour there was before Pi's queue was
1113
+ // used at all. Nothing is lost, and nothing is delivered twice.
1114
+ await this.clearPiQueues(conversation.id);
981
1115
  // Taken AND opened, in that order, by two separate writes. The take is what bounds this loop -
982
1116
  // every pass strictly shrinks the waiting set - and it is what makes a crash honest, because a
983
1117
  // server that dies between the two has a message that is neither waiting nor answered, and the
@@ -1045,6 +1179,10 @@ export class RunService {
1045
1179
  await this.store.markMessageWaiting(conversationId, message.id);
1046
1180
  this.broadcaster.broadcastState();
1047
1181
  }
1182
+ // The turn that is running answers this message itself, through Pi's own follow-up queue, rather
1183
+ // than by a turn of its own once the running one is over. The message stays in the waiting list
1184
+ // either way, so it is still the user's to withdraw - Pi is holding a copy, not the only copy.
1185
+ await this.submitToPiQueue(conversationId, message);
1048
1186
  return;
1049
1187
  }
1050
1188
  const controller = new AbortController();
@@ -1199,6 +1337,12 @@ export class RunService {
1199
1337
  try {
1200
1338
  if (this.activeRuns.has(conversationId))
1201
1339
  await this.stopRun(conversationId);
1340
+ // PI'S QUEUES ARE EMPTIED BEFORE THE SELECTED MESSAGE IS ANSWERED ON ITS OWN. The message may be
1341
+ // in Pi's follow-up queue - it was handed over when it was written - and Pi would deliver it when
1342
+ // the aborted run ended. The message is about to be answered as a turn of its own, so the queue
1343
+ // copy has to go: a message answered twice is worse than one answered once, and the transcript
1344
+ // would show the same words under two answers.
1345
+ await this.clearPiQueues(conversationId);
1202
1346
  await this.awaitRunSlotReleased(conversationId);
1203
1347
  await this.processWaitingMessage(conversationId, messageId);
1204
1348
  }
@@ -1300,6 +1444,12 @@ export class RunService {
1300
1444
  async cancelQueuedUnsafe(conversationId, messageId) {
1301
1445
  if (this.interruptTargets.get(conversationId) === messageId)
1302
1446
  throw new Error("这条消息正在打断并回答,无法撤回");
1447
+ // PI'S QUEUES ARE EMPTIED BEFORE THE MESSAGE IS TAKEN BACK. A message that was handed to Pi's
1448
+ // follow-up queue is in Pi's hands as well as in Super Pi's, and Pi would deliver it when the run
1449
+ // ends - the model would answer words the user believes they withdrew. Pi cannot take back one
1450
+ // message, so its queues are emptied; the other messages Pi was holding are still in Super Pi's
1451
+ // waiting list and are answered as turns of their own.
1452
+ await this.clearPiQueues(conversationId);
1303
1453
  await this.store.cancelWaitingMessage(conversationId, messageId);
1304
1454
  this.broadcaster.broadcastState();
1305
1455
  }