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 +10 -0
- package/package.json +1 -1
- package/src/views.js +40 -22
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
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:
|
|
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.
|
|
2375
|
-
|
|
2376
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
|
2446
|
-
if (
|
|
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
|
|
2471
|
-
if
|
|
2472
|
-
if
|
|
2473
|
-
if
|
|
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
|
|