@evandu/super-pi 0.1.76 → 0.1.77
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.
package/README.md
CHANGED
|
@@ -123,16 +123,25 @@ MIT
|
|
|
123
123
|
|
|
124
124
|
## Web tools and terminal
|
|
125
125
|
|
|
126
|
-
### Add
|
|
126
|
+
### Add a sidebar tool
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
2. Open the **Tools** tab in Super Pi's right sidebar — it shows only your curated tool icons. Click **工具设置** (Tool settings) at the bottom to open the setup popup.
|
|
130
|
-
3. Inside the popup enter the URL under **添加网址工具**, optionally name it, and add it. The same popup holds the preset tools and the customisation notes.
|
|
131
|
-
4. Select its icon to display the page in the popup, or choose **Open in new tab**.
|
|
132
|
-
5. Use **Copy development prompt** to copy a starter request for an agent building another standalone HTTP tool.
|
|
128
|
+
Open **Tools → 工具设置** and use **复制工具开发 Prompt** in the setup dialog. Ask the Super Pi agent to create the tool; its built-in `tool_register` submits the complete files and mounts the result globally, so the tool is available from every workspace without a separate server.
|
|
133
129
|
|
|
134
|
-
|
|
130
|
+
For a tool that needs local computation, include a Python script in the registration and declare its parameter schema. The script receives one JSON object on stdin and must print one JSON object on stdout. Load the built-in browser SDK from the mounted page:
|
|
135
131
|
|
|
132
|
+
```html
|
|
133
|
+
<script src="./super-pi-tool.js"></script>
|
|
134
|
+
<script>
|
|
135
|
+
async function calculate(params) {
|
|
136
|
+
const run = await SuperPiTool.run(params);
|
|
137
|
+
return await SuperPiTool.wait(run.id);
|
|
138
|
+
}
|
|
139
|
+
</script>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`SuperPiTool.run`, `get`, `wait`, and `cancel` are relative, tool-scoped APIs. A page should not call `/api/exec`, guess a tool id, start an HTTP server, or send shell commands. The server enforces the declared schema, timeout, cancellation, and Python process isolation.
|
|
143
|
+
|
|
144
|
+
Legacy URL bookmarks remain available for opening external HTTP/HTTPS pages, but they are not the mechanism for registering or running a Super Pi tool.
|
|
136
145
|
### Enable the terminal
|
|
137
146
|
|
|
138
147
|
The terminal is off by default. Set the environment variable before starting Super Pi, then restart it after changing the setting.
|
|
@@ -145,7 +145,7 @@ const extension = (pi: ExtensionApi): void => {
|
|
|
145
145
|
// cancellation. The text says what was asked and what was chosen, in words.
|
|
146
146
|
return text(JSON.stringify({ answers: (payload.answers ?? []).map((answer) => ({ question: p.question, selected: answer.selected, ...(answer.note ? { note: answer.note } : {}) })), ...(payload.summary ? { summary: payload.summary } : {}) }));
|
|
147
147
|
} });
|
|
148
|
-
pi.registerTool({ name: "tool_register", label: "Register Tool", description: "Register a global Super Pi sidebar tool. Use this when the user asks to create or add a right-sidebar tool. Send complete HTML/JS/CSS files directly; they are stored by Super Pi and work in every workspace. An optional registered Python script receives validated JSON on stdin and must print one JSON object to stdout.
|
|
148
|
+
pi.registerTool({ name: "tool_register", label: "Register Tool", description: "Register a global Super Pi sidebar tool. Use this when the user asks to create or add a right-sidebar tool. Send complete HTML/JS/CSS files directly; they are stored by Super Pi and work in every workspace. An optional registered Python script receives validated JSON on stdin and must print one JSON object to stdout. In the HTML, load ./super-pi-tool.js and use SuperPiTool.run/get/wait/cancel for the tool's own Python jobs; do not call /api/exec, guess a tool id, or start another HTTP server.", promptSnippet: "register a newly created global right-sidebar tool with Super Pi", parameters: ToolRegisterParameters, execute: async (_id, raw, _signal, _update, _context): Promise<TaskToolResult> => {
|
|
149
149
|
const endpoint = process.env.SUPER_PI_TOOLS_ENDPOINT;
|
|
150
150
|
const text = (body: string, isError?: boolean): TaskToolResult => ({ content: [{ type: "text", text: body }], details: undefined, ...(isError ? { isError: true } : {}) });
|
|
151
151
|
if (!endpoint) return text("The Super Pi tool registry is not available in this run.", true);
|
|
@@ -156,7 +156,7 @@ const extension = (pi: ExtensionApi): void => {
|
|
|
156
156
|
const response = await fetch(endpoint, { method: "POST", headers: { "content-type": "application/json", ...(capability ? { "x-super-pi-capability": capability } : {}) }, body: JSON.stringify({ ...p, conversationId, capability }) });
|
|
157
157
|
const payload = await response.json() as { id?: string; error?: string };
|
|
158
158
|
if (!response.ok || payload.error) return text(payload.error ?? `Tool registration failed (HTTP ${response.status}).`, true);
|
|
159
|
-
return text(JSON.stringify({ registered: true, id: payload.id, url: `/tools/${payload.id}
|
|
159
|
+
return text(JSON.stringify({ registered: true, id: payload.id, url: `/tools/${payload.id}/`, runtime: "Load ./super-pi-tool.js; use SuperPiTool.run(params), SuperPiTool.get(runId), SuperPiTool.wait(runId), and SuperPiTool.cancel(runId)." }));
|
|
160
160
|
} catch (error) { return text(`Tool registration could not reach Super Pi: ${error instanceof Error ? error.message : "unknown error"}`, true); }
|
|
161
161
|
} });
|
|
162
162
|
// Read ONCE at load, not per call: the environment cannot change under a running Pi, and `prepareLoadout`
|
|
@@ -53,7 +53,7 @@ npx @evandu/super-pi`})})]}),(0,m.jsxs)(`div`,{className:`help-card`,children:[(
|
|
|
53
53
|
|
|
54
54
|
工具不属于当前 workspace;通过内置 tool_register 一次性提交完整文件(index.html、JS/CSS,必要时 .py),Super Pi 会保存为全局工具并自动出现在每个 workspace 的工具 Tab。
|
|
55
55
|
|
|
56
|
-
如需本地计算,声明 script: { path, timeoutMs, parameters }。脚本必须是 Python,参数以 JSON 从 stdin 传入,stdout 必须只输出一个 JSON 对象;通过 POST /api/tools/<toolId>/run 创建任务,再轮询 GET /api/tools/runs/<runId> 获取状态、日志和结果,DELETE 同一路径取消。不要让网页传 shell 命令、任意文件路径或未声明参数。最后返回注册 id、访问地址、文件清单和验证结果。`);u(e),window.setTimeout(()=>u(!1),1800)};return(0,p.useEffect)(()=>{let e=e=>{e.key===`Escape`&&n()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[n]),(0,d.createPortal)((0,m.jsx)(`div`,{className:`tool-overlay`,"data-tool-settings-overlay":`1`,onClick:e=>{e.target===e.currentTarget&&n()},children:(0,m.jsxs)(`div`,{className:`tool-dialog`,role:`dialog`,"aria-modal":`true`,"aria-labelledby":`tool-settings-title`,"data-tool-settings":`1`,children:[(0,m.jsxs)(`div`,{className:`tool-dialog-head`,children:[(0,m.jsx)(`h2`,{className:`tool-dialog-title`,id:`tool-settings-title`,children:`⚙ 工具设置`}),(0,m.jsx)(`button`,{type:`button`,className:`tool-dialog-close`,"data-tool-settings-close":`1`,title:`关闭(Esc)`,"aria-label":`关闭工具设置`,onClick:n,children:`✕`})]}),(0,m.jsxs)(`div`,{className:`tool-dialog-body`,children:[(0,m.jsxs)(`div`,{className:`tool-prompt-card`,children:[(0,m.jsx)(`strong`,{children:`给 Agent 开发新工具`}),(0,m.jsx)(`p`,{className:`tool-hint`,children:`工具会挂载到 Super Pi 的统一 HTTP 服务,不需要再启动独立服务器。`}),(0,m.jsx)(`button`,{type:`button`,className:`tool-link-copy-prompt`,"data-tool-development-prompt":`1`,onClick:()=>void f(),children:l?`已复制`:`复制工具开发 Prompt`})]}),(0,m.jsx)(`div`,{className:`rail-section-head`,children:(0,m.jsx)(`h2`,{className:`rail-section-title`,children:`从预置列表添加`})}),(0,m.jsx)(`div`,{className:`tool-presets`,"data-tool-presets":Yt.length,children:Yt.map(n=>{let r=e.some(e=>e.kind===n.kind);return(0,m.jsxs)(`div`,{className:`tool-preset`,"data-tool-preset":n.kind,children:[(0,m.jsx)(`span`,{className:`tool-preset-glyph`,"aria-hidden":`true`,children:n.glyph}),(0,m.jsxs)(`span`,{className:`tool-preset-text`,children:[(0,m.jsx)(`strong`,{children:n.name}),(0,m.jsx)(`small`,{children:n.blurb})]}),(0,m.jsx)(`button`,{type:`button`,className:`tool-preset-add`,"data-tool-add-preset":n.kind,disabled:r,title:r?`${n.name}已经在工具栏里了`:`添加${n.name}`,"aria-label":r?`${n.name}已添加`:`添加${n.name}`,onClick:()=>t(an(e,n.kind)),children:r?`已添加`:`添加`})]},n.kind)})}),(0,m.jsx)(`div`,{className:`rail-section-head`,children:(0,m.jsx)(`h2`,{className:`rail-section-title`,children:`添加网址工具`})}),(0,m.jsx)(`p`,{className:`tool-hint`,children:`填一个网址,它会变成一个图标;点开后在弹窗里用按钮在新标签页打开。网址只接受 http / https / mailto。`}),(0,m.jsx)(`input`,{className:`tool-input`,"data-tool-url-input":`1`,value:r,maxLength:2048,placeholder:`https://example.com/工具`,"aria-label":`工具网址`,onChange:e=>{i(e.target.value),c(void 0)}}),(0,m.jsx)(`input`,{className:`tool-input`,"data-tool-url-name":`1`,value:a,maxLength:40,placeholder:`名称(可留空,默认用域名)`,"aria-label":`工具名称(可选)`,onChange:e=>o(e.target.value)}),(0,m.jsx)(`button`,{type:`button`,className:`tool-url-add`,"data-tool-url-add":`1`,disabled:!r.trim(),onClick:()=>{let n=$t(r);n.ok?e.length>=32?c(`工具最多 32 个,先删掉一个再加。`):(t([...e,{id:rn(`link`,e.map(e=>e.id)),kind:`link`,name:a.trim()||si(n.url),glyph:`↗`,url:n.url}]),i(``),o(``),c(void 0)):c(n.reason)},children:`添加网址工具`}),s&&(0,m.jsx)(`p`,{className:`tool-error`,"data-tool-error":`1`,role:`alert`,children:s}),(0,m.jsxs)(`p`,{className:`tool-hint`,"data-tool-scope-note":`1`,children:[`能自定义到哪:从上面挑预置工具,或自己填一个网址。`,(0,m.jsx)(`b`,{children:`没有开放「写一段脚本来执行」这一档`}),`——它在浏览器里等同于任意代码执行,会拿着本应用的权限跑。计算器是手写的算术解析器,不含 eval,也认不出变量名。`]})]})]})}),document.body)}function si(e){try{return new URL(e,typeof window>`u`?`http://localhost`:window.location.href).hostname||e.slice(0,30)}catch{return e.slice(0,30)}}function ci({tool:e,onClose:t}){return(0,p.useEffect)(()=>{let e=e=>{e.key===`Escape`&&t()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[t]),(0,d.createPortal)((0,m.jsx)(`div`,{className:`tool-overlay`,"data-tool-overlay":e.id,onClick:e=>{e.target===e.currentTarget&&t()},children:(0,m.jsxs)(`div`,{className:`tool-dialog`,role:`dialog`,"aria-modal":`true`,"aria-labelledby":`tool-dialog-title`,"data-tool-dialog":e.id,children:[(0,m.jsxs)(`div`,{className:`tool-dialog-head`,children:[(0,m.jsxs)(`h2`,{className:`tool-dialog-title`,id:`tool-dialog-title`,children:[(0,m.jsx)(`span`,{className:`tool-dialog-glyph`,"aria-hidden":`true`,children:e.glyph}),e.name]}),(0,m.jsx)(`button`,{type:`button`,className:`tool-dialog-close`,"data-tool-close":`1`,title:`关闭(Esc)`,"aria-label":`关闭${e.name}`,onClick:t,children:`✕`})]}),(0,m.jsxs)(`div`,{className:`tool-dialog-body`,children:[e.kind===`calculator`&&(0,m.jsx)(ui,{}),e.kind===`timestamp`&&(0,m.jsx)(di,{}),e.kind===`link`&&(0,m.jsx)(fi,{tool:e})]})]})}),document.body)}function li({image:e,onClose:t}){return(0,p.useEffect)(()=>{let e=e=>{e.key===`Escape`&&t()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[t]),(0,d.createPortal)((0,m.jsx)(`div`,{className:`lightbox-overlay`,"data-lightbox-overlay":`1`,onClick:e=>{e.target===e.currentTarget&&t()},children:(0,m.jsxs)(`div`,{className:`lightbox-dialog`,role:`dialog`,"aria-modal":`true`,"aria-labelledby":`lightbox-title`,"data-lightbox-dialog":`1`,children:[(0,m.jsxs)(`div`,{className:`lightbox-head`,children:[(0,m.jsx)(`h2`,{className:`lightbox-title`,id:`lightbox-title`,children:e.name||`图片大图`}),(0,m.jsx)(`button`,{type:`button`,className:`lightbox-close`,"data-lightbox-close":`1`,title:`关闭(Esc)`,"aria-label":`关闭大图`,onClick:t,children:`✕`})]}),(0,m.jsx)(`div`,{className:`lightbox-body`,children:(0,m.jsx)(`img`,{src:e.url,alt:e.name||`图片大图`,className:`lightbox-image`})})]})}),document.body)}function ui(){let[e,t]=(0,p.useState)(`12 * (3 + 4)`),[n,r]=(0,p.useState)(!1),i=vn(e),a=e=>{r(!1),t(t=>n?e:t+e)};return(0,m.jsxs)(`div`,{className:`tool-calc`,"data-tool-calc":`1`,children:[(0,m.jsx)(`input`,{className:`tool-input tool-calc-input`,"data-calc-input":`1`,value:e,maxLength:240,"aria-label":`算式`,spellCheck:!1,autoComplete:`off`,placeholder:`例如 (12 + 8) / 4`,onChange:e=>{r(!1),t(e.target.value)},onKeyDown:e=>{e.key===`Enter`&&e.preventDefault()}}),(0,m.jsx)(`div`,{className:`tool-calc-keys`,"data-calc-keys":`1`,children:[[`7`,`7`],[`8`,`8`],[`9`,`9`],[`÷`,`/`],[`(`,`(`],[`)`,`)`],[`4`,`4`],[`5`,`5`],[`6`,`6`],[`×`,`*`],[`^`,`^`],[`%`,`%`],[`1`,`1`],[`2`,`2`],[`3`,`3`],[`-`,`-`],[`←`,`back`],[`C`,`clear`],[`0`,`0`],[`.`,`.`],[`+`,`+`]].map(([e,n])=>(0,m.jsx)(`button`,{type:`button`,className:`tool-calc-key`,"data-calc-key":n,"aria-label":e===`C`?`清空`:e===`←`?`退格`:`输入 ${e}`,onClick:()=>{n===`clear`?(t(``),r(!1)):n===`back`?(t(e=>e.slice(0,-1)),r(!1)):a(n)},children:e},e))}),i.ok?(0,m.jsx)(`p`,{className:`tool-calc-result`,"data-calc-result":`1`,"data-calc-value":String(i.value),children:yn(i.value)}):(0,m.jsx)(`p`,{className:`tool-calc-error`,"data-calc-error":`1`,role:`alert`,children:i.reason})]})}function di(){let[e,t]=(0,p.useState)(``),n=bn(e),r=n.ok?xn(n.ms):null;return(0,m.jsxs)(`div`,{className:`tool-stamp`,"data-tool-stamp":`1`,children:[(0,m.jsx)(`input`,{className:`tool-input`,"data-stamp-input":`1`,value:e,maxLength:64,spellCheck:!1,"aria-label":`时间戳或日期`,placeholder:`1757000000 或 2026-10-08T09:00:00`,onChange:e=>t(e.target.value)}),r?(0,m.jsxs)(`dl`,{className:`tool-stamp-out`,"data-stamp-result":`1`,children:[(0,m.jsxs)(`div`,{children:[(0,m.jsx)(`dt`,{children:`本地时间`}),(0,m.jsx)(`dd`,{"data-stamp-local":`1`,children:r.local})]}),(0,m.jsxs)(`div`,{children:[(0,m.jsx)(`dt`,{children:`ISO(UTC)`}),(0,m.jsx)(`dd`,{"data-stamp-iso":`1`,children:r.iso})]}),(0,m.jsxs)(`div`,{children:[(0,m.jsx)(`dt`,{children:`秒`}),(0,m.jsx)(`dd`,{"data-stamp-seconds":`1`,children:r.seconds})]}),(0,m.jsxs)(`div`,{children:[(0,m.jsx)(`dt`,{children:`毫秒`}),(0,m.jsx)(`dd`,{"data-stamp-ms":`1`,children:r.milliseconds})]})]}):(0,m.jsx)(`p`,{className:`tool-stamp-error`,"data-stamp-error":`1`,role:`alert`,children:n.ok?`这个日期无法显示。`:n.reason})]})}function fi({tool:e}){let[t,n]=(0,p.useState)(!1),r=async()=>{let e=await gr(`请为 Super Pi 开发一个全局右侧栏工具并自动挂载。不要启动独立 HTTP 服务器。通过内置 tool_register 提交完整的 HTML/JS/CSS 文件;工具将被 Super Pi 保存并在所有 workspace 可用。如需计算,注册一个 Python 脚本(不接受 shell 命令),参数经 JSON stdin 传入,stdout 只输出 JSON 结果。页面用 /api/tools/<toolId>/run 创建任务、轮询 /api/tools/runs/<runId> 读取状态/日志/结果,DELETE 取消。`);n(e),window.setTimeout(()=>n(!1),1800)};return(0,m.jsxs)(`div`,{className:`tool-link`,"data-tool-link":e.id,children:[(0,m.jsx)(`p`,{className:`tool-hint`,children:`这是旧版外部网页链接;新工具由 Super Pi 统一托管,并可选绑定受控 Python 脚本。`}),(0,m.jsx)(`code`,{className:`tool-link-url`,"data-tool-link-url":e.id,children:e.url}),(0,m.jsxs)(`div`,{className:`tool-link-actions`,children:[(0,m.jsx)(`a`,{className:`tool-link-open`,"data-tool-link-open":e.id,href:e.url,target:`_blank`,rel:`noopener noreferrer`,title:e.url,children:`在新标签页打开 ?`}),(0,m.jsx)(`button`,{type:`button`,className:`tool-link-copy-prompt`,"data-tool-copy-prompt":e.id,onClick:()=>void r(),children:t?`已复制`:`复制开发 Prompt`})]}),/^https?:\/\//i.test(e.url??``)&&(0,m.jsx)(`iframe`,{className:`tool-link-frame`,"data-tool-frame":e.id,src:e.url,title:e.name,sandbox:`allow-forms allow-modals allow-pointer-lock allow-popups allow-same-origin allow-scripts`,referrerPolicy:`no-referrer`})]})}var pi=`(max-width: 1100px)`,mi=(e,t,n)=>Math.min(Math.max(e,t),Math.max(t,n));function hi({tool:e,onClose:t}){return(0,d.createPortal)((0,m.jsx)(`div`,{className:`tool-overlay`,onClick:e=>{e.target===e.currentTarget&&t()},children:(0,m.jsxs)(`div`,{className:`tool-dialog`,role:`dialog`,"aria-modal":`true`,children:[(0,m.jsxs)(`div`,{className:`tool-dialog-head`,children:[(0,m.jsxs)(`h2`,{className:`tool-dialog-title`,children:[`🧩 `,e.name]}),(0,m.jsx)(`button`,{type:`button`,className:`tool-dialog-close`,onClick:t,children:`✕`})]}),(0,m.jsx)(`div`,{className:`tool-dialog-body`,children:(0,m.jsx)(`iframe`,{className:`tool-link-frame`,src:`/tools/${encodeURIComponent(e.id)}/`,title:e.name,sandbox:`allow-forms allow-modals allow-popups allow-scripts`,referrerPolicy:`no-referrer`})})]})}),document.body)}function gi(e,t,n){let r=Math.max(0,Math.round(n)-480),i=mi(520,180,Math.max(180,r-240)),a=mi(Math.round(e),180,i),o=mi(640,240,Math.max(240,r-a));return{sidebar:a,rail:mi(Math.round(t),240,o),sidebarMin:180,sidebarMax:i,railMin:240,railMax:o,sumCap:r}}var _i=`super-pi.rail-widths.v1`,vi=`super-pi.reader-state.v1`,yi=48,bi=160,xi=e=>{e.dataset.programmaticScroll=`true`,window.setTimeout(()=>{delete e.dataset.programmaticScroll},0)};function Si(){try{if(typeof window>`u`)return{conversations:{}};let e=JSON.parse(window.localStorage.getItem(`super-pi.reader-state.v1`)??`null`);if(!e||typeof e!=`object`)return{conversations:{}};let t={};if(e.conversations&&typeof e.conversations==`object`)for(let[n,r]of Object.entries(e.conversations)){let e=r;typeof e.followTail==`boolean`&&(t[n]={followTail:e.followTail,scrollTop:Number.isFinite(e.scrollTop)?Math.max(0,Number(e.scrollTop)):0,...typeof e.anchorKey==`string`?{anchorKey:e.anchorKey}:{},...Number.isFinite(e.anchorOffset)?{anchorOffset:Number(e.anchorOffset)}:{}})}let n={};if(e.readAt&&typeof e.readAt==`object`)for(let[t,r]of Object.entries(e.readAt))typeof r==`string`&&(n[t]=r);return{activeId:typeof e.activeId==`string`?e.activeId:void 0,workspaceId:typeof e.workspaceId==`string`?e.workspaceId:void 0,conversations:t,readAt:n}}catch{return{conversations:{}}}}function Ci(e){try{typeof window<`u`&&window.localStorage.setItem(vi,JSON.stringify(e))}catch{}}function wi(){try{if(typeof window>`u`)return{};let e=window.localStorage.getItem(_i);if(e===null)return{};let t=JSON.parse(e),n=e=>typeof e==`number`&&Number.isFinite(e)&&e>0?Math.round(e):void 0;return{sidebar:n(t.sidebar),rail:n(t.rail)}}catch{return{}}}function Ti(e){try{if(typeof window>`u`)return;window.localStorage.setItem(_i,JSON.stringify(e))}catch{}}function Ei({side:e,width:t,min:n,max:r,label:i,onWidth:a}){let[o,s]=(0,p.useState)(!1),c=e=>e.shiftKey?64:16;return(0,m.jsx)(`div`,{className:`rail-resizer rail-resizer-${e}${o?` rail-resizer-dragging`:``}`,"data-rail-resizer":e,"data-rail-resizer-dragging":o?`true`:`false`,"data-rail-resizer-width":t,role:`separator`,"aria-orientation":`vertical`,"aria-label":i,"aria-valuenow":t,"aria-valuemin":n,"aria-valuemax":r,tabIndex:0,onMouseDown:i=>{i.preventDefault(),i.currentTarget.focus();let o=i.clientX,c=t;s(!0);let l=document.body,u=l.style.cursor,d=l.style.userSelect;l.style.cursor=`col-resize`,l.style.userSelect=`none`;let f=t=>{let i=e===`left`?t.clientX-o:o-t.clientX;a(mi(c+i,n,r))},p=()=>{l.style.cursor=u,l.style.userSelect=d,s(!1),window.removeEventListener(`mousemove`,f),window.removeEventListener(`mouseup`,p)};window.addEventListener(`mousemove`,f),window.addEventListener(`mouseup`,p)},onKeyDown:i=>{let o=c(i);i.key===`ArrowLeft`?(i.preventDefault(),a(mi(e===`left`?t-o:t+o,n,r))):i.key===`ArrowRight`?(i.preventDefault(),a(mi(e===`left`?t+o:t-o,n,r))):i.key===`Home`?(i.preventDefault(),a(n)):i.key===`End`&&(i.preventDefault(),a(r))}})}function Di(){let[e,t]=(0,p.useState)(tr),[n,r]=(0,p.useState)([]),i=(0,p.useRef)([]),a=(0,p.useRef)(null);a.current||=Si();let o=n.reduce((e,t)=>t.apply(e),e),[s,c]=(0,p.useState)(a.current.activeId),[l,u]=(0,p.useState)(!1),[d,f]=(0,p.useState)(),[h,g]=(0,p.useState)({}),[_,v]=(0,p.useState)(a.current.workspaceId),[y,b]=(0,p.useState)(!1),x=(0,p.useRef)(void 0),S=(0,p.useRef)(a.current.conversations),C=(0,p.useRef)(a.current.readAt??{}),[w,T]=(0,p.useState)(0),ee=(0,p.useRef)(null),E=(0,p.useRef)(null),te=(0,p.useRef)(void 0),ne=(0,p.useRef)(a.current.activeId),[re,ie]=(0,p.useState)({followTail:!0,scrollTop:0}),ae=(0,p.useRef)(void 0),oe=(0,p.useRef)(new Map),se=(0,p.useRef)(void 0),D=(0,p.useCallback)(()=>{if(se.current!==void 0&&(cancelAnimationFrame(se.current),se.current=void 0),!oe.current.size)return;let e=[...oe.current.values()];oe.current.clear();let n=new Map,r=new Map;for(let t of e){let e=`${t.conversationId}\u0000${t.messageId}`;if(t.type===`message.thinking`){let n=r.get(e);r.set(e,{text:`${n?.text??``}${t.delta}`,at:n?.at??t.at})}else n.set(e,`${n.get(e)??``}${t.delta}`)}t(e=>({...e,conversations:e.conversations.map(e=>{let t=e.messages.map(t=>{let i=`${e.id}\u0000${t.id}`,a=n.get(i),o=r.get(i);return a===void 0&&o===void 0?t:{...t,...a===void 0?{}:{content:t.content+a},...o===void 0?{}:{thinking:(t.thinking??``)+o.text,thinkingBlocks:[...t.thinkingBlocks??[],{at:o.at??t.content.length,text:o.text}]}}});return t.every((t,n)=>t===e.messages[n])?e:{...e,messages:t}})}))},[]),ce=(0,p.useCallback)(e=>{let t=`${e.type}\u0000${e.conversationId}\u0000${e.messageId}`,n=oe.current.get(t);oe.current.set(t,n?{...n,delta:n.delta+e.delta}:e),se.current===void 0&&(se.current=requestAnimationFrame(D))},[D]),le=(0,p.useCallback)(e=>{ae.current=e},[]),[ue,de]=(0,p.useState)({}),[fe,pe]=(0,p.useState)(()=>{if(typeof window>`u`)return``;try{return window.localStorage.getItem(`super-pi.user-nickname`)??``}catch{return``}}),me=e=>{let t=e.slice(0,24);pe(t);try{window.localStorage.setItem(`super-pi.user-nickname`,t)}catch{}},he=fe.trim()||`你`,[ge,_e]=(0,p.useState)([]),[ve,ye]=(0,p.useState)([]),[be,xe]=(0,p.useState)(new Map),[Se,O]=(0,p.useState)(new Map),[k,Ce]=(0,p.useState)(),[we,Te]=(0,p.useState)(),[Ee,De]=(0,p.useState)(),[A,Oe]=(0,p.useState)(),[ke,Ae]=(0,p.useState)(),[je,Me]=(0,p.useState)(`idle`),[Ne,Pe]=(0,p.useState)(()=>typeof window<`u`&&typeof window.matchMedia==`function`&&window.matchMedia(`(max-width: 1100px)`).matches),[Fe,Ie]=(0,p.useState)(`chat`),[Le,Re]=(0,p.useState)(()=>typeof window>`u`?1440:window.innerWidth);(0,p.useEffect)(()=>{let e=()=>Re(window.innerWidth);return window.addEventListener(`resize`,e),e(),()=>window.removeEventListener(`resize`,e)},[]);let[ze,Be]=(0,p.useState)(wi),j=gi(ze.sidebar??270,ze.rail??320,Le);(0,p.useEffect)(()=>{Ti({sidebar:j.sidebar,rail:j.rail})},[j.sidebar,j.rail]);let Ve=e=>Be(t=>({...t,sidebar:e})),He=e=>Be(t=>({...t,rail:e}));(0,p.useEffect)(()=>{if(typeof window>`u`||typeof window.matchMedia!=`function`)return;let e=window.matchMedia(pi),t=e=>{e.matches&&Pe(!0)};return e.addEventListener(`change`,t),e.matches&&Pe(!0),()=>e.removeEventListener(`change`,t)},[]);let[Ue,We]=(0,p.useState)(new Map),[Ge,qe]=(0,p.useState)({}),[Je,Ye]=(0,p.useState)({}),[Xe,Ze]=(0,p.useState)({}),[Qe,$e]=(0,p.useState)(),[et,tt]=(0,p.useState)(),[nt,it]=(0,p.useState)(),ot=(0,p.useRef)(new Map),[st,ct]=(0,p.useState)({}),lt=(0,p.useRef)(new Set),[ut,dt]=(0,p.useState)([]),[ft,pt]=(0,p.useState)([]),[mt,ht]=(0,p.useState)(),[gt,_t]=(0,p.useState)(!1),vt=(0,p.useRef)(null),yt=(0,p.useRef)(!1),bt=(0,p.useRef)(0),xt=(0,p.useRef)([]),St=(0,p.useRef)(void 0),Ct=(0,p.useRef)(!1),[Tt,Dt]=(0,p.useState)(()=>Date.now());(0,p.useEffect)(()=>{let e=window.setInterval(()=>Dt(Date.now()),3e4);return()=>window.clearInterval(e)},[]);let Ot=(0,p.useMemo)(()=>o.conversations.filter(e=>!e.archivedAt),[o.conversations]),M=(0,p.useMemo)(()=>Ot.find(e=>e.id===s)??Ot.find(e=>e.workspaceId===_)??(_&&o.workspaces.some(e=>e.id===_)?void 0:Ot[0]),[s,Ot,_,o.workspaces]),kt=(0,p.useMemo)(()=>o.workspaces.find(e=>e.id===_)??o.workspaces[0],[o.workspaces,_]),jt=(0,p.useRef)(_);jt.current=_,(0,p.useEffect)(()=>{M&&v(M.workspaceId)},[M?.id]),(0,p.useEffect)(()=>{St.current=s},[s]),(0,p.useEffect)(()=>{te.current=M?.id},[M?.id]),(0,p.useEffect)(()=>{let e=M?.id;if(!e)return;let t=o.conversations.find(t=>t.id===e);if(!t)return;let n=C.current[e];C.current[e]=t.updatedAt,n!==t.updatedAt&&T(e=>e+1),Ci({activeId:St.current,workspaceId:M?.workspaceId??_,conversations:S.current,readAt:C.current})},[M?.id,o.conversations]),(0,p.useEffect)(()=>{l&&s&&Ci({activeId:s,workspaceId:M?.workspaceId??_,conversations:S.current})},[s,M?.workspaceId,l,_]),(0,p.useEffect)(()=>{let e=ee.current;if(!e||!M?.id)return;let t=M.id,n=S.current[t]??{followTail:!0,scrollTop:0};ie(n),requestAnimationFrame(()=>{if(ee.current===e){if(n.followTail)xi(e),e.scrollTop=e.scrollHeight;else{let t=n.anchorKey?e.querySelector(`[data-transcript-key="${CSS.escape(n.anchorKey)}"]`):null;e.scrollTop=t?Math.max(0,e.scrollTop+t.getBoundingClientRect().top-(n.anchorOffset??0)-e.getBoundingClientRect().top):n.scrollTop}}}),ne.current=void 0},[M?.id]);let Mt=(0,p.useCallback)((e,t=!1,n=!1)=>{let r=te.current;if(!r)return;let i=e.scrollHeight-e.clientHeight-e.scrollTop,a=S.current[r]??{followTail:!0,scrollTop:0},o=t?!1:n?!0:a.followTail?i<=bi:i<=yi,s,c;if(!o){let t=e.getBoundingClientRect().top,n=[...e.querySelectorAll(`[data-transcript-key]`)].find(e=>e.getBoundingClientRect().bottom>t);s=n?.dataset.transcriptKey,c=n?n.getBoundingClientRect().top-t:void 0}let l={followTail:o,scrollTop:e.scrollTop,...s?{anchorKey:s,anchorOffset:c}:{}};S.current[r]=l,ie(l),Ci({activeId:r,workspaceId:jt.current,conversations:S.current})},[]),Nt=st[M?.id??``]??{text:``,images:[],attachments:[],uploading:0},Pt=Nt.text,Ft=Nt.images,It=Nt.attachments,Lt=Nt.uploading,zt=(e,t)=>ct(n=>({...n,[e]:t(n[e]??{text:``,images:[],attachments:[],uploading:0})})),Vt=e=>zt(M?.id??``,t=>({...t,text:e})),Ht=e=>zt(M?.id??``,t=>({...t,attachments:t.attachments.some(t=>t.kind===e.kind&&t.id===e.id)?t.attachments:[...t.attachments,e]})),Ut=(e,t)=>zt(M?.id??``,n=>({...n,attachments:n.attachments.filter(n=>n.kind!==e||n.id!==t)})),[Wt,Gt]=(0,p.useState)({visible:!1,query:``,rect:null}),Kt=e=>zt(M?.id??``,t=>({...t,images:typeof e==`function`?e(t.images):e})),[qt,Jt]=(0,p.useState)([]),Yt=qt.length>0?qt:M?[M.id]:[],Xt=(0,p.useMemo)(()=>Yt.map(e=>Ot.find(t=>t.id===e)).filter(e=>e!==void 0),[Yt,Ot]),Zt=(0,p.useCallback)(e=>{c(e);let t=Ot.find(t=>t.id===e)?.workspaceId;t&&v(t),an(`session`),Jt(t=>t.includes(e)?t:t.length>0?[...t,e]:[...M?.id&&M.id!==e?[M.id]:[],e])},[M]),N=(0,p.useCallback)(e=>{let t=qt.indexOf(e);if(t<0)return;let n=qt.filter(t=>t!==e);if(Jt(n),M?.id!==e)return;let r=n[Math.min(t,n.length-1)];r&&c(r)},[qt,M]),P=Et(_),[Qt,$t]=(0,p.useState)(Gr[0].id),[en,tn]=(0,p.useState)(!1);(0,p.useEffect)(()=>{let e=!1;return fetch(`/api/terminal`).then(e=>e.json()).then(t=>{e||tn(!!t?.enabled)}).catch(()=>{e||tn(!1)}),()=>{e=!0}},[]);let[rn,an]=(0,p.useState)(`session`);(0,p.useEffect)(()=>{rn===`file`&&!P.active&&an(`session`)},[rn,P.active]);let on=(0,p.useCallback)(e=>{P.setActivePath(e),an(`file`),$t(`files`)},[P.setActivePath]),sn=(0,p.useCallback)(async e=>{await P.openFile(e),an(`file`)},[P.openFile]),cn=(0,p.useMemo)(()=>Xt.map(e=>({id:`session:${e.id}`,rawId:e.id,kind:`session`,label:e.title,title:e.title,closable:Xt.length>1||P.open.length>0,badge:(0,m.jsx)(Yr,{state:Jr(e,ge.some(t=>t.conversationId===e.id),C.current[e.id])})})),[Xt,P.open.length,ge,o.conversations,w]),ln=(0,p.useMemo)(()=>P.open.map(e=>({id:`file:${e.path}`,rawId:e.path,kind:`file`,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})),[P.open]),un=(0,p.useMemo)(()=>[...cn,...ln],[cn,ln]),dn=rn===`file`&&P.activePath?`file:${P.activePath}`:M?.id?`session:${M.id}`:void 0,fn=(0,p.useCallback)(e=>{if(e.startsWith(`file:`)){let t=e.slice(5);on(t)}else if(e.startsWith(`session:`)){let t=e.slice(8);Zt(t)}},[on,Zt]),pn=(0,p.useCallback)(e=>{if(e.startsWith(`file:`)){let t=e.slice(5);P.closeFile(t),P.activePath===t&&P.open.length<=1&&an(`session`)}else if(e.startsWith(`session:`)){let t=e.slice(8);N(t)}},[P,N]),mn=P.active,hn=M?.id;(0,p.useEffect)(()=>{hn&&!hn.startsWith(`pending:`)&&bn({type:`conversation.select`,conversationId:hn})},[hn]);let F=(e,t=`info`)=>{if(!e)return;let n=++bt.current;ye(r=>[...r,{key:n,text:e,kind:t}]),xt.current.push(window.setTimeout(()=>ye(e=>e.filter(e=>e.key!==n)),7e3))},gn=(e,t)=>{let n=e.findIndex(e=>e.id===t.id);if(n<0)return[t,...e];let r=[...e];return r[n]=t,r},_n=(e,t)=>{let n=e.findIndex(e=>e.id===t.id);if(n<0)return[t,...e];let r=[...e];return r[n]=t,r},vn=(e,t)=>{if(t.blocking)_e(n=>[...n.filter(n=>n.conversationId!==e||n.request.requestId!==t.requestId),{conversationId:e,request:t}]);else if(t.method===`notify`)F(t.message||t.title,t.notifyType??`info`);else if(t.method===`setStatus`){let e=t.statusKey??``;xe(n=>{let r=new Map(n);return t.statusText===void 0?r.delete(e):r.set(e,t.statusText),r})}else if(t.method===`setWidget`){let e=t.widgetKey??``;O(n=>{let r=new Map(n);return t.widgetLines===void 0?r.delete(e):r.set(e,{lines:t.widgetLines,placement:t.widgetPlacement??`aboveEditor`}),r})}else t.method===`setTitle`?Ce(t.title):t.method===`set_editor_text`&&e===St.current&&Vt(t.text??``)},yn=(e,t,n)=>{_e(n=>n.filter(n=>n.conversationId!==e||n.request.requestId!==t));let r=Dr[n];r&&F(r,`warning`)};(0,p.useEffect)(()=>{let e=!1,n=0,o,s=()=>{if(e)return;let l=location.protocol===`https:`?`wss`:`ws`,d=new WebSocket(`${l}://${location.host}/ws`);x.current=d,d.onopen=()=>{e||(n=0,u(!0),d.send(JSON.stringify({type:`stream.configure`,coalesce:!0})),d.send(JSON.stringify({type:`catalog.fetch`,conversationId:St.current})),d.send(JSON.stringify({type:`agent.catalog.fetch`})),d.send(JSON.stringify({type:`subagent.catalog.fetch`})))},d.onclose=()=>{if(!e){Ct.current=!1;for(let e of i.current)e.onFailure?.();i.current=[],r([]),u(!1),o=window.setTimeout(s,Math.min(hr,mr*2**n++))}},d.onmessage=({data:n})=>{if(e)return;let o=JSON.parse(n);if(o.type!==`message.delta`&&o.type!==`message.thinking`&&D(),o.type.startsWith(`terminal.`))ae.current?.(o);else if(o.type===`conversation.autoRename.result`){let e=ot.current.get(o.conversationId);e&&(ot.current.delete(o.conversationId),o.ok?e.resolve(o.title):e.reject(Error(o.error)))}else if(o.type===`message.thinking`)ce(o);else if(o.type===`state`)D(),Ct.current=!1,i.current=i.current.filter(e=>!e.accepted?.(o.state)),r(i.current),t(o.state),u(!0),c(e=>{let t=o.state.conversations.filter(e=>!e.archivedAt),n=a.current?.activeId,r=!!(e&&t.some(t=>t.id===e)),s=!!(e&&i.current.some(t=>t.temporaryId===e));return r||s?e:n&&t.some(e=>e.id===n)?n:t.find(e=>e.workspaceId===jt.current)?.id}),v(e=>{let t=a.current?.workspaceId;return t&&o.state.workspaces.some(e=>e.id===t)?t:e&&(o.state.workspaces.some(t=>t.id===e)||i.current.some(t=>t.temporaryId===e))?e:o.state.workspaces[0]?.id});else if(o.type===`message.delta`)ce(o);else if(o.type===`message.revert.result`)vi(void 0),$e(void 0),o.ok||f(o.message);else if(o.type===`operation.result`){let e=i.current.find(e=>e.id===o.requestId);if(i.current=i.current.filter(e=>e.id!==o.requestId),r(i.current),e?.temporaryId){let t=e.temporaryId;c(n=>n===t?o.ok?o.entityId:e.previousActiveId:n),Jt(e=>e.flatMap(e=>e===t?o.ok&&o.entityId?[o.entityId]:[]:[e])),v(e=>e===t?o.ok?o.entityId:void 0:e)}o.ok||(e?.onFailure?.(),f(o.message??`操作失败`))}else o.type===`error`?(f(o.message),Ct.current&&(Ct.current=!1,F(`重命名失败:${o.message}`,`error`))):o.type===`extension.ui`?vn(o.conversationId,o.request):o.type===`extension.ui.closed`?yn(o.conversationId,o.requestId,o.reason):o.type===`model.catalog`?(!o.conversationId||o.conversationId===St.current)&&Te(o.catalog):o.type===`agent.catalog`?De(o.catalog):o.type===`subagent.catalog`?Oe(o.catalog):o.type===`server.version`?(Ae(o.version),o.version.needsRestart&&F(`检测到 Super Pi 代码已更新(v${o.version.runningVersion} → v${o.version.diskVersion}),请执行 /super_pi restart 重启服务生效`,`warning`)):o.type===`subagent.event`?t(e=>({...e,subagentRuns:gn(e.subagentRuns??[],o.run)})):o.type===`workflow.event`?t(e=>({...e,workflows:_n(e.workflows??[],o.workflow)})):o.type===`message.tools`?t(e=>({...e,conversations:e.conversations.map(e=>e.id===o.conversationId?{...e,messages:e.messages.map(e=>e.id===o.messageId?{...e,toolCalls:o.toolCalls}:e)}:e)})):o.type===`pi.queue`?We(e=>{let t=new Map(e);return t.set(o.conversationId,{steering:o.steering,followUp:o.followUp}),t}):o.type===`conversation.fork.result`&&(Ni(e=>e===o.conversationId?void 0:e),F(o.ok?`已从「${o.title}」分叉出新会话,放在侧边栏最上面;没有切换过去。${o.messagesCopied?`复制了 ${o.messagesCopied} 条消息。`:`原会话还没有消息,只复制了会话本身。`}`:`分叉失败:${o.message}`,o.ok?`info`:`error`))}};return s(),()=>{e=!0,D(),window.clearTimeout(o);for(let e of xt.current)window.clearTimeout(e);x.current?.close()}},[ce,D]);let bn=(0,p.useCallback)(e=>x.current?.readyState===WebSocket.OPEN&&(x.current.send(JSON.stringify(e)),!0),[]),I=(e,t,n,a,o,s)=>{if(x.current?.readyState!==WebSocket.OPEN||i.current.some(e=>e.key===t))return!1;let c=nn(),l={id:c,key:t,apply:n,temporaryId:a,previousActiveId:St.current,onFailure:o,accepted:s};return i.current=[...i.current,l],r(i.current),x.current.send(JSON.stringify({...e,operationRequestId:c})),!0},xn=(e,t)=>n=>({...n,conversations:n.conversations.map(n=>n.id===e?{...n,...t}:n)}),Sn=(e,t)=>n=>({...n,workspaces:n.workspaces.map(n=>n.id===e?{...n,...t}:n)}),Cn=n.some(e=>e.key===`send:${M?.id}`||e.key===`configuration:${M?.id}`||e.temporaryId===M?.id||e.key===`archive:${M?.id}`),wn=(e,t,n,r,i)=>{e&&x.current?.send(JSON.stringify({type:`question.answer`,conversationId:e,messageId:t,callIndex:n,answers:r,cancelled:i}))},Tn=(e,t,n)=>I({type:`extension.ui.answer`,conversationId:e,requestId:t,...n},`answer:${e}:${t}`,e=>e),En=M?.model??we?.defaultModel,Dn=M?.thinkingLevel??we?.defaultThinkingLevel,On=we?.source===`pi`,An=!!we?.models.find(e=>e.provider===En?.provider&&e.modelId===En?.modelId)?.imageInput,jn=(e,t)=>!e||!t?e:{...e,models:e.models.filter(e=>(o.modelFilterRules??[]).every(n=>{try{return!new RegExp(n.workspacePattern,`i`).test(t.name)||new RegExp(n.modelPattern,`i`).test(e.label)||new RegExp(n.modelPattern,`i`).test(`${e.provider}/${e.modelId}`)}catch{return!0}}))},Mn=(jn(we,o.workspaces.find(e=>e.id===M?.workspaceId))?.models??[]).map(e=>({key:`${e.provider}/${e.modelId}`,label:e.label,detail:e.detail,badge:Cr(e),selected:En?.provider===e.provider&&En.modelId===e.modelId})),Nn=(we?.levels??[]).map(e=>({key:e,label:e,selected:e===Dn})),Pn=e=>{if(!M)return;let[t,n]=e.split(`/`);t&&n&&I({type:`conversation.model`,conversationId:M.id,provider:t,modelId:n},`configuration:${M.id}`,xn(M.id,{model:we?.models.find(e=>e.provider===t&&e.modelId===n)??{provider:t,modelId:n,label:n}}))},Fn=e=>{M&&I({type:`conversation.thinking`,conversationId:M.id,level:e},`configuration:${M.id}`,xn(M.id,{thinkingLevel:e}))},In=Ee?.agents.find(e=>e.id===M?.agentId),Rn=[{key:L,label:`跟随 Pi 默认`,detail:`不使用任何 agent 角色`,selected:!M?.agentId},...(Ee?.agents??[]).map(e=>({key:e.id,label:e.name,detail:e.toolSummary,color:e.color,selected:M?.agentId===e.id}))],zn=In?.model,Bn=!!(zn&&M?.model&&M.model.provider===zn.provider&&M.model.modelId===zn.modelId);In?.thinkingLevel&&(M?.thinkingLevel,In.thinkingLevel);let Vn=e=>{if(M){let t=Ee?.agents.find(t=>t.id===e);I({type:`conversation.agent`,conversationId:M.id,agentId:e===L?null:e},`configuration:${M.id}`,xn(M.id,{agentId:t?.id,...t?.model?{model:{...t.model,label:t.model.modelId}}:{},...t?.thinkingLevel?{thinkingLevel:t.thinkingLevel}:{}}))}},Hn=ge.filter(e=>e.conversationId===M?.id),Un=(0,p.useMemo)(()=>(o.subagentRuns??[]).filter(e=>e.conversationId===M?.id),[o.subagentRuns,M?.id]),Wn=(0,p.useMemo)(()=>(o.workflows??[]).filter(e=>e.conversationId===M?.id),[o.workflows,M?.id]),Gn=(0,p.useMemo)(()=>new Map(Wn.map(e=>[e.id,e])),[Wn]),Kn=(0,p.useMemo)(()=>new Map((A?.agents??[]).map(e=>[e.id,e.name])),[A]),qn=(0,p.useMemo)(()=>new Map((M?.messages??[]).map(e=>[e.id,e])),[M?.messages]),Jn=(0,p.useMemo)(()=>new Map(Un.map(e=>[e.id,e])),[Un]),Yn=(0,p.useMemo)(()=>{if(!M)return[];let e=Qe?.conversationId===M.id?(()=>{let e=M.messages.find(e=>e.id===Qe.messageId);if(e)return{index:M.messages.indexOf(e),at:Date.parse(e.createdAt)||0,...Qe.kind===`withdraw`?{inclusive:!0}:{inclusive:!1}}})():void 0,t=[];return M.messages.forEach((n,r)=>{e&&(r>e.index||e.inclusive&&r===e.index)||t.push({at:Date.parse(n.createdAt)||0,seq:r,key:`m:${n.id}`})}),Un.forEach((n,r)=>{e&&(Date.parse(n.startedAt)||0)>e.at||t.push({at:Date.parse(n.startedAt)||0,seq:t.length+r,key:`r:${n.id}`})}),Wn.forEach((n,r)=>{e&&(Date.parse(n.startedAt)||0)>e.at||t.push({at:Date.parse(n.startedAt)||0,seq:t.length+r,key:`w:${n.id}`})}),t.sort((e,t)=>e.at-t.at||e.seq-t.seq).map(e=>e.key)},[M,Un,Wn,Qe]);(0,p.useEffect)(()=>{let e=ee.current;if(!e||!M?.id)return;let t=()=>{S.current[M.id]?.followTail&&(xi(e),e.scrollTop=e.scrollHeight)};if(t(),typeof ResizeObserver>`u`)return;let n=new ResizeObserver(t);n.observe(e);for(let t of e.children)n.observe(t);return()=>n.disconnect()},[M?.id,M?.messages,Un,Wn,Yn]);let Xn=e=>qe(t=>({...t,[e]:!t[e]})),Zn=(e,t)=>Ye(n=>({...n,[`${e}:${t}`]:!n[`${e}:${t}`]})),Qn=(e,t)=>Ze(n=>({...n,[`${e}:${t}`]:n[`${e}:${t}`]!==!1})),[$n,er]=(0,p.useState)({}),ir=e=>er(t=>({...t,[e]:t[e]===!1})),ar=e=>{I({type:`subagent.stop`,runId:e},`stop:${e}`,e=>e)||F(`连接断开,无法停止子智能体,请重试`,`error`)},or=e=>{I({type:`workflow.stop`,workflowId:e},`stop:${e}`,e=>e)||F(`连接断开,无法停止编排,请重试`,`error`)},sr=e=>[...Se].filter(([,t])=>t.placement===e).map(([e,t])=>{let n=t.lines;return(0,m.jsxs)(`div`,{className:`extui-widget`,"data-widget-key":e,"data-widget-lines":n.length,children:[n.slice(0,Or).map((e,t)=>(0,m.jsx)(`div`,{children:e},`${t}:${e}`)),n.length>Or&&(0,m.jsx)(`div`,{children:`… (widget truncated)`})]},e)}),cr=(e,t)=>{let n=`pending:${nn()}`,r={id:n,name:e,path:t,updatedAt:new Date().toISOString()};I({type:`workspace.create`,name:e,path:t},`create-workspace`,e=>({...e,workspaces:[...e.workspaces,r]}),n)&&(v(n),c(void 0),b(!1))},[lr,yr]=(0,p.useState)(),br=(e,t)=>yr(n=>n?.kind===e&&n.id===t?n:{kind:e,id:t}),xr=()=>yr(void 0),Sr=(e,t)=>lr?.kind===e&&lr.id===t,wr=(e,t,n)=>t.startsWith(`pending:`)?!1:I(e===`workspace`?{type:`workspace.rename`,workspaceId:t,name:n}:{type:`conversation.rename`,conversationId:t,name:n},`rename:${t}`,e===`workspace`?Sn(t,{name:n}):xn(t,{title:n}))?(Ct.current=!0,xr(),!0):(F(`连接断开,改名未保存,请重试`,`error`),!1),Tr=async e=>{if(e.startsWith(`pending:`)){F(`这个会话还在创建中,稍后再试`,`warning`);return}if(ot.current.has(e))return;F(`正在根据对话内容生成标题…`,`info`);let t=new Promise((t,n)=>{ot.current.set(e,{resolve:t,reject:n}),window.setTimeout(()=>{ot.current.delete(e)&&n(Error(`模型在 30 秒内没有返回标题`))},32e3)});if(!bn({type:`conversation.autoRename`,conversationId:e}))ot.current.delete(e),F(`连接断开,无法生成标题`,`error`);else try{let n=await t;wr(`conversation`,e,n),F(`已重命名为「${n}」`,`info`)}catch(e){F(`重命名失败:${e instanceof Error?e.message:String(e)}`,`error`)}},jr=e=>{if(e.id.startsWith(`pending:`))return;let t=o.conversations.filter(t=>t.workspaceId===e.id).length;window.confirm(`删除工作区「${e.name}」?${t?`其中 ${t} 条会话将一并删除,`:``}此操作不可撤销。`)&&I({type:`workspace.delete`,workspaceId:e.id},`delete:${e.id}`,t=>({...t,workspaces:t.workspaces.filter(t=>t.id!==e.id),conversations:t.conversations.filter(t=>t.workspaceId!==e.id)}))},[Mr,Nr]=(0,p.useState)(),Fr=Mr?o.workspaces.find(e=>e.id===Mr):void 0,{theme:Br,setTheme:Kr}=kn(),[qr,Xr]=(0,p.useState)(!1),[Zr,Qr]=(0,p.useState)(!1),$r=(e,t,n,r)=>{e.startsWith(`pending:`)||(I({type:`workspace.settings`,workspaceId:e,model:n?{provider:n.provider,modelId:n.modelId}:null,thinkingLevel:r??null,agentId:t??null},`defaults:${e}`,Sn(e,{defaultModel:n,defaultThinkingLevel:r,defaultAgentId:t}))?Nr(void 0):F(`连接断开,设置未保存,请重试`,`error`))},ei=e=>{if(e.startsWith(`pending:`)||i.current.some(t=>t.key===`defaults:${e}`))return;let t=`pending:${nn()}`,n={id:t,workspaceId:e,title:`新会话`,status:`idle`,updatedAt:new Date().toISOString(),messages:[]};I({type:`conversation.create`,workspaceId:e},`create-session:${e}`,e=>({...e,conversations:[n,...e.conversations]}),t)&&(v(e),c(t),an(`session`),Jt(e=>[...e.filter(e=>e!==t),t]))},ti=e=>{e.startsWith(`pending:`)||I({type:`conversation.archive`,conversationId:e},`archive:${e}`,xn(e,{archivedAt:new Date().toISOString()}))},ri=e=>I({type:`conversation.unarchive`,conversationId:e},`archive:${e}`,xn(e,{archivedAt:void 0})),ii=async e=>{if(!M){F(`请先选择或创建会话`,`error`);return}if(!An){F(`当前模型不支持图片输入`,`error`);return}let t=[`image/png`,`image/jpeg`,`image/gif`,`image/webp`],n=e.filter(e=>t.includes(e.type)?e.size>5242880?(F(`图片「${e.name}」超过 5MB 上限`,`error`),!1):!0:(F(`不支持的文件格式「${e.name}」,仅支持 PNG/JPEG/GIF/WebP`,`error`),!1));if(n.length===0)return;let r=M.id;zt(r,e=>({...e,uploading:e.uploading+n.length}));let i=n.map(e=>({id:nn(),conversationId:r,name:e.name,url:URL.createObjectURL(e)}));pt(e=>[...e,...i]);for(let[e,t]of n.entries()){let n=i[e];try{let e=await new Promise((e,n)=>{let r=new FileReader;r.onload=()=>{let t=r.result,n=t.indexOf(`,`);e(n>=0?t.slice(n+1):t)},r.onerror=n,r.readAsDataURL(t)}),i=await fetch(`/api/upload`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({conversationId:r,name:t.name,mimeType:t.type,data:e})});if(!(i.headers.get(`content-type`)??``).includes(`application/json`)){F(`服务器未返回 JSON 响应,请刷新或重启服务`,`error`);continue}if(i.ok){let e=await i.json();if(lt.current.has(n.id))continue;pt(e=>e.filter(e=>e.id!==n.id)),URL.revokeObjectURL(n.url),zt(r,t=>({...t,images:[...t.images,{id:e.id,name:e.name,mimeType:e.mimeType,bytes:e.bytes}]}))}else{let e=await i.json().catch(()=>({error:`上传失败`}));F(e.error||`上传失败: ${i.status}`,`error`)}}catch(e){F(`上传错误: ${e instanceof Error?e.message:`网络错误`}`,`error`)}finally{pt(e=>e.map(e=>e.id===n.id?{...e,failed:!0}:e)),zt(r,e=>({...e,uploading:Math.max(0,e.uploading-1)}))}}},ai=e=>{let t=e.clipboardData?.items;if(!t)return;let n=[];for(let e=0;e<t.length;e++){let r=t[e];if(r.type.startsWith(`image/`)){let e=r.getAsFile();e&&n.push(e)}}if(n.length>0){if(!An){F(`当前模型不支持图片输入`,`error`);return}e.preventDefault(),ii(n)}},oi=e=>{e.dataTransfer?.types?.includes(`Files`)&&(e.preventDefault(),_t(!0))},si=e=>{e.currentTarget.contains(e.relatedTarget)||_t(!1)},ci=e=>{_t(!1);let t=Array.from(e.dataTransfer?.files??[]).filter(e=>e.type.startsWith(`image/`));if(t.length>0){if(e.preventDefault(),!An){F(`当前模型不支持图片输入`,`error`);return}ii(t)}},ui=t=>{if(t.preventDefault(),!M||Cn||!Pt.trim()&&Ft.length===0)return;if(Ft.length>0&&!An){F(`当前模型不支持图片输入,请移除图片或切换至支持图文的模型`,`error`);return}if(Lt>0){F(`图片正在上传中,请稍候…`,`info`);return}let n=M.status===`running`,r=`pending:${nn()}`,i=M.id,a=Pt,o=Ft,s=new Set([...e.conversations.find(e=>e.id===i)?.messages.map(e=>e.id)??[],...e.queuedMessages?.map(e=>e.id)??[]]);I({type:`message.send`,conversationId:M.id,content:Pt,...Ft.length>0?{images:Ft}:{},...It.length>0?{attachments:It}:{}},`send:${i}`,e=>e.conversations.find(e=>e.id===i)?.messages.some(e=>!s.has(e.id)&&e.role===`user`&&e.content===a.trim())||e.queuedMessages?.some(e=>!s.has(e.id)&&e.conversationId===i&&e.content===a.trim())||n?e:{...e,conversations:e.conversations.map(e=>e.id===i?{...e,messages:[...e.messages,{id:r,role:`user`,content:a,images:o,createdAt:new Date().toISOString()}]}:e)},void 0,()=>{dt(e=>[...e,{id:r,conversationId:i,content:a,images:o}]),zt(i,e=>e.text||e.images.length?e:{...e,text:a,images:o})},e=>!!(e.conversations.find(e=>e.id===i)?.messages.some(e=>!s.has(e.id)&&e.role===`user`&&e.content===a.trim())||e.queuedMessages?.some(e=>!s.has(e.id)&&e.conversationId===i&&e.content===a.trim())))?zt(M.id,e=>({...e,text:``,images:[]})):F(`连接断开,消息未发送,草稿已保留,请重试`,`error`)},di=(0,p.useMemo)(()=>(o.queuedMessages??[]).filter(e=>e.conversationId===M?.id),[o.queuedMessages,M?.id]),fi=e=>{I({type:`message.cancel`,conversationId:M?.id??``,queueId:e},`queue:${e}`,t=>({...t,queuedMessages:t.queuedMessages?.filter(t=>t.id!==e)}))||F(`连接断开,取消没有生效,请重试`,`error`)},mi=e=>{I({type:`message.steer`,conversationId:M?.id??``,queueId:e},`queue:${e}`,t=>({...t,queuedMessages:t.queuedMessages?.map(t=>t.id===e?{...t,priority:!0}:t)}))||F(`连接断开,优先发送没有生效,请重试`,`error`)},hi=e=>{I({type:`message.sendQueued`,conversationId:M?.id??``,queueId:e},`send-queued:${e}`,t=>({...t,queuedMessages:t.queuedMessages?.filter(t=>t.id!==e)}),void 0,void 0,t=>!t.queuedMessages?.some(t=>t.id===e))||F(`连接断开,发送没有生效,请重试`,`error`)},[_i,vi]=(0,p.useState)(),Di=(e,t=!1)=>{if(!M||_i)return;let n=M.messages.find(t=>t.id===e);n&&(n&&ct(e=>{let t=e[M.id]??{text:``,images:[],uploading:0};return t.text.trim()||t.images.length?e:{...e,[M.id]:{...t,text:n.content,images:n.images??[]}}}),$e({conversationId:M.id,messageId:e,kind:`withdraw`})),bn({type:`message.revert`,conversationId:M.id,messageId:e,withdraw:!0})?vi(e):($e(void 0),F(`连接断开,回退未执行,请重试`,`error`))},[Oi,ki]=(0,p.useState)(),Ai=(0,p.useRef)(void 0),ji=async(e,t)=>{Ai.current&&clearTimeout(Ai.current);let n=await gr(t);ki({id:e,kind:n?`copied`:`error`}),n||F(`复制失败:浏览器拒绝了剪贴板写入,可以手动选中这条消息的文字复制`,`error`),Ai.current=setTimeout(()=>ki(void 0),2e3)};(0,p.useEffect)(()=>()=>{Ai.current&&clearTimeout(Ai.current)},[]);let[Mi,Ni]=(0,p.useState)(),Pi=e=>{Mi||e.startsWith(`pending:`)||(bn({type:`conversation.fork`,conversationId:e})?Ni(e):F(`连接断开,分叉没有执行,请重试`,`error`))};return(0,m.jsxs)(`main`,{className:`shell${Ne?` shell-rail-collapsed`:``} mobile-panel-${Fe}`,"data-rail-collapsed":Ne,"data-selected-workspace":_??``,style:{"--sidebar-width":`${j.sidebar}px`,"--rail-col":Ne?void 0:`${j.rail}px`},children:[(0,m.jsxs)(`aside`,{children:[(0,m.jsxs)(`div`,{className:`brand`,children:[(0,m.jsx)(`span`,{children:`π`}),(0,m.jsxs)(`div`,{children:[(0,m.jsx)(`strong`,{children:`SUPER PI`}),(0,m.jsx)(`small`,{children:`agent workspace`})]})]}),(0,m.jsx)(`div`,{className:`sidebar-actions`,children:(0,m.jsx)(`button`,{className:`workspace-new`,onClick:()=>b(!0),children:`+ 工作区`})}),(0,m.jsx)(`div`,{className:`label`,children:`工作区`}),(0,m.jsx)(`nav`,{children:o.workspaces.map(e=>{let t=o.conversations.filter(t=>t.workspaceId===e.id),n=t.filter(e=>!e.archivedAt).sort(dr),r=t.filter(e=>e.archivedAt).sort(fr),i=h[e.id]??!1,a=ue[e.id]??!0;return(0,m.jsxs)(`section`,{className:`workspace-group`,children:[(0,m.jsx)(`div`,{className:`workspace-header`,children:Sr(`workspace`,e.id)?(0,m.jsx)(Hr,{initial:e.name,label:`重命名工作区 ${e.name}`,onCommit:t=>wr(`workspace`,e.id,t),onClose:xr}):(0,m.jsxs)(m.Fragment,{children:[(0,m.jsxs)(`button`,{className:`workspace-toggle`,"aria-expanded":!i,title:e.path,onContextMenu:t=>{t.preventDefault();let n=t.currentTarget.getBoundingClientRect();tt({workspace:e,x:t.clientX||n.left+24,y:t.clientY||n.top+n.height})},onClick:()=>{v(e.id),c(Ot.find(t=>t.workspaceId===e.id)?.id),g(t=>({...t,[e.id]:!i}))},children:[(0,m.jsx)(`span`,{className:`chevron chevron-folder`,"aria-hidden":`true`,children:i?`📁`:`📂`}),(0,m.jsxs)(`span`,{children:[(0,m.jsx)(`strong`,{onDoubleClick:()=>br(`workspace`,e.id),children:e.name}),(0,m.jsx)(`small`,{children:e.path})]})]}),(0,m.jsx)(`button`,{className:`workspace-add`,title:`在此工作区新建会话`,onClick:()=>ei(e.id),children:`+`}),(0,m.jsx)(`button`,{className:`workspace-settings`,title:`工作区设置(默认智能体 / 模型 / 思考强度)`,"aria-label":`工作区设置 ${e.name}`,onClick:()=>Nr(e.id),children:`⚙`}),(0,m.jsx)(`button`,{className:`workspace-delete`,title:`删除工作区`,onClick:()=>jr(e),children:`×`})]})}),!i&&(n.length?n.map(t=>(0,m.jsx)(`div`,{className:`conversation-row`,children:Sr(`conversation`,t.id)?(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(Hr,{initial:t.title,label:`重命名会话 ${t.title}`,onCommit:e=>wr(`conversation`,t.id,e),onClose:xr}),(0,m.jsx)(`small`,{className:`conversation-time`,children:ur(t.updatedAt,Tt)})]}):(0,m.jsxs)(m.Fragment,{children:[(0,m.jsxs)(`button`,{className:`conversation ${M?.id===t.id?`selected`:``}`,"data-conversation":t.id,title:t.title,"aria-label":`打开会话 ${t.title}`,onContextMenu:e=>{e.preventDefault();let n=e.currentTarget.getBoundingClientRect();it({conversation:t,x:e.clientX||n.left+24,y:e.clientY||n.top+n.height})},onClick:()=>{Zt(t.id),v(e.id)},children:[(0,m.jsx)(`i`,{className:`conversation-mark`,children:(0,m.jsx)(Yr,{state:Jr(t,ge.some(e=>e.conversationId===t.id),C.current[t.id])})}),(0,m.jsx)(`span`,{"data-conversation-title":t.id,onDoubleClick:()=>br(`conversation`,t.id),children:t.title}),(0,m.jsx)(`small`,{children:ur(t.updatedAt,Tt)})]}),(0,m.jsx)(`button`,{type:`button`,className:`conversation-fork`,"data-fork-conversation":t.id,"data-fork-state":Mi===t.id?`pending`:`idle`,disabled:!!Mi,title:`从此会话分叉出一个新会话`,"aria-label":`从会话 ${t.title} 分叉出一个新会话`,onClick:()=>Pi(t.id),children:`⑂`}),(0,m.jsx)(`button`,{className:`conversation-archive`,title:`归档会话`,"aria-label":`归档会话 ${t.title}`,onClick:()=>ti(t.id),children:`×`})]})},t.id)):(0,m.jsx)(`div`,{className:`conversation-empty`,children:`暂无会话`})),!i&&r.length>0&&(0,m.jsxs)(`div`,{className:`archived-group`,"data-archived-count":r.length,children:[(0,m.jsxs)(`button`,{className:`archived-toggle`,title:a?`展开已归档会话`:`收起已归档会话`,"aria-expanded":!a,onClick:()=>de(t=>({...t,[e.id]:!a})),children:[(0,m.jsx)(`span`,{className:`chevron`,"aria-hidden":`true`,children:a?`▸`:`▾`}),(0,m.jsxs)(`span`,{className:`archived-label`,children:[`Archived (`,r.length,`)`]})]}),!a&&r.map(e=>(0,m.jsx)(`div`,{className:`conversation-archived`,"data-archived-at":e.archivedAt,children:Sr(`conversation`,e.id)?(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(Hr,{initial:e.title,label:`重命名已归档会话 ${e.title}`,onCommit:t=>wr(`conversation`,e.id,t),onClose:xr}),(0,m.jsx)(`small`,{children:pr(e.archivedAt)})]}):(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(`i`,{className:`conversation-mark`,"aria-hidden":`true`}),(0,m.jsx)(`span`,{"data-archived-title":e.id,onDoubleClick:()=>br(`conversation`,e.id),children:e.title}),(0,m.jsx)(`small`,{children:pr(e.archivedAt)}),(0,m.jsx)(`button`,{type:`button`,className:`conversation-fork`,"data-fork-conversation":e.id,"data-fork-state":Mi===e.id?`pending`:`idle`,disabled:!!Mi,title:`从此会话分叉出一个新会话`,"aria-label":`从已归档会话 ${e.title} 分叉出一个新会话`,onClick:()=>Pi(e.id),children:`⑂`}),(0,m.jsx)(`button`,{className:`conversation-restore`,title:`恢复为正常会话`,"aria-label":`恢复会话 ${e.title}`,onClick:()=>ri(e.id),children:`↑`})]})},e.id))]})]},e.id)})}),(0,m.jsxs)(`footer`,{children:[(0,m.jsxs)(`div`,{className:`footer-status`,children:[(0,m.jsx)(`i`,{className:l?`online`:`offline`}),l?`已连接`:`正在连接`,(0,m.jsx)(`span`,{children:`本地持久化`})]}),(0,m.jsxs)(`button`,{type:`button`,className:`theme-settings-trigger`,"data-theme-open":`1`,"aria-haspopup":`dialog`,"aria-expanded":qr,onClick:()=>Xr(!0),children:[(0,m.jsx)(`span`,{"aria-hidden":`true`,children:`⚙`}),`设置`]}),(0,m.jsxs)(`button`,{type:`button`,className:`theme-settings-trigger`,"data-help-open":`1`,"aria-haspopup":`dialog`,"aria-expanded":Zr,onClick:()=>Qr(!0),children:[(0,m.jsx)(`span`,{"aria-hidden":`true`,children:`📖`}),`使用指南`]}),(0,m.jsxs)(`button`,{type:`button`,className:`theme-settings-trigger restart-needed-trigger`,title:`检查 Super Pi 更新`,onClick:async()=>{Me(`updating`);try{let e=await fetch(`/api/update-check`);if(!e.ok)throw Error(`check failed`);let t=await e.json();if(t.needsRestart)F(`当前代码已更新,正在重启服务…`,`info`);else if(t.updateAvailable){if(!(await fetch(`/api/update`,{method:`POST`})).ok)throw Error(`update failed`);F(`发现新版本 v${t.latestVersion},正在更新并重启…`,`info`)}else F(`当前已是最新版本`,`info`)}catch{F(`检查更新失败,请检查网络或服务日志`,`error`)}finally{Me(`idle`)}},children:[(0,m.jsx)(`span`,{"aria-hidden":`true`,children:je===`updating`?`⟳`:`↻`}),je===`updating`?`正在检查更新…`:`检查更新`]}),ke?.needsRestart&&(0,m.jsxs)(`button`,{type:`button`,className:`theme-settings-trigger restart-needed-trigger`,title:`代码已更新:当前运行 v${ke.runningVersion},磁盘版本 v${ke.diskVersion}。点击复制重启命令 /super_pi restart`,onClick:async()=>{Me(`updating`);try{if(!(await fetch(`/api/update`,{method:`POST`})).ok)throw Error(`update failed`);F(`正在更新 Super Pi,服务即将重启…`,`info`)}catch{Me(`idle`),F(`更新启动失败,请检查服务日志`,`error`)}},children:[(0,m.jsx)(`span`,{"aria-hidden":`true`,children:je===`updating`?`⟳`:`⚠️`}),je===`updating`?`正在更新并重启…`:`更新并重启服务`]})]})]}),(0,m.jsx)(Ei,{side:`left`,width:j.sidebar,min:j.sidebarMin,max:j.sidebarMax,label:`调整左侧栏宽度`,onWidth:Ve}),(0,m.jsxs)(`section`,{className:`chat`,"data-main-view":rn,"data-main-view-kind":rn,children:[un.length>0&&(0,m.jsx)(`div`,{className:`chat-tab-strip`,"data-chat-tab-strip":`1`,"data-session-count":Xt.length,"data-file-count":P.open.length,children:(0,m.jsx)(At,{tabs:un,prefix:`workspace-tab`,ariaLabel:`工作区标签页`,activeId:dn,onSelect:fn,onClose:pn,minWidth:120,maxWidth:320})}),(0,m.jsxs)(`header`,{children:[(0,m.jsxs)(`div`,{className:`chat-head`,children:[(0,m.jsx)(`h1`,{children:M?.title??`准备开始`}),be.size>0?[...be].map(([e,t])=>(0,m.jsx)(`p`,{"data-role":`run-status`,"data-status-key":e,children:`扩展状态:${e?`${e} · `:``}${t}`},e)):(0,m.jsx)(`p`,{"data-role":`run-status`,children:M?.status===`running`?`Agent 正在工作`:`Pi runtime · Web 控制台`})]}),(0,m.jsxs)(`span`,{className:`chat-view chat-view-${rn}`,"data-main-view-indicator":rn,"data-main-view-kind":rn,title:rn===`file`?`中间主画面正在显示文件 ${P.active?.path??``}`:`中间主画面正在显示这个会话的消息流`,children:[(0,m.jsx)(`span`,{className:`chat-view-label`,children:`主区显示`}),(0,m.jsx)(`span`,{className:`chat-view-kind`,children:rn===`file`?`文件`:`会话`}),rn===`file`&&P.active&&(0,m.jsx)(`span`,{className:`chat-view-name`,"data-main-view-name":P.active.name,children:P.active.name})]}),(0,m.jsx)(`div`,{className:`badge`,children:M?.status??`idle`})]}),d&&(0,m.jsx)(`div`,{className:`error`,children:d}),M?.error&&(0,m.jsx)(`div`,{className:`error`,"data-role":`conversation-error`,children:M.error}),k&&(0,m.jsxs)(`div`,{className:`extui-title-note`,children:[`扩展建议标题:`,k]}),ve.length>0&&(0,m.jsx)(`div`,{className:`toasts`,role:`status`,"aria-live":`polite`,"data-toast-count":ve.length,children:ve.map(e=>(0,m.jsx)(`div`,{className:`toast toast-${e.kind}`,"data-toast-kind":e.kind,children:e.text},e.key))}),rn===`file`?(0,m.jsx)(`div`,{className:`chat-file-view`,"data-main-file-view":mn?.path,"data-file-view-kind":`editor`,children:mn?(0,m.jsx)(wt,{file:mn,state:P.saves[mn.path]?.state??`idle`,message:P.saves[mn.path]?.message??``,external:P.external[mn.path]===!0,onChange:e=>P.updateBuffer(mn.path,e),onSave:()=>void P.save(mn.path,!1),onReload:()=>void P.reload(mn.path),onOverwrite:()=>void P.save(mn.path,!0)}):(0,m.jsx)(`p`,{className:`rail-empty`,"data-main-file-empty":`1`,children:`没有选中的文件。`})}):(0,m.jsxs)(m.Fragment,{children:[(0,m.jsxs)(`div`,{className:`messages`,ref:ee,"data-message-scroll-container":M?.id??``,onScroll:e=>{let t=e.currentTarget;t.dataset.programmaticScroll===`true`?delete t.dataset.programmaticScroll:Mt(t)},children:[!M&&(0,m.jsxs)(`div`,{className:`empty`,children:[(0,m.jsx)(`div`,{className:`orb`,children:`π`}),(0,m.jsx)(`h2`,{children:`开始一个新会话`}),o.workspaces.length===0?(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(`p`,{children:`先创建一个工作区,再开始会话。`}),(0,m.jsx)(`button`,{onClick:()=>b(!0),children:`+ 新建工作区`})]}):(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(`p`,{children:o.workspaces.length===1&&kt?`在「${kt.name}」中创建会话,即可向 Super Pi 发送任务。`:`选择工作区即可开始会话,向 Super Pi 发送任务。`}),o.workspaces.length>1&&(0,m.jsxs)(`div`,{className:`empty-workspace-select`,children:[(0,m.jsx)(`label`,{htmlFor:`empty-workspace-choice`,children:`工作区:`}),(0,m.jsxs)(`select`,{id:`empty-workspace-choice`,"aria-label":`选择工作区`,value:kt?.id,onChange:e=>{e.target.value===`__new__`?b(!0):v(e.target.value)},children:[o.workspaces.map(e=>(0,m.jsx)(`option`,{value:e.id,children:e.name},e.id)),(0,m.jsx)(`option`,{value:`__new__`,children:`+ 新建工作区…`})]})]}),kt&&(0,m.jsx)(`button`,{onClick:()=>ei(kt.id),children:`新建会话`}),(0,m.jsx)(`span`,{className:`empty-secondary-action`,role:`button`,tabIndex:0,onClick:()=>b(!0),onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),b(!0))},children:`+ 新建工作区`})]})]}),Yn.map(e=>{if(e.startsWith(`m:`)){let t=qn.get(e.slice(2));if(!t)return null;let n=!M?.messages.some(n=>n.id!==t.id&&n.role===`user`&&Yn.indexOf(`m:${n.id}`)>Yn.indexOf(e)),r=t.role===`user`&&M?.status!==`running`&&_i!==t.id,i=r&&n&&!t.id.startsWith(`pending:`),a=r&&!n&&!!t.piEntryId,o=t.content?(0,m.jsx)(Ke,{source:t.content}):(0,m.jsx)(`span`,{className:`typing`,children:`正在思考`}),s=t.toolCalls??[];if(t.role===`user`){let n=Oi?.id===t.id?Oi.kind:void 0,r=t.content||(t.images?.length?`[图片]`:``);return(0,m.jsxs)(`article`,{className:`message user`,"data-transcript-key":e,children:[(0,m.jsxs)(`div`,{className:`message-body`,children:[(0,m.jsxs)(`div`,{className:`bubble`,children:[(0,m.jsxs)(`div`,{className:`meta`,children:[he,(0,m.jsx)(`time`,{children:rr(t.createdAt)})]}),t.images&&t.images.length>0&&(0,m.jsx)(`div`,{className:`message-images`,"data-message-images":`1`,children:t.images.map(e=>(0,m.jsx)(`button`,{type:`button`,className:`message-image-btn`,"data-message-image-id":e.id,onClick:()=>ht({url:`/api/upload/${encodeURIComponent(M?.id??``)}/${encodeURIComponent(e.id)}`,name:e.name,bytes:e.bytes}),title:e.name?`查看大图:${e.name}`:`查看大图`,children:(0,m.jsx)(`img`,{src:`/api/upload/${encodeURIComponent(M?.id??``)}/${encodeURIComponent(e.id)}`,alt:e.name||`图片`,className:`message-image-thumb`,loading:`lazy`})},e.id))}),(0,m.jsx)(`div`,{className:`content`,children:o})]}),t.attachments&&t.attachments.length>0&&(0,m.jsx)(`div`,{className:`message-attachments`,"data-message-attachments":`1`,children:t.attachments.map(e=>{let n=`att:${t.id}:${e.kind}:${e.id}`,r=Je[n]??!1;return(0,m.jsxs)(`div`,{className:`message-attachment`,"data-message-attachment":e.kind,children:[(0,m.jsxs)(`button`,{type:`button`,className:`message-attachment-head`,"data-message-attachment-toggle":e.id,"aria-expanded":r,onClick:()=>Ye(e=>({...e,[n]:!r})),children:[(0,m.jsx)(`span`,{className:`message-attachment-glyph`,"aria-hidden":`true`,children:`▤`}),(0,m.jsx)(`span`,{className:`message-attachment-label`,children:`技能`}),(0,m.jsx)(`span`,{className:`message-attachment-name`,title:e.name,children:e.name}),(0,m.jsx)(`span`,{className:`message-attachment-chevron`,"aria-hidden":`true`,children:r?`▾`:`▸`})]}),r&&(0,m.jsx)(`pre`,{className:`message-attachment-body`,"data-message-attachment-body":e.id,children:e.content})]},n)})}),(0,m.jsxs)(`div`,{className:`message-actions`,children:[(0,m.jsx)(`button`,{type:`button`,className:`message-copy`,"data-copy-message":t.id,"data-copy-state":n,"aria-label":`复制这条消息:${r}`,onClick:()=>ji(t.id,r),children:n===`copied`?`已复制`:n===`error`?`复制失败`:`复制`}),i&&(0,m.jsx)(`button`,{type:`button`,className:`message-revert`,"data-withdraw-message":t.id,title:`撤回并编辑:删除这条消息并将文字恢复到输入框继续编辑`,onClick:()=>Di(t.id,!0),children:`撤回`}),a&&(0,m.jsx)(`button`,{type:`button`,className:`message-revert`,"data-revert-message":t.id,title:`回退到这里:删除后续全部内容(Pi 的记录和这里的历史一起清掉,不会新建会话,也不动任何代码文件)`,onClick:()=>Di(t.id),children:`回退`})]})]}),(0,m.jsx)(`div`,{className:`avatar`,title:he,children:he})]},e)}if(t.role===`tool`){let n=(()=>{let e=/<summary>([\s\S]*?)<\/summary>/.exec(t.content);return e?e[1].trim():`后台回传`})(),r=(()=>{let e=/<task id="[^"]+" state="([^"]+)"/.exec(t.content);return e?e[1]:`completed`})(),i=(()=>{let e=/<task_(?:result|error)>\n?([\s\S]*?)\n?<\/(?:task_result|task_error)>/.exec(t.content);return e?e[1]:t.content})(),a=`tool:${t.id}`,o=Je[a]??!1;return(0,m.jsx)(`article`,{className:`message tool`,"data-transcript-key":e,"data-tool-state":r,children:(0,m.jsxs)(`div`,{className:`message-body`,children:[(0,m.jsx)(`div`,{className:`bg-result-head`,"data-bg-result-head":t.id,children:(0,m.jsxs)(`button`,{type:`button`,className:`bg-result-toggle`,"data-bg-result-toggle":t.id,"aria-expanded":o,title:o?`折叠后台回传`:`展开后台回传`,onClick:()=>Ye(e=>({...e,[a]:!o})),children:[(0,m.jsx)(`span`,{className:`bg-result-glyph`,"aria-hidden":`true`,children:r===`error`?`✕`:`↩`}),(0,m.jsx)(`span`,{className:`bg-result-label`,children:`后台回传`}),(0,m.jsx)(`span`,{className:`bg-result-summary`,title:n,children:n}),!o&&(0,m.jsx)(`span`,{className:`bg-result-preview`,"data-bg-result-preview":t.id,title:`展开查看完整输出`,children:i.split(`
|
|
56
|
+
如需本地计算,声明 script: { path, timeoutMs, parameters }。脚本必须是 Python,参数以 JSON 从 stdin 传入,stdout 必须只输出一个 JSON 对象。在 index.html 中加载 <script src="./super-pi-tool.js"><\/script>,然后调用 SuperPiTool.run(params)、SuperPiTool.get(runId)、SuperPiTool.wait(runId) 或 SuperPiTool.cancel(runId)。这些都是已绑定本工具身份的相对接口;不要调用 /api/exec,不要猜测 toolId,也不要传 shell 命令、任意文件路径或未声明参数。页面必须实际执行一次示例任务并显示运行状态、日志和结构化结果。最后返回注册 id、访问地址、文件清单和验证结果。`);u(e),window.setTimeout(()=>u(!1),1800)};return(0,p.useEffect)(()=>{let e=e=>{e.key===`Escape`&&n()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[n]),(0,d.createPortal)((0,m.jsx)(`div`,{className:`tool-overlay`,"data-tool-settings-overlay":`1`,onClick:e=>{e.target===e.currentTarget&&n()},children:(0,m.jsxs)(`div`,{className:`tool-dialog`,role:`dialog`,"aria-modal":`true`,"aria-labelledby":`tool-settings-title`,"data-tool-settings":`1`,children:[(0,m.jsxs)(`div`,{className:`tool-dialog-head`,children:[(0,m.jsx)(`h2`,{className:`tool-dialog-title`,id:`tool-settings-title`,children:`⚙ 工具设置`}),(0,m.jsx)(`button`,{type:`button`,className:`tool-dialog-close`,"data-tool-settings-close":`1`,title:`关闭(Esc)`,"aria-label":`关闭工具设置`,onClick:n,children:`✕`})]}),(0,m.jsxs)(`div`,{className:`tool-dialog-body`,children:[(0,m.jsxs)(`div`,{className:`tool-prompt-card`,children:[(0,m.jsx)(`strong`,{children:`给 Agent 开发新工具`}),(0,m.jsx)(`p`,{className:`tool-hint`,children:`工具会挂载到 Super Pi 的统一 HTTP 服务,不需要再启动独立服务器。`}),(0,m.jsx)(`button`,{type:`button`,className:`tool-link-copy-prompt`,"data-tool-development-prompt":`1`,onClick:()=>void f(),children:l?`已复制`:`复制工具开发 Prompt`})]}),(0,m.jsx)(`div`,{className:`rail-section-head`,children:(0,m.jsx)(`h2`,{className:`rail-section-title`,children:`从预置列表添加`})}),(0,m.jsx)(`div`,{className:`tool-presets`,"data-tool-presets":Yt.length,children:Yt.map(n=>{let r=e.some(e=>e.kind===n.kind);return(0,m.jsxs)(`div`,{className:`tool-preset`,"data-tool-preset":n.kind,children:[(0,m.jsx)(`span`,{className:`tool-preset-glyph`,"aria-hidden":`true`,children:n.glyph}),(0,m.jsxs)(`span`,{className:`tool-preset-text`,children:[(0,m.jsx)(`strong`,{children:n.name}),(0,m.jsx)(`small`,{children:n.blurb})]}),(0,m.jsx)(`button`,{type:`button`,className:`tool-preset-add`,"data-tool-add-preset":n.kind,disabled:r,title:r?`${n.name}已经在工具栏里了`:`添加${n.name}`,"aria-label":r?`${n.name}已添加`:`添加${n.name}`,onClick:()=>t(an(e,n.kind)),children:r?`已添加`:`添加`})]},n.kind)})}),(0,m.jsx)(`div`,{className:`rail-section-head`,children:(0,m.jsx)(`h2`,{className:`rail-section-title`,children:`添加网址工具`})}),(0,m.jsx)(`p`,{className:`tool-hint`,children:`填一个网址,它会变成一个图标;点开后在弹窗里用按钮在新标签页打开。网址只接受 http / https / mailto。`}),(0,m.jsx)(`input`,{className:`tool-input`,"data-tool-url-input":`1`,value:r,maxLength:2048,placeholder:`https://example.com/工具`,"aria-label":`工具网址`,onChange:e=>{i(e.target.value),c(void 0)}}),(0,m.jsx)(`input`,{className:`tool-input`,"data-tool-url-name":`1`,value:a,maxLength:40,placeholder:`名称(可留空,默认用域名)`,"aria-label":`工具名称(可选)`,onChange:e=>o(e.target.value)}),(0,m.jsx)(`button`,{type:`button`,className:`tool-url-add`,"data-tool-url-add":`1`,disabled:!r.trim(),onClick:()=>{let n=$t(r);n.ok?e.length>=32?c(`工具最多 32 个,先删掉一个再加。`):(t([...e,{id:rn(`link`,e.map(e=>e.id)),kind:`link`,name:a.trim()||si(n.url),glyph:`↗`,url:n.url}]),i(``),o(``),c(void 0)):c(n.reason)},children:`添加网址工具`}),s&&(0,m.jsx)(`p`,{className:`tool-error`,"data-tool-error":`1`,role:`alert`,children:s}),(0,m.jsxs)(`p`,{className:`tool-hint`,"data-tool-scope-note":`1`,children:[`能自定义到哪:从上面挑预置工具,或自己填一个网址。`,(0,m.jsx)(`b`,{children:`没有开放「写一段脚本来执行」这一档`}),`——它在浏览器里等同于任意代码执行,会拿着本应用的权限跑。计算器是手写的算术解析器,不含 eval,也认不出变量名。`]})]})]})}),document.body)}function si(e){try{return new URL(e,typeof window>`u`?`http://localhost`:window.location.href).hostname||e.slice(0,30)}catch{return e.slice(0,30)}}function ci({tool:e,onClose:t}){return(0,p.useEffect)(()=>{let e=e=>{e.key===`Escape`&&t()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[t]),(0,d.createPortal)((0,m.jsx)(`div`,{className:`tool-overlay`,"data-tool-overlay":e.id,onClick:e=>{e.target===e.currentTarget&&t()},children:(0,m.jsxs)(`div`,{className:`tool-dialog`,role:`dialog`,"aria-modal":`true`,"aria-labelledby":`tool-dialog-title`,"data-tool-dialog":e.id,children:[(0,m.jsxs)(`div`,{className:`tool-dialog-head`,children:[(0,m.jsxs)(`h2`,{className:`tool-dialog-title`,id:`tool-dialog-title`,children:[(0,m.jsx)(`span`,{className:`tool-dialog-glyph`,"aria-hidden":`true`,children:e.glyph}),e.name]}),(0,m.jsx)(`button`,{type:`button`,className:`tool-dialog-close`,"data-tool-close":`1`,title:`关闭(Esc)`,"aria-label":`关闭${e.name}`,onClick:t,children:`✕`})]}),(0,m.jsxs)(`div`,{className:`tool-dialog-body`,children:[e.kind===`calculator`&&(0,m.jsx)(ui,{}),e.kind===`timestamp`&&(0,m.jsx)(di,{}),e.kind===`link`&&(0,m.jsx)(fi,{tool:e})]})]})}),document.body)}function li({image:e,onClose:t}){return(0,p.useEffect)(()=>{let e=e=>{e.key===`Escape`&&t()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[t]),(0,d.createPortal)((0,m.jsx)(`div`,{className:`lightbox-overlay`,"data-lightbox-overlay":`1`,onClick:e=>{e.target===e.currentTarget&&t()},children:(0,m.jsxs)(`div`,{className:`lightbox-dialog`,role:`dialog`,"aria-modal":`true`,"aria-labelledby":`lightbox-title`,"data-lightbox-dialog":`1`,children:[(0,m.jsxs)(`div`,{className:`lightbox-head`,children:[(0,m.jsx)(`h2`,{className:`lightbox-title`,id:`lightbox-title`,children:e.name||`图片大图`}),(0,m.jsx)(`button`,{type:`button`,className:`lightbox-close`,"data-lightbox-close":`1`,title:`关闭(Esc)`,"aria-label":`关闭大图`,onClick:t,children:`✕`})]}),(0,m.jsx)(`div`,{className:`lightbox-body`,children:(0,m.jsx)(`img`,{src:e.url,alt:e.name||`图片大图`,className:`lightbox-image`})})]})}),document.body)}function ui(){let[e,t]=(0,p.useState)(`12 * (3 + 4)`),[n,r]=(0,p.useState)(!1),i=vn(e),a=e=>{r(!1),t(t=>n?e:t+e)};return(0,m.jsxs)(`div`,{className:`tool-calc`,"data-tool-calc":`1`,children:[(0,m.jsx)(`input`,{className:`tool-input tool-calc-input`,"data-calc-input":`1`,value:e,maxLength:240,"aria-label":`算式`,spellCheck:!1,autoComplete:`off`,placeholder:`例如 (12 + 8) / 4`,onChange:e=>{r(!1),t(e.target.value)},onKeyDown:e=>{e.key===`Enter`&&e.preventDefault()}}),(0,m.jsx)(`div`,{className:`tool-calc-keys`,"data-calc-keys":`1`,children:[[`7`,`7`],[`8`,`8`],[`9`,`9`],[`÷`,`/`],[`(`,`(`],[`)`,`)`],[`4`,`4`],[`5`,`5`],[`6`,`6`],[`×`,`*`],[`^`,`^`],[`%`,`%`],[`1`,`1`],[`2`,`2`],[`3`,`3`],[`-`,`-`],[`←`,`back`],[`C`,`clear`],[`0`,`0`],[`.`,`.`],[`+`,`+`]].map(([e,n])=>(0,m.jsx)(`button`,{type:`button`,className:`tool-calc-key`,"data-calc-key":n,"aria-label":e===`C`?`清空`:e===`←`?`退格`:`输入 ${e}`,onClick:()=>{n===`clear`?(t(``),r(!1)):n===`back`?(t(e=>e.slice(0,-1)),r(!1)):a(n)},children:e},e))}),i.ok?(0,m.jsx)(`p`,{className:`tool-calc-result`,"data-calc-result":`1`,"data-calc-value":String(i.value),children:yn(i.value)}):(0,m.jsx)(`p`,{className:`tool-calc-error`,"data-calc-error":`1`,role:`alert`,children:i.reason})]})}function di(){let[e,t]=(0,p.useState)(``),n=bn(e),r=n.ok?xn(n.ms):null;return(0,m.jsxs)(`div`,{className:`tool-stamp`,"data-tool-stamp":`1`,children:[(0,m.jsx)(`input`,{className:`tool-input`,"data-stamp-input":`1`,value:e,maxLength:64,spellCheck:!1,"aria-label":`时间戳或日期`,placeholder:`1757000000 或 2026-10-08T09:00:00`,onChange:e=>t(e.target.value)}),r?(0,m.jsxs)(`dl`,{className:`tool-stamp-out`,"data-stamp-result":`1`,children:[(0,m.jsxs)(`div`,{children:[(0,m.jsx)(`dt`,{children:`本地时间`}),(0,m.jsx)(`dd`,{"data-stamp-local":`1`,children:r.local})]}),(0,m.jsxs)(`div`,{children:[(0,m.jsx)(`dt`,{children:`ISO(UTC)`}),(0,m.jsx)(`dd`,{"data-stamp-iso":`1`,children:r.iso})]}),(0,m.jsxs)(`div`,{children:[(0,m.jsx)(`dt`,{children:`秒`}),(0,m.jsx)(`dd`,{"data-stamp-seconds":`1`,children:r.seconds})]}),(0,m.jsxs)(`div`,{children:[(0,m.jsx)(`dt`,{children:`毫秒`}),(0,m.jsx)(`dd`,{"data-stamp-ms":`1`,children:r.milliseconds})]})]}):(0,m.jsx)(`p`,{className:`tool-stamp-error`,"data-stamp-error":`1`,role:`alert`,children:n.ok?`这个日期无法显示。`:n.reason})]})}function fi({tool:e}){let[t,n]=(0,p.useState)(!1),r=async()=>{let e=await gr(`请为 Super Pi 开发一个全局右侧栏工具并自动挂载。不要启动独立 HTTP 服务器。通过内置 tool_register 提交完整的 HTML/JS/CSS 文件;工具将被 Super Pi 保存并在所有 workspace 可用。如需计算,注册一个 Python 脚本(不接受 shell 命令),参数经 JSON stdin 传入,stdout 只输出 JSON 结果。在页面里加载 ./super-pi-tool.js,使用 SuperPiTool.run(params)、wait(runId)、get(runId) 和 cancel(runId) 执行、等待、查看和取消任务。它们会自动绑定当前工具;不要调用 /api/exec,也不要猜测 toolId。`);n(e),window.setTimeout(()=>n(!1),1800)};return(0,m.jsxs)(`div`,{className:`tool-link`,"data-tool-link":e.id,children:[(0,m.jsx)(`p`,{className:`tool-hint`,children:`这是旧版外部网页链接;新工具由 Super Pi 统一托管,并可选绑定受控 Python 脚本。`}),(0,m.jsx)(`code`,{className:`tool-link-url`,"data-tool-link-url":e.id,children:e.url}),(0,m.jsxs)(`div`,{className:`tool-link-actions`,children:[(0,m.jsx)(`a`,{className:`tool-link-open`,"data-tool-link-open":e.id,href:e.url,target:`_blank`,rel:`noopener noreferrer`,title:e.url,children:`在新标签页打开 ?`}),(0,m.jsx)(`button`,{type:`button`,className:`tool-link-copy-prompt`,"data-tool-copy-prompt":e.id,onClick:()=>void r(),children:t?`已复制`:`复制开发 Prompt`})]}),/^https?:\/\//i.test(e.url??``)&&(0,m.jsx)(`iframe`,{className:`tool-link-frame`,"data-tool-frame":e.id,src:e.url,title:e.name,sandbox:`allow-forms allow-modals allow-pointer-lock allow-popups allow-same-origin allow-scripts`,referrerPolicy:`no-referrer`})]})}var pi=`(max-width: 1100px)`,mi=(e,t,n)=>Math.min(Math.max(e,t),Math.max(t,n));function hi({tool:e,onClose:t}){return(0,d.createPortal)((0,m.jsx)(`div`,{className:`tool-overlay`,onClick:e=>{e.target===e.currentTarget&&t()},children:(0,m.jsxs)(`div`,{className:`tool-dialog`,role:`dialog`,"aria-modal":`true`,children:[(0,m.jsxs)(`div`,{className:`tool-dialog-head`,children:[(0,m.jsxs)(`h2`,{className:`tool-dialog-title`,children:[`🧩 `,e.name]}),(0,m.jsx)(`button`,{type:`button`,className:`tool-dialog-close`,onClick:t,children:`✕`})]}),(0,m.jsx)(`div`,{className:`tool-dialog-body`,children:(0,m.jsx)(`iframe`,{className:`tool-link-frame`,src:`/tools/${encodeURIComponent(e.id)}/`,title:e.name,sandbox:`allow-forms allow-modals allow-popups allow-scripts`,referrerPolicy:`no-referrer`})})]})}),document.body)}function gi(e,t,n){let r=Math.max(0,Math.round(n)-480),i=mi(520,180,Math.max(180,r-240)),a=mi(Math.round(e),180,i),o=mi(640,240,Math.max(240,r-a));return{sidebar:a,rail:mi(Math.round(t),240,o),sidebarMin:180,sidebarMax:i,railMin:240,railMax:o,sumCap:r}}var _i=`super-pi.rail-widths.v1`,vi=`super-pi.reader-state.v1`,yi=48,bi=160,xi=e=>{e.dataset.programmaticScroll=`true`,window.setTimeout(()=>{delete e.dataset.programmaticScroll},0)};function Si(){try{if(typeof window>`u`)return{conversations:{}};let e=JSON.parse(window.localStorage.getItem(`super-pi.reader-state.v1`)??`null`);if(!e||typeof e!=`object`)return{conversations:{}};let t={};if(e.conversations&&typeof e.conversations==`object`)for(let[n,r]of Object.entries(e.conversations)){let e=r;typeof e.followTail==`boolean`&&(t[n]={followTail:e.followTail,scrollTop:Number.isFinite(e.scrollTop)?Math.max(0,Number(e.scrollTop)):0,...typeof e.anchorKey==`string`?{anchorKey:e.anchorKey}:{},...Number.isFinite(e.anchorOffset)?{anchorOffset:Number(e.anchorOffset)}:{}})}let n={};if(e.readAt&&typeof e.readAt==`object`)for(let[t,r]of Object.entries(e.readAt))typeof r==`string`&&(n[t]=r);return{activeId:typeof e.activeId==`string`?e.activeId:void 0,workspaceId:typeof e.workspaceId==`string`?e.workspaceId:void 0,conversations:t,readAt:n}}catch{return{conversations:{}}}}function Ci(e){try{typeof window<`u`&&window.localStorage.setItem(vi,JSON.stringify(e))}catch{}}function wi(){try{if(typeof window>`u`)return{};let e=window.localStorage.getItem(_i);if(e===null)return{};let t=JSON.parse(e),n=e=>typeof e==`number`&&Number.isFinite(e)&&e>0?Math.round(e):void 0;return{sidebar:n(t.sidebar),rail:n(t.rail)}}catch{return{}}}function Ti(e){try{if(typeof window>`u`)return;window.localStorage.setItem(_i,JSON.stringify(e))}catch{}}function Ei({side:e,width:t,min:n,max:r,label:i,onWidth:a}){let[o,s]=(0,p.useState)(!1),c=e=>e.shiftKey?64:16;return(0,m.jsx)(`div`,{className:`rail-resizer rail-resizer-${e}${o?` rail-resizer-dragging`:``}`,"data-rail-resizer":e,"data-rail-resizer-dragging":o?`true`:`false`,"data-rail-resizer-width":t,role:`separator`,"aria-orientation":`vertical`,"aria-label":i,"aria-valuenow":t,"aria-valuemin":n,"aria-valuemax":r,tabIndex:0,onMouseDown:i=>{i.preventDefault(),i.currentTarget.focus();let o=i.clientX,c=t;s(!0);let l=document.body,u=l.style.cursor,d=l.style.userSelect;l.style.cursor=`col-resize`,l.style.userSelect=`none`;let f=t=>{let i=e===`left`?t.clientX-o:o-t.clientX;a(mi(c+i,n,r))},p=()=>{l.style.cursor=u,l.style.userSelect=d,s(!1),window.removeEventListener(`mousemove`,f),window.removeEventListener(`mouseup`,p)};window.addEventListener(`mousemove`,f),window.addEventListener(`mouseup`,p)},onKeyDown:i=>{let o=c(i);i.key===`ArrowLeft`?(i.preventDefault(),a(mi(e===`left`?t-o:t+o,n,r))):i.key===`ArrowRight`?(i.preventDefault(),a(mi(e===`left`?t+o:t-o,n,r))):i.key===`Home`?(i.preventDefault(),a(n)):i.key===`End`&&(i.preventDefault(),a(r))}})}function Di(){let[e,t]=(0,p.useState)(tr),[n,r]=(0,p.useState)([]),i=(0,p.useRef)([]),a=(0,p.useRef)(null);a.current||=Si();let o=n.reduce((e,t)=>t.apply(e),e),[s,c]=(0,p.useState)(a.current.activeId),[l,u]=(0,p.useState)(!1),[d,f]=(0,p.useState)(),[h,g]=(0,p.useState)({}),[_,v]=(0,p.useState)(a.current.workspaceId),[y,b]=(0,p.useState)(!1),x=(0,p.useRef)(void 0),S=(0,p.useRef)(a.current.conversations),C=(0,p.useRef)(a.current.readAt??{}),[w,T]=(0,p.useState)(0),ee=(0,p.useRef)(null),E=(0,p.useRef)(null),te=(0,p.useRef)(void 0),ne=(0,p.useRef)(a.current.activeId),[re,ie]=(0,p.useState)({followTail:!0,scrollTop:0}),ae=(0,p.useRef)(void 0),oe=(0,p.useRef)(new Map),se=(0,p.useRef)(void 0),D=(0,p.useCallback)(()=>{if(se.current!==void 0&&(cancelAnimationFrame(se.current),se.current=void 0),!oe.current.size)return;let e=[...oe.current.values()];oe.current.clear();let n=new Map,r=new Map;for(let t of e){let e=`${t.conversationId}\u0000${t.messageId}`;if(t.type===`message.thinking`){let n=r.get(e);r.set(e,{text:`${n?.text??``}${t.delta}`,at:n?.at??t.at})}else n.set(e,`${n.get(e)??``}${t.delta}`)}t(e=>({...e,conversations:e.conversations.map(e=>{let t=e.messages.map(t=>{let i=`${e.id}\u0000${t.id}`,a=n.get(i),o=r.get(i);return a===void 0&&o===void 0?t:{...t,...a===void 0?{}:{content:t.content+a},...o===void 0?{}:{thinking:(t.thinking??``)+o.text,thinkingBlocks:[...t.thinkingBlocks??[],{at:o.at??t.content.length,text:o.text}]}}});return t.every((t,n)=>t===e.messages[n])?e:{...e,messages:t}})}))},[]),ce=(0,p.useCallback)(e=>{let t=`${e.type}\u0000${e.conversationId}\u0000${e.messageId}`,n=oe.current.get(t);oe.current.set(t,n?{...n,delta:n.delta+e.delta}:e),se.current===void 0&&(se.current=requestAnimationFrame(D))},[D]),le=(0,p.useCallback)(e=>{ae.current=e},[]),[ue,de]=(0,p.useState)({}),[fe,pe]=(0,p.useState)(()=>{if(typeof window>`u`)return``;try{return window.localStorage.getItem(`super-pi.user-nickname`)??``}catch{return``}}),me=e=>{let t=e.slice(0,24);pe(t);try{window.localStorage.setItem(`super-pi.user-nickname`,t)}catch{}},he=fe.trim()||`你`,[ge,_e]=(0,p.useState)([]),[ve,ye]=(0,p.useState)([]),[be,xe]=(0,p.useState)(new Map),[Se,O]=(0,p.useState)(new Map),[k,Ce]=(0,p.useState)(),[we,Te]=(0,p.useState)(),[Ee,De]=(0,p.useState)(),[A,Oe]=(0,p.useState)(),[ke,Ae]=(0,p.useState)(),[je,Me]=(0,p.useState)(`idle`),[Ne,Pe]=(0,p.useState)(()=>typeof window<`u`&&typeof window.matchMedia==`function`&&window.matchMedia(`(max-width: 1100px)`).matches),[Fe,Ie]=(0,p.useState)(`chat`),[Le,Re]=(0,p.useState)(()=>typeof window>`u`?1440:window.innerWidth);(0,p.useEffect)(()=>{let e=()=>Re(window.innerWidth);return window.addEventListener(`resize`,e),e(),()=>window.removeEventListener(`resize`,e)},[]);let[ze,Be]=(0,p.useState)(wi),j=gi(ze.sidebar??270,ze.rail??320,Le);(0,p.useEffect)(()=>{Ti({sidebar:j.sidebar,rail:j.rail})},[j.sidebar,j.rail]);let Ve=e=>Be(t=>({...t,sidebar:e})),He=e=>Be(t=>({...t,rail:e}));(0,p.useEffect)(()=>{if(typeof window>`u`||typeof window.matchMedia!=`function`)return;let e=window.matchMedia(pi),t=e=>{e.matches&&Pe(!0)};return e.addEventListener(`change`,t),e.matches&&Pe(!0),()=>e.removeEventListener(`change`,t)},[]);let[Ue,We]=(0,p.useState)(new Map),[Ge,qe]=(0,p.useState)({}),[Je,Ye]=(0,p.useState)({}),[Xe,Ze]=(0,p.useState)({}),[Qe,$e]=(0,p.useState)(),[et,tt]=(0,p.useState)(),[nt,it]=(0,p.useState)(),ot=(0,p.useRef)(new Map),[st,ct]=(0,p.useState)({}),lt=(0,p.useRef)(new Set),[ut,dt]=(0,p.useState)([]),[ft,pt]=(0,p.useState)([]),[mt,ht]=(0,p.useState)(),[gt,_t]=(0,p.useState)(!1),vt=(0,p.useRef)(null),yt=(0,p.useRef)(!1),bt=(0,p.useRef)(0),xt=(0,p.useRef)([]),St=(0,p.useRef)(void 0),Ct=(0,p.useRef)(!1),[Tt,Dt]=(0,p.useState)(()=>Date.now());(0,p.useEffect)(()=>{let e=window.setInterval(()=>Dt(Date.now()),3e4);return()=>window.clearInterval(e)},[]);let Ot=(0,p.useMemo)(()=>o.conversations.filter(e=>!e.archivedAt),[o.conversations]),M=(0,p.useMemo)(()=>Ot.find(e=>e.id===s)??Ot.find(e=>e.workspaceId===_)??(_&&o.workspaces.some(e=>e.id===_)?void 0:Ot[0]),[s,Ot,_,o.workspaces]),kt=(0,p.useMemo)(()=>o.workspaces.find(e=>e.id===_)??o.workspaces[0],[o.workspaces,_]),jt=(0,p.useRef)(_);jt.current=_,(0,p.useEffect)(()=>{M&&v(M.workspaceId)},[M?.id]),(0,p.useEffect)(()=>{St.current=s},[s]),(0,p.useEffect)(()=>{te.current=M?.id},[M?.id]),(0,p.useEffect)(()=>{let e=M?.id;if(!e)return;let t=o.conversations.find(t=>t.id===e);if(!t)return;let n=C.current[e];C.current[e]=t.updatedAt,n!==t.updatedAt&&T(e=>e+1),Ci({activeId:St.current,workspaceId:M?.workspaceId??_,conversations:S.current,readAt:C.current})},[M?.id,o.conversations]),(0,p.useEffect)(()=>{l&&s&&Ci({activeId:s,workspaceId:M?.workspaceId??_,conversations:S.current})},[s,M?.workspaceId,l,_]),(0,p.useEffect)(()=>{let e=ee.current;if(!e||!M?.id)return;let t=M.id,n=S.current[t]??{followTail:!0,scrollTop:0};ie(n),requestAnimationFrame(()=>{if(ee.current===e){if(n.followTail)xi(e),e.scrollTop=e.scrollHeight;else{let t=n.anchorKey?e.querySelector(`[data-transcript-key="${CSS.escape(n.anchorKey)}"]`):null;e.scrollTop=t?Math.max(0,e.scrollTop+t.getBoundingClientRect().top-(n.anchorOffset??0)-e.getBoundingClientRect().top):n.scrollTop}}}),ne.current=void 0},[M?.id]);let Mt=(0,p.useCallback)((e,t=!1,n=!1)=>{let r=te.current;if(!r)return;let i=e.scrollHeight-e.clientHeight-e.scrollTop,a=S.current[r]??{followTail:!0,scrollTop:0},o=t?!1:n?!0:a.followTail?i<=bi:i<=yi,s,c;if(!o){let t=e.getBoundingClientRect().top,n=[...e.querySelectorAll(`[data-transcript-key]`)].find(e=>e.getBoundingClientRect().bottom>t);s=n?.dataset.transcriptKey,c=n?n.getBoundingClientRect().top-t:void 0}let l={followTail:o,scrollTop:e.scrollTop,...s?{anchorKey:s,anchorOffset:c}:{}};S.current[r]=l,ie(l),Ci({activeId:r,workspaceId:jt.current,conversations:S.current})},[]),Nt=st[M?.id??``]??{text:``,images:[],attachments:[],uploading:0},Pt=Nt.text,Ft=Nt.images,It=Nt.attachments,Lt=Nt.uploading,zt=(e,t)=>ct(n=>({...n,[e]:t(n[e]??{text:``,images:[],attachments:[],uploading:0})})),Vt=e=>zt(M?.id??``,t=>({...t,text:e})),Ht=e=>zt(M?.id??``,t=>({...t,attachments:t.attachments.some(t=>t.kind===e.kind&&t.id===e.id)?t.attachments:[...t.attachments,e]})),Ut=(e,t)=>zt(M?.id??``,n=>({...n,attachments:n.attachments.filter(n=>n.kind!==e||n.id!==t)})),[Wt,Gt]=(0,p.useState)({visible:!1,query:``,rect:null}),Kt=e=>zt(M?.id??``,t=>({...t,images:typeof e==`function`?e(t.images):e})),[qt,Jt]=(0,p.useState)([]),Yt=qt.length>0?qt:M?[M.id]:[],Xt=(0,p.useMemo)(()=>Yt.map(e=>Ot.find(t=>t.id===e)).filter(e=>e!==void 0),[Yt,Ot]),Zt=(0,p.useCallback)(e=>{c(e);let t=Ot.find(t=>t.id===e)?.workspaceId;t&&v(t),an(`session`),Jt(t=>t.includes(e)?t:t.length>0?[...t,e]:[...M?.id&&M.id!==e?[M.id]:[],e])},[M]),N=(0,p.useCallback)(e=>{let t=qt.indexOf(e);if(t<0)return;let n=qt.filter(t=>t!==e);if(Jt(n),M?.id!==e)return;let r=n[Math.min(t,n.length-1)];r&&c(r)},[qt,M]),P=Et(_),[Qt,$t]=(0,p.useState)(Gr[0].id),[en,tn]=(0,p.useState)(!1);(0,p.useEffect)(()=>{let e=!1;return fetch(`/api/terminal`).then(e=>e.json()).then(t=>{e||tn(!!t?.enabled)}).catch(()=>{e||tn(!1)}),()=>{e=!0}},[]);let[rn,an]=(0,p.useState)(`session`);(0,p.useEffect)(()=>{rn===`file`&&!P.active&&an(`session`)},[rn,P.active]);let on=(0,p.useCallback)(e=>{P.setActivePath(e),an(`file`),$t(`files`)},[P.setActivePath]),sn=(0,p.useCallback)(async e=>{await P.openFile(e),an(`file`)},[P.openFile]),cn=(0,p.useMemo)(()=>Xt.map(e=>({id:`session:${e.id}`,rawId:e.id,kind:`session`,label:e.title,title:e.title,closable:Xt.length>1||P.open.length>0,badge:(0,m.jsx)(Yr,{state:Jr(e,ge.some(t=>t.conversationId===e.id),C.current[e.id])})})),[Xt,P.open.length,ge,o.conversations,w]),ln=(0,p.useMemo)(()=>P.open.map(e=>({id:`file:${e.path}`,rawId:e.path,kind:`file`,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})),[P.open]),un=(0,p.useMemo)(()=>[...cn,...ln],[cn,ln]),dn=rn===`file`&&P.activePath?`file:${P.activePath}`:M?.id?`session:${M.id}`:void 0,fn=(0,p.useCallback)(e=>{if(e.startsWith(`file:`)){let t=e.slice(5);on(t)}else if(e.startsWith(`session:`)){let t=e.slice(8);Zt(t)}},[on,Zt]),pn=(0,p.useCallback)(e=>{if(e.startsWith(`file:`)){let t=e.slice(5);P.closeFile(t),P.activePath===t&&P.open.length<=1&&an(`session`)}else if(e.startsWith(`session:`)){let t=e.slice(8);N(t)}},[P,N]),mn=P.active,hn=M?.id;(0,p.useEffect)(()=>{hn&&!hn.startsWith(`pending:`)&&bn({type:`conversation.select`,conversationId:hn})},[hn]);let F=(e,t=`info`)=>{if(!e)return;let n=++bt.current;ye(r=>[...r,{key:n,text:e,kind:t}]),xt.current.push(window.setTimeout(()=>ye(e=>e.filter(e=>e.key!==n)),7e3))},gn=(e,t)=>{let n=e.findIndex(e=>e.id===t.id);if(n<0)return[t,...e];let r=[...e];return r[n]=t,r},_n=(e,t)=>{let n=e.findIndex(e=>e.id===t.id);if(n<0)return[t,...e];let r=[...e];return r[n]=t,r},vn=(e,t)=>{if(t.blocking)_e(n=>[...n.filter(n=>n.conversationId!==e||n.request.requestId!==t.requestId),{conversationId:e,request:t}]);else if(t.method===`notify`)F(t.message||t.title,t.notifyType??`info`);else if(t.method===`setStatus`){let e=t.statusKey??``;xe(n=>{let r=new Map(n);return t.statusText===void 0?r.delete(e):r.set(e,t.statusText),r})}else if(t.method===`setWidget`){let e=t.widgetKey??``;O(n=>{let r=new Map(n);return t.widgetLines===void 0?r.delete(e):r.set(e,{lines:t.widgetLines,placement:t.widgetPlacement??`aboveEditor`}),r})}else t.method===`setTitle`?Ce(t.title):t.method===`set_editor_text`&&e===St.current&&Vt(t.text??``)},yn=(e,t,n)=>{_e(n=>n.filter(n=>n.conversationId!==e||n.request.requestId!==t));let r=Dr[n];r&&F(r,`warning`)};(0,p.useEffect)(()=>{let e=!1,n=0,o,s=()=>{if(e)return;let l=location.protocol===`https:`?`wss`:`ws`,d=new WebSocket(`${l}://${location.host}/ws`);x.current=d,d.onopen=()=>{e||(n=0,u(!0),d.send(JSON.stringify({type:`stream.configure`,coalesce:!0})),d.send(JSON.stringify({type:`catalog.fetch`,conversationId:St.current})),d.send(JSON.stringify({type:`agent.catalog.fetch`})),d.send(JSON.stringify({type:`subagent.catalog.fetch`})))},d.onclose=()=>{if(!e){Ct.current=!1;for(let e of i.current)e.onFailure?.();i.current=[],r([]),u(!1),o=window.setTimeout(s,Math.min(hr,mr*2**n++))}},d.onmessage=({data:n})=>{if(e)return;let o=JSON.parse(n);if(o.type!==`message.delta`&&o.type!==`message.thinking`&&D(),o.type.startsWith(`terminal.`))ae.current?.(o);else if(o.type===`conversation.autoRename.result`){let e=ot.current.get(o.conversationId);e&&(ot.current.delete(o.conversationId),o.ok?e.resolve(o.title):e.reject(Error(o.error)))}else if(o.type===`message.thinking`)ce(o);else if(o.type===`state`)D(),Ct.current=!1,i.current=i.current.filter(e=>!e.accepted?.(o.state)),r(i.current),t(o.state),u(!0),c(e=>{let t=o.state.conversations.filter(e=>!e.archivedAt),n=a.current?.activeId,r=!!(e&&t.some(t=>t.id===e)),s=!!(e&&i.current.some(t=>t.temporaryId===e));return r||s?e:n&&t.some(e=>e.id===n)?n:t.find(e=>e.workspaceId===jt.current)?.id}),v(e=>{let t=a.current?.workspaceId;return t&&o.state.workspaces.some(e=>e.id===t)?t:e&&(o.state.workspaces.some(t=>t.id===e)||i.current.some(t=>t.temporaryId===e))?e:o.state.workspaces[0]?.id});else if(o.type===`message.delta`)ce(o);else if(o.type===`message.revert.result`)vi(void 0),$e(void 0),o.ok||f(o.message);else if(o.type===`operation.result`){let e=i.current.find(e=>e.id===o.requestId);if(i.current=i.current.filter(e=>e.id!==o.requestId),r(i.current),e?.temporaryId){let t=e.temporaryId;c(n=>n===t?o.ok?o.entityId:e.previousActiveId:n),Jt(e=>e.flatMap(e=>e===t?o.ok&&o.entityId?[o.entityId]:[]:[e])),v(e=>e===t?o.ok?o.entityId:void 0:e)}o.ok||(e?.onFailure?.(),f(o.message??`操作失败`))}else o.type===`error`?(f(o.message),Ct.current&&(Ct.current=!1,F(`重命名失败:${o.message}`,`error`))):o.type===`extension.ui`?vn(o.conversationId,o.request):o.type===`extension.ui.closed`?yn(o.conversationId,o.requestId,o.reason):o.type===`model.catalog`?(!o.conversationId||o.conversationId===St.current)&&Te(o.catalog):o.type===`agent.catalog`?De(o.catalog):o.type===`subagent.catalog`?Oe(o.catalog):o.type===`server.version`?(Ae(o.version),o.version.needsRestart&&F(`检测到 Super Pi 代码已更新(v${o.version.runningVersion} → v${o.version.diskVersion}),请执行 /super_pi restart 重启服务生效`,`warning`)):o.type===`subagent.event`?t(e=>({...e,subagentRuns:gn(e.subagentRuns??[],o.run)})):o.type===`workflow.event`?t(e=>({...e,workflows:_n(e.workflows??[],o.workflow)})):o.type===`message.tools`?t(e=>({...e,conversations:e.conversations.map(e=>e.id===o.conversationId?{...e,messages:e.messages.map(e=>e.id===o.messageId?{...e,toolCalls:o.toolCalls}:e)}:e)})):o.type===`pi.queue`?We(e=>{let t=new Map(e);return t.set(o.conversationId,{steering:o.steering,followUp:o.followUp}),t}):o.type===`conversation.fork.result`&&(Ni(e=>e===o.conversationId?void 0:e),F(o.ok?`已从「${o.title}」分叉出新会话,放在侧边栏最上面;没有切换过去。${o.messagesCopied?`复制了 ${o.messagesCopied} 条消息。`:`原会话还没有消息,只复制了会话本身。`}`:`分叉失败:${o.message}`,o.ok?`info`:`error`))}};return s(),()=>{e=!0,D(),window.clearTimeout(o);for(let e of xt.current)window.clearTimeout(e);x.current?.close()}},[ce,D]);let bn=(0,p.useCallback)(e=>x.current?.readyState===WebSocket.OPEN&&(x.current.send(JSON.stringify(e)),!0),[]),I=(e,t,n,a,o,s)=>{if(x.current?.readyState!==WebSocket.OPEN||i.current.some(e=>e.key===t))return!1;let c=nn(),l={id:c,key:t,apply:n,temporaryId:a,previousActiveId:St.current,onFailure:o,accepted:s};return i.current=[...i.current,l],r(i.current),x.current.send(JSON.stringify({...e,operationRequestId:c})),!0},xn=(e,t)=>n=>({...n,conversations:n.conversations.map(n=>n.id===e?{...n,...t}:n)}),Sn=(e,t)=>n=>({...n,workspaces:n.workspaces.map(n=>n.id===e?{...n,...t}:n)}),Cn=n.some(e=>e.key===`send:${M?.id}`||e.key===`configuration:${M?.id}`||e.temporaryId===M?.id||e.key===`archive:${M?.id}`),wn=(e,t,n,r,i)=>{e&&x.current?.send(JSON.stringify({type:`question.answer`,conversationId:e,messageId:t,callIndex:n,answers:r,cancelled:i}))},Tn=(e,t,n)=>I({type:`extension.ui.answer`,conversationId:e,requestId:t,...n},`answer:${e}:${t}`,e=>e),En=M?.model??we?.defaultModel,Dn=M?.thinkingLevel??we?.defaultThinkingLevel,On=we?.source===`pi`,An=!!we?.models.find(e=>e.provider===En?.provider&&e.modelId===En?.modelId)?.imageInput,jn=(e,t)=>!e||!t?e:{...e,models:e.models.filter(e=>(o.modelFilterRules??[]).every(n=>{try{return!new RegExp(n.workspacePattern,`i`).test(t.name)||new RegExp(n.modelPattern,`i`).test(e.label)||new RegExp(n.modelPattern,`i`).test(`${e.provider}/${e.modelId}`)}catch{return!0}}))},Mn=(jn(we,o.workspaces.find(e=>e.id===M?.workspaceId))?.models??[]).map(e=>({key:`${e.provider}/${e.modelId}`,label:e.label,detail:e.detail,badge:Cr(e),selected:En?.provider===e.provider&&En.modelId===e.modelId})),Nn=(we?.levels??[]).map(e=>({key:e,label:e,selected:e===Dn})),Pn=e=>{if(!M)return;let[t,n]=e.split(`/`);t&&n&&I({type:`conversation.model`,conversationId:M.id,provider:t,modelId:n},`configuration:${M.id}`,xn(M.id,{model:we?.models.find(e=>e.provider===t&&e.modelId===n)??{provider:t,modelId:n,label:n}}))},Fn=e=>{M&&I({type:`conversation.thinking`,conversationId:M.id,level:e},`configuration:${M.id}`,xn(M.id,{thinkingLevel:e}))},In=Ee?.agents.find(e=>e.id===M?.agentId),Rn=[{key:L,label:`跟随 Pi 默认`,detail:`不使用任何 agent 角色`,selected:!M?.agentId},...(Ee?.agents??[]).map(e=>({key:e.id,label:e.name,detail:e.toolSummary,color:e.color,selected:M?.agentId===e.id}))],zn=In?.model,Bn=!!(zn&&M?.model&&M.model.provider===zn.provider&&M.model.modelId===zn.modelId);In?.thinkingLevel&&(M?.thinkingLevel,In.thinkingLevel);let Vn=e=>{if(M){let t=Ee?.agents.find(t=>t.id===e);I({type:`conversation.agent`,conversationId:M.id,agentId:e===L?null:e},`configuration:${M.id}`,xn(M.id,{agentId:t?.id,...t?.model?{model:{...t.model,label:t.model.modelId}}:{},...t?.thinkingLevel?{thinkingLevel:t.thinkingLevel}:{}}))}},Hn=ge.filter(e=>e.conversationId===M?.id),Un=(0,p.useMemo)(()=>(o.subagentRuns??[]).filter(e=>e.conversationId===M?.id),[o.subagentRuns,M?.id]),Wn=(0,p.useMemo)(()=>(o.workflows??[]).filter(e=>e.conversationId===M?.id),[o.workflows,M?.id]),Gn=(0,p.useMemo)(()=>new Map(Wn.map(e=>[e.id,e])),[Wn]),Kn=(0,p.useMemo)(()=>new Map((A?.agents??[]).map(e=>[e.id,e.name])),[A]),qn=(0,p.useMemo)(()=>new Map((M?.messages??[]).map(e=>[e.id,e])),[M?.messages]),Jn=(0,p.useMemo)(()=>new Map(Un.map(e=>[e.id,e])),[Un]),Yn=(0,p.useMemo)(()=>{if(!M)return[];let e=Qe?.conversationId===M.id?(()=>{let e=M.messages.find(e=>e.id===Qe.messageId);if(e)return{index:M.messages.indexOf(e),at:Date.parse(e.createdAt)||0,...Qe.kind===`withdraw`?{inclusive:!0}:{inclusive:!1}}})():void 0,t=[];return M.messages.forEach((n,r)=>{e&&(r>e.index||e.inclusive&&r===e.index)||t.push({at:Date.parse(n.createdAt)||0,seq:r,key:`m:${n.id}`})}),Un.forEach((n,r)=>{e&&(Date.parse(n.startedAt)||0)>e.at||t.push({at:Date.parse(n.startedAt)||0,seq:t.length+r,key:`r:${n.id}`})}),Wn.forEach((n,r)=>{e&&(Date.parse(n.startedAt)||0)>e.at||t.push({at:Date.parse(n.startedAt)||0,seq:t.length+r,key:`w:${n.id}`})}),t.sort((e,t)=>e.at-t.at||e.seq-t.seq).map(e=>e.key)},[M,Un,Wn,Qe]);(0,p.useEffect)(()=>{let e=ee.current;if(!e||!M?.id)return;let t=()=>{S.current[M.id]?.followTail&&(xi(e),e.scrollTop=e.scrollHeight)};if(t(),typeof ResizeObserver>`u`)return;let n=new ResizeObserver(t);n.observe(e);for(let t of e.children)n.observe(t);return()=>n.disconnect()},[M?.id,M?.messages,Un,Wn,Yn]);let Xn=e=>qe(t=>({...t,[e]:!t[e]})),Zn=(e,t)=>Ye(n=>({...n,[`${e}:${t}`]:!n[`${e}:${t}`]})),Qn=(e,t)=>Ze(n=>({...n,[`${e}:${t}`]:n[`${e}:${t}`]!==!1})),[$n,er]=(0,p.useState)({}),ir=e=>er(t=>({...t,[e]:t[e]===!1})),ar=e=>{I({type:`subagent.stop`,runId:e},`stop:${e}`,e=>e)||F(`连接断开,无法停止子智能体,请重试`,`error`)},or=e=>{I({type:`workflow.stop`,workflowId:e},`stop:${e}`,e=>e)||F(`连接断开,无法停止编排,请重试`,`error`)},sr=e=>[...Se].filter(([,t])=>t.placement===e).map(([e,t])=>{let n=t.lines;return(0,m.jsxs)(`div`,{className:`extui-widget`,"data-widget-key":e,"data-widget-lines":n.length,children:[n.slice(0,Or).map((e,t)=>(0,m.jsx)(`div`,{children:e},`${t}:${e}`)),n.length>Or&&(0,m.jsx)(`div`,{children:`… (widget truncated)`})]},e)}),cr=(e,t)=>{let n=`pending:${nn()}`,r={id:n,name:e,path:t,updatedAt:new Date().toISOString()};I({type:`workspace.create`,name:e,path:t},`create-workspace`,e=>({...e,workspaces:[...e.workspaces,r]}),n)&&(v(n),c(void 0),b(!1))},[lr,yr]=(0,p.useState)(),br=(e,t)=>yr(n=>n?.kind===e&&n.id===t?n:{kind:e,id:t}),xr=()=>yr(void 0),Sr=(e,t)=>lr?.kind===e&&lr.id===t,wr=(e,t,n)=>t.startsWith(`pending:`)?!1:I(e===`workspace`?{type:`workspace.rename`,workspaceId:t,name:n}:{type:`conversation.rename`,conversationId:t,name:n},`rename:${t}`,e===`workspace`?Sn(t,{name:n}):xn(t,{title:n}))?(Ct.current=!0,xr(),!0):(F(`连接断开,改名未保存,请重试`,`error`),!1),Tr=async e=>{if(e.startsWith(`pending:`)){F(`这个会话还在创建中,稍后再试`,`warning`);return}if(ot.current.has(e))return;F(`正在根据对话内容生成标题…`,`info`);let t=new Promise((t,n)=>{ot.current.set(e,{resolve:t,reject:n}),window.setTimeout(()=>{ot.current.delete(e)&&n(Error(`模型在 30 秒内没有返回标题`))},32e3)});if(!bn({type:`conversation.autoRename`,conversationId:e}))ot.current.delete(e),F(`连接断开,无法生成标题`,`error`);else try{let n=await t;wr(`conversation`,e,n),F(`已重命名为「${n}」`,`info`)}catch(e){F(`重命名失败:${e instanceof Error?e.message:String(e)}`,`error`)}},jr=e=>{if(e.id.startsWith(`pending:`))return;let t=o.conversations.filter(t=>t.workspaceId===e.id).length;window.confirm(`删除工作区「${e.name}」?${t?`其中 ${t} 条会话将一并删除,`:``}此操作不可撤销。`)&&I({type:`workspace.delete`,workspaceId:e.id},`delete:${e.id}`,t=>({...t,workspaces:t.workspaces.filter(t=>t.id!==e.id),conversations:t.conversations.filter(t=>t.workspaceId!==e.id)}))},[Mr,Nr]=(0,p.useState)(),Fr=Mr?o.workspaces.find(e=>e.id===Mr):void 0,{theme:Br,setTheme:Kr}=kn(),[qr,Xr]=(0,p.useState)(!1),[Zr,Qr]=(0,p.useState)(!1),$r=(e,t,n,r)=>{e.startsWith(`pending:`)||(I({type:`workspace.settings`,workspaceId:e,model:n?{provider:n.provider,modelId:n.modelId}:null,thinkingLevel:r??null,agentId:t??null},`defaults:${e}`,Sn(e,{defaultModel:n,defaultThinkingLevel:r,defaultAgentId:t}))?Nr(void 0):F(`连接断开,设置未保存,请重试`,`error`))},ei=e=>{if(e.startsWith(`pending:`)||i.current.some(t=>t.key===`defaults:${e}`))return;let t=`pending:${nn()}`,n={id:t,workspaceId:e,title:`新会话`,status:`idle`,updatedAt:new Date().toISOString(),messages:[]};I({type:`conversation.create`,workspaceId:e},`create-session:${e}`,e=>({...e,conversations:[n,...e.conversations]}),t)&&(v(e),c(t),an(`session`),Jt(e=>[...e.filter(e=>e!==t),t]))},ti=e=>{e.startsWith(`pending:`)||I({type:`conversation.archive`,conversationId:e},`archive:${e}`,xn(e,{archivedAt:new Date().toISOString()}))},ri=e=>I({type:`conversation.unarchive`,conversationId:e},`archive:${e}`,xn(e,{archivedAt:void 0})),ii=async e=>{if(!M){F(`请先选择或创建会话`,`error`);return}if(!An){F(`当前模型不支持图片输入`,`error`);return}let t=[`image/png`,`image/jpeg`,`image/gif`,`image/webp`],n=e.filter(e=>t.includes(e.type)?e.size>5242880?(F(`图片「${e.name}」超过 5MB 上限`,`error`),!1):!0:(F(`不支持的文件格式「${e.name}」,仅支持 PNG/JPEG/GIF/WebP`,`error`),!1));if(n.length===0)return;let r=M.id;zt(r,e=>({...e,uploading:e.uploading+n.length}));let i=n.map(e=>({id:nn(),conversationId:r,name:e.name,url:URL.createObjectURL(e)}));pt(e=>[...e,...i]);for(let[e,t]of n.entries()){let n=i[e];try{let e=await new Promise((e,n)=>{let r=new FileReader;r.onload=()=>{let t=r.result,n=t.indexOf(`,`);e(n>=0?t.slice(n+1):t)},r.onerror=n,r.readAsDataURL(t)}),i=await fetch(`/api/upload`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({conversationId:r,name:t.name,mimeType:t.type,data:e})});if(!(i.headers.get(`content-type`)??``).includes(`application/json`)){F(`服务器未返回 JSON 响应,请刷新或重启服务`,`error`);continue}if(i.ok){let e=await i.json();if(lt.current.has(n.id))continue;pt(e=>e.filter(e=>e.id!==n.id)),URL.revokeObjectURL(n.url),zt(r,t=>({...t,images:[...t.images,{id:e.id,name:e.name,mimeType:e.mimeType,bytes:e.bytes}]}))}else{let e=await i.json().catch(()=>({error:`上传失败`}));F(e.error||`上传失败: ${i.status}`,`error`)}}catch(e){F(`上传错误: ${e instanceof Error?e.message:`网络错误`}`,`error`)}finally{pt(e=>e.map(e=>e.id===n.id?{...e,failed:!0}:e)),zt(r,e=>({...e,uploading:Math.max(0,e.uploading-1)}))}}},ai=e=>{let t=e.clipboardData?.items;if(!t)return;let n=[];for(let e=0;e<t.length;e++){let r=t[e];if(r.type.startsWith(`image/`)){let e=r.getAsFile();e&&n.push(e)}}if(n.length>0){if(!An){F(`当前模型不支持图片输入`,`error`);return}e.preventDefault(),ii(n)}},oi=e=>{e.dataTransfer?.types?.includes(`Files`)&&(e.preventDefault(),_t(!0))},si=e=>{e.currentTarget.contains(e.relatedTarget)||_t(!1)},ci=e=>{_t(!1);let t=Array.from(e.dataTransfer?.files??[]).filter(e=>e.type.startsWith(`image/`));if(t.length>0){if(e.preventDefault(),!An){F(`当前模型不支持图片输入`,`error`);return}ii(t)}},ui=t=>{if(t.preventDefault(),!M||Cn||!Pt.trim()&&Ft.length===0)return;if(Ft.length>0&&!An){F(`当前模型不支持图片输入,请移除图片或切换至支持图文的模型`,`error`);return}if(Lt>0){F(`图片正在上传中,请稍候…`,`info`);return}let n=M.status===`running`,r=`pending:${nn()}`,i=M.id,a=Pt,o=Ft,s=new Set([...e.conversations.find(e=>e.id===i)?.messages.map(e=>e.id)??[],...e.queuedMessages?.map(e=>e.id)??[]]);I({type:`message.send`,conversationId:M.id,content:Pt,...Ft.length>0?{images:Ft}:{},...It.length>0?{attachments:It}:{}},`send:${i}`,e=>e.conversations.find(e=>e.id===i)?.messages.some(e=>!s.has(e.id)&&e.role===`user`&&e.content===a.trim())||e.queuedMessages?.some(e=>!s.has(e.id)&&e.conversationId===i&&e.content===a.trim())||n?e:{...e,conversations:e.conversations.map(e=>e.id===i?{...e,messages:[...e.messages,{id:r,role:`user`,content:a,images:o,createdAt:new Date().toISOString()}]}:e)},void 0,()=>{dt(e=>[...e,{id:r,conversationId:i,content:a,images:o}]),zt(i,e=>e.text||e.images.length?e:{...e,text:a,images:o})},e=>!!(e.conversations.find(e=>e.id===i)?.messages.some(e=>!s.has(e.id)&&e.role===`user`&&e.content===a.trim())||e.queuedMessages?.some(e=>!s.has(e.id)&&e.conversationId===i&&e.content===a.trim())))?zt(M.id,e=>({...e,text:``,images:[]})):F(`连接断开,消息未发送,草稿已保留,请重试`,`error`)},di=(0,p.useMemo)(()=>(o.queuedMessages??[]).filter(e=>e.conversationId===M?.id),[o.queuedMessages,M?.id]),fi=e=>{I({type:`message.cancel`,conversationId:M?.id??``,queueId:e},`queue:${e}`,t=>({...t,queuedMessages:t.queuedMessages?.filter(t=>t.id!==e)}))||F(`连接断开,取消没有生效,请重试`,`error`)},mi=e=>{I({type:`message.steer`,conversationId:M?.id??``,queueId:e},`queue:${e}`,t=>({...t,queuedMessages:t.queuedMessages?.map(t=>t.id===e?{...t,priority:!0}:t)}))||F(`连接断开,优先发送没有生效,请重试`,`error`)},hi=e=>{I({type:`message.sendQueued`,conversationId:M?.id??``,queueId:e},`send-queued:${e}`,t=>({...t,queuedMessages:t.queuedMessages?.filter(t=>t.id!==e)}),void 0,void 0,t=>!t.queuedMessages?.some(t=>t.id===e))||F(`连接断开,发送没有生效,请重试`,`error`)},[_i,vi]=(0,p.useState)(),Di=(e,t=!1)=>{if(!M||_i)return;let n=M.messages.find(t=>t.id===e);n&&(n&&ct(e=>{let t=e[M.id]??{text:``,images:[],uploading:0};return t.text.trim()||t.images.length?e:{...e,[M.id]:{...t,text:n.content,images:n.images??[]}}}),$e({conversationId:M.id,messageId:e,kind:`withdraw`})),bn({type:`message.revert`,conversationId:M.id,messageId:e,withdraw:!0})?vi(e):($e(void 0),F(`连接断开,回退未执行,请重试`,`error`))},[Oi,ki]=(0,p.useState)(),Ai=(0,p.useRef)(void 0),ji=async(e,t)=>{Ai.current&&clearTimeout(Ai.current);let n=await gr(t);ki({id:e,kind:n?`copied`:`error`}),n||F(`复制失败:浏览器拒绝了剪贴板写入,可以手动选中这条消息的文字复制`,`error`),Ai.current=setTimeout(()=>ki(void 0),2e3)};(0,p.useEffect)(()=>()=>{Ai.current&&clearTimeout(Ai.current)},[]);let[Mi,Ni]=(0,p.useState)(),Pi=e=>{Mi||e.startsWith(`pending:`)||(bn({type:`conversation.fork`,conversationId:e})?Ni(e):F(`连接断开,分叉没有执行,请重试`,`error`))};return(0,m.jsxs)(`main`,{className:`shell${Ne?` shell-rail-collapsed`:``} mobile-panel-${Fe}`,"data-rail-collapsed":Ne,"data-selected-workspace":_??``,style:{"--sidebar-width":`${j.sidebar}px`,"--rail-col":Ne?void 0:`${j.rail}px`},children:[(0,m.jsxs)(`aside`,{children:[(0,m.jsxs)(`div`,{className:`brand`,children:[(0,m.jsx)(`span`,{children:`π`}),(0,m.jsxs)(`div`,{children:[(0,m.jsx)(`strong`,{children:`SUPER PI`}),(0,m.jsx)(`small`,{children:`agent workspace`})]})]}),(0,m.jsx)(`div`,{className:`sidebar-actions`,children:(0,m.jsx)(`button`,{className:`workspace-new`,onClick:()=>b(!0),children:`+ 工作区`})}),(0,m.jsx)(`div`,{className:`label`,children:`工作区`}),(0,m.jsx)(`nav`,{children:o.workspaces.map(e=>{let t=o.conversations.filter(t=>t.workspaceId===e.id),n=t.filter(e=>!e.archivedAt).sort(dr),r=t.filter(e=>e.archivedAt).sort(fr),i=h[e.id]??!1,a=ue[e.id]??!0;return(0,m.jsxs)(`section`,{className:`workspace-group`,children:[(0,m.jsx)(`div`,{className:`workspace-header`,children:Sr(`workspace`,e.id)?(0,m.jsx)(Hr,{initial:e.name,label:`重命名工作区 ${e.name}`,onCommit:t=>wr(`workspace`,e.id,t),onClose:xr}):(0,m.jsxs)(m.Fragment,{children:[(0,m.jsxs)(`button`,{className:`workspace-toggle`,"aria-expanded":!i,title:e.path,onContextMenu:t=>{t.preventDefault();let n=t.currentTarget.getBoundingClientRect();tt({workspace:e,x:t.clientX||n.left+24,y:t.clientY||n.top+n.height})},onClick:()=>{v(e.id),c(Ot.find(t=>t.workspaceId===e.id)?.id),g(t=>({...t,[e.id]:!i}))},children:[(0,m.jsx)(`span`,{className:`chevron chevron-folder`,"aria-hidden":`true`,children:i?`📁`:`📂`}),(0,m.jsxs)(`span`,{children:[(0,m.jsx)(`strong`,{onDoubleClick:()=>br(`workspace`,e.id),children:e.name}),(0,m.jsx)(`small`,{children:e.path})]})]}),(0,m.jsx)(`button`,{className:`workspace-add`,title:`在此工作区新建会话`,onClick:()=>ei(e.id),children:`+`}),(0,m.jsx)(`button`,{className:`workspace-settings`,title:`工作区设置(默认智能体 / 模型 / 思考强度)`,"aria-label":`工作区设置 ${e.name}`,onClick:()=>Nr(e.id),children:`⚙`}),(0,m.jsx)(`button`,{className:`workspace-delete`,title:`删除工作区`,onClick:()=>jr(e),children:`×`})]})}),!i&&(n.length?n.map(t=>(0,m.jsx)(`div`,{className:`conversation-row`,children:Sr(`conversation`,t.id)?(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(Hr,{initial:t.title,label:`重命名会话 ${t.title}`,onCommit:e=>wr(`conversation`,t.id,e),onClose:xr}),(0,m.jsx)(`small`,{className:`conversation-time`,children:ur(t.updatedAt,Tt)})]}):(0,m.jsxs)(m.Fragment,{children:[(0,m.jsxs)(`button`,{className:`conversation ${M?.id===t.id?`selected`:``}`,"data-conversation":t.id,title:t.title,"aria-label":`打开会话 ${t.title}`,onContextMenu:e=>{e.preventDefault();let n=e.currentTarget.getBoundingClientRect();it({conversation:t,x:e.clientX||n.left+24,y:e.clientY||n.top+n.height})},onClick:()=>{Zt(t.id),v(e.id)},children:[(0,m.jsx)(`i`,{className:`conversation-mark`,children:(0,m.jsx)(Yr,{state:Jr(t,ge.some(e=>e.conversationId===t.id),C.current[t.id])})}),(0,m.jsx)(`span`,{"data-conversation-title":t.id,onDoubleClick:()=>br(`conversation`,t.id),children:t.title}),(0,m.jsx)(`small`,{children:ur(t.updatedAt,Tt)})]}),(0,m.jsx)(`button`,{type:`button`,className:`conversation-fork`,"data-fork-conversation":t.id,"data-fork-state":Mi===t.id?`pending`:`idle`,disabled:!!Mi,title:`从此会话分叉出一个新会话`,"aria-label":`从会话 ${t.title} 分叉出一个新会话`,onClick:()=>Pi(t.id),children:`⑂`}),(0,m.jsx)(`button`,{className:`conversation-archive`,title:`归档会话`,"aria-label":`归档会话 ${t.title}`,onClick:()=>ti(t.id),children:`×`})]})},t.id)):(0,m.jsx)(`div`,{className:`conversation-empty`,children:`暂无会话`})),!i&&r.length>0&&(0,m.jsxs)(`div`,{className:`archived-group`,"data-archived-count":r.length,children:[(0,m.jsxs)(`button`,{className:`archived-toggle`,title:a?`展开已归档会话`:`收起已归档会话`,"aria-expanded":!a,onClick:()=>de(t=>({...t,[e.id]:!a})),children:[(0,m.jsx)(`span`,{className:`chevron`,"aria-hidden":`true`,children:a?`▸`:`▾`}),(0,m.jsxs)(`span`,{className:`archived-label`,children:[`Archived (`,r.length,`)`]})]}),!a&&r.map(e=>(0,m.jsx)(`div`,{className:`conversation-archived`,"data-archived-at":e.archivedAt,children:Sr(`conversation`,e.id)?(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(Hr,{initial:e.title,label:`重命名已归档会话 ${e.title}`,onCommit:t=>wr(`conversation`,e.id,t),onClose:xr}),(0,m.jsx)(`small`,{children:pr(e.archivedAt)})]}):(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(`i`,{className:`conversation-mark`,"aria-hidden":`true`}),(0,m.jsx)(`span`,{"data-archived-title":e.id,onDoubleClick:()=>br(`conversation`,e.id),children:e.title}),(0,m.jsx)(`small`,{children:pr(e.archivedAt)}),(0,m.jsx)(`button`,{type:`button`,className:`conversation-fork`,"data-fork-conversation":e.id,"data-fork-state":Mi===e.id?`pending`:`idle`,disabled:!!Mi,title:`从此会话分叉出一个新会话`,"aria-label":`从已归档会话 ${e.title} 分叉出一个新会话`,onClick:()=>Pi(e.id),children:`⑂`}),(0,m.jsx)(`button`,{className:`conversation-restore`,title:`恢复为正常会话`,"aria-label":`恢复会话 ${e.title}`,onClick:()=>ri(e.id),children:`↑`})]})},e.id))]})]},e.id)})}),(0,m.jsxs)(`footer`,{children:[(0,m.jsxs)(`div`,{className:`footer-status`,children:[(0,m.jsx)(`i`,{className:l?`online`:`offline`}),l?`已连接`:`正在连接`,(0,m.jsx)(`span`,{children:`本地持久化`})]}),(0,m.jsxs)(`button`,{type:`button`,className:`theme-settings-trigger`,"data-theme-open":`1`,"aria-haspopup":`dialog`,"aria-expanded":qr,onClick:()=>Xr(!0),children:[(0,m.jsx)(`span`,{"aria-hidden":`true`,children:`⚙`}),`设置`]}),(0,m.jsxs)(`button`,{type:`button`,className:`theme-settings-trigger`,"data-help-open":`1`,"aria-haspopup":`dialog`,"aria-expanded":Zr,onClick:()=>Qr(!0),children:[(0,m.jsx)(`span`,{"aria-hidden":`true`,children:`📖`}),`使用指南`]}),(0,m.jsxs)(`button`,{type:`button`,className:`theme-settings-trigger restart-needed-trigger`,title:`检查 Super Pi 更新`,onClick:async()=>{Me(`updating`);try{let e=await fetch(`/api/update-check`);if(!e.ok)throw Error(`check failed`);let t=await e.json();if(t.needsRestart)F(`当前代码已更新,正在重启服务…`,`info`);else if(t.updateAvailable){if(!(await fetch(`/api/update`,{method:`POST`})).ok)throw Error(`update failed`);F(`发现新版本 v${t.latestVersion},正在更新并重启…`,`info`)}else F(`当前已是最新版本`,`info`)}catch{F(`检查更新失败,请检查网络或服务日志`,`error`)}finally{Me(`idle`)}},children:[(0,m.jsx)(`span`,{"aria-hidden":`true`,children:je===`updating`?`⟳`:`↻`}),je===`updating`?`正在检查更新…`:`检查更新`]}),ke?.needsRestart&&(0,m.jsxs)(`button`,{type:`button`,className:`theme-settings-trigger restart-needed-trigger`,title:`代码已更新:当前运行 v${ke.runningVersion},磁盘版本 v${ke.diskVersion}。点击复制重启命令 /super_pi restart`,onClick:async()=>{Me(`updating`);try{if(!(await fetch(`/api/update`,{method:`POST`})).ok)throw Error(`update failed`);F(`正在更新 Super Pi,服务即将重启…`,`info`)}catch{Me(`idle`),F(`更新启动失败,请检查服务日志`,`error`)}},children:[(0,m.jsx)(`span`,{"aria-hidden":`true`,children:je===`updating`?`⟳`:`⚠️`}),je===`updating`?`正在更新并重启…`:`更新并重启服务`]})]})]}),(0,m.jsx)(Ei,{side:`left`,width:j.sidebar,min:j.sidebarMin,max:j.sidebarMax,label:`调整左侧栏宽度`,onWidth:Ve}),(0,m.jsxs)(`section`,{className:`chat`,"data-main-view":rn,"data-main-view-kind":rn,children:[un.length>0&&(0,m.jsx)(`div`,{className:`chat-tab-strip`,"data-chat-tab-strip":`1`,"data-session-count":Xt.length,"data-file-count":P.open.length,children:(0,m.jsx)(At,{tabs:un,prefix:`workspace-tab`,ariaLabel:`工作区标签页`,activeId:dn,onSelect:fn,onClose:pn,minWidth:120,maxWidth:320})}),(0,m.jsxs)(`header`,{children:[(0,m.jsxs)(`div`,{className:`chat-head`,children:[(0,m.jsx)(`h1`,{children:M?.title??`准备开始`}),be.size>0?[...be].map(([e,t])=>(0,m.jsx)(`p`,{"data-role":`run-status`,"data-status-key":e,children:`扩展状态:${e?`${e} · `:``}${t}`},e)):(0,m.jsx)(`p`,{"data-role":`run-status`,children:M?.status===`running`?`Agent 正在工作`:`Pi runtime · Web 控制台`})]}),(0,m.jsxs)(`span`,{className:`chat-view chat-view-${rn}`,"data-main-view-indicator":rn,"data-main-view-kind":rn,title:rn===`file`?`中间主画面正在显示文件 ${P.active?.path??``}`:`中间主画面正在显示这个会话的消息流`,children:[(0,m.jsx)(`span`,{className:`chat-view-label`,children:`主区显示`}),(0,m.jsx)(`span`,{className:`chat-view-kind`,children:rn===`file`?`文件`:`会话`}),rn===`file`&&P.active&&(0,m.jsx)(`span`,{className:`chat-view-name`,"data-main-view-name":P.active.name,children:P.active.name})]}),(0,m.jsx)(`div`,{className:`badge`,children:M?.status??`idle`})]}),d&&(0,m.jsx)(`div`,{className:`error`,children:d}),M?.error&&(0,m.jsx)(`div`,{className:`error`,"data-role":`conversation-error`,children:M.error}),k&&(0,m.jsxs)(`div`,{className:`extui-title-note`,children:[`扩展建议标题:`,k]}),ve.length>0&&(0,m.jsx)(`div`,{className:`toasts`,role:`status`,"aria-live":`polite`,"data-toast-count":ve.length,children:ve.map(e=>(0,m.jsx)(`div`,{className:`toast toast-${e.kind}`,"data-toast-kind":e.kind,children:e.text},e.key))}),rn===`file`?(0,m.jsx)(`div`,{className:`chat-file-view`,"data-main-file-view":mn?.path,"data-file-view-kind":`editor`,children:mn?(0,m.jsx)(wt,{file:mn,state:P.saves[mn.path]?.state??`idle`,message:P.saves[mn.path]?.message??``,external:P.external[mn.path]===!0,onChange:e=>P.updateBuffer(mn.path,e),onSave:()=>void P.save(mn.path,!1),onReload:()=>void P.reload(mn.path),onOverwrite:()=>void P.save(mn.path,!0)}):(0,m.jsx)(`p`,{className:`rail-empty`,"data-main-file-empty":`1`,children:`没有选中的文件。`})}):(0,m.jsxs)(m.Fragment,{children:[(0,m.jsxs)(`div`,{className:`messages`,ref:ee,"data-message-scroll-container":M?.id??``,onScroll:e=>{let t=e.currentTarget;t.dataset.programmaticScroll===`true`?delete t.dataset.programmaticScroll:Mt(t)},children:[!M&&(0,m.jsxs)(`div`,{className:`empty`,children:[(0,m.jsx)(`div`,{className:`orb`,children:`π`}),(0,m.jsx)(`h2`,{children:`开始一个新会话`}),o.workspaces.length===0?(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(`p`,{children:`先创建一个工作区,再开始会话。`}),(0,m.jsx)(`button`,{onClick:()=>b(!0),children:`+ 新建工作区`})]}):(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)(`p`,{children:o.workspaces.length===1&&kt?`在「${kt.name}」中创建会话,即可向 Super Pi 发送任务。`:`选择工作区即可开始会话,向 Super Pi 发送任务。`}),o.workspaces.length>1&&(0,m.jsxs)(`div`,{className:`empty-workspace-select`,children:[(0,m.jsx)(`label`,{htmlFor:`empty-workspace-choice`,children:`工作区:`}),(0,m.jsxs)(`select`,{id:`empty-workspace-choice`,"aria-label":`选择工作区`,value:kt?.id,onChange:e=>{e.target.value===`__new__`?b(!0):v(e.target.value)},children:[o.workspaces.map(e=>(0,m.jsx)(`option`,{value:e.id,children:e.name},e.id)),(0,m.jsx)(`option`,{value:`__new__`,children:`+ 新建工作区…`})]})]}),kt&&(0,m.jsx)(`button`,{onClick:()=>ei(kt.id),children:`新建会话`}),(0,m.jsx)(`span`,{className:`empty-secondary-action`,role:`button`,tabIndex:0,onClick:()=>b(!0),onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),b(!0))},children:`+ 新建工作区`})]})]}),Yn.map(e=>{if(e.startsWith(`m:`)){let t=qn.get(e.slice(2));if(!t)return null;let n=!M?.messages.some(n=>n.id!==t.id&&n.role===`user`&&Yn.indexOf(`m:${n.id}`)>Yn.indexOf(e)),r=t.role===`user`&&M?.status!==`running`&&_i!==t.id,i=r&&n&&!t.id.startsWith(`pending:`),a=r&&!n&&!!t.piEntryId,o=t.content?(0,m.jsx)(Ke,{source:t.content}):(0,m.jsx)(`span`,{className:`typing`,children:`正在思考`}),s=t.toolCalls??[];if(t.role===`user`){let n=Oi?.id===t.id?Oi.kind:void 0,r=t.content||(t.images?.length?`[图片]`:``);return(0,m.jsxs)(`article`,{className:`message user`,"data-transcript-key":e,children:[(0,m.jsxs)(`div`,{className:`message-body`,children:[(0,m.jsxs)(`div`,{className:`bubble`,children:[(0,m.jsxs)(`div`,{className:`meta`,children:[he,(0,m.jsx)(`time`,{children:rr(t.createdAt)})]}),t.images&&t.images.length>0&&(0,m.jsx)(`div`,{className:`message-images`,"data-message-images":`1`,children:t.images.map(e=>(0,m.jsx)(`button`,{type:`button`,className:`message-image-btn`,"data-message-image-id":e.id,onClick:()=>ht({url:`/api/upload/${encodeURIComponent(M?.id??``)}/${encodeURIComponent(e.id)}`,name:e.name,bytes:e.bytes}),title:e.name?`查看大图:${e.name}`:`查看大图`,children:(0,m.jsx)(`img`,{src:`/api/upload/${encodeURIComponent(M?.id??``)}/${encodeURIComponent(e.id)}`,alt:e.name||`图片`,className:`message-image-thumb`,loading:`lazy`})},e.id))}),(0,m.jsx)(`div`,{className:`content`,children:o})]}),t.attachments&&t.attachments.length>0&&(0,m.jsx)(`div`,{className:`message-attachments`,"data-message-attachments":`1`,children:t.attachments.map(e=>{let n=`att:${t.id}:${e.kind}:${e.id}`,r=Je[n]??!1;return(0,m.jsxs)(`div`,{className:`message-attachment`,"data-message-attachment":e.kind,children:[(0,m.jsxs)(`button`,{type:`button`,className:`message-attachment-head`,"data-message-attachment-toggle":e.id,"aria-expanded":r,onClick:()=>Ye(e=>({...e,[n]:!r})),children:[(0,m.jsx)(`span`,{className:`message-attachment-glyph`,"aria-hidden":`true`,children:`▤`}),(0,m.jsx)(`span`,{className:`message-attachment-label`,children:`技能`}),(0,m.jsx)(`span`,{className:`message-attachment-name`,title:e.name,children:e.name}),(0,m.jsx)(`span`,{className:`message-attachment-chevron`,"aria-hidden":`true`,children:r?`▾`:`▸`})]}),r&&(0,m.jsx)(`pre`,{className:`message-attachment-body`,"data-message-attachment-body":e.id,children:e.content})]},n)})}),(0,m.jsxs)(`div`,{className:`message-actions`,children:[(0,m.jsx)(`button`,{type:`button`,className:`message-copy`,"data-copy-message":t.id,"data-copy-state":n,"aria-label":`复制这条消息:${r}`,onClick:()=>ji(t.id,r),children:n===`copied`?`已复制`:n===`error`?`复制失败`:`复制`}),i&&(0,m.jsx)(`button`,{type:`button`,className:`message-revert`,"data-withdraw-message":t.id,title:`撤回并编辑:删除这条消息并将文字恢复到输入框继续编辑`,onClick:()=>Di(t.id,!0),children:`撤回`}),a&&(0,m.jsx)(`button`,{type:`button`,className:`message-revert`,"data-revert-message":t.id,title:`回退到这里:删除后续全部内容(Pi 的记录和这里的历史一起清掉,不会新建会话,也不动任何代码文件)`,onClick:()=>Di(t.id),children:`回退`})]})]}),(0,m.jsx)(`div`,{className:`avatar`,title:he,children:he})]},e)}if(t.role===`tool`){let n=(()=>{let e=/<summary>([\s\S]*?)<\/summary>/.exec(t.content);return e?e[1].trim():`后台回传`})(),r=(()=>{let e=/<task id="[^"]+" state="([^"]+)"/.exec(t.content);return e?e[1]:`completed`})(),i=(()=>{let e=/<task_(?:result|error)>\n?([\s\S]*?)\n?<\/(?:task_result|task_error)>/.exec(t.content);return e?e[1]:t.content})(),a=`tool:${t.id}`,o=Je[a]??!1;return(0,m.jsx)(`article`,{className:`message tool`,"data-transcript-key":e,"data-tool-state":r,children:(0,m.jsxs)(`div`,{className:`message-body`,children:[(0,m.jsx)(`div`,{className:`bg-result-head`,"data-bg-result-head":t.id,children:(0,m.jsxs)(`button`,{type:`button`,className:`bg-result-toggle`,"data-bg-result-toggle":t.id,"aria-expanded":o,title:o?`折叠后台回传`:`展开后台回传`,onClick:()=>Ye(e=>({...e,[a]:!o})),children:[(0,m.jsx)(`span`,{className:`bg-result-glyph`,"aria-hidden":`true`,children:r===`error`?`✕`:`↩`}),(0,m.jsx)(`span`,{className:`bg-result-label`,children:`后台回传`}),(0,m.jsx)(`span`,{className:`bg-result-summary`,title:n,children:n}),!o&&(0,m.jsx)(`span`,{className:`bg-result-preview`,"data-bg-result-preview":t.id,title:`展开查看完整输出`,children:i.split(`
|
|
57
57
|
`).slice(-1)[0]}),(0,m.jsx)(`span`,{className:`bg-result-chevron`,"aria-hidden":`true`,children:o?`▾`:`▸`})]})}),o&&(0,m.jsx)(`div`,{className:`bg-result-body`,"data-bg-result-body":t.id,children:(0,m.jsx)(`pre`,{className:`bg-result-pre`,"data-bg-result-pre":t.id,children:i})})]})},e)}let c=t.thinking?.trim()?t.thinking:``,l=t.thinkingBlocks?.length?t.thinkingBlocks:c?[{at:0,text:c}]:[],u=(e,n)=>{let r=typeof e.at==`number`&&Number.isFinite(e.at)?e.at:n;return Math.max(0,Math.min(r,t.content.length))},d=new Set(s.map((e,t)=>c&&typeof e.thinkingAt==`number`&&e.thinkingAt<c.length?t:-1).filter(e=>e>=0)),f=[],p=0,h=s.map((e,t)=>({call:e,index:t,at:u(e,t)})).filter(({index:e})=>!d.has(e)).sort((e,t)=>e.at-t.at||e.index-t.index);for(let{call:e,index:n,at:r}of h){let i=Math.max(p,r);i>p&&f.push({text:t.content.slice(p,i)}),p=i,f.push({call:e,index:n})}f.push({text:t.content.slice(p)});let g=[];if(c){let e=0,t=s.map((e,t)=>({call:e,index:t,at:Math.max(0,Math.min(e.thinkingAt??0,c.length))})).filter(({index:e})=>d.has(e)).sort((e,t)=>e.at-t.at||e.index-t.index);for(let{call:n,index:r,at:i}of t){let t=Math.max(e,i);t>e&&g.push({text:c.slice(e,t)}),e=t,g.push({call:n,index:r})}g.push({text:c.slice(e)})}c.split(`
|
|
58
58
|
`).map(e=>e.trim()).find(e=>e.length>0);let _=e=>e.call.question?(0,m.jsx)(`div`,{className:`call-blocks`,"data-call-block-index":e.index,children:(0,m.jsx)(zr,{question:e.call.question,index:e.index,expanded:`${t.id}:${e.index}`in Xe?!!Xe[`${t.id}:${e.index}`]:!0,onToggle:()=>Qn(t.id,e.index),onAnswer:(n,r)=>wn(M?.id??``,t.id,e.index,n,r)})}):(0,m.jsx)(`div`,{className:`call-blocks`,"data-call-block-index":e.index,children:(0,m.jsx)(Pr,{call:e.call,index:e.index,expanded:`${t.id}:${e.index}`in Je&&!!Je[`${t.id}:${e.index}`],onToggle:()=>Zn(t.id,e.index)})});return(0,m.jsxs)(`article`,{className:`message assistant`,"data-transcript-key":e,children:[(0,m.jsx)(`div`,{className:`avatar`,children:`π`}),(0,m.jsxs)(`div`,{className:`message-body`,children:[(0,m.jsxs)(`div`,{className:`meta`,children:[t.agentIdentity?.name??`Super Pi`,(0,m.jsx)(`time`,{children:rr(t.createdAt)})]}),l.map((e,n)=>(0,m.jsxs)(Ir,{messageId:`${t.id}:${n}`,nestedCount:s.filter(t=>typeof t.thinkingAt==`number`&&t.thinkingAt>=e.at&&(l[n+1]?.at===void 0||t.thinkingAt<l[n+1].at)).length,preview:e.text.split(`
|
|
59
59
|
`).map(e=>e.trim()).find(Boolean)??``,expanded:$n[`${t.id}:${n}`]!==!1,onToggle:()=>ir(`${t.id}:${n}`),children:[(0,m.jsx)(`div`,{className:`content thinking-text`,children:(0,m.jsx)(Ke,{source:e.text})}),s.filter(t=>typeof t.thinkingAt==`number`&&t.thinkingAt>=e.at&&(l[n+1]?.at===void 0||t.thinkingAt<l[n+1].at)).map((e,t)=>_({call:e,index:s.indexOf(e)}))]},`${t.id}:thinking:${n}`)),f.map((e,t)=>`text`in e?e.text?(0,m.jsx)(`div`,{className:`content`,children:(0,m.jsx)(Ke,{source:e.text})},`t${t}`):null:_(e))]})]},e)}if(e.startsWith(`w:`)){let t=Gn.get(e.slice(2));return t?(0,m.jsxs)(`div`,{"data-transcript-key":e,children:[n.some(e=>e.key===`stop:${t.id}`)&&(0,m.jsx)(`span`,{children:`停止中…`}),(0,m.jsx)(Rt,{workflow:t,names:Kn,stopping:n.some(e=>e.key===`stop:${t.id}`),onStop:()=>or(t.id)},e)]},e):null}let t=Jn.get(e.slice(2));return t?(0,m.jsxs)(`div`,{"data-transcript-key":e,children:[n.some(e=>e.key===`stop:${t.id}`)&&(0,m.jsx)(`span`,{children:`停止中…`}),(0,m.jsx)(Lr,{run:t,stopping:n.some(e=>e.key===`stop:${t.id}`),name:Kn.get(t.agentId)??``,expanded:!!Ge[t.id],onToggle:()=>Xn(t.id),onStop:()=>ar(t.id)},e)]},e):null})]}),(0,m.jsxs)(`div`,{className:`stream-tail`,"data-stream-tail":`1`,"data-follow-tail":re.followTail?`true`:`false`,children:[(0,m.jsx)(`span`,{className:`stream-tail-status`,"data-run-status":M?.status??`idle`,"data-stream-state":l?M?.status===`running`?`streaming`:di.length?`waiting`:M?.status===`failed`?`failed`:`ready`:`waiting`,children:l?M?.status===`running`?`Streaming · 正在生成`:di.length?`Waiting · 消息排队中`:M?.status===`failed`?`运行失败`:M?.status===`stopped`?`已停止`:`就绪`:`连接中 / 等待连接`}),!re.followTail&&(0,m.jsx)(`button`,{type:`button`,className:`stream-tail-jump`,"data-jump-to-bottom":`1`,onClick:()=>{let e=ee.current;e&&(xi(e),e.scrollTop=e.scrollHeight,Mt(e,!1,!0))},children:`↓ 跳到底部`}),(0,m.jsx)(`span`,{className:`stream-tail-mode`,"data-follow-tail":re.followTail?`true`:`false`,children:re.followTail?`贴底`:`自由浏览`})]}),(0,m.jsx)(Rr,{items:di,pi:Ue.get(M?.id??``),running:M?.status===`running`,onCancel:fi,onSteer:mi,onSendQueued:hi}),(Hn.length>0||[...Se.values()].some(e=>e.placement===`aboveEditor`))&&(0,m.jsxs)(`div`,{className:`extui`,"data-extui-count":Hn.length,children:[Hn.map(e=>(0,m.jsx)(Vr,{request:e.request,pending:n.some(t=>t.key===`answer:${e.conversationId}:${e.request.requestId}`),onAnswer:t=>Tn(e.conversationId,e.request.requestId,t)},e.request.requestId)),sr(`aboveEditor`)]}),(0,m.jsxs)(`form`,{onSubmit:ui,onPaste:ai,onDragOver:oi,onDragLeave:si,onDrop:ci,className:gt?`composer-dropzone-active`:void 0,children:[(0,m.jsx)(`textarea`,{ref:E,value:Pt,onCompositionStart:()=>{yt.current=!0},onCompositionEnd:()=>{yt.current=!1},onChange:e=>{Vt(e.target.value);let t=e.target.value,n=e.target.selectionStart??t.length,r=t.slice(0,n),i=r.lastIndexOf(`/`),a=r.slice(i+1),o=i>0?r[i-1]:``;i>=0&&(i===0||/\s/.test(o))&&/^[\w:-]*$/u.test(a)?Gt({visible:!0,query:a,rect:e.currentTarget.getBoundingClientRect()}):Gt({visible:!1,query:``,rect:null})},placeholder:M?An?`描述你要完成的任务(支持粘贴/拖放图片)…`:`描述你要完成的任务…`:`先创建会话`,disabled:!M,onKeyDown:e=>{if(!(yt.current||nr(e))){if(Wt.visible){if(e.key===`Escape`){e.preventDefault(),Gt({visible:!1,query:``,rect:null});return}if([`ArrowDown`,`ArrowUp`,`Enter`].includes(e.key)){e.preventDefault();return}}e.key===`Enter`&&!e.shiftKey&&(e.preventDefault(),e.currentTarget.form?.requestSubmit())}}}),Wt.visible&&Wt.rect&&(0,m.jsx)(at,{query:Wt.query,anchorRect:Wt.rect,workspaceId:M?.workspaceId,command:bn,onSelect:e=>{if(e.id.startsWith(`skill:`)){let t=e.id.slice(6),n=document.querySelector(`[data-selected-workspace]`)?.dataset.selectedWorkspace;n&&fetch(`/api/memory/tool`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({workspaceId:n,conversationId:M?.id,kind:`skill.read`,skillId:t})}).then(async n=>{if(!n.ok){F(`读取技能失败`,`error`);return}let r=await n.json();typeof r.content==`string`&&Ht({kind:`skill`,id:r.id??t,name:r.name??e.label,content:r.content})}).catch(()=>F(`读取技能失败`,`error`))}let t=E.current?.selectionStart??Pt.length,n=Pt.slice(0,t),r=n.lastIndexOf(`/`),i=rt(e,Wt.query),a=`${n.slice(0,r)}/${i} ${Pt.slice(t)}`;Vt(a),Gt({visible:!1,query:``,rect:null})},onClose:()=>Gt({visible:!1,query:``,rect:null})}),ut.filter(e=>e.conversationId===M?.id).map(e=>(0,m.jsxs)(`div`,{"data-failed-send":e.id,children:[(0,m.jsxs)(`span`,{children:[`发送失败:`,e.content]}),(0,m.jsx)(`button`,{type:`button`,disabled:!!(Pt||Ft.length),onClick:()=>{zt(e.conversationId,t=>({...t,text:e.content,images:e.images})),dt(t=>t.filter(t=>t.id!==e.id))},children:`恢复草稿`}),(0,m.jsx)(`button`,{type:`button`,onClick:()=>dt(t=>t.filter(t=>t.id!==e.id)),children:`移除`})]},e.id)),ft.filter(e=>e.conversationId===M?.id).map(e=>(0,m.jsxs)(`div`,{"data-upload-preview":e.id,children:[(0,m.jsx)(`img`,{src:e.url,alt:e.name,width:48,height:48}),(0,m.jsx)(`span`,{children:e.failed?`上传失败`:`上传中…`}),(0,m.jsx)(`button`,{type:`button`,onClick:()=>{lt.current.add(e.id),URL.revokeObjectURL(e.url),pt(t=>t.filter(t=>t.id!==e.id))},children:`移除`})]},e.id)),(Ft.length>0||Lt>0)&&(0,m.jsxs)(`div`,{className:`composer-attachments`,"data-composer-attachments":`1`,children:[Ft.map((e,t)=>(0,m.jsxs)(`div`,{className:`composer-attachment-item`,"data-composer-attachment-id":e.id,children:[(0,m.jsx)(`img`,{src:`/api/upload/${encodeURIComponent(M?.id??``)}/${encodeURIComponent(e.id)}`,alt:e.name||`已附图片`,className:`composer-attachment-thumb`,onClick:()=>ht({url:`/api/upload/${encodeURIComponent(M?.id??``)}/${encodeURIComponent(e.id)}`,name:e.name,bytes:e.bytes}),title:`点击查看大图`}),(0,m.jsx)(`span`,{className:`composer-attachment-name`,title:e.name,children:e.name||`图片`}),(0,m.jsx)(`button`,{type:`button`,className:`composer-attachment-remove`,"data-composer-attachment-remove":e.id,onClick:()=>Kt(e=>e.filter((e,n)=>n!==t)),"aria-label":`移除 ${e.name||`图片`}`,title:`移除图片`,children:`✕`})]},e.id)),Lt>0&&(0,m.jsx)(`div`,{className:`composer-attachment-item composer-attachment-uploading`,children:(0,m.jsxs)(`span`,{children:[`图片上传中 (`,Lt,`)…`]})}),It.map(e=>(0,m.jsxs)(`div`,{className:`composer-attachment-item composer-attachment-skill`,"data-composer-skill-id":e.id,children:[(0,m.jsx)(`span`,{className:`composer-attachment-glyph`,"aria-hidden":`true`,children:`▤`}),(0,m.jsx)(`span`,{className:`composer-attachment-name`,title:e.name,children:e.name}),(0,m.jsx)(`button`,{type:`button`,className:`composer-attachment-remove`,"data-composer-skill-remove":e.id,onClick:()=>Ut(e.kind,e.id),"aria-label":`移除 ${e.name}`,title:`移除技能`,children:`✕`})]},`${e.kind}:${e.id}`))]}),(0,m.jsxs)(`div`,{className:`composer`,children:[(0,m.jsxs)(`div`,{className:`session-picker`,"data-session-picker":`1`,children:[(0,m.jsx)(Ur,{kind:`agent`,label:In?.name??`跟随 Pi 默认`,value:M?.agentId??L,options:Rn,disabled:!Ee||Ee.source===`unavailable`||Cn,note:Ar(Ee),onChoose:Vn}),(0,m.jsx)(Ur,{kind:`model`,label:En?.label??(we?`Pi 默认`:`读取中…`),value:En?`${En.provider}/${En.modelId}`:void 0,detail:Bn?`来自 ${In?.name}`:En&&we?.source===`pi`&&!M?.model?`Pi 默认模型`:En?`${En.provider}/${En.modelId}`:void 0,options:Mn,disabled:!On||Cn,note:kr(`模型`,we),onChoose:Pn}),(0,m.jsx)(Ur,{kind:`thinking`,label:Dn??(we?`Pi 默认`:`读取中…`),value:Dn,options:Nn,disabled:!On||Cn,note:kr(`思考等级`,we),onChoose:Fn})]}),(0,m.jsxs)(`div`,{className:`composer-hints`,children:[(0,m.jsxs)(`button`,{type:`button`,className:`composer-attach-btn`,"data-composer-attach":`1`,onClick:()=>vt.current?.click(),disabled:!M||!An||Lt>0,title:M?An?`添加图片(支持剪贴板粘贴与拖拽)`:`当前模型不支持图片输入`:`请先创建或选择会话`,"aria-label":`添加图片`,children:[(0,m.jsx)(`span`,{"aria-hidden":`true`,children:`+`}),` 图片`]}),(0,m.jsx)(`input`,{ref:vt,type:`file`,accept:`image/png,image/jpeg,image/gif,image/webp`,multiple:!0,style:{display:`none`},onChange:e=>{e.target.files?.length&&(ii(Array.from(e.target.files)),e.target.value=``)}}),(0,m.jsx)(`span`,{children:`Enter 发送 · Shift + Enter 换行`})]}),(0,m.jsxs)(`div`,{className:`composer-actions`,children:[M?.status===`running`&&(0,m.jsx)(`button`,{type:`button`,className:`stop`,"data-stop-run":M.id,disabled:n.some(e=>e.key===`stop:${M.id}`),onClick:()=>I({type:`run.stop`,conversationId:M.id},`stop:${M.id}`,e=>e),children:n.some(e=>e.key===`stop:${M.id}`)?`停止中…`:`停止`}),(0,m.jsx)(`button`,{className:`send ${M?.status===`running`?`send-queued`:``}`,"data-send-mode":M?.status===`running`?`queued`:`direct`,disabled:!Pt.trim()&&Ft.length===0||!M||Lt>0||!!_i||Cn,title:M?_i?`正在撤回:Pi 的历史还没截断完成,撤回结束后即可发送`:!Pt.trim()&&Ft.length===0?`请输入消息或添加图片后发送`:Lt>0?`图片上传中…`:M.status===`running`?`这一轮结束后发送(发送后可在上面的排队区取消或插队)`:void 0:`请先创建或选择会话`,children:_i?`撤回中…`:M?.status===`running`?`排队发送 ↑`:`发送 ↑`})]})]})]}),sr(`belowEditor`)]})]}),(0,m.jsx)(Ei,{side:`right`,width:Ne?30:j.rail,min:j.railMin,max:j.railMax,label:`调整右侧栏宽度`,onWidth:He}),(0,m.jsxs)(`nav`,{className:`mobile-nav`,"aria-label":`移动端主导航`,children:[(0,m.jsxs)(`button`,{type:`button`,"data-mobile-nav":`workspaces`,className:Fe===`workspaces`?`active`:``,onClick:()=>Ie(`workspaces`),children:[`☰`,(0,m.jsx)(`span`,{children:`工作区`})]}),(0,m.jsxs)(`button`,{type:`button`,"data-mobile-nav":`chat`,className:Fe===`chat`?`active`:``,onClick:()=>Ie(`chat`),children:[`◉`,(0,m.jsx)(`span`,{children:`对话`})]}),(0,m.jsxs)(`button`,{type:`button`,"data-mobile-nav":`rail`,className:Fe===`rail`?`active`:``,onClick:()=>{Ie(`rail`),Pe(!1)},children:[`▣`,(0,m.jsx)(`span`,{children:`工具`})]})]}),` `,et&&(0,m.jsx)(_r,{workspace:et.workspace,x:et.x,y:et.y,notify:F,onClose:()=>tt(void 0)}),nt&&(0,m.jsx)(vr,{conversation:nt.conversation,x:nt.x,y:nt.y,notify:F,onRename:()=>void Tr(nt.conversation.id),onClose:()=>it(void 0)}),y&&(0,m.jsx)(Er,{onClose:()=>b(!1),onCreate:cr}),qr&&(0,m.jsx)(Ln,{workspaceId:_,theme:Br,onChooseTheme:e=>Kr(e),onClose:()=>Xr(!1),agentCatalog:Ee,subagentCatalog:A,modelFilterRules:o.modelFilterRules??[],pendingSettings:n.some(e=>e.key===`model-rules`||e.key===`agent-directories`),onSetModelRules:e=>I({type:`model.rules.set`,rules:e},`model-rules`,t=>({...t,modelFilterRules:e})),customAgentDirs:o.customAgentDirs??[],onSetCustomAgentDirs:e=>I({type:`agents.setCustomDirs`,dirs:e},`agent-directories`,t=>({...t,customAgentDirs:e})),onRescanAgents:()=>bn({type:`agents.rescan`}),userNickname:fe,onSetUserNickname:me}),Zr&&(0,m.jsx)(Bt,{onClose:()=>Qr(!1)}),Fr&&(0,m.jsx)(Wr,{workspace:Fr,catalog:jn(we,Fr),agents:Ee,onClose:()=>Nr(void 0),onRename:()=>{let e=Fr.id;Nr(void 0),br(`workspace`,e)},onSave:(e,t,n)=>$r(Fr.id,e,t,n)}),mt&&(0,m.jsx)(li,{image:mt,onClose:()=>ht(void 0)}),(0,m.jsx)(ni,{todos:o.todosByConversation?.[M?.id??``]??[],tasks:o.backgroundTasks??[],collapsed:Ne,onToggle:()=>Pe(e=>!e),workspaceId:_,workspaceLabel:o.workspaces.find(e=>e.id===_)?.path,connected:l,files:P,onOpenFile:e=>void sn(e),activeTab:Qt,onSelectTab:$t,terminalAvailable:en,sendCommand:bn,registerTerminalSink:le})]})}On(Dn()),(0,f.createRoot)(document.getElementById(`root`)).render((0,m.jsx)(p.StrictMode,{children:(0,m.jsx)(Di,{})}));
|
package/dist/client/index.html
CHANGED
|
@@ -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-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-DiG980gH.js"></script>
|
|
12
12
|
<link rel="stylesheet" crossorigin href="/assets/index-CKdlMXJt.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
@@ -18,6 +18,41 @@ const MIME_TYPES = {
|
|
|
18
18
|
".ico": "image/x-icon",
|
|
19
19
|
".png": "image/png",
|
|
20
20
|
};
|
|
21
|
+
/**
|
|
22
|
+
* The only browser API a mounted tool needs. It is served beneath the tool's own
|
|
23
|
+
* mount, so every request stays relative to that mount and the server can derive
|
|
24
|
+
* the tool identity without exposing a registry id, a workspace, or a credential.
|
|
25
|
+
*/
|
|
26
|
+
function toolBrowserSdk() {
|
|
27
|
+
return String.raw `(() => {
|
|
28
|
+
const request = async (path, init) => {
|
|
29
|
+
const response = await fetch(path, init);
|
|
30
|
+
let body;
|
|
31
|
+
try { body = await response.json(); } catch { body = { error: "Super Pi returned an unreadable response" }; }
|
|
32
|
+
if (!response.ok) throw new Error(body && body.error ? body.error : "Tool request failed (HTTP " + response.status + ")");
|
|
33
|
+
return body;
|
|
34
|
+
};
|
|
35
|
+
const api = {
|
|
36
|
+
run: (params = {}) => request("./api/run", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ params }) }),
|
|
37
|
+
get: (runId) => request("./api/runs/" + encodeURIComponent(runId), { method: "GET" }),
|
|
38
|
+
cancel: (runId) => request("./api/runs/" + encodeURIComponent(runId), { method: "DELETE" }),
|
|
39
|
+
wait: async (runId, { intervalMs = 250, signal } = {}) => {
|
|
40
|
+
for (;;) {
|
|
41
|
+
if (signal && signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
42
|
+
const run = await api.get(runId);
|
|
43
|
+
if (run.status !== "running") return run;
|
|
44
|
+
await new Promise((resolve, reject) => {
|
|
45
|
+
const timer = setTimeout(resolve, Math.max(50, intervalMs));
|
|
46
|
+
if (signal) signal.addEventListener("abort", () => { clearTimeout(timer); reject(new DOMException("Aborted", "AbortError")); }, { once: true });
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
runAndWait: async (params = {}, options) => { const run = await api.run(params); return api.wait(run.id, options); },
|
|
51
|
+
};
|
|
52
|
+
Object.freeze(api);
|
|
53
|
+
Object.defineProperty(window, "SuperPiTool", { value: api, writable: false, configurable: false });
|
|
54
|
+
})();`;
|
|
55
|
+
}
|
|
21
56
|
/**
|
|
22
57
|
* Checks if the remote address is a local loopback interface.
|
|
23
58
|
*/
|
|
@@ -138,17 +173,15 @@ export function createHttpServer(options) {
|
|
|
138
173
|
return createServer(async (request, response) => {
|
|
139
174
|
// Internal tools use a narrow per-run capability, never the global management token.
|
|
140
175
|
const requestPath = request.url?.split("?")[0] ?? "";
|
|
141
|
-
const opaqueToolApi = requestPath.startsWith("/api/tools/") && request.headers.origin === "null";
|
|
142
176
|
// Tool registration is checked after its JSON body is parsed: an agent proves its turn capability
|
|
143
177
|
// there, while an interactive browser uses the ordinary server access token.
|
|
144
|
-
if (requestPath !== "/api/memory/tool" && requestPath !== "/api/tools" && !
|
|
178
|
+
if (requestPath !== "/api/memory/tool" && requestPath !== "/api/tools" && !requestPath.startsWith("/tools/") && !isAuthorized(request, accessToken)) {
|
|
145
179
|
response.writeHead(401, { "www-authenticate": "Bearer" }).end("Authentication required");
|
|
146
180
|
return;
|
|
147
181
|
}
|
|
148
182
|
const url = new URL(request.url ?? "/", `http://${request.headers.host ?? "localhost"}`);
|
|
149
183
|
const remote = request.socket.remoteAddress ?? "";
|
|
150
184
|
const json = (status, body) => { response.writeHead(status, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" }); response.end(JSON.stringify(body)); };
|
|
151
|
-
const toolJson = (status, body) => { response.writeHead(status, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store", "access-control-allow-origin": "null", "vary": "Origin" }); response.end(JSON.stringify(body)); };
|
|
152
185
|
if (url.pathname === "/api/tools") {
|
|
153
186
|
try {
|
|
154
187
|
if (request.method === "GET") {
|
|
@@ -211,89 +244,101 @@ export function createHttpServer(options) {
|
|
|
211
244
|
response.writeHead(404).end("Tool not found");
|
|
212
245
|
return;
|
|
213
246
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
247
|
+
// A mounted tool never learns its random registry id. Its API is instead addressed relative
|
|
248
|
+
// to its own mount (`./api/run`), and this route supplies that identity from the URL. Besides
|
|
249
|
+
// making a generated tool usable on its first render, this binds status and cancellation to the
|
|
250
|
+
// same tool that created a run: an opaque frame for tool A cannot inspect tool B's run ids.
|
|
251
|
+
const toolOrigin = request.headers.origin === "null";
|
|
252
|
+
const toolApi = (status, body) => {
|
|
253
|
+
response.writeHead(status, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store", "access-control-allow-origin": "null", "vary": "Origin" });
|
|
254
|
+
response.end(JSON.stringify(body));
|
|
255
|
+
};
|
|
256
|
+
if (resource === "api/run") {
|
|
257
|
+
if (!toolOrigin) {
|
|
258
|
+
toolApi(403, { error: "tool runtime API is only available to the sandboxed tool frame" });
|
|
218
259
|
return;
|
|
219
260
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
261
|
+
if (request.method === "OPTIONS") {
|
|
262
|
+
response.writeHead(204, { "access-control-allow-origin": "null", "access-control-allow-methods": "POST", "access-control-allow-headers": "content-type", "vary": "Origin" }).end();
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (request.method !== "POST") {
|
|
266
|
+
toolApi(405, { error: "method" });
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
let raw = "";
|
|
270
|
+
for await (const chunk of request) {
|
|
271
|
+
raw += chunk;
|
|
272
|
+
if (raw.length > 64_000) {
|
|
273
|
+
toolApi(413, { error: "request too large" });
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
try {
|
|
278
|
+
const body = JSON.parse(raw);
|
|
279
|
+
const resolved = await toolRegistry.resolve(id);
|
|
280
|
+
if (!resolved?.tool.script) {
|
|
281
|
+
toolApi(404, { error: "python script is not registered" });
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
const run = toolRunner.start({ toolId: id, script: resolve(resolved.root, resolved.tool.script.path), cwd: resolved.root, definition: resolved.tool.script, params: body.params ?? {} });
|
|
285
|
+
toolApi(202, run);
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
toolApi(400, { error: error instanceof Error ? error.message : "tool run failed" });
|
|
289
|
+
}
|
|
241
290
|
return;
|
|
242
291
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
json(413, { error: "request too large" });
|
|
292
|
+
const runMatch = /^api\/runs\/([^/]+)$/.exec(resource);
|
|
293
|
+
if (runMatch) {
|
|
294
|
+
if (!toolOrigin) {
|
|
295
|
+
toolApi(403, { error: "tool runtime API is only available to the sandboxed tool frame" });
|
|
248
296
|
return;
|
|
249
297
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const body = JSON.parse(raw);
|
|
253
|
-
const resolved = await toolRegistry.resolve(id);
|
|
254
|
-
if (!resolved?.tool.script) {
|
|
255
|
-
toolJson(404, { error: "python script is not registered" });
|
|
298
|
+
if (request.method === "OPTIONS") {
|
|
299
|
+
response.writeHead(204, { "access-control-allow-origin": "null", "access-control-allow-methods": "GET, DELETE", "vary": "Origin" }).end();
|
|
256
300
|
return;
|
|
257
301
|
}
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
302
|
+
const runId = decodeURIComponent(runMatch[1]);
|
|
303
|
+
if (request.method === "GET") {
|
|
304
|
+
const run = toolRunner.get(runId, id);
|
|
305
|
+
if (!run) {
|
|
306
|
+
toolApi(404, { error: "run not found" });
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
toolApi(200, run);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (request.method === "DELETE") {
|
|
313
|
+
const run = toolRunner.cancel(runId, id);
|
|
314
|
+
if (!run) {
|
|
315
|
+
toolApi(404, { error: "run not found" });
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
toolApi(200, run);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
toolApi(405, { error: "method" });
|
|
271
322
|
return;
|
|
272
323
|
}
|
|
273
|
-
if (
|
|
274
|
-
response.writeHead(
|
|
324
|
+
if (resource === "super-pi-tool.js") {
|
|
325
|
+
response.writeHead(200, { "content-type": "text/javascript; charset=utf-8", "cache-control": "no-store" }).end(toolBrowserSdk());
|
|
275
326
|
return;
|
|
276
327
|
}
|
|
277
|
-
|
|
278
|
-
const
|
|
279
|
-
if (!
|
|
280
|
-
|
|
328
|
+
try {
|
|
329
|
+
const resolved = await toolRegistry.resolve(id, resource);
|
|
330
|
+
if (!resolved) {
|
|
331
|
+
response.writeHead(404).end("Tool not found");
|
|
281
332
|
return;
|
|
282
333
|
}
|
|
283
|
-
|
|
334
|
+
response.writeHead(200, { "content-type": MIME_TYPES[extname(resolved.file)] ?? "application/octet-stream", "cache-control": "no-store", "content-security-policy": "default-src 'self'; connect-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'" });
|
|
335
|
+
createReadStream(resolved.file).pipe(response);
|
|
284
336
|
return;
|
|
285
337
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
if (!run) {
|
|
289
|
-
toolJson(404, { error: "run not found" });
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
toolJson(200, run);
|
|
338
|
+
catch {
|
|
339
|
+
response.writeHead(404).end("Tool file not found");
|
|
293
340
|
return;
|
|
294
341
|
}
|
|
295
|
-
toolJson(405, { error: "method" });
|
|
296
|
-
return;
|
|
297
342
|
}
|
|
298
343
|
/**
|
|
299
344
|
* THE TERMINAL IS NOT AN API ENDPOINT, and the routing above deliberately cannot reach one.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evandu/super-pi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.77",
|
|
4
4
|
"description": "Web control plane and companion for Pi",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -85,6 +85,7 @@
|
|
|
85
85
|
"scripts/model-filter.test.mjs",
|
|
86
86
|
"scripts/active-memory.test.mjs",
|
|
87
87
|
"scripts/tool-runner.test.mjs",
|
|
88
|
+
"scripts/tool-runtime-api.test.mjs",
|
|
88
89
|
"scripts/extension-load.test.mjs"
|
|
89
90
|
],
|
|
90
91
|
"check:browser": [
|