dsh-neotui 0.1.27 → 0.1.29
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 +72 -27
package/package.json
CHANGED
package/src/panels.js
CHANGED
|
@@ -1818,6 +1818,65 @@ 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
|
+
// the cursor starts at the END of the existing value: typing appends and
|
|
1843
|
+
// edits in place instead of wiping the original (modify, not replace)
|
|
1844
|
+
this.input.setValue(String(value ?? ""));
|
|
1845
|
+
this.#layout();
|
|
1846
|
+
}
|
|
1847
|
+
#layout() {
|
|
1848
|
+
const lines = [];
|
|
1849
|
+
lines.push([{ t: " 当前值预览:", fg: K.DIM, underline: true }]);
|
|
1850
|
+
const v = this.input.value;
|
|
1851
|
+
if (v === "") lines.push([{ t: "(空)", fg: K.FAINT }]);
|
|
1852
|
+
else for (const ln of v.split("\n").slice(0, 6)) lines.push([{ t: " " + truncate(ln, this.w - 6), fg: K.TXT }]);
|
|
1853
|
+
lines.push([{ t: "" }]);
|
|
1854
|
+
this.lines = lines;
|
|
1855
|
+
}
|
|
1856
|
+
render(screen) {
|
|
1857
|
+
super.render(screen);
|
|
1858
|
+
this.input.render(screen);
|
|
1859
|
+
}
|
|
1860
|
+
onKey(ev) {
|
|
1861
|
+
if (ev.type === "key" && ev.name === "escape") { this.app.closeOverlay(); this.app.focus(this.app.chat); return true; }
|
|
1862
|
+
if (ev.type === "key" && ev.name === "enter") {
|
|
1863
|
+
const v = this.input.value;
|
|
1864
|
+
this.app.closeOverlay();
|
|
1865
|
+
this.app.focus(this.app.chat);
|
|
1866
|
+
this.onCommit?.(v);
|
|
1867
|
+
return true;
|
|
1868
|
+
}
|
|
1869
|
+
const handled = this.input.onKey(ev);
|
|
1870
|
+
if (handled) this.#layout();
|
|
1871
|
+
this.app.redraw();
|
|
1872
|
+
return true;
|
|
1873
|
+
}
|
|
1874
|
+
onMouse(ev) {
|
|
1875
|
+
if (this.input.inside(ev.x, ev.y)) { this.input.onMouse(ev); this.app.redraw(); return true; }
|
|
1876
|
+
return super.onMouse(ev);
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1821
1880
|
export class ModelPanel extends Widget {
|
|
1822
1881
|
constructor(app) {
|
|
1823
1882
|
super({ x: 30, y: 0, w: app.screen.w - 30, h: app.screen.h - 1 });
|
|
@@ -1842,15 +1901,13 @@ export class ModelPanel extends Widget {
|
|
|
1842
1901
|
this.scanning = false;
|
|
1843
1902
|
const listW = 26;
|
|
1844
1903
|
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 取消" });
|
|
1904
|
+
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
1905
|
}
|
|
1848
1906
|
relayout(x, y, w, h) {
|
|
1849
1907
|
this.x = x; this.y = y; this.w = w; this.h = h;
|
|
1850
1908
|
const listW = 26;
|
|
1851
1909
|
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;
|
|
1910
|
+
this.formView.x = x + listW + 1; this.formView.y = y + 1; this.formView.w = w - listW - 2; this.formView.h = h - 2;
|
|
1854
1911
|
}
|
|
1855
1912
|
async load() {
|
|
1856
1913
|
try {
|
|
@@ -1875,6 +1932,7 @@ export class ModelPanel extends Widget {
|
|
|
1875
1932
|
// the route key only appears for a brand-new draft (rename once)
|
|
1876
1933
|
if (this.draftRoute === route) items.push({ kind: "field", key: "route", label: "路由名", value: route });
|
|
1877
1934
|
items.push({ kind: "field", key: "displayName", label: "显示名", value: p.displayName ?? "" });
|
|
1935
|
+
items.push({ kind: "field", key: "api", label: "协议 api", value: p.api ?? "openai-completions" });
|
|
1878
1936
|
items.push({ kind: "field", key: "baseURL", label: "baseURL", value: p.baseURL ?? "" });
|
|
1879
1937
|
items.push({ kind: "field", key: "apiKeyEnv", label: "apiKeyEnv", value: p.apiKeyEnv ?? "", note: "环境变量名(密钥不落盘)" });
|
|
1880
1938
|
// models are NOT flat here: one 模型管理 entry summarizing the first
|
|
@@ -1988,22 +2046,17 @@ export class ModelPanel extends Widget {
|
|
|
1988
2046
|
screen.text(this.x + 1, this.y, " 模型供应商", { fg: K.DIM });
|
|
1989
2047
|
this.listView.render(screen);
|
|
1990
2048
|
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
2049
|
}
|
|
1996
2050
|
#startEdit(label, value, commit) {
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
this.app
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
this.
|
|
2005
|
-
|
|
2006
|
-
this.#rebuild();
|
|
2051
|
+
// a REAL standalone edit buffer in the middle of the window: own caret,
|
|
2052
|
+
// isolated from NORMAL/INSERT routing, paste supported
|
|
2053
|
+
const popup = new EditPopup(this.app, {
|
|
2054
|
+
title: `编辑 ${label}`,
|
|
2055
|
+
value,
|
|
2056
|
+
onCommit: (text) => { commit(text); this.#rebuild(); this.app.redraw(); },
|
|
2057
|
+
});
|
|
2058
|
+
this.app.overlay = popup;
|
|
2059
|
+
this.app.focus(popup.input);
|
|
2007
2060
|
this.app.redraw();
|
|
2008
2061
|
}
|
|
2009
2062
|
#activateItem() {
|
|
@@ -2206,13 +2259,6 @@ export class ModelPanel extends Widget {
|
|
|
2206
2259
|
}
|
|
2207
2260
|
onKey(ev) {
|
|
2208
2261
|
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
2262
|
if (this.scanMode) {
|
|
2217
2263
|
if (ev.name === "escape") { this.scanMode = false; this.#rebuild(); return true; }
|
|
2218
2264
|
if (ev.name === "up") { this.scanCursor = Math.max(0, this.scanCursor - 1); this.#rebuild(); this.app.redraw(); return true; }
|
|
@@ -2245,7 +2291,7 @@ export class ModelPanel extends Widget {
|
|
|
2245
2291
|
if (ev.name === "enter") { this.#activateItem(); return true; }
|
|
2246
2292
|
return false;
|
|
2247
2293
|
}
|
|
2248
|
-
if (ev.name === "escape") {
|
|
2294
|
+
if (ev.name === "escape") { return false; } // App falls back to chat mode
|
|
2249
2295
|
// dual-focus navigation: ↑/↓ move the cursor INSIDE the focused region —
|
|
2250
2296
|
// the provider column in list focus, the option rows in form focus.
|
|
2251
2297
|
// → enters the form, ← returns to the list.
|
|
@@ -2280,7 +2326,6 @@ export class ModelPanel extends Widget {
|
|
|
2280
2326
|
if (ev.kind === "wheel-up") { this.formView.scroll(-3); return true; }
|
|
2281
2327
|
if (ev.kind === "wheel-down") { this.formView.scroll(3); return true; }
|
|
2282
2328
|
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
2329
|
if (ev.x < this.x + 26) {
|
|
2285
2330
|
const idx = ev.y - this.listView.y + this.listView.scrollY;
|
|
2286
2331
|
// one click = select AND open (same as Enter)
|