dsh-codearts 1.2.4 → 1.2.6

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.
@@ -22,90 +22,138 @@ function stripModelSuffixes(name) {
22
22
  return base.replace(/\s*[((]\s*(?:(?:免费|福利)(?:\s+x\d+(?:\.\d+)?)?|x\d+(?:\.\d+)?)\s*[))]\s*$/, "").trim();
23
23
  }
24
24
  //#endregion
25
+ //#region src/client/panel-switch.ts
26
+ /**
27
+ * 「要不要在 DSH 侧边栏占一格」这个偏好,浏览器侧的单一真相。
28
+ *
29
+ * 为什么不靠组件返回 null 来隐藏:宿主是按**槽位里的注册条目**(entry metadata)
30
+ * 画侧边栏导航行的 —— 只要注册还在,那一行就在;渲染函数 return null 只会留下一个
31
+ * 「空白但可点」的按钮。所以开关必须落到「注册 / 不注册」上,这里只存那个意图,
32
+ * 真正的注册动作由 index.tsx 的 sync 控制器按它做。
33
+ *
34
+ * 默认 **false**:读不到配置时必须当作关闭。侧边栏那一格是所有插件共用的全局座位,
35
+ * 猜错的代价比「少一个入口」大得多(用户还能从「设置 → 插件」进同一张卡片)。
36
+ */
37
+ let wanted = false;
38
+ /** 当前是否要显示侧边栏入口。 */
39
+ function sidebarPanelWanted() {
40
+ return wanted;
41
+ }
42
+ const listeners = /* @__PURE__ */ new Set();
43
+ /**
44
+ * 改开关。值没变就直接返回 —— 调用方会在每次刷新状态时调用它,
45
+ * 无变化的重注册会让侧边栏闪一下。
46
+ */
47
+ function setSidebarPanelWanted(next) {
48
+ if (wanted === next) return;
49
+ wanted = next;
50
+ for (const listener of [...listeners]) try {
51
+ listener();
52
+ } catch (error) {
53
+ console.error("[dsh-codearts] sidebar panel listener failed:", error);
54
+ }
55
+ }
56
+ /** 订阅开关变化,返回取消订阅函数。 */
57
+ function subscribeSidebarPanelWanted(listener) {
58
+ listeners.add(listener);
59
+ return () => {
60
+ listeners.delete(listener);
61
+ };
62
+ }
63
+ //#endregion
25
64
  //#region \0dsh-css:/home/runner/work/dsh-codearts/dsh-codearts/src/client/CodeArtsPluginCard.module.css.mjs
26
- const css = ".xoziZG_card{border:.5px solid var(--dsw-alias-settings-card-stroke);border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-settings-card-fill);max-width:720px}.xoziZG_header{align-items:flex-start;gap:12px;padding:14px 16px;display:flex}.xoziZG_headText{flex-direction:column;flex:1;gap:6px;min-width:0;display:flex}.xoziZG_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:22px}.xoziZG_desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}.xoziZG_statusLine{color:var(--dsw-alias-label-primary);align-items:center;gap:6px;font-size:14px;font-weight:500;line-height:22px;display:inline-flex}.xoziZG_meta{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:18px}.xoziZG_body{border-top:.5px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 16px}.xoziZG_tabs{margin-top:12px}.xoziZG_tabPanel{padding-top:2px}.xoziZG_accountToolbar{gap:8px;margin-top:12px;display:flex}.xoziZG_accountToolbar>*{flex:1}.xoziZG_manualForm{border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);margin-top:12px;padding:12px}.xoziZG_accountCard{flex-direction:column;gap:10px;margin-top:12px;display:flex}.xoziZG_accountEntry{border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-2)}.xoziZG_accountRow{align-items:center;gap:10px;padding:12px;display:flex}.xoziZG_claimAllRow{border-top:.5px solid var(--dsw-alias-border-l2);justify-content:flex-end;padding:10px 12px 0;display:flex}.xoziZG_avatar{background:var(--dsw-alias-bg-layer-3);width:32px;height:32px;color:var(--dsw-alias-label-primary);border-radius:50%;flex:none;justify-content:center;align-items:center;font-size:14px;font-weight:600;display:inline-flex}.xoziZG_accountInfo{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.xoziZG_accountNameRow{align-items:center;gap:6px;min-width:0;display:flex}.xoziZG_accountName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:600;line-height:20px;overflow:hidden}.xoziZG_accountRowActions{flex:none;gap:6px;display:flex}.xoziZG_accountHint{color:var(--dsw-alias-label-tertiary);margin:0;padding:0 12px 12px;font-size:12px;line-height:18px}.xoziZG_creditBlock{border-top:.5px solid var(--dsw-alias-border-l2);padding:12px}.xoziZG_balanceRow{flex-wrap:wrap;align-items:baseline;gap:6px;display:flex}.xoziZG_balance{color:var(--dsw-alias-label-primary);letter-spacing:-.02em;font-variant-numeric:tabular-nums;font-size:28px;font-weight:600;line-height:34px}.xoziZG_balanceUnit{color:var(--dsw-alias-label-secondary);font-size:12px}.xoziZG_balanceMeta{color:var(--dsw-alias-label-tertiary);margin-left:2px;font-size:12px}.xoziZG_updated{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;margin-left:auto;font-size:12px}.xoziZG_creditRows{flex-direction:column;gap:4px;margin-top:8px;display:flex}.xoziZG_creditRow{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;justify-content:space-between;align-items:baseline;gap:12px;font-size:12px;line-height:18px;display:flex}.xoziZG_usageBar{background:var(--dsw-alias-border-l2);border-radius:2px;height:4px;margin-top:4px;display:block;overflow:hidden}.xoziZG_usageFill{background:var(--dsw-alias-brand-primary);border-radius:2px;height:100%;transition:width .2s;display:block}.xoziZG_expiryRow{margin-top:2px}.xoziZG_expiryLabel{color:var(--dsw-alias-state-warn-label)}.xoziZG_bar{background:var(--dsw-alias-bg-layer-3);border-radius:2px;height:4px;margin-top:6px;display:block;overflow:hidden}.xoziZG_barFill{background:var(--dsw-alias-state-warn-primary);border-radius:2px;height:100%;display:block}.xoziZG_creditLink{margin-top:8px;display:inline-block}.xoziZG_creditFooter{border-top:.5px solid var(--dsw-alias-border-l2);justify-content:space-between;align-items:center;gap:8px;margin-top:12px;padding-top:10px;display:flex}.xoziZG_creditHint{color:var(--dsw-alias-label-tertiary);margin:8px 0 0;font-size:12px;line-height:18px}.xoziZG_section{margin-top:16px}.xoziZG_sectionHead{justify-content:space-between;align-items:baseline;gap:12px;display:flex}.xoziZG_sectionLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;font-weight:500}.xoziZG_count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:12px}.xoziZG_hint{color:var(--dsw-alias-label-secondary);margin:8px 0 0;font-size:13px;line-height:20px}.xoziZG_error{color:var(--dsw-alias-state-error-primary);margin:10px 0 0;font-size:13px;line-height:20px}.xoziZG_actions{flex-wrap:wrap;align-items:center;gap:8px;margin-top:12px;display:flex}.xoziZG_danger{color:var(--dsw-alias-state-error-primary)}.xoziZG_link{appearance:none;color:var(--dsw-alias-brand-text);font:inherit;text-align:left;word-break:break-all;cursor:pointer;background:0 0;border:none;padding:0;font-size:12px}.xoziZG_link:hover{text-decoration:underline}.xoziZG_choices{flex-direction:column;gap:2px;margin-top:8px;display:flex}.xoziZG_choiceRow{border-radius:var(--dsw-radius-sm);align-items:center;gap:8px;margin:0 -6px;padding:5px 6px;display:flex}.xoziZG_choiceRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.xoziZG_statGrid{grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin:10px 0 14px;display:grid}.xoziZG_statCard{border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);flex-direction:column;gap:1px;padding:8px 10px;display:flex}.xoziZG_statLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}.xoziZG_statValue{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;font-size:15px;line-height:20px}.xoziZG_statSub{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:11px;line-height:16px}.xoziZG_modelChart{flex-direction:column;gap:2px;margin-top:8px;display:flex}.xoziZG_modelRow{font-variant-numeric:tabular-nums;border-radius:6px;grid-template-columns:minmax(0,1.2fr) minmax(48px,1fr) 56px 52px 64px;align-items:center;gap:8px;padding:4px 0;display:grid;position:relative}.xoziZG_modelRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.xoziZG_usageTip{z-index:5;border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-3);min-width:180px;max-width:260px;color:var(--dsw-alias-label-secondary);text-align:left;font-variant-numeric:tabular-nums;pointer-events:none;padding:8px 10px;font-size:12px;line-height:1.9;position:absolute;bottom:calc(100% + 6px);right:0;box-shadow:0 10px 28px #00000073}.xoziZG_usageTipName{max-width:240px;color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.xoziZG_usageTipSep{background:var(--dsw-alias-border-l2);height:1px;margin:4px 0}.xoziZG_usageTipRow{justify-content:space-between;gap:24px;display:flex}.xoziZG_usageTipRow span:last-child{color:var(--dsw-alias-label-primary);font-weight:600}.xoziZG_modelName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:18px;overflow:hidden}.xoziZG_modelTrack{background:var(--dsw-alias-border-l2);border-radius:4px;height:8px;display:block;overflow:hidden}.xoziZG_modelFill{background:var(--dsw-alias-brand-primary);border-radius:4px;height:100%;transition:width .16s;display:block}.xoziZG_modelTokens,.xoziZG_modelShare,.xoziZG_modelRequests{color:var(--dsw-alias-label-secondary);text-align:right;white-space:nowrap;font-size:11px;line-height:16px}.xoziZG_modelRequests{color:var(--dsw-alias-label-tertiary)}.xoziZG_check{flex:1;min-width:0}.xoziZG_check>span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.xoziZG_manual{border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);margin-top:12px;padding:0 12px}.xoziZG_manual[open]{padding-bottom:12px}.xoziZG_manualSummary{color:var(--dsw-alias-label-secondary);cursor:pointer;align-items:center;gap:6px;padding:9px 0;font-size:13px;list-style:none;display:flex}.xoziZG_manualSummary::-webkit-details-marker{display:none}.xoziZG_field{flex-direction:column;gap:5px;margin-top:10px;display:flex}.xoziZG_fieldLabel{color:var(--dsw-alias-label-tertiary);font-size:12px}.xoziZG_area{box-sizing:border-box;resize:vertical;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);width:100%;color:var(--dsw-alias-label-primary);padding:6px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:20px}.xoziZG_area::placeholder{color:var(--dsw-alias-label-dimmed)}.xoziZG_area:focus{border-color:var(--dsw-alias-state-business-primary);outline:none}";
27
- const tagId = "dsh-codearts/CodeArtsPluginCard.module.css";
28
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
65
+ const css$1 = ".xoziZG_card{border:.5px solid var(--dsw-alias-settings-card-stroke);border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-settings-card-fill);max-width:720px}.xoziZG_header{align-items:flex-start;gap:12px;padding:14px 16px;display:flex}.xoziZG_headText{flex-direction:column;flex:1;gap:6px;min-width:0;display:flex}.xoziZG_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:22px}.xoziZG_desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}.xoziZG_statusLine{color:var(--dsw-alias-label-primary);align-items:center;gap:6px;font-size:14px;font-weight:500;line-height:22px;display:inline-flex}.xoziZG_meta{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:12px;line-height:18px}.xoziZG_body{border-top:.5px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 16px}.xoziZG_tabs{margin-top:12px}.xoziZG_tabPanel{padding-top:2px}.xoziZG_accountToolbar{gap:8px;margin-top:12px;display:flex}.xoziZG_accountToolbar>*{flex:1}.xoziZG_manualForm{border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);margin-top:12px;padding:12px}.xoziZG_accountCard{flex-direction:column;gap:10px;margin-top:12px;display:flex}.xoziZG_accountEntry{border:.5px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-2)}.xoziZG_accountRow{align-items:center;gap:10px;padding:12px;display:flex}.xoziZG_claimAllRow{border-top:.5px solid var(--dsw-alias-border-l2);justify-content:flex-end;padding:10px 12px 0;display:flex}.xoziZG_avatar{background:var(--dsw-alias-bg-layer-3);width:32px;height:32px;color:var(--dsw-alias-label-primary);border-radius:50%;flex:none;justify-content:center;align-items:center;font-size:14px;font-weight:600;display:inline-flex}.xoziZG_accountInfo{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.xoziZG_accountNameRow{align-items:center;gap:6px;min-width:0;display:flex}.xoziZG_accountName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:600;line-height:20px;overflow:hidden}.xoziZG_accountRowActions{flex:none;gap:6px;display:flex}.xoziZG_accountHint{color:var(--dsw-alias-label-tertiary);margin:0;padding:0 12px 12px;font-size:12px;line-height:18px}.xoziZG_creditBlock{border-top:.5px solid var(--dsw-alias-border-l2);padding:12px}.xoziZG_balanceRow{flex-wrap:wrap;align-items:baseline;gap:6px;display:flex}.xoziZG_balance{color:var(--dsw-alias-label-primary);letter-spacing:-.02em;font-variant-numeric:tabular-nums;font-size:28px;font-weight:600;line-height:34px}.xoziZG_balanceUnit{color:var(--dsw-alias-label-secondary);font-size:12px}.xoziZG_balanceMeta{color:var(--dsw-alias-label-tertiary);margin-left:2px;font-size:12px}.xoziZG_updated{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;margin-left:auto;font-size:12px}.xoziZG_creditRows{flex-direction:column;gap:4px;margin-top:8px;display:flex}.xoziZG_creditRow{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;justify-content:space-between;align-items:baseline;gap:12px;font-size:12px;line-height:18px;display:flex}.xoziZG_usageBar{background:var(--dsw-alias-border-l2);border-radius:2px;height:4px;margin-top:4px;display:block;overflow:hidden}.xoziZG_usageFill{background:var(--dsw-alias-brand-primary);border-radius:2px;height:100%;transition:width .2s;display:block}.xoziZG_expiryRow{margin-top:2px}.xoziZG_expiryLabel{color:var(--dsw-alias-state-warn-label)}.xoziZG_bar{background:var(--dsw-alias-bg-layer-3);border-radius:2px;height:4px;margin-top:6px;display:block;overflow:hidden}.xoziZG_barFill{background:var(--dsw-alias-state-warn-primary);border-radius:2px;height:100%;display:block}.xoziZG_creditLink{margin-top:8px;display:inline-block}.xoziZG_creditFooter{border-top:.5px solid var(--dsw-alias-border-l2);justify-content:space-between;align-items:center;gap:8px;margin-top:12px;padding-top:10px;display:flex}.xoziZG_creditHint{color:var(--dsw-alias-label-tertiary);margin:8px 0 0;font-size:12px;line-height:18px}.xoziZG_section{margin-top:16px}.xoziZG_sectionHead{justify-content:space-between;align-items:baseline;gap:12px;display:flex}.xoziZG_sectionLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;font-weight:500}.xoziZG_count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:12px}.xoziZG_hint{color:var(--dsw-alias-label-secondary);margin:8px 0 0;font-size:13px;line-height:20px}.xoziZG_error{color:var(--dsw-alias-state-error-primary);margin:10px 0 0;font-size:13px;line-height:20px}.xoziZG_actions{flex-wrap:wrap;align-items:center;gap:8px;margin-top:12px;display:flex}.xoziZG_danger{color:var(--dsw-alias-state-error-primary)}.xoziZG_link{appearance:none;color:var(--dsw-alias-brand-text);font:inherit;text-align:left;word-break:break-all;cursor:pointer;background:0 0;border:none;padding:0;font-size:12px}.xoziZG_link:hover{text-decoration:underline}.xoziZG_choices{flex-direction:column;gap:2px;margin-top:8px;display:flex}.xoziZG_choiceRow{border-radius:var(--dsw-radius-sm);align-items:center;gap:8px;margin:0 -6px;padding:5px 6px;display:flex}.xoziZG_choiceRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.xoziZG_statGrid{grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin:10px 0 14px;display:grid}.xoziZG_statCard{border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);flex-direction:column;gap:1px;padding:8px 10px;display:flex}.xoziZG_statLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}.xoziZG_statValue{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;font-size:15px;line-height:20px}.xoziZG_statSub{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:11px;line-height:16px}.xoziZG_modelChart{flex-direction:column;gap:2px;margin-top:8px;display:flex}.xoziZG_modelRow{font-variant-numeric:tabular-nums;border-radius:6px;grid-template-columns:minmax(0,1.2fr) minmax(48px,1fr) 56px 52px 64px;align-items:center;gap:8px;padding:4px 0;display:grid;position:relative}.xoziZG_modelRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.xoziZG_usageTip{z-index:5;border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-3);min-width:180px;max-width:260px;color:var(--dsw-alias-label-secondary);text-align:left;font-variant-numeric:tabular-nums;pointer-events:none;padding:8px 10px;font-size:12px;line-height:1.9;position:absolute;bottom:calc(100% + 6px);right:0;box-shadow:0 10px 28px #00000073}.xoziZG_usageTipName{max-width:240px;color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.xoziZG_usageTipSep{background:var(--dsw-alias-border-l2);height:1px;margin:4px 0}.xoziZG_usageTipRow{justify-content:space-between;gap:24px;display:flex}.xoziZG_usageTipRow span:last-child{color:var(--dsw-alias-label-primary);font-weight:600}.xoziZG_modelName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:18px;overflow:hidden}.xoziZG_modelTrack{background:var(--dsw-alias-border-l2);border-radius:4px;height:8px;display:block;overflow:hidden}.xoziZG_modelFill{background:var(--dsw-alias-brand-primary);border-radius:4px;height:100%;transition:width .16s;display:block}.xoziZG_modelTokens,.xoziZG_modelShare,.xoziZG_modelRequests{color:var(--dsw-alias-label-secondary);text-align:right;white-space:nowrap;font-size:11px;line-height:16px}.xoziZG_modelRequests{color:var(--dsw-alias-label-tertiary)}.xoziZG_trendBars{align-items:flex-end;gap:3px;height:44px;margin:8px 0 2px;display:flex}.xoziZG_trendSlot{flex-direction:column;flex:1;justify-content:flex-end;min-width:0;height:100%;display:flex}.xoziZG_trendBar{background:var(--dsw-alias-brand-primary);border-radius:2px 2px 0 0;min-height:2px;transition:height .16s,background .16s}.xoziZG_trendBar[data-tone=mid]{background:var(--dsw-alias-state-warn-label)}.xoziZG_trendBar[data-tone=low]{background:var(--dsw-alias-state-error-primary,var(--dsw-alias-state-warn-label))}.xoziZG_trendBar[data-tone=empty]{background:var(--dsw-alias-border-l2)}.xoziZG_trendAxis{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;gap:3px;font-size:10px;line-height:14px;display:flex}.xoziZG_trendAxisSlot{text-align:center;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.xoziZG_trendTip{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;margin:2px 0 0;font-size:11px;line-height:16px}.xoziZG_check{flex:1;min-width:0}.xoziZG_check>span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.xoziZG_manual{border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-md);margin-top:12px;padding:0 12px}.xoziZG_manual[open]{padding-bottom:12px}.xoziZG_manualSummary{color:var(--dsw-alias-label-secondary);cursor:pointer;align-items:center;gap:6px;padding:9px 0;font-size:13px;list-style:none;display:flex}.xoziZG_manualSummary::-webkit-details-marker{display:none}.xoziZG_field{flex-direction:column;gap:5px;margin-top:10px;display:flex}.xoziZG_fieldLabel{color:var(--dsw-alias-label-tertiary);font-size:12px}.xoziZG_area{box-sizing:border-box;resize:vertical;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);width:100%;color:var(--dsw-alias-label-primary);padding:6px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:20px}.xoziZG_area::placeholder{color:var(--dsw-alias-label-dimmed)}.xoziZG_area:focus{border-color:var(--dsw-alias-state-business-primary);outline:none}.xoziZG_skeleton{flex-direction:column;gap:10px;padding:4px 0;display:flex}.xoziZG_skeletonRow{border-radius:var(--dsw-radius-sm,4px);background:var(--dsw-alias-border-l2);height:12px;animation:1.4s ease-in-out infinite xoziZG_skeletonBreath}.xoziZG_skeletonRow:nth-child(2){width:72%}.xoziZG_skeletonRow:nth-child(3){width:46%}@keyframes xoziZG_skeletonBreath{0%,to{opacity:.45}50%{opacity:.9}}@media (prefers-reduced-motion:reduce){.xoziZG_skeletonRow{animation:none}}";
66
+ const tagId$1 = "dsh-codearts/CodeArtsPluginCard.module.css";
67
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
29
68
  const tag = document.createElement("style");
30
69
  tag.dataset.plugin = "dsh-codearts";
31
- tag.dataset.pluginCss = tagId;
32
- tag.textContent = css;
70
+ tag.dataset.pluginCss = tagId$1;
71
+ tag.textContent = css$1;
33
72
  document.head.appendChild(tag);
34
73
  }
35
74
  var CodeArtsPluginCard_module_css_default = {
36
- "field": "xoziZG_field",
37
- "manualForm": "xoziZG_manualForm",
38
- "accountRow": "xoziZG_accountRow",
39
- "section": "xoziZG_section",
75
+ "header": "xoziZG_header",
76
+ "modelChart": "xoziZG_modelChart",
77
+ "trendBar": "xoziZG_trendBar",
78
+ "creditHint": "xoziZG_creditHint",
79
+ "creditLink": "xoziZG_creditLink",
80
+ "tabs": "xoziZG_tabs",
81
+ "accountCard": "xoziZG_accountCard",
82
+ "usageTipSep": "xoziZG_usageTipSep",
40
83
  "expiryLabel": "xoziZG_expiryLabel",
41
- "check": "xoziZG_check",
42
- "balance": "xoziZG_balance",
84
+ "creditRow": "xoziZG_creditRow",
85
+ "skeletonRow": "xoziZG_skeletonRow",
86
+ "updated": "xoziZG_updated",
87
+ "manual": "xoziZG_manual",
88
+ "desc": "xoziZG_desc",
89
+ "usageFill": "xoziZG_usageFill",
90
+ "trendTip": "xoziZG_trendTip",
91
+ "balanceRow": "xoziZG_balanceRow",
92
+ "modelShare": "xoziZG_modelShare",
93
+ "accountNameRow": "xoziZG_accountNameRow",
94
+ "usageTip": "xoziZG_usageTip",
43
95
  "accountToolbar": "xoziZG_accountToolbar",
44
- "barFill": "xoziZG_barFill",
45
- "statLabel": "xoziZG_statLabel",
96
+ "balance": "xoziZG_balance",
97
+ "actions": "xoziZG_actions",
98
+ "choices": "xoziZG_choices",
46
99
  "meta": "xoziZG_meta",
47
- "accountNameRow": "xoziZG_accountNameRow",
100
+ "section": "xoziZG_section",
101
+ "modelRow": "xoziZG_modelRow",
48
102
  "expiryRow": "xoziZG_expiryRow",
49
- "balanceUnit": "xoziZG_balanceUnit",
50
- "accountInfo": "xoziZG_accountInfo",
103
+ "claimAllRow": "xoziZG_claimAllRow",
104
+ "sectionHead": "xoziZG_sectionHead",
105
+ "modelTokens": "xoziZG_modelTokens",
106
+ "trendBars": "xoziZG_trendBars",
107
+ "skeleton": "xoziZG_skeleton",
108
+ "skeletonBreath": "xoziZG_skeletonBreath",
109
+ "statGrid": "xoziZG_statGrid",
51
110
  "hint": "xoziZG_hint",
52
- "count": "xoziZG_count",
53
- "choices": "xoziZG_choices",
54
- "usageFill": "xoziZG_usageFill",
111
+ "accountRowActions": "xoziZG_accountRowActions",
112
+ "choiceRow": "xoziZG_choiceRow",
113
+ "error": "xoziZG_error",
114
+ "headText": "xoziZG_headText",
55
115
  "card": "xoziZG_card",
56
- "usageTipName": "xoziZG_usageTipName",
57
- "balanceRow": "xoziZG_balanceRow",
58
- "creditBlock": "xoziZG_creditBlock",
59
- "header": "xoziZG_header",
116
+ "accountEntry": "xoziZG_accountEntry",
117
+ "statValue": "xoziZG_statValue",
60
118
  "statSub": "xoziZG_statSub",
61
- "usageTip": "xoziZG_usageTip",
119
+ "body": "xoziZG_body",
120
+ "accountHint": "xoziZG_accountHint",
121
+ "usageTipName": "xoziZG_usageTipName",
122
+ "trendSlot": "xoziZG_trendSlot",
62
123
  "modelRequests": "xoziZG_modelRequests",
63
- "usageBar": "xoziZG_usageBar",
64
- "bar": "xoziZG_bar",
65
- "creditRow": "xoziZG_creditRow",
66
- "sectionLabel": "xoziZG_sectionLabel",
67
- "accountEntry": "xoziZG_accountEntry",
68
- "modelTrack": "xoziZG_modelTrack",
69
- "link": "xoziZG_link",
70
- "updated": "xoziZG_updated",
71
- "desc": "xoziZG_desc",
72
124
  "avatar": "xoziZG_avatar",
73
- "creditHint": "xoziZG_creditHint",
74
- "tabPanel": "xoziZG_tabPanel",
75
- "fieldLabel": "xoziZG_fieldLabel",
76
- "modelName": "xoziZG_modelName",
125
+ "barFill": "xoziZG_barFill",
126
+ "sectionLabel": "xoziZG_sectionLabel",
77
127
  "danger": "xoziZG_danger",
78
- "statValue": "xoziZG_statValue",
79
- "area": "xoziZG_area",
80
- "claimAllRow": "xoziZG_claimAllRow",
81
- "tabs": "xoziZG_tabs",
82
- "usageTipSep": "xoziZG_usageTipSep",
83
- "name": "xoziZG_name",
84
- "headText": "xoziZG_headText",
85
- "accountRowActions": "xoziZG_accountRowActions",
86
- "sectionHead": "xoziZG_sectionHead",
87
- "modelRow": "xoziZG_modelRow",
88
- "modelFill": "xoziZG_modelFill",
89
- "accountHint": "xoziZG_accountHint",
90
- "modelChart": "xoziZG_modelChart",
91
- "error": "xoziZG_error",
92
- "body": "xoziZG_body",
93
- "manualSummary": "xoziZG_manualSummary",
94
- "balanceMeta": "xoziZG_balanceMeta",
95
- "statGrid": "xoziZG_statGrid",
96
- "choiceRow": "xoziZG_choiceRow",
97
- "modelShare": "xoziZG_modelShare",
98
- "accountName": "xoziZG_accountName",
99
- "modelTokens": "xoziZG_modelTokens",
100
- "creditLink": "xoziZG_creditLink",
101
- "creditFooter": "xoziZG_creditFooter",
102
- "manual": "xoziZG_manual",
103
128
  "statusLine": "xoziZG_statusLine",
104
129
  "creditRows": "xoziZG_creditRows",
130
+ "accountInfo": "xoziZG_accountInfo",
131
+ "trendAxisSlot": "xoziZG_trendAxisSlot",
132
+ "check": "xoziZG_check",
133
+ "field": "xoziZG_field",
134
+ "creditFooter": "xoziZG_creditFooter",
135
+ "balanceMeta": "xoziZG_balanceMeta",
136
+ "trendAxis": "xoziZG_trendAxis",
137
+ "manualSummary": "xoziZG_manualSummary",
138
+ "balanceUnit": "xoziZG_balanceUnit",
139
+ "manualForm": "xoziZG_manualForm",
140
+ "accountRow": "xoziZG_accountRow",
141
+ "creditBlock": "xoziZG_creditBlock",
142
+ "name": "xoziZG_name",
143
+ "modelName": "xoziZG_modelName",
144
+ "tabPanel": "xoziZG_tabPanel",
145
+ "bar": "xoziZG_bar",
146
+ "statLabel": "xoziZG_statLabel",
105
147
  "statCard": "xoziZG_statCard",
106
- "accountCard": "xoziZG_accountCard",
148
+ "usageBar": "xoziZG_usageBar",
149
+ "link": "xoziZG_link",
150
+ "fieldLabel": "xoziZG_fieldLabel",
151
+ "accountName": "xoziZG_accountName",
107
152
  "usageTipRow": "xoziZG_usageTipRow",
108
- "actions": "xoziZG_actions"
153
+ "count": "xoziZG_count",
154
+ "modelTrack": "xoziZG_modelTrack",
155
+ "modelFill": "xoziZG_modelFill",
156
+ "area": "xoziZG_area"
109
157
  };
110
158
  //#endregion
111
159
  //#region src/client/CodeArtsPluginCard.tsx
@@ -155,11 +203,21 @@ function claimMessagesOf(data, accountIds, t) {
155
203
  for (const id of accountIds) out[id] = claimText(data, t);
156
204
  return out;
157
205
  }
158
- /** 开关的兜底值:后端没这条路由(老版本)时按「都显示」渲染,与原有行为一致。 */
206
+ /**
207
+ * 开关的兜底值:后端没这条路由(老版本)时按「都显示」渲染,与原有行为一致。
208
+ * 侧边栏那一格相反 —— 缺省关闭。
209
+ */
159
210
  const DOCK_DEFAULT = {
160
211
  credits: true,
161
- queue: true
212
+ queue: true,
213
+ sidebar: false
162
214
  };
215
+ /**
216
+ * 上一次读到的状态。`/api/codearts/status` 会顺带去上游拉积分(有 TTL 缓存),
217
+ * 冷的时候要等近一秒;模块级留一份快照,再进这一页就能立刻按旧值画出来,
218
+ * 后台再刷新 —— 比每次都先闪骨架舒服,也不会把「还没读到」误判成未登录。
219
+ */
220
+ let lastStatusSnapshot = null;
163
221
  /** 一个 Tab 面板;未选中的用 hidden 留在 DOM 里,草稿和滚动位置都别丢。 */
164
222
  function TabPanel({ id, labelledBy, active, children }) {
165
223
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -478,6 +536,63 @@ function UsageBars({ rows, t }) {
478
536
  });
479
537
  }
480
538
  /**
539
+ * 趋势柱:近 7 天按天、今天按小时。
540
+ *
541
+ * 两张图共用一套柱子,但编码不同,因为要看的问题不同:
542
+ * - `hit`:柱高就是命中率,颜色分档 —— 命中率一掉就能看见,这是诊断「系统提示词
543
+ * 前缀变了 / 上下文突然变长」最直接的信号;
544
+ * - `volume`:柱高按组内最大值归一化 —— 只用来看「什么时候在跑」,绝对值看悬停。
545
+ *
546
+ * 悬停柱子时下面那行换成该格明细,移开回到提示语。轴标签只画整数倍格,24 格全画会挤成一片。
547
+ */
548
+ function UsageTrend({ points, mode, axisEvery, hint, t }) {
549
+ const [hovered, setHovered] = (0, react.useState)(null);
550
+ const max = points.reduce((peak, point) => Math.max(peak, point.totals.totalTokens), 0);
551
+ const shown = hovered === null ? void 0 : points[hovered];
552
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
553
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
554
+ className: CodeArtsPluginCard_module_css_default.trendBars,
555
+ children: points.map((point, index) => {
556
+ const pct = hitPercentOf(point.totals);
557
+ const share = mode === "hit" ? pct === void 0 ? 0 : Math.max(pct, 2) : max === 0 ? 0 : point.totals.totalTokens / max * 100;
558
+ const tone = mode === "hit" ? pct === void 0 ? "empty" : pct >= 80 ? "ok" : pct >= 50 ? "mid" : "low" : point.totals.totalTokens === 0 ? "empty" : "ok";
559
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
560
+ className: CodeArtsPluginCard_module_css_default.trendSlot,
561
+ title: `${point.label} · ${trendDetail(point.totals, t)}`,
562
+ onMouseEnter: () => setHovered(index),
563
+ onMouseLeave: () => setHovered(null),
564
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
565
+ className: CodeArtsPluginCard_module_css_default.trendBar,
566
+ "data-tone": tone,
567
+ style: { height: `${share}%` }
568
+ })
569
+ }, point.key);
570
+ })
571
+ }),
572
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
573
+ className: CodeArtsPluginCard_module_css_default.trendAxis,
574
+ children: points.map((point, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
575
+ className: CodeArtsPluginCard_module_css_default.trendAxisSlot,
576
+ children: index % axisEvery === 0 ? point.label : ""
577
+ }, point.key))
578
+ }),
579
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
580
+ className: CodeArtsPluginCard_module_css_default.trendTip,
581
+ children: shown === void 0 ? hint : `${shown.label} · ${trendDetail(shown.totals, t)}`
582
+ })
583
+ ] });
584
+ }
585
+ /** 一格趋势的明细文案:输入 / 缓存 / 命中率 / 请求数(输出与思考在明细卡里看)。 */
586
+ function trendDetail(totals, t) {
587
+ const pct = hitPercentOf(totals);
588
+ return [
589
+ t("usageStatIn", { n: formatTokens$1(totals.promptTokens) }),
590
+ `${t("usageTipCached")} ${formatTokens$1(totals.cachedTokens ?? 0)}`,
591
+ `${t("usageTipHit")} ${pct === void 0 ? "—" : `${pct}%`}`,
592
+ t("usageRequestCount", { n: formatCount(totals.requests) })
593
+ ].join(" · ");
594
+ }
595
+ /**
481
596
  * 用量区块。
482
597
  *
483
598
  * 只展示**本机经插件发出**的请求(不等于账号总额度 —— 账号额度见上方积分区块);
@@ -567,6 +682,37 @@ function UsageSection({ usage, accounts, t }) {
567
682
  })
568
683
  ]
569
684
  }),
685
+ (usage.recentDays ?? []).length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
686
+ className: CodeArtsPluginCard_module_css_default.hint,
687
+ children: t("usageTrendWeek")
688
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageTrend, {
689
+ mode: "hit",
690
+ axisEvery: 1,
691
+ hint: t("usageTrendHint"),
692
+ points: (usage.recentDays ?? []).map((day) => ({
693
+ key: day.date,
694
+ label: day.date.slice(5),
695
+ totals: day.totals
696
+ })),
697
+ t
698
+ })] }),
699
+ (usage.todayByHour ?? []).length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
700
+ className: CodeArtsPluginCard_module_css_default.hint,
701
+ children: t("usageTrendHour")
702
+ }), (usage.todayByHour ?? []).some((entry) => entry.totals.requests > 0) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsageTrend, {
703
+ mode: "volume",
704
+ axisEvery: 6,
705
+ hint: t("usageTrendHint"),
706
+ points: (usage.todayByHour ?? []).map((entry) => ({
707
+ key: String(entry.hour),
708
+ label: String(entry.hour),
709
+ totals: entry.totals
710
+ })),
711
+ t
712
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
713
+ className: CodeArtsPluginCard_module_css_default.trendTip,
714
+ children: t("usageTrendHourEmpty")
715
+ })] }),
570
716
  accounts.length > 1 && accountRows.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
571
717
  className: CodeArtsPluginCard_module_css_default.hint,
572
718
  children: t("usageByAccount")
@@ -613,7 +759,7 @@ function UsageSection({ usage, accounts, t }) {
613
759
  });
614
760
  }
615
761
  function CodeArtsPluginCard({ t }) {
616
- const [status, setStatus] = (0, react.useState)(null);
762
+ const [status, setStatus] = (0, react.useState)(lastStatusSnapshot);
617
763
  const [token, setToken] = (0, react.useState)("");
618
764
  const [label, setLabel] = (0, react.useState)("");
619
765
  const [busy, setBusy] = (0, react.useState)(false);
@@ -645,19 +791,33 @@ function CodeArtsPluginCard({ t }) {
645
791
  const [dockWritable, setDockWritable] = (0, react.useState)(false);
646
792
  const [dockSaving, setDockSaving] = (0, react.useState)(false);
647
793
  const [dockError, setDockError] = (0, react.useState)(null);
794
+ /**
795
+ * 首次读取状态的三态。为什么要单独记:`status === null` 只说明「还没读到」,
796
+ * 直接按它画就会先闪一下「未登录」,等数据回来再翻成「已登录」—— 看起来像
797
+ * 掉线又自己好了。只有服务端明确回 signed-out 才允许画登录框。
798
+ */
799
+ const [statusPhase, setStatusPhase] = (0, react.useState)(lastStatusSnapshot === null ? "loading" : "ready");
800
+ const [loadError, setLoadError] = (0, react.useState)(null);
648
801
  async function refreshDock() {
649
802
  try {
650
803
  const res = await fetch("/api/codearts/dock", { headers: { Accept: "application/json" } });
651
804
  if (!res.ok) return;
652
805
  const data = await res.json();
653
- if (data.dock !== void 0) setDock(data.dock);
806
+ if (data.dock !== void 0) {
807
+ const next = {
808
+ ...DOCK_DEFAULT,
809
+ ...data.dock
810
+ };
811
+ setDock(next);
812
+ setSidebarPanelWanted(next.sidebar);
813
+ }
654
814
  setDockWritable(data.writable === true);
655
815
  } catch {}
656
816
  }
657
817
  /**
658
818
  * 改一个开关:先按新值渲染,写失败再退回去。
659
819
  *
660
- * 这里不像模型勾选那样攒草稿再点保存 —— 只有两个布尔,攒草稿反而让用户猜
820
+ * 这里不像模型勾选那样攒草稿再点保存 —— 只有几个布尔,攒草稿反而让用户猜
661
821
  * 「我刚才那一下到底生效没有」。
662
822
  */
663
823
  async function toggleDock(key, on) {
@@ -668,6 +828,7 @@ function CodeArtsPluginCard({ t }) {
668
828
  });
669
829
  setDockSaving(true);
670
830
  setDockError(null);
831
+ if (key === "sidebar") setSidebarPanelWanted(on);
671
832
  try {
672
833
  const res = await fetch("/api/codearts/dock", {
673
834
  method: "POST",
@@ -682,9 +843,17 @@ function CodeArtsPluginCard({ t }) {
682
843
  throw new Error(typeof data.error === "string" ? data.error : `HTTP ${res.status}`);
683
844
  }
684
845
  const data = await res.json();
685
- if (data.dock !== void 0) setDock(data.dock);
846
+ if (data.dock !== void 0) {
847
+ const next = {
848
+ ...DOCK_DEFAULT,
849
+ ...data.dock
850
+ };
851
+ setDock(next);
852
+ setSidebarPanelWanted(next.sidebar);
853
+ }
686
854
  } catch (error) {
687
855
  setDock(previous);
856
+ if (key === "sidebar") setSidebarPanelWanted(previous.sidebar);
688
857
  setDockError(error instanceof Error ? error.message : String(error));
689
858
  } finally {
690
859
  setDockSaving(false);
@@ -732,14 +901,16 @@ function CodeArtsPluginCard({ t }) {
732
901
  }
733
902
  async function refresh() {
734
903
  try {
735
- const data = await (await fetch("/api/codearts/status", { headers: { Accept: "application/json" } })).json();
904
+ const res = await fetch("/api/codearts/status", { headers: { Accept: "application/json" } });
905
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
906
+ const data = await res.json();
907
+ lastStatusSnapshot = data;
736
908
  setStatus(data);
737
- } catch {
738
- setStatus({
739
- status: "signed-out",
740
- models: [],
741
- accounts: []
742
- });
909
+ setLoadError(null);
910
+ setStatusPhase("ready");
911
+ } catch (error) {
912
+ setLoadError(error instanceof Error ? error.message : String(error));
913
+ setStatusPhase((phase) => phase === "ready" ? "ready" : "failed");
743
914
  }
744
915
  }
745
916
  async function refreshSelection() {
@@ -965,6 +1136,10 @@ function CodeArtsPluginCard({ t }) {
965
1136
  }
966
1137
  const signedIn = status?.status === "signed-in";
967
1138
  const expired = status?.status === "expired";
1139
+ /** 首次数据还没回来:标题栏说「加载中」,正文放骨架,别画登录框。 */
1140
+ const loading = statusPhase === "loading";
1141
+ /** 连状态都读不到(服务没起 / 路由 404):给错误和重试,不要假装没登录。 */
1142
+ const failed = statusPhase === "failed" && status === null;
968
1143
  const accounts = status?.accounts ?? [];
969
1144
  const accountName = (account) => account.label?.trim() || account.userName?.trim() || account.id;
970
1145
  /**
@@ -979,8 +1154,10 @@ function CodeArtsPluginCard({ t }) {
979
1154
  if (Number.isFinite(exp)) parts.push(exp <= Date.now() ? t("accountExpired", { time: formatStored(exp) }) : t("accountExpires", { time: formatStored(exp) }));
980
1155
  return parts.join(" · ");
981
1156
  };
982
- const dotState = signedIn ? "done" : expired ? "warning" : "idle";
983
- const statusText = signedIn ? t("statusSignedIn") : expired ? t("statusExpired") : t("statusSignedOut");
1157
+ const dotState = !loading && signedIn ? "done" : !loading && expired ? "warning" : "idle";
1158
+ const statusText = loading ? t("statusLoading") : failed ? t("statusUnavailable") : signedIn ? t("statusSignedIn") : expired ? t("statusExpired") : t("statusSignedOut");
1159
+ /** 读不到状态时别在下面写「去登录」—— 用户可能已经登录了,只是接口没起来。 */
1160
+ const descText = loading ? t("descLoading") : failed ? null : signedIn ? t("descSignedIn") : t("descSignedOut");
984
1161
  const storedTime = formatStored(status?.storedAt);
985
1162
  const storedLabel = status?.label?.trim() ?? "";
986
1163
  const meta = !signedIn || storedTime === "" ? "" : storedLabel === "" ? t("storedMetaNoLabel", { time: storedTime }) : t("storedMeta", {
@@ -1058,15 +1235,15 @@ function CodeArtsPluginCard({ t }) {
1058
1235
  className: CodeArtsPluginCard_module_css_default.meta,
1059
1236
  children: meta
1060
1237
  }),
1061
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1238
+ descText !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1062
1239
  className: CodeArtsPluginCard_module_css_default.desc,
1063
- children: signedIn ? t("descSignedIn") : t("descSignedOut")
1240
+ children: descText
1064
1241
  })
1065
1242
  ]
1066
1243
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1067
1244
  variant: "outline",
1068
1245
  size: "sm",
1069
- disabled: busy,
1246
+ disabled: busy || loading,
1070
1247
  onClick: () => void refresh(),
1071
1248
  children: t("refresh")
1072
1249
  })]
@@ -1077,7 +1254,31 @@ function CodeArtsPluginCard({ t }) {
1077
1254
  className: CodeArtsPluginCard_module_css_default.error,
1078
1255
  children: status.piAiError
1079
1256
  }),
1080
- signedIn ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1257
+ failed ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1258
+ className: CodeArtsPluginCard_module_css_default.section,
1259
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1260
+ className: CodeArtsPluginCard_module_css_default.error,
1261
+ children: t("loadFailed", { message: loadError ?? "" })
1262
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1263
+ className: CodeArtsPluginCard_module_css_default.actions,
1264
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1265
+ variant: "outline",
1266
+ size: "sm",
1267
+ disabled: busy,
1268
+ onClick: () => void refresh(),
1269
+ children: t("retry")
1270
+ })
1271
+ })]
1272
+ }) : loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1273
+ className: CodeArtsPluginCard_module_css_default.skeleton,
1274
+ "aria-busy": "true",
1275
+ "aria-label": t("statusLoading"),
1276
+ children: [
1277
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: CodeArtsPluginCard_module_css_default.skeletonRow }),
1278
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: CodeArtsPluginCard_module_css_default.skeletonRow }),
1279
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: CodeArtsPluginCard_module_css_default.skeletonRow })
1280
+ ]
1281
+ }) : signedIn ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1081
1282
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedTabs, {
1082
1283
  className: CodeArtsPluginCard_module_css_default.tabs,
1083
1284
  items: tabs,
@@ -1361,11 +1562,11 @@ function CodeArtsPluginCard({ t }) {
1361
1562
  t
1362
1563
  })
1363
1564
  }),
1364
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TabPanel, {
1565
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(TabPanel, {
1365
1566
  id: panelId("display"),
1366
1567
  labelledBy: tabId("display"),
1367
1568
  active: activeTab === "display",
1368
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1569
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1369
1570
  className: CodeArtsPluginCard_module_css_default.section,
1370
1571
  children: [
1371
1572
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -1410,7 +1611,35 @@ function CodeArtsPluginCard({ t }) {
1410
1611
  children: t("displayFailed", { message: dockError })
1411
1612
  })
1412
1613
  ]
1413
- })
1614
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1615
+ className: CodeArtsPluginCard_module_css_default.section,
1616
+ children: [
1617
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1618
+ className: CodeArtsPluginCard_module_css_default.sectionHead,
1619
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1620
+ className: CodeArtsPluginCard_module_css_default.sectionLabel,
1621
+ children: t("displaySidebarTitle")
1622
+ })
1623
+ }),
1624
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1625
+ className: CodeArtsPluginCard_module_css_default.hint,
1626
+ children: t("displaySidebarHint")
1627
+ }),
1628
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1629
+ className: CodeArtsPluginCard_module_css_default.choices,
1630
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1631
+ className: CodeArtsPluginCard_module_css_default.choiceRow,
1632
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Checkbox, {
1633
+ className: CodeArtsPluginCard_module_css_default.check,
1634
+ checked: dock.sidebar,
1635
+ disabled: !dockWritable || dockSaving,
1636
+ onChange: (on) => void toggleDock("sidebar", on),
1637
+ label: t("displaySidebarToggle")
1638
+ })
1639
+ })
1640
+ })
1641
+ ]
1642
+ })]
1414
1643
  })
1415
1644
  ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1416
1645
  className: CodeArtsPluginCard_module_css_default.section,
@@ -1704,7 +1933,8 @@ function CodeArtsCreditDock({ useProjection, t, zone = "dock" }) {
1704
1933
  const [modelInfo, setModelInfo] = (0, react.useState)({});
1705
1934
  const [flags, setFlags] = (0, react.useState)({
1706
1935
  credits: true,
1707
- queue: true
1936
+ queue: true,
1937
+ sidebar: false
1708
1938
  });
1709
1939
  const [queued, setQueued] = (0, react.useState)(null);
1710
1940
  const [anchor, setAnchor] = (0, react.useState)(null);
@@ -1740,7 +1970,8 @@ function CodeArtsCreditDock({ useProjection, t, zone = "dock" }) {
1740
1970
  if (data === void 0 || !mounted.current) return;
1741
1971
  setFlags(data.dock ?? {
1742
1972
  credits: true,
1743
- queue: true
1973
+ queue: true,
1974
+ sidebar: false
1744
1975
  });
1745
1976
  setRows(rowsOf(data));
1746
1977
  setModelInfo(Object.fromEntries((data.models ?? []).map((model) => [model.id, {
@@ -2048,6 +2279,95 @@ function CodeArtsCreditDock({ useProjection, t, zone = "dock" }) {
2048
2279
  });
2049
2280
  }
2050
2281
  //#endregion
2282
+ //#region \0dsh-css:/home/runner/work/dsh-codearts/dsh-codearts/src/client/CodeArtsPanel.module.css.mjs
2283
+ const css = ".Z66cgG_page{box-sizing:border-box;height:100%;min-height:0;padding:calc(28px + var(--dsh-frame-top-clearance,0px)) clamp(24px, 4vw, 48px) 48px;color:var(--dsw-alias-label-primary);flex-direction:column;display:flex;overflow:auto}.Z66cgG_head{flex-direction:column;gap:6px;width:100%;max-width:860px;margin:0 auto;display:flex}.Z66cgG_back{appearance:none;font:inherit;cursor:pointer;border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);align-self:flex-start;align-items:center;gap:6px;margin-bottom:10px;padding:5px 12px;font-size:13px;line-height:20px;display:inline-flex}.Z66cgG_back:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}.Z66cgG_backCaret{border-bottom:1.6px solid;border-left:1.6px solid;width:7px;height:7px;display:inline-block;transform:rotate(45deg)}.Z66cgG_title{margin:0;font-size:20px;font-weight:600;line-height:1.4}.Z66cgG_desc{color:var(--dsw-alias-label-tertiary);margin:0;font-size:13px;line-height:1.5}.Z66cgG_body{width:100%;max-width:860px;margin:20px auto 0}.Z66cgG_icon{flex:none;display:block}";
2284
+ const tagId = "dsh-codearts/CodeArtsPanel.module.css";
2285
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
2286
+ const tag = document.createElement("style");
2287
+ tag.dataset.plugin = "dsh-codearts";
2288
+ tag.dataset.pluginCss = tagId;
2289
+ tag.textContent = css;
2290
+ document.head.appendChild(tag);
2291
+ }
2292
+ var CodeArtsPanel_module_css_default = {
2293
+ "title": "Z66cgG_title",
2294
+ "icon": "Z66cgG_icon",
2295
+ "back": "Z66cgG_back",
2296
+ "page": "Z66cgG_page",
2297
+ "backCaret": "Z66cgG_backCaret",
2298
+ "body": "Z66cgG_body",
2299
+ "head": "Z66cgG_head",
2300
+ "desc": "Z66cgG_desc"
2301
+ };
2302
+ //#endregion
2303
+ //#region src/client/CodeArtsPanel.tsx
2304
+ /** 侧边栏按钮里的图标。宿主按 { size, active } 渲染,我们用 currentColor 跟随主题。 */
2305
+ function CodeArtsPanelIcon({ size }) {
2306
+ const px = typeof size === "number" && size > 0 ? size : 16;
2307
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
2308
+ className: CodeArtsPanel_module_css_default.icon,
2309
+ width: px,
2310
+ height: px,
2311
+ viewBox: "0 0 16 16",
2312
+ fill: "none",
2313
+ "aria-hidden": "true",
2314
+ focusable: "false",
2315
+ children: [
2316
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
2317
+ d: "M5.1 3.9 1.9 8l3.2 4.1",
2318
+ stroke: "currentColor",
2319
+ strokeWidth: "1",
2320
+ strokeLinecap: "round",
2321
+ strokeLinejoin: "round"
2322
+ }),
2323
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
2324
+ d: "M10.9 3.9 14.1 8l-3.2 4.1",
2325
+ stroke: "currentColor",
2326
+ strokeWidth: "1",
2327
+ strokeLinecap: "round",
2328
+ strokeLinejoin: "round"
2329
+ }),
2330
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
2331
+ d: "M9 3.2 7 12.8",
2332
+ stroke: "currentColor",
2333
+ strokeWidth: "1",
2334
+ strokeLinecap: "round"
2335
+ })
2336
+ ]
2337
+ });
2338
+ }
2339
+ /** 侧边栏入口页:一个返回按钮 + 标题,正文直接复用设置里那张卡片。 */
2340
+ function CodeArtsPanelPage({ t, backToConversation }) {
2341
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2342
+ className: CodeArtsPanel_module_css_default.page,
2343
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2344
+ className: CodeArtsPanel_module_css_default.head,
2345
+ children: [
2346
+ backToConversation !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2347
+ type: "button",
2348
+ className: CodeArtsPanel_module_css_default.back,
2349
+ onClick: backToConversation,
2350
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2351
+ className: CodeArtsPanel_module_css_default.backCaret,
2352
+ "aria-hidden": "true"
2353
+ }), t("panelBack")]
2354
+ }),
2355
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
2356
+ className: CodeArtsPanel_module_css_default.title,
2357
+ children: t("cardTitle")
2358
+ }),
2359
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2360
+ className: CodeArtsPanel_module_css_default.desc,
2361
+ children: t("panelDesc")
2362
+ })
2363
+ ]
2364
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2365
+ className: CodeArtsPanel_module_css_default.body,
2366
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodeArtsPluginCard, { t })
2367
+ })]
2368
+ });
2369
+ }
2370
+ //#endregion
2051
2371
  //#region src/client/locales.ts
2052
2372
  const zh = {
2053
2373
  tabLabel: "CodeArts",
@@ -2061,6 +2381,11 @@ const zh = {
2061
2381
  statusExpired: "凭证已过期",
2062
2382
  descSignedIn: "凭证已就绪,勾选的模型会出现在模型选择器里。",
2063
2383
  descSignedOut: "用华为云账号登录,或手动粘贴一个 CodeArts cloud_dragon_token。",
2384
+ statusLoading: "加载中…",
2385
+ statusUnavailable: "状态读取失败",
2386
+ descLoading: "正在读取账号、模型与用量…",
2387
+ loadFailed: "读不到插件状态({message})",
2388
+ retry: "重试",
2064
2389
  storedMeta: "{label} · 存于 {time}",
2065
2390
  storedMetaNoLabel: "存于 {time}",
2066
2391
  refresh: "刷新",
@@ -2125,6 +2450,10 @@ const zh = {
2125
2450
  usageStatHit: "缓存命中",
2126
2451
  usageStatRequests: "今日请求",
2127
2452
  usageRequests: "{n} 次请求",
2453
+ usageTrendWeek: "近 7 天 · 缓存命中率",
2454
+ usageTrendHour: "今天 · 逐小时 token",
2455
+ usageTrendHint: "悬停柱子看那一格明细",
2456
+ usageTrendHourEmpty: "今天还没有逐小时数据(启用前的小时不回填)",
2128
2457
  usageByAccount: "今日按账号",
2129
2458
  usageRequestCount: "{n} 条",
2130
2459
  usageTokens: "输入 {prompt} · 输出 {completion}",
@@ -2170,6 +2499,9 @@ const zh = {
2170
2499
  displayQueueToggle: "显示上游排队位置",
2171
2500
  displayNotWritable: "这个宿主没给插件开配置存储,开关只能看不能改。",
2172
2501
  displayFailed: "保存失败:{message}",
2502
+ displaySidebarTitle: "侧边栏图标",
2503
+ displaySidebarHint: "勾上以后,DSH 左侧那一列图标里会多一个 CodeArts,点它直接在中间列打开当前这一页,不必每次都绕进设置。默认不勾:这列图标是全宿主共用的,每个插件都来占一格很快就挤满了。改完立刻生效,没有保存步骤。",
2504
+ displaySidebarToggle: "在侧边栏图标列显示 CodeArts",
2173
2505
  queueDock: "排队第 {position} 位",
2174
2506
  queueDockTitle: "上游排队:{model} · 第 {position} 位 / 共 {length} 条",
2175
2507
  claimButton: "签到领积分",
@@ -2180,7 +2512,9 @@ const zh = {
2180
2512
  claimDone: "已领取 {amount} 积分",
2181
2513
  claimAlready: "今日已领,明天再来",
2182
2514
  claimUnavailable: "这个账号当前没有可领的签到活动",
2183
- claimFailed: "领取失败:{message}"
2515
+ claimFailed: "领取失败:{message}",
2516
+ panelBack: "返回对话",
2517
+ panelDesc: "账号、模型开关与用量都在这一页里管,和设置 → 插件里那张卡片是同一份状态。"
2184
2518
  };
2185
2519
  const en = {
2186
2520
  tabLabel: "CodeArts",
@@ -2194,6 +2528,11 @@ const en = {
2194
2528
  statusExpired: "Credential expired",
2195
2529
  descSignedIn: "Credential is ready. Checked models show up in the model picker.",
2196
2530
  descSignedOut: "Sign in with your Huawei Cloud account, or paste a CodeArts cloud_dragon_token.",
2531
+ statusLoading: "Loading…",
2532
+ statusUnavailable: "Status unavailable",
2533
+ descLoading: "Reading accounts, models and usage…",
2534
+ loadFailed: "Could not read plugin status ({message})",
2535
+ retry: "Retry",
2197
2536
  storedMeta: "{label} · stored {time}",
2198
2537
  storedMetaNoLabel: "stored {time}",
2199
2538
  refresh: "Refresh",
@@ -2258,6 +2597,10 @@ const en = {
2258
2597
  usageStatHit: "Cache hit",
2259
2598
  usageStatRequests: "Today requests",
2260
2599
  usageRequests: "{n} request(s)",
2600
+ usageTrendWeek: "Last 7 days · cache hit rate",
2601
+ usageTrendHour: "Today · tokens by hour",
2602
+ usageTrendHint: "Hover a bar for that slot",
2603
+ usageTrendHourEmpty: "No hourly data yet today (hours before this feature are not backfilled)",
2261
2604
  usageByAccount: "Today by account",
2262
2605
  usageRequestCount: "{n} reqs",
2263
2606
  usageTokens: "in {prompt} · out {completion}",
@@ -2303,6 +2646,9 @@ const en = {
2303
2646
  displayQueueToggle: "Show the upstream queue position",
2304
2647
  displayNotWritable: "This host gives the plugin nowhere to persist settings, so the switches are read-only.",
2305
2648
  displayFailed: "Failed to save: {message}",
2649
+ displaySidebarTitle: "Sidebar icon",
2650
+ displaySidebarHint: "Once checked, a CodeArts icon joins the icon column on the left of DSH and opens this very page in the middle column, so you do not have to go through Settings each time. Off by default: that column is shared by the whole host, and it fills up quickly if every plugin claims a slot. Takes effect immediately — there is nothing to save.",
2651
+ displaySidebarToggle: "Show CodeArts in the sidebar icon column",
2306
2652
  queueDock: "Queued #{position}",
2307
2653
  queueDockTitle: "Upstream queue: {model} · position {position} of {length}",
2308
2654
  claimButton: "Claim daily credits",
@@ -2313,13 +2659,32 @@ const en = {
2313
2659
  claimDone: "Claimed {amount} credits",
2314
2660
  claimAlready: "Already claimed today — come back tomorrow",
2315
2661
  claimUnavailable: "No check-in campaign available for this account",
2316
- claimFailed: "Claim failed: {message}"
2662
+ claimFailed: "Claim failed: {message}",
2663
+ panelBack: "Back to chat",
2664
+ panelDesc: "Manage accounts, model switches and usage here — same state as the card under Settings → Plugins."
2317
2665
  };
2318
2666
  //#endregion
2319
2667
  //#region src/client/index.tsx
2320
2668
  const name = "dsh-codearts-client";
2321
2669
  const inject = ["slots", "locale"];
2670
+ /**
2671
+ * 侧边栏按钮的 id,同时也是中间列面板的 key —— 宿主靠这两个同名值把
2672
+ * 「点了哪一行」和「中间列画哪一页」配对。
2673
+ */
2674
+ const PANEL_ID = "dsh-codearts";
2322
2675
  function apply(ctx) {
2676
+ /**
2677
+ * 回对话页。软取 layout 服务:`ctx.get` 在服务没提供时返回 undefined 而不会抛,
2678
+ * 所以老宿主/非桌面端只是没有返回按钮,不影响整块 client 加载。
2679
+ * 故意不写进 `inject` —— 依赖缺失会让 apply 整个不跑。
2680
+ */
2681
+ const backToConversation = () => {
2682
+ try {
2683
+ ctx.get("layout")?.selectPanel?.(null);
2684
+ } catch (error) {
2685
+ console.error("[dsh-codearts] failed to switch back to conversation:", error);
2686
+ }
2687
+ };
2323
2688
  try {
2324
2689
  const namespace = "settings.codearts";
2325
2690
  ctx.effect(() => ctx.locale.register(namespace, {
@@ -2352,6 +2717,85 @@ function apply(ctx) {
2352
2717
  zone: "dock"
2353
2718
  })
2354
2719
  }, CodeArtsCreditDock));
2720
+ /** 每次 claim 拿到的 inject disposer;拆的时候逐个调用(连带注销里面的注册)。 */
2721
+ const panelClaims = [];
2722
+ const dropPanelSlots = () => {
2723
+ for (const dispose of panelClaims.splice(0)) try {
2724
+ dispose();
2725
+ } catch (error) {
2726
+ console.error("[dsh-codearts] failed to release sidebar panel:", error);
2727
+ }
2728
+ };
2729
+ const syncPanelRegistration = () => {
2730
+ dropPanelSlots();
2731
+ if (!sidebarPanelWanted()) return;
2732
+ try {
2733
+ panelClaims.push(ctx.slots.inject("sidebar.panellist", () => {
2734
+ try {
2735
+ return ctx.slots.register({
2736
+ name: "sidebar.panellist",
2737
+ id: PANEL_ID,
2738
+ order: 50,
2739
+ label: () => t("tabLabel"),
2740
+ locale: namespace
2741
+ }, CodeArtsPanelIcon);
2742
+ } catch (error) {
2743
+ console.error("[dsh-codearts] sidebar panel entry unavailable:", error);
2744
+ return () => {};
2745
+ }
2746
+ }));
2747
+ } catch (error) {
2748
+ console.error("[dsh-codearts] sidebar panel slot unavailable:", error);
2749
+ }
2750
+ try {
2751
+ panelClaims.push(ctx.slots.inject("main", () => {
2752
+ try {
2753
+ return ctx.slots.register({
2754
+ name: "main",
2755
+ key: PANEL_ID,
2756
+ locale: namespace,
2757
+ inject: () => ({
2758
+ t,
2759
+ backToConversation
2760
+ })
2761
+ }, CodeArtsPanelPage);
2762
+ } catch (error) {
2763
+ console.error("[dsh-codearts] main panel page unavailable:", error);
2764
+ return () => {};
2765
+ }
2766
+ }));
2767
+ } catch (error) {
2768
+ console.error("[dsh-codearts] main panel slot unavailable:", error);
2769
+ }
2770
+ };
2771
+ /**
2772
+ * 开关关掉时如果用户正站在那一页上,先回对话 —— 否则中间列会留在
2773
+ * 一个已经没了的 key 上。软取 layout:没有这个服务就跳过。
2774
+ */
2775
+ const leavePanelIfActive = () => {
2776
+ try {
2777
+ const layout = ctx.get("layout");
2778
+ if (layout?.panelInfo?.getSnapshot?.()?.activePanelId === PANEL_ID) layout.selectPanel?.(null);
2779
+ } catch {}
2780
+ };
2781
+ ctx.effect(() => {
2782
+ const off = subscribeSidebarPanelWanted(() => {
2783
+ if (!sidebarPanelWanted()) leavePanelIfActive();
2784
+ syncPanelRegistration();
2785
+ });
2786
+ syncPanelRegistration();
2787
+ return () => {
2788
+ off();
2789
+ dropPanelSlots();
2790
+ };
2791
+ }, "dsh-codearts: sidebar panel opt-in");
2792
+ (async () => {
2793
+ try {
2794
+ const res = await fetch("/api/codearts/dock", { headers: { Accept: "application/json" } });
2795
+ if (!res.ok) return;
2796
+ setSidebarPanelWanted((await res.json()).dock?.sidebar === true);
2797
+ } catch {}
2798
+ })();
2355
2799
  } catch (error) {
2356
2800
  console.error("[dsh-codearts] client card failed to load (host provider unaffected):", error);
2357
2801
  }