dsh-neotui 0.2.1 → 0.2.2
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/CHANGELOG.md +22 -0
- package/package.json +1 -1
- package/src/panels.js +9 -23
- package/src/views.js +61 -40
- package/src/widgets.js +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.2 — 2026-08-16
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- 底栏在子代理状态后显示“有 x 条命令正在排队 Ctrl+N 查看详情”。
|
|
8
|
+
- Ctrl+N 打开精简排队命令面板;每条命令一行,支持 `dd` 删除。
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- AskUser 窗口按实际内容自适应高度,长内容才启用滚动。
|
|
13
|
+
- 选项标签与描述只显示一次,完整描述按终端宽度换行。
|
|
14
|
+
- 自定义答案常驻在选项下一行并支持最多六行自动换行。
|
|
15
|
+
- 长正文使用 Ctrl+↑/Ctrl+↓、PgUp/PgDn、Home/End 和滚轮翻页。
|
|
16
|
+
- 权限审批窗口保留完整原因和命令,明确显示滚动提示与位置。
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- AskUser 自定义回答、真实跳过项、精确鼠标 hitbox 和文本光标编辑。
|
|
21
|
+
- 权限申请窗口支持 ↑/↓ 逐行滚动,不改变安全默认“拒绝”。
|
|
22
|
+
- 超长审批命令不再只保留前六行。
|
|
23
|
+
- Queue 面板移除未要求的编辑与 steering 操作,`dd` 同时兼容 text/key 事件。
|
|
24
|
+
|
|
3
25
|
## 0.2.1 — 2026-08-16
|
|
4
26
|
|
|
5
27
|
### Fixed
|
package/package.json
CHANGED
package/src/panels.js
CHANGED
|
@@ -1751,18 +1751,17 @@ export class QueuePanel extends Popup {
|
|
|
1751
1751
|
const items = app.queueItems ?? [];
|
|
1752
1752
|
const w = Math.max(24, Math.min(84, app.screen.w - 4));
|
|
1753
1753
|
const h = Math.max(7, Math.min(24, app.screen.h - 4));
|
|
1754
|
-
super({ x: Math.max(0, Math.floor((app.screen.w - w) / 2)), y: Math.max(0, Math.floor((app.screen.h - h) / 2)), w, h, title: "
|
|
1755
|
-
this.app = app; this.items = items; this.sel = 0; this.pending = false; this.
|
|
1754
|
+
super({ x: Math.max(0, Math.floor((app.screen.w - w) / 2)), y: Math.max(0, Math.floor((app.screen.h - h) / 2)), w, h, title: "排队命令 · ↑↓ 选择 · dd 删除 · Esc 关闭", lines: [], buttons: [], scrollable: true });
|
|
1755
|
+
this.app = app; this.items = items; this.sel = 0; this.pending = false; this.dArmed=false; this.rebuild();
|
|
1756
1756
|
}
|
|
1757
1757
|
rebuild() {
|
|
1758
|
-
const lines = [
|
|
1758
|
+
const lines = [];
|
|
1759
1759
|
for (let i = 0; i < this.items.length; i++) {
|
|
1760
1760
|
const item = this.items[i];
|
|
1761
1761
|
const text = partsText(item.message?.content).replace(/\s+/g, " ");
|
|
1762
1762
|
lines.push([{ t: `${i === this.sel ? "▸" : " "} ${item.placement === "queued" ? "⏳" : item.placement === "steering" ? "↪" : "ℹ"} ${truncate(text || item.id, this.w - 8)}`, fg: i === this.sel ? T.SELFG : K.TXT, bg: i === this.sel ? T.MENUSEL : -1 }]);
|
|
1763
1763
|
}
|
|
1764
1764
|
if (!this.items.length) lines.push([{ t: " (队列为空)", fg: K.FAINT }]);
|
|
1765
|
-
if (this.editInput) lines.push([{ t: ` 编辑: ${truncate(this.editInput.value, this.w - 8)}`, fg: K.ACCENT }]);
|
|
1766
1765
|
this.lines = lines;
|
|
1767
1766
|
}
|
|
1768
1767
|
syncItems(items) {
|
|
@@ -1782,7 +1781,6 @@ export class QueuePanel extends Popup {
|
|
|
1782
1781
|
const action = kind === "edit" ? { kind, content: [{ type: "text", text: content }] } : { kind };
|
|
1783
1782
|
await this.app.api.call("session.updateQueue", { sessionId: this.app.currentSession, itemId: item.id, action });
|
|
1784
1783
|
if (kind === "remove") this.syncItems(this.items.filter((row) => row.id !== item.id));
|
|
1785
|
-
this.editInput = null;
|
|
1786
1784
|
} catch (e) {
|
|
1787
1785
|
const code = this.#errorCode(e);
|
|
1788
1786
|
if (code === "queue-item-not-found" || /queue-item-not-found/.test(e.message ?? "")) {
|
|
@@ -1793,31 +1791,19 @@ export class QueuePanel extends Popup {
|
|
|
1793
1791
|
} else this.app.toast(`队列操作失败: ${e.message}`);
|
|
1794
1792
|
} finally { this.pending = false; this.rebuild(); this.app.redraw(); }
|
|
1795
1793
|
}
|
|
1796
|
-
#beginEdit() {
|
|
1797
|
-
const item = this.items[this.sel];
|
|
1798
|
-
if (!item || item.placement !== "queued") { this.app.toast("只有排队消息可以编辑"); return; }
|
|
1799
|
-
this.editInput = new Input({ x: this.x + 2, y: this.y + this.h - 3, w: this.w - 4, h: 1, prompt: "编辑: ", allowEmptyEnter: false, onEnter: (value) => this.#mutate("edit", value) });
|
|
1800
|
-
this.editInput.setValue(partsText(item.message?.content));
|
|
1801
|
-
this.rebuild();
|
|
1802
|
-
}
|
|
1803
1794
|
onKey(ev) {
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
if (this.editInput.onKey(ev)) { this.rebuild(); this.app.redraw(); return true; }
|
|
1807
|
-
}
|
|
1795
|
+
const ch=ev.type==="text"?ev.text:ev.type==="key"&&ev.name==="char"?ev.key:null;
|
|
1796
|
+
if(ch==="d"){if(this.dArmed){this.dArmed=false;this.#mutate("remove");}else{this.dArmed=true;this.app.toast("再按 d 删除这条排队命令");}return true;}
|
|
1808
1797
|
if (ev.type === "key") {
|
|
1809
1798
|
if (ev.name === "escape") { this.app.closeOverlay(); return true; }
|
|
1810
|
-
if (ev.name === "up") { this.sel = Math.max(0, this.sel - 1); this.rebuild(); return true; }
|
|
1811
|
-
if (ev.name === "down") { this.sel = Math.min(this.items.length - 1, this.sel + 1); this.rebuild(); return true; }
|
|
1812
|
-
if (ev.name === "char" && ev.key === "e" && !ev.ctrl) { this.#beginEdit(); return true; }
|
|
1813
|
-
if (ev.name === "char" && ev.key === "s" && !ev.ctrl) { this.#mutate("steer"); return true; }
|
|
1814
|
-
if (ev.name === "char" && ev.key === "d" && !ev.ctrl) { this.#mutate("remove"); return true; }
|
|
1799
|
+
if (ev.name === "up") { this.dArmed=false;this.sel = Math.max(0, this.sel - 1); this.rebuild(); return true; }
|
|
1800
|
+
if (ev.name === "down") { this.dArmed=false;this.sel = Math.min(this.items.length - 1, this.sel + 1); this.rebuild(); return true; }
|
|
1815
1801
|
}
|
|
1816
|
-
return
|
|
1802
|
+
this.dArmed=false;return false;
|
|
1817
1803
|
}
|
|
1818
1804
|
onMouse(ev) {
|
|
1819
1805
|
if (ev.kind === "press" && ev.button === 0) {
|
|
1820
|
-
const idx = ev.y - this.y -
|
|
1806
|
+
const idx = ev.y - this.y - 1;
|
|
1821
1807
|
if (idx >= 0 && idx < this.items.length) { this.sel = idx; this.rebuild(); this.app.redraw(); return true; }
|
|
1822
1808
|
}
|
|
1823
1809
|
return super.onMouse(ev);
|
package/src/views.js
CHANGED
|
@@ -2344,14 +2344,31 @@ function truncateText(s, n) {
|
|
|
2344
2344
|
return s.length > n ? s.slice(0, n) + "\n…(截断)" : s;
|
|
2345
2345
|
}
|
|
2346
2346
|
|
|
2347
|
+
function wrapDisplayText(value, width) {
|
|
2348
|
+
const out = [];
|
|
2349
|
+
for (const raw of String(value ?? "").split("\n")) {
|
|
2350
|
+
if (!raw) { out.push(""); continue; }
|
|
2351
|
+
let line = "", used = 0;
|
|
2352
|
+
for (const g of graphemes(raw)) {
|
|
2353
|
+
const gw = graphemeWidth(g);
|
|
2354
|
+
if (line && used + gw > width) { out.push(line); line = ""; used = 0; }
|
|
2355
|
+
line += g; used += gw;
|
|
2356
|
+
}
|
|
2357
|
+
out.push(line);
|
|
2358
|
+
}
|
|
2359
|
+
return out;
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2347
2362
|
// ---- Question popup ----
|
|
2348
2363
|
|
|
2349
2364
|
export class QuestionPopup extends Popup {
|
|
2350
2365
|
constructor({ app, frame }) {
|
|
2351
2366
|
const questions = frame.questions ?? [];
|
|
2352
2367
|
const planReview = questions.length === 1 && questions[0]?.intent?.kind === "plan-review";
|
|
2353
|
-
const w = Math.max(12, Math.min(planReview ?
|
|
2354
|
-
const
|
|
2368
|
+
const w = Math.max(12, Math.min(planReview ? 96 : 92, app.screen.w - 2));
|
|
2369
|
+
const wrapCount=(value,width)=>wrapDisplayText(value,width).length;
|
|
2370
|
+
const estimated=questions.reduce((n,q)=>n+1+wrapCount(q.question??"",w-4)+(q.detail?wrapCount(q.detail,w-4):0)+(q.options??[]).reduce((m,o)=>m+1+(o.description?wrapCount(o.description,w-10):0),0)+(q.intent?.kind==="plan-review"?0:4),0)+2;
|
|
2371
|
+
const h=Math.max(10,Math.min(app.screen.h-2,estimated));
|
|
2355
2372
|
super({
|
|
2356
2373
|
x: Math.max(0, Math.floor((app.screen.w - w) / 2)), y: Math.max(0, Math.floor((app.screen.h - h) / 2)),
|
|
2357
2374
|
w, h, title: planReview ? "✎ 计划审阅" : "❓ 需要你的回答",
|
|
@@ -2387,44 +2404,44 @@ export class QuestionPopup extends Popup {
|
|
|
2387
2404
|
const q = this.questions[this.questionIdx];
|
|
2388
2405
|
if (!q) return;
|
|
2389
2406
|
const draft = this.drafts[this.questionIdx];
|
|
2390
|
-
let ly = this.y + 1;
|
|
2391
|
-
screen.text(this.x + 2, ly++, truncate(`▎ ${q.header ?? `问题 ${this.questionIdx + 1}/${this.questions.length}`}`, this.w - 4), { fg: K.ACCENT, bold: true });
|
|
2392
|
-
screen.text(this.x + 2, ly++, truncate(q.question ?? "", this.w - 4), { fg: K.TXT });
|
|
2393
|
-
if (this.planReview) screen.text(this.x + 2, ly++, "Enter 执行计划 · ↓ 继续规划 · Esc 取消审阅", { fg: K.FAINT });
|
|
2394
2407
|
const opts = q.options ?? [];
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
this.
|
|
2401
|
-
this.
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
}
|
|
2408
|
+
const optionDescriptionRows=opts.reduce((n,o)=>n+(o.description?wrapDisplayText(o.description,this.w-10).length:0),0);
|
|
2409
|
+
const actionRows = opts.length + optionDescriptionRows + (this.planReview ? 0 : Math.min(6,Math.max(3,wrapDisplayText(draft.custom||"在此输入…",this.w-10).length))+2);
|
|
2410
|
+
const actionTop = Math.max(this.y + 5, this.y + this.h - 1 - Math.min(actionRows,this.h-6));
|
|
2411
|
+
const doc = [
|
|
2412
|
+
{ text: `▎ ${q.header ?? `问题 ${this.questionIdx + 1}/${this.questions.length}`}`, fg: K.ACCENT, bold: true },
|
|
2413
|
+
...wrapDisplayText(q.question ?? "", this.w - 4).map((text) => ({ text, fg: K.TXT })),
|
|
2414
|
+
...(q.detail ? wrapDisplayText(q.detail, this.w - 4).map((text) => ({ text, fg: K.DIM })) : []),
|
|
2415
|
+
];
|
|
2416
|
+
|
|
2417
|
+
const room = Math.max(1, actionTop - (this.y + 1));
|
|
2418
|
+
const maxScroll = Math.max(0, doc.length - room);
|
|
2419
|
+
this.detailScrollY = Math.max(0, Math.min(this.detailScrollY, maxScroll)); this.detailPage = room; this.detailTotal = doc.length;
|
|
2420
|
+
let ly = this.y + 1;
|
|
2421
|
+
for (const line of doc.slice(this.detailScrollY, this.detailScrollY + room)) screen.text(this.x + 2, ly++, truncate(line.text, this.w - 4), { fg: line.fg, attrs: line.bold ? 1 : 0 });
|
|
2422
|
+
if(maxScroll>0){screen.text(this.x+this.w-26,this.y,`正文 Ctrl+↑↓ 翻页 ${this.detailScrollY+1}-${Math.min(doc.length,this.detailScrollY+room)}/${doc.length}`,{fg:K.ACCENT,bg:T.BG2});}
|
|
2423
|
+
screen.hline(this.x+1,this.x+this.w-2,actionTop-1,"─",{fg:T.BORDER2,bg:T.BG2});screen.text(this.x+3,actionTop-1," 回答(↑/↓ 选择) ",{fg:K.ACCENT,bg:T.BG2});
|
|
2424
|
+
ly=actionTop;
|
|
2409
2425
|
this.optionRows = []; this.optionHitboxes = [];
|
|
2410
|
-
for (let i = 0; i < opts.length
|
|
2426
|
+
for (let i = 0; i < opts.length; i++) {
|
|
2411
2427
|
this.optionRows[i] = ly;
|
|
2412
2428
|
const chosen = draft.selected.includes(opts[i].label);
|
|
2413
2429
|
const cursor = this.selIdx === i;
|
|
2414
2430
|
const glyph = q.multiSelect ? (chosen ? "☑" : "☐") : (chosen ? "●" : "○");
|
|
2415
2431
|
const optionText=truncate(` ${cursor ? "▸" : " "} ${glyph} ${opts[i].label}`, this.w - 6);
|
|
2416
|
-
|
|
2432
|
+
const descLines=opts[i].description?wrapDisplayText(opts[i].description,this.w-10):[];
|
|
2433
|
+
this.optionHitboxes[i]={x1:this.x+2,x2:this.x+2+strWidth(optionText)-1,y1:ly,y2:ly+descLines.length};
|
|
2417
2434
|
screen.text(this.x + 2, ly++, optionText, { fg: cursor ? T.SELFG : K.TXT, bg: cursor ? T.MENUSEL : -1 });
|
|
2418
|
-
|
|
2435
|
+
for(const line of descLines)screen.text(this.x+7,ly++,line,{fg:K.FAINT,bg:cursor?T.MENUSEL:-1});
|
|
2419
2436
|
}
|
|
2420
|
-
if (!this.planReview
|
|
2437
|
+
if (!this.planReview) {
|
|
2421
2438
|
this.optionRows[opts.length] = ly;
|
|
2422
2439
|
const cursor = this.selIdx === opts.length;
|
|
2423
2440
|
const customText=truncate(` ${cursor ? "▸" : " "} ✎ 输入自己的回答`, this.w - 6);
|
|
2424
2441
|
this.optionHitboxes[opts.length]={x1:this.x+2,x2:this.x+2+strWidth(customText)-1,y1:ly,y2:ly};
|
|
2425
2442
|
screen.text(this.x + 2, ly++, customText, { fg: cursor ? T.SELFG : K.ACCENT, bg: cursor ? T.MENUSEL : -1 });
|
|
2426
|
-
|
|
2427
|
-
|
|
2443
|
+
{const chars=Array.from(draft.custom),shown=this.customEditing?[...chars.slice(0,this.customCursor),"▏",...chars.slice(this.customCursor)].join(""):draft.custom;const inputLines=wrapDisplayText(shown||"在此输入…",this.w-10).slice(-6);for(let i=0;i<inputLines.length;i++)screen.text(this.x+4,ly++,`${i===0?"> ":" "}${inputLines[i]}`,{fg:this.customEditing?K.TXT:K.FAINT,bg:this.customEditing?T.BG2:-1});}
|
|
2444
|
+
{this.optionRows[opts.length+1]=ly;const skip=this.selIdx===opts.length+1,skipText=` ${skip?"▸":" "} ↷ 跳过此问题`;this.optionHitboxes[opts.length+1]={x1:this.x+2,x2:this.x+2+strWidth(skipText)-1,y1:ly,y2:ly};screen.text(this.x+2,ly++,skipText,{fg:skip?T.SELFG:K.FAINT,bg:skip?T.MENUSEL:-1});}
|
|
2428
2445
|
}
|
|
2429
2446
|
}
|
|
2430
2447
|
|
|
@@ -2441,8 +2458,9 @@ export class QuestionPopup extends Popup {
|
|
|
2441
2458
|
}
|
|
2442
2459
|
|
|
2443
2460
|
onMouse(ev) {
|
|
2444
|
-
if (
|
|
2445
|
-
|
|
2461
|
+
if (ev.kind === "wheel-up" || ev.kind === "wheel-down") {
|
|
2462
|
+
const max=Math.max(0,(this.detailTotal??0)-(this.detailPage??1));
|
|
2463
|
+
this.detailScrollY = Math.max(0,Math.min(max,this.detailScrollY + (ev.kind === "wheel-up" ? -3 : 3)));
|
|
2446
2464
|
this.app.redraw(); return true;
|
|
2447
2465
|
}
|
|
2448
2466
|
if (ev.kind === "press" && ev.button === 0) {
|
|
@@ -2469,14 +2487,16 @@ export class QuestionPopup extends Popup {
|
|
|
2469
2487
|
const choices = count + (this.planReview ? 0 : 2);
|
|
2470
2488
|
if (ev.type === "text") { if(this.customEditing||this.selIdx===count){this.customEditing=true;draft.selected=[];const chars=Array.from(draft.custom);chars.splice(this.customCursor,0,...Array.from(ev.text));draft.custom=chars.join("");this.customCursor+=Array.from(ev.text).length;draft.skipped=false;return true;} return false; }
|
|
2471
2489
|
if (ev.type !== "key") return false;
|
|
2472
|
-
if (
|
|
2473
|
-
const total
|
|
2490
|
+
if (["pageup", "pagedown", "home", "end"].includes(ev.name)) {
|
|
2491
|
+
const total=this.detailTotal??0,max=Math.max(0,total-this.detailPage);
|
|
2474
2492
|
if (ev.name === "pageup") this.detailScrollY = Math.max(0, this.detailScrollY - this.detailPage);
|
|
2475
|
-
else if (ev.name === "pagedown") this.detailScrollY = Math.min(
|
|
2493
|
+
else if (ev.name === "pagedown") this.detailScrollY = Math.min(max, this.detailScrollY + this.detailPage);
|
|
2476
2494
|
else if (ev.name === "home") this.detailScrollY = 0;
|
|
2477
|
-
else this.detailScrollY =
|
|
2495
|
+
else this.detailScrollY = max;
|
|
2478
2496
|
this.app.redraw(); return true;
|
|
2479
2497
|
}
|
|
2498
|
+
if(!this.customEditing&&ev.ctrl&&ev.name==="up"){this.detailScrollY=Math.max(0,this.detailScrollY-this.detailPage);return true;}
|
|
2499
|
+
if(!this.customEditing&&ev.ctrl&&ev.name==="down"){this.detailScrollY=Math.min(Math.max(0,(this.detailTotal??0)-this.detailPage),this.detailScrollY+this.detailPage);return true;}
|
|
2480
2500
|
if(this.customEditing&&ev.name==="left"){this.customCursor=Math.max(0,this.customCursor-1);return true;}
|
|
2481
2501
|
if(this.customEditing&&ev.name==="right"){this.customCursor=Math.min(Array.from(draft.custom).length,this.customCursor+1);return true;}
|
|
2482
2502
|
if(this.customEditing&&ev.name==="home"){this.customCursor=0;return true;}
|
|
@@ -2538,13 +2558,14 @@ export class ApprovalPopup extends Popup {
|
|
|
2538
2558
|
const maxW = Math.max(12, app.screen.w - 2);
|
|
2539
2559
|
const w = Math.min(72, maxW);
|
|
2540
2560
|
const command = app.chat?.toolCommandForCall?.(frame.callId) ?? null;
|
|
2541
|
-
const
|
|
2542
|
-
|
|
2543
|
-
];
|
|
2544
|
-
if (command) lines.push([{ t: "
|
|
2561
|
+
const wrap=(value,width)=>{const out=[];for(const raw of String(value??"").split("\n")){const gs=graphemes(raw);let line="",used=0;for(const g of gs){const gw=graphemeWidth(g);if(used+gw>width&&line){out.push(line);line="";used=0;}line+=g;used+=gw;}out.push(line);}return out;};
|
|
2562
|
+
const reason=frame.reason ?? `工具 ${frame.toolName ?? "tool"} 请求越权执行`;
|
|
2563
|
+
const lines = [[{t:" 请求原因",fg:K.DIM,underline:true}],...wrap(reason,w-6).map(line=>[{t:` ${line}`,fg:K.WARN}])];
|
|
2564
|
+
if (command) lines.push([{ t: " 将执行", fg: K.DIM, underline: true }], ...wrap(command,w-6).map((line) => [{ t: " " + line, fg: K.TXT }]));
|
|
2565
|
+
lines.push([{t:" 导航:↑/↓ 逐行 · PgUp/PgDn 翻页 · 滚轮 · ←/→ 选择",fg:K.FAINT}]);
|
|
2545
2566
|
super({
|
|
2546
|
-
x: Math.max(0, Math.floor((app.screen.w - w) / 2)), y: Math.max(0, Math.floor(app.screen.h
|
|
2547
|
-
w, h: Math.min(app.screen.h
|
|
2567
|
+
x: Math.max(0, Math.floor((app.screen.w - w) / 2)), y: Math.max(0, Math.floor((app.screen.h - Math.min(app.screen.h - 2, Math.max(10, Math.min(24, lines.length + 4)))) / 2)),
|
|
2568
|
+
w, h: Math.min(app.screen.h - 2, Math.max(10, Math.min(24, lines.length + 4))), title: "⚠ 工具需要授权 · 可滚动",
|
|
2548
2569
|
lines,
|
|
2549
2570
|
buttons: [
|
|
2550
2571
|
{ label: "允许一次", action: "allowed-once" },
|
|
@@ -3837,7 +3858,7 @@ export class App {
|
|
|
3837
3858
|
if (ev.ctrl && ev.key === "t") { this.setMode("trajectory"); return; }
|
|
3838
3859
|
if (ev.ctrl && ev.key === "e") { this.quickJumpStep(); return; }
|
|
3839
3860
|
if (ev.ctrl && ev.key === "j") { this.showJobs(); return; }
|
|
3840
|
-
if (ev.ctrl && ev.key === "
|
|
3861
|
+
if (ev.ctrl && ev.key === "n") { this.showQueue(); return; }
|
|
3841
3862
|
if (ev.ctrl && ev.key === "y") {
|
|
3842
3863
|
const next = busyEnter() === "queue" ? "steer" : "queue";
|
|
3843
3864
|
saveTuiConfig({ busyEnter: next });
|
|
@@ -4031,7 +4052,6 @@ export class App {
|
|
|
4031
4052
|
if (rawGoal && !["complete", "completed", "cleared"].includes(rawGoal.phase)) {
|
|
4032
4053
|
row0.left.push({ t: ` 🎯 ${truncate(rawGoal.objective ?? "目标", 14)} · Ctrl+G `, fg: T.SELFG, bg: T.WARN, bold: true });
|
|
4033
4054
|
}
|
|
4034
|
-
if (this.queueItems.length) row0.left.push({ t: ` ⏳队列 ${this.queueItems.length} (Ctrl+U) `, fg: T.SELFG, bg: T.WARN, bold: true });
|
|
4035
4055
|
if (this.sidebarVisible) row0.left.push({ t: " " + truncate(t || "(未选择会话)", 40) + " ", fg: T.TXT, bg: T.STATUSBG });
|
|
4036
4056
|
else row0.left.push({ t: " " + truncate(t || "(未选择会话)", 40) + " ", fg: T.TXT, bg: T.STATUSBG });
|
|
4037
4057
|
if (cur?.running) row0.left.push({ t: " ●运行 ", fg: T.OK, bg: T.STATUSBG });
|
|
@@ -4118,6 +4138,7 @@ export class App {
|
|
|
4118
4138
|
row2.left.push({ t: ` ${done}已完成${failed > 0 ? ` ${failed}失败` : ""} `, fg: done > 0 ? T.OK : T.FAINT, bg: T.STATUSBG });
|
|
4119
4139
|
row2.left.push({ t: ` ${subStats.running > 0 ? `${subStats.running} 个子代理运行中` : "没有子代理运行"} ${subStats.completed}已完成 `, fg: subStats.running > 0 ? T.PURPLE : T.FAINT, bg: T.STATUSBG, bold: subStats.running > 0 });
|
|
4120
4140
|
if (sub) row2.left.push({ t: ` 🛰 ${truncate(sub.label ?? sub.mode ?? "子代理", 20)} `, fg: T.PURPLE, bg: T.STATUSBG });
|
|
4141
|
+
if(this.queueItems.length)row2.left.push({t:` 有${this.queueItems.length}条命令正在排队 Ctrl+N查看详情 `,fg:T.SELFG,bg:T.WARN,bold:true});
|
|
4121
4142
|
row2.right.push({ t: " Ctrl+J 任务/子代理 ", fg: T.DIM, bg: T.STATUSBG });
|
|
4122
4143
|
rows.push(row2);
|
|
4123
4144
|
}
|
package/src/widgets.js
CHANGED
|
@@ -788,8 +788,8 @@ export class Popup extends Widget {
|
|
|
788
788
|
}
|
|
789
789
|
if (this.maxScroll() > 0) {
|
|
790
790
|
// "↑N" / "↓N" overflow indicators on the border row
|
|
791
|
-
if (this.scrollY > 0) screen.text(this.x + this.w -
|
|
792
|
-
if (this.scrollY < this.maxScroll()) screen.text(this.x + this.w -
|
|
791
|
+
if (this.scrollY > 0) screen.text(this.x + this.w - 10, this.y, ` ↑ ${this.scrollY}/${this.lines.length}`, { fg: T.ACCENT, bg: T.BG2 });
|
|
792
|
+
if (this.scrollY < this.maxScroll()) screen.text(this.x + this.w - 12, this.y + this.h - 1, ` ↓ ${this.scrollY + this.contentRows()}/${this.lines.length}`, { fg: T.ACCENT, bg: T.BG2 });
|
|
793
793
|
}
|
|
794
794
|
} else {
|
|
795
795
|
for (const line of this.lines) draw(line);
|
|
@@ -832,6 +832,8 @@ export class Popup extends Widget {
|
|
|
832
832
|
onKey(ev) {
|
|
833
833
|
if (ev.type !== "key") return false;
|
|
834
834
|
if (this.scrollable) {
|
|
835
|
+
if (ev.name === "up") { this.scrollY = Math.max(0, this.scrollY - 1); return true; }
|
|
836
|
+
if (ev.name === "down") { this.scrollY = Math.min(this.maxScroll(), this.scrollY + 1); return true; }
|
|
835
837
|
if (ev.name === "pgup") { this.scrollY = Math.max(0, this.scrollY - this.contentRows()); return true; }
|
|
836
838
|
if (ev.name === "pgdn") { this.scrollY = Math.min(this.maxScroll(), this.scrollY + this.contentRows()); return true; }
|
|
837
839
|
}
|