dsh-dock 0.13.0 → 0.15.0

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/client.js CHANGED
@@ -2187,7 +2187,7 @@ function initFeatureState(defs) {
2187
2187
  }
2188
2188
  function syncFeatureStateFromHost(defs) {
2189
2189
  if (typeof fetch !== "function") return;
2190
- fetch("/dsh-dock/features").then(async (res) => {
2190
+ fetch("dsh-dock/features").then(async (res) => {
2191
2191
  if (!res.ok) return;
2192
2192
  const data = await res.json().catch(() => null);
2193
2193
  const persisted = data && data.ok && data.data && data.data.persisted;
@@ -2214,7 +2214,7 @@ function syncFeatureStateFromHost(defs) {
2214
2214
  }
2215
2215
  function pushFeatureEnabledToHost(id, enabled) {
2216
2216
  if (typeof fetch !== "function") return;
2217
- fetch("/dsh-dock/features", {
2217
+ fetch("dsh-dock/features", {
2218
2218
  method: "POST",
2219
2219
  headers: { "content-type": "application/json" },
2220
2220
  body: JSON.stringify({ id, enabled })
@@ -2299,8 +2299,16 @@ function subscribePanel(fn) {
2299
2299
  // features/tokenlog/view.jsx
2300
2300
  var import_react2 = __toESM(require("react"), 1);
2301
2301
  var import_jsx_runtime = require("react/jsx-runtime");
2302
+ var routeGone = false;
2303
+ function isTokenlogRouteGone() {
2304
+ return routeGone;
2305
+ }
2306
+ function routeGoneMessage() {
2307
+ return "\u5BBF\u4E3B\u672A\u542F\u7528\u300C\u7528\u91CF\u8BB0\u5F55\u300D\u8DEF\u7531\uFF08\u6216\u5BBF\u4E3B\u8FDB\u7A0B\u662F\u65E7\u7248\u672C\uFF09\uFF0C\u8BF7\u5728\u529F\u80FD\u575E\u91CC\u542F\u7528\u8BE5\u529F\u80FD\u6216\u91CD\u542F dsh web";
2308
+ }
2302
2309
  function rpcCall(method, args) {
2303
- return fetch("/dsh-dock/tokenlog/" + method, {
2310
+ if (routeGone) return Promise.reject(new Error(routeGoneMessage()));
2311
+ return fetch("dsh-dock/tokenlog/" + method, {
2304
2312
  method: "POST",
2305
2313
  headers: { "content-type": "application/json" },
2306
2314
  body: JSON.stringify(args === void 0 ? {} : args)
@@ -2308,7 +2316,8 @@ function rpcCall(method, args) {
2308
2316
  const data = await res.json().catch(() => ({}));
2309
2317
  if (res.ok && data && data.ok === true) return data.data;
2310
2318
  if (res.status === 405 || res.status === 404) {
2311
- throw new Error("\u5BBF\u4E3B\u8FDB\u7A0B\u662F\u65E7\u7248\u672C\uFF08\u6CA1\u6709\u7528\u91CF\u8BB0\u5F55\u8DEF\u7531\uFF09\uFF0C\u91CD\u542F dsh web \u540E\u91CD\u8BD5");
2319
+ routeGone = true;
2320
+ throw new Error(routeGoneMessage());
2312
2321
  }
2313
2322
  if (data && data.ok === false) throw new Error(data.error && data.error.message || "HTTP " + res.status);
2314
2323
  throw new Error("HTTP " + res.status + (data && data.error && data.error.message ? ": " + data.error.message : ""));
@@ -3670,32 +3679,37 @@ function TokenLogChip(props) {
3670
3679
  (0, import_react2.useEffect)(() => {
3671
3680
  if (!sid) return;
3672
3681
  let cancel = false;
3673
- const load = () => rpcCall("query", { sessionId: sid }).then((d) => {
3674
- if (!cancel) setSnap({ totals: d && d.totals, rate: d && d.rateUsdCny || 7.2, err: "" });
3675
- }).catch((e) => {
3676
- if (!cancel) setSnap((s) => ({ totals: s.totals, rate: s.rate, err: String(e && e.message || e) }));
3677
- });
3682
+ let timer = null;
3683
+ const load = () => {
3684
+ if (cancel || isTokenlogRouteGone()) return null;
3685
+ return rpcCall("query", { sessionId: sid }).then((d) => {
3686
+ if (!cancel) setSnap({ totals: d && d.totals, rate: d && d.rateUsdCny || 7.2, err: "" });
3687
+ }).catch((e) => {
3688
+ if (!cancel) setSnap((s) => ({ totals: s.totals, rate: s.rate, err: String(e && e.message || e) }));
3689
+ });
3690
+ };
3678
3691
  load();
3679
- const timer = setInterval(load, 1e4);
3692
+ timer = setInterval(load, 1e4);
3680
3693
  return () => {
3681
3694
  cancel = true;
3682
- clearInterval(timer);
3695
+ if (timer) clearInterval(timer);
3683
3696
  };
3684
3697
  }, [sid]);
3685
3698
  if (!sid) return null;
3686
3699
  const t = snap.totals;
3687
- const label = snap.err && !t ? snap.err.includes("\u91CD\u542F") ? "\u7528\u91CF\xB7\u9700\u91CD\u542F\u5BBF\u4E3B" : "\u7528\u91CF\xB7\u5931\u8D25" : t ? "\u26C1 " + fmtCompact(t.totalTokens) + (t.cost > 0 ? " \xB7 " + fmtCostCny(t.cost, snap.rate) : "") : "\u26C1 \u2026";
3688
- const title = t ? "\u672C\u4F1A\u8BDD " + fmtNum(t.calls) + " \u6B21\u8C03\u7528 \xB7 " + fmtNum(t.totalTokens) + " Token \xB7 " + fmtCostCny(t.cost, snap.rate) + "\uFF08\u4F30\u7B97\uFF09\n\u70B9\u51FB\u5728\u529F\u80FD\u575E\u67E5\u770B\u7528\u91CF\u8BB0\u5F55" : (snap.err ? snap.err + "\n" : "") + "\u70B9\u51FB\u5728\u529F\u80FD\u575E\u67E5\u770B\u7528\u91CF\u8BB0\u5F55";
3700
+ const gone = isTokenlogRouteGone();
3701
+ const label = gone ? "\u7528\u91CF\xB7\u672A\u542F\u7528" : snap.err && !t ? snap.err.includes("\u91CD\u542F") || snap.err.includes("\u542F\u7528") ? "\u7528\u91CF\xB7\u672A\u542F\u7528" : "\u7528\u91CF\xB7\u5931\u8D25" : t ? "\u26C1 " + fmtCompact(t.totalTokens) + (t.cost > 0 ? " \xB7 " + fmtCostCny(t.cost, snap.rate) : "") : "\u26C1 \u2026";
3702
+ const title = gone ? routeGoneMessage() + "\n\uFF08\u53EF\u5728\u529F\u80FD\u575E\u91CC\u542F\u7528\u300C\u7528\u91CF\u8BB0\u5F55\u300D\uFF09" : t ? "\u672C\u4F1A\u8BDD " + fmtNum(t.calls) + " \u6B21\u8C03\u7528 \xB7 " + fmtNum(t.totalTokens) + " Token \xB7 " + fmtCostCny(t.cost, snap.rate) + "\uFF08\u4F30\u7B97\uFF09\n\u70B9\u51FB\u5728\u529F\u80FD\u575E\u67E5\u770B\u7528\u91CF\u8BB0\u5F55" : (snap.err ? snap.err + "\n" : "") + "\u70B9\u51FB\u5728\u529F\u80FD\u575E\u67E5\u770B\u7528\u91CF\u8BB0\u5F55";
3689
3703
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
3690
3704
  "button",
3691
3705
  {
3692
3706
  type: "button",
3693
- className: "dockchip" + (snap.err && !t ? " err" : ""),
3707
+ className: "dockchip" + (snap.err && !t && !gone ? " err" : ""),
3694
3708
  title,
3695
3709
  "aria-label": "\u4F1A\u8BDD\u7528\u91CF",
3696
3710
  onClick: () => openPanel("tokenlog", { sessionId: sid, navAt: Date.now() }),
3697
3711
  children: [
3698
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dockchip-dot", style: { background: "var(--dk-warn)" } }),
3712
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dockchip-dot", style: { background: gone ? "var(--dsw-alias-label-tertiary,#8b95ab)" : "var(--dk-warn)" } }),
3699
3713
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: label })
3700
3714
  ]
3701
3715
  }
@@ -3731,7 +3745,7 @@ var modelsStore = {
3731
3745
  load() {
3732
3746
  if (this.snap.loading) return;
3733
3747
  this.set({ loading: true, error: null });
3734
- fetch("/dsh-dock/models", { cache: "no-store", signal: AbortSignal.timeout(2e4) }).then((res) => res.ok ? res.json() : Promise.reject(new Error("\u6A21\u578B\u76EE\u5F55\u63A5\u53E3 HTTP " + res.status))).then((data) => this.set({ data, loading: false, error: null })).catch((e) => this.set({ loading: false, error: e && e.message || String(e) }));
3748
+ fetch("dsh-dock/models", { cache: "no-store", signal: AbortSignal.timeout(2e4) }).then((res) => res.ok ? res.json() : Promise.reject(new Error("\u6A21\u578B\u76EE\u5F55\u63A5\u53E3 HTTP " + res.status))).then((data) => this.set({ data, loading: false, error: null })).catch((e) => this.set({ loading: false, error: e && e.message || String(e) }));
3735
3749
  }
3736
3750
  };
3737
3751
  function useModels() {
@@ -3748,35 +3762,190 @@ var DKM_TAGS = [["video", "\u89C6\u9891"], ["audio", "\u97F3\u9891"], ["document
3748
3762
  var DKM_PI_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
3749
3763
  var DKM_DS_LEVELS = ["off", "low", "high", "max"];
3750
3764
  var DKM_EFFORT_NAMES = { off: "\u5173\u95ED", minimal: "\u6781\u4F4E", low: "\u4F4E", medium: "\u4E2D", high: "\u9AD8", xhigh: "\u8D85\u9AD8", max: "\u6700\u9AD8" };
3765
+ var DKM_APIS = [
3766
+ ["openai-completions", "OpenAI \u517C\u5BB9\uFF08chat/completions\uFF09"],
3767
+ ["openai-responses", "OpenAI Responses"],
3768
+ ["anthropic-messages", "Anthropic Messages"]
3769
+ ];
3770
+ var DKM_ROUTE_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
3771
+ function dkmEnvFromRoute(route) {
3772
+ const s = String(route || "").toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "");
3773
+ if (!s) return "";
3774
+ return (/^[0-9]/.test(s) ? "KEY_" + s : s) + "_API_KEY";
3775
+ }
3776
+ var DKM_ALL_INPUT = DKM_MODALITIES.map((x) => x[0]);
3777
+ var DKM_ALL_TAGS = DKM_TAGS.map((x) => x[0]);
3778
+ function dkmToggleIn(list, v) {
3779
+ return list.indexOf(v) >= 0 ? list.filter((x) => x !== v) : list.concat([v]);
3780
+ }
3781
+ function dkmAllInputs(on) {
3782
+ return { input: on ? DKM_ALL_INPUT.slice() : [], tags: on ? DKM_ALL_TAGS.slice() : [] };
3783
+ }
3784
+ function dkmAllLevels(on) {
3785
+ const lv = {};
3786
+ for (const k of DKM_PI_LEVELS) lv[k] = !!on;
3787
+ return { effortsMode: "custom", effortLevels: lv };
3788
+ }
3789
+ function draftFromModel(m) {
3790
+ const levels = {};
3791
+ if (m.efforts === "custom" && m.effortMap && typeof m.effortMap === "object") {
3792
+ for (const lv of DKM_PI_LEVELS) levels[lv] = m.effortMap[lv] !== void 0;
3793
+ }
3794
+ return {
3795
+ id: m.id || "",
3796
+ name: m.name || "",
3797
+ contextWindow: m.contextWindow != null ? String(m.contextWindow) : "",
3798
+ maxTokens: m.maxTokens != null ? String(m.maxTokens) : "",
3799
+ input: m.input && m.input.length ? m.input.slice() : ["text"],
3800
+ tags: (m.tags || []).slice(),
3801
+ effortsMode: m.efforts === "custom" ? "custom" : m.efforts === "off" ? "off" : "inherit",
3802
+ effortLevels: levels,
3803
+ raw: m.raw && typeof m.raw === "object" ? m.raw : null
3804
+ };
3805
+ }
3806
+ function emptyModelDraft() {
3807
+ return { id: "", name: "", contextWindow: "", maxTokens: "", input: ["text"], tags: [], effortsMode: "inherit", effortLevels: {}, raw: null };
3808
+ }
3809
+ function emptyProviderForm() {
3810
+ return {
3811
+ route: "",
3812
+ displayName: "",
3813
+ api: "openai-completions",
3814
+ baseURL: "",
3815
+ apiKeyEnv: "",
3816
+ apiKey: "",
3817
+ defaultInput: ["text"],
3818
+ models: [],
3819
+ extra: {}
3820
+ };
3821
+ }
3822
+ function providerFormFromProvider(p) {
3823
+ const raw = p.rawProfile && typeof p.rawProfile === "object" && !Array.isArray(p.rawProfile) ? p.rawProfile : {};
3824
+ const extra = Object.assign({}, raw);
3825
+ delete extra.displayName;
3826
+ delete extra.api;
3827
+ delete extra.baseURL;
3828
+ delete extra.apiKeyEnv;
3829
+ delete extra.defaultInput;
3830
+ delete extra.models;
3831
+ const models = draftFromProvider(p).models;
3832
+ return {
3833
+ route: p.route || p.id || "",
3834
+ displayName: typeof raw.displayName === "string" ? raw.displayName : p.displayName || "",
3835
+ api: typeof raw.api === "string" && raw.api ? raw.api : p.api || "openai-completions",
3836
+ baseURL: typeof raw.baseURL === "string" ? raw.baseURL : p.baseURL || "",
3837
+ apiKeyEnv: typeof raw.apiKeyEnv === "string" ? raw.apiKeyEnv : p.apiKeyEnv || "",
3838
+ apiKey: "",
3839
+ defaultInput: Array.isArray(raw.defaultInput) && raw.defaultInput.length ? raw.defaultInput.slice() : Array.isArray(p.defaultInput) && p.defaultInput.length ? p.defaultInput.slice() : ["text"],
3840
+ models: models.length ? models : [emptyModelDraft()],
3841
+ extra
3842
+ };
3843
+ }
3751
3844
  function draftFromProvider(p) {
3752
3845
  return {
3753
3846
  id: p.id,
3754
3847
  kind: p.kind,
3755
3848
  thinking: p.thinking === "disabled" ? "disabled" : "enabled",
3756
3849
  defaultEffort: DKM_DS_LEVELS.indexOf(p.defaultEffort) >= 0 ? p.defaultEffort : "high",
3757
- models: (p.models || []).map((m) => {
3758
- const levels = {};
3759
- if (m.efforts === "custom" && m.effortMap && typeof m.effortMap === "object") {
3760
- for (const lv of DKM_PI_LEVELS) levels[lv] = m.effortMap[lv] !== void 0;
3761
- }
3762
- return {
3763
- id: m.id || "",
3764
- name: m.name || "",
3765
- contextWindow: m.contextWindow != null ? String(m.contextWindow) : "",
3766
- maxTokens: m.maxTokens != null ? String(m.maxTokens) : "",
3767
- input: m.input && m.input.length ? m.input.slice() : ["text"],
3768
- tags: (m.tags || []).slice(),
3769
- effortsMode: m.efforts === "custom" ? "custom" : m.efforts === "off" ? "off" : "inherit",
3770
- effortLevels: levels,
3771
- raw: m.raw && typeof m.raw === "object" ? m.raw : null
3772
- };
3773
- })
3850
+ models: (p.models || []).map(draftFromModel)
3774
3851
  };
3775
3852
  }
3776
- function ModelsView() {
3853
+ function ModelRow(props) {
3854
+ const m = props.m, i = props.i, kind = props.kind, onPatch = props.onPatch, onDel = props.onDel;
3855
+ return import_react3.default.createElement(
3856
+ "div",
3857
+ { key: i, className: "dkm-model" },
3858
+ import_react3.default.createElement(
3859
+ "div",
3860
+ { className: "dkm-model-head" },
3861
+ import_react3.default.createElement("span", { className: "dkm-model-idx" }, "#" + (i + 1)),
3862
+ import_react3.default.createElement("button", { type: "button", className: "dkm-del", onClick: () => onDel(i) }, "\u5220\u9664")
3863
+ ),
3864
+ import_react3.default.createElement(
3865
+ "div",
3866
+ { className: "dkm-grid" },
3867
+ import_react3.default.createElement("input", { className: "dkm-input", placeholder: "\u6A21\u578B id\uFF08\u5FC5\u586B\uFF0C\u5982 deepseek-v4-pro\uFF09", value: m.id, onChange: (e) => onPatch(i, { id: e.target.value }) }),
3868
+ import_react3.default.createElement("input", { className: "dkm-input", placeholder: "\u663E\u793A\u540D\u79F0\uFF08\u53EF\u9009\uFF09", value: m.name, onChange: (e) => onPatch(i, { name: e.target.value }) }),
3869
+ import_react3.default.createElement("input", { className: "dkm-input", placeholder: "\u4E0A\u4E0B\u6587\u7A97\u53E3", inputMode: "numeric", value: m.contextWindow, onChange: (e) => onPatch(i, { contextWindow: e.target.value }) }),
3870
+ import_react3.default.createElement("input", { className: "dkm-input", placeholder: "\u6700\u5927\u8F93\u51FA", inputMode: "numeric", value: m.maxTokens, onChange: (e) => onPatch(i, { maxTokens: e.target.value }) })
3871
+ ),
3872
+ import_react3.default.createElement(
3873
+ "div",
3874
+ { className: "dkm-checks" },
3875
+ import_react3.default.createElement("span", { className: "dkm-label" }, "\u8F93\u5165\u7C7B\u578B\uFF1A"),
3876
+ DKM_MODALITIES.map(([v, label]) => import_react3.default.createElement(
3877
+ "label",
3878
+ {
3879
+ key: v,
3880
+ className: "dkm-check",
3881
+ title: v === "image" ? "\u7531\u4F60\u58F0\u660E\u8BE5\u7AEF\u70B9\u652F\u6301\u56FE\u7247\uFF1A\u4FDD\u5B58\u540E\u539F\u56FE\u76F4\u53D1\uFF0C\u5E76\u53EF\u4F5C\u4E3A\u89C6\u89C9\u6A21\u578B\u5019\u9009\u3002\u7CFB\u7EDF\u4E0D\u4F1A\u66FF\u4F60\u731C\u6D4B\uFF1B\u7AEF\u70B9\u5B9E\u9645\u4E0D\u652F\u6301\u65F6\u53D6\u6D88\u52FE\u9009\u5373\u53EF\u6062\u590D\u8D70\u56FE\u7247\u7406\u89E3\u4EE3\u7406" : null
3882
+ },
3883
+ import_react3.default.createElement("input", {
3884
+ type: "checkbox",
3885
+ checked: m.input.indexOf(v) >= 0,
3886
+ onChange: () => {
3887
+ const next = dkmToggleIn(m.input, v);
3888
+ onPatch(i, { input: next.length > 0 ? next : ["text"] });
3889
+ }
3890
+ }),
3891
+ label
3892
+ )),
3893
+ import_react3.default.createElement("span", { className: "dkm-label dkm-tag", title: "\u6807\u6CE8\u4EC5\u5728\u672C\u9762\u677F\u5C55\u793A\u4E0E\u6301\u4E45\u5316\uFF0C\u4E0D\u53C2\u4E0E\u8BF7\u6C42\u8DEF\u7531\uFF08\u5B98\u65B9 schema \u53EA\u63A5\u53D7\u6587\u672C/\u56FE\u7247\uFF09" }, "\u6807\u6CE8\uFF1A"),
3894
+ DKM_TAGS.map(([v, label]) => import_react3.default.createElement(
3895
+ "label",
3896
+ { key: v, className: "dkm-check dkm-tag" },
3897
+ import_react3.default.createElement("input", { type: "checkbox", checked: m.tags.indexOf(v) >= 0, onChange: () => onPatch(i, { tags: dkmToggleIn(m.tags, v) }) }),
3898
+ label
3899
+ )),
3900
+ import_react3.default.createElement("button", { type: "button", className: "dkm-mini", title: "\u4E00\u952E\u52FE\u9009\u5168\u90E8\u8F93\u5165\u7C7B\u578B\u4E0E\u6807\u6CE8", onClick: () => onPatch(i, dkmAllInputs(true)) }, "\u5168\u9009"),
3901
+ import_react3.default.createElement("button", { type: "button", className: "dkm-mini", title: "\u4E00\u952E\u53D6\u6D88\u5168\u90E8\u52FE\u9009\uFF08\u8F93\u5165\u7559\u7A7A = \u7EE7\u627F\u76EE\u5F55\u9ED8\u8BA4\uFF09", onClick: () => onPatch(i, dkmAllInputs(false)) }, "\u53D6\u6D88\u5168\u9009")
3902
+ ),
3903
+ import_react3.default.createElement(
3904
+ "div",
3905
+ { className: "dkm-efforts" },
3906
+ import_react3.default.createElement("span", { className: "dkm-label" }, "\u601D\u8003\u5F3A\u5EA6\uFF1A"),
3907
+ kind === "deepseek" ? import_react3.default.createElement("span", { className: "dkm-sub" }, "\u968F Provider \u8BBE\u7F6E\uFF08Off/Low/High/Max\uFF0C\u5168\u6A21\u578B\u5171\u4EAB\uFF09") : import_react3.default.createElement(
3908
+ "span",
3909
+ null,
3910
+ import_react3.default.createElement(
3911
+ "select",
3912
+ {
3913
+ className: "dkm-select",
3914
+ value: m.effortsMode,
3915
+ onChange: (e) => onPatch(i, {
3916
+ effortsMode: e.target.value,
3917
+ effortLevels: e.target.value === "custom" && m.effortsMode !== "custom" ? { off: true, low: true, high: true } : m.effortLevels
3918
+ })
3919
+ },
3920
+ import_react3.default.createElement("option", { value: "inherit" }, "\u8DDF\u968F\u76EE\u5F55\u9ED8\u8BA4"),
3921
+ import_react3.default.createElement("option", { value: "off" }, "\u4E0D\u652F\u6301\u601D\u8003"),
3922
+ import_react3.default.createElement("option", { value: "custom" }, "\u81EA\u5B9A\u4E49\u6863\u4F4D")
3923
+ ),
3924
+ m.effortsMode === "custom" ? DKM_PI_LEVELS.map((lv) => import_react3.default.createElement(
3925
+ "label",
3926
+ { key: lv, className: "dkm-check" },
3927
+ import_react3.default.createElement("input", {
3928
+ type: "checkbox",
3929
+ checked: !!m.effortLevels[lv],
3930
+ onChange: () => {
3931
+ const nl = Object.assign({}, m.effortLevels);
3932
+ nl[lv] = !nl[lv];
3933
+ onPatch(i, { effortLevels: nl });
3934
+ }
3935
+ }),
3936
+ DKM_EFFORT_NAMES[lv] || lv
3937
+ )) : null,
3938
+ import_react3.default.createElement("button", { type: "button", className: "dkm-mini", title: "\u4E00\u952E\u52FE\u9009\u5168\u90E8\u5F3A\u5EA6\u6863\uFF08\u5207\u4E3A\u81EA\u5B9A\u4E49\uFF09", onClick: () => onPatch(i, dkmAllLevels(true)) }, "\u5168\u9009"),
3939
+ import_react3.default.createElement("button", { type: "button", className: "dkm-mini", title: "\u4E00\u952E\u53D6\u6D88\u5168\u90E8\u5F3A\u5EA6\u6863", onClick: () => onPatch(i, dkmAllLevels(false)) }, "\u53D6\u6D88\u5168\u9009")
3940
+ )
3941
+ )
3942
+ );
3943
+ }
3944
+ function ModelsView(props) {
3777
3945
  const snap = useModels();
3778
3946
  const data = snap.data;
3779
3947
  const providers = data && Array.isArray(data.providers) ? data.providers : [];
3948
+ const params = props && props.params;
3780
3949
  const [selId, setSelId] = import_react3.default.useState(null);
3781
3950
  const [draft, setDraft] = import_react3.default.useState(null);
3782
3951
  const [builtKey, setBuiltKey] = import_react3.default.useState(null);
@@ -3789,6 +3958,8 @@ function ModelsView() {
3789
3958
  const [vpSaving, setVpSaving] = import_react3.default.useState(false);
3790
3959
  const [vpMsg, setVpMsg] = import_react3.default.useState(null);
3791
3960
  const [vpDirectExpanded, setVpDirectExpanded] = import_react3.default.useState(false);
3961
+ const [page, setPage] = import_react3.default.useState("settings");
3962
+ const [pageOpenCreate, setPageOpenCreate] = import_react3.default.useState(false);
3792
3963
  import_react3.default.useEffect(() => {
3793
3964
  modelsStore.load();
3794
3965
  }, []);
@@ -3813,7 +3984,7 @@ function ModelsView() {
3813
3984
  const saveVp = () => {
3814
3985
  if (!vpDraft || vpSaving) return;
3815
3986
  setVpSaving(true);
3816
- fetch("/dsh-dock/models", {
3987
+ fetch("dsh-dock/models", {
3817
3988
  method: "POST",
3818
3989
  headers: { "content-type": "application/json" },
3819
3990
  body: JSON.stringify({ visionProxy: vpDraft, revisions: data && data.revisions || {} }),
@@ -3851,6 +4022,13 @@ function ModelsView() {
3851
4022
  setSelId(id);
3852
4023
  setMsg(null);
3853
4024
  };
4025
+ import_react3.default.useEffect(() => {
4026
+ if (params && params.page === "providers") setPage("providers");
4027
+ if (params && params.openProviderForm) {
4028
+ setPage("providers");
4029
+ setPageOpenCreate(true);
4030
+ }
4031
+ }, []);
3854
4032
  const save = () => {
3855
4033
  if (!draft || !cur || !dirty || saving) return;
3856
4034
  const models = draft.models.map((m) => ({
@@ -3870,7 +4048,7 @@ function ModelsView() {
3870
4048
  payload.defaultEffort = draft.defaultEffort;
3871
4049
  }
3872
4050
  setSaving(true);
3873
- fetch("/dsh-dock/models", {
4051
+ fetch("dsh-dock/models", {
3874
4052
  method: "POST",
3875
4053
  headers: { "content-type": "application/json" },
3876
4054
  body: JSON.stringify(payload),
@@ -3884,98 +4062,28 @@ function ModelsView() {
3884
4062
  }
3885
4063
  }).catch((e) => setMsg({ ok: false, text: e && e.message || String(e) })).then(() => setSaving(false));
3886
4064
  };
3887
- const modelRow = (m, i) => import_react3.default.createElement(
3888
- "div",
3889
- { key: i, className: "dkm-model" },
3890
- import_react3.default.createElement(
3891
- "div",
3892
- { className: "dkm-model-head" },
3893
- import_react3.default.createElement("span", { className: "dkm-model-idx" }, "#" + (i + 1)),
3894
- import_react3.default.createElement("button", {
3895
- type: "button",
3896
- className: "dkm-del",
3897
- onClick: () => patchDraft({ models: draft.models.filter((_, j) => j !== i) })
3898
- }, "\u5220\u9664")
3899
- ),
3900
- import_react3.default.createElement(
3901
- "div",
3902
- { className: "dkm-grid" },
3903
- import_react3.default.createElement("input", { className: "dkm-input", placeholder: "\u6A21\u578B id\uFF08\u5FC5\u586B\uFF0C\u5982 deepseek-v4-pro\uFF09", value: m.id, onChange: (e) => patchModel(i, { id: e.target.value }) }),
3904
- import_react3.default.createElement("input", { className: "dkm-input", placeholder: "\u663E\u793A\u540D\u79F0\uFF08\u53EF\u9009\uFF09", value: m.name, onChange: (e) => patchModel(i, { name: e.target.value }) }),
3905
- import_react3.default.createElement("input", { className: "dkm-input", placeholder: "\u4E0A\u4E0B\u6587\u7A97\u53E3", inputMode: "numeric", value: m.contextWindow, onChange: (e) => patchModel(i, { contextWindow: e.target.value }) }),
3906
- import_react3.default.createElement("input", { className: "dkm-input", placeholder: "\u6700\u5927\u8F93\u51FA", inputMode: "numeric", value: m.maxTokens, onChange: (e) => patchModel(i, { maxTokens: e.target.value }) })
3907
- ),
3908
- import_react3.default.createElement(
3909
- "div",
3910
- { className: "dkm-checks" },
3911
- import_react3.default.createElement("span", { className: "dkm-label" }, "\u8F93\u5165\u7C7B\u578B\uFF1A"),
3912
- DKM_MODALITIES.map(([v, label]) => import_react3.default.createElement(
3913
- "label",
3914
- {
3915
- key: v,
3916
- className: "dkm-check",
3917
- title: v === "image" ? "\u7531\u4F60\u58F0\u660E\u8BE5\u7AEF\u70B9\u652F\u6301\u56FE\u7247\uFF1A\u4FDD\u5B58\u540E\u539F\u56FE\u76F4\u53D1\uFF0C\u5E76\u53EF\u4F5C\u4E3A\u89C6\u89C9\u6A21\u578B\u5019\u9009\u3002\u7CFB\u7EDF\u4E0D\u4F1A\u66FF\u4F60\u731C\u6D4B\uFF1B\u7AEF\u70B9\u5B9E\u9645\u4E0D\u652F\u6301\u65F6\u53D6\u6D88\u52FE\u9009\u5373\u53EF\u6062\u590D\u8D70\u56FE\u7247\u7406\u89E3\u4EE3\u7406" : null
3918
- },
3919
- import_react3.default.createElement("input", {
3920
- type: "checkbox",
3921
- checked: m.input.indexOf(v) >= 0,
3922
- onChange: () => {
3923
- const next = toggleIn(m.input, v);
3924
- patchModel(i, { input: next.length > 0 ? next : ["text"] });
3925
- }
3926
- }),
3927
- label
3928
- )),
3929
- import_react3.default.createElement("span", { className: "dkm-label dkm-tag", title: "\u6807\u6CE8\u4EC5\u5728\u672C\u9762\u677F\u5C55\u793A\u4E0E\u6301\u4E45\u5316\uFF0C\u4E0D\u53C2\u4E0E\u8BF7\u6C42\u8DEF\u7531\uFF08\u5B98\u65B9 schema \u53EA\u63A5\u53D7\u6587\u672C/\u56FE\u7247\uFF09" }, "\u6807\u6CE8\uFF1A"),
3930
- DKM_TAGS.map(([v, label]) => import_react3.default.createElement(
3931
- "label",
3932
- { key: v, className: "dkm-check dkm-tag" },
3933
- import_react3.default.createElement("input", { type: "checkbox", checked: m.tags.indexOf(v) >= 0, onChange: () => patchModel(i, { tags: toggleIn(m.tags, v) }) }),
3934
- label
3935
- )),
3936
- import_react3.default.createElement("button", { type: "button", className: "dkm-mini", title: "\u4E00\u952E\u52FE\u9009\u5168\u90E8\u8F93\u5165\u7C7B\u578B\u4E0E\u6807\u6CE8", onClick: () => patchModel(i, allInputs(true)) }, "\u5168\u9009"),
3937
- import_react3.default.createElement("button", { type: "button", className: "dkm-mini", title: "\u4E00\u952E\u53D6\u6D88\u5168\u90E8\u52FE\u9009\uFF08\u8F93\u5165\u7559\u7A7A = \u7EE7\u627F\u76EE\u5F55\u9ED8\u8BA4\uFF09", onClick: () => patchModel(i, allInputs(false)) }, "\u53D6\u6D88\u5168\u9009")
3938
- ),
3939
- import_react3.default.createElement(
3940
- "div",
3941
- { className: "dkm-efforts" },
3942
- import_react3.default.createElement("span", { className: "dkm-label" }, "\u601D\u8003\u5F3A\u5EA6\uFF1A"),
3943
- cur.kind === "deepseek" ? import_react3.default.createElement("span", { className: "dkm-sub" }, "\u968F Provider \u8BBE\u7F6E\uFF08Off/Low/High/Max\uFF0C\u5168\u6A21\u578B\u5171\u4EAB\uFF09") : import_react3.default.createElement(
3944
- "span",
3945
- null,
3946
- import_react3.default.createElement(
3947
- "select",
3948
- {
3949
- className: "dkm-select",
3950
- value: m.effortsMode,
3951
- onChange: (e) => patchModel(i, {
3952
- effortsMode: e.target.value,
3953
- effortLevels: e.target.value === "custom" && m.effortsMode !== "custom" ? { off: true, low: true, high: true } : m.effortLevels
3954
- })
3955
- },
3956
- import_react3.default.createElement("option", { value: "inherit" }, "\u8DDF\u968F\u76EE\u5F55\u9ED8\u8BA4"),
3957
- import_react3.default.createElement("option", { value: "off" }, "\u4E0D\u652F\u6301\u601D\u8003"),
3958
- import_react3.default.createElement("option", { value: "custom" }, "\u81EA\u5B9A\u4E49\u6863\u4F4D")
3959
- ),
3960
- m.effortsMode === "custom" ? DKM_PI_LEVELS.map((lv) => import_react3.default.createElement(
3961
- "label",
3962
- { key: lv, className: "dkm-check" },
3963
- import_react3.default.createElement("input", {
3964
- type: "checkbox",
3965
- checked: !!m.effortLevels[lv],
3966
- onChange: () => {
3967
- const nl = Object.assign({}, m.effortLevels);
3968
- nl[lv] = !nl[lv];
3969
- patchModel(i, { effortLevels: nl });
3970
- }
3971
- }),
3972
- DKM_EFFORT_NAMES[lv] || lv
3973
- )) : null,
3974
- import_react3.default.createElement("button", { type: "button", className: "dkm-mini", title: "\u4E00\u952E\u52FE\u9009\u5168\u90E8\u5F3A\u5EA6\u6863\uFF08\u5207\u4E3A\u81EA\u5B9A\u4E49\uFF09", onClick: () => patchModel(i, allLevels(true)) }, "\u5168\u9009"),
3975
- import_react3.default.createElement("button", { type: "button", className: "dkm-mini", title: "\u4E00\u952E\u53D6\u6D88\u5168\u90E8\u5F3A\u5EA6\u6863", onClick: () => patchModel(i, allLevels(false)) }, "\u53D6\u6D88\u5168\u9009")
3976
- )
3977
- )
3978
- );
4065
+ const modelRow = (m, i) => ModelRow({
4066
+ m,
4067
+ i,
4068
+ kind: cur.kind,
4069
+ onPatch: (j, patch) => patchModel(j, patch),
4070
+ onDel: (j) => patchDraft({ models: draft.models.filter((_, k) => k !== j) })
4071
+ });
4072
+ if (page === "providers") {
4073
+ return import_react3.default.createElement(ProvidersPage, {
4074
+ data,
4075
+ providers,
4076
+ loading: snap.loading,
4077
+ error: snap.error,
4078
+ openCreate: pageOpenCreate,
4079
+ onCreateHandled: () => setPageOpenCreate(false),
4080
+ // 返回主页时顺手重读目录:子页面里增删改过的 Provider 立即反映到模型编辑器
4081
+ onBack: () => {
4082
+ setPage("settings");
4083
+ refreshModels();
4084
+ }
4085
+ });
4086
+ }
3979
4087
  const body = [];
3980
4088
  if (data) {
3981
4089
  body.push(import_react3.default.createElement(
@@ -4115,6 +4223,44 @@ function ModelsView() {
4115
4223
  )
4116
4224
  ));
4117
4225
  }
4226
+ if (data) {
4227
+ const custom = providers.filter((p) => p.kind === "pi-ai");
4228
+ const missingKey = custom.filter((p) => p.apiKeyEnv && !p.apiKeyConfigured).length;
4229
+ body.push(import_react3.default.createElement(
4230
+ "div",
4231
+ { key: "pf-entry", className: "dkp-card" },
4232
+ import_react3.default.createElement(
4233
+ "div",
4234
+ { className: "dkp-rowhead" },
4235
+ import_react3.default.createElement(
4236
+ "span",
4237
+ { className: "dkp-identity" },
4238
+ import_react3.default.createElement("span", { className: "dkp-name" }, "\u6A21\u578B\u63D0\u4F9B\u5546"),
4239
+ import_react3.default.createElement("span", { className: "dkp-tag" }, custom.length + " \u4E2A\u81EA\u5B9A\u4E49\u8DEF\u7531"),
4240
+ custom.length > 0 ? import_react3.default.createElement("span", {
4241
+ className: "dkp-dot " + (missingKey > 0 ? "warn" : "ok"),
4242
+ role: "img",
4243
+ title: missingKey > 0 ? "\u6709 " + missingKey + " \u4E2A Provider \u672A\u914D\u7F6E\u5BC6\u94A5" : "\u5BC6\u94A5\u5747\u5DF2\u914D\u7F6E",
4244
+ "aria-label": missingKey > 0 ? "\u6709 Provider \u672A\u914D\u7F6E\u5BC6\u94A5" : "\u5BC6\u94A5\u5747\u5DF2\u914D\u7F6E"
4245
+ }) : null
4246
+ ),
4247
+ import_react3.default.createElement(
4248
+ "span",
4249
+ { className: "dkp-actions" },
4250
+ import_react3.default.createElement("button", {
4251
+ type: "button",
4252
+ className: "dkp-btn ghost sm",
4253
+ onClick: () => setPage("providers")
4254
+ }, "\u7BA1\u7406")
4255
+ )
4256
+ ),
4257
+ import_react3.default.createElement(
4258
+ "p",
4259
+ { className: "dkp-hint" },
4260
+ "\u65B0\u589E / \u7F16\u8F91 / \u5220\u9664\u81EA\u5B9A\u4E49\u6A21\u578B Provider\uFF08OpenAI \u517C\u5BB9\u3001Anthropic \u7B49\uFF09\uFF0C\u5199\u5165\u5B98\u65B9\u914D\u7F6E\u5E76\u70ED\u751F\u6548\uFF1BAPI Key \u5B58\u672C\u673A\u51ED\u636E\u5E93\uFF08\u6587\u4EF6\u4EC5 root \u53EF\u8BFB\uFF09\u3002"
4261
+ )
4262
+ ));
4263
+ }
4118
4264
  if (data && providers.length === 0) {
4119
4265
  body.push(import_react3.default.createElement("div", { key: "empty", className: "dkm-note" }, "\u6CA1\u6709\u53EF\u7F16\u8F91\u7684\u6A21\u578B Provider\uFF08Host \u4FA7\u5C1A\u672A\u914D\u7F6E\uFF09"));
4120
4266
  }
@@ -4133,63 +4279,589 @@ function ModelsView() {
4133
4279
  if (cur && draft) {
4134
4280
  body.push(import_react3.default.createElement(
4135
4281
  "div",
4136
- { key: "prov", className: "dkm-prov" },
4282
+ { key: "prov", className: "dkm-prov" },
4283
+ import_react3.default.createElement(
4284
+ "div",
4285
+ { className: "dkm-prov-head" },
4286
+ import_react3.default.createElement("span", { className: "dkm-name" }, cur.displayName),
4287
+ import_react3.default.createElement("span", { className: "dkm-badge" }, cur.kind === "deepseek" ? "\u5B98\u65B9 DeepSeek" : "\u81EA\u5B9A\u4E49\u8DEF\u7531"),
4288
+ import_react3.default.createElement("span", { className: "dkm-sub" }, [cur.api, cur.baseURL].filter(Boolean).join(" \xB7 "))
4289
+ ),
4290
+ cur.kind === "deepseek" ? import_react3.default.createElement(
4291
+ "div",
4292
+ { className: "dkm-controls" },
4293
+ import_react3.default.createElement("span", { className: "dkm-label" }, "\u601D\u8003\u6A21\u5F0F\uFF1A"),
4294
+ import_react3.default.createElement(
4295
+ "select",
4296
+ { className: "dkm-select", value: draft.thinking, onChange: (e) => patchDraft({ thinking: e.target.value }) },
4297
+ import_react3.default.createElement("option", { value: "enabled" }, "\u5F00\u542F"),
4298
+ import_react3.default.createElement("option", { value: "disabled" }, "\u5173\u95ED")
4299
+ ),
4300
+ import_react3.default.createElement("span", { className: "dkm-label" }, "\u9ED8\u8BA4\u5F3A\u5EA6\uFF1A"),
4301
+ import_react3.default.createElement(
4302
+ "select",
4303
+ { className: "dkm-select", value: draft.defaultEffort, onChange: (e) => patchDraft({ defaultEffort: e.target.value }) },
4304
+ DKM_DS_LEVELS.map((lv) => import_react3.default.createElement("option", { key: lv, value: lv }, DKM_EFFORT_NAMES[lv] || lv))
4305
+ )
4306
+ ) : null,
4307
+ import_react3.default.createElement(
4308
+ "div",
4309
+ { className: "dkm-toolbar" },
4310
+ import_react3.default.createElement("span", { className: "dkm-toolbar-label" }, "\u6279\u91CF\uFF08\u5168\u90E8\u6A21\u578B\uFF09\uFF1A"),
4311
+ import_react3.default.createElement("button", { type: "button", className: "dkm-mini", title: "\u5168\u90E8\u6A21\u578B\uFF1A\u52FE\u9009\u5168\u90E8\u8F93\u5165\u7C7B\u578B\u4E0E\u6807\u6CE8", onClick: () => bulkModels(() => allInputs(true)) }, "\u8F93\u5165\u5168\u9009"),
4312
+ import_react3.default.createElement("button", { type: "button", className: "dkm-mini", title: "\u5168\u90E8\u6A21\u578B\uFF1A\u53D6\u6D88\u5168\u90E8\u8F93\u5165\u52FE\u9009\uFF08\u7559\u7A7A = \u7EE7\u627F\u76EE\u5F55\u9ED8\u8BA4\uFF09", onClick: () => bulkModels(() => allInputs(false)) }, "\u8F93\u5165\u53D6\u6D88\u5168\u9009"),
4313
+ cur.kind !== "deepseek" ? import_react3.default.createElement("button", { type: "button", className: "dkm-mini", title: "\u5168\u90E8\u6A21\u578B\uFF1A\u52FE\u9009\u5168\u90E8\u5F3A\u5EA6\u6863\uFF08\u5207\u4E3A\u81EA\u5B9A\u4E49\uFF09", onClick: () => bulkModels(() => allLevels(true)) }, "\u5F3A\u5EA6\u5168\u9009") : null,
4314
+ cur.kind !== "deepseek" ? import_react3.default.createElement("button", { type: "button", className: "dkm-mini", title: "\u5168\u90E8\u6A21\u578B\uFF1A\u53D6\u6D88\u5168\u90E8\u5F3A\u5EA6\u6863", onClick: () => bulkModels(() => allLevels(false)) }, "\u5F3A\u5EA6\u53D6\u6D88\u5168\u9009") : null
4315
+ ),
4316
+ import_react3.default.createElement(
4317
+ "div",
4318
+ { style: { display: "flex", flexDirection: "column", gap: "8px" } },
4319
+ draft.models.map(modelRow),
4320
+ import_react3.default.createElement("button", {
4321
+ type: "button",
4322
+ className: "dkm-add",
4323
+ onClick: () => patchDraft({
4324
+ models: draft.models.concat([{ id: "", name: "", contextWindow: "", maxTokens: "", input: ["text"], tags: [], effortsMode: "inherit", effortLevels: {} }])
4325
+ })
4326
+ }, "+ \u6DFB\u52A0\u6A21\u578B")
4327
+ ),
4328
+ import_react3.default.createElement(
4329
+ "div",
4330
+ { className: "dkm-savebar" },
4331
+ import_react3.default.createElement("span", { className: "dkm-msg" }, dirty ? "\u6709\u672A\u4FDD\u5B58\u7684\u4FEE\u6539" : "\u65E0\u672A\u4FDD\u5B58\u4FEE\u6539"),
4332
+ import_react3.default.createElement("button", { type: "button", className: "dkm-save", disabled: !dirty || saving, onClick: save }, saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u5199\u56DE\u5B98\u65B9\u914D\u7F6E"),
4333
+ import_react3.default.createElement("button", { type: "button", className: "dkb-refresh", disabled: saving || snap.loading, onClick: refreshModels }, snap.loading ? "\u5237\u65B0\u4E2D\u2026" : "\u91CD\u65B0\u62C9\u53D6"),
4334
+ msg ? import_react3.default.createElement("span", { className: "dkm-msg " + (msg.ok ? "ok" : "err") }, msg.text) : null
4335
+ )
4336
+ ));
4337
+ }
4338
+ return import_react3.default.createElement("div", { className: "dkm-list" }, body);
4339
+ }
4340
+ function ProviderModelRow(props) {
4341
+ const m = props.m;
4342
+ const i = props.i;
4343
+ const check = (v, list, kind) => import_react3.default.createElement(
4344
+ "label",
4345
+ { key: v[0], className: "dkp-check" },
4346
+ import_react3.default.createElement("input", {
4347
+ type: "checkbox",
4348
+ checked: list.indexOf(v[0]) >= 0,
4349
+ onChange: () => {
4350
+ const next = dkmToggleIn(list, v[0]);
4351
+ props.onPatch(i, kind === "input" ? { input: next.length > 0 ? next : ["text"] } : { tags: next });
4352
+ }
4353
+ }),
4354
+ v[1]
4355
+ );
4356
+ return import_react3.default.createElement(
4357
+ "div",
4358
+ { className: "dkp-model-entry" },
4359
+ import_react3.default.createElement(
4360
+ "div",
4361
+ { className: "dkp-model-row" },
4362
+ import_react3.default.createElement("input", { className: "dkp-input", placeholder: "\u6A21\u578B id\uFF08\u5FC5\u586B\uFF09", value: m.id, onChange: (e) => props.onPatch(i, { id: e.target.value }) }),
4363
+ import_react3.default.createElement("input", { className: "dkp-input", placeholder: "\u663E\u793A\u540D\u79F0\uFF08\u53EF\u9009\uFF09", value: m.name, onChange: (e) => props.onPatch(i, { name: e.target.value }) }),
4364
+ import_react3.default.createElement("input", { className: "dkp-input", placeholder: "\u4E0A\u4E0B\u6587\u7A97\u53E3", inputMode: "numeric", value: m.contextWindow, onChange: (e) => props.onPatch(i, { contextWindow: e.target.value }) }),
4365
+ import_react3.default.createElement("input", { className: "dkp-input", placeholder: "\u6700\u5927\u8F93\u51FA", inputMode: "numeric", value: m.maxTokens, onChange: (e) => props.onPatch(i, { maxTokens: e.target.value }) }),
4366
+ import_react3.default.createElement("button", { type: "button", className: "dkp-iconbtn danger", title: "\u5220\u9664\u8BE5\u6A21\u578B", "aria-label": "\u5220\u9664\u6A21\u578B", onClick: () => props.onDel(i) }, "\u2715")
4367
+ ),
4368
+ import_react3.default.createElement(
4369
+ "div",
4370
+ { className: "dkp-model-opts" },
4371
+ import_react3.default.createElement(
4372
+ "div",
4373
+ { className: "dkp-checks" },
4374
+ import_react3.default.createElement("span", { className: "dkp-flabel" }, "\u8F93\u5165\u7C7B\u578B\uFF1A"),
4375
+ DKM_MODALITIES.map((v) => check(v, m.input, "input")),
4376
+ import_react3.default.createElement("span", { className: "dkp-flabel dkp-dim", title: "\u6807\u6CE8\u4EC5\u5728\u672C\u9762\u677F\u5C55\u793A\u4E0E\u6301\u4E45\u5316\uFF0C\u4E0D\u53C2\u4E0E\u8BF7\u6C42\u8DEF\u7531\uFF08\u5B98\u65B9 schema \u53EA\u63A5\u53D7\u6587\u672C/\u56FE\u7247\uFF09" }, "\u6807\u6CE8\uFF1A"),
4377
+ DKM_TAGS.map((v) => check(v, m.tags, "tags")),
4378
+ import_react3.default.createElement("button", { type: "button", className: "dkp-mini", title: "\u4E00\u952E\u52FE\u9009\u5168\u90E8\u8F93\u5165\u7C7B\u578B\u4E0E\u6807\u6CE8", onClick: () => props.onPatch(i, dkmAllInputs(true)) }, "\u5168\u9009"),
4379
+ import_react3.default.createElement("button", { type: "button", className: "dkp-mini", title: "\u4E00\u952E\u53D6\u6D88\u5168\u90E8\u52FE\u9009", onClick: () => props.onPatch(i, dkmAllInputs(false)) }, "\u53D6\u6D88\u5168\u9009")
4380
+ ),
4381
+ import_react3.default.createElement(
4382
+ "div",
4383
+ { className: "dkp-checks" },
4384
+ import_react3.default.createElement("span", { className: "dkp-flabel" }, "\u601D\u8003\u5F3A\u5EA6\uFF1A"),
4385
+ import_react3.default.createElement(
4386
+ "select",
4387
+ {
4388
+ className: "dkp-input dkp-select dkp-select-sm",
4389
+ value: m.effortsMode,
4390
+ onChange: (e) => props.onPatch(i, {
4391
+ effortsMode: e.target.value,
4392
+ effortLevels: e.target.value === "custom" && m.effortsMode !== "custom" ? { off: true, low: true, high: true } : m.effortLevels
4393
+ })
4394
+ },
4395
+ import_react3.default.createElement("option", { value: "inherit" }, "\u8DDF\u968F\u76EE\u5F55\u9ED8\u8BA4"),
4396
+ import_react3.default.createElement("option", { value: "off" }, "\u4E0D\u652F\u6301\u601D\u8003"),
4397
+ import_react3.default.createElement("option", { value: "custom" }, "\u81EA\u5B9A\u4E49\u6863\u4F4D")
4398
+ ),
4399
+ m.effortsMode === "custom" ? DKM_PI_LEVELS.map((lv) => import_react3.default.createElement(
4400
+ "label",
4401
+ { key: lv, className: "dkp-check" },
4402
+ import_react3.default.createElement("input", {
4403
+ type: "checkbox",
4404
+ checked: !!m.effortLevels[lv],
4405
+ onChange: () => {
4406
+ const nl = Object.assign({}, m.effortLevels);
4407
+ nl[lv] = !nl[lv];
4408
+ props.onPatch(i, { effortLevels: nl });
4409
+ }
4410
+ }),
4411
+ DKM_EFFORT_NAMES[lv] || lv
4412
+ )) : null,
4413
+ import_react3.default.createElement("button", { type: "button", className: "dkp-mini", title: "\u4E00\u952E\u52FE\u9009\u5168\u90E8\u5F3A\u5EA6\u6863\uFF08\u5207\u4E3A\u81EA\u5B9A\u4E49\uFF09", onClick: () => props.onPatch(i, dkmAllLevels(true)) }, "\u5168\u9009"),
4414
+ import_react3.default.createElement("button", { type: "button", className: "dkp-mini", title: "\u4E00\u952E\u53D6\u6D88\u5168\u90E8\u5F3A\u5EA6\u6863", onClick: () => props.onPatch(i, dkmAllLevels(false)) }, "\u53D6\u6D88\u5168\u9009")
4415
+ )
4416
+ )
4417
+ );
4418
+ }
4419
+ function ProvidersPage(props) {
4420
+ const data = props.data;
4421
+ const providers = props.providers || [];
4422
+ const piProviders = providers.filter((p) => p.kind === "pi-ai");
4423
+ const [pfMode, setPfMode] = import_react3.default.useState(null);
4424
+ const [pfDraft, setPfDraft] = import_react3.default.useState(null);
4425
+ const [pfDirty, setPfDirty] = import_react3.default.useState(false);
4426
+ const [pfSaving, setPfSaving] = import_react3.default.useState(false);
4427
+ const [pfMsg, setPfMsg] = import_react3.default.useState(null);
4428
+ const [pfDelArm, setPfDelArm] = import_react3.default.useState(null);
4429
+ const [pfFetching, setPfFetching] = import_react3.default.useState(false);
4430
+ const [pfFetchedList, setPfFetchedList] = import_react3.default.useState(null);
4431
+ const openPfCreate = () => {
4432
+ setPfMode("create");
4433
+ setPfDraft(emptyProviderForm());
4434
+ setPfDirty(false);
4435
+ setPfMsg(null);
4436
+ setPfDelArm(null);
4437
+ setPfFetchedList(null);
4438
+ };
4439
+ const openPfEdit = (p) => {
4440
+ setPfMode("edit");
4441
+ setPfDraft(providerFormFromProvider(p));
4442
+ setPfDirty(false);
4443
+ setPfMsg(null);
4444
+ setPfDelArm(null);
4445
+ setPfFetchedList(null);
4446
+ };
4447
+ const closePf = () => {
4448
+ setPfMode(null);
4449
+ setPfDraft(null);
4450
+ setPfDirty(false);
4451
+ setPfMsg(null);
4452
+ setPfDelArm(null);
4453
+ setPfFetchedList(null);
4454
+ };
4455
+ const patchPf = (patch) => {
4456
+ setPfDraft(Object.assign({}, pfDraft, patch));
4457
+ setPfDirty(true);
4458
+ setPfMsg(null);
4459
+ };
4460
+ const patchPfModel = (i, patch) => {
4461
+ const models = pfDraft.models.slice();
4462
+ models[i] = Object.assign({}, models[i], patch);
4463
+ patchPf({ models });
4464
+ };
4465
+ const delPfModel = (i) => patchPf({ models: pfDraft.models.filter((_, j) => j !== i) });
4466
+ const addPfModel = () => patchPf({ models: pfDraft.models.concat([emptyModelDraft()]) });
4467
+ const togglePfModel = (id, name, on) => {
4468
+ const base = pfDraft.models.filter((m) => String(m.id || "").trim());
4469
+ if (on) {
4470
+ if (base.some((m) => String(m.id).trim() === id)) return;
4471
+ base.push(Object.assign(emptyModelDraft(), { id, name: name || "", input: pfDraft.defaultInput.slice() }));
4472
+ } else {
4473
+ const i = base.findIndex((m) => String(m.id).trim() === id);
4474
+ if (i >= 0) base.splice(i, 1);
4475
+ }
4476
+ patchPf({ models: base });
4477
+ };
4478
+ const setAllPfModels = (on) => {
4479
+ const base = [];
4480
+ if (on && pfFetchedList) {
4481
+ for (const mm of pfFetchedList) {
4482
+ const id = String(mm && mm.id || "").trim();
4483
+ if (!id) continue;
4484
+ base.push(Object.assign(emptyModelDraft(), { id, name: mm && mm.name || "", input: pfDraft.defaultInput.slice() }));
4485
+ }
4486
+ }
4487
+ patchPf({ models: base });
4488
+ };
4489
+ const pfSelectedCount = () => pfDraft ? pfDraft.models.filter((m) => String(m.id || "").trim()).length : 0;
4490
+ const pfModelsPayload = () => pfDraft.models.map((m) => ({
4491
+ id: String(m.id || "").trim(),
4492
+ name: String(m.name || "").trim(),
4493
+ contextWindow: m.contextWindow === "" || m.contextWindow == null ? void 0 : Number(m.contextWindow),
4494
+ maxTokens: m.maxTokens === "" || m.maxTokens == null ? void 0 : Number(m.maxTokens),
4495
+ input: m.input,
4496
+ tags: m.tags,
4497
+ effortsMode: m.effortsMode,
4498
+ effortLevels: m.effortLevels,
4499
+ raw: m.raw
4500
+ }));
4501
+ const fetchPfModels = () => {
4502
+ if (!pfDraft || pfFetching) return;
4503
+ setPfFetching(true);
4504
+ setPfMsg(null);
4505
+ fetch("dsh-dock/models", {
4506
+ method: "POST",
4507
+ headers: { "content-type": "application/json" },
4508
+ body: JSON.stringify({
4509
+ action: "listModels",
4510
+ baseURL: pfDraft.baseURL,
4511
+ api: pfDraft.api,
4512
+ apiKeyEnv: pfDraft.apiKeyEnv,
4513
+ apiKey: pfDraft.apiKey
4514
+ }),
4515
+ signal: AbortSignal.timeout(3e4)
4516
+ }).then((res) => res.json().then((b) => ({ status: res.status, body: b })).catch(() => ({ status: res.status, body: {} }))).then((r) => {
4517
+ if (r.status === 200 && r.body && r.body.ok) {
4518
+ setPfFetchedList(Array.isArray(r.body.models) ? r.body.models : []);
4519
+ } else {
4520
+ setPfMsg({ ok: false, text: r.body && r.body.error || "HTTP " + r.status });
4521
+ }
4522
+ }).catch((e) => setPfMsg({ ok: false, text: e && e.message || String(e) })).then(() => setPfFetching(false));
4523
+ };
4524
+ const savePf = () => {
4525
+ if (!pfDraft || pfSaving) return;
4526
+ const route = pfDraft.route.trim();
4527
+ const baseURL = pfDraft.baseURL.trim();
4528
+ const apiKey = pfDraft.apiKey.trim();
4529
+ let apiKeyEnv = pfDraft.apiKeyEnv.trim();
4530
+ if (apiKey && !apiKeyEnv) {
4531
+ apiKeyEnv = dkmEnvFromRoute(route);
4532
+ setPfDraft(Object.assign({}, pfDraft, { apiKeyEnv }));
4533
+ }
4534
+ const models = pfModelsPayload();
4535
+ if (!route) {
4536
+ setPfMsg({ ok: false, text: "\u8DEF\u7531 key \u4E0D\u80FD\u4E3A\u7A7A\uFF08\u5982 modelscope\uFF09" });
4537
+ return;
4538
+ }
4539
+ if (!DKM_ROUTE_KEY_RE.test(route)) {
4540
+ setPfMsg({ ok: false, text: "\u8DEF\u7531 key \u975E\u6CD5\uFF1A\u53EA\u80FD\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u4E0E . _ -\uFF0C\u4E14\u4EE5\u5B57\u6BCD/\u6570\u5B57\u5F00\u5934" });
4541
+ return;
4542
+ }
4543
+ if (!/^https?:\/\//i.test(baseURL)) {
4544
+ setPfMsg({ ok: false, text: "baseURL \u987B\u4EE5 http:// \u6216 https:// \u5F00\u5934" });
4545
+ return;
4546
+ }
4547
+ const ids = models.map((m) => m.id);
4548
+ if (ids.length === 0 || ids.some((x) => !x)) {
4549
+ setPfMsg({ ok: false, text: "\u6BCF\u4E2A\u6A21\u578B\u90FD\u8981\u6709 id\uFF08\u53EF\u70B9\u300C\u83B7\u53D6\u6A21\u578B\u5217\u8868\u300D\u81EA\u52A8\u62C9\u53D6\uFF09" });
4550
+ return;
4551
+ }
4552
+ if (new Set(ids).size !== ids.length) {
4553
+ setPfMsg({ ok: false, text: "\u6A21\u578B id \u6709\u91CD\u590D" });
4554
+ return;
4555
+ }
4556
+ const profile = Object.assign({}, pfDraft.extra || {});
4557
+ const dn = pfDraft.displayName.trim();
4558
+ if (dn) profile.displayName = dn;
4559
+ else delete profile.displayName;
4560
+ profile.api = pfDraft.api;
4561
+ profile.baseURL = baseURL;
4562
+ if (apiKeyEnv) profile.apiKeyEnv = apiKeyEnv;
4563
+ else delete profile.apiKeyEnv;
4564
+ profile.defaultInput = pfDraft.defaultInput;
4565
+ profile.models = models;
4566
+ const payload = {
4567
+ action: pfMode === "create" ? "create" : "update",
4568
+ route,
4569
+ profile,
4570
+ revisions: data && data.revisions || {}
4571
+ };
4572
+ if (apiKey) payload.apiKey = apiKey;
4573
+ setPfSaving(true);
4574
+ setPfMsg(null);
4575
+ fetch("dsh-dock/models", {
4576
+ method: "POST",
4577
+ headers: { "content-type": "application/json" },
4578
+ body: JSON.stringify(payload),
4579
+ signal: AbortSignal.timeout(3e4)
4580
+ }).then((res) => res.json().then((b) => ({ status: res.status, body: b })).catch(() => ({ status: res.status, body: {} }))).then((r) => {
4581
+ if (r.status === 200 && r.body && r.body.ok) {
4582
+ setPfMsg({ ok: true, text: "Provider\u300C" + route + "\u300D\u5DF2" + (pfMode === "create" ? "\u65B0\u589E" : "\u66F4\u65B0") + "\uFF1B\u4F1A\u8BDD\u6A21\u578B\u9009\u62E9\u5668\u5373\u65F6\u53EF\u9009" });
4583
+ setPfMode(null);
4584
+ setPfDraft(null);
4585
+ setPfDirty(false);
4586
+ modelsStore.load();
4587
+ } else {
4588
+ setPfMsg({ ok: false, text: r.body && r.body.error || "HTTP " + r.status });
4589
+ }
4590
+ }).catch((e) => setPfMsg({ ok: false, text: e && e.message || String(e) })).then(() => setPfSaving(false));
4591
+ };
4592
+ const deletePf = (p) => {
4593
+ const route = p.route || p.id;
4594
+ if (pfDelArm !== route) {
4595
+ setPfDelArm(route);
4596
+ setPfMsg(null);
4597
+ return;
4598
+ }
4599
+ setPfDelArm(null);
4600
+ setPfSaving(true);
4601
+ setPfMsg(null);
4602
+ fetch("dsh-dock/models", {
4603
+ method: "POST",
4604
+ headers: { "content-type": "application/json" },
4605
+ body: JSON.stringify({ action: "delete", route, revisions: data && data.revisions || {} }),
4606
+ signal: AbortSignal.timeout(2e4)
4607
+ }).then((res) => res.json().then((b) => ({ status: res.status, body: b })).catch(() => ({ status: res.status, body: {} }))).then((r) => {
4608
+ if (r.status === 200 && r.body && r.body.ok) {
4609
+ setPfMsg({ ok: true, text: "Provider\u300C" + route + "\u300D\u5DF2\u5220\u9664" });
4610
+ if (pfMode === "edit" && pfDraft && pfDraft.route === route) {
4611
+ setPfMode(null);
4612
+ setPfDraft(null);
4613
+ }
4614
+ modelsStore.load();
4615
+ } else {
4616
+ setPfMsg({ ok: false, text: r.body && r.body.error || "HTTP " + r.status });
4617
+ }
4618
+ }).catch((e) => setPfMsg({ ok: false, text: e && e.message || String(e) })).then(() => setPfSaving(false));
4619
+ };
4620
+ const openedRef = import_react3.default.useRef(false);
4621
+ import_react3.default.useEffect(() => {
4622
+ if (props.openCreate && !openedRef.current) {
4623
+ openedRef.current = true;
4624
+ openPfCreate();
4625
+ if (typeof props.onCreateHandled === "function") props.onCreateHandled();
4626
+ }
4627
+ }, []);
4628
+ const input = (p) => import_react3.default.createElement("input", Object.assign({ className: "dkp-input" }, p));
4629
+ const field = (label, node, hint) => import_react3.default.createElement(
4630
+ "div",
4631
+ { className: "dkp-field", key: label },
4632
+ import_react3.default.createElement("label", { className: "dkp-flabel" }, label),
4633
+ node,
4634
+ hint ? import_react3.default.createElement("p", { className: "dkp-hint" }, hint) : null
4635
+ );
4636
+ const editor = !pfDraft ? null : import_react3.default.createElement(
4637
+ "div",
4638
+ { className: "dkp-editor" },
4639
+ import_react3.default.createElement(
4640
+ "div",
4641
+ { className: "dkp-editor-head" },
4642
+ import_react3.default.createElement("span", { className: "dkp-editor-title" }, pfMode === "create" ? "\u65B0\u589E Provider" : "\u7F16\u8F91 Provider"),
4643
+ pfMode === "edit" ? import_react3.default.createElement("span", { className: "dkp-editor-route" }, pfDraft.route) : null
4644
+ ),
4645
+ field("\u8DEF\u7531 key", input({
4646
+ value: pfDraft.route,
4647
+ disabled: pfMode === "edit",
4648
+ placeholder: "acme-gateway",
4649
+ onChange: (e) => patchPf({ route: e.target.value })
4650
+ }), pfMode === "edit" ? "\u8DEF\u7531 key \u662F\u914D\u7F6E\u952E\uFF0C\u4FDD\u5B58\u540E\u4E0D\u5EFA\u8BAE\u4FEE\u6539\u3002" : "\u552F\u4E00\u6807\u8BC6\uFF1A\u53EA\u80FD\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u4E0E . _ -\uFF0C\u4E14\u4EE5\u5B57\u6BCD/\u6570\u5B57\u5F00\u5934\u3002"),
4651
+ field("\u663E\u793A\u540D\u79F0", input({
4652
+ value: pfDraft.displayName,
4653
+ placeholder: pfDraft.route || "\u5982 \u9B54\u642D ModelScope",
4654
+ onChange: (e) => patchPf({ displayName: e.target.value })
4655
+ })),
4656
+ field("baseURL", input({
4657
+ value: pfDraft.baseURL,
4658
+ placeholder: pfDraft.api === "anthropic-messages" ? "https://gateway.example" : "https://gateway.example/v1",
4659
+ onChange: (e) => patchPf({ baseURL: e.target.value })
4660
+ }), "OpenAI \u517C\u5BB9\u7AEF\u70B9\u901A\u5E38\u4EE5 /v1 \u7ED3\u5C3E\uFF1BAnthropic Messages \u7531 SDK \u81EA\u884C\u8FFD\u52A0 /v1/messages\u3002"),
4661
+ field("API \u534F\u8BAE", import_react3.default.createElement(
4662
+ "select",
4663
+ {
4664
+ className: "dkp-input dkp-select",
4665
+ value: pfDraft.api,
4666
+ onChange: (e) => patchPf({ api: e.target.value })
4667
+ },
4668
+ DKM_APIS.map(([v, label]) => import_react3.default.createElement("option", { key: v, value: v }, label))
4669
+ )),
4670
+ field("API Key", import_react3.default.createElement("input", {
4671
+ className: "dkp-input",
4672
+ type: "password",
4673
+ autoComplete: "new-password",
4674
+ placeholder: pfMode === "edit" ? "\u7559\u7A7A = \u4E0D\u6539\u52A8\u5DF2\u5B58\u5BC6\u94A5" : "sk-\u2026\uFF08\u4EC5\u672C\u6B21\u5199\u5165\u672C\u673A\u51ED\u636E\u5E93\uFF0C\u4E0D\u56DE\u663E\uFF09",
4675
+ value: pfDraft.apiKey,
4676
+ onChange: (e) => patchPf({ apiKey: e.target.value })
4677
+ })),
4678
+ import_react3.default.createElement(
4679
+ "details",
4680
+ { className: "dkp-customized" },
4681
+ import_react3.default.createElement("summary", { className: "dkp-summary" }, "\u81EA\u5B9A\u4E49\u8BBE\u7F6E"),
4137
4682
  import_react3.default.createElement(
4138
4683
  "div",
4139
- { className: "dkm-prov-head" },
4140
- import_react3.default.createElement("span", { className: "dkm-name" }, cur.displayName),
4141
- import_react3.default.createElement("span", { className: "dkm-badge" }, cur.kind === "deepseek" ? "\u5B98\u65B9 DeepSeek" : "\u81EA\u5B9A\u4E49\u8DEF\u7531"),
4142
- import_react3.default.createElement("span", { className: "dkm-sub" }, [cur.api, cur.baseURL].filter(Boolean).join(" \xB7 "))
4143
- ),
4144
- cur.kind === "deepseek" ? import_react3.default.createElement(
4684
+ { className: "dkp-custombody" },
4685
+ field("apiKeyEnv", input({
4686
+ value: pfDraft.apiKeyEnv,
4687
+ placeholder: "\u7559\u7A7A = \u6309\u8DEF\u7531 key \u81EA\u52A8\u751F\u6210\uFF08" + (dkmEnvFromRoute(pfDraft.route) || "ROUTE_API_KEY") + "\uFF09",
4688
+ onChange: (e) => patchPf({ apiKeyEnv: e.target.value })
4689
+ }), "\u51ED\u636E\u5E93\u6761\u76EE\u540D\uFF1B\u7559\u7A7A\u65F6\u6309\u8DEF\u7531 key \u63A8\u5BFC\uFF0C\u4E00\u822C\u65E0\u9700\u586B\u5199\u3002"),
4690
+ import_react3.default.createElement(
4691
+ "div",
4692
+ { className: "dkp-field" },
4693
+ import_react3.default.createElement("span", { className: "dkp-flabel" }, "\u9ED8\u8BA4\u8F93\u5165\u7C7B\u578B"),
4694
+ import_react3.default.createElement(
4695
+ "div",
4696
+ { className: "dkp-checks" },
4697
+ DKM_MODALITIES.map(([v, label]) => import_react3.default.createElement(
4698
+ "label",
4699
+ { key: v, className: "dkp-check" },
4700
+ import_react3.default.createElement("input", {
4701
+ type: "checkbox",
4702
+ checked: pfDraft.defaultInput.indexOf(v) >= 0,
4703
+ onChange: () => {
4704
+ const next = dkmToggleIn(pfDraft.defaultInput, v);
4705
+ patchPf({ defaultInput: next.length > 0 ? next : ["text"] });
4706
+ }
4707
+ }),
4708
+ label
4709
+ )),
4710
+ import_react3.default.createElement("span", { className: "dkp-hint" }, "\u6A21\u578B\u672A\u5355\u72EC\u58F0\u660E\u8F93\u5165\u7C7B\u578B\u65F6\u7EE7\u627F\u8FD9\u91CC\u3002")
4711
+ )
4712
+ )
4713
+ )
4714
+ ),
4715
+ import_react3.default.createElement(
4716
+ "section",
4717
+ { className: "dkp-catalog", "aria-label": "\u6A21\u578B" },
4718
+ import_react3.default.createElement(
4145
4719
  "div",
4146
- { className: "dkm-controls" },
4147
- import_react3.default.createElement("span", { className: "dkm-label" }, "\u601D\u8003\u6A21\u5F0F\uFF1A"),
4720
+ { className: "dkp-catalog-head" },
4148
4721
  import_react3.default.createElement(
4149
- "select",
4150
- { className: "dkm-select", value: draft.thinking, onChange: (e) => patchDraft({ thinking: e.target.value }) },
4151
- import_react3.default.createElement("option", { value: "enabled" }, "\u5F00\u542F"),
4152
- import_react3.default.createElement("option", { value: "disabled" }, "\u5173\u95ED")
4722
+ "div",
4723
+ { className: "dkp-catalog-titlewrap" },
4724
+ import_react3.default.createElement("span", { className: "dkp-catalog-title" }, "\u6A21\u578B"),
4725
+ import_react3.default.createElement(
4726
+ "span",
4727
+ { className: "dkp-catalog-meta" },
4728
+ "\u5DF2\u9009 " + pfSelectedCount() + " \u4E2A" + (pfFetchedList ? " / \u4E0A\u6E38\u5171 " + pfFetchedList.length + " \u4E2A" : "")
4729
+ )
4153
4730
  ),
4154
- import_react3.default.createElement("span", { className: "dkm-label" }, "\u9ED8\u8BA4\u5F3A\u5EA6\uFF1A"),
4155
4731
  import_react3.default.createElement(
4156
- "select",
4157
- { className: "dkm-select", value: draft.defaultEffort, onChange: (e) => patchDraft({ defaultEffort: e.target.value }) },
4158
- DKM_DS_LEVELS.map((lv) => import_react3.default.createElement("option", { key: lv, value: lv }, DKM_EFFORT_NAMES[lv] || lv))
4732
+ "div",
4733
+ { className: "dkp-actions" },
4734
+ import_react3.default.createElement("button", {
4735
+ type: "button",
4736
+ className: "dkp-btn ghost sm",
4737
+ disabled: pfFetching || !pfDraft.baseURL,
4738
+ title: "\u8C03\u7528 baseURL/models \u62C9\u53D6\u8BE5\u7AEF\u70B9\u53EF\u7528\u6A21\u578B\uFF0C\u52FE\u9009\u540E\u52A0\u5165\u4E0B\u65B9\u5217\u8868",
4739
+ onClick: fetchPfModels
4740
+ }, pfFetching ? "\u62C9\u53D6\u4E2D\u2026" : "\u83B7\u53D6\u6A21\u578B\u5217\u8868"),
4741
+ pfFetchedList && pfFetchedList.length > 0 ? import_react3.default.createElement("button", { type: "button", className: "dkp-btn ghost sm", title: "\u52FE\u9009\u4E0A\u6E38\u8FD4\u56DE\u7684\u5168\u90E8\u6A21\u578B", onClick: () => setAllPfModels(true) }, "\u5168\u9009") : null,
4742
+ pfFetchedList && pfFetchedList.length > 0 ? import_react3.default.createElement("button", { type: "button", className: "dkp-btn ghost sm", title: "\u6E05\u7A7A\u5DF2\u52FE\u9009\u7684\u6A21\u578B", onClick: () => setAllPfModels(false) }, "\u6E05\u7A7A") : null
4159
4743
  )
4160
- ) : null,
4161
- import_react3.default.createElement(
4162
- "div",
4163
- { className: "dkm-toolbar" },
4164
- import_react3.default.createElement("span", { className: "dkm-toolbar-label" }, "\u6279\u91CF\uFF08\u5168\u90E8\u6A21\u578B\uFF09\uFF1A"),
4165
- import_react3.default.createElement("button", { type: "button", className: "dkm-mini", title: "\u5168\u90E8\u6A21\u578B\uFF1A\u52FE\u9009\u5168\u90E8\u8F93\u5165\u7C7B\u578B\u4E0E\u6807\u6CE8", onClick: () => bulkModels(() => allInputs(true)) }, "\u8F93\u5165\u5168\u9009"),
4166
- import_react3.default.createElement("button", { type: "button", className: "dkm-mini", title: "\u5168\u90E8\u6A21\u578B\uFF1A\u53D6\u6D88\u5168\u90E8\u8F93\u5165\u52FE\u9009\uFF08\u7559\u7A7A = \u7EE7\u627F\u76EE\u5F55\u9ED8\u8BA4\uFF09", onClick: () => bulkModels(() => allInputs(false)) }, "\u8F93\u5165\u53D6\u6D88\u5168\u9009"),
4167
- cur.kind !== "deepseek" ? import_react3.default.createElement("button", { type: "button", className: "dkm-mini", title: "\u5168\u90E8\u6A21\u578B\uFF1A\u52FE\u9009\u5168\u90E8\u5F3A\u5EA6\u6863\uFF08\u5207\u4E3A\u81EA\u5B9A\u4E49\uFF09", onClick: () => bulkModels(() => allLevels(true)) }, "\u5F3A\u5EA6\u5168\u9009") : null,
4168
- cur.kind !== "deepseek" ? import_react3.default.createElement("button", { type: "button", className: "dkm-mini", title: "\u5168\u90E8\u6A21\u578B\uFF1A\u53D6\u6D88\u5168\u90E8\u5F3A\u5EA6\u6863", onClick: () => bulkModels(() => allLevels(false)) }, "\u5F3A\u5EA6\u53D6\u6D88\u5168\u9009") : null
4169
4744
  ),
4170
- import_react3.default.createElement(
4745
+ pfFetchedList ? import_react3.default.createElement(
4171
4746
  "div",
4172
- { style: { display: "flex", flexDirection: "column", gap: "8px" } },
4173
- draft.models.map(modelRow),
4174
- import_react3.default.createElement("button", {
4175
- type: "button",
4176
- className: "dkm-add",
4177
- onClick: () => patchDraft({
4178
- models: draft.models.concat([{ id: "", name: "", contextWindow: "", maxTokens: "", input: ["text"], tags: [], effortsMode: "inherit", effortLevels: {} }])
4179
- })
4180
- }, "+ \u6DFB\u52A0\u6A21\u578B")
4747
+ { className: "dkp-picker" },
4748
+ pfFetchedList.length === 0 ? import_react3.default.createElement("div", { className: "dkp-hint" }, "\u4E0A\u6E38\u6CA1\u6709\u8FD4\u56DE\u4EFB\u4F55\u6A21\u578B") : pfFetchedList.map((mm) => {
4749
+ const id = String(mm && mm.id || "").trim();
4750
+ if (!id) return null;
4751
+ const checked = pfDraft.models.some((m) => String(m.id || "").trim() === id);
4752
+ return import_react3.default.createElement(
4753
+ "label",
4754
+ { key: id },
4755
+ import_react3.default.createElement("input", {
4756
+ type: "checkbox",
4757
+ checked,
4758
+ onChange: () => togglePfModel(id, mm && mm.name || "", !checked)
4759
+ }),
4760
+ import_react3.default.createElement("span", { className: "dkp-picker-id" }, id),
4761
+ mm && mm.name ? import_react3.default.createElement("span", { className: "dkp-picker-name" }, mm.name) : null
4762
+ );
4763
+ })
4764
+ ) : null,
4765
+ pfDraft.models.length === 0 ? import_react3.default.createElement("div", { className: "dkp-empty" }, "\u8FD8\u6CA1\u6709\u6A21\u578B\u2014\u2014\u70B9\u300C\u83B7\u53D6\u6A21\u578B\u5217\u8868\u300D\u52FE\u9009\u4E0A\u6E38\u6A21\u578B\uFF0C\u6216\u300C\u6DFB\u52A0\u6A21\u578B\u300D\u624B\u52A8\u586B\u5199\u3002") : import_react3.default.createElement(
4766
+ "div",
4767
+ { className: "dkp-model-list" },
4768
+ pfDraft.models.map((m, i) => import_react3.default.createElement(ProviderModelRow, { key: i, m, i, onPatch: patchPfModel, onDel: delPfModel }))
4181
4769
  ),
4770
+ import_react3.default.createElement("button", { type: "button", className: "dkp-addmodel", onClick: addPfModel }, "\uFF0B \u6DFB\u52A0\u6A21\u578B")
4771
+ ),
4772
+ pfMsg && !pfMsg.ok ? import_react3.default.createElement("p", { className: "dkp-error" }, pfMsg.text) : null,
4773
+ import_react3.default.createElement(
4774
+ "div",
4775
+ { className: "dkp-editor-actions" },
4776
+ import_react3.default.createElement("span", { className: "dkp-hint" }, pfDirty ? "\u6709\u672A\u4FDD\u5B58\u7684\u4FEE\u6539" : ""),
4777
+ import_react3.default.createElement("button", { type: "button", className: "dkp-btn ghost", onClick: closePf }, "\u53D6\u6D88"),
4182
4778
  import_react3.default.createElement(
4183
- "div",
4184
- { className: "dkm-savebar" },
4185
- import_react3.default.createElement("span", { className: "dkm-msg" }, dirty ? "\u6709\u672A\u4FDD\u5B58\u7684\u4FEE\u6539" : "\u65E0\u672A\u4FDD\u5B58\u4FEE\u6539"),
4186
- import_react3.default.createElement("button", { type: "button", className: "dkm-save", disabled: !dirty || saving, onClick: save }, saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u5199\u56DE\u5B98\u65B9\u914D\u7F6E"),
4187
- import_react3.default.createElement("button", { type: "button", className: "dkb-refresh", disabled: saving || snap.loading, onClick: refreshModels }, snap.loading ? "\u5237\u65B0\u4E2D\u2026" : "\u91CD\u65B0\u62C9\u53D6"),
4188
- msg ? import_react3.default.createElement("span", { className: "dkm-msg " + (msg.ok ? "ok" : "err") }, msg.text) : null
4779
+ "button",
4780
+ { type: "button", className: "dkp-btn primary", disabled: pfSaving, onClick: savePf },
4781
+ pfSaving ? "\u4FDD\u5B58\u4E2D\u2026" : pfMode === "create" ? "\u65B0\u589E Provider" : "\u4FDD\u5B58\u4FEE\u6539"
4189
4782
  )
4783
+ )
4784
+ );
4785
+ const body = [
4786
+ import_react3.default.createElement("button", { key: "back", type: "button", className: "dkp-back", onClick: props.onBack }, "\u2190 \u6A21\u578B\u8BBE\u7F6E"),
4787
+ import_react3.default.createElement("h2", { key: "title", className: "dkp-title" }, "\u6A21\u578B\u63D0\u4F9B\u5546"),
4788
+ import_react3.default.createElement(
4789
+ "p",
4790
+ { key: "intro", className: "dkp-intro" },
4791
+ "\u65B0\u589E / \u7F16\u8F91 / \u5220\u9664\u81EA\u5B9A\u4E49\u6A21\u578B Provider\uFF08OpenAI \u517C\u5BB9\u3001Anthropic \u7B49\uFF09\uFF0C\u5199\u5165\u5B98\u65B9\u914D\u7F6E\u5E76\u70ED\u751F\u6548\uFF1BAPI Key \u5B58\u672C\u673A\u51ED\u636E\u5E93\uFF08\u6587\u4EF6\u4EC5 root \u53EF\u8BFB\uFF09\u3002\u5185\u7F6E DeepSeek \u5B98\u65B9 Provider \u5728\u672C\u9875\u53EA\u8BFB\uFF0C\u5176\u6A21\u578B\u76EE\u5F55\u8BF7\u56DE\u300C\u6A21\u578B\u8BBE\u7F6E\u300D\u7F16\u8F91\u3002"
4792
+ )
4793
+ ];
4794
+ if (props.error) {
4795
+ body.push(import_react3.default.createElement("p", { key: "err", className: "dkp-error" }, "\u6A21\u578B\u76EE\u5F55\u62C9\u53D6\u5931\u8D25\uFF1A" + props.error));
4796
+ }
4797
+ if (pfMsg && !pfDraft) {
4798
+ body.push(import_react3.default.createElement("p", { key: "msg", className: "dkp-msg " + (pfMsg.ok ? "ok" : "err"), role: "status" }, pfMsg.text));
4799
+ }
4800
+ if (!data) {
4801
+ body.push(import_react3.default.createElement("p", { key: "loading", className: "dkp-hint" }, props.loading ? "\u6B63\u5728\u62C9\u53D6\u6A21\u578B\u76EE\u5F55\u2026" : "\u7B49\u5F85\u62C9\u53D6\u6A21\u578B\u76EE\u5F55"));
4802
+ } else if (piProviders.length === 0) {
4803
+ body.push(import_react3.default.createElement("div", { key: "empty", className: "dkp-empty" }, "\u8FD8\u6CA1\u6709\u81EA\u5B9A\u4E49 Provider\u2014\u2014\u70B9\u4E0B\u65B9\u300C\u65B0\u589E Provider\u300D\u6DFB\u52A0\u3002"));
4804
+ } else {
4805
+ body.push(import_react3.default.createElement(
4806
+ "ul",
4807
+ { key: "rows", className: "dkp-rows" },
4808
+ piProviders.map((p) => {
4809
+ const route = p.route || p.id;
4810
+ const editing = pfMode === "edit" && !!pfDraft && pfDraft.route === route;
4811
+ const keyMissing = !!(p.apiKeyEnv && !p.apiKeyConfigured);
4812
+ return import_react3.default.createElement(
4813
+ "li",
4814
+ { key: p.id, className: "dkp-card" },
4815
+ import_react3.default.createElement(
4816
+ "div",
4817
+ { className: "dkp-rowhead" },
4818
+ import_react3.default.createElement(
4819
+ "span",
4820
+ { className: "dkp-identity" },
4821
+ import_react3.default.createElement("span", { className: "dkp-name" }, p.displayName || route),
4822
+ import_react3.default.createElement("span", { className: "dkp-tag" }, route),
4823
+ import_react3.default.createElement("span", { className: "dkp-tag" }, (p.models ? p.models.length : 0) + " \u6A21\u578B"),
4824
+ import_react3.default.createElement("span", {
4825
+ className: "dkp-dot " + (keyMissing ? "missing" : "ok"),
4826
+ role: "img",
4827
+ title: p.apiKeyEnv ? p.apiKeyConfigured ? p.apiKeyEnv + " \u5DF2\u914D\u7F6E" : p.apiKeyEnv + " \u672A\u914D\u7F6E" : "\u672A\u8BBE apiKeyEnv",
4828
+ "aria-label": p.apiKeyEnv ? p.apiKeyConfigured ? "\u5BC6\u94A5\u5DF2\u914D\u7F6E" : "\u5BC6\u94A5\u672A\u914D\u7F6E" : "\u672A\u8BBE apiKeyEnv"
4829
+ }),
4830
+ p.apiKeyEnv ? import_react3.default.createElement(
4831
+ "span",
4832
+ { className: "dkp-tag" + (keyMissing ? " warn" : " ok") },
4833
+ p.apiKeyConfigured ? p.apiKeyEnv + " \u5DF2\u914D\u7F6E" : p.apiKeyEnv + " \u672A\u914D\u7F6E"
4834
+ ) : null
4835
+ ),
4836
+ import_react3.default.createElement(
4837
+ "span",
4838
+ { className: "dkp-actions" },
4839
+ import_react3.default.createElement("button", {
4840
+ type: "button",
4841
+ className: "dkp-btn ghost sm",
4842
+ "aria-expanded": editing,
4843
+ onClick: () => editing ? closePf() : openPfEdit(p)
4844
+ }, editing ? "\u6536\u8D77" : "\u7F16\u8F91"),
4845
+ import_react3.default.createElement("button", {
4846
+ type: "button",
4847
+ className: "dkp-btn danger sm" + (pfDelArm === route ? " arm" : ""),
4848
+ title: pfDelArm === route ? "\u518D\u70B9\u4E00\u6B21\u786E\u8BA4\u5220\u9664" : "\u5220\u9664\u8BE5 Provider",
4849
+ onClick: () => deletePf(p)
4850
+ }, pfDelArm === route ? "\u786E\u8BA4\u5220\u9664\uFF1F" : "\u5220\u9664")
4851
+ )
4852
+ ),
4853
+ import_react3.default.createElement("div", { className: "dkp-sub" }, [p.api, p.baseURL].filter(Boolean).join(" \xB7 ")),
4854
+ editing ? editor : null
4855
+ );
4856
+ })
4190
4857
  ));
4191
4858
  }
4192
- return import_react3.default.createElement("div", { className: "dkm-list" }, body);
4859
+ body.push(import_react3.default.createElement(
4860
+ "div",
4861
+ { key: "add", className: "dkp-addblock" },
4862
+ pfMode === "create" && pfDraft ? import_react3.default.createElement("div", { className: "dkp-card dkp-addcard" }, editor) : import_react3.default.createElement("button", { type: "button", className: "dkp-addbtn", onClick: openPfCreate }, "\uFF0B \u65B0\u589E Provider")
4863
+ ));
4864
+ return import_react3.default.createElement("div", { className: "dkp-page" }, body);
4193
4865
  }
4194
4866
  function ModelsStat() {
4195
4867
  const snap = useModels();
@@ -4205,7 +4877,7 @@ var feature2 = {
4205
4877
  name: "\u6A21\u578B\u8BBE\u7F6E",
4206
4878
  order: 100,
4207
4879
  accent: "#22d3ee",
4208
- description: "\u7F16\u8F91\u5404 Provider \u6A21\u578B\u76EE\u5F55\uFF1A\u8F93\u5165\u7C7B\u578B\uFF08\u6587\u672C/\u56FE\u7247 + \u6807\u6CE8\uFF09\u4E0E\u601D\u8003\u5F3A\u5EA6\u6863\u4F4D\uFF1B\u5199\u56DE\u5B98\u65B9\u914D\u7F6E\u70ED\u751F\u6548\uFF0C\u4F1A\u8BDD\u6A21\u578B\u9009\u62E9\u5668\u5373\u65F6\u53EF\u9009",
4880
+ description: "\u7F16\u8F91\u5404 Provider \u6A21\u578B\u76EE\u5F55\uFF08\u8F93\u5165\u7C7B\u578B/\u601D\u8003\u5F3A\u5EA6\u6863\u4F4D\uFF09\uFF1B\u81EA\u5B9A\u4E49 Provider \u7684\u589E\u5220\u6539\u5728\u300C\u6A21\u578B\u63D0\u4F9B\u5546\u300D\u5B50\u9875\u9762\uFF0C\u5199\u56DE\u5B98\u65B9\u914D\u7F6E\u70ED\u751F\u6548\uFF0C\u4F1A\u8BDD\u6A21\u578B\u9009\u62E9\u5668\u5373\u65F6\u53EF\u9009",
4209
4881
  css: [
4210
4882
  ".dkm-note{color:var(--dsw-alias-label-secondary);font-size:12px;}",
4211
4883
  ".dkm-warn{color:var(--dk-warn);}",
@@ -4250,7 +4922,99 @@ var feature2 = {
4250
4922
  ".dkm-mini{cursor:pointer;border:1px solid var(--dsw-alias-border-l2);background:transparent;color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 8px;font-family:inherit;font-size:11px;flex:none;}",
4251
4923
  ".dkm-mini:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-secondary);}",
4252
4924
  ".dkm-toolbar{display:flex;align-items:center;gap:6px;flex-wrap:wrap;border-top:1px dashed var(--dsw-alias-border-l1);padding-top:8px;}",
4253
- ".dkm-toolbar-label{color:var(--dsw-alias-label-tertiary);font-size:11px;flex:none;}"
4925
+ ".dkm-toolbar-label{color:var(--dsw-alias-label-tertiary);font-size:11px;flex:none;}",
4926
+ // ---- 模型提供商子页面(dkp- 前缀):对齐官方「模型」设置页的排版与尺寸 ----
4927
+ // 官方用量参考:section 限宽 720、行卡片 .5px 描边 + radius-xl + 12/14 内距、
4928
+ // 输入高 32、按钮高 36(行内小按钮 28)、新增按钮 44 虚线、编辑器 bg-module-platform。
4929
+ ".dkp-page{width:100%;max-width:720px;display:flex;flex-direction:column;gap:12px;color:var(--dsw-alias-label-primary);}",
4930
+ ".dkp-back{box-sizing:border-box;align-self:flex-start;cursor:pointer;border:none;background:transparent;color:var(--dsw-alias-label-tertiary);border-radius:var(--dsw-radius-sm,6px);padding:4px 8px;margin-left:-8px;font-family:inherit;font-size:12px;line-height:18px;}",
4931
+ ".dkp-back:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);}",
4932
+ ".dkp-title{margin:0;font-size:16px;font-weight:500;line-height:24px;}",
4933
+ ".dkp-intro{margin:0;color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:22px;}",
4934
+ ".dkp-rows{list-style:none;margin:12px 0 0;padding:0;display:flex;flex-direction:column;gap:8px;}",
4935
+ ".dkp-card{box-sizing:border-box;margin:0;border:.5px solid var(--dsw-alias-settings-card-stroke,var(--dsw-alias-border-l2));background:var(--dsw-alias-settings-card-fill,var(--dsw-alias-bg-layer-1));border-radius:var(--dsw-radius-xl,12px);padding:12px 14px;display:flex;flex-direction:column;gap:10px;}",
4936
+ ".dkp-rowhead{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}",
4937
+ ".dkp-identity{display:inline-flex;align-items:center;gap:6px;min-width:0;flex-wrap:wrap;}",
4938
+ ".dkp-name{font-size:14px;font-weight:500;line-height:22px;}",
4939
+ ".dkp-tag{flex:none;border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-xs,4px);color:var(--dsw-alias-label-secondary);padding:1px 6px;font-size:11px;line-height:16px;}",
4940
+ ".dkp-tag.ok{color:var(--dsw-alias-state-success-primary);border-color:currentColor;}",
4941
+ ".dkp-tag.warn{color:var(--dk-warn);border-color:currentColor;}",
4942
+ ".dkp-dot{box-sizing:border-box;flex:none;width:8px;height:8px;border-radius:50%;display:inline-block;}",
4943
+ ".dkp-dot.ok{background:var(--dsw-alias-state-success-primary);}",
4944
+ ".dkp-dot.missing{background:var(--dsw-alias-state-error-primary);}",
4945
+ ".dkp-dot.warn{background:var(--dk-warn);}",
4946
+ ".dkp-sub{margin:0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
4947
+ ".dkp-hint{margin:0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;}",
4948
+ ".dkp-dim{color:var(--dsw-alias-label-tertiary);}",
4949
+ ".dkp-error{margin:0;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px;}",
4950
+ ".dkp-msg{margin:0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;}",
4951
+ ".dkp-msg.ok{color:var(--dsw-alias-state-success-primary);}",
4952
+ ".dkp-msg.err{color:var(--dsw-alias-state-error-primary);}",
4953
+ ".dkp-actions{flex:none;margin-left:auto;display:inline-flex;align-items:center;gap:4px;}",
4954
+ ".dkp-btn{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:4px;height:36px;padding:0 14px;border:none;border-radius:var(--dsw-radius-md,8px);background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:14px;line-height:22px;cursor:pointer;}",
4955
+ ".dkp-btn.ghost{border:.5px solid var(--dsw-alias-border-l3);}",
4956
+ ".dkp-btn.ghost:hover{background:var(--dsw-alias-interactive-bg-hover);}",
4957
+ ".dkp-btn.primary{background:var(--dsw-alias-button-primary-fill,var(--dk-accent));color:var(--dsw-alias-label-primary-foreground,#fff);}",
4958
+ ".dkp-btn.primary:hover{background:var(--dsw-alias-button-primary-hover,var(--dk-accent));}",
4959
+ ".dkp-btn.danger{color:var(--dsw-alias-state-error-primary);}",
4960
+ ".dkp-btn.danger:hover{background:var(--dsw-alias-interactive-bg-hover-danger,rgba(220,38,38,.1));}",
4961
+ ".dkp-btn.arm{color:var(--dsw-alias-state-error-primary);border-color:currentColor;}",
4962
+ ".dkp-btn.sm{height:28px;padding:0 10px;border-radius:var(--dsw-radius-sm,6px);font-size:12px;line-height:18px;}",
4963
+ ".dkp-btn[disabled]{opacity:.4;cursor:default;}",
4964
+ ".dkp-editor{border-radius:var(--dsw-radius-lg,10px);background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-1));padding:14px 16px;display:flex;flex-direction:column;gap:14px;}",
4965
+ ".dkp-editor-head{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;}",
4966
+ ".dkp-editor-title{font-size:14px;font-weight:500;line-height:22px;}",
4967
+ ".dkp-editor-route{color:var(--dsw-alias-label-tertiary);font-family:var(--ds-font-family-code,ui-monospace,monospace);font-size:12px;line-height:18px;}",
4968
+ ".dkp-field{display:flex;flex-direction:column;gap:6px;min-width:0;}",
4969
+ ".dkp-flabel{display:inline-flex;align-items:center;gap:10px;color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px;}",
4970
+ ".dkp-input{box-sizing:border-box;width:100%;height:32px;padding:0 10px;border:.5px solid var(--dsw-alias-border-l4,var(--dsw-alias-border-l2));border-radius:var(--dsw-radius-md,8px);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:14px;line-height:22px;}",
4971
+ ".dkp-input:focus{border-color:var(--dsw-alias-state-business-primary,var(--dk-accent));outline:none;}",
4972
+ ".dkp-input::placeholder{color:var(--dsw-alias-label-dimmed,var(--dsw-alias-label-tertiary));}",
4973
+ ".dkp-input:disabled{opacity:.6;cursor:default;}",
4974
+ "select.dkp-input{appearance:auto;cursor:pointer;max-width:240px;}",
4975
+ ".dkp-select-sm{height:28px;max-width:160px;font-size:12px;line-height:18px;}",
4976
+ ".dkp-checks{display:flex;align-items:center;gap:16px;flex-wrap:wrap;min-height:32px;}",
4977
+ ".dkp-check{display:inline-flex;align-items:center;gap:6px;cursor:pointer;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;}",
4978
+ ".dkp-check input{accent-color:var(--dk-accent);margin:0;}",
4979
+ ".dkp-mini{cursor:pointer;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;background:transparent;color:var(--dsw-alias-label-tertiary);padding:1px 8px;font-family:inherit;font-size:11px;line-height:16px;flex:none;}",
4980
+ ".dkp-mini:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-secondary);}",
4981
+ ".dkp-customized{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:10px;}",
4982
+ ".dkp-summary{display:flex;align-items:center;gap:6px;width:fit-content;margin-left:-4px;padding:2px 4px;border-radius:var(--dsw-radius-sm,6px);list-style:none;cursor:pointer;color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px;}",
4983
+ ".dkp-summary::-webkit-details-marker{display:none;}",
4984
+ '.dkp-summary::before{content:"";width:5px;height:5px;border-right:1.5px solid;border-bottom:1.5px solid;transform:rotate(-45deg);transition:transform .12s;}',
4985
+ ".dkp-customized[open]>.dkp-summary::before{transform:rotate(45deg);}",
4986
+ ".dkp-summary:hover{color:var(--dsw-alias-label-primary);}",
4987
+ ".dkp-custombody{display:flex;flex-direction:column;gap:12px;padding-top:12px;}",
4988
+ ".dkp-catalog{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:12px;display:flex;flex-direction:column;gap:10px;}",
4989
+ ".dkp-catalog-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap;}",
4990
+ ".dkp-catalog-titlewrap{display:flex;flex-direction:column;gap:2px;}",
4991
+ ".dkp-catalog-title{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px;}",
4992
+ ".dkp-catalog-meta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;}",
4993
+ ".dkp-model-list{display:flex;flex-direction:column;gap:8px;}",
4994
+ ".dkp-model-entry{border:.5px solid var(--dsw-alias-border-l4,var(--dsw-alias-border-l2));border-radius:var(--dsw-radius-lg,10px);padding:6px;display:flex;flex-direction:column;gap:6px;}",
4995
+ ".dkp-model-row{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(0,1fr) minmax(0,.7fr) minmax(0,.7fr) auto;gap:6px;align-items:center;}",
4996
+ ".dkp-model-row .dkp-input{height:30px;padding:0 8px;font-size:13px;}",
4997
+ ".dkp-model-opts{display:flex;flex-direction:column;gap:4px;padding:2px 2px 0;}",
4998
+ ".dkp-iconbtn{box-sizing:border-box;width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;border:none;border-radius:var(--dsw-radius-sm,6px);background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:12px;line-height:1;}",
4999
+ ".dkp-iconbtn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);}",
5000
+ ".dkp-iconbtn.danger:hover{background:var(--dsw-alias-interactive-bg-hover-danger,rgba(220,38,38,.1));color:var(--dsw-alias-state-error-primary);}",
5001
+ ".dkp-addmodel{box-sizing:border-box;align-self:flex-start;display:inline-flex;align-items:center;gap:4px;height:28px;padding:0 10px;border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-sm,6px);background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;line-height:18px;cursor:pointer;}",
5002
+ ".dkp-addmodel:hover{background:var(--dsw-alias-interactive-bg-hover);}",
5003
+ ".dkp-picker{max-height:320px;overflow-y:auto;border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md,8px);padding:4px 6px;display:flex;flex-direction:column;gap:2px;}",
5004
+ ".dkp-picker label{display:flex;align-items:center;gap:8px;min-width:0;cursor:pointer;padding:6px 8px;border-radius:var(--dsw-radius-md,8px);}",
5005
+ ".dkp-picker label:hover{background:var(--dsw-alias-interactive-bg-hover);}",
5006
+ ".dkp-picker input{accent-color:var(--dk-accent);margin:0;flex:none;}",
5007
+ ".dkp-picker-id{flex:none;font-family:var(--ds-font-family-code,ui-monospace,monospace);font-size:13px;line-height:20px;}",
5008
+ ".dkp-picker-name{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
5009
+ ".dkp-empty{border:1px dashed var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-lg,10px);padding:12px;text-align:center;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;}",
5010
+ ".dkp-addblock{display:flex;align-items:stretch;}",
5011
+ ".dkp-addbtn{box-sizing:border-box;flex:1 1 auto;min-width:180px;height:44px;display:inline-flex;align-items:center;justify-content:center;gap:6px;border:1px dashed var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-lg,10px);background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:14px;line-height:22px;cursor:pointer;}",
5012
+ ".dkp-addbtn:hover{background:var(--dsw-alias-interactive-bg-hover);}",
5013
+ ".dkp-addcard{flex:1 1 auto;padding:14px 16px;}",
5014
+ ".dkp-addcard .dkp-editor{background:transparent;padding:0;}",
5015
+ ".dkp-editor-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap;border-top:.5px solid var(--dsw-alias-border-l2);padding-top:12px;}",
5016
+ ".dkp-editor-actions .dkp-hint{margin-right:auto;}",
5017
+ "@media (max-width:680px){.dkp-page{max-width:none;gap:14px}.dkp-intro{font-size:13px;line-height:20px}.dkp-model-row{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}.dkp-model-row .dkp-iconbtn{justify-self:end}.dkp-editor{padding:12px}.dkp-addcard{padding:12px 14px}.dkp-btn{height:40px}.dkp-btn.sm{height:34px}.dkp-addbtn{height:48px}}"
4254
5018
  ].join("\n"),
4255
5019
  View: ModelsView,
4256
5020
  HomeStat: ModelsStat
@@ -4402,7 +5166,7 @@ var balanceStore = {
4402
5166
  load() {
4403
5167
  if (this.snap.loading) return;
4404
5168
  this.set({ loading: true });
4405
- fetch("/dsh-dock/balance", { signal: AbortSignal.timeout(2e4) }).then((res) => res.ok ? res.json() : Promise.reject(new Error("\u4F59\u989D\u63A5\u53E3 HTTP " + res.status))).then((data) => this.set({ data, loading: false, error: null })).catch((e) => this.set({ loading: false, error: e && e.message || String(e) }));
5169
+ fetch("dsh-dock/balance", { signal: AbortSignal.timeout(2e4) }).then((res) => res.ok ? res.json() : Promise.reject(new Error("\u4F59\u989D\u63A5\u53E3 HTTP " + res.status))).then((data) => this.set({ data, loading: false, error: null })).catch((e) => this.set({ loading: false, error: e && e.message || String(e) }));
4406
5170
  }
4407
5171
  };
4408
5172
  function useBalance(ctx) {
@@ -4725,7 +5489,7 @@ var feature5 = {
4725
5489
  var import_react8 = require("react");
4726
5490
  var import_jsx_runtime2 = require("react/jsx-runtime");
4727
5491
  function rpcCall2(method, args) {
4728
- return fetch("/dsh-dock/animation/" + method, {
5492
+ return fetch("dsh-dock/animation/" + method, {
4729
5493
  method: "POST",
4730
5494
  headers: { "content-type": "application/json" },
4731
5495
  body: JSON.stringify(args === void 0 ? {} : args)
@@ -6402,7 +7166,7 @@ var feature6 = {
6402
7166
  var import_react9 = require("react");
6403
7167
  var import_jsx_runtime3 = require("react/jsx-runtime");
6404
7168
  function rpcCall3(method, args) {
6405
- return fetch("/dsh-dock/notify/" + method, {
7169
+ return fetch("dsh-dock/notify/" + method, {
6406
7170
  method: "POST",
6407
7171
  headers: { "content-type": "application/json" },
6408
7172
  body: JSON.stringify(args === void 0 ? {} : args)
@@ -7166,7 +7930,7 @@ var feature7 = {
7166
7930
  var import_react10 = require("react");
7167
7931
  var import_jsx_runtime4 = require("react/jsx-runtime");
7168
7932
  function rpcCall4(method, args) {
7169
- return fetch("/dsh-dock/runstate/" + method, {
7933
+ return fetch("dsh-dock/runstate/" + method, {
7170
7934
  method: "POST",
7171
7935
  headers: { "content-type": "application/json" },
7172
7936
  body: JSON.stringify(args === void 0 ? {} : args)
@@ -10149,7 +10913,7 @@ function initialDash() {
10149
10913
  }
10150
10914
  function useTaskPulse() {
10151
10915
  const [status, setStatus] = (0, import_react20.useState)({ loading: true, active: [], error: false });
10152
- const refresh = (0, import_react20.useCallback)(() => fetch("/dsh-dock/animation/status", {
10916
+ const refresh = (0, import_react20.useCallback)(() => fetch("dsh-dock/animation/status", {
10153
10917
  method: "POST",
10154
10918
  headers: { "content-type": "application/json" },
10155
10919
  body: "{}"
@@ -10922,7 +11686,7 @@ var import_react21 = require("react");
10922
11686
  var import_qrcode = __toESM(require_browser(), 1);
10923
11687
  var import_jsx_runtime14 = require("react/jsx-runtime");
10924
11688
  function rpc(method, payload) {
10925
- return fetch("/dsh-dock/mobile-relay/" + method, {
11689
+ return fetch("dsh-dock/mobile-relay/" + method, {
10926
11690
  method: "POST",
10927
11691
  headers: { "content-type": "application/json" },
10928
11692
  body: JSON.stringify(payload || {})
@@ -11221,247 +11985,6 @@ function RemoteCard() {
11221
11985
  function MobileRelayView() {
11222
11986
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(RemoteCard, {});
11223
11987
  }
11224
- function MobileRelayOverlay() {
11225
- (0, import_react21.useEffect)(() => {
11226
- if (typeof window === "undefined" || typeof document === "undefined") return void 0;
11227
- const host = window.location && window.location.hostname || "";
11228
- const loopback = host === "127.0.0.1" || host === "localhost" || host === "::1" || host.endsWith(".localhost");
11229
- if (loopback) return void 0;
11230
- if (typeof MutationObserver === "undefined") return void 0;
11231
- let done = false;
11232
- const tryDismiss = () => {
11233
- if (done) return;
11234
- const dialogs = document.querySelectorAll('[role="dialog"]');
11235
- for (const dialog of dialogs) {
11236
- const text = dialog.textContent || "";
11237
- if (!/预览版说明|Preview Notice/.test(text)) continue;
11238
- const btn = [...dialog.querySelectorAll("button")].find((b) => {
11239
- const t = (b.textContent || "").trim();
11240
- return t === "\u7EE7\u7EED" || t === "Continue";
11241
- });
11242
- if (btn) {
11243
- done = true;
11244
- btn.click();
11245
- observer.disconnect();
11246
- }
11247
- break;
11248
- }
11249
- };
11250
- const observer = new MutationObserver(tryDismiss);
11251
- observer.observe(document.body, { childList: true, subtree: true });
11252
- tryDismiss();
11253
- const stopTimer = setTimeout(() => observer.disconnect(), 6e4);
11254
- return () => {
11255
- clearTimeout(stopTimer);
11256
- observer.disconnect();
11257
- };
11258
- }, []);
11259
- (0, import_react21.useEffect)(() => {
11260
- if (typeof window === "undefined" || typeof document === "undefined") return void 0;
11261
- const mq = window.matchMedia ? window.matchMedia("(max-width:700px)") : null;
11262
- if (!mq) return void 0;
11263
- if (window.__dshDockMobileDrawer === "host") return void 0;
11264
- window.__dshDockMobileDrawer = "client";
11265
- const narrow = () => mq.matches;
11266
- const frameEl = () => document.querySelector('[class$="frame"]');
11267
- const sidebarCollapsed = () => {
11268
- const f = frameEl();
11269
- return !!f && f.hasAttribute("data-sidebar-collapsed");
11270
- };
11271
- const dialogOpen = () => !!document.querySelector('[role="dialog"][class*="panel"]');
11272
- const rightOpen = () => !!document.querySelector("[data-sidebar-right-panel][data-sidebar-right-open]");
11273
- const collapse = () => {
11274
- const b = document.querySelector('button[aria-label="\u6536\u8D77\u4FA7\u8FB9\u680F"],button[aria-label="Collapse sidebar"]');
11275
- if (b) b.click();
11276
- };
11277
- const expand = () => {
11278
- const b = document.querySelector('button[aria-label="\u6253\u5F00\u4FA7\u8FB9\u680F"],button[aria-label="Open sidebar"]');
11279
- if (b) b.click();
11280
- };
11281
- const closeDetails = () => {
11282
- const panel = document.querySelector("[data-sidebar-right-panel][data-sidebar-right-open]");
11283
- if (!panel) return;
11284
- const toggle = panel.querySelector("[data-sidebar-right-toggle]");
11285
- if (toggle) toggle.click();
11286
- };
11287
- const scrim = document.createElement("div");
11288
- scrim.className = "dsh-mobile-scrim";
11289
- document.body.appendChild(scrim);
11290
- document.querySelectorAll(".dsh-mobile-tabbar").forEach((el) => el.remove());
11291
- const tabbar = document.createElement("nav");
11292
- tabbar.className = "dsh-mobile-tabbar";
11293
- tabbar.setAttribute("aria-label", "\u5E95\u90E8\u5BFC\u822A");
11294
- let tasksActive = 0;
11295
- let tasksWaiting = 0;
11296
- const tabDefs = [
11297
- {
11298
- id: "sessions",
11299
- label: "\u4F1A\u8BDD",
11300
- icon: '<svg viewBox="0 0 24 24"><path d="M21 12a8 8 0 0 1-8 8H5l-2 2V12a8 8 0 0 1 8-8h2a8 8 0 0 1 8 8z"/></svg>',
11301
- act: () => expand()
11302
- },
11303
- {
11304
- id: "dock",
11305
- label: "\u63D2\u4EF6",
11306
- icon: '<svg viewBox="0 0 24 24"><rect x="4" y="4" width="7" height="7" rx="1.5"/><rect x="13" y="4" width="7" height="7" rx="1.5"/><rect x="4" y="13" width="7" height="7" rx="1.5"/><rect x="13" y="13" width="7" height="7" rx="1.5"/></svg>',
11307
- act: () => {
11308
- openPanel(tasksActive > 0 || tasksWaiting > 0 ? "runstate" : "home");
11309
- }
11310
- },
11311
- {
11312
- id: "settings",
11313
- label: "\u8BBE\u7F6E",
11314
- icon: '<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3.2"/><path d="M19.4 15a1.7 1.7 0 0 0 .34 1.87l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06A1.7 1.7 0 0 0 15 19.4a1.7 1.7 0 0 0-1 1.55V21a2 2 0 1 1-4 0v-.09A1.7 1.7 0 0 0 9 19.4a1.7 1.7 0 0 0-1.87.34l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.7 1.7 0 0 0 4.6 15a1.7 1.7 0 0 0-1.55-1H3a2 2 0 1 1 0-4h.09A1.7 1.7 0 0 0 4.6 9a1.7 1.7 0 0 0-.34-1.87l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.7 1.7 0 0 0 9 4.6a1.7 1.7 0 0 0 1-1.55V3a2 2 0 1 1 4 0v.09a1.7 1.7 0 0 0 1 1.51 1.7 1.7 0 0 0 1.87-.34l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.7 1.7 0 0 0 19.4 9c.23.6.86 1 1.51 1H21a2 2 0 1 1 0 4h-.09a1.7 1.7 0 0 0-1.51 1z"/></svg>',
11315
- act: () => {
11316
- const b = [...document.querySelectorAll("button")].find((x) => (x.getAttribute("aria-label") || "") === "\u8BBE\u7F6E" || (x.textContent || "").trim() === "\u8BBE\u7F6E");
11317
- if (b) b.click();
11318
- }
11319
- }
11320
- ];
11321
- let tasksBadge = null;
11322
- const tabButtons = tabDefs.map((def) => {
11323
- const b = document.createElement("button");
11324
- b.type = "button";
11325
- b.className = "dsh-mobile-tab";
11326
- b.dataset.tab = def.id;
11327
- b.innerHTML = def.icon + "<span>" + def.label + "</span>";
11328
- b.addEventListener("click", () => def.act());
11329
- if (def.id === "dock") {
11330
- tasksBadge = document.createElement("span");
11331
- tasksBadge.className = "dsh-mobile-tab-badge";
11332
- tasksBadge.style.display = "none";
11333
- b.appendChild(tasksBadge);
11334
- }
11335
- tabbar.appendChild(b);
11336
- return b;
11337
- });
11338
- document.body.appendChild(tabbar);
11339
- const syncTabs = () => {
11340
- const expanded = !sidebarCollapsed();
11341
- const settingsOpen = dialogOpen();
11342
- for (const b of tabButtons) {
11343
- const id = b.dataset.tab;
11344
- const on = id === "sessions" && expanded || id === "dock" && panelNav.open || id === "settings" && settingsOpen;
11345
- b.classList.toggle("on", on);
11346
- }
11347
- };
11348
- const unsubPanel = subscribePanel(syncTabs);
11349
- let runstateGone = false;
11350
- const pollTasks = () => {
11351
- if (runstateGone || !tasksBadge || !narrow() || document.visibilityState === "hidden" || !tabbar.isConnected) return;
11352
- fetch("/dsh-dock/runstate/status", {
11353
- method: "POST",
11354
- headers: { "content-type": "application/json" },
11355
- body: "{}"
11356
- }).then(async (res) => {
11357
- if (res.status === 404 || res.status === 405) {
11358
- runstateGone = true;
11359
- tasksActive = 0;
11360
- tasksWaiting = 0;
11361
- tasksBadge.style.display = "none";
11362
- return null;
11363
- }
11364
- const body = await res.json().catch(() => null);
11365
- if (!body) return null;
11366
- const active = body.data && body.data.active || [];
11367
- const waiting = active.filter((t) => t.approvals && t.approvals.length).length;
11368
- tasksActive = active.length;
11369
- tasksWaiting = waiting;
11370
- if (waiting > 0) {
11371
- tasksBadge.textContent = String(waiting);
11372
- tasksBadge.className = "dsh-mobile-tab-badge wait";
11373
- tasksBadge.style.display = "";
11374
- } else if (active.length > 0) {
11375
- tasksBadge.textContent = String(active.length);
11376
- tasksBadge.className = "dsh-mobile-tab-badge";
11377
- tasksBadge.style.display = "";
11378
- } else {
11379
- tasksBadge.style.display = "none";
11380
- }
11381
- }).catch(() => {
11382
- });
11383
- };
11384
- const tasksTimer = setInterval(pollTasks, 3e3);
11385
- const vv = window.visualViewport || null;
11386
- const kbdSync = () => {
11387
- if (!vv) return;
11388
- const kbd = vv.height > 0 && window.innerHeight - vv.height > 120;
11389
- document.documentElement.classList.toggle("dsh-dock-kbd", kbd);
11390
- };
11391
- const sync = () => {
11392
- kbdSync();
11393
- document.querySelectorAll(".dsh-mobile-tabbar").forEach((el) => {
11394
- if (el !== tabbar) el.remove();
11395
- });
11396
- const on = narrow() && !!frameEl() && !dialogOpen();
11397
- const collapsed = sidebarCollapsed();
11398
- const right = rightOpen();
11399
- if (scrim) scrim.style.display = on && !collapsed ? "" : "none";
11400
- tabbar.style.display = narrow() && !!frameEl() && !panelNav.open && !right ? "" : "none";
11401
- syncTabs();
11402
- };
11403
- sync();
11404
- pollTasks();
11405
- const observer = typeof MutationObserver !== "undefined" ? new MutationObserver(sync) : null;
11406
- if (observer) observer.observe(document.documentElement, { attributes: true, attributeFilter: ["data-sidebar-collapsed"], subtree: true });
11407
- const timer = setInterval(sync, 1500);
11408
- const onMq = () => sync();
11409
- if (mq.addEventListener) mq.addEventListener("change", onMq);
11410
- else if (mq.addListener) mq.addListener(onMq);
11411
- window.addEventListener("resize", kbdSync);
11412
- if (vv && vv.addEventListener) vv.addEventListener("resize", kbdSync);
11413
- const onVisible = () => {
11414
- if (document.visibilityState === "visible") pollTasks();
11415
- };
11416
- document.addEventListener("visibilitychange", onVisible);
11417
- const onClick = (e) => {
11418
- if (!narrow()) return;
11419
- const t = e.target;
11420
- if (!t || !t.closest) return;
11421
- if (t.closest(".dsh-mobile-tabbar")) return;
11422
- const expanded = !!document.querySelector('button[aria-label="\u6536\u8D77\u4FA7\u8FB9\u680F"],button[aria-label="Collapse sidebar"]');
11423
- const col = document.querySelector('[class*="sidebarCol"]');
11424
- const inSidebar = col && col.contains(t);
11425
- if (!expanded && !inSidebar) return;
11426
- if (inSidebar) {
11427
- const row = t.closest('[role="treeitem"]');
11428
- const rowKey = row && row.getAttribute("data-row-key") || "";
11429
- const sessionRow = rowKey && rowKey.startsWith("session:") || row && !rowKey && !row.querySelector('[role="treeitem"]') && row.closest('[role="tree"]');
11430
- const fresh = t.closest('[class*="newSession"]');
11431
- if (sessionRow || fresh) setTimeout(() => {
11432
- collapse();
11433
- closeDetails();
11434
- }, 300);
11435
- else {
11436
- setTimeout(() => {
11437
- if (document.querySelector('[role="dialog"]')) collapse();
11438
- }, 260);
11439
- }
11440
- return;
11441
- }
11442
- if (t.closest('[role="dialog"],[class*="dockm"],[class*="dgfab"],[class*="dgwin"],[class*="dgame"],[data-sidebar-right-panel]')) return;
11443
- collapse();
11444
- };
11445
- document.addEventListener("click", onClick, true);
11446
- return () => {
11447
- document.removeEventListener("click", onClick, true);
11448
- if (observer) observer.disconnect();
11449
- clearInterval(timer);
11450
- clearInterval(tasksTimer);
11451
- unsubPanel();
11452
- if (mq.removeEventListener) mq.removeEventListener("change", onMq);
11453
- else if (mq.removeListener) mq.removeListener(onMq);
11454
- window.removeEventListener("resize", kbdSync);
11455
- if (vv && vv.removeEventListener) vv.removeEventListener("resize", kbdSync);
11456
- document.removeEventListener("visibilitychange", onVisible);
11457
- document.documentElement.classList.remove("dsh-dock-kbd");
11458
- if (scrim) scrim.remove();
11459
- tabbar.remove();
11460
- if (window.__dshDockMobileDrawer === "client") delete window.__dshDockMobileDrawer;
11461
- };
11462
- }, []);
11463
- return null;
11464
- }
11465
11988
  function MobileRelayHomeStat() {
11466
11989
  const [summary, setSummary] = (0, import_react21.useState)("\u672A\u5F00\u542F\uFF0C\u5F00\u542F\u540E\u53EF\u8FDC\u7A0B\u767B\u5F55");
11467
11990
  (0, import_react21.useEffect)(() => {
@@ -11487,53 +12010,9 @@ var feature10 = {
11487
12010
  defaultEnabled: false,
11488
12011
  css: `
11489
12012
  .dmr{--dmr-accent:var(--dk-accent,#2f6fed);--dmr-accent-soft:color-mix(in srgb,var(--dmr-accent) 13%,transparent);display:flex;flex-direction:column;gap:16px;max-width:720px;color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5}.dmr h3,.dmr h4,.dmr p{margin:0}.dmr h3{font-size:17px;line-height:1.25;letter-spacing:-.01em}.dmr h4{font-size:13px}.dmr-hero,.dmr-status-head{display:flex;align-items:flex-start;gap:12px}.dmr-hero>div,.dmr-status-head>div{min-width:0;display:flex;flex-direction:column;gap:4px}.dmr-hero p,.dmr-status-head p,.dmr-field small,.dmr-share small,.dmr-overview-card small,.dmr-task small,.dmr-note p{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.55}.dmr-hero-icon{width:42px;height:42px;display:inline-flex;align-items:center;justify-content:center;flex:none;border-radius:14px;background:var(--dmr-accent-soft);color:var(--dmr-accent);border:1px solid color-mix(in srgb,var(--dmr-accent) 35%,var(--dsw-alias-border-l1))}.dmr-steps{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin:0;padding:0;list-style:none}.dmr-steps li{display:flex;gap:9px;padding:11px;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:12px}.dmr-steps li>span{width:21px;height:21px;display:inline-flex;align-items:center;justify-content:center;flex:none;border-radius:50%;font-size:11px;font-weight:700;color:var(--dmr-accent);background:var(--dmr-accent-soft)}.dmr-steps div{display:flex;flex-direction:column;gap:3px;min-width:0}.dmr-steps strong{font-size:12px}.dmr-steps small{font-size:11px;color:var(--dsw-alias-label-secondary);line-height:1.45}.dmr-field{display:flex;flex-direction:column;gap:6px;font-weight:600}.dmr-field input,.dmr-field select,.dmr-note textarea{box-sizing:border-box;width:100%;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:10px;padding:10px 12px;min-height:44px;font:inherit;outline:none;transition:border-color .18s ease,box-shadow .18s ease}.dmr-field input:focus,.dmr-field select:focus,.dmr-note textarea:focus{border-color:var(--dmr-accent);box-shadow:0 0 0 3px var(--dmr-accent-soft)}.dmr-field small{font-weight:400}.dmr-network-grid{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(150px,.6fr);gap:12px}.dmr-security{padding:12px 13px;border:1px solid color-mix(in srgb,var(--dk-warn) 48%,var(--dsw-alias-border-l1));border-radius:12px;background:color-mix(in srgb,var(--dk-warn) 9%,transparent)}.dmr-security strong{display:block;margin-bottom:4px;color:var(--dk-warn);font-size:12px}.dmr-security p{font-size:12px;color:var(--dsw-alias-label-secondary);line-height:1.55}.dmr-primary,.dmr-secondary,.dmr-text-button,.dmr-icon-button{font:inherit;touch-action:manipulation;cursor:pointer;transition:transform .15s ease,background .18s ease,border-color .18s ease,opacity .18s ease}.dmr-primary,.dmr-secondary{min-height:44px;display:inline-flex;align-items:center;justify-content:center;gap:8px;border-radius:10px;padding:9px 13px;font-weight:600}.dmr-primary{align-self:flex-start;border:1px solid var(--dmr-accent);background:var(--dmr-accent);color:#fff}.dmr-primary:hover{filter:brightness(1.05)}.dmr-secondary{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary)}.dmr-secondary:hover{border-color:var(--dmr-accent);background:var(--dmr-accent-soft)}.dmr-primary:active,.dmr-secondary:active,.dmr-icon-button:active{transform:scale(.98)}.dmr-primary:disabled,.dmr-secondary:disabled,.dmr-text-button:disabled,.dmr-icon-button:disabled{cursor:not-allowed;opacity:.5}.dmr-message{display:flex;align-items:flex-start;gap:7px;padding:10px 12px;border-radius:10px;font-size:12px}.dmr-message.success{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 10%,transparent)}.dmr-message.error{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent)}.dmr-status-head{justify-content:space-between}.dmr-eyebrow{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:600;color:var(--dmr-accent)}.dmr-eyebrow i{width:7px;height:7px;border-radius:50%;background:currentColor;box-shadow:0 0 0 4px var(--dmr-accent-soft)}.dmr-icon-button{display:inline-flex;align-items:center;justify-content:center;flex:none;width:44px;height:44px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:transparent;color:var(--dsw-alias-label-secondary)}.dmr-icon-button:hover{color:var(--dsw-alias-state-error-primary);border-color:currentColor}.dmr-share{display:flex;flex-direction:column;gap:9px;padding:13px;border:1px solid color-mix(in srgb,var(--dmr-accent) 32%,var(--dsw-alias-border-l1));border-radius:13px;background:var(--dmr-accent-soft)}.dmr-share-layout{display:grid;grid-template-columns:196px minmax(0,1fr);gap:16px;align-items:center}.dmr-qr-card{box-sizing:border-box;display:flex;flex-direction:column;align-items:center;gap:5px;padding:10px;border-radius:12px;background:#fff;color:#111827;text-align:center}.dmr-qr-card img{display:block;width:176px;height:176px;max-width:100%;object-fit:contain}.dmr-qr-card strong{font-size:12px}.dmr-qr-card small{color:#4b5563;font-size:10px}.dmr-qr-loading{display:grid;place-items:center;width:176px;height:176px;color:#64748b;font-size:12px}.dmr-share-detail{min-width:0;display:flex;flex-direction:column;gap:10px}.dmr-share-detail>div:first-child{display:flex;flex-direction:column;gap:4px}.dmr-link{padding:10px 11px;border-radius:8px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);font:12px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;word-break:break-all;color:var(--dsw-alias-label-secondary)}.dmr-share-actions{display:flex;gap:8px;flex-wrap:wrap}.dmr-share-actions .dmr-primary{align-self:auto}.dmr-overview{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.dmr-overview-card{display:flex;flex-direction:column;gap:3px;padding:12px;border:1px solid var(--dsw-alias-border-l1);border-radius:12px;background:var(--dsw-alias-bg-layer-1)}.dmr-overview-card>span{font-size:11px;color:var(--dsw-alias-label-tertiary)}.dmr-overview-card strong{font-size:16px;letter-spacing:-.01em}.dmr-section{display:flex;flex-direction:column;gap:9px}.dmr-section-head{display:flex;align-items:center;justify-content:space-between;gap:10px}.dmr-section-head small{font-size:11px;color:var(--dsw-alias-label-tertiary)}.dmr-text-button{border:0;background:transparent;color:var(--dmr-accent);padding:8px;min-height:36px;font-weight:600}.dmr-task-list{display:flex;flex-direction:column;gap:8px}.dmr-task{display:flex;align-items:flex-start;gap:9px;padding:10px 11px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;background:var(--dsw-alias-bg-layer-1)}.dmr-task>div{display:flex;min-width:0;flex:1;flex-direction:column;gap:2px}.dmr-task strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dmr-phase{flex:none;border-radius:999px;padding:3px 8px;font-size:11px;background:var(--dmr-accent-soft);color:var(--dmr-accent)}.dmr-phase.write{color:var(--dsw-alias-state-success-primary)}.dmr-phase.code{color:var(--dk-warn)}.dmr-phase.search{color:#0d9488}.dmr-empty{padding:18px 12px;text-align:center;border:1px dashed var(--dsw-alias-border-l2);border-radius:10px;color:var(--dsw-alias-label-secondary);font-size:12px}.dmr-note{display:flex;flex-direction:column;gap:9px;padding-top:2px}.dmr-note blockquote{margin:0;padding:10px 12px;border-left:3px solid var(--dmr-accent);border-radius:0 9px 9px 0;background:var(--dmr-accent-soft);white-space:pre-wrap;font-size:12px}.dmr-note textarea{min-height:88px;resize:vertical;line-height:1.5}.dmr-note .dmr-secondary{align-self:flex-start}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media (max-width:680px){.dmr{gap:14px;font-size:14px}.dmr h3{font-size:18px}.dmr-steps{grid-template-columns:1fr;gap:8px}.dmr-steps li{padding:10px}.dmr-steps small,.dmr-hero p,.dmr-status-head p,.dmr-field small,.dmr-share small,.dmr-overview-card small,.dmr-task small,.dmr-note p{font-size:12px}.dmr-network-grid{grid-template-columns:1fr;gap:10px}.dmr-field input,.dmr-field select{font-size:16px}.dmr-primary,.dmr-secondary{width:100%;font-size:14px}.dmr-share-layout{grid-template-columns:1fr;gap:12px}.dmr-qr-card{width:min(240px,100%);margin:0 auto}.dmr-qr-card img,.dmr-qr-loading{width:210px;height:210px}.dmr-share-actions{flex-direction:column}.dmr-share-actions .dmr-primary{width:100%}.dmr-overview{gap:8px}.dmr-overview-card{padding:11px}.dmr-status-head{gap:8px}.dmr-link{font-size:11px}.dmr-note textarea{font-size:16px;min-height:104px}.dmr-note .dmr-secondary{align-self:stretch}.dmr-compact .dmr-hero-icon{width:40px;height:40px;border-radius:13px}}@media (prefers-reduced-motion:reduce){.dmr-primary,.dmr-secondary,.dmr-text-button,.dmr-icon-button,.dmr-field input,.dmr-field select,.dmr-note textarea{transition:none}}.dmr-lan{flex:none;margin-top:6px;padding-top:16px;border-top:1px solid var(--dsw-alias-border-l1)}.dmr-lan-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.dmr-lan-title{min-width:0;display:flex;flex-direction:column;gap:4px}.dmr-lan-title p{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.55}.dmr-lan-badge{flex:none;display:inline-flex;align-items:center;gap:6px;border-radius:999px;padding:5px 10px;font-size:11px;font-weight:700;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary)}.dmr-lan-badge.on{border-color:color-mix(in srgb,var(--dmr-accent) 45%,transparent);background:var(--dmr-accent-soft);color:var(--dmr-accent)}.dmr-lan-active,.dmr-lan-idle{display:flex;flex-direction:column;gap:12px}.dmr-lan-active .dmr-share-detail small strong{color:var(--dsw-alias-state-error-primary)}.dmr-danger{border-color:color-mix(in srgb,var(--dsw-alias-state-error-primary) 45%,var(--dsw-alias-border-l2));color:var(--dsw-alias-state-error-primary)}.dmr-danger:hover{border-color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent)}
11490
- /* \u62BD\u5C49\u906E\u7F69\uFF1A\u4FA7\u680F\u6D6E\u5C42\u5C55\u5F00\u65F6\u76D6\u4F4F\u9875\u9762\u3002\u5DE6\u7F18\u6D6E\u52A8\u300C\u62BD\u5C49\u628A\u624B\u300D\u5DF2\u79FB\u9664\u2014\u2014\u4F1A\u8BDD\u5165\u53E3
11491
- * \u7EDF\u4E00\u8D70\u5E95\u90E8 Tab \u680F\u300C\u4F1A\u8BDD\u300D\u9875\u7B7E\uFF0C\u628A\u624B\u60AC\u6D6E\u5728\u5185\u5BB9\u4E0A\u8FD8\u538B\u8FC7\u8F93\u5165\u533A\uFF08\u7528\u6237\u5B9E\u6D4B\uFF09\u3002 */
11492
- @media (max-width:700px){.dsh-mobile-scrim{position:fixed;inset:0;z-index:75;background:rgba(8,10,14,.45);backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px);touch-action:none}
11493
- /* ---- \u4EFF ZCode \u624B\u673A\u7AEF\uFF1A\u5E95\u90E8 Tab \u680F + \u8F93\u5165\u533A\u5438\u9644\u5E95\u90E8 ---- */
11494
- .dsh-mobile-tabbar{position:fixed;left:0;right:0;bottom:0;z-index:65;display:flex;align-items:stretch;justify-content:space-around;padding:6px 8px calc(8px + env(safe-area-inset-bottom,0px));background:color-mix(in srgb,var(--dsw-alias-bg-layer-2,#1c2230) 92%,transparent);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-top:1px solid var(--dsw-alias-border-l1,rgba(127,139,161,.25));touch-action:manipulation}
11495
- .dsh-mobile-tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;min-height:48px;padding:4px 0;border:0;background:transparent;color:var(--dsw-alias-label-tertiary,#8b95ab);font:600 11px/1.2 inherit;cursor:pointer;-webkit-tap-highlight-color:transparent;border-radius:10px}
11496
- .dsh-mobile-tab svg{width:21px;height:21px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
11497
- .dsh-mobile-tab.on{color:var(--dsw-alias-label-primary,#e6eaf2)}
11498
- .dsh-mobile-tab.on svg{color:var(--dk-accent,#4d9fff)}
11499
- .dsh-mobile-tab:active{transform:scale(.94)}
11500
- /* \u300C\u4EFB\u52A1\u300D\u9875\u7B7E\u89D2\u6807\uFF1A\u8FDB\u884C\u4E2D=\u84DD\uFF08\u6570\u91CF\uFF09\uFF0C\u7B49\u5F85\u786E\u8BA4=\u7EA2\uFF08\u6570\u91CF+\u8109\u51B2\uFF09\u2014\u2014\u63A5\u529B\u573A\u666F\u91CC
11501
- * \u7B49\u5F85\u786E\u8BA4\u662F\u6700\u9700\u8981\u4EBA\u4ECB\u5165\u7684\u72B6\u6001\uFF0C\u4E00\u773C\u53EF\u89C1\uFF1B\u6570\u636E\u6765\u81EA runstate \u5BBF\u4E3B\u8DEF\u7531\u4F4E\u9891\u8F6E\u8BE2\u3002 */
11502
- .dsh-mobile-tab{position:relative}
11503
- .dsh-mobile-tab-badge{position:absolute;top:0;right:calc(50% - 26px);box-sizing:border-box;min-width:16px;height:16px;padding:0 4px;border-radius:9px;display:flex;align-items:center;justify-content:center;background:#4d9fff;color:#fff;font-size:10px;font-weight:700;line-height:1;font-variant-numeric:tabular-nums;box-shadow:0 2px 8px rgba(0,0,0,.35);pointer-events:none}
11504
- .dsh-mobile-tab-badge.wait{background:#ef4444;animation:dsh-badge-pulse 1.2s ease-in-out infinite}
11505
- @keyframes dsh-badge-pulse{0%,100%{transform:scale(1)}50%{transform:scale(1.18)}}
11506
- @media (prefers-reduced-motion:reduce){.dsh-mobile-tab-badge.wait{animation:none}}
11507
- /* \u8F93\u5165\u533A\u5438\u9644\u5230 Tab \u680F\u4E0A\u65B9\uFF1A\u5BBF\u4E3B\u628A\u6B22\u8FCE\u5185\u5BB9 justify-content:center \u5782\u76F4\u5C45\u4E2D\uFF0C
11508
- * \u624B\u673A\u89C6\u53E3\u4E0B\u8F93\u5165\u5361\u7247\u60AC\u5728\u4E2D\u90E8\u3001\u4E0B\u65B9\u7559\u51FA\u5927\u7247\u7A7A\u767D\u3002\u6539\u7528\u300C\u8F93\u5165\u533A margin-top:auto\u300D
11509
- * \u8D34\u5E95\uFF0C\u5E76\u7ED9\u6EDA\u52A8\u4F53\u7559\u51FA Tab \u680F\u9AD8\u5EA6\u7684\u5185\u8FB9\u8DDD\uFF0C\u907F\u514D\u5185\u5BB9\u88AB Tab \u680F\u906E\u6321\u3002
11510
- * \u26A0\uFE0F \u7EDD\u4E0D\u80FD\u7ED9\u6EDA\u52A8\u4F53\u52A0 justify-content:flex-end/center \u8D34\u5E95\uFF1A\u5185\u5BB9\u4F1A\u88AB\u6324\u5230\u6EDA\u52A8\u4F53
11511
- * \u8D77\u70B9\u4E4B\u5916\uFF0CChrome \u4E0D\u628A\u8D77\u70B9\u65B9\u5411\u6EA2\u51FA\u7B97\u8FDB scrollHeight\uFF08\u624B\u673A\u89C6\u53E3\u5B9E\u6D4B
11512
- * scrollHeight==clientHeight\u3001maxScroll==0\uFF09\uFF0C\u4F1A\u8BDD\u8BB0\u5F55\u5F7B\u5E95\u6EDA\u4E0D\u52A8\u3001\u9996\u6761\u6D88\u606F\u6C38\u8FDC
11513
- * \u770B\u4E0D\u5230\u3002\u5BBF\u4E3B\u534A\u90E8\u65E7\u7248\u6CE8\u5165\u91CC\u8FD8\u7559\u7740\u90A3\u6761 flex-end\uFF08\u8981\u91CD\u542F dsh web \u624D\u6D88\u5931\uFF09\uFF0C\u8FD9\u91CC
11514
- * \u7528\u66F4\u9AD8\u4F18\u5148\u7EA7\u7684\u9009\u62E9\u5668\u663E\u5F0F\u590D\u4F4D\uFF1A\u5237\u65B0\u9875\u9762\u5373\u4FEE\u597D\uFF0C\u4E0D\u5FC5\u7B49\u91CD\u542F\u3002 */
11515
- [class*="scrollBody"][data-conversation-scroll]{justify-content:flex-start !important;padding-bottom:calc(72px + env(safe-area-inset-bottom,0px)) !important}
11516
- [class*="scrollBody"]{padding-bottom:calc(72px + env(safe-area-inset-bottom,0px)) !important}
11517
- [class*="scrollBody"]>[class*="composerSeat"]{margin-top:auto;padding-bottom:6px}
11518
- /* \u8F93\u5165\u533A\u5DE5\u5177\u884C\u4E0A\u7684 dsh-dock chip\uFF08\u7528\u91CF/\u4F59\u989D\uFF09\uFF1A\u7A84\u5C4F\u4E0B 28cqw \u7684\u5BBD\u5EA6\u4E0A\u9650\u628A\u5B83\u4EEC\u6324\u6210
11519
- * \u300C\u2026\u300D\u300C\u4F59..\u300D\uFF08\u624B\u673A\u5B9E\u6D4B\uFF09\uFF0C\u65E2\u8BFB\u4E0D\u51FA\u6570\u503C\u53C8\u767D\u5360\u8F93\u5165\u884C\u7A7A\u95F4\u3002\u624B\u673A\u7AEF\u6574\u7EC4\u6536\u8D77\uFF0C\u6570\u503C\u5728
11520
- * \u5E95\u90E8\u300C\u529F\u80FD\u575E\u300D\u91CC\u770B\uFF08\u7528\u91CF/\u4F59\u989D\u9875\u4FE1\u606F\u66F4\u5168\uFF09\uFF1B\u684C\u9762\u7AEF\u4E0D\u52A8\u3002 */
11521
- .dockchip-row{display:none}
11522
- /* \u6536\u8D77\u7684\u4FA7\u680F\u8F68\u9053\uFF08collapsed rail\uFF09\u5728\u7A84\u5C4F\u53EA\u5269 1px \u5360\u4F4D\uFF0C\u4F46\u8F68\u9053\u91CC\u7684\u6309\u94AE
11523
- * \u4ECD\u4F1A\u6EA2\u51FA\u8D34\u5728\u5DE6\u8FB9\u7F18\uFF08\u529F\u80FD\u575E/\u8FDB\u5316/\u8BBE\u7F6E\u4E09\u679A\u9732\u51FA\u534A\u8FB9\u7684\u5706\u94AE\uFF09\u3002\u9690\u85CF\u6574\u5217\uFF0C
11524
- * \u8FD9\u4E9B\u5165\u53E3\u5DF2\u7531\u5E95\u90E8 Tab \u680F\u63A5\u7BA1\uFF1B\u5C55\u5F00\u62BD\u5C49\uFF08z 80\uFF09\u4E0D\u53D7\u5F71\u54CD\u3002 */
11525
- [class*="sidebarCol"]:has([class*="collapsed"]){visibility:hidden}
11526
- /* \u8F6F\u952E\u76D8\u5F39\u51FA\uFF08visualViewport \u5224\u5B9A\uFF0C\u6302 html.dsh-dock-kbd\uFF09\uFF1ATab \u680F\u6574\u4F53\u8BA9\u4F4D\uFF08\u6253\u5B57\u65F6
11527
- * \u5B83\u53EA\u4F1A\u6324\u5360\u952E\u76D8\u4E0A\u65B9\u7684\u4E00\u7EBF\u7A7A\u95F4\uFF09\uFF0C\u6EDA\u52A8\u4F53\u7559\u767D\u6536\u7A84\u3002\u9009\u62E9\u5668\u7279\u5F02\u6027\u9AD8\u4E8E\u4E0A\u9762\u7684 72px
11528
- * \u7559\u767D\u89C4\u5219\uFF0C\u80FD\u538B\u8FC7\u53BB\u3002\u5BBF\u4E3B\u63A5\u7BA1\u671F\u95F4\u7531\u5BBF\u4E3B\u811A\u672C\u540C\u6B65\u7F6E/\u6458\u8FD9\u4E2A\u7C7B\u3002 */
11529
- html.dsh-dock-kbd .dsh-mobile-tabbar{display:none !important}
11530
- html.dsh-dock-kbd [class*="scrollBody"]{padding-bottom:12px !important}
11531
- /* \u529F\u80FD\u575E\u9762\u677F\u5728\u624B\u673A\u7AEF\u62AC\u5230\u539F\u751F\u5F39\u5C42\u4E4B\u4E0A\uFF1A\u4ECE\u4FA7\u680F\u8FDB\u7684\u5B98\u65B9\u63D2\u4EF6\u7BA1\u7406\u9875\u7B49\u539F\u751F\u5F39\u5C42 z \u5E8F\u9AD8\u4E8E
11532
- * \u9762\u677F\u9ED8\u8BA4\u7684 200\uFF0C\u529F\u80FD\u575E\u5F00\u5728\u4E0B\u9762\u4F1A\u88AB\u76D6\u4F4F\u2014\u2014\u300C\u63D2\u4EF6\u300D\u9875\u7B7E\u70B9\u4E86\u50CF\u6CA1\u53CD\u5E94/\u9732\u51FA\u539F\u9875\u9762\u3002 */
11533
- [class*="dockm-backdrop"]{z-index:2000 !important}}`,
12013
+ `,
11534
12014
  View: MobileRelayView,
11535
- HomeStat: MobileRelayHomeStat,
11536
- Overlay: MobileRelayOverlay
12015
+ HomeStat: MobileRelayHomeStat
11537
12016
  };
11538
12017
 
11539
12018
  // features/mobile/view.js
@@ -11541,54 +12020,36 @@ var import_react22 = __toESM(require("react"), 1);
11541
12020
  var import_react23 = require("react");
11542
12021
  var MOBILE_QUERY_NARROW = "(max-width: 820px)";
11543
12022
  var MOBILE_QUERY_COARSE = "(pointer: coarse) and (hover: none)";
11544
- var MOBILE_STORE_KEY = "dsh-dock/mobile/v1";
11545
- var mobilePref = { floating: true };
11546
- try {
11547
- if (typeof localStorage !== "undefined") {
11548
- const raw = localStorage.getItem(MOBILE_STORE_KEY);
11549
- const obj = raw ? JSON.parse(raw) : null;
11550
- if (obj && typeof obj === "object" && typeof obj.floating === "boolean") mobilePref.floating = obj.floating;
11551
- }
11552
- } catch {
11553
- }
11554
- var prefListeners = /* @__PURE__ */ new Set();
11555
- function notifyPref() {
11556
- for (const fn of prefListeners) {
11557
- try {
11558
- fn();
11559
- } catch {
11560
- }
11561
- }
11562
- }
11563
- function floatingShown() {
11564
- return mobilePref.floating !== false;
11565
- }
11566
- function setFloatingShown(value) {
11567
- mobilePref.floating = value !== false;
12023
+ var MOBILE_QUERY_CHROME = "(max-width: 700px)";
12024
+ function mobileActive() {
12025
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
11568
12026
  try {
11569
- if (typeof localStorage !== "undefined") localStorage.setItem(MOBILE_STORE_KEY, JSON.stringify(mobilePref));
12027
+ return window.matchMedia(MOBILE_QUERY_NARROW).matches || window.matchMedia(MOBILE_QUERY_COARSE).matches;
11570
12028
  } catch {
12029
+ return false;
11571
12030
  }
11572
- notifyPref();
11573
12031
  }
11574
- function subscribePref(fn) {
11575
- prefListeners.add(fn);
11576
- return () => {
11577
- prefListeners.delete(fn);
12032
+ function watchQuery(query, onChange) {
12033
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return () => {
11578
12034
  };
11579
- }
11580
- function mobileActive() {
11581
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
12035
+ let mq = null;
11582
12036
  try {
11583
- return window.matchMedia(MOBILE_QUERY_NARROW).matches || window.matchMedia(MOBILE_QUERY_COARSE).matches;
12037
+ mq = window.matchMedia(query);
11584
12038
  } catch {
11585
- return false;
12039
+ return () => {
12040
+ };
11586
12041
  }
12042
+ const update = () => onChange(mq.matches);
12043
+ update();
12044
+ if (typeof mq.addEventListener === "function") mq.addEventListener("change", update);
12045
+ else if (typeof mq.addListener === "function") mq.addListener(update);
12046
+ return () => {
12047
+ if (typeof mq.removeEventListener === "function") mq.removeEventListener("change", update);
12048
+ else if (typeof mq.removeListener === "function") mq.removeListener(update);
12049
+ };
11587
12050
  }
11588
- function MobileOverlay(props) {
11589
- const ctx = props && props.ctx;
12051
+ function MobileOverlay() {
11590
12052
  const [active, setActive] = (0, import_react23.useState)(() => mobileActive());
11591
- const [floating, setFloating] = (0, import_react23.useState)(() => floatingShown());
11592
12053
  (0, import_react23.useEffect)(() => {
11593
12054
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return void 0;
11594
12055
  let mqs = [];
@@ -11628,56 +12089,171 @@ function MobileOverlay(props) {
11628
12089
  document.body.removeAttribute("data-dk-mobile");
11629
12090
  };
11630
12091
  }, [active]);
11631
- (0, import_react23.useEffect)(() => subscribePref(() => setFloating(floatingShown())), []);
11632
- const [relayBand, setRelayBand] = (0, import_react23.useState)(() => {
11633
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
11634
- try {
11635
- return window.matchMedia("(max-width: 700px)").matches;
11636
- } catch {
11637
- return false;
11638
- }
11639
- });
11640
- (0, import_react23.useEffect)(() => {
11641
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") return void 0;
11642
- let mq = null;
11643
- try {
11644
- mq = window.matchMedia("(max-width: 700px)");
11645
- } catch {
11646
- return void 0;
12092
+ (0, import_react23.useEffect)(() => mobileChrome(), []);
12093
+ return null;
12094
+ }
12095
+ function mobileChrome() {
12096
+ if (typeof window === "undefined" || typeof document === "undefined") return void 0;
12097
+ const mq = window.matchMedia ? window.matchMedia(MOBILE_QUERY_CHROME) : null;
12098
+ if (!mq) return void 0;
12099
+ if (window.__dshDockMobileDrawer === "host") return void 0;
12100
+ window.__dshDockMobileDrawer = "client";
12101
+ const narrow = () => mq.matches;
12102
+ const frameEl = () => document.querySelector('[class$="frame"]');
12103
+ const sidebarCollapsed = () => {
12104
+ const f = frameEl();
12105
+ return !!f && f.hasAttribute("data-sidebar-collapsed");
12106
+ };
12107
+ const dialogOpen = () => !!document.querySelector('[role="dialog"][class*="panel"]');
12108
+ const rightOpen = () => !!document.querySelector("[data-sidebar-right-panel][data-sidebar-right-open]");
12109
+ const collapse = () => {
12110
+ const b = document.querySelector('button[aria-label="\u6536\u8D77\u4FA7\u8FB9\u680F"],button[aria-label="Collapse sidebar"]');
12111
+ if (b) b.click();
12112
+ };
12113
+ const expand = () => {
12114
+ const b = document.querySelector('button[aria-label="\u6253\u5F00\u4FA7\u8FB9\u680F"],button[aria-label="Open sidebar"]');
12115
+ if (b) b.click();
12116
+ };
12117
+ const closeDetails = () => {
12118
+ const panel = document.querySelector("[data-sidebar-right-panel][data-sidebar-right-open]");
12119
+ if (!panel) return;
12120
+ const toggle = panel.querySelector("[data-sidebar-right-toggle]");
12121
+ if (toggle) toggle.click();
12122
+ };
12123
+ const scrim = document.createElement("div");
12124
+ scrim.className = "dsh-mobile-scrim";
12125
+ document.body.appendChild(scrim);
12126
+ document.querySelectorAll(".dsh-mobile-tabbar").forEach((el) => el.remove());
12127
+ const burger = document.createElement("button");
12128
+ burger.type = "button";
12129
+ burger.className = "dsh-mobile-burger";
12130
+ burger.setAttribute("aria-label", "\u6253\u5F00\u4F1A\u8BDD\u5217\u8868");
12131
+ burger.setAttribute("aria-expanded", "false");
12132
+ burger.innerHTML = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h16"/><path d="M4 12h16"/><path d="M4 17h16"/></svg><span class="dsh-mobile-burger-badge" hidden></span>';
12133
+ burger.addEventListener("click", () => expand());
12134
+ document.body.appendChild(burger);
12135
+ const syncBurger = () => {
12136
+ const expanded = !sidebarCollapsed();
12137
+ burger.setAttribute("aria-expanded", expanded ? "true" : "false");
12138
+ burger.classList.toggle("on", expanded);
12139
+ };
12140
+ const badge = burger.querySelector(".dsh-mobile-burger-badge");
12141
+ let runstateGone = false;
12142
+ const pollTasks = () => {
12143
+ if (runstateGone || !badge || !narrow() || document.visibilityState === "hidden" || !burger.isConnected) return;
12144
+ const on = stateOf("runstate").enabled !== false;
12145
+ if (!on && !window.__dshDockRunstate) {
12146
+ badge.hidden = true;
12147
+ return;
11647
12148
  }
11648
- const update = () => setRelayBand(mq.matches);
11649
- update();
11650
- if (typeof mq.addEventListener === "function") mq.addEventListener("change", update);
11651
- else if (typeof mq.addListener === "function") mq.addListener(update);
11652
- return () => {
11653
- if (typeof mq.removeEventListener === "function") mq.removeEventListener("change", update);
11654
- else if (typeof mq.removeListener === "function") mq.removeListener(update);
11655
- };
11656
- }, []);
11657
- if (!active || !floating) return null;
11658
- const relayChromeActive = typeof window !== "undefined" && window.__dshDockMobileDrawer != null && relayBand;
11659
- if (relayChromeActive) return null;
11660
- const onToggle = () => {
11661
- try {
11662
- const layout = ctx && ctx.get ? ctx.get("layout") : null;
11663
- if (layout && typeof layout.toggleSidebar === "function") layout.toggleSidebar();
11664
- else console.warn("[dsh-dock] \u624B\u673A\u9002\u914D\uFF1Actx.layout.toggleSidebar \u4E0D\u53EF\u7528\uFF08dsh \u7248\u672C\u8FC7\u65E7\uFF1F\uFF09");
11665
- } catch (err) {
11666
- console.error("[dsh-dock] \u624B\u673A\u9002\u914D\uFF1A\u5207\u6362\u4FA7\u680F\u5931\u8D25\uFF1A", err);
12149
+ fetch("dsh-dock/runstate/status", {
12150
+ method: "POST",
12151
+ headers: { "content-type": "application/json" },
12152
+ body: "{}"
12153
+ }).then(async (res) => {
12154
+ if (res.status === 404 || res.status === 405) {
12155
+ runstateGone = true;
12156
+ badge.hidden = true;
12157
+ return null;
12158
+ }
12159
+ const body = await res.json().catch(() => null);
12160
+ if (!body) return null;
12161
+ const active = body.data && body.data.active || [];
12162
+ const waiting = active.reduce((n, t) => n + (Array.isArray(t.approvals) ? t.approvals.length : 0), 0);
12163
+ if (waiting > 0) {
12164
+ badge.textContent = waiting > 99 ? "99+" : String(waiting);
12165
+ badge.className = "dsh-mobile-burger-badge wait";
12166
+ badge.hidden = false;
12167
+ burger.setAttribute("title", waiting + " \u4E2A\u4EFB\u52A1\u7B49\u5F85\u4F60\u786E\u8BA4");
12168
+ } else if (active.length > 0) {
12169
+ badge.textContent = active.length > 99 ? "99+" : String(active.length);
12170
+ badge.className = "dsh-mobile-burger-badge";
12171
+ badge.hidden = false;
12172
+ burger.setAttribute("title", active.length + " \u4E2A\u4EFB\u52A1\u8FDB\u884C\u4E2D");
12173
+ } else {
12174
+ badge.hidden = true;
12175
+ burger.removeAttribute("title");
12176
+ }
12177
+ }).catch(() => {
12178
+ });
12179
+ };
12180
+ const tasksTimer = setInterval(pollTasks, 3e3);
12181
+ const onVisible = () => {
12182
+ if (document.visibilityState === "visible") pollTasks();
12183
+ };
12184
+ document.addEventListener("visibilitychange", onVisible);
12185
+ pollTasks();
12186
+ const vv = window.visualViewport || null;
12187
+ const kbdSync = () => {
12188
+ if (!vv) return;
12189
+ const kbd = vv.height > 0 && window.innerHeight - vv.height > 120;
12190
+ document.documentElement.classList.toggle("dsh-dock-kbd", kbd);
12191
+ };
12192
+ const sync = () => {
12193
+ kbdSync();
12194
+ document.querySelectorAll(".dsh-mobile-tabbar").forEach((el) => el.remove());
12195
+ const on = narrow() && !!frameEl() && !dialogOpen();
12196
+ const collapsed = sidebarCollapsed();
12197
+ const right = rightOpen();
12198
+ if (scrim) scrim.style.display = on && !collapsed ? "" : "none";
12199
+ burger.style.display = narrow() && !!frameEl() && !panelNav.open && !right ? "" : "none";
12200
+ syncBurger();
12201
+ };
12202
+ sync();
12203
+ const observer = typeof MutationObserver !== "undefined" ? new MutationObserver(sync) : null;
12204
+ if (observer) observer.observe(document.documentElement, { attributes: true, attributeFilter: ["data-sidebar-collapsed"], subtree: true });
12205
+ const timer = setInterval(sync, 1500);
12206
+ const onMq = () => sync();
12207
+ if (mq.addEventListener) mq.addEventListener("change", onMq);
12208
+ else if (mq.addListener) mq.addListener(onMq);
12209
+ window.addEventListener("resize", kbdSync);
12210
+ if (vv && vv.addEventListener) vv.addEventListener("resize", kbdSync);
12211
+ const onClick = (e) => {
12212
+ if (!narrow()) return;
12213
+ const t = e.target;
12214
+ if (!t || !t.closest) return;
12215
+ if (t.closest(".dsh-mobile-burger")) return;
12216
+ const expanded = !!document.querySelector('button[aria-label="\u6536\u8D77\u4FA7\u8FB9\u680F"],button[aria-label="Collapse sidebar"]');
12217
+ const col = document.querySelector('[class*="sidebarCol"]');
12218
+ const inSidebar = col && col.contains(t);
12219
+ if (!expanded && !inSidebar) return;
12220
+ if (inSidebar) {
12221
+ const row = t.closest('[role="treeitem"]');
12222
+ const rowKey = row && row.getAttribute("data-row-key") || "";
12223
+ const sessionRow = rowKey && rowKey.startsWith("session:") || row && !rowKey && !row.querySelector('[role="treeitem"]') && row.closest('[role="tree"]');
12224
+ const fresh = t.closest('[class*="newSession"]');
12225
+ if (sessionRow || fresh) {
12226
+ setTimeout(() => {
12227
+ collapse();
12228
+ closeDetails();
12229
+ }, 300);
12230
+ return;
12231
+ }
12232
+ if (rowKey.startsWith("workspace:")) return;
12233
+ setTimeout(() => {
12234
+ if (document.querySelector('[role="dialog"]')) collapse();
12235
+ }, 260);
12236
+ return;
11667
12237
  }
12238
+ if (t.closest('[role="dialog"],[class*="dockm"],[class*="dgfab"],[class*="dgwin"],[class*="dgame"],[data-sidebar-right-panel]')) return;
12239
+ collapse();
12240
+ };
12241
+ document.addEventListener("click", onClick, true);
12242
+ return () => {
12243
+ document.removeEventListener("click", onClick, true);
12244
+ if (observer) observer.disconnect();
12245
+ clearInterval(timer);
12246
+ clearInterval(tasksTimer);
12247
+ document.removeEventListener("visibilitychange", onVisible);
12248
+ if (mq.removeEventListener) mq.removeEventListener("change", onMq);
12249
+ else if (mq.removeListener) mq.removeListener(onMq);
12250
+ window.removeEventListener("resize", kbdSync);
12251
+ if (vv && vv.removeEventListener) vv.removeEventListener("resize", kbdSync);
12252
+ document.documentElement.classList.remove("dsh-dock-kbd");
12253
+ if (scrim) scrim.remove();
12254
+ burger.remove();
12255
+ if (window.__dshDockMobileDrawer === "client") delete window.__dshDockMobileDrawer;
11668
12256
  };
11669
- return import_react22.default.createElement("button", {
11670
- type: "button",
11671
- className: "dkmob-fab",
11672
- "aria-label": "\u5F00\u5173\u4FA7\u680F",
11673
- title: "\u5F00\u5173\u4FA7\u680F",
11674
- onClick: onToggle
11675
- }, import_react22.default.createElement(
11676
- "svg",
11677
- { width: 20, height: 20, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.3, "aria-hidden": true },
11678
- import_react22.default.createElement("rect", { x: 1.5, y: 2.5, width: 13, height: 11, rx: 2 }),
11679
- import_react22.default.createElement("line", { x1: 6, y1: 2.5, x2: 6, y2: 13.5 })
11680
- ));
11681
12257
  }
11682
12258
  function useViewportSize() {
11683
12259
  const [size, setSize] = (0, import_react23.useState)(() => typeof window === "undefined" ? null : { w: window.innerWidth, h: window.innerHeight });
@@ -11689,31 +12265,38 @@ function useViewportSize() {
11689
12265
  }, []);
11690
12266
  return size;
11691
12267
  }
11692
- function MobileView() {
12268
+ function useMobileState() {
11693
12269
  const [active, setActive] = (0, import_react23.useState)(() => mobileActive());
11694
- const [floating, setFloating] = (0, import_react23.useState)(() => floatingShown());
11695
- const size = useViewportSize();
12270
+ const [chrome, setChrome] = (0, import_react23.useState)(() => typeof window === "undefined" || typeof window.matchMedia !== "function" ? false : window.matchMedia(MOBILE_QUERY_CHROME).matches);
12271
+ const [owner, setOwner] = (0, import_react23.useState)(() => typeof window === "undefined" ? null : window.__dshDockMobileDrawer || null);
11696
12272
  (0, import_react23.useEffect)(() => {
11697
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") return void 0;
12273
+ const offChrome = watchQuery(MOBILE_QUERY_CHROME, setChrome);
11698
12274
  let mqs = [];
11699
12275
  try {
11700
12276
  mqs = [window.matchMedia(MOBILE_QUERY_NARROW), window.matchMedia(MOBILE_QUERY_COARSE)];
11701
12277
  } catch {
11702
- return void 0;
12278
+ mqs = [];
11703
12279
  }
11704
- const update = () => setActive(mqs.some((mq) => mq.matches));
11705
- update();
12280
+ const compute = () => setActive(mqs.some((mq) => mq.matches));
11706
12281
  for (const mq of mqs) {
11707
- if (typeof mq.addEventListener === "function") mq.addEventListener("change", update);
11708
- else if (typeof mq.addListener === "function") mq.addListener(update);
12282
+ if (typeof mq.addEventListener === "function") mq.addEventListener("change", compute);
12283
+ else if (typeof mq.addListener === "function") mq.addListener(compute);
11709
12284
  }
12285
+ const timer = setInterval(() => setOwner(typeof window === "undefined" ? null : window.__dshDockMobileDrawer || null), 1e3);
11710
12286
  return () => {
12287
+ offChrome();
11711
12288
  for (const mq of mqs) {
11712
- if (typeof mq.removeEventListener === "function") mq.removeEventListener("change", update);
11713
- else if (typeof mq.removeListener === "function") mq.removeListener(update);
12289
+ if (typeof mq.removeEventListener === "function") mq.removeEventListener("change", compute);
12290
+ else if (typeof mq.removeListener === "function") mq.removeListener(compute);
11714
12291
  }
12292
+ clearInterval(timer);
11715
12293
  };
11716
12294
  }, []);
12295
+ return { active, chrome, owner };
12296
+ }
12297
+ function MobileView() {
12298
+ const { active, chrome, owner } = useMobileState();
12299
+ const size = useViewportSize();
11717
12300
  return import_react22.default.createElement(
11718
12301
  "div",
11719
12302
  { className: "dkmob-root" },
@@ -11732,73 +12315,44 @@ function MobileView() {
11732
12315
  import_react22.default.createElement(
11733
12316
  "div",
11734
12317
  { className: "dkmob-row" },
11735
- import_react22.default.createElement("span", { className: "dkmob-label" }, "\u6D6E\u52A8\u4FA7\u680F\u6309\u94AE"),
11736
- import_react22.default.createElement("button", {
11737
- type: "button",
11738
- className: "dock-sw" + (floating ? " on" : ""),
11739
- role: "switch",
11740
- "aria-checked": floating,
11741
- "aria-label": (floating ? "\u9690\u85CF" : "\u663E\u793A") + "\u6D6E\u52A8\u4FA7\u680F\u6309\u94AE",
11742
- title: floating ? "\u9690\u85CF\u5DE6\u4E0B\u89D2\u7684\u6D6E\u52A8\u4FA7\u680F\u5F00\u5173" : "\u663E\u793A\u5DE6\u4E0B\u89D2\u7684\u6D6E\u52A8\u4FA7\u680F\u5F00\u5173",
11743
- onClick: () => setFloatingShown(!floating)
11744
- }),
11745
- import_react22.default.createElement("span", { className: "dkmob-dim" }, "44px \u5706\u94AE\u56FA\u5B9A\u5728\u5DE6\u4E0B\u89D2\uFF0C\u8C03\u7528 dsh \u5B98\u65B9 toggleSidebar \u5F00\u5408\u7A84\u5C4F\u62BD\u5C49\uFF1B\u4E0D\u9700\u8981\u65F6\u53EF\u5728\u6B64\u5173\u95ED\u3002")
12318
+ import_react22.default.createElement("span", { className: "dkmob-label" }, "\u624B\u673A\u7AEF chrome"),
12319
+ import_react22.default.createElement("span", { className: "dkmob-state" + (chrome ? " on" : "") }, chrome ? "\u2264700px\uFF0C\u751F\u6548\u4E2D" : "\u672A\u547D\u4E2D"),
12320
+ import_react22.default.createElement(
12321
+ "span",
12322
+ { className: "dkmob-dim" },
12323
+ "\u5DE6\u4E0A\u89D2\u6C49\u5821\u83DC\u5355\uFF08\u5C55\u5F00\u5DE6\u4FA7\u4F1A\u8BDD\u62BD\u5C49\uFF09+ \u4FA7\u680F\u62BD\u5C49\u906E\u7F69 + \u8F6F\u952E\u76D8\u8BA9\u4F4D\uFF1B\u5E95\u90E8 Tab \u680F\u5DF2\u6309\u7528\u6237\u8981\u6C42\u79FB\u9664\uFF0C\u63D2\u4EF6/\u8BBE\u7F6E\u7B49\u5165\u53E3\u5728\u62BD\u5C49\u91CC\u3002",
12324
+ owner === "host" ? "\u5F53\u524D\u7531\u5BBF\u4E3B\u6CE8\u5165\u7248\u9A71\u52A8\uFF08\u91CD\u542F dsh web \u540E\u751F\u6548\u7684\u90A3\u5957\uFF09\u3002" : owner === "client" ? "\u5F53\u524D\u7531\u5BA2\u6237\u7AEF\u81EA\u5EFA\u7248\u9A71\u52A8\uFF08\u5237\u65B0\u5373\u751F\u6548\uFF09\u3002" : ""
12325
+ )
11746
12326
  ),
11747
12327
  import_react22.default.createElement(
11748
12328
  "div",
11749
12329
  { className: "dkmob-note" },
11750
12330
  "\u751F\u6548\u4E2D\u7684\u4F18\u5316\uFF1A\u8F93\u5165\u7C7B\u63A7\u4EF6\u5B57\u53F7\u62C9\u5230 16px\uFF08\u907F\u514D iOS \u805A\u7126\u7F29\u653E\u6574\u9875\uFF09\xB7 \u7EAF\u56FE\u6807\u6309\u94AE\u9762\u79EF\u515C\u5E95 \u226536px \xB7 ",
11751
- "\u53BB\u6389\u70B9\u6309\u9AD8\u4EAE\u4E0E\u53CC\u51FB\u7F29\u653E\u5224\u5B9A \xB7 \u4EE3\u7801\u5757\u6A2A\u5411\u6EDA\u52A8\u3001\u884C\u5185\u4EE3\u7801\u81EA\u52A8\u6362\u884C \xB7 viewport meta \u8865 viewport-fit=cover",
12331
+ "\u53BB\u6389\u70B9\u6309\u9AD8\u4EAE\u4E0E\u53CC\u51FB\u7F29\u653E\u5224\u5B9A \xB7 \u4EE3\u7801\u5757\u6A2A\u5411\u6EDA\u52A8\u3001\u884C\u5185\u4EE3\u7801\u81EA\u52A8\u6362\u884C \xB7 \u6EDA\u52A8\u4F53 touch-action:pan-y + iOS \u60EF\u6027\u6EDA\u52A8 \xB7 ",
12332
+ "\u8BBE\u7F6E\u5F39\u5C42\u5185\u5BB9\u5217 min-height:0\uFF08\u5426\u5219\u7A84\u5C4F\u5F39\u5C42\u88AB\u5185\u5BB9\u6491\u9AD8\u540E\u6574\u7247\u6ED1\u4E0D\u52A8\uFF09\xB7 viewport meta \u8865 viewport-fit=cover",
11752
12333
  "\uFF08standalone \u5168\u5C4F\u65F6\u7ED9\u5706\u89D2/\u5218\u6D77\u7559\u5B89\u5168\u533A\uFF09\u3002\u505C\u7528\u672C\u529F\u80FD\u5373\u5168\u90E8\u8FD8\u539F\u3002"
11753
12334
  )
11754
12335
  );
11755
12336
  }
11756
12337
  function MobileStat() {
11757
- const [active, setActive] = (0, import_react23.useState)(() => mobileActive());
11758
- const [floating, setFloating] = (0, import_react23.useState)(() => floatingShown());
11759
- (0, import_react23.useEffect)(() => {
11760
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") return void 0;
11761
- let mqs = [];
11762
- try {
11763
- mqs = [window.matchMedia(MOBILE_QUERY_NARROW), window.matchMedia(MOBILE_QUERY_COARSE)];
11764
- } catch {
11765
- return void 0;
11766
- }
11767
- const update = () => setActive(mqs.some((mq) => mq.matches));
11768
- update();
11769
- for (const mq of mqs) {
11770
- if (typeof mq.addEventListener === "function") mq.addEventListener("change", update);
11771
- else if (typeof mq.addListener === "function") mq.addListener(update);
11772
- }
11773
- return () => {
11774
- for (const mq of mqs) {
11775
- if (typeof mq.removeEventListener === "function") mq.removeEventListener("change", update);
11776
- else if (typeof mq.removeListener === "function") mq.removeListener(update);
11777
- }
11778
- };
11779
- }, []);
11780
- (0, import_react23.useEffect)(() => subscribePref(() => setFloating(floatingShown())), []);
12338
+ const { active, chrome } = useMobileState();
11781
12339
  if (!active) return import_react22.default.createElement("span", null, "\u5F53\u524D\u89C6\u53E3\u672A\u547D\u4E2D\u624B\u673A\u5F62\u6001\uFF0C\u6837\u5F0F\u672A\u6CE8\u5165");
11782
- return import_react22.default.createElement("span", null, "\u624B\u673A\u6A21\u5F0F\u751F\u6548\u4E2D \xB7 \u6D6E\u52A8\u4FA7\u680F\u6309\u94AE" + (floating ? "\u663E\u793A" : "\u5DF2\u9690\u85CF"));
12340
+ return import_react22.default.createElement("span", null, "\u624B\u673A\u6A21\u5F0F\u751F\u6548\u4E2D \xB7 " + (chrome ? "\u5DE6\u4E0A\u89D2\u6C49\u5821\u83DC\u5355\u5DF2\u63A5\u7BA1\u4F1A\u8BDD\u5165\u53E3" : "chrome \u5E26\u5BBD\u672A\u547D\u4E2D\uFF08\u2264700px \u624D\u542F\u7528\uFF09"));
11783
12341
  }
11784
12342
  var css6 = [
11785
- // 浮动侧栏按钮:44px 触控面积(Apple HIG 最低建议),token 取主题变量自动适配亮暗色;
11786
- // z-index 190 压在功能坞弹层(dockm-backdrop,200)之下,弹层打开时被遮罩盖住不误触。
11787
- ".dkmob-fab{position:fixed;left:calc(env(safe-area-inset-left,0px) + 62px);bottom:calc(env(safe-area-inset-bottom,0px) + 74px);z-index:190;width:44px;height:44px;border-radius:50%;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);display:inline-flex;align-items:center;justify-content:center;padding:0;box-shadow:0 4px 14px rgb(0 0 0 / .22);cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent;transition:transform .12s var(--ds-ease-in-out),background .15s var(--ds-ease-in-out),color .15s var(--ds-ease-in-out);}",
11788
- ".dkmob-fab:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);}",
11789
- ".dkmob-fab:active{transform:scale(.94);}",
11790
12343
  // 输入类控件 16px:防 iOS 聚焦自动放大(<16px 的输入框聚焦时 Safari 强制缩放页面)
11791
12344
  'body[data-dk-mobile="1"] :is(textarea, input:not([type]), input[type="text"], input[type="search"], input[type="url"], input[type="password"], input[type="number"], input[type="email"], [contenteditable="true"]){font-size:16px !important;}',
11792
12345
  // 触控基础:去点按高亮、消双击缩放判定
11793
12346
  'body[data-dk-mobile="1"] :is(button, [role="button"]){-webkit-tap-highlight-color:transparent;touch-action:manipulation;}',
11794
12347
  // 纯图标按钮面积兜底:实测窄屏下一批 28×28 的工具钮很难点中;36px 是不引起行高剧变的上限。
11795
- // 只收「唯一子元素是 svg」的按钮,避免撑坏带文字的行内控件;排除远程访问的抽屉把手
11796
- // (.dsh-mobile-drawer-btn 贴边设计 30px 宽,被撑到 36 会破坏边缘吸附观感)与本模块 FAB。
11797
- 'body[data-dk-mobile="1"] button:has(> svg:only-child):not(.dsh-mobile-drawer-btn):not(.dkmob-fab){min-width:36px;min-height:36px;}',
12348
+ // 只收「唯一子元素是 svg」的按钮,避免撑坏带文字的行内控件;排除本模块 chrome 按钮。
12349
+ 'body[data-dk-mobile="1"] button:has(> svg:only-child):not(.dsh-mobile-burger){min-width:36px;min-height:36px;}',
11798
12350
  // 代码块横向滚动、行内代码换行:窄屏不撑破版面
11799
12351
  'body[data-dk-mobile="1"] pre{max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;}',
11800
12352
  'body[data-dk-mobile="1"] code{overflow-wrap:anywhere;}',
11801
12353
  'body[data-dk-mobile="1"] pre code{overflow-wrap:normal;}',
12354
+ // 滚动兜底:所有纵向滚动体可纵向平移 + iOS 惯性滚动(祖先合成层/触控声明不再吃掉触摸滚动)。
12355
+ 'body[data-dk-mobile="1"] :is([class*="scrollBody"], [class*="sidebarCol"] [class*="list"], [role="dialog"] [class*="options"], [class*="dockm-content"]){-webkit-overflow-scrolling:touch;overscroll-behavior-y:contain;touch-action:pan-y;}',
11802
12356
  // iOS 横竖屏切换时的字号自动膨胀关掉(:has 挂到 html 上;dsh 自身样式已依赖 :has,基线足够)
11803
12357
  'html:has(body[data-dk-mobile="1"]){-webkit-text-size-adjust:100%;text-size-adjust:100%;}',
11804
12358
  // 功能页自身
@@ -11808,14 +12362,85 @@ var css6 = [
11808
12362
  ".dkmob-state{flex:none;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);border-radius:999px;padding:1px 10px;font-size:11px;}",
11809
12363
  ".dkmob-state.on{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary);}",
11810
12364
  ".dkmob-dim{color:var(--dsw-alias-label-secondary);font-size:12px;}",
11811
- ".dkmob-note{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.7;border-top:1px solid var(--dsw-alias-border-l1);padding-top:10px;}"
12365
+ ".dkmob-note{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.7;border-top:1px solid var(--dsw-alias-border-l1);padding-top:10px;}",
12366
+ // 手机端 chrome:左上角汉堡菜单 + 抽屉遮罩(窄屏 ≤700px;与宿主半部同款)
12367
+ "@media (max-width:700px){",
12368
+ ".dsh-mobile-scrim{position:fixed;inset:0;z-index:75;background:rgba(8,10,14,.45);backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px);touch-action:none}",
12369
+ // 左上角汉堡菜单(三条横线):44×44 触控面积,避让刘海/圆角安全区;z 66 高于正文、
12370
+ // 低于遮罩 75 / 抽屉 80——抽屉展开时被自然盖住。
12371
+ // ⚠️ 视觉上刻意「不贴合卡片」:页面左上角本来就是空的,原生同类控件(右上的侧栏开关)
12372
+ // 是**裸图标**——无底色、无边框、无阴影。早期版本做成白底圆角卡片 + 阴影 + 模糊,
12373
+ // 悬浮在白底页面上像一块贴上去的异物(用户实测反馈「没有贴合边缘,很突兀」)。
12374
+ // 现在与原生同款:透明底、无边框无阴影,只留图标;点按才给极轻的底色反馈。
12375
+ // 视觉重量靠图标本身撑住(22px、1.9px 描边、主文字色),不靠容器。
12376
+ ".dsh-mobile-burger{position:fixed;left:env(safe-area-inset-left,0px);top:env(safe-area-inset-top,0px);z-index:66;box-sizing:border-box;width:44px;height:44px;display:flex;align-items:center;justify-content:center;padding:0;border:0;border-radius:0;background:transparent;color:var(--dsw-alias-label-primary,#e6eaf2);cursor:pointer;-webkit-tap-highlight-color:transparent;touch-action:manipulation;transition:color .15s var(--ds-ease-in-out,ease)}",
12377
+ ".dsh-mobile-burger svg{width:22px;height:22px;fill:none;stroke:currentColor;stroke-width:1.9;stroke-linecap:round}",
12378
+ // 展开态:图标换成强调色(与原生「已激活」的语义一致),不引入额外容器。
12379
+ ".dsh-mobile-burger.on{color:var(--dk-accent,#4d9fff)}",
12380
+ // 点按反馈走图标本身(用 :active 的底色透出,范围就是 44×44 触控区)。
12381
+ ".dsh-mobile-burger:active{background:color-mix(in srgb,currentColor 12%,transparent)}",
12382
+ // 待确认任务角标:锚在按钮右上角的小圆点/数字。刻意做成「贴边小圆点」而不是
12383
+ // 底部 Tab 栏那种带容器的页签角标——汉堡是裸图标,角标再带容器就又突兀了。
12384
+ // 等待确认(要人介入)= 红点 + 脉冲;进行中 = 蓝色数字(弱一级,不抢注意力)。
12385
+ ".dsh-mobile-burger-badge{position:absolute;top:4px;right:4px;box-sizing:border-box;min-width:8px;height:8px;padding:0;border-radius:999px;background:#4d9fff;font:700 9px/8px inherit;font-variant-numeric:tabular-nums;text-align:center;color:#fff;pointer-events:none;box-shadow:0 0 0 2px var(--dsw-alias-bg-layer-1,#12151b)}",
12386
+ ".dsh-mobile-burger-badge[hidden]{display:none}",
12387
+ ".dsh-mobile-burger-badge:not([hidden]){min-width:16px;height:16px;line-height:16px;padding:0 4px}",
12388
+ ".dsh-mobile-burger-badge.wait{background:#ef4444;animation:dsh-burger-badge-pulse 1.2s ease-in-out infinite}",
12389
+ "@keyframes dsh-burger-badge-pulse{0%,100%{transform:scale(1)}50%{transform:scale(1.18)}}",
12390
+ "@media (prefers-reduced-motion:reduce){.dsh-mobile-burger-badge.wait{animation:none}}",
12391
+ // ⚠️ 「设置页滑不动」的关键兜底(客户端无条件携带 = 刷新即生效,不必等宿主重启):
12392
+ // 窄屏把设置弹层改成纵向堆叠后,内容列若是默认 min-height:auto 就会被内容撑高
12393
+ // (无头 390×844 实测列高 1216px 塞在 844px 视口里)、再被 .panel 的 overflow:hidden
12394
+ // 裁掉——列内滚动容器([class*=options])因此 scrollHeight==clientHeight,手指怎么滑
12395
+ // 都不动。补 min-height:0 后 scrollTop 0 → 425。宿主半部 MOBILE_LAYOUT_CSS 同款;
12396
+ // 两处都写是因为宿主注入要重启 dsh web 才更新,而客户端样式刷新即生效,覆盖窗口期。
12397
+ '[role="dialog"][class*="panel"]>[class*="content"]{min-height:0!important}',
12398
+ // 输入区吸附到视口底部 + 滚动体留白(与宿主半部同款)。
12399
+ // ⚠️ 底部留白只保留 iPhone home indicator 的安全区,不再额外留白。沿革:原 72px 是
12400
+ // 「底部 Tab 栏高度 + 安全区」;Tab 栏移除后先收到 24px,但实测(真实实例 390×844
12401
+ // 像素扫描)输入卡片下方仍有 **51px** 空白带,其中 24px 是这里的滚动体内边距、
12402
+ // 6px 是下面 composerSeat 的内边距——两者都是 Tab 栏时代的遗留,在没有底栏的页面上
12403
+ // 纯属浪费。收窄到「只有安全区」后卡片下沿更贴近视口底,这 30px 直接变成会话区
12404
+ // 可显示的正文(滚动体内边距在 flex 容器里会把 composerSeat 连同消息区一起下移/增高)。
12405
+ // dsh 自带的 ~21px 底部间距(卡片 margin)保留,视觉上仍有呼吸感,不会贴死屏幕边缘。
12406
+ // ⚠️ 绝不能给滚动体加 justify-content:flex-end/center 贴底:内容会被挤到滚动体
12407
+ // 起点之外,Chrome 不把起点方向溢出算进 scrollHeight(手机视口实测
12408
+ // scrollHeight==clientHeight、maxScroll==0),会话记录彻底滚不动、首条消息永远看不到。
12409
+ // 宿主半部旧版注入里还留着那条 flex-end(要重启 dsh web 才消失),这里用更高优先级
12410
+ // 的选择器显式复位:刷新页面即修好,不必等重启。贴底只能用输入区自己的 margin-top:auto。
12411
+ '[class*="scrollBody"][data-conversation-scroll]{justify-content:flex-start !important;padding-bottom:env(safe-area-inset-bottom,0px) !important}',
12412
+ 'html:not(.dsh-dock-kbd) [class*="scrollBody"]{padding-bottom:env(safe-area-inset-bottom,0px) !important}',
12413
+ 'html.dsh-dock-kbd [class*="scrollBody"]{padding-bottom:env(safe-area-inset-bottom,0px) !important}',
12414
+ '[class*="scrollBody"]>[class*="composerSeat"]{margin-top:auto;padding-bottom:0}',
12415
+ // 输入区工具行上的 dsh-dock chip(用量/余额):窄屏下 28cqw 的宽度上限把它们挤成
12416
+ // 「…」「余..」(手机实测),既读不出数值又白占输入行空间。手机端整组收起,数值在
12417
+ // 功能坞(抽屉里进)看;桌面端不动。
12418
+ ".dockchip-row{display:none}",
12419
+ '[class*="sidebarCol"]:has([class*="collapsed"]){visibility:hidden}',
12420
+ // ⚠️ 列级 visibility:hidden 在 dsh 0.2.0 上挡不住:工作区浏览器给自己的
12421
+ // headerActions/sectionLabel 显式声明 visibility:visible(展开/收拢动画用),visibility
12422
+ // 可被后代覆盖,轨道按钮会从隐藏列里「透」出来(实测手机截图左缘游离按钮)。
12423
+ // display:none 不可被后代翻转——但只能打在**轨道根节点**上(类名同时含 root 与
12424
+ // collapsed 两个语义后缀),绝不能打在列上:frame 是「侧栏 0px + 中列 1fr + 右轨 0px」
12425
+ // 的网格,列 display:none 会从网格流里摘掉,中列被自动放置顶进 0px 轨(实测整页空屏)。
12426
+ // 弹层打开时豁免(设置弹层挂在列内部,display:none 会连坐成空气弹层),此时列退回
12427
+ // visibility 机制(由下一条 [role=dialog] 强制可见),窄屏弹层全屏铺满盖住透出的轨道。
12428
+ '[class*="sidebarCol"]:has([class*="collapsed"]):not(:has([role="dialog"])) [class*="root"][class*="collapsed"]{display:none !important}',
12429
+ // 设置/插件管理弹层挂在侧栏列内部:上面隐藏整列会把它连坐隐藏(空气弹层)。
12430
+ // visibility 可被后代覆盖,弹层关闭态整体卸载,所以只在真正打开时命中。
12431
+ '[class*="sidebarCol"]:has([class*="collapsed"]) [role="dialog"]{visibility:visible}',
12432
+ "html.dsh-dock-kbd .dsh-mobile-burger{display:none !important}",
12433
+ // 功能坞面板在手机端抬到原生弹层之上:从抽屉进的官方插件管理页等原生弹层 z 序高于
12434
+ // 面板默认的 200,功能坞开在下面会被盖住。抽屉展开时从侧栏 rail 点功能坞图标进入。
12435
+ '[class*="dockm-backdrop"]{z-index:2000 !important}',
12436
+ "}"
11812
12437
  ].join("\n");
11813
12438
  var feature11 = {
11814
12439
  id: "mobile",
11815
12440
  name: "\u624B\u673A\u9002\u914D",
11816
12441
  order: 170,
11817
12442
  accent: "#34d399",
11818
- description: "\u624B\u673A/\u7A84\u5C4F\u6392\u7248\u4E0E\u89E6\u63A7\u515C\u5E95\uFF1A16px \u8F93\u5165\u9632 iOS \u7F29\u653E\u3001\u56FE\u6807\u6309\u94AE\u89E6\u63A7\u9762\u79EF\u3001\u4EE3\u7801\u5757\u6EDA\u52A8\u3001\u6D6E\u52A8\u4FA7\u680F\u5F00\u5173",
12443
+ description: "\u624B\u673A/\u7A84\u5C4F\u6392\u7248\u4E0E\u89E6\u63A7\u515C\u5E95\uFF1A\u5DE6\u4E0A\u89D2\u6C49\u5821\u83DC\u5355 + \u4FA7\u680F\u62BD\u5C49\u300116px \u8F93\u5165\u9632 iOS \u7F29\u653E\u3001\u56FE\u6807\u6309\u94AE\u89E6\u63A7\u9762\u79EF\u3001\u4EE3\u7801\u5757\u6EDA\u52A8\u3001\u6EDA\u52A8\u515C\u5E95",
11819
12444
  defaultEnabled: true,
11820
12445
  css: css6,
11821
12446
  Overlay: MobileOverlay,
@@ -11824,7 +12449,7 @@ var feature11 = {
11824
12449
  };
11825
12450
 
11826
12451
  // src/client.jsx
11827
- var DOCK_VERSION = "0.13.0";
12452
+ var DOCK_VERSION = "0.15.0";
11828
12453
  var BUILTIN_FEATURES = [feature, feature2, feature3, feature4, feature5, feature6, feature7, feature8, feature9, feature10, feature11];
11829
12454
  var PLANNED_FEATURES = [];
11830
12455
  var PLANNED_NOTES = {};