dsh-neotui 0.1.22 → 0.1.23

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/panels.js +25 -17
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-neotui",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
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/panels.js CHANGED
@@ -112,7 +112,7 @@ export class Picker extends Widget {
112
112
  export function buildModelPicker(app) {
113
113
  const w = Math.min(70, app.screen.w - 4), h = Math.min(24, app.screen.h - 4);
114
114
  const manageProviders = {
115
- label: "⚙ 管理供应商…(填表式)", hint: "M",
115
+ label: "⚙ 管理供应商…", hint: "M",
116
116
  provider: null, model: null,
117
117
  action: () => { app.overlay = null; app.setMode("models"); },
118
118
  };
@@ -1640,9 +1640,9 @@ export class SettingsPanel extends Widget {
1640
1640
  ns: "默认展开/折叠", applies: "live", local: true,
1641
1641
  value: { 思考块默认展开: fd.think, 工具块默认展开: fd.bash, 任务清单默认显示: fd.todos },
1642
1642
  });
1643
- // the model provider manager opens its own big form buffer (CC Switch 式)
1643
+ // the model provider manager opens its own simple form buffer
1644
1644
  this.namespaces.splice(2, 0, {
1645
- ns: "模型供应商…(填表式)", applies: "live", local: true, modelsEntry: true,
1645
+ ns: "模型供应商…", applies: "live", local: true, modelsEntry: true,
1646
1646
  value: {},
1647
1647
  });
1648
1648
  this.selectNs(0);
@@ -1816,9 +1816,7 @@ export class SettingsPanel extends Widget {
1816
1816
  }
1817
1817
  }
1818
1818
 
1819
- // ---- Model provider management (CC Switch-style big form buffer) ----
1820
-
1821
- const MODEL_APIS = ["openai-completions", "anthropic-messages", "google-genai"];
1819
+ // ---- Model provider management (simple form buffer) ----
1822
1820
 
1823
1821
  export class ModelPanel extends Widget {
1824
1822
  constructor(app) {
@@ -1833,6 +1831,7 @@ export class ModelPanel extends Widget {
1833
1831
  this.formIdx = 0; // form item cursor
1834
1832
  this.formItems = []; // {kind:"field"|"model"|"button", ...}
1835
1833
  this.modelsSel = -1; // selected model row (shows its subfields)
1834
+ this.draftRoute = null; // the un-saved new provider's route (shows the rename field)
1836
1835
  this.editing = null; // { label, commit } while the inline editor is open
1837
1836
  this.scanMode = false;
1838
1837
  this.scanItems = [];
@@ -1871,9 +1870,9 @@ export class ModelPanel extends Widget {
1871
1870
  if (route == null) return [];
1872
1871
  const p = this.#profile(route);
1873
1872
  const items = [];
1874
- items.push({ kind: "field", key: "route", label: "路由名", value: route, editable: !(route in this.providers && this.routes.includes(route)) });
1873
+ // the route key only appears for a brand-new draft (rename once)
1874
+ if (this.draftRoute === route) items.push({ kind: "field", key: "route", label: "路由名", value: route });
1875
1875
  items.push({ kind: "field", key: "displayName", label: "显示名", value: p.displayName ?? "" });
1876
- items.push({ kind: "field", key: "api", label: "协议 api", value: p.api ?? "openai-completions", cycle: MODEL_APIS });
1877
1876
  items.push({ kind: "field", key: "baseURL", label: "baseURL", value: p.baseURL ?? "" });
1878
1877
  items.push({ kind: "field", key: "apiKeyEnv", label: "apiKeyEnv", value: p.apiKeyEnv ?? "", note: "环境变量名(密钥不落盘)" });
1879
1878
  const models = p.models ?? [];
@@ -1896,22 +1895,28 @@ export class ModelPanel extends Widget {
1896
1895
  return items;
1897
1896
  }
1898
1897
  #rebuild() {
1899
- // left list
1898
+ // left list: the cursor is ALWAYS visible (● on the selected provider),
1899
+ // and the row being edited shows ✎ — the mode only changes the color.
1900
1900
  const listLines = [];
1901
1901
  for (let i = 0; i < this.routes.length; i++) {
1902
1902
  const r = this.routes[i];
1903
1903
  const p = this.providers[r] ?? {};
1904
- const sel = i === this.sel && this.mode === "list";
1905
- listLines.push([{ t: ` ${sel ? "▸" : " "} ${truncate(p.displayName || r, 18)}`, fg: sel ? T.SELFG : T.TXT, bg: sel ? T.MENUSEL : T.BG2, bold: sel }]);
1906
- }
1907
- const addSel = this.sel === this.routes.length && this.mode === "list";
1908
- listLines.push([{ t: ` ${addSel ? "▸" : " "} + 添加供应商`, fg: addSel ? T.SELFG : T.ACCENT, bg: addSel ? T.MENUSEL : T.BG2, bold: true }]);
1904
+ const cur = i === this.sel;
1905
+ const editing = cur && this.mode === "form";
1906
+ listLines.push([{
1907
+ t: ` ${cur ? "●" : " "} ${truncate(p.displayName || r, 18)}${editing ? "" : ""}`,
1908
+ fg: cur ? T.SELFG : T.TXT, bg: cur ? (editing ? T.MENUSEL : T.SELBG) : T.BG2, bold: cur,
1909
+ }]);
1910
+ }
1911
+ const addCur = this.sel === this.routes.length;
1912
+ listLines.push([{ t: ` ${addCur ? "●" : " "} + 添加供应商`, fg: addCur ? T.SELFG : T.ACCENT, bg: addCur ? T.MENUSEL : T.BG2, bold: true }]);
1909
1913
  this.listView.setLines(listLines);
1910
1914
  // right form
1911
1915
  const route = this.#route();
1912
1916
  const formLines = [];
1913
1917
  if (route == null) {
1914
- formLines.push([{ t: " 选择左侧供应商,或“+ 添加供应商”新建(CC Switch 式填表)", fg: K.FAINT }]);
1918
+ formLines.push([{ t: " 左侧 ↑/↓ 选择供应商,Enter 打开编辑", fg: K.FAINT }]);
1919
+ formLines.push([{ t: " 把光标移到底部的“+ 添加供应商”回车即可新建", fg: K.FAINT }]);
1915
1920
  this.formItems = [];
1916
1921
  } else if (this.scanMode) {
1917
1922
  formLines.push([{ t: ` 扫描 ${truncate(this.#profile(route).baseURL ?? "", 44)} — 空格勾选,Enter 添加,↑/↓ 移动`, fg: K.ACCENT, bold: true }]);
@@ -1942,7 +1947,7 @@ export class ModelPanel extends Widget {
1942
1947
  }
1943
1948
  formLines.push([{ t: truncate(t, w), fg: cur ? T.SELFG : T.TXT, bg: cur ? T.MENUSEL : T.BG2 }]);
1944
1949
  }
1945
- formLines.push([{ t: " Enter 编辑/执行 · ← 回列表 · Esc 退出面板", fg: K.FAINT }]);
1950
+ formLines.push([{ t: " ↑/↓ 移动 · Enter 编辑或执行 · ← 回列表 · Esc 退出", fg: K.FAINT }]);
1946
1951
  }
1947
1952
  this.formView.setLines(formLines);
1948
1953
  }
@@ -1978,6 +1983,7 @@ export class ModelPanel extends Widget {
1978
1983
  let name = "新供应商", i = 2;
1979
1984
  while (this.providers[name] !== undefined) name = `新供应商${i++}`;
1980
1985
  this.providers[name] = { displayName: "", api: "openai-completions", baseURL: "", apiKeyEnv: "", models: [] };
1986
+ this.draftRoute = name;
1981
1987
  this.routes = Object.keys(this.providers);
1982
1988
  this.sel = this.routes.indexOf(name);
1983
1989
  this.mode = "form";
@@ -2083,6 +2089,7 @@ export class ModelPanel extends Widget {
2083
2089
  expectedRevision: this.revision,
2084
2090
  });
2085
2091
  this.revision = res?.revision ?? this.revision;
2092
+ this.draftRoute = null;
2086
2093
  this.app.toast(`已保存 ${Object.keys(this.providers).length} 个供应商`);
2087
2094
  } catch (e) { this.app.toast(`保存失败: ${e.message}`); }
2088
2095
  }
@@ -2215,7 +2222,8 @@ export class ModelPanel extends Widget {
2215
2222
  if (this.editing && this.input.inside(ev.x, ev.y)) return this.input.onMouse(ev);
2216
2223
  if (ev.x < this.x + 26) {
2217
2224
  const idx = ev.y - this.listView.y + this.listView.scrollY;
2218
- if (idx >= 0 && idx <= this.routes.length) { this.sel = idx; this.mode = "list"; this.#rebuild(); this.app.redraw(); return true; }
2225
+ // one click = select AND open (same as Enter)
2226
+ if (idx >= 0 && idx <= this.routes.length) { this.sel = idx; this.#activateItem(); return true; }
2219
2227
  return false;
2220
2228
  }
2221
2229
  if (this.scanMode) {