zcode-pro 0.18.0 → 0.19.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
@@ -35,6 +35,7 @@
35
35
  tabFeatures: "\u529F\u80FD",
36
36
  tabStyles: "\u6837\u5F0F",
37
37
  tabProxy: "\u4EE3\u7406",
38
+ tabBehavior: "\u884C\u4E3A",
38
39
  tabAgents: "\u5168\u5C40\u63D0\u793A\u8BCD",
39
40
  agentsDesc: "\u5199\u5165 ~/.zcode/AGENTS.md\uFF0C\u4F5C\u4E3A\u9ED8\u8BA4\u6307\u4EE4\u7528\u4E8E\u6240\u6709\u9879\u76EE\u7684\u6BCF\u6B21\u4F1A\u8BDD\uFF1B\u4FDD\u5B58\u540E\u4ECE\u65B0\u4F1A\u8BDD\u8D77\u751F\u6548\uFF0C\u9879\u76EE\u5185\u7684 AGENTS.md \u53EF\u8865\u5145\u6216\u8986\u76D6\u3002\u5185\u5BB9\u8D85\u8FC7 100KB \u4F1A\u88AB\u5E94\u7528\u622A\u65AD\u3002",
40
41
  agentsPlaceholder: "\u586B\u5199\u5E0C\u671B\u6240\u6709\u9879\u76EE\u9ED8\u8BA4\u9075\u5FAA\u7684\u6307\u4EE4\uFF1B\u6E05\u7A7A\u5E76\u4FDD\u5B58\u5373\u79FB\u9664\u5168\u5C40\u63D0\u793A\u8BCD",
@@ -146,6 +147,30 @@
146
147
  featureSessionSwitchDesc: "alt+z \u5728\u5F53\u524D\u4E0E\u4E0A\u6B21\u4F1A\u8BDD\u95F4\u6765\u56DE\u5207\u6362\uFF1B\u6309\u4F4F alt \u518D\u6309 x/c \u5F39\u51FA\u6700\u8FD1\u4F1A\u8BDD\u5217\u8868\u524D\u540E\u9009\u62E9\uFF0C\u677E\u5F00 alt \u5207\u6362\uFF08\u7C7B alt+tab\uFF09\u3002",
147
148
  featureTitleLock: "\u91CD\u547D\u540D\u4F1A\u8BDD\u540D\u9501\u5B9A",
148
149
  featureTitleLockDesc: "\u624B\u52A8\u91CD\u547D\u540D\u8FC7\u7684\u4F1A\u8BDD\u4E0D\u518D\u88AB\u81EA\u52A8\u6807\u9898\u8986\u76D6\uFF1A\u4F1A\u8BDD\u6709\u65B0\u56DE\u7B54\u540E\u754C\u9762\u53EF\u80FD\u6539\u663E\u81EA\u52A8\u6807\u9898\uFF0C\u91CD\u542F\u540E\u53C8\u53D8\u56DE\uFF0C\u672C\u529F\u80FD\u8BA9\u4FA7\u680F\u59CB\u7EC8\u663E\u793A\u91CD\u547D\u540D\u540E\u7684\u540D\u5B57\u3002",
150
+ tabTitleGen: "\u4EFB\u52A1\u540D\u63D0\u793A\u8BCD",
151
+ titleGenShortDesc: "\u65B0\u4F1A\u8BDD\u5F00\u59CB\u4F5C\u7B54\u65F6\uFF0C\u6309\u6240\u9009\u4F9B\u5E94\u5546\u4E0E\u63D0\u793A\u8BCD\u751F\u6210\u4EFB\u52A1\u540D\u5E76\u6309\u624B\u52A8\u91CD\u547D\u540D\u4FDD\u5B58\uFF0C\u5B98\u65B9\u81EA\u52A8\u6807\u9898\u4E0D\u518D\u8986\u76D6\u3002",
152
+ titleGenEnabled: "\u542F\u7528",
153
+ titleGenProvider: "\u4F9B\u5E94\u5546",
154
+ titleGenProviderNone: "\u4E0D\u8DDF\u968F\uFF08\u81EA\u586B\u5730\u5740\uFF09",
155
+ titleGenProviderSession: "\u8DDF\u968F\u5F53\u524D\u4F1A\u8BDD",
156
+ titleGenProviderDeleted: "\uFF08\u5DF2\u5220\u9664\uFF09",
157
+ titleGenModel: "\u6A21\u578B",
158
+ titleGenModelHint: "\u4ECE\u5217\u8868\u9009\u62E9\uFF0C\u6216\u76F4\u63A5\u586B\u5199\u6A21\u578B id",
159
+ titleGenModelEmpty: "\uFF08\u672A\u9009\u62E9\uFF09",
160
+ titleGenModelSessionHint: "\u4ECE\u5217\u8868\u9009\u62E9\u6A21\u578B\uFF1B\u5217\u8868\u7B2C\u4E00\u9879\u4E3A\u8DDF\u968F\u4F1A\u8BDD\u5F53\u524D\u6A21\u578B",
161
+ titleGenModelFollowSession: "\u8DDF\u968F\u5F53\u524D\u4F1A\u8BDD\u5F53\u524D\u6A21\u578B",
162
+ titleGenModelManualHint: "\u8F93\u5165\u6A21\u578B id",
163
+ titleGenBaseUrl: "\u63A5\u53E3\u5730\u5740",
164
+ titleGenApiKey: "API Key",
165
+ titleGenFormat: "\u534F\u8BAE",
166
+ titleGenPrompt: "\u63D0\u793A\u8BCD",
167
+ titleGenPromptPlaceholder: "\u7559\u7A7A\u4F7F\u7528\u5185\u7F6E\u9ED8\u8BA4\u63D0\u793A\u8BCD\uFF08\u70B9\u51FB\u4E0B\u65B9\u6309\u94AE\u53EF\u67E5\u770B\uFF09",
168
+ titleGenPromptReset: "\u4F7F\u7528\u9ED8\u8BA4\u63D0\u793A\u8BCD",
169
+ titleGenSample: "\u6D4B\u8BD5\u6D88\u606F",
170
+ titleGenTest: "\u6D4B\u8BD5\u751F\u6210",
171
+ titleGenTesting: "\u751F\u6210\u4E2D\u2026",
172
+ titleGenTestOk: "\u751F\u6210\u7ED3\u679C\uFF1A",
173
+ titleGenTestFailed: "\u751F\u6210\u5931\u8D25",
149
174
  featureWsRunning: "\u6298\u53E0\u9879\u76EE\u8FD0\u884C\u63D0\u793A\uFF08\u5B9E\u9A8C\u6027\uFF09",
150
175
  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",
151
176
  tabPerformance: "\u6027\u80FD",
@@ -220,10 +245,6 @@
220
245
  visionMoveDown: "\u4E0B\u79FB",
221
246
  visionName: "\u540D\u79F0",
222
247
  visionUseProvider: "\u4F9B\u5E94\u5546",
223
- visionUseProviderHint: "\u4E0B\u62C9\u9009\u62E9\u6216\u624B\u52A8\u8F93\u5165\uFF1Asession=\u8DDF\u968F\u5F53\u524D\u4F1A\u8BDD\u4F9B\u5E94\u5546\uFF0C\u6216\u4F9B\u5E94\u5546\u540D/ID\uFF1B\u8BBE\u7F6E\u540E baseUrl/API Key/\u683C\u5F0F\u81EA\u52A8\u53D6\u8BE5\u4F9B\u5E94\u5546",
224
- visionUseProviderNone: "\u4E0D\u8DDF\u968F\uFF08\u81EA\u586B\u5730\u5740\uFF09",
225
- visionUseProviderSession: "\u8DDF\u968F\u5F53\u524D\u4F1A\u8BDD\uFF08session\uFF09",
226
- visionUseProviderUnknown: "\u672A\u627E\u5230\u8BE5\u4F9B\u5E94\u5546\uFF0C\u8BC6\u522B\u65F6\u4F1A\u62A5\u9519\uFF1B\u8BF7\u4ECE\u4E0B\u62C9\u9009\u62E9\uFF0C\u6216\u8F93\u5165 session",
227
248
  visionSkipAfterFailures: "\u8FDE\u7EED\u5931\u8D25\u6B21\u6570",
228
249
  visionSkipAfterFailuresHint: "\u67D0\u4EE3\u7406\u8FDE\u7EED\u5931\u8D25\u6EE1\u6B21\u6570\u540E\u6682\u65F6\u8DF3\u8FC7\u5B83\uFF0C\u76F4\u63A5\u8BD5\u4E0B\u4E00\u4E2A\uFF1B0 = \u4E0D\u8DF3\u8FC7",
229
250
  visionSkipMinutes: "\u8DF3\u8FC7\u5206\u949F\u6570",
@@ -332,7 +353,6 @@
332
353
  upgradeDone: "\u5DF2\u66F4\u65B0\u5230 {v}",
333
354
  upgradeFinished: "\u5347\u7EA7\u5B8C\u6210",
334
355
  upgradeCheckFailed: "\u68C0\u67E5\u66F4\u65B0\u5931\u8D25",
335
- version: "\u7248\u672C",
336
356
  close: "\u5173\u95ED",
337
357
  cancel: "\u53D6\u6D88",
338
358
  pathLabel: "\u8DEF\u5F84",
@@ -376,6 +396,7 @@
376
396
  tabFeatures: "Features",
377
397
  tabStyles: "Styles",
378
398
  tabProxy: "Proxy",
399
+ tabBehavior: "Behavior",
379
400
  tabAgents: "Global Prompt",
380
401
  agentsDesc: "Written to ~/.zcode/AGENTS.md and used as default instructions in every session across all projects. Takes effect for new sessions; per-project AGENTS.md can extend or override it. Content over 100 KB is truncated by the app.",
381
402
  agentsPlaceholder: "Instructions followed by all projects by default; save empty to remove the global prompt",
@@ -487,6 +508,30 @@
487
508
  featureSessionSwitchDesc: "alt+z toggles between the current and the last session; hold alt and press x/c to move the highlight across recently used sessions, release alt to switch (like alt+tab).",
488
509
  featureTitleLock: "Keep renamed session titles",
489
510
  featureTitleLockDesc: "Sessions you renamed keep their custom title: after new replies the app may show an auto-generated title instead, which reverts after a restart; this keeps the renamed title in the sidebar.",
511
+ tabTitleGen: "Task name prompt",
512
+ titleGenShortDesc: "When a new session starts answering, generates the task name with the chosen provider and prompt and saves it as a manual rename the built-in title never overrides.",
513
+ titleGenEnabled: "Enable",
514
+ titleGenProvider: "Provider",
515
+ titleGenProviderNone: "Custom endpoint (no provider)",
516
+ titleGenProviderSession: "Follow current session",
517
+ titleGenProviderDeleted: " (deleted)",
518
+ titleGenModel: "Model",
519
+ titleGenModelHint: "Pick from the list, or type a model id",
520
+ titleGenModelEmpty: "(not set)",
521
+ titleGenModelSessionHint: "Pick from the list; the first entry follows the session model",
522
+ titleGenModelFollowSession: "Follow the session model",
523
+ titleGenModelManualHint: "Type a model id",
524
+ titleGenBaseUrl: "Base URL",
525
+ titleGenApiKey: "API Key",
526
+ titleGenFormat: "Protocol",
527
+ titleGenPrompt: "Prompt",
528
+ titleGenPromptPlaceholder: "Leave empty for the built-in default prompt (button below shows it)",
529
+ titleGenPromptReset: "Use default prompt",
530
+ titleGenSample: "Test message",
531
+ titleGenTest: "Test",
532
+ titleGenTesting: "Generating\u2026",
533
+ titleGenTestOk: "Result: ",
534
+ titleGenTestFailed: "Generation failed",
490
535
  featureWsRunning: "Running indicator on collapsed projects (experimental)",
491
536
  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.",
492
537
  tabPerformance: "Performance",
@@ -561,10 +606,6 @@
561
606
  visionMoveDown: "Down",
562
607
  visionName: "Name",
563
608
  visionUseProvider: "Provider",
564
- visionUseProviderHint: "Pick from the list or type: session = follow the current session provider, or a provider name/ID; baseUrl/API key/format follow that provider",
565
- visionUseProviderNone: "Not following (enter base URL)",
566
- visionUseProviderSession: "Follow session provider (session)",
567
- visionUseProviderUnknown: "Unknown provider; recognition will fail. Pick from the list or type session",
568
609
  visionSkipAfterFailures: "Skip after failures",
569
610
  visionSkipAfterFailuresHint: "After this many consecutive failures a proxy is skipped and the next one is tried; 0 = never skip",
570
611
  visionSkipMinutes: "Skip minutes",
@@ -673,7 +714,6 @@
673
714
  upgradeDone: "Updated to {v}",
674
715
  upgradeFinished: "Update finished",
675
716
  upgradeCheckFailed: "Update check failed",
676
- version: "Version",
677
717
  close: "Close",
678
718
  cancel: "Cancel",
679
719
  pathLabel: "Path",
@@ -870,7 +910,7 @@
870
910
  var overlayClassBare = "fixed inset-0 isolate z-50 flex items-center justify-center duration-100 p-4 platform-linux-desktop:top-12";
871
911
  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";
872
912
  var dialogStack = [];
873
- function openDialog({ title, description, onMount, onClose, width = "sm:max-w-md", overlay = "dim", draggable = false, posKey = "", dismissOnOutside = true }) {
913
+ function openDialog({ title, description, onMount, onClose, width = "sm:max-w-md", overlay = "dim", draggable = false, posKey = "", dismissOnOutside = true, bodyClass = "" }) {
874
914
  const L = t();
875
915
  const prevActive = document.activeElement;
876
916
  let cleanupDrag = null;
@@ -914,7 +954,7 @@
914
954
  titleEl,
915
955
  ...description ? [h("p", { class: "mt-2 text-ui-sm/relaxed text-foreground-subtle" }, description)] : []
916
956
  );
917
- const body = h("div", { class: "mt-4 zcodepro-dialog-body" });
957
+ const body = h("div", { class: "zcodepro-dialog-body " + (bodyClass || "mt-4") });
918
958
  content.append(body);
919
959
  let restorePos = null;
920
960
  if (draggable) {
@@ -1176,7 +1216,7 @@
1176
1216
  "data-state": checked ? "on" : "off"
1177
1217
  }, knob);
1178
1218
  const extraEl = extra ? h("div", {
1179
- class: "flex shrink-0 items-center pt-0.5",
1219
+ class: "flex shrink-0 items-center",
1180
1220
  onClick: (e) => {
1181
1221
  e.stopPropagation();
1182
1222
  },
@@ -1190,7 +1230,7 @@
1190
1230
  const row = h(
1191
1231
  "div",
1192
1232
  {
1193
- class: `flex cursor-pointer items-start gap-2 rounded-lg ${desc ? "p-2" : "p-1.5"} transition-colors hover:bg-surface-hover`,
1233
+ class: `flex cursor-pointer items-center gap-2 rounded-lg ${desc ? "p-2" : "p-1.5"} transition-colors hover:bg-surface-hover`,
1194
1234
  onClick: () => {
1195
1235
  onToggle();
1196
1236
  }
@@ -1278,13 +1318,14 @@
1278
1318
  align-items: center;
1279
1319
  justify-content: center;
1280
1320
  height: 24px;
1281
- padding: 0 14px;
1321
+ padding: 0 8px;
1282
1322
  border: none;
1283
1323
  border-radius: 9999px;
1284
1324
  background: transparent;
1285
1325
  cursor: pointer;
1286
1326
  font-size: 12px;
1287
1327
  line-height: 1;
1328
+ white-space: nowrap;
1288
1329
  color: var(--color-muted-foreground, #888);
1289
1330
  transition: color 0.15s ease, background-color 0.15s ease;
1290
1331
  }
@@ -1373,6 +1414,8 @@
1373
1414
  .zcodepro-textarea:disabled { opacity: 0.5; }
1374
1415
  /* \u5C0F\u578B\u6587\u672C\u57DF\uFF08\u89C6\u89C9\u4EE3\u7406\u63D0\u793A\u8BCD\u7B49\uFF09\uFF1A11rem \u6700\u5C0F\u9AD8\u5EA6\u53EA\u5C5E\u4E8E\u5168\u5C40\u63D0\u793A\u8BCD\u5927\u7F16\u8F91\u6846\uFF0C\u8FD9\u91CC\u6309 rows \u5B9A\u9AD8 */
1375
1416
  .zcodepro-textarea.zcodepro-textarea-sm { min-height: 0; }
1417
+ /* \u5168\u5C40\u63D0\u793A\u8BCD\u5927\u7F16\u8F91\u6846\uFF1A\u9ED8\u8BA4/\u6700\u5927\u9AD8\u5EA6\u90FD\u6BD4\u9ED8\u8BA4\u6587\u672C\u57DF\u66F4\u9AD8 */
1418
+ .zcodepro-textarea.zcodepro-textarea-lg { min-height: 16rem; max-height: 40rem; }
1376
1419
  /* \u884C\u9AD8\u6ED1\u6746\uFF08\u6837\u5F0F\u8C03\u6574\u6807\u7B7E\u9875\uFF09 */
1377
1420
  .zcodepro-range {
1378
1421
  -webkit-appearance: none;
@@ -1558,6 +1601,14 @@
1558
1601
  }
1559
1602
  /* \u539F\u4FA7\u680F\u83DC\u5355\u6574\u4F53\u9690\u85CF\uFF08toolbar-icons \u5F00\u542F\u65F6\uFF09 */
1560
1603
  .zcodepro-tb-hidden { display: none !important; }
1604
+ /* \u539F\u751F\u9876\u680F\u65B0\u5EFA\u4EFB\u52A1\u6309\u94AE\uFF08\u6587\u4EF6\u67E5\u770B\u7B49\u5B50\u9875\u5C55\u5F00\u7684\u90A3\u4E2A\uFF09\uFF1A\u5E38\u9A7B DOM\u3001\u9760\u52A8\u753B\u5BB9\u5668
1605
+ \u5728 w-7/w-0 \u95F4\u5207\u6362\u663E\u9690\u3002\u529F\u80FD\u5F00\u542F\u671F\u95F4\u6574\u4F53\u9489\u6B7B\u6536\u8D77\u6001\u2014\u2014\u65B0\u5EFA\u5165\u53E3\u7EDF\u4E00\u7531\u6211\u4EEC
1606
+ \u7684\u6309\u94AE\u884C\u63D0\u4F9B\uFF0C\u4E5F\u5C31\u4E0D\u518D\u9700\u8981\u968F\u5B50\u9875\u8FDB\u51FA\u52A8\u6001\u663E\u9690\u81EA\u5DF1\u7684\u65B0\u5EFA\u6309\u94AE */
1607
+ .zcodepro-tb-hidden-native div[class*="transition-[opacity,width]"] {
1608
+ width: 0 !important;
1609
+ opacity: 0 !important;
1610
+ pointer-events: none !important;
1611
+ }
1561
1612
  /* \u5DF2\u7F6E\u9876\u5206\u533A\u53EF\u6298\u53E0\uFF08pinned-collapse.js\uFF09\uFF1A\u6807\u9898\u6574\u884C\u53EF\u70B9\uFF0C\u6298\u53E0\u65F6\u85CF\u8D77\u4EFB\u52A1\u5217\u8868\uFF0C
1562
1613
  chevron \u65CB\u5411\u4E0E\u201C\u9879\u76EE\u201D\u5206\u533A\u4E00\u81F4\uFF08\u6536\u8D77\u6307\u5411\u53F3\u4FA7\uFF09 */
1563
1614
  .zcodepro-pin-head {
@@ -2212,6 +2263,12 @@
2212
2263
  const path = resolveTargetPath(content);
2213
2264
  if (!path) return;
2214
2265
  const L = t();
2266
+ const fire = (endpoint, label) => {
2267
+ rpc(endpoint, { method: "POST", body: { path } }).then((r) => {
2268
+ if (r && r.ok === false) showToast(`${label}\u5931\u8D25\uFF1A${r.error || "\u672A\u77E5\u9519\u8BEF"}`, "error");
2269
+ }).catch(() => {
2270
+ });
2271
+ };
2215
2272
  const noApps = items.find((el) => NO_APPS_TEXTS.some((x) => itemText(el) === x)) || content.querySelector('[data-disabled][role="menuitem"]');
2216
2273
  const openAnchor = noApps && content.contains(noApps) ? noApps : copyAbs;
2217
2274
  appendMenuItem(content, copyAbs, openAnchor, {
@@ -2219,10 +2276,7 @@
2219
2276
  label: L.fileOpenDefault,
2220
2277
  icon: ICON_OPEN_DEFAULT,
2221
2278
  onPick: () => {
2222
- try {
2223
- void rpc("/open-path", { method: "POST", body: { path } });
2224
- } catch {
2225
- }
2279
+ void fire("/open-path", L.fileOpenDefault);
2226
2280
  }
2227
2281
  });
2228
2282
  appendMenuItem(content, copyAbs, copyAbs, {
@@ -2230,10 +2284,7 @@
2230
2284
  label: L.fileReveal,
2231
2285
  icon: ICON_REVEAL,
2232
2286
  onPick: () => {
2233
- try {
2234
- void rpc("/reveal-path", { method: "POST", body: { path } });
2235
- } catch {
2236
- }
2287
+ void fire("/reveal-path", L.fileReveal);
2237
2288
  }
2238
2289
  });
2239
2290
  })();
@@ -2445,6 +2496,56 @@
2445
2496
  }, true);
2446
2497
  }
2447
2498
 
2499
+ // src/inject/provider-fields.js
2500
+ var PROVIDER_FIELD_CLS = "h-8 w-full rounded-lg border border-border bg-input px-2.5 text-ui-sm text-foreground outline-none transition-shadow placeholder:text-foreground-subtle focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40";
2501
+ function comboField(current2, options, onChange, { disabled = false, placeholder = "", staleLabel = "\uFF08\u624B\u52A8\u8F93\u5165\uFF09", emptyLabel = "" } = {}) {
2502
+ const sel = h("select", { class: PROVIDER_FIELD_CLS, disabled: disabled || void 0 });
2503
+ const input = h("input", { type: "text", value: current2, placeholder, class: PROVIDER_FIELD_CLS, disabled: disabled || void 0 });
2504
+ const sync3 = () => {
2505
+ const cur = input.value;
2506
+ const opts = [...options];
2507
+ if (cur && !opts.some((o) => o.value === cur)) opts.push({ value: cur, label: `${cur}${staleLabel}` });
2508
+ if (!cur && !opts.some((o) => o.value === "")) opts.unshift({ value: "", label: emptyLabel });
2509
+ sel.replaceChildren(...opts.map((o) => h("option", { value: o.value, title: o.value }, o.label)));
2510
+ sel.value = cur;
2511
+ };
2512
+ sel.addEventListener("change", () => {
2513
+ input.value = sel.value;
2514
+ onChange(sel.value, "select");
2515
+ });
2516
+ input.addEventListener("input", () => onChange(input.value, "type"));
2517
+ input.addEventListener("blur", () => sync3());
2518
+ sync3();
2519
+ return h("div", { class: "flex flex-col gap-1" }, sel, input);
2520
+ }
2521
+ function providerOptionsOf(providers, L) {
2522
+ return [
2523
+ { value: "", label: L.titleGenProviderNone },
2524
+ { value: "session", label: L.titleGenProviderSession },
2525
+ ...providers.map((p) => ({ value: p.id, label: p.name }))
2526
+ ];
2527
+ }
2528
+ function modelOptionsOf(providerValue, providers, { followSession = false, followSessionLabel = "" } = {}) {
2529
+ const v = (providerValue || "").trim();
2530
+ const cur = providers.find((p) => p.id === v);
2531
+ if (cur) return cur.models.map((m) => ({ value: m, label: m }));
2532
+ if (v === "session") {
2533
+ const head = followSession && followSessionLabel ? [{ value: "", label: followSessionLabel }] : [];
2534
+ return [...head, ...[...new Set(providers.flatMap((p) => p.models))].map((m) => ({ value: m, label: m }))];
2535
+ }
2536
+ return [];
2537
+ }
2538
+ function modelPlaceholderOf(providerValue, L) {
2539
+ const v = (providerValue || "").trim();
2540
+ if (v === "session") return L.titleGenModelSessionHint;
2541
+ if (v === "") return L.titleGenModelManualHint;
2542
+ return L.titleGenModelHint;
2543
+ }
2544
+ function isManualConn(providerValue, providers) {
2545
+ const v = (providerValue || "").trim();
2546
+ return v === "" || v !== "session" && !providers.some((p) => p.id === v);
2547
+ }
2548
+
2448
2549
  // src/inject/features/styles.js
2449
2550
  var STYLE_DEFAULTS = {
2450
2551
  rowGap: 20,
@@ -2850,8 +2951,10 @@
2850
2951
  openDialog({
2851
2952
  title: L.settingsTitle,
2852
2953
  // 宽度类必须用宿主样式表已有的工具类(注入的类名不会生成 CSS):
2853
- // sm:max-w-3xl 不存在会失去约束拉满全屏;max-w-2xl=42rem 存在且尺寸合适
2854
- width: "max-w-2xl",
2954
+ // sm:max-w-3xl 不存在会失去约束拉满全屏;max-w-3xl=48rem 存在——英文界面
2955
+ // 标签栏较长,42rem 会挤出一块空白并触发横向滚动
2956
+ width: "max-w-3xl",
2957
+ bodyClass: "mt-1",
2855
2958
  overlay: "none",
2856
2959
  draggable: true,
2857
2960
  posKey: "settings",
@@ -2912,7 +3015,9 @@
2912
3015
  const config = await getConfig(true);
2913
3016
  const agentsRes = await rpc("/agents");
2914
3017
  const visionRes = await rpc("/vision");
2915
- const visionProvidersRes = await rpc("/vision/providers");
3018
+ const titleGenRes = await rpc("/title-gen");
3019
+ const titleGenProvidersRes = await rpc("/title-gen/providers");
3020
+ const titleGenProviders = titleGenProvidersRes.ok && Array.isArray(titleGenProvidersRes.providers) ? titleGenProvidersRes.providers : [];
2916
3021
  const rtkRes = await rpc("/rtk");
2917
3022
  const headroomRes = await rpc("/headroom");
2918
3023
  const setFeature = async (key, value) => {
@@ -2934,7 +3039,7 @@
2934
3039
  "data-zcodepro-status": "1"
2935
3040
  },
2936
3041
  h("span", { class: "inline-block size-2 rounded-full bg-emerald-500" }),
2937
- h("span", { class: "text-foreground-subtle/70" }, `${L.version} ${health.version} \xB7 ${HELPER_URL}`)
3042
+ h("span", { class: "text-foreground-subtle/70" }, `${health.version} \xB7 ${HELPER_URL}`)
2938
3043
  );
2939
3044
  const rows = h("div", { class: "divide-y divide-border rounded-xl border border-border" });
2940
3045
  const refreshRows = () => {
@@ -3103,19 +3208,20 @@
3103
3208
  );
3104
3209
  const paneStyles = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
3105
3210
  const panePerf = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
3106
- const paneAgents = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
3211
+ const paneAgents = h("div", { role: "tabpanel", class: "mt-2", style: "display:none" });
3107
3212
  const paneProxy = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
3108
3213
  const paneSync = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
3109
3214
  const paneVision = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
3110
3215
  const paneRtk = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
3111
3216
  const paneHeadroom = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
3217
+ const paneTitleGen = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
3112
3218
  const paneDebug = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
3113
3219
  const panes = { features: paneFeatures, styles: paneStyles, perf: panePerf, agents: paneAgents, proxy: paneProxy, sync: paneSync, vision: paneVision, headroom: paneHeadroom, rtk: paneRtk, debug: paneDebug };
3114
3220
  const tabDefs = [
3115
3221
  ["features", L.tabFeatures],
3222
+ ["agents", L.tabBehavior],
3116
3223
  ["styles", L.tabStyles],
3117
3224
  ["perf", L.tabPerformance],
3118
- ["agents", L.tabAgents],
3119
3225
  ["proxy", L.tabProxy],
3120
3226
  ["sync", L.tabSync],
3121
3227
  ["vision", L.tabVision],
@@ -3125,7 +3231,7 @@
3125
3231
  // 普通用户不可见
3126
3232
  ...config.devMode === true || config.devActive === true ? [["debug", L.tabDebug]] : []
3127
3233
  ];
3128
- const tablist = h("div", { role: "tablist", "aria-orientation": "horizontal", class: "zcodepro-tablist mt-4" });
3234
+ const tablist = h("div", { role: "tablist", "aria-orientation": "horizontal", class: "zcodepro-tablist mt-1" });
3129
3235
  const renderTabs = () => tablist.replaceChildren(...tabDefs.map(([id, label]) => h("button", {
3130
3236
  type: "button",
3131
3237
  role: "tab",
@@ -3334,7 +3440,7 @@
3334
3440
  )
3335
3441
  );
3336
3442
  const agentsArea = h("textarea", {
3337
- class: "zcodepro-textarea",
3443
+ class: "zcodepro-textarea zcodepro-textarea-lg",
3338
3444
  placeholder: L.agentsPlaceholder,
3339
3445
  spellcheck: "false"
3340
3446
  });
@@ -3365,12 +3471,36 @@
3365
3471
  agentsSaveBtn.disabled = false;
3366
3472
  }
3367
3473
  };
3368
- paneAgents.append(
3474
+ const agentsPromptPane = h("div", { class: "mt-4" });
3475
+ agentsPromptPane.append(
3369
3476
  h("p", { class: "text-ui-sm/relaxed text-foreground-subtle" }, L.agentsDesc),
3370
3477
  ...agentsFailed ? [h("p", { class: "mt-2 text-ui-sm text-destructive" }, L.agentsLoadFailed + ": " + errText(agentsRes))] : [],
3371
3478
  h("div", { class: "mt-3" }, agentsArea),
3372
3479
  h("div", { class: "mt-3 flex justify-end" }, agentsSaveBtn)
3373
3480
  );
3481
+ let agentsSubTab = "prompt";
3482
+ const agentsSubPanes = { prompt: agentsPromptPane, titleGen: paneTitleGen };
3483
+ const agentsSubList = h("div", { role: "tablist", "aria-orientation": "horizontal", class: "zcodepro-tablist" });
3484
+ const renderAgentsSubTabs = () => {
3485
+ agentsSubList.replaceChildren(...[
3486
+ ["prompt", L.tabAgents],
3487
+ ["titleGen", L.tabTitleGen]
3488
+ ].map(([id, label]) => h("button", {
3489
+ type: "button",
3490
+ role: "tab",
3491
+ class: "zcodepro-tab",
3492
+ "aria-selected": String(agentsSubTab === id),
3493
+ "data-state": agentsSubTab === id ? "active" : "inactive",
3494
+ onClick: () => {
3495
+ agentsSubTab = id;
3496
+ for (const [pid, pane] of Object.entries(agentsSubPanes)) pane.style.display = pid === id ? "" : "none";
3497
+ renderAgentsSubTabs();
3498
+ }
3499
+ }, label)));
3500
+ };
3501
+ for (const [pid, pane] of Object.entries(agentsSubPanes)) pane.style.display = pid === agentsSubTab ? "" : "none";
3502
+ renderAgentsSubTabs();
3503
+ paneAgents.append(agentsSubList, agentsPromptPane, paneTitleGen);
3374
3504
  const savedProxy = typeof config.proxy === "string" ? config.proxy : "";
3375
3505
  const proxyInputCls = "h-8 w-full rounded-lg border border-border bg-input px-2.5 text-ui-sm text-foreground outline-none transition-shadow placeholder:text-foreground-subtle focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40";
3376
3506
  const proxyInput = h("input", { type: "text", value: savedProxy, placeholder: L.proxyPlaceholder, spellcheck: "false", class: proxyInputCls + " min-w-0 flex-1" });
@@ -3966,7 +4096,6 @@
3966
4096
  skipMinutes: 30,
3967
4097
  forceIntercept: true
3968
4098
  };
3969
- const visionProviders = visionProvidersRes.ok && Array.isArray(visionProvidersRes.providers) ? visionProvidersRes.providers : [];
3970
4099
  let visionCfg = visionRes.ok && visionRes.config && typeof visionRes.config === "object" ? structuredClone(visionRes.config) : null;
3971
4100
  let visionSaveTimer = null;
3972
4101
  const persistVision = async () => {
@@ -4158,53 +4287,45 @@
4158
4287
  h(
4159
4288
  "div",
4160
4289
  { class: "mt-2 grid grid-cols-1 gap-2 sm:grid-cols-2" },
4161
- field(L.visionUseProvider, (() => {
4162
- const wrap = h("div", { class: "flex flex-col gap-1" });
4163
- const input = h("input", { type: "text", value: p.useProvider || "", placeholder: L.visionUseProviderHint, class: visionInputCls });
4164
- const warn = h("p", { class: "text-ui-xs text-destructive", style: "display:none" }, L.visionUseProviderUnknown);
4165
- const isKnown = (v) => !v || v === "session" || visionProviders.some((pr) => pr.id === v || pr.name === v || (pr.aliases || []).includes(v));
4166
- const sel = h("select", { class: visionInputCls });
4167
- const syncSelect = () => {
4168
- const cur = (p.useProvider || "").trim();
4169
- const opts = [h("option", { value: "" }, L.visionUseProviderNone), h("option", { value: "session" }, L.visionUseProviderSession)];
4170
- for (const pr of visionProviders) {
4171
- opts.push(h("option", { value: pr.id }, pr.name && pr.name !== pr.id ? `${pr.name}\uFF08${pr.id}\uFF09` : pr.id));
4172
- }
4173
- if (cur && !opts.some((o) => o.value === cur)) opts.push(h("option", { value: cur }, `${cur}\uFF08${L.visionUseProvider}\uFF09`));
4174
- sel.replaceChildren(...opts);
4175
- sel.value = cur;
4176
- };
4177
- sel.addEventListener("change", () => {
4178
- input.value = sel.value;
4179
- p.useProvider = sel.value.trim();
4180
- warn.style.display = "none";
4290
+ // 供应商与模型走 provider-fields 共用逻辑:跟随会话时模型下拉为
4291
+ // 全部供应商模型汇总,具体供应商为其模型列表;已删 id 标注「已删除」
4292
+ field(L.visionUseProvider, comboField(
4293
+ p.useProvider || "",
4294
+ providerOptionsOf(titleGenProviders, L),
4295
+ (v, src) => {
4296
+ p.useProvider = v;
4181
4297
  saveVisionSoon();
4182
- });
4183
- input.addEventListener("input", () => {
4184
- p.useProvider = input.value.trim();
4298
+ if (src === "select") renderVision();
4299
+ },
4300
+ { placeholder: L.titleGenProviderSession, staleLabel: L.titleGenProviderDeleted }
4301
+ )),
4302
+ // 跟随会话时模型下拉头部有「跟随当前会话当前模型」(值为空,
4303
+ // 插件 ≥1.6.1 在调用时解析为会话当前模型);具体供应商为其模型列表
4304
+ field(L.visionModel, comboField(
4305
+ p.model || "",
4306
+ modelOptionsOf(p.useProvider, titleGenProviders, {
4307
+ followSession: (p.useProvider || "").trim() === "session",
4308
+ followSessionLabel: L.titleGenModelFollowSession
4309
+ }),
4310
+ (v) => {
4311
+ p.model = v;
4185
4312
  saveVisionSoon();
4186
- });
4187
- input.addEventListener("blur", () => {
4188
- p.useProvider = input.value.trim();
4189
- warn.style.display = isKnown(p.useProvider) ? "none" : "";
4190
- syncSelect();
4191
- });
4192
- syncSelect();
4193
- wrap.append(sel, input, warn);
4194
- return wrap;
4195
- })()),
4196
- field(L.visionModel, textIn(p.model, (v) => {
4197
- p.model = v;
4198
- saveVisionSoon();
4199
- })),
4200
- field(L.visionBaseUrl, textIn(p.baseUrl, (v) => {
4201
- p.baseUrl = v;
4202
- saveVisionSoon();
4203
- })),
4204
- field(L.visionApiKey + " \xB7 " + L.visionApiKeyHint, textIn(p.apiKey, (v) => {
4205
- p.apiKey = v;
4206
- saveVisionSoon();
4207
- }, { type: "password" }))
4313
+ },
4314
+ {
4315
+ placeholder: modelPlaceholderOf(p.useProvider, L),
4316
+ emptyLabel: (p.useProvider || "").trim() === "session" ? L.titleGenModelFollowSession : L.titleGenModelEmpty
4317
+ }
4318
+ )),
4319
+ ...isManualConn(p.useProvider, titleGenProviders) ? [
4320
+ field(L.visionBaseUrl, textIn(p.baseUrl, (v) => {
4321
+ p.baseUrl = v;
4322
+ saveVisionSoon();
4323
+ })),
4324
+ field(L.visionApiKey + " \xB7 " + L.visionApiKeyHint, textIn(p.apiKey, (v) => {
4325
+ p.apiKey = v;
4326
+ saveVisionSoon();
4327
+ }, { type: "password" }))
4328
+ ] : []
4208
4329
  ),
4209
4330
  (() => {
4210
4331
  const n = h("textarea", { class: "zcodepro-textarea zcodepro-textarea-sm", rows: "3", placeholder: L.visionPrompt });
@@ -4945,13 +5066,190 @@
4945
5066
  );
4946
5067
  };
4947
5068
  renderHeadroomPane();
5069
+ let titleGenCfg = titleGenRes.ok && titleGenRes.titleGen && typeof titleGenRes.titleGen === "object" ? structuredClone(titleGenRes.titleGen) : null;
5070
+ const titleGenDefaultPrompt = titleGenRes.ok && typeof titleGenRes.defaultPrompt === "string" ? titleGenRes.defaultPrompt : "";
5071
+ let titleGenSaveTimer = null;
5072
+ const persistTitleGen = async () => {
5073
+ const res = await rpc("/title-gen", { method: "POST", body: { titleGen: titleGenCfg } });
5074
+ clearConfigCache();
5075
+ if (!res.ok) showToast(L.failed + ": " + errText(res), "error");
5076
+ return res.ok;
5077
+ };
5078
+ const saveTitleGenSoon = () => {
5079
+ clearTimeout(titleGenSaveTimer);
5080
+ titleGenSaveTimer = setTimeout(() => {
5081
+ void persistTitleGen();
5082
+ }, 500);
5083
+ };
5084
+ const renderTitleGenPane = () => {
5085
+ if (!titleGenCfg) {
5086
+ paneTitleGen.replaceChildren(
5087
+ h("p", { class: "text-ui-sm/relaxed text-foreground-subtle" }, L.titleGenShortDesc),
5088
+ h("p", { class: "mt-2 text-ui-sm text-destructive" }, L.visionLoadFailed + ": " + errText(titleGenRes))
5089
+ );
5090
+ return;
5091
+ }
5092
+ const inputCls = "h-8 w-full rounded-lg border border-border bg-input px-2.5 text-ui-sm text-foreground outline-none transition-shadow placeholder:text-foreground-subtle focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40";
5093
+ const field = (labelText, node) => h(
5094
+ "label",
5095
+ { class: "block min-w-0" },
5096
+ h("span", { class: "mb-1 block text-ui-xs font-medium text-foreground-subtle" }, labelText),
5097
+ node
5098
+ );
5099
+ const showInlineConn = isManualConn(titleGenCfg.provider, titleGenProviders);
5100
+ const providerCombo = comboField(
5101
+ titleGenCfg.provider || "",
5102
+ providerOptionsOf(titleGenProviders, L),
5103
+ (v, src) => {
5104
+ titleGenCfg.provider = v;
5105
+ if (src === "select") {
5106
+ void persistTitleGen();
5107
+ renderTitleGenPane();
5108
+ } else saveTitleGenSoon();
5109
+ },
5110
+ { placeholder: L.titleGenProviderSession, staleLabel: L.titleGenProviderDeleted }
5111
+ );
5112
+ const modelCombo = comboField(
5113
+ titleGenCfg.model || "",
5114
+ // 跟随会话时选项表头部有“跟随当前会话当前模型”(值为空,生成时用
5115
+ // 会话当前模型),选了具体模型后也能再切回它
5116
+ modelOptionsOf(titleGenCfg.provider, titleGenProviders, {
5117
+ followSession: true,
5118
+ followSessionLabel: L.titleGenModelFollowSession
5119
+ }),
5120
+ (v) => {
5121
+ titleGenCfg.model = v;
5122
+ saveTitleGenSoon();
5123
+ },
5124
+ {
5125
+ placeholder: modelPlaceholderOf(titleGenCfg.provider, L),
5126
+ emptyLabel: (titleGenCfg.provider || "").trim() === "session" ? L.titleGenModelFollowSession : L.titleGenModelEmpty
5127
+ }
5128
+ );
5129
+ const textIn = (value, onInput, { type = "text", placeholder = "" } = {}) => {
5130
+ const n = h("input", { type, value: value || "", placeholder, class: inputCls });
5131
+ n.addEventListener("input", () => onInput(n.value));
5132
+ return n;
5133
+ };
5134
+ const formatSel = h(
5135
+ "select",
5136
+ { class: inputCls },
5137
+ h("option", { value: "anthropic" }, "anthropic"),
5138
+ h("option", { value: "openai" }, "openai")
5139
+ );
5140
+ formatSel.value = titleGenCfg.format === "openai" ? "openai" : "anthropic";
5141
+ formatSel.addEventListener("change", () => {
5142
+ titleGenCfg.format = formatSel.value;
5143
+ void persistTitleGen();
5144
+ });
5145
+ const inlineConnCard = h(
5146
+ "div",
5147
+ {
5148
+ class: "mt-2 grid grid-cols-1 gap-2 rounded-xl border border-border p-2 sm:grid-cols-3",
5149
+ style: showInlineConn ? "" : "display:none"
5150
+ },
5151
+ field(L.titleGenBaseUrl, textIn(titleGenCfg.baseUrl, (v) => {
5152
+ titleGenCfg.baseUrl = v;
5153
+ saveTitleGenSoon();
5154
+ }, { placeholder: "https://\u2026" })),
5155
+ field(L.titleGenApiKey, textIn(titleGenCfg.apiKey, (v) => {
5156
+ titleGenCfg.apiKey = v;
5157
+ saveTitleGenSoon();
5158
+ }, { type: "password" })),
5159
+ field(L.titleGenFormat, formatSel)
5160
+ );
5161
+ const promptArea = h("textarea", {
5162
+ class: "zcodepro-textarea zcodepro-textarea-sm",
5163
+ rows: "8",
5164
+ placeholder: L.titleGenPromptPlaceholder
5165
+ });
5166
+ promptArea.value = titleGenCfg.prompt || "";
5167
+ promptArea.addEventListener("input", () => {
5168
+ titleGenCfg.prompt = promptArea.value;
5169
+ saveTitleGenSoon();
5170
+ });
5171
+ const sampleText = isZhLocale() ? "\u5E2E\u6211\u770B\u4E0B\u8FD9\u4E2A\u62A5\u9519\u662F\u4EC0\u4E48\u539F\u56E0\uFF1ANullPointerException at com.example.OrderService.calculate(OrderService.java:87)" : "Why does my Spring Boot app throw NullPointerException intermittently in OrderService.calculate?";
5172
+ const sampleInput = h("input", {
5173
+ type: "text",
5174
+ class: inputCls + " min-w-0 flex-1",
5175
+ value: sampleText
5176
+ });
5177
+ let testBtn;
5178
+ const testState = h("span", { class: "min-w-0 flex-1 truncate text-ui-xs/relaxed text-foreground-subtle" });
5179
+ testBtn = btnSmall(L.titleGenTest, async () => {
5180
+ testBtn.disabled = true;
5181
+ testBtn.textContent = L.titleGenTesting;
5182
+ testState.textContent = L.titleGenTesting;
5183
+ const res = await rpc("/title-gen/run", {
5184
+ method: "POST",
5185
+ body: { text: sampleInput.value, test: true }
5186
+ });
5187
+ testBtn.disabled = false;
5188
+ testBtn.textContent = L.titleGenTest;
5189
+ if (res && res.ok) {
5190
+ testState.textContent = L.titleGenTestOk + res.title;
5191
+ showToast(res.title, "success");
5192
+ } else {
5193
+ testState.textContent = res && errText(res) || L.titleGenTestFailed;
5194
+ testState.className = "min-w-0 flex-1 truncate text-ui-xs/relaxed text-destructive";
5195
+ showToast(L.titleGenTestFailed + ": " + (res ? errText(res) : ""), "error");
5196
+ }
5197
+ }, "shrink-0");
5198
+ paneTitleGen.replaceChildren(
5199
+ // 信息卡:一句话简介在左,启用开关右对齐
5200
+ h(
5201
+ "div",
5202
+ { class: "mt-3 flex items-center justify-between gap-2 rounded-xl border border-border p-2" },
5203
+ h("span", { class: "min-w-0 text-ui-sm/relaxed text-foreground-subtle" }, L.titleGenShortDesc),
5204
+ h("div", { class: "shrink-0" }, settingRow(L.titleGenEnabled, "", titleGenCfg.enabled === true, async () => {
5205
+ titleGenCfg.enabled = !(titleGenCfg.enabled === true);
5206
+ await persistTitleGen();
5207
+ renderTitleGenPane();
5208
+ }))
5209
+ ),
5210
+ // 供应商 + 模型
5211
+ h(
5212
+ "div",
5213
+ { class: "mt-2 grid grid-cols-1 gap-2 rounded-xl border border-border p-2 sm:grid-cols-2" },
5214
+ field(L.titleGenProvider, providerCombo),
5215
+ field(L.titleGenModel, modelCombo)
5216
+ ),
5217
+ inlineConnCard,
5218
+ // 提示词 + 载入默认
5219
+ h(
5220
+ "div",
5221
+ { class: "mt-2 rounded-xl border border-border p-2" },
5222
+ h(
5223
+ "div",
5224
+ { class: "mb-1 flex items-center justify-between gap-2" },
5225
+ h("span", { class: "text-ui-xs font-medium text-foreground-subtle" }, L.titleGenPrompt),
5226
+ btnSmall(L.titleGenPromptReset, () => {
5227
+ promptArea.value = titleGenDefaultPrompt;
5228
+ titleGenCfg.prompt = titleGenDefaultPrompt;
5229
+ void persistTitleGen();
5230
+ promptArea.focus();
5231
+ })
5232
+ ),
5233
+ promptArea
5234
+ ),
5235
+ // 测试
5236
+ h(
5237
+ "div",
5238
+ { class: "mt-2 flex items-center gap-2 rounded-xl border border-border p-2" },
5239
+ sampleInput,
5240
+ testBtn
5241
+ ),
5242
+ testState
5243
+ );
5244
+ };
5245
+ renderTitleGenPane();
4948
5246
  body.append(
4949
5247
  statusLine,
4950
5248
  tablist,
4951
5249
  paneFeatures,
5250
+ paneAgents,
4952
5251
  paneStyles,
4953
5252
  panePerf,
4954
- paneAgents,
4955
5253
  paneProxy,
4956
5254
  paneSync,
4957
5255
  paneVision,
@@ -5424,6 +5722,16 @@
5424
5722
  const map = await fetchPinned();
5425
5723
  if (map) scan2();
5426
5724
  }
5725
+ function setRowTitle(key, text) {
5726
+ for (const item of findRows(key)) {
5727
+ for (const el of titleElsOf(item)) writeTitle(el, text);
5728
+ }
5729
+ }
5730
+ function refreshPinnedTitles() {
5731
+ return fetchPinned().then((m) => {
5732
+ if (m) scan2();
5733
+ });
5734
+ }
5427
5735
  function startTitleLock() {
5428
5736
  if (installed2 || typeof document === "undefined") return;
5429
5737
  installed2 = true;
@@ -5459,8 +5767,146 @@
5459
5767
  });
5460
5768
  }
5461
5769
 
5462
- // src/inject/features/pinned-expand.js
5770
+ // src/inject/features/title-gen.js
5771
+ var CONV2 = '[class*="@md/conversation"]';
5772
+ var USER_ROW = '[class*="user-row"]';
5773
+ var SCAN_DEBOUNCE_MS3 = 300;
5774
+ var SCAN_FALLBACK_MS3 = 5e3;
5775
+ var SELECT_WAIT_MS = 2e3;
5776
+ var MSG_WAIT_MS = 1e4;
5777
+ var QUIET_FALLBACK_MS = 12e4;
5778
+ var COLLECT_MAX_MS = 6e5;
5463
5779
  var installed3 = false;
5780
+ var knownKeys = /* @__PURE__ */ new Set();
5781
+ var attempted = /* @__PURE__ */ new Set();
5782
+ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
5783
+ function sessIdOf2(key) {
5784
+ const i = key.indexOf(":sess_");
5785
+ return i > 0 ? key.slice(i + 1) : "";
5786
+ }
5787
+ function currentKey() {
5788
+ for (const el of document.querySelectorAll("[data-task-item-key]")) {
5789
+ if ((el.className + "").includes("bg-selected")) return el.getAttribute("data-task-item-key") || "";
5790
+ }
5791
+ return "";
5792
+ }
5793
+ function userTexts() {
5794
+ return [...document.querySelectorAll(`${CONV2} ${USER_ROW}`)].map((r) => {
5795
+ const el = r.querySelector(".whitespace-pre-wrap");
5796
+ return ((el ? el.textContent : r.innerText) || "").trim();
5797
+ }).filter(Boolean);
5798
+ }
5799
+ async function onNewTaskRow(key) {
5800
+ const selDeadline = Date.now() + SELECT_WAIT_MS;
5801
+ while (currentKey() !== key && Date.now() < selDeadline) await sleep(200);
5802
+ if (currentKey() !== key) return;
5803
+ const cfg = await getConfig().catch(() => null);
5804
+ if (!cfg || !cfg.titleGen || cfg.titleGen.enabled !== true) return;
5805
+ const sessId = sessIdOf2(key);
5806
+ if (!sessId || attempted.has(sessId)) return;
5807
+ attempted.add(sessId);
5808
+ let texts = [];
5809
+ const msgDeadline = Date.now() + MSG_WAIT_MS;
5810
+ while (Date.now() < msgDeadline) {
5811
+ texts = userTexts();
5812
+ if (texts.length) break;
5813
+ await sleep(400);
5814
+ }
5815
+ if (!texts.length) return;
5816
+ let assistantSeen = false;
5817
+ const convObserver = new MutationObserver((muts) => {
5818
+ if (currentKey() !== key) return;
5819
+ for (const m of muts) {
5820
+ const t2 = m.type === "characterData" ? m.target.parentElement : m.target;
5821
+ if (t2 instanceof Element && t2.closest(USER_ROW)) continue;
5822
+ for (const n of [t2, ...m.addedNodes]) {
5823
+ if (n instanceof Element && n.closest && n.closest(CONV2)) {
5824
+ assistantSeen = true;
5825
+ return;
5826
+ }
5827
+ }
5828
+ }
5829
+ });
5830
+ convObserver.observe(document.body, { childList: true, subtree: true, characterData: true });
5831
+ const startAt = Date.now();
5832
+ let lastCount = texts.length;
5833
+ let lastChange = Date.now();
5834
+ try {
5835
+ while (Date.now() - startAt < COLLECT_MAX_MS) {
5836
+ await sleep(500);
5837
+ if (currentKey() !== key) {
5838
+ lastChange = Date.now();
5839
+ continue;
5840
+ }
5841
+ texts = userTexts();
5842
+ if (texts.length !== lastCount) {
5843
+ lastCount = texts.length;
5844
+ lastChange = Date.now();
5845
+ }
5846
+ if (assistantSeen) break;
5847
+ if (Date.now() - lastChange >= QUIET_FALLBACK_MS) break;
5848
+ }
5849
+ } finally {
5850
+ convObserver.disconnect();
5851
+ }
5852
+ if (!texts.length) return;
5853
+ const res = await rpc("/title-gen/run", { method: "POST", body: { sessId, text: texts.join("\n") } }).catch(() => null);
5854
+ if (res && res.ok && res.written && res.title) {
5855
+ setRowTitle(key, res.title);
5856
+ void refreshPinnedTitles();
5857
+ }
5858
+ }
5859
+ function scanForNewRows() {
5860
+ for (const el of document.querySelectorAll("[data-task-item-key]")) {
5861
+ const key = el.getAttribute("data-task-item-key") || "";
5862
+ if (!key || knownKeys.has(key)) continue;
5863
+ knownKeys.add(key);
5864
+ void onNewTaskRow(key);
5865
+ }
5866
+ }
5867
+ var TASK_SEL2 = "[data-task-item-key]";
5868
+ var inTaskRow3 = (node) => !!(node instanceof Element && node.closest && node.closest(TASK_SEL2));
5869
+ function taskRowMutations3(muts) {
5870
+ for (const m of muts) {
5871
+ if (m.type === "attributes") {
5872
+ if (inTaskRow3(m.target)) return true;
5873
+ } else if (m.type === "characterData") {
5874
+ if (m.target.parentElement && inTaskRow3(m.target.parentElement)) return true;
5875
+ } else if (inTaskRow3(m.target)) {
5876
+ return true;
5877
+ } else {
5878
+ for (const n of m.addedNodes) {
5879
+ if (n instanceof Element && (n.matches(TASK_SEL2) || !!n.querySelector(TASK_SEL2))) return true;
5880
+ }
5881
+ }
5882
+ }
5883
+ return false;
5884
+ }
5885
+ var scanDebounce3 = 0;
5886
+ function scheduleScan3() {
5887
+ if (scanDebounce3) return;
5888
+ scanDebounce3 = setTimeout(() => {
5889
+ scanDebounce3 = 0;
5890
+ scanForNewRows();
5891
+ }, SCAN_DEBOUNCE_MS3);
5892
+ }
5893
+ function startTitleGen() {
5894
+ if (installed3 || typeof document === "undefined") return;
5895
+ installed3 = true;
5896
+ scanForNewRows();
5897
+ new MutationObserver((muts) => {
5898
+ if (taskRowMutations3(muts)) scheduleScan3();
5899
+ }).observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ["class"], characterData: true });
5900
+ setInterval(() => {
5901
+ if (!document.hidden) scanForNewRows();
5902
+ }, SCAN_FALLBACK_MS3);
5903
+ document.addEventListener("visibilitychange", () => {
5904
+ if (!document.hidden) scanForNewRows();
5905
+ });
5906
+ }
5907
+
5908
+ // src/inject/features/pinned-expand.js
5909
+ var installed4 = false;
5464
5910
  var keepCollapsed = false;
5465
5911
  var CONFIG_POLL_MIN_MS = 5e3;
5466
5912
  var CONFIG_POLL_MAX_MS = 6e4;
@@ -5487,8 +5933,8 @@
5487
5933
  }, pollMs);
5488
5934
  }
5489
5935
  function startPinnedExpandSuppression() {
5490
- if (installed3 || typeof document === "undefined") return;
5491
- installed3 = true;
5936
+ if (installed4 || typeof document === "undefined") return;
5937
+ installed4 = true;
5492
5938
  void refreshConfig();
5493
5939
  pollConfig();
5494
5940
  document.addEventListener("visibilitychange", () => {
@@ -5567,10 +6013,10 @@
5567
6013
  }
5568
6014
 
5569
6015
  // src/inject/features/ws-running.js
5570
- var installed4 = false;
6016
+ var installed5 = false;
5571
6017
  var enabled3 = true;
5572
- var SCAN_DEBOUNCE_MS3 = 150;
5573
- var SCAN_FALLBACK_MS3 = 5e3;
6018
+ var SCAN_DEBOUNCE_MS4 = 150;
6019
+ var SCAN_FALLBACK_MS4 = 5e3;
5574
6020
  var PEEK_FIRST_MS = 2e4;
5575
6021
  var PEEK_REPEAT_MS = 6e4;
5576
6022
  var PEEK_SETTLE_MS = 160;
@@ -5578,7 +6024,7 @@
5578
6024
  var PEEK_UNMOUNT_WAIT_MS = 800;
5579
6025
  var CONFIG_POLL_MIN_MS2 = 5e3;
5580
6026
  var CONFIG_POLL_MAX_MS2 = 6e4;
5581
- var TASK_SEL2 = "[data-task-item-key]";
6027
+ var TASK_SEL3 = "[data-task-item-key]";
5582
6028
  var WS_SEL = '[data-testid^="workspace-item-"]';
5583
6029
  var runningByWs = /* @__PURE__ */ new Map();
5584
6030
  var peekTimers = /* @__PURE__ */ new Map();
@@ -5601,10 +6047,10 @@
5601
6047
  return [...holder.children].some((c) => c.tagName === "svg" && (c.getAttribute("class") || "").includes("lucide-loader"));
5602
6048
  }
5603
6049
  function domRowsOf(ws) {
5604
- return [...document.querySelectorAll(TASK_SEL2)].filter((e) => workspaceOf2(e.getAttribute("data-task-item-key") || "") === ws);
6050
+ return [...document.querySelectorAll(TASK_SEL3)].filter((e) => workspaceOf2(e.getAttribute("data-task-item-key") || "") === ws);
5605
6051
  }
5606
6052
  function currentTaskRow2() {
5607
- for (const el of document.querySelectorAll(TASK_SEL2)) {
6053
+ for (const el of document.querySelectorAll(TASK_SEL3)) {
5608
6054
  if ((el.className + "").includes("bg-selected")) return el;
5609
6055
  }
5610
6056
  return null;
@@ -5620,12 +6066,12 @@
5620
6066
  const backKey = cur ? cur.getAttribute("data-task-item-key") : "";
5621
6067
  synthClick(head);
5622
6068
  if (!backKey) return;
5623
- const back = cur && cur.isConnected ? cur : document.querySelector(`${TASK_SEL2}[data-task-item-key="${CSS.escape(backKey)}"]`);
6069
+ const back = cur && cur.isConnected ? cur : document.querySelector(`${TASK_SEL3}[data-task-item-key="${CSS.escape(backKey)}"]`);
5624
6070
  if (back) synthClick(back);
5625
6071
  }
5626
6072
  function scan3() {
5627
6073
  wsWithRows = /* @__PURE__ */ new Set();
5628
- for (const el of document.querySelectorAll(TASK_SEL2)) {
6074
+ for (const el of document.querySelectorAll(TASK_SEL3)) {
5629
6075
  const key = el.getAttribute("data-task-item-key") || "";
5630
6076
  const ws = workspaceOf2(key);
5631
6077
  if (!ws) continue;
@@ -5651,8 +6097,8 @@
5651
6097
  if (m.type !== "childList") continue;
5652
6098
  for (const n of m.removedNodes) {
5653
6099
  if (!(n instanceof Element)) continue;
5654
- if (n.matches(TASK_SEL2)) removed.push(n);
5655
- else for (const r of n.querySelectorAll(TASK_SEL2)) removed.push(r);
6100
+ if (n.matches(TASK_SEL3)) removed.push(n);
6101
+ else for (const r of n.querySelectorAll(TASK_SEL3)) removed.push(r);
5656
6102
  }
5657
6103
  }
5658
6104
  if (!removed.length) return;
@@ -5835,7 +6281,7 @@
5835
6281
  var PEEK_EXEMPT_CLASS = "zcodepro-peek-exempt";
5836
6282
  var cssAttr = (v) => String(v).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
5837
6283
  function installPeekHider(ws) {
5838
- const rowSel3 = `${TASK_SEL2}[data-task-item-key^="${cssAttr(ws + ":")}"]`;
6284
+ const rowSel3 = `${TASK_SEL3}[data-task-item-key^="${cssAttr(ws + ":")}"]`;
5839
6285
  for (const r of document.querySelectorAll(rowSel3)) r.classList.add(PEEK_EXEMPT_CLASS);
5840
6286
  const style = document.createElement("style");
5841
6287
  style.textContent = `${rowSel3}:not(.${PEEK_EXEMPT_CLASS}){display:none!important}`;
@@ -5847,7 +6293,7 @@
5847
6293
  for (const r of document.querySelectorAll("." + PEEK_EXEMPT_CLASS)) r.classList.remove(PEEK_EXEMPT_CLASS);
5848
6294
  }
5849
6295
  function waitForRowsGone(ws) {
5850
- const sel = `${TASK_SEL2}[data-task-item-key^="${cssAttr(ws + ":")}"]:not(.${PEEK_EXEMPT_CLASS})`;
6296
+ const sel = `${TASK_SEL3}[data-task-item-key^="${cssAttr(ws + ":")}"]:not(.${PEEK_EXEMPT_CLASS})`;
5851
6297
  return new Promise((resolve) => {
5852
6298
  const t0 = Date.now();
5853
6299
  const timer = setInterval(() => {
@@ -5869,12 +6315,12 @@
5869
6315
  }
5870
6316
  function relevant(muts) {
5871
6317
  for (const m of muts) {
5872
- if (inSel(m.target, TASK_SEL2) || inSel(m.target, WS_SEL)) return true;
6318
+ if (inSel(m.target, TASK_SEL3) || inSel(m.target, WS_SEL)) return true;
5873
6319
  for (const n of m.addedNodes) {
5874
- if (n instanceof Element && (n.matches(TASK_SEL2) || n.matches(WS_SEL) || n.querySelector(TASK_SEL2) || n.querySelector(WS_SEL))) return true;
6320
+ if (n instanceof Element && (n.matches(TASK_SEL3) || n.matches(WS_SEL) || n.querySelector(TASK_SEL3) || n.querySelector(WS_SEL))) return true;
5875
6321
  }
5876
6322
  for (const n of m.removedNodes) {
5877
- if (n instanceof Element && (n.matches(TASK_SEL2) || n.matches(WS_SEL) || n.querySelector(TASK_SEL2))) return true;
6323
+ if (n instanceof Element && (n.matches(TASK_SEL3) || n.matches(WS_SEL) || n.querySelector(TASK_SEL3))) return true;
5878
6324
  }
5879
6325
  }
5880
6326
  return false;
@@ -5905,27 +6351,27 @@
5905
6351
  pollConfig2();
5906
6352
  }, pollMs2);
5907
6353
  }
5908
- var scanDebounce3 = 0;
6354
+ var scanDebounce4 = 0;
5909
6355
  function startWsRunningSpin() {
5910
- if (installed4 || typeof document === "undefined") return;
5911
- installed4 = true;
6356
+ if (installed5 || typeof document === "undefined") return;
6357
+ installed5 = true;
5912
6358
  void refreshConfig2();
5913
6359
  pollConfig2();
5914
6360
  const observer2 = new MutationObserver((muts) => {
5915
6361
  if (!relevant(muts)) return;
5916
6362
  handleRemovals(muts);
5917
- if (!scanDebounce3) {
5918
- scanDebounce3 = setTimeout(() => {
5919
- scanDebounce3 = 0;
6363
+ if (!scanDebounce4) {
6364
+ scanDebounce4 = setTimeout(() => {
6365
+ scanDebounce4 = 0;
5920
6366
  scan3();
5921
- }, SCAN_DEBOUNCE_MS3);
6367
+ }, SCAN_DEBOUNCE_MS4);
5922
6368
  }
5923
6369
  });
5924
6370
  observer2.observe(document.body, { childList: true, subtree: true });
5925
6371
  scan3();
5926
6372
  setInterval(() => {
5927
6373
  if (!document.hidden && enabled3) scan3();
5928
- }, SCAN_FALLBACK_MS3);
6374
+ }, SCAN_FALLBACK_MS4);
5929
6375
  document.addEventListener("visibilitychange", () => {
5930
6376
  if (!document.hidden) scan3();
5931
6377
  });
@@ -6089,7 +6535,7 @@
6089
6535
  // src/inject/features/toolbar-icons.js
6090
6536
  var HIDE_CLASS = "zcodepro-tb-hidden";
6091
6537
  var SYNC_DEBOUNCE_MS = 150;
6092
- var SCAN_FALLBACK_MS4 = 5e3;
6538
+ var SCAN_FALLBACK_MS5 = 5e3;
6093
6539
  var ROW_SEL = "[data-task-item-key]";
6094
6540
  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"]';
6095
6541
  var enabled4 = true;
@@ -6161,6 +6607,7 @@
6161
6607
  menu.classList.add(HIDE_CLASS);
6162
6608
  origs = next;
6163
6609
  rowEl?.remove();
6610
+ navRow.classList.add("zcodepro-tb-hidden-native");
6164
6611
  const L = t();
6165
6612
  const mk = (kind, orig, nameSel, fallback) => h("button", {
6166
6613
  type: "button",
@@ -6186,7 +6633,9 @@
6186
6633
  const pIcon = iconOf(next.plugin);
6187
6634
  if (pIcon) pluginBtn.append(pIcon);
6188
6635
  rowEl = h("div", { class: "zcodepro-tb-row" }, newBtn, searchBtn, autoBtn, pluginBtn);
6189
- navRow.append(rowEl);
6636
+ const updBtn = [...navRow.querySelectorAll("button")].find((b) => /bg-(success|emerald)/.test(b.className || ""));
6637
+ if (updBtn) updBtn.before(rowEl);
6638
+ else navRow.append(rowEl);
6190
6639
  return true;
6191
6640
  }
6192
6641
  function sidebarCollapsed() {
@@ -6216,10 +6665,18 @@
6216
6665
  if (sidebarCollapsed() || sidebarTooNarrow()) {
6217
6666
  if (rowEl) rowEl.style.display = "none";
6218
6667
  menu.classList.remove(HIDE_CLASS);
6668
+ document.querySelectorAll(".zcodepro-tb-hidden-native").forEach((el) => el.classList.remove("zcodepro-tb-hidden-native"));
6219
6669
  return;
6220
6670
  }
6221
6671
  const alive = origs && origs.menu === menu && origs.newTask.isConnected && origs.search.isConnected && origs.auto.isConnected && origs.plugin.isConnected && rowEl && rowEl.isConnected;
6222
- if (!alive && !rebuild()) return;
6672
+ if (!alive && !rebuild()) {
6673
+ if (rowEl) rowEl.style.display = "none";
6674
+ setTimeout(scheduleSync, 400);
6675
+ setTimeout(scheduleSync, 1200);
6676
+ return;
6677
+ }
6678
+ const navRow = document.querySelector('[data-testid="desktop-top-nav-back"]')?.parentElement;
6679
+ if (navRow) navRow.classList.add("zcodepro-tb-hidden-native");
6223
6680
  menu.classList.add(HIDE_CLASS);
6224
6681
  if (rowEl) rowEl.style.display = "";
6225
6682
  syncActive();
@@ -6255,6 +6712,7 @@
6255
6712
  rowEl = null;
6256
6713
  if (origs) {
6257
6714
  origs.menu.classList.remove(HIDE_CLASS);
6715
+ document.querySelectorAll(".zcodepro-tb-hidden-native").forEach((el) => el.classList.remove("zcodepro-tb-hidden-native"));
6258
6716
  origs = null;
6259
6717
  }
6260
6718
  }
@@ -6275,7 +6733,7 @@
6275
6733
  }).observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ["class", "aria-pressed"] });
6276
6734
  setInterval(() => {
6277
6735
  if (!document.hidden) sync();
6278
- }, SCAN_FALLBACK_MS4);
6736
+ }, SCAN_FALLBACK_MS5);
6279
6737
  document.addEventListener("visibilitychange", () => {
6280
6738
  if (!document.hidden) sync();
6281
6739
  });
@@ -6286,7 +6744,7 @@
6286
6744
  var HEAD_CLASS = "zcodepro-pin-head";
6287
6745
  var STORE_KEY = "zcodepro:pinned-collapsed";
6288
6746
  var SYNC_DEBOUNCE_MS2 = 300;
6289
- var SCAN_FALLBACK_MS5 = 5e3;
6747
+ var SCAN_FALLBACK_MS6 = 5e3;
6290
6748
  var enabled5 = true;
6291
6749
  var syncTimer2 = 0;
6292
6750
  function findPinnedHeader() {
@@ -6405,7 +6863,7 @@
6405
6863
  }).observe(document.body, { childList: true, subtree: true });
6406
6864
  setInterval(() => {
6407
6865
  if (!document.hidden) sync2();
6408
- }, SCAN_FALLBACK_MS5);
6866
+ }, SCAN_FALLBACK_MS6);
6409
6867
  document.addEventListener("visibilitychange", () => {
6410
6868
  if (!document.hidden) sync2();
6411
6869
  });
@@ -6486,6 +6944,10 @@
6486
6944
  startTitleLock();
6487
6945
  } catch {
6488
6946
  }
6947
+ try {
6948
+ startTitleGen();
6949
+ } catch {
6950
+ }
6489
6951
  try {
6490
6952
  startImageMenu();
6491
6953
  } catch {