dsh-composer-ux 0.14.0 → 0.14.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 CHANGED
@@ -2,6 +2,34 @@
2
2
 
3
3
  本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## [0.14.1] — 2026-09-30(模型下拉能选了 + 面板内容能滚了)
6
+
7
+ 起因(用户原话):**「跟这个一样 点开指定模型之后可以选择」**「**我点完优化模型之后,看不到下边呀,给他安个滑动条**」。
8
+
9
+ ### 1. 「模型」那一行换成真下拉(按 provider 分组、长了自己滚)
10
+
11
+ 以前是「跟随会话 / 指定」两个按钮 + 一个要你自己写 `provider/model` 的输入框 —— 得记住路由名。
12
+ 现在点开就是清单:第一项 **跟随会话模型**(并显示会话当前用的那条),下面按 provider 分组列出
13
+ 可用模型(组标题吸顶),选中即写回设置。清单长得装不下时**自己滚**(限高 260px),
14
+ 不会把卡片顶出面板。最后一项留了「手动输入 provider/model…」,清单读不到时不至于没路走。
15
+
16
+ 清单从哪来:新增只读路由 `GET /composer-ux/models`(客户端拿不到 `llm` 服务,只有宿主知道
17
+ `listProviders()` + `listModels()`)。三条纪律:**单个 provider 读不到只让那一组空着并写出原因**
18
+ (一个坏端点不该废掉整个下拉)· 60 秒缓存(下拉打开很频繁,`?fresh=1` 绕过,界面上就是「重试」)·
19
+ 永不抛(坏掉也要给客户端一句能显示的话)。
20
+
21
+ ### 2. 面板内容区能滚了(「看不到下边」的那条)
22
+
23
+ `quickSectionBody` 以前是 `overflow: hidden` —— 卡片(七行 + 详情 + 提示词框)比面板还高时,
24
+ 下面的内容就被面板底部裁掉、够不到。现在纵轴给滑动条(`overflow-y: auto` + `overscroll-behavior: contain`),
25
+ 横轴仍不滚。
26
+
27
+ ## 验证
28
+
29
+ - `npm test`:**2293 passed / 0 failed**(22 个套件)。新增 §5m:分组、单点失败隔离、缓存与 `?fresh=1`、非 GET 405。
30
+ - 变异测试:**155/155 全部被咬住**(新增 FB「一个坏端点废掉整个下拉」、FC「面板内容区又不可滚」)。
31
+ - 测试抓到一个真 bug 并修掉:`catch (error)` 遮蔽了外层那个 `let error` ⇒ 单个 provider 的失败原因永远写不进去。
32
+ - `typecheck` 0 错误。
5
33
  ## [0.14.0] — 2026-09-30(照 dsh-prompt-optimizer 移植:优化选项卡片 + 它的提示词与本体)
6
34
 
7
35
  起因(用户原话):**「https://github.com/WestFox-AwA/dsh-prompt-optimizer 全面模仿他的 UI 和 UI 中的所有功能,效果是与快捷指令部分 ui 拼接在一起,也模仿他的内部提示词」**。
package/README.md CHANGED
@@ -506,7 +506,7 @@ Windows 上 DSH 给模型的终端工具是 **PowerShell**(工具名 `pwsh`)
506
506
  | 工具循环的消息形状 / 轮次封顶 / 异常必须降级 | 同上第 6 节(假 llm 驱动整条循环,逐条检查 `role:'tool'` 与 `toolCallId`) |
507
507
  | 工具路径:默认关、开了才派、**查完吐散文必须回落**、拿不到工作目录就不派 | `test/quick-commands.mjs` 5g 节(13 条,真路由 + 假 llm) |
508
508
  | 工具读到的内容不进台账 | 同 5g 节:断言台账里只有工具名与次数、没有读到的文件内容 |
509
- | 变异守卫真的会咬人,且**必须是指定的那条测试变红** | `npm run test:mutations`:**153 条全部咬住**(判据是 `red.some(line => line.includes(item.expect))`,不是"有红就行") |
509
+ | 变异守卫真的会咬人,且**必须是指定的那条测试变红** | `npm run test:mutations`:**155 条全部咬住**(判据是 `red.some(line => line.includes(item.expect))`,不是"有红就行") |
510
510
  | 发布门禁三条判据 | `npm run gates` 全过;`check:tag` 实测 `v0.12.0` 的 tag 名与所指提交里的版本一致 |
511
511
  | 三平台 CI | GitHub Actions:ubuntu / windows / macos × Node 20(`npm ci → typecheck → build → test → check:pack → check:docs`) |
512
512
 
@@ -639,9 +639,9 @@ node build.mjs # 产出 lib/index.js + lib/client.js
639
639
  # ⚠️ 宿主半会**内联** schemastery / cosmokit:
640
640
  # 优先用 DSH 检出里的 vendor 副本,检出不在时退到 node_modules
641
641
  # 里同版本的 npm 包(两者逐字节相同)——CI 上走的就是退路
642
- npm test # 22 个套件;当前 2282 passed, 0 failed(2026-09-29 实测;CI 三平台同样全绿)
642
+ npm test # 22 个套件;当前 2293 passed, 0 failed(2026-09-29 实测;CI 三平台同样全绿)
643
643
  # 走 scripts/run-tests.mjs:顺带把"多少套件/多少条"记进 test/.last-run.json
644
- npm run test:mutations # 手动跑:变异测试,证明那套护栏真的在咬人(153 条,须单独跑)
644
+ npm run test:mutations # 手动跑:变异测试,证明那套护栏真的在咬人(155 条,须单独跑)
645
645
  # 同样记录结果,供下面的文档门禁核对
646
646
  npm run gates # 发版门禁三条一起跑:tag 指向 / 包内容 / 文档数字
647
647
  npm run check:tag # ① tag 名里的版本 == 该 tag 所指提交里的 package.json 版本
package/lib/client.js CHANGED
@@ -1005,6 +1005,7 @@ var NAMESPACE = "composer-ux";
1005
1005
  var REPO_URL = "https://github.com/fangwen9527/dsh-composer-ux";
1006
1006
  var RESTART_API_PATH = "/composer-ux/restart";
1007
1007
  var USAGE_API_PATH = "/composer-ux/usage";
1008
+ var MODELS_API_PATH = "/composer-ux/models";
1008
1009
  var SYNC_API_PATH = "/composer-ux/sync-prices";
1009
1010
  var ENABLED_FIELD = "enabled";
1010
1011
  var KEYS_ENABLED_FIELD = "keysEnabled";
@@ -1570,13 +1571,13 @@ async function requestBook(method, body) {
1570
1571
  }
1571
1572
  const row2 = typeof parsed === "object" && parsed !== null ? parsed : {};
1572
1573
  const book = sanitizeBook(row2.book);
1573
- const textOf2 = (value) => typeof value === "string" && value !== "" ? value : void 0;
1574
+ const textOf3 = (value) => typeof value === "string" && value !== "" ? value : void 0;
1574
1575
  return {
1575
1576
  ok: row2.ok === true,
1576
1577
  ...book === void 0 ? {} : { book },
1577
- ...textOf2(row2.error) === void 0 ? {} : { error: textOf2(row2.error) },
1578
- ...textOf2(row2.file) === void 0 ? {} : { file: textOf2(row2.file) },
1579
- ...textOf2(row2.quarantined) === void 0 ? {} : { quarantined: textOf2(row2.quarantined) }
1578
+ ...textOf3(row2.error) === void 0 ? {} : { error: textOf3(row2.error) },
1579
+ ...textOf3(row2.file) === void 0 ? {} : { file: textOf3(row2.file) },
1580
+ ...textOf3(row2.quarantined) === void 0 ? {} : { quarantined: textOf3(row2.quarantined) }
1580
1581
  };
1581
1582
  } catch (error) {
1582
1583
  return { ok: false, error: error instanceof Error ? error.message : String(error) };
@@ -2892,7 +2893,12 @@ var quickSectionBody = {
2892
2893
  minHeight: 0,
2893
2894
  display: "flex",
2894
2895
  flexDirection: "column",
2895
- overflow: "hidden"
2896
+ // 0.14.1:**这一半要能滚**。以前是 overflow: hidden —— 卡片(七行 + 详情 + 提示词框)
2897
+ // 比面板还高时,下面的内容就被面板底部裁掉、够不到(用户报的「点完看不到下边」)。
2898
+ // 横轴仍然不滚(面板宽度固定),纵轴给滑动条;overscroll 收敛,免得滚到页面上。
2899
+ overflowX: "hidden",
2900
+ overflowY: "auto",
2901
+ overscrollBehavior: "contain"
2896
2902
  };
2897
2903
  var quickSectionToggle = {
2898
2904
  display: "flex",
@@ -3182,6 +3188,68 @@ var optHint = {
3182
3188
  fontSize: 10.5,
3183
3189
  lineHeight: 1.5
3184
3190
  };
3191
+ var optPickerButton = {
3192
+ flex: "1 1 auto",
3193
+ minWidth: 0,
3194
+ textAlign: "left",
3195
+ padding: "3px 8px",
3196
+ border: "0.5px solid var(--dsw-alias-border-l2)",
3197
+ borderRadius: 6,
3198
+ background: "transparent",
3199
+ color: "var(--dsw-alias-label-primary)",
3200
+ fontSize: 11.5,
3201
+ lineHeight: 1.7,
3202
+ cursor: "pointer",
3203
+ whiteSpace: "nowrap",
3204
+ overflow: "hidden",
3205
+ textOverflow: "ellipsis"
3206
+ };
3207
+ var optPickerPanel = {
3208
+ maxHeight: 260,
3209
+ overflowY: "auto",
3210
+ overscrollBehavior: "contain",
3211
+ border: "0.5px solid var(--dsw-alias-border-l2)",
3212
+ borderRadius: 8,
3213
+ background: "var(--dsw-alias-bg-layer-1)",
3214
+ padding: 4,
3215
+ display: "flex",
3216
+ flexDirection: "column",
3217
+ gap: 1
3218
+ };
3219
+ var optPickerGroup = {
3220
+ position: "sticky",
3221
+ top: 0,
3222
+ zIndex: 1,
3223
+ background: "var(--dsw-alias-bg-layer-1)",
3224
+ color: "var(--dsw-alias-label-tertiary)",
3225
+ fontSize: 10.5,
3226
+ fontWeight: 600,
3227
+ padding: "4px 6px 2px"
3228
+ };
3229
+ var optPickerItem = {
3230
+ display: "flex",
3231
+ alignItems: "baseline",
3232
+ gap: 6,
3233
+ width: "100%",
3234
+ textAlign: "left",
3235
+ padding: "4px 8px",
3236
+ border: "none",
3237
+ borderRadius: 6,
3238
+ background: "transparent",
3239
+ color: "var(--dsw-alias-label-primary)",
3240
+ fontSize: 11.5,
3241
+ lineHeight: 1.6,
3242
+ cursor: "pointer"
3243
+ };
3244
+ var optPickerItemActive = {
3245
+ ...optPickerItem,
3246
+ background: "var(--dsw-alias-bg-layer-2)",
3247
+ fontWeight: 600
3248
+ };
3249
+ var optPickerModelId = {
3250
+ color: "var(--dsw-alias-label-tertiary)",
3251
+ fontSize: 10.5
3252
+ };
3185
3253
 
3186
3254
  // src/client/ContextMenuHost.tsx
3187
3255
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -6530,7 +6598,50 @@ function QuickCommandsButton({
6530
6598
  var import_react15 = require("react");
6531
6599
 
6532
6600
  // src/client/OptimizeOptionsCard.tsx
6533
- var import_react12 = require("react");
6601
+ var import_react12 = __toESM(require("react"), 1);
6602
+
6603
+ // src/client/model-catalog.ts
6604
+ var textOf = (value) => typeof value === "string" ? value.trim() : "";
6605
+ async function fetchModelCatalog(options = {}) {
6606
+ const url = options.fresh === true ? `${MODELS_API_PATH}?fresh=1` : MODELS_API_PATH;
6607
+ try {
6608
+ const response = await fetch(url, {
6609
+ method: "GET",
6610
+ headers: { accept: "application/json" },
6611
+ ...options.signal === void 0 ? {} : { signal: options.signal }
6612
+ });
6613
+ if (!response.ok) return { kind: "error", error: `\u5BBF\u4E3B\u56DE\u4E86 ${String(response.status)}` };
6614
+ const body = await response.json();
6615
+ if (body.ok !== true) return { kind: "error", error: textOf(body.error) === "" ? "\u5BBF\u4E3B\u6CA1\u7ED9\u51FA\u539F\u56E0" : textOf(body.error) };
6616
+ const rawGroups = Array.isArray(body.groups) ? body.groups : [];
6617
+ const groups = [];
6618
+ for (const raw of rawGroups) {
6619
+ if (typeof raw !== "object" || raw === null) continue;
6620
+ const row2 = raw;
6621
+ const id = textOf(row2.id);
6622
+ if (id === "") continue;
6623
+ const models = (Array.isArray(row2.models) ? row2.models : []).map((item) => {
6624
+ const one = typeof item === "object" && item !== null ? item : {};
6625
+ const model = textOf(one.id);
6626
+ const name2 = textOf(one.name);
6627
+ return { id: model, name: name2 === "" ? model : name2 };
6628
+ }).filter((item) => item.id !== "");
6629
+ const error = textOf(row2.error);
6630
+ groups.push({
6631
+ id,
6632
+ name: textOf(row2.name) === "" ? id : textOf(row2.name),
6633
+ models,
6634
+ ...error === "" ? {} : { error }
6635
+ });
6636
+ }
6637
+ const currentRow = typeof body.current === "object" && body.current !== null ? body.current : {};
6638
+ return { kind: "ok", groups, current: { provider: textOf(currentRow.provider), model: textOf(currentRow.model) } };
6639
+ } catch (error) {
6640
+ return { kind: "error", error: error instanceof Error ? error.message : String(error) };
6641
+ }
6642
+ }
6643
+
6644
+ // src/client/OptimizeOptionsCard.tsx
6534
6645
  var import_jsx_runtime12 = require("react/jsx-runtime");
6535
6646
  function Segmented(props) {
6536
6647
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: optRow, children: [
@@ -6576,6 +6687,23 @@ function currentPrompt(settings) {
6576
6687
  function OptimizeOptionsCard({ settings, actions }) {
6577
6688
  const [detailOpen, setDetailOpen] = (0, import_react12.useState)(false);
6578
6689
  const [modelText, setModelText] = (0, import_react12.useState)(settings.optimizerModel);
6690
+ const [pickerOpen, setPickerOpen] = (0, import_react12.useState)(false);
6691
+ const [catalog, setCatalog] = (0, import_react12.useState)(null);
6692
+ const [manual, setManual] = (0, import_react12.useState)(false);
6693
+ const [retrySeq, setRetrySeq] = (0, import_react12.useState)(0);
6694
+ (0, import_react12.useEffect)(() => {
6695
+ if (!pickerOpen) return;
6696
+ let alive = true;
6697
+ setCatalog({ kind: "loading" });
6698
+ const controller = new AbortController();
6699
+ void fetchModelCatalog({ fresh: retrySeq > 0, signal: controller.signal }).then((result) => {
6700
+ if (alive) setCatalog(result);
6701
+ });
6702
+ return () => {
6703
+ alive = false;
6704
+ controller.abort();
6705
+ };
6706
+ }, [pickerOpen, retrySeq]);
6579
6707
  const prompt = currentPrompt(settings);
6580
6708
  const tierOff = settings.optimizerTier === "off";
6581
6709
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: optCard, role: "group", "aria-label": "\u4F18\u5316\u9009\u9879", children: [
@@ -6601,61 +6729,134 @@ function OptimizeOptionsCard({ settings, actions }) {
6601
6729
  }
6602
6730
  ),
6603
6731
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: optRow, children: [
6604
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: optLabel, title: "\u89E3\u91CA\u5C42\u7528\u54EA\u4E2A\u6A21\u578B\uFF1A\u8DDF\u968F\u4F1A\u8BDD\u5F53\u524D\u9009\u7684\uFF0C\u6216\u81EA\u5DF1\u6307\u5B9A", children: "\u6A21\u578B" }),
6605
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: optSegmented, role: "group", "aria-label": "\u6A21\u578B", children: [
6606
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6607
- "button",
6608
- {
6609
- type: "button",
6610
- "aria-pressed": settings.optimizerModel === "",
6611
- title: "\u7528\u4F1A\u8BDD\u5F53\u524D\u9009\u7684\u6A21\u578B\uFF08\u9ED8\u8BA4\uFF09",
6612
- style: settings.optimizerModel === "" ? optSegButtonActive : optSegButton,
6613
- onMouseDown: (event) => {
6614
- event.preventDefault();
6615
- },
6616
- onClick: () => {
6617
- setModelText("");
6618
- actions.setModel("");
6619
- },
6620
- children: "\u8DDF\u968F\u4F1A\u8BDD"
6621
- }
6622
- ),
6732
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: optLabel, title: "\u89E3\u91CA\u5C42\u7528\u54EA\u4E2A\u6A21\u578B\uFF1A\u8DDF\u968F\u4F1A\u8BDD\u5F53\u524D\u9009\u7684\uFF0C\u6216\u4ECE\u6E05\u5355\u91CC\u6307\u5B9A\u4E00\u6761", children: "\u6A21\u578B" }),
6733
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
6734
+ "button",
6735
+ {
6736
+ type: "button",
6737
+ "aria-label": "\u6A21\u578B",
6738
+ "aria-expanded": pickerOpen,
6739
+ title: "\u70B9\u5F00\u9009\u6A21\u578B\uFF1A\u7B2C\u4E00\u9879\u662F\u8DDF\u968F\u4F1A\u8BDD\uFF1B\u4E0B\u9762\u6309 provider \u5206\u7EC4\u5217\u51FA\u53EF\u7528\u6A21\u578B\uFF08\u6E05\u5355\u957F\u4E86\u53EF\u4EE5\u6EDA\uFF09",
6740
+ style: optPickerButton,
6741
+ onMouseDown: (event) => {
6742
+ event.preventDefault();
6743
+ },
6744
+ onClick: () => {
6745
+ setPickerOpen(!pickerOpen);
6746
+ },
6747
+ children: [
6748
+ settings.optimizerModel === "" ? "\u8DDF\u968F\u4F1A\u8BDD\u6A21\u578B" : settings.optimizerModel,
6749
+ " \u25BE"
6750
+ ]
6751
+ }
6752
+ )
6753
+ ] }),
6754
+ pickerOpen && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: optPickerPanel, role: "listbox", "aria-label": "\u6A21\u578B\u6E05\u5355", children: [
6755
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
6756
+ "button",
6757
+ {
6758
+ type: "button",
6759
+ style: settings.optimizerModel === "" ? optPickerItemActive : optPickerItem,
6760
+ onMouseDown: (event) => {
6761
+ event.preventDefault();
6762
+ },
6763
+ onClick: () => {
6764
+ setModelText("");
6765
+ actions.setModel("");
6766
+ setManual(false);
6767
+ setPickerOpen(false);
6768
+ },
6769
+ children: [
6770
+ "\u8DDF\u968F\u4F1A\u8BDD\u6A21\u578B",
6771
+ catalog !== null && catalog.kind === "ok" && catalog.current.model !== "" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { style: optPickerModelId, children: [
6772
+ "\u5F53\u524D\uFF1A",
6773
+ catalog.current.model
6774
+ ] })
6775
+ ]
6776
+ }
6777
+ ),
6778
+ catalog === null || catalog.kind === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: optHint, children: "\u6B63\u5728\u8BFB\u6A21\u578B\u6E05\u5355\u2026" }) : catalog.kind === "error" ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
6779
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { style: optHint, children: [
6780
+ "\u8BFB\u4E0D\u5230\u6A21\u578B\u6E05\u5355\uFF1A",
6781
+ catalog.error
6782
+ ] }),
6623
6783
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6624
6784
  "button",
6625
6785
  {
6626
6786
  type: "button",
6627
- "aria-pressed": settings.optimizerModel !== "",
6628
- title: "\u6307\u5B9A\u4E00\u6761\u8DEF\u7531\uFF1A\u5199 provider/model\uFF08\u53EA\u5199\u6A21\u578B\u540D\u5219\u6CBF\u7528\u4F1A\u8BDD\u7684 provider\uFF09",
6629
- style: settings.optimizerModel !== "" ? optSegButtonActive : optSegButton,
6787
+ style: optPickerItem,
6630
6788
  onMouseDown: (event) => {
6631
6789
  event.preventDefault();
6632
6790
  },
6633
6791
  onClick: () => {
6634
- actions.setModel(modelText.trim() === "" ? "provider/model" : modelText.trim());
6792
+ setRetrySeq(retrySeq + 1);
6635
6793
  },
6636
- children: "\u6307\u5B9A"
6794
+ children: "\u91CD\u8BD5"
6637
6795
  }
6638
6796
  )
6639
- ] })
6797
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
6798
+ catalog.groups.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: optHint, children: "\u5BBF\u4E3B\u6CA1\u62A5\u51FA\u4EFB\u4F55 provider\uFF08\u5728\u8BBE\u7F6E\u91CC\u770B\u8FC7\u6A21\u578B\u8DEF\u7531\u5417\uFF1F\uFF09" }),
6799
+ catalog.groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react12.default.Fragment, { children: [
6800
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: optPickerGroup, children: group.name }),
6801
+ group.models.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: optHint, children: group.error === void 0 ? "\u8FD9\u4E00\u7EC4\u6CA1\u6709\u6A21\u578B" : `\u8FD9\u4E00\u7EC4\u8BFB\u4E0D\u5230\uFF1A${group.error}` }),
6802
+ group.models.map((model) => {
6803
+ const value = `${group.id}/${model.id}`;
6804
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
6805
+ "button",
6806
+ {
6807
+ type: "button",
6808
+ style: settings.optimizerModel === value ? optPickerItemActive : optPickerItem,
6809
+ onMouseDown: (event) => {
6810
+ event.preventDefault();
6811
+ },
6812
+ onClick: () => {
6813
+ actions.setModel(value);
6814
+ setModelText(value);
6815
+ setPickerOpen(false);
6816
+ },
6817
+ children: [
6818
+ model.name,
6819
+ model.name === model.id ? null : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: optPickerModelId, children: model.id })
6820
+ ]
6821
+ },
6822
+ value
6823
+ );
6824
+ })
6825
+ ] }, group.id))
6826
+ ] }),
6827
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6828
+ "button",
6829
+ {
6830
+ type: "button",
6831
+ style: optPickerItem,
6832
+ onMouseDown: (event) => {
6833
+ event.preventDefault();
6834
+ },
6835
+ onClick: () => {
6836
+ setManual(!manual);
6837
+ },
6838
+ children: "\u624B\u52A8\u8F93\u5165 provider/model\u2026"
6839
+ }
6840
+ ),
6841
+ manual && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6842
+ "input",
6843
+ {
6844
+ "aria-label": "\u624B\u52A8\u8F93\u5165\u6A21\u578B",
6845
+ title: "\u5199 provider/model\uFF0C\u4F8B\u5982 go/deepseek-flash",
6846
+ value: modelText,
6847
+ onChange: (event) => {
6848
+ setModelText(event.target.value);
6849
+ },
6850
+ onBlur: () => {
6851
+ const next = modelText.trim();
6852
+ actions.setModel(next);
6853
+ setModelText(next);
6854
+ },
6855
+ style: optInput,
6856
+ spellCheck: false
6857
+ }
6858
+ )
6640
6859
  ] }),
6641
- settings.optimizerModel !== "" && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6642
- "input",
6643
- {
6644
- "aria-label": "\u6307\u5B9A\u7684\u6A21\u578B",
6645
- title: "\u5199 provider/model\uFF0C\u4F8B\u5982 go/deepseek-flash\uFF1B\u7559\u7A7A = \u8DDF\u968F\u4F1A\u8BDD",
6646
- value: modelText,
6647
- onChange: (event) => {
6648
- setModelText(event.target.value);
6649
- },
6650
- onBlur: () => {
6651
- const next = modelText.trim();
6652
- actions.setModel(next);
6653
- setModelText(next);
6654
- },
6655
- style: optInput,
6656
- spellCheck: false
6657
- }
6658
- ),
6659
6860
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
6660
6861
  Segmented,
6661
6862
  {
@@ -7473,7 +7674,7 @@ function QuickCommandsPanel({
7473
7674
  }
7474
7675
 
7475
7676
  // src/client/optimize-state.ts
7476
- var textOf = (value) => typeof value === "string" && value !== "" ? value : void 0;
7677
+ var textOf2 = (value) => typeof value === "string" && value !== "" ? value : void 0;
7477
7678
  async function request(method, body) {
7478
7679
  try {
7479
7680
  const response = await fetch(OPTIMIZE_STATE_API_PATH, {
@@ -7494,9 +7695,9 @@ async function request(method, body) {
7494
7695
  state: sanitizeDockSnapshot(row2.state),
7495
7696
  hidden: row2.hidden === true,
7496
7697
  keep: row2.keep !== false,
7497
- ...textOf(row2.file) === void 0 ? {} : { file: textOf(row2.file) },
7498
- ...textOf(row2.error) === void 0 ? {} : { error: textOf(row2.error) },
7499
- ...textOf(row2.quarantined) === void 0 ? {} : { quarantined: textOf(row2.quarantined) },
7698
+ ...textOf2(row2.file) === void 0 ? {} : { file: textOf2(row2.file) },
7699
+ ...textOf2(row2.error) === void 0 ? {} : { error: textOf2(row2.error) },
7700
+ ...textOf2(row2.quarantined) === void 0 ? {} : { quarantined: textOf2(row2.quarantined) },
7500
7701
  ...row2.tooBig === true ? { tooBig: true } : {}
7501
7702
  };
7502
7703
  } catch (error) {