zcode-pro 1.1.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",
@@ -255,6 +282,38 @@
255
282
  switcherUnseen: "\u672A\u67E5\u770B",
256
283
  switcherAttentionHint: "\u8FD0\u884C\u4E2D / \u8FD0\u884C\u5B8C\u672A\u67E5\u770B\u7684\u4EFB\u52A1 \xB7 {prev} / {next} \u6216 \u2191 / \u2193 \u9009\u62E9\uFF0CEnter \u6216\u677E\u5F00 alt \u5207\u6362",
257
284
  switcherAttentionEmpty: "\u6682\u65E0\u8FD0\u884C\u4E2D\u6216\u672A\u67E5\u770B\u7684\u4EFB\u52A1",
285
+ featureNewtabPanels: "\u65B0\u6807\u7B7E\u9875\u63D2\u4EF6\u9762\u677F\u5165\u53E3",
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",
287
+ ntpDescSuffix: "\u63D2\u4EF6\u9762\u677F",
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",
258
317
  tabShortcuts: "\u5FEB\u6377\u952E",
259
318
  hkTitle: "\u4F1A\u8BDD\u5FEB\u6377\u5207\u6362",
260
319
  hkAltZ: "\u5728\u5F53\u524D\u4E0E\u4E0A\u6B21\u4F7F\u7528\u7684\u4F1A\u8BDD\u95F4\u6765\u56DE\u5207\u6362",
@@ -655,6 +714,11 @@
655
714
  tbPluginStore: "Plugin store",
656
715
  featurePinnedCollapse: "Collapsible Pinned section",
657
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",
658
722
  pinnedToggleAria: "Collapse/expand Pinned",
659
723
  sidebarProjectSpacingName: "Project spacing",
660
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.",
@@ -675,6 +739,38 @@
675
739
  switcherUnseen: "unseen",
676
740
  switcherAttentionHint: "Running / finished but unseen tasks \xB7 {prev} / {next} or \u2191 / \u2193 to choose, Enter or release alt to switch",
677
741
  switcherAttentionEmpty: "No running or unseen tasks",
742
+ featureNewtabPanels: "Plugin panels on the new tab page",
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.',
744
+ ntpDescSuffix: "plugin panel",
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",
678
774
  tabShortcuts: "Shortcuts",
679
775
  hkTitle: "Session quick switch",
680
776
  hkAltZ: "Toggle between the current and the last-used session",
@@ -1014,20 +1110,20 @@
1014
1110
  }
1015
1111
  }
1016
1112
  function observeRadixPopups(onPopup) {
1017
- const seen = /* @__PURE__ */ new WeakSet();
1113
+ const seen2 = /* @__PURE__ */ new WeakSet();
1018
1114
  const check = (node) => {
1019
1115
  if (!(node instanceof HTMLElement)) return;
1020
1116
  if (node.hasAttribute("data-radix-popper-content-wrapper")) {
1021
1117
  requestAnimationFrame(() => {
1022
1118
  const content = node.firstElementChild;
1023
- if (content && !seen.has(content)) {
1024
- seen.add(content);
1119
+ if (content && !seen2.has(content)) {
1120
+ seen2.add(content);
1025
1121
  onPopup(content);
1026
1122
  }
1027
1123
  });
1028
1124
  }
1029
- if (node.getAttribute && node.getAttribute("role") === "menu" && !seen.has(node)) {
1030
- seen.add(node);
1125
+ if (node.getAttribute && node.getAttribute("role") === "menu" && !seen2.has(node)) {
1126
+ seen2.add(node);
1031
1127
  requestAnimationFrame(() => {
1032
1128
  if (node.isConnected) onPopup(node);
1033
1129
  });
@@ -1687,6 +1783,56 @@
1687
1783
  text-align: center;
1688
1784
  color: var(--color-muted-foreground, #888);
1689
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
+ }
1690
1836
  .zcodepro-switcher-ws {
1691
1837
  font-size: 11px;
1692
1838
  color: var(--color-muted-foreground, #888);
@@ -2496,10 +2642,10 @@
2496
2642
  }
2497
2643
  function handleProjectMenu(content) {
2498
2644
  if (content.querySelector("[data-zcodepro-item]")) return;
2499
- const items = itemsOf(content);
2645
+ const items2 = itemsOf(content);
2500
2646
  const newUi = isNewProjectSidebar();
2501
2647
  const menuRow = newUi ? openMenuProjectRow() : null;
2502
- 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(
2503
2649
  (el) => el.hasAttribute("data-testid") && REMOVE_TEXTS.some((x) => itemText(el).startsWith(x))
2504
2650
  );
2505
2651
  if (!anchor) return;
@@ -2592,7 +2738,7 @@
2592
2738
  if (looksLikePath(p2)) return p2;
2593
2739
  return pathFromReactFiber(content);
2594
2740
  }
2595
- function appendMenuItem(content, template, anchor, { marker, label, icon, onPick }) {
2741
+ function appendMenuItem(content, template, anchor, { marker, label, icon: icon2, onPick }) {
2596
2742
  const item = template.cloneNode(true);
2597
2743
  item.removeAttribute("data-testid");
2598
2744
  item.removeAttribute("data-highlighted");
@@ -2601,7 +2747,7 @@
2601
2747
  item.setAttribute("data-zcodepro-item", marker);
2602
2748
  for (const child of [...item.childNodes]) child.remove();
2603
2749
  const origIcon = template.querySelector("svg");
2604
- 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));
2605
2751
  item.addEventListener("mouseenter", () => {
2606
2752
  for (const el of content.querySelectorAll('[role="menuitem"]')) el.removeAttribute("data-highlighted");
2607
2753
  item.setAttribute("data-highlighted", "");
@@ -2616,8 +2762,8 @@
2616
2762
  }
2617
2763
  function handleFileMenu(content) {
2618
2764
  if (content.dataset.zcodeproFileMenu) return;
2619
- const items = itemsOf(content);
2620
- 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)));
2621
2767
  if (!copyAbs) return;
2622
2768
  content.dataset.zcodeproFileMenu = "1";
2623
2769
  void (async () => {
@@ -2632,7 +2778,7 @@
2632
2778
  }).catch(() => {
2633
2779
  });
2634
2780
  };
2635
- 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"]');
2636
2782
  const openAnchor = noApps && content.contains(noApps) ? noApps : copyAbs;
2637
2783
  appendMenuItem(content, copyAbs, openAnchor, {
2638
2784
  marker: "file-open-default",
@@ -2842,10 +2988,10 @@
2842
2988
  function startImageMenu() {
2843
2989
  if (typeof window === "undefined") return;
2844
2990
  if (typeof navigator.clipboard?.write !== "function" || typeof window.ClipboardItem === "undefined") return;
2845
- let enabled6 = true;
2991
+ let enabled9 = true;
2846
2992
  const refresh = () => {
2847
2993
  void getConfig().then((c) => {
2848
- enabled6 = !(c.features && c.features.imageCopy === false);
2994
+ enabled9 = !(c.features && c.features.imageCopy === false);
2849
2995
  });
2850
2996
  };
2851
2997
  refresh();
@@ -2854,7 +3000,7 @@
2854
3000
  const img = copyTargetOf(e.target, e.clientX, e.clientY);
2855
3001
  if (!img) return;
2856
3002
  e.preventDefault();
2857
- if (enabled6) showMenu(e.clientX, e.clientY, img);
3003
+ if (enabled9) showMenu(e.clientX, e.clientY, img);
2858
3004
  refresh();
2859
3005
  }, true);
2860
3006
  }
@@ -2958,7 +3104,7 @@
2958
3104
  function waitFor(getter, timeoutMs, step) {
2959
3105
  const start = Date.now();
2960
3106
  return new Promise((resolve, reject) => {
2961
- const tick2 = () => {
3107
+ const tick6 = () => {
2962
3108
  let v = null;
2963
3109
  try {
2964
3110
  v = getter();
@@ -2972,9 +3118,9 @@
2972
3118
  reject(new Error(step));
2973
3119
  return;
2974
3120
  }
2975
- setTimeout(tick2, 120);
3121
+ setTimeout(tick6, 120);
2976
3122
  };
2977
- tick2();
3123
+ tick6();
2978
3124
  });
2979
3125
  }
2980
3126
  function setReactInputValue(input, value) {
@@ -3746,7 +3892,7 @@
3746
3892
  setState(L.upgradeFound.replaceAll("{v}", res.latest || ""));
3747
3893
  btn.textContent = L.upgradeNow;
3748
3894
  };
3749
- const tick2 = async () => {
3895
+ const tick6 = async () => {
3750
3896
  const res = await rpc(endpoint + "/upgrade");
3751
3897
  if (!res.ok) {
3752
3898
  stopPoll();
@@ -3786,9 +3932,9 @@
3786
3932
  btn.textContent = L.upgradeStop;
3787
3933
  setState(L.upgradeRunning);
3788
3934
  timer = setInterval(() => {
3789
- void tick2();
3935
+ void tick6();
3790
3936
  }, 1500);
3791
- void tick2();
3937
+ void tick6();
3792
3938
  };
3793
3939
  void (async () => {
3794
3940
  const res = await rpc(endpoint + "/upgrade");
@@ -3997,11 +4143,36 @@
3997
4143
  if (await setFeature("imageCopy", next)) f.imageCopy = next;
3998
4144
  refreshRows();
3999
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
+ }),
4000
4156
  settingRow(L.featureWsRunning, L.featureWsRunningDesc, f.wsRunningSpin !== false, async () => {
4001
4157
  const next = !(f.wsRunningSpin !== false);
4002
4158
  if (await setFeature("wsRunningSpin", next)) f.wsRunningSpin = next;
4003
4159
  refreshRows();
4004
4160
  }),
4161
+ settingRow(L.featureNewtabPanels, L.featureNewtabPanelsDesc, f.newtabPanels !== false, async () => {
4162
+ const next = !(f.newtabPanels !== false);
4163
+ if (await setFeature("newtabPanels", next)) f.newtabPanels = next;
4164
+ refreshRows();
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
+ }),
4005
4176
  (() => {
4006
4177
  const repoLink = h("span", {
4007
4178
  class: "cursor-pointer underline-offset-4 hover:underline",
@@ -4034,7 +4205,7 @@
4034
4205
  const WECHAT_ID = "ai4only";
4035
4206
  const ns = "http://www.w3.org/2000/svg";
4036
4207
  const extIcon = () => {
4037
- const icon = h("svg", {
4208
+ const icon2 = h("svg", {
4038
4209
  viewBox: "0 0 24 24",
4039
4210
  fill: "none",
4040
4211
  stroke: "currentColor",
@@ -4046,11 +4217,11 @@
4046
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"]) {
4047
4218
  const p2 = document.createElementNS(ns, "path");
4048
4219
  p2.setAttribute("d", d);
4049
- icon.append(p2);
4220
+ icon2.append(p2);
4050
4221
  }
4051
- return icon;
4222
+ return icon2;
4052
4223
  };
4053
- const communityCard = (title, desc, icon, onClick) => h(
4224
+ const communityCard = (title, desc, icon2, onClick) => h(
4054
4225
  "div",
4055
4226
  {
4056
4227
  class: "flex cursor-pointer items-center justify-between gap-2 rounded-xl border border-border p-2 transition-colors hover:bg-surface-hover",
@@ -4062,7 +4233,7 @@
4062
4233
  h("div", { class: "truncate text-ui-sm font-medium text-foreground" }, title),
4063
4234
  h("div", { class: "mt-0.5 truncate text-ui-xs/relaxed text-foreground-subtle" }, desc)
4064
4235
  ),
4065
- icon()
4236
+ icon2()
4066
4237
  );
4067
4238
  const communityCards = h(
4068
4239
  "div",
@@ -4205,7 +4376,7 @@
4205
4376
  ),
4206
4377
  hkCard
4207
4378
  );
4208
- 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" });
4209
4380
  const panePerf = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
4210
4381
  const paneAgents = h("div", { role: "tabpanel", class: "mt-2", style: "display:none" });
4211
4382
  const paneProxy = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
@@ -4420,7 +4591,162 @@
4420
4591
  for (const c of fontCells) c.setOptions(list);
4421
4592
  })();
4422
4593
  renderToolbarSwitch();
4423
- 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(
4424
4750
  toolbarSwitchWrap,
4425
4751
  h(
4426
4752
  "div",
@@ -5418,6 +5744,11 @@
5418
5744
  h(
5419
5745
  "div",
5420
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
+ }),
5421
5752
  settingRow(L.visionForceIntercept, "", visionCfg.forceIntercept !== false, async () => {
5422
5753
  visionCfg.forceIntercept = !(visionCfg.forceIntercept !== false);
5423
5754
  await persistVision();
@@ -6493,11 +6824,11 @@
6493
6824
  const rootEl = document.getElementById("root");
6494
6825
  const containerKey = rootEl && Object.keys(rootEl).find((k) => k.startsWith("__reactContainer$"));
6495
6826
  if (!containerKey) return null;
6496
- const seen = /* @__PURE__ */ new Set();
6827
+ const seen2 = /* @__PURE__ */ new Set();
6497
6828
  let found = null;
6498
6829
  const scanObj = (o) => {
6499
- if (!o || typeof o !== "object" || seen.has(o) || found) return;
6500
- seen.add(o);
6830
+ if (!o || typeof o !== "object" || seen2.has(o) || found) return;
6831
+ seen2.add(o);
6501
6832
  const ts = o.zcodeTaskService;
6502
6833
  if (ts && typeof ts === "object" && typeof ts.releaseWorkspacePreparation === "function") found = o;
6503
6834
  };
@@ -6636,22 +6967,22 @@
6636
6967
  }, 0);
6637
6968
  }
6638
6969
  function apply() {
6639
- const seen = /* @__PURE__ */ new Set();
6970
+ const seen2 = /* @__PURE__ */ new Set();
6640
6971
  for (const row of document.querySelectorAll(WS_SEL())) {
6641
6972
  const path = rowPath(row);
6642
6973
  if (!path) continue;
6643
- seen.add(path);
6974
+ seen2.add(path);
6644
6975
  const head = wsHead(row);
6645
6976
  const collapsed = !head || head.getAttribute("aria-expanded") === "false";
6646
6977
  const domRunning = !!(runningByWs.get(path) && runningByWs.get(path).size);
6647
6978
  const normP = (v) => String(v || "").replace(/[\\/]+$/, "");
6648
6979
  const running = enabled2 && collapsed && (activeProjectPaths.size ? [...activeProjectPaths].some((p2) => normP(p2) === normP(path)) : domRunning);
6649
- const icon = iconHolderOf(row);
6650
- icon?.classList.toggle("zcodepro-ws-running", running);
6651
- setRunningIcon(icon, running);
6980
+ const icon2 = iconHolderOf(row);
6981
+ icon2?.classList.toggle("zcodepro-ws-running", running);
6982
+ setRunningIcon(icon2, running);
6652
6983
  }
6653
6984
  applySectionSpin();
6654
- 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);
6655
6986
  }
6656
6987
  var headInfoCache = /* @__PURE__ */ new WeakMap();
6657
6988
  var PROJECT_HEAD_TEXTS = /* @__PURE__ */ new Set(["\u9879\u76EE", "projects"]);
@@ -7417,6 +7748,608 @@
7417
7748
  window.addEventListener("blur", () => closePopup(false));
7418
7749
  }
7419
7750
 
7751
+ // src/inject/features/newtab-panels.js
7752
+ var GRID_HINTS = ["\u8FD0\u884C\u547D\u4EE4\u548C\u67E5\u770B\u8F93\u51FA", "Run commands and view output"];
7753
+ var OFFICIAL_SUFFIX = "@zcode-plugins-official";
7754
+ var OPEN_FN_MARK = "\u6253\u5F00\u63D2\u4EF6 UI";
7755
+ var enabled3 = true;
7756
+ var injectTimer = 0;
7757
+ function eachFiber(visit) {
7758
+ const rootEl = document.getElementById("root");
7759
+ const key = rootEl && Object.keys(rootEl).find((k) => k.startsWith("__reactContainer$"));
7760
+ if (!key) return;
7761
+ let visited = 0;
7762
+ const walk = (f) => {
7763
+ if (!f || visited > 2e5) return;
7764
+ visited++;
7765
+ if (visit(f)) return true;
7766
+ return walk(f.child) || walk(f.sibling);
7767
+ };
7768
+ walk(rootEl[key]);
7769
+ }
7770
+ function findOpenSidePane() {
7771
+ let found = null;
7772
+ eachFiber((f) => {
7773
+ const et = f.elementType;
7774
+ if (et && typeof et === "object" && typeof et.$$typeof === "symbol" && String(et.$$typeof).includes("react.context")) {
7775
+ const val = f.memoizedProps && f.memoizedProps.value;
7776
+ if (typeof val === "function" && val.toString().includes(OPEN_FN_MARK)) {
7777
+ found = val;
7778
+ return true;
7779
+ }
7780
+ }
7781
+ return false;
7782
+ });
7783
+ return found;
7784
+ }
7785
+ function findBridge() {
7786
+ let holder = null;
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);
7791
+ if (typeof o.pluginUiBridgeService === "object" && o.pluginUiBridgeService) {
7792
+ holder = o.pluginUiBridgeService;
7793
+ return;
7794
+ }
7795
+ for (const v of Object.values(o)) if (v && typeof v === "object") scan5(v);
7796
+ };
7797
+ eachFiber((f) => {
7798
+ for (const h2 of [f.memoizedProps, f.memoizedState]) {
7799
+ if (!h2 || typeof h2 !== "object") continue;
7800
+ scan5(h2);
7801
+ if (holder) return true;
7802
+ }
7803
+ return false;
7804
+ });
7805
+ return holder;
7806
+ }
7807
+ function sessionScope() {
7808
+ const rows = [...document.querySelectorAll("[data-task-item-key]")];
7809
+ const cur = rows.find((e) => (e.className + "").includes("bg-selected")) || rows[rows.length - 1];
7810
+ const key = cur ? cur.getAttribute("data-task-item-key") : "";
7811
+ if (!key) return null;
7812
+ const i = key.indexOf(":sess_");
7813
+ return i > 0 ? { workspacePath: key.slice(0, i), sessionId: key.slice(i + 1) } : null;
7814
+ }
7815
+ async function thirdPartySurfaces(bridge, workspacePath) {
7816
+ try {
7817
+ const list = await bridge.listSurfaces({ workspacePath });
7818
+ return (list || []).filter((s) => !String(s.pluginId || "").endsWith(OFFICIAL_SUFFIX));
7819
+ } catch {
7820
+ return [];
7821
+ }
7822
+ }
7823
+ function surfaceTitle(s, locale) {
7824
+ const title = s.title;
7825
+ const pick = (obj) => {
7826
+ const lang = (locale || "").split("-")[0];
7827
+ return obj[locale] || lang && obj[lang] || Object.entries(obj).find(([k]) => k.toLowerCase().startsWith(lang || "\0"))?.[1] || obj.en || Object.values(obj)[0] || "";
7828
+ };
7829
+ if (typeof title === "string") return title;
7830
+ if (title && typeof title === "object") {
7831
+ return pick(title) || s.pluginName || s.id;
7832
+ }
7833
+ return s.pluginName || s.id;
7834
+ }
7835
+ var PANEL_ICON_PATHS = ['<rect width="7" height="9" x="3" y="3" rx="1"/><rect width="7" height="5" x="14" y="3" rx="1"/><rect width="7" height="9" x="14" y="12" rx="1"/><rect width="7" height="5" x="3" y="16" rx="1"/>'];
7836
+ function findNewTabGrid() {
7837
+ const item = document.querySelector("[data-side-pane-open-tab-item]");
7838
+ if (!item || item.offsetParent === null) return null;
7839
+ const grid = item.parentElement;
7840
+ if (!grid || grid === document.body) return null;
7841
+ const text = grid.textContent || "";
7842
+ return GRID_HINTS.some((h2) => text.includes(h2)) ? grid : null;
7843
+ }
7844
+ async function injectPanels(grid) {
7845
+ if (grid.dataset.zcodeproPanels) return;
7846
+ const template = grid.querySelector("[data-side-pane-open-tab-item]");
7847
+ if (!template) return;
7848
+ const scope = sessionScope();
7849
+ if (!scope) return;
7850
+ const bridge = findBridge();
7851
+ if (!bridge) return;
7852
+ const surfaces = await thirdPartySurfaces(bridge, scope.workspacePath);
7853
+ if (!surfaces.length) {
7854
+ grid.dataset.zcodeproPanels = "1";
7855
+ return;
7856
+ }
7857
+ grid.dataset.zcodeproPanels = "1";
7858
+ const L = t();
7859
+ const locale = isZhLocale() ? "zh-CN" : "en-US";
7860
+ for (const s of surfaces) {
7861
+ const card = template.cloneNode(true);
7862
+ card.removeAttribute("data-side-pane-open-tab-item");
7863
+ card.setAttribute("data-zcodepro-panel", s.id);
7864
+ card.setAttribute("data-zcodepro-panel-name", s.pluginName || "");
7865
+ const title = surfaceTitle(s, locale);
7866
+ const desc = (s.pluginName || "") + " \xB7 " + L.ntpDescSuffix;
7867
+ const titleEl = card.querySelector("span.font-semibold");
7868
+ if (titleEl) titleEl.textContent = title;
7869
+ const descEl = card.querySelector("span[title]");
7870
+ if (descEl) {
7871
+ descEl.textContent = desc;
7872
+ descEl.setAttribute("title", desc);
7873
+ }
7874
+ const svg = card.querySelector("svg");
7875
+ if (svg) {
7876
+ svg.innerHTML = PANEL_ICON_PATHS[0];
7877
+ svg.setAttribute("viewBox", "0 0 24 24");
7878
+ }
7879
+ card.addEventListener("click", (ev) => {
7880
+ ev.stopPropagation();
7881
+ const sc = sessionScope();
7882
+ const open = findOpenSidePane();
7883
+ if (!sc || !open) {
7884
+ showToast(L.ntpNeedSession, "error");
7885
+ return;
7886
+ }
7887
+ try {
7888
+ open({
7889
+ parentSessionId: sc.sessionId,
7890
+ pluginId: s.pluginId,
7891
+ surfaceId: s.id,
7892
+ serverName: s.server,
7893
+ resourceUri: s.resourceUri,
7894
+ remoteSessionId: null,
7895
+ title,
7896
+ workspacePath: sc.workspacePath
7897
+ });
7898
+ } catch (e) {
7899
+ console.warn("[zcodepro] \u6253\u5F00\u63D2\u4EF6\u9762\u677F\u5931\u8D25", e);
7900
+ showToast(L.ntpNeedSession, "error");
7901
+ }
7902
+ });
7903
+ grid.append(card);
7904
+ }
7905
+ }
7906
+ function tryInject() {
7907
+ if (!enabled3 || document.hidden) return;
7908
+ const grid = findNewTabGrid();
7909
+ if (!grid) return;
7910
+ if (grid.dataset.zcodeproPanels && !grid.querySelector("[data-zcodepro-panel]")) {
7911
+ delete grid.dataset.zcodeproPanels;
7912
+ }
7913
+ void injectPanels(grid);
7914
+ }
7915
+ function startNewtabPanels() {
7916
+ ensureStyle();
7917
+ const refreshEnabled = () => {
7918
+ void getConfig().then((cfg) => {
7919
+ enabled3 = cfg.features ? cfg.features.newtabPanels !== false : true;
7920
+ }).catch(() => {
7921
+ });
7922
+ };
7923
+ refreshEnabled();
7924
+ window.addEventListener("zcodepro:config-changed", refreshEnabled);
7925
+ const observer2 = new MutationObserver(() => {
7926
+ if (injectTimer) return;
7927
+ injectTimer = setTimeout(() => {
7928
+ injectTimer = 0;
7929
+ tryInject();
7930
+ }, 300);
7931
+ });
7932
+ observer2.observe(document.body, { childList: true, subtree: true });
7933
+ setInterval(tryInject, 5e3);
7934
+ }
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
+
7420
8353
  // src/inject/features/title-lock.js
7421
8354
  var TASK_SEL2 = "[data-task-item-key]";
7422
8355
  var SCAN_DEBOUNCE_MS3 = 150;
@@ -7427,7 +8360,7 @@
7427
8360
  var MAP_REFRESH_MS = 6e4;
7428
8361
  var installed3 = false;
7429
8362
  var seenRows = /* @__PURE__ */ new WeakSet();
7430
- var enabled3 = true;
8363
+ var enabled6 = true;
7431
8364
  var pinned = null;
7432
8365
  var resolved = false;
7433
8366
  var fetching = null;
@@ -7442,7 +8375,7 @@
7442
8375
  const t2 = item.querySelector('[class*="truncate"]');
7443
8376
  return t2 ? [t2] : [];
7444
8377
  }
7445
- function findRows(key) {
8378
+ function findRows2(key) {
7446
8379
  return [...document.querySelectorAll(`${TASK_SEL2}[data-task-item-key="${CSS.escape(key)}"]`)];
7447
8380
  }
7448
8381
  async function fetchPinned() {
@@ -7451,8 +8384,8 @@
7451
8384
  const res = await rpc("/pinned-titles");
7452
8385
  if (!res || !res.ok) return null;
7453
8386
  resolved = true;
7454
- enabled3 = res.enabled !== false;
7455
- if (!enabled3) {
8387
+ enabled6 = res.enabled !== false;
8388
+ if (!enabled6) {
7456
8389
  pinned = null;
7457
8390
  return null;
7458
8391
  }
@@ -7467,7 +8400,7 @@
7467
8400
  }
7468
8401
  }
7469
8402
  function isMismatch(item) {
7470
- if (!enabled3 || !pinned) return null;
8403
+ if (!enabled6 || !pinned) return null;
7471
8404
  const key = item.getAttribute("data-task-item-key") || "";
7472
8405
  const want = pinned.get(sessIdOf(key));
7473
8406
  if (want === void 0) return null;
@@ -7485,10 +8418,10 @@
7485
8418
  }
7486
8419
  function scheduleEnforce(key, { immediate = false } = {}) {
7487
8420
  if (immediate) {
7488
- if (enabled3 && pinned) {
8421
+ if (enabled6 && pinned) {
7489
8422
  const want = pinned.get(sessIdOf(key));
7490
8423
  if (want !== void 0) {
7491
- for (const item of findRows(key)) {
8424
+ for (const item of findRows2(key)) {
7492
8425
  for (const el of titleElsOf(item)) writeTitle(el, want);
7493
8426
  }
7494
8427
  }
@@ -7504,7 +8437,7 @@
7504
8437
  const want = map.get(sessIdOf(key));
7505
8438
  if (want === void 0) return;
7506
8439
  let wrote = false;
7507
- for (const item of findRows(key)) {
8440
+ for (const item of findRows2(key)) {
7508
8441
  if (isMismatch(item) !== key) continue;
7509
8442
  for (const el of titleElsOf(item)) writeTitle(el, want);
7510
8443
  wrote = true;
@@ -7520,13 +8453,13 @@
7520
8453
  if (!map) return;
7521
8454
  const want = map.get(sessIdOf(key));
7522
8455
  if (want === void 0) return;
7523
- for (const item of findRows(key)) {
8456
+ for (const item of findRows2(key)) {
7524
8457
  if (isMismatch(item) !== key) continue;
7525
8458
  for (const el of titleElsOf(item)) writeTitle(el, want);
7526
8459
  }
7527
8460
  }
7528
- function scan3() {
7529
- if (!enabled3 || !pinned || pinned.size === 0) return;
8461
+ function scan4() {
8462
+ if (!enabled6 || !pinned || pinned.size === 0) return;
7530
8463
  for (const item of document.querySelectorAll(TASK_SEL2)) {
7531
8464
  const key = isMismatch(item);
7532
8465
  if (!key) {
@@ -7560,23 +8493,23 @@
7560
8493
  if (scanDebounce3) return;
7561
8494
  scanDebounce3 = setTimeout(() => {
7562
8495
  scanDebounce3 = 0;
7563
- scan3();
8496
+ scan4();
7564
8497
  }, SCAN_DEBOUNCE_MS3);
7565
8498
  }
7566
8499
  var mapRefreshTimer = 0;
7567
8500
  function refreshMapSoon() {
7568
8501
  if (mapRefreshTimer) return;
7569
- const tick2 = () => {
8502
+ const tick6 = () => {
7570
8503
  if (document.hidden) {
7571
- mapRefreshTimer = setTimeout(tick2, 1e4);
8504
+ mapRefreshTimer = setTimeout(tick6, 1e4);
7572
8505
  return;
7573
8506
  }
7574
8507
  mapRefreshTimer = 0;
7575
- if (enabled3 && resolved) void fetchPinned().then((m) => {
7576
- if (m) scan3();
8508
+ if (enabled6 && resolved) void fetchPinned().then((m) => {
8509
+ if (m) scan4();
7577
8510
  });
7578
8511
  };
7579
- mapRefreshTimer = setTimeout(tick2, MAP_REFRESH_MS);
8512
+ mapRefreshTimer = setTimeout(tick6, MAP_REFRESH_MS);
7580
8513
  }
7581
8514
  var editingKey = null;
7582
8515
  var bootDelay = 0;
@@ -7590,16 +8523,16 @@
7590
8523
  }
7591
8524
  async function bootstrap() {
7592
8525
  const map = await fetchPinned();
7593
- if (map) scan3();
8526
+ if (map) scan4();
7594
8527
  }
7595
8528
  function setRowTitle(key, text) {
7596
- for (const item of findRows(key)) {
8529
+ for (const item of findRows2(key)) {
7597
8530
  for (const el of titleElsOf(item)) writeTitle(el, text);
7598
8531
  }
7599
8532
  }
7600
8533
  function refreshPinnedTitles() {
7601
8534
  return fetchPinned().then((m) => {
7602
- if (m) scan3();
8535
+ if (m) scan4();
7603
8536
  });
7604
8537
  }
7605
8538
  var RENAME_CTX_VALID_MS = 15e3;
@@ -7618,7 +8551,7 @@
7618
8551
  renameCtx = null;
7619
8552
  return;
7620
8553
  }
7621
- if (!enabled3 || !pinned) return;
8554
+ if (!enabled6 || !pinned) return;
7622
8555
  const want = pinned.get(sessIdOf(renameCtx.key));
7623
8556
  if (want === void 0) return;
7624
8557
  for (const el of document.querySelectorAll('[role="dialog"] input, [role="dialog"] textarea, [role="dialog"] [contenteditable="true"]')) {
@@ -7656,10 +8589,10 @@
7656
8589
  }, true);
7657
8590
  scheduleBootRetry();
7658
8591
  setInterval(() => {
7659
- if (!document.hidden) scan3();
8592
+ if (!document.hidden) scan4();
7660
8593
  }, SCAN_FALLBACK_MS3);
7661
8594
  document.addEventListener("visibilitychange", () => {
7662
- if (!document.hidden) scan3();
8595
+ if (!document.hidden) scan4();
7663
8596
  });
7664
8597
  window.addEventListener("zcodepro:config-changed", () => {
7665
8598
  void bootstrap();
@@ -7675,7 +8608,7 @@
7675
8608
  if (editingKey === null) return;
7676
8609
  editingKey = null;
7677
8610
  void fetchPinned().then((m) => {
7678
- if (m) scan3();
8611
+ if (m) scan4();
7679
8612
  });
7680
8613
  });
7681
8614
  }
@@ -7906,7 +8839,7 @@
7906
8839
  await reclaim(ws);
7907
8840
  blockUntil.set(ws, Date.now() + cooldownMs);
7908
8841
  }
7909
- async function tick() {
8842
+ async function tick3() {
7910
8843
  const cfg = await getConfig();
7911
8844
  if ((cfg.features || {}).idleReclaim === false) {
7912
8845
  idleSince.clear();
@@ -7968,20 +8901,171 @@
7968
8901
  }
7969
8902
  function startIdleReclaim() {
7970
8903
  const loop = () => {
7971
- tick().catch(() => {
8904
+ tick3().catch(() => {
7972
8905
  });
7973
8906
  };
7974
8907
  loop();
7975
8908
  setInterval(loop, CHECK_MS);
7976
8909
  }
7977
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
+
7978
9062
  // src/inject/features/toolbar-icons.js
7979
9063
  var HIDE_CLASS = "zcodepro-tb-hidden";
7980
9064
  var SYNC_DEBOUNCE_MS = 150;
7981
9065
  var SCAN_FALLBACK_MS5 = 5e3;
7982
9066
  var ROW_SEL = "[data-task-item-key]";
7983
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"]';
7984
- var enabled4 = true;
9068
+ var enabled7 = true;
7985
9069
  var rowEl = null;
7986
9070
  var origs = null;
7987
9071
  var syncTimer = 0;
@@ -8089,8 +9173,8 @@
8089
9173
  }
8090
9174
  });
8091
9175
  const newBtn = mk("newTask", parts.newTask, "span.truncate", L.tbNewTask);
8092
- const icon = iconOf(parts.newTask);
8093
- if (icon) newBtn.append(icon);
9176
+ const icon2 = iconOf(parts.newTask);
9177
+ if (icon2) newBtn.append(icon2);
8094
9178
  const searchBtn = mk("search", parts.search, "span.truncate", L.tbSearch);
8095
9179
  const sIcon = iconOf(parts.search);
8096
9180
  if (sIcon) searchBtn.append(sIcon);
@@ -8124,7 +9208,7 @@
8124
9208
  return scroll.getBoundingClientRect().width < NARROW_SIDEBAR_PX;
8125
9209
  }
8126
9210
  function sync() {
8127
- if (!enabled4) return;
9211
+ if (!enabled7) return;
8128
9212
  const parts = findParts();
8129
9213
  if (!parts) {
8130
9214
  if (rowEl) rowEl.style.display = "none";
@@ -8186,8 +9270,8 @@
8186
9270
  }
8187
9271
  async function refreshConfig2() {
8188
9272
  const cfg = await getConfig(true).catch(() => null);
8189
- enabled4 = !cfg || !cfg.features || cfg.features.toolbarIcons !== false;
8190
- if (enabled4) sync();
9273
+ enabled7 = !cfg || !cfg.features || cfg.features.toolbarIcons !== false;
9274
+ if (enabled7) sync();
8191
9275
  else teardown();
8192
9276
  }
8193
9277
  function startToolbarIcons() {
@@ -8213,7 +9297,7 @@
8213
9297
  var STORE_KEY = "zcodepro:pinned-collapsed";
8214
9298
  var SYNC_DEBOUNCE_MS2 = 300;
8215
9299
  var SCAN_FALLBACK_MS6 = 5e3;
8216
- var enabled5 = true;
9300
+ var enabled8 = true;
8217
9301
  var syncTimer2 = 0;
8218
9302
  function findPinnedHeader() {
8219
9303
  for (const h3 of document.querySelectorAll("h3")) {
@@ -8270,7 +9354,7 @@
8270
9354
  }
8271
9355
  h3.classList.add(HEAD_CLASS);
8272
9356
  h3.addEventListener("click", () => {
8273
- if (enabled5) toggle();
9357
+ if (enabled8) toggle();
8274
9358
  });
8275
9359
  const btn = h("button", {
8276
9360
  type: "button",
@@ -8279,7 +9363,7 @@
8279
9363
  "aria-expanded": collapsed ? "false" : "true",
8280
9364
  onClick: (e) => {
8281
9365
  e.stopPropagation();
8282
- if (enabled5) toggle();
9366
+ if (enabled8) toggle();
8283
9367
  }
8284
9368
  });
8285
9369
  btn.append(chevronSvg());
@@ -8287,7 +9371,7 @@
8287
9371
  return true;
8288
9372
  }
8289
9373
  function sync2() {
8290
- if (enabled5) apply2();
9374
+ if (enabled8) apply2();
8291
9375
  }
8292
9376
  function scheduleSync2() {
8293
9377
  if (syncTimer2) return;
@@ -8316,8 +9400,8 @@
8316
9400
  }
8317
9401
  async function refreshConfig3() {
8318
9402
  const cfg = await getConfig(true).catch(() => null);
8319
- enabled5 = !cfg || !cfg.features || cfg.features.pinnedCollapse !== false;
8320
- if (enabled5) sync2();
9403
+ enabled8 = !cfg || !cfg.features || cfg.features.pinnedCollapse !== false;
9404
+ if (enabled8) sync2();
8321
9405
  else teardown2();
8322
9406
  }
8323
9407
  function startPinnedCollapse() {
@@ -8352,12 +9436,12 @@
8352
9436
  return;
8353
9437
  }
8354
9438
  const sig = status.updates.map((u) => `${u.name}:${u.installed}>${u.latest}`).join(",");
8355
- let seen = "";
9439
+ let seen2 = "";
8356
9440
  try {
8357
- seen = window.localStorage.getItem(NOTIFIED_KEY) || "";
9441
+ seen2 = window.localStorage.getItem(NOTIFIED_KEY) || "";
8358
9442
  } catch {
8359
9443
  }
8360
- if (seen === sig) return;
9444
+ if (seen2 === sig) return;
8361
9445
  try {
8362
9446
  window.localStorage.setItem(NOTIFIED_KEY, sig);
8363
9447
  } catch {
@@ -8432,10 +9516,30 @@
8432
9516
  startIdleReclaim();
8433
9517
  } catch {
8434
9518
  }
9519
+ try {
9520
+ startSandboxReclaim();
9521
+ } catch {
9522
+ }
9523
+ try {
9524
+ startPluginCardRetry();
9525
+ } catch {
9526
+ }
8435
9527
  try {
8436
9528
  startToolbarIcons();
8437
9529
  } catch {
8438
9530
  }
9531
+ try {
9532
+ startNewtabPanels();
9533
+ } catch {
9534
+ }
9535
+ try {
9536
+ startStatusBar();
9537
+ } catch {
9538
+ }
9539
+ try {
9540
+ startTurnUsage();
9541
+ } catch {
9542
+ }
8439
9543
  try {
8440
9544
  startPinnedCollapse();
8441
9545
  } catch {