zcode-pro 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/inject.js CHANGED
@@ -42,6 +42,28 @@
42
42
  switchAttention: "r"
43
43
  // 打开运行中 / 未查看
44
44
  };
45
+ var STATUS_SESSION_IDS = /* @__PURE__ */ new Set(["ctx", "sessionCost", "questions"]);
46
+ var STATUS_GLOBAL_IDS = /* @__PURE__ */ new Set(["todayTokens", "todayCost", "totalTokens", "totalCost", "tasks"]);
47
+ var DEFAULT_TURN_ITEMS = [
48
+ { id: "turnIn", on: true },
49
+ // 输入
50
+ { id: "turnOut", on: true },
51
+ // 输出
52
+ { id: "turnCache", on: true }
53
+ // 缓存命中率
54
+ ];
55
+ var DEFAULT_STATUS_ITEMS = [
56
+ { id: "ctx", on: true },
57
+ // 上下文占用
58
+ { id: "questions", on: true },
59
+ // 提问次数
60
+ { id: "todayTokens", on: true },
61
+ // 今日 token(今天的会话记录完整,数据准确)
62
+ { id: "totalTokens", on: true },
63
+ // 累计 token(官方使用统计,全历史)
64
+ { id: "tasks", on: true }
65
+ // 任务总数
66
+ ];
45
67
  function zcodeAppVersion() {
46
68
  const v = liveBoot().zcodeVersion;
47
69
  return typeof v === "string" && v ? v : "";
@@ -235,6 +257,11 @@
235
257
  tbPluginStore: "\u63D2\u4EF6\u5E02\u573A",
236
258
  featurePinnedCollapse: "\u5DF2\u7F6E\u9876\u5206\u533A\u53EF\u6298\u53E0",
237
259
  featurePinnedCollapseDesc: "\u4FA7\u8FB9\u680F\u201C\u5DF2\u7F6E\u9876\u201D\u6807\u9898\u53EF\u70B9\u51FB\u6298\u53E0/\u5C55\u5F00\u5176\u4EFB\u52A1\u5217\u8868\uFF08\u4E0E\u201C\u9879\u76EE\u201D\u201C\u5206\u7EC4\u201D\u5206\u533A\u4E00\u81F4\uFF09\uFF0C\u72B6\u6001\u8BB0\u4F4F\u3002",
260
+ featureSandboxReclaim: "\u63D2\u4EF6\u754C\u9762\u6C99\u7BB1\u56DE\u6536",
261
+ featureSandboxReclaimDesc: "\u63D2\u4EF6\u5361\u7247\u6C99\u7BB1\u6570\u91CF\u8FBE\u5230\u4E0A\u9650\u65F6\u81EA\u52A8\u91CA\u653E\u6700\u65E9\u521B\u5EFA\u7684\uFF08\u6B63\u5728\u67E5\u770B\u7684\u6392\u6700\u540E\uFF09\uFF0C\u907F\u514D 64 \u4E2A\u540D\u989D\u8017\u5C3D\u540E\u65B0\u5361\u7247\u6253\u4E0D\u5F00\uFF1B\u88AB\u91CA\u653E\u7684\u65E7\u5361\u7247\u91CD\u65B0\u6253\u5F00\u6240\u5728\u4F1A\u8BDD\u5373\u6062\u590D\uFF08\u9884\u7B97\u53EF\u5728\u914D\u7F6E\u6587\u4EF6 sandboxReclaimBudget \u8C03\u6574\uFF09\u3002",
262
+ featurePluginCardRetry: "\u63D2\u4EF6\u5361\u7247\u81EA\u52A8\u91CD\u8BD5",
263
+ featurePluginCardRetryDesc: "\u56E0\u540D\u989D\u4E0A\u9650\u52A0\u8F7D\u5931\u8D25\u7684\u63D2\u4EF6\u5361\u7247\u81EA\u52A8\u70B9\u51FB\u201C\u91CD\u65B0\u52A0\u8F7D\u201D\uFF0C\u540D\u989D\u7A7A\u51FA\u540E\u81EA\u52A8\u6062\u590D\uFF0C\u65E0\u9700\u624B\u52A8\u8E72\u5B88\u3002",
264
+ visionCardUi: "\u601D\u8003\u8FC7\u7A0B\u663E\u793A\u8BC6\u56FE\u5361\u7247",
238
265
  pinnedToggleAria: "\u6298\u53E0/\u5C55\u5F00\u5DF2\u7F6E\u9876",
239
266
  sidebarProjectSpacingName: "\u9879\u76EE\u95F4\u8DDD",
240
267
  sidebarProjectSpacingDesc: "\u4FA7\u8FB9\u680F\u4E2D\u76F8\u90BB\u9879\u76EE\u884C\u4E4B\u95F4\u7684\u89C6\u89C9\u95F4\u8DDD\uFF08\u542B\u884C\u5185\u7559\u767D\uFF09\uFF1B\u8BBE\u7F6E\u540E\u5404\u5206\u533A\uFF08\u5DF2\u7F6E\u9876/\u9879\u76EE/\u4EFB\u52A1\uFF09\u4E4B\u95F4\u7684\u95F4\u8DDD\u4E5F\u7EDF\u4E00\u4E3A\u8BE5\u503C\u3002",
@@ -259,6 +286,33 @@
259
286
  featureNewtabPanelsDesc: "\u5B98\u65B9\u201C\u65B0\u6807\u7B7E\u9875\u201D\u53EA\u5217\u5B98\u65B9\u9762\u677F\u4E0E\u63A8\u8350\u7684\u5B98\u65B9\u63D2\u4EF6\uFF0C\u6B64\u529F\u80FD\u628A\u7B2C\u4E09\u65B9\u5E02\u573A\u63D2\u4EF6\uFF08headroom\u3001rtk \u7B49\uFF09\u7684\u9762\u677F\u5361\u7247\u52A0\u8FDB\u53BB\uFF0C\u70B9\u51FB\u5728\u4FA7\u8FB9\u9762\u677F\u6253\u5F00\u3002",
260
287
  ntpDescSuffix: "\u63D2\u4EF6\u9762\u677F",
261
288
  ntpNeedSession: "\u5148\u6253\u5F00\u4E00\u4E2A\u4EFB\u52A1\u4F1A\u8BDD\uFF0C\u518D\u4ECE\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u63D2\u4EF6\u9762\u677F",
289
+ featureStatusBarDesc: "\u4E3B\u754C\u9762\u5E95\u90E8\u53F3\u4E0B\u89D2\u663E\u793A\u7EDF\u8BA1\u9879\uFF1A\u4E0A\u4E0B\u6587\u5360\u7528\u3001\u4F1A\u8BDD/\u4ECA\u65E5/\u7D2F\u8BA1 token\u3001\u4EFB\u52A1\u6570\u7B49\uFF1B\u5404\u9879\u7684\u987A\u5E8F\u4E0E\u663E\u9690\u5728\u201C\u6837\u5F0F \u2192 \u72B6\u6001\u680F\u201D\u91CC\u8C03\u6574\u3002",
290
+ tabStylesPane: "\u6837\u5F0F\u8C03\u6574",
291
+ tabStatusPane: "\u72B6\u6001\u680F",
292
+ sbItemCtx: "\u4E0A\u4E0B\u6587\u5360\u7528",
293
+ sbItemQuestions: "\u63D0\u95EE\u6B21\u6570",
294
+ sbMaster: "\u542F\u7528\u7EDF\u8BA1",
295
+ sbMasterDesc: "\u603B\u5F00\u5173\uFF1A\u5173\u95ED\u540E\u5E95\u90E8\u72B6\u6001\u680F\u4E0E\u6BCF\u8F6E\u56DE\u7B54\u7684\u7528\u91CF\u6807\u6CE8\u4E00\u8D77\u505C\u7528\u3002",
296
+ sbGroupSession: "\u4F1A\u8BDD\u7EDF\u8BA1\uFF08\u8DDF\u968F\u5F53\u524D\u4F1A\u8BDD\uFF09",
297
+ sbGroupGlobal: "\u603B\u7EDF\u8BA1",
298
+ sbGroupTurn: "\u6BCF\u8F6E\u7EDF\u8BA1\uFF08\u56DE\u7B54\u672B\u5C3E\uFF09",
299
+ sbLabelIn: "\u8F93\u5165",
300
+ sbLabelOut: "\u8F93\u51FA",
301
+ sbLabelCache: "\u7F13\u5B58",
302
+ turnUsageTitle: "\u672C\u8F6E token",
303
+ sbLabelQuestions: "\u63D0\u95EE",
304
+ sbItemTodayTokens: "\u4ECA\u65E5 token",
305
+ sbItemTasks: "\u4EFB\u52A1\u603B\u6570",
306
+ sbLabelCtx: "\u4E0A\u4E0B\u6587",
307
+ sbLabelTurns: "\u8C03\u7528",
308
+ sbLabelToday: "\u4ECA\u65E5",
309
+ sbLabelTasks: "\u4EFB\u52A1",
310
+ sbItemTotalTokens: "\u7D2F\u8BA1 token",
311
+ sbLabelTotal: "\u7D2F\u8BA1",
312
+ sbMoveUp: "\u4E0A\u79FB",
313
+ sbMoveDown: "\u4E0B\u79FB",
314
+ sbVisible: "\u663E\u793A",
315
+ sbHidden: "\u9690\u85CF",
262
316
  tabShortcuts: "\u5FEB\u6377\u952E",
263
317
  hkTitle: "\u4F1A\u8BDD\u5FEB\u6377\u5207\u6362",
264
318
  hkAltZ: "\u5728\u5F53\u524D\u4E0E\u4E0A\u6B21\u4F7F\u7528\u7684\u4F1A\u8BDD\u95F4\u6765\u56DE\u5207\u6362",
@@ -659,6 +713,11 @@
659
713
  tbPluginStore: "Plugin store",
660
714
  featurePinnedCollapse: "Collapsible Pinned section",
661
715
  featurePinnedCollapseDesc: 'The sidebar "Pinned" header collapses/expands its task list (like Projects and Groups); the state is remembered.',
716
+ featureSandboxReclaim: "Plugin card sandbox reclaim",
717
+ featureSandboxReclaimDesc: "Releases the oldest plugin-card sandboxes once the cap is hit (the one being viewed goes last), so new cards keep opening after the 64-slot pool fills up; released cards come back when their session is reopened (budget adjustable via sandboxReclaimBudget in the config file).",
718
+ featurePluginCardRetry: "Plugin card auto-retry",
719
+ featurePluginCardRetryDesc: 'Auto-clicks "Reload" on cards that failed the slot cap; they recover as soon as a slot frees up.',
720
+ visionCardUi: "Vision cards",
662
721
  pinnedToggleAria: "Collapse/expand Pinned",
663
722
  sidebarProjectSpacingName: "Project spacing",
664
723
  sidebarProjectSpacingDesc: "Visual spacing between adjacent project rows in the sidebar (row padding included); when set, section gaps (Pinned/Projects/Tasks) follow the same value.",
@@ -683,6 +742,33 @@
683
742
  featureNewtabPanelsDesc: 'The built-in "new tab page" only lists built-in panels and recommended official plugins; this adds cards for third-party marketplace plugin panels (headroom, rtk, \u2026) that open in the side pane.',
684
743
  ntpDescSuffix: "plugin panel",
685
744
  ntpNeedSession: "Open a task session first, then open plugin panels from the new tab page",
745
+ featureStatusBarDesc: 'Shows statistics at the bottom-right of the main view: context usage, session/today/total tokens, task count, etc.; reorder or hide each item under "Styles \u2192 Status bar".',
746
+ tabStylesPane: "Styles",
747
+ tabStatusPane: "Status bar",
748
+ sbItemCtx: "Context usage",
749
+ sbItemQuestions: "Questions asked",
750
+ sbMaster: "Enable statistics",
751
+ sbMasterDesc: "Master switch: turning this off disables both the status bar and per-turn labels.",
752
+ sbGroupSession: "Session statistics (follows current session)",
753
+ sbGroupGlobal: "Global statistics",
754
+ sbGroupTurn: "Per-turn statistics (answer rows)",
755
+ sbLabelIn: "in",
756
+ sbLabelOut: "out",
757
+ sbLabelCache: "cache",
758
+ turnUsageTitle: "Turn tokens",
759
+ sbLabelQuestions: "asks",
760
+ sbItemTodayTokens: "Today tokens",
761
+ sbItemTasks: "Tasks",
762
+ sbLabelCtx: "ctx",
763
+ sbLabelTurns: "calls",
764
+ sbLabelToday: "today",
765
+ sbLabelTasks: "tasks",
766
+ sbItemTotalTokens: "Total tokens",
767
+ sbLabelTotal: "total",
768
+ sbMoveUp: "Up",
769
+ sbMoveDown: "Down",
770
+ sbVisible: "shown",
771
+ sbHidden: "hidden",
686
772
  tabShortcuts: "Shortcuts",
687
773
  hkTitle: "Session quick switch",
688
774
  hkAltZ: "Toggle between the current and the last-used session",
@@ -1022,20 +1108,20 @@
1022
1108
  }
1023
1109
  }
1024
1110
  function observeRadixPopups(onPopup) {
1025
- const seen = /* @__PURE__ */ new WeakSet();
1111
+ const seen2 = /* @__PURE__ */ new WeakSet();
1026
1112
  const check = (node) => {
1027
1113
  if (!(node instanceof HTMLElement)) return;
1028
1114
  if (node.hasAttribute("data-radix-popper-content-wrapper")) {
1029
1115
  requestAnimationFrame(() => {
1030
1116
  const content = node.firstElementChild;
1031
- if (content && !seen.has(content)) {
1032
- seen.add(content);
1117
+ if (content && !seen2.has(content)) {
1118
+ seen2.add(content);
1033
1119
  onPopup(content);
1034
1120
  }
1035
1121
  });
1036
1122
  }
1037
- if (node.getAttribute && node.getAttribute("role") === "menu" && !seen.has(node)) {
1038
- seen.add(node);
1123
+ if (node.getAttribute && node.getAttribute("role") === "menu" && !seen2.has(node)) {
1124
+ seen2.add(node);
1039
1125
  requestAnimationFrame(() => {
1040
1126
  if (node.isConnected) onPopup(node);
1041
1127
  });
@@ -1695,6 +1781,56 @@
1695
1781
  text-align: center;
1696
1782
  color: var(--color-muted-foreground, #888);
1697
1783
  }
1784
+ /* \u72B6\u6001\u680F\u7EDF\u8BA1\uFF08status-bar.js\uFF09\uFF1A\u8F93\u5165\u6846\u4E0B\u65B9\u53F3\u5BF9\u9F50\u7684\u4E00\u884C\u5C0F\u5B57\uFF0C\u6BCF\u9879\u5E26 lucide \u5C0F\u56FE\u6807\u3002
1785
+ \u4E0A\u4E0B\u95F4\u8DDD\u7531\u811A\u672C\u6309\u89C6\u56FE\u63A7\u5236\uFF08status-bar.js\uFF1A\u4E0B\u65B9\u6709\u5185\u5BB9\u65F6\u5BF9\u79F0 7px\uFF0C
1786
+ \u4E0B\u65B9\u662F\u7A97\u5E95\u7559\u767D\u65F6\u8D1F margin \u538B\u5165\u8D34\u5E95\uFF09 */
1787
+ .zcodepro-statusbar {
1788
+ display: flex;
1789
+ justify-content: flex-end;
1790
+ align-items: center;
1791
+ gap: 8px;
1792
+ padding: 7px 14px 0;
1793
+ font-size: 11px;
1794
+ line-height: 1.4;
1795
+ color: var(--color-muted-foreground, #888);
1796
+ user-select: none;
1797
+ pointer-events: none;
1798
+ }
1799
+ .zcodepro-statusbar span[data-tone='warn'] { color: var(--color-amber-500, #f59e0b); }
1800
+ .zcodepro-statusbar span[data-tone='danger'] { color: var(--color-red-500, #ef4444); }
1801
+ .zcodepro-statusbar-sep { opacity: 0.45; }
1802
+ /* \u4F1A\u8BDD\u7EC4\u4E0E\u5168\u5C40\u7EC4\u4E4B\u95F4\u7684\u5206\u754C\u7AD6\u7EBF */
1803
+ .zcodepro-statusbar-groupsep {
1804
+ width: 1px;
1805
+ height: 10px;
1806
+ background: currentColor;
1807
+ opacity: 0.3;
1808
+ margin: 0 2px;
1809
+ }
1810
+ /* \u6BCF\u9879\u5185\u90E8\u7528 flex \u5BF9\u9F50\uFF1A\u56FE\u6807\u3001\u6807\u7B7E\u3001\u6570\u503C\u5782\u76F4\u5C45\u4E2D\uFF0C\u95F4\u8DDD\u7EDF\u4E00 */
1811
+ .zcodepro-statusbar span[title] {
1812
+ display: inline-flex;
1813
+ align-items: center;
1814
+ gap: 3px;
1815
+ white-space: nowrap;
1816
+ }
1817
+ .zcodepro-sb-icon {
1818
+ width: 12px;
1819
+ height: 12px;
1820
+ flex-shrink: 0;
1821
+ opacity: 0.6;
1822
+ }
1823
+ .zcodepro-statusbar b { font-weight: 600; color: var(--color-foreground-subtle, #666); }
1824
+ .zcodepro-statusbar i {
1825
+ font-style: normal;
1826
+ opacity: 0.7;
1827
+ font-size: 10px;
1828
+ }
1829
+ /* \u9879\u5185\u77ED\u6807\u7B7E\uFF08\u5982\u201C\u4E0A\u4E0B\u6587\u201D\u201C\u7D2F\u8BA1\u201D\uFF09\uFF0C\u5F31\u5316\u4E0D\u62A2\u6570\u503C */
1830
+ .zcodepro-statusbar em {
1831
+ font-style: normal;
1832
+ opacity: 0.75;
1833
+ }
1698
1834
  .zcodepro-switcher-ws {
1699
1835
  font-size: 11px;
1700
1836
  color: var(--color-muted-foreground, #888);
@@ -2504,10 +2640,10 @@
2504
2640
  }
2505
2641
  function handleProjectMenu(content) {
2506
2642
  if (content.querySelector("[data-zcodepro-item]")) return;
2507
- const items = itemsOf(content);
2643
+ const items2 = itemsOf(content);
2508
2644
  const newUi = isNewProjectSidebar();
2509
2645
  const menuRow = newUi ? openMenuProjectRow() : null;
2510
- const anchor = newUi ? items.find((el) => el.getAttribute("data-testid") === "virtual-project-edit") : items.find(
2646
+ const anchor = newUi ? items2.find((el) => el.getAttribute("data-testid") === "virtual-project-edit") : items2.find(
2511
2647
  (el) => el.hasAttribute("data-testid") && REMOVE_TEXTS.some((x) => itemText(el).startsWith(x))
2512
2648
  );
2513
2649
  if (!anchor) return;
@@ -2600,7 +2736,7 @@
2600
2736
  if (looksLikePath(p2)) return p2;
2601
2737
  return pathFromReactFiber(content);
2602
2738
  }
2603
- function appendMenuItem(content, template, anchor, { marker, label, icon, onPick }) {
2739
+ function appendMenuItem(content, template, anchor, { marker, label, icon: icon2, onPick }) {
2604
2740
  const item = template.cloneNode(true);
2605
2741
  item.removeAttribute("data-testid");
2606
2742
  item.removeAttribute("data-highlighted");
@@ -2609,7 +2745,7 @@
2609
2745
  item.setAttribute("data-zcodepro-item", marker);
2610
2746
  for (const child of [...item.childNodes]) child.remove();
2611
2747
  const origIcon = template.querySelector("svg");
2612
- item.append(buildIcon(origIcon?.getAttribute("class") || "size-4", icon), document.createTextNode(label));
2748
+ item.append(buildIcon(origIcon?.getAttribute("class") || "size-4", icon2), document.createTextNode(label));
2613
2749
  item.addEventListener("mouseenter", () => {
2614
2750
  for (const el of content.querySelectorAll('[role="menuitem"]')) el.removeAttribute("data-highlighted");
2615
2751
  item.setAttribute("data-highlighted", "");
@@ -2624,8 +2760,8 @@
2624
2760
  }
2625
2761
  function handleFileMenu(content) {
2626
2762
  if (content.dataset.zcodeproFileMenu) return;
2627
- const items = itemsOf(content);
2628
- const copyAbs = items.find((el) => COPY_ABS_TEXTS.some((x) => itemText(el).startsWith(x)));
2763
+ const items2 = itemsOf(content);
2764
+ const copyAbs = items2.find((el) => COPY_ABS_TEXTS.some((x) => itemText(el).startsWith(x)));
2629
2765
  if (!copyAbs) return;
2630
2766
  content.dataset.zcodeproFileMenu = "1";
2631
2767
  void (async () => {
@@ -2640,7 +2776,7 @@
2640
2776
  }).catch(() => {
2641
2777
  });
2642
2778
  };
2643
- const noApps = items.find((el) => NO_APPS_TEXTS.some((x) => itemText(el) === x)) || content.querySelector('[data-disabled][role="menuitem"]');
2779
+ const noApps = items2.find((el) => NO_APPS_TEXTS.some((x) => itemText(el) === x)) || content.querySelector('[data-disabled][role="menuitem"]');
2644
2780
  const openAnchor = noApps && content.contains(noApps) ? noApps : copyAbs;
2645
2781
  appendMenuItem(content, copyAbs, openAnchor, {
2646
2782
  marker: "file-open-default",
@@ -2850,10 +2986,10 @@
2850
2986
  function startImageMenu() {
2851
2987
  if (typeof window === "undefined") return;
2852
2988
  if (typeof navigator.clipboard?.write !== "function" || typeof window.ClipboardItem === "undefined") return;
2853
- let enabled7 = true;
2989
+ let enabled9 = true;
2854
2990
  const refresh = () => {
2855
2991
  void getConfig().then((c) => {
2856
- enabled7 = !(c.features && c.features.imageCopy === false);
2992
+ enabled9 = !(c.features && c.features.imageCopy === false);
2857
2993
  });
2858
2994
  };
2859
2995
  refresh();
@@ -2862,7 +2998,7 @@
2862
2998
  const img = copyTargetOf(e.target, e.clientX, e.clientY);
2863
2999
  if (!img) return;
2864
3000
  e.preventDefault();
2865
- if (enabled7) showMenu(e.clientX, e.clientY, img);
3001
+ if (enabled9) showMenu(e.clientX, e.clientY, img);
2866
3002
  refresh();
2867
3003
  }, true);
2868
3004
  }
@@ -2966,7 +3102,7 @@
2966
3102
  function waitFor(getter, timeoutMs, step) {
2967
3103
  const start = Date.now();
2968
3104
  return new Promise((resolve, reject) => {
2969
- const tick2 = () => {
3105
+ const tick6 = () => {
2970
3106
  let v = null;
2971
3107
  try {
2972
3108
  v = getter();
@@ -2980,9 +3116,9 @@
2980
3116
  reject(new Error(step));
2981
3117
  return;
2982
3118
  }
2983
- setTimeout(tick2, 120);
3119
+ setTimeout(tick6, 120);
2984
3120
  };
2985
- tick2();
3121
+ tick6();
2986
3122
  });
2987
3123
  }
2988
3124
  function setReactInputValue(input, value) {
@@ -3754,7 +3890,7 @@
3754
3890
  setState(L.upgradeFound.replaceAll("{v}", res.latest || ""));
3755
3891
  btn.textContent = L.upgradeNow;
3756
3892
  };
3757
- const tick2 = async () => {
3893
+ const tick6 = async () => {
3758
3894
  const res = await rpc(endpoint + "/upgrade");
3759
3895
  if (!res.ok) {
3760
3896
  stopPoll();
@@ -3794,9 +3930,9 @@
3794
3930
  btn.textContent = L.upgradeStop;
3795
3931
  setState(L.upgradeRunning);
3796
3932
  timer = setInterval(() => {
3797
- void tick2();
3933
+ void tick6();
3798
3934
  }, 1500);
3799
- void tick2();
3935
+ void tick6();
3800
3936
  };
3801
3937
  void (async () => {
3802
3938
  const res = await rpc(endpoint + "/upgrade");
@@ -4005,6 +4141,16 @@
4005
4141
  if (await setFeature("imageCopy", next)) f.imageCopy = next;
4006
4142
  refreshRows();
4007
4143
  }),
4144
+ settingRow(L.featureSandboxReclaim, L.featureSandboxReclaimDesc, f.sandboxReclaim !== false, async () => {
4145
+ const next = !(f.sandboxReclaim !== false);
4146
+ if (await setFeature("sandboxReclaim", next)) f.sandboxReclaim = next;
4147
+ refreshRows();
4148
+ }),
4149
+ settingRow(L.featurePluginCardRetry, L.featurePluginCardRetryDesc, f.pluginCardRetry !== false, async () => {
4150
+ const next = !(f.pluginCardRetry !== false);
4151
+ if (await setFeature("pluginCardRetry", next)) f.pluginCardRetry = next;
4152
+ refreshRows();
4153
+ }),
4008
4154
  settingRow(L.featureWsRunning, L.featureWsRunningDesc, f.wsRunningSpin !== false, async () => {
4009
4155
  const next = !(f.wsRunningSpin !== false);
4010
4156
  if (await setFeature("wsRunningSpin", next)) f.wsRunningSpin = next;
@@ -4047,7 +4193,7 @@
4047
4193
  const WECHAT_ID = "ai4only";
4048
4194
  const ns = "http://www.w3.org/2000/svg";
4049
4195
  const extIcon = () => {
4050
- const icon = h("svg", {
4196
+ const icon2 = h("svg", {
4051
4197
  viewBox: "0 0 24 24",
4052
4198
  fill: "none",
4053
4199
  stroke: "currentColor",
@@ -4059,11 +4205,11 @@
4059
4205
  for (const d of ["M15 3h6v6", "M10 14 21 3", "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"]) {
4060
4206
  const p2 = document.createElementNS(ns, "path");
4061
4207
  p2.setAttribute("d", d);
4062
- icon.append(p2);
4208
+ icon2.append(p2);
4063
4209
  }
4064
- return icon;
4210
+ return icon2;
4065
4211
  };
4066
- const communityCard = (title, desc, icon, onClick) => h(
4212
+ const communityCard = (title, desc, icon2, onClick) => h(
4067
4213
  "div",
4068
4214
  {
4069
4215
  class: "flex cursor-pointer items-center justify-between gap-2 rounded-xl border border-border p-2 transition-colors hover:bg-surface-hover",
@@ -4075,7 +4221,7 @@
4075
4221
  h("div", { class: "truncate text-ui-sm font-medium text-foreground" }, title),
4076
4222
  h("div", { class: "mt-0.5 truncate text-ui-xs/relaxed text-foreground-subtle" }, desc)
4077
4223
  ),
4078
- icon()
4224
+ icon2()
4079
4225
  );
4080
4226
  const communityCards = h(
4081
4227
  "div",
@@ -4218,7 +4364,7 @@
4218
4364
  ),
4219
4365
  hkCard
4220
4366
  );
4221
- const paneStyles = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
4367
+ const paneStyles = h("div", { role: "tabpanel", class: "mt-2", style: "display:none" });
4222
4368
  const panePerf = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
4223
4369
  const paneAgents = h("div", { role: "tabpanel", class: "mt-2", style: "display:none" });
4224
4370
  const paneProxy = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
@@ -4433,7 +4579,184 @@
4433
4579
  for (const c of fontCells) c.setOptions(list);
4434
4580
  })();
4435
4581
  renderToolbarSwitch();
4436
- paneStyles.append(
4582
+ const paneStylesSub1 = h("div", { class: "mt-4" });
4583
+ const paneStylesSub2 = h("div", { class: "mt-4", style: "display:none" });
4584
+ const ITEM_NAMES = {
4585
+ ctx: "sbItemCtx",
4586
+ questions: "sbItemQuestions",
4587
+ todayTokens: "sbItemTodayTokens",
4588
+ totalTokens: "sbItemTotalTokens",
4589
+ tasks: "sbItemTasks"
4590
+ };
4591
+ const sbItems = () => {
4592
+ const saved = config.statusBar && Array.isArray(config.statusBar.items) ? config.statusBar.items : null;
4593
+ if (!saved) return DEFAULT_STATUS_ITEMS.map((x) => ({ ...x }));
4594
+ const byId = new Map(saved.map((x) => [x.id, x]));
4595
+ const list = saved.filter((x) => ITEM_NAMES[x.id]).map((x) => ({ on: x.on !== false, id: x.id }));
4596
+ for (const d of DEFAULT_STATUS_ITEMS) if (!byId.has(d.id)) list.push({ ...d });
4597
+ return [
4598
+ ...list.filter((x) => STATUS_SESSION_IDS.has(x.id)),
4599
+ ...list.filter((x) => STATUS_GLOBAL_IDS.has(x.id))
4600
+ ];
4601
+ };
4602
+ let sbSaving = false;
4603
+ const saveSbItems = async (next) => {
4604
+ if (sbSaving) return;
4605
+ sbSaving = true;
4606
+ try {
4607
+ const res = await rpc("/config", { method: "POST", body: { statusBar: { items: next } } });
4608
+ clearConfigCache();
4609
+ window.dispatchEvent(new CustomEvent("zcodepro:config-changed"));
4610
+ if (!res.ok) {
4611
+ showToast(L.failed + ": " + errText(res), "error");
4612
+ renderSbItems();
4613
+ return;
4614
+ }
4615
+ if (res.config && res.config.statusBar) config.statusBar = res.config.statusBar;
4616
+ } finally {
4617
+ sbSaving = false;
4618
+ }
4619
+ renderSbItems();
4620
+ };
4621
+ const sbList = h("div", { class: "divide-y divide-border rounded-xl border border-border" });
4622
+ const sbGlobalList = h("div", { class: "divide-y divide-border rounded-xl border border-border" });
4623
+ const TURN_NAMES = { turnIn: "sbLabelIn", turnOut: "sbLabelOut", turnCache: "sbLabelCache" };
4624
+ const turnItems = () => {
4625
+ const saved = config.statusBar && Array.isArray(config.statusBar.turnItems) ? config.statusBar.turnItems : null;
4626
+ if (!saved) return DEFAULT_TURN_ITEMS.map((x) => ({ ...x }));
4627
+ const byId = new Map(saved.map((x) => [x.id, x]));
4628
+ const list = saved.filter((x) => TURN_NAMES[x.id]).map((x) => ({ on: x.on !== false, id: x.id }));
4629
+ for (const d of DEFAULT_TURN_ITEMS) if (!byId.has(d.id)) list.push({ ...d });
4630
+ return list;
4631
+ };
4632
+ const saveTurnItems = async (next) => {
4633
+ if (sbSaving) return;
4634
+ sbSaving = true;
4635
+ try {
4636
+ const res = await rpc("/config", { method: "POST", body: { statusBar: { turnItems: next } } });
4637
+ clearConfigCache();
4638
+ window.dispatchEvent(new CustomEvent("zcodepro:config-changed"));
4639
+ if (!res.ok) {
4640
+ showToast(L.failed + ": " + errText(res), "error");
4641
+ renderSbItems();
4642
+ return;
4643
+ }
4644
+ if (res.config && res.config.statusBar) config.statusBar = res.config.statusBar;
4645
+ } finally {
4646
+ sbSaving = false;
4647
+ }
4648
+ renderSbItems();
4649
+ };
4650
+ const turnList = h("div", { class: "divide-y divide-border rounded-xl border border-border" });
4651
+ const renderSbItems = () => {
4652
+ turnList.replaceChildren(...turnItems().map((it, i) => h(
4653
+ "div",
4654
+ { class: "flex items-center gap-2 px-3 py-2" },
4655
+ h("span", { class: "min-w-0 flex-1 truncate text-ui-sm text-foreground-subtle" }, L[TURN_NAMES[it.id]] || it.id),
4656
+ btnSmall(it.on ? L.sbVisible : L.sbHidden, () => {
4657
+ void saveTurnItems(turnItems().map((x) => x.id === it.id ? { ...x, on: !x.on } : x));
4658
+ }),
4659
+ btnSmall("\u2191", () => {
4660
+ const a = turnItems();
4661
+ if (i > 0) {
4662
+ [a[i - 1], a[i]] = [a[i], a[i - 1]];
4663
+ void saveTurnItems(a);
4664
+ }
4665
+ }),
4666
+ btnSmall("\u2193", () => {
4667
+ const a = turnItems();
4668
+ if (i < a.length - 1) {
4669
+ [a[i], a[i + 1]] = [a[i + 1], a[i]];
4670
+ void saveTurnItems(a);
4671
+ }
4672
+ })
4673
+ )));
4674
+ const list = sbItems();
4675
+ const renderGroup = (el, groupIds) => {
4676
+ const idxs = list.map((x, i) => i).filter((i) => groupIds.has(list[i].id));
4677
+ el.replaceChildren(...idxs.map((i) => {
4678
+ const it = list[i];
4679
+ const swap = (dir) => {
4680
+ const a = sbItems();
4681
+ const same = a.map((x, j) => j).filter((j) => groupIds.has(a[j].id));
4682
+ const pos = same.indexOf(i);
4683
+ const other = same[pos + dir];
4684
+ if (other == null) return;
4685
+ [a[i], a[other]] = [a[other], a[i]];
4686
+ void saveSbItems(a);
4687
+ };
4688
+ return h(
4689
+ "div",
4690
+ { class: "flex items-center gap-2 px-3 py-2" },
4691
+ h(
4692
+ "span",
4693
+ { class: "min-w-0 flex-1 truncate text-ui-sm text-foreground-subtle" },
4694
+ L[ITEM_NAMES[it.id]] || it.id
4695
+ ),
4696
+ btnSmall(it.on ? L.sbVisible : L.sbHidden, () => {
4697
+ void saveSbItems(sbItems().map((x, j) => j === i ? { ...x, on: !x.on } : x));
4698
+ }),
4699
+ btnSmall("\u2191", () => swap(-1)),
4700
+ btnSmall("\u2193", () => swap(1))
4701
+ );
4702
+ }));
4703
+ };
4704
+ renderGroup(sbList, STATUS_SESSION_IDS);
4705
+ renderGroup(sbGlobalList, STATUS_GLOBAL_IDS);
4706
+ };
4707
+ renderSbItems();
4708
+ const sbPaneRerender = () => {
4709
+ renderSbItems();
4710
+ };
4711
+ let masterRow = null;
4712
+ const renderMaster = () => {
4713
+ const next = masterRow ? !((config.features || {}).statusBar !== false) : (config.features || {}).statusBar !== false;
4714
+ const fresh = settingRow(L.sbMaster, L.sbMasterDesc, (config.features || {}).statusBar !== false, async () => {
4715
+ const to = !((config.features || {}).statusBar !== false);
4716
+ const res = await rpc("/config", { method: "POST", body: { features: { statusBar: to } } });
4717
+ clearConfigCache();
4718
+ if (res.ok) {
4719
+ config.features = { ...config.features || {}, statusBar: to };
4720
+ }
4721
+ window.dispatchEvent(new CustomEvent("zcodepro:config-changed"));
4722
+ renderMaster();
4723
+ });
4724
+ fresh.setAttribute("data-zcodepro-sb-master", "1");
4725
+ if (masterRow) masterRow.replaceWith(fresh);
4726
+ masterRow = fresh;
4727
+ };
4728
+ renderMaster();
4729
+ paneStylesSub2.append(
4730
+ masterRow,
4731
+ h("p", { class: "mt-2 text-ui-sm/relaxed text-foreground-subtle" }, L.featureStatusBarDesc),
4732
+ h("div", { class: "mt-3 mb-1 text-ui-sm font-medium" }, L.sbGroupSession),
4733
+ sbList,
4734
+ h("div", { class: "mt-3 mb-1 text-ui-sm font-medium" }, L.sbGroupGlobal),
4735
+ sbGlobalList,
4736
+ h("div", { class: "mt-3 mb-1 text-ui-sm font-medium" }, L.sbGroupTurn),
4737
+ turnList
4738
+ );
4739
+ let stylesSubTab = "styles";
4740
+ const stylesSubTablist = h("div", { role: "tablist", "aria-orientation": "horizontal", class: "zcodepro-tablist" });
4741
+ const renderStylesSubTabs = () => stylesSubTablist.replaceChildren(...[
4742
+ ["styles", L.tabStylesPane],
4743
+ ["status", L.tabStatusPane]
4744
+ ].map(([id, label]) => h("button", {
4745
+ type: "button",
4746
+ role: "tab",
4747
+ class: "zcodepro-tab",
4748
+ "aria-selected": String(stylesSubTab === id),
4749
+ "data-state": stylesSubTab === id ? "active" : "inactive",
4750
+ onClick: () => {
4751
+ stylesSubTab = id;
4752
+ paneStylesSub1.style.display = id === "styles" ? "" : "none";
4753
+ paneStylesSub2.style.display = id === "status" ? "" : "none";
4754
+ renderStylesSubTabs();
4755
+ }
4756
+ }, label)));
4757
+ renderStylesSubTabs();
4758
+ paneStyles.append(stylesSubTablist, paneStylesSub1, paneStylesSub2);
4759
+ paneStylesSub1.append(
4437
4760
  toolbarSwitchWrap,
4438
4761
  h(
4439
4762
  "div",
@@ -5431,6 +5754,11 @@
5431
5754
  h(
5432
5755
  "div",
5433
5756
  { class: "ml-auto flex items-center gap-4" },
5757
+ settingRow(L.visionCardUi, "", visionCfg.cardUi !== false, async () => {
5758
+ visionCfg.cardUi = !(visionCfg.cardUi !== false);
5759
+ await persistVision();
5760
+ renderVision();
5761
+ }),
5434
5762
  settingRow(L.visionForceIntercept, "", visionCfg.forceIntercept !== false, async () => {
5435
5763
  visionCfg.forceIntercept = !(visionCfg.forceIntercept !== false);
5436
5764
  await persistVision();
@@ -6506,11 +6834,11 @@
6506
6834
  const rootEl = document.getElementById("root");
6507
6835
  const containerKey = rootEl && Object.keys(rootEl).find((k) => k.startsWith("__reactContainer$"));
6508
6836
  if (!containerKey) return null;
6509
- const seen = /* @__PURE__ */ new Set();
6837
+ const seen2 = /* @__PURE__ */ new Set();
6510
6838
  let found = null;
6511
6839
  const scanObj = (o) => {
6512
- if (!o || typeof o !== "object" || seen.has(o) || found) return;
6513
- seen.add(o);
6840
+ if (!o || typeof o !== "object" || seen2.has(o) || found) return;
6841
+ seen2.add(o);
6514
6842
  const ts = o.zcodeTaskService;
6515
6843
  if (ts && typeof ts === "object" && typeof ts.releaseWorkspacePreparation === "function") found = o;
6516
6844
  };
@@ -6649,22 +6977,22 @@
6649
6977
  }, 0);
6650
6978
  }
6651
6979
  function apply() {
6652
- const seen = /* @__PURE__ */ new Set();
6980
+ const seen2 = /* @__PURE__ */ new Set();
6653
6981
  for (const row of document.querySelectorAll(WS_SEL())) {
6654
6982
  const path = rowPath(row);
6655
6983
  if (!path) continue;
6656
- seen.add(path);
6984
+ seen2.add(path);
6657
6985
  const head = wsHead(row);
6658
6986
  const collapsed = !head || head.getAttribute("aria-expanded") === "false";
6659
6987
  const domRunning = !!(runningByWs.get(path) && runningByWs.get(path).size);
6660
6988
  const normP = (v) => String(v || "").replace(/[\\/]+$/, "");
6661
6989
  const running = enabled2 && collapsed && (activeProjectPaths.size ? [...activeProjectPaths].some((p2) => normP(p2) === normP(path)) : domRunning);
6662
- const icon = iconHolderOf(row);
6663
- icon?.classList.toggle("zcodepro-ws-running", running);
6664
- setRunningIcon(icon, running);
6990
+ const icon2 = iconHolderOf(row);
6991
+ icon2?.classList.toggle("zcodepro-ws-running", running);
6992
+ setRunningIcon(icon2, running);
6665
6993
  }
6666
6994
  applySectionSpin();
6667
- for (const ws of [...runningByWs.keys()]) if (!seen.has(ws)) runningByWs.delete(ws);
6995
+ for (const ws of [...runningByWs.keys()]) if (!seen2.has(ws)) runningByWs.delete(ws);
6668
6996
  }
6669
6997
  var headInfoCache = /* @__PURE__ */ new WeakMap();
6670
6998
  var PROJECT_HEAD_TEXTS = /* @__PURE__ */ new Set(["\u9879\u76EE", "projects"]);
@@ -7466,20 +7794,20 @@
7466
7794
  }
7467
7795
  function findBridge() {
7468
7796
  let holder = null;
7469
- const seen = /* @__PURE__ */ new Set();
7470
- const scan4 = (o) => {
7471
- if (!o || typeof o !== "object" || seen.has(o) || holder) return;
7472
- seen.add(o);
7797
+ const seen2 = /* @__PURE__ */ new Set();
7798
+ const scan5 = (o) => {
7799
+ if (!o || typeof o !== "object" || seen2.has(o) || holder) return;
7800
+ seen2.add(o);
7473
7801
  if (typeof o.pluginUiBridgeService === "object" && o.pluginUiBridgeService) {
7474
7802
  holder = o.pluginUiBridgeService;
7475
7803
  return;
7476
7804
  }
7477
- for (const v of Object.values(o)) if (v && typeof v === "object") scan4(v);
7805
+ for (const v of Object.values(o)) if (v && typeof v === "object") scan5(v);
7478
7806
  };
7479
7807
  eachFiber((f) => {
7480
7808
  for (const h2 of [f.memoizedProps, f.memoizedState]) {
7481
7809
  if (!h2 || typeof h2 !== "object") continue;
7482
- scan4(h2);
7810
+ scan5(h2);
7483
7811
  if (holder) return true;
7484
7812
  }
7485
7813
  return false;
@@ -7615,6 +7943,423 @@
7615
7943
  setInterval(tryInject, 5e3);
7616
7944
  }
7617
7945
 
7946
+ // src/inject/features/status-bar.js
7947
+ var enabled4 = true;
7948
+ var items = DEFAULT_STATUS_ITEMS.map((x) => ({ ...x }));
7949
+ var barEl = null;
7950
+ var lastStats = null;
7951
+ function readCtx() {
7952
+ for (const el of document.querySelectorAll("[title], [aria-label]")) {
7953
+ if (el.closest(".zcodepro-statusbar")) continue;
7954
+ const text = el.getAttribute("title") || el.getAttribute("aria-label") || "";
7955
+ if (!/上下文|context/i.test(text)) continue;
7956
+ const m = text.match(/([\d][\d,]*)\s*\/[^0-9]*([\d][\d,]*)/);
7957
+ if (!m) continue;
7958
+ const used = Number(m[1].replace(/,/g, ""));
7959
+ const size = Number(m[2].replace(/,/g, ""));
7960
+ if (used > 0 && size > used) return { used, size };
7961
+ }
7962
+ return null;
7963
+ }
7964
+ var usageSvc = null;
7965
+ var official = null;
7966
+ var officialAt = 0;
7967
+ function eachFiber2(visit) {
7968
+ const rootEl = document.getElementById("root");
7969
+ const key = rootEl && Object.keys(rootEl).find((k) => k.startsWith("__reactContainer$"));
7970
+ if (!key) return;
7971
+ let visited = 0;
7972
+ const walk = (f) => {
7973
+ if (!f || visited > 2e5) return false;
7974
+ visited++;
7975
+ if (visit(f)) return true;
7976
+ return walk(f.child) || walk(f.sibling);
7977
+ };
7978
+ walk(rootEl[key]);
7979
+ }
7980
+ function findUsageService() {
7981
+ let found = null;
7982
+ const seen2 = /* @__PURE__ */ new Set();
7983
+ const scan5 = (o) => {
7984
+ if (!o || typeof o !== "object" || seen2.has(o) || found) return;
7985
+ seen2.add(o);
7986
+ if (typeof o.usageStatsService === "object" && o.usageStatsService) {
7987
+ found = o.usageStatsService;
7988
+ return;
7989
+ }
7990
+ for (const v of Object.values(o)) if (v && typeof v === "object") scan5(v);
7991
+ };
7992
+ eachFiber2((f) => {
7993
+ for (const h2 of [f.memoizedProps, f.memoizedState]) {
7994
+ if (!h2 || typeof h2 !== "object") continue;
7995
+ scan5(h2);
7996
+ if (found) return true;
7997
+ }
7998
+ return false;
7999
+ });
8000
+ return found;
8001
+ }
8002
+ function refreshOfficial() {
8003
+ const period = official ? 5 * 6e4 : 3e4;
8004
+ if (Date.now() - officialAt < period) return;
8005
+ officialAt = Date.now();
8006
+ void (async () => {
8007
+ try {
8008
+ if (!usageSvc) usageSvc = findUsageService();
8009
+ if (!usageSvc || typeof usageSvc.getAppUsageSnapshot !== "function") return;
8010
+ const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
8011
+ const [all, d7] = await Promise.all([
8012
+ usageSvc.getAppUsageSnapshot({ range: "all", timeZone }),
8013
+ usageSvc.getAppUsageSnapshot({ range: "7d", timeZone })
8014
+ ]);
8015
+ const now = /* @__PURE__ */ new Date();
8016
+ const key = now.getFullYear() + "-" + String(now.getMonth() + 1).padStart(2, "0") + "-" + String(now.getDate()).padStart(2, "0");
8017
+ let todayTokens = 0;
8018
+ const day = (d7 && Array.isArray(d7.dailyModelUsage) ? d7.dailyModelUsage : []).find((d) => d.date === key);
8019
+ if (day) for (const m of day.models || []) todayTokens += Number(m.totalTokens) || 0;
8020
+ official = {
8021
+ todayTokens,
8022
+ totalTokens: Number(all && all.summary && all.summary.totalTokens) || 0,
8023
+ totalSessions: Number(all && all.summary && all.summary.totalSessions) || 0
8024
+ };
8025
+ renderBar();
8026
+ } catch {
8027
+ usageSvc = null;
8028
+ }
8029
+ })();
8030
+ }
8031
+ var GLOBAL_GROUP = /* @__PURE__ */ new Set(["todayTokens", "totalTokens", "tasks"]);
8032
+ var SB_ICONS = {
8033
+ ctx: '<path d="M21.21 15.89A10 10 0 1 1 8 2.83"/><path d="M22 12A10 10 0 0 0 12 2v10z"/>',
8034
+ asks: '<path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"/>',
8035
+ today: '<circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/>',
8036
+ total: '<ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5V19A9 3 0 0 0 21 19V5"/><path d="M3 12A9 3 0 0 0 21 12"/>',
8037
+ tasks: '<rect x="3" y="5" width="6" height="6" rx="1"/><path d="m3 17 2 2 4-4"/><path d="M13 6h8"/><path d="M13 12h8"/><path d="M13 18h8"/>',
8038
+ coins: '<circle cx="8" cy="8" r="6"/><path d="M18.09 10.37A6 6 0 1 1 10.34 18"/><path d="M7 6h1v4"/><path d="m16.71 13.88.7.71-2.82 2.82"/>'
8039
+ };
8040
+ var sbIcon = (name) => `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="zcodepro-sb-icon">${SB_ICONS[name]}</svg>`;
8041
+ function fmtTokens(n) {
8042
+ if (n >= 1e9) return (n / 1e9).toFixed(1) + "B";
8043
+ if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
8044
+ if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
8045
+ return String(n);
8046
+ }
8047
+ function fmtNum(n) {
8048
+ if (!isZhLocale()) return fmtTokens(n);
8049
+ if (n < 1e4) return String(n);
8050
+ if (n >= 1e8) {
8051
+ const yi = Math.round(n / 1e8 * 10) / 10;
8052
+ return (yi % 1 === 0 ? String(yi) : yi.toFixed(1)) + "\u4EBF";
8053
+ }
8054
+ const wan = Math.round(n / 1e4 * 10) / 10;
8055
+ return (wan % 1 === 0 ? String(wan) : wan.toFixed(1)) + "\u4E07";
8056
+ }
8057
+ function itemContent(id, L) {
8058
+ const g = lastStats && lastStats.global;
8059
+ const label = (text) => `<em>${text}</em>`;
8060
+ switch (id) {
8061
+ case "ctx": {
8062
+ const dom = readCtx();
8063
+ if (dom) {
8064
+ const pct = Math.round(dom.used / dom.size * 1e3) / 10;
8065
+ const tone = pct >= 95 ? "danger" : pct >= 80 ? "warn" : "";
8066
+ return {
8067
+ html: sbIcon("ctx") + label(L.sbLabelCtx) + `<b>${fmtNum(dom.used)}/${fmtNum(dom.size)}${isZhLocale() ? "\uFF08" : "("}${pct}%${isZhLocale() ? "\uFF09" : ")"}</b>`,
8068
+ title: `${L.sbItemCtx}\uFF1A${dom.used.toLocaleString()} / ${dom.size.toLocaleString()}`,
8069
+ tone
8070
+ };
8071
+ }
8072
+ return null;
8073
+ }
8074
+ case "questions": {
8075
+ const nav = document.querySelector('[data-testid="v4-turn-navigator"]');
8076
+ const n = nav ? Number(nav.getAttribute("data-item-count")) : 0;
8077
+ return n > 0 ? { html: sbIcon("asks") + label(L.sbLabelQuestions) + String(n), title: L.sbItemQuestions } : null;
8078
+ }
8079
+ case "todayTokens": {
8080
+ const n = official ? official.todayTokens : g ? g.todayTokens : 0;
8081
+ return n > 0 ? { html: sbIcon("today") + label(L.sbLabelToday) + fmtNum(n), title: L.sbItemTodayTokens } : null;
8082
+ }
8083
+ case "totalTokens": {
8084
+ const n = official ? official.totalTokens : g ? g.totalTokens : 0;
8085
+ return n > 0 ? { html: sbIcon("total") + label(L.sbLabelTotal) + fmtNum(n), title: L.sbItemTotalTokens } : null;
8086
+ }
8087
+ case "tasks": {
8088
+ const n = official ? official.totalSessions : g ? g.tasks : 0;
8089
+ return n > 0 ? { html: sbIcon("tasks") + label(L.sbLabelTasks) + String(n), title: L.sbItemTasks } : null;
8090
+ }
8091
+ default:
8092
+ return null;
8093
+ }
8094
+ }
8095
+ function renderBar() {
8096
+ const ta = document.querySelector("textarea, [contenteditable=true]");
8097
+ const host = ta && (ta.closest("form") || ta.parentElement);
8098
+ if (!enabled4 || !host || !host.parentElement) {
8099
+ if (barEl) barEl.remove();
8100
+ barEl = null;
8101
+ return;
8102
+ }
8103
+ if (!barEl || !barEl.isConnected) {
8104
+ if (!barEl) {
8105
+ barEl = document.createElement("div");
8106
+ barEl.className = "zcodepro-statusbar";
8107
+ }
8108
+ host.parentElement.insertBefore(barEl, host.nextSibling);
8109
+ }
8110
+ const hasSession = !![...document.querySelectorAll("[data-task-item-key]")].find((e) => (e.className + "").includes("bg-selected"));
8111
+ barEl.style.marginBottom = hasSession ? "-14px" : "0";
8112
+ barEl.style.paddingBottom = hasSession ? "0" : "7px";
8113
+ const L = t();
8114
+ const parts = [];
8115
+ let lastGroup = "";
8116
+ for (const it of items) {
8117
+ if (!it.on) continue;
8118
+ const c = itemContent(it.id, L);
8119
+ if (!c) continue;
8120
+ const group = GLOBAL_GROUP.has(it.id) ? "g" : "s";
8121
+ if (lastGroup && group !== lastGroup) parts.push('<span class="zcodepro-statusbar-groupsep"></span>');
8122
+ lastGroup = group;
8123
+ parts.push(`<span title="${c.title.replace(/"/g, "&quot;")}"${c.tone ? ` data-tone="${c.tone}"` : ""}>${c.html}</span>`);
8124
+ }
8125
+ const SEP = '<span class="zcodepro-statusbar-sep">\xB7</span>';
8126
+ const GROUPSEP = '<span class="zcodepro-statusbar-groupsep"></span>';
8127
+ barEl.innerHTML = parts.reduce((acc, p, i) => {
8128
+ if (i === 0) return p;
8129
+ if (p === GROUPSEP || parts[i - 1] === GROUPSEP) return acc + p;
8130
+ return acc + SEP + p;
8131
+ }, "");
8132
+ barEl.style.display = parts.length ? "" : "none";
8133
+ }
8134
+ async function tick() {
8135
+ if (!enabled4 || document.hidden) return;
8136
+ refreshOfficial();
8137
+ const res = await rpc("/stats/usage").catch(() => null);
8138
+ if (res && res.ok) lastStats = res;
8139
+ renderBar();
8140
+ }
8141
+ function startStatusBar() {
8142
+ ensureStyle();
8143
+ const refreshCfg = () => {
8144
+ void getConfig().then((cfg) => {
8145
+ enabled4 = cfg.features ? cfg.features.statusBar !== false : true;
8146
+ const saved = cfg.statusBar && Array.isArray(cfg.statusBar.items) ? cfg.statusBar.items : null;
8147
+ if (saved) {
8148
+ const byId = new Map(saved.map((x) => [x.id, x]));
8149
+ const list = saved.filter((x) => DEFAULT_STATUS_ITEMS.some((d) => d.id === x.id)).map((x) => ({ ...x }));
8150
+ const known = new Set(list.map((x) => x.id));
8151
+ for (const d of DEFAULT_STATUS_ITEMS) {
8152
+ if (!known.has(d.id)) list.push({ ...d });
8153
+ }
8154
+ items = [
8155
+ ...list.filter((x) => !GLOBAL_GROUP.has(x.id)),
8156
+ ...list.filter((x) => GLOBAL_GROUP.has(x.id))
8157
+ ];
8158
+ } else {
8159
+ items = DEFAULT_STATUS_ITEMS.map((x) => ({ ...x }));
8160
+ }
8161
+ renderBar();
8162
+ }).catch(() => {
8163
+ });
8164
+ };
8165
+ refreshCfg();
8166
+ window.addEventListener("zcodepro:config-changed", refreshCfg);
8167
+ document.addEventListener("visibilitychange", () => {
8168
+ if (!document.hidden) void tick();
8169
+ });
8170
+ setInterval(() => {
8171
+ void tick();
8172
+ }, 2e3);
8173
+ let rerenderTimer = 0;
8174
+ new MutationObserver(() => {
8175
+ if (rerenderTimer) return;
8176
+ rerenderTimer = setTimeout(() => {
8177
+ rerenderTimer = 0;
8178
+ renderBar();
8179
+ }, 300);
8180
+ }).observe(document.body, { childList: true, subtree: true });
8181
+ }
8182
+
8183
+ // src/inject/features/turn-usage.js
8184
+ var enabled5 = true;
8185
+ var recentTurns = [];
8186
+ var scanTimer = 0;
8187
+ var turnCfg = [
8188
+ { id: "turnIn", on: true },
8189
+ { id: "turnOut", on: true },
8190
+ { id: "turnCache", on: true }
8191
+ ];
8192
+ var refreshTurnItems = () => {
8193
+ void getConfig().then((cfg) => {
8194
+ const saved = cfg.statusBar && Array.isArray(cfg.statusBar.turnItems) ? cfg.statusBar.turnItems : null;
8195
+ if (!saved) return;
8196
+ const known = new Set(turnCfg.map((x) => x.id));
8197
+ const list = saved.filter((x) => x && known.has(x.id)).map((x) => ({ id: x.id, on: x.on !== false }));
8198
+ for (const d of turnCfg) if (!list.some((x) => x.id === d.id)) list.push({ ...d });
8199
+ turnCfg = list;
8200
+ }).catch(() => {
8201
+ });
8202
+ };
8203
+ var ICONS = {
8204
+ up: '<path d="m5 12 7-7 7 7"/><path d="M12 19V5"/>',
8205
+ down: '<path d="M12 5v14"/><path d="m19 12-7 7-7-7"/>',
8206
+ cache: '<path d="m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z"/><path d="m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65"/><path d="m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65"/>'
8207
+ };
8208
+ var icon = (name) => `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:11px;height:11px;vertical-align:-1.5px;opacity:.6">${ICONS[name]}</svg>`;
8209
+ function fmt(n) {
8210
+ if (!isZhLocale()) {
8211
+ if (n >= 1e9) return (n / 1e9).toFixed(1) + "B";
8212
+ if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
8213
+ if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
8214
+ return String(n);
8215
+ }
8216
+ if (n >= 1e8) return (n / 1e8).toFixed(1) + "\u4EBF";
8217
+ if (n >= 1e4) {
8218
+ const wan = Math.round(n / 1e4 * 10) / 10;
8219
+ return (wan % 1 === 0 ? String(wan) : wan.toFixed(1)) + "\u4E07";
8220
+ }
8221
+ return String(n);
8222
+ }
8223
+ function findRows() {
8224
+ const rows = [];
8225
+ for (const btn of document.querySelectorAll('[data-testid^="v4-copy-"]')) {
8226
+ const row = btn.parentElement;
8227
+ if (!row || row.dataset.zcodeproTurn) continue;
8228
+ if (!row.querySelector('[data-testid^="v4-feedback-"]')) continue;
8229
+ const timeEl = [...row.querySelectorAll("span")].find((s) => /\d{1,2}:\d{2}$/.test((s.textContent || "").trim()));
8230
+ if (timeEl) {
8231
+ const ts = rowTimeTs((timeEl.textContent || "").trim());
8232
+ if (ts != null) rows.push({ row, ts });
8233
+ }
8234
+ }
8235
+ return rows;
8236
+ }
8237
+ function currentSessionId() {
8238
+ const rows = [...document.querySelectorAll("[data-task-item-key]")];
8239
+ const cur = rows.find((e) => (e.className + "").includes("bg-selected")) || rows[rows.length - 1];
8240
+ const key = cur ? cur.getAttribute("data-task-item-key") : "";
8241
+ const i = key.lastIndexOf(":sess");
8242
+ return i > 0 ? key.slice(i + 1) : "";
8243
+ }
8244
+ function injectRow(row, bucket) {
8245
+ row.dataset.zcodeproTurn = "1";
8246
+ if (!bucket || bucket.in + bucket.out <= 0) return;
8247
+ const L = t();
8248
+ const el = document.createElement("span");
8249
+ el.dataset.zcodeproTurnSpan = "1";
8250
+ el.className = "select-none text-ui-sm text-foreground-subtlest";
8251
+ el.style.whiteSpace = "nowrap";
8252
+ el.style.display = "inline-flex";
8253
+ el.style.alignItems = "center";
8254
+ el.style.gap = "4px";
8255
+ el.style.marginLeft = "10px";
8256
+ const cachePct = bucket.in > 0 && bucket.cacheRead > 0 ? (bucket.cacheRead / bucket.in * 100).toFixed(2) : "";
8257
+ const group = (html) => `<span style="display:inline-flex;align-items:center;gap:2px">${html}</span>`;
8258
+ const em = (text) => `<em style="font-style:normal;opacity:.75">${text}</em>`;
8259
+ const DOT = '<span style="opacity:.45">\xB7</span>';
8260
+ const sections = [];
8261
+ for (const s of turnCfg) {
8262
+ if (!s.on) continue;
8263
+ if (s.id === "turnIn") sections.push(group(icon("up") + em(L.sbLabelIn) + fmt(bucket.in)));
8264
+ else if (s.id === "turnOut") sections.push(group(icon("down") + em(L.sbLabelOut) + fmt(bucket.out)));
8265
+ else if (s.id === "turnCache" && cachePct) sections.push(group(icon("cache") + em(L.sbLabelCache) + cachePct + "%"));
8266
+ }
8267
+ if (!sections.length) {
8268
+ delete row.dataset.zcodeproTurn;
8269
+ el.remove();
8270
+ return;
8271
+ }
8272
+ const VBAR = '<span style="display:inline-block;width:1px;height:10px;background:currentColor;opacity:.3;margin:0 5px"></span>';
8273
+ el.innerHTML = VBAR + sections.join(DOT);
8274
+ el.title = `${L.turnUsageTitle}\uFF1A${L.sbLabelIn} ${bucket.in.toLocaleString()} / ${L.sbLabelOut} ${bucket.out.toLocaleString()}` + (bucket.cacheRead > 0 ? ` / ${L.sbLabelCache} ${bucket.cacheRead.toLocaleString()}` : "");
8275
+ row.append(el);
8276
+ }
8277
+ function rowTimeTs(text) {
8278
+ const now = /* @__PURE__ */ new Date();
8279
+ let m = text.match(/^(\d{1,2}):(\d{2})$/);
8280
+ if (m) return new Date(now.getFullYear(), now.getMonth(), now.getDate(), +m[1], +m[2]).getTime();
8281
+ m = text.match(/^昨天\s*(\d{1,2}):(\d{2})$/);
8282
+ if (m) {
8283
+ const y = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
8284
+ return new Date(y.getFullYear(), y.getMonth(), y.getDate(), +m[1], +m[2]).getTime();
8285
+ }
8286
+ m = text.match(/^(\d{1,2})\/(\d{1,2})\s*(\d{1,2}):(\d{2})$/);
8287
+ if (m) return new Date(now.getFullYear(), +m[1] - 1, +m[2], +m[3], +m[4]).getTime();
8288
+ return null;
8289
+ }
8290
+ var lastSid = "";
8291
+ function scan3() {
8292
+ if (!enabled5) return;
8293
+ const sid = currentSessionId();
8294
+ if (sid !== lastSid) {
8295
+ lastSid = sid;
8296
+ recentTurns = [];
8297
+ void tick2();
8298
+ }
8299
+ for (const el of document.querySelectorAll('[data-zcodepro-turn-span], span[title^="\u672C\u8F6E"], span[title^="Turn"]')) el.remove();
8300
+ for (const row of document.querySelectorAll("[data-zcodepro-turn]")) delete row.dataset.zcodeproTurn;
8301
+ if (document.hidden || !recentTurns.length) return;
8302
+ const rows = findRows().sort((a, b) => a.ts - b.ts);
8303
+ if (!rows.length) return;
8304
+ const agg = rows.map(() => ({ in: 0, out: 0, cacheRead: 0 }));
8305
+ const dayOf = (t2) => {
8306
+ const d = new Date(t2);
8307
+ return d.getFullYear() * 1e4 + (d.getMonth() + 1) * 100 + d.getDate();
8308
+ };
8309
+ for (const b of recentTurns) {
8310
+ let owner = -1;
8311
+ for (let i = 0; i < rows.length; i++) {
8312
+ if (rows[i].ts <= b.ts) owner = i;
8313
+ else break;
8314
+ }
8315
+ if (owner < 0) continue;
8316
+ if (dayOf(rows[owner].ts) !== dayOf(b.ts)) continue;
8317
+ agg[owner].in += b.in;
8318
+ agg[owner].out += b.out;
8319
+ agg[owner].cacheRead += b.cacheRead;
8320
+ }
8321
+ for (let i = 0; i < rows.length; i++) {
8322
+ if (agg[i].in + agg[i].out > 0) injectRow(rows[i].row, agg[i]);
8323
+ }
8324
+ }
8325
+ async function tick2() {
8326
+ if (!enabled5 || document.hidden) return;
8327
+ const sid = currentSessionId();
8328
+ const res = await rpc("/stats/usage" + (sid ? "?session=" + encodeURIComponent(sid) : "")).catch(() => null);
8329
+ if (res && res.ok && Array.isArray(res.recentTurns)) {
8330
+ recentTurns = res.recentTurns;
8331
+ scan3();
8332
+ }
8333
+ }
8334
+ function startTurnUsage() {
8335
+ void getConfig().then((cfg) => {
8336
+ enabled5 = cfg.features ? cfg.features.statusBar !== false : true;
8337
+ }).catch(() => {
8338
+ });
8339
+ refreshTurnItems();
8340
+ window.addEventListener("zcodepro:config-changed", () => {
8341
+ void getConfig().then((cfg) => {
8342
+ enabled5 = cfg.features ? cfg.features.statusBar !== false : true;
8343
+ }).catch(() => {
8344
+ });
8345
+ refreshTurnItems();
8346
+ });
8347
+ const observer2 = new MutationObserver(() => {
8348
+ if (scanTimer) return;
8349
+ scanTimer = setTimeout(() => {
8350
+ scanTimer = 0;
8351
+ scan3();
8352
+ }, 1200);
8353
+ });
8354
+ observer2.observe(document.body, { childList: true, subtree: true });
8355
+ setInterval(() => {
8356
+ void tick2();
8357
+ }, 5e3);
8358
+ document.addEventListener("visibilitychange", () => {
8359
+ if (!document.hidden) void tick2();
8360
+ });
8361
+ }
8362
+
7618
8363
  // src/inject/features/title-lock.js
7619
8364
  var TASK_SEL2 = "[data-task-item-key]";
7620
8365
  var SCAN_DEBOUNCE_MS3 = 150;
@@ -7625,7 +8370,7 @@
7625
8370
  var MAP_REFRESH_MS = 6e4;
7626
8371
  var installed3 = false;
7627
8372
  var seenRows = /* @__PURE__ */ new WeakSet();
7628
- var enabled4 = true;
8373
+ var enabled6 = true;
7629
8374
  var pinned = null;
7630
8375
  var resolved = false;
7631
8376
  var fetching = null;
@@ -7640,7 +8385,7 @@
7640
8385
  const t2 = item.querySelector('[class*="truncate"]');
7641
8386
  return t2 ? [t2] : [];
7642
8387
  }
7643
- function findRows(key) {
8388
+ function findRows2(key) {
7644
8389
  return [...document.querySelectorAll(`${TASK_SEL2}[data-task-item-key="${CSS.escape(key)}"]`)];
7645
8390
  }
7646
8391
  async function fetchPinned() {
@@ -7649,8 +8394,8 @@
7649
8394
  const res = await rpc("/pinned-titles");
7650
8395
  if (!res || !res.ok) return null;
7651
8396
  resolved = true;
7652
- enabled4 = res.enabled !== false;
7653
- if (!enabled4) {
8397
+ enabled6 = res.enabled !== false;
8398
+ if (!enabled6) {
7654
8399
  pinned = null;
7655
8400
  return null;
7656
8401
  }
@@ -7665,7 +8410,7 @@
7665
8410
  }
7666
8411
  }
7667
8412
  function isMismatch(item) {
7668
- if (!enabled4 || !pinned) return null;
8413
+ if (!enabled6 || !pinned) return null;
7669
8414
  const key = item.getAttribute("data-task-item-key") || "";
7670
8415
  const want = pinned.get(sessIdOf(key));
7671
8416
  if (want === void 0) return null;
@@ -7683,10 +8428,10 @@
7683
8428
  }
7684
8429
  function scheduleEnforce(key, { immediate = false } = {}) {
7685
8430
  if (immediate) {
7686
- if (enabled4 && pinned) {
8431
+ if (enabled6 && pinned) {
7687
8432
  const want = pinned.get(sessIdOf(key));
7688
8433
  if (want !== void 0) {
7689
- for (const item of findRows(key)) {
8434
+ for (const item of findRows2(key)) {
7690
8435
  for (const el of titleElsOf(item)) writeTitle(el, want);
7691
8436
  }
7692
8437
  }
@@ -7702,7 +8447,7 @@
7702
8447
  const want = map.get(sessIdOf(key));
7703
8448
  if (want === void 0) return;
7704
8449
  let wrote = false;
7705
- for (const item of findRows(key)) {
8450
+ for (const item of findRows2(key)) {
7706
8451
  if (isMismatch(item) !== key) continue;
7707
8452
  for (const el of titleElsOf(item)) writeTitle(el, want);
7708
8453
  wrote = true;
@@ -7718,13 +8463,13 @@
7718
8463
  if (!map) return;
7719
8464
  const want = map.get(sessIdOf(key));
7720
8465
  if (want === void 0) return;
7721
- for (const item of findRows(key)) {
8466
+ for (const item of findRows2(key)) {
7722
8467
  if (isMismatch(item) !== key) continue;
7723
8468
  for (const el of titleElsOf(item)) writeTitle(el, want);
7724
8469
  }
7725
8470
  }
7726
- function scan3() {
7727
- if (!enabled4 || !pinned || pinned.size === 0) return;
8471
+ function scan4() {
8472
+ if (!enabled6 || !pinned || pinned.size === 0) return;
7728
8473
  for (const item of document.querySelectorAll(TASK_SEL2)) {
7729
8474
  const key = isMismatch(item);
7730
8475
  if (!key) {
@@ -7758,23 +8503,23 @@
7758
8503
  if (scanDebounce3) return;
7759
8504
  scanDebounce3 = setTimeout(() => {
7760
8505
  scanDebounce3 = 0;
7761
- scan3();
8506
+ scan4();
7762
8507
  }, SCAN_DEBOUNCE_MS3);
7763
8508
  }
7764
8509
  var mapRefreshTimer = 0;
7765
8510
  function refreshMapSoon() {
7766
8511
  if (mapRefreshTimer) return;
7767
- const tick2 = () => {
8512
+ const tick6 = () => {
7768
8513
  if (document.hidden) {
7769
- mapRefreshTimer = setTimeout(tick2, 1e4);
8514
+ mapRefreshTimer = setTimeout(tick6, 1e4);
7770
8515
  return;
7771
8516
  }
7772
8517
  mapRefreshTimer = 0;
7773
- if (enabled4 && resolved) void fetchPinned().then((m) => {
7774
- if (m) scan3();
8518
+ if (enabled6 && resolved) void fetchPinned().then((m) => {
8519
+ if (m) scan4();
7775
8520
  });
7776
8521
  };
7777
- mapRefreshTimer = setTimeout(tick2, MAP_REFRESH_MS);
8522
+ mapRefreshTimer = setTimeout(tick6, MAP_REFRESH_MS);
7778
8523
  }
7779
8524
  var editingKey = null;
7780
8525
  var bootDelay = 0;
@@ -7788,16 +8533,16 @@
7788
8533
  }
7789
8534
  async function bootstrap() {
7790
8535
  const map = await fetchPinned();
7791
- if (map) scan3();
8536
+ if (map) scan4();
7792
8537
  }
7793
8538
  function setRowTitle(key, text) {
7794
- for (const item of findRows(key)) {
8539
+ for (const item of findRows2(key)) {
7795
8540
  for (const el of titleElsOf(item)) writeTitle(el, text);
7796
8541
  }
7797
8542
  }
7798
8543
  function refreshPinnedTitles() {
7799
8544
  return fetchPinned().then((m) => {
7800
- if (m) scan3();
8545
+ if (m) scan4();
7801
8546
  });
7802
8547
  }
7803
8548
  var RENAME_CTX_VALID_MS = 15e3;
@@ -7816,7 +8561,7 @@
7816
8561
  renameCtx = null;
7817
8562
  return;
7818
8563
  }
7819
- if (!enabled4 || !pinned) return;
8564
+ if (!enabled6 || !pinned) return;
7820
8565
  const want = pinned.get(sessIdOf(renameCtx.key));
7821
8566
  if (want === void 0) return;
7822
8567
  for (const el of document.querySelectorAll('[role="dialog"] input, [role="dialog"] textarea, [role="dialog"] [contenteditable="true"]')) {
@@ -7854,10 +8599,10 @@
7854
8599
  }, true);
7855
8600
  scheduleBootRetry();
7856
8601
  setInterval(() => {
7857
- if (!document.hidden) scan3();
8602
+ if (!document.hidden) scan4();
7858
8603
  }, SCAN_FALLBACK_MS3);
7859
8604
  document.addEventListener("visibilitychange", () => {
7860
- if (!document.hidden) scan3();
8605
+ if (!document.hidden) scan4();
7861
8606
  });
7862
8607
  window.addEventListener("zcodepro:config-changed", () => {
7863
8608
  void bootstrap();
@@ -7873,7 +8618,7 @@
7873
8618
  if (editingKey === null) return;
7874
8619
  editingKey = null;
7875
8620
  void fetchPinned().then((m) => {
7876
- if (m) scan3();
8621
+ if (m) scan4();
7877
8622
  });
7878
8623
  });
7879
8624
  }
@@ -8104,7 +8849,7 @@
8104
8849
  await reclaim(ws);
8105
8850
  blockUntil.set(ws, Date.now() + cooldownMs);
8106
8851
  }
8107
- async function tick() {
8852
+ async function tick3() {
8108
8853
  const cfg = await getConfig();
8109
8854
  if ((cfg.features || {}).idleReclaim === false) {
8110
8855
  idleSince.clear();
@@ -8166,20 +8911,171 @@
8166
8911
  }
8167
8912
  function startIdleReclaim() {
8168
8913
  const loop = () => {
8169
- tick().catch(() => {
8914
+ tick3().catch(() => {
8170
8915
  });
8171
8916
  };
8172
8917
  loop();
8173
8918
  setInterval(loop, CHECK_MS);
8174
8919
  }
8175
8920
 
8921
+ // src/inject/features/sandbox-reclaim.js
8922
+ var CHECK_MS2 = 3e4;
8923
+ var MIN_BUDGET = 8;
8924
+ var MAX_BUDGET = 64;
8925
+ var RETRY_COOLDOWN_MS = 10 * 6e4;
8926
+ var SEEN_KEY = "zcodepro:sbx-seen";
8927
+ var seen = /* @__PURE__ */ new Map();
8928
+ function loadSeen() {
8929
+ try {
8930
+ const raw = JSON.parse(localStorage.getItem(SEEN_KEY) || "{}");
8931
+ for (const [id, v] of Object.entries(raw)) {
8932
+ if (id && v && Number.isFinite(v.firstSeen)) seen.set(id, { initId: v.initId, firstSeen: v.firstSeen });
8933
+ }
8934
+ } catch {
8935
+ }
8936
+ }
8937
+ var saveSeenTimer = null;
8938
+ function saveSeenSoon() {
8939
+ if (saveSeenTimer) return;
8940
+ saveSeenTimer = setTimeout(() => {
8941
+ saveSeenTimer = null;
8942
+ try {
8943
+ const out = {};
8944
+ for (const [id, v] of seen) out[id] = v;
8945
+ localStorage.setItem(SEEN_KEY, JSON.stringify(out));
8946
+ } catch {
8947
+ }
8948
+ }, 2e3);
8949
+ }
8950
+ var cooldown = /* @__PURE__ */ new Map();
8951
+ var disabledLogged = false;
8952
+ function onPortsMessage(e) {
8953
+ const d = e.data;
8954
+ if (!d || d.type !== "zcode:plugin-sandbox-ports" || typeof d.sandboxId !== "string") return;
8955
+ seen.set(d.sandboxId, { initId: Number(d.initId) || 0, firstSeen: Date.now() });
8956
+ saveSeenSoon();
8957
+ }
8958
+ function extractSandboxId(wv) {
8959
+ const m = /instance\/(sb-[0-9a-f]+)\//.exec(wv.getAttribute("src") || "");
8960
+ return m ? m[1] : null;
8961
+ }
8962
+ function isVisible(wv) {
8963
+ const root = wv.closest("[data-sandbox-page-root]");
8964
+ if (!root) return wv.getClientRects().length > 0;
8965
+ if (!root.getClientRects().length) return false;
8966
+ return Number(getComputedStyle(root).opacity) > 0.5;
8967
+ }
8968
+ async function tick4() {
8969
+ const cfg = await getConfig();
8970
+ if ((cfg.features || {}).sandboxReclaim === false) {
8971
+ seen.clear();
8972
+ return;
8973
+ }
8974
+ const bridge = window.zcodePluginSandbox;
8975
+ if (!bridge || typeof bridge.disposeSandbox !== "function") {
8976
+ if (!disabledLogged) {
8977
+ disabledLogged = true;
8978
+ console.info("[zcodepro] sandbox-reclaim: \u5F53\u524D\u7A97\u53E3\u65E0 zcodePluginSandbox \u6865\uFF0C\u6C99\u7BB1\u56DE\u6536\u8DF3\u8FC7");
8979
+ }
8980
+ return;
8981
+ }
8982
+ let budget = Number(cfg.sandboxReclaimBudget);
8983
+ if (!Number.isFinite(budget)) budget = 60;
8984
+ budget = Math.min(MAX_BUDGET, Math.max(MIN_BUDGET, budget));
8985
+ const now = Date.now();
8986
+ const visible = /* @__PURE__ */ new Set();
8987
+ for (const wv of document.querySelectorAll("webview")) {
8988
+ const id = extractSandboxId(wv);
8989
+ if (id) {
8990
+ if (isVisible(wv)) visible.add(id);
8991
+ if (!seen.has(id)) {
8992
+ seen.set(id, { initId: void 0, firstSeen: now });
8993
+ saveSeenSoon();
8994
+ }
8995
+ }
8996
+ }
8997
+ while (seen.size > budget) {
8998
+ let pool = [...seen.keys()].filter((id2) => (cooldown.get(id2) || 0) <= now && !visible.has(id2));
8999
+ if (!pool.length) pool = [...seen.keys()].filter((id2) => (cooldown.get(id2) || 0) <= now);
9000
+ pool.sort((a, b) => ((seen.get(a) || {}).firstSeen || 0) - ((seen.get(b) || {}).firstSeen || 0));
9001
+ if (!pool.length) break;
9002
+ const id = pool[0];
9003
+ const entry = seen.get(id);
9004
+ try {
9005
+ await bridge.disposeSandbox(id, entry && entry.initId);
9006
+ console.info("[zcodepro] sandbox-reclaim: \u767B\u8BB0\u6570 " + seen.size + " \u8D85\u9884\u7B97 " + budget + "\uFF0C\u5DF2\u9A71\u9010\u6700\u65E9\u7684 " + id + (visible.has(id) ? "\uFF08\u53EF\u89C1\uFF0C\u5C5E\u515C\u5E95\uFF09" : ""));
9007
+ } catch (err) {
9008
+ console.warn("[zcodepro] sandbox-reclaim: \u91CA\u653E\u5931\u8D25 " + id, err);
9009
+ seen.delete(id);
9010
+ continue;
9011
+ }
9012
+ seen.delete(id);
9013
+ saveSeenSoon();
9014
+ cooldown.set(id, now + RETRY_COOLDOWN_MS);
9015
+ }
9016
+ if (cooldown.size > 256) {
9017
+ for (const [k, v] of cooldown) {
9018
+ if (v <= now) cooldown.delete(k);
9019
+ }
9020
+ }
9021
+ }
9022
+ function startSandboxReclaim() {
9023
+ loadSeen();
9024
+ window.addEventListener("message", onPortsMessage);
9025
+ const loop = () => {
9026
+ tick4().catch(() => {
9027
+ });
9028
+ };
9029
+ loop();
9030
+ setInterval(loop, CHECK_MS2);
9031
+ }
9032
+
9033
+ // src/inject/features/plugin-card-retry.js
9034
+ var SCAN_MS = 15e3;
9035
+ var CLICK_INTERVAL_MS = 3e4;
9036
+ var MARKERS = ["\u63D2\u4EF6\u754C\u9762"];
9037
+ var lastClickAt = 0;
9038
+ function failedRetryButtons() {
9039
+ const out = [];
9040
+ for (const btn of document.querySelectorAll("button")) {
9041
+ if ((btn.textContent || "").trim() !== "\u91CD\u65B0\u52A0\u8F7D") continue;
9042
+ let anc = btn;
9043
+ for (let i = 0; i < 4 && anc.parentElement; i++) {
9044
+ anc = anc.parentElement;
9045
+ const ctx = anc.textContent || "";
9046
+ if (MARKERS.some((m) => ctx.includes(m))) {
9047
+ out.push(btn);
9048
+ break;
9049
+ }
9050
+ }
9051
+ }
9052
+ return out;
9053
+ }
9054
+ async function tick5() {
9055
+ const cfg = await getConfig();
9056
+ if ((cfg.features || {}).pluginCardRetry === false) return;
9057
+ const now = Date.now();
9058
+ if (now - lastClickAt < CLICK_INTERVAL_MS) return;
9059
+ for (const btn of failedRetryButtons()) {
9060
+ lastClickAt = now;
9061
+ btn.click();
9062
+ break;
9063
+ }
9064
+ }
9065
+ function startPluginCardRetry() {
9066
+ setInterval(() => {
9067
+ tick5().catch(() => {
9068
+ });
9069
+ }, SCAN_MS);
9070
+ }
9071
+
8176
9072
  // src/inject/features/toolbar-icons.js
8177
9073
  var HIDE_CLASS = "zcodepro-tb-hidden";
8178
9074
  var SYNC_DEBOUNCE_MS = 150;
8179
9075
  var SCAN_FALLBACK_MS5 = 5e3;
8180
9076
  var ROW_SEL = "[data-task-item-key]";
8181
9077
  var MENU_SEL = '[data-testid="task-new-button"], [data-testid="automations-open"], [data-testid="plugin-store-sidebar-open"], [data-testid="sidebar-search-open"], [data-slot="command"], [data-testid="desktop-top-nav-back"]';
8182
- var enabled5 = true;
9078
+ var enabled7 = true;
8183
9079
  var rowEl = null;
8184
9080
  var origs = null;
8185
9081
  var syncTimer = 0;
@@ -8287,8 +9183,8 @@
8287
9183
  }
8288
9184
  });
8289
9185
  const newBtn = mk("newTask", parts.newTask, "span.truncate", L.tbNewTask);
8290
- const icon = iconOf(parts.newTask);
8291
- if (icon) newBtn.append(icon);
9186
+ const icon2 = iconOf(parts.newTask);
9187
+ if (icon2) newBtn.append(icon2);
8292
9188
  const searchBtn = mk("search", parts.search, "span.truncate", L.tbSearch);
8293
9189
  const sIcon = iconOf(parts.search);
8294
9190
  if (sIcon) searchBtn.append(sIcon);
@@ -8322,7 +9218,7 @@
8322
9218
  return scroll.getBoundingClientRect().width < NARROW_SIDEBAR_PX;
8323
9219
  }
8324
9220
  function sync() {
8325
- if (!enabled5) return;
9221
+ if (!enabled7) return;
8326
9222
  const parts = findParts();
8327
9223
  if (!parts) {
8328
9224
  if (rowEl) rowEl.style.display = "none";
@@ -8384,8 +9280,8 @@
8384
9280
  }
8385
9281
  async function refreshConfig2() {
8386
9282
  const cfg = await getConfig(true).catch(() => null);
8387
- enabled5 = !cfg || !cfg.features || cfg.features.toolbarIcons !== false;
8388
- if (enabled5) sync();
9283
+ enabled7 = !cfg || !cfg.features || cfg.features.toolbarIcons !== false;
9284
+ if (enabled7) sync();
8389
9285
  else teardown();
8390
9286
  }
8391
9287
  function startToolbarIcons() {
@@ -8411,7 +9307,7 @@
8411
9307
  var STORE_KEY = "zcodepro:pinned-collapsed";
8412
9308
  var SYNC_DEBOUNCE_MS2 = 300;
8413
9309
  var SCAN_FALLBACK_MS6 = 5e3;
8414
- var enabled6 = true;
9310
+ var enabled8 = true;
8415
9311
  var syncTimer2 = 0;
8416
9312
  function findPinnedHeader() {
8417
9313
  for (const h3 of document.querySelectorAll("h3")) {
@@ -8468,7 +9364,7 @@
8468
9364
  }
8469
9365
  h3.classList.add(HEAD_CLASS);
8470
9366
  h3.addEventListener("click", () => {
8471
- if (enabled6) toggle();
9367
+ if (enabled8) toggle();
8472
9368
  });
8473
9369
  const btn = h("button", {
8474
9370
  type: "button",
@@ -8477,7 +9373,7 @@
8477
9373
  "aria-expanded": collapsed ? "false" : "true",
8478
9374
  onClick: (e) => {
8479
9375
  e.stopPropagation();
8480
- if (enabled6) toggle();
9376
+ if (enabled8) toggle();
8481
9377
  }
8482
9378
  });
8483
9379
  btn.append(chevronSvg());
@@ -8485,7 +9381,7 @@
8485
9381
  return true;
8486
9382
  }
8487
9383
  function sync2() {
8488
- if (enabled6) apply2();
9384
+ if (enabled8) apply2();
8489
9385
  }
8490
9386
  function scheduleSync2() {
8491
9387
  if (syncTimer2) return;
@@ -8514,8 +9410,8 @@
8514
9410
  }
8515
9411
  async function refreshConfig3() {
8516
9412
  const cfg = await getConfig(true).catch(() => null);
8517
- enabled6 = !cfg || !cfg.features || cfg.features.pinnedCollapse !== false;
8518
- if (enabled6) sync2();
9413
+ enabled8 = !cfg || !cfg.features || cfg.features.pinnedCollapse !== false;
9414
+ if (enabled8) sync2();
8519
9415
  else teardown2();
8520
9416
  }
8521
9417
  function startPinnedCollapse() {
@@ -8550,12 +9446,12 @@
8550
9446
  return;
8551
9447
  }
8552
9448
  const sig = status.updates.map((u) => `${u.name}:${u.installed}>${u.latest}`).join(",");
8553
- let seen = "";
9449
+ let seen2 = "";
8554
9450
  try {
8555
- seen = window.localStorage.getItem(NOTIFIED_KEY) || "";
9451
+ seen2 = window.localStorage.getItem(NOTIFIED_KEY) || "";
8556
9452
  } catch {
8557
9453
  }
8558
- if (seen === sig) return;
9454
+ if (seen2 === sig) return;
8559
9455
  try {
8560
9456
  window.localStorage.setItem(NOTIFIED_KEY, sig);
8561
9457
  } catch {
@@ -8630,6 +9526,14 @@
8630
9526
  startIdleReclaim();
8631
9527
  } catch {
8632
9528
  }
9529
+ try {
9530
+ startSandboxReclaim();
9531
+ } catch {
9532
+ }
9533
+ try {
9534
+ startPluginCardRetry();
9535
+ } catch {
9536
+ }
8633
9537
  try {
8634
9538
  startToolbarIcons();
8635
9539
  } catch {
@@ -8638,6 +9542,14 @@
8638
9542
  startNewtabPanels();
8639
9543
  } catch {
8640
9544
  }
9545
+ try {
9546
+ startStatusBar();
9547
+ } catch {
9548
+ }
9549
+ try {
9550
+ startTurnUsage();
9551
+ } catch {
9552
+ }
8641
9553
  try {
8642
9554
  startPinnedCollapse();
8643
9555
  } catch {