zcode-pro 1.2.0 → 1.2.1

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,34 @@
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
+ featureStatusBar: "\u72B6\u6001\u680F\u7EDF\u8BA1",
290
+ 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",
291
+ tabStylesPane: "\u6837\u5F0F\u8C03\u6574",
292
+ tabStatusPane: "\u72B6\u6001\u680F",
293
+ sbItemCtx: "\u4E0A\u4E0B\u6587\u5360\u7528",
294
+ sbItemQuestions: "\u63D0\u95EE\u6B21\u6570",
295
+ sbGroupSession: "\u4F1A\u8BDD\u7EDF\u8BA1\uFF08\u8DDF\u968F\u5F53\u524D\u4F1A\u8BDD\uFF09",
296
+ sbGroupGlobal: "\u603B\u7EDF\u8BA1",
297
+ sbGroupTurn: "\u6BCF\u8F6E\u7EDF\u8BA1\uFF08\u56DE\u7B54\u672B\u5C3E\uFF09",
298
+ sbLabelIn: "\u8F93\u5165",
299
+ sbLabelOut: "\u8F93\u51FA",
300
+ sbLabelCache: "\u7F13\u5B58",
301
+ featureTurnUsage: "\u6BCF\u8F6E\u56DE\u7B54 token \u7528\u91CF",
302
+ featureTurnUsageDesc: "\u5728\u6BCF\u6761\u56DE\u7B54\u672B\u5C3E\u64CD\u4F5C\u6309\u94AE\u7684\u65F6\u95F4\u540E\u9762\u663E\u793A\u8BE5\u8F6E\u7684\u8F93\u5165/\u8F93\u51FA token\uFF08\u672C\u5730\u8BB0\u5F55\u53EA\u4FDD\u7559\u6700\u8FD1\u82E5\u5E72\u8F6E\uFF0C\u5BF9\u4E0D\u4E0A\u65F6\u95F4\u7684\u65E7\u56DE\u7B54\u4E0D\u663E\u793A\uFF09\u3002",
303
+ turnUsageTitle: "\u672C\u8F6E token",
304
+ sbLabelQuestions: "\u63D0\u95EE",
305
+ sbItemTodayTokens: "\u4ECA\u65E5 token",
306
+ sbItemTasks: "\u4EFB\u52A1\u603B\u6570",
307
+ sbLabelCtx: "\u4E0A\u4E0B\u6587",
308
+ sbLabelTurns: "\u8C03\u7528",
309
+ sbLabelToday: "\u4ECA\u65E5",
310
+ sbLabelTasks: "\u4EFB\u52A1",
311
+ sbItemTotalTokens: "\u7D2F\u8BA1 token",
312
+ sbLabelTotal: "\u7D2F\u8BA1",
313
+ sbMoveUp: "\u4E0A\u79FB",
314
+ sbMoveDown: "\u4E0B\u79FB",
315
+ sbVisible: "\u663E\u793A",
316
+ sbHidden: "\u9690\u85CF",
262
317
  tabShortcuts: "\u5FEB\u6377\u952E",
263
318
  hkTitle: "\u4F1A\u8BDD\u5FEB\u6377\u5207\u6362",
264
319
  hkAltZ: "\u5728\u5F53\u524D\u4E0E\u4E0A\u6B21\u4F7F\u7528\u7684\u4F1A\u8BDD\u95F4\u6765\u56DE\u5207\u6362",
@@ -659,6 +714,11 @@
659
714
  tbPluginStore: "Plugin store",
660
715
  featurePinnedCollapse: "Collapsible Pinned section",
661
716
  featurePinnedCollapseDesc: 'The sidebar "Pinned" header collapses/expands its task list (like Projects and Groups); the state is remembered.',
717
+ featureSandboxReclaim: "Plugin card sandbox reclaim",
718
+ 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).",
719
+ featurePluginCardRetry: "Plugin card auto-retry",
720
+ featurePluginCardRetryDesc: 'Auto-clicks "Reload" on cards that failed the slot cap; they recover as soon as a slot frees up.',
721
+ visionCardUi: "Vision cards",
662
722
  pinnedToggleAria: "Collapse/expand Pinned",
663
723
  sidebarProjectSpacingName: "Project spacing",
664
724
  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 +743,34 @@
683
743
  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
744
  ntpDescSuffix: "plugin panel",
685
745
  ntpNeedSession: "Open a task session first, then open plugin panels from the new tab page",
746
+ featureStatusBar: "Status bar statistics",
747
+ 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".',
748
+ tabStylesPane: "Styles",
749
+ tabStatusPane: "Status bar",
750
+ sbItemCtx: "Context usage",
751
+ sbItemQuestions: "Questions asked",
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
+ featureTurnUsage: "Per-turn token usage",
759
+ featureTurnUsageDesc: "Shows that turn's in/out tokens after the time in each answer's action row (local records keep only the most recent turns; older answers without a match stay unlabelled).",
760
+ turnUsageTitle: "Turn tokens",
761
+ sbLabelQuestions: "asks",
762
+ sbItemTodayTokens: "Today tokens",
763
+ sbItemTasks: "Tasks",
764
+ sbLabelCtx: "ctx",
765
+ sbLabelTurns: "calls",
766
+ sbLabelToday: "today",
767
+ sbLabelTasks: "tasks",
768
+ sbItemTotalTokens: "Total tokens",
769
+ sbLabelTotal: "total",
770
+ sbMoveUp: "Up",
771
+ sbMoveDown: "Down",
772
+ sbVisible: "shown",
773
+ sbHidden: "hidden",
686
774
  tabShortcuts: "Shortcuts",
687
775
  hkTitle: "Session quick switch",
688
776
  hkAltZ: "Toggle between the current and the last-used session",
@@ -1022,20 +1110,20 @@
1022
1110
  }
1023
1111
  }
1024
1112
  function observeRadixPopups(onPopup) {
1025
- const seen = /* @__PURE__ */ new WeakSet();
1113
+ const seen2 = /* @__PURE__ */ new WeakSet();
1026
1114
  const check = (node) => {
1027
1115
  if (!(node instanceof HTMLElement)) return;
1028
1116
  if (node.hasAttribute("data-radix-popper-content-wrapper")) {
1029
1117
  requestAnimationFrame(() => {
1030
1118
  const content = node.firstElementChild;
1031
- if (content && !seen.has(content)) {
1032
- seen.add(content);
1119
+ if (content && !seen2.has(content)) {
1120
+ seen2.add(content);
1033
1121
  onPopup(content);
1034
1122
  }
1035
1123
  });
1036
1124
  }
1037
- if (node.getAttribute && node.getAttribute("role") === "menu" && !seen.has(node)) {
1038
- seen.add(node);
1125
+ if (node.getAttribute && node.getAttribute("role") === "menu" && !seen2.has(node)) {
1126
+ seen2.add(node);
1039
1127
  requestAnimationFrame(() => {
1040
1128
  if (node.isConnected) onPopup(node);
1041
1129
  });
@@ -1695,6 +1783,56 @@
1695
1783
  text-align: center;
1696
1784
  color: var(--color-muted-foreground, #888);
1697
1785
  }
1786
+ /* \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
1787
+ \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
1788
+ \u4E0B\u65B9\u662F\u7A97\u5E95\u7559\u767D\u65F6\u8D1F margin \u538B\u5165\u8D34\u5E95\uFF09 */
1789
+ .zcodepro-statusbar {
1790
+ display: flex;
1791
+ justify-content: flex-end;
1792
+ align-items: center;
1793
+ gap: 8px;
1794
+ padding: 7px 14px 0;
1795
+ font-size: 11px;
1796
+ line-height: 1.4;
1797
+ color: var(--color-muted-foreground, #888);
1798
+ user-select: none;
1799
+ pointer-events: none;
1800
+ }
1801
+ .zcodepro-statusbar span[data-tone='warn'] { color: var(--color-amber-500, #f59e0b); }
1802
+ .zcodepro-statusbar span[data-tone='danger'] { color: var(--color-red-500, #ef4444); }
1803
+ .zcodepro-statusbar-sep { opacity: 0.45; }
1804
+ /* \u4F1A\u8BDD\u7EC4\u4E0E\u5168\u5C40\u7EC4\u4E4B\u95F4\u7684\u5206\u754C\u7AD6\u7EBF */
1805
+ .zcodepro-statusbar-groupsep {
1806
+ width: 1px;
1807
+ height: 10px;
1808
+ background: currentColor;
1809
+ opacity: 0.3;
1810
+ margin: 0 2px;
1811
+ }
1812
+ /* \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 */
1813
+ .zcodepro-statusbar span[title] {
1814
+ display: inline-flex;
1815
+ align-items: center;
1816
+ gap: 3px;
1817
+ white-space: nowrap;
1818
+ }
1819
+ .zcodepro-sb-icon {
1820
+ width: 12px;
1821
+ height: 12px;
1822
+ flex-shrink: 0;
1823
+ opacity: 0.6;
1824
+ }
1825
+ .zcodepro-statusbar b { font-weight: 600; color: var(--color-foreground-subtle, #666); }
1826
+ .zcodepro-statusbar i {
1827
+ font-style: normal;
1828
+ opacity: 0.7;
1829
+ font-size: 10px;
1830
+ }
1831
+ /* \u9879\u5185\u77ED\u6807\u7B7E\uFF08\u5982\u201C\u4E0A\u4E0B\u6587\u201D\u201C\u7D2F\u8BA1\u201D\uFF09\uFF0C\u5F31\u5316\u4E0D\u62A2\u6570\u503C */
1832
+ .zcodepro-statusbar em {
1833
+ font-style: normal;
1834
+ opacity: 0.75;
1835
+ }
1698
1836
  .zcodepro-switcher-ws {
1699
1837
  font-size: 11px;
1700
1838
  color: var(--color-muted-foreground, #888);
@@ -2504,10 +2642,10 @@
2504
2642
  }
2505
2643
  function handleProjectMenu(content) {
2506
2644
  if (content.querySelector("[data-zcodepro-item]")) return;
2507
- const items = itemsOf(content);
2645
+ const items2 = itemsOf(content);
2508
2646
  const newUi = isNewProjectSidebar();
2509
2647
  const menuRow = newUi ? openMenuProjectRow() : null;
2510
- const anchor = newUi ? items.find((el) => el.getAttribute("data-testid") === "virtual-project-edit") : items.find(
2648
+ const anchor = newUi ? items2.find((el) => el.getAttribute("data-testid") === "virtual-project-edit") : items2.find(
2511
2649
  (el) => el.hasAttribute("data-testid") && REMOVE_TEXTS.some((x) => itemText(el).startsWith(x))
2512
2650
  );
2513
2651
  if (!anchor) return;
@@ -2600,7 +2738,7 @@
2600
2738
  if (looksLikePath(p2)) return p2;
2601
2739
  return pathFromReactFiber(content);
2602
2740
  }
2603
- function appendMenuItem(content, template, anchor, { marker, label, icon, onPick }) {
2741
+ function appendMenuItem(content, template, anchor, { marker, label, icon: icon2, onPick }) {
2604
2742
  const item = template.cloneNode(true);
2605
2743
  item.removeAttribute("data-testid");
2606
2744
  item.removeAttribute("data-highlighted");
@@ -2609,7 +2747,7 @@
2609
2747
  item.setAttribute("data-zcodepro-item", marker);
2610
2748
  for (const child of [...item.childNodes]) child.remove();
2611
2749
  const origIcon = template.querySelector("svg");
2612
- item.append(buildIcon(origIcon?.getAttribute("class") || "size-4", icon), document.createTextNode(label));
2750
+ item.append(buildIcon(origIcon?.getAttribute("class") || "size-4", icon2), document.createTextNode(label));
2613
2751
  item.addEventListener("mouseenter", () => {
2614
2752
  for (const el of content.querySelectorAll('[role="menuitem"]')) el.removeAttribute("data-highlighted");
2615
2753
  item.setAttribute("data-highlighted", "");
@@ -2624,8 +2762,8 @@
2624
2762
  }
2625
2763
  function handleFileMenu(content) {
2626
2764
  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)));
2765
+ const items2 = itemsOf(content);
2766
+ const copyAbs = items2.find((el) => COPY_ABS_TEXTS.some((x) => itemText(el).startsWith(x)));
2629
2767
  if (!copyAbs) return;
2630
2768
  content.dataset.zcodeproFileMenu = "1";
2631
2769
  void (async () => {
@@ -2640,7 +2778,7 @@
2640
2778
  }).catch(() => {
2641
2779
  });
2642
2780
  };
2643
- const noApps = items.find((el) => NO_APPS_TEXTS.some((x) => itemText(el) === x)) || content.querySelector('[data-disabled][role="menuitem"]');
2781
+ const noApps = items2.find((el) => NO_APPS_TEXTS.some((x) => itemText(el) === x)) || content.querySelector('[data-disabled][role="menuitem"]');
2644
2782
  const openAnchor = noApps && content.contains(noApps) ? noApps : copyAbs;
2645
2783
  appendMenuItem(content, copyAbs, openAnchor, {
2646
2784
  marker: "file-open-default",
@@ -2850,10 +2988,10 @@
2850
2988
  function startImageMenu() {
2851
2989
  if (typeof window === "undefined") return;
2852
2990
  if (typeof navigator.clipboard?.write !== "function" || typeof window.ClipboardItem === "undefined") return;
2853
- let enabled7 = true;
2991
+ let enabled9 = true;
2854
2992
  const refresh = () => {
2855
2993
  void getConfig().then((c) => {
2856
- enabled7 = !(c.features && c.features.imageCopy === false);
2994
+ enabled9 = !(c.features && c.features.imageCopy === false);
2857
2995
  });
2858
2996
  };
2859
2997
  refresh();
@@ -2862,7 +3000,7 @@
2862
3000
  const img = copyTargetOf(e.target, e.clientX, e.clientY);
2863
3001
  if (!img) return;
2864
3002
  e.preventDefault();
2865
- if (enabled7) showMenu(e.clientX, e.clientY, img);
3003
+ if (enabled9) showMenu(e.clientX, e.clientY, img);
2866
3004
  refresh();
2867
3005
  }, true);
2868
3006
  }
@@ -2966,7 +3104,7 @@
2966
3104
  function waitFor(getter, timeoutMs, step) {
2967
3105
  const start = Date.now();
2968
3106
  return new Promise((resolve, reject) => {
2969
- const tick2 = () => {
3107
+ const tick6 = () => {
2970
3108
  let v = null;
2971
3109
  try {
2972
3110
  v = getter();
@@ -2980,9 +3118,9 @@
2980
3118
  reject(new Error(step));
2981
3119
  return;
2982
3120
  }
2983
- setTimeout(tick2, 120);
3121
+ setTimeout(tick6, 120);
2984
3122
  };
2985
- tick2();
3123
+ tick6();
2986
3124
  });
2987
3125
  }
2988
3126
  function setReactInputValue(input, value) {
@@ -3754,7 +3892,7 @@
3754
3892
  setState(L.upgradeFound.replaceAll("{v}", res.latest || ""));
3755
3893
  btn.textContent = L.upgradeNow;
3756
3894
  };
3757
- const tick2 = async () => {
3895
+ const tick6 = async () => {
3758
3896
  const res = await rpc(endpoint + "/upgrade");
3759
3897
  if (!res.ok) {
3760
3898
  stopPoll();
@@ -3794,9 +3932,9 @@
3794
3932
  btn.textContent = L.upgradeStop;
3795
3933
  setState(L.upgradeRunning);
3796
3934
  timer = setInterval(() => {
3797
- void tick2();
3935
+ void tick6();
3798
3936
  }, 1500);
3799
- void tick2();
3937
+ void tick6();
3800
3938
  };
3801
3939
  void (async () => {
3802
3940
  const res = await rpc(endpoint + "/upgrade");
@@ -4005,6 +4143,16 @@
4005
4143
  if (await setFeature("imageCopy", next)) f.imageCopy = next;
4006
4144
  refreshRows();
4007
4145
  }),
4146
+ settingRow(L.featureSandboxReclaim, L.featureSandboxReclaimDesc, f.sandboxReclaim !== false, async () => {
4147
+ const next = !(f.sandboxReclaim !== false);
4148
+ if (await setFeature("sandboxReclaim", next)) f.sandboxReclaim = next;
4149
+ refreshRows();
4150
+ }),
4151
+ settingRow(L.featurePluginCardRetry, L.featurePluginCardRetryDesc, f.pluginCardRetry !== false, async () => {
4152
+ const next = !(f.pluginCardRetry !== false);
4153
+ if (await setFeature("pluginCardRetry", next)) f.pluginCardRetry = next;
4154
+ refreshRows();
4155
+ }),
4008
4156
  settingRow(L.featureWsRunning, L.featureWsRunningDesc, f.wsRunningSpin !== false, async () => {
4009
4157
  const next = !(f.wsRunningSpin !== false);
4010
4158
  if (await setFeature("wsRunningSpin", next)) f.wsRunningSpin = next;
@@ -4015,6 +4163,16 @@
4015
4163
  if (await setFeature("newtabPanels", next)) f.newtabPanels = next;
4016
4164
  refreshRows();
4017
4165
  }),
4166
+ settingRow(L.featureStatusBar, L.featureStatusBarDesc, f.statusBar !== false, async () => {
4167
+ const next = !(f.statusBar !== false);
4168
+ if (await setFeature("statusBar", next)) f.statusBar = next;
4169
+ refreshRows();
4170
+ }),
4171
+ settingRow(L.featureTurnUsage, L.featureTurnUsageDesc, f.turnUsage !== false, async () => {
4172
+ const next = !(f.turnUsage !== false);
4173
+ if (await setFeature("turnUsage", next)) f.turnUsage = next;
4174
+ refreshRows();
4175
+ }),
4018
4176
  (() => {
4019
4177
  const repoLink = h("span", {
4020
4178
  class: "cursor-pointer underline-offset-4 hover:underline",
@@ -4047,7 +4205,7 @@
4047
4205
  const WECHAT_ID = "ai4only";
4048
4206
  const ns = "http://www.w3.org/2000/svg";
4049
4207
  const extIcon = () => {
4050
- const icon = h("svg", {
4208
+ const icon2 = h("svg", {
4051
4209
  viewBox: "0 0 24 24",
4052
4210
  fill: "none",
4053
4211
  stroke: "currentColor",
@@ -4059,11 +4217,11 @@
4059
4217
  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
4218
  const p2 = document.createElementNS(ns, "path");
4061
4219
  p2.setAttribute("d", d);
4062
- icon.append(p2);
4220
+ icon2.append(p2);
4063
4221
  }
4064
- return icon;
4222
+ return icon2;
4065
4223
  };
4066
- const communityCard = (title, desc, icon, onClick) => h(
4224
+ const communityCard = (title, desc, icon2, onClick) => h(
4067
4225
  "div",
4068
4226
  {
4069
4227
  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 +4233,7 @@
4075
4233
  h("div", { class: "truncate text-ui-sm font-medium text-foreground" }, title),
4076
4234
  h("div", { class: "mt-0.5 truncate text-ui-xs/relaxed text-foreground-subtle" }, desc)
4077
4235
  ),
4078
- icon()
4236
+ icon2()
4079
4237
  );
4080
4238
  const communityCards = h(
4081
4239
  "div",
@@ -4218,7 +4376,7 @@
4218
4376
  ),
4219
4377
  hkCard
4220
4378
  );
4221
- const paneStyles = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
4379
+ const paneStyles = h("div", { role: "tabpanel", class: "mt-2", style: "display:none" });
4222
4380
  const panePerf = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
4223
4381
  const paneAgents = h("div", { role: "tabpanel", class: "mt-2", style: "display:none" });
4224
4382
  const paneProxy = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
@@ -4433,7 +4591,162 @@
4433
4591
  for (const c of fontCells) c.setOptions(list);
4434
4592
  })();
4435
4593
  renderToolbarSwitch();
4436
- paneStyles.append(
4594
+ const paneStylesSub1 = h("div", { class: "mt-4" });
4595
+ const paneStylesSub2 = h("div", { class: "mt-4", style: "display:none" });
4596
+ const ITEM_NAMES = {
4597
+ ctx: "sbItemCtx",
4598
+ questions: "sbItemQuestions",
4599
+ todayTokens: "sbItemTodayTokens",
4600
+ totalTokens: "sbItemTotalTokens",
4601
+ tasks: "sbItemTasks"
4602
+ };
4603
+ const sbItems = () => {
4604
+ const saved = config.statusBar && Array.isArray(config.statusBar.items) ? config.statusBar.items : null;
4605
+ if (!saved) return DEFAULT_STATUS_ITEMS.map((x) => ({ ...x }));
4606
+ const byId = new Map(saved.map((x) => [x.id, x]));
4607
+ const list = saved.filter((x) => ITEM_NAMES[x.id]).map((x) => ({ on: x.on !== false, id: x.id }));
4608
+ for (const d of DEFAULT_STATUS_ITEMS) if (!byId.has(d.id)) list.push({ ...d });
4609
+ return [
4610
+ ...list.filter((x) => STATUS_SESSION_IDS.has(x.id)),
4611
+ ...list.filter((x) => STATUS_GLOBAL_IDS.has(x.id))
4612
+ ];
4613
+ };
4614
+ let sbSaving = false;
4615
+ const saveSbItems = async (next) => {
4616
+ if (sbSaving) return;
4617
+ sbSaving = true;
4618
+ try {
4619
+ const res = await rpc("/config", { method: "POST", body: { statusBar: { items: next } } });
4620
+ clearConfigCache();
4621
+ window.dispatchEvent(new CustomEvent("zcodepro:config-changed"));
4622
+ if (!res.ok) {
4623
+ showToast(L.failed + ": " + errText(res), "error");
4624
+ renderSbItems();
4625
+ return;
4626
+ }
4627
+ if (res.config && res.config.statusBar) config.statusBar = res.config.statusBar;
4628
+ } finally {
4629
+ sbSaving = false;
4630
+ }
4631
+ renderSbItems();
4632
+ };
4633
+ const sbList = h("div", { class: "divide-y divide-border rounded-xl border border-border" });
4634
+ const sbGlobalList = h("div", { class: "divide-y divide-border rounded-xl border border-border" });
4635
+ const TURN_NAMES = { turnIn: "sbLabelIn", turnOut: "sbLabelOut", turnCache: "sbLabelCache" };
4636
+ const turnItems = () => {
4637
+ const saved = config.statusBar && Array.isArray(config.statusBar.turnItems) ? config.statusBar.turnItems : null;
4638
+ if (!saved) return DEFAULT_TURN_ITEMS.map((x) => ({ ...x }));
4639
+ const byId = new Map(saved.map((x) => [x.id, x]));
4640
+ const list = saved.filter((x) => TURN_NAMES[x.id]).map((x) => ({ on: x.on !== false, id: x.id }));
4641
+ for (const d of DEFAULT_TURN_ITEMS) if (!byId.has(d.id)) list.push({ ...d });
4642
+ return list;
4643
+ };
4644
+ const saveTurnItems = async (next) => {
4645
+ if (sbSaving) return;
4646
+ sbSaving = true;
4647
+ try {
4648
+ const res = await rpc("/config", { method: "POST", body: { statusBar: { turnItems: next } } });
4649
+ clearConfigCache();
4650
+ window.dispatchEvent(new CustomEvent("zcodepro:config-changed"));
4651
+ if (!res.ok) {
4652
+ showToast(L.failed + ": " + errText(res), "error");
4653
+ renderSbItems();
4654
+ return;
4655
+ }
4656
+ if (res.config && res.config.statusBar) config.statusBar = res.config.statusBar;
4657
+ } finally {
4658
+ sbSaving = false;
4659
+ }
4660
+ renderSbItems();
4661
+ };
4662
+ const turnList = h("div", { class: "divide-y divide-border rounded-xl border border-border" });
4663
+ const renderSbItems = () => {
4664
+ turnList.replaceChildren(...turnItems().map((it, i) => h(
4665
+ "div",
4666
+ { class: "flex items-center gap-2 px-3 py-2" },
4667
+ h("span", { class: "min-w-0 flex-1 truncate text-ui-sm text-foreground-subtle" }, L[TURN_NAMES[it.id]] || it.id),
4668
+ btnSmall(it.on ? L.sbVisible : L.sbHidden, () => {
4669
+ void saveTurnItems(turnItems().map((x) => x.id === it.id ? { ...x, on: !x.on } : x));
4670
+ }),
4671
+ btnSmall("\u2191", () => {
4672
+ const a = turnItems();
4673
+ if (i > 0) {
4674
+ [a[i - 1], a[i]] = [a[i], a[i - 1]];
4675
+ void saveTurnItems(a);
4676
+ }
4677
+ }),
4678
+ btnSmall("\u2193", () => {
4679
+ const a = turnItems();
4680
+ if (i < a.length - 1) {
4681
+ [a[i], a[i + 1]] = [a[i + 1], a[i]];
4682
+ void saveTurnItems(a);
4683
+ }
4684
+ })
4685
+ )));
4686
+ const list = sbItems();
4687
+ const renderGroup = (el, groupIds) => {
4688
+ const idxs = list.map((x, i) => i).filter((i) => groupIds.has(list[i].id));
4689
+ el.replaceChildren(...idxs.map((i) => {
4690
+ const it = list[i];
4691
+ const swap = (dir) => {
4692
+ const a = sbItems();
4693
+ const same = a.map((x, j) => j).filter((j) => groupIds.has(a[j].id));
4694
+ const pos = same.indexOf(i);
4695
+ const other = same[pos + dir];
4696
+ if (other == null) return;
4697
+ [a[i], a[other]] = [a[other], a[i]];
4698
+ void saveSbItems(a);
4699
+ };
4700
+ return h(
4701
+ "div",
4702
+ { class: "flex items-center gap-2 px-3 py-2" },
4703
+ h(
4704
+ "span",
4705
+ { class: "min-w-0 flex-1 truncate text-ui-sm text-foreground-subtle" },
4706
+ L[ITEM_NAMES[it.id]] || it.id
4707
+ ),
4708
+ btnSmall(it.on ? L.sbVisible : L.sbHidden, () => {
4709
+ void saveSbItems(sbItems().map((x, j) => j === i ? { ...x, on: !x.on } : x));
4710
+ }),
4711
+ btnSmall("\u2191", () => swap(-1)),
4712
+ btnSmall("\u2193", () => swap(1))
4713
+ );
4714
+ }));
4715
+ };
4716
+ renderGroup(sbList, STATUS_SESSION_IDS);
4717
+ renderGroup(sbGlobalList, STATUS_GLOBAL_IDS);
4718
+ };
4719
+ renderSbItems();
4720
+ paneStylesSub2.append(
4721
+ h("p", { class: "text-ui-sm/relaxed text-foreground-subtle" }, L.featureStatusBarDesc),
4722
+ h("div", { class: "mt-3 mb-1 text-ui-sm font-medium" }, L.sbGroupSession),
4723
+ sbList,
4724
+ h("div", { class: "mt-3 mb-1 text-ui-sm font-medium" }, L.sbGroupGlobal),
4725
+ sbGlobalList,
4726
+ h("div", { class: "mt-3 mb-1 text-ui-sm font-medium" }, L.sbGroupTurn),
4727
+ turnList
4728
+ );
4729
+ let stylesSubTab = "styles";
4730
+ const stylesSubTablist = h("div", { role: "tablist", "aria-orientation": "horizontal", class: "zcodepro-tablist" });
4731
+ const renderStylesSubTabs = () => stylesSubTablist.replaceChildren(...[
4732
+ ["styles", L.tabStylesPane],
4733
+ ["status", L.tabStatusPane]
4734
+ ].map(([id, label]) => h("button", {
4735
+ type: "button",
4736
+ role: "tab",
4737
+ class: "zcodepro-tab",
4738
+ "aria-selected": String(stylesSubTab === id),
4739
+ "data-state": stylesSubTab === id ? "active" : "inactive",
4740
+ onClick: () => {
4741
+ stylesSubTab = id;
4742
+ paneStylesSub1.style.display = id === "styles" ? "" : "none";
4743
+ paneStylesSub2.style.display = id === "status" ? "" : "none";
4744
+ renderStylesSubTabs();
4745
+ }
4746
+ }, label)));
4747
+ renderStylesSubTabs();
4748
+ paneStyles.append(stylesSubTablist, paneStylesSub1, paneStylesSub2);
4749
+ paneStylesSub1.append(
4437
4750
  toolbarSwitchWrap,
4438
4751
  h(
4439
4752
  "div",
@@ -5431,6 +5744,11 @@
5431
5744
  h(
5432
5745
  "div",
5433
5746
  { class: "ml-auto flex items-center gap-4" },
5747
+ settingRow(L.visionCardUi, "", visionCfg.cardUi !== false, async () => {
5748
+ visionCfg.cardUi = !(visionCfg.cardUi !== false);
5749
+ await persistVision();
5750
+ renderVision();
5751
+ }),
5434
5752
  settingRow(L.visionForceIntercept, "", visionCfg.forceIntercept !== false, async () => {
5435
5753
  visionCfg.forceIntercept = !(visionCfg.forceIntercept !== false);
5436
5754
  await persistVision();
@@ -6506,11 +6824,11 @@
6506
6824
  const rootEl = document.getElementById("root");
6507
6825
  const containerKey = rootEl && Object.keys(rootEl).find((k) => k.startsWith("__reactContainer$"));
6508
6826
  if (!containerKey) return null;
6509
- const seen = /* @__PURE__ */ new Set();
6827
+ const seen2 = /* @__PURE__ */ new Set();
6510
6828
  let found = null;
6511
6829
  const scanObj = (o) => {
6512
- if (!o || typeof o !== "object" || seen.has(o) || found) return;
6513
- seen.add(o);
6830
+ if (!o || typeof o !== "object" || seen2.has(o) || found) return;
6831
+ seen2.add(o);
6514
6832
  const ts = o.zcodeTaskService;
6515
6833
  if (ts && typeof ts === "object" && typeof ts.releaseWorkspacePreparation === "function") found = o;
6516
6834
  };
@@ -6649,22 +6967,22 @@
6649
6967
  }, 0);
6650
6968
  }
6651
6969
  function apply() {
6652
- const seen = /* @__PURE__ */ new Set();
6970
+ const seen2 = /* @__PURE__ */ new Set();
6653
6971
  for (const row of document.querySelectorAll(WS_SEL())) {
6654
6972
  const path = rowPath(row);
6655
6973
  if (!path) continue;
6656
- seen.add(path);
6974
+ seen2.add(path);
6657
6975
  const head = wsHead(row);
6658
6976
  const collapsed = !head || head.getAttribute("aria-expanded") === "false";
6659
6977
  const domRunning = !!(runningByWs.get(path) && runningByWs.get(path).size);
6660
6978
  const normP = (v) => String(v || "").replace(/[\\/]+$/, "");
6661
6979
  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);
6980
+ const icon2 = iconHolderOf(row);
6981
+ icon2?.classList.toggle("zcodepro-ws-running", running);
6982
+ setRunningIcon(icon2, running);
6665
6983
  }
6666
6984
  applySectionSpin();
6667
- for (const ws of [...runningByWs.keys()]) if (!seen.has(ws)) runningByWs.delete(ws);
6985
+ for (const ws of [...runningByWs.keys()]) if (!seen2.has(ws)) runningByWs.delete(ws);
6668
6986
  }
6669
6987
  var headInfoCache = /* @__PURE__ */ new WeakMap();
6670
6988
  var PROJECT_HEAD_TEXTS = /* @__PURE__ */ new Set(["\u9879\u76EE", "projects"]);
@@ -7466,20 +7784,20 @@
7466
7784
  }
7467
7785
  function findBridge() {
7468
7786
  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);
7787
+ const seen2 = /* @__PURE__ */ new Set();
7788
+ const scan5 = (o) => {
7789
+ if (!o || typeof o !== "object" || seen2.has(o) || holder) return;
7790
+ seen2.add(o);
7473
7791
  if (typeof o.pluginUiBridgeService === "object" && o.pluginUiBridgeService) {
7474
7792
  holder = o.pluginUiBridgeService;
7475
7793
  return;
7476
7794
  }
7477
- for (const v of Object.values(o)) if (v && typeof v === "object") scan4(v);
7795
+ for (const v of Object.values(o)) if (v && typeof v === "object") scan5(v);
7478
7796
  };
7479
7797
  eachFiber((f) => {
7480
7798
  for (const h2 of [f.memoizedProps, f.memoizedState]) {
7481
7799
  if (!h2 || typeof h2 !== "object") continue;
7482
- scan4(h2);
7800
+ scan5(h2);
7483
7801
  if (holder) return true;
7484
7802
  }
7485
7803
  return false;
@@ -7615,6 +7933,423 @@
7615
7933
  setInterval(tryInject, 5e3);
7616
7934
  }
7617
7935
 
7936
+ // src/inject/features/status-bar.js
7937
+ var enabled4 = true;
7938
+ var items = DEFAULT_STATUS_ITEMS.map((x) => ({ ...x }));
7939
+ var barEl = null;
7940
+ var lastStats = null;
7941
+ function readCtx() {
7942
+ for (const el of document.querySelectorAll("[title], [aria-label]")) {
7943
+ if (el.closest(".zcodepro-statusbar")) continue;
7944
+ const text = el.getAttribute("title") || el.getAttribute("aria-label") || "";
7945
+ if (!/上下文|context/i.test(text)) continue;
7946
+ const m = text.match(/([\d][\d,]*)\s*\/[^0-9]*([\d][\d,]*)/);
7947
+ if (!m) continue;
7948
+ const used = Number(m[1].replace(/,/g, ""));
7949
+ const size = Number(m[2].replace(/,/g, ""));
7950
+ if (used > 0 && size > used) return { used, size };
7951
+ }
7952
+ return null;
7953
+ }
7954
+ var usageSvc = null;
7955
+ var official = null;
7956
+ var officialAt = 0;
7957
+ function eachFiber2(visit) {
7958
+ const rootEl = document.getElementById("root");
7959
+ const key = rootEl && Object.keys(rootEl).find((k) => k.startsWith("__reactContainer$"));
7960
+ if (!key) return;
7961
+ let visited = 0;
7962
+ const walk = (f) => {
7963
+ if (!f || visited > 2e5) return false;
7964
+ visited++;
7965
+ if (visit(f)) return true;
7966
+ return walk(f.child) || walk(f.sibling);
7967
+ };
7968
+ walk(rootEl[key]);
7969
+ }
7970
+ function findUsageService() {
7971
+ let found = null;
7972
+ const seen2 = /* @__PURE__ */ new Set();
7973
+ const scan5 = (o) => {
7974
+ if (!o || typeof o !== "object" || seen2.has(o) || found) return;
7975
+ seen2.add(o);
7976
+ if (typeof o.usageStatsService === "object" && o.usageStatsService) {
7977
+ found = o.usageStatsService;
7978
+ return;
7979
+ }
7980
+ for (const v of Object.values(o)) if (v && typeof v === "object") scan5(v);
7981
+ };
7982
+ eachFiber2((f) => {
7983
+ for (const h2 of [f.memoizedProps, f.memoizedState]) {
7984
+ if (!h2 || typeof h2 !== "object") continue;
7985
+ scan5(h2);
7986
+ if (found) return true;
7987
+ }
7988
+ return false;
7989
+ });
7990
+ return found;
7991
+ }
7992
+ function refreshOfficial() {
7993
+ const period = official ? 5 * 6e4 : 3e4;
7994
+ if (Date.now() - officialAt < period) return;
7995
+ officialAt = Date.now();
7996
+ void (async () => {
7997
+ try {
7998
+ if (!usageSvc) usageSvc = findUsageService();
7999
+ if (!usageSvc || typeof usageSvc.getAppUsageSnapshot !== "function") return;
8000
+ const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
8001
+ const [all, d7] = await Promise.all([
8002
+ usageSvc.getAppUsageSnapshot({ range: "all", timeZone }),
8003
+ usageSvc.getAppUsageSnapshot({ range: "7d", timeZone })
8004
+ ]);
8005
+ const now = /* @__PURE__ */ new Date();
8006
+ const key = now.getFullYear() + "-" + String(now.getMonth() + 1).padStart(2, "0") + "-" + String(now.getDate()).padStart(2, "0");
8007
+ let todayTokens = 0;
8008
+ const day = (d7 && Array.isArray(d7.dailyModelUsage) ? d7.dailyModelUsage : []).find((d) => d.date === key);
8009
+ if (day) for (const m of day.models || []) todayTokens += Number(m.totalTokens) || 0;
8010
+ official = {
8011
+ todayTokens,
8012
+ totalTokens: Number(all && all.summary && all.summary.totalTokens) || 0,
8013
+ totalSessions: Number(all && all.summary && all.summary.totalSessions) || 0
8014
+ };
8015
+ renderBar();
8016
+ } catch {
8017
+ usageSvc = null;
8018
+ }
8019
+ })();
8020
+ }
8021
+ var GLOBAL_GROUP = /* @__PURE__ */ new Set(["todayTokens", "totalTokens", "tasks"]);
8022
+ var SB_ICONS = {
8023
+ ctx: '<path d="M21.21 15.89A10 10 0 1 1 8 2.83"/><path d="M22 12A10 10 0 0 0 12 2v10z"/>',
8024
+ asks: '<path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"/>',
8025
+ 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"/>',
8026
+ 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"/>',
8027
+ 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"/>',
8028
+ 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"/>'
8029
+ };
8030
+ 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>`;
8031
+ function fmtTokens(n) {
8032
+ if (n >= 1e9) return (n / 1e9).toFixed(1) + "B";
8033
+ if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
8034
+ if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
8035
+ return String(n);
8036
+ }
8037
+ function fmtNum(n) {
8038
+ if (!isZhLocale()) return fmtTokens(n);
8039
+ if (n < 1e4) return String(n);
8040
+ if (n >= 1e8) {
8041
+ const yi = Math.round(n / 1e8 * 10) / 10;
8042
+ return (yi % 1 === 0 ? String(yi) : yi.toFixed(1)) + "\u4EBF";
8043
+ }
8044
+ const wan = Math.round(n / 1e4 * 10) / 10;
8045
+ return (wan % 1 === 0 ? String(wan) : wan.toFixed(1)) + "\u4E07";
8046
+ }
8047
+ function itemContent(id, L) {
8048
+ const g = lastStats && lastStats.global;
8049
+ const label = (text) => `<em>${text}</em>`;
8050
+ switch (id) {
8051
+ case "ctx": {
8052
+ const dom = readCtx();
8053
+ if (dom) {
8054
+ const pct = Math.round(dom.used / dom.size * 1e3) / 10;
8055
+ const tone = pct >= 95 ? "danger" : pct >= 80 ? "warn" : "";
8056
+ return {
8057
+ html: sbIcon("ctx") + label(L.sbLabelCtx) + `<b>${fmtNum(dom.used)}/${fmtNum(dom.size)}${isZhLocale() ? "\uFF08" : "("}${pct}%${isZhLocale() ? "\uFF09" : ")"}</b>`,
8058
+ title: `${L.sbItemCtx}\uFF1A${dom.used.toLocaleString()} / ${dom.size.toLocaleString()}`,
8059
+ tone
8060
+ };
8061
+ }
8062
+ return null;
8063
+ }
8064
+ case "questions": {
8065
+ const nav = document.querySelector('[data-testid="v4-turn-navigator"]');
8066
+ const n = nav ? Number(nav.getAttribute("data-item-count")) : 0;
8067
+ return n > 0 ? { html: sbIcon("asks") + label(L.sbLabelQuestions) + String(n), title: L.sbItemQuestions } : null;
8068
+ }
8069
+ case "todayTokens": {
8070
+ const n = official ? official.todayTokens : g ? g.todayTokens : 0;
8071
+ return n > 0 ? { html: sbIcon("today") + label(L.sbLabelToday) + fmtNum(n), title: L.sbItemTodayTokens } : null;
8072
+ }
8073
+ case "totalTokens": {
8074
+ const n = official ? official.totalTokens : g ? g.totalTokens : 0;
8075
+ return n > 0 ? { html: sbIcon("total") + label(L.sbLabelTotal) + fmtNum(n), title: L.sbItemTotalTokens } : null;
8076
+ }
8077
+ case "tasks": {
8078
+ const n = official ? official.totalSessions : g ? g.tasks : 0;
8079
+ return n > 0 ? { html: sbIcon("tasks") + label(L.sbLabelTasks) + String(n), title: L.sbItemTasks } : null;
8080
+ }
8081
+ default:
8082
+ return null;
8083
+ }
8084
+ }
8085
+ function renderBar() {
8086
+ const ta = document.querySelector("textarea, [contenteditable=true]");
8087
+ const host = ta && (ta.closest("form") || ta.parentElement);
8088
+ if (!enabled4 || !host || !host.parentElement) {
8089
+ if (barEl) barEl.remove();
8090
+ barEl = null;
8091
+ return;
8092
+ }
8093
+ if (!barEl || !barEl.isConnected) {
8094
+ if (!barEl) {
8095
+ barEl = document.createElement("div");
8096
+ barEl.className = "zcodepro-statusbar";
8097
+ }
8098
+ host.parentElement.insertBefore(barEl, host.nextSibling);
8099
+ }
8100
+ const hasSession = !![...document.querySelectorAll("[data-task-item-key]")].find((e) => (e.className + "").includes("bg-selected"));
8101
+ barEl.style.marginBottom = hasSession ? "-14px" : "0";
8102
+ barEl.style.paddingBottom = hasSession ? "0" : "7px";
8103
+ const L = t();
8104
+ const parts = [];
8105
+ let lastGroup = "";
8106
+ for (const it of items) {
8107
+ if (!it.on) continue;
8108
+ const c = itemContent(it.id, L);
8109
+ if (!c) continue;
8110
+ const group = GLOBAL_GROUP.has(it.id) ? "g" : "s";
8111
+ if (lastGroup && group !== lastGroup) parts.push('<span class="zcodepro-statusbar-groupsep"></span>');
8112
+ lastGroup = group;
8113
+ parts.push(`<span title="${c.title.replace(/"/g, "&quot;")}"${c.tone ? ` data-tone="${c.tone}"` : ""}>${c.html}</span>`);
8114
+ }
8115
+ const SEP = '<span class="zcodepro-statusbar-sep">\xB7</span>';
8116
+ const GROUPSEP = '<span class="zcodepro-statusbar-groupsep"></span>';
8117
+ barEl.innerHTML = parts.reduce((acc, p, i) => {
8118
+ if (i === 0) return p;
8119
+ if (p === GROUPSEP || parts[i - 1] === GROUPSEP) return acc + p;
8120
+ return acc + SEP + p;
8121
+ }, "");
8122
+ barEl.style.display = parts.length ? "" : "none";
8123
+ }
8124
+ async function tick() {
8125
+ if (!enabled4 || document.hidden) return;
8126
+ refreshOfficial();
8127
+ const res = await rpc("/stats/usage").catch(() => null);
8128
+ if (res && res.ok) lastStats = res;
8129
+ renderBar();
8130
+ }
8131
+ function startStatusBar() {
8132
+ ensureStyle();
8133
+ const refreshCfg = () => {
8134
+ void getConfig().then((cfg) => {
8135
+ enabled4 = cfg.features ? cfg.features.statusBar !== false : true;
8136
+ const saved = cfg.statusBar && Array.isArray(cfg.statusBar.items) ? cfg.statusBar.items : null;
8137
+ if (saved) {
8138
+ const byId = new Map(saved.map((x) => [x.id, x]));
8139
+ const list = saved.filter((x) => DEFAULT_STATUS_ITEMS.some((d) => d.id === x.id)).map((x) => ({ ...x }));
8140
+ const known = new Set(list.map((x) => x.id));
8141
+ for (const d of DEFAULT_STATUS_ITEMS) {
8142
+ if (!known.has(d.id)) list.push({ ...d });
8143
+ }
8144
+ items = [
8145
+ ...list.filter((x) => !GLOBAL_GROUP.has(x.id)),
8146
+ ...list.filter((x) => GLOBAL_GROUP.has(x.id))
8147
+ ];
8148
+ } else {
8149
+ items = DEFAULT_STATUS_ITEMS.map((x) => ({ ...x }));
8150
+ }
8151
+ renderBar();
8152
+ }).catch(() => {
8153
+ });
8154
+ };
8155
+ refreshCfg();
8156
+ window.addEventListener("zcodepro:config-changed", refreshCfg);
8157
+ document.addEventListener("visibilitychange", () => {
8158
+ if (!document.hidden) void tick();
8159
+ });
8160
+ setInterval(() => {
8161
+ void tick();
8162
+ }, 2e3);
8163
+ let rerenderTimer = 0;
8164
+ new MutationObserver(() => {
8165
+ if (rerenderTimer) return;
8166
+ rerenderTimer = setTimeout(() => {
8167
+ rerenderTimer = 0;
8168
+ renderBar();
8169
+ }, 300);
8170
+ }).observe(document.body, { childList: true, subtree: true });
8171
+ }
8172
+
8173
+ // src/inject/features/turn-usage.js
8174
+ var enabled5 = true;
8175
+ var recentTurns = [];
8176
+ var scanTimer = 0;
8177
+ var turnCfg = [
8178
+ { id: "turnIn", on: true },
8179
+ { id: "turnOut", on: true },
8180
+ { id: "turnCache", on: true }
8181
+ ];
8182
+ var refreshTurnItems = () => {
8183
+ void getConfig().then((cfg) => {
8184
+ const saved = cfg.statusBar && Array.isArray(cfg.statusBar.turnItems) ? cfg.statusBar.turnItems : null;
8185
+ if (!saved) return;
8186
+ const known = new Set(turnCfg.map((x) => x.id));
8187
+ const list = saved.filter((x) => x && known.has(x.id)).map((x) => ({ id: x.id, on: x.on !== false }));
8188
+ for (const d of turnCfg) if (!list.some((x) => x.id === d.id)) list.push({ ...d });
8189
+ turnCfg = list;
8190
+ }).catch(() => {
8191
+ });
8192
+ };
8193
+ var ICONS = {
8194
+ up: '<path d="m5 12 7-7 7 7"/><path d="M12 19V5"/>',
8195
+ down: '<path d="M12 5v14"/><path d="m19 12-7 7-7-7"/>',
8196
+ 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"/>'
8197
+ };
8198
+ 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>`;
8199
+ function fmt(n) {
8200
+ if (!isZhLocale()) {
8201
+ if (n >= 1e9) return (n / 1e9).toFixed(1) + "B";
8202
+ if (n >= 1e6) return (n / 1e6).toFixed(1) + "M";
8203
+ if (n >= 1e3) return (n / 1e3).toFixed(1) + "k";
8204
+ return String(n);
8205
+ }
8206
+ if (n >= 1e8) return (n / 1e8).toFixed(1) + "\u4EBF";
8207
+ if (n >= 1e4) {
8208
+ const wan = Math.round(n / 1e4 * 10) / 10;
8209
+ return (wan % 1 === 0 ? String(wan) : wan.toFixed(1)) + "\u4E07";
8210
+ }
8211
+ return String(n);
8212
+ }
8213
+ function findRows() {
8214
+ const rows = [];
8215
+ for (const btn of document.querySelectorAll('[data-testid^="v4-copy-"]')) {
8216
+ const row = btn.parentElement;
8217
+ if (!row || row.dataset.zcodeproTurn) continue;
8218
+ if (!row.querySelector('[data-testid^="v4-feedback-"]')) continue;
8219
+ const timeEl = [...row.querySelectorAll("span")].find((s) => /\d{1,2}:\d{2}$/.test((s.textContent || "").trim()));
8220
+ if (timeEl) {
8221
+ const ts = rowTimeTs((timeEl.textContent || "").trim());
8222
+ if (ts != null) rows.push({ row, ts });
8223
+ }
8224
+ }
8225
+ return rows;
8226
+ }
8227
+ function currentSessionId() {
8228
+ const rows = [...document.querySelectorAll("[data-task-item-key]")];
8229
+ const cur = rows.find((e) => (e.className + "").includes("bg-selected")) || rows[rows.length - 1];
8230
+ const key = cur ? cur.getAttribute("data-task-item-key") : "";
8231
+ const i = key.lastIndexOf(":sess");
8232
+ return i > 0 ? key.slice(i + 1) : "";
8233
+ }
8234
+ function injectRow(row, bucket) {
8235
+ row.dataset.zcodeproTurn = "1";
8236
+ if (!bucket || bucket.in + bucket.out <= 0) return;
8237
+ const L = t();
8238
+ const el = document.createElement("span");
8239
+ el.dataset.zcodeproTurnSpan = "1";
8240
+ el.className = "select-none text-ui-sm text-foreground-subtlest";
8241
+ el.style.whiteSpace = "nowrap";
8242
+ el.style.display = "inline-flex";
8243
+ el.style.alignItems = "center";
8244
+ el.style.gap = "4px";
8245
+ el.style.marginLeft = "10px";
8246
+ const cachePct = bucket.in > 0 && bucket.cacheRead > 0 ? (bucket.cacheRead / bucket.in * 100).toFixed(2) : "";
8247
+ const group = (html) => `<span style="display:inline-flex;align-items:center;gap:2px">${html}</span>`;
8248
+ const em = (text) => `<em style="font-style:normal;opacity:.75">${text}</em>`;
8249
+ const DOT = '<span style="opacity:.45">\xB7</span>';
8250
+ const sections = [];
8251
+ for (const s of turnCfg) {
8252
+ if (!s.on) continue;
8253
+ if (s.id === "turnIn") sections.push(group(icon("up") + em(L.sbLabelIn) + fmt(bucket.in)));
8254
+ else if (s.id === "turnOut") sections.push(group(icon("down") + em(L.sbLabelOut) + fmt(bucket.out)));
8255
+ else if (s.id === "turnCache" && cachePct) sections.push(group(icon("cache") + em(L.sbLabelCache) + cachePct + "%"));
8256
+ }
8257
+ if (!sections.length) {
8258
+ delete row.dataset.zcodeproTurn;
8259
+ el.remove();
8260
+ return;
8261
+ }
8262
+ const VBAR = '<span style="display:inline-block;width:1px;height:10px;background:currentColor;opacity:.3;margin:0 5px"></span>';
8263
+ el.innerHTML = VBAR + sections.join(DOT);
8264
+ el.title = `${L.turnUsageTitle}\uFF1A${L.sbLabelIn} ${bucket.in.toLocaleString()} / ${L.sbLabelOut} ${bucket.out.toLocaleString()}` + (bucket.cacheRead > 0 ? ` / ${L.sbLabelCache} ${bucket.cacheRead.toLocaleString()}` : "");
8265
+ row.append(el);
8266
+ }
8267
+ function rowTimeTs(text) {
8268
+ const now = /* @__PURE__ */ new Date();
8269
+ let m = text.match(/^(\d{1,2}):(\d{2})$/);
8270
+ if (m) return new Date(now.getFullYear(), now.getMonth(), now.getDate(), +m[1], +m[2]).getTime();
8271
+ m = text.match(/^昨天\s*(\d{1,2}):(\d{2})$/);
8272
+ if (m) {
8273
+ const y = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
8274
+ return new Date(y.getFullYear(), y.getMonth(), y.getDate(), +m[1], +m[2]).getTime();
8275
+ }
8276
+ m = text.match(/^(\d{1,2})\/(\d{1,2})\s*(\d{1,2}):(\d{2})$/);
8277
+ if (m) return new Date(now.getFullYear(), +m[1] - 1, +m[2], +m[3], +m[4]).getTime();
8278
+ return null;
8279
+ }
8280
+ var lastSid = "";
8281
+ function scan3() {
8282
+ if (!enabled5) return;
8283
+ const sid = currentSessionId();
8284
+ if (sid !== lastSid) {
8285
+ lastSid = sid;
8286
+ recentTurns = [];
8287
+ void tick2();
8288
+ }
8289
+ for (const el of document.querySelectorAll('[data-zcodepro-turn-span], span[title^="\u672C\u8F6E"], span[title^="Turn"]')) el.remove();
8290
+ for (const row of document.querySelectorAll("[data-zcodepro-turn]")) delete row.dataset.zcodeproTurn;
8291
+ if (document.hidden || !recentTurns.length) return;
8292
+ const rows = findRows().sort((a, b) => a.ts - b.ts);
8293
+ if (!rows.length) return;
8294
+ const agg = rows.map(() => ({ in: 0, out: 0, cacheRead: 0 }));
8295
+ const dayOf = (t2) => {
8296
+ const d = new Date(t2);
8297
+ return d.getFullYear() * 1e4 + (d.getMonth() + 1) * 100 + d.getDate();
8298
+ };
8299
+ for (const b of recentTurns) {
8300
+ let owner = -1;
8301
+ for (let i = 0; i < rows.length; i++) {
8302
+ if (rows[i].ts <= b.ts) owner = i;
8303
+ else break;
8304
+ }
8305
+ if (owner < 0) continue;
8306
+ if (dayOf(rows[owner].ts) !== dayOf(b.ts)) continue;
8307
+ agg[owner].in += b.in;
8308
+ agg[owner].out += b.out;
8309
+ agg[owner].cacheRead += b.cacheRead;
8310
+ }
8311
+ for (let i = 0; i < rows.length; i++) {
8312
+ if (agg[i].in + agg[i].out > 0) injectRow(rows[i].row, agg[i]);
8313
+ }
8314
+ }
8315
+ async function tick2() {
8316
+ if (!enabled5 || document.hidden) return;
8317
+ const sid = currentSessionId();
8318
+ const res = await rpc("/stats/usage" + (sid ? "?session=" + encodeURIComponent(sid) : "")).catch(() => null);
8319
+ if (res && res.ok && Array.isArray(res.recentTurns)) {
8320
+ recentTurns = res.recentTurns;
8321
+ scan3();
8322
+ }
8323
+ }
8324
+ function startTurnUsage() {
8325
+ void getConfig().then((cfg) => {
8326
+ enabled5 = cfg.features ? cfg.features.turnUsage !== false : true;
8327
+ }).catch(() => {
8328
+ });
8329
+ refreshTurnItems();
8330
+ window.addEventListener("zcodepro:config-changed", () => {
8331
+ void getConfig().then((cfg) => {
8332
+ enabled5 = cfg.features ? cfg.features.turnUsage !== false : true;
8333
+ }).catch(() => {
8334
+ });
8335
+ refreshTurnItems();
8336
+ });
8337
+ const observer2 = new MutationObserver(() => {
8338
+ if (scanTimer) return;
8339
+ scanTimer = setTimeout(() => {
8340
+ scanTimer = 0;
8341
+ scan3();
8342
+ }, 1200);
8343
+ });
8344
+ observer2.observe(document.body, { childList: true, subtree: true });
8345
+ setInterval(() => {
8346
+ void tick2();
8347
+ }, 5e3);
8348
+ document.addEventListener("visibilitychange", () => {
8349
+ if (!document.hidden) void tick2();
8350
+ });
8351
+ }
8352
+
7618
8353
  // src/inject/features/title-lock.js
7619
8354
  var TASK_SEL2 = "[data-task-item-key]";
7620
8355
  var SCAN_DEBOUNCE_MS3 = 150;
@@ -7625,7 +8360,7 @@
7625
8360
  var MAP_REFRESH_MS = 6e4;
7626
8361
  var installed3 = false;
7627
8362
  var seenRows = /* @__PURE__ */ new WeakSet();
7628
- var enabled4 = true;
8363
+ var enabled6 = true;
7629
8364
  var pinned = null;
7630
8365
  var resolved = false;
7631
8366
  var fetching = null;
@@ -7640,7 +8375,7 @@
7640
8375
  const t2 = item.querySelector('[class*="truncate"]');
7641
8376
  return t2 ? [t2] : [];
7642
8377
  }
7643
- function findRows(key) {
8378
+ function findRows2(key) {
7644
8379
  return [...document.querySelectorAll(`${TASK_SEL2}[data-task-item-key="${CSS.escape(key)}"]`)];
7645
8380
  }
7646
8381
  async function fetchPinned() {
@@ -7649,8 +8384,8 @@
7649
8384
  const res = await rpc("/pinned-titles");
7650
8385
  if (!res || !res.ok) return null;
7651
8386
  resolved = true;
7652
- enabled4 = res.enabled !== false;
7653
- if (!enabled4) {
8387
+ enabled6 = res.enabled !== false;
8388
+ if (!enabled6) {
7654
8389
  pinned = null;
7655
8390
  return null;
7656
8391
  }
@@ -7665,7 +8400,7 @@
7665
8400
  }
7666
8401
  }
7667
8402
  function isMismatch(item) {
7668
- if (!enabled4 || !pinned) return null;
8403
+ if (!enabled6 || !pinned) return null;
7669
8404
  const key = item.getAttribute("data-task-item-key") || "";
7670
8405
  const want = pinned.get(sessIdOf(key));
7671
8406
  if (want === void 0) return null;
@@ -7683,10 +8418,10 @@
7683
8418
  }
7684
8419
  function scheduleEnforce(key, { immediate = false } = {}) {
7685
8420
  if (immediate) {
7686
- if (enabled4 && pinned) {
8421
+ if (enabled6 && pinned) {
7687
8422
  const want = pinned.get(sessIdOf(key));
7688
8423
  if (want !== void 0) {
7689
- for (const item of findRows(key)) {
8424
+ for (const item of findRows2(key)) {
7690
8425
  for (const el of titleElsOf(item)) writeTitle(el, want);
7691
8426
  }
7692
8427
  }
@@ -7702,7 +8437,7 @@
7702
8437
  const want = map.get(sessIdOf(key));
7703
8438
  if (want === void 0) return;
7704
8439
  let wrote = false;
7705
- for (const item of findRows(key)) {
8440
+ for (const item of findRows2(key)) {
7706
8441
  if (isMismatch(item) !== key) continue;
7707
8442
  for (const el of titleElsOf(item)) writeTitle(el, want);
7708
8443
  wrote = true;
@@ -7718,13 +8453,13 @@
7718
8453
  if (!map) return;
7719
8454
  const want = map.get(sessIdOf(key));
7720
8455
  if (want === void 0) return;
7721
- for (const item of findRows(key)) {
8456
+ for (const item of findRows2(key)) {
7722
8457
  if (isMismatch(item) !== key) continue;
7723
8458
  for (const el of titleElsOf(item)) writeTitle(el, want);
7724
8459
  }
7725
8460
  }
7726
- function scan3() {
7727
- if (!enabled4 || !pinned || pinned.size === 0) return;
8461
+ function scan4() {
8462
+ if (!enabled6 || !pinned || pinned.size === 0) return;
7728
8463
  for (const item of document.querySelectorAll(TASK_SEL2)) {
7729
8464
  const key = isMismatch(item);
7730
8465
  if (!key) {
@@ -7758,23 +8493,23 @@
7758
8493
  if (scanDebounce3) return;
7759
8494
  scanDebounce3 = setTimeout(() => {
7760
8495
  scanDebounce3 = 0;
7761
- scan3();
8496
+ scan4();
7762
8497
  }, SCAN_DEBOUNCE_MS3);
7763
8498
  }
7764
8499
  var mapRefreshTimer = 0;
7765
8500
  function refreshMapSoon() {
7766
8501
  if (mapRefreshTimer) return;
7767
- const tick2 = () => {
8502
+ const tick6 = () => {
7768
8503
  if (document.hidden) {
7769
- mapRefreshTimer = setTimeout(tick2, 1e4);
8504
+ mapRefreshTimer = setTimeout(tick6, 1e4);
7770
8505
  return;
7771
8506
  }
7772
8507
  mapRefreshTimer = 0;
7773
- if (enabled4 && resolved) void fetchPinned().then((m) => {
7774
- if (m) scan3();
8508
+ if (enabled6 && resolved) void fetchPinned().then((m) => {
8509
+ if (m) scan4();
7775
8510
  });
7776
8511
  };
7777
- mapRefreshTimer = setTimeout(tick2, MAP_REFRESH_MS);
8512
+ mapRefreshTimer = setTimeout(tick6, MAP_REFRESH_MS);
7778
8513
  }
7779
8514
  var editingKey = null;
7780
8515
  var bootDelay = 0;
@@ -7788,16 +8523,16 @@
7788
8523
  }
7789
8524
  async function bootstrap() {
7790
8525
  const map = await fetchPinned();
7791
- if (map) scan3();
8526
+ if (map) scan4();
7792
8527
  }
7793
8528
  function setRowTitle(key, text) {
7794
- for (const item of findRows(key)) {
8529
+ for (const item of findRows2(key)) {
7795
8530
  for (const el of titleElsOf(item)) writeTitle(el, text);
7796
8531
  }
7797
8532
  }
7798
8533
  function refreshPinnedTitles() {
7799
8534
  return fetchPinned().then((m) => {
7800
- if (m) scan3();
8535
+ if (m) scan4();
7801
8536
  });
7802
8537
  }
7803
8538
  var RENAME_CTX_VALID_MS = 15e3;
@@ -7816,7 +8551,7 @@
7816
8551
  renameCtx = null;
7817
8552
  return;
7818
8553
  }
7819
- if (!enabled4 || !pinned) return;
8554
+ if (!enabled6 || !pinned) return;
7820
8555
  const want = pinned.get(sessIdOf(renameCtx.key));
7821
8556
  if (want === void 0) return;
7822
8557
  for (const el of document.querySelectorAll('[role="dialog"] input, [role="dialog"] textarea, [role="dialog"] [contenteditable="true"]')) {
@@ -7854,10 +8589,10 @@
7854
8589
  }, true);
7855
8590
  scheduleBootRetry();
7856
8591
  setInterval(() => {
7857
- if (!document.hidden) scan3();
8592
+ if (!document.hidden) scan4();
7858
8593
  }, SCAN_FALLBACK_MS3);
7859
8594
  document.addEventListener("visibilitychange", () => {
7860
- if (!document.hidden) scan3();
8595
+ if (!document.hidden) scan4();
7861
8596
  });
7862
8597
  window.addEventListener("zcodepro:config-changed", () => {
7863
8598
  void bootstrap();
@@ -7873,7 +8608,7 @@
7873
8608
  if (editingKey === null) return;
7874
8609
  editingKey = null;
7875
8610
  void fetchPinned().then((m) => {
7876
- if (m) scan3();
8611
+ if (m) scan4();
7877
8612
  });
7878
8613
  });
7879
8614
  }
@@ -8104,7 +8839,7 @@
8104
8839
  await reclaim(ws);
8105
8840
  blockUntil.set(ws, Date.now() + cooldownMs);
8106
8841
  }
8107
- async function tick() {
8842
+ async function tick3() {
8108
8843
  const cfg = await getConfig();
8109
8844
  if ((cfg.features || {}).idleReclaim === false) {
8110
8845
  idleSince.clear();
@@ -8166,20 +8901,171 @@
8166
8901
  }
8167
8902
  function startIdleReclaim() {
8168
8903
  const loop = () => {
8169
- tick().catch(() => {
8904
+ tick3().catch(() => {
8170
8905
  });
8171
8906
  };
8172
8907
  loop();
8173
8908
  setInterval(loop, CHECK_MS);
8174
8909
  }
8175
8910
 
8911
+ // src/inject/features/sandbox-reclaim.js
8912
+ var CHECK_MS2 = 3e4;
8913
+ var MIN_BUDGET = 8;
8914
+ var MAX_BUDGET = 64;
8915
+ var RETRY_COOLDOWN_MS = 10 * 6e4;
8916
+ var SEEN_KEY = "zcodepro:sbx-seen";
8917
+ var seen = /* @__PURE__ */ new Map();
8918
+ function loadSeen() {
8919
+ try {
8920
+ const raw = JSON.parse(localStorage.getItem(SEEN_KEY) || "{}");
8921
+ for (const [id, v] of Object.entries(raw)) {
8922
+ if (id && v && Number.isFinite(v.firstSeen)) seen.set(id, { initId: v.initId, firstSeen: v.firstSeen });
8923
+ }
8924
+ } catch {
8925
+ }
8926
+ }
8927
+ var saveSeenTimer = null;
8928
+ function saveSeenSoon() {
8929
+ if (saveSeenTimer) return;
8930
+ saveSeenTimer = setTimeout(() => {
8931
+ saveSeenTimer = null;
8932
+ try {
8933
+ const out = {};
8934
+ for (const [id, v] of seen) out[id] = v;
8935
+ localStorage.setItem(SEEN_KEY, JSON.stringify(out));
8936
+ } catch {
8937
+ }
8938
+ }, 2e3);
8939
+ }
8940
+ var cooldown = /* @__PURE__ */ new Map();
8941
+ var disabledLogged = false;
8942
+ function onPortsMessage(e) {
8943
+ const d = e.data;
8944
+ if (!d || d.type !== "zcode:plugin-sandbox-ports" || typeof d.sandboxId !== "string") return;
8945
+ seen.set(d.sandboxId, { initId: Number(d.initId) || 0, firstSeen: Date.now() });
8946
+ saveSeenSoon();
8947
+ }
8948
+ function extractSandboxId(wv) {
8949
+ const m = /instance\/(sb-[0-9a-f]+)\//.exec(wv.getAttribute("src") || "");
8950
+ return m ? m[1] : null;
8951
+ }
8952
+ function isVisible(wv) {
8953
+ const root = wv.closest("[data-sandbox-page-root]");
8954
+ if (!root) return wv.getClientRects().length > 0;
8955
+ if (!root.getClientRects().length) return false;
8956
+ return Number(getComputedStyle(root).opacity) > 0.5;
8957
+ }
8958
+ async function tick4() {
8959
+ const cfg = await getConfig();
8960
+ if ((cfg.features || {}).sandboxReclaim === false) {
8961
+ seen.clear();
8962
+ return;
8963
+ }
8964
+ const bridge = window.zcodePluginSandbox;
8965
+ if (!bridge || typeof bridge.disposeSandbox !== "function") {
8966
+ if (!disabledLogged) {
8967
+ disabledLogged = true;
8968
+ console.info("[zcodepro] sandbox-reclaim: \u5F53\u524D\u7A97\u53E3\u65E0 zcodePluginSandbox \u6865\uFF0C\u6C99\u7BB1\u56DE\u6536\u8DF3\u8FC7");
8969
+ }
8970
+ return;
8971
+ }
8972
+ let budget = Number(cfg.sandboxReclaimBudget);
8973
+ if (!Number.isFinite(budget)) budget = 60;
8974
+ budget = Math.min(MAX_BUDGET, Math.max(MIN_BUDGET, budget));
8975
+ const now = Date.now();
8976
+ const visible = /* @__PURE__ */ new Set();
8977
+ for (const wv of document.querySelectorAll("webview")) {
8978
+ const id = extractSandboxId(wv);
8979
+ if (id) {
8980
+ if (isVisible(wv)) visible.add(id);
8981
+ if (!seen.has(id)) {
8982
+ seen.set(id, { initId: void 0, firstSeen: now });
8983
+ saveSeenSoon();
8984
+ }
8985
+ }
8986
+ }
8987
+ while (seen.size > budget) {
8988
+ let pool = [...seen.keys()].filter((id2) => (cooldown.get(id2) || 0) <= now && !visible.has(id2));
8989
+ if (!pool.length) pool = [...seen.keys()].filter((id2) => (cooldown.get(id2) || 0) <= now);
8990
+ pool.sort((a, b) => ((seen.get(a) || {}).firstSeen || 0) - ((seen.get(b) || {}).firstSeen || 0));
8991
+ if (!pool.length) break;
8992
+ const id = pool[0];
8993
+ const entry = seen.get(id);
8994
+ try {
8995
+ await bridge.disposeSandbox(id, entry && entry.initId);
8996
+ 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" : ""));
8997
+ } catch (err) {
8998
+ console.warn("[zcodepro] sandbox-reclaim: \u91CA\u653E\u5931\u8D25 " + id, err);
8999
+ seen.delete(id);
9000
+ continue;
9001
+ }
9002
+ seen.delete(id);
9003
+ saveSeenSoon();
9004
+ cooldown.set(id, now + RETRY_COOLDOWN_MS);
9005
+ }
9006
+ if (cooldown.size > 256) {
9007
+ for (const [k, v] of cooldown) {
9008
+ if (v <= now) cooldown.delete(k);
9009
+ }
9010
+ }
9011
+ }
9012
+ function startSandboxReclaim() {
9013
+ loadSeen();
9014
+ window.addEventListener("message", onPortsMessage);
9015
+ const loop = () => {
9016
+ tick4().catch(() => {
9017
+ });
9018
+ };
9019
+ loop();
9020
+ setInterval(loop, CHECK_MS2);
9021
+ }
9022
+
9023
+ // src/inject/features/plugin-card-retry.js
9024
+ var SCAN_MS = 15e3;
9025
+ var CLICK_INTERVAL_MS = 3e4;
9026
+ var MARKERS = ["\u63D2\u4EF6\u754C\u9762"];
9027
+ var lastClickAt = 0;
9028
+ function failedRetryButtons() {
9029
+ const out = [];
9030
+ for (const btn of document.querySelectorAll("button")) {
9031
+ if ((btn.textContent || "").trim() !== "\u91CD\u65B0\u52A0\u8F7D") continue;
9032
+ let anc = btn;
9033
+ for (let i = 0; i < 4 && anc.parentElement; i++) {
9034
+ anc = anc.parentElement;
9035
+ const ctx = anc.textContent || "";
9036
+ if (MARKERS.some((m) => ctx.includes(m))) {
9037
+ out.push(btn);
9038
+ break;
9039
+ }
9040
+ }
9041
+ }
9042
+ return out;
9043
+ }
9044
+ async function tick5() {
9045
+ const cfg = await getConfig();
9046
+ if ((cfg.features || {}).pluginCardRetry === false) return;
9047
+ const now = Date.now();
9048
+ if (now - lastClickAt < CLICK_INTERVAL_MS) return;
9049
+ for (const btn of failedRetryButtons()) {
9050
+ lastClickAt = now;
9051
+ btn.click();
9052
+ break;
9053
+ }
9054
+ }
9055
+ function startPluginCardRetry() {
9056
+ setInterval(() => {
9057
+ tick5().catch(() => {
9058
+ });
9059
+ }, SCAN_MS);
9060
+ }
9061
+
8176
9062
  // src/inject/features/toolbar-icons.js
8177
9063
  var HIDE_CLASS = "zcodepro-tb-hidden";
8178
9064
  var SYNC_DEBOUNCE_MS = 150;
8179
9065
  var SCAN_FALLBACK_MS5 = 5e3;
8180
9066
  var ROW_SEL = "[data-task-item-key]";
8181
9067
  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;
9068
+ var enabled7 = true;
8183
9069
  var rowEl = null;
8184
9070
  var origs = null;
8185
9071
  var syncTimer = 0;
@@ -8287,8 +9173,8 @@
8287
9173
  }
8288
9174
  });
8289
9175
  const newBtn = mk("newTask", parts.newTask, "span.truncate", L.tbNewTask);
8290
- const icon = iconOf(parts.newTask);
8291
- if (icon) newBtn.append(icon);
9176
+ const icon2 = iconOf(parts.newTask);
9177
+ if (icon2) newBtn.append(icon2);
8292
9178
  const searchBtn = mk("search", parts.search, "span.truncate", L.tbSearch);
8293
9179
  const sIcon = iconOf(parts.search);
8294
9180
  if (sIcon) searchBtn.append(sIcon);
@@ -8322,7 +9208,7 @@
8322
9208
  return scroll.getBoundingClientRect().width < NARROW_SIDEBAR_PX;
8323
9209
  }
8324
9210
  function sync() {
8325
- if (!enabled5) return;
9211
+ if (!enabled7) return;
8326
9212
  const parts = findParts();
8327
9213
  if (!parts) {
8328
9214
  if (rowEl) rowEl.style.display = "none";
@@ -8384,8 +9270,8 @@
8384
9270
  }
8385
9271
  async function refreshConfig2() {
8386
9272
  const cfg = await getConfig(true).catch(() => null);
8387
- enabled5 = !cfg || !cfg.features || cfg.features.toolbarIcons !== false;
8388
- if (enabled5) sync();
9273
+ enabled7 = !cfg || !cfg.features || cfg.features.toolbarIcons !== false;
9274
+ if (enabled7) sync();
8389
9275
  else teardown();
8390
9276
  }
8391
9277
  function startToolbarIcons() {
@@ -8411,7 +9297,7 @@
8411
9297
  var STORE_KEY = "zcodepro:pinned-collapsed";
8412
9298
  var SYNC_DEBOUNCE_MS2 = 300;
8413
9299
  var SCAN_FALLBACK_MS6 = 5e3;
8414
- var enabled6 = true;
9300
+ var enabled8 = true;
8415
9301
  var syncTimer2 = 0;
8416
9302
  function findPinnedHeader() {
8417
9303
  for (const h3 of document.querySelectorAll("h3")) {
@@ -8468,7 +9354,7 @@
8468
9354
  }
8469
9355
  h3.classList.add(HEAD_CLASS);
8470
9356
  h3.addEventListener("click", () => {
8471
- if (enabled6) toggle();
9357
+ if (enabled8) toggle();
8472
9358
  });
8473
9359
  const btn = h("button", {
8474
9360
  type: "button",
@@ -8477,7 +9363,7 @@
8477
9363
  "aria-expanded": collapsed ? "false" : "true",
8478
9364
  onClick: (e) => {
8479
9365
  e.stopPropagation();
8480
- if (enabled6) toggle();
9366
+ if (enabled8) toggle();
8481
9367
  }
8482
9368
  });
8483
9369
  btn.append(chevronSvg());
@@ -8485,7 +9371,7 @@
8485
9371
  return true;
8486
9372
  }
8487
9373
  function sync2() {
8488
- if (enabled6) apply2();
9374
+ if (enabled8) apply2();
8489
9375
  }
8490
9376
  function scheduleSync2() {
8491
9377
  if (syncTimer2) return;
@@ -8514,8 +9400,8 @@
8514
9400
  }
8515
9401
  async function refreshConfig3() {
8516
9402
  const cfg = await getConfig(true).catch(() => null);
8517
- enabled6 = !cfg || !cfg.features || cfg.features.pinnedCollapse !== false;
8518
- if (enabled6) sync2();
9403
+ enabled8 = !cfg || !cfg.features || cfg.features.pinnedCollapse !== false;
9404
+ if (enabled8) sync2();
8519
9405
  else teardown2();
8520
9406
  }
8521
9407
  function startPinnedCollapse() {
@@ -8550,12 +9436,12 @@
8550
9436
  return;
8551
9437
  }
8552
9438
  const sig = status.updates.map((u) => `${u.name}:${u.installed}>${u.latest}`).join(",");
8553
- let seen = "";
9439
+ let seen2 = "";
8554
9440
  try {
8555
- seen = window.localStorage.getItem(NOTIFIED_KEY) || "";
9441
+ seen2 = window.localStorage.getItem(NOTIFIED_KEY) || "";
8556
9442
  } catch {
8557
9443
  }
8558
- if (seen === sig) return;
9444
+ if (seen2 === sig) return;
8559
9445
  try {
8560
9446
  window.localStorage.setItem(NOTIFIED_KEY, sig);
8561
9447
  } catch {
@@ -8630,6 +9516,14 @@
8630
9516
  startIdleReclaim();
8631
9517
  } catch {
8632
9518
  }
9519
+ try {
9520
+ startSandboxReclaim();
9521
+ } catch {
9522
+ }
9523
+ try {
9524
+ startPluginCardRetry();
9525
+ } catch {
9526
+ }
8633
9527
  try {
8634
9528
  startToolbarIcons();
8635
9529
  } catch {
@@ -8638,6 +9532,14 @@
8638
9532
  startNewtabPanels();
8639
9533
  } catch {
8640
9534
  }
9535
+ try {
9536
+ startStatusBar();
9537
+ } catch {
9538
+ }
9539
+ try {
9540
+ startTurnUsage();
9541
+ } catch {
9542
+ }
8641
9543
  try {
8642
9544
  startPinnedCollapse();
8643
9545
  } catch {