zcode-pro 0.20.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/inject.js CHANGED
@@ -30,6 +30,35 @@
30
30
  function clearConfigCache() {
31
31
  configCache = { value: null, at: 0 };
32
32
  }
33
+ function zcodeAppVersion() {
34
+ const v = liveBoot().zcodeVersion;
35
+ return typeof v === "string" && v ? v : "";
36
+ }
37
+ function zcodeVersionGte(version, major, minor, patch = 0) {
38
+ const m = String(version || "").match(/^(\d+)\.(\d+)\.(\d+)/);
39
+ if (!m) return false;
40
+ const a = Number(m[1]), b = Number(m[2]), c = Number(m[3]);
41
+ return a > major || a === major && (b > minor || b === minor && c >= patch);
42
+ }
43
+ function officialRenameSupported() {
44
+ return zcodeVersionGte(zcodeAppVersion(), 3, 16, 1);
45
+ }
46
+ var newSidebarCached = false;
47
+ function isNewProjectSidebar() {
48
+ if (newSidebarCached) return true;
49
+ if (officialRenameSupported()) {
50
+ newSidebarCached = true;
51
+ return true;
52
+ }
53
+ try {
54
+ if (document.querySelector("section[data-project-id]")) {
55
+ newSidebarCached = true;
56
+ return true;
57
+ }
58
+ } catch {
59
+ }
60
+ return false;
61
+ }
33
62
  var zh = {
34
63
  settingsTitle: "ZCode Pro",
35
64
  tabFeatures: "\u529F\u80FD",
@@ -76,7 +105,7 @@
76
105
  syncRecLink: "\u575A\u679C\u4E91 WebDAV",
77
106
  syncRecSuffix: "\uFF1A\u767B\u5F55\u4E0E\u5BC6\u7801\u586B\u201C\u5E94\u7528\u5BC6\u7801\u201D\uFF0C\u4E0D\u662F\u8D26\u53F7\u5BC6\u7801\u3002",
78
107
  syncIncludeZcp: "ZCode Pro \u8BBE\u7F6E",
79
- syncIncludeZcpDesc: "\u529F\u80FD\u5F00\u5173\u3001\u6837\u5F0F\u8C03\u6574\u3001\u9879\u76EE\u522B\u540D\u4E0E\u4EE3\u7406\u8BBE\u7F6E\uFF1B\u540C\u6B65\u914D\u7F6E\u4E0E\u51ED\u636E\u672C\u8EAB\u4E0D\u53C2\u4E0E\u540C\u6B65\u3002",
108
+ syncIncludeZcpDesc: "\u529F\u80FD\u5F00\u5173\u3001\u6837\u5F0F\u8C03\u6574\u4E0E\u4EE3\u7406\u8BBE\u7F6E\uFF1B\u540C\u6B65\u914D\u7F6E\u4E0E\u51ED\u636E\u672C\u8EAB\u4E0D\u53C2\u4E0E\u540C\u6B65\u3002\u9879\u76EE\u522B\u540D\u4E0D\u540C\u6B65\uFF08\u5B98\u65B9\u5DF2\u652F\u6301\u6539\u9879\u76EE\u540D\uFF09\u3002",
80
109
  syncIncludeModel: "\u6A21\u578B\u8BBE\u7F6E",
81
110
  syncIncludeModelDesc: "\u81EA\u5B9A\u4E49\u4F9B\u5E94\u5546\u4E0E\u6A21\u578B\u914D\u7F6E\uFF08config.json \u4E0E provider_config.json\uFF0C\u542B API Key\uFF09\u3002\u8D26\u53F7\u767B\u5F55\u7684\u51ED\u636E\uFF08\u5982 BigModel \u8BA2\u9605\uFF09\u4E0D\u53C2\u4E0E\u540C\u6B65\uFF0C\u5404\u673A\u5668\u7528\u5404\u81EA\u8D26\u53F7\u767B\u5F55\u3002",
82
111
  syncUpload: "\u4E0A\u4F20\u5230\u4E91\u7AEF",
@@ -176,8 +205,8 @@
176
205
  titleGenTesting: "\u751F\u6210\u4E2D\u2026",
177
206
  titleGenTestOk: "\u751F\u6210\u7ED3\u679C\uFF1A",
178
207
  titleGenTestFailed: "\u751F\u6210\u5931\u8D25",
179
- featureWsRunning: "\u6298\u53E0\u9879\u76EE\u8FD0\u884C\u63D0\u793A\uFF08\u5B9E\u9A8C\u6027\uFF09",
180
- featureWsRunningDesc: "\u6298\u53E0\u7684\u9879\u76EE\u91CC\u4ECD\u6709\u4F1A\u8BDD\u5728\u8FD0\u884C\u65F6\uFF0C\u9879\u76EE\u56FE\u6807\u65CB\u8F6C\u63D0\u793A\uFF1B\u5168\u90E8\u7ED3\u675F\u6216\u5C55\u5F00\u9879\u76EE\u540E\u6062\u590D\u3002\u671F\u95F4\u6BCF 60 \u79D2\u5728\u540E\u53F0\u77ED\u6682\u5C55\u5F00\u8BE5\u9879\u76EE\u590D\u6838\u8FD0\u884C\u72B6\u6001\u518D\u6536\u8D77\uFF0C\u4F1A\u8BDD\u884C\u4E0D\u663E\u793A\uFF0C\u4EC5\u9879\u76EE\u4F4D\u7F6E\u53EF\u80FD\u6709\u7EA6\u56DB\u5206\u4E4B\u4E00\u79D2\u7684\u8F7B\u5FAE\u9AD8\u5EA6\u8D77\u4F0F\u3002",
208
+ featureWsRunning: "\u6298\u53E0\u65F6\u4EFB\u52A1\u8FD0\u884C\u4E2D\u63D0\u793A",
209
+ featureWsRunningDesc: "\u6709\u4F1A\u8BDD\u5728\u8FD0\u884C\u65F6\uFF0C\u6298\u53E0\u7684\u9879\u76EE\u4E0E\u201C\u4EFB\u52A1\u201D\u201C\u5DF2\u7F6E\u9876\u201D\u7B49\u5206\u533A\u5934\u663E\u793A\u4E0E\u4EFB\u52A1\u8FD0\u884C\u4E2D\u540C\u6B3E\u7684\u52A0\u8F7D\u56FE\u6807\uFF0C\u5168\u90E8\u7ED3\u675F\u540E\u6D88\u5931\uFF1B3.16.1 \u4E4B\u524D\u6BCF 60 \u79D2\u4F1A\u5728\u540E\u53F0\u77ED\u6682\u5C55\u5F00\u9879\u76EE\u590D\u6838\u518D\u6536\u8D77\u3002",
181
210
  tabPerformance: "\u6027\u80FD",
182
211
  perfDesc: "ZCode \u4E3A\u6BCF\u4E2A\u6253\u5F00\u8FC7\u7684\u9879\u76EE\u5E38\u9A7B\u4E00\u4E2A\u4F1A\u8BDD\u8FDB\u7A0B\uFF0C\u5E94\u7528\u81EA\u8EAB\u4E0D\u4F1A\u56DE\u6536\uFF0C\u9879\u76EE\u591A\u4E86\u5185\u5B58\u5360\u7528\u5C31\u9AD8\u3002\u5F00\u542F\u540E\uFF0C\u957F\u65F6\u95F4\u672A\u4F7F\u7528\u4E14\u6CA1\u6709\u4EFB\u52A1\u5728\u8DD1\u7684\u9879\u76EE\u4F1A\u88AB\u81EA\u52A8\u91CA\u653E\uFF1A\u4F1A\u8BDD\u8BB0\u5F55\u4E0D\u53D7\u5F71\u54CD\uFF0C\u518D\u6B21\u6253\u5F00\u65F6\u81EA\u52A8\u6062\u590D\uFF0C\u4EC5\u9996\u6B21\u56DE\u590D\u6709\u51E0\u79D2\u7B49\u5F85\u3002\u6B63\u5728\u4F7F\u7528\u7684\u9879\u76EE\u3001\u8FD0\u884C\u4E2D\u7684\u4F1A\u8BDD\u4E0E\u540E\u53F0\u547D\u4EE4\u3001\u5B9A\u65F6\u4EFB\u52A1\u90FD\u4E0D\u4F1A\u88AB\u56DE\u6536\u3002\u540E\u53F0\u547D\u4EE4\u68C0\u6D4B\u4F9D\u8D56\u7CFB\u7EDF\u8FDB\u7A0B\u4FE1\u606F\uFF08Linux / macOS / Windows \u5404\u8D70\u7CFB\u7EDF\u81EA\u5E26\u5DE5\u5177\uFF09\uFF0C\u68C0\u6D4B\u4E0D\u53EF\u7528\u65F6\u529F\u80FD\u81EA\u52A8\u505C\u7528\u3002",
183
212
  featureIdleReclaim: "\u7A7A\u95F2\u9879\u76EE\u5185\u5B58\u56DE\u6536",
@@ -217,17 +246,16 @@
217
246
  imageCopied: "\u56FE\u7247\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F",
218
247
  imageCopyFailed: "\u590D\u5236\u56FE\u7247\u5931\u8D25",
219
248
  openFolderItem: "\u6253\u5F00\u6587\u4EF6\u5939",
249
+ openFolderPrimary: "\u4E3B\u76EE\u5F55",
220
250
  copyPathItem: "\u590D\u5236\u8DEF\u5F84",
221
251
  pathCopied: "\u8DEF\u5F84\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F",
222
252
  pathCopyFailed: "\u590D\u5236\u8DEF\u5F84\u5931\u8D25",
223
253
  fileOpenDefault: "\u9ED8\u8BA4\u5E94\u7528\u6253\u5F00",
224
254
  fileReveal: "\u6253\u5F00\u6240\u5728\u76EE\u5F55",
225
- featurePinnedExpand: "\u7F6E\u9876\u4F1A\u8BDD\u4FDD\u6301\u9879\u76EE\u6298\u53E0\uFF08\u5B9E\u9A8C\u6027\uFF09",
226
- featurePinnedExpandDesc: "\u70B9\u51FB\u6298\u53E0\u9879\u76EE\u7684\u7F6E\u9876\u4F1A\u8BDD\u540E\u5C06\u5176\u4FDD\u6301\u6298\u53E0\u3002\u53D7\u9650\u4E8E\u5E94\u7528\u673A\u5236\uFF0C\u9879\u76EE\u4F1A\u5148\u77ED\u6682\u5C55\u5F00\u518D\u7F29\u8D77\u3002",
227
- featureAutoUpdatePlugins: "\u81EA\u52A8\u66F4\u65B0 zcode-plugins \u63D2\u4EF6",
228
- featureAutoUpdatePluginsDesc: "\u542F\u52A8\u65F6\u81EA\u52A8\u68C0\u67E5\u5E76\u66F4\u65B0\u5DF2\u5B89\u88C5\u7684 zcode-plugins \u63D2\u4EF6\uFF08\u53EA\u5347\u7EA7\u4E0D\u5B89\u88C5\uFF0C\u5378\u8F7D\u4E0D\u4F1A\u88AB\u64A4\u9500\uFF09\uFF0C\u66F4\u65B0\u5728\u65B0\u4F1A\u8BDD\u751F\u6548\u3002",
229
- pluginsUpdateTitle: "\u63D2\u4EF6\u66F4\u65B0 \xB7 zcode-plugins",
230
- pluginsCheckNow: "\u68C0\u67E5\u5E76\u66F4\u65B0",
255
+ featureAutoUpdatePlugins: "\u81EA\u52A8\u66F4\u65B0",
256
+ featureAutoUpdatePluginsRepo: "duanluan/zcode-plugins",
257
+ featureAutoUpdatePluginsDesc: "",
258
+ pluginsCheckNow: "\u68C0\u67E5\u66F4\u65B0",
231
259
  pluginsUpdating: "\u6B63\u5728\u66F4\u65B0\u63D2\u4EF6\u2026",
232
260
  pluginsUpToDate: "\u63D2\u4EF6\u5DF2\u662F\u6700\u65B0",
233
261
  pluginsUpdatesFound: "{n} \u4E2A\u63D2\u4EF6\u53EF\u66F4\u65B0",
@@ -305,6 +333,8 @@
305
333
  hrUsageNone: "\u5F53\u524D\u6CA1\u6709\u8D70 headroom \u7684\u4F9B\u5E94\u5546\uFF0C\u672A\u5207\u6362",
306
334
  headroomLoadFailed: "\u8BFB\u53D6 Headroom \u914D\u7F6E\u5931\u8D25",
307
335
  headroomStatusTitle: "\u8FD0\u884C\u72B6\u6001",
336
+ hrInstRunning: "\u8FD0\u884C\u4E2D",
337
+ hrInstStopped: "\u672A\u8FD0\u884C",
308
338
  headroomStatusLoadFailed: "\u8BFB\u53D6\u8FD0\u884C\u72B6\u6001\u5931\u8D25",
309
339
  headroomProxyUp: "\u4EE3\u7406\u8FD0\u884C\u4E2D",
310
340
  headroomProxyDown: "\u4EE3\u7406\u672A\u8FD0\u884C",
@@ -370,6 +400,30 @@
370
400
  aliasSaved: "\u522B\u540D\u5DF2\u66F4\u65B0",
371
401
  aliasCleared: "\u5DF2\u6062\u590D\u771F\u5B9E\u540D\u79F0",
372
402
  aliasHint: "\u78C1\u76D8\u76EE\u5F55\u540D\u4E0D\u53D8\uFF1A\u7EC8\u7AEF\u3001\u6587\u4EF6\u7BA1\u7406\u5668\u4E0E\u5176\u4ED6\u5F15\u7528\u771F\u5B9E\u8DEF\u5F84\u7684\u754C\u9762\u4ECD\u663E\u793A\u539F\u540D\u3002",
403
+ featureProjectMenuDescNew: "\u9879\u76EE\u201C\u66F4\u591A\u201D\u83DC\u5355\u4E2D\u7684\u5207\u6362\u6587\u4EF6\u5939\u3001\u6253\u5F00\u6587\u4EF6\u5939\u4E0E\u590D\u5236\u8DEF\u5F84\u3002\u5B98\u65B9\u5DF2\u652F\u6301\u4FEE\u6539\u9879\u76EE\u540D\u79F0\uFF08\u7F16\u8F91\u9879\u76EE\uFF09\uFF0C\u81EA\u5B9A\u4E49\u522B\u540D\u5DF2\u505C\u7528\u3002",
404
+ aliasMigrateTitle: "\u8FC1\u79FB\u9879\u76EE\u522B\u540D",
405
+ aliasMigrateDesc: "\u5F53\u524D ZCode \u5DF2\u652F\u6301\u4FEE\u6539\u9879\u76EE\u540D\u79F0\uFF08\u9879\u76EE\u201C\u66F4\u591A\u201D\u83DC\u5355 \u2192 \u7F16\u8F91\u9879\u76EE\uFF09\uFF0C\u81EA\u5B9A\u4E49\u522B\u540D\u529F\u80FD\u5DF2\u505C\u7528\u3002\u53EF\u628A\u5DF2\u4FDD\u5B58\u7684\u522B\u540D\u5199\u5165\u5B98\u65B9\u9879\u76EE\u540D\u79F0\uFF0C\u52FE\u9009\u8981\u8FC1\u79FB\u7684\u9879\u76EE\uFF1B\u8FC1\u79FB\u53EA\u6539\u9879\u76EE\u540D\u79F0\uFF0C\u78C1\u76D8\u76EE\u5F55\u4E0D\u53D7\u5F71\u54CD\u3002",
406
+ aliasMigrateItemHint: "\u8FC1\u79FB\u540E\u5C06\u4F5C\u4E3A\u9879\u76EE\u540D\u79F0\u4FDD\u5B58\uFF08\u53EF\u968F\u65F6\u5728\u201C\u7F16\u8F91\u9879\u76EE\u201D\u4E2D\u518D\u6539\uFF09\u3002\u4EC5\u65E0\u6807\u8BB0\u7684\u6761\u76EE\u53EF\u52FE\u9009\uFF1B\u5DF2\u8FC1\u79FB\u3001\u5DF2\u5E76\u5165\u3001\u9879\u76EE\u5DF2\u5220\u9664\u3001\u672C\u673A\u4E0D\u5B58\u5728\uFF08\u65E7\u673A\u5668\u8DEF\u5F84\uFF09\u4EE5\u53CA\u9879\u76EE\u5DF2\u91CD\u547D\u540D\u7684\u90FD\u4E0D\u53EF\u52FE\u9009\u2014\u2014\u8981\u6539\u8FD9\u4E9B\u9879\u76EE\u7684\u540D\u5B57\uFF0C\u76F4\u63A5\u7528\u9879\u76EE\u201C\u66F4\u591A\u201D\u83DC\u5355\u91CC\u7684\u201C\u7F16\u8F91\u9879\u76EE\u201D\u3002",
407
+ aliasMigrateStateDone: "\u5DF2\u8FC1\u79FB",
408
+ aliasMigrateStateMerged: "\u5DF2\u5E76\u5165\u300C{name}\u300D",
409
+ aliasMigrateStateDeleted: "\u9879\u76EE\u5DF2\u5220\u9664",
410
+ aliasMigrateStateMissing: "\u672C\u673A\u4E0D\u5B58\u5728",
411
+ aliasMigrateStateRenamed: "\u9879\u76EE\u5DF2\u91CD\u547D\u540D",
412
+ aliasMigrateDeadSummary: "\u5DF2\u5931\u6548 {n} \u6761\uFF08\u672C\u673A\u4E0D\u5B58\u5728 / \u9879\u76EE\u5DF2\u5220\u9664\uFF0C\u591A\u4E3A\u8BBE\u7F6E\u540C\u6B65\u5E26\u6765\u7684\u65E7\u673A\u5668\u8DEF\u5F84\uFF09",
413
+ aliasMigrateClearDead: "\u6E05\u9664\u5931\u6548\u6761\u76EE",
414
+ aliasMigrateDeadCleared: "\u5DF2\u6E05\u9664 {n} \u6761\u5931\u6548\u522B\u540D",
415
+ aliasMigrateDeadClearFailed: "\u90E8\u5206\u5931\u6548\u522B\u540D\u672A\u80FD\u6E05\u9664\uFF0C\u8BF7\u91CD\u8BD5",
416
+ aliasMigrateGo: "\u8FC1\u79FB\u6240\u9009",
417
+ aliasMigrateRunning: "\u8FC1\u79FB\u4E2D\u2026",
418
+ aliasMigrateProgress: "\u6B63\u5728\u8FC1\u79FB {i}/{n}\uFF1A{name}",
419
+ aliasMigrateStateFailed: "\u5931\u8D25",
420
+ aliasMigrateSkip: "\u6682\u4E0D\u8FC1\u79FB",
421
+ aliasMigrateDone: "\u5DF2\u8FC1\u79FB {n} \u4E2A\u522B\u540D",
422
+ aliasMigratePartial: "{n} \u4E2A\u5DF2\u8FC1\u79FB\uFF0C{m} \u4E2A\u672A\u6210\u529F\uFF1B\u672A\u6210\u529F\u7684\u53EF\u7A0D\u540E\u5728 ZCode Pro \u8BBE\u7F6E\u4E2D\u91CD\u8BD5",
423
+ aliasMigrateAllFailed: "\u522B\u540D\u8FC1\u79FB\u672A\u6210\u529F\uFF0C\u53EF\u7A0D\u540E\u5728 ZCode Pro \u8BBE\u7F6E\u4E2D\u91CD\u8BD5",
424
+ aliasMigrateCardName: "\u522B\u540D\u8FC1\u79FB",
425
+ aliasMigrateCardDesc: "\u5B98\u65B9\u5DF2\u652F\u6301\u4FEE\u6539\u9879\u76EE\u540D\u79F0\uFF0C\u8FD8\u6709 {n} \u4E2A\u81EA\u5B9A\u4E49\u522B\u540D\u672A\u8FC1\u79FB\u3002",
426
+ aliasMigrateRun: "\u8FC1\u79FB",
373
427
  taskOrderSaved: "\u987A\u5E8F\u5DF2\u66F4\u65B0",
374
428
  qqGroupTitle: "QQ \u7FA4\uFF1A428403354",
375
429
  qqGroupDesc: "\u95EE\u9898\u53CD\u9988\u4E0E\u4EA4\u6D41",
@@ -441,7 +495,7 @@
441
495
  syncRecLink: "Jianguoyun WebDAV",
442
496
  syncRecSuffix: " \u2014 use the app password, not your account password.",
443
497
  syncIncludeZcp: "ZCode Pro settings",
444
- syncIncludeZcpDesc: "Feature toggles, style adjustments, project aliases and the proxy setting; the sync config and credentials themselves are never synced.",
498
+ syncIncludeZcpDesc: "Feature toggles, style adjustments and the proxy setting; the sync config and credentials themselves are never synced. Project aliases are not synced (renaming is built in now).",
445
499
  syncIncludeModel: "Model settings",
446
500
  syncIncludeModelDesc: "Custom providers and model config (config.json and provider_config.json, API keys included). Account sign-in credentials (e.g. the BigModel subscription) are not synced; each computer signs in with its own account.",
447
501
  syncUpload: "Upload",
@@ -541,8 +595,8 @@
541
595
  titleGenTesting: "Generating\u2026",
542
596
  titleGenTestOk: "Result: ",
543
597
  titleGenTestFailed: "Generation failed",
544
- featureWsRunning: "Running indicator on collapsed projects (experimental)",
545
- featureWsRunningDesc: "Spins a collapsed project's icon while any of its sessions is still running; stops when they all finish or the project is expanded. While spinning, the project is briefly re-expanded in the background every 60 s to re-check and then collapsed \u2014 rows stay hidden, with only a subtle quarter-second height ripple at the project.",
598
+ featureWsRunning: "Running indicator when collapsed",
599
+ featureWsRunningDesc: "Shows the same spinning loader as running tasks on collapsed projects and on section headers (\u201CTasks\u201D, \u201CPinned\u201D) while any session is running; clears when they all finish. Before ZCode 3.16.1 the project is briefly re-expanded in the background every 60 s to re-check.",
546
600
  tabPerformance: "Performance",
547
601
  perfDesc: "ZCode keeps a session process resident for every project you have opened and never reclaims it, so memory grows with projects. When enabled, projects unused for a while with nothing running release their memory automatically: sessions are untouched and reload when reopened, with a few seconds wait on the first reply. The project in use, running sessions, background commands and scheduled tasks are never reclaimed. Background-command detection reads system process info via built-in tools (Linux / macOS / Windows); when unavailable, the feature disables itself.",
548
602
  featureIdleReclaim: "Idle project memory reclaim",
@@ -582,17 +636,16 @@
582
636
  imageCopied: "Image copied to the clipboard.",
583
637
  imageCopyFailed: "Failed to copy the image.",
584
638
  openFolderItem: "Open folder",
639
+ openFolderPrimary: "primary",
585
640
  copyPathItem: "Copy path",
586
641
  pathCopied: "Path copied to the clipboard.",
587
642
  pathCopyFailed: "Failed to copy the path.",
588
643
  fileOpenDefault: "Open with default app",
589
644
  fileReveal: "Reveal in file manager",
590
- featurePinnedExpand: "Keep projects collapsed for pinned sessions (experimental)",
591
- featurePinnedExpandDesc: "Keeps the project collapsed after clicking a pinned session. Note: it briefly expands first, then collapses.",
592
- featureAutoUpdatePlugins: "Auto-update zcode-plugins plugins",
593
- featureAutoUpdatePluginsDesc: "Checks and updates installed zcode-plugins plugins on startup (updates only \u2014 never installs; uninstalled plugins stay uninstalled). Updates apply to new sessions.",
594
- pluginsUpdateTitle: "Plugin updates \xB7 zcode-plugins",
595
- pluginsCheckNow: "Check & update",
645
+ featureAutoUpdatePlugins: "Auto-update",
646
+ featureAutoUpdatePluginsRepo: "duanluan/zcode-plugins",
647
+ featureAutoUpdatePluginsDesc: "",
648
+ pluginsCheckNow: "Check for updates",
596
649
  pluginsUpdating: "Updating plugins\u2026",
597
650
  pluginsUpToDate: "Plugins are up to date",
598
651
  pluginsUpdatesFound: "{n} plugin(s) can be updated",
@@ -670,6 +723,8 @@
670
723
  hrUsageNone: "No providers route through headroom; nothing changed",
671
724
  headroomLoadFailed: "Failed to load the Headroom config",
672
725
  headroomStatusTitle: "Status",
726
+ hrInstRunning: "running",
727
+ hrInstStopped: "down",
673
728
  headroomStatusLoadFailed: "Failed to load the status",
674
729
  headroomProxyUp: "Proxy running",
675
730
  headroomProxyDown: "Proxy down",
@@ -735,6 +790,30 @@
735
790
  aliasSaved: "Alias updated.",
736
791
  aliasCleared: "Real name restored.",
737
792
  aliasHint: "The directory name on disk is unchanged: terminals, file managers and other path-based UI still show the real name.",
793
+ featureProjectMenuDescNew: 'Switch folder, open folder and copy path in the project "More" menu. Renaming is built in since ZCode 3.16.1 ("Edit project"), so the custom alias is retired.',
794
+ aliasMigrateTitle: "Migrate project aliases",
795
+ aliasMigrateDesc: 'This ZCode supports renaming projects (project "More" menu \u2192 Edit project), so the custom alias feature is retired. Write your saved aliases into the official project names by picking the projects below; only the project name changes, folders on disk are untouched.',
796
+ aliasMigrateItemHint: 'The alias becomes the project name (re-editable anytime via "Edit project"). Only unmarked entries can be checked; migrated, merged, deleted, not-on-this-machine and manually-renamed ones cannot \u2014 to rename those projects, use "Edit project" in the project "More" menu directly.',
797
+ aliasMigrateStateDone: "Migrated",
798
+ aliasMigrateStateMerged: 'Merged into "{name}"',
799
+ aliasMigrateStateDeleted: "Project deleted",
800
+ aliasMigrateStateMissing: "Not on this machine",
801
+ aliasMigrateStateRenamed: "Project renamed",
802
+ aliasMigrateDeadSummary: "{n} stale entries (not on this machine / project deleted \u2014 usually old-machine paths from settings sync)",
803
+ aliasMigrateClearDead: "Clear stale entries",
804
+ aliasMigrateDeadCleared: "Removed {n} stale alias(es)",
805
+ aliasMigrateDeadClearFailed: "Some stale aliases could not be removed; please retry",
806
+ aliasMigrateGo: "Migrate selected",
807
+ aliasMigrateRunning: "Migrating\u2026",
808
+ aliasMigrateProgress: "Migrating {i}/{n}: {name}",
809
+ aliasMigrateStateFailed: "Failed",
810
+ aliasMigrateSkip: "Not now",
811
+ aliasMigrateDone: "Migrated {n} alias(es)",
812
+ aliasMigratePartial: "{n} migrated, {m} failed; retry the rest later in ZCode Pro settings",
813
+ aliasMigrateAllFailed: "Alias migration failed; retry later in ZCode Pro settings",
814
+ aliasMigrateCardName: "Alias migration",
815
+ aliasMigrateCardDesc: "Project renaming is built in now; {n} custom alias(es) are not migrated yet.",
816
+ aliasMigrateRun: "Migrate",
738
817
  taskOrderSaved: "Order updated.",
739
818
  qqGroupTitle: "QQ group: 428403354",
740
819
  qqGroupDesc: "Feedback & discussion",
@@ -918,7 +997,7 @@
918
997
  var overlayClassBare = "fixed inset-0 isolate z-50 flex items-center justify-center duration-100 p-4 platform-linux-desktop:top-12";
919
998
  var contentClass = "w-full sm:max-w-md rounded-2xl border-none bg-popover/98 p-5 text-ui-base/relaxed text-foreground ring-border shadow-2xl";
920
999
  var dialogStack = [];
921
- function openDialog({ title, description, onMount, onClose, width = "sm:max-w-md", overlay = "dim", draggable = false, posKey = "", dismissOnOutside = true, bodyClass = "" }) {
1000
+ function openDialog({ title, description, onMount, onClose, width = "sm:max-w-md", overlay = "dim", draggable = false, posKey = "", dismissOnOutside = true, bodyClass = "", closable = false }) {
922
1001
  const L = t();
923
1002
  const prevActive = document.activeElement;
924
1003
  let cleanupDrag = null;
@@ -958,8 +1037,25 @@
958
1037
  content.style.pointerEvents = "auto";
959
1038
  }
960
1039
  const titleEl = h("h2", { class: "text-lg font-semibold leading-none tracking-tight text-foreground" }, title);
961
- content.append(
1040
+ const titleRow = closable ? h(
1041
+ "div",
1042
+ { class: "flex items-start justify-between gap-2" },
962
1043
  titleEl,
1044
+ h("button", {
1045
+ type: "button",
1046
+ class: "zcodepro-close",
1047
+ title: L.close,
1048
+ "aria-label": L.close,
1049
+ onMousedown: (e) => {
1050
+ e.stopPropagation();
1051
+ },
1052
+ onClick: () => {
1053
+ close();
1054
+ }
1055
+ }, "\u2715")
1056
+ ) : titleEl;
1057
+ content.append(
1058
+ titleRow,
963
1059
  ...description ? [h("p", { class: "mt-2 text-ui-sm/relaxed text-foreground-subtle" }, description)] : []
964
1060
  );
965
1061
  const body = h("div", { class: "zcodepro-dialog-body " + (bodyClass || "mt-4") });
@@ -1154,13 +1250,13 @@
1154
1250
  }
1155
1251
  function unitField({ value = null, fallback = { value: 100, unit: "%" }, step = { px: 10, "%": 1 }, onCommit }) {
1156
1252
  const RANGES = { px: [320, 3840], "%": [20, 100] };
1157
- const norm2 = (v) => {
1253
+ const norm3 = (v) => {
1158
1254
  if (!v || !RANGES[v.unit]) return null;
1159
1255
  const [min, max] = RANGES[v.unit];
1160
1256
  const n = v.unit === "px" ? Math.round(v.value) : Math.round(v.value * 10) / 10;
1161
1257
  return Number.isFinite(n) ? { value: Math.min(max, Math.max(min, n)), unit: v.unit } : null;
1162
1258
  };
1163
- let current2 = norm2(value) || norm2(fallback) || { value: 100, unit: "%" };
1259
+ let current2 = norm3(value) || norm3(fallback) || { value: 100, unit: "%" };
1164
1260
  const input = h("input", {
1165
1261
  type: "text",
1166
1262
  inputmode: "decimal",
@@ -1171,7 +1267,7 @@
1171
1267
  input.value = `${current2.value}${current2.unit}`;
1172
1268
  };
1173
1269
  const commit = (v) => {
1174
- const next = norm2(v);
1270
+ const next = norm3(v);
1175
1271
  if (!next || next.value === current2.value && next.unit === current2.unit) {
1176
1272
  display();
1177
1273
  return;
@@ -1208,7 +1304,7 @@
1208
1304
  return {
1209
1305
  el: input,
1210
1306
  reset() {
1211
- current2 = norm2(fallback) || current2;
1307
+ current2 = norm3(fallback) || current2;
1212
1308
  display();
1213
1309
  },
1214
1310
  get() {
@@ -1247,7 +1343,7 @@
1247
1343
  "div",
1248
1344
  { class: "min-w-0 flex-1" },
1249
1345
  h("div", { class: "text-ui-sm font-medium text-foreground" }, name),
1250
- ...desc ? [h("div", { class: "mt-0.5 text-ui-xs/relaxed text-foreground-subtle" }, desc)] : []
1346
+ h("div", { class: "mt-0.5 text-ui-xs/relaxed text-foreground-subtle", "data-zcodepro-row-desc": "" }, desc ?? "")
1251
1347
  ),
1252
1348
  ...extraEl ? [extraEl] : [],
1253
1349
  h("button", {
@@ -1525,13 +1621,9 @@
1525
1621
  .zcodepro-imgmenu-item:hover {
1526
1622
  background-color: var(--color-accent, color-mix(in oklab, var(--color-foreground, #888) 10%, transparent));
1527
1623
  }
1528
- /* \u6298\u53E0\u9879\u76EE\u8FD0\u884C\u63D0\u793A\uFF08ws-running.js\uFF09\uFF1A\u9879\u76EE\u56FE\u6807\u65CB\u8F6C\u3002\u52A8\u753B\u7C7B\u6302\u5728\u56FE\u6807\u5916\u5C42\u7684
1529
- \u7A33\u5B9A span \u4E0A\uFF0C\u5C55\u5F00/\u6536\u8D77\u65F6\u5E94\u7528\u91CD\u5EFA svg \u4E5F\u4E0D\u53D7\u5F71\u54CD\uFF1B\u65F6\u957F\u4E0E\u5E94\u7528 loader \u4E00\u81F4 */
1530
- @keyframes zcodepro-ws-spin { to { transform: rotate(360deg); } }
1531
- span.zcodepro-ws-running > svg {
1532
- animation: zcodepro-ws-spin 1s linear infinite;
1533
- transform-origin: center;
1534
- }
1624
+ /* \u6298\u53E0\u9879\u76EE\u8FD0\u884C\u63D0\u793A\uFF1Afolder \u56FE\u6807\u8BA9\u4F4D\u7ED9 loader\uFF08loader \u7531\u811A\u672C\u63D2\u5165\uFF0C
1625
+ folder \u53EF\u80FD\u88AB React \u968F\u65F6\u91CD\u5EFA\uFF0C\u9760\u7C7B\u9009\u62E9\u5668\u7EDF\u4E00\u9690\u85CF\uFF0C\u540E\u4EE3\u5199\u6CD5\u517C\u5BB9\u5305\u88C5\u5C42\uFF09 */
1626
+ span.zcodepro-ws-running svg:not(.zcodepro-ws-loader) { display: none !important; }
1535
1627
  /* \u65E0\u611F\u63A2\u67E5\u671F\u95F4\u76D6\u5728\u9879\u76EE\u884C\u4E0A\u7684\u51BB\u7ED3\u514B\u9686\uFF1A\u56FA\u5B9A\u5B9A\u4F4D\u3001\u4E0D\u54CD\u5E94\u6307\u9488 */
1536
1628
  .zcodepro-ws-frozen {
1537
1629
  position: fixed;
@@ -1645,12 +1737,82 @@
1645
1737
  }
1646
1738
  .zcodepro-pin-head:hover .zcodepro-pin-chevron svg { opacity: 1; }
1647
1739
  .zcodepro-pin-collapsed .zcodepro-pin-chevron svg { transform: rotate(-90deg); }
1648
- .zcodepro-pin-collapsed > ul { display: none !important; }
1740
+ .zcodepro-pin-collapsed > :is(ul, div):has(li[data-task-item-key]) { display: none !important; }
1649
1741
  /* \u6298\u53E0\u540E\u53BB\u6389\u6807\u9898\u81EA\u5E26\u7684\u5E95\u90E8\u7559\u767D\uFF0C\u4E0E\u4E0B\u4E00\u5206\u533A\u7684\u95F4\u9694\u548C\u5176\u4ED6\u5206\u533A\u8282\u594F\u4E00\u81F4 */
1650
1742
  .zcodepro-pin-collapsed .zcodepro-pin-head { padding-bottom: 0; }
1651
1743
  `));
1652
1744
  }
1653
1745
 
1746
+ // src/inject/features/copy-path.js
1747
+ async function appendCopyPathItem(menu, anchorItem, project) {
1748
+ if (menu.querySelector('[data-zcodepro-item="copy-path"]')) return;
1749
+ const L = t();
1750
+ const item = anchorItem.cloneNode(true);
1751
+ item.removeAttribute("data-testid");
1752
+ item.removeAttribute("data-highlighted");
1753
+ item.setAttribute("data-zcodepro-item", "copy-path");
1754
+ for (const child of [...item.childNodes]) child.remove();
1755
+ const origIcon = anchorItem.querySelector("svg");
1756
+ const iconClass = origIcon ? origIcon.getAttribute("class") : "h-3.5 w-3.5";
1757
+ item.append(hCopyIcon(iconClass), document.createTextNode(L.copyPathItem));
1758
+ item.addEventListener("mouseenter", () => {
1759
+ for (const el of menu.querySelectorAll('[role="menuitem"]')) el.removeAttribute("data-highlighted");
1760
+ item.setAttribute("data-highlighted", "");
1761
+ });
1762
+ item.addEventListener("mouseleave", () => item.removeAttribute("data-highlighted"));
1763
+ item.addEventListener("click", () => {
1764
+ closeRadixMenu(menu);
1765
+ void copyText(project.path);
1766
+ });
1767
+ anchorItem.before(item);
1768
+ }
1769
+ async function copyText(text) {
1770
+ const L = t();
1771
+ if (await copyToClipboard(text)) showToast(L.pathCopied);
1772
+ else showToast(L.pathCopyFailed, "error");
1773
+ }
1774
+ async function copyToClipboard(text) {
1775
+ try {
1776
+ await navigator.clipboard.writeText(text);
1777
+ return true;
1778
+ } catch {
1779
+ return copyViaHiddenInput(text);
1780
+ }
1781
+ }
1782
+ function copyViaHiddenInput(text) {
1783
+ try {
1784
+ const input = h("input", { type: "text", style: "position:fixed;left:-9999px;top:0;opacity:0" });
1785
+ input.value = text;
1786
+ document.body.append(input);
1787
+ input.select();
1788
+ const ok = document.execCommand("copy");
1789
+ input.remove();
1790
+ return ok;
1791
+ } catch {
1792
+ return false;
1793
+ }
1794
+ }
1795
+ function hCopyIcon(cls) {
1796
+ const ns = "http://www.w3.org/2000/svg";
1797
+ const svg = document.createElementNS(ns, "svg");
1798
+ svg.setAttribute("viewBox", "0 0 24 24");
1799
+ svg.setAttribute("fill", "none");
1800
+ svg.setAttribute("stroke", "currentColor");
1801
+ svg.setAttribute("stroke-width", "2");
1802
+ svg.setAttribute("stroke-linecap", "round");
1803
+ svg.setAttribute("stroke-linejoin", "round");
1804
+ svg.setAttribute("class", cls);
1805
+ for (const d of [
1806
+ "M8 8h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V10a2 2 0 0 1 2-2Z",
1807
+ "M4 16c-1.1 0-2-.9-2-2V4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2"
1808
+ ]) {
1809
+ const p = document.createElementNS(ns, "path");
1810
+ p.setAttribute("d", d);
1811
+ svg.append(p);
1812
+ }
1813
+ return svg;
1814
+ }
1815
+
1654
1816
  // src/inject/features/alias.js
1655
1817
  var norm = (p) => String(p || "").replace(/[\\/]+$/, "");
1656
1818
  var basenameOf = (p) => norm(p).split(/[\\/]/).pop() || "";
@@ -1679,6 +1841,7 @@
1679
1841
  return false;
1680
1842
  }
1681
1843
  async function startAliasWatcher() {
1844
+ if (isNewProjectSidebar()) return;
1682
1845
  await syncFromConfig();
1683
1846
  if (observer) return;
1684
1847
  observer = new MutationObserver((muts) => {
@@ -2053,76 +2216,6 @@
2053
2216
  return svg;
2054
2217
  }
2055
2218
 
2056
- // src/inject/features/copy-path.js
2057
- async function appendCopyPathItem(menu, anchorItem, project) {
2058
- if (menu.querySelector('[data-zcodepro-item="copy-path"]')) return;
2059
- const L = t();
2060
- const item = anchorItem.cloneNode(true);
2061
- item.removeAttribute("data-testid");
2062
- item.removeAttribute("data-highlighted");
2063
- item.setAttribute("data-zcodepro-item", "copy-path");
2064
- for (const child of [...item.childNodes]) child.remove();
2065
- const origIcon = anchorItem.querySelector("svg");
2066
- const iconClass = origIcon ? origIcon.getAttribute("class") : "h-3.5 w-3.5";
2067
- item.append(hCopyIcon(iconClass), document.createTextNode(L.copyPathItem));
2068
- item.addEventListener("mouseenter", () => {
2069
- for (const el of menu.querySelectorAll('[role="menuitem"]')) el.removeAttribute("data-highlighted");
2070
- item.setAttribute("data-highlighted", "");
2071
- });
2072
- item.addEventListener("mouseleave", () => item.removeAttribute("data-highlighted"));
2073
- item.addEventListener("click", () => {
2074
- closeRadixMenu(menu);
2075
- void copyText(project.path);
2076
- });
2077
- anchorItem.before(item);
2078
- }
2079
- async function copyText(text) {
2080
- const L = t();
2081
- if (await copyToClipboard(text)) showToast(L.pathCopied);
2082
- else showToast(L.pathCopyFailed, "error");
2083
- }
2084
- async function copyToClipboard(text) {
2085
- try {
2086
- await navigator.clipboard.writeText(text);
2087
- return true;
2088
- } catch {
2089
- return copyViaHiddenInput(text);
2090
- }
2091
- }
2092
- function copyViaHiddenInput(text) {
2093
- try {
2094
- const input = h("input", { type: "text", style: "position:fixed;left:-9999px;top:0;opacity:0" });
2095
- input.value = text;
2096
- document.body.append(input);
2097
- input.select();
2098
- const ok = document.execCommand("copy");
2099
- input.remove();
2100
- return ok;
2101
- } catch {
2102
- return false;
2103
- }
2104
- }
2105
- function hCopyIcon(cls) {
2106
- const ns = "http://www.w3.org/2000/svg";
2107
- const svg = document.createElementNS(ns, "svg");
2108
- svg.setAttribute("viewBox", "0 0 24 24");
2109
- svg.setAttribute("fill", "none");
2110
- svg.setAttribute("stroke", "currentColor");
2111
- svg.setAttribute("stroke-width", "2");
2112
- svg.setAttribute("stroke-linecap", "round");
2113
- svg.setAttribute("stroke-linejoin", "round");
2114
- svg.setAttribute("class", cls);
2115
- for (const d of [
2116
- "M8 8h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V10a2 2 0 0 1 2-2Z",
2117
- "M4 16c-1.1 0-2-.9-2-2V4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2"
2118
- ]) {
2119
- const p = document.createElementNS(ns, "path");
2120
- p.setAttribute("d", d);
2121
- svg.append(p);
2122
- }
2123
- return svg;
2124
- }
2125
-
2126
2219
  // src/inject/features/project-menu.js
2127
2220
  var REMOVE_TEXTS = ["\u79FB\u9664", "Remove"];
2128
2221
  function extractPathFromTestId2(testid) {
@@ -2131,37 +2224,213 @@
2131
2224
  m = testid.match(/[-:=,|]([A-Za-z]:\\.*)$/);
2132
2225
  return m ? m[1] : null;
2133
2226
  }
2134
- function basenameOf2(p) {
2135
- return String(p || "").replace(/[\\/]+$/, "").split(/[\\/]/).pop() || "";
2227
+ function openMenuProjectRow() {
2228
+ const trigger = document.querySelector(
2229
+ '[data-testid="virtual-project-menu"][aria-expanded="true"], [data-testid="virtual-project-menu"][data-state="open"], [data-testid="virtual-project-row"][data-state="open"], [data-testid="virtual-project-row"][aria-expanded="true"]'
2230
+ );
2231
+ const row = trigger ? trigger.closest("section[data-project-id]") : null;
2232
+ if (!row) return null;
2233
+ const host = row.getAttribute("data-project-host");
2234
+ return host === null || host === "local" ? row : null;
2136
2235
  }
2137
2236
  async function resolveProjectPath(removeItem) {
2138
- const testid = removeItem.getAttribute("data-testid") || "";
2139
- const extracted = extractPathFromTestId2(testid);
2237
+ const extracted = extractPathFromTestId2(removeItem.getAttribute("data-testid") || "");
2140
2238
  if (!extracted) return null;
2141
2239
  const res = await rpc("/projects");
2142
2240
  if (res.ok && Array.isArray(res.projects)) {
2143
- const norm2 = (p) => String(p).replace(/[\\/]+$/, "");
2144
- const hit = res.projects.find((p) => norm2(p.path) === norm2(extracted));
2241
+ const norm3 = (p) => String(p).replace(/[\\/]+$/, "");
2242
+ const hit = res.projects.find((p) => norm3(p.path) === norm3(extracted));
2145
2243
  if (hit) return hit;
2146
2244
  }
2147
- return { path: extracted, name: basenameOf2(extracted) };
2245
+ return { path: extracted, name: String(extracted).replace(/[\\/]+$/, "").split(/[\\/]/).pop() || "" };
2246
+ }
2247
+ function cloneMenuItem(anchor, testid, label) {
2248
+ const item = anchor.cloneNode(true);
2249
+ item.removeAttribute("data-testid");
2250
+ item.removeAttribute("data-highlighted");
2251
+ item.removeAttribute("id");
2252
+ item.setAttribute("data-zcodepro-item", testid);
2253
+ for (const child of [...item.childNodes]) child.remove();
2254
+ const origIcon = anchor.querySelector("svg");
2255
+ const iconClass = origIcon ? origIcon.getAttribute("class") : "h-3.5 w-3.5";
2256
+ const ns = "http://www.w3.org/2000/svg";
2257
+ const svg = document.createElementNS(ns, "svg");
2258
+ svg.setAttribute("viewBox", "0 0 24 24");
2259
+ svg.setAttribute("fill", "none");
2260
+ svg.setAttribute("stroke", "currentColor");
2261
+ svg.setAttribute("stroke-width", "2");
2262
+ svg.setAttribute("stroke-linecap", "round");
2263
+ svg.setAttribute("stroke-linejoin", "round");
2264
+ svg.setAttribute("class", iconClass);
2265
+ const p = document.createElementNS(ns, "path");
2266
+ p.setAttribute("d", "M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z");
2267
+ svg.append(p);
2268
+ item.append(svg, document.createTextNode(label));
2269
+ return item;
2270
+ }
2271
+ function wireHighlight(menu, item) {
2272
+ item.addEventListener("mouseenter", () => {
2273
+ for (const el of menu.querySelectorAll('[role="menuitem"]')) el.removeAttribute("data-highlighted");
2274
+ item.setAttribute("data-highlighted", "");
2275
+ });
2276
+ item.addEventListener("mouseleave", () => item.removeAttribute("data-highlighted"));
2277
+ }
2278
+ function attachFolderHover(menu, item, roots) {
2279
+ const L = t();
2280
+ let sub = null;
2281
+ let subHovered = false;
2282
+ let hideTimer = 0;
2283
+ let aliveTimer = 0;
2284
+ const removeSub = () => {
2285
+ clearTimeout(hideTimer);
2286
+ clearInterval(aliveTimer);
2287
+ sub?.remove();
2288
+ sub = null;
2289
+ subHovered = false;
2290
+ };
2291
+ const position = () => {
2292
+ const rect = item.getBoundingClientRect();
2293
+ sub.style.left = rect.right + "px";
2294
+ sub.style.top = rect.top + "px";
2295
+ const r = sub.getBoundingClientRect();
2296
+ if (r.right > window.innerWidth - 8) {
2297
+ sub.style.left = "";
2298
+ sub.style.right = window.innerWidth - rect.left + "px";
2299
+ }
2300
+ };
2301
+ const ensureSub = () => {
2302
+ if (sub) {
2303
+ position();
2304
+ return;
2305
+ }
2306
+ sub = document.createElement("div");
2307
+ sub.setAttribute("role", "menu");
2308
+ sub.setAttribute("data-zcodepro-item", "open-folder-sub");
2309
+ sub.className = "zcodepro-card flex flex-col gap-0.5 rounded-lg border border-border bg-menu p-1 text-ui-sm shadow-lg";
2310
+ sub.style.cssText = "position:fixed;z-index:60;min-width:14rem;";
2311
+ for (const root of roots) {
2312
+ const base = String(root.path).replace(/[\\/]+$/, "").split(/[\\/]/).pop() || root.path;
2313
+ const row = document.createElement("div");
2314
+ row.setAttribute("role", "menuitem");
2315
+ row.className = "flex cursor-pointer items-center gap-2 truncate rounded-md px-2 py-1.5 text-foreground transition-colors hover:bg-surface-hover";
2316
+ row.title = root.path;
2317
+ row.append(document.createTextNode(base + (root.primary ? ` \xB7 ${L.openFolderPrimary}` : "")));
2318
+ row.addEventListener("click", () => {
2319
+ try {
2320
+ menu.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", code: "Escape", bubbles: true, cancelable: true }));
2321
+ } catch {
2322
+ }
2323
+ removeSub();
2324
+ void rpc("/open-folder", { method: "POST", body: { path: root.path } });
2325
+ });
2326
+ sub.append(row);
2327
+ }
2328
+ sub.addEventListener("mouseenter", () => {
2329
+ subHovered = true;
2330
+ clearTimeout(hideTimer);
2331
+ });
2332
+ sub.addEventListener("mouseleave", () => {
2333
+ subHovered = false;
2334
+ scheduleHide();
2335
+ });
2336
+ document.body.append(sub);
2337
+ position();
2338
+ aliveTimer = setInterval(() => {
2339
+ if (!menu.isConnected) removeSub();
2340
+ }, 400);
2341
+ };
2342
+ const scheduleHide = () => {
2343
+ clearTimeout(hideTimer);
2344
+ hideTimer = setTimeout(() => {
2345
+ if (!subHovered) removeSub();
2346
+ }, 300);
2347
+ };
2348
+ item.addEventListener("mouseenter", () => {
2349
+ clearTimeout(hideTimer);
2350
+ ensureSub();
2351
+ });
2352
+ item.addEventListener("mouseleave", scheduleHide);
2353
+ item.addEventListener("click", () => {
2354
+ clearTimeout(hideTimer);
2355
+ ensureSub();
2356
+ });
2357
+ }
2358
+ async function injectNewMenuItems(content, menuRow) {
2359
+ const L = t();
2360
+ const projectId = menuRow.getAttribute("data-project-id");
2361
+ const editItem = content.querySelector('[data-testid="virtual-project-edit"]');
2362
+ if (!editItem || !projectId) return;
2363
+ if (content.querySelector("[data-zcodepro-item]")) return;
2364
+ const config = await getConfig();
2365
+ if (config.features && config.features.projectMenu === false) return;
2366
+ const res = await rpc("/projects-v2");
2367
+ if (!res.ok || !res.available) return;
2368
+ const proj = (res.projects || []).find((p) => p.id === projectId);
2369
+ if (!proj) return;
2370
+ const roots = (proj.roots || []).slice().sort((a, b) => (b.primary ? 1 : 0) - (a.primary ? 1 : 0));
2371
+ if (!roots.length) return;
2372
+ const primary = roots.find((r) => r.primary) || roots[0];
2373
+ const openItem = cloneMenuItem(editItem, "open-folder", L.openFolderItem);
2374
+ wireHighlight(content, openItem);
2375
+ if (roots.length === 1) {
2376
+ openItem.addEventListener("click", () => {
2377
+ try {
2378
+ content.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", code: "Escape", bubbles: true, cancelable: true }));
2379
+ } catch {
2380
+ }
2381
+ void rpc("/open-folder", { method: "POST", body: { path: primary.path } });
2382
+ });
2383
+ } else {
2384
+ const ns = "http://www.w3.org/2000/svg";
2385
+ const chev = document.createElementNS(ns, "svg");
2386
+ chev.setAttribute("viewBox", "0 0 24 24");
2387
+ chev.setAttribute("fill", "none");
2388
+ chev.setAttribute("stroke", "currentColor");
2389
+ chev.setAttribute("stroke-width", "2");
2390
+ chev.setAttribute("stroke-linecap", "round");
2391
+ chev.setAttribute("stroke-linejoin", "round");
2392
+ chev.setAttribute("class", "ml-auto size-3.5 shrink-0 text-foreground-subtle");
2393
+ const chevPath = document.createElementNS(ns, "path");
2394
+ chevPath.setAttribute("d", "m9 18 6-6-6-6");
2395
+ chev.append(chevPath);
2396
+ openItem.append(chev);
2397
+ attachFolderHover(content, openItem, roots);
2398
+ }
2399
+ editItem.after(openItem);
2400
+ const copyItem = cloneMenuItem(editItem, "copy-path", L.copyPathItem);
2401
+ wireHighlight(content, copyItem);
2402
+ copyItem.addEventListener("click", () => {
2403
+ try {
2404
+ content.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", code: "Escape", bubbles: true, cancelable: true }));
2405
+ } catch {
2406
+ }
2407
+ void copyToClipboard(primary.path).then((ok) => showToast(ok ? L.pathCopied : L.pathCopyFailed, ok ? void 0 : "error"));
2408
+ });
2409
+ openItem.after(copyItem);
2148
2410
  }
2149
2411
  function handleProjectMenu(content) {
2150
- if (content.querySelector('[data-zcodepro-item="alias"]')) return;
2412
+ if (content.querySelector("[data-zcodepro-item]")) return;
2151
2413
  const items = itemsOf(content);
2152
- const removeItem = items.find(
2414
+ const newUi = isNewProjectSidebar();
2415
+ const menuRow = newUi ? openMenuProjectRow() : null;
2416
+ const anchor = newUi ? items.find((el) => el.getAttribute("data-testid") === "virtual-project-edit") : items.find(
2153
2417
  (el) => el.hasAttribute("data-testid") && REMOVE_TEXTS.some((x) => itemText(el).startsWith(x))
2154
2418
  );
2155
- if (!removeItem) return;
2419
+ if (!anchor) return;
2420
+ if (newUi) {
2421
+ if (!menuRow) return;
2422
+ void injectNewMenuItems(content, menuRow);
2423
+ return;
2424
+ }
2156
2425
  void (async () => {
2157
2426
  const config = await getConfig();
2158
2427
  if (config.features && config.features.projectMenu === false) return;
2159
- const project = await resolveProjectPath(removeItem);
2428
+ const project = await resolveProjectPath(anchor);
2160
2429
  if (!project) return;
2161
- appendAliasItem(content, removeItem, project);
2162
- appendRelocateItem(content, removeItem, project);
2163
- appendOpenFolderItem(content, removeItem, project);
2164
- appendCopyPathItem(content, removeItem, project);
2430
+ appendAliasItem(content, anchor, project);
2431
+ appendRelocateItem(content, anchor, project);
2432
+ appendOpenFolderItem(content, anchor, project);
2433
+ appendCopyPathItem(content, anchor, project);
2165
2434
  })();
2166
2435
  }
2167
2436
 
@@ -2554,6 +2823,489 @@
2554
2823
  return v === "" || v !== "session" && !providers.some((p) => p.id === v);
2555
2824
  }
2556
2825
 
2826
+ // src/inject/features/ws-rows.js
2827
+ var norm2 = (p) => String(p || "").replace(/[\\/]+$/, "");
2828
+ var NEW_ROW_SEL = "section[data-project-id]";
2829
+ var OLD_ROW_SEL = '[data-testid^="workspace-item-"]';
2830
+ var pathByProjectId = /* @__PURE__ */ new Map();
2831
+ function projectRowSelector() {
2832
+ return isNewProjectSidebar() ? NEW_ROW_SEL : OLD_ROW_SEL;
2833
+ }
2834
+ function projectRows(root = document) {
2835
+ return [...root.querySelectorAll(projectRowSelector())];
2836
+ }
2837
+ function rowPath(row) {
2838
+ if (!row) return null;
2839
+ if (row.hasAttribute && row.hasAttribute("data-project-id")) {
2840
+ const host = row.getAttribute("data-project-host");
2841
+ if (host !== null && host !== "local") return null;
2842
+ const id = row.getAttribute("data-project-id");
2843
+ let found = null;
2844
+ for (const el of row.querySelectorAll("[data-task-item-key]")) {
2845
+ const key = el.getAttribute("data-task-item-key") || "";
2846
+ const cut = key.lastIndexOf(":");
2847
+ if (cut > 1) {
2848
+ found = key.slice(0, cut);
2849
+ break;
2850
+ }
2851
+ }
2852
+ if (found) pathByProjectId.set(id, found);
2853
+ return found || pathByProjectId.get(id) || null;
2854
+ }
2855
+ const testid = row.getAttribute && row.getAttribute("data-testid");
2856
+ if (!testid || !testid.startsWith("workspace-item-")) return null;
2857
+ return norm2(testid.slice("workspace-item-".length)) || null;
2858
+ }
2859
+ function findProjectRow(path) {
2860
+ const want = norm2(path);
2861
+ if (!want) return null;
2862
+ for (const row of projectRows()) {
2863
+ const p = rowPath(row);
2864
+ if (p && norm2(p) === want) return row;
2865
+ }
2866
+ return null;
2867
+ }
2868
+
2869
+ // src/inject/features/alias-migration.js
2870
+ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
2871
+ var basenameOf2 = (p) => String(p || "").replace(/[\\/]+$/, "").split(/[\\/]/).pop() || "";
2872
+ function waitFor(getter, timeoutMs, step) {
2873
+ const start = Date.now();
2874
+ return new Promise((resolve, reject) => {
2875
+ const tick2 = () => {
2876
+ let v = null;
2877
+ try {
2878
+ v = getter();
2879
+ } catch {
2880
+ }
2881
+ if (v) {
2882
+ resolve(v);
2883
+ return;
2884
+ }
2885
+ if (Date.now() - start >= timeoutMs) {
2886
+ reject(new Error(step));
2887
+ return;
2888
+ }
2889
+ setTimeout(tick2, 120);
2890
+ };
2891
+ tick2();
2892
+ });
2893
+ }
2894
+ function setReactInputValue(input, value) {
2895
+ const setter = Object.getOwnPropertyDescriptor(input.constructor.prototype, "value")?.set || Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set;
2896
+ if (!setter) throw new Error("input-setter");
2897
+ setter.call(input, value);
2898
+ input.dispatchEvent(new Event("input", { bubbles: true }));
2899
+ }
2900
+ function pressEscape() {
2901
+ try {
2902
+ document.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", code: "Escape", bubbles: true, cancelable: true }));
2903
+ } catch {
2904
+ }
2905
+ }
2906
+ async function markDismissed() {
2907
+ await rpc("/config", { method: "POST", body: { migrations: { aliasRenameDismissed: true } } });
2908
+ clearConfigCache();
2909
+ }
2910
+ var MIGRATE_LOCK_KEY = "zcodepro:alias-migrate-lock";
2911
+ var MIGRATE_LOCK_MS = 5 * 6e4;
2912
+ function migrateLockFresh() {
2913
+ try {
2914
+ const at = Number(localStorage.getItem(MIGRATE_LOCK_KEY));
2915
+ return Number.isFinite(at) && Date.now() - at < MIGRATE_LOCK_MS;
2916
+ } catch {
2917
+ return false;
2918
+ }
2919
+ }
2920
+ function touchMigrateLock() {
2921
+ try {
2922
+ localStorage.setItem(MIGRATE_LOCK_KEY, String(Date.now()));
2923
+ } catch {
2924
+ }
2925
+ }
2926
+ async function migrateOne(path, alias) {
2927
+ if (document.querySelector('[data-testid="virtual-project-name"], [role="menu"]')) {
2928
+ pressEscape();
2929
+ await sleep(300);
2930
+ }
2931
+ let row = findProjectRow(path);
2932
+ if (!row) {
2933
+ const byName = [...document.querySelectorAll("section[data-project-id]")].filter((el) => {
2934
+ const host = el.getAttribute("data-project-host");
2935
+ return (host === null || host === "local") && el.getAttribute("data-project-name") === basenameOf2(path);
2936
+ });
2937
+ if (byName.length === 1) row = byName[0];
2938
+ }
2939
+ if (!row) throw new Error("project-row");
2940
+ const projectId = row.getAttribute("data-project-id");
2941
+ const rowById = () => document.querySelector(`section[data-project-id="${CSS.escape(projectId)}"]`);
2942
+ try {
2943
+ row.scrollIntoView({ block: "center" });
2944
+ } catch {
2945
+ }
2946
+ const trigger = await waitFor(
2947
+ () => rowById()?.querySelector('[data-testid="virtual-project-menu"]'),
2948
+ 4e3,
2949
+ "menu-trigger"
2950
+ );
2951
+ trigger.dispatchEvent(new PointerEvent("pointerdown", { bubbles: true, cancelable: true, pointerId: 1, isPrimary: true, button: 0 }));
2952
+ trigger.dispatchEvent(new PointerEvent("pointerup", { bubbles: true, cancelable: true, pointerId: 1, isPrimary: true, button: 0 }));
2953
+ trigger.click();
2954
+ const menu = await waitFor(() => {
2955
+ for (const m of document.querySelectorAll('[role="menu"]')) {
2956
+ if (m.querySelector('[data-testid="virtual-project-edit"]')) return m;
2957
+ }
2958
+ return null;
2959
+ }, 4e3, "menu-open");
2960
+ menu.querySelector('[data-testid="virtual-project-edit"]').click();
2961
+ const input = await waitFor(
2962
+ () => document.querySelector('[data-testid="virtual-project-name"]'),
2963
+ 5e3,
2964
+ "editor-open"
2965
+ );
2966
+ const wantName = alias.trim();
2967
+ setReactInputValue(input, wantName);
2968
+ const save = await waitFor(
2969
+ () => {
2970
+ const btn = document.querySelector('[data-testid="virtual-project-save"]');
2971
+ return btn && !btn.disabled ? btn : null;
2972
+ },
2973
+ 4e3,
2974
+ "save-ready"
2975
+ );
2976
+ save.click();
2977
+ await waitFor(() => {
2978
+ const r = rowById();
2979
+ return r && r.getAttribute("data-project-name") === wantName ? r : null;
2980
+ }, 8e3, "name-updated");
2981
+ await sleep(250);
2982
+ if (document.querySelector('[data-testid="virtual-project-name"]')) pressEscape();
2983
+ const res = await rpc("/project/alias", { method: "POST", body: { path, alias: "" } });
2984
+ if (!res.ok) throw new Error("alias-clear");
2985
+ }
2986
+ async function classifyEntry(path, alias, db) {
2987
+ const want = norm0(path);
2988
+ if (db) {
2989
+ let alsoDeleted = false;
2990
+ for (const proj of db.projects || []) {
2991
+ const hit = (proj.roots || []).find((r) => norm0(r.path) === want);
2992
+ if (!hit) continue;
2993
+ if (hit.primary) {
2994
+ if (proj.name === alias) return { state: "done", note: "" };
2995
+ return { state: "", note: "", overridden: !!proj.overridden };
2996
+ }
2997
+ alsoDeleted = (db.deletedRoots || []).some((p) => norm0(p) === want);
2998
+ return { state: "merged", note: proj.name, alsoDeleted };
2999
+ }
3000
+ if ((db.deletedRoots || []).some((p) => norm0(p) === want)) return { state: "deleted", note: "" };
3001
+ return { state: "missing", note: "" };
3002
+ }
3003
+ const localRows = [...document.querySelectorAll("section[data-project-id]")].filter((el) => {
3004
+ const host = el.getAttribute("data-project-host");
3005
+ return host === null || host === "local";
3006
+ });
3007
+ const byPath = localRows.find((el) => norm0(rowPath(el)) === want);
3008
+ if (byPath) {
3009
+ return (byPath.getAttribute("data-project-name") || "") === alias ? { state: "done", note: "" } : { state: "", note: "" };
3010
+ }
3011
+ const byName = localRows.filter((el) => el.getAttribute("data-project-name") === alias);
3012
+ if (byName.length === 1) return { state: "done", note: "" };
3013
+ for (const task of document.querySelectorAll("[data-task-item-key]")) {
3014
+ const key = task.getAttribute("data-task-item-key") || "";
3015
+ const cut = key.lastIndexOf(":");
3016
+ if (cut <= 1 || norm0(key.slice(0, cut)) !== want) continue;
3017
+ const owner = task.closest("section[data-project-id]");
3018
+ const ownerName = owner ? owner.getAttribute("data-project-name") || "" : "";
3019
+ if (ownerName) return { state: "merged", note: ownerName };
3020
+ }
3021
+ return { state: "missing", note: "" };
3022
+ }
3023
+ async function fetchProjectDb() {
3024
+ const res = await rpc("/projects-v2");
3025
+ return res.ok && res.available ? res : null;
3026
+ }
3027
+ var norm0 = (p) => String(p || "").replace(/[\\/]+$/, "");
3028
+ function openMigrationDialog() {
3029
+ ensureStyle();
3030
+ const L = t();
3031
+ let running = false;
3032
+ let decided = false;
3033
+ const decide = () => {
3034
+ if (decided) return;
3035
+ decided = true;
3036
+ void markDismissed();
3037
+ };
3038
+ touchMigrateLock();
3039
+ openDialog({
3040
+ title: L.aliasMigrateTitle,
3041
+ description: L.aliasMigrateDesc,
3042
+ width: "sm:max-w-md",
3043
+ // 迁移不可中途撤销且过程可见:点遮罩不关闭(防误触),右上角 ✕ 关闭
3044
+ dismissOnOutside: false,
3045
+ closable: true,
3046
+ onClose: () => {
3047
+ decide();
3048
+ window.dispatchEvent(new CustomEvent("zcodepro:aliases-changed"));
3049
+ },
3050
+ onMount: ({ body, close }) => {
3051
+ void (async () => {
3052
+ const config = await getConfig(true);
3053
+ const db = await fetchProjectDb();
3054
+ const entries = [];
3055
+ for (const [path, aliasRaw] of Object.entries(config.aliases || {})) {
3056
+ const alias = String(aliasRaw || "").trim();
3057
+ if (!alias) continue;
3058
+ const cls = await classifyEntry(path, alias, db);
3059
+ entries.push({
3060
+ path,
3061
+ alias,
3062
+ checked: cls.state === "" && !cls.overridden,
3063
+ state: cls.state,
3064
+ note: cls.note || "",
3065
+ overridden: !!cls.overridden,
3066
+ alsoDeleted: !!cls.alsoDeleted
3067
+ });
3068
+ }
3069
+ if (!entries.length) {
3070
+ close();
3071
+ return;
3072
+ }
3073
+ const badgesOf = (e) => {
3074
+ const out = [];
3075
+ if (e.state === "run") out.push({ text: L.aliasMigrateRunning, cls: "text-amber-500" });
3076
+ if (e.state === "ok") out.push({ text: L.aliasMigrateStateDone, cls: "text-emerald-600" });
3077
+ if (e.state === "fail") out.push({ text: L.aliasMigrateStateFailed, cls: "text-destructive" });
3078
+ if (e.state === "done") out.push({ text: L.aliasMigrateStateDone, cls: "text-emerald-600" });
3079
+ if (e.state === "merged") out.push({ text: L.aliasMigrateStateMerged.replace("{name}", e.note), cls: "text-amber-500" });
3080
+ if (e.state === "deleted" || e.state === "merged" && e.alsoDeleted) out.push({ text: L.aliasMigrateStateDeleted, cls: "text-foreground-subtle/70" });
3081
+ if (e.state === "missing") out.push({ text: L.aliasMigrateStateMissing, cls: "text-foreground-subtle/70" });
3082
+ if (e.state === "" && e.overridden) out.push({ text: L.aliasMigrateStateRenamed, cls: "text-foreground-subtle/70" });
3083
+ return out;
3084
+ };
3085
+ const progressEl = h("div", { class: "mb-1 text-ui-xs text-foreground-subtle", style: "display:none" });
3086
+ const listEl = h("div", { class: "max-h-[50vh] space-y-1.5 overflow-y-auto" });
3087
+ let deadOpen = false;
3088
+ const renderList = () => {
3089
+ const alive = entries.filter((e) => e.state !== "missing" && e.state !== "deleted");
3090
+ const dead = entries.filter((e) => e.state === "missing" || e.state === "deleted");
3091
+ const rowOf = (e, deadRow) => {
3092
+ const cb = h("input", { type: "checkbox" });
3093
+ cb.checked = e.checked;
3094
+ cb.disabled = running || e.state !== "" && e.state !== "fail" || e.overridden;
3095
+ cb.addEventListener("change", () => {
3096
+ e.checked = cb.checked;
3097
+ });
3098
+ const badges = badgesOf(e);
3099
+ const dim = deadRow || e.state !== "" && e.state !== "fail" && e.state !== "run" || e.overridden;
3100
+ return h(
3101
+ "label",
3102
+ { class: "flex cursor-pointer items-start gap-2 rounded-lg p-2 transition-colors hover:bg-surface-hover" + (dim ? " opacity-60" : "") },
3103
+ h("span", { class: "mt-0.5" }, cb),
3104
+ h(
3105
+ "span",
3106
+ { class: "min-w-0 flex-1" },
3107
+ h("span", { class: "block truncate text-ui-sm font-medium text-foreground" }, e.alias),
3108
+ h("span", { class: "block truncate font-mono text-ui-xs text-foreground-subtle" }, e.path)
3109
+ ),
3110
+ ...badges.map((b) => h("span", { class: "shrink-0 self-center text-ui-xs " + b.cls }, b.text))
3111
+ );
3112
+ };
3113
+ const parts = alive.map((e) => rowOf(e, false));
3114
+ if (dead.length) {
3115
+ const deadRows = h(
3116
+ "div",
3117
+ { class: "space-y-1", style: deadOpen ? "" : "display:none" },
3118
+ ...dead.map((e) => rowOf(e, true))
3119
+ );
3120
+ const toggle2 = h("button", {
3121
+ type: "button",
3122
+ class: "flex w-full items-center gap-1 rounded-lg p-2 text-left text-ui-xs text-foreground-subtle transition-colors hover:bg-surface-hover",
3123
+ onClick: () => {
3124
+ deadOpen = !deadOpen;
3125
+ renderList();
3126
+ }
3127
+ }, (deadOpen ? "\u25BE " : "\u25B8 ") + L.aliasMigrateDeadSummary.replace("{n}", String(dead.length)));
3128
+ parts.push(toggle2, deadRows);
3129
+ }
3130
+ listEl.replaceChildren(...parts);
3131
+ };
3132
+ renderList();
3133
+ const clearDead = async () => {
3134
+ if (running) return;
3135
+ const dead = entries.filter((e) => e.state === "missing" || e.state === "deleted");
3136
+ if (!dead.length) return;
3137
+ running = true;
3138
+ goBtn.setAttribute("disabled", "true");
3139
+ let ok = 0;
3140
+ for (const e of dead) {
3141
+ const r = await rpc("/project/alias", { method: "POST", body: { path: e.path, alias: "" } });
3142
+ if (r.ok) {
3143
+ ok++;
3144
+ entries.splice(entries.indexOf(e), 1);
3145
+ }
3146
+ }
3147
+ running = false;
3148
+ goBtn.removeAttribute("disabled");
3149
+ goBtn.textContent = L.aliasMigrateGo;
3150
+ clearConfigCache();
3151
+ renderList();
3152
+ window.dispatchEvent(new CustomEvent("zcodepro:aliases-changed"));
3153
+ if (ok === dead.length) showToast(L.aliasMigrateDeadCleared.replace("{n}", String(ok)));
3154
+ else showToast(L.aliasMigrateDeadClearFailed, "error");
3155
+ };
3156
+ const goBtn = btnPrimary(L.aliasMigrateGo, () => {
3157
+ void run();
3158
+ }, "min-w-24");
3159
+ const run = async () => {
3160
+ if (running) return;
3161
+ const todo = entries.filter((e) => e.checked);
3162
+ if (!todo.length) return;
3163
+ running = true;
3164
+ goBtn.setAttribute("disabled", "true");
3165
+ goBtn.textContent = L.aliasMigrateRunning;
3166
+ let ok = 0;
3167
+ const failed = [];
3168
+ for (let i = 0; i < todo.length; i++) {
3169
+ const e = todo[i];
3170
+ progressEl.style.display = "";
3171
+ progressEl.textContent = L.aliasMigrateProgress.replace("{i}", String(i + 1)).replace("{n}", String(todo.length)).replace("{name}", e.alias);
3172
+ e.state = "run";
3173
+ renderList();
3174
+ try {
3175
+ await migrateOne(e.path, e.alias);
3176
+ e.state = "ok";
3177
+ e.checked = false;
3178
+ ok++;
3179
+ } catch (err) {
3180
+ console.warn("[zcodepro] alias-migrate: " + e.path + " \u5931\u8D25\u4E8E " + (err && err.message));
3181
+ e.state = "fail";
3182
+ failed.push(e);
3183
+ }
3184
+ renderList();
3185
+ }
3186
+ running = false;
3187
+ progressEl.style.display = "none";
3188
+ goBtn.removeAttribute("disabled");
3189
+ goBtn.textContent = L.aliasMigrateGo;
3190
+ decide();
3191
+ close();
3192
+ if (!failed.length) {
3193
+ showToast(L.aliasMigrateDone.replace("{n}", String(ok)), "ok");
3194
+ } else if (ok) {
3195
+ showToast(L.aliasMigratePartial.replace("{n}", String(ok)).replace("{m}", String(failed.length)), "error");
3196
+ } else {
3197
+ showToast(L.aliasMigrateAllFailed, "error");
3198
+ }
3199
+ };
3200
+ body.append(
3201
+ progressEl,
3202
+ listEl,
3203
+ h("p", { class: "text-ui-xs/relaxed text-foreground-subtle" }, L.aliasMigrateItemHint),
3204
+ (() => {
3205
+ const footer = document.createElement("div");
3206
+ footer.className = "mt-6 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end";
3207
+ const hasDead = entries.some((e) => e.state === "missing" || e.state === "deleted");
3208
+ if (hasDead) {
3209
+ const clearBtn = btnSecondary(L.aliasMigrateClearDead, () => {
3210
+ void clearDead().then(() => {
3211
+ if (!entries.some((e) => e.state === "missing" || e.state === "deleted")) clearBtn.style.display = "none";
3212
+ });
3213
+ });
3214
+ footer.append(clearBtn);
3215
+ }
3216
+ footer.append(
3217
+ btnSecondary(L.aliasMigrateSkip, () => {
3218
+ if (running) return;
3219
+ decide();
3220
+ close();
3221
+ }),
3222
+ goBtn
3223
+ );
3224
+ return footer;
3225
+ })()
3226
+ );
3227
+ })();
3228
+ }
3229
+ });
3230
+ }
3231
+ function migrationCard() {
3232
+ const L = t();
3233
+ const card = h("div", {
3234
+ class: "flex items-center gap-2 rounded-lg p-2",
3235
+ "data-zcodepro-migration-card": ""
3236
+ });
3237
+ const render = (count) => {
3238
+ card.replaceChildren(
3239
+ h(
3240
+ "div",
3241
+ { class: "min-w-0 flex-1" },
3242
+ h("div", { class: "text-ui-sm font-medium text-foreground" }, L.aliasMigrateCardName),
3243
+ h(
3244
+ "div",
3245
+ { class: "mt-0.5 text-ui-xs/relaxed text-foreground-subtle" },
3246
+ count === null ? "\u2026" : L.aliasMigrateCardDesc.replace("{n}", String(count))
3247
+ )
3248
+ ),
3249
+ btnSmall(L.aliasMigrateRun, () => {
3250
+ openMigrationDialog();
3251
+ })
3252
+ );
3253
+ };
3254
+ render(null);
3255
+ const refresh = async () => {
3256
+ const config = await getConfig(true);
3257
+ const db = await fetchProjectDb();
3258
+ let count = 0;
3259
+ for (const [p, a] of Object.entries(config.aliases || {})) {
3260
+ if (!String(a || "").trim()) continue;
3261
+ const cls = await classifyEntry(p, String(a).trim(), db);
3262
+ if (cls.state === "" && !cls.overridden) count++;
3263
+ }
3264
+ if (!count) {
3265
+ card.remove();
3266
+ return;
3267
+ }
3268
+ render(count);
3269
+ };
3270
+ const onAliasesChanged = () => {
3271
+ if (!card.isConnected) {
3272
+ window.removeEventListener("zcodepro:aliases-changed", onAliasesChanged);
3273
+ return;
3274
+ }
3275
+ void refresh();
3276
+ };
3277
+ window.addEventListener("zcodepro:aliases-changed", onAliasesChanged);
3278
+ void refresh();
3279
+ return card;
3280
+ }
3281
+ function startAliasMigrationWatcher() {
3282
+ if (!officialRenameSupported() && !isNewProjectSidebar()) return;
3283
+ void (async () => {
3284
+ try {
3285
+ const config = await getConfig();
3286
+ const hasAliases = Object.values(config.aliases || {}).some((a) => String(a || "").trim());
3287
+ if (!hasAliases) return;
3288
+ if (config.migrations && config.migrations.aliasRenameDismissed) return;
3289
+ await waitFor(() => document.querySelector("section[data-project-id]"), 45e3, "sidebar");
3290
+ let focused = false;
3291
+ for (let i = 0; i < 600; i++) {
3292
+ if (document.visibilityState === "visible" && document.hasFocus()) {
3293
+ focused = true;
3294
+ break;
3295
+ }
3296
+ await sleep(500);
3297
+ }
3298
+ if (!focused) return;
3299
+ if (migrateLockFresh()) return;
3300
+ await waitFor(() => document.querySelector(".zcodepro-overlay") ? null : true, 12e4, "dialog-busy");
3301
+ const fresh = await getConfig(true);
3302
+ const still = Object.values(fresh.aliases || {}).some((a) => String(a || "").trim());
3303
+ if (still && !(fresh.migrations && fresh.migrations.aliasRenameDismissed)) openMigrationDialog();
3304
+ } catch {
3305
+ }
3306
+ })();
3307
+ }
3308
+
2557
3309
  // src/inject/features/styles.js
2558
3310
  var STYLE_DEFAULTS = {
2559
3311
  rowGap: 20,
@@ -2728,7 +3480,10 @@
2728
3480
  `${LIST} [data-testid^="workspace-item-"]{height:auto !important;min-height:${28 + 2 * pad}px !important;padding-block:${pad}px !important;padding-top:${pad}px !important;padding-bottom:${pad}px !important;}`,
2729
3481
  `${LIST} [data-testid^="workspace-item-"] > div:first-child{align-self:stretch !important;}`,
2730
3482
  `${SCROLL} [data-slot="collapsible-trigger"],${SCROLL} .flex.h-7{height:auto !important;}`,
2731
- `${SCROLL} [data-slot="collapsible-trigger"]{min-height:${28 + 2 * pad}px !important;padding-block:${pad}px !important;padding-top:${pad}px !important;padding-bottom:${pad}px !important;}`
3483
+ `${SCROLL} [data-slot="collapsible-trigger"]{min-height:${28 + 2 * pad}px !important;padding-block:${pad}px !important;padding-top:${pad}px !important;padding-bottom:${pad}px !important;}`,
3484
+ // 3.16.1 新结构:项目行(virtual-project-row,固定 h-8)按视觉总量放行内留白;
3485
+ // 分区间距由新侧栏容器自己的布局管理,不动
3486
+ `section[data-project-id] [data-testid="virtual-project-row"]{height:auto !important;min-height:${28 + 2 * pad}px !important;padding-block:${Math.round(pad)}px !important;}`
2732
3487
  );
2733
3488
  }
2734
3489
  const sts = styles.sidebarTaskSpacing;
@@ -2739,7 +3494,10 @@
2739
3494
  `ul.space-y-0\\.5:has(> li[data-task-item-key]) > :not(:last-child){margin-block-end:${m}px !important;margin-bottom:${m}px !important;}`,
2740
3495
  `ul.space-y-0\\.5:has(> li[data-task-item-key]) > li{padding-block:${pad}px !important;padding-top:${pad}px !important;padding-bottom:${pad}px !important;}`,
2741
3496
  `${SCROLL} .flex.flex-col.gap-2{gap:${m}px !important;row-gap:${m}px !important;}`,
2742
- `${SCROLL} div.cursor-pointer[class*="pl-8"]{padding-block:${pad}px !important;padding-top:${pad}px !important;padding-bottom:${pad}px !important;}`
3497
+ `${SCROLL} div.cursor-pointer[class*="pl-8"]{padding-block:${pad}px !important;padding-top:${pad}px !important;padding-bottom:${pad}px !important;}`,
3498
+ // 3.16.1 新结构:任务行(li[data-task-item-key])行内留白 + 相邻行间距
3499
+ `li[data-task-item-key]{padding-block:${Math.max(1, Math.round(pad))}px !important;}`,
3500
+ `li[data-task-item-key] + li[data-task-item-key]{margin-block-start:${m}px !important;}`
2743
3501
  );
2744
3502
  }
2745
3503
  return parts.join("");
@@ -2780,7 +3538,8 @@
2780
3538
  thinkingLineHeight: `[data-testid^="chat-assistant-history-content"]`,
2781
3539
  userLineHeight: `${CONV} [class*="user-row"] .whitespace-pre-wrap`,
2782
3540
  contentWidth: "[data-v4-timeline-content-column]",
2783
- sidebarProjectSpacing: '[data-testid^="workspace-item-"]',
3541
+ // 双结构:新侧栏项目行 / 旧 testid 行
3542
+ sidebarProjectSpacing: 'section[data-project-id] [data-testid="virtual-project-row"], [data-testid^="workspace-item-"]',
2784
3543
  sidebarTaskSpacing: "li[data-task-item-key]"
2785
3544
  };
2786
3545
  var HL_MARGIN = {
@@ -2824,6 +3583,7 @@
2824
3583
  } catch {
2825
3584
  }
2826
3585
  }
3586
+ var PLUGINS_REPO_URL = "https://github.com/duanluan/zcode-plugins";
2827
3587
  function makeUpgradeControls({ endpoint, metaRefresh, idleText }) {
2828
3588
  const L = t();
2829
3589
  const state = h("span", { class: "min-w-0 flex-1 truncate text-left text-ui-xs/relaxed text-foreground-subtle" });
@@ -3050,10 +3810,46 @@
3050
3810
  h("span", { class: "text-foreground-subtle/70" }, `${health.version} \xB7 ${HELPER_URL}`)
3051
3811
  );
3052
3812
  const rows = h("div", { class: "divide-y divide-border rounded-xl border border-border" });
3813
+ const pluginsStatusLine = h("span", { class: "max-w-40 truncate text-ui-xs/relaxed text-foreground-subtle" }, "\u2026");
3814
+ const pluginsBtn = btnSmall(L.pluginsCheckNow, () => {
3815
+ void runPluginsUpdate();
3816
+ });
3817
+ let pluginsUpdating = false;
3818
+ const runPluginsUpdate = async () => {
3819
+ if (pluginsUpdating) return;
3820
+ pluginsUpdating = true;
3821
+ pluginsBtn.disabled = true;
3822
+ pluginsStatusLine.textContent = L.pluginsUpdating;
3823
+ const res = await rpc("/plugins/update", { method: "POST", body: {} });
3824
+ pluginsUpdating = false;
3825
+ pluginsBtn.disabled = false;
3826
+ if (!res.ok) {
3827
+ pluginsStatusLine.textContent = "";
3828
+ showToast(L.pluginsUpdateFailed + ": " + errText(res), "error");
3829
+ return;
3830
+ }
3831
+ pluginsStatusLine.textContent = res.updated > 0 ? L.pluginsUpdatedDone.replaceAll("{n}", String(res.updated)) : L.pluginsUpToDate;
3832
+ showToast(pluginsStatusLine.textContent, "success");
3833
+ };
3834
+ void (async () => {
3835
+ const cfg = await getConfig(true).catch(() => null);
3836
+ const res = await rpc("/plugins/status");
3837
+ if (!res.ok) {
3838
+ pluginsStatusLine.textContent = "";
3839
+ return;
3840
+ }
3841
+ const n = (res.updates || []).length;
3842
+ if (cfg && (cfg.features || {}).autoUpdatePlugins === true && n > 0) {
3843
+ void runPluginsUpdate();
3844
+ return;
3845
+ }
3846
+ pluginsStatusLine.textContent = n > 0 ? L.pluginsUpdatesFound.replaceAll("{n}", String(n)) : L.pluginsUpToDate;
3847
+ })();
3053
3848
  const refreshRows = () => {
3054
3849
  const f = config.features || {};
3055
3850
  rows.replaceChildren(
3056
- settingRow(L.featureProjectMenu, L.featureProjectMenuDesc, f.projectMenu !== false, async () => {
3851
+ ...isNewProjectSidebar() ? [migrationCard()] : [],
3852
+ settingRow(L.featureProjectMenu, isNewProjectSidebar() ? L.featureProjectMenuDescNew : L.featureProjectMenuDesc, f.projectMenu !== false, async () => {
3057
3853
  const next = !(f.projectMenu !== false);
3058
3854
  if (await setFeature("projectMenu", next)) f.projectMenu = next;
3059
3855
  refreshRows();
@@ -3094,16 +3890,32 @@
3094
3890
  if (await setFeature("wsRunningSpin", next)) f.wsRunningSpin = next;
3095
3891
  refreshRows();
3096
3892
  }),
3097
- settingRow(L.featurePinnedExpand, L.featurePinnedExpandDesc, f.pinnedKeepCollapsed !== false, async () => {
3098
- const next = !(f.pinnedKeepCollapsed !== false);
3099
- if (await setFeature("pinnedKeepCollapsed", next)) f.pinnedKeepCollapsed = next;
3100
- refreshRows();
3101
- }),
3102
- settingRow(L.featureAutoUpdatePlugins, L.featureAutoUpdatePluginsDesc, f.autoUpdatePlugins === true, async () => {
3103
- const next = !(f.autoUpdatePlugins === true);
3104
- if (await setFeature("autoUpdatePlugins", next)) f.autoUpdatePlugins = next;
3105
- refreshRows();
3106
- })
3893
+ (() => {
3894
+ const repoLink = h("span", {
3895
+ class: "cursor-pointer underline-offset-4 hover:underline",
3896
+ title: PLUGINS_REPO_URL,
3897
+ onClick: (ev) => {
3898
+ ev.stopPropagation();
3899
+ openExternal(PLUGINS_REPO_URL);
3900
+ }
3901
+ }, L.featureAutoUpdatePluginsRepo);
3902
+ const row = settingRow("", "", f.autoUpdatePlugins === true, async () => {
3903
+ const next = !(f.autoUpdatePlugins === true);
3904
+ if (await setFeature("autoUpdatePlugins", next)) f.autoUpdatePlugins = next;
3905
+ refreshRows();
3906
+ }, h("div", { class: "flex items-center gap-2" }, pluginsBtn));
3907
+ const nameEl = row.querySelector(".text-ui-sm.font-medium");
3908
+ if (nameEl) {
3909
+ nameEl.replaceChildren(
3910
+ document.createTextNode(L.featureAutoUpdatePlugins + " "),
3911
+ repoLink,
3912
+ document.createTextNode(" \u63D2\u4EF6")
3913
+ );
3914
+ }
3915
+ const descEl = row.querySelector("[data-zcodepro-row-desc]");
3916
+ if (descEl) descEl.replaceChildren(pluginsStatusLine);
3917
+ return row;
3918
+ })()
3107
3919
  );
3108
3920
  };
3109
3921
  refreshRows();
@@ -3154,64 +3966,10 @@
3154
3966
  )
3155
3967
  );
3156
3968
  let activeTab = "features";
3157
- const pluginsStatusLine = h("span", { class: "text-ui-xs/relaxed text-foreground-subtle" }, "\u2026");
3158
- const pluginsBtn = btnSmall(L.pluginsCheckNow, () => {
3159
- void runPluginsUpdate();
3160
- });
3161
- const runPluginsUpdate = async () => {
3162
- pluginsBtn.disabled = true;
3163
- pluginsStatusLine.textContent = L.pluginsUpdating;
3164
- const res = await rpc("/plugins/update", { method: "POST", body: {} });
3165
- pluginsBtn.disabled = false;
3166
- if (!res.ok) {
3167
- pluginsStatusLine.textContent = "";
3168
- showToast(L.pluginsUpdateFailed + ": " + errText(res), "error");
3169
- return;
3170
- }
3171
- pluginsStatusLine.textContent = res.updated > 0 ? L.pluginsUpdatedDone.replaceAll("{n}", String(res.updated)) : L.pluginsUpToDate;
3172
- showToast(pluginsStatusLine.textContent, "success");
3173
- };
3174
- void (async () => {
3175
- const res = await rpc("/plugins/status");
3176
- if (!res.ok) {
3177
- pluginsStatusLine.textContent = "";
3178
- return;
3179
- }
3180
- pluginsStatusLine.textContent = (res.updates || []).length > 0 ? L.pluginsUpdatesFound.replaceAll("{n}", String(res.updates.length)) : L.pluginsUpToDate;
3181
- })();
3182
- const PLUGINS_REPO_URL = "https://github.com/duanluan/zcode-plugins";
3183
- const repoName = "zcode-plugins";
3184
- const titleParts = L.pluginsUpdateTitle.split(repoName);
3185
- const pluginsTitle = h("div", { class: "truncate text-ui-sm font-medium text-foreground" });
3186
- if (titleParts.length === 2 && typeof window.zcode?.openExternal === "function") {
3187
- pluginsTitle.append(
3188
- titleParts[0],
3189
- h("span", {
3190
- class: "cursor-pointer underline-offset-4 hover:underline",
3191
- title: PLUGINS_REPO_URL,
3192
- onClick: () => openExternal(PLUGINS_REPO_URL)
3193
- }, repoName),
3194
- titleParts[1]
3195
- );
3196
- } else {
3197
- pluginsTitle.append(L.pluginsUpdateTitle);
3198
- }
3199
- const pluginsCard = h(
3200
- "div",
3201
- { class: "mt-3 flex items-center justify-between gap-2 rounded-xl border border-border p-2" },
3202
- h(
3203
- "div",
3204
- { class: "min-w-0" },
3205
- pluginsTitle,
3206
- h("div", { class: "mt-0.5 truncate text-ui-xs/relaxed text-foreground-subtle" }, pluginsStatusLine)
3207
- ),
3208
- pluginsBtn
3209
- );
3210
3969
  const paneFeatures = h(
3211
3970
  "div",
3212
3971
  { role: "tabpanel", class: "mt-4" },
3213
3972
  h("div", {}, rows),
3214
- pluginsCard,
3215
3973
  communityCards
3216
3974
  );
3217
3975
  const paneStyles = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
@@ -3349,6 +4107,7 @@
3349
4107
  )
3350
4108
  };
3351
4109
  const cells = [
4110
+ // 侧栏间距双结构:3.15.x 旧容器/行 + 3.16.1 新项目行(virtual-project-row)与任务行
3352
4111
  styleCell(L.sidebarProjectSpacingName, L.sidebarProjectSpacingDesc, "sidebarProjectSpacing", { max: 24 }),
3353
4112
  styleCell(L.sidebarTaskSpacingName, L.sidebarTaskSpacingDesc, "sidebarTaskSpacing", { max: 24 }),
3354
4113
  widthCell,
@@ -4868,6 +5627,7 @@
4868
5627
  h("span", { class: "text-ui-sm text-foreground-subtle/70" }, "\u2026")
4869
5628
  );
4870
5629
  const hrFacts = h("div", { class: "mt-1.5 grid grid-cols-2 gap-x-4 gap-y-1 sm:grid-cols-3" });
5630
+ const hrInstList = h("div", { class: "mt-1.5 space-y-1", style: "display:none" });
4871
5631
  const hrHintLine = h("p", { class: "mt-1.5 text-ui-xs/relaxed text-amber-500", style: "display:none" });
4872
5632
  const hrRefresh = async () => {
4873
5633
  const res = await rpc("/headroom/status");
@@ -4876,6 +5636,8 @@
4876
5636
  h("span", { class: "text-ui-sm text-destructive" }, L.headroomStatusLoadFailed + ": " + errText(res))
4877
5637
  );
4878
5638
  hrFacts.replaceChildren();
5639
+ hrInstList.replaceChildren();
5640
+ hrInstList.style.display = "none";
4879
5641
  hrHintLine.style.display = "none";
4880
5642
  return;
4881
5643
  }
@@ -4895,9 +5657,23 @@
4895
5657
  fact(L.headroomCurrentBackend, hrDevLabel(s.backend)),
4896
5658
  fact(L.headroomDesiredBackend, hrDevLabel(s.desiredBackend)),
4897
5659
  fact(L.headroomPowerState, HR_PM[s.powerMode] || s.powerMode),
4898
- fact(L.headroomWatcher, s.watcherRunning ? L.headroomWatcherOn : L.headroomWatcherOff),
5660
+ fact(L.headroomWatcher, s.watcherRunning == null ? "\u2014" : s.watcherRunning ? L.headroomWatcherOn : L.headroomWatcherOff),
4899
5661
  fact(L.headroomSaverDetect, HR_DETECT[s.saverDetect] || s.saverDetect)
4900
5662
  );
5663
+ const insts = Array.isArray(s.instances) ? s.instances : [];
5664
+ hrInstList.replaceChildren(...insts.map((it) => h(
5665
+ "div",
5666
+ { class: "flex min-w-0 items-center gap-2" },
5667
+ h("span", { class: "shrink-0 font-mono text-ui-xs text-foreground-subtle" }, `:${it.port}`),
5668
+ h(
5669
+ "span",
5670
+ { class: "min-w-0 flex-1 truncate text-ui-xs text-foreground-subtle", title: `${it.upstream} | ${it.providers}` },
5671
+ `${it.format} \u2192 ${it.upstream}`
5672
+ ),
5673
+ h("span", { class: "max-w-[40%] shrink-0 truncate text-ui-xs text-foreground-subtle/70", title: it.providers }, it.providers),
5674
+ h("span", { class: "shrink-0 text-ui-xs " + (it.up ? "text-emerald-600" : "text-destructive") }, it.up ? L.hrInstRunning : L.hrInstStopped)
5675
+ )));
5676
+ hrInstList.style.display = insts.length ? "" : "none";
4901
5677
  hrHintLine.textContent = s.saverHint || "";
4902
5678
  hrHintLine.style.display = s.saverHint ? "" : "none";
4903
5679
  };
@@ -5085,6 +5861,7 @@
5085
5861
  ),
5086
5862
  hrStatusLine,
5087
5863
  hrFacts,
5864
+ hrInstList,
5088
5865
  hrHintLine
5089
5866
  ),
5090
5867
  h(
@@ -5462,7 +6239,7 @@
5462
6239
  let item = find();
5463
6240
  if (!item) {
5464
6241
  const ws = workspaceOf(key);
5465
- const header = document.querySelector(`[data-testid="workspace-item-${CSS.escape(ws)}"]`);
6242
+ const header = findProjectRow(ws);
5466
6243
  const expander = header && header.querySelector("[aria-expanded]");
5467
6244
  if (expander && expander.getAttribute("aria-expanded") === "false") {
5468
6245
  expander.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
@@ -5608,7 +6385,7 @@
5608
6385
  return;
5609
6386
  }
5610
6387
  if (!popupEl) await openPopup();
5611
- moveHighlight(k === "x" ? 1 : -1);
6388
+ moveHighlight(k === "x" ? -1 : 1);
5612
6389
  })();
5613
6390
  }, true);
5614
6391
  document.addEventListener("keyup", (e) => {
@@ -5626,6 +6403,7 @@
5626
6403
  var REVERIFY_MS = 3e3;
5627
6404
  var MAP_REFRESH_MS = 6e4;
5628
6405
  var installed2 = false;
6406
+ var seenRows = /* @__PURE__ */ new WeakSet();
5629
6407
  var enabled2 = true;
5630
6408
  var pinned = null;
5631
6409
  var resolved = false;
@@ -5682,7 +6460,17 @@
5682
6460
  if (first && !walker.nextNode()) first.nodeValue = text;
5683
6461
  else el.textContent = text;
5684
6462
  }
5685
- function scheduleEnforce(key) {
6463
+ function scheduleEnforce(key, { immediate = false } = {}) {
6464
+ if (immediate) {
6465
+ if (enabled2 && pinned) {
6466
+ const want = pinned.get(sessIdOf(key));
6467
+ if (want !== void 0) {
6468
+ for (const item of findRows(key)) {
6469
+ for (const el of titleElsOf(item)) writeTitle(el, want);
6470
+ }
6471
+ }
6472
+ }
6473
+ }
5686
6474
  if (pendingKeys.has(key)) return;
5687
6475
  pendingKeys.add(key);
5688
6476
  setTimeout(() => {
@@ -5718,7 +6506,13 @@
5718
6506
  if (!enabled2 || !pinned || pinned.size === 0) return;
5719
6507
  for (const item of document.querySelectorAll(TASK_SEL)) {
5720
6508
  const key = isMismatch(item);
5721
- if (key) scheduleEnforce(key);
6509
+ if (!key) {
6510
+ seenRows.add(item);
6511
+ continue;
6512
+ }
6513
+ const fresh = !seenRows.has(item);
6514
+ seenRows.add(item);
6515
+ scheduleEnforce(key, { immediate: fresh });
5722
6516
  }
5723
6517
  }
5724
6518
  var inTaskRow2 = (node) => !!(node instanceof Element && node.closest && node.closest(TASK_SEL));
@@ -5875,7 +6669,7 @@
5875
6669
  var installed3 = false;
5876
6670
  var knownKeys = /* @__PURE__ */ new Set();
5877
6671
  var attempted = /* @__PURE__ */ new Set();
5878
- var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
6672
+ var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
5879
6673
  function sessIdOf2(key) {
5880
6674
  const i = key.indexOf(":sess_");
5881
6675
  return i > 0 ? key.slice(i + 1) : "";
@@ -5892,9 +6686,23 @@
5892
6686
  return ((el ? el.textContent : r.innerText) || "").trim();
5893
6687
  }).filter(Boolean);
5894
6688
  }
6689
+ var URL_RE = /https?:\/\/[^\s\u4e00-\u9fff\u3000-\u303f\uff00-\uffef]+/g;
6690
+ function assistantText() {
6691
+ const els = [...document.querySelectorAll('[class*="@md/conversation"] [class*="assistant-row"]')].filter((e) => !e.querySelector("[data-is-streaming]"));
6692
+ const last = els[els.length - 1];
6693
+ if (!last) return "";
6694
+ const text = (last.innerText || "").replace(URL_RE, "").trim();
6695
+ return text.slice(0, 600);
6696
+ }
6697
+ function linkDominant(t2) {
6698
+ const raw = t2.replace(/\s+/g, "");
6699
+ if (!raw) return true;
6700
+ const stripped = raw.replace(URL_RE, "");
6701
+ return stripped.length < raw.length / 3;
6702
+ }
5895
6703
  async function onNewTaskRow(key) {
5896
6704
  const selDeadline = Date.now() + SELECT_WAIT_MS;
5897
- while (currentKey() !== key && Date.now() < selDeadline) await sleep(200);
6705
+ while (currentKey() !== key && Date.now() < selDeadline) await sleep2(200);
5898
6706
  if (currentKey() !== key) return;
5899
6707
  const cfg = await getConfig().catch(() => null);
5900
6708
  if (!cfg || !cfg.titleGen || cfg.titleGen.enabled !== true) return;
@@ -5906,7 +6714,7 @@
5906
6714
  while (Date.now() < msgDeadline) {
5907
6715
  texts = userTexts();
5908
6716
  if (texts.length) break;
5909
- await sleep(400);
6717
+ await sleep2(400);
5910
6718
  }
5911
6719
  if (!texts.length) return;
5912
6720
  let assistantSeen = false;
@@ -5929,7 +6737,7 @@
5929
6737
  let lastChange = Date.now();
5930
6738
  try {
5931
6739
  while (Date.now() - startAt < COLLECT_MAX_MS) {
5932
- await sleep(500);
6740
+ await sleep2(500);
5933
6741
  if (currentKey() !== key) {
5934
6742
  lastChange = Date.now();
5935
6743
  continue;
@@ -5946,11 +6754,23 @@
5946
6754
  convObserver.disconnect();
5947
6755
  }
5948
6756
  if (!texts.length) return;
6757
+ if (linkDominant(texts.join("\n"))) {
6758
+ const deadline = Date.now() + 6e4;
6759
+ while (Date.now() < deadline) {
6760
+ await sleep2(2e3);
6761
+ if (currentKey() !== key) continue;
6762
+ const a = assistantText();
6763
+ if (a && a.length > 80) {
6764
+ texts.push(`\uFF08\u9996\u8F6E\u56DE\u7B54\u6458\u8981\uFF09${a}`);
6765
+ break;
6766
+ }
6767
+ }
6768
+ }
5949
6769
  const callTitleGen = () => rpc("/title-gen/run", { method: "POST", body: { sessId, text: texts.join("\n") } }).catch(() => null);
5950
6770
  let res = await callTitleGen();
5951
6771
  for (const delay of [5e3, 2e4]) {
5952
6772
  if (res && res.ok) break;
5953
- await sleep(delay);
6773
+ await sleep2(delay);
5954
6774
  res = await callTitleGen();
5955
6775
  }
5956
6776
  if (res && res.ok && res.written && res.title) {
@@ -6007,115 +6827,8 @@
6007
6827
  });
6008
6828
  }
6009
6829
 
6010
- // src/inject/features/pinned-expand.js
6011
- var installed4 = false;
6012
- var keepCollapsed = false;
6013
- var CONFIG_POLL_MIN_MS = 5e3;
6014
- var CONFIG_POLL_MAX_MS = 6e4;
6015
- var pollMs = CONFIG_POLL_MIN_MS;
6016
- async function refreshConfig() {
6017
- try {
6018
- const res = await rpc("/config");
6019
- if (res && res.ok && res.config && res.config.features) {
6020
- const next = res.config.features.pinnedKeepCollapsed !== false;
6021
- if (next !== keepCollapsed) {
6022
- keepCollapsed = next;
6023
- return true;
6024
- }
6025
- }
6026
- } catch {
6027
- }
6028
- return false;
6029
- }
6030
- function pollConfig() {
6031
- setTimeout(async () => {
6032
- if (document.hidden) pollMs = CONFIG_POLL_MAX_MS;
6033
- else pollMs = await refreshConfig() ? CONFIG_POLL_MIN_MS : Math.min(Math.round(pollMs * 1.5), CONFIG_POLL_MAX_MS);
6034
- pollConfig();
6035
- }, pollMs);
6036
- }
6037
- function startPinnedExpandSuppression() {
6038
- if (installed4 || typeof document === "undefined") return;
6039
- installed4 = true;
6040
- void refreshConfig();
6041
- pollConfig();
6042
- document.addEventListener("visibilitychange", () => {
6043
- if (!document.hidden) void refreshConfig();
6044
- });
6045
- document.addEventListener("click", (e) => {
6046
- if (!keepCollapsed) return;
6047
- const taskRow = e.target.closest && e.target.closest('[data-testid^="task-item-"]');
6048
- if (!taskRow) return;
6049
- const key = taskRow.getAttribute("data-task-item-key") || "";
6050
- const sep = key.lastIndexOf(":");
6051
- if (sep <= 0) return;
6052
- const wsKey = key.slice(0, sep);
6053
- const wsRow = document.querySelector(`[data-testid="workspace-item-${cssEscape(wsKey)}"]`);
6054
- if (!wsRow || wsRow.getAttribute("aria-expanded") !== "false") return;
6055
- const head = wsRow.matches("[aria-expanded]") ? wsRow : wsRow.querySelector("[aria-expanded]");
6056
- if (!head) return;
6057
- collapseAfterContentLoaded(wsRow);
6058
- }, true);
6059
- document.addEventListener("click", (e) => {
6060
- const row = e.target.closest && e.target.closest('[data-testid^="workspace-item-"]');
6061
- if (row) row.__zcodeproUserTouched = Date.now();
6062
- }, true);
6063
- }
6064
- function currentTaskRow() {
6065
- for (const el of document.querySelectorAll("[data-task-item-key]")) {
6066
- if ((el.className + "").includes("bg-selected")) return el;
6067
- }
6068
- return null;
6069
- }
6070
- function collapseNavSafe(head) {
6071
- const cur = currentTaskRow();
6072
- const backKey = cur ? cur.getAttribute("data-task-item-key") : "";
6073
- head.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
6074
- if (!backKey) return;
6075
- const back = cur && cur.isConnected ? cur : document.querySelector(`[data-task-item-key="${cssEscape(backKey)}"]`);
6076
- if (back) back.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true }));
6077
- }
6078
- function collapseAfterContentLoaded(wsRow) {
6079
- const deadline = Date.now() + 25e3;
6080
- let lastLen = (document.querySelector("main") || document.body).innerText.length;
6081
- let grewAt = 0;
6082
- let settledAt = 0;
6083
- const headOf = () => {
6084
- const h2 = wsRow.matches("[aria-expanded]") ? wsRow : wsRow.querySelector("[aria-expanded]");
6085
- return h2 || null;
6086
- };
6087
- const userTouched2 = () => wsRow.__zcodeproUserTouched && Date.now() - wsRow.__zcodeproUserTouched < 800;
6088
- const tick2 = () => {
6089
- if (!keepCollapsed || Date.now() > deadline || !wsRow.isConnected) return done();
6090
- const len = (document.querySelector("main") || document.body).innerText.length;
6091
- if (len > lastLen) grewAt = Date.now();
6092
- if (grewAt && Date.now() - grewAt >= 1e3) settledAt = settledAt || Date.now();
6093
- lastLen = len;
6094
- const head = headOf();
6095
- if (head && head.getAttribute("aria-expanded") === "true" && !userTouched2()) {
6096
- collapseNavSafe(head);
6097
- }
6098
- if (settledAt && Date.now() - settledAt >= 1e3) {
6099
- const h2 = headOf();
6100
- if (h2 && h2.getAttribute("aria-expanded") === "true" && !userTouched2()) {
6101
- collapseNavSafe(h2);
6102
- }
6103
- return done();
6104
- }
6105
- setTimeout(tick2, 200);
6106
- };
6107
- const done = () => {
6108
- wsRow.__zcodeproSuppending = false;
6109
- };
6110
- wsRow.__zcodeproSuppending = true;
6111
- setTimeout(tick2, 400);
6112
- }
6113
- function cssEscape(s) {
6114
- return String(s).replace(/(["\\\]])/g, "\\$1");
6115
- }
6116
-
6117
6830
  // src/inject/features/ws-running.js
6118
- var installed5 = false;
6831
+ var installed4 = false;
6119
6832
  var enabled3 = true;
6120
6833
  var SCAN_DEBOUNCE_MS4 = 150;
6121
6834
  var SCAN_FALLBACK_MS4 = 5e3;
@@ -6124,34 +6837,148 @@
6124
6837
  var PEEK_SETTLE_MS = 160;
6125
6838
  var PEEK_MAX_MS = 1500;
6126
6839
  var PEEK_UNMOUNT_WAIT_MS = 800;
6127
- var CONFIG_POLL_MIN_MS2 = 5e3;
6128
- var CONFIG_POLL_MAX_MS2 = 6e4;
6840
+ var CONFIG_POLL_MIN_MS = 5e3;
6841
+ var CONFIG_POLL_MAX_MS = 6e4;
6129
6842
  var TASK_SEL3 = "[data-task-item-key]";
6130
- var WS_SEL = '[data-testid^="workspace-item-"]';
6843
+ var WS_SEL = () => projectRowSelector();
6844
+ function findWsRow(ws) {
6845
+ return findProjectRow(ws);
6846
+ }
6847
+ var LOADER_FALLBACK_PATHS = [
6848
+ "M12 2v4",
6849
+ "m16.2 7.8 2.9-2.9",
6850
+ "M18 12h4",
6851
+ "m19.8 16.2-2.9 2.9",
6852
+ "M12 18v4",
6853
+ "m7.8 19.8-2.9-2.9",
6854
+ "M6 12H2",
6855
+ "m4.2 7.8 2.9-2.9"
6856
+ ];
6857
+ function makeLoaderSvg(baseClass) {
6858
+ const ns = "http://www.w3.org/2000/svg";
6859
+ const proto = document.querySelector("svg.lucide-loader");
6860
+ if (proto) {
6861
+ const clone = proto.cloneNode(true);
6862
+ const origClass = proto.getAttribute("class") || "";
6863
+ const keep = origClass.split(/\s+/).filter((c) => /^(size-|text-)/.test(c)).join(" ");
6864
+ clone.setAttribute("class", ((keep || baseClass) + " animate-spin zcodepro-ws-loader").trim());
6865
+ return clone;
6866
+ }
6867
+ const svg = document.createElementNS(ns, "svg");
6868
+ svg.setAttribute("viewBox", "0 0 24 24");
6869
+ svg.setAttribute("fill", "none");
6870
+ svg.setAttribute("stroke", "currentColor");
6871
+ svg.setAttribute("stroke-width", "2");
6872
+ svg.setAttribute("stroke-linecap", "round");
6873
+ svg.setAttribute("stroke-linejoin", "round");
6874
+ svg.setAttribute("class", (baseClass + " animate-spin zcodepro-ws-loader").trim());
6875
+ for (const d of LOADER_FALLBACK_PATHS) {
6876
+ const path = document.createElementNS(ns, "path");
6877
+ path.setAttribute("d", d);
6878
+ svg.append(path);
6879
+ }
6880
+ return svg;
6881
+ }
6882
+ function setRunningIcon(holder, on) {
6883
+ if (!holder) return;
6884
+ if (on) {
6885
+ if (holder.querySelector(":scope > svg.zcodepro-ws-loader")) return;
6886
+ const orig0 = holder.querySelector(":scope > svg:not(.zcodepro-ws-loader)");
6887
+ const base = (orig0 ? orig0.getAttribute("class") || "" : "size-4").replace(/lucide-[a-z-]*/g, "").trim() || "size-4";
6888
+ holder.append(makeLoaderSvg(base));
6889
+ } else {
6890
+ holder.querySelectorAll(":scope > svg.zcodepro-ws-loader").forEach((el) => el.remove());
6891
+ }
6892
+ }
6893
+ function iconHolderOf(row) {
6894
+ if (row.hasAttribute && row.hasAttribute("data-project-id")) {
6895
+ const toggle2 = row.querySelector('[data-testid="virtual-project-toggle"]');
6896
+ return toggle2 ? toggle2.querySelector(":scope > span") : null;
6897
+ }
6898
+ return row.firstElementChild && row.firstElementChild.querySelector(":scope > span");
6899
+ }
6900
+ function isRunningRow(el) {
6901
+ const scope = el.firstElementChild;
6902
+ if (!scope) return false;
6903
+ const svg = scope.querySelector('svg[class*="lucide-loader"]');
6904
+ return !!svg;
6905
+ }
6131
6906
  var runningByWs = /* @__PURE__ */ new Map();
6132
6907
  var peekTimers = /* @__PURE__ */ new Map();
6133
6908
  var activePeeks = /* @__PURE__ */ new Set();
6134
6909
  var peekedWs = /* @__PURE__ */ new Set();
6135
6910
  var wsWithRows = /* @__PURE__ */ new Set();
6911
+ var servicesRef = null;
6912
+ function findServices() {
6913
+ if (servicesRef) return servicesRef;
6914
+ try {
6915
+ const rootEl = document.getElementById("root");
6916
+ const containerKey = rootEl && Object.keys(rootEl).find((k) => k.startsWith("__reactContainer$"));
6917
+ if (!containerKey) return null;
6918
+ const seen = /* @__PURE__ */ new Set();
6919
+ let found = null;
6920
+ const scanObj = (o) => {
6921
+ if (!o || typeof o !== "object" || seen.has(o) || found) return;
6922
+ seen.add(o);
6923
+ const ts = o.zcodeTaskService;
6924
+ if (ts && typeof ts === "object" && typeof ts.releaseWorkspacePreparation === "function") found = o;
6925
+ };
6926
+ let visited = 0;
6927
+ const visit = (f) => {
6928
+ if (!f || found || visited > 6e4) return;
6929
+ visited++;
6930
+ for (const holder of [f.memoizedProps, f.memoizedState]) {
6931
+ if (!holder || typeof holder !== "object") continue;
6932
+ scanObj(holder);
6933
+ for (const v of Object.values(holder)) {
6934
+ if (v && typeof v === "object") scanObj(v);
6935
+ }
6936
+ }
6937
+ visit(f.child);
6938
+ visit(f.sibling);
6939
+ };
6940
+ visit(rootEl[containerKey]);
6941
+ if (found) servicesRef = found;
6942
+ return found;
6943
+ } catch {
6944
+ return null;
6945
+ }
6946
+ }
6947
+ async function authoritativeIdle(ws) {
6948
+ const services = findServices();
6949
+ const ts = services && services.zcodeTaskService;
6950
+ if (!ts || typeof ts.listActiveTaskIds !== "function") return null;
6951
+ try {
6952
+ const base = String(ws).replace(/[\\/]+$/, "");
6953
+ const a = await ts.listActiveTaskIds({ workspacePath: base });
6954
+ const b = await ts.listActiveTaskIds({ workspacePath: base + "/" });
6955
+ const ids = [...Array.isArray(a) ? a : [], ...Array.isArray(b) ? b : []].map(String);
6956
+ if (!Array.isArray(a) || !Array.isArray(b)) return null;
6957
+ if (!ids.length) return true;
6958
+ const st = await rpc("/task-statuses?ids=" + encodeURIComponent(ids.join(","))).catch(() => null);
6959
+ const TERMINAL = /* @__PURE__ */ new Set(["completed", "error", "interrupted"]);
6960
+ const live = (id) => {
6961
+ const rec = st && st.ok && st.available ? st.statuses[id] : void 0;
6962
+ if (rec === void 0) return true;
6963
+ if (!TERMINAL.has(rec.status)) return true;
6964
+ return Date.now() - rec.updatedAt < 3e5;
6965
+ };
6966
+ return !ids.some(live);
6967
+ } catch {
6968
+ return null;
6969
+ }
6970
+ }
6136
6971
  function workspaceOf2(key) {
6137
6972
  const i = key.indexOf(":sess_");
6138
6973
  return i > 0 ? key.slice(0, i) : key;
6139
6974
  }
6140
- function findWsRow(ws) {
6141
- return document.querySelector(`[data-testid="workspace-item-${CSS.escape(ws)}"]`);
6142
- }
6143
6975
  function wsHead(row) {
6144
6976
  return row.matches("[aria-expanded]") ? row : row.querySelector("[aria-expanded]");
6145
6977
  }
6146
- function isRunningRow(el) {
6147
- const holder = el.firstElementChild && el.firstElementChild.querySelector(":scope > span");
6148
- if (!holder) return false;
6149
- return [...holder.children].some((c) => c.tagName === "svg" && (c.getAttribute("class") || "").includes("lucide-loader"));
6150
- }
6151
6978
  function domRowsOf(ws) {
6152
6979
  return [...document.querySelectorAll(TASK_SEL3)].filter((e) => workspaceOf2(e.getAttribute("data-task-item-key") || "") === ws);
6153
6980
  }
6154
- function currentTaskRow2() {
6981
+ function currentTaskRow() {
6155
6982
  for (const el of document.querySelectorAll(TASK_SEL3)) {
6156
6983
  if ((el.className + "").includes("bg-selected")) return el;
6157
6984
  }
@@ -6164,7 +6991,7 @@
6164
6991
  }
6165
6992
  };
6166
6993
  function toggleNavSafe(head) {
6167
- const cur = currentTaskRow2();
6994
+ const cur = currentTaskRow();
6168
6995
  const backKey = cur ? cur.getAttribute("data-task-item-key") : "";
6169
6996
  synthClick(head);
6170
6997
  if (!backKey) return;
@@ -6232,17 +7059,169 @@
6232
7059
  }
6233
7060
  function apply() {
6234
7061
  const seen = /* @__PURE__ */ new Set();
6235
- for (const row of document.querySelectorAll(WS_SEL)) {
6236
- const path = row.getAttribute("data-testid").slice("workspace-item-".length);
7062
+ for (const row of document.querySelectorAll(WS_SEL())) {
7063
+ const path = rowPath(row);
7064
+ if (!path) continue;
6237
7065
  seen.add(path);
6238
7066
  const head = wsHead(row);
6239
7067
  const collapsed = !head || head.getAttribute("aria-expanded") === "false";
6240
- const running = !!(enabled3 && collapsed && runningByWs.get(path) && runningByWs.get(path).size);
6241
- const icon = row.firstElementChild && row.firstElementChild.querySelector(":scope > span");
6242
- if (icon) icon.classList.toggle("zcodepro-ws-running", running);
6243
- }
7068
+ const domRunning = !!(runningByWs.get(path) && runningByWs.get(path).size);
7069
+ const normP = (v) => String(v || "").replace(/[\\/]+$/, "");
7070
+ const running = enabled3 && collapsed && (activeProjectPaths.size ? [...activeProjectPaths].some((p2) => normP(p2) === normP(path)) : domRunning);
7071
+ const icon = iconHolderOf(row);
7072
+ icon?.classList.toggle("zcodepro-ws-running", running);
7073
+ setRunningIcon(icon, running);
7074
+ }
7075
+ applySectionSpin();
6244
7076
  for (const ws of [...runningByWs.keys()]) if (!seen.has(ws)) runningByWs.delete(ws);
6245
7077
  }
7078
+ var headInfoCache = /* @__PURE__ */ new WeakMap();
7079
+ var PROJECT_HEAD_TEXTS = /* @__PURE__ */ new Set(["\u9879\u76EE", "projects"]);
7080
+ var TASK_HEAD_TEXTS = /* @__PURE__ */ new Set(["\u4EFB\u52A1", "tasks"]);
7081
+ function kindFromHeadText(head) {
7082
+ const text = (head.textContent || "").trim().toLowerCase();
7083
+ if (PROJECT_HEAD_TEXTS.has(text)) return "project";
7084
+ if (TASK_HEAD_TEXTS.has(text)) return "task";
7085
+ return null;
7086
+ }
7087
+ function sectionHeads() {
7088
+ const heads = [];
7089
+ for (const sv of document.querySelectorAll("svg[data-purpose-section-chevron]")) {
7090
+ const btn = sv.closest("button");
7091
+ if (btn) heads.push(btn);
7092
+ }
7093
+ for (const h3 of document.querySelectorAll("h3")) heads.push(h3);
7094
+ return heads.map((head) => {
7095
+ const cached = headInfoCache.get(head);
7096
+ if (cached) return { head, cont: cached.cont, kind: cached.kind };
7097
+ const textKind = head.tagName === "BUTTON" ? kindFromHeadText(head) : "task";
7098
+ let cont = head.parentElement;
7099
+ while (cont && !cont.querySelector("li[data-task-item-key]") && cont !== document.body) cont = cont.parentElement;
7100
+ const contOk = cont && cont !== document.body;
7101
+ const kind = textKind || (contOk ? cont.querySelector("section[data-project-id]") ? "project" : "task" : null);
7102
+ headInfoCache.set(head, { cont: contOk ? cont : null, kind });
7103
+ return { head, cont: contOk ? cont : null, kind };
7104
+ }).filter((x) => x.head);
7105
+ }
7106
+ var SEC_SPIN_CLASS = "zcodepro-ws-sec-spin";
7107
+ var anyActiveCache = { at: 0, value: null, busy: false };
7108
+ var sectionActive = { task: false, project: false };
7109
+ var activeProjectPaths = /* @__PURE__ */ new Set();
7110
+ setInterval(() => {
7111
+ if (!anyActiveCache.busy && Date.now() - anyActiveCache.at >= 1e4) globalAnyActive();
7112
+ }, 1e4);
7113
+ function globalAnyActive() {
7114
+ if (anyActiveCache.value !== null && Date.now() - anyActiveCache.at < 1e4) return anyActiveCache.value;
7115
+ if (anyActiveCache.busy) return anyActiveCache.value === true;
7116
+ anyActiveCache.busy = true;
7117
+ sectionActive.task = false;
7118
+ sectionActive.project = false;
7119
+ activeProjectPaths = /* @__PURE__ */ new Set();
7120
+ void (async () => {
7121
+ let hit = null;
7122
+ try {
7123
+ const res = await rpc("/projects-v2").catch(() => null);
7124
+ const services = findServices();
7125
+ const ts = services && services.zcodeTaskService;
7126
+ if (res && res.ok && res.available && ts && typeof ts.listActiveTaskIds === "function") {
7127
+ hit = false;
7128
+ const projPaths = /* @__PURE__ */ new Set();
7129
+ for (const proj of res.projects || []) {
7130
+ for (const r of proj.roots || []) {
7131
+ const b = String(r.path || "").replace(/[\\/]+$/, "");
7132
+ if (b) projPaths.add(b);
7133
+ }
7134
+ }
7135
+ const taskSidePaths = /* @__PURE__ */ new Set();
7136
+ for (const el of document.querySelectorAll("[data-task-item-key]")) {
7137
+ const sec = el.closest("section[data-project-id]");
7138
+ if (sec) continue;
7139
+ const k = el.getAttribute("data-task-item-key") || "";
7140
+ const cut = k.lastIndexOf(":");
7141
+ if (cut > 1) taskSidePaths.add(k.slice(0, cut).replace(/[\\/]+$/, ""));
7142
+ }
7143
+ const scanPaths = /* @__PURE__ */ new Set([...projPaths, ...taskSidePaths]);
7144
+ const byKind = { task: /* @__PURE__ */ new Set(), project: /* @__PURE__ */ new Set() };
7145
+ const byKindByPath = /* @__PURE__ */ new Map();
7146
+ const rootToProject = /* @__PURE__ */ new Map();
7147
+ const projectPaths = /* @__PURE__ */ new Map();
7148
+ for (const proj of res.projects || []) {
7149
+ const names = [];
7150
+ for (const r of proj.roots || []) {
7151
+ const b = String(r.path || "").replace(/[\\/]+$/, "");
7152
+ if (b) {
7153
+ rootToProject.set(b, proj.name);
7154
+ names.push(b);
7155
+ }
7156
+ }
7157
+ projectPaths.set(proj.name, names);
7158
+ }
7159
+ for (const base of scanPaths) {
7160
+ const a = await ts.listActiveTaskIds({ workspacePath: base });
7161
+ const b = await ts.listActiveTaskIds({ workspacePath: base + "/" });
7162
+ const ids = [...Array.isArray(a) ? a : [], ...Array.isArray(b) ? b : []].map(String);
7163
+ if (!ids.length) continue;
7164
+ byKindByPath.set(base, ids);
7165
+ const kind = projPaths.has(base) ? "project" : "task";
7166
+ for (const id of ids) byKind[kind].add(id);
7167
+ }
7168
+ const allIds = [...byKind.task, ...byKind.project];
7169
+ const st = allIds.length ? await rpc("/task-statuses?ids=" + encodeURIComponent(allIds.join(","))).catch(() => null) : null;
7170
+ const TERMINAL = /* @__PURE__ */ new Set(["completed", "error", "interrupted"]);
7171
+ const live = (id) => {
7172
+ const rec = st && st.ok && st.available ? st.statuses[id] : void 0;
7173
+ if (rec === void 0) return true;
7174
+ if (!TERMINAL.has(rec.status)) return true;
7175
+ return Date.now() - rec.updatedAt < 3e5;
7176
+ };
7177
+ activeProjectPaths = /* @__PURE__ */ new Set();
7178
+ for (const [base, ids] of byKindByPath) {
7179
+ if (!projPaths.has(base)) continue;
7180
+ if (ids.some(live)) {
7181
+ const projName = rootToProject.get(base);
7182
+ if (projName) for (const p2 of projectPaths.get(projName) || []) activeProjectPaths.add(p2);
7183
+ }
7184
+ }
7185
+ const taskDomRunning = !!document.querySelector('[data-task-item-key]:not(section[data-project-id] *) svg[class*="lucide-loader"]');
7186
+ if (taskDomRunning && [...byKind.task].some(live)) sectionActive.task = true;
7187
+ if ([...byKind.project].some(live)) sectionActive.project = true;
7188
+ hit = sectionActive.task || sectionActive.project;
7189
+ }
7190
+ } catch {
7191
+ }
7192
+ if (hit !== null) anyActiveCache = { at: Date.now(), value: hit, busy: false };
7193
+ else anyActiveCache.busy = false;
7194
+ apply();
7195
+ })();
7196
+ return anyActiveCache.value === true;
7197
+ }
7198
+ function applySectionSpin() {
7199
+ if (!enabled3) {
7200
+ document.querySelectorAll("." + SEC_SPIN_CLASS).forEach((el) => el.remove());
7201
+ return;
7202
+ }
7203
+ for (const { head, cont, kind } of sectionHeads()) {
7204
+ const running = !!(cont && cont.querySelector('li[data-task-item-key] svg[class*="lucide-loader"]'));
7205
+ let expanded = true;
7206
+ if (head.tagName === "BUTTON") expanded = head.getAttribute("aria-expanded") !== "false";
7207
+ else {
7208
+ const list = head.nextElementSibling;
7209
+ expanded = !list || list.offsetHeight > 0;
7210
+ }
7211
+ if (!kind) return;
7212
+ const scoped = kind === "project" ? sectionActive.project : sectionActive.task;
7213
+ const want = !expanded && scoped;
7214
+ const existing = head.querySelector(":scope > ." + SEC_SPIN_CLASS);
7215
+ if (want && !existing) {
7216
+ const span = document.createElement("span");
7217
+ span.className = "flex size-4 shrink-0 items-center justify-center " + SEC_SPIN_CLASS;
7218
+ span.append(makeLoaderSvg("size-4"));
7219
+ head.prepend(span);
7220
+ } else if (!want && existing) {
7221
+ existing.remove();
7222
+ }
7223
+ }
7224
+ }
6246
7225
  function schedulePeek(ws, delay) {
6247
7226
  clearTimeout(peekTimers.get(ws));
6248
7227
  peekTimers.set(ws, setTimeout(() => {
@@ -6274,6 +7253,24 @@
6274
7253
  }
6275
7254
  var peekChain = Promise.resolve();
6276
7255
  async function peek(ws, opts) {
7256
+ if (!opts?.manual) {
7257
+ const auth = await authoritativeIdle(ws);
7258
+ if (auth !== null) {
7259
+ if (auth) {
7260
+ runningByWs.delete(ws);
7261
+ } else {
7262
+ let set = runningByWs.get(ws);
7263
+ if (!set) {
7264
+ set = /* @__PURE__ */ new Set();
7265
+ runningByWs.set(ws, set);
7266
+ }
7267
+ set.add(ws + ":active");
7268
+ schedulePeek(ws, PEEK_REPEAT_MS);
7269
+ }
7270
+ apply();
7271
+ return true;
7272
+ }
7273
+ }
6277
7274
  const run = () => peekInner(ws, opts);
6278
7275
  const p = peekChain.then(run, run);
6279
7276
  peekChain = p.catch(() => {
@@ -6285,7 +7282,7 @@
6285
7282
  const row = findWsRow(ws);
6286
7283
  const head = row && wsHead(row);
6287
7284
  if (!row || !head || head.getAttribute("aria-expanded") !== "false") return false;
6288
- if (!currentTaskRow2()) {
7285
+ if (!currentTaskRow()) {
6289
7286
  schedulePeek(ws, PEEK_REPEAT_MS);
6290
7287
  return false;
6291
7288
  }
@@ -6301,9 +7298,10 @@
6301
7298
  document.body.append(frozen);
6302
7299
  row.style.visibility = "hidden";
6303
7300
  const hider = installPeekHider(ws);
7301
+ let readOk = false;
6304
7302
  try {
6305
7303
  toggleNavSafe(head);
6306
- await settleAndRead(ws);
7304
+ readOk = await settleAndRead(ws);
6307
7305
  const rowNow = findWsRow(ws) || row;
6308
7306
  const headNow = wsHead(rowNow);
6309
7307
  if (headNow && headNow.getAttribute("aria-expanded") === "true" && !userTouched(rowNow)) {
@@ -6319,10 +7317,10 @@
6319
7317
  apply();
6320
7318
  const set = runningByWs.get(ws);
6321
7319
  if (set && set.size && enabled3) schedulePeek(ws, PEEK_REPEAT_MS);
6322
- return true;
7320
+ return readOk;
6323
7321
  }
6324
7322
  function wsRunningSnapshot() {
6325
- const curKey = currentTaskRow2()?.getAttribute("data-task-item-key") || "";
7323
+ const curKey = currentTaskRow()?.getAttribute("data-task-item-key") || "";
6326
7324
  return {
6327
7325
  running: [...runningByWs.entries()].filter(([, set]) => set.size > 0).map(([ws]) => ws),
6328
7326
  current: curKey ? workspaceOf2(curKey) || null : null
@@ -6352,6 +7350,7 @@
6352
7350
  const t0 = Date.now();
6353
7351
  let lastCount = -1;
6354
7352
  let stableAt = 0;
7353
+ let zeroRows = false;
6355
7354
  const timer = setInterval(() => {
6356
7355
  const rows = domRowsOf(ws);
6357
7356
  const elapsed = Date.now() - t0;
@@ -6363,7 +7362,7 @@
6363
7362
  if (!settled && elapsed < PEEK_MAX_MS) return;
6364
7363
  clearInterval(timer);
6365
7364
  if (lastCount === 0) {
6366
- runningByWs.delete(ws);
7365
+ zeroRows = true;
6367
7366
  } else {
6368
7367
  let set = runningByWs.get(ws);
6369
7368
  if (!set) {
@@ -6376,7 +7375,7 @@
6376
7375
  else set.delete(key);
6377
7376
  }
6378
7377
  }
6379
- resolve();
7378
+ resolve(!zeroRows);
6380
7379
  }, 80);
6381
7380
  });
6382
7381
  }
@@ -6417,18 +7416,18 @@
6417
7416
  }
6418
7417
  function relevant(muts) {
6419
7418
  for (const m of muts) {
6420
- if (inSel(m.target, TASK_SEL3) || inSel(m.target, WS_SEL)) return true;
7419
+ if (inSel(m.target, TASK_SEL3) || inSel(m.target, WS_SEL())) return true;
6421
7420
  for (const n of m.addedNodes) {
6422
- if (n instanceof Element && (n.matches(TASK_SEL3) || n.matches(WS_SEL) || n.querySelector(TASK_SEL3) || n.querySelector(WS_SEL))) return true;
7421
+ if (n instanceof Element && (n.matches(TASK_SEL3) || n.matches(WS_SEL()) || n.querySelector(TASK_SEL3) || n.querySelector(WS_SEL()))) return true;
6423
7422
  }
6424
7423
  for (const n of m.removedNodes) {
6425
- if (n instanceof Element && (n.matches(TASK_SEL3) || n.matches(WS_SEL) || n.querySelector(TASK_SEL3))) return true;
7424
+ if (n instanceof Element && (n.matches(TASK_SEL3) || n.matches(WS_SEL()) || n.querySelector(TASK_SEL3))) return true;
6426
7425
  }
6427
7426
  }
6428
7427
  return false;
6429
7428
  }
6430
- var pollMs2 = CONFIG_POLL_MIN_MS2;
6431
- async function refreshConfig2() {
7429
+ var pollMs = CONFIG_POLL_MIN_MS;
7430
+ async function refreshConfig() {
6432
7431
  try {
6433
7432
  const res = await rpc("/config");
6434
7433
  if (res && res.ok && res.config && res.config.features) {
@@ -6446,19 +7445,19 @@
6446
7445
  }
6447
7446
  return false;
6448
7447
  }
6449
- function pollConfig2() {
7448
+ function pollConfig() {
6450
7449
  setTimeout(async () => {
6451
- if (document.hidden) pollMs2 = CONFIG_POLL_MAX_MS2;
6452
- else pollMs2 = await refreshConfig2() ? CONFIG_POLL_MIN_MS2 : Math.min(Math.round(pollMs2 * 1.5), CONFIG_POLL_MAX_MS2);
6453
- pollConfig2();
6454
- }, pollMs2);
7450
+ if (document.hidden) pollMs = CONFIG_POLL_MAX_MS;
7451
+ else pollMs = await refreshConfig() ? CONFIG_POLL_MIN_MS : Math.min(Math.round(pollMs * 1.5), CONFIG_POLL_MAX_MS);
7452
+ pollConfig();
7453
+ }, pollMs);
6455
7454
  }
6456
7455
  var scanDebounce4 = 0;
6457
7456
  function startWsRunningSpin() {
6458
- if (installed5 || typeof document === "undefined") return;
6459
- installed5 = true;
6460
- void refreshConfig2();
6461
- pollConfig2();
7457
+ if (installed4 || typeof document === "undefined") return;
7458
+ installed4 = true;
7459
+ void refreshConfig();
7460
+ pollConfig();
6462
7461
  const observer2 = new MutationObserver((muts) => {
6463
7462
  if (!relevant(muts)) return;
6464
7463
  handleRemovals(muts);
@@ -6479,14 +7478,13 @@
6479
7478
  });
6480
7479
  document.addEventListener("click", (e) => {
6481
7480
  if (!e.isTrusted) return;
6482
- const row = e.target instanceof Element && e.target.closest(WS_SEL);
7481
+ const row = e.target instanceof Element && e.target.closest(WS_SEL());
6483
7482
  if (row) wsTouchAt.set(row, Date.now());
6484
7483
  }, true);
6485
7484
  }
6486
7485
 
6487
7486
  // src/inject/features/idle-reclaim.js
6488
7487
  var CHECK_MS = 3e4;
6489
- var WS_SEL2 = '[data-testid^="workspace-item-"]';
6490
7488
  var MIN_MINUTES = 1;
6491
7489
  var MAX_MINUTES = 1440;
6492
7490
  var idleSince = /* @__PURE__ */ new Map();
@@ -6494,46 +7492,16 @@
6494
7492
  var scanDisabledLogged = false;
6495
7493
  var bootAt = Date.now();
6496
7494
  var startupSweepDone = false;
6497
- var servicesRef = null;
6498
- function findServices() {
6499
- if (servicesRef) return servicesRef;
6500
- try {
6501
- const rootEl = document.getElementById("root");
6502
- const containerKey = rootEl && Object.keys(rootEl).find((k) => k.startsWith("__reactContainer$"));
6503
- if (!containerKey) return null;
6504
- const seen = /* @__PURE__ */ new Set();
6505
- let found = null;
6506
- const scanObj = (o) => {
6507
- if (!o || typeof o !== "object" || seen.has(o) || found) return;
6508
- seen.add(o);
6509
- const ts = o.zcodeTaskService;
6510
- if (ts && typeof ts === "object" && typeof ts.releaseWorkspacePreparation === "function") found = o;
6511
- };
6512
- let visited = 0;
6513
- const visit = (f) => {
6514
- if (!f || found || visited > 6e4) return;
6515
- visited++;
6516
- for (const holder of [f.memoizedProps, f.memoizedState]) {
6517
- if (!holder || typeof holder !== "object") continue;
6518
- scanObj(holder);
6519
- for (const v of Object.values(holder)) {
6520
- if (v && typeof v === "object") scanObj(v);
6521
- }
6522
- }
6523
- visit(f.child);
6524
- visit(f.sibling);
6525
- };
6526
- visit(rootEl[containerKey]);
6527
- if (found) servicesRef = found;
6528
- return found;
6529
- } catch {
6530
- return null;
6531
- }
6532
- }
6533
- function sidebarWorkspaces() {
7495
+ async function sidebarWorkspaces() {
6534
7496
  const out = /* @__PURE__ */ new Set();
6535
- for (const el of document.querySelectorAll(WS_SEL2)) {
6536
- out.add(el.getAttribute("data-testid").slice("workspace-item-".length));
7497
+ const res = await rpc("/projects-v2").catch(() => null);
7498
+ if (res && res.ok && res.available) {
7499
+ for (const proj of res.projects || []) {
7500
+ for (const r of proj.roots || []) {
7501
+ const p = String(r.path || "").replace(/[\/]+$/, "");
7502
+ if (p) out.add(p);
7503
+ }
7504
+ }
6537
7505
  }
6538
7506
  return out;
6539
7507
  }
@@ -6552,8 +7520,14 @@
6552
7520
  }
6553
7521
  }
6554
7522
  async function tryReclaim(ws, cooldownMs) {
6555
- const verifiedIdle = await verifyWorkspaceNotRunning(ws);
6556
- if (!verifiedIdle) {
7523
+ const auth = await authoritativeIdle(ws);
7524
+ if (auth === null) {
7525
+ const verifiedIdle = await verifyWorkspaceNotRunning(ws);
7526
+ if (!verifiedIdle) {
7527
+ blockUntil.set(ws, Date.now() + cooldownMs);
7528
+ return;
7529
+ }
7530
+ } else if (!auth) {
6557
7531
  blockUntil.set(ws, Date.now() + cooldownMs);
6558
7532
  return;
6559
7533
  }
@@ -6583,7 +7557,7 @@
6583
7557
  }
6584
7558
  const busy = new Set(snap.running);
6585
7559
  busy.add(snap.current);
6586
- const workspaces = sidebarWorkspaces();
7560
+ const workspaces = await sidebarWorkspaces();
6587
7561
  for (const ws of [...idleSince.keys(), ...blockUntil.keys()]) {
6588
7562
  if (!workspaces.has(ws)) {
6589
7563
  idleSince.delete(ws);
@@ -6639,24 +7613,34 @@
6639
7613
  var SYNC_DEBOUNCE_MS = 150;
6640
7614
  var SCAN_FALLBACK_MS5 = 5e3;
6641
7615
  var ROW_SEL = "[data-task-item-key]";
6642
- var MENU_SEL = '[data-testid="task-new-button"], [data-testid="automations-open"], [data-testid="plugin-store-sidebar-open"], [data-slot="command"], [data-testid="desktop-top-nav-back"]';
7616
+ 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"]';
6643
7617
  var enabled4 = true;
6644
7618
  var rowEl = null;
6645
7619
  var origs = null;
6646
7620
  var syncTimer = 0;
6647
7621
  var widthObserver = null;
6648
7622
  var widthObserved = null;
6649
- function findMenu() {
6650
- const nt = document.querySelector('[data-testid="task-new-button"]');
6651
- const menu = nt?.parentElement;
6652
- return menu && menu.querySelector('[data-testid="automations-open"]') ? menu : null;
6653
- }
6654
- function originalsOf(menu) {
6655
- const newTask = menu.querySelector('[data-testid="task-new-button"]');
6656
- const search = newTask && menu.querySelector('button [class*="lucide-search"]')?.closest("button");
6657
- const auto = menu.querySelector('[data-testid="automations-open"]');
6658
- const plugin = menu.querySelector('[data-testid="plugin-store-sidebar-open"]');
6659
- return newTask && search && auto && plugin ? { menu, newTask, search, auto, plugin } : null;
7623
+ function findParts() {
7624
+ const newTask = document.querySelector('[data-testid="task-new-button"]');
7625
+ if (!newTask) return null;
7626
+ if (!isNewProjectSidebar()) {
7627
+ const menu = newTask.parentElement;
7628
+ if (!menu || !menu.querySelector('[data-testid="automations-open"]')) return null;
7629
+ const search2 = menu.querySelector('button [class*="lucide-search"]')?.closest("button");
7630
+ const auto2 = menu.querySelector('[data-testid="automations-open"]');
7631
+ const plugin2 = menu.querySelector('[data-testid="plugin-store-sidebar-open"]');
7632
+ return search2 && auto2 && plugin2 ? { newTask, search: search2, auto: auto2, plugin: plugin2, hideEls: [menu] } : null;
7633
+ }
7634
+ const search = document.querySelector('[data-testid="sidebar-search-open"]');
7635
+ const auto = document.querySelector('[data-testid="automations-open"]');
7636
+ const plugin = document.querySelector('[data-testid="plugin-store-sidebar-open"]');
7637
+ if (!search || !auto || !plugin) return null;
7638
+ const hideEls = [.../* @__PURE__ */ new Set([
7639
+ newTask.closest('[data-testid="sidebar-primary-navigation"]'),
7640
+ auto.closest('[data-testid="sidebar-primary-navigation"]'),
7641
+ search.closest('[data-testid="sidebar-search-toolbar"]')
7642
+ ])].filter(Boolean);
7643
+ return hideEls.length ? { newTask, search, auto, plugin, hideEls } : null;
6660
7644
  }
6661
7645
  function nameOf(orig, nameSel, fallback) {
6662
7646
  let name = nameSel && (orig.querySelector(nameSel)?.textContent || "").trim();
@@ -6682,6 +7666,7 @@
6682
7666
  else btn.removeAttribute("data-active");
6683
7667
  if (kind === "auto" || kind === "plugin") btn.setAttribute("aria-pressed", on ? "true" : "false");
6684
7668
  }
7669
+ var lastSelectedKey = "";
6685
7670
  function syncActive() {
6686
7671
  if (!origs || !rowEl) return;
6687
7672
  const auto = origs.auto.getAttribute("aria-pressed") === "true";
@@ -6691,23 +7676,37 @@
6691
7676
  for (const el of document.querySelectorAll(ROW_SEL)) {
6692
7677
  if ((el.className + "").includes("bg-selected")) {
6693
7678
  selected = true;
7679
+ lastSelectedKey = el.getAttribute("data-task-item-key") || lastSelectedKey;
6694
7680
  break;
6695
7681
  }
6696
7682
  }
6697
- setActive("newTask", !selected && !auto && !plugin);
7683
+ let newTaskOn = false;
7684
+ if (!selected && lastSelectedKey) {
7685
+ const stillMounted = document.querySelector(`${ROW_SEL}[data-task-item-key="${CSS.escape(lastSelectedKey)}"]`);
7686
+ if (stillMounted) {
7687
+ newTaskOn = !auto && !plugin;
7688
+ lastSelectedKey = "";
7689
+ } else {
7690
+ const btn = rowEl?.querySelector('[data-kind="newTask"]');
7691
+ newTaskOn = btn ? btn.getAttribute("data-active") === "1" : false;
7692
+ }
7693
+ } else if (!selected) {
7694
+ newTaskOn = !auto && !plugin;
7695
+ }
7696
+ setActive("newTask", newTaskOn);
6698
7697
  setActive("search", palette);
6699
7698
  setActive("auto", auto);
6700
7699
  setActive("plugin", plugin);
6701
7700
  }
6702
7701
  function rebuild() {
6703
- const menu = findMenu();
6704
- if (!menu) return false;
6705
- const next = originalsOf(menu);
7702
+ const parts = findParts();
6706
7703
  const navRow = document.querySelector('[data-testid="desktop-top-nav-back"]')?.parentElement;
6707
- if (!next || !navRow) return false;
6708
- if (origs && origs.menu !== menu) origs.menu.classList.remove(HIDE_CLASS);
6709
- menu.classList.add(HIDE_CLASS);
6710
- origs = next;
7704
+ if (!parts || !navRow) return false;
7705
+ if (origs && origs.hideEls[0] !== parts.hideEls[0]) {
7706
+ for (const el of origs.hideEls) el.classList.remove(HIDE_CLASS);
7707
+ }
7708
+ for (const el of parts.hideEls) el.classList.add(HIDE_CLASS);
7709
+ origs = parts;
6711
7710
  rowEl?.remove();
6712
7711
  navRow.classList.add("zcodepro-tb-hidden-native");
6713
7712
  const L = t();
@@ -6722,17 +7721,17 @@
6722
7721
  setTimeout(syncActive, 150);
6723
7722
  }
6724
7723
  });
6725
- const newBtn = mk("newTask", next.newTask, "span.truncate", L.tbNewTask);
6726
- const icon = iconOf(next.newTask);
7724
+ const newBtn = mk("newTask", parts.newTask, "span.truncate", L.tbNewTask);
7725
+ const icon = iconOf(parts.newTask);
6727
7726
  if (icon) newBtn.append(icon);
6728
- const searchBtn = mk("search", next.search, "span.truncate", L.tbSearch);
6729
- const sIcon = iconOf(next.search);
7727
+ const searchBtn = mk("search", parts.search, "span.truncate", L.tbSearch);
7728
+ const sIcon = iconOf(parts.search);
6730
7729
  if (sIcon) searchBtn.append(sIcon);
6731
- const autoBtn = mk("auto", next.auto, null, L.tbAutomations);
6732
- const aIcon = iconOf(next.auto);
7730
+ const autoBtn = mk("auto", parts.auto, null, L.tbAutomations);
7731
+ const aIcon = iconOf(parts.auto);
6733
7732
  if (aIcon) autoBtn.append(aIcon);
6734
- const pluginBtn = mk("plugin", next.plugin, null, L.tbPluginStore);
6735
- const pIcon = iconOf(next.plugin);
7733
+ const pluginBtn = mk("plugin", parts.plugin, null, L.tbPluginStore);
7734
+ const pIcon = iconOf(parts.plugin);
6736
7735
  if (pIcon) pluginBtn.append(pIcon);
6737
7736
  rowEl = h("div", { class: "zcodepro-tb-row" }, newBtn, searchBtn, autoBtn, pluginBtn);
6738
7737
  const updBtn = [...navRow.querySelectorAll("button")].find((b) => /bg-(success|emerald)/.test(b.className || ""));
@@ -6747,7 +7746,7 @@
6747
7746
  }
6748
7747
  var NARROW_SIDEBAR_PX = 260;
6749
7748
  function sidebarTooNarrow() {
6750
- const scroll = document.querySelector(".flex.flex-1.min-h-0.flex-col.gap-3.overflow-y-auto");
7749
+ const scroll = document.querySelector(".flex.flex-1.min-h-0.flex-col.gap-3.overflow-y-auto") || document.querySelector("aside .min-h-0.flex-1.overflow-y-auto");
6751
7750
  if (!scroll) return false;
6752
7751
  if (!widthObserver) widthObserver = new ResizeObserver(() => scheduleSync());
6753
7752
  if (widthObserved !== scroll) {
@@ -6759,18 +7758,18 @@
6759
7758
  }
6760
7759
  function sync() {
6761
7760
  if (!enabled4) return;
6762
- const menu = findMenu();
6763
- if (!menu) {
7761
+ const parts = findParts();
7762
+ if (!parts) {
6764
7763
  if (rowEl) rowEl.style.display = "none";
6765
7764
  return;
6766
7765
  }
6767
7766
  if (sidebarCollapsed() || sidebarTooNarrow()) {
6768
7767
  if (rowEl) rowEl.style.display = "none";
6769
- menu.classList.remove(HIDE_CLASS);
7768
+ for (const el of parts.hideEls) el.classList.remove(HIDE_CLASS);
6770
7769
  document.querySelectorAll(".zcodepro-tb-hidden-native").forEach((el) => el.classList.remove("zcodepro-tb-hidden-native"));
6771
7770
  return;
6772
7771
  }
6773
- const alive = origs && origs.menu === menu && origs.newTask.isConnected && origs.search.isConnected && origs.auto.isConnected && origs.plugin.isConnected && rowEl && rowEl.isConnected;
7772
+ const alive = origs && origs.hideEls[0] === parts.hideEls[0] && origs.newTask.isConnected && origs.search.isConnected && origs.auto.isConnected && origs.plugin.isConnected && rowEl && rowEl.isConnected;
6774
7773
  if (!alive && !rebuild()) {
6775
7774
  if (rowEl) rowEl.style.display = "none";
6776
7775
  setTimeout(scheduleSync, 400);
@@ -6779,7 +7778,7 @@
6779
7778
  }
6780
7779
  const navRow = document.querySelector('[data-testid="desktop-top-nav-back"]')?.parentElement;
6781
7780
  if (navRow) navRow.classList.add("zcodepro-tb-hidden-native");
6782
- menu.classList.add(HIDE_CLASS);
7781
+ for (const el of parts.hideEls) el.classList.add(HIDE_CLASS);
6783
7782
  if (rowEl) rowEl.style.display = "";
6784
7783
  syncActive();
6785
7784
  }
@@ -6813,12 +7812,12 @@
6813
7812
  rowEl?.remove();
6814
7813
  rowEl = null;
6815
7814
  if (origs) {
6816
- origs.menu.classList.remove(HIDE_CLASS);
7815
+ for (const el of origs.hideEls) el.classList.remove(HIDE_CLASS);
6817
7816
  document.querySelectorAll(".zcodepro-tb-hidden-native").forEach((el) => el.classList.remove("zcodepro-tb-hidden-native"));
6818
7817
  origs = null;
6819
7818
  }
6820
7819
  }
6821
- async function refreshConfig3() {
7820
+ async function refreshConfig2() {
6822
7821
  const cfg = await getConfig(true).catch(() => null);
6823
7822
  enabled4 = !cfg || !cfg.features || cfg.features.toolbarIcons !== false;
6824
7823
  if (enabled4) sync();
@@ -6827,9 +7826,9 @@
6827
7826
  function startToolbarIcons() {
6828
7827
  ensureStyle();
6829
7828
  window.addEventListener("zcodepro:config-changed", () => {
6830
- void refreshConfig3();
7829
+ void refreshConfig2();
6831
7830
  });
6832
- void refreshConfig3();
7831
+ void refreshConfig2();
6833
7832
  new MutationObserver((muts) => {
6834
7833
  if (relevantMutations(muts)) scheduleSync();
6835
7834
  }).observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ["class", "aria-pressed"] });
@@ -6851,8 +7850,8 @@
6851
7850
  var syncTimer2 = 0;
6852
7851
  function findPinnedHeader() {
6853
7852
  for (const h3 of document.querySelectorAll("h3")) {
6854
- const ul = h3.nextElementSibling;
6855
- if (ul && ul.tagName === "UL" && ul.querySelector("li[data-task-item-key]")) return h3;
7853
+ const list = h3.nextElementSibling;
7854
+ if (list && list.querySelector("li[data-task-item-key]")) return h3;
6856
7855
  }
6857
7856
  return null;
6858
7857
  }
@@ -6948,7 +7947,7 @@
6948
7947
  h3.parentElement?.classList.remove(COLLAPSED_CLASS);
6949
7948
  }
6950
7949
  }
6951
- async function refreshConfig4() {
7950
+ async function refreshConfig3() {
6952
7951
  const cfg = await getConfig(true).catch(() => null);
6953
7952
  enabled5 = !cfg || !cfg.features || cfg.features.pinnedCollapse !== false;
6954
7953
  if (enabled5) sync2();
@@ -6957,9 +7956,9 @@
6957
7956
  function startPinnedCollapse() {
6958
7957
  ensureStyle();
6959
7958
  window.addEventListener("zcodepro:config-changed", () => {
6960
- void refreshConfig4();
7959
+ void refreshConfig3();
6961
7960
  });
6962
- void refreshConfig4();
7961
+ void refreshConfig3();
6963
7962
  new MutationObserver((muts) => {
6964
7963
  if (relevantMutations2(muts)) scheduleSync2();
6965
7964
  }).observe(document.body, { childList: true, subtree: true });
@@ -7034,6 +8033,10 @@
7034
8033
  void startAliasWatcher();
7035
8034
  } catch {
7036
8035
  }
8036
+ try {
8037
+ startAliasMigrationWatcher();
8038
+ } catch {
8039
+ }
7037
8040
  try {
7038
8041
  startTaskOrderWatcher();
7039
8042
  } catch {
@@ -7054,10 +8057,6 @@
7054
8057
  startImageMenu();
7055
8058
  } catch {
7056
8059
  }
7057
- try {
7058
- startPinnedExpandSuppression();
7059
- } catch {
7060
- }
7061
8060
  try {
7062
8061
  startWsRunningSpin();
7063
8062
  } catch {