dsh-neotui 0.1.23 → 0.1.25
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 +93 -20
package/package.json
CHANGED
package/src/panels.js
CHANGED
|
@@ -1833,6 +1833,8 @@ export class ModelPanel extends Widget {
|
|
|
1833
1833
|
this.modelsSel = -1; // selected model row (shows its subfields)
|
|
1834
1834
|
this.draftRoute = null; // the un-saved new provider's route (shows the rename field)
|
|
1835
1835
|
this.editing = null; // { label, commit } while the inline editor is open
|
|
1836
|
+
this.sub = null; // the 模型管理 sub-buffer ({ cursor })
|
|
1837
|
+
this.subItems = [];
|
|
1836
1838
|
this.scanMode = false;
|
|
1837
1839
|
this.scanItems = [];
|
|
1838
1840
|
this.scanSel = new Set();
|
|
@@ -1875,6 +1877,22 @@ export class ModelPanel extends Widget {
|
|
|
1875
1877
|
items.push({ kind: "field", key: "displayName", label: "显示名", value: p.displayName ?? "" });
|
|
1876
1878
|
items.push({ kind: "field", key: "baseURL", label: "baseURL", value: p.baseURL ?? "" });
|
|
1877
1879
|
items.push({ kind: "field", key: "apiKeyEnv", label: "apiKeyEnv", value: p.apiKeyEnv ?? "", note: "环境变量名(密钥不落盘)" });
|
|
1880
|
+
// models are NOT flat here: one 模型管理 entry summarizing the first
|
|
1881
|
+
// five, which opens its own sub-buffer (scan on top, model form below)
|
|
1882
|
+
const models = p.models ?? [];
|
|
1883
|
+
const names = models.slice(0, 5).map((m) => m.id || "(未命名)").join(" · ");
|
|
1884
|
+
items.push({ kind: "button", label: "模型管理", sub: names + (models.length > 5 ? " · …" : ""), action: () => this.#openModels() });
|
|
1885
|
+
items.push({ kind: "button", label: "💾 保存配置", action: () => this.#save() });
|
|
1886
|
+
items.push({ kind: "button", label: "🗑 删除供应商", action: () => this.#deleteProvider() });
|
|
1887
|
+
return items;
|
|
1888
|
+
}
|
|
1889
|
+
/** The 模型管理 sub-buffer: scan first, then the model-info form rows. */
|
|
1890
|
+
#subItems() {
|
|
1891
|
+
const route = this.#route();
|
|
1892
|
+
if (route == null) return [];
|
|
1893
|
+
const p = this.#profile(route);
|
|
1894
|
+
const items = [];
|
|
1895
|
+
items.push({ kind: "button", label: "🔄 自动扫描可用模型(读 /models 端点)", action: () => this.#scan() });
|
|
1878
1896
|
const models = p.models ?? [];
|
|
1879
1897
|
for (let mi = 0; mi < models.length; mi++) {
|
|
1880
1898
|
const m = models[mi];
|
|
@@ -1888,12 +1906,15 @@ export class ModelPanel extends Widget {
|
|
|
1888
1906
|
}
|
|
1889
1907
|
items.push({ kind: "button", label: "+ 添加模型", action: () => this.#addModel() });
|
|
1890
1908
|
items.push({ kind: "button", label: "🗑 删除选中模型", action: () => this.#deleteModel() });
|
|
1891
|
-
items.push({ kind: "button", label: "🔄 自动扫描模型(读 /models 端点)", action: () => this.#scan() });
|
|
1892
1909
|
items.push({ kind: "button", label: "◉ 设为当前会话模型(选中模型)", action: () => this.#setDefaultModel() });
|
|
1893
|
-
items.push({ kind: "button", label: "💾 保存配置", action: () => this.#save() });
|
|
1894
|
-
items.push({ kind: "button", label: "🗑 删除供应商", action: () => this.#deleteProvider() });
|
|
1895
1910
|
return items;
|
|
1896
1911
|
}
|
|
1912
|
+
#openModels() {
|
|
1913
|
+
this.sub = { cursor: 0 };
|
|
1914
|
+
this.modelsSel = -1;
|
|
1915
|
+
this.#rebuild();
|
|
1916
|
+
this.app.redraw();
|
|
1917
|
+
}
|
|
1897
1918
|
#rebuild() {
|
|
1898
1919
|
// left list: the cursor is ALWAYS visible (● on the selected provider),
|
|
1899
1920
|
// and the row being edited shows ✎ — the mode only changes the color.
|
|
@@ -1930,11 +1951,16 @@ export class ModelPanel extends Widget {
|
|
|
1930
1951
|
formLines.push([{ t: " Enter 添加选中 · Esc 取消扫描", fg: K.FAINT }]);
|
|
1931
1952
|
this.formItems = [];
|
|
1932
1953
|
} else {
|
|
1933
|
-
this.
|
|
1954
|
+
const isSub = this.sub != null;
|
|
1955
|
+
const items = isSub ? this.#subItems() : this.#formRows();
|
|
1956
|
+
if (isSub) this.subItems = items;
|
|
1957
|
+
else this.formItems = items;
|
|
1934
1958
|
const w = Math.max(30, this.formView.w - 4);
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1959
|
+
const cursor = isSub ? this.sub.cursor : this.formIdx;
|
|
1960
|
+
if (isSub) formLines.push([{ t: ` 模型管理 — ${truncate(this.#profile(route).displayName || route, 30)} (Esc 返回)`, fg: K.ACCENT, bold: true }]);
|
|
1961
|
+
for (let i = 0; i < items.length; i++) {
|
|
1962
|
+
const it = items[i];
|
|
1963
|
+
const cur = i === cursor;
|
|
1938
1964
|
let t;
|
|
1939
1965
|
if (it.kind === "field") {
|
|
1940
1966
|
const v = it.value === "" || it.value == null ? "(空)" : String(it.value);
|
|
@@ -1946,8 +1972,12 @@ export class ModelPanel extends Widget {
|
|
|
1946
1972
|
t = ` ${cur ? "▸" : " "} ${it.label}`;
|
|
1947
1973
|
}
|
|
1948
1974
|
formLines.push([{ t: truncate(t, w), fg: cur ? T.SELFG : T.TXT, bg: cur ? T.MENUSEL : T.BG2 }]);
|
|
1975
|
+
// the 模型管理 preview: an indented, non-focusable summary line
|
|
1976
|
+
if (!isSub && it.kind === "button" && it.sub) {
|
|
1977
|
+
formLines.push([{ t: ` ${truncate(it.sub, w - 8)}`, fg: K.FAINT, bg: T.BG2 }]);
|
|
1978
|
+
}
|
|
1949
1979
|
}
|
|
1950
|
-
formLines.push([{ t: " ↑/↓ 移动 · Enter 编辑或执行 · ←
|
|
1980
|
+
formLines.push([{ t: isSub ? " ↑/↓ 移动 · Enter 编辑或执行 · Esc 返回" : " ↑/↓ 换供应商 · →/Tab 进表单(再按下移) · ← 上移/回列表 · Enter 编辑或执行 · Esc 退出", fg: K.FAINT }]);
|
|
1951
1981
|
}
|
|
1952
1982
|
this.formView.setLines(formLines);
|
|
1953
1983
|
}
|
|
@@ -1955,7 +1985,7 @@ export class ModelPanel extends Widget {
|
|
|
1955
1985
|
screen.fillRect(this.x, this.y, this.x + this.w - 1, this.y + this.h - 1, " ", {});
|
|
1956
1986
|
const mid = this.x + 26;
|
|
1957
1987
|
screen.vline(mid, this.y, this.y + this.h - 1, "│", { fg: T.BORDER });
|
|
1958
|
-
screen.text(this.x + 1, this.y, " 模型供应商
|
|
1988
|
+
screen.text(this.x + 1, this.y, " 模型供应商", { fg: K.DIM });
|
|
1959
1989
|
this.listView.render(screen);
|
|
1960
1990
|
this.formView.render(screen);
|
|
1961
1991
|
if (this.editing) {
|
|
@@ -1999,8 +2029,10 @@ export class ModelPanel extends Widget {
|
|
|
1999
2029
|
this.app.redraw();
|
|
2000
2030
|
return;
|
|
2001
2031
|
}
|
|
2002
|
-
// form
|
|
2003
|
-
const
|
|
2032
|
+
// form (or the 模型管理 sub-buffer — same item kinds, different source)
|
|
2033
|
+
const items = this.sub != null ? this.subItems : this.formItems;
|
|
2034
|
+
const idx = this.sub != null ? this.sub.cursor : this.formIdx;
|
|
2035
|
+
const it = items[idx];
|
|
2004
2036
|
if (!it) return;
|
|
2005
2037
|
const route = this.#route();
|
|
2006
2038
|
const p = this.#profile(route);
|
|
@@ -2194,21 +2226,56 @@ export class ModelPanel extends Widget {
|
|
|
2194
2226
|
if (ev.name === "enter") { this.#scanCommit(); return true; }
|
|
2195
2227
|
return false;
|
|
2196
2228
|
}
|
|
2197
|
-
if (
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
if (
|
|
2201
|
-
|
|
2229
|
+
if (this.sub != null) {
|
|
2230
|
+
// inside the 模型管理 sub-buffer: ↑/↓ walk its rows; Esc returns
|
|
2231
|
+
if (ev.name === "escape") { this.sub = null; this.#rebuild(); return true; }
|
|
2232
|
+
if (ev.name === "up" || (ev.name === "char" && ev.key === "k" && !ev.ctrl)) {
|
|
2233
|
+
this.sub.cursor = Math.max(0, this.sub.cursor - 1);
|
|
2234
|
+
this.app.redraw();
|
|
2235
|
+
return true;
|
|
2236
|
+
}
|
|
2237
|
+
if (ev.name === "down" || (ev.name === "char" && ev.key === "j" && !ev.ctrl)) {
|
|
2238
|
+
this.sub.cursor = Math.min(Math.max(0, this.#subItems().length - 1), this.sub.cursor + 1);
|
|
2239
|
+
this.app.redraw();
|
|
2240
|
+
return true;
|
|
2241
|
+
}
|
|
2242
|
+
if (ev.name === "enter") { this.#activateItem(); return true; }
|
|
2243
|
+
return false;
|
|
2202
2244
|
}
|
|
2245
|
+
if (ev.name === "escape") { this.editing = null; return false; } // App falls back to chat mode
|
|
2246
|
+
// ↑/↓ ALWAYS walk the provider column (the list is the primary axis —
|
|
2247
|
+
// UCAS and + 添加供应商 are both reachable by the arrow keys alone)
|
|
2203
2248
|
if (ev.name === "up" || (ev.name === "char" && ev.key === "k" && !ev.ctrl)) {
|
|
2204
|
-
|
|
2205
|
-
|
|
2249
|
+
this.sel = Math.max(0, this.sel - 1);
|
|
2250
|
+
this.mode = "list";
|
|
2251
|
+
this.#rebuild();
|
|
2206
2252
|
this.app.redraw();
|
|
2207
2253
|
return true;
|
|
2208
2254
|
}
|
|
2209
2255
|
if (ev.name === "down" || (ev.name === "char" && ev.key === "j" && !ev.ctrl)) {
|
|
2210
|
-
|
|
2211
|
-
|
|
2256
|
+
this.sel = Math.min(this.routes.length, this.sel + 1);
|
|
2257
|
+
this.mode = "list";
|
|
2258
|
+
this.#rebuild();
|
|
2259
|
+
this.app.redraw();
|
|
2260
|
+
return true;
|
|
2261
|
+
}
|
|
2262
|
+
// →/Tab: into the form, then down its rows; ←/Shift+Tab: up the form,
|
|
2263
|
+
// and at the top back to the provider list
|
|
2264
|
+
if (ev.name === "right" || (ev.name === "char" && ev.key === "l" && !ev.ctrl) || ev.name === "tab") {
|
|
2265
|
+
if (this.#route() != null) {
|
|
2266
|
+
if (this.mode === "form") this.formIdx = Math.min(Math.max(0, this.formItems.length - 1), this.formIdx + 1);
|
|
2267
|
+
else this.mode = "form";
|
|
2268
|
+
this.#rebuild();
|
|
2269
|
+
}
|
|
2270
|
+
this.app.redraw();
|
|
2271
|
+
return true;
|
|
2272
|
+
}
|
|
2273
|
+
if (ev.name === "left" || (ev.name === "char" && ev.key === "h" && !ev.ctrl) || ev.name === "backtab") {
|
|
2274
|
+
if (this.mode === "form") {
|
|
2275
|
+
if (this.formIdx > 0) this.formIdx--;
|
|
2276
|
+
else this.mode = "list";
|
|
2277
|
+
this.#rebuild();
|
|
2278
|
+
}
|
|
2212
2279
|
this.app.redraw();
|
|
2213
2280
|
return true;
|
|
2214
2281
|
}
|
|
@@ -2238,6 +2305,12 @@ export class ModelPanel extends Widget {
|
|
|
2238
2305
|
return false;
|
|
2239
2306
|
}
|
|
2240
2307
|
const idx = ev.y - this.formView.y + this.formView.scrollY;
|
|
2308
|
+
if (this.sub != null) {
|
|
2309
|
+
// the sub-buffer's title row is visual-only; rows below it are items
|
|
2310
|
+
const itemIdx = idx - 1;
|
|
2311
|
+
if (itemIdx >= 0 && itemIdx < this.#subItems().length) { this.sub.cursor = itemIdx; this.#activateItem(); return true; }
|
|
2312
|
+
return false;
|
|
2313
|
+
}
|
|
2241
2314
|
if (idx >= 0 && idx < this.formItems.length) { this.formIdx = idx; this.mode = "form"; this.#activateItem(); return true; }
|
|
2242
2315
|
return false;
|
|
2243
2316
|
}
|