dsh-neotui 0.1.27 → 0.1.28
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/package.json +1 -1
- package/src/panels.js +69 -27
package/package.json
CHANGED
package/src/panels.js
CHANGED
|
@@ -1818,6 +1818,63 @@ export class SettingsPanel extends Widget {
|
|
|
1818
1818
|
|
|
1819
1819
|
// ---- Model provider management (simple form buffer) ----
|
|
1820
1820
|
|
|
1821
|
+
/** A centered standalone edit buffer (ControlPanel-style): its own Input with
|
|
1822
|
+
* a visible caret, full key isolation from NORMAL/INSERT routing, and paste
|
|
1823
|
+
* support — Enter commits, Esc cancels. */
|
|
1824
|
+
export class EditPopup extends Popup {
|
|
1825
|
+
constructor(app, { title, value, onCommit }) {
|
|
1826
|
+
const w = Math.min(80, app.screen.w - 8);
|
|
1827
|
+
const h = Math.min(16, app.screen.h - 6);
|
|
1828
|
+
super({
|
|
1829
|
+
x: Math.floor((app.screen.w - w) / 2), y: Math.floor((app.screen.h - h) / 2),
|
|
1830
|
+
w, h, title,
|
|
1831
|
+
lines: [],
|
|
1832
|
+
buttons: [],
|
|
1833
|
+
onAction: () => {},
|
|
1834
|
+
});
|
|
1835
|
+
this.app = app;
|
|
1836
|
+
this.onCommit = onCommit;
|
|
1837
|
+
this.input = new Input({
|
|
1838
|
+
x: this.x + 2, y: this.y + h - 2, w: w - 4, h: 1,
|
|
1839
|
+
multi: true, maxLines: 4, app,
|
|
1840
|
+
prompt: "> ", placeholder: "输入值…(Ctrl+Shift+V 粘贴,Enter 确定,Esc 取消)",
|
|
1841
|
+
});
|
|
1842
|
+
this.input.setValue(String(value ?? ""), { select: true });
|
|
1843
|
+
this.#layout();
|
|
1844
|
+
}
|
|
1845
|
+
#layout() {
|
|
1846
|
+
const lines = [];
|
|
1847
|
+
lines.push([{ t: " 当前值预览:", fg: K.DIM, underline: true }]);
|
|
1848
|
+
const v = this.input.value;
|
|
1849
|
+
if (v === "") lines.push([{ t: "(空)", fg: K.FAINT }]);
|
|
1850
|
+
else for (const ln of v.split("\n").slice(0, 6)) lines.push([{ t: " " + truncate(ln, this.w - 6), fg: K.TXT }]);
|
|
1851
|
+
lines.push([{ t: "" }]);
|
|
1852
|
+
this.lines = lines;
|
|
1853
|
+
}
|
|
1854
|
+
render(screen) {
|
|
1855
|
+
super.render(screen);
|
|
1856
|
+
this.input.render(screen);
|
|
1857
|
+
}
|
|
1858
|
+
onKey(ev) {
|
|
1859
|
+
if (ev.type === "key" && ev.name === "escape") { this.app.closeOverlay(); this.app.focus(this.app.chat); return true; }
|
|
1860
|
+
if (ev.type === "key" && ev.name === "enter") {
|
|
1861
|
+
const v = this.input.value;
|
|
1862
|
+
this.app.closeOverlay();
|
|
1863
|
+
this.app.focus(this.app.chat);
|
|
1864
|
+
this.onCommit?.(v);
|
|
1865
|
+
return true;
|
|
1866
|
+
}
|
|
1867
|
+
const handled = this.input.onKey(ev);
|
|
1868
|
+
if (handled) this.#layout();
|
|
1869
|
+
this.app.redraw();
|
|
1870
|
+
return true;
|
|
1871
|
+
}
|
|
1872
|
+
onMouse(ev) {
|
|
1873
|
+
if (this.input.inside(ev.x, ev.y)) { this.input.onMouse(ev); this.app.redraw(); return true; }
|
|
1874
|
+
return super.onMouse(ev);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1821
1878
|
export class ModelPanel extends Widget {
|
|
1822
1879
|
constructor(app) {
|
|
1823
1880
|
super({ x: 30, y: 0, w: app.screen.w - 30, h: app.screen.h - 1 });
|
|
@@ -1842,15 +1899,13 @@ export class ModelPanel extends Widget {
|
|
|
1842
1899
|
this.scanning = false;
|
|
1843
1900
|
const listW = 26;
|
|
1844
1901
|
this.listView = new ScrollView({ x: this.x + 1, y: this.y + 1, w: listW, h: this.h - 2, showScrollbar: true });
|
|
1845
|
-
this.formView = new ScrollView({ x: this.x + listW + 1, y: this.y + 1, w: this.w - listW - 2, h: this.h -
|
|
1846
|
-
this.input = new Input({ x: this.x + listW + 1, y: this.y + this.h - 2, w: this.w - listW - 2, h: 1, prompt: "值: ", placeholder: "Enter 提交 · Esc 取消" });
|
|
1902
|
+
this.formView = new ScrollView({ x: this.x + listW + 1, y: this.y + 1, w: this.w - listW - 2, h: this.h - 2, showScrollbar: true });
|
|
1847
1903
|
}
|
|
1848
1904
|
relayout(x, y, w, h) {
|
|
1849
1905
|
this.x = x; this.y = y; this.w = w; this.h = h;
|
|
1850
1906
|
const listW = 26;
|
|
1851
1907
|
this.listView.x = x + 1; this.listView.y = y + 1; this.listView.w = listW; this.listView.h = h - 2;
|
|
1852
|
-
this.formView.x = x + listW + 1; this.formView.y = y + 1; this.formView.w = w - listW - 2; this.formView.h = h -
|
|
1853
|
-
this.input.x = x + listW + 1; this.input.y = y + h - 2; this.input.w = w - listW - 2;
|
|
1908
|
+
this.formView.x = x + listW + 1; this.formView.y = y + 1; this.formView.w = w - listW - 2; this.formView.h = h - 2;
|
|
1854
1909
|
}
|
|
1855
1910
|
async load() {
|
|
1856
1911
|
try {
|
|
@@ -1988,22 +2043,17 @@ export class ModelPanel extends Widget {
|
|
|
1988
2043
|
screen.text(this.x + 1, this.y, " 模型供应商", { fg: K.DIM });
|
|
1989
2044
|
this.listView.render(screen);
|
|
1990
2045
|
this.formView.render(screen);
|
|
1991
|
-
if (this.editing) {
|
|
1992
|
-
screen.text(this.x + 28, this.y + this.h - 3, `编辑 ${this.editing.label}`, { fg: K.WARN, bold: true });
|
|
1993
|
-
this.input.render(screen);
|
|
1994
|
-
}
|
|
1995
2046
|
}
|
|
1996
2047
|
#startEdit(label, value, commit) {
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
this.app
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
this.
|
|
2005
|
-
|
|
2006
|
-
this.#rebuild();
|
|
2048
|
+
// a REAL standalone edit buffer in the middle of the window: own caret,
|
|
2049
|
+
// isolated from NORMAL/INSERT routing, paste supported
|
|
2050
|
+
const popup = new EditPopup(this.app, {
|
|
2051
|
+
title: `编辑 ${label}`,
|
|
2052
|
+
value,
|
|
2053
|
+
onCommit: (text) => { commit(text); this.#rebuild(); this.app.redraw(); },
|
|
2054
|
+
});
|
|
2055
|
+
this.app.overlay = popup;
|
|
2056
|
+
this.app.focus(popup.input);
|
|
2007
2057
|
this.app.redraw();
|
|
2008
2058
|
}
|
|
2009
2059
|
#activateItem() {
|
|
@@ -2206,13 +2256,6 @@ export class ModelPanel extends Widget {
|
|
|
2206
2256
|
}
|
|
2207
2257
|
onKey(ev) {
|
|
2208
2258
|
if (ev.type !== "key") return false;
|
|
2209
|
-
if (this.editing) {
|
|
2210
|
-
if (ev.name === "escape") { this.editing = null; this.#rebuild(); return true; }
|
|
2211
|
-
if (ev.name === "enter") { this.#commitEdit(); return true; }
|
|
2212
|
-
const handled = this.input.onKey(ev);
|
|
2213
|
-
if (handled) this.app.redraw();
|
|
2214
|
-
return true;
|
|
2215
|
-
}
|
|
2216
2259
|
if (this.scanMode) {
|
|
2217
2260
|
if (ev.name === "escape") { this.scanMode = false; this.#rebuild(); return true; }
|
|
2218
2261
|
if (ev.name === "up") { this.scanCursor = Math.max(0, this.scanCursor - 1); this.#rebuild(); this.app.redraw(); return true; }
|
|
@@ -2245,7 +2288,7 @@ export class ModelPanel extends Widget {
|
|
|
2245
2288
|
if (ev.name === "enter") { this.#activateItem(); return true; }
|
|
2246
2289
|
return false;
|
|
2247
2290
|
}
|
|
2248
|
-
if (ev.name === "escape") {
|
|
2291
|
+
if (ev.name === "escape") { return false; } // App falls back to chat mode
|
|
2249
2292
|
// dual-focus navigation: ↑/↓ move the cursor INSIDE the focused region —
|
|
2250
2293
|
// the provider column in list focus, the option rows in form focus.
|
|
2251
2294
|
// → enters the form, ← returns to the list.
|
|
@@ -2280,7 +2323,6 @@ export class ModelPanel extends Widget {
|
|
|
2280
2323
|
if (ev.kind === "wheel-up") { this.formView.scroll(-3); return true; }
|
|
2281
2324
|
if (ev.kind === "wheel-down") { this.formView.scroll(3); return true; }
|
|
2282
2325
|
if (ev.kind !== "press" || ev.button !== 0) return false;
|
|
2283
|
-
if (this.editing && this.input.inside(ev.x, ev.y)) return this.input.onMouse(ev);
|
|
2284
2326
|
if (ev.x < this.x + 26) {
|
|
2285
2327
|
const idx = ev.y - this.listView.y + this.listView.scrollY;
|
|
2286
2328
|
// one click = select AND open (same as Enter)
|