zcode-pro 1.3.1 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/inject.js CHANGED
@@ -17,15 +17,24 @@
17
17
  }).then((r) => r.json()).catch((err) => ({ ok: false, error: String(err && err.message || err) }));
18
18
  }
19
19
  var configCache = { value: null, at: 0 };
20
+ var configInflight = null;
20
21
  async function getConfig(force = false) {
21
22
  const now = Date.now();
22
23
  if (!force && configCache.value && now - configCache.at < 3e3) return configCache.value;
23
- const res = await rpc("/config");
24
- if (res.ok && res.config) {
25
- configCache = { value: res.config, at: now };
26
- return res.config;
27
- }
28
- return configCache.value || { features: {} };
24
+ if (configInflight) return configInflight;
25
+ configInflight = (async () => {
26
+ try {
27
+ const res = await rpc("/config");
28
+ if (res.ok && res.config) {
29
+ configCache = { value: res.config, at: Date.now() };
30
+ return res.config;
31
+ }
32
+ return configCache.value || { features: {} };
33
+ } finally {
34
+ configInflight = null;
35
+ }
36
+ })();
37
+ return configInflight;
29
38
  }
30
39
  function clearConfigCache() {
31
40
  configCache = { value: null, at: 0 };
@@ -96,7 +105,7 @@
96
105
  var zh = {
97
106
  settingsTitle: "ZCode Pro",
98
107
  tabFeatures: "\u529F\u80FD",
99
- tabStyles: "\u6837\u5F0F",
108
+ tabStyles: "\u5916\u89C2",
100
109
  tabProxy: "\u4EE3\u7406",
101
110
  tabBehavior: "\u884C\u4E3A",
102
111
  tabAgents: "\u5168\u5C40\u63D0\u793A\u8BCD",
@@ -177,8 +186,6 @@
177
186
  syncVerDeleteDesc: "\u5C06\u5220\u9664\u4E91\u7AEF\u7684\u8FD9\u4EFD\u5907\u4EFD\uFF0C\u4E0D\u53EF\u6062\u590D\uFF1A",
178
187
  syncVerDeleted: "\u5DF2\u5220\u9664\u4E91\u7AEF\u5907\u4EFD",
179
188
  syncVerSaved: "\u5DF2\u4FDD\u5B58\u5230",
180
- devMode: "\u5F00\u53D1\u6A21\u5F0F",
181
- 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",
182
189
  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",
183
190
  devReload: "\u91CD\u8F7D ZCode Pro",
184
191
  devReloading: "\u91CD\u8F7D\u4E2D\u2026",
@@ -286,13 +293,65 @@
286
293
  featureNewtabPanelsDesc: "\u5B98\u65B9\u201C\u65B0\u6807\u7B7E\u9875\u201D\u53EA\u5217\u5B98\u65B9\u9762\u677F\u4E0E\u63A8\u8350\u7684\u5B98\u65B9\u63D2\u4EF6\uFF0C\u6B64\u529F\u80FD\u628A\u7B2C\u4E09\u65B9\u5E02\u573A\u63D2\u4EF6\uFF08headroom\u3001rtk \u7B49\uFF09\u7684\u9762\u677F\u5361\u7247\u52A0\u8FDB\u53BB\uFF0C\u70B9\u51FB\u5728\u4FA7\u8FB9\u9762\u677F\u6253\u5F00\u3002",
287
294
  ntpDescSuffix: "\u63D2\u4EF6\u9762\u677F",
288
295
  ntpNeedSession: "\u5148\u6253\u5F00\u4E00\u4E2A\u4EFB\u52A1\u4F1A\u8BDD\uFF0C\u518D\u4ECE\u65B0\u6807\u7B7E\u9875\u6253\u5F00\u63D2\u4EF6\u9762\u677F",
289
- featureStatusBarDesc: "\u4E3B\u754C\u9762\u5E95\u90E8\u53F3\u4E0B\u89D2\u663E\u793A\u7EDF\u8BA1\u9879\uFF1A\u4E0A\u4E0B\u6587\u5360\u7528\u3001\u4F1A\u8BDD/\u4ECA\u65E5/\u7D2F\u8BA1 token\u3001\u4EFB\u52A1\u6570\u7B49\uFF1B\u5404\u9879\u7684\u987A\u5E8F\u4E0E\u663E\u9690\u5728\u201C\u6837\u5F0F \u2192 \u72B6\u6001\u680F\u201D\u91CC\u8C03\u6574\u3002",
290
- tabStylesPane: "\u6837\u5F0F\u8C03\u6574",
296
+ featureStatusBarDesc: "\u63D0\u95EE\u8F93\u5165\u6846\u53F3\u4E0B\u89D2\u548C\u6BCF\u8F6E\u56DE\u7B54\u6700\u540E\u663E\u793A\u7EDF\u8BA1\u9879\u3002",
297
+ tabStylesPane: "\u6837\u5F0F",
291
298
  tabStatusPane: "\u72B6\u6001\u680F",
299
+ themeColorsPane: "\u4E3B\u9898",
300
+ themeBgRegions: "\u5206\u533A\u80CC\u666F",
301
+ themeBgRegionFollow: "\u65E0\u80CC\u666F",
302
+ themeBgRegionLeft: "\u5DE6\u4FA7\u8FB9\u680F",
303
+ themeBgRegionCenter: "\u4E2D\u95F4\u533A\u57DF",
304
+ themeBgRegionRight: "\u53F3\u4FA7\u8FB9\u680F",
305
+ themeBgPane: "\u80CC\u666F\u56FE",
306
+ themeSkinTitle: "\u76AE\u80A4",
307
+ themeSkinDesc: "\u77E5\u540D\u7F16\u8F91\u5668\u4E3B\u9898\u914D\u8272\uFF0C\u81EA\u52A8\u5339\u914D ZCode \u7684\u6D45\u8272 / \u6DF1\u8272\u6A21\u5F0F\uFF1B\u9ED8\u8BA4\u4FDD\u6301 ZCode \u539F\u6837\uFF0C\u81EA\u5B9A\u4E49\u5728\u4E0B\u65B9\u81EA\u884C\u8C03\u8272\u3002",
308
+ themeSkinCustom: "\u81EA\u5B9A\u4E49",
309
+ themeCustomTitle: "\u81EA\u5B9A\u4E49\u4E3B\u9898",
310
+ themeCustomHint: "\u6539\u989C\u8272\u5373\u5B9E\u65F6\u751F\u6548\uFF08\u81EA\u52A8\u5207\u5230\u81EA\u5B9A\u4E49\u76AE\u80A4\uFF09\uFF1B\u6D45\u8272\u3001\u6DF1\u8272\u5404\u914D\u4E00\u6B21\uFF0C\u9009\u597D 5 \u4E2A\u989C\u8272\u5373\u53EF\u751F\u6210\u6574\u5957\u754C\u9762\u914D\u8272\u3002",
311
+ themeCustomLight: "\u6D45\u8272",
312
+ themeCustomDark: "\u6DF1\u8272",
313
+ themeCustomBg: "\u80CC\u666F",
314
+ themeCustomCard: "\u5361\u7247",
315
+ themeCustomFg: "\u6B63\u6587",
316
+ themeCustomMuted: "\u6B21\u7EA7\u6587\u5B57",
317
+ themeCustomAccent: "\u5F3A\u8C03\u8272",
318
+ themeBgDesc: "\u80CC\u666F\u6765\u6E90\u53EF\u9009\u672C\u5730\u56FE\u5E93\u3001\u6587\u4EF6\u5939\uFF08\u5B9A\u65F6\u8F6E\u6362\uFF09\u6216\u7F51\u5740\uFF08\u5B9A\u65F6\u5237\u65B0\uFF09\uFF0C\u5DE6\u680F / \u4E2D\u680F / \u53F3\u680F\u53EF\u5206\u533A\u72EC\u7ACB\u8BBE\u7F6E\uFF1B\u4FA7\u680F\u3001\u4F1A\u8BDD\u6D41\u3001\u8F93\u5165\u6846\u900F\u51FA\u80CC\u666F\uFF0C\u5F39\u7A97\u83DC\u5355\u4FDD\u6301\u4E0D\u900F\u660E\u4FDD\u53EF\u8BFB\u3002\u672C\u5730\u56FE\u5E93\u968F WebDAV \u540C\u6B65\u4E0A\u4F20\uFF08\u8986\u76D6\u5F0F\uFF0C\u4E0D\u7559\u5386\u53F2\u7248\u672C\uFF09\u3002",
319
+ themeBgModeNone: "\u65E0\u80CC\u666F",
320
+ themeBgModeLibrary: "\u672C\u5730\u56FE\u5E93",
321
+ themeBgModeFolder: "\u6587\u4EF6\u5939",
322
+ themeBgModeUrl: "\u7F51\u5740",
323
+ themeBgLibrary: "\u6211\u7684\u56FE\u5E93",
324
+ themeBgUpload: "\u4E0A\u4F20\u56FE\u7247",
325
+ themeBgEmpty: "\u8FD8\u6CA1\u6709\u4E0A\u4F20\u56FE\u7247\uFF0C\u652F\u6301 png / jpg / webp / gif / bmp / avif\uFF0C\u5355\u5F20 10MB \u4EE5\u5185",
326
+ themeBgDelete: "\u5220\u9664",
327
+ themeBgNoneHint: "\u9009\u62E9\u4E00\u79CD\u80CC\u666F\u6765\u6E90\uFF1B\u6587\u4EF6\u5939\u53EF\u5B9A\u65F6\u8F6E\u6362\uFF0C\u7F51\u5740\u53EF\u914D\u5408\u968F\u673A\u56FE API \u5B9A\u65F6\u5237\u65B0\u3002",
328
+ themeBgFolderPick: "\u6D4F\u89C8",
329
+ themeBgFolderPickTitle: "\u9009\u62E9\u80CC\u666F\u56FE\u6587\u4EF6\u5939",
330
+ themeBgFolderHint: "\u9009\u62E9\u4E00\u4E2A\u88C5\u58C1\u7EB8\u7684\u6587\u4EF6\u5939\uFF0C\u56FE\u7247\u539F\u5730\u8BFB\u53D6\u4E0D\u590D\u5236\u3002",
331
+ themeBgFolderRandom: "\u968F\u673A\u987A\u5E8F",
332
+ themeBgIntervalHint: "\u5207\u6362\u95F4\u9694\uFF08\u5206\u949F\uFF09",
333
+ themeBgIntervalUnit: "\u5206\u949F\uFF0C0 = \u4E0D\u5207\u6362",
334
+ themeBgFolderCount: "\u6587\u4EF6\u5939\u91CC\u6709 {n} \u5F20\u56FE\u7247\uFF0C\u5B9A\u65F6\u8F6E\u6362\u4E2D\u3002",
335
+ themeBgFolderMissing: "\u6587\u4EF6\u5939\u4E0D\u5B58\u5728\u6216\u91CC\u9762\u6CA1\u6709\u56FE\u7247\u3002",
336
+ themeBgUrlHint: "\u5B9A\u65F6\u5237\u65B0\u9002\u5408\u968F\u673A\u56FE API\uFF08\u6BCF\u6B21\u8BF7\u6C42\u8FD4\u56DE\u4E0D\u540C\u56FE\uFF09\uFF1B\u5237\u65B0\u65F6\u5E26\u7F13\u5B58\u53C2\u6570\u907F\u514D\u547D\u4E2D\u7F13\u5B58\u3002",
337
+ themeBgUrlIntervalHint: "\u5237\u65B0\u95F4\u9694\uFF08\u5206\u949F\uFF09",
338
+ themeBgUrlInvalid: "\u7F51\u5740\u9700\u4EE5 http(s):// \u5F00\u5934",
339
+ themeBgFit: "\u7F29\u653E\u65B9\u5F0F",
340
+ themeBgFitCover: "\u586B\u6EE1",
341
+ themeBgFitContain: "\u9002\u5E94",
342
+ themeBgFitFill: "\u62C9\u4F38",
343
+ themeBgFitTile: "\u5E73\u94FA",
344
+ themeBgMask: "\u906E\u7F69\u4E0D\u900F\u660E\u5EA6",
345
+ themeBgMaskHint: "\u6570\u503C\u8D8A\u9AD8\uFF0C\u56FE\u7247\u8D8A\u5411\u4E3B\u9898\u80CC\u666F\u8272\u6DE1\u5316\uFF0C\u6587\u5B57\u5BF9\u6BD4\u8D8A\u6E05\u6670",
346
+ themeBgMaskHint2: "\u56FE\u7247\u5411\u4E3B\u9898\u80CC\u666F\u8272\u6DE1\u5316\u7684\u7A0B\u5EA6",
347
+ themeBgBlur: "\u56FE\u7247\u6A21\u7CCA",
348
+ themeBgPanel: "\u9762\u677F\u4E0D\u900F\u660E\u5EA6",
349
+ themeBgPanelHint: "\u4FA7\u680F\u3001\u9762\u677F\u4E0E\u6807\u7B7E\u680F\u7684\u4E0D\u900F\u660E\u7A0B\u5EA6\u3002\u8D8A\u4F4E\uFF0C\u900F\u51FA\u7684\u56FE\u7247\u8D8A\u591A\u3002",
350
+ themeBgDeletedActive: "\u5F53\u524D\u80CC\u666F\u5DF2\u5220\u9664\uFF0C\u5DF2\u81EA\u52A8\u505C\u7528\u80CC\u666F",
292
351
  sbItemCtx: "\u4E0A\u4E0B\u6587\u5360\u7528",
293
352
  sbItemQuestions: "\u63D0\u95EE\u6B21\u6570",
294
353
  sbMaster: "\u542F\u7528\u7EDF\u8BA1",
295
- sbMasterDesc: "\u603B\u5F00\u5173\uFF1A\u5173\u95ED\u540E\u5E95\u90E8\u72B6\u6001\u680F\u4E0E\u6BCF\u8F6E\u56DE\u7B54\u7684\u7528\u91CF\u6807\u6CE8\u4E00\u8D77\u505C\u7528\u3002",
354
+ sbMasterDesc: "\u603B\u5F00\u5173\uFF1A\u5173\u95ED\u540E\u8F93\u5165\u6846\u53F3\u4E0B\u89D2\u7684\u7EDF\u8BA1\u884C\u4E0E\u6BCF\u8F6E\u56DE\u7B54\u7684\u7528\u91CF\u6807\u6CE8\u4E00\u8D77\u505C\u7528\u3002",
296
355
  sbGroupSession: "\u4F1A\u8BDD\u7EDF\u8BA1\uFF08\u8DDF\u968F\u5F53\u524D\u4F1A\u8BDD\uFF09",
297
356
  sbGroupGlobal: "\u603B\u7EDF\u8BA1",
298
357
  sbGroupTurn: "\u6BCF\u8F6E\u7EDF\u8BA1\uFF08\u56DE\u7B54\u672B\u5C3E\uFF09",
@@ -505,7 +564,7 @@
505
564
  aliasMigrateDesc: "\u5F53\u524D ZCode \u5DF2\u652F\u6301\u4FEE\u6539\u9879\u76EE\u540D\u79F0\uFF08\u9879\u76EE\u201C\u66F4\u591A\u201D\u83DC\u5355 \u2192 \u7F16\u8F91\u9879\u76EE\uFF09\uFF0C\u81EA\u5B9A\u4E49\u522B\u540D\u529F\u80FD\u5DF2\u505C\u7528\u3002\u53EF\u628A\u5DF2\u4FDD\u5B58\u7684\u522B\u540D\u5199\u5165\u5B98\u65B9\u9879\u76EE\u540D\u79F0\uFF0C\u52FE\u9009\u8981\u8FC1\u79FB\u7684\u9879\u76EE\uFF1B\u8FC1\u79FB\u53EA\u6539\u9879\u76EE\u540D\u79F0\uFF0C\u78C1\u76D8\u76EE\u5F55\u4E0D\u53D7\u5F71\u54CD\u3002",
506
565
  aliasMigrateItemHint: "\u8FC1\u79FB\u540E\u5C06\u4F5C\u4E3A\u9879\u76EE\u540D\u79F0\u4FDD\u5B58\uFF08\u53EF\u968F\u65F6\u5728\u201C\u7F16\u8F91\u9879\u76EE\u201D\u4E2D\u518D\u6539\uFF09\u3002\u4EC5\u65E0\u6807\u8BB0\u7684\u6761\u76EE\u53EF\u52FE\u9009\uFF1B\u5DF2\u8FC1\u79FB\u3001\u5DF2\u5E76\u5165\u3001\u9879\u76EE\u5DF2\u5220\u9664\u3001\u672C\u673A\u4E0D\u5B58\u5728\uFF08\u65E7\u673A\u5668\u8DEF\u5F84\uFF09\u4EE5\u53CA\u9879\u76EE\u5DF2\u91CD\u547D\u540D\u7684\u90FD\u4E0D\u53EF\u52FE\u9009\u2014\u2014\u8981\u6539\u8FD9\u4E9B\u9879\u76EE\u7684\u540D\u5B57\uFF0C\u76F4\u63A5\u7528\u9879\u76EE\u201C\u66F4\u591A\u201D\u83DC\u5355\u91CC\u7684\u201C\u7F16\u8F91\u9879\u76EE\u201D\u3002",
507
566
  aliasMigrateStateDone: "\u5DF2\u8FC1\u79FB",
508
- aliasMigrateStateMerged: "\u5DF2\u5E76\u5165\u300C{name}\u300D",
567
+ aliasMigrateStateMerged: "\u5DF2\u5E76\u5165\u201C{name}\u201D",
509
568
  aliasMigrateStateDeleted: "\u9879\u76EE\u5DF2\u5220\u9664",
510
569
  aliasMigrateStateMissing: "\u672C\u673A\u4E0D\u5B58\u5728",
511
570
  aliasMigrateStateRenamed: "\u9879\u76EE\u5DF2\u91CD\u547D\u540D",
@@ -552,7 +611,7 @@
552
611
  var en = {
553
612
  settingsTitle: "ZCode Pro",
554
613
  tabFeatures: "Features",
555
- tabStyles: "Styles",
614
+ tabStyles: "Appearance",
556
615
  tabProxy: "Proxy",
557
616
  tabBehavior: "Behavior",
558
617
  tabAgents: "Global Prompt",
@@ -633,8 +692,6 @@
633
692
  syncVerDeleteDesc: "This backup will be deleted from the server and cannot be recovered: ",
634
693
  syncVerDeleted: "Backup deleted from the server.",
635
694
  syncVerSaved: "Saved to",
636
- devMode: "Developer mode",
637
- devModeRowDesc: 'Shows the "Reload ZCode Pro" button; can also be enabled with the ZCODEPRO_DEV=1 environment variable. This tab is visible only while developer mode is active.',
638
695
  devModeDesc: 'For development: after changing zcode-pro code, click "Reload ZCode Pro" instead of restarting ZCode. It builds automatically (when running from the source repo), restarts the ZCode Pro launcher process (fresh helper and injected script), and every ZCode window refreshes and re-injects. The ZCode app itself and all sessions stay untouched.',
639
696
  devReload: "Reload ZCode Pro",
640
697
  devReloading: "Reloading\u2026",
@@ -742,13 +799,65 @@
742
799
  featureNewtabPanelsDesc: 'The built-in "new tab page" only lists built-in panels and recommended official plugins; this adds cards for third-party marketplace plugin panels (headroom, rtk, \u2026) that open in the side pane.',
743
800
  ntpDescSuffix: "plugin panel",
744
801
  ntpNeedSession: "Open a task session first, then open plugin panels from the new tab page",
745
- featureStatusBarDesc: 'Shows statistics at the bottom-right of the main view: context usage, session/today/total tokens, task count, etc.; reorder or hide each item under "Styles \u2192 Status bar".',
802
+ featureStatusBarDesc: "Shows statistics at the bottom-right of the input box and at the end of each answer.",
746
803
  tabStylesPane: "Styles",
747
804
  tabStatusPane: "Status bar",
805
+ themeColorsPane: "Theme",
806
+ themeBgRegions: "Regional backgrounds",
807
+ themeBgRegionFollow: "None",
808
+ themeBgRegionLeft: "Left sidebar",
809
+ themeBgRegionCenter: "Center area",
810
+ themeBgRegionRight: "Right sidebar",
811
+ themeBgPane: "Background",
812
+ themeSkinTitle: "Skins",
813
+ themeSkinDesc: 'Famous editor theme palettes, auto-matching ZCode\u2019s light / dark mode; "Default" keeps ZCode as-is, "Custom" is tuned below.',
814
+ themeSkinCustom: "Custom",
815
+ themeCustomTitle: "Custom theme",
816
+ themeCustomHint: "Color changes apply live (auto-switches to the custom skin); set light and dark separately \u2014 pick 5 colors and the whole palette is derived.",
817
+ themeCustomLight: "Light",
818
+ themeCustomDark: "Dark",
819
+ themeCustomBg: "Background",
820
+ themeCustomCard: "Card",
821
+ themeCustomFg: "Text",
822
+ themeCustomMuted: "Muted text",
823
+ themeCustomAccent: "Accent",
824
+ themeBgDesc: "Sources: local library, folder (rotate on a timer) or URL (refresh on a timer); left / center / right regions can each have their own. Sidebar, chat and composer reveal the background while menus and dialogs stay opaque. The library uploads with the zcode-pro sync group (overwrite, no history versions).",
825
+ themeBgModeNone: "None",
826
+ themeBgModeLibrary: "Library",
827
+ themeBgModeFolder: "Folder",
828
+ themeBgModeUrl: "URL",
829
+ themeBgLibrary: "My library",
830
+ themeBgUpload: "Upload",
831
+ themeBgEmpty: "No images yet. png / jpg / webp / gif / bmp / avif, up to 10MB each",
832
+ themeBgDelete: "Delete",
833
+ themeBgNoneHint: "Pick a background source; folders rotate on a timer, URLs refresh on a timer for random-image APIs.",
834
+ themeBgFolderPick: "Browse",
835
+ themeBgFolderPickTitle: "Choose a wallpaper folder",
836
+ themeBgFolderHint: "Pick a folder of wallpapers; images are read in place, not copied.",
837
+ themeBgFolderRandom: "Random order",
838
+ themeBgIntervalHint: "Switch interval (minutes)",
839
+ themeBgIntervalUnit: "min, 0 = off",
840
+ themeBgFolderCount: "{n} images in the folder, rotating on the timer.",
841
+ themeBgFolderMissing: "Folder missing or contains no images.",
842
+ themeBgUrlHint: "Timed refresh suits random-image APIs (each request returns a new image); a cache-buster is appended on refresh.",
843
+ themeBgUrlIntervalHint: "Refresh interval (minutes)",
844
+ themeBgUrlInvalid: "URL must start with http(s)://",
845
+ themeBgFit: "Fit",
846
+ themeBgFitCover: "Fill",
847
+ themeBgFitContain: "Fit",
848
+ themeBgFitFill: "Stretch",
849
+ themeBgFitTile: "Tile",
850
+ themeBgMask: "Mask opacity",
851
+ themeBgMaskHint: "Higher values fade the image toward the theme background color for clearer text",
852
+ themeBgMaskHint2: "How much the image fades toward the theme background color",
853
+ themeBgBlur: "Image blur",
854
+ themeBgPanel: "Panel opacity",
855
+ themeBgPanelHint: "Opacity of sidebar, panels and tab bar. Lower reveals more of the image.",
856
+ themeBgDeletedActive: "Active background was deleted; background disabled",
748
857
  sbItemCtx: "Context usage",
749
858
  sbItemQuestions: "Questions asked",
750
859
  sbMaster: "Enable statistics",
751
- sbMasterDesc: "Master switch: turning this off disables both the status bar and per-turn labels.",
860
+ sbMasterDesc: "Master switch: turning this off disables both the input-box statistics and per-turn labels.",
752
861
  sbGroupSession: "Session statistics (follows current session)",
753
862
  sbGroupGlobal: "Global statistics",
754
863
  sbGroupTurn: "Per-turn statistics (answer rows)",
@@ -1159,8 +1268,8 @@
1159
1268
  clearTimeout(toastTimer);
1160
1269
  toastTimer = setTimeout(() => el.remove(), kind === "error" ? 5e3 : 2600);
1161
1270
  }
1162
- var overlayClass = "fixed inset-0 isolate z-50 flex items-center justify-center bg-black/60 supports-backdrop-filter:backdrop-blur-xs duration-100 p-4 platform-linux-desktop:top-12";
1163
- var overlayClassBare = "fixed inset-0 isolate z-50 flex items-center justify-center duration-100 p-4 platform-linux-desktop:top-12";
1271
+ var overlayClass = "fixed inset-0 isolate z-50 flex items-start justify-center bg-black/60 supports-backdrop-filter:backdrop-blur-xs duration-100 p-4 platform-linux-desktop:top-12";
1272
+ var overlayClassBare = "fixed inset-0 isolate z-50 flex items-start justify-center duration-100 p-4 platform-linux-desktop:top-12";
1164
1273
  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";
1165
1274
  var dialogStack = [];
1166
1275
  function openDialog({ title, description, onMount, onClose, width = "sm:max-w-md", overlay = "dim", draggable = false, posKey = "", dismissOnOutside = true, bodyClass = "", closable = false }) {
@@ -1188,7 +1297,8 @@
1188
1297
  const content = h("div", {
1189
1298
  role: "dialog",
1190
1299
  "aria-modal": "true",
1191
- class: contentClass.replace("sm:max-w-md", width) + " zcodepro-card"
1300
+ class: contentClass.replace("sm:max-w-md", width) + " zcodepro-card",
1301
+ style: "margin-top:12vh;max-height:calc(88vh - 3rem);overflow-y:auto;"
1192
1302
  });
1193
1303
  const overlayEl = h("div", {
1194
1304
  "data-overlay": overlay,
@@ -1604,6 +1714,32 @@
1604
1714
  color: var(--color-foreground, #111);
1605
1715
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
1606
1716
  }
1717
+ /* \u4E3B\u9898\uFF08\u6837\u5F0F \u2192 \u4E3B\u9898\uFF09\uFF1A\u76AE\u80A4\u5361\u7247\uFF08\u8272\u5757\u9884\u89C8 + \u540D\u79F0\uFF09\u4E0E\u80CC\u666F\u7F29\u7565\u56FE */
1718
+ .zcodepro-skin-card {
1719
+ display: flex; flex-direction: column; gap: 6px;
1720
+ padding: 8px; border: 1px solid var(--color-card-border, #8883);
1721
+ border-radius: 10px; background: transparent; cursor: pointer; text-align: left;
1722
+ transition: border-color 0.15s ease, background-color 0.15s ease;
1723
+ }
1724
+ .zcodepro-skin-card:hover { background-color: color-mix(in oklab, var(--color-foreground, #888) 6%, transparent); }
1725
+ .zcodepro-skin-card[data-state="active"] { border-color: var(--color-primary, #333); }
1726
+ .zcodepro-skin-swatches { display: flex; height: 18px; border-radius: 5px; overflow: hidden; }
1727
+ .zcodepro-skin-swatches > span { flex: 1; }
1728
+ .zcodepro-bg-card {
1729
+ position: relative; width: 100%; aspect-ratio: 16 / 9;
1730
+ border-radius: 8px; border: 2px solid transparent; padding: 0;
1731
+ background-size: cover; background-position: center; cursor: pointer; overflow: hidden;
1732
+ }
1733
+ .zcodepro-bg-card:hover { filter: brightness(1.15); }
1734
+ .zcodepro-bg-card[data-state="active"] { border-color: var(--color-primary, #333); }
1735
+ .zcodepro-bg-card img { width: 100%; height: 100%; object-fit: cover; display: block; }
1736
+ .zcodepro-bg-del {
1737
+ position: absolute; top: 4px; right: 4px; width: 20px; height: 20px;
1738
+ border-radius: 6px; border: none; cursor: pointer; font-size: 11px; line-height: 1;
1739
+ color: #fff; background: rgba(0, 0, 0, 0.55);
1740
+ display: flex; align-items: center; justify-content: center;
1741
+ }
1742
+ .zcodepro-bg-del:hover { background: rgba(220, 38, 38, 0.85); }
1607
1743
  /* \u7D27\u51D1\u5C0F\u6309\u94AE\uFF1A\u51E0\u4F55/\u914D\u8272\u5199\u5165\u81EA\u6709\u89C4\u5219\u5E76\u53D6\u4E3B\u9898\u53D8\u91CF\uFF08\u9AD8\u5EA6=24px\uFF0C\u4E0E\u6807\u7B7E\u9875\u4E00\u81F4\uFF09 */
1608
1744
  .zcodepro-btn-sm {
1609
1745
  display: inline-flex;
@@ -1691,6 +1827,8 @@
1691
1827
  -webkit-appearance: none;
1692
1828
  appearance: none;
1693
1829
  width: 100%;
1830
+ /* \u4E0D\u7ED9 padding\uFF1Apadding \u4F1A\u7F29\u5C0F thumb \u7684\u53EF\u6ED1\u533A\u95F4\uFF08\u62D6\u4E0D\u5230\u6700\u5DE6/\u6700\u53F3\uFF09\u3002
1831
+ \u8F68\u9053\u8D77\u70B9\u5BF9\u9F50\u7531\u5F39\u7A97\u91CC\u7684\u56FA\u5B9A\u6807\u7B7E\u5217\u5BBD\u8D1F\u8D23 */
1694
1832
  height: 4px;
1695
1833
  border-radius: 9999px;
1696
1834
  background: color-mix(in oklab, var(--color-foreground, #888) 18%, transparent);
@@ -3806,6 +3944,478 @@
3806
3944
  if (hlEl) hlEl.textContent = "";
3807
3945
  }
3808
3946
 
3947
+ // src/inject/features/theme.js
3948
+ var SC = (light, dark, lightA, darkA) => ({ light, dark, lightA, darkA });
3949
+ var SCALES = {
3950
+ dracula: SC(
3951
+ ["#fefeff", "#f5f4f9", "#f1f0f6", "#edebf4", "#dedee4", "#c4c3cb", "#aaa9b3", "#885cdb", "#9a6ff0", "#af85ff", "#5f5c77", "#2b2a3a"],
3952
+ ["#21222c", "#282a36", "#2d303d", "#343746", "#484b59", "#656873", "#80828a", "#aa80e5", "#bd93f9", "#d3a9ff", "#7b89b3", "#f8f8f2"],
3953
+ ["#2b2a3a03", "#2b2a3a06", "#2b2a3a0f", "#2b2a3a17", "#2b2a3a1f", "#2b2a3a26", "#2b2a3a31", "#2b2a3a44", "#2b2a3a72", "#2b2a3a7c", "#2b2a3a9b", "#2b2a3adf"],
3954
+ ["#f8f8f203", "#f8f8f206", "#f8f8f20f", "#f8f8f217", "#f8f8f21f", "#f8f8f226", "#f8f8f231", "#f8f8f244", "#f8f8f272", "#f8f8f27c", "#f8f8f29b", "#f8f8f2df"]
3955
+ ),
3956
+ nord: SC(
3957
+ ["#fbfcfe", "#f2f5f9", "#eef2f7", "#eaeff5", "#dce0e5", "#c3c7cd", "#aaaeb6", "#4d6f99", "#5e81ac", "#7296c2", "#576377", "#2e3440"],
3958
+ ["#272c36", "#2e3440", "#343a48", "#3b4252", "#4e5564", "#686e7c", "#808692", "#75adbd", "#88c0d0", "#9ed7e7", "#8e9ab0", "#eceff4"],
3959
+ ["#2e344003", "#2e344006", "#2e34400f", "#2e344017", "#2e34401f", "#2e344026", "#2e344031", "#2e344044", "#2e344072", "#2e34407c", "#2e34409b", "#2e3440df"],
3960
+ ["#eceff403", "#eceff406", "#eceff40f", "#eceff417", "#eceff41f", "#eceff426", "#eceff431", "#eceff444", "#eceff472", "#eceff47c", "#eceff49b", "#eceff4df"]
3961
+ ),
3962
+ tokyonight: SC(
3963
+ ["#fafbfe", "#eef0f6", "#e9ebf2", "#e3e6ef", "#d9dce5", "#c0c5d1", "#a8aebd", "#2d488e", "#3d59a1", "#506eb8", "#5a6684", "#2f3a55"],
3964
+ ["#13141d", "#1a1b26", "#1e212f", "#24283b", "#34394f", "#4b516a", "#606784", "#688fe3", "#7aa2f7", "#8fb8ff", "#8893c3", "#c0caf5"],
3965
+ ["#2f3a5503", "#2f3a5506", "#2f3a550f", "#2f3a5517", "#2f3a551f", "#2f3a5526", "#2f3a5531", "#2f3a5544", "#2f3a5572", "#2f3a557c", "#2f3a559b", "#2f3a55df"],
3966
+ ["#c0caf503", "#c0caf506", "#c0caf50f", "#c0caf517", "#c0caf51f", "#c0caf526", "#c0caf531", "#c0caf544", "#c0caf572", "#c0caf57c", "#c0caf59b", "#c0caf5df"]
3967
+ ),
3968
+ catppuccin: SC(
3969
+ ["#fbfcfe", "#eff1f5", "#eaecf1", "#e4e7ed", "#dde0e6", "#c8cbd5", "#b4b7c3", "#6174e8", "#7287fd", "#869dff", "#73768c", "#4c4f69"],
3970
+ ["#181825", "#1e1e2e", "#262738", "#313244", "#424357", "#595c71", "#6e7289", "#a1abea", "#b4befe", "#cad5ff", "#979cb5", "#cdd6f4"],
3971
+ ["#4c4f6903", "#4c4f6906", "#4c4f690f", "#4c4f6917", "#4c4f691f", "#4c4f6926", "#4c4f6931", "#4c4f6944", "#4c4f6972", "#4c4f697c", "#4c4f699b", "#4c4f69df"],
3972
+ ["#cdd6f403", "#cdd6f406", "#cdd6f40f", "#cdd6f417", "#cdd6f41f", "#cdd6f426", "#cdd6f431", "#cdd6f444", "#cdd6f472", "#cdd6f47c", "#cdd6f49b", "#cdd6f4df"]
3973
+ ),
3974
+ gruvbox: SC(
3975
+ ["#fefbf0", "#f8f2dd", "#f5eed4", "#f3eacc", "#e3decb", "#cbc5b5", "#b3aea0", "#8e6b00", "#a07d0a", "#b6922e", "#706558", "#3c3836"],
3976
+ ["#1d2021", "#282828", "#312f2e", "#3c3836", "#4f4944", "#696256", "#807867", "#c38600", "#d79921", "#eeaf40", "#a1927f", "#ebdbb2"],
3977
+ ["#3c383603", "#3c383606", "#3c38360f", "#3c383617", "#3c38361f", "#3c383626", "#3c383631", "#3c383644", "#3c383672", "#3c38367c", "#3c38369b", "#3c3836df"],
3978
+ ["#ebdbb203", "#ebdbb206", "#ebdbb20f", "#ebdbb217", "#ebdbb21f", "#ebdbb226", "#ebdbb231", "#ebdbb244", "#ebdbb272", "#ebdbb27c", "#ebdbb29b", "#ebdbb2df"]
3979
+ ),
3980
+ onedark: SC(
3981
+ ["#fcfdff", "#f4f6fa", "#f0f3f8", "#edf0f6", "#dfe1e6", "#c6c8ce", "#adb0b7", "#3870c4", "#4a82d8", "#5f98f0", "#606774", "#333842"],
3982
+ ["#21252b", "#282c34", "#2d313a", "#333842", "#404550", "#525863", "#626874", "#4e9cdb", "#61afef", "#77c6ff", "#6a717e", "#abb2bf"],
3983
+ ["#33384203", "#33384206", "#3338420f", "#33384217", "#3338421f", "#33384226", "#33384231", "#33384244", "#33384272", "#3338427c", "#3338429b", "#333842df"],
3984
+ ["#abb2bf03", "#abb2bf06", "#abb2bf0f", "#abb2bf17", "#abb2bf1f", "#abb2bf26", "#abb2bf31", "#abb2bf44", "#abb2bf72", "#abb2bf7c", "#abb2bf9b", "#abb2bfdf"]
3985
+ ),
3986
+ solarized: SC(
3987
+ ["#fffbf0", "#fdf6e3", "#fcf4dd", "#fbf1d7", "#ebe8d8", "#d7d7ca", "#c2c6bc", "#0078be", "#268bd2", "#41a1ea", "#889899", "#586e75"],
3988
+ ["#00212b", "#002b36", "#03303b", "#073642", "#1b424d", "#31525b", "#446168", "#008e86", "#2aa198", "#47b7ae", "#62777d", "#93a1a1"],
3989
+ ["#586e7503", "#586e7506", "#586e750f", "#586e7517", "#586e751f", "#586e7526", "#586e7531", "#586e7544", "#586e7572", "#586e757c", "#586e759b", "#586e75df"],
3990
+ ["#93a1a103", "#93a1a106", "#93a1a10f", "#93a1a117", "#93a1a11f", "#93a1a126", "#93a1a131", "#93a1a144", "#93a1a172", "#93a1a17c", "#93a1a19b", "#93a1a1df"]
3991
+ ),
3992
+ rosepine: SC(
3993
+ ["#fffaf3", "#faf4ed", "#f8f1ea", "#f6efe8", "#e8e3e1", "#d3ced3", "#bfbac5", "#7e6896", "#907aa9", "#a58fbf", "#837d93", "#575279"],
3994
+ ["#141220", "#191724", "#1c1a28", "#1f1d2e", "#323143", "#4e4d60", "#69677b", "#b194d3", "#c4a7e7", "#dbbdff", "#817e99", "#e0def4"],
3995
+ ["#57527903", "#57527906", "#5752790f", "#57527917", "#5752791f", "#57527926", "#57527931", "#57527944", "#57527972", "#5752797c", "#5752799b", "#575279df"],
3996
+ ["#e0def403", "#e0def406", "#e0def40f", "#e0def417", "#e0def41f", "#e0def426", "#e0def431", "#e0def444", "#e0def472", "#e0def47c", "#e0def49b", "#e0def4df"]
3997
+ ),
3998
+ everforest: SC(
3999
+ ["#fefdf5", "#faf7ec", "#f8f4e8", "#f6f2e4", "#e7e6dd", "#d2d2cb", "#bcbeb9", "#588c47", "#6a9e59", "#7fb46e", "#76827b", "#4f585e"],
4000
+ ["#272e33", "#2d353b", "#30393f", "#343f44", "#454e50", "#5d625f", "#73756d", "#95ad6e", "#a7c080", "#bdd796", "#a7aea2", "#d3c6aa"],
4001
+ ["#4f585e03", "#4f585e06", "#4f585e0f", "#4f585e17", "#4f585e1f", "#4f585e26", "#4f585e31", "#4f585e44", "#4f585e72", "#4f585e7c", "#4f585e9b", "#4f585edf"],
4002
+ ["#d3c6aa03", "#d3c6aa06", "#d3c6aa0f", "#d3c6aa17", "#d3c6aa1f", "#d3c6aa26", "#d3c6aa31", "#d3c6aa44", "#d3c6aa72", "#d3c6aa7c", "#d3c6aa9b", "#d3c6aadf"]
4003
+ ),
4004
+ nightowl: SC(
4005
+ ["#fdfdfe", "#f6f7f9", "#f3f4f7", "#f0f2f5", "#e2e3e7", "#cbcbd2", "#b3b4bd", "#2253c2", "#3366d6", "#477cee", "#64667c", "#403f53"],
4006
+ ["#010e1c", "#011627", "#072033", "#122d42", "#284054", "#445a6e", "#5f7285", "#7097ea", "#82aaff", "#97c1ff", "#778ea7", "#d6deeb"],
4007
+ ["#403f5303", "#403f5306", "#403f530f", "#403f5317", "#403f531f", "#403f5326", "#403f5331", "#403f5344", "#403f5372", "#403f537c", "#403f539b", "#403f53df"],
4008
+ ["#d6deeb03", "#d6deeb06", "#d6deeb0f", "#d6deeb17", "#d6deeb1f", "#d6deeb26", "#d6deeb31", "#d6deeb44", "#d6deeb72", "#d6deeb7c", "#d6deeb9b", "#d6deebdf"]
4009
+ ),
4010
+ kanagawa: SC(
4011
+ ["#fcf9f0", "#f4f1e8", "#f0ede4", "#edeae1", "#e1ded5", "#cac8c0", "#b4b2aa", "#2d4d95", "#3d5fa8", "#5074bf", "#72726a", "#43443e"],
4012
+ ["#16161d", "#1f1f28", "#24242f", "#2a2a37", "#3c3c46", "#57565a", "#6f6d6c", "#6c8ac4", "#7e9cd8", "#93b2ef", "#848277", "#dcd7ba"],
4013
+ ["#43443e03", "#43443e06", "#43443e0f", "#43443e17", "#43443e1f", "#43443e26", "#43443e31", "#43443e44", "#43443e72", "#43443e7c", "#43443e9b", "#43443edf"],
4014
+ ["#dcd7ba03", "#dcd7ba06", "#dcd7ba0f", "#dcd7ba17", "#dcd7ba1f", "#dcd7ba26", "#dcd7ba31", "#dcd7ba44", "#dcd7ba72", "#dcd7ba7c", "#dcd7ba9b", "#dcd7badf"]
4015
+ )
4016
+ };
4017
+ var THEME_SKINS = [
4018
+ {
4019
+ id: "default",
4020
+ zh: "\u9ED8\u8BA4",
4021
+ en: "Default",
4022
+ preview: ["#f8f8f8", "#ffffff", "#ececee", "#dfdfdf", "#8b8b8b", "#18181b", "#9a9a9a", "#18181b"]
4023
+ },
4024
+ { id: "dracula", hue: "dracula", zh: "\u5FB7\u53E4\u62C9", en: "Dracula" },
4025
+ { id: "nord", hue: "nord", zh: "\u6781\u591C", en: "Nord" },
4026
+ { id: "tokyonight", hue: "tokyonight", zh: "\u4E1C\u4EAC\u591C", en: "Tokyo Night" },
4027
+ { id: "catppuccin", hue: "catppuccin", zh: "\u5976\u5496", en: "Catppuccin" },
4028
+ { id: "gruvbox", hue: "gruvbox", zh: "\u6696\u5CA9", en: "Gruvbox" },
4029
+ { id: "onedark", hue: "onedark", zh: "One Dark", en: "One Dark" },
4030
+ { id: "solarized", hue: "solarized", zh: "\u65E5\u6652", en: "Solarized" },
4031
+ { id: "rosepine", hue: "rosepine", zh: "\u73AB\u7470\u677E", en: "Ros\xE9 Pine" },
4032
+ { id: "everforest", hue: "everforest", zh: "\u5E38\u7EFF", en: "Everforest" },
4033
+ { id: "nightowl", hue: "nightowl", zh: "\u591C\u67AD", en: "Night Owl" },
4034
+ { id: "kanagawa", hue: "kanagawa", zh: "\u795E\u5948\u5DDD", en: "Kanagawa" }
4035
+ ];
4036
+ for (const s of THEME_SKINS) {
4037
+ if (!s.hue) continue;
4038
+ const L = SCALES[s.hue].light;
4039
+ s.preview = [L[1], L[0], L[3], L[5], L[8], L[9], L[10], L[11]];
4040
+ }
4041
+ var THEME_SKIN_IDS = THEME_SKINS.map((s) => s.id);
4042
+ var hexA = (hex, alphaHex) => `#${hex.slice(1)}${alphaHex}`;
4043
+ function skinVars(s, isDark) {
4044
+ if (!s) return null;
4045
+ const fg = s[11];
4046
+ return {
4047
+ "--color-background": s[1],
4048
+ "--color-background-win-alt": s[3],
4049
+ "--color-background-alt": `color-mix(in oklab, ${s[1]} ${isDark ? 60 : 70}%, transparent)`,
4050
+ "--color-sidebar": s[isDark ? 1 : 2],
4051
+ "--color-header": s[isDark ? 2 : 0],
4052
+ "--color-panel": s[isDark ? 2 : 0],
4053
+ "--color-card": s[isDark ? 3 : 0],
4054
+ "--color-card-selected": s[isDark ? 4 : 1],
4055
+ "--color-input": s[isDark ? 3 : 0],
4056
+ "--color-input-focused": s[isDark ? 3 : 0],
4057
+ "--color-menu": s[isDark ? 3 : 0],
4058
+ "--color-menu-hover": s[isDark ? 4 : 2],
4059
+ "--color-popover": s[isDark ? 3 : 0],
4060
+ "--color-popover-header": s[isDark ? 2 : 1],
4061
+ "--color-highspeed-composer-surface": s[isDark ? 0 : 1],
4062
+ "--color-tab-active": s[isDark ? 1 : 0],
4063
+ "--color-foreground": fg,
4064
+ "--color-foreground-subtle": s[10],
4065
+ "--color-foreground-subtlest": `color-mix(in oklab, ${s[10]} 60%, transparent)`,
4066
+ "--color-foreground-inverse": isDark ? "#101010" : "#ffffff",
4067
+ "--color-popover-foreground": fg,
4068
+ "--color-tooltip-foreground": fg,
4069
+ "--color-brand": fg,
4070
+ "--color-primary": fg,
4071
+ "--color-primary-foreground": isDark ? "#101010" : "#ffffff",
4072
+ "--color-accent": s[2],
4073
+ "--color-highspeed-accent": s[8],
4074
+ "--highspeed-sidebar-model-icon": s[8],
4075
+ "--highspeed-sidebar-model-gradient": `linear-gradient(90deg,${s[7]} 0%,${s[8]} 50%,${s[9]} 100%)`,
4076
+ "--color-hover": hexA(fg, isDark ? "12" : "0f"),
4077
+ "--color-surface-hover": hexA(fg, isDark ? "12" : "0f"),
4078
+ "--color-border": hexA(fg, "1a"),
4079
+ "--color-border-hover": hexA(fg, "26"),
4080
+ "--color-card-border": hexA(fg, "1a"),
4081
+ "--color-input-border": hexA(fg, "1a"),
4082
+ "--color-input-border-hover": hexA(fg, "26"),
4083
+ "--color-input-border-focused": hexA(fg, "26"),
4084
+ "--color-popover-border": hexA(fg, "1a"),
4085
+ "--color-tab-border": hexA(fg, "1a"),
4086
+ "--color-highspeed-composer-border-pre-animation": hexA(fg, "1a")
4087
+ };
4088
+ }
4089
+ var CUSTOM_DEFAULTS = {
4090
+ light: { bg: "#f7f8fa", card: "#ffffff", fg: "#24262b", muted: "#6b7280", accent: "#6366f1" },
4091
+ dark: { bg: "#18181d", card: "#232329", fg: "#e8e8ec", muted: "#9494a0", accent: "#8b8ff5" }
4092
+ };
4093
+ var mixOf = (x, t2, y) => `color-mix(in oklab, ${x} ${Math.round((1 - t2) * 100)}%, ${y} ${Math.round(t2 * 100)}%)`;
4094
+ function anchorsScale(a, isDark) {
4095
+ const { bg, card, fg, muted, accent } = a;
4096
+ return isDark ? [
4097
+ mixOf(bg, 0.12, "#000"),
4098
+ // 0 最深(输入区)
4099
+ bg,
4100
+ // 1 背景
4101
+ mixOf(bg, 0.45, card),
4102
+ // 2 顶栏/面板
4103
+ card,
4104
+ // 3 卡片/输入
4105
+ mixOf(card, 0.12, fg),
4106
+ // 4 选中/悬停
4107
+ mixOf(card, 0.28, fg),
4108
+ // 5
4109
+ mixOf(card, 0.42, fg),
4110
+ // 6
4111
+ mixOf(accent, 0.14, "#000"),
4112
+ // 7
4113
+ accent,
4114
+ // 8 品牌级
4115
+ mixOf(accent, 0.18, "#fff"),
4116
+ // 9
4117
+ mixOf(muted, 0.18, fg),
4118
+ // 10 次级文字
4119
+ fg
4120
+ // 11 主文字
4121
+ ] : [
4122
+ card,
4123
+ // 0 最浅(顶栏/卡片)
4124
+ bg,
4125
+ // 1 背景
4126
+ mixOf(bg, 0.05, fg),
4127
+ // 2 侧栏
4128
+ mixOf(bg, 0.11, fg),
4129
+ // 3 winAlt
4130
+ mixOf(bg, 0.17, fg),
4131
+ // 4 悬停
4132
+ mixOf(bg, 0.26, fg),
4133
+ // 5
4134
+ mixOf(bg, 0.36, fg),
4135
+ // 6
4136
+ mixOf(accent, 0.14, "#000"),
4137
+ accent,
4138
+ mixOf(accent, 0.18, "#fff"),
4139
+ mixOf(muted, 0.18, fg),
4140
+ fg
4141
+ ];
4142
+ }
4143
+ var skinStyleEl = null;
4144
+ var bgStyleEl = null;
4145
+ var injectedKeys = /* @__PURE__ */ new Set();
4146
+ var classObserver = null;
4147
+ var currentTheme = { skin: "default", custom: null, bg: { mode: "none", maskOpacity: 0.82, blurPx: 0, panelOpacity: 0.3 } };
4148
+ var lastModeSig = "";
4149
+ var bgTimer = null;
4150
+ var regionStyleEl = null;
4151
+ var regionTimers = [];
4152
+ var regionFolderIdx = { left: -1, center: -1, right: -1 };
4153
+ var REGION_SELECTORS = {
4154
+ left: 'div[class*="workspace-sidebar-panel-width"]',
4155
+ center: '[data-testid="conversation-column"]',
4156
+ right: '[data-testid="conversation-column"] ~ [data-testid="browser"]'
4157
+ };
4158
+ var folderIdx = -1;
4159
+ var urlTick = 0;
4160
+ var applySeq = 0;
4161
+ var SURFACE_TOKENS = [
4162
+ "--color-background",
4163
+ "--color-background-win-alt",
4164
+ "--color-sidebar",
4165
+ "--color-header",
4166
+ "--color-panel",
4167
+ "--color-card",
4168
+ "--color-card-selected",
4169
+ "--color-input",
4170
+ "--color-input-focused",
4171
+ "--color-highspeed-composer-surface"
4172
+ ];
4173
+ var isDarkMode = () => document.documentElement.classList.contains("theme-zai-dark") || document.documentElement.classList.contains("dark");
4174
+ var probeEl = null;
4175
+ function resolvedColor(token) {
4176
+ try {
4177
+ if (!probeEl || !probeEl.isConnected) {
4178
+ probeEl = document.createElement("div");
4179
+ probeEl.style.display = "none";
4180
+ (document.body || document.documentElement).append(probeEl);
4181
+ }
4182
+ probeEl.style.color = "";
4183
+ probeEl.style.color = `var(${token})`;
4184
+ const v = getComputedStyle(probeEl).color;
4185
+ return /^rgba?\(/.test(v) ? v : "";
4186
+ } catch {
4187
+ return "";
4188
+ }
4189
+ }
4190
+ function ensureEl(id) {
4191
+ let el = document.getElementById(id);
4192
+ if (!el || !el.isConnected) {
4193
+ el = document.createElement("style");
4194
+ el.id = id;
4195
+ (document.head || document.documentElement).append(el);
4196
+ }
4197
+ return el;
4198
+ }
4199
+ function applySkin(skinId, custom) {
4200
+ skinStyleEl = ensureEl("__zcodepro_skin__");
4201
+ const root = document.documentElement;
4202
+ for (const id of [...THEME_SKIN_IDS, "custom"]) root.classList.remove("zcodepro-skin-" + id);
4203
+ const preset = THEME_SKINS.find((s) => s.id === skinId && s.hue);
4204
+ const isCustom = skinId === "custom";
4205
+ if (!preset && !isCustom) {
4206
+ skinStyleEl.textContent = "";
4207
+ return;
4208
+ }
4209
+ const cls = preset ? preset.id : "custom";
4210
+ root.classList.add("zcodepro-skin-" + cls);
4211
+ const scaleOf = (isDark) => {
4212
+ if (isCustom) {
4213
+ const dflt = CUSTOM_DEFAULTS[isDark ? "dark" : "light"];
4214
+ const src = custom && custom[isDark ? "dark" : "light"] || {};
4215
+ const a = {};
4216
+ for (const k of ["bg", "card", "fg", "muted", "accent"]) {
4217
+ a[k] = /^#[0-9a-f]{6}$/i.test(src[k]) ? src[k] : dflt[k];
4218
+ }
4219
+ return anchorsScale(a, isDark);
4220
+ }
4221
+ const sc = SCALES[preset.hue];
4222
+ return isDark ? sc.dark : sc.light;
4223
+ };
4224
+ const rules = (isDark) => {
4225
+ const vars = skinVars(scaleOf(isDark), isDark);
4226
+ return vars ? Object.entries(vars).map(([k, v]) => `${k}:${v};`).join("") : "";
4227
+ };
4228
+ const sb = (isDark) => {
4229
+ const fg = scaleOf(isDark)[11];
4230
+ return `::-webkit-scrollbar-thumb{background-color:${hexA(fg, "59")} !important;}::-webkit-scrollbar-thumb:hover{background-color:${hexA(fg, "8c")} !important;}`;
4231
+ };
4232
+ skinStyleEl.textContent = `html.theme-zai-light.zcodepro-skin-${cls}{${rules(false)}}html.theme-zai-dark.zcodepro-skin-${cls},html.dark.zcodepro-skin-${cls}{${rules(true)}}html.theme-zai-light.zcodepro-skin-${cls}{${sb(false)}}html.theme-zai-dark.zcodepro-skin-${cls},html.dark.zcodepro-skin-${cls}{${sb(true)}}`;
4233
+ }
4234
+ function clearBgOverrides() {
4235
+ const root = document.documentElement;
4236
+ for (const k of injectedKeys) root.style.removeProperty(k);
4237
+ injectedKeys = /* @__PURE__ */ new Set();
4238
+ }
4239
+ async function applyBackground(bg) {
4240
+ const seq = ++applySeq;
4241
+ bgStyleEl = ensureEl("__zcodepro_bg__");
4242
+ const root = document.documentElement;
4243
+ if (bgTimer) {
4244
+ clearInterval(bgTimer);
4245
+ bgTimer = null;
4246
+ }
4247
+ root.classList.remove("zcodepro-bg-on");
4248
+ clearBgOverrides();
4249
+ const bgActive = !!(bg && bg.mode && bg.mode !== "none");
4250
+ const anyRegion = !!(bg && bg.regions && (bg.regions.left || bg.regions.center || bg.regions.right));
4251
+ if (!bgActive) bgStyleEl.textContent = "";
4252
+ if (!bgActive && !anyRegion) {
4253
+ await applyRegionBackgrounds(bg);
4254
+ return;
4255
+ }
4256
+ const num01 = (v, dflt) => typeof v === "number" && Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : dflt;
4257
+ const maskOpacity = num01(bg.maskOpacity, 0.82);
4258
+ const alpha = Math.min(0.99, maskOpacity);
4259
+ const blurPx = typeof bg.blurPx === "number" && Number.isFinite(bg.blurPx) ? Math.min(24, Math.max(0, Math.round(bg.blurPx))) : 0;
4260
+ const panelOpacity = num01(bg.panelOpacity, 0.3);
4261
+ const resolvedBg = resolvedColor("--color-background");
4262
+ const mHex = resolvedBg.match(/^rgba?\(([\d.]+),\s*([\d.]+),\s*([\d.]+)/);
4263
+ const rgb = mHex ? [Math.round(+mHex[1]), Math.round(+mHex[2]), Math.round(+mHex[3])].join(",") : isDarkMode() ? "13,13,13" : "248,248,248";
4264
+ const panelMix = Math.round(panelOpacity * 100);
4265
+ if (panelMix < 100) {
4266
+ for (const t2 of SURFACE_TOKENS) {
4267
+ const v = resolvedColor(t2);
4268
+ if (v) {
4269
+ root.style.setProperty(t2, `color-mix(in srgb, ${v} ${panelMix}%, transparent)`);
4270
+ injectedKeys.add(t2);
4271
+ }
4272
+ }
4273
+ }
4274
+ await applyRegionBackgrounds(bg, { rgb, alpha, fit: bg.fit, seq });
4275
+ if (seq !== applySeq) return;
4276
+ if (!bgActive) return;
4277
+ const FIT_CSS = {
4278
+ cover: "background-size:100% 100%,cover !important;background-repeat:no-repeat !important;",
4279
+ contain: "background-size:100% 100%,contain !important;background-repeat:no-repeat !important;",
4280
+ fill: "background-size:100% 100%,100% 100% !important;background-repeat:no-repeat !important;",
4281
+ tile: "background-size:100% 100%,auto !important;background-repeat:no-repeat,repeat !important;"
4282
+ };
4283
+ const fitCss = FIT_CSS[bg.fit] || FIT_CSS.cover;
4284
+ const blurCss = blurPx > 0 ? `filter:blur(${blurPx}px);` : "";
4285
+ const spread = blurPx > 0 ? Math.max(20, blurPx * 2) : 0;
4286
+ const setImage = (image) => {
4287
+ bgStyleEl.textContent = `html.zcodepro-bg-on{background-color:rgb(${rgb}) !important;}html.zcodepro-bg-on::before{content:'' !important;position:fixed;inset:-${spread}px;z-index:-1;pointer-events:none;background-image:linear-gradient(rgba(${rgb},${alpha}),rgba(${rgb},${alpha})),${image} !important;${fitCss}background-position:center !important;${blurCss}}`;
4288
+ root.classList.add("zcodepro-bg-on");
4289
+ };
4290
+ const helperUrl = liveBoot().helperUrl || HELPER_URL;
4291
+ if (bg.mode === "library") {
4292
+ if (!bg.file) {
4293
+ bgStyleEl.textContent = "";
4294
+ return;
4295
+ }
4296
+ setImage(`url("${helperUrl}/bg/file/${encodeURIComponent(bg.file)}")`);
4297
+ return;
4298
+ }
4299
+ if (bg.mode === "folder") {
4300
+ const res = await rpc("/bg/folder/list");
4301
+ if (seq !== applySeq) return;
4302
+ const files = res.ok && Array.isArray(res.files) ? res.files.filter((f) => f && f.name) : [];
4303
+ if (!files.length) {
4304
+ bgStyleEl.textContent = "";
4305
+ root.classList.remove("zcodepro-bg-on");
4306
+ return;
4307
+ }
4308
+ const pick = () => {
4309
+ let idx = 0;
4310
+ if (bg.folderRandom) idx = files.length > 1 ? Math.floor(Math.random() * files.length) : 0;
4311
+ else {
4312
+ folderIdx = (folderIdx + 1) % files.length;
4313
+ idx = folderIdx;
4314
+ }
4315
+ setImage(`url("${helperUrl}/bg/folder/file/${encodeURIComponent(files[idx].name)}")`);
4316
+ };
4317
+ pick();
4318
+ const ms = Math.max(0, Number(bg.intervalMin) || 0) * 6e4;
4319
+ if (ms >= 3e4) bgTimer = setInterval(pick, ms);
4320
+ return;
4321
+ }
4322
+ if (bg.mode === "url") {
4323
+ const u0 = /^https?:\/\/\S+$/i.test(bg.url || "") && !/[<>"'\\]/.test(bg.url) ? bg.url : "";
4324
+ if (!u0) {
4325
+ bgStyleEl.textContent = "";
4326
+ return;
4327
+ }
4328
+ const bust = () => {
4329
+ urlTick += 1;
4330
+ const sep = u0.includes("?") ? "&" : "?";
4331
+ setImage(`url("${u0}${sep}_zcp=${urlTick}")`);
4332
+ };
4333
+ bust();
4334
+ const ms = Math.max(0, Number(bg.intervalMin) || 0) * 6e4;
4335
+ if (ms >= 3e4) bgTimer = setInterval(bust, ms);
4336
+ }
4337
+ }
4338
+ async function applyRegionBackgrounds(bg, pre) {
4339
+ regionStyleEl = ensureEl("__zcodepro_bg_regions__");
4340
+ for (const t2 of regionTimers) clearInterval(t2);
4341
+ regionTimers = [];
4342
+ const rules = /* @__PURE__ */ new Map();
4343
+ const flush = () => {
4344
+ regionStyleEl.textContent = [...rules.values()].join("");
4345
+ };
4346
+ const alpha = pre ? pre.alpha : 0.82;
4347
+ const rgb = pre ? pre.rgb : "13,13,13";
4348
+ const FIT_CSS = {
4349
+ cover: "100% 100%,cover",
4350
+ contain: "100% 100%,contain",
4351
+ fill: "100% 100%,100% 100%",
4352
+ tile: "100% 100%,auto"
4353
+ };
4354
+ const sizeVal = FIT_CSS[bg && bg.fit] || FIT_CSS.cover;
4355
+ const repeatVal = bg && bg.fit === "tile" ? "no-repeat,repeat" : "no-repeat";
4356
+ const helperUrl = liveBoot().helperUrl || HELPER_URL;
4357
+ const safeUrl = (u) => /^https?:\/\/\S+$/i.test(u || "") && !/[<>"'\\]/.test(u) ? u : "";
4358
+ const addRule = (region, image) => {
4359
+ if (!image) return;
4360
+ rules.set(region, `${REGION_SELECTORS[region]}{background-image:linear-gradient(rgba(${rgb},${alpha}),rgba(${rgb},${alpha})),${image} !important;background-size:${sizeVal} !important;background-position:center !important;background-repeat:${repeatVal} !important;}`);
4361
+ };
4362
+ const seq = applySeq;
4363
+ for (const region of ["left", "center", "right"]) {
4364
+ const r = bg && bg.regions && bg.regions[region];
4365
+ if (!r) continue;
4366
+ if (r.mode === "library") {
4367
+ if (r.file) addRule(region, `url("${helperUrl}/bg/file/${encodeURIComponent(r.file)}")`);
4368
+ } else if (r.mode === "url") {
4369
+ const u = safeUrl(r.url);
4370
+ if (u) addRule(region, `url("${u}")`);
4371
+ } else if (r.mode === "folder" && r.folder) {
4372
+ const res = await rpc("/bg/folder/list?dir=" + encodeURIComponent(r.folder));
4373
+ if (seq !== applySeq) return;
4374
+ const files = res.ok && !res.missing && Array.isArray(res.files) ? res.files.filter((f) => f && f.name) : [];
4375
+ if (!files.length) continue;
4376
+ const pick = () => {
4377
+ let idx = 0;
4378
+ if (bg.folderRandom !== false) idx = files.length > 1 ? Math.floor(Math.random() * files.length) : 0;
4379
+ else {
4380
+ regionFolderIdx[region] = (regionFolderIdx[region] + 1) % files.length;
4381
+ idx = regionFolderIdx[region];
4382
+ }
4383
+ addRule(region, `url("${helperUrl}/bg/folder/file/${encodeURIComponent(files[idx].name)}")`);
4384
+ flush();
4385
+ };
4386
+ pick();
4387
+ const ms = Math.max(0, Number(bg.intervalMin) || 0) * 6e4;
4388
+ if (ms >= 3e4) regionTimers.push(setInterval(pick, ms));
4389
+ }
4390
+ }
4391
+ flush();
4392
+ }
4393
+ function applyAll(theme) {
4394
+ if (theme && typeof theme === "object") currentTheme = theme;
4395
+ applySkin(currentTheme.skin || "default", currentTheme.custom);
4396
+ void applyBackground(currentTheme.bg);
4397
+ }
4398
+ function startTheme() {
4399
+ if (!document.head && !document.documentElement) return;
4400
+ applyAll({ skin: "default", custom: null, bg: { mode: "none", maskOpacity: 0.82, blurPx: 0, panelOpacity: 0.3 } });
4401
+ void getConfig().then((cfg) => applyAll(cfg.theme)).catch(() => {
4402
+ });
4403
+ window.addEventListener("zcodepro:config-changed", () => {
4404
+ void getConfig(true).then((cfg) => applyAll(cfg.theme)).catch(() => {
4405
+ });
4406
+ });
4407
+ if (!classObserver) {
4408
+ lastModeSig = isDarkMode() ? "d" : "l";
4409
+ classObserver = new MutationObserver(() => {
4410
+ const sig = isDarkMode() ? "d" : "l";
4411
+ if (sig === lastModeSig) return;
4412
+ lastModeSig = sig;
4413
+ applyAll(currentTheme);
4414
+ });
4415
+ classObserver.observe(document.documentElement, { attributes: true, attributeFilter: ["class"] });
4416
+ }
4417
+ }
4418
+
3809
4419
  // src/inject/features/settings-dialog.js
3810
4420
  function openExternal(url) {
3811
4421
  try {
@@ -4009,15 +4619,17 @@
4009
4619
  }, "\u2715");
4010
4620
  titleEl.replaceChildren(titleLeft, closeX);
4011
4621
  }
4012
- const health = await rpc("/health");
4013
- const config = await getConfig(true);
4014
- const agentsRes = await rpc("/agents");
4015
- const visionRes = await rpc("/vision");
4016
- const titleGenRes = await rpc("/title-gen");
4017
- const titleGenProvidersRes = await rpc("/title-gen/providers");
4622
+ const [health, config, agentsRes, visionRes, titleGenRes, titleGenProvidersRes, rtkRes, headroomRes] = await Promise.all([
4623
+ rpc("/health"),
4624
+ getConfig(true),
4625
+ rpc("/agents"),
4626
+ rpc("/vision"),
4627
+ rpc("/title-gen"),
4628
+ rpc("/title-gen/providers"),
4629
+ rpc("/rtk"),
4630
+ rpc("/headroom")
4631
+ ]);
4018
4632
  const titleGenProviders = titleGenProvidersRes.ok && Array.isArray(titleGenProvidersRes.providers) ? titleGenProvidersRes.providers : [];
4019
- const rtkRes = await rpc("/rtk");
4020
- const headroomRes = await rpc("/headroom");
4021
4633
  const setFeature = async (key, value) => {
4022
4634
  const res = await rpc("/config", { method: "POST", body: { features: { [key]: value } } });
4023
4635
  clearConfigCache();
@@ -4581,6 +5193,8 @@
4581
5193
  renderToolbarSwitch();
4582
5194
  const paneStylesSub1 = h("div", { class: "mt-4" });
4583
5195
  const paneStylesSub2 = h("div", { class: "mt-4", style: "display:none" });
5196
+ const paneStylesSub3 = h("div", { class: "mt-4", style: "display:none" });
5197
+ const paneStylesSub4 = h("div", { class: "mt-4", style: "display:none" });
4584
5198
  const ITEM_NAMES = {
4585
5199
  ctx: "sbItemCtx",
4586
5200
  questions: "sbItemQuestions",
@@ -4736,11 +5350,521 @@
4736
5350
  h("div", { class: "mt-3 mb-1 text-ui-sm font-medium" }, L.sbGroupTurn),
4737
5351
  turnList
4738
5352
  );
5353
+ let themeCfg = structuredClone(config.theme || {});
5354
+ const numOrBg = (v, d) => typeof v === "number" && Number.isFinite(v) ? v : d;
5355
+ themeCfg.skin = themeCfg.skin || "default";
5356
+ themeCfg.custom = {
5357
+ light: themeCfg.custom && themeCfg.custom.light || {},
5358
+ dark: themeCfg.custom && themeCfg.custom.dark || {}
5359
+ };
5360
+ themeCfg.bg = {
5361
+ ...themeCfg.bg || {},
5362
+ mode: themeCfg.bg?.mode || "none",
5363
+ maskOpacity: numOrBg(themeCfg.bg?.maskOpacity, 0.82),
5364
+ blurPx: numOrBg(themeCfg.bg?.blurPx, 0),
5365
+ panelOpacity: numOrBg(themeCfg.bg?.panelOpacity, 0.3)
5366
+ };
5367
+ themeCfg.bg.regions = themeCfg.bg.regions && typeof themeCfg.bg.regions === "object" ? themeCfg.bg.regions : {};
5368
+ let bgFiles = [];
5369
+ let bgLoading = false;
5370
+ let folderInfo = { count: 0, dir: "" };
5371
+ const themeInputCls = "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";
5372
+ const numField = (val, min, max, commit, title) => {
5373
+ const n = h("input", {
5374
+ type: "number",
5375
+ min: String(min),
5376
+ max: String(max),
5377
+ step: "1",
5378
+ title,
5379
+ class: "h-7 w-24 rounded-lg border border-border bg-input px-2 text-right text-ui-xs tabular-nums text-foreground outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40"
5380
+ });
5381
+ n.value = String(val);
5382
+ n.addEventListener("change", () => {
5383
+ const v = Math.max(min, Math.min(max, Math.round(Number(n.value) || 0)));
5384
+ n.value = String(v);
5385
+ commit(v);
5386
+ });
5387
+ return n;
5388
+ };
5389
+ const bgUrl = (name) => `${HELPER_URL}/bg/file/${encodeURIComponent(name)}`;
5390
+ const saveTheme = async (patch) => {
5391
+ themeCfg = {
5392
+ skin: patch.skin !== void 0 ? patch.skin : themeCfg.skin,
5393
+ custom: patch.custom !== void 0 ? patch.custom : themeCfg.custom,
5394
+ bg: { ...themeCfg.bg, ...patch.bg || {} }
5395
+ };
5396
+ const res = await rpc("/config", { method: "POST", body: { theme: themeCfg } });
5397
+ clearConfigCache();
5398
+ window.dispatchEvent(new CustomEvent("zcodepro:config-changed"));
5399
+ if (!res.ok) showToast(L.failed + ": " + errText(res), "error");
5400
+ renderThemeColors();
5401
+ renderThemeBg();
5402
+ };
5403
+ const refreshBgFiles = async () => {
5404
+ bgLoading = true;
5405
+ renderThemeBg();
5406
+ const res = await rpc("/bg");
5407
+ bgLoading = false;
5408
+ if (res.ok && Array.isArray(res.files)) bgFiles = res.files;
5409
+ if (themeCfg.bg.mode === "library" && themeCfg.bg.file && !bgFiles.some((f) => f.name === themeCfg.bg.file)) {
5410
+ themeCfg = { ...themeCfg, bg: { ...themeCfg.bg, file: "" } };
5411
+ await saveTheme({ bg: { file: "" } });
5412
+ showToast(L.themeBgDeletedActive, "success");
5413
+ return;
5414
+ }
5415
+ renderThemeBg();
5416
+ };
5417
+ const refreshFolderInfo = async () => {
5418
+ if (!themeCfg.bg.folder) {
5419
+ folderInfo = { count: 0, dir: "" };
5420
+ renderThemeBg();
5421
+ return;
5422
+ }
5423
+ const res = await rpc("/bg/folder/list");
5424
+ folderInfo = res.ok && !res.missing && Array.isArray(res.files) ? { count: res.files.length, dir: res.dir || themeCfg.bg.folder } : { count: 0, dir: themeCfg.bg.folder };
5425
+ renderThemeBg();
5426
+ };
5427
+ const bgInput = h("input", { type: "file", accept: ".png,.jpg,.jpeg,.webp,.gif,.bmp,.avif", multiple: "", style: "display:none" });
5428
+ bgInput.addEventListener("change", () => {
5429
+ const files = [...bgInput.files || []];
5430
+ bgInput.value = "";
5431
+ if (!files.length) return;
5432
+ void (async () => {
5433
+ let first = themeCfg.bg.file;
5434
+ for (const f of files) {
5435
+ const dataUrl = await new Promise((resolve) => {
5436
+ const r = new FileReader();
5437
+ r.onload = () => resolve(String(r.result || ""));
5438
+ r.onerror = () => resolve("");
5439
+ r.readAsDataURL(f);
5440
+ });
5441
+ if (!dataUrl) continue;
5442
+ const res = await rpc("/bg", { method: "POST", body: { name: f.name, data: dataUrl } });
5443
+ if (!res.ok) {
5444
+ showToast(L.failed + ": " + errText(res), "error");
5445
+ continue;
5446
+ }
5447
+ if (Array.isArray(res.files)) bgFiles = res.files;
5448
+ if (!first) first = f.name;
5449
+ }
5450
+ if (first && first !== themeCfg.bg.file) await saveTheme({ bg: { file: first } });
5451
+ else renderThemeBg();
5452
+ })();
5453
+ });
5454
+ let customSaveTimer = null;
5455
+ const CUSTOM_FIELDS = [
5456
+ ["bg", "themeCustomBg"],
5457
+ ["card", "themeCustomCard"],
5458
+ ["fg", "themeCustomFg"],
5459
+ ["muted", "themeCustomMuted"],
5460
+ ["accent", "themeCustomAccent"]
5461
+ ];
5462
+ const customVal = (mode, k) => themeCfg.custom[mode] && /^#[0-9a-f]{6}$/i.test(themeCfg.custom[mode][k] || "") ? themeCfg.custom[mode][k] : CUSTOM_DEFAULTS[mode][k];
5463
+ const customInput = (mode, k, label) => {
5464
+ const inp = h("input", {
5465
+ type: "color",
5466
+ value: customVal(mode, k),
5467
+ title: label,
5468
+ style: "width:30px;height:22px;padding:0;border:1px solid var(--color-card-border);border-radius:5px;background:transparent;cursor:pointer"
5469
+ });
5470
+ inp.addEventListener("input", () => {
5471
+ const next = {
5472
+ light: { ...themeCfg.custom.light },
5473
+ dark: { ...themeCfg.custom.dark }
5474
+ };
5475
+ next[mode][k] = inp.value.toLowerCase();
5476
+ clearTimeout(customSaveTimer);
5477
+ customSaveTimer = setTimeout(() => {
5478
+ void saveTheme({ skin: "custom", custom: next });
5479
+ }, 250);
5480
+ });
5481
+ return h("label", { class: "flex items-center gap-1.5 text-ui-xs text-foreground-subtle" }, inp, label);
5482
+ };
5483
+ const customSwatch = (mode) => [
5484
+ customVal(mode, "bg"),
5485
+ customVal(mode, "card"),
5486
+ customVal(mode, "fg"),
5487
+ customVal(mode, "muted"),
5488
+ customVal(mode, "accent")
5489
+ ];
5490
+ const renderThemeColors = () => {
5491
+ paneStylesSub3.replaceChildren(
5492
+ h("div", { class: "mb-1 text-ui-sm font-medium" }, L.themeSkinTitle),
5493
+ h("p", { class: "mb-2 text-ui-xs/relaxed text-foreground-subtle" }, L.themeSkinDesc),
5494
+ // 4 列 × 3 行可视,超出滚动;色条 8 块(底/卡片/加深面/边框/强调×2/次级文字/正文)
5495
+ h(
5496
+ "div",
5497
+ { class: "grid grid-cols-4 gap-2", style: "max-height:13.5rem;overflow-y:auto;" },
5498
+ ...THEME_SKINS.map((skin) => h(
5499
+ "button",
5500
+ {
5501
+ type: "button",
5502
+ class: "zcodepro-skin-card",
5503
+ "data-state": themeCfg.skin === skin.id ? "active" : "inactive",
5504
+ onClick: () => void saveTheme({ skin: skin.id })
5505
+ },
5506
+ h(
5507
+ "div",
5508
+ { class: "zcodepro-skin-swatches" },
5509
+ ...skin.preview.map((c) => h("span", { style: `background:${c}` }))
5510
+ ),
5511
+ h("span", { class: "text-ui-xs text-foreground" }, isZhLocale() ? skin.zh : skin.en)
5512
+ )),
5513
+ h(
5514
+ "button",
5515
+ {
5516
+ type: "button",
5517
+ class: "zcodepro-skin-card",
5518
+ "data-state": themeCfg.skin === "custom" ? "active" : "inactive",
5519
+ onClick: () => void saveTheme({ skin: "custom" })
5520
+ },
5521
+ h(
5522
+ "div",
5523
+ { class: "zcodepro-skin-swatches" },
5524
+ ...customSwatch("light").map((c) => h("span", { style: `background:${c}` })),
5525
+ ...customSwatch("dark").map((c) => h("span", { style: `background:${c}` }))
5526
+ ),
5527
+ h("span", { class: "text-ui-xs text-foreground" }, L.themeSkinCustom)
5528
+ )
5529
+ ),
5530
+ h("div", { class: "mt-3 mb-1 text-ui-sm font-medium" }, L.themeCustomTitle),
5531
+ h("p", { class: "mb-2 text-ui-xs/relaxed text-foreground-subtle" }, L.themeCustomHint),
5532
+ h(
5533
+ "div",
5534
+ { class: "grid grid-cols-2 gap-3" },
5535
+ ...["light", "dark"].map((mode) => h(
5536
+ "div",
5537
+ { class: "rounded-xl border border-border p-2" },
5538
+ h(
5539
+ "div",
5540
+ { class: "mb-1.5 text-ui-xs font-medium text-foreground-subtle" },
5541
+ mode === "light" ? L.themeCustomLight : L.themeCustomDark
5542
+ ),
5543
+ h(
5544
+ "div",
5545
+ { class: "flex flex-col gap-1.5" },
5546
+ ...CUSTOM_FIELDS.map(([k, key]) => customInput(mode, k, L[key]))
5547
+ )
5548
+ ))
5549
+ )
5550
+ );
5551
+ };
5552
+ const setRegion = (region, value) => {
5553
+ void saveTheme({ bg: { regions: { ...themeCfg.bg.regions, [region]: value } } });
5554
+ };
5555
+ const regionRow = (region, label) => {
5556
+ const cur = themeCfg.bg.regions[region] || null;
5557
+ const mode = cur ? cur.mode : null;
5558
+ const rBtn = (id, text) => h("button", {
5559
+ type: "button",
5560
+ class: "zcodepro-tab",
5561
+ "data-state": mode === id ? "active" : "inactive",
5562
+ onClick: () => {
5563
+ if (id === null) {
5564
+ setRegion(region, null);
5565
+ return;
5566
+ }
5567
+ const keep = themeCfg.bg.regions[region];
5568
+ setRegion(region, { mode: id, file: keep && keep.file || "", folder: keep && keep.folder || "", url: keep && keep.url || "" });
5569
+ }
5570
+ }, text);
5571
+ let valueCtrl = null;
5572
+ if (mode === "library") {
5573
+ const sel = h("select", { class: "h-7 max-w-40 rounded-lg border border-border bg-input px-1.5 text-ui-xs text-foreground outline-none" });
5574
+ sel.append(h("option", { value: "" }, "\u2014"));
5575
+ for (const f of bgFiles) sel.append(h("option", { value: f.name }, f.name));
5576
+ sel.value = cur && cur.file || "";
5577
+ sel.addEventListener("change", () => setRegion(region, { ...cur, file: sel.value }));
5578
+ valueCtrl = sel;
5579
+ } else if (mode === "folder") {
5580
+ const inp = h("input", { type: "text", value: cur && cur.folder || "", class: themeInputCls.replace("h-8", "h-7"), placeholder: "/path/to/wallpapers" });
5581
+ inp.addEventListener("change", () => {
5582
+ if (inp.value.trim()) setRegion(region, { ...cur, folder: inp.value.trim() });
5583
+ });
5584
+ valueCtrl = inp;
5585
+ } else if (mode === "url") {
5586
+ const inp = h("input", { type: "url", value: cur && cur.url || "", class: themeInputCls.replace("h-8", "h-7"), placeholder: "https://..." });
5587
+ inp.addEventListener("change", () => {
5588
+ if (/^https?:\/\/\S+$/.test(inp.value.trim())) setRegion(region, { ...cur, url: inp.value.trim() });
5589
+ });
5590
+ valueCtrl = inp;
5591
+ }
5592
+ return h(
5593
+ "div",
5594
+ { class: "flex flex-wrap items-center gap-2" },
5595
+ h("span", { class: "w-16 shrink-0 text-ui-xs text-foreground" }, label),
5596
+ h(
5597
+ "div",
5598
+ { class: "flex flex-wrap gap-1" },
5599
+ rBtn(null, L.themeBgRegionFollow),
5600
+ rBtn("library", L.themeBgModeLibrary),
5601
+ rBtn("folder", L.themeBgModeFolder),
5602
+ rBtn("url", L.themeBgModeUrl)
5603
+ ),
5604
+ valueCtrl || h("span", { class: "text-ui-xs text-foreground-subtle" }, "")
5605
+ );
5606
+ };
5607
+ const renderThemeBg = () => {
5608
+ const sliderRow = (label, title, min, max, step, val, unit, commit, hint) => {
5609
+ const lab = h("span", { class: "shrink-0 whitespace-nowrap text-ui-xs font-medium text-foreground-subtle", title, style: "width:72px" }, label);
5610
+ const valLabel = h("span", { class: "w-12 text-right text-ui-xs tabular-nums text-foreground-subtle" }, val + unit);
5611
+ const range = h("input", {
5612
+ type: "range",
5613
+ min: String(min),
5614
+ max: String(max),
5615
+ step: String(step),
5616
+ value: String(val),
5617
+ class: "zcodepro-range",
5618
+ "aria-label": label,
5619
+ style: "flex:1"
5620
+ });
5621
+ let timer = null;
5622
+ range.addEventListener("input", () => {
5623
+ const v = Number(range.value);
5624
+ valLabel.textContent = v + unit;
5625
+ clearTimeout(timer);
5626
+ timer = setTimeout(() => commit(v), 300);
5627
+ });
5628
+ return h(
5629
+ "div",
5630
+ { class: "flex flex-col gap-0.5" },
5631
+ h("div", { class: "flex items-center gap-3" }, lab, range, valLabel),
5632
+ hint ? h("span", { class: "text-ui-xs/relaxed text-foreground-subtle" }, hint) : null
5633
+ );
5634
+ };
5635
+ const numOr = (v, d) => typeof v === "number" && Number.isFinite(v) ? v : d;
5636
+ const maskRow = () => sliderRow(
5637
+ L.themeBgMask,
5638
+ L.themeBgMaskHint,
5639
+ 0,
5640
+ 99,
5641
+ 1,
5642
+ Math.round(numOr(themeCfg.bg.maskOpacity, 0.82) * 100),
5643
+ "%",
5644
+ (v) => {
5645
+ void saveTheme({ bg: { maskOpacity: v / 100 } });
5646
+ },
5647
+ L.themeBgMaskHint2
5648
+ );
5649
+ const blurRow = () => sliderRow(
5650
+ L.themeBgBlur,
5651
+ "",
5652
+ 0,
5653
+ 24,
5654
+ 1,
5655
+ numOr(themeCfg.bg.blurPx, 0),
5656
+ "px",
5657
+ (v) => {
5658
+ void saveTheme({ bg: { blurPx: v } });
5659
+ },
5660
+ ""
5661
+ );
5662
+ const panelRow = () => sliderRow(
5663
+ L.themeBgPanel,
5664
+ "",
5665
+ 0,
5666
+ 100,
5667
+ 1,
5668
+ Math.round(numOr(themeCfg.bg.panelOpacity, 0.3) * 100),
5669
+ "%",
5670
+ (v) => {
5671
+ void saveTheme({ bg: { panelOpacity: v / 100 } });
5672
+ },
5673
+ L.themeBgPanelHint
5674
+ );
5675
+ const modeBtn = (id, label) => h("button", {
5676
+ type: "button",
5677
+ class: "zcodepro-tab",
5678
+ "data-state": themeCfg.bg.mode === id ? "active" : "inactive",
5679
+ onClick: () => void saveTheme({ bg: { mode: id } })
5680
+ }, label);
5681
+ const folderInput = h("input", { type: "text", value: themeCfg.bg.folder, class: themeInputCls, placeholder: "/path/to/wallpapers" });
5682
+ const applyFolder = async (dir) => {
5683
+ const v = (dir || "").trim();
5684
+ if (!v) return;
5685
+ const res = await rpc("/bg/folder", { method: "POST", body: { dir: v } });
5686
+ if (!res.ok) {
5687
+ showToast(L.failed + ": " + errText(res), "error");
5688
+ renderThemeBg();
5689
+ return;
5690
+ }
5691
+ folderInfo = { count: (res.files || []).length, dir: res.dir || v };
5692
+ await saveTheme({ bg: { mode: "folder", folder: v } });
5693
+ };
5694
+ folderInput.addEventListener("change", () => {
5695
+ void applyFolder(folderInput.value);
5696
+ });
5697
+ const browseBtn = btnSmall(L.themeBgFolderPick, () => {
5698
+ void (async () => {
5699
+ browseBtn.disabled = true;
5700
+ try {
5701
+ const res = await rpc("/pick-folder?start=" + encodeURIComponent(themeCfg.bg.folder || "") + "&title=" + encodeURIComponent(L.themeBgFolderPickTitle));
5702
+ if (res && res.ok && res.path) {
5703
+ folderInput.value = res.path;
5704
+ await applyFolder(res.path);
5705
+ }
5706
+ } finally {
5707
+ browseBtn.disabled = false;
5708
+ }
5709
+ })();
5710
+ });
5711
+ const randomCheck = h("input", { type: "checkbox" });
5712
+ randomCheck.checked = themeCfg.bg.folderRandom !== false;
5713
+ randomCheck.addEventListener("change", () => {
5714
+ void saveTheme({ bg: { folderRandom: randomCheck.checked } });
5715
+ });
5716
+ const urlInput = h("input", { type: "url", value: themeCfg.bg.url, class: themeInputCls, placeholder: "https://..." });
5717
+ urlInput.addEventListener("change", () => {
5718
+ const v = urlInput.value.trim();
5719
+ if (!v) {
5720
+ void saveTheme({ bg: { mode: "none", url: "" } });
5721
+ return;
5722
+ }
5723
+ if (!/^https?:\/\/\S+$/.test(v)) {
5724
+ showToast(L.themeBgUrlInvalid, "error");
5725
+ renderThemeBg();
5726
+ return;
5727
+ }
5728
+ void saveTheme({ bg: { mode: "url", url: v } });
5729
+ });
5730
+ const libCard = (f) => {
5731
+ const del = h("button", {
5732
+ type: "button",
5733
+ class: "zcodepro-bg-del",
5734
+ title: L.themeBgDelete,
5735
+ onClick: (ev) => {
5736
+ ev.stopPropagation();
5737
+ void (async () => {
5738
+ const res = await rpc("/bg/delete", { method: "POST", body: { name: f.name } });
5739
+ if (!res.ok) {
5740
+ showToast(L.failed + ": " + errText(res), "error");
5741
+ return;
5742
+ }
5743
+ if (Array.isArray(res.files)) bgFiles = res.files;
5744
+ await refreshBgFiles();
5745
+ })();
5746
+ }
5747
+ }, "\u2715");
5748
+ return h("button", {
5749
+ type: "button",
5750
+ class: "zcodepro-bg-card",
5751
+ "data-state": themeCfg.bg.mode === "library" && themeCfg.bg.file === f.name ? "active" : "inactive",
5752
+ title: f.name,
5753
+ onClick: () => void saveTheme({ bg: { mode: "library", file: themeCfg.bg.file === f.name ? "" : f.name } })
5754
+ }, h("img", { src: bgUrl(f.name), alt: "", loading: "lazy" }), del);
5755
+ };
5756
+ const section = (titleText, body2) => h(
5757
+ "div",
5758
+ { class: "mt-3" },
5759
+ h("div", { class: "mb-1.5 text-ui-xs font-medium text-foreground-subtle" }, titleText),
5760
+ body2
5761
+ );
5762
+ const modeArea = themeCfg.bg.mode === "library" ? [
5763
+ section(L.themeBgLibrary + (bgFiles.length ? `\uFF08${bgFiles.length}\uFF09` : ""), h(
5764
+ "div",
5765
+ { class: "flex flex-col gap-2" },
5766
+ h(
5767
+ "div",
5768
+ { class: "flex items-center justify-between gap-2" },
5769
+ h("span", { class: "text-ui-xs/relaxed text-foreground-subtle" }, bgLoading ? "\u2026" : bgFiles.length ? "" : L.themeBgEmpty),
5770
+ btnSmall(L.themeBgUpload, () => bgInput.click())
5771
+ ),
5772
+ bgFiles.length ? h(
5773
+ "div",
5774
+ { class: "grid grid-cols-4 gap-2", style: "max-height:13.5rem;overflow-y:auto;" },
5775
+ ...bgFiles.map(libCard)
5776
+ ) : null
5777
+ ))
5778
+ ] : themeCfg.bg.mode === "folder" ? [
5779
+ section(L.themeBgModeFolder, h(
5780
+ "div",
5781
+ { class: "flex flex-col gap-2" },
5782
+ h("div", { class: "flex items-center gap-2" }, folderInput, browseBtn),
5783
+ h(
5784
+ "div",
5785
+ { class: "flex items-center gap-2" },
5786
+ h("label", { class: "flex items-center gap-1.5 text-ui-xs text-foreground-subtle" }, randomCheck, L.themeBgFolderRandom),
5787
+ numField(themeCfg.bg.intervalMin, 0, 1440, (v) => {
5788
+ void saveTheme({ bg: { intervalMin: v } });
5789
+ }, L.themeBgIntervalHint),
5790
+ h("span", { class: "text-ui-xs text-foreground-subtle" }, L.themeBgIntervalUnit)
5791
+ ),
5792
+ h(
5793
+ "p",
5794
+ { class: "text-ui-xs/relaxed text-foreground-subtle" },
5795
+ themeCfg.bg.folder ? folderInfo.count > 0 ? L.themeBgFolderCount.replaceAll("{n}", String(folderInfo.count)) : L.themeBgFolderMissing : L.themeBgFolderHint
5796
+ )
5797
+ ))
5798
+ ] : themeCfg.bg.mode === "url" ? [
5799
+ section(L.themeBgModeUrl, h(
5800
+ "div",
5801
+ { class: "flex flex-col gap-2" },
5802
+ urlInput,
5803
+ h(
5804
+ "div",
5805
+ { class: "flex items-center gap-2" },
5806
+ numField(themeCfg.bg.intervalMin, 0, 1440, (v) => {
5807
+ void saveTheme({ bg: { intervalMin: v } });
5808
+ }, L.themeBgUrlIntervalHint),
5809
+ h("span", { class: "text-ui-xs text-foreground-subtle" }, L.themeBgIntervalUnit)
5810
+ ),
5811
+ h("p", { class: "text-ui-xs/relaxed text-foreground-subtle" }, L.themeBgUrlHint)
5812
+ ))
5813
+ ] : [h("p", { class: "mt-3 text-ui-xs/relaxed text-foreground-subtle" }, L.themeBgNoneHint)];
5814
+ const fitBtn = (id, label) => h("button", {
5815
+ type: "button",
5816
+ class: "zcodepro-tab",
5817
+ "data-state": (themeCfg.bg.fit || "cover") === id ? "active" : "inactive",
5818
+ onClick: () => void saveTheme({ bg: { fit: id } })
5819
+ }, label);
5820
+ paneStylesSub4.replaceChildren(
5821
+ h("p", { class: "text-ui-sm/relaxed text-foreground-subtle" }, L.themeBgDesc),
5822
+ h(
5823
+ "div",
5824
+ { class: "mt-2 flex flex-wrap gap-1" },
5825
+ modeBtn("none", L.themeBgModeNone),
5826
+ modeBtn("library", L.themeBgModeLibrary),
5827
+ modeBtn("folder", L.themeBgModeFolder),
5828
+ modeBtn("url", L.themeBgModeUrl)
5829
+ ),
5830
+ ...modeArea,
5831
+ section(L.themeBgRegions, h(
5832
+ "div",
5833
+ { class: "flex flex-col gap-2" },
5834
+ regionRow("left", L.themeBgRegionLeft),
5835
+ regionRow("center", L.themeBgRegionCenter),
5836
+ regionRow("right", L.themeBgRegionRight)
5837
+ )),
5838
+ h(
5839
+ "div",
5840
+ { class: "mt-3 flex flex-wrap items-center gap-2" },
5841
+ h("span", { class: "text-ui-xs font-medium text-foreground-subtle" }, L.themeBgFit),
5842
+ fitBtn("cover", L.themeBgFitCover),
5843
+ fitBtn("contain", L.themeBgFitContain),
5844
+ fitBtn("fill", L.themeBgFitFill),
5845
+ fitBtn("tile", L.themeBgFitTile)
5846
+ ),
5847
+ h(
5848
+ "div",
5849
+ { class: "mt-3 flex flex-col gap-2" },
5850
+ maskRow(),
5851
+ blurRow(),
5852
+ panelRow()
5853
+ ),
5854
+ bgInput
5855
+ );
5856
+ };
5857
+ renderThemeColors();
5858
+ renderThemeBg();
5859
+ void refreshBgFiles();
5860
+ void refreshFolderInfo();
4739
5861
  let stylesSubTab = "styles";
4740
5862
  const stylesSubTablist = h("div", { role: "tablist", "aria-orientation": "horizontal", class: "zcodepro-tablist" });
4741
5863
  const renderStylesSubTabs = () => stylesSubTablist.replaceChildren(...[
4742
5864
  ["styles", L.tabStylesPane],
4743
- ["status", L.tabStatusPane]
5865
+ ["status", L.tabStatusPane],
5866
+ ["colors", L.themeColorsPane],
5867
+ ["bgimg", L.themeBgPane]
4744
5868
  ].map(([id, label]) => h("button", {
4745
5869
  type: "button",
4746
5870
  role: "tab",
@@ -4751,11 +5875,13 @@
4751
5875
  stylesSubTab = id;
4752
5876
  paneStylesSub1.style.display = id === "styles" ? "" : "none";
4753
5877
  paneStylesSub2.style.display = id === "status" ? "" : "none";
5878
+ paneStylesSub3.style.display = id === "colors" ? "" : "none";
5879
+ paneStylesSub4.style.display = id === "bgimg" ? "" : "none";
4754
5880
  renderStylesSubTabs();
4755
5881
  }
4756
5882
  }, label)));
4757
5883
  renderStylesSubTabs();
4758
- paneStyles.append(stylesSubTablist, paneStylesSub1, paneStylesSub2);
5884
+ paneStyles.append(stylesSubTablist, paneStylesSub1, paneStylesSub2, paneStylesSub3, paneStylesSub4);
4759
5885
  paneStylesSub1.append(
4760
5886
  toolbarSwitchWrap,
4761
5887
  h(
@@ -5399,15 +6525,6 @@
5399
6525
  ),
5400
6526
  h("p", { class: "mt-2 text-ui-xs/relaxed text-foreground-subtle" }, L.syncHint)
5401
6527
  );
5402
- const persistDevMode = async (v) => {
5403
- const res = await rpc("/config", { method: "POST", body: { devMode: v } });
5404
- if (!res.ok) {
5405
- showToast(L.failed + ": " + errText(res), "error");
5406
- return false;
5407
- }
5408
- if (res.config) config.devMode = res.config.devMode;
5409
- return true;
5410
- };
5411
6528
  const devReloadBtn = btnSmall(L.devReload, () => {
5412
6529
  void runDevReload();
5413
6530
  }, "shrink-0", "primary");
@@ -5425,33 +6542,24 @@
5425
6542
  }
5426
6543
  }, 2e3);
5427
6544
  };
5428
- const devModeWrap = h("div");
5429
- const renderDevPane = () => {
5430
- const dev = config.devMode === true;
5431
- devModeWrap.replaceChildren(
5432
- h("p", { class: "text-ui-sm/relaxed text-foreground-subtle" }, L.devModeDesc),
6545
+ paneDebug.append(
6546
+ h("p", { class: "text-ui-sm/relaxed text-foreground-subtle" }, L.devModeDesc),
6547
+ h(
6548
+ "div",
6549
+ { class: "mt-3 rounded-xl border border-border" },
5433
6550
  h(
5434
6551
  "div",
5435
- { class: "mt-3 divide-y divide-border rounded-xl border border-border" },
5436
- settingRow(L.devMode, L.devModeRowDesc, dev, async () => {
5437
- if (await persistDevMode(!dev)) renderDevPane();
5438
- }),
5439
- ...dev ? [h(
6552
+ { class: "flex items-center justify-between gap-2 px-2 py-1.5" },
6553
+ h(
5440
6554
  "div",
5441
- { class: "flex items-center justify-between gap-2 px-2 py-1.5" },
5442
- h(
5443
- "div",
5444
- { class: "min-w-0" },
5445
- h("div", { class: "text-ui-sm font-medium text-foreground" }, L.devReload),
5446
- h("div", { class: "mt-0.5 text-ui-xs/relaxed text-foreground-subtle" }, L.devReloadDesc)
5447
- ),
5448
- devReloadBtn
5449
- )] : []
6555
+ { class: "min-w-0" },
6556
+ h("div", { class: "text-ui-sm font-medium text-foreground" }, L.devReload),
6557
+ h("div", { class: "mt-0.5 text-ui-xs/relaxed text-foreground-subtle" }, L.devReloadDesc)
6558
+ ),
6559
+ devReloadBtn
5450
6560
  )
5451
- );
5452
- };
5453
- renderDevPane();
5454
- paneDebug.append(devModeWrap);
6561
+ )
6562
+ );
5455
6563
  const VISION_DEFAULT_PROMPT = "\u8BF7\u8BE6\u7EC6\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\u7684\u5168\u90E8\u5185\u5BB9\u3002\u82E5\u662F\u754C\u9762\u6216\u56FE\u8868\u622A\u56FE\uFF0C\u8BF7\u5148\u628A\u6240\u6709\u9519\u8BEF\u3001\u8B66\u544A\u3001\u5F02\u5E38\u72B6\u6001\u9010\u5B57\u5F15\u7528\u51FA\u6765\uFF08\u542B\u5B8C\u6574\u539F\u6587\uFF09\uFF0C\u518D\u63CF\u8FF0\u6574\u4F53\u5E03\u5C40\u3001\u6587\u5B57\u4E0E\u5173\u952E\u6570\u636E\u3002";
5456
6564
  const VISION_DEFAULT_CFG = {
5457
6565
  enabled: true,
@@ -5469,6 +6577,7 @@
5469
6577
  forceIntercept: true
5470
6578
  };
5471
6579
  let visionCfg = visionRes.ok && visionRes.config && typeof visionRes.config === "object" ? structuredClone(visionRes.config) : null;
6580
+ let visionAutoInitTried = false;
5472
6581
  let visionSaveTimer = null;
5473
6582
  const persistVision = async () => {
5474
6583
  visionCfg.chain = visionCfg.proxies.map((p) => (p.name || "").trim()).filter(Boolean);
@@ -5539,6 +6648,14 @@
5539
6648
  return;
5540
6649
  }
5541
6650
  if (!visionCfg) {
6651
+ if (!visionAutoInitTried && visionRes.hook) {
6652
+ visionAutoInitTried = true;
6653
+ void (async () => {
6654
+ const res = await rpc("/vision", { method: "POST", body: { config: VISION_DEFAULT_CFG } });
6655
+ if (res.ok) visionCfg = structuredClone(res.config);
6656
+ renderVision();
6657
+ })();
6658
+ }
5542
6659
  const initBtn = btnSmall(L.visionAddProxy, async () => {
5543
6660
  const res = await rpc("/vision", { method: "POST", body: { config: VISION_DEFAULT_CFG } });
5544
6661
  if (res.ok) {
@@ -7970,6 +9087,7 @@
7970
9087
  var items = DEFAULT_STATUS_ITEMS.map((x) => ({ ...x }));
7971
9088
  var barEl = null;
7972
9089
  var lastStats = null;
9090
+ var lastRenderedHtml = null;
7973
9091
  function readCtx() {
7974
9092
  for (const el of document.querySelectorAll("[title], [aria-label]")) {
7975
9093
  if (el.closest(".zcodepro-statusbar")) continue;
@@ -8120,12 +9238,14 @@
8120
9238
  if (!enabled4 || !host || !host.parentElement) {
8121
9239
  if (barEl) barEl.remove();
8122
9240
  barEl = null;
9241
+ lastRenderedHtml = null;
8123
9242
  return;
8124
9243
  }
8125
9244
  if (!barEl || !barEl.isConnected) {
8126
9245
  if (!barEl) {
8127
9246
  barEl = document.createElement("div");
8128
9247
  barEl.className = "zcodepro-statusbar";
9248
+ lastRenderedHtml = null;
8129
9249
  }
8130
9250
  host.parentElement.insertBefore(barEl, host.nextSibling);
8131
9251
  }
@@ -8146,18 +9266,23 @@
8146
9266
  }
8147
9267
  const SEP = '<span class="zcodepro-statusbar-sep">\xB7</span>';
8148
9268
  const GROUPSEP = '<span class="zcodepro-statusbar-groupsep"></span>';
8149
- barEl.innerHTML = parts.reduce((acc, p, i) => {
9269
+ const html = parts.reduce((acc, p, i) => {
8150
9270
  if (i === 0) return p;
8151
9271
  if (p === GROUPSEP || parts[i - 1] === GROUPSEP) return acc + p;
8152
9272
  return acc + SEP + p;
8153
9273
  }, "");
8154
9274
  barEl.style.display = parts.length ? "" : "none";
9275
+ if (html === lastRenderedHtml) return;
9276
+ lastRenderedHtml = html;
9277
+ barEl.innerHTML = html;
8155
9278
  }
8156
9279
  async function tick() {
8157
9280
  if (!enabled4 || document.hidden) return;
8158
9281
  refreshOfficial();
8159
- const res = await rpc("/stats/usage").catch(() => null);
8160
- if (res && res.ok) lastStats = res;
9282
+ if (!official) {
9283
+ const res = await rpc("/stats/usage").catch(() => null);
9284
+ if (res && res.ok) lastStats = res;
9285
+ }
8161
9286
  renderBar();
8162
9287
  }
8163
9288
  function startStatusBar() {
@@ -9580,6 +10705,10 @@
9580
10705
  startStyleAdjustments();
9581
10706
  } catch {
9582
10707
  }
10708
+ try {
10709
+ startTheme();
10710
+ } catch {
10711
+ }
9583
10712
  try {
9584
10713
  startSettingsEntry();
9585
10714
  } catch {