zcode-pro 0.18.0 → 0.20.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",
@@ -58,12 +59,16 @@
58
59
  proxyScopeSyncDesc: "WebDAV \u4E0A\u4F20\u4E0E\u4E0B\u8F7D\u8D70\u4EE3\u7406\u3002\u9ED8\u8BA4\u5173\u95ED\uFF1A\u575A\u679C\u4E91\u7B49\u56FD\u5185\u7F51\u76D8\u76F4\u8FDE\u5373\u53EF\uFF0C\u4EE3\u7406\u505C\u4E86\u540C\u6B65\u4F1A\u8DDF\u7740\u5931\u8D25\u3002",
59
60
  tabSync: "\u540C\u6B65",
60
61
  tabDebug: "\u8C03\u8BD5",
61
- syncDesc: "\u624B\u52A8\u628A ZCode Pro \u8BBE\u7F6E\u4E0E\u6A21\u578B\u8BBE\u7F6E\u5907\u4EFD\u5230 WebDAV\uFF0C\u6216\u5728\u65B0\u7535\u8111\u4E0A\u6062\u590D\u3002\u4E24\u9879\u5404\u81EA\u72EC\u7ACB\u6210\u6587\u4EF6\uFF0C\u53EF\u53EA\u4E0A\u4F20\u6216\u53EA\u8986\u76D6\u5176\u4E2D\u4E00\u9879\uFF1B\u6BCF\u6B21\u4E0A\u4F20\u4F1A\u989D\u5916\u4FDD\u7559\u4E00\u4EFD\u5E26\u65F6\u95F4\u6233\u7684\u5386\u53F2\u7248\u672C\uFF0C\u4E0B\u8F7D\u65F6\u4EFB\u9009\u4E00\u4E2A\u7248\u672C\u6062\u590D\uFF1B\u4EC5\u5728\u70B9\u51FB\u6309\u94AE\u65F6\u4F20\u8F93\uFF0C\u4E0D\u505A\u81EA\u52A8\u540C\u6B65\u3002",
62
+ syncDesc: "\u624B\u52A8\u628A ZCode Pro \u8BBE\u7F6E\u4E0E\u6A21\u578B\u8BBE\u7F6E\u5907\u4EFD\u5230 WebDAV\uFF0C\u6216\u5728\u65B0\u7535\u8111\u4E0A\u6062\u590D\u3002\u4E24\u9879\u5404\u81EA\u72EC\u7ACB\u6210\u6587\u4EF6\uFF0C\u53EF\u53EA\u4E0A\u4F20\u6216\u53EA\u8986\u76D6\u5176\u4E2D\u4E00\u9879\uFF1B\u6BCF\u6B21\u4E0A\u4F20\u4F1A\u989D\u5916\u4FDD\u7559\u4E00\u4EFD\u5E26\u65F6\u95F4\u6233\u7684\u5386\u53F2\u7248\u672C\uFF08\u4FDD\u7559\u591A\u5C11\u4E2A\u89C1\u201C\u5386\u53F2\u7248\u672C\u201D\u4E00\u884C\uFF0C\u4E0A\u4F20\u540E\u81EA\u52A8\u5220\u9664\u8D85\u51FA\u7684\u6700\u65E7\u7248\u672C\uFF09\uFF0C\u4E0B\u8F7D\u65F6\u4EFB\u9009\u4E00\u4E2A\u7248\u672C\u6062\u590D\uFF1B\u4EC5\u5728\u70B9\u51FB\u6309\u94AE\u65F6\u4F20\u8F93\uFF0C\u4E0D\u505A\u81EA\u52A8\u540C\u6B65\u3002",
62
63
  syncUrlPlaceholder: "https://dav.jianguoyun.com/dav/",
63
64
  syncDir: "\u4E0A\u4F20\u76EE\u5F55",
64
65
  syncDirPlaceholder: "zcode-pro",
65
66
  syncLogin: "\u767B\u5F55",
66
67
  syncPassword: "\u5BC6\u7801",
68
+ syncHistoryLimit: "\u5386\u53F2\u7248\u672C",
69
+ syncHistoryLimitUnit: "\u4E2A",
70
+ syncHistoryLimitHint: "\u4E91\u7AEF\u6700\u591A\u4FDD\u7559\u6700\u8FD1\u51E0\u6B21\u4E0A\u4F20\u7559\u4E0B\u7684\u5386\u53F2\u7248\u672C\uFF0C\u4E0A\u4F20\u6210\u529F\u540E\u81EA\u52A8\u5220\u9664\u66F4\u65E9\u7684\uFF1B\u56FA\u5B9A\u540D\u6700\u65B0\u7248\u4E0D\u53D7\u5F71\u54CD\uFF1B0 = \u4E0D\u4FDD\u7559\u5386\u53F2\u7248\u672C",
71
+ syncPruned: "\u5DF2\u6E05\u7406 {n} \u4E2A\u66F4\u65E9\u7684\u5386\u53F2\u7248\u672C",
67
72
  syncSave: "\u4FDD\u5B58",
68
73
  syncSaved: "\u540C\u6B65\u914D\u7F6E\u5DF2\u4FDD\u5B58",
69
74
  syncFormHint: "\u540C\u6B65\u6587\u4EF6\u5B58\u5230\u201C\u4E0A\u4F20\u76EE\u5F55\u201D\uFF08\u9ED8\u8BA4 zcode-pro\uFF0C\u53EF\u591A\u7EA7\uFF0C\u81EA\u52A8\u521B\u5EFA\uFF09\uFF1Azcodepro.json \u4E0E model.json \u59CB\u7EC8\u662F\u6700\u65B0\u4E00\u7248\uFF0C\u65C1\u8FB9\u53E6\u4FDD\u7559\u5E26\u65F6\u95F4\u6233\u7684\u5386\u53F2\u7248\u672C\u3002",
@@ -84,8 +89,8 @@
84
89
  syncConfirmTitle: "\u4ECE\u4E91\u7AEF\u4E0B\u8F7D",
85
90
  syncConfirmDesc: "\u5C06\u7528\u6240\u9009\u6587\u4EF6\u8986\u76D6\u672C\u673A\u5BF9\u5E94\u7684\u4E00\u9879\uFF08\u8986\u76D6\u524D\u4F1A\u5728\u539F\u6587\u4EF6\u65C1\u7559\u4E0B .zcodepro-backup \u5907\u4EFD\uFF09\uFF1B\u672C\u673A\u7684\u540C\u6B65\u914D\u7F6E\u4E0E\u767B\u5F55\u51ED\u636E\u4E0D\u53D7\u5F71\u54CD\u3002\u6A21\u578B\u8BBE\u7F6E\u6062\u590D\u540E\u5EFA\u8BAE\u91CD\u542F ZCode \u751F\u6548\u3002\u786E\u8BA4\u5BFC\u5165\uFF1F",
86
91
  syncConfirmGo: "\u786E\u8BA4\u5BFC\u5165",
87
- syncPickDesc: "\u56FA\u5B9A\u540D\u6587\u4EF6\u59CB\u7EC8\u662F\u6700\u65B0\u4E00\u7248\uFF0C\u5E26\u65F6\u95F4\u6233\u7684\u662F\u6BCF\u6B21\u4E0A\u4F20\u7559\u4E0B\u7684\u5386\u53F2\u7248\u672C\uFF1B\u300C\u5BFC\u5165\u300D\u7528\u6240\u9009\u6587\u4EF6\u8986\u76D6\u672C\u673A\u5BF9\u5E94\u7684\u4E00\u9879\u3002",
88
- syncVerDeleteLatestNote: "\u6CE8\u610F\uFF1A\u8FD9\u662F\u201C\u6700\u65B0\u201D\u6587\u4EF6\uFF0C\u65E7\u7248 zcode-pro \u7684\u300C\u4ECE\u4E91\u7AEF\u4E0B\u8F7D\u300D\u4F9D\u8D56\u5B83\uFF0C\u5220\u9664\u540E\u65E7\u7248\u673A\u5668\u5C06\u65E0\u6CD5\u4E0B\u8F7D\uFF1B\u5E26\u65F6\u95F4\u6233\u7684\u5386\u53F2\u7248\u672C\u4E0D\u53D7\u5F71\u54CD\u3002",
92
+ syncPickDesc: "\u56FA\u5B9A\u540D\u6587\u4EF6\u59CB\u7EC8\u662F\u6700\u65B0\u4E00\u7248\uFF0C\u5E26\u65F6\u95F4\u6233\u7684\u662F\u6BCF\u6B21\u4E0A\u4F20\u7559\u4E0B\u7684\u5386\u53F2\u7248\u672C\uFF1B\u201C\u5BFC\u5165\u201D\u7528\u6240\u9009\u6587\u4EF6\u8986\u76D6\u672C\u673A\u5BF9\u5E94\u7684\u4E00\u9879\u3002",
93
+ syncVerDeleteLatestNote: "\u6CE8\u610F\uFF1A\u8FD9\u662F\u201C\u6700\u65B0\u201D\u6587\u4EF6\uFF0C\u65E7\u7248 zcode-pro \u7684\u201C\u4ECE\u4E91\u7AEF\u4E0B\u8F7D\u201D\u4F9D\u8D56\u5B83\uFF0C\u5220\u9664\u540E\u65E7\u7248\u673A\u5668\u5C06\u65E0\u6CD5\u4E0B\u8F7D\uFF1B\u5E26\u65F6\u95F4\u6233\u7684\u5386\u53F2\u7248\u672C\u4E0D\u53D7\u5F71\u54CD\u3002",
89
94
  syncHint: "\u540C\u6B65\u767B\u5F55\u51ED\u636E\u4EC5\u4FDD\u5B58\u5728\u672C\u673A zcodepro.json\uFF1B\u6A21\u578B\u8BBE\u7F6E\u5305\u542B\u81EA\u5B9A\u4E49\u4F9B\u5E94\u5546\u7684 API Key\uFF0C\u53EA\u4F1A\u4E0A\u4F20\u5230\u4F60\u81EA\u5DF1\u7684 WebDAV\u3002",
90
95
  syncTest: "\u6D4B\u8BD5",
91
96
  syncTesting: "\u6D4B\u8BD5\u4E2D\u2026",
@@ -109,8 +114,8 @@
109
114
  syncVerDeleted: "\u5DF2\u5220\u9664\u4E91\u7AEF\u5907\u4EFD",
110
115
  syncVerSaved: "\u5DF2\u4FDD\u5B58\u5230",
111
116
  devMode: "\u5F00\u53D1\u6A21\u5F0F",
112
- devModeRowDesc: "\u5F00\u542F\u540E\u51FA\u73B0\u300C\u91CD\u8F7D ZCode Pro\u300D\u6309\u94AE\uFF1B\u4E5F\u53EF\u7ED9\u542F\u52A8\u5668\u8BBE\u73AF\u5883\u53D8\u91CF ZCODEPRO_DEV=1 \u542F\u7528\u3002\u6B64\u6807\u7B7E\u9875\u4EC5\u5728\u5F00\u53D1\u6A21\u5F0F\u751F\u6548\u65F6\u663E\u793A\u3002",
113
- devModeDesc: "\u5F00\u53D1\u8C03\u8BD5\u7528\uFF1A\u6539\u5B8C zcode-pro \u4EE3\u7801\u540E\u4E0D\u5FC5\u91CD\u542F ZCode\uFF0C\u70B9\u300C\u91CD\u8F7D ZCode Pro\u300D\u5373\u53EF\u52A0\u8F7D\u65B0\u4EE3\u7801\u3002\u6D41\u7A0B\u4E3A\u81EA\u52A8\u6784\u5EFA\uFF08\u9700\u5728\u6E90\u7801\u76EE\u5F55\u8FD0\u884C\uFF09\u2192 \u4EE5\u65B0\u8FDB\u7A0B\u91CD\u542F ZCode Pro\uFF08\u672C\u5730\u52A9\u624B\u4E0E\u6CE8\u5165\u811A\u672C\u90FD\u6362\u65B0\uFF09\u2192 \u6240\u6709 ZCode \u7A97\u53E3\u81EA\u52A8\u5237\u65B0\u5E76\u91CD\u65B0\u6CE8\u5165\uFF1BZCode \u672C\u4F53\u4E0E\u5DF2\u6253\u5F00\u7684\u4F1A\u8BDD\u4E0D\u53D7\u5F71\u54CD\u3002",
117
+ devModeRowDesc: "\u5F00\u542F\u540E\u51FA\u73B0\u201C\u91CD\u8F7D ZCode Pro\u201D\u6309\u94AE\uFF1B\u4E5F\u53EF\u7ED9\u542F\u52A8\u5668\u8BBE\u73AF\u5883\u53D8\u91CF ZCODEPRO_DEV=1 \u542F\u7528\u3002\u6B64\u6807\u7B7E\u9875\u4EC5\u5728\u5F00\u53D1\u6A21\u5F0F\u751F\u6548\u65F6\u663E\u793A\u3002",
118
+ devModeDesc: "\u5F00\u53D1\u8C03\u8BD5\u7528\uFF1A\u6539\u5B8C zcode-pro \u4EE3\u7801\u540E\u4E0D\u5FC5\u91CD\u542F ZCode\uFF0C\u70B9\u201C\u91CD\u8F7D ZCode Pro\u201D\u5373\u53EF\u52A0\u8F7D\u65B0\u4EE3\u7801\u3002\u6D41\u7A0B\u4E3A\u81EA\u52A8\u6784\u5EFA\uFF08\u9700\u5728\u6E90\u7801\u76EE\u5F55\u8FD0\u884C\uFF09\u2192 \u4EE5\u65B0\u8FDB\u7A0B\u91CD\u542F ZCode Pro\uFF08\u672C\u5730\u52A9\u624B\u4E0E\u6CE8\u5165\u811A\u672C\u90FD\u6362\u65B0\uFF09\u2192 \u6240\u6709 ZCode \u7A97\u53E3\u81EA\u52A8\u5237\u65B0\u5E76\u91CD\u65B0\u6CE8\u5165\uFF1BZCode \u672C\u4F53\u4E0E\u5DF2\u6253\u5F00\u7684\u4F1A\u8BDD\u4E0D\u53D7\u5F71\u54CD\u3002",
114
119
  devReload: "\u91CD\u8F7D ZCode Pro",
115
120
  devReloading: "\u91CD\u8F7D\u4E2D\u2026",
116
121
  devReloadDesc: "\u70B9\u51FB\u540E\u7EA6 2-3 \u79D2\u5185\u6240\u6709 ZCode \u7A97\u53E3\u81EA\u52A8\u5237\u65B0\u4E00\u6B21\u5E76\u52A0\u8F7D\u65B0\u811A\u672C\u3002",
@@ -131,7 +136,7 @@
131
136
  lineHeightName: "\u56DE\u7B54\u884C\u9AD8",
132
137
  lineHeightDesc: "\u56DE\u7B54\u6B63\u6587\u7684\u884C\u9AD8\uFF08\u500D\u6570\uFF09\u3002",
133
138
  thinkingLineHeightName: "\u601D\u8003\u884C\u9AD8",
134
- thinkingLineHeightDesc: "\u300C\u5DF2\u5DE5\u4F5C/\u5DE5\u4F5C\u4E2D\u300D\u8FC7\u7A0B\u8BB0\u5F55\uFF08\u8BFB\u53D6/\u7F16\u8F91/\u67E5\u9605/\u7EC8\u7AEF\u6761\u76EE\u4E0E\u601D\u8003\u5185\u5BB9\uFF09\u7684\u884C\u9AD8\uFF08\u500D\u6570\uFF09\u3002",
139
+ thinkingLineHeightDesc: "\u201C\u5DF2\u5DE5\u4F5C/\u5DE5\u4F5C\u4E2D\u201D\u8FC7\u7A0B\u8BB0\u5F55\uFF08\u8BFB\u53D6/\u7F16\u8F91/\u67E5\u9605/\u7EC8\u7AEF\u6761\u76EE\u4E0E\u601D\u8003\u5185\u5BB9\uFF09\u7684\u884C\u9AD8\uFF08\u500D\u6570\uFF09\u3002",
135
140
  userLineHeightName: "\u63D0\u95EE\u884C\u9AD8",
136
141
  userLineHeightDesc: "\u63D0\u95EE\u5185\u5BB9\u7684\u884C\u9AD8\uFF08\u500D\u6570\uFF09\u3002",
137
142
  contentWidthName: "\u5185\u5BB9\u5BBD\u5EA6",
@@ -146,6 +151,30 @@
146
151
  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
152
  featureTitleLock: "\u91CD\u547D\u540D\u4F1A\u8BDD\u540D\u9501\u5B9A",
148
153
  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",
154
+ tabTitleGen: "\u4EFB\u52A1\u540D\u63D0\u793A\u8BCD",
155
+ 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",
156
+ titleGenEnabled: "\u542F\u7528",
157
+ titleGenProvider: "\u4F9B\u5E94\u5546",
158
+ titleGenProviderNone: "\u4E0D\u8DDF\u968F\uFF08\u81EA\u586B\u5730\u5740\uFF09",
159
+ titleGenProviderSession: "\u8DDF\u968F\u5F53\u524D\u4F1A\u8BDD",
160
+ titleGenProviderDeleted: "\uFF08\u5DF2\u5220\u9664\uFF09",
161
+ titleGenModel: "\u6A21\u578B",
162
+ titleGenModelHint: "\u4ECE\u5217\u8868\u9009\u62E9\uFF0C\u6216\u76F4\u63A5\u586B\u5199\u6A21\u578B id",
163
+ titleGenModelEmpty: "\uFF08\u672A\u9009\u62E9\uFF09",
164
+ titleGenModelSessionHint: "\u4ECE\u5217\u8868\u9009\u62E9\u6A21\u578B\uFF1B\u5217\u8868\u7B2C\u4E00\u9879\u4E3A\u8DDF\u968F\u4F1A\u8BDD\u5F53\u524D\u6A21\u578B",
165
+ titleGenModelFollowSession: "\u8DDF\u968F\u5F53\u524D\u4F1A\u8BDD\u5F53\u524D\u6A21\u578B",
166
+ titleGenModelManualHint: "\u8F93\u5165\u6A21\u578B id",
167
+ titleGenBaseUrl: "\u63A5\u53E3\u5730\u5740",
168
+ titleGenApiKey: "API Key",
169
+ titleGenFormat: "\u534F\u8BAE",
170
+ titleGenPrompt: "\u63D0\u793A\u8BCD",
171
+ titleGenPromptPlaceholder: "\u7559\u7A7A\u4F7F\u7528\u5185\u7F6E\u9ED8\u8BA4\u63D0\u793A\u8BCD\uFF08\u70B9\u51FB\u4E0B\u65B9\u6309\u94AE\u53EF\u67E5\u770B\uFF09",
172
+ titleGenPromptReset: "\u4F7F\u7528\u9ED8\u8BA4\u63D0\u793A\u8BCD",
173
+ titleGenSample: "\u6D4B\u8BD5\u6D88\u606F",
174
+ titleGenTest: "\u6D4B\u8BD5\u751F\u6210",
175
+ titleGenTesting: "\u751F\u6210\u4E2D\u2026",
176
+ titleGenTestOk: "\u751F\u6210\u7ED3\u679C\uFF1A",
177
+ titleGenTestFailed: "\u751F\u6210\u5931\u8D25",
149
178
  featureWsRunning: "\u6298\u53E0\u9879\u76EE\u8FD0\u884C\u63D0\u793A\uFF08\u5B9E\u9A8C\u6027\uFF09",
150
179
  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
180
  tabPerformance: "\u6027\u80FD",
@@ -220,10 +249,6 @@
220
249
  visionMoveDown: "\u4E0B\u79FB",
221
250
  visionName: "\u540D\u79F0",
222
251
  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
252
  visionSkipAfterFailures: "\u8FDE\u7EED\u5931\u8D25\u6B21\u6570",
228
253
  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
254
  visionSkipMinutes: "\u8DF3\u8FC7\u5206\u949F\u6570",
@@ -332,7 +357,6 @@
332
357
  upgradeDone: "\u5DF2\u66F4\u65B0\u5230 {v}",
333
358
  upgradeFinished: "\u5347\u7EA7\u5B8C\u6210",
334
359
  upgradeCheckFailed: "\u68C0\u67E5\u66F4\u65B0\u5931\u8D25",
335
- version: "\u7248\u672C",
336
360
  close: "\u5173\u95ED",
337
361
  cancel: "\u53D6\u6D88",
338
362
  pathLabel: "\u8DEF\u5F84",
@@ -376,6 +400,7 @@
376
400
  tabFeatures: "Features",
377
401
  tabStyles: "Styles",
378
402
  tabProxy: "Proxy",
403
+ tabBehavior: "Behavior",
379
404
  tabAgents: "Global Prompt",
380
405
  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
406
  agentsPlaceholder: "Instructions followed by all projects by default; save empty to remove the global prompt",
@@ -399,12 +424,16 @@
399
424
  proxyScopeSyncDesc: "WebDAV upload and download go through the proxy. Off by default: domestic WebDAV providers work fine direct, and a stopped proxy breaks sync.",
400
425
  tabSync: "Sync",
401
426
  tabDebug: "Debug",
402
- syncDesc: "Manually back up ZCode Pro settings and model settings to WebDAV, or restore them on a new computer. The two items are stored as separate files, so you can upload or overwrite either one alone; every upload also keeps a timestamped history version and download lets you pick any of them. Data moves only when you click, never automatically.",
427
+ syncDesc: 'Manually back up ZCode Pro settings and model settings to WebDAV, or restore them on a new computer. The two items are stored as separate files, so you can upload or overwrite either one alone; every upload also keeps a timestamped history version (how many to keep is set in the "History" row; older ones are removed automatically after upload) and download lets you pick any of them. Data moves only when you click, never automatically.',
403
428
  syncUrlPlaceholder: "https://dav.example.com/dav/",
404
- syncDir: "\u4E0A\u4F20\u76EE\u5F55",
429
+ syncDir: "Folder",
405
430
  syncDirPlaceholder: "zcode-pro",
406
431
  syncLogin: "Login",
407
432
  syncPassword: "Password",
433
+ syncHistoryLimit: "History",
434
+ syncHistoryLimitUnit: "versions",
435
+ syncHistoryLimitHint: "How many recent uploads keep their history versions on the server; older ones are removed automatically after a successful upload. The fixed-name latest file is never removed. 0 = keep no history versions.",
436
+ syncPruned: "removed {n} older history versions",
408
437
  syncSave: "Save",
409
438
  syncSaved: "Sync settings saved.",
410
439
  syncFormHint: "Sync files go inside the folder (default zcode-pro, multi-level allowed, created automatically): zcodepro.json and model.json are always the latest version; timestamped history versions are kept alongside.",
@@ -487,6 +516,30 @@
487
516
  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
517
  featureTitleLock: "Keep renamed session titles",
489
518
  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.",
519
+ tabTitleGen: "Task name prompt",
520
+ 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.",
521
+ titleGenEnabled: "Enable",
522
+ titleGenProvider: "Provider",
523
+ titleGenProviderNone: "Custom endpoint (no provider)",
524
+ titleGenProviderSession: "Follow current session",
525
+ titleGenProviderDeleted: " (deleted)",
526
+ titleGenModel: "Model",
527
+ titleGenModelHint: "Pick from the list, or type a model id",
528
+ titleGenModelEmpty: "(not set)",
529
+ titleGenModelSessionHint: "Pick from the list; the first entry follows the session model",
530
+ titleGenModelFollowSession: "Follow the session model",
531
+ titleGenModelManualHint: "Type a model id",
532
+ titleGenBaseUrl: "Base URL",
533
+ titleGenApiKey: "API Key",
534
+ titleGenFormat: "Protocol",
535
+ titleGenPrompt: "Prompt",
536
+ titleGenPromptPlaceholder: "Leave empty for the built-in default prompt (button below shows it)",
537
+ titleGenPromptReset: "Use default prompt",
538
+ titleGenSample: "Test message",
539
+ titleGenTest: "Test",
540
+ titleGenTesting: "Generating\u2026",
541
+ titleGenTestOk: "Result: ",
542
+ titleGenTestFailed: "Generation failed",
490
543
  featureWsRunning: "Running indicator on collapsed projects (experimental)",
491
544
  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
545
  tabPerformance: "Performance",
@@ -561,10 +614,6 @@
561
614
  visionMoveDown: "Down",
562
615
  visionName: "Name",
563
616
  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
617
  visionSkipAfterFailures: "Skip after failures",
569
618
  visionSkipAfterFailuresHint: "After this many consecutive failures a proxy is skipped and the next one is tried; 0 = never skip",
570
619
  visionSkipMinutes: "Skip minutes",
@@ -673,7 +722,6 @@
673
722
  upgradeDone: "Updated to {v}",
674
723
  upgradeFinished: "Update finished",
675
724
  upgradeCheckFailed: "Update check failed",
676
- version: "Version",
677
725
  close: "Close",
678
726
  cancel: "Cancel",
679
727
  pathLabel: "Path",
@@ -870,7 +918,7 @@
870
918
  var overlayClassBare = "fixed inset-0 isolate z-50 flex items-center justify-center duration-100 p-4 platform-linux-desktop:top-12";
871
919
  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
920
  var dialogStack = [];
873
- function openDialog({ title, description, onMount, onClose, width = "sm:max-w-md", overlay = "dim", draggable = false, posKey = "", dismissOnOutside = true }) {
921
+ function openDialog({ title, description, onMount, onClose, width = "sm:max-w-md", overlay = "dim", draggable = false, posKey = "", dismissOnOutside = true, bodyClass = "" }) {
874
922
  const L = t();
875
923
  const prevActive = document.activeElement;
876
924
  let cleanupDrag = null;
@@ -914,7 +962,7 @@
914
962
  titleEl,
915
963
  ...description ? [h("p", { class: "mt-2 text-ui-sm/relaxed text-foreground-subtle" }, description)] : []
916
964
  );
917
- const body = h("div", { class: "mt-4 zcodepro-dialog-body" });
965
+ const body = h("div", { class: "zcodepro-dialog-body " + (bodyClass || "mt-4") });
918
966
  content.append(body);
919
967
  let restorePos = null;
920
968
  if (draggable) {
@@ -1176,7 +1224,7 @@
1176
1224
  "data-state": checked ? "on" : "off"
1177
1225
  }, knob);
1178
1226
  const extraEl = extra ? h("div", {
1179
- class: "flex shrink-0 items-center pt-0.5",
1227
+ class: "flex shrink-0 items-center",
1180
1228
  onClick: (e) => {
1181
1229
  e.stopPropagation();
1182
1230
  },
@@ -1190,7 +1238,7 @@
1190
1238
  const row = h(
1191
1239
  "div",
1192
1240
  {
1193
- class: `flex cursor-pointer items-start gap-2 rounded-lg ${desc ? "p-2" : "p-1.5"} transition-colors hover:bg-surface-hover`,
1241
+ class: `flex cursor-pointer items-center gap-2 rounded-lg ${desc ? "p-2" : "p-1.5"} transition-colors hover:bg-surface-hover`,
1194
1242
  onClick: () => {
1195
1243
  onToggle();
1196
1244
  }
@@ -1278,13 +1326,14 @@
1278
1326
  align-items: center;
1279
1327
  justify-content: center;
1280
1328
  height: 24px;
1281
- padding: 0 14px;
1329
+ padding: 0 8px;
1282
1330
  border: none;
1283
1331
  border-radius: 9999px;
1284
1332
  background: transparent;
1285
1333
  cursor: pointer;
1286
1334
  font-size: 12px;
1287
1335
  line-height: 1;
1336
+ white-space: nowrap;
1288
1337
  color: var(--color-muted-foreground, #888);
1289
1338
  transition: color 0.15s ease, background-color 0.15s ease;
1290
1339
  }
@@ -1373,6 +1422,8 @@
1373
1422
  .zcodepro-textarea:disabled { opacity: 0.5; }
1374
1423
  /* \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
1424
  .zcodepro-textarea.zcodepro-textarea-sm { min-height: 0; }
1425
+ /* \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 */
1426
+ .zcodepro-textarea.zcodepro-textarea-lg { min-height: 16rem; max-height: 40rem; }
1376
1427
  /* \u884C\u9AD8\u6ED1\u6746\uFF08\u6837\u5F0F\u8C03\u6574\u6807\u7B7E\u9875\uFF09 */
1377
1428
  .zcodepro-range {
1378
1429
  -webkit-appearance: none;
@@ -1558,6 +1609,14 @@
1558
1609
  }
1559
1610
  /* \u539F\u4FA7\u680F\u83DC\u5355\u6574\u4F53\u9690\u85CF\uFF08toolbar-icons \u5F00\u542F\u65F6\uFF09 */
1560
1611
  .zcodepro-tb-hidden { display: none !important; }
1612
+ /* \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
1613
+ \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
1614
+ \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 */
1615
+ .zcodepro-tb-hidden-native div[class*="transition-[opacity,width]"] {
1616
+ width: 0 !important;
1617
+ opacity: 0 !important;
1618
+ pointer-events: none !important;
1619
+ }
1561
1620
  /* \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
1621
  chevron \u65CB\u5411\u4E0E\u201C\u9879\u76EE\u201D\u5206\u533A\u4E00\u81F4\uFF08\u6536\u8D77\u6307\u5411\u53F3\u4FA7\uFF09 */
1563
1622
  .zcodepro-pin-head {
@@ -2212,6 +2271,12 @@
2212
2271
  const path = resolveTargetPath(content);
2213
2272
  if (!path) return;
2214
2273
  const L = t();
2274
+ const fire = (endpoint, label) => {
2275
+ rpc(endpoint, { method: "POST", body: { path } }).then((r) => {
2276
+ if (r && r.ok === false) showToast(`${label}\u5931\u8D25\uFF1A${r.error || "\u672A\u77E5\u9519\u8BEF"}`, "error");
2277
+ }).catch(() => {
2278
+ });
2279
+ };
2215
2280
  const noApps = items.find((el) => NO_APPS_TEXTS.some((x) => itemText(el) === x)) || content.querySelector('[data-disabled][role="menuitem"]');
2216
2281
  const openAnchor = noApps && content.contains(noApps) ? noApps : copyAbs;
2217
2282
  appendMenuItem(content, copyAbs, openAnchor, {
@@ -2219,10 +2284,7 @@
2219
2284
  label: L.fileOpenDefault,
2220
2285
  icon: ICON_OPEN_DEFAULT,
2221
2286
  onPick: () => {
2222
- try {
2223
- void rpc("/open-path", { method: "POST", body: { path } });
2224
- } catch {
2225
- }
2287
+ void fire("/open-path", L.fileOpenDefault);
2226
2288
  }
2227
2289
  });
2228
2290
  appendMenuItem(content, copyAbs, copyAbs, {
@@ -2230,10 +2292,7 @@
2230
2292
  label: L.fileReveal,
2231
2293
  icon: ICON_REVEAL,
2232
2294
  onPick: () => {
2233
- try {
2234
- void rpc("/reveal-path", { method: "POST", body: { path } });
2235
- } catch {
2236
- }
2295
+ void fire("/reveal-path", L.fileReveal);
2237
2296
  }
2238
2297
  });
2239
2298
  })();
@@ -2445,6 +2504,56 @@
2445
2504
  }, true);
2446
2505
  }
2447
2506
 
2507
+ // src/inject/provider-fields.js
2508
+ 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";
2509
+ function comboField(current2, options, onChange, { disabled = false, placeholder = "", staleLabel = "\uFF08\u624B\u52A8\u8F93\u5165\uFF09", emptyLabel = "" } = {}) {
2510
+ const sel = h("select", { class: PROVIDER_FIELD_CLS, disabled: disabled || void 0 });
2511
+ const input = h("input", { type: "text", value: current2, placeholder, class: PROVIDER_FIELD_CLS, disabled: disabled || void 0 });
2512
+ const sync3 = () => {
2513
+ const cur = input.value;
2514
+ const opts = [...options];
2515
+ if (cur && !opts.some((o) => o.value === cur)) opts.push({ value: cur, label: `${cur}${staleLabel}` });
2516
+ if (!cur && !opts.some((o) => o.value === "")) opts.unshift({ value: "", label: emptyLabel });
2517
+ sel.replaceChildren(...opts.map((o) => h("option", { value: o.value, title: o.value }, o.label)));
2518
+ sel.value = cur;
2519
+ };
2520
+ sel.addEventListener("change", () => {
2521
+ input.value = sel.value;
2522
+ onChange(sel.value, "select");
2523
+ });
2524
+ input.addEventListener("input", () => onChange(input.value, "type"));
2525
+ input.addEventListener("blur", () => sync3());
2526
+ sync3();
2527
+ return h("div", { class: "flex flex-col gap-1" }, sel, input);
2528
+ }
2529
+ function providerOptionsOf(providers, L) {
2530
+ return [
2531
+ { value: "", label: L.titleGenProviderNone },
2532
+ { value: "session", label: L.titleGenProviderSession },
2533
+ ...providers.map((p) => ({ value: p.id, label: p.name }))
2534
+ ];
2535
+ }
2536
+ function modelOptionsOf(providerValue, providers, { followSession = false, followSessionLabel = "" } = {}) {
2537
+ const v = (providerValue || "").trim();
2538
+ const cur = providers.find((p) => p.id === v);
2539
+ if (cur) return cur.models.map((m) => ({ value: m, label: m }));
2540
+ if (v === "session") {
2541
+ const head = followSession && followSessionLabel ? [{ value: "", label: followSessionLabel }] : [];
2542
+ return [...head, ...[...new Set(providers.flatMap((p) => p.models))].map((m) => ({ value: m, label: m }))];
2543
+ }
2544
+ return [];
2545
+ }
2546
+ function modelPlaceholderOf(providerValue, L) {
2547
+ const v = (providerValue || "").trim();
2548
+ if (v === "session") return L.titleGenModelSessionHint;
2549
+ if (v === "") return L.titleGenModelManualHint;
2550
+ return L.titleGenModelHint;
2551
+ }
2552
+ function isManualConn(providerValue, providers) {
2553
+ const v = (providerValue || "").trim();
2554
+ return v === "" || v !== "session" && !providers.some((p) => p.id === v);
2555
+ }
2556
+
2448
2557
  // src/inject/features/styles.js
2449
2558
  var STYLE_DEFAULTS = {
2450
2559
  rowGap: 20,
@@ -2850,8 +2959,10 @@
2850
2959
  openDialog({
2851
2960
  title: L.settingsTitle,
2852
2961
  // 宽度类必须用宿主样式表已有的工具类(注入的类名不会生成 CSS):
2853
- // sm:max-w-3xl 不存在会失去约束拉满全屏;max-w-2xl=42rem 存在且尺寸合适
2854
- width: "max-w-2xl",
2962
+ // sm:max-w-3xl 不存在会失去约束拉满全屏;max-w-3xl=48rem 存在——英文界面
2963
+ // 标签栏较长,42rem 会挤出一块空白并触发横向滚动
2964
+ width: "max-w-3xl",
2965
+ bodyClass: "mt-1",
2855
2966
  overlay: "none",
2856
2967
  draggable: true,
2857
2968
  posKey: "settings",
@@ -2912,7 +3023,9 @@
2912
3023
  const config = await getConfig(true);
2913
3024
  const agentsRes = await rpc("/agents");
2914
3025
  const visionRes = await rpc("/vision");
2915
- const visionProvidersRes = await rpc("/vision/providers");
3026
+ const titleGenRes = await rpc("/title-gen");
3027
+ const titleGenProvidersRes = await rpc("/title-gen/providers");
3028
+ const titleGenProviders = titleGenProvidersRes.ok && Array.isArray(titleGenProvidersRes.providers) ? titleGenProvidersRes.providers : [];
2916
3029
  const rtkRes = await rpc("/rtk");
2917
3030
  const headroomRes = await rpc("/headroom");
2918
3031
  const setFeature = async (key, value) => {
@@ -2934,7 +3047,7 @@
2934
3047
  "data-zcodepro-status": "1"
2935
3048
  },
2936
3049
  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}`)
3050
+ h("span", { class: "text-foreground-subtle/70" }, `${health.version} \xB7 ${HELPER_URL}`)
2938
3051
  );
2939
3052
  const rows = h("div", { class: "divide-y divide-border rounded-xl border border-border" });
2940
3053
  const refreshRows = () => {
@@ -3103,19 +3216,20 @@
3103
3216
  );
3104
3217
  const paneStyles = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
3105
3218
  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" });
3219
+ const paneAgents = h("div", { role: "tabpanel", class: "mt-2", style: "display:none" });
3107
3220
  const paneProxy = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
3108
3221
  const paneSync = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
3109
3222
  const paneVision = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
3110
3223
  const paneRtk = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
3111
3224
  const paneHeadroom = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
3225
+ const paneTitleGen = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
3112
3226
  const paneDebug = h("div", { role: "tabpanel", class: "mt-4", style: "display:none" });
3113
3227
  const panes = { features: paneFeatures, styles: paneStyles, perf: panePerf, agents: paneAgents, proxy: paneProxy, sync: paneSync, vision: paneVision, headroom: paneHeadroom, rtk: paneRtk, debug: paneDebug };
3114
3228
  const tabDefs = [
3115
3229
  ["features", L.tabFeatures],
3230
+ ["agents", L.tabBehavior],
3116
3231
  ["styles", L.tabStyles],
3117
3232
  ["perf", L.tabPerformance],
3118
- ["agents", L.tabAgents],
3119
3233
  ["proxy", L.tabProxy],
3120
3234
  ["sync", L.tabSync],
3121
3235
  ["vision", L.tabVision],
@@ -3125,7 +3239,7 @@
3125
3239
  // 普通用户不可见
3126
3240
  ...config.devMode === true || config.devActive === true ? [["debug", L.tabDebug]] : []
3127
3241
  ];
3128
- const tablist = h("div", { role: "tablist", "aria-orientation": "horizontal", class: "zcodepro-tablist mt-4" });
3242
+ const tablist = h("div", { role: "tablist", "aria-orientation": "horizontal", class: "zcodepro-tablist mt-1" });
3129
3243
  const renderTabs = () => tablist.replaceChildren(...tabDefs.map(([id, label]) => h("button", {
3130
3244
  type: "button",
3131
3245
  role: "tab",
@@ -3334,7 +3448,7 @@
3334
3448
  )
3335
3449
  );
3336
3450
  const agentsArea = h("textarea", {
3337
- class: "zcodepro-textarea",
3451
+ class: "zcodepro-textarea zcodepro-textarea-lg",
3338
3452
  placeholder: L.agentsPlaceholder,
3339
3453
  spellcheck: "false"
3340
3454
  });
@@ -3365,12 +3479,36 @@
3365
3479
  agentsSaveBtn.disabled = false;
3366
3480
  }
3367
3481
  };
3368
- paneAgents.append(
3482
+ const agentsPromptPane = h("div", { class: "mt-4" });
3483
+ agentsPromptPane.append(
3369
3484
  h("p", { class: "text-ui-sm/relaxed text-foreground-subtle" }, L.agentsDesc),
3370
3485
  ...agentsFailed ? [h("p", { class: "mt-2 text-ui-sm text-destructive" }, L.agentsLoadFailed + ": " + errText(agentsRes))] : [],
3371
3486
  h("div", { class: "mt-3" }, agentsArea),
3372
3487
  h("div", { class: "mt-3 flex justify-end" }, agentsSaveBtn)
3373
3488
  );
3489
+ let agentsSubTab = "prompt";
3490
+ const agentsSubPanes = { prompt: agentsPromptPane, titleGen: paneTitleGen };
3491
+ const agentsSubList = h("div", { role: "tablist", "aria-orientation": "horizontal", class: "zcodepro-tablist" });
3492
+ const renderAgentsSubTabs = () => {
3493
+ agentsSubList.replaceChildren(...[
3494
+ ["prompt", L.tabAgents],
3495
+ ["titleGen", L.tabTitleGen]
3496
+ ].map(([id, label]) => h("button", {
3497
+ type: "button",
3498
+ role: "tab",
3499
+ class: "zcodepro-tab",
3500
+ "aria-selected": String(agentsSubTab === id),
3501
+ "data-state": agentsSubTab === id ? "active" : "inactive",
3502
+ onClick: () => {
3503
+ agentsSubTab = id;
3504
+ for (const [pid, pane] of Object.entries(agentsSubPanes)) pane.style.display = pid === id ? "" : "none";
3505
+ renderAgentsSubTabs();
3506
+ }
3507
+ }, label)));
3508
+ };
3509
+ for (const [pid, pane] of Object.entries(agentsSubPanes)) pane.style.display = pid === agentsSubTab ? "" : "none";
3510
+ renderAgentsSubTabs();
3511
+ paneAgents.append(agentsSubList, agentsPromptPane, paneTitleGen);
3374
3512
  const savedProxy = typeof config.proxy === "string" ? config.proxy : "";
3375
3513
  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
3514
  const proxyInput = h("input", { type: "text", value: savedProxy, placeholder: L.proxyPlaceholder, spellcheck: "false", class: proxyInputCls + " min-w-0 flex-1" });
@@ -3524,17 +3662,34 @@
3524
3662
  )
3525
3663
  );
3526
3664
  const savedSync = config.sync && typeof config.sync === "object" ? config.sync : { url: "", dir: "zcode-pro", login: "", password: "", include: { zcodepro: true, model: true }, lastPushAt: null, lastPullAt: null };
3527
- const syncInputCls = "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";
3665
+ const syncInputBase = "h-8 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";
3666
+ const syncInputCls = syncInputBase + " w-full";
3667
+ const syncLabel = (text) => h("span", { class: "w-20 shrink-0 text-ui-sm font-medium text-foreground" }, text);
3528
3668
  const syncFormRow = (labelText, node) => h(
3529
3669
  "div",
3530
3670
  { class: "flex items-center gap-3 px-2 py-1.5" },
3531
- h("span", { class: "w-20 shrink-0 text-ui-sm font-medium text-foreground" }, labelText),
3671
+ syncLabel(labelText),
3532
3672
  node
3533
3673
  );
3534
3674
  const syncUrlInput = h("input", { type: "text", value: savedSync.url || "", placeholder: L.syncUrlPlaceholder, spellcheck: "false", autocomplete: "off", class: syncInputCls + " min-w-0 flex-1" });
3535
3675
  const syncDirInput = h("input", { type: "text", value: savedSync.dir || "zcode-pro", placeholder: L.syncDirPlaceholder, spellcheck: "false", autocomplete: "off", class: syncInputCls + " min-w-0 flex-1" });
3536
3676
  const syncLoginInput = h("input", { type: "text", value: savedSync.login || "", spellcheck: "false", autocomplete: "off", class: syncInputCls + " min-w-0 flex-1" });
3537
3677
  const syncPasswordInput = h("input", { type: "password", value: savedSync.password || "", autocomplete: "new-password", class: syncInputCls + " min-w-0 flex-1" });
3678
+ const syncHistoryInput = h("input", {
3679
+ type: "number",
3680
+ min: "0",
3681
+ max: "100",
3682
+ step: "1",
3683
+ value: Number.isInteger(savedSync.historyLimit) ? savedSync.historyLimit : 10,
3684
+ title: L.syncHistoryLimitHint,
3685
+ class: syncInputBase + " w-20 shrink-0 text-center"
3686
+ });
3687
+ const syncHistoryValue = () => {
3688
+ const s = syncHistoryInput.value.trim();
3689
+ if (!s) return 10;
3690
+ const n = Number(s);
3691
+ return Number.isFinite(n) ? Math.min(100, Math.max(0, Math.round(n))) : 10;
3692
+ };
3538
3693
  let syncFormOriginal = null;
3539
3694
  const syncSaveBtn = btnSmall(L.syncSave, () => {
3540
3695
  void saveSyncForm();
@@ -3543,15 +3698,16 @@
3543
3698
  url: syncUrlInput.value.trim(),
3544
3699
  dir: syncDirInput.value.trim(),
3545
3700
  login: syncLoginInput.value.trim(),
3546
- password: syncPasswordInput.value
3701
+ password: syncPasswordInput.value,
3702
+ historyLimit: syncHistoryValue()
3547
3703
  });
3548
3704
  const syncCheckDirty = () => {
3549
3705
  const c = syncFormValues();
3550
- syncSaveBtn.disabled = c.url === syncFormOriginal.url && c.dir === syncFormOriginal.dir && c.login === syncFormOriginal.login && c.password === syncFormOriginal.password;
3706
+ syncSaveBtn.disabled = c.url === syncFormOriginal.url && c.dir === syncFormOriginal.dir && c.login === syncFormOriginal.login && c.password === syncFormOriginal.password && c.historyLimit === syncFormOriginal.historyLimit;
3551
3707
  };
3552
3708
  syncFormOriginal = syncFormValues();
3553
3709
  syncSaveBtn.disabled = true;
3554
- for (const el of [syncUrlInput, syncDirInput, syncLoginInput, syncPasswordInput]) {
3710
+ for (const el of [syncUrlInput, syncDirInput, syncLoginInput, syncPasswordInput, syncHistoryInput]) {
3555
3711
  el.addEventListener("input", syncCheckDirty);
3556
3712
  }
3557
3713
  const saveSyncForm = async () => {
@@ -3562,12 +3718,14 @@
3562
3718
  url: res.sync.url,
3563
3719
  dir: res.sync.dir,
3564
3720
  login: res.sync.login,
3565
- password: res.sync.password
3721
+ password: res.sync.password,
3722
+ historyLimit: Number.isInteger(res.sync.historyLimit) ? res.sync.historyLimit : 10
3566
3723
  };
3567
3724
  syncUrlInput.value = res.sync.url;
3568
3725
  syncDirInput.value = res.sync.dir;
3569
3726
  syncLoginInput.value = res.sync.login;
3570
3727
  syncPasswordInput.value = res.sync.password;
3728
+ syncHistoryInput.value = syncFormOriginal.historyLimit;
3571
3729
  showToast(L.syncSaved);
3572
3730
  } else {
3573
3731
  showToast(L.failed + ": " + errText(res), "error");
@@ -3863,7 +4021,7 @@
3863
4021
  return;
3864
4022
  }
3865
4023
  renderSyncStatus(res.sync || {});
3866
- showToast(L.syncUploaded, "success");
4024
+ showToast(res.pruned ? L.syncUploaded + (isZhLocale() ? "\uFF1B" : "; ") + L.syncPruned.replace("{n}", res.pruned) : L.syncUploaded, "success");
3867
4025
  };
3868
4026
  paneSync.append(
3869
4027
  h("p", { class: "text-ui-sm/relaxed text-foreground-subtle" }, L.syncDesc),
@@ -3871,12 +4029,27 @@
3871
4029
  "div",
3872
4030
  { class: "mt-3 rounded-xl border border-border p-1" },
3873
4031
  syncFormRow("URL", syncUrlInput),
3874
- syncFormRow(L.syncDir, syncDirInput),
3875
- syncFormRow(L.syncLogin, syncLoginInput),
3876
- syncFormRow(L.syncPassword, syncPasswordInput),
4032
+ // 登录与密码一行、上传目录与历史版本一行:短字段成对排,少占纵向空间
4033
+ h(
4034
+ "div",
4035
+ { class: "flex items-center gap-3 px-2 py-1.5" },
4036
+ syncLabel(L.syncLogin),
4037
+ syncLoginInput,
4038
+ syncLabel(L.syncPassword),
4039
+ syncPasswordInput
4040
+ ),
3877
4041
  h(
3878
4042
  "div",
3879
- { class: "flex items-start justify-between gap-2 px-2 py-1.5" },
4043
+ { class: "flex items-center gap-3 px-2 py-1.5" },
4044
+ syncLabel(L.syncDir),
4045
+ syncDirInput,
4046
+ syncLabel(L.syncHistoryLimit),
4047
+ syncHistoryInput,
4048
+ h("span", { class: "shrink-0 text-ui-xs text-foreground-subtle" }, L.syncHistoryLimitUnit)
4049
+ ),
4050
+ h(
4051
+ "div",
4052
+ { class: "flex items-center justify-between gap-2 px-2 py-1.5" },
3880
4053
  h(
3881
4054
  "div",
3882
4055
  { class: "min-w-0" },
@@ -3966,7 +4139,6 @@
3966
4139
  skipMinutes: 30,
3967
4140
  forceIntercept: true
3968
4141
  };
3969
- const visionProviders = visionProvidersRes.ok && Array.isArray(visionProvidersRes.providers) ? visionProvidersRes.providers : [];
3970
4142
  let visionCfg = visionRes.ok && visionRes.config && typeof visionRes.config === "object" ? structuredClone(visionRes.config) : null;
3971
4143
  let visionSaveTimer = null;
3972
4144
  const persistVision = async () => {
@@ -4158,53 +4330,45 @@
4158
4330
  h(
4159
4331
  "div",
4160
4332
  { 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";
4333
+ // 供应商与模型走 provider-fields 共用逻辑:跟随会话时模型下拉为
4334
+ // 全部供应商模型汇总,具体供应商为其模型列表;已删 id 标注“已删除”
4335
+ field(L.visionUseProvider, comboField(
4336
+ p.useProvider || "",
4337
+ providerOptionsOf(titleGenProviders, L),
4338
+ (v, src) => {
4339
+ p.useProvider = v;
4181
4340
  saveVisionSoon();
4182
- });
4183
- input.addEventListener("input", () => {
4184
- p.useProvider = input.value.trim();
4341
+ if (src === "select") renderVision();
4342
+ },
4343
+ { placeholder: L.titleGenProviderSession, staleLabel: L.titleGenProviderDeleted }
4344
+ )),
4345
+ // 跟随会话时模型下拉头部有“跟随当前会话当前模型”(值为空,
4346
+ // 插件 ≥1.6.1 在调用时解析为会话当前模型);具体供应商为其模型列表
4347
+ field(L.visionModel, comboField(
4348
+ p.model || "",
4349
+ modelOptionsOf(p.useProvider, titleGenProviders, {
4350
+ followSession: (p.useProvider || "").trim() === "session",
4351
+ followSessionLabel: L.titleGenModelFollowSession
4352
+ }),
4353
+ (v) => {
4354
+ p.model = v;
4185
4355
  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" }))
4356
+ },
4357
+ {
4358
+ placeholder: modelPlaceholderOf(p.useProvider, L),
4359
+ emptyLabel: (p.useProvider || "").trim() === "session" ? L.titleGenModelFollowSession : L.titleGenModelEmpty
4360
+ }
4361
+ )),
4362
+ ...isManualConn(p.useProvider, titleGenProviders) ? [
4363
+ field(L.visionBaseUrl, textIn(p.baseUrl, (v) => {
4364
+ p.baseUrl = v;
4365
+ saveVisionSoon();
4366
+ })),
4367
+ field(L.visionApiKey + " \xB7 " + L.visionApiKeyHint, textIn(p.apiKey, (v) => {
4368
+ p.apiKey = v;
4369
+ saveVisionSoon();
4370
+ }, { type: "password" }))
4371
+ ] : []
4208
4372
  ),
4209
4373
  (() => {
4210
4374
  const n = h("textarea", { class: "zcodepro-textarea zcodepro-textarea-sm", rows: "3", placeholder: L.visionPrompt });
@@ -4945,13 +5109,190 @@
4945
5109
  );
4946
5110
  };
4947
5111
  renderHeadroomPane();
5112
+ let titleGenCfg = titleGenRes.ok && titleGenRes.titleGen && typeof titleGenRes.titleGen === "object" ? structuredClone(titleGenRes.titleGen) : null;
5113
+ const titleGenDefaultPrompt = titleGenRes.ok && typeof titleGenRes.defaultPrompt === "string" ? titleGenRes.defaultPrompt : "";
5114
+ let titleGenSaveTimer = null;
5115
+ const persistTitleGen = async () => {
5116
+ const res = await rpc("/title-gen", { method: "POST", body: { titleGen: titleGenCfg } });
5117
+ clearConfigCache();
5118
+ if (!res.ok) showToast(L.failed + ": " + errText(res), "error");
5119
+ return res.ok;
5120
+ };
5121
+ const saveTitleGenSoon = () => {
5122
+ clearTimeout(titleGenSaveTimer);
5123
+ titleGenSaveTimer = setTimeout(() => {
5124
+ void persistTitleGen();
5125
+ }, 500);
5126
+ };
5127
+ const renderTitleGenPane = () => {
5128
+ if (!titleGenCfg) {
5129
+ paneTitleGen.replaceChildren(
5130
+ h("p", { class: "text-ui-sm/relaxed text-foreground-subtle" }, L.titleGenShortDesc),
5131
+ h("p", { class: "mt-2 text-ui-sm text-destructive" }, L.visionLoadFailed + ": " + errText(titleGenRes))
5132
+ );
5133
+ return;
5134
+ }
5135
+ 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";
5136
+ const field = (labelText, node) => h(
5137
+ "label",
5138
+ { class: "block min-w-0" },
5139
+ h("span", { class: "mb-1 block text-ui-xs font-medium text-foreground-subtle" }, labelText),
5140
+ node
5141
+ );
5142
+ const showInlineConn = isManualConn(titleGenCfg.provider, titleGenProviders);
5143
+ const providerCombo = comboField(
5144
+ titleGenCfg.provider || "",
5145
+ providerOptionsOf(titleGenProviders, L),
5146
+ (v, src) => {
5147
+ titleGenCfg.provider = v;
5148
+ if (src === "select") {
5149
+ void persistTitleGen();
5150
+ renderTitleGenPane();
5151
+ } else saveTitleGenSoon();
5152
+ },
5153
+ { placeholder: L.titleGenProviderSession, staleLabel: L.titleGenProviderDeleted }
5154
+ );
5155
+ const modelCombo = comboField(
5156
+ titleGenCfg.model || "",
5157
+ // 跟随会话时选项表头部有“跟随当前会话当前模型”(值为空,生成时用
5158
+ // 会话当前模型),选了具体模型后也能再切回它
5159
+ modelOptionsOf(titleGenCfg.provider, titleGenProviders, {
5160
+ followSession: true,
5161
+ followSessionLabel: L.titleGenModelFollowSession
5162
+ }),
5163
+ (v) => {
5164
+ titleGenCfg.model = v;
5165
+ saveTitleGenSoon();
5166
+ },
5167
+ {
5168
+ placeholder: modelPlaceholderOf(titleGenCfg.provider, L),
5169
+ emptyLabel: (titleGenCfg.provider || "").trim() === "session" ? L.titleGenModelFollowSession : L.titleGenModelEmpty
5170
+ }
5171
+ );
5172
+ const textIn = (value, onInput, { type = "text", placeholder = "" } = {}) => {
5173
+ const n = h("input", { type, value: value || "", placeholder, class: inputCls });
5174
+ n.addEventListener("input", () => onInput(n.value));
5175
+ return n;
5176
+ };
5177
+ const formatSel = h(
5178
+ "select",
5179
+ { class: inputCls },
5180
+ h("option", { value: "anthropic" }, "anthropic"),
5181
+ h("option", { value: "openai" }, "openai")
5182
+ );
5183
+ formatSel.value = titleGenCfg.format === "openai" ? "openai" : "anthropic";
5184
+ formatSel.addEventListener("change", () => {
5185
+ titleGenCfg.format = formatSel.value;
5186
+ void persistTitleGen();
5187
+ });
5188
+ const inlineConnCard = h(
5189
+ "div",
5190
+ {
5191
+ class: "mt-2 grid grid-cols-1 gap-2 rounded-xl border border-border p-2 sm:grid-cols-3",
5192
+ style: showInlineConn ? "" : "display:none"
5193
+ },
5194
+ field(L.titleGenBaseUrl, textIn(titleGenCfg.baseUrl, (v) => {
5195
+ titleGenCfg.baseUrl = v;
5196
+ saveTitleGenSoon();
5197
+ }, { placeholder: "https://\u2026" })),
5198
+ field(L.titleGenApiKey, textIn(titleGenCfg.apiKey, (v) => {
5199
+ titleGenCfg.apiKey = v;
5200
+ saveTitleGenSoon();
5201
+ }, { type: "password" })),
5202
+ field(L.titleGenFormat, formatSel)
5203
+ );
5204
+ const promptArea = h("textarea", {
5205
+ class: "zcodepro-textarea zcodepro-textarea-sm",
5206
+ rows: "8",
5207
+ placeholder: L.titleGenPromptPlaceholder
5208
+ });
5209
+ promptArea.value = titleGenCfg.prompt || "";
5210
+ promptArea.addEventListener("input", () => {
5211
+ titleGenCfg.prompt = promptArea.value;
5212
+ saveTitleGenSoon();
5213
+ });
5214
+ 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?";
5215
+ const sampleInput = h("input", {
5216
+ type: "text",
5217
+ class: inputCls + " min-w-0 flex-1",
5218
+ value: sampleText
5219
+ });
5220
+ let testBtn;
5221
+ const testState = h("span", { class: "min-w-0 flex-1 truncate text-ui-xs/relaxed text-foreground-subtle" });
5222
+ testBtn = btnSmall(L.titleGenTest, async () => {
5223
+ testBtn.disabled = true;
5224
+ testBtn.textContent = L.titleGenTesting;
5225
+ testState.textContent = L.titleGenTesting;
5226
+ const res = await rpc("/title-gen/run", {
5227
+ method: "POST",
5228
+ body: { text: sampleInput.value, test: true }
5229
+ });
5230
+ testBtn.disabled = false;
5231
+ testBtn.textContent = L.titleGenTest;
5232
+ if (res && res.ok) {
5233
+ testState.textContent = L.titleGenTestOk + res.title;
5234
+ showToast(res.title, "success");
5235
+ } else {
5236
+ testState.textContent = res && errText(res) || L.titleGenTestFailed;
5237
+ testState.className = "min-w-0 flex-1 truncate text-ui-xs/relaxed text-destructive";
5238
+ showToast(L.titleGenTestFailed + ": " + (res ? errText(res) : ""), "error");
5239
+ }
5240
+ }, "shrink-0");
5241
+ paneTitleGen.replaceChildren(
5242
+ // 信息卡:一句话简介在左,启用开关右对齐
5243
+ h(
5244
+ "div",
5245
+ { class: "mt-3 flex items-center justify-between gap-2 rounded-xl border border-border p-2" },
5246
+ h("span", { class: "min-w-0 text-ui-sm/relaxed text-foreground-subtle" }, L.titleGenShortDesc),
5247
+ h("div", { class: "shrink-0" }, settingRow(L.titleGenEnabled, "", titleGenCfg.enabled === true, async () => {
5248
+ titleGenCfg.enabled = !(titleGenCfg.enabled === true);
5249
+ await persistTitleGen();
5250
+ renderTitleGenPane();
5251
+ }))
5252
+ ),
5253
+ // 供应商 + 模型
5254
+ h(
5255
+ "div",
5256
+ { class: "mt-2 grid grid-cols-1 gap-2 rounded-xl border border-border p-2 sm:grid-cols-2" },
5257
+ field(L.titleGenProvider, providerCombo),
5258
+ field(L.titleGenModel, modelCombo)
5259
+ ),
5260
+ inlineConnCard,
5261
+ // 提示词 + 载入默认
5262
+ h(
5263
+ "div",
5264
+ { class: "mt-2 rounded-xl border border-border p-2" },
5265
+ h(
5266
+ "div",
5267
+ { class: "mb-1 flex items-center justify-between gap-2" },
5268
+ h("span", { class: "text-ui-xs font-medium text-foreground-subtle" }, L.titleGenPrompt),
5269
+ btnSmall(L.titleGenPromptReset, () => {
5270
+ promptArea.value = titleGenDefaultPrompt;
5271
+ titleGenCfg.prompt = titleGenDefaultPrompt;
5272
+ void persistTitleGen();
5273
+ promptArea.focus();
5274
+ })
5275
+ ),
5276
+ promptArea
5277
+ ),
5278
+ // 测试
5279
+ h(
5280
+ "div",
5281
+ { class: "mt-2 flex items-center gap-2 rounded-xl border border-border p-2" },
5282
+ sampleInput,
5283
+ testBtn
5284
+ ),
5285
+ testState
5286
+ );
5287
+ };
5288
+ renderTitleGenPane();
4948
5289
  body.append(
4949
5290
  statusLine,
4950
5291
  tablist,
4951
5292
  paneFeatures,
5293
+ paneAgents,
4952
5294
  paneStyles,
4953
5295
  panePerf,
4954
- paneAgents,
4955
5296
  paneProxy,
4956
5297
  paneSync,
4957
5298
  paneVision,
@@ -5424,15 +5765,68 @@
5424
5765
  const map = await fetchPinned();
5425
5766
  if (map) scan2();
5426
5767
  }
5768
+ function setRowTitle(key, text) {
5769
+ for (const item of findRows(key)) {
5770
+ for (const el of titleElsOf(item)) writeTitle(el, text);
5771
+ }
5772
+ }
5773
+ function refreshPinnedTitles() {
5774
+ return fetchPinned().then((m) => {
5775
+ if (m) scan2();
5776
+ });
5777
+ }
5778
+ var RENAME_CTX_VALID_MS = 15e3;
5779
+ var renameCtx = null;
5780
+ var renamePrefilled = /* @__PURE__ */ new WeakSet();
5781
+ function setNativeValue(el, text) {
5782
+ if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
5783
+ Object.getOwnPropertyDescriptor(el.constructor.prototype, "value").set.call(el, text);
5784
+ } else {
5785
+ el.textContent = text;
5786
+ }
5787
+ el.dispatchEvent(new Event("input", { bubbles: true }));
5788
+ }
5789
+ function prefillRenameDialog() {
5790
+ if (!renameCtx || !renameCtx.armed || Date.now() - renameCtx.at > RENAME_CTX_VALID_MS) {
5791
+ renameCtx = null;
5792
+ return;
5793
+ }
5794
+ if (!enabled2 || !pinned) return;
5795
+ const want = pinned.get(sessIdOf(renameCtx.key));
5796
+ if (want === void 0) return;
5797
+ for (const el of document.querySelectorAll('[role="dialog"] input, [role="dialog"] textarea, [role="dialog"] [contenteditable="true"]')) {
5798
+ if (renamePrefilled.has(el) || !el.getClientRects().length) continue;
5799
+ if (el.closest(TASK_SEL) || el.closest(".zcodepro-overlay")) continue;
5800
+ const cur = String(el.value ?? el.textContent ?? "").trim();
5801
+ if (!cur) continue;
5802
+ renamePrefilled.add(el);
5803
+ if (cur !== String(want).trim()) setNativeValue(el, String(want));
5804
+ renameCtx = null;
5805
+ return;
5806
+ }
5807
+ }
5427
5808
  function startTitleLock() {
5428
5809
  if (installed2 || typeof document === "undefined") return;
5429
5810
  installed2 = true;
5430
5811
  void bootstrap();
5431
5812
  new MutationObserver((muts) => {
5813
+ if (renameCtx) prefillRenameDialog();
5432
5814
  if (!taskRowMutations2(muts)) return;
5433
5815
  refreshMapSoon();
5434
5816
  scheduleScan2();
5435
5817
  }).observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ["class"], characterData: true });
5818
+ document.addEventListener("contextmenu", (e) => {
5819
+ const row = e.target instanceof Element && e.target.closest(TASK_SEL);
5820
+ renameCtx = row ? { key: row.getAttribute("data-task-item-key") || "", at: 0, armed: false } : null;
5821
+ }, true);
5822
+ document.addEventListener("click", (e) => {
5823
+ if (!renameCtx || renameCtx.armed) return;
5824
+ const item = e.target instanceof Element && e.target.closest('[role="menuitem"], [data-radix-collection-item]');
5825
+ if (item && /重命名|rename/i.test(item.textContent || "")) {
5826
+ renameCtx.armed = true;
5827
+ renameCtx.at = Date.now();
5828
+ }
5829
+ }, true);
5436
5830
  scheduleBootRetry();
5437
5831
  setInterval(() => {
5438
5832
  if (!document.hidden) scan2();
@@ -5459,8 +5853,152 @@
5459
5853
  });
5460
5854
  }
5461
5855
 
5462
- // src/inject/features/pinned-expand.js
5856
+ // src/inject/features/title-gen.js
5857
+ var CONV2 = '[class*="@md/conversation"]';
5858
+ var USER_ROW = '[class*="user-row"]';
5859
+ var SCAN_DEBOUNCE_MS3 = 300;
5860
+ var SCAN_FALLBACK_MS3 = 5e3;
5861
+ var SELECT_WAIT_MS = 2e3;
5862
+ var MSG_WAIT_MS = 1e4;
5863
+ var QUIET_FALLBACK_MS = 12e4;
5864
+ var COLLECT_MAX_MS = 6e5;
5463
5865
  var installed3 = false;
5866
+ var knownKeys = /* @__PURE__ */ new Set();
5867
+ var attempted = /* @__PURE__ */ new Set();
5868
+ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
5869
+ function sessIdOf2(key) {
5870
+ const i = key.indexOf(":sess_");
5871
+ return i > 0 ? key.slice(i + 1) : "";
5872
+ }
5873
+ function currentKey() {
5874
+ for (const el of document.querySelectorAll("[data-task-item-key]")) {
5875
+ if ((el.className + "").includes("bg-selected")) return el.getAttribute("data-task-item-key") || "";
5876
+ }
5877
+ return "";
5878
+ }
5879
+ function userTexts() {
5880
+ return [...document.querySelectorAll(`${CONV2} ${USER_ROW}`)].map((r) => {
5881
+ const el = r.querySelector(".whitespace-pre-wrap");
5882
+ return ((el ? el.textContent : r.innerText) || "").trim();
5883
+ }).filter(Boolean);
5884
+ }
5885
+ async function onNewTaskRow(key) {
5886
+ const selDeadline = Date.now() + SELECT_WAIT_MS;
5887
+ while (currentKey() !== key && Date.now() < selDeadline) await sleep(200);
5888
+ if (currentKey() !== key) return;
5889
+ const cfg = await getConfig().catch(() => null);
5890
+ if (!cfg || !cfg.titleGen || cfg.titleGen.enabled !== true) return;
5891
+ const sessId = sessIdOf2(key);
5892
+ if (!sessId || attempted.has(sessId)) return;
5893
+ attempted.add(sessId);
5894
+ let texts = [];
5895
+ const msgDeadline = Date.now() + MSG_WAIT_MS;
5896
+ while (Date.now() < msgDeadline) {
5897
+ texts = userTexts();
5898
+ if (texts.length) break;
5899
+ await sleep(400);
5900
+ }
5901
+ if (!texts.length) return;
5902
+ let assistantSeen = false;
5903
+ const convObserver = new MutationObserver((muts) => {
5904
+ if (currentKey() !== key) return;
5905
+ for (const m of muts) {
5906
+ const t2 = m.type === "characterData" ? m.target.parentElement : m.target;
5907
+ if (t2 instanceof Element && t2.closest(USER_ROW)) continue;
5908
+ for (const n of [t2, ...m.addedNodes]) {
5909
+ if (n instanceof Element && n.closest && n.closest(CONV2)) {
5910
+ assistantSeen = true;
5911
+ return;
5912
+ }
5913
+ }
5914
+ }
5915
+ });
5916
+ convObserver.observe(document.body, { childList: true, subtree: true, characterData: true });
5917
+ const startAt = Date.now();
5918
+ let lastCount = texts.length;
5919
+ let lastChange = Date.now();
5920
+ try {
5921
+ while (Date.now() - startAt < COLLECT_MAX_MS) {
5922
+ await sleep(500);
5923
+ if (currentKey() !== key) {
5924
+ lastChange = Date.now();
5925
+ continue;
5926
+ }
5927
+ texts = userTexts();
5928
+ if (texts.length !== lastCount) {
5929
+ lastCount = texts.length;
5930
+ lastChange = Date.now();
5931
+ }
5932
+ if (assistantSeen) break;
5933
+ if (Date.now() - lastChange >= QUIET_FALLBACK_MS) break;
5934
+ }
5935
+ } finally {
5936
+ convObserver.disconnect();
5937
+ }
5938
+ if (!texts.length) return;
5939
+ const callTitleGen = () => rpc("/title-gen/run", { method: "POST", body: { sessId, text: texts.join("\n") } }).catch(() => null);
5940
+ let res = await callTitleGen();
5941
+ for (const delay of [5e3, 2e4]) {
5942
+ if (res && res.ok) break;
5943
+ await sleep(delay);
5944
+ res = await callTitleGen();
5945
+ }
5946
+ if (res && res.ok && res.written && res.title) {
5947
+ setRowTitle(key, res.title);
5948
+ void refreshPinnedTitles();
5949
+ }
5950
+ }
5951
+ function scanForNewRows() {
5952
+ for (const el of document.querySelectorAll("[data-task-item-key]")) {
5953
+ const key = el.getAttribute("data-task-item-key") || "";
5954
+ if (!key || knownKeys.has(key)) continue;
5955
+ knownKeys.add(key);
5956
+ void onNewTaskRow(key);
5957
+ }
5958
+ }
5959
+ var TASK_SEL2 = "[data-task-item-key]";
5960
+ var inTaskRow3 = (node) => !!(node instanceof Element && node.closest && node.closest(TASK_SEL2));
5961
+ function taskRowMutations3(muts) {
5962
+ for (const m of muts) {
5963
+ if (m.type === "attributes") {
5964
+ if (inTaskRow3(m.target)) return true;
5965
+ } else if (m.type === "characterData") {
5966
+ if (m.target.parentElement && inTaskRow3(m.target.parentElement)) return true;
5967
+ } else if (inTaskRow3(m.target)) {
5968
+ return true;
5969
+ } else {
5970
+ for (const n of m.addedNodes) {
5971
+ if (n instanceof Element && (n.matches(TASK_SEL2) || !!n.querySelector(TASK_SEL2))) return true;
5972
+ }
5973
+ }
5974
+ }
5975
+ return false;
5976
+ }
5977
+ var scanDebounce3 = 0;
5978
+ function scheduleScan3() {
5979
+ if (scanDebounce3) return;
5980
+ scanDebounce3 = setTimeout(() => {
5981
+ scanDebounce3 = 0;
5982
+ scanForNewRows();
5983
+ }, SCAN_DEBOUNCE_MS3);
5984
+ }
5985
+ function startTitleGen() {
5986
+ if (installed3 || typeof document === "undefined") return;
5987
+ installed3 = true;
5988
+ scanForNewRows();
5989
+ new MutationObserver((muts) => {
5990
+ if (taskRowMutations3(muts)) scheduleScan3();
5991
+ }).observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ["class"], characterData: true });
5992
+ setInterval(() => {
5993
+ if (!document.hidden) scanForNewRows();
5994
+ }, SCAN_FALLBACK_MS3);
5995
+ document.addEventListener("visibilitychange", () => {
5996
+ if (!document.hidden) scanForNewRows();
5997
+ });
5998
+ }
5999
+
6000
+ // src/inject/features/pinned-expand.js
6001
+ var installed4 = false;
5464
6002
  var keepCollapsed = false;
5465
6003
  var CONFIG_POLL_MIN_MS = 5e3;
5466
6004
  var CONFIG_POLL_MAX_MS = 6e4;
@@ -5487,8 +6025,8 @@
5487
6025
  }, pollMs);
5488
6026
  }
5489
6027
  function startPinnedExpandSuppression() {
5490
- if (installed3 || typeof document === "undefined") return;
5491
- installed3 = true;
6028
+ if (installed4 || typeof document === "undefined") return;
6029
+ installed4 = true;
5492
6030
  void refreshConfig();
5493
6031
  pollConfig();
5494
6032
  document.addEventListener("visibilitychange", () => {
@@ -5567,10 +6105,10 @@
5567
6105
  }
5568
6106
 
5569
6107
  // src/inject/features/ws-running.js
5570
- var installed4 = false;
6108
+ var installed5 = false;
5571
6109
  var enabled3 = true;
5572
- var SCAN_DEBOUNCE_MS3 = 150;
5573
- var SCAN_FALLBACK_MS3 = 5e3;
6110
+ var SCAN_DEBOUNCE_MS4 = 150;
6111
+ var SCAN_FALLBACK_MS4 = 5e3;
5574
6112
  var PEEK_FIRST_MS = 2e4;
5575
6113
  var PEEK_REPEAT_MS = 6e4;
5576
6114
  var PEEK_SETTLE_MS = 160;
@@ -5578,7 +6116,7 @@
5578
6116
  var PEEK_UNMOUNT_WAIT_MS = 800;
5579
6117
  var CONFIG_POLL_MIN_MS2 = 5e3;
5580
6118
  var CONFIG_POLL_MAX_MS2 = 6e4;
5581
- var TASK_SEL2 = "[data-task-item-key]";
6119
+ var TASK_SEL3 = "[data-task-item-key]";
5582
6120
  var WS_SEL = '[data-testid^="workspace-item-"]';
5583
6121
  var runningByWs = /* @__PURE__ */ new Map();
5584
6122
  var peekTimers = /* @__PURE__ */ new Map();
@@ -5601,10 +6139,10 @@
5601
6139
  return [...holder.children].some((c) => c.tagName === "svg" && (c.getAttribute("class") || "").includes("lucide-loader"));
5602
6140
  }
5603
6141
  function domRowsOf(ws) {
5604
- return [...document.querySelectorAll(TASK_SEL2)].filter((e) => workspaceOf2(e.getAttribute("data-task-item-key") || "") === ws);
6142
+ return [...document.querySelectorAll(TASK_SEL3)].filter((e) => workspaceOf2(e.getAttribute("data-task-item-key") || "") === ws);
5605
6143
  }
5606
6144
  function currentTaskRow2() {
5607
- for (const el of document.querySelectorAll(TASK_SEL2)) {
6145
+ for (const el of document.querySelectorAll(TASK_SEL3)) {
5608
6146
  if ((el.className + "").includes("bg-selected")) return el;
5609
6147
  }
5610
6148
  return null;
@@ -5620,12 +6158,12 @@
5620
6158
  const backKey = cur ? cur.getAttribute("data-task-item-key") : "";
5621
6159
  synthClick(head);
5622
6160
  if (!backKey) return;
5623
- const back = cur && cur.isConnected ? cur : document.querySelector(`${TASK_SEL2}[data-task-item-key="${CSS.escape(backKey)}"]`);
6161
+ const back = cur && cur.isConnected ? cur : document.querySelector(`${TASK_SEL3}[data-task-item-key="${CSS.escape(backKey)}"]`);
5624
6162
  if (back) synthClick(back);
5625
6163
  }
5626
6164
  function scan3() {
5627
6165
  wsWithRows = /* @__PURE__ */ new Set();
5628
- for (const el of document.querySelectorAll(TASK_SEL2)) {
6166
+ for (const el of document.querySelectorAll(TASK_SEL3)) {
5629
6167
  const key = el.getAttribute("data-task-item-key") || "";
5630
6168
  const ws = workspaceOf2(key);
5631
6169
  if (!ws) continue;
@@ -5651,8 +6189,8 @@
5651
6189
  if (m.type !== "childList") continue;
5652
6190
  for (const n of m.removedNodes) {
5653
6191
  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);
6192
+ if (n.matches(TASK_SEL3)) removed.push(n);
6193
+ else for (const r of n.querySelectorAll(TASK_SEL3)) removed.push(r);
5656
6194
  }
5657
6195
  }
5658
6196
  if (!removed.length) return;
@@ -5835,7 +6373,7 @@
5835
6373
  var PEEK_EXEMPT_CLASS = "zcodepro-peek-exempt";
5836
6374
  var cssAttr = (v) => String(v).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
5837
6375
  function installPeekHider(ws) {
5838
- const rowSel3 = `${TASK_SEL2}[data-task-item-key^="${cssAttr(ws + ":")}"]`;
6376
+ const rowSel3 = `${TASK_SEL3}[data-task-item-key^="${cssAttr(ws + ":")}"]`;
5839
6377
  for (const r of document.querySelectorAll(rowSel3)) r.classList.add(PEEK_EXEMPT_CLASS);
5840
6378
  const style = document.createElement("style");
5841
6379
  style.textContent = `${rowSel3}:not(.${PEEK_EXEMPT_CLASS}){display:none!important}`;
@@ -5847,7 +6385,7 @@
5847
6385
  for (const r of document.querySelectorAll("." + PEEK_EXEMPT_CLASS)) r.classList.remove(PEEK_EXEMPT_CLASS);
5848
6386
  }
5849
6387
  function waitForRowsGone(ws) {
5850
- const sel = `${TASK_SEL2}[data-task-item-key^="${cssAttr(ws + ":")}"]:not(.${PEEK_EXEMPT_CLASS})`;
6388
+ const sel = `${TASK_SEL3}[data-task-item-key^="${cssAttr(ws + ":")}"]:not(.${PEEK_EXEMPT_CLASS})`;
5851
6389
  return new Promise((resolve) => {
5852
6390
  const t0 = Date.now();
5853
6391
  const timer = setInterval(() => {
@@ -5869,12 +6407,12 @@
5869
6407
  }
5870
6408
  function relevant(muts) {
5871
6409
  for (const m of muts) {
5872
- if (inSel(m.target, TASK_SEL2) || inSel(m.target, WS_SEL)) return true;
6410
+ if (inSel(m.target, TASK_SEL3) || inSel(m.target, WS_SEL)) return true;
5873
6411
  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;
6412
+ if (n instanceof Element && (n.matches(TASK_SEL3) || n.matches(WS_SEL) || n.querySelector(TASK_SEL3) || n.querySelector(WS_SEL))) return true;
5875
6413
  }
5876
6414
  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;
6415
+ if (n instanceof Element && (n.matches(TASK_SEL3) || n.matches(WS_SEL) || n.querySelector(TASK_SEL3))) return true;
5878
6416
  }
5879
6417
  }
5880
6418
  return false;
@@ -5905,27 +6443,27 @@
5905
6443
  pollConfig2();
5906
6444
  }, pollMs2);
5907
6445
  }
5908
- var scanDebounce3 = 0;
6446
+ var scanDebounce4 = 0;
5909
6447
  function startWsRunningSpin() {
5910
- if (installed4 || typeof document === "undefined") return;
5911
- installed4 = true;
6448
+ if (installed5 || typeof document === "undefined") return;
6449
+ installed5 = true;
5912
6450
  void refreshConfig2();
5913
6451
  pollConfig2();
5914
6452
  const observer2 = new MutationObserver((muts) => {
5915
6453
  if (!relevant(muts)) return;
5916
6454
  handleRemovals(muts);
5917
- if (!scanDebounce3) {
5918
- scanDebounce3 = setTimeout(() => {
5919
- scanDebounce3 = 0;
6455
+ if (!scanDebounce4) {
6456
+ scanDebounce4 = setTimeout(() => {
6457
+ scanDebounce4 = 0;
5920
6458
  scan3();
5921
- }, SCAN_DEBOUNCE_MS3);
6459
+ }, SCAN_DEBOUNCE_MS4);
5922
6460
  }
5923
6461
  });
5924
6462
  observer2.observe(document.body, { childList: true, subtree: true });
5925
6463
  scan3();
5926
6464
  setInterval(() => {
5927
6465
  if (!document.hidden && enabled3) scan3();
5928
- }, SCAN_FALLBACK_MS3);
6466
+ }, SCAN_FALLBACK_MS4);
5929
6467
  document.addEventListener("visibilitychange", () => {
5930
6468
  if (!document.hidden) scan3();
5931
6469
  });
@@ -6089,7 +6627,7 @@
6089
6627
  // src/inject/features/toolbar-icons.js
6090
6628
  var HIDE_CLASS = "zcodepro-tb-hidden";
6091
6629
  var SYNC_DEBOUNCE_MS = 150;
6092
- var SCAN_FALLBACK_MS4 = 5e3;
6630
+ var SCAN_FALLBACK_MS5 = 5e3;
6093
6631
  var ROW_SEL = "[data-task-item-key]";
6094
6632
  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
6633
  var enabled4 = true;
@@ -6161,6 +6699,7 @@
6161
6699
  menu.classList.add(HIDE_CLASS);
6162
6700
  origs = next;
6163
6701
  rowEl?.remove();
6702
+ navRow.classList.add("zcodepro-tb-hidden-native");
6164
6703
  const L = t();
6165
6704
  const mk = (kind, orig, nameSel, fallback) => h("button", {
6166
6705
  type: "button",
@@ -6186,7 +6725,9 @@
6186
6725
  const pIcon = iconOf(next.plugin);
6187
6726
  if (pIcon) pluginBtn.append(pIcon);
6188
6727
  rowEl = h("div", { class: "zcodepro-tb-row" }, newBtn, searchBtn, autoBtn, pluginBtn);
6189
- navRow.append(rowEl);
6728
+ const updBtn = [...navRow.querySelectorAll("button")].find((b) => /bg-(success|emerald)/.test(b.className || ""));
6729
+ if (updBtn) updBtn.before(rowEl);
6730
+ else navRow.append(rowEl);
6190
6731
  return true;
6191
6732
  }
6192
6733
  function sidebarCollapsed() {
@@ -6216,10 +6757,18 @@
6216
6757
  if (sidebarCollapsed() || sidebarTooNarrow()) {
6217
6758
  if (rowEl) rowEl.style.display = "none";
6218
6759
  menu.classList.remove(HIDE_CLASS);
6760
+ document.querySelectorAll(".zcodepro-tb-hidden-native").forEach((el) => el.classList.remove("zcodepro-tb-hidden-native"));
6219
6761
  return;
6220
6762
  }
6221
6763
  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;
6764
+ if (!alive && !rebuild()) {
6765
+ if (rowEl) rowEl.style.display = "none";
6766
+ setTimeout(scheduleSync, 400);
6767
+ setTimeout(scheduleSync, 1200);
6768
+ return;
6769
+ }
6770
+ const navRow = document.querySelector('[data-testid="desktop-top-nav-back"]')?.parentElement;
6771
+ if (navRow) navRow.classList.add("zcodepro-tb-hidden-native");
6223
6772
  menu.classList.add(HIDE_CLASS);
6224
6773
  if (rowEl) rowEl.style.display = "";
6225
6774
  syncActive();
@@ -6255,6 +6804,7 @@
6255
6804
  rowEl = null;
6256
6805
  if (origs) {
6257
6806
  origs.menu.classList.remove(HIDE_CLASS);
6807
+ document.querySelectorAll(".zcodepro-tb-hidden-native").forEach((el) => el.classList.remove("zcodepro-tb-hidden-native"));
6258
6808
  origs = null;
6259
6809
  }
6260
6810
  }
@@ -6275,7 +6825,7 @@
6275
6825
  }).observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ["class", "aria-pressed"] });
6276
6826
  setInterval(() => {
6277
6827
  if (!document.hidden) sync();
6278
- }, SCAN_FALLBACK_MS4);
6828
+ }, SCAN_FALLBACK_MS5);
6279
6829
  document.addEventListener("visibilitychange", () => {
6280
6830
  if (!document.hidden) sync();
6281
6831
  });
@@ -6286,7 +6836,7 @@
6286
6836
  var HEAD_CLASS = "zcodepro-pin-head";
6287
6837
  var STORE_KEY = "zcodepro:pinned-collapsed";
6288
6838
  var SYNC_DEBOUNCE_MS2 = 300;
6289
- var SCAN_FALLBACK_MS5 = 5e3;
6839
+ var SCAN_FALLBACK_MS6 = 5e3;
6290
6840
  var enabled5 = true;
6291
6841
  var syncTimer2 = 0;
6292
6842
  function findPinnedHeader() {
@@ -6405,7 +6955,7 @@
6405
6955
  }).observe(document.body, { childList: true, subtree: true });
6406
6956
  setInterval(() => {
6407
6957
  if (!document.hidden) sync2();
6408
- }, SCAN_FALLBACK_MS5);
6958
+ }, SCAN_FALLBACK_MS6);
6409
6959
  document.addEventListener("visibilitychange", () => {
6410
6960
  if (!document.hidden) sync2();
6411
6961
  });
@@ -6486,6 +7036,10 @@
6486
7036
  startTitleLock();
6487
7037
  } catch {
6488
7038
  }
7039
+ try {
7040
+ startTitleGen();
7041
+ } catch {
7042
+ }
6489
7043
  try {
6490
7044
  startImageMenu();
6491
7045
  } catch {