dsh-neotui 0.2.0 → 0.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.1 — 2026-08-16
4
+
5
+ ### Fixed
6
+
7
+ - AskUser 问题始终显示“输入自己的回答”和常驻输入栏。
8
+ - “跳过此问题”改为真实、可导航的列表项,不再使用重复的底部按钮。
9
+ - 自定义回答支持左右键、Home、End、Backspace、Delete 和光标位置插入。
10
+ - 鼠标只在选项实际文字区域内生效,同行空白区域不再误提交。
11
+ - 自定义输入栏固定在自定义回答项下一行,跳过项固定在输入栏下方。
12
+
3
13
  ## 0.2.0 — 2026-08-16
4
14
 
5
15
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-neotui",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Neo-TUI: mouse-driven terminal UI client for DeepSeek Harness (B-tier per dsh-tui-design.md)",
5
5
  "type": "module",
6
6
  "bin": {
package/src/views.js CHANGED
@@ -2356,10 +2356,7 @@ export class QuestionPopup extends Popup {
2356
2356
  x: Math.max(0, Math.floor((app.screen.w - w) / 2)), y: Math.max(0, Math.floor((app.screen.h - h) / 2)),
2357
2357
  w, h, title: planReview ? "✎ 计划审阅" : "❓ 需要你的回答",
2358
2358
  lines: ["", ...questions.map((q) => q.question ?? q.id)],
2359
- buttons: planReview ? [] : [
2360
- { label: "回答…", action: "custom" },
2361
- { label: "跳过", action: "cancel" },
2362
- ],
2359
+ buttons: [],
2363
2360
  });
2364
2361
  this.app = app;
2365
2362
  this.frame = frame;
@@ -2368,14 +2365,13 @@ export class QuestionPopup extends Popup {
2368
2365
  this.detailScrollY = 0;
2369
2366
  this.detailPage = 1;
2370
2367
  this.optionRows = [];
2368
+ this.optionHitboxes = [];
2371
2369
  this.questionIdx = 0;
2372
2370
  this.drafts = questions.map(() => ({ selected: [], custom: "", skipped: false }));
2373
2371
  this.selIdx = 0;
2374
- this.onAction = (btn) => {
2375
- if (btn.action === "cancel") this.#skipCurrent();
2376
- else if (btn.action === "custom") this.#continueCurrent();
2377
- else if (btn.action === "__cancel__") this.#cancel();
2378
- };
2372
+ this.customEditing = false;
2373
+ this.customCursor = 0;
2374
+ this.onAction = (btn) => { if (btn.action === "__cancel__") this.#cancel(); };
2379
2375
  this.#layout();
2380
2376
  }
2381
2377
 
@@ -2410,16 +2406,26 @@ export class QuestionPopup extends Popup {
2410
2406
  screen.text(this.x + 2, ly++, `计划 ${start}–${end} / ${detailLines.length} 行 · PgUp/PgDn/Home/End/滚轮`, { fg: K.FAINT });
2411
2407
  }
2412
2408
  }
2413
- this.optionRows = [];
2409
+ this.optionRows = []; this.optionHitboxes = [];
2414
2410
  for (let i = 0; i < opts.length && ly < this.y + this.h - 3; i++) {
2415
2411
  this.optionRows[i] = ly;
2416
2412
  const chosen = draft.selected.includes(opts[i].label);
2417
2413
  const cursor = this.selIdx === i;
2418
2414
  const glyph = q.multiSelect ? (chosen ? "☑" : "☐") : (chosen ? "●" : "○");
2419
- screen.text(this.x + 2, ly++, truncate(` ${cursor ? "▸" : " "} ${glyph} ${opts[i].label}`, this.w - 6), { fg: cursor ? T.SELFG : K.TXT, bg: cursor ? T.MENUSEL : -1 });
2415
+ const optionText=truncate(` ${cursor ? "▸" : " "} ${glyph} ${opts[i].label}`, this.w - 6);
2416
+ this.optionHitboxes[i]={x1:this.x+2,x2:this.x+2+strWidth(optionText)-1,y1:ly,y2:ly+(opts[i].description?1:0)};
2417
+ screen.text(this.x + 2, ly++, optionText, { fg: cursor ? T.SELFG : K.TXT, bg: cursor ? T.MENUSEL : -1 });
2420
2418
  if (opts[i].description && ly < this.y + this.h - 3) screen.text(this.x + 7, ly++, truncate(opts[i].description, this.w - 10), { fg: K.FAINT });
2421
2419
  }
2422
- if (draft.custom) screen.text(this.x + 2, Math.min(ly, this.y + this.h - 3), truncate(`自定义: ${draft.custom}`, this.w - 4), { fg: K.ACCENT });
2420
+ if (!this.planReview && ly < this.y + this.h - 3) {
2421
+ this.optionRows[opts.length] = ly;
2422
+ const cursor = this.selIdx === opts.length;
2423
+ const customText=truncate(` ${cursor ? "▸" : " "} ✎ 输入自己的回答`, this.w - 6);
2424
+ this.optionHitboxes[opts.length]={x1:this.x+2,x2:this.x+2+strWidth(customText)-1,y1:ly,y2:ly};
2425
+ screen.text(this.x + 2, ly++, customText, { fg: cursor ? T.SELFG : K.ACCENT, bg: cursor ? T.MENUSEL : -1 });
2426
+ if(ly<this.y+this.h-3){const chars=Array.from(draft.custom),shown=this.customEditing?[...chars.slice(0,this.customCursor),"▏",...chars.slice(this.customCursor)].join(""):draft.custom;const inputText=truncate(` > ${shown||"在此输入…"}`,this.w-8);screen.text(this.x+2,ly++,inputText,{fg:this.customEditing?K.TXT:K.FAINT,bg:this.customEditing?T.BG2:-1});}
2427
+ if(ly<this.y+this.h-3){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
+ }
2423
2429
  }
2424
2430
 
2425
2431
  #choose(i) {
@@ -2442,13 +2448,16 @@ export class QuestionPopup extends Popup {
2442
2448
  if (ev.kind === "press" && ev.button === 0) {
2443
2449
  const q = this.questions[this.questionIdx];
2444
2450
  for (let i = 0; i < (q?.options?.length ?? 0); i++) {
2445
- const ly = this.optionRows[i];
2446
- if (ly != null && (ev.y === ly || (q.options[i].description && ev.y === ly + 1))) {
2447
- this.selIdx = i; this.#choose(i);
2451
+ const box=this.optionHitboxes[i];
2452
+ if (box && ev.x>=box.x1 && ev.x<=box.x2 && ev.y>=box.y1 && ev.y<=box.y2) {
2453
+ this.selIdx = i; this.customEditing = false; this.#choose(i);
2448
2454
  if (!q.multiSelect) this.#continueCurrent();
2449
2455
  return true;
2450
2456
  }
2451
2457
  }
2458
+ const count=q?.options?.length??0,customBox=this.optionHitboxes[count];
2459
+ if(customBox&&ev.x>=customBox.x1&&ev.x<=customBox.x2&&ev.y===customBox.y1){this.selIdx=count;this.customEditing=true;this.customCursor=Array.from(this.drafts[this.questionIdx].custom).length;this.drafts[this.questionIdx].selected=[];this.app.redraw();return true;}
2460
+ const skipBox=this.optionHitboxes[count+1];if(skipBox&&ev.x>=skipBox.x1&&ev.x<=skipBox.x2&&ev.y===skipBox.y1){this.selIdx=count+1;this.#skipCurrent();return true;}
2452
2461
  }
2453
2462
  return super.onMouse(ev);
2454
2463
  }
@@ -2456,9 +2465,10 @@ export class QuestionPopup extends Popup {
2456
2465
  onKey(ev) {
2457
2466
  const q = this.questions[this.questionIdx];
2458
2467
  const draft = this.drafts[this.questionIdx];
2459
- if (ev.type === "text") { draft.custom += ev.text; draft.skipped = false; return true; }
2460
- if (ev.type !== "key") return false;
2461
2468
  const count = q?.options?.length ?? 0;
2469
+ const choices = count + (this.planReview ? 0 : 2);
2470
+ 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
+ if (ev.type !== "key") return false;
2462
2472
  if (this.planReview && ["pageup", "pagedown", "home", "end"].includes(ev.name)) {
2463
2473
  const total = String(q?.detail ?? "").split("\n").length;
2464
2474
  if (ev.name === "pageup") this.detailScrollY = Math.max(0, this.detailScrollY - this.detailPage);
@@ -2467,11 +2477,19 @@ export class QuestionPopup extends Popup {
2467
2477
  else this.detailScrollY = Math.max(0, total - this.detailPage);
2468
2478
  this.app.redraw(); return true;
2469
2479
  }
2470
- if (ev.name === "up") { this.selIdx = Math.max(0, this.selIdx - 1); return true; }
2471
- if (ev.name === "down") { this.selIdx = Math.min(Math.max(0, count - 1), this.selIdx + 1); return true; }
2472
- if (ev.name === "char" && ev.key === " " && count) { this.#choose(this.selIdx); return true; }
2473
- if (ev.name === "backspace" && draft.custom) { draft.custom = Array.from(draft.custom).slice(0, -1).join(""); return true; }
2480
+ if(this.customEditing&&ev.name==="left"){this.customCursor=Math.max(0,this.customCursor-1);return true;}
2481
+ if(this.customEditing&&ev.name==="right"){this.customCursor=Math.min(Array.from(draft.custom).length,this.customCursor+1);return true;}
2482
+ if(this.customEditing&&ev.name==="home"){this.customCursor=0;return true;}
2483
+ if(this.customEditing&&ev.name==="end"){this.customCursor=Array.from(draft.custom).length;return true;}
2484
+ if (ev.name === "up") { this.customEditing=false;this.selIdx = Math.max(0, this.selIdx - 1); return true; }
2485
+ if (ev.name === "down") { this.customEditing=false;this.selIdx = Math.min(Math.max(0, choices - 1), this.selIdx + 1); return true; }
2486
+ if (ev.name === "char" && ev.key === " " && count && this.selIdx<count) { this.#choose(this.selIdx); return true; }
2487
+ if (ev.name === "backspace" && this.customEditing && this.customCursor>0) {const chars=Array.from(draft.custom);chars.splice(this.customCursor-1,1);draft.custom=chars.join("");this.customCursor--;return true;}
2488
+ if(ev.name==="delete"&&this.customEditing){const chars=Array.from(draft.custom);if(this.customCursor<chars.length){chars.splice(this.customCursor,1);draft.custom=chars.join("");}return true;}
2474
2489
  if (ev.name === "enter") {
2490
+ if(!this.planReview&&this.selIdx===count+1){this.#skipCurrent();return true;}
2491
+ if(!this.planReview&&this.selIdx===count&&!this.customEditing){this.customEditing=true;this.customCursor=Array.from(draft.custom).length;draft.selected=[];return true;}
2492
+ if(this.customEditing){if(!draft.custom.trim()){this.app.toast("请输入自己的回答");return true;}this.#continueCurrent();return true;}
2475
2493
  if (count && draft.selected.length === 0 && !draft.custom) this.#choose(this.selIdx);
2476
2494
  this.#continueCurrent(); return true;
2477
2495
  }
@@ -2491,7 +2509,7 @@ export class QuestionPopup extends Popup {
2491
2509
  }
2492
2510
 
2493
2511
  #advanceOrSubmit() {
2494
- if (this.questionIdx < this.questions.length - 1) { this.questionIdx++; this.selIdx = 0; this.detailScrollY = 0; this.#layout(); this.app.redraw(); return; }
2512
+ if (this.questionIdx < this.questions.length - 1) { this.questionIdx++; this.selIdx = 0; this.customEditing=false;this.customCursor=0;this.detailScrollY = 0; this.#layout(); this.app.redraw(); return; }
2495
2513
  this.#submit();
2496
2514
  }
2497
2515