dsh-dock 0.11.3 → 0.11.5

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
@@ -2346,6 +2346,21 @@ function fmtCostCnyExact(usd, rate) {
2346
2346
  const v = (Number(usd) || 0) * (Number(rate) > 0 ? Number(rate) : 7.2);
2347
2347
  return "\xA5" + v.toFixed(4);
2348
2348
  }
2349
+ function fmtDurCompact(ms) {
2350
+ if (ms === null || ms === void 0 || isNaN(ms)) return "\u2014";
2351
+ if (ms < 1e3) return Math.round(ms) + "ms";
2352
+ if (ms < 6e4) return (ms / 1e3).toFixed(1) + "s";
2353
+ if (ms < 36e5) return Math.round(ms / 6e4) + "m";
2354
+ return (ms / 36e5).toFixed(1) + "h";
2355
+ }
2356
+ function startOfToday() {
2357
+ const d = /* @__PURE__ */ new Date();
2358
+ d.setHours(0, 0, 0, 0);
2359
+ return d.getTime();
2360
+ }
2361
+ function hourOf(ts) {
2362
+ return new Date(ts).getHours();
2363
+ }
2349
2364
  var EXPORT_COLUMNS = [
2350
2365
  ["\u65F6\u95F4", (r) => fmtTime(r.time)],
2351
2366
  ["\u4F1A\u8BDDID", (r) => r.sessionId || ""],
@@ -2428,6 +2443,204 @@ function statusInfo(r) {
2428
2443
  if (r.status === "interrupted") return { code: 500, label: "500", cls: "warn", title: "\u4E2D\u65AD" };
2429
2444
  return { code: 0, label: "\u2026", cls: "pend", title: "\u8FDB\u884C\u4E2D" };
2430
2445
  }
2446
+ var CHART_COLORS = ["#60a5fa", "#fbbf24", "#4ade80", "#f472b6", "#a78bfa", "#fb923c", "#2dd4bf", "#e879f9", "#94a3b8", "#f87171"];
2447
+ function chartColor(i) {
2448
+ return CHART_COLORS[i % CHART_COLORS.length];
2449
+ }
2450
+ var CHART_METRICS = [
2451
+ ["totalTokens", "\u603B Token", (v) => fmtCompact(v)],
2452
+ ["calls", "\u8C03\u7528\u6B21\u6570", (v) => fmtNum(v)],
2453
+ ["cost", "\u91D1\u989D", (v, rate) => fmtCostCny(v, rate)],
2454
+ ["outputTokens", "\u8F93\u51FA Token", (v) => fmtCompact(v)]
2455
+ ];
2456
+ var CHART_DIMS = [
2457
+ ["model", "\u6A21\u578B"],
2458
+ ["provider", "\u63D0\u4F9B\u5546"],
2459
+ ["status", "\u72B6\u6001"],
2460
+ ["effort", "\u63A8\u7406\u5F3A\u5EA6"]
2461
+ ];
2462
+ function emptyAgg() {
2463
+ return { calls: 0, totalTokens: 0, outputTokens: 0, inputTokens: 0, cacheReadTokens: 0, reasoningTokens: 0, cost: 0, llmMs: 0, timed: 0 };
2464
+ }
2465
+ function addToAgg(a, r) {
2466
+ a.calls += 1;
2467
+ a.totalTokens += Number(r.totalTokens) || 0;
2468
+ a.outputTokens += Number(r.outputTokens) || 0;
2469
+ a.inputTokens += Number(r.inputTokens) || 0;
2470
+ a.cacheReadTokens += Number(r.cacheReadTokens) || 0;
2471
+ a.reasoningTokens += Number(r.reasoningTokens) || 0;
2472
+ a.cost += Number(r.cost) || 0;
2473
+ if (typeof r.llmMs === "number") {
2474
+ a.llmMs += r.llmMs;
2475
+ a.timed += 1;
2476
+ }
2477
+ }
2478
+ function groupByKey(records, keyFn, metric) {
2479
+ const map = /* @__PURE__ */ new Map();
2480
+ for (const r of records) {
2481
+ const k = keyFn(r);
2482
+ let a = map.get(k);
2483
+ if (!a) {
2484
+ a = emptyAgg();
2485
+ a.key = k;
2486
+ map.set(k, a);
2487
+ }
2488
+ addToAgg(a, r);
2489
+ }
2490
+ return [...map.values()].sort((a, b) => (b[metric] || 0) - (a[metric] || 0));
2491
+ }
2492
+ function DonutChart({ items, metric, metricFmt, rate, centerLabel, emptyText }) {
2493
+ const total = items.reduce((s, it) => s + (Number(it[metric]) || 0), 0);
2494
+ if (!items.length || total <= 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-chart-empty", children: emptyText || "\u6682\u65E0\u6570\u636E" });
2495
+ const shown = items.slice(0, 8);
2496
+ const restSum = items.slice(8).reduce((s, it) => s + (Number(it[metric]) || 0), 0);
2497
+ if (restSum > 0) {
2498
+ const restAgg = Object.assign(emptyAgg(), { key: "\u5176\u4ED6(" + (items.length - 8) + "\u9879)" });
2499
+ restAgg[metric] = restSum;
2500
+ shown.push(restAgg);
2501
+ }
2502
+ const R = 15.9155;
2503
+ let acc = 0;
2504
+ const segs = shown.map((it, i) => {
2505
+ const v = Number(it[metric]) || 0;
2506
+ const frac = v / total;
2507
+ const seg = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2508
+ "circle",
2509
+ {
2510
+ cx: "21",
2511
+ cy: "21",
2512
+ r: R,
2513
+ fill: "none",
2514
+ stroke: chartColor(i),
2515
+ strokeWidth: frac > 0.02 ? 7 : 5,
2516
+ strokeDasharray: frac * 100 + " " + (100 - frac * 100),
2517
+ strokeDashoffset: String(25 - acc * 100),
2518
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("title", { children: it.key + "\uFF1A" + metricFmt(v, rate) + "\uFF08" + (frac * 100).toFixed(1) + "%\uFF09" })
2519
+ },
2520
+ it.key
2521
+ );
2522
+ acc += frac;
2523
+ return seg;
2524
+ });
2525
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-donut-wrap", children: [
2526
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className: "dtok-donut", viewBox: "0 0 42 42", role: "img", children: [
2527
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "21", cy: "21", r: R, fill: "none", stroke: "var(--dsw-alias-border-l1)", strokeWidth: "7" }),
2528
+ segs,
2529
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: "21", y: "19.5", textAnchor: "middle", className: "dtok-donut-v", children: metricFmt(total, rate) }),
2530
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: "21", y: "26", textAnchor: "middle", className: "dtok-donut-l", children: centerLabel })
2531
+ ] }),
2532
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-legend", children: shown.map((it, i) => {
2533
+ const v = Number(it[metric]) || 0;
2534
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-legend-row", title: it.key, children: [
2535
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-dot", style: { background: chartColor(i) } }),
2536
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-legend-k", children: it.key }),
2537
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dtok-legend-v", children: [
2538
+ metricFmt(v, rate),
2539
+ " \xB7 ",
2540
+ (v / total * 100).toFixed(1),
2541
+ "%"
2542
+ ] })
2543
+ ] }, it.key);
2544
+ }) })
2545
+ ] });
2546
+ }
2547
+ function HBarChart({ items, metric, metricFmt, rate, maxRows, emptyText }) {
2548
+ if (!items.length) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-chart-empty", children: emptyText || "\u6682\u65E0\u6570\u636E" });
2549
+ const shown = items.slice(0, maxRows || 12);
2550
+ const max = Math.max(...shown.map((it) => Number(it[metric]) || 0), 1e-9);
2551
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-hbars", children: shown.map((it, i) => {
2552
+ const v = Number(it[metric]) || 0;
2553
+ const pct = Math.max(1.5, v / max * 100);
2554
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-hbar-row", title: it.key + "\uFF1A" + metricFmt(v, rate), children: [
2555
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-hbar-k", children: it.key }),
2556
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-hbar-track", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-hbar-fill", style: { width: pct + "%", background: chartColor(i) } }) }),
2557
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-hbar-v", children: metricFmt(v, rate) })
2558
+ ] }, it.key);
2559
+ }) });
2560
+ }
2561
+ function HourChart({ hours, metric, metricFmt, rate, emptyText }) {
2562
+ const total = hours.reduce((s, h) => s + (Number(h[metric]) || 0), 0);
2563
+ if (total <= 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-chart-empty", children: emptyText || "\u6682\u65E0\u6570\u636E" });
2564
+ const max = Math.max(...hours.map((h) => Number(h[metric]) || 0), 1e-9);
2565
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-hours", role: "img", children: hours.map((h) => {
2566
+ const v = Number(h[metric]) || 0;
2567
+ const pct = v > 0 ? Math.max(2, v / max * 100) : 0;
2568
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-hour-col", title: h.hour + " \u65F6\uFF1A" + metricFmt(v, rate), children: pct > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-hour-bar", style: { height: pct + "%" } }) : null }, h.hour);
2569
+ }) });
2570
+ }
2571
+ function ChartsPanel({ records, rate }) {
2572
+ const [metric, setMetric] = (0, import_react2.useState)("totalTokens");
2573
+ const [dim, setDimLocal] = (0, import_react2.useState)("model");
2574
+ const metricDef = CHART_METRICS.find((m) => m[0] === metric) || CHART_METRICS[0];
2575
+ const metricFmt = metricDef[2];
2576
+ const todayRecs = (0, import_react2.useMemo)(() => {
2577
+ const t0 = startOfToday();
2578
+ return records.filter((r) => Number(r.time) >= t0);
2579
+ }, [records]);
2580
+ const todayByModel = (0, import_react2.useMemo)(() => groupByKey(todayRecs, (r) => r.model || "(\u672A\u77E5)", metric), [todayRecs, metric]);
2581
+ const byDim = (0, import_react2.useMemo)(() => groupByKey(records, (r) => r[dim] || "(\u7A7A)", metric), [records, dim, metric]);
2582
+ const modelTime = (0, import_react2.useMemo)(() => groupByKey(records, (r) => r.model || "(\u672A\u77E5)", "llmMs"), [records]);
2583
+ const timedCalls = (0, import_react2.useMemo)(() => records.reduce((s, r) => s + (typeof r.llmMs === "number" ? 1 : 0), 0), [records]);
2584
+ const hours = (0, import_react2.useMemo)(() => {
2585
+ const arr = [];
2586
+ for (let h = 0; h < 24; h++) {
2587
+ const a = emptyAgg();
2588
+ a.hour = h;
2589
+ arr.push(a);
2590
+ }
2591
+ for (const r of records) addToAgg(arr[hourOf(r.time)], r);
2592
+ return arr;
2593
+ }, [records]);
2594
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-charts", children: [
2595
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-chart-toolbar", children: [
2596
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-chart-toolbar-label", children: "\u6307\u6807" }),
2597
+ CHART_METRICS.map(([id, label]) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn tiny" + (metric === id ? " primary" : ""), onClick: () => setMetric(id), children: label }, id)),
2598
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-chart-toolbar-note", children: "\u56FE\u8868\u8DDF\u968F\u4E0A\u65B9\u7B5B\u9009\u6761\u4EF6\uFF08\u65F6\u95F4 / \u4F1A\u8BDD / \u63D0\u4F9B\u5546 / \u6A21\u578B / \u72B6\u6001 / \u5F3A\u5EA6\uFF09\u5B9E\u65F6\u8054\u52A8" })
2599
+ ] }),
2600
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-chart-grid", children: [
2601
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-chart-card", children: [
2602
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-chart-title", children: [
2603
+ "\u4ECA\u65E5\u6A21\u578B\u7528\u91CF\u5360\u6BD4",
2604
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dtok-chart-sub", children: [
2605
+ metricDef[1],
2606
+ " \xB7 ",
2607
+ todayRecs.length,
2608
+ " \u6B21\u8C03\u7528"
2609
+ ] })
2610
+ ] }),
2611
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DonutChart, { items: todayByModel, metric, metricFmt, rate, centerLabel: "\u4ECA\u65E5" + metricDef[1], emptyText: "\u4ECA\u65E5\u6682\u65E0\u8C03\u7528" })
2612
+ ] }),
2613
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-chart-card", children: [
2614
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-chart-title", children: [
2615
+ "24 \u5C0F\u65F6\u8C03\u7528\u5206\u5E03",
2616
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dtok-chart-sub", children: [
2617
+ "\u6309\u5C0F\u65F6\u805A\u5408 \xB7 ",
2618
+ metricDef[1]
2619
+ ] })
2620
+ ] }),
2621
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HourChart, { hours, metric, metricFmt, rate })
2622
+ ] }),
2623
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-chart-card", children: [
2624
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-chart-title", children: [
2625
+ "\u6A21\u578B\u8017\u65F6\u6392\u884C",
2626
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dtok-chart-sub", children: [
2627
+ "\u7D2F\u8BA1 LLM \u8017\u65F6 \xB7 \u5DF2\u8BA1\u65F6 ",
2628
+ timedCalls,
2629
+ " \u6B21"
2630
+ ] })
2631
+ ] }),
2632
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HBarChart, { items: modelTime, metric: "llmMs", metricFmt: (v) => fmtDurCompact(v), rate, emptyText: "\u6682\u65E0\u8BA1\u65F6\u6570\u636E" })
2633
+ ] }),
2634
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-chart-card", children: [
2635
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-chart-title", children: [
2636
+ "\u5206\u5E03\u7EDF\u8BA1",
2637
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dtok-chart-sub", children: CHART_DIMS.map(([id, label]) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn tiny" + (dim === id ? " primary" : ""), style: { marginLeft: 4 }, onClick: () => setDimLocal(id), children: label }, id)) })
2638
+ ] }),
2639
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HBarChart, { items: byDim, metric, metricFmt, rate })
2640
+ ] })
2641
+ ] })
2642
+ ] });
2643
+ }
2431
2644
  var css = `
2432
2645
  .dtok-root{display:flex;flex-direction:column;gap:8px;width:100%;min-width:0;}
2433
2646
  .dtok-status{display:flex;align-items:center;gap:10px;font-size:12px;color:var(--dsw-alias-label-secondary);flex-wrap:wrap;}
@@ -2558,6 +2771,35 @@ var css = `
2558
2771
  .dtok-src{font-size:11px;color:var(--dsw-alias-label-tertiary,#94a3b8);}
2559
2772
  .dtok-src.custom{color:var(--dsw-alias-state-success-primary,#4ade80);}
2560
2773
  .dtok-src.fallback{color:var(--dk-warn);}
2774
+ /* ---- \u56FE\u8868\u7EDF\u8BA1\uFF08\u7EAF CSS/SVG\uFF09 ---- */
2775
+ .dtok-charts{display:flex;flex-direction:column;gap:10px;min-width:0;}
2776
+ .dtok-chart-toolbar{display:flex;align-items:center;gap:6px;flex-wrap:wrap;padding:6px 10px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;background:var(--dsw-alias-bg-layer-1);}
2777
+ .dtok-chart-toolbar-label{font-size:12px;color:var(--dsw-alias-label-secondary);flex:none;}
2778
+ .dtok-chart-toolbar-note{font-size:11px;color:var(--dsw-alias-label-tertiary,#94a3b8);margin-left:auto;}
2779
+ .dtok-chart-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));gap:10px;}
2780
+ .dtok-chart-card{border:1px solid var(--dsw-alias-border-l1);border-radius:10px;background:var(--dsw-alias-bg-layer-1);padding:10px 12px;min-width:0;display:flex;flex-direction:column;gap:8px;}
2781
+ .dtok-chart-title{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);display:flex;align-items:center;gap:8px;flex-wrap:wrap;}
2782
+ .dtok-chart-sub{font-size:11px;font-weight:400;color:var(--dsw-alias-label-secondary);}
2783
+ .dtok-chart-empty{text-align:center;color:var(--dsw-alias-label-secondary);padding:28px 0;font-size:12px;}
2784
+ .dtok-donut-wrap{display:flex;align-items:center;gap:14px;flex-wrap:wrap;}
2785
+ .dtok-donut{width:150px;height:150px;flex:none;}
2786
+ .dtok-donut-v{font-size:6.5px;font-weight:700;fill:var(--dsw-alias-label-primary);}
2787
+ .dtok-donut-l{font-size:3.6px;fill:var(--dsw-alias-label-secondary);}
2788
+ .dtok-legend{flex:1;min-width:150px;display:flex;flex-direction:column;gap:3px;max-height:170px;overflow:auto;}
2789
+ .dtok-legend-row{display:flex;align-items:center;gap:6px;font-size:11px;min-width:0;}
2790
+ .dtok-dot{width:8px;height:8px;border-radius:50%;flex:none;}
2791
+ .dtok-legend-k{color:var(--dsw-alias-label-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;}
2792
+ .dtok-legend-v{color:var(--dsw-alias-label-secondary);margin-left:auto;flex:none;}
2793
+ .dtok-hbars{display:flex;flex-direction:column;gap:4px;max-height:220px;overflow:auto;}
2794
+ .dtok-hbar-row{display:flex;align-items:center;gap:8px;font-size:11px;min-width:0;}
2795
+ .dtok-hbar-k{flex:0 1 34%;color:var(--dsw-alias-label-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
2796
+ .dtok-hbar-track{flex:1;height:12px;border-radius:4px;background:rgb(127 127 127 / .16);overflow:hidden;min-width:40px;}
2797
+ .dtok-hbar-fill{display:block;height:100%;border-radius:4px;transition:width .3s ease;}
2798
+ .dtok-hbar-v{flex:none;color:var(--dsw-alias-label-secondary);min-width:52px;text-align:right;}
2799
+ .dtok-hours{display:flex;align-items:flex-end;gap:3px;height:130px;padding-top:4px;}
2800
+ .dtok-hour-col{flex:1;display:flex;align-items:flex-end;height:100%;min-width:0;}
2801
+ .dtok-hour-bar{display:block;width:100%;border-radius:3px 3px 0 0;background:linear-gradient(180deg,var(--dk-accent),rgb(127 127 127 / .25));min-height:2px;transition:height .3s ease;}
2802
+ .dtok-hour-col:hover .dtok-hour-bar{filter:brightness(1.3);}
2561
2803
  `;
2562
2804
  function Detail({ rec, onClose, rate }) {
2563
2805
  const st = statusInfo(rec);
@@ -2975,6 +3217,7 @@ function TokenLogView(props) {
2975
3217
  const [page, setPage] = (0, import_react2.useState)(0);
2976
3218
  const [detailRec, setDetailRec] = (0, import_react2.useState)(null);
2977
3219
  const [showPricing, setShowPricing] = (0, import_react2.useState)(() => !!(props && props.params && props.params.openPricing));
3220
+ const [tab, setTab] = (0, import_react2.useState)(() => props && props.params && props.params.charts ? "charts" : "detail");
2978
3221
  const [fetchedAt, setFetchedAt] = (0, import_react2.useState)(0);
2979
3222
  const [justUpdated, setJustUpdated] = (0, import_react2.useState)(false);
2980
3223
  const wasLoadingRef = (0, import_react2.useRef)(false);
@@ -3182,83 +3425,95 @@ function TokenLogView(props) {
3182
3425
  "\u9519\u8BEF: ",
3183
3426
  err
3184
3427
  ] }, "err"));
3185
- if (cards.length) bodyNodes.push(
3186
- // key 带 fetchedAt:每次「手动查询 / 首次加载」拿到新数据时重建一次,KPI 卡重播回弹动画
3187
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-cards" + (fetchedAt ? " dtok-pop" : ""), children: cards.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-card", style: { "--i": String(i) }, children: [
3188
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "v", children: c.v }),
3189
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "l", children: c.l })
3190
- ] }, c.l)) }, "cards-" + fetchedAt)
3191
- );
3192
- if (summaryRows.length) bodyNodes.push(
3193
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-section-title", children: [
3194
- "\u7EDF\u8BA1\u5206\u7EC4: ",
3195
- dim || "\u65E0",
3196
- " (",
3197
- summaryRows.length,
3198
- " \u7EC4)"
3199
- ] }, "sum"),
3200
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-table-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: "dtok-table", children: [
3201
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
3202
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u7EF4\u5EA6" }),
3203
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8C03\u7528" }),
3204
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u5165(\u547D\u4E2D)" }),
3205
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u5165(\u672A\u547D\u4E2D)" }),
3206
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u547D\u4E2D\u7387" }),
3207
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u51FA" }),
3208
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u603BToken" }),
3209
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u91D1\u989D\uFF08\u4EBA\u6C11\u5E01\uFF09" }),
3210
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8017\u65F6" })
3211
- ] }) }),
3212
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: summaryRows })
3213
- ] }) }, "sumtab")
3214
- );
3215
- bodyNodes.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-section-title", children: "\u6309\u7EF4\u5EA6\u7EDF\u8BA1" }, "dimtitle"));
3216
3428
  bodyNodes.push(
3217
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-filter", style: { padding: "4px 0 0", border: "none", background: "transparent" }, children: ["", "provider", "model", "status", "effort"].map((d) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn" + (dim === d ? " primary" : ""), onClick: () => {
3218
- setDim(d);
3219
- runQuery(d);
3220
- }, children: d === "" ? "\u65E0\u5206\u7EC4" : d }, d)) }, "dimrow")
3429
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-filter", style: { padding: "2px 0 0", border: "none", background: "transparent", gap: 4 }, children: [
3430
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn" + (tab === "detail" ? " primary" : ""), onClick: () => setTab("detail"), children: "\u660E\u7EC6" }),
3431
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn" + (tab === "charts" ? " primary" : ""), onClick: () => setTab("charts"), children: "\u56FE\u8868\u7EDF\u8BA1" })
3432
+ ] }, "tabs")
3221
3433
  );
3222
- bodyNodes.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-section-title", children: "\u8C03\u7528\u660E\u7EC6" }, "dettitle"));
3223
- if (sorted.length === 0) {
3224
- bodyNodes.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-empty", children: loading ? "\u52A0\u8F7D\u4E2D\u2026" : "\u65E0\u5339\u914D\u8BB0\u5F55" }, "empty"));
3225
- } else {
3226
- const pager = (key, cls) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-pager " + cls, children: [
3227
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn", disabled: pageSafe <= 0, onClick: () => setPage(pageSafe - 1), children: "\u4E0A\u4E00\u9875" }),
3228
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
3229
- "\u7B2C ",
3230
- pageSafe + 1,
3231
- " / ",
3232
- pageCount,
3233
- " \u9875 \xB7 \u5171 ",
3234
- sorted.length,
3235
- " \u6761"
3236
- ] }),
3237
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn", disabled: pageSafe >= pageCount - 1, onClick: () => setPage(pageSafe + 1), children: "\u4E0B\u4E00\u9875" })
3238
- ] }, key);
3239
- bodyNodes.push(pager("pager-top", "top"));
3434
+ if (tab === "charts") {
3240
3435
  bodyNodes.push(
3436
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartsPanel, { records, rate: rateCny }, "charts-" + fetchedAt)
3437
+ );
3438
+ } else {
3439
+ if (cards.length) bodyNodes.push(
3440
+ // key 带 fetchedAt:每次「手动查询 / 首次加载」拿到新数据时重建一次,KPI 卡重播回弹动画
3441
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-cards" + (fetchedAt ? " dtok-pop" : ""), children: cards.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-card", style: { "--i": String(i) }, children: [
3442
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "v", children: c.v }),
3443
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "l", children: c.l })
3444
+ ] }, c.l)) }, "cards-" + fetchedAt)
3445
+ );
3446
+ if (summaryRows.length) bodyNodes.push(
3447
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-section-title", children: [
3448
+ "\u7EDF\u8BA1\u5206\u7EC4: ",
3449
+ dim || "\u65E0",
3450
+ " (",
3451
+ summaryRows.length,
3452
+ " \u7EC4)"
3453
+ ] }, "sum"),
3241
3454
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-table-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: "dtok-table", children: [
3242
3455
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
3243
- sortTh("\u65F6\u95F4", "time"),
3244
- sortTh("\u4F1A\u8BDDID", "sessionId"),
3245
- sortTh("\u63D0\u4F9B\u5546", "provider"),
3246
- sortTh("\u6A21\u578B", "model"),
3247
- sortTh("\u8F93\u5165(\u547D\u4E2D)", "cacheReadTokens"),
3248
- sortTh("\u8F93\u5165(\u672A\u547D\u4E2D)", "inputTokens"),
3249
- sortTh("\u547D\u4E2D%", "cacheHitPercent"),
3250
- sortTh("\u8F93\u51FA", "outputTokens"),
3251
- sortTh("\u63A8\u7406", "reasoningTokens"),
3252
- sortTh("\u603B\u989D", "totalTokens"),
3253
- sortTh("\u91D1\u989D\uFF08\u4EBA\u6C11\u5E01\uFF09", "cost"),
3254
- sortTh("\u5F3A\u5EA6", "effort"),
3255
- sortTh("\u72B6\u6001", "status"),
3256
- sortTh("\u8017\u65F6", "llmMs")
3456
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u7EF4\u5EA6" }),
3457
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8C03\u7528" }),
3458
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u5165(\u547D\u4E2D)" }),
3459
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u5165(\u672A\u547D\u4E2D)" }),
3460
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u547D\u4E2D\u7387" }),
3461
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8F93\u51FA" }),
3462
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u603BToken" }),
3463
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u91D1\u989D\uFF08\u4EBA\u6C11\u5E01\uFF09" }),
3464
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "\u8017\u65F6" })
3257
3465
  ] }) }),
3258
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: detailRows })
3259
- ] }) }, "detail")
3466
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: summaryRows })
3467
+ ] }) }, "sumtab")
3468
+ );
3469
+ bodyNodes.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-section-title", children: "\u6309\u7EF4\u5EA6\u7EDF\u8BA1" }, "dimtitle"));
3470
+ bodyNodes.push(
3471
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-filter", style: { padding: "4px 0 0", border: "none", background: "transparent" }, children: ["", "provider", "model", "status", "effort"].map((d) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn" + (dim === d ? " primary" : ""), onClick: () => {
3472
+ setDim(d);
3473
+ runQuery(d);
3474
+ }, children: d === "" ? "\u65E0\u5206\u7EC4" : d }, d)) }, "dimrow")
3260
3475
  );
3261
- bodyNodes.push(pager("pager-bottom", "bottom"));
3476
+ bodyNodes.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-section-title", children: "\u8C03\u7528\u660E\u7EC6" }, "dettitle"));
3477
+ if (sorted.length === 0) {
3478
+ bodyNodes.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-empty", children: loading ? "\u52A0\u8F7D\u4E2D\u2026" : "\u65E0\u5339\u914D\u8BB0\u5F55" }, "empty"));
3479
+ } else {
3480
+ const pager = (key, cls) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-pager " + cls, children: [
3481
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn", disabled: pageSafe <= 0, onClick: () => setPage(pageSafe - 1), children: "\u4E0A\u4E00\u9875" }),
3482
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
3483
+ "\u7B2C ",
3484
+ pageSafe + 1,
3485
+ " / ",
3486
+ pageCount,
3487
+ " \u9875 \xB7 \u5171 ",
3488
+ sorted.length,
3489
+ " \u6761"
3490
+ ] }),
3491
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "dtok-btn", disabled: pageSafe >= pageCount - 1, onClick: () => setPage(pageSafe + 1), children: "\u4E0B\u4E00\u9875" })
3492
+ ] }, key);
3493
+ bodyNodes.push(pager("pager-top", "top"));
3494
+ bodyNodes.push(
3495
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dtok-table-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: "dtok-table", children: [
3496
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
3497
+ sortTh("\u65F6\u95F4", "time"),
3498
+ sortTh("\u4F1A\u8BDDID", "sessionId"),
3499
+ sortTh("\u63D0\u4F9B\u5546", "provider"),
3500
+ sortTh("\u6A21\u578B", "model"),
3501
+ sortTh("\u8F93\u5165(\u547D\u4E2D)", "cacheReadTokens"),
3502
+ sortTh("\u8F93\u5165(\u672A\u547D\u4E2D)", "inputTokens"),
3503
+ sortTh("\u547D\u4E2D%", "cacheHitPercent"),
3504
+ sortTh("\u8F93\u51FA", "outputTokens"),
3505
+ sortTh("\u63A8\u7406", "reasoningTokens"),
3506
+ sortTh("\u603B\u989D", "totalTokens"),
3507
+ sortTh("\u91D1\u989D\uFF08\u4EBA\u6C11\u5E01\uFF09", "cost"),
3508
+ sortTh("\u5F3A\u5EA6", "effort"),
3509
+ sortTh("\u72B6\u6001", "status"),
3510
+ sortTh("\u8017\u65F6", "llmMs")
3511
+ ] }) }),
3512
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: detailRows })
3513
+ ] }) }, "detail")
3514
+ );
3515
+ bodyNodes.push(pager("pager-bottom", "bottom"));
3516
+ }
3262
3517
  }
3263
3518
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-root", onClick: (e) => e.stopPropagation(), children: [
3264
3519
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dtok-status", children: [
@@ -10916,10 +11171,10 @@ function MobileRelayView() {
10916
11171
  function MobileRelayOverlay() {
10917
11172
  (0, import_react21.useEffect)(() => {
10918
11173
  if (typeof window === "undefined" || typeof document === "undefined") return void 0;
10919
- if (window.__dshDockMobileDrawer) return void 0;
11174
+ const hostPresent = !!(window.__dshDockMobileDrawer && document.querySelector(".dsh-mobile-drawer-btn"));
10920
11175
  const mq = window.matchMedia ? window.matchMedia("(max-width:700px)") : null;
10921
11176
  if (!mq) return void 0;
10922
- window.__dshDockMobileDrawer = "client";
11177
+ if (!hostPresent) window.__dshDockMobileDrawer = "client";
10923
11178
  const narrow = () => mq.matches;
10924
11179
  const frameEl = () => document.querySelector('[class$="frame"]');
10925
11180
  const sidebarCollapsed = () => {
@@ -10941,17 +11196,72 @@ function MobileRelayOverlay() {
10941
11196
  const c = d.querySelector('button[aria-label="\u5173\u95ED\u8BE6\u60C5"]');
10942
11197
  if (c) c.click();
10943
11198
  };
10944
- const fab = document.createElement("button");
10945
- fab.type = "button";
10946
- fab.className = "dsh-mobile-drawer-btn";
10947
- fab.setAttribute("aria-label", "\u6253\u5F00\u4F1A\u8BDD\u5217\u8868");
10948
- fab.innerHTML = '<svg viewBox="0 0 24 24"><path d="M4 7h16M4 12h16M4 17h10"/></svg>';
10949
- fab.addEventListener("click", expand);
10950
- const scrim = document.createElement("div");
10951
- scrim.className = "dsh-mobile-scrim";
10952
- document.body.appendChild(fab);
10953
- document.body.appendChild(scrim);
11199
+ let fab = null;
11200
+ let scrim = null;
11201
+ if (!hostPresent) {
11202
+ fab = document.createElement("button");
11203
+ fab.type = "button";
11204
+ fab.className = "dsh-mobile-drawer-btn";
11205
+ fab.setAttribute("aria-label", "\u6253\u5F00\u4F1A\u8BDD\u5217\u8868");
11206
+ fab.innerHTML = '<svg viewBox="0 0 24 24"><path d="M4 7h16M4 12h16M4 17h10"/></svg>';
11207
+ fab.addEventListener("click", expand);
11208
+ scrim = document.createElement("div");
11209
+ scrim.className = "dsh-mobile-scrim";
11210
+ document.body.appendChild(fab);
11211
+ document.body.appendChild(scrim);
11212
+ }
11213
+ const tabbar = document.createElement("nav");
11214
+ tabbar.className = "dsh-mobile-tabbar";
11215
+ tabbar.setAttribute("aria-label", "\u5E95\u90E8\u5BFC\u822A");
11216
+ const tabDefs = [
11217
+ {
11218
+ id: "sessions",
11219
+ label: "\u4F1A\u8BDD",
11220
+ 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>',
11221
+ act: () => expand()
11222
+ },
11223
+ {
11224
+ id: "dock",
11225
+ label: "\u529F\u80FD\u575E",
11226
+ 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>',
11227
+ act: () => {
11228
+ if (panelNav.open) setPanelOpen(false);
11229
+ else openPanel("home");
11230
+ }
11231
+ },
11232
+ {
11233
+ id: "settings",
11234
+ label: "\u8BBE\u7F6E",
11235
+ 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>',
11236
+ act: () => {
11237
+ const b = [...document.querySelectorAll("button")].find((x) => (x.getAttribute("aria-label") || "") === "\u8BBE\u7F6E" || (x.textContent || "").trim() === "\u8BBE\u7F6E");
11238
+ if (b) b.click();
11239
+ }
11240
+ }
11241
+ ];
11242
+ const tabButtons = tabDefs.map((def) => {
11243
+ const b = document.createElement("button");
11244
+ b.type = "button";
11245
+ b.className = "dsh-mobile-tab";
11246
+ b.dataset.tab = def.id;
11247
+ b.innerHTML = def.icon + "<span>" + def.label + "</span>";
11248
+ b.addEventListener("click", () => def.act());
11249
+ tabbar.appendChild(b);
11250
+ return b;
11251
+ });
11252
+ document.body.appendChild(tabbar);
11253
+ const syncTabs = () => {
11254
+ const expanded = !sidebarCollapsed();
11255
+ const settingsOpen = dialogOpen();
11256
+ for (const b of tabButtons) {
11257
+ const id = b.dataset.tab;
11258
+ const on = id === "sessions" && expanded || id === "dock" && panelNav.open || id === "settings" && settingsOpen;
11259
+ b.classList.toggle("on", on);
11260
+ }
11261
+ };
11262
+ const unsubPanel = subscribePanel(syncTabs);
10954
11263
  const place = () => {
11264
+ if (!fab) return;
10955
11265
  const vh = window.innerHeight;
10956
11266
  let top = null;
10957
11267
  const g = document.querySelector(".dgfab");
@@ -10969,10 +11279,14 @@ function MobileRelayOverlay() {
10969
11279
  const sync = () => {
10970
11280
  const on = narrow() && !!frameEl() && !dialogOpen();
10971
11281
  const collapsed = sidebarCollapsed();
10972
- const showFab = on && collapsed;
10973
- fab.style.display = showFab ? "" : "none";
10974
- if (showFab) place();
10975
- scrim.style.display = on && !collapsed ? "" : "none";
11282
+ if (fab) {
11283
+ const showFab = on && collapsed && !panelNav.open;
11284
+ fab.style.display = showFab ? "" : "none";
11285
+ if (showFab) place();
11286
+ }
11287
+ if (scrim) scrim.style.display = on && !collapsed ? "" : "none";
11288
+ tabbar.style.display = narrow() && !!frameEl() && !panelNav.open ? "" : "none";
11289
+ syncTabs();
10976
11290
  };
10977
11291
  sync();
10978
11292
  const observer = typeof MutationObserver !== "undefined" ? new MutationObserver(sync) : null;
@@ -10986,7 +11300,7 @@ function MobileRelayOverlay() {
10986
11300
  if (!narrow()) return;
10987
11301
  const t = e.target;
10988
11302
  if (!t || !t.closest) return;
10989
- if (t.closest(".dsh-mobile-drawer-btn")) return;
11303
+ if (t.closest(".dsh-mobile-drawer-btn") || t.closest(".dsh-mobile-tabbar")) return;
10990
11304
  const expanded = !!document.querySelector('button[aria-label="\u6536\u8D77\u4FA7\u8FB9\u680F"],button[aria-label="Collapse sidebar"]');
10991
11305
  const col = document.querySelector('[class*="sidebarCol"]');
10992
11306
  const inSidebar = col && col.contains(t);
@@ -11004,16 +11318,18 @@ function MobileRelayOverlay() {
11004
11318
  if (t.closest('[role="dialog"],[class*="dockm"],[class*="dgfab"],[class*="dgwin"],[class*="dgame"],[class*="detailsCol"]')) return;
11005
11319
  collapse();
11006
11320
  };
11007
- document.addEventListener("click", onClick, true);
11321
+ if (!hostPresent) document.addEventListener("click", onClick, true);
11008
11322
  return () => {
11009
- document.removeEventListener("click", onClick, true);
11323
+ if (!hostPresent) document.removeEventListener("click", onClick, true);
11010
11324
  if (observer) observer.disconnect();
11011
11325
  clearInterval(timer);
11326
+ unsubPanel();
11012
11327
  if (mq.removeEventListener) mq.removeEventListener("change", onMq);
11013
11328
  else if (mq.removeListener) mq.removeListener(onMq);
11014
11329
  window.removeEventListener("resize", place);
11015
- fab.remove();
11016
- scrim.remove();
11330
+ if (fab) fab.remove();
11331
+ if (scrim) scrim.remove();
11332
+ tabbar.remove();
11017
11333
  if (window.__dshDockMobileDrawer === "client") delete window.__dshDockMobileDrawer;
11018
11334
  };
11019
11335
  }, []);
@@ -11040,7 +11356,25 @@ var feature10 = {
11040
11356
  .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)}
11041
11357
  /* \u62BD\u5C49\u628A\u624B\uFF1A\u8D34\u5DE6\u8FB9\u7F18\uFF08\u540C\u8DA3\u5473\u6E38\u620F\u6D6E\u6807\u7684\u8FB9\u7F18\u5438\u9644\u8BED\u8A00\uFF09\uFF0Ctop \u7531 place() \u52A8\u6001\u8BBE\u5B9A\uFF1B
11042
11358
  * z \u5E8F 70 < \u906E\u7F69 75 < \u4FA7\u680F 80 < overlayLayer 90\uFF0C\u9762\u677F/\u5F39\u7A97\u6253\u5F00\u65F6\u81EA\u7136\u88AB\u76D6\u3002 */
11043
- @media (max-width:700px){.dsh-mobile-drawer-btn{position:fixed;left:0;top:38vh;z-index:70;box-sizing:border-box;width:30px;height:48px;padding:0;display:flex;align-items:center;justify-content:center;border:1px solid var(--dsw-alias-border-l1,rgba(127,139,161,.35));border-left:none;border-radius:0 12px 12px 0;background:color-mix(in srgb,var(--dsw-alias-bg-layer-2,#1c2230) 88%,transparent);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);color:var(--dsw-alias-label-primary,#e6eaf2);box-shadow:4px 0 18px rgba(0,0,0,.28);cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent}.dsh-mobile-drawer-btn:active{transform:scale(.94)}.dsh-mobile-drawer-btn svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}.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}}`,
11359
+ @media (max-width:700px){.dsh-mobile-drawer-btn{position:fixed;left:0;top:38vh;z-index:70;box-sizing:border-box;width:30px;height:48px;padding:0;display:flex;align-items:center;justify-content:center;border:1px solid var(--dsw-alias-border-l1,rgba(127,139,161,.35));border-left:none;border-radius:0 12px 12px 0;background:color-mix(in srgb,var(--dsw-alias-bg-layer-2,#1c2230) 88%,transparent);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);color:var(--dsw-alias-label-primary,#e6eaf2);box-shadow:4px 0 18px rgba(0,0,0,.28);cursor:pointer;touch-action:manipulation;-webkit-tap-highlight-color:transparent}.dsh-mobile-drawer-btn:active{transform:scale(.94)}.dsh-mobile-drawer-btn svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}.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}
11360
+ /* ---- \u4EFF ZCode \u624B\u673A\u7AEF\uFF1A\u5E95\u90E8 Tab \u680F + \u8F93\u5165\u533A\u5438\u9644\u5E95\u90E8 ---- */
11361
+ .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}
11362
+ .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}
11363
+ .dsh-mobile-tab svg{width:21px;height:21px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
11364
+ .dsh-mobile-tab.on{color:var(--dsw-alias-label-primary,#e6eaf2)}
11365
+ .dsh-mobile-tab.on svg{color:var(--dk-accent,#4d9fff)}
11366
+ .dsh-mobile-tab:active{transform:scale(.94)}
11367
+ /* \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
11368
+ * \u624B\u673A\u89C6\u53E3\u4E0B\u8F93\u5165\u5361\u7247\u60AC\u5728\u4E2D\u90E8\u3001\u4E0B\u65B9\u7559\u51FA\u5927\u7247\u7A7A\u767D\u3002\u6539\u4E3A flex-end \u8D34\u5E95\uFF0C\u5E76\u7ED9
11369
+ * \u6EDA\u52A8\u4F53\u7559\u51FA Tab \u680F\u9AD8\u5EA6\u7684\u5185\u8FB9\u8DDD\uFF0C\u907F\u514D\u5185\u5BB9\u88AB Tab \u680F\u906E\u6321\u3002 */
11370
+ [class*="scrollBody"]{justify-content:flex-end !important;padding-bottom:calc(72px + env(safe-area-inset-bottom,0px)) !important}
11371
+ [class*="scrollBody"]>[class*="composerSeat"]{padding-bottom:6px}
11372
+ /* \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
11373
+ * \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
11374
+ * \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 */
11375
+ [class*="sidebarCol"]:has([class*="collapsed"]){visibility:hidden}
11376
+ /* \u62BD\u5C49\u628A\u624B\u4ECE\u5C4F\u5E55\u4E2D\u90E8\u632A\u5230 Tab \u680F\u4E0A\u65B9\u4E00\u70B9\uFF0C\u514D\u5F97\u5B64\u96F6\u96F6\u60AC\u5728\u534A\u7A7A */
11377
+ .dsh-mobile-drawer-btn{top:auto !important;bottom:calc(96px + env(safe-area-inset-bottom,0px))}}`,
11044
11378
  View: MobileRelayView,
11045
11379
  HomeStat: MobileRelayHomeStat,
11046
11380
  Overlay: MobileRelayOverlay