pi-web-ui 0.80.0 → 0.80.1

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.
@@ -1366,18 +1366,24 @@ let oneShotBashSeq = 0;
1366
1366
  /** 应用 head / tail 参数到输出顶层行(替代 `| head` / `| tail` 管道——管道会
1367
1367
  * 缓冲输出、让可见终端全程哑火,还容易白白触发静默解阻)。两者同时给时先
1368
1368
  * 截头再截尾。 */
1369
- export function applyHeadTail(text, head, tail) {
1369
+ export function applyHeadTail(text, head, tail, lang = "en") {
1370
1370
  // 只对真实数据行切片;省略提示行单独存,最后再包回输出,避免提示行在
1371
1371
  // head+tail 组合时被当成数据行参与第二次截取(导致尾部少截一行)。
1372
1372
  let data = text.split("\n");
1373
1373
  let headNote = null;
1374
1374
  let tailNote = null;
1375
1375
  if (head && head > 0 && data.length > head) {
1376
- headNote = `…(后 ${data.length - head} 行已省略)`;
1376
+ const n = data.length - head;
1377
+ headNote = pick(lang, `…(后 ${n} 行已省略)`, `…[${n} lines omitted below]…`, "terminals.headtail.omitted.below", {
1378
+ n,
1379
+ });
1377
1380
  data = data.slice(0, head);
1378
1381
  }
1379
1382
  if (tail && tail > 0 && data.length > tail) {
1380
- tailNote = `…(前 ${data.length - tail} 行已省略)`;
1383
+ const n = data.length - tail;
1384
+ tailNote = pick(lang, `…(前 ${n} 行已省略)`, `…[${n} lines omitted above]…`, "terminals.headtail.omitted.above", {
1385
+ n,
1386
+ });
1381
1387
  data = data.slice(-tail);
1382
1388
  }
1383
1389
  const parts = [];
@@ -1460,10 +1466,12 @@ export function makeTerminalBashTool(terminals, opts) {
1460
1466
  const redirect = stripped && limiter.kind === "tail" ? detectStdoutRedirect(runCommand) : null;
1461
1467
  const tailFile = redirect ? { file: redirect.file, lines: limiter.lines ?? 10 } : undefined;
1462
1468
  // 复杂子表达式先 hoist 成干净 const(issue #91 v2:vars key 不写复杂表达式)。
1463
- const limiterSegment = limiter.segment;
1464
- const limiterTailLines = limiter.lines ?? 10;
1465
- const limiterTailZh = limiter.kind === "tail" ? `本次返回末尾 ${limiterTailLines} 行。` : "本次返回全部输出。";
1466
- const limiterTailEn = limiter.kind === "tail"
1469
+ // 注意:limiter 对「没有尾部限输出管道」的命令是 null(issue #121)——
1470
+ // 这些 const 一律走可选链,只在 stripped=true(limiterNote 才被取用)时才有意义。
1471
+ const limiterSegment = limiter?.segment ?? "";
1472
+ const limiterTailLines = limiter?.lines ?? 10;
1473
+ const limiterTailZh = limiter?.kind === "tail" ? `本次返回末尾 ${limiterTailLines} 行。` : "本次返回全部输出。";
1474
+ const limiterTailEn = limiter?.kind === "tail"
1467
1475
  ? `Returning the last ${limiterTailLines} lines this time.`
1468
1476
  : "Returning the full output this time.";
1469
1477
  const limiterNote = stripped
@@ -1497,7 +1505,7 @@ export function makeTerminalBashTool(terminals, opts) {
1497
1505
  if (m) {
1498
1506
  terminals.setSentinelPending(termId, false);
1499
1507
  closeOneShot();
1500
- const text = applyHeadTail(cleanBashOutput(collected), p.head, effectiveTail);
1508
+ const text = applyHeadTail(cleanBashOutput(collected), p.head, effectiveTail, lang);
1501
1509
  return {
1502
1510
  content: [
1503
1511
  {
@@ -1517,7 +1525,7 @@ export function makeTerminalBashTool(terminals, opts) {
1517
1525
  }
1518
1526
  // 静默解阻(仅持久终端):转后台 + 注册完成观察器,立即把控制权还给模型。
1519
1527
  if (persist && idleMs > 0 && Date.now() - lastDataAt >= idleMs) {
1520
- return backgroundResult(terminals, opts, runCommand, applyHeadTail(cleanBashOutput(collected), p.head, effectiveTail), Math.round((Date.now() - lastDataAt) / 1000), lang);
1528
+ return backgroundResult(terminals, opts, runCommand, applyHeadTail(cleanBashOutput(collected), p.head, effectiveTail, lang), Math.round((Date.now() - lastDataAt) / 1000), lang);
1521
1529
  }
1522
1530
  }
1523
1531
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-web-ui",
3
- "version": "0.80.0",
3
+ "version": "0.80.1",
4
4
  "description": "Web chat interface for the pi coding agent, powered by the pi SDK (@earendil-works/pi-coding-agent) — one-command run, Docker/systemd/launchd deployable",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -1,4 +1,4 @@
1
- import{a as l,j as n}from"./markdown-D3PKeHAZ.js";import{u as P,b as O,T as M,a as p,c as W,F as Y,d as z,e as Z,f as H,g as ee,h as ne,i as te,j as se,r as ae}from"./index-D7BpHJ59.js";import{D as re,o as ie}from"./xterm-B96xOxS9.js";import"./react-w24rH0km.js";function le(t){let c=null;return{clean:t.replace(/\r?\n?\[pi-term-exit:(-?\d+)\]\r?\n?/g,(h,u)=>(c=Number(u),`\r
1
+ import{a as l,j as n}from"./markdown-D3PKeHAZ.js";import{u as P,b as O,T as M,a as p,c as W,F as Y,d as z,e as Z,f as H,g as ee,h as ne,i as te,j as se,r as ae}from"./index-BFA8Pn_5.js";import{D as re,o as ie}from"./xterm-B96xOxS9.js";import"./react-w24rH0km.js";function le(t){let c=null;return{clean:t.replace(/\r?\n?\[pi-term-exit:(-?\d+)\]\r?\n?/g,(h,u)=>(c=Number(u),`\r
2
2
  `)).replace(/\r?\n?\x1b\[90m\[(?:进程已退出,退出码 |Process exited with code )-?\d+\]\x1b\[0m\r?\n?/g,`\r
3
3
  `),exitCode:c}}function ce({conversationId:t,terminalId:c,command:s,cwd:h,title:u,active:w,running:f,exitCode:k,register:g}){const C=l.useRef(null),j=l.useRef(null),{locale:o}=P(),y=l.useRef(o);y.current=o;const E=s?JSON.stringify(s):"";l.useEffect(()=>{const m=C.current;if(!m)return;const r=new re({theme:O(),fontFamily:'"SF Mono", "JetBrains Mono", ui-monospace, Menlo, Consolas, monospace',fontSize:13,cursorBlink:!0,scrollback:8e3}),v=new ie;r.loadAddon(v),r.open(m),j.current={term:r,fit:v},w&&r.focus();const b=()=>{r.options.theme=O()};window.addEventListener(M,b),r.attachCustomKeyEventHandler(d=>{if(d.type!=="keydown")return!0;const S=d.key?.toLowerCase();if((d.ctrlKey||d.metaKey)&&S==="v")return!1;if(d.ctrlKey&&!d.shiftKey&&!d.altKey&&S==="c"&&r.hasSelection()){const D=r.textarea;return D&&(D.value=r.getSelection(),D.select()),!1}return!0});const _=g(t,c,{write:d=>r.write(le(d).clean),dispose:()=>r.dispose()}),$=()=>{try{v.fit(),p({type:"terminal_resize",terminalId:c,conversationId:t,cols:r.cols,rows:r.rows})}catch{}},B=requestAnimationFrame(()=>{try{v.fit()}catch{}s?p({type:"run_command",terminalId:c,conversationId:t,command:s,cols:r.cols,rows:r.rows}):p({type:"terminal_create",terminalId:c,title:u,locale:y.current,conversationId:t,cwd:h,cols:r.cols,rows:r.rows})}),R=r.onData(d=>{p({type:"terminal_input",terminalId:c,conversationId:t,data:d})});let T=null;return typeof ResizeObserver<"u"&&(T=new ResizeObserver(()=>{m.offsetWidth>0&&m.offsetHeight>0&&$()}),T.observe(m)),()=>{cancelAnimationFrame(B),R.dispose(),window.removeEventListener(M,b),T?.disconnect(),_(),r.dispose(),j.current=null}},[t,c,E,g]),l.useEffect(()=>{if(!w)return;const m=requestAnimationFrame(()=>{const r=j.current;if(r){try{r.fit.fit(),p({type:"terminal_resize",terminalId:c,conversationId:t,cols:r.term.cols,rows:r.term.rows})}catch{}r.term.focus()}});return()=>cancelAnimationFrame(m)},[w]);const{t:F}=P(),x=l.useRef(void 0);return l.useEffect(()=>{if(f===x.current||(x.current=f,f!==!1))return;const m=j.current;m&&m.term.write(`\r
4
4
  \x1B[90m${F("exitBanner",{code:k??""})}\x1B[0m\r