dsh-dock 0.11.0 → 0.11.2

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/README.md CHANGED
@@ -95,7 +95,7 @@ dsh plugin --profile web remove dsh-dock # 卸载
95
95
  - **⛁ 会话用量**:显示当前会话的 Token 数与估算费用(10 秒自动刷新)
96
96
  - **余额**:显示当前选中模型的 Provider 账户余额(跟随模型切换实时更新)
97
97
 
98
- 点击芯片直接跳转功能坞对应页。每个功能可在面板页脚或设置页单独设置「会话页显示 / 隐藏」。
98
+ 点击芯片直接跳转功能坞对应页(「会话用量」打开后即按当前会话筛选出结果,无需再点查询)。每个功能可在面板页脚或设置页单独设置「会话页显示 / 隐藏」。
99
99
 
100
100
  ### 各功能使用
101
101
 
@@ -252,7 +252,7 @@ dsh plugin --profile web remove dsh-dock # 卸载
252
252
  - **⛁ 会话用量**:显示当前会话的 Token 数与估算费用(10 秒自动刷新)
253
253
  - **余额**:显示当前选中模型的 Provider 账户余额(跟随模型切换实时更新)
254
254
 
255
- 点击芯片直接跳转功能坞对应页。每个功能可在面板页脚或设置页单独设置「会话页显示 / 隐藏」。
255
+ 点击芯片直接跳转功能坞对应页(「会话用量」打开后即按当前会话筛选出结果,无需再点查询)。每个功能可在面板页脚或设置页单独设置「会话页显示 / 隐藏」。
256
256
 
257
257
  ### 各功能使用
258
258
 
package/client.js CHANGED
@@ -2470,9 +2470,9 @@ function Detail({ rec, onClose, rate }) {
2470
2470
  ["\u9519\u8BEF\u4FE1\u606F", rec.errorMsg || "\u2014"],
2471
2471
  ["\u9519\u8BEF\u7801", rec.errorCode || "\u2014"],
2472
2472
  ["Request ID", rec.requestId || "\u2014"],
2473
- ["\u8F93\u5165 Token(\u672A\u7F13\u5B58)", fmtNum(rec.inputTokens)],
2473
+ ["\u8F93\u5165 Token(\u7F13\u5B58\u547D\u4E2D)", fmtNum(rec.cacheReadTokens)],
2474
+ ["\u8F93\u5165 Token(\u7F13\u5B58\u672A\u547D\u4E2D)", fmtNum(rec.inputTokens)],
2474
2475
  ["\u8F93\u51FA Token", fmtNum(rec.outputTokens)],
2475
- ["\u7F13\u5B58\u547D\u4E2D Token", fmtNum(rec.cacheReadTokens)],
2476
2476
  ["\u7F13\u5B58\u5199\u5165 Token", fmtNum(rec.cacheWriteTokens)],
2477
2477
  ["\u63A8\u7406 Token", fmtNum(rec.reasoningTokens)],
2478
2478
  ["\u8BA1\u8D39\u8F93\u5165", fmtNum(rec.billedInput)],
@@ -2721,22 +2721,23 @@ function PricingEditor({ onClose, onSaved, embedded }) {
2721
2721
  ] }),
2722
2722
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-grid", children: [
2723
2723
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
2724
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u57FA\u51C6\u8F93\u5165" }),
2724
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u57FA\u51C6\u8F93\u5165\uFF08\u7F13\u5B58\u547D\u4E2D\uFF09" }),
2725
+ num(r.cacheRead, (v) => patchRow(i, { cacheRead: v }))
2726
+ ] }),
2727
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
2728
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u57FA\u51C6\u8F93\u5165\uFF08\u7F13\u5B58\u672A\u547D\u4E2D\uFF09" }),
2725
2729
  num(r.input, (v) => patchRow(i, { input: v }))
2726
2730
  ] }),
2727
2731
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
2728
2732
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u57FA\u51C6\u8F93\u51FA" }),
2729
2733
  num(r.output, (v) => patchRow(i, { output: v }))
2730
2734
  ] }),
2731
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
2732
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u57FA\u51C6\u7F13\u5B58\u547D\u4E2D" }),
2733
- num(r.cacheRead, (v) => patchRow(i, { cacheRead: v }))
2734
- ] }),
2735
2735
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
2736
2736
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u57FA\u51C6\u7F13\u5B58\u5199\u5165" }),
2737
2737
  num(r.cacheWrite, (v) => patchRow(i, { cacheWrite: v }))
2738
2738
  ] })
2739
2739
  ] }),
2740
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-price-hint", style: { marginTop: 2 }, children: "\u7F13\u5B58\u5199\u5165\uFF1D\u672A\u547D\u4E2D\u7F13\u5B58\u3001\u672C\u6B21\u65B0\u5EFA\u7F13\u5B58\u7684\u90A3\u90E8\u5206\u8F93\u5165\uFF08Claude \u7B49\u6309\u6EA2\u4EF7\u5355\u72EC\u6536\u5EFA\u7F13\u5B58\u8D39\uFF09\uFF1BDeepSeek \u4E0D\u6536\u6B64\u9879\uFF0C\u586B 0 \u5373\u53EF\u3002" }),
2740
2741
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-price-hint", style: { marginTop: 2 }, children: "\u5206\u65F6\u6BB5\u4EF7\uFF08\u53EF\u591A\u6BB5\uFF0C\u547D\u4E2D\u54EA\u6BB5\u7528\u54EA\u6BB5\uFF1B\u65F6\u6BB5\u5916\u56DE\u843D\u5230\u57FA\u51C6\u4EF7\u3002start>end \u8868\u793A\u8DE8\u96F6\u70B9\uFF0C\u5982 23~7\uFF1Bstart=end \u8868\u793A\u5168\u5929\uFF09" }),
2741
2742
  r.peaks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-price-hint", children: "\uFF08\u672A\u8BBE\u7F6E\u5206\u65F6\u6BB5\uFF0C\u6309\u57FA\u51C6\u4EF7\u8BA1\u8D39\uFF09" }) : null,
2742
2743
  r.peaks.map((s, k) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-pseg", children: [
@@ -2745,13 +2746,13 @@ function PricingEditor({ onClose, onSaved, embedded }) {
2745
2746
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-pseg-tilde", children: "~" }),
2746
2747
  num(s.end, (v) => patchSeg(i, k, { end: v }), { width: 56 }),
2747
2748
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-pseg-tilde", children: "\u65F6" }),
2748
- segNum(s.input, (v) => patchSeg(i, k, { input: v }), "\u8F93\u5165"),
2749
- segNum(s.output, (v) => patchSeg(i, k, { output: v }), "\u8F93\u51FA"),
2750
2749
  segNum(s.cacheRead, (v) => patchSeg(i, k, { cacheRead: v }), "\u7F13\u5B58\u547D\u4E2D"),
2751
- segNum(s.cacheWrite, (v) => patchSeg(i, k, { cacheWrite: v }), "\u7F13\u5B58\u5199\u5165"),
2750
+ segNum(s.input, (v) => patchSeg(i, k, { input: v }), "\u672A\u547D\u4E2D"),
2751
+ segNum(s.output, (v) => patchSeg(i, k, { output: v }), "\u8F93\u51FA"),
2752
+ segNum(s.cacheWrite, (v) => patchSeg(i, k, { cacheWrite: v }), "\u5199\u5165"),
2752
2753
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn tiny", onClick: () => delSeg(i, k), children: "\u5220\u9664\u65F6\u6BB5" })
2753
2754
  ] }, k)),
2754
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-price-hint", style: { opacity: 0.85 }, children: "\u65F6\u6BB5\u4EF7\u4ECE\u5DE6\u5230\u53F3\uFF1A\u8F93\u5165 / \u8F93\u51FA / \u7F13\u5B58\u547D\u4E2D / \u7F13\u5B58\u5199\u5165" }),
2755
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-price-hint", style: { opacity: 0.85 }, children: "\u65F6\u6BB5\u4EF7\u4ECE\u5DE6\u5230\u53F3\uFF1A\u8F93\u5165\uFF08\u547D\u4E2D\uFF09/ \u8F93\u5165\uFF08\u672A\u547D\u4E2D\uFF09/ \u8F93\u51FA / \u7F13\u5B58\u5199\u5165" }),
2755
2756
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn tiny", onClick: () => addSeg(i), children: "+ \u6DFB\u52A0\u65F6\u6BB5" })
2756
2757
  ] }, i)),
2757
2758
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-grid", style: { marginTop: 6 }, children: [
@@ -2772,17 +2773,17 @@ function PricingEditor({ onClose, onSaved, embedded }) {
2772
2773
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-section-title", style: { margin: "0 0 4px" }, children: "\u515C\u5E95\u5355\u4EF7\uFF08\u672A\u5339\u914D\u4EFB\u4F55\u6761\u76EE\u65F6\u4F7F\u7528\uFF09" }),
2773
2774
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-grid", children: [
2774
2775
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
2775
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u8F93\u5165" }),
2776
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u8F93\u5165\uFF08\u7F13\u5B58\u547D\u4E2D\uFF09" }),
2777
+ num(fb.cacheRead, (v) => setFb((s) => Object.assign({}, s, { cacheRead: v })))
2778
+ ] }),
2779
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
2780
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u8F93\u5165\uFF08\u7F13\u5B58\u672A\u547D\u4E2D\uFF09" }),
2776
2781
  num(fb.input, (v) => setFb((s) => Object.assign({}, s, { input: v })))
2777
2782
  ] }),
2778
2783
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
2779
2784
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u8F93\u51FA" }),
2780
2785
  num(fb.output, (v) => setFb((s) => Object.assign({}, s, { output: v })))
2781
2786
  ] }),
2782
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
2783
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u7F13\u5B58\u547D\u4E2D" }),
2784
- num(fb.cacheRead, (v) => setFb((s) => Object.assign({}, s, { cacheRead: v })))
2785
- ] }),
2786
2787
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-price-field", children: [
2787
2788
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u7F13\u5B58\u5199\u5165" }),
2788
2789
  num(fb.cacheWrite, (v) => setFb((s) => Object.assign({}, s, { cacheWrite: v })))
@@ -2798,17 +2799,17 @@ function PricingEditor({ onClose, onSaved, embedded }) {
2798
2799
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-table-wrap", style: { marginTop: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: "dtok-table", children: [
2799
2800
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
2800
2801
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u5339\u914D" }),
2801
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u5165" }),
2802
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u5165(\u547D\u4E2D)" }),
2803
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u5165(\u672A\u547D\u4E2D)" }),
2802
2804
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u51FA" }),
2803
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u7F13\u5B58\u547D\u4E2D" }),
2804
2805
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u7F13\u5B58\u5199\u5165" }),
2805
2806
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u5206\u65F6\u6BB5" })
2806
2807
  ] }) }),
2807
2808
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: cfg.builtin.map((b) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
2808
2809
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: b.match }),
2810
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: b.cacheRead }),
2809
2811
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: b.input }),
2810
2812
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: b.output }),
2811
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: b.cacheRead }),
2812
2813
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: b.cacheWrite }),
2813
2814
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: b.peaks && b.peaks.length ? b.peaks.map((s) => s.start + "~" + s.end).join("\u3001") + "\u65F6" : "\u2014" })
2814
2815
  ] }, b.match)) })
@@ -2822,6 +2823,7 @@ function PricingEditor({ onClose, onSaved, embedded }) {
2822
2823
  }
2823
2824
  function TokenLogView(props) {
2824
2825
  const navSession = props && props.params && props.params.sessionId ? props.params.sessionId : null;
2826
+ const navAt = props && props.params && props.params.navAt ? props.params.navAt : 0;
2825
2827
  const [savedFilters] = (0, import_react2.useState)(loadSavedFilters);
2826
2828
  const [fromStr, setFromStr] = (0, import_react2.useState)(() => savedFilters && savedFilters.fromStr || "");
2827
2829
  const [toStr, setToStr] = (0, import_react2.useState)(() => savedFilters && savedFilters.toStr || "");
@@ -2829,7 +2831,7 @@ function TokenLogView(props) {
2829
2831
  const [model, setModel] = (0, import_react2.useState)(() => savedFilters && savedFilters.model || "");
2830
2832
  const [status, setStatus] = (0, import_react2.useState)(() => savedFilters && savedFilters.status || "");
2831
2833
  const [effort, setEffort] = (0, import_react2.useState)(() => savedFilters && savedFilters.effort || "");
2832
- const [sessionId, setSessionId] = (0, import_react2.useState)(() => savedFilters && savedFilters.sessionId || "");
2834
+ const [sessionId, setSessionId] = (0, import_react2.useState)(() => navSession || savedFilters && savedFilters.sessionId || "");
2833
2835
  const [dim, setDim] = (0, import_react2.useState)(() => savedFilters && savedFilters.dim || "");
2834
2836
  const [sortKey, setSortKey] = (0, import_react2.useState)("time");
2835
2837
  const [sortDir, setSortDir] = (0, import_react2.useState)("desc");
@@ -2858,6 +2860,7 @@ function TokenLogView(props) {
2858
2860
  if (savedFilters.sessionId) q.sessionId = savedFilters.sessionId;
2859
2861
  if (savedFilters.dim) q.dim = savedFilters.dim;
2860
2862
  }
2863
+ if (navSession) q.sessionId = navSession;
2861
2864
  rpcCall("scan", {}).then(() => cancel ? null : rpcCall("query", q)).then((d) => {
2862
2865
  if (cancel) return;
2863
2866
  setData(d);
@@ -2891,8 +2894,10 @@ function TokenLogView(props) {
2891
2894
  setPage(0);
2892
2895
  }).catch((e) => setErr(String(e && e.message || e))).finally(() => setLoading(false));
2893
2896
  }, [buildQ]);
2897
+ const navAppliedRef = (0, import_react2.useRef)(navAt);
2894
2898
  (0, import_react2.useEffect)(() => {
2895
- if (!navSession) return;
2899
+ if (!navSession || navAppliedRef.current === navAt) return;
2900
+ navAppliedRef.current = navAt;
2896
2901
  setSessionId(navSession);
2897
2902
  setLoading(true);
2898
2903
  setErr("");
@@ -2900,7 +2905,7 @@ function TokenLogView(props) {
2900
2905
  setData(d);
2901
2906
  setPage(0);
2902
2907
  }).catch((e) => setErr(String(e && e.message || e))).finally(() => setLoading(false));
2903
- }, [navSession]);
2908
+ }, [navAt]);
2904
2909
  const resetFilters = (0, import_react2.useCallback)(() => {
2905
2910
  setFromStr("");
2906
2911
  setToStr("");
@@ -2969,8 +2974,8 @@ function TokenLogView(props) {
2969
2974
  const cards = totals ? [
2970
2975
  { v: fmtNum(totals.calls), l: "\u8C03\u7528\u6B21\u6570" },
2971
2976
  { v: fmtCompact(totals.totalTokens), l: "\u603B Token" },
2972
- { v: fmtCompact(totals.inputTokens), l: "\u8F93\u5165(\u672A\u7F13\u5B58)" },
2973
- { v: fmtCompact(totals.cacheReadTokens), l: "\u7F13\u5B58\u547D\u4E2D" },
2977
+ { v: fmtCompact(totals.cacheReadTokens), l: "\u8F93\u5165(\u547D\u4E2D)" },
2978
+ { v: fmtCompact(totals.inputTokens), l: "\u8F93\u5165(\u672A\u547D\u4E2D)" },
2974
2979
  { v: totals.cacheHitPct + "%", l: "\u7F13\u5B58\u547D\u4E2D\u7387" },
2975
2980
  { v: fmtCompact(totals.outputTokens), l: "\u8F93\u51FA" },
2976
2981
  { v: fmtCostCny(totals.cost, rateCny), l: "\u6D88\u8017\u91D1\u989D(\u4F30\u7B97\xB7\u4EBA\u6C11\u5E01)" },
@@ -2981,8 +2986,8 @@ function TokenLogView(props) {
2981
2986
  const summaryRows = (data && data.summary || []).map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
2982
2987
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: r.key }),
2983
2988
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtNum(r.calls) }),
2984
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtCompact(r.inputTokens) }),
2985
2989
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtCompact(r.cacheReadTokens) }),
2990
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtCompact(r.inputTokens) }),
2986
2991
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: r.cacheHitPct + "%" }),
2987
2992
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtCompact(r.outputTokens) }),
2988
2993
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtCompact(r.totalTokens) }),
@@ -3003,8 +3008,8 @@ function TokenLogView(props) {
3003
3008
  }, children: shortId(r.sessionId) }) }),
3004
3009
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: r.provider || "\u2014" }),
3005
3010
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: r.model || "\u2014" }),
3006
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtNum(r.inputTokens) }),
3007
3011
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtNum(r.cacheReadTokens) }),
3012
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtNum(r.inputTokens) }),
3008
3013
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: r.cacheHitPercent + "%" }),
3009
3014
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtNum(r.outputTokens) }),
3010
3015
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: fmtNum(r.reasoningTokens) }),
@@ -3047,8 +3052,8 @@ function TokenLogView(props) {
3047
3052
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
3048
3053
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u7EF4\u5EA6" }),
3049
3054
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8C03\u7528" }),
3050
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u5165" }),
3051
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u7F13\u5B58" }),
3055
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u5165(\u547D\u4E2D)" }),
3056
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u5165(\u672A\u547D\u4E2D)" }),
3052
3057
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u547D\u4E2D\u7387" }),
3053
3058
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u51FA" }),
3054
3059
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u603BToken" }),
@@ -3090,8 +3095,8 @@ function TokenLogView(props) {
3090
3095
  sortTh("\u4F1A\u8BDDID", "sessionId"),
3091
3096
  sortTh("\u63D0\u4F9B\u5546", "provider"),
3092
3097
  sortTh("\u6A21\u578B", "model"),
3093
- sortTh("\u8F93\u5165", "inputTokens"),
3094
- sortTh("\u7F13\u5B58", "cacheReadTokens"),
3098
+ sortTh("\u8F93\u5165(\u547D\u4E2D)", "cacheReadTokens"),
3099
+ sortTh("\u8F93\u5165(\u672A\u547D\u4E2D)", "inputTokens"),
3095
3100
  sortTh("\u547D\u4E2D%", "cacheHitPercent"),
3096
3101
  sortTh("\u8F93\u51FA", "outputTokens"),
3097
3102
  sortTh("\u63A8\u7406", "reasoningTokens"),
@@ -3221,7 +3226,7 @@ function TokenLogChip(props) {
3221
3226
  className: "dockchip" + (snap.err && !t ? " err" : ""),
3222
3227
  title,
3223
3228
  "aria-label": "\u4F1A\u8BDD\u7528\u91CF",
3224
- onClick: () => openPanel("tokenlog", { sessionId: sid }),
3229
+ onClick: () => openPanel("tokenlog", { sessionId: sid, navAt: Date.now() }),
3225
3230
  children: [
3226
3231
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dockchip-dot", style: { background: "var(--dk-warn)" } }),
3227
3232
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: label })
@@ -10878,7 +10883,7 @@ var feature10 = {
10878
10883
  };
10879
10884
 
10880
10885
  // src/client.jsx
10881
- var DOCK_VERSION = "0.11.0";
10886
+ var DOCK_VERSION = "0.11.2";
10882
10887
  var BUILTIN_FEATURES = [feature, feature2, feature3, feature4, feature5, feature6, feature7, feature8, feature9, feature10];
10883
10888
  var PLANNED_FEATURES = [];
10884
10889
  var PLANNED_NOTES = {};
@@ -11038,11 +11043,14 @@ var SHELL_CSS = [
11038
11043
  // 注意:旧上限 min(280px,36cqw) 仍偏宽——标准 780px 输入卡下(.row 内容约 762px),
11039
11044
  // add 钮(28)+工作区选择器(~214)+chips(280) 已与右侧模型选择器+发送键(~345px)合计超宽,
11040
11045
  // 即便 chips 未截断 .row 也会换行把模型选择器/发送键挤到第二行。实测把上限收到 189px
11041
- // (≈25cqw)并使 chips 内距收紧(padding 2px 6px、gap 2px)后,实测 4~6M 用量 + 余额
11042
- // 两 chip 可完整显示且不再换行;超限时仍被省略号截断,完整值在 title 悬浮提示里。
11046
+ // (≈25cqw)并使 chips 内距收紧(padding 2px 6px、gap 2px)后,「4~6M 用量 + 余额」
11047
+ // 两 chip 可完整显示且不再换行。v0.11.2:配置单价后费用段「· ¥0.03」让两 chip 合计
11048
+ // 约需 204px,189px 恰好截掉约 2 个数字(费用/余额小数位看不到)——按用户反馈放宽到
11049
+ // 216px(≈28cqw),标准卡下实测完整显示;若再出现把模型选择器挤换行,优先压缩用量
11050
+ // chip 的 ⛁ 前缀换空间,别再收这个上限。超限仍省略号截断,完整值在 title 悬浮提示里。
11043
11051
  // 2) 截断:超限部分用省略号截断(完整数值在 title 悬浮提示里),防 chips 凸出输入卡圆角(悬空)。
11044
- ".dockchip-row{display:inline-flex;align-items:center;gap:2px;min-width:0;flex:0 1 auto;overflow:hidden;max-width:189px;}",
11045
- "@supports (width:1cqw){.dockchip-row{max-width:min(189px,25cqw);}}",
11052
+ ".dockchip-row{display:inline-flex;align-items:center;gap:2px;min-width:0;flex:0 1 auto;overflow:hidden;max-width:216px;}",
11053
+ "@supports (width:1cqw){.dockchip-row{max-width:min(216px,28cqw);}}",
11046
11054
  ".dockchip{display:inline-flex;align-items:center;gap:5px;cursor:pointer;border:none;background:transparent;color:var(--dsw-alias-label-tertiary);border-radius:8px;padding:2px 6px;font-family:inherit;font-size:11px;line-height:18px;white-space:nowrap;min-width:0;overflow:hidden;transition:background .15s var(--ds-ease-in-out),color .15s var(--ds-ease-in-out);}",
11047
11055
  ".dockchip > span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;}",
11048
11056
  ".dockchip:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);}",
@@ -851,7 +851,8 @@ export const feature = {
851
851
  if (method === 'export') {
852
852
  loadPricingConfig()
853
853
  const list = buildQuery(payload || {}).map(withCost)
854
- const header = ['time', 'provider', 'model', 'apiKey', 'inputTokens', 'outputTokens', 'cacheReadTokens', 'cacheWriteTokens', 'reasoningTokens', 'billedInput', 'cacheHitPercent', 'totalTokens', 'costCny', 'effort', 'status', 'statusCode', 'errorCode', 'errorMsg', 'llmMs', 'sessionId', 'turn', 'step']
854
+ // 列序对齐界面习惯(缓存命中在前):… 命中 → 未命中 → 输出 → 写入
855
+ const header = ['time', 'provider', 'model', 'apiKey', 'cacheReadTokens', 'inputTokens', 'outputTokens', 'cacheWriteTokens', 'reasoningTokens', 'billedInput', 'cacheHitPercent', 'totalTokens', 'costCny', 'effort', 'status', 'statusCode', 'errorCode', 'errorMsg', 'llmMs', 'sessionId', 'turn', 'step']
855
856
  const esc = (v) => {
856
857
  const s = String(v === undefined || v === null ? '' : v)
857
858
  return /[,"\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s
@@ -189,9 +189,9 @@ function Detail({ rec, onClose, rate }) {
189
189
  ["错误信息", rec.errorMsg || "—"],
190
190
  ["错误码", rec.errorCode || "—"],
191
191
  ["Request ID", rec.requestId || "—"],
192
- ["输入 Token(未缓存)", fmtNum(rec.inputTokens)],
192
+ ["输入 Token(缓存命中)", fmtNum(rec.cacheReadTokens)],
193
+ ["输入 Token(缓存未命中)", fmtNum(rec.inputTokens)],
193
194
  ["输出 Token", fmtNum(rec.outputTokens)],
194
- ["缓存命中 Token", fmtNum(rec.cacheReadTokens)],
195
195
  ["缓存写入 Token", fmtNum(rec.cacheWriteTokens)],
196
196
  ["推理 Token", fmtNum(rec.reasoningTokens)],
197
197
  ["计费输入", fmtNum(rec.billedInput)],
@@ -451,11 +451,14 @@ function PricingEditor({ onClose, onSaved, embedded }) {
451
451
  <button className="dtok-btn tiny" onClick={() => delRow(i)}>删除模型</button>
452
452
  </div>
453
453
  <div className="dtok-price-grid">
454
- <div className="dtok-price-field"><label>基准输入</label>{num(r.input, (v) => patchRow(i, { input: v }))}</div>
454
+ <div className="dtok-price-field"><label>基准输入(缓存命中)</label>{num(r.cacheRead, (v) => patchRow(i, { cacheRead: v }))}</div>
455
+ <div className="dtok-price-field"><label>基准输入(缓存未命中)</label>{num(r.input, (v) => patchRow(i, { input: v }))}</div>
455
456
  <div className="dtok-price-field"><label>基准输出</label>{num(r.output, (v) => patchRow(i, { output: v }))}</div>
456
- <div className="dtok-price-field"><label>基准缓存命中</label>{num(r.cacheRead, (v) => patchRow(i, { cacheRead: v }))}</div>
457
457
  <div className="dtok-price-field"><label>基准缓存写入</label>{num(r.cacheWrite, (v) => patchRow(i, { cacheWrite: v }))}</div>
458
458
  </div>
459
+ <div className="dtok-price-hint" style={{ marginTop: 2 }}>
460
+ 缓存写入=未命中缓存、本次新建缓存的那部分输入(Claude 等按溢价单独收建缓存费);DeepSeek 不收此项,填 0 即可。
461
+ </div>
459
462
  <div className="dtok-price-hint" style={{ marginTop: 2 }}>
460
463
  分时段价(可多段,命中哪段用哪段;时段外回落到基准价。start&gt;end 表示跨零点,如 23~7;start=end 表示全天)
461
464
  </div>
@@ -467,14 +470,14 @@ function PricingEditor({ onClose, onSaved, embedded }) {
467
470
  <span className="dtok-pseg-tilde">~</span>
468
471
  {num(s.end, (v) => patchSeg(i, k, { end: v }), { width: 56 })}
469
472
  <span className="dtok-pseg-tilde">时</span>
470
- {segNum(s.input, (v) => patchSeg(i, k, { input: v }), "输入")}
471
- {segNum(s.output, (v) => patchSeg(i, k, { output: v }), "输出")}
472
473
  {segNum(s.cacheRead, (v) => patchSeg(i, k, { cacheRead: v }), "缓存命中")}
473
- {segNum(s.cacheWrite, (v) => patchSeg(i, k, { cacheWrite: v }), "缓存写入")}
474
+ {segNum(s.input, (v) => patchSeg(i, k, { input: v }), "未命中")}
475
+ {segNum(s.output, (v) => patchSeg(i, k, { output: v }), "输出")}
476
+ {segNum(s.cacheWrite, (v) => patchSeg(i, k, { cacheWrite: v }), "写入")}
474
477
  <button className="dtok-btn tiny" onClick={() => delSeg(i, k)}>删除时段</button>
475
478
  </div>
476
479
  ))}
477
- <div className="dtok-price-hint" style={{ opacity: .85 }}>时段价从左到右:输入 / 输出 / 缓存命中 / 缓存写入</div>
480
+ <div className="dtok-price-hint" style={{ opacity: .85 }}>时段价从左到右:输入(命中)/ 输入(未命中)/ 输出 / 缓存写入</div>
478
481
  <button className="dtok-btn tiny" onClick={() => addSeg(i)}>+ 添加时段</button>
479
482
  </div>
480
483
  ))}
@@ -492,9 +495,9 @@ function PricingEditor({ onClose, onSaved, embedded }) {
492
495
  <div>
493
496
  <div className="dtok-section-title" style={{ margin: "0 0 4px" }}>兜底单价(未匹配任何条目时使用)</div>
494
497
  <div className="dtok-price-grid">
495
- <div className="dtok-price-field"><label>输入</label>{num(fb.input, (v) => setFb((s) => Object.assign({}, s, { input: v })))}</div>
498
+ <div className="dtok-price-field"><label>输入(缓存命中)</label>{num(fb.cacheRead, (v) => setFb((s) => Object.assign({}, s, { cacheRead: v })))}</div>
499
+ <div className="dtok-price-field"><label>输入(缓存未命中)</label>{num(fb.input, (v) => setFb((s) => Object.assign({}, s, { input: v })))}</div>
496
500
  <div className="dtok-price-field"><label>输出</label>{num(fb.output, (v) => setFb((s) => Object.assign({}, s, { output: v })))}</div>
497
- <div className="dtok-price-field"><label>缓存命中</label>{num(fb.cacheRead, (v) => setFb((s) => Object.assign({}, s, { cacheRead: v })))}</div>
498
501
  <div className="dtok-price-field"><label>缓存写入</label>{num(fb.cacheWrite, (v) => setFb((s) => Object.assign({}, s, { cacheWrite: v })))}</div>
499
502
  </div>
500
503
  </div>
@@ -504,11 +507,11 @@ function PricingEditor({ onClose, onSaved, embedded }) {
504
507
  <summary className="dtok-price-hint" style={{ cursor: "pointer" }}>查看内置默认单价({cfg.builtin.length} 条,自定义配置未命中时按此兜底)</summary>
505
508
  <div className="dtok-table-wrap" style={{ marginTop: 6 }}>
506
509
  <table className="dtok-table">
507
- <thead><tr><th>匹配</th><th>输入</th><th>输出</th><th>缓存命中</th><th>缓存写入</th><th>分时段</th></tr></thead>
510
+ <thead><tr><th>匹配</th><th>输入(命中)</th><th>输入(未命中)</th><th>输出</th><th>缓存写入</th><th>分时段</th></tr></thead>
508
511
  <tbody>
509
512
  {cfg.builtin.map((b) => (
510
513
  <tr key={b.match}>
511
- <td>{b.match}</td><td>{b.input}</td><td>{b.output}</td><td>{b.cacheRead}</td><td>{b.cacheWrite}</td>
514
+ <td>{b.match}</td><td>{b.cacheRead}</td><td>{b.input}</td><td>{b.output}</td><td>{b.cacheWrite}</td>
512
515
  <td>{b.peaks && b.peaks.length ? b.peaks.map((s) => s.start + "~" + s.end).join("、") + "时" : "—"}</td>
513
516
  </tr>
514
517
  ))}
@@ -528,8 +531,10 @@ function PricingEditor({ onClose, onSaved, embedded }) {
528
531
 
529
532
  // ---------- 主视图(嵌入 dock 面板内容区) ----------
530
533
  export function TokenLogView(props) {
531
- // props.params.sessionId(chips 点击带入):立即按该会话筛选并查询
534
+ // props.params.sessionId(chips 点击带入):立即按该会话筛选并查询。
535
+ // navAt:每次 chip 点击的时间戳——面板已打开时再次点击(即便同一会话)也触发重查。
532
536
  const navSession = props && props.params && props.params.sessionId ? props.params.sessionId : null;
537
+ const navAt = props && props.params && props.params.navAt ? props.params.navAt : 0;
533
538
  // 挂载即视为「打开」:恢复上次暂存的条件(时间不选=显示全部记录); savedFilters 稳定快照, 仅初始化时读取一次
534
539
  const [savedFilters] = useState(loadSavedFilters);
535
540
  const [fromStr, setFromStr] = useState(() => (savedFilters && savedFilters.fromStr) || "");
@@ -538,7 +543,8 @@ export function TokenLogView(props) {
538
543
  const [model, setModel] = useState(() => (savedFilters && savedFilters.model) || "");
539
544
  const [status, setStatus] = useState(() => (savedFilters && savedFilters.status) || "");
540
545
  const [effort, setEffort] = useState(() => (savedFilters && savedFilters.effort) || "");
541
- const [sessionId, setSessionId] = useState(() => (savedFilters && savedFilters.sessionId) || "");
546
+ // 会话入口(chip/深链)带入的 sessionId 优先于暂存条件:下拉框初值即选中该会话
547
+ const [sessionId, setSessionId] = useState(() => navSession || (savedFilters && savedFilters.sessionId) || "");
542
548
  const [dim, setDim] = useState(() => (savedFilters && savedFilters.dim) || "");
543
549
  const [sortKey, setSortKey] = useState("time");
544
550
  const [sortDir, setSortDir] = useState("desc");
@@ -572,6 +578,9 @@ export function TokenLogView(props) {
572
578
  if (savedFilters.sessionId) q.sessionId = savedFilters.sessionId;
573
579
  if (savedFilters.dim) q.dim = savedFilters.dim;
574
580
  }
581
+ // 从会话入口点入时按带入的会话查(优先于暂存条件):打开即出当前会话结果,
582
+ // 不再需要手动点「查询」。只保留这一条初始查询,避免并发的会话查询被本链路覆盖。
583
+ if (navSession) q.sessionId = navSession;
575
584
  rpcCall("scan", {})
576
585
  .then(() => (cancel ? null : rpcCall("query", q)))
577
586
  .then((d) => {
@@ -606,16 +615,20 @@ export function TokenLogView(props) {
606
615
  .finally(() => setLoading(false));
607
616
  }, [buildQ]);
608
617
 
609
- // chips 定位:按带入的会话立即筛选查询(覆盖暂存条件里的会话项)
618
+ // chips 定位:面板已打开时再次从会话入口点击 → 立即按该会话筛选查询。
619
+ // 挂载时的首次带入已并入初始查询(sessionId 初值 + 挂载 effect),以 navAt 判同跳过,
620
+ // 避免打开面板时同一条件跑两遍、且挂载查询链路较慢把会话结果覆盖回全量。
621
+ const navAppliedRef = useRef(navAt);
610
622
  useEffect(() => {
611
- if (!navSession) return;
623
+ if (!navSession || navAppliedRef.current === navAt) return;
624
+ navAppliedRef.current = navAt;
612
625
  setSessionId(navSession);
613
626
  setLoading(true); setErr("");
614
627
  rpcCall("query", Object.assign({}, buildQ(true), { sessionId: navSession }))
615
628
  .then((d) => { setData(d); setPage(0); })
616
629
  .catch((e) => setErr(String((e && e.message) || e)))
617
630
  .finally(() => setLoading(false));
618
- }, [navSession]);
631
+ }, [navAt]);
619
632
 
620
633
  // 重置: 清空所有筛选(时间不选=显示全部记录), 并立即查询
621
634
  const resetFilters = useCallback(() => { setFromStr(""); setToStr("");
@@ -684,8 +697,8 @@ export function TokenLogView(props) {
684
697
  const cards = totals ? [
685
698
  { v: fmtNum(totals.calls), l: "调用次数" },
686
699
  { v: fmtCompact(totals.totalTokens), l: "总 Token" },
687
- { v: fmtCompact(totals.inputTokens), l: "输入(未缓存)" },
688
- { v: fmtCompact(totals.cacheReadTokens), l: "缓存命中" },
700
+ { v: fmtCompact(totals.cacheReadTokens), l: "输入(命中)" },
701
+ { v: fmtCompact(totals.inputTokens), l: "输入(未命中)" },
689
702
  { v: totals.cacheHitPct + "%", l: "缓存命中率" },
690
703
  { v: fmtCompact(totals.outputTokens), l: "输出" },
691
704
  { v: fmtCostCny(totals.cost, rateCny), l: "消耗金额(估算·人民币)" },
@@ -702,8 +715,8 @@ export function TokenLogView(props) {
702
715
 
703
716
  const summaryRows = (data && data.summary || []).map((r) => (
704
717
  <tr key={r.key}>
705
- <td>{r.key}</td><td>{fmtNum(r.calls)}</td><td>{fmtCompact(r.inputTokens)}</td>
706
- <td>{fmtCompact(r.cacheReadTokens)}</td><td>{r.cacheHitPct + "%"}</td><td>{fmtCompact(r.outputTokens)}</td>
718
+ <td>{r.key}</td><td>{fmtNum(r.calls)}</td><td>{fmtCompact(r.cacheReadTokens)}</td>
719
+ <td>{fmtCompact(r.inputTokens)}</td><td>{r.cacheHitPct + "%"}</td><td>{fmtCompact(r.outputTokens)}</td>
707
720
  <td>{fmtCompact(r.totalTokens)}</td>
708
721
  <td>{fmtCostCny(r.cost, rateCny)}</td>
709
722
  <td>{fmtDuration(r.llmMs)}</td>
@@ -724,8 +737,8 @@ export function TokenLogView(props) {
724
737
  <td><span className="dtok-sid" title="点击按此会话筛选" onClick={() => { setSessionId(r.sessionId); runQuery(); }}>{shortId(r.sessionId)}</span></td>
725
738
  <td>{r.provider || "—"}</td>
726
739
  <td>{r.model || "—"}</td>
727
- <td>{fmtNum(r.inputTokens)}</td>
728
740
  <td>{fmtNum(r.cacheReadTokens)}</td>
741
+ <td>{fmtNum(r.inputTokens)}</td>
729
742
  <td>{r.cacheHitPercent + "%"}</td>
730
743
  <td>{fmtNum(r.outputTokens)}</td>
731
744
  <td>{fmtNum(r.reasoningTokens)}</td>
@@ -756,7 +769,7 @@ export function TokenLogView(props) {
756
769
  <div key="sumtab" className="dtok-table-wrap">
757
770
  <table className="dtok-table">
758
771
  <thead><tr>
759
- <th>维度</th><th>调用</th><th>输入</th><th>缓存</th><th>命中率</th><th>输出</th><th>总Token</th><th>金额(人民币)</th><th>耗时</th>
772
+ <th>维度</th><th>调用</th><th>输入(命中)</th><th>输入(未命中)</th><th>命中率</th><th>输出</th><th>总Token</th><th>金额(人民币)</th><th>耗时</th>
760
773
  </tr></thead>
761
774
  <tbody>{summaryRows}</tbody>
762
775
  </table>
@@ -788,7 +801,7 @@ export function TokenLogView(props) {
788
801
  <table className="dtok-table">
789
802
  <thead><tr>
790
803
  {sortTh("时间", "time")}{sortTh("会话ID", "sessionId")}{sortTh("提供商", "provider")}{sortTh("模型", "model")}
791
- {sortTh("输入", "inputTokens")}{sortTh("缓存", "cacheReadTokens")}{sortTh("命中%", "cacheHitPercent")}{sortTh("输出", "outputTokens")}
804
+ {sortTh("输入(命中)", "cacheReadTokens")}{sortTh("输入(未命中)", "inputTokens")}{sortTh("命中%", "cacheHitPercent")}{sortTh("输出", "outputTokens")}
792
805
  {sortTh("推理", "reasoningTokens")}{sortTh("总额", "totalTokens")}{sortTh("金额(人民币)", "cost")}{sortTh("强度", "effort")}
793
806
  {sortTh("状态", "status")}{sortTh("耗时", "llmMs")}
794
807
  </tr></thead>
@@ -899,7 +912,7 @@ export function TokenLogChip(props) {
899
912
  : (snap.err ? snap.err + "\n" : "") + "点击在功能坞查看用量记录";
900
913
  return (
901
914
  <button type="button" className={"dockchip" + (snap.err && !t ? " err" : "")} title={title} aria-label="会话用量"
902
- onClick={() => openPanel("tokenlog", { sessionId: sid })}>
915
+ onClick={() => openPanel("tokenlog", { sessionId: sid, navAt: Date.now() })}>
903
916
  <span className="dockchip-dot" style={{ background: "var(--dk-warn)" }} />
904
917
  <span>{label}</span>
905
918
  </button>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-dock",
3
- "version": "0.11.0",
3
+ "version": "0.11.2",
4
4
  "description": "DeepSeek Harness 功能坞插件(dsh-dock):模块化功能中枢——每个功能一个 features/ 模块(可单独提取成包发布,又可经 dockBridge 装回面板)。含用量记录(LLM 调用记账/费用统计,支持各模型自定义单价与多段分时价)、模型设置、图片理解代理、模型余额、任务动画(19 种运行动画 + 桌面伙伴 3D 场景)、任务通知(完成/异常/需确认通知、提示音、系统通知、钉钉/飞书推送)、运行状态(进行中任务与最近完成一览);侧栏入口 + 可拖拽弹窗(最大化/最小化/缩放)。",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/src/client.jsx CHANGED
@@ -30,7 +30,7 @@ import { feature as fGames } from "../features/games/view.jsx";
30
30
  import { feature as fMobileRelay } from "../features/mobile-relay/view.jsx";
31
31
 
32
32
  const name = "dsh-dock";
33
- const DOCK_VERSION = "0.11.0";
33
+ const DOCK_VERSION = "0.11.2";
34
34
 
35
35
  // ---- 内置功能注册表:新功能 = features/<id>/ 加模块 + 这里 import 一行 ----
36
36
  const BUILTIN_FEATURES = [fTokenlog, fModelconfig, fHeartbeat, fTheme, fBalance, fAnimation, fNotify, fRunState, fGames, fMobileRelay];
@@ -206,11 +206,14 @@ const SHELL_CSS = [
206
206
  // 注意:旧上限 min(280px,36cqw) 仍偏宽——标准 780px 输入卡下(.row 内容约 762px),
207
207
  // add 钮(28)+工作区选择器(~214)+chips(280) 已与右侧模型选择器+发送键(~345px)合计超宽,
208
208
  // 即便 chips 未截断 .row 也会换行把模型选择器/发送键挤到第二行。实测把上限收到 189px
209
- // (≈25cqw)并使 chips 内距收紧(padding 2px 6px、gap 2px)后,实测 4~6M 用量 + 余额
210
- // 两 chip 可完整显示且不再换行;超限时仍被省略号截断,完整值在 title 悬浮提示里。
209
+ // (≈25cqw)并使 chips 内距收紧(padding 2px 6px、gap 2px)后,「4~6M 用量 + 余额」
210
+ // 两 chip 可完整显示且不再换行。v0.11.2:配置单价后费用段「· ¥0.03」让两 chip 合计
211
+ // 约需 204px,189px 恰好截掉约 2 个数字(费用/余额小数位看不到)——按用户反馈放宽到
212
+ // 216px(≈28cqw),标准卡下实测完整显示;若再出现把模型选择器挤换行,优先压缩用量
213
+ // chip 的 ⛁ 前缀换空间,别再收这个上限。超限仍省略号截断,完整值在 title 悬浮提示里。
211
214
  // 2) 截断:超限部分用省略号截断(完整数值在 title 悬浮提示里),防 chips 凸出输入卡圆角(悬空)。
212
- ".dockchip-row{display:inline-flex;align-items:center;gap:2px;min-width:0;flex:0 1 auto;overflow:hidden;max-width:189px;}",
213
- "@supports (width:1cqw){.dockchip-row{max-width:min(189px,25cqw);}}",
215
+ ".dockchip-row{display:inline-flex;align-items:center;gap:2px;min-width:0;flex:0 1 auto;overflow:hidden;max-width:216px;}",
216
+ "@supports (width:1cqw){.dockchip-row{max-width:min(216px,28cqw);}}",
214
217
  ".dockchip{display:inline-flex;align-items:center;gap:5px;cursor:pointer;border:none;background:transparent;color:var(--dsw-alias-label-tertiary);border-radius:8px;padding:2px 6px;font-family:inherit;font-size:11px;line-height:18px;white-space:nowrap;min-width:0;overflow:hidden;transition:background .15s var(--ds-ease-in-out),color .15s var(--ds-ease-in-out);}",
215
218
  ".dockchip > span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;}",
216
219
  ".dockchip:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);}",