zcode-pro 1.4.0 → 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/README.en.md CHANGED
@@ -37,8 +37,8 @@ Grouped by where each feature lives in the settings window: the category column
37
37
  | | UI style adjustments | Fine-tune paragraph spacing, content width, line heights, list/quote/table spacing and cell padding; focusing an input highlights the affected areas; applies instantly |
38
38
  | Appearance · Status bar | Status bar statistics | Shows statistics at the bottom-right of the input box and at the end of each answer: context usage (color-shifts when high), questions asked, today/total tokens and task count, all from official data; reorder or hide each item under Settings → Appearance → Status bar |
39
39
  | | Per-turn token usage | Shows that turn's in/out tokens and cache hit rate after the time in each answer's action row (local records keep only a recent window; older answers stay unlabelled) |
40
- | Appearance · Theme | UI skins | Eleven famous editor theme palettes (Dracula / Nord / Tokyo Night / Catppuccin / Gruvbox / One Dark / Solarized / Rosé Pine / Everforest / Night Owl / Kanagawa), each with light and dark variants that follow ZCode's current mode automatically; plus a custom theme: five anchor colors per mode derive the whole palette, applying live |
41
- | Appearance · Background | Background image | Window-wide background from four sources: none / local library (upload many, pick the active one, delete freely) / folder (read in place, rotate on a timer, random or by filename) / URL (refresh on a timer for random-image APIs); single visibility slider; sidebar, chat and composer reveal the background while menus and dialogs stay opaque; the library uploads with the zcode-pro WebDAV sync group (overwrite, no history versions) |
40
+ | Appearance · Theme | UI skins | Eleven famous editor theme palettes (Dracula / Nord / Tokyo Night / Catppuccin / Gruvbox / One Dark / Solarized / Rosé Pine / Everforest / Night Owl / Kanagawa), auto-matching ZCode's light / dark mode; plus a custom theme: pick 5 colors each for light and dark to derive the whole palette, applying live |
41
+ | Appearance · Background | Background image | Window-wide background from four sources: none / local library (upload many, pick the active one, delete freely) / folder (read in place, rotate on a timer, random or by filename) / URL (refresh on a timer for random-image APIs); three sliders — mask opacity (image fading toward the theme background color), image blur, panel opacity (sidebar/panels/tab bar); sidebar, chat and composer reveal the background while menus and dialogs stay opaque; the library uploads with the zcode-pro WebDAV sync group (overwrite, no history versions) |
42
42
  | Performance | Idle project memory reclaim | Projects unused for a while with no running tasks release their memory automatically, and unused ones are reclaimed once after startup; sessions are untouched and reload when reopened (durations adjustable) |
43
43
  | Proxy | Software proxy | Set an HTTP proxy for plugin updates/installs and tool upgrades, with connectivity test |
44
44
  | Sync | Settings sync | Manually back up ZCode Pro settings and model settings to WebDAV, or restore them on a new computer; transfers only on click |
package/README.md CHANGED
@@ -37,8 +37,8 @@ ZCode 桌面版界面增强工具。不修改客户端文件,通过 ZCode Pro
37
37
  | | 界面样式调整 | 微调段落间距、内容宽度、各类行高、列表/引用块/表格间距与单元格边距;聚焦输入框时高亮预览受影响区域,即改即生效 |
38
38
  | 外观 · 状态栏 | 状态栏统计 | 提问输入框右下角和每轮回答最后显示统计项:上下文占用(占用高时变色)、提问次数、今日/累计 token、任务总数,全部取自官方数据;各项的顺序与显隐在 设置 → 外观 → 状态栏 调整 |
39
39
  | | 每轮回答 token 用量 | 在每条回答末尾操作按钮的时间后面显示该轮的输入/输出 token 与缓存命中率(本地记录只保留最近一段,更早的回答不标注) |
40
- | 外观 · 主题 | 界面皮肤 | 内置 11 套知名编辑器主题配色(Dracula / Nord / Tokyo Night / Catppuccin / Gruvbox / One Dark / Solarized / Rosé Pine / Everforest / Night Owl / Kanagawa),每套含明、暗两套变体,跟随 ZCode 当前明暗自动切换;另可自定义主题:明暗各五个锚点色派生整套配色,改色实时生效 |
41
- | 外观 · 背景图 | 背景图 | 整窗铺背景,四种来源:无背景 / 本地图库(可传多张、点选当前、可单删)/ 文件夹(原地读取,按间隔定时轮换,可选随机或按文件名顺序)/ 网址(可按间隔定时刷新,配合随机图 API);可见度单滑杆调节;侧栏 / 会话流 / 输入框透出背景,弹窗与菜单保持不透明保可读;图库随 WebDAV 同步的 zcode-pro 组一起上传(覆盖式,不留历史版本) |
40
+ | 外观 · 主题 | 界面皮肤 | 内置 11 套知名编辑器主题配色(Dracula / Nord / Tokyo Night / Catppuccin / Gruvbox / One Dark / Solarized / Rosé Pine / Everforest / Night Owl / Kanagawa),自动匹配 ZCode 的浅色 / 深色模式;另可自定义主题:浅色、深色各选 5 个颜色生成整套配色,改色实时生效 |
41
+ | 外观 · 背景图 | 背景图 | 整窗铺背景,四种来源:无背景 / 本地图库(可传多张、点选当前、可单删)/ 文件夹(原地读取,按间隔定时轮换,可选随机或按文件名顺序)/ 网址(可按间隔定时刷新,配合随机图 API);遮罩不透明度(图片向主题背景色淡化程度)、图片模糊、面板不透明度(侧栏、面板与标签栏的不透明程度)三个滑杆调节;侧栏 / 会话流 / 输入框透出背景,弹窗与菜单保持不透明保可读;图库随 WebDAV 同步的 zcode-pro 组一起上传(覆盖式,不留历史版本) |
42
42
  | 性能 | 空闲项目内存回收 | 长时间未使用且无任务运行的项目自动释放其占用的内存,启动后也会自动回收一次未使用的项目;会话记录不受影响,再次打开时自动恢复(时长可调) |
43
43
  | 代理 | 软件代理 | 为插件更新安装与本体升级设置 HTTP 代理,附连通性检测 |
44
44
  | 同步 | 设置同步 | 手动把 ZCode Pro 设置与模型设置备份到 WebDAV,或在新电脑上恢复;仅在点击时传输 |
package/dist/inject.js CHANGED
@@ -304,12 +304,12 @@
304
304
  themeBgRegionRight: "\u53F3\u4FA7\u8FB9\u680F",
305
305
  themeBgPane: "\u80CC\u666F\u56FE",
306
306
  themeSkinTitle: "\u76AE\u80A4",
307
- themeSkinDesc: "\u77E5\u540D\u7F16\u8F91\u5668\u4E3B\u9898\u914D\u8272\uFF0C\u660E\u6697\u4E24\u5957\u53D8\u4F53\u8DDF\u968F ZCode \u5F53\u524D\u660E\u6697\u81EA\u52A8\u5207\u6362\uFF1B\u9ED8\u8BA4\u4FDD\u6301 ZCode \u539F\u6837\uFF0C\u81EA\u5B9A\u4E49\u5728\u4E0B\u65B9\u81EA\u884C\u8C03\u8272\u3002",
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
308
  themeSkinCustom: "\u81EA\u5B9A\u4E49",
309
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\u6BCF\u5957\u542B\u660E\u3001\u6697\u4E24\u4E2A\u53D8\u4F53\uFF0C\u4E94\u4E2A\u951A\u70B9\u8272\u6D3E\u751F\u6574\u5957\u754C\u9762\u914D\u8272\u3002",
311
- themeCustomLight: "\u660E\u8272\u53D8\u4F53",
312
- themeCustomDark: "\u6697\u8272\u53D8\u4F53",
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
313
  themeCustomBg: "\u80CC\u666F",
314
314
  themeCustomCard: "\u5361\u7247",
315
315
  themeCustomFg: "\u6B63\u6587",
@@ -341,7 +341,12 @@
341
341
  themeBgFitContain: "\u9002\u5E94",
342
342
  themeBgFitFill: "\u62C9\u4F38",
343
343
  themeBgFitTile: "\u5E73\u94FA",
344
- themeVisibility: "\u80CC\u666F\u53EF\u89C1\u5EA6",
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",
345
350
  themeBgDeletedActive: "\u5F53\u524D\u80CC\u666F\u5DF2\u5220\u9664\uFF0C\u5DF2\u81EA\u52A8\u505C\u7528\u80CC\u666F",
346
351
  sbItemCtx: "\u4E0A\u4E0B\u6587\u5360\u7528",
347
352
  sbItemQuestions: "\u63D0\u95EE\u6B21\u6570",
@@ -805,12 +810,12 @@
805
810
  themeBgRegionRight: "Right sidebar",
806
811
  themeBgPane: "Background",
807
812
  themeSkinTitle: "Skins",
808
- themeSkinDesc: 'Famous editor theme palettes; light and dark variants follow ZCode\u2019s current mode automatically; "Default" keeps ZCode as-is, "Custom" is tuned below.',
813
+ themeSkinDesc: 'Famous editor theme palettes, auto-matching ZCode\u2019s light / dark mode; "Default" keeps ZCode as-is, "Custom" is tuned below.',
809
814
  themeSkinCustom: "Custom",
810
815
  themeCustomTitle: "Custom theme",
811
- themeCustomHint: "Color changes apply live (auto-switches to the custom skin); light and dark variants, five anchor colors derive the whole palette.",
812
- themeCustomLight: "Light variant",
813
- themeCustomDark: "Dark variant",
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",
814
819
  themeCustomBg: "Background",
815
820
  themeCustomCard: "Card",
816
821
  themeCustomFg: "Text",
@@ -842,7 +847,12 @@
842
847
  themeBgFitContain: "Fit",
843
848
  themeBgFitFill: "Stretch",
844
849
  themeBgFitTile: "Tile",
845
- themeVisibility: "Background visibility",
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.",
846
856
  themeBgDeletedActive: "Active background was deleted; background disabled",
847
857
  sbItemCtx: "Context usage",
848
858
  sbItemQuestions: "Questions asked",
@@ -1258,8 +1268,8 @@
1258
1268
  clearTimeout(toastTimer);
1259
1269
  toastTimer = setTimeout(() => el.remove(), kind === "error" ? 5e3 : 2600);
1260
1270
  }
1261
- 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";
1262
- 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";
1263
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";
1264
1274
  var dialogStack = [];
1265
1275
  function openDialog({ title, description, onMount, onClose, width = "sm:max-w-md", overlay = "dim", draggable = false, posKey = "", dismissOnOutside = true, bodyClass = "", closable = false }) {
@@ -1287,7 +1297,8 @@
1287
1297
  const content = h("div", {
1288
1298
  role: "dialog",
1289
1299
  "aria-modal": "true",
1290
- 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;"
1291
1302
  });
1292
1303
  const overlayEl = h("div", {
1293
1304
  "data-overlay": overlay,
@@ -1816,6 +1827,8 @@
1816
1827
  -webkit-appearance: none;
1817
1828
  appearance: none;
1818
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 */
1819
1832
  height: 4px;
1820
1833
  border-radius: 9999px;
1821
1834
  background: color-mix(in oklab, var(--color-foreground, #888) 18%, transparent);
@@ -4131,7 +4144,7 @@
4131
4144
  var bgStyleEl = null;
4132
4145
  var injectedKeys = /* @__PURE__ */ new Set();
4133
4146
  var classObserver = null;
4134
- var currentTheme = { skin: "default", custom: null, bg: { mode: "none", visibility: 0.8 } };
4147
+ var currentTheme = { skin: "default", custom: null, bg: { mode: "none", maskOpacity: 0.82, blurPx: 0, panelOpacity: 0.3 } };
4135
4148
  var lastModeSig = "";
4136
4149
  var bgTimer = null;
4137
4150
  var regionStyleEl = null;
@@ -4240,12 +4253,15 @@
4240
4253
  await applyRegionBackgrounds(bg);
4241
4254
  return;
4242
4255
  }
4243
- const visibility = typeof bg.visibility === "number" && Number.isFinite(bg.visibility) ? Math.min(1, Math.max(0, bg.visibility)) : 0.8;
4244
- const alpha = 1 - visibility;
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);
4245
4261
  const resolvedBg = resolvedColor("--color-background");
4246
4262
  const mHex = resolvedBg.match(/^rgba?\(([\d.]+),\s*([\d.]+),\s*([\d.]+)/);
4247
4263
  const rgb = mHex ? [Math.round(+mHex[1]), Math.round(+mHex[2]), Math.round(+mHex[3])].join(",") : isDarkMode() ? "13,13,13" : "248,248,248";
4248
- const panelMix = Math.min(100, Math.round(alpha * 100) + 10);
4264
+ const panelMix = Math.round(panelOpacity * 100);
4249
4265
  if (panelMix < 100) {
4250
4266
  for (const t2 of SURFACE_TOKENS) {
4251
4267
  const v = resolvedColor(t2);
@@ -4255,7 +4271,7 @@
4255
4271
  }
4256
4272
  }
4257
4273
  }
4258
- await applyRegionBackgrounds(bg, { mask, rgb, alpha, fit: bg.fit, visibility, seq });
4274
+ await applyRegionBackgrounds(bg, { rgb, alpha, fit: bg.fit, seq });
4259
4275
  if (seq !== applySeq) return;
4260
4276
  if (!bgActive) return;
4261
4277
  const FIT_CSS = {
@@ -4265,8 +4281,10 @@
4265
4281
  tile: "background-size:100% 100%,auto !important;background-repeat:no-repeat,repeat !important;"
4266
4282
  };
4267
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;
4268
4286
  const setImage = (image) => {
4269
- bgStyleEl.textContent = `html.zcodepro-bg-on{background-color:rgb(${rgb}) !important;background-image:linear-gradient(rgba(${rgb},${alpha}),rgba(${rgb},${alpha})),${image} !important;${fitCss}background-position:center !important;}`;
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}}`;
4270
4288
  root.classList.add("zcodepro-bg-on");
4271
4289
  };
4272
4290
  const helperUrl = liveBoot().helperUrl || HELPER_URL;
@@ -4325,7 +4343,7 @@
4325
4343
  const flush = () => {
4326
4344
  regionStyleEl.textContent = [...rules.values()].join("");
4327
4345
  };
4328
- const alpha = pre ? pre.alpha : 0.2;
4346
+ const alpha = pre ? pre.alpha : 0.82;
4329
4347
  const rgb = pre ? pre.rgb : "13,13,13";
4330
4348
  const FIT_CSS = {
4331
4349
  cover: "100% 100%,cover",
@@ -4379,7 +4397,7 @@
4379
4397
  }
4380
4398
  function startTheme() {
4381
4399
  if (!document.head && !document.documentElement) return;
4382
- applyAll({ skin: "default", custom: null, bg: { mode: "none", visibility: 0.8 } });
4400
+ applyAll({ skin: "default", custom: null, bg: { mode: "none", maskOpacity: 0.82, blurPx: 0, panelOpacity: 0.3 } });
4383
4401
  void getConfig().then((cfg) => applyAll(cfg.theme)).catch(() => {
4384
4402
  });
4385
4403
  window.addEventListener("zcodepro:config-changed", () => {
@@ -5333,21 +5351,18 @@
5333
5351
  turnList
5334
5352
  );
5335
5353
  let themeCfg = structuredClone(config.theme || {});
5354
+ const numOrBg = (v, d) => typeof v === "number" && Number.isFinite(v) ? v : d;
5336
5355
  themeCfg.skin = themeCfg.skin || "default";
5337
5356
  themeCfg.custom = {
5338
5357
  light: themeCfg.custom && themeCfg.custom.light || {},
5339
5358
  dark: themeCfg.custom && themeCfg.custom.dark || {}
5340
5359
  };
5341
5360
  themeCfg.bg = {
5342
- mode: "none",
5343
- file: "",
5344
- folder: "",
5345
- folderRandom: true,
5346
- url: "",
5347
- intervalMin: 0,
5348
- fit: "cover",
5349
- visibility: 0.8,
5350
- ...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)
5351
5366
  };
5352
5367
  themeCfg.bg.regions = themeCfg.bg.regions && typeof themeCfg.bg.regions === "object" ? themeCfg.bg.regions : {};
5353
5368
  let bgFiles = [];
@@ -5590,27 +5605,73 @@
5590
5605
  );
5591
5606
  };
5592
5607
  const renderThemeBg = () => {
5593
- const vis = Math.round((themeCfg.bg.visibility ?? 0.8) * 100);
5594
- const visLabel = h("span", { class: "w-10 text-right text-ui-xs tabular-nums text-foreground-subtle" }, vis + "%");
5595
- const visRange = h("input", {
5596
- type: "range",
5597
- min: "0",
5598
- max: "100",
5599
- step: "5",
5600
- value: String(vis),
5601
- class: "zcodepro-range",
5602
- "aria-label": L.themeVisibility,
5603
- style: "flex:1"
5604
- });
5605
- let visTimer = null;
5606
- visRange.addEventListener("input", () => {
5607
- const v = Number(visRange.value);
5608
- visLabel.textContent = v + "%";
5609
- clearTimeout(visTimer);
5610
- visTimer = setTimeout(() => {
5611
- void saveTheme({ bg: { visibility: v / 100 } });
5612
- }, 300);
5613
- });
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
+ );
5614
5675
  const modeBtn = (id, label) => h("button", {
5615
5676
  type: "button",
5616
5677
  class: "zcodepro-tab",
@@ -5785,10 +5846,10 @@
5785
5846
  ),
5786
5847
  h(
5787
5848
  "div",
5788
- { class: "mt-3 flex items-center gap-3" },
5789
- h("span", { class: "shrink-0 text-ui-xs font-medium text-foreground-subtle" }, L.themeVisibility),
5790
- visRange,
5791
- visLabel
5849
+ { class: "mt-3 flex flex-col gap-2" },
5850
+ maskRow(),
5851
+ blurRow(),
5852
+ panelRow()
5792
5853
  ),
5793
5854
  bgInput
5794
5855
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zcode-pro",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "description": "ZCode Pro — 为 ZCode 桌面版增加界面增强(不修改客户端文件,通过 ZCode Pro 快捷方式启动即可),核心功能:项目自定义别名",
5
5
  "type": "module",
6
6
  "bin": {
@@ -17,7 +17,7 @@ import { reorderWorkspaceTasks, reorderGroupMembers } from './taskOrder.mjs';
17
17
  import { webdavRequest, webdavPutWithMkcol, webdavHttpText, parseWebdavListing, findCurl } from './webdav.mjs';
18
18
  import { resolveZcodeExecutable } from './paths.mjs';
19
19
 
20
- const VERSION = '1.4.0';
20
+ const VERSION = '1.4.1';
21
21
 
22
22
  // 全局提示词固定在用户主目录:官方加载器按 HOME/USERPROFILE 拼 .zcode/AGENTS.md,
23
23
  // 不读 ZCODE_DATA_BASE_DIR(数据根迁走时全局指令仍在原位)。
@@ -68,8 +68,10 @@ function listBgFiles(dataRoot) {
68
68
  // 主题配置(设置 → 外观 → 主题/背景图):skin 为皮肤 id(default = 不覆盖,
69
69
  // custom = 用 custom 锚点色现算色阶);bg.mode 选背景来源——none 无背景 /
70
70
  // library 本地图库(file 为当前文件)/ folder 文件夹(定时切换,folderRandom
71
- // 随机或顺序)/ url 网址(intervalMin 针对随机图 API 定时刷新);visibility
72
- // 为图片可见度 0-1。主题功能未发过版,无旧结构需要迁移
71
+ // 随机或顺序)/ url 网址(intervalMin 针对随机图 API 定时刷新);
72
+ // maskOpacity 遮罩不透明度 0-0.99(越高图片越向主题背景色淡化,文字对比越
73
+ // 清晰,默认 0.82)、blurPx 图片模糊 0-24px(默认 0)、panelOpacity 面板
74
+ // 不透明度 0-1(侧栏/面板/标签栏,越低透出图片越多,默认 0.3)
73
75
  const THEME_SKINS = new Set(['default', 'dracula', 'nord', 'tokyonight', 'catppuccin', 'gruvbox', 'onedark', 'solarized', 'rosepine', 'everforest', 'nightowl', 'kanagawa', 'custom']);
74
76
  const BG_MODES = new Set(['none', 'library', 'folder', 'url']);
75
77
  const BG_FITS = new Set(['cover', 'contain', 'fill', 'tile']);
@@ -103,7 +105,6 @@ function mergeBgRegion(src) {
103
105
  export function mergeThemeConfig(saved) {
104
106
  const t = saved && typeof saved === 'object' ? saved : {};
105
107
  const bg = t.bg && typeof t.bg === 'object' ? t.bg : {};
106
- const vis = Number(bg.visibility);
107
108
  const cfg = {
108
109
  skin: THEME_SKINS.has(t.skin) ? t.skin : 'default',
109
110
  custom: mergeCustomSkin(t.custom),
@@ -115,7 +116,11 @@ export function mergeThemeConfig(saved) {
115
116
  url: (str(bg.url, 2000) || '').trim(),
116
117
  intervalMin: Number.isFinite(Number(bg.intervalMin)) ? Math.min(1440, Math.max(0, Math.round(Number(bg.intervalMin)))) : 0,
117
118
  fit: BG_FITS.has(bg.fit) ? bg.fit : 'cover',
118
- visibility: Number.isFinite(vis) ? Math.min(1, Math.max(0, vis)) : 0.8,
119
+ // null/undefined/非数值都落默认(Number(null)=0 是有限数,须先排除 null)
120
+ maskOpacity: bg.maskOpacity != null && Number.isFinite(Number(bg.maskOpacity))
121
+ ? Math.min(0.99, Math.max(0, Number(bg.maskOpacity))) : 0.82,
122
+ blurPx: bg.blurPx != null && Number.isFinite(Number(bg.blurPx)) ? Math.min(24, Math.max(0, Math.round(Number(bg.blurPx)))) : 0,
123
+ panelOpacity: bg.panelOpacity != null && Number.isFinite(Number(bg.panelOpacity)) ? Math.min(1, Math.max(0, Number(bg.panelOpacity))) : 0.3,
119
124
  regions: {
120
125
  left: mergeBgRegion(bg.regions && bg.regions.left),
121
126
  center: mergeBgRegion(bg.regions && bg.regions.center),
@@ -515,7 +520,9 @@ export function startHelper({ port, token, dataRoot, state, agentsFile = default
515
520
  url: bt.bg && bt.bg.url !== undefined ? bt.bg.url : cur.bg.url,
516
521
  intervalMin: bt.bg && bt.bg.intervalMin !== undefined ? bt.bg.intervalMin : cur.bg.intervalMin,
517
522
  fit: bt.bg && bt.bg.fit !== undefined ? bt.bg.fit : cur.bg.fit,
518
- visibility: bt.bg && bt.bg.visibility !== undefined ? bt.bg.visibility : cur.bg.visibility,
523
+ maskOpacity: bt.bg && bt.bg.maskOpacity !== undefined ? bt.bg.maskOpacity : cur.bg.maskOpacity,
524
+ blurPx: bt.bg && bt.bg.blurPx !== undefined ? bt.bg.blurPx : cur.bg.blurPx,
525
+ panelOpacity: bt.bg && bt.bg.panelOpacity !== undefined ? bt.bg.panelOpacity : cur.bg.panelOpacity,
519
526
  regions: bt.bg && bt.bg.regions !== undefined ? bt.bg.regions : cur.bg.regions,
520
527
  },
521
528
  };
@@ -317,12 +317,12 @@ const zh = {
317
317
  themeBgRegionRight: '右侧边栏',
318
318
  themeBgPane: '背景图',
319
319
  themeSkinTitle: '皮肤',
320
- themeSkinDesc: '知名编辑器主题配色,明暗两套变体跟随 ZCode 当前明暗自动切换;默认保持 ZCode 原样,自定义在下方自行调色。',
320
+ themeSkinDesc: '知名编辑器主题配色,自动匹配 ZCode 的浅色 / 深色模式;默认保持 ZCode 原样,自定义在下方自行调色。',
321
321
  themeSkinCustom: '自定义',
322
322
  themeCustomTitle: '自定义主题',
323
- themeCustomHint: '改颜色即实时生效(自动切到自定义皮肤);每套含明、暗两个变体,五个锚点色派生整套界面配色。',
324
- themeCustomLight: '明色变体',
325
- themeCustomDark: '暗色变体',
323
+ themeCustomHint: '改颜色即实时生效(自动切到自定义皮肤);浅色、深色各配一次,选好 5 个颜色即可生成整套界面配色。',
324
+ themeCustomLight: '浅色',
325
+ themeCustomDark: '深色',
326
326
  themeCustomBg: '背景',
327
327
  themeCustomCard: '卡片',
328
328
  themeCustomFg: '正文',
@@ -354,7 +354,12 @@ const zh = {
354
354
  themeBgFitContain: '适应',
355
355
  themeBgFitFill: '拉伸',
356
356
  themeBgFitTile: '平铺',
357
- themeVisibility: '背景可见度',
357
+ themeBgMask: '遮罩不透明度',
358
+ themeBgMaskHint: '数值越高,图片越向主题背景色淡化,文字对比越清晰',
359
+ themeBgMaskHint2: '图片向主题背景色淡化的程度',
360
+ themeBgBlur: '图片模糊',
361
+ themeBgPanel: '面板不透明度',
362
+ themeBgPanelHint: '侧栏、面板与标签栏的不透明程度。越低,透出的图片越多。',
358
363
  themeBgDeletedActive: '当前背景已删除,已自动停用背景',
359
364
  sbItemCtx: '上下文占用',
360
365
  sbItemQuestions: '提问次数',
@@ -819,12 +824,12 @@ const en = {
819
824
  themeBgRegionRight: 'Right sidebar',
820
825
  themeBgPane: 'Background',
821
826
  themeSkinTitle: 'Skins',
822
- themeSkinDesc: 'Famous editor theme palettes; light and dark variants follow ZCode\u2019s current mode automatically; "Default" keeps ZCode as-is, "Custom" is tuned below.',
827
+ themeSkinDesc: 'Famous editor theme palettes, auto-matching ZCode\u2019s light / dark mode; "Default" keeps ZCode as-is, "Custom" is tuned below.',
823
828
  themeSkinCustom: 'Custom',
824
829
  themeCustomTitle: 'Custom theme',
825
- themeCustomHint: 'Color changes apply live (auto-switches to the custom skin); light and dark variants, five anchor colors derive the whole palette.',
826
- themeCustomLight: 'Light variant',
827
- themeCustomDark: 'Dark variant',
830
+ themeCustomHint: 'Color changes apply live (auto-switches to the custom skin); set light and dark separately — pick 5 colors and the whole palette is derived.',
831
+ themeCustomLight: 'Light',
832
+ themeCustomDark: 'Dark',
828
833
  themeCustomBg: 'Background',
829
834
  themeCustomCard: 'Card',
830
835
  themeCustomFg: 'Text',
@@ -856,7 +861,12 @@ const en = {
856
861
  themeBgFitContain: 'Fit',
857
862
  themeBgFitFill: 'Stretch',
858
863
  themeBgFitTile: 'Tile',
859
- themeVisibility: 'Background visibility',
864
+ themeBgMask: 'Mask opacity',
865
+ themeBgMaskHint: 'Higher values fade the image toward the theme background color for clearer text',
866
+ themeBgMaskHint2: 'How much the image fades toward the theme background color',
867
+ themeBgBlur: 'Image blur',
868
+ themeBgPanel: 'Panel opacity',
869
+ themeBgPanelHint: 'Opacity of sidebar, panels and tab bar. Lower reveals more of the image.',
860
870
  themeBgDeletedActive: 'Active background was deleted; background disabled',
861
871
  sbItemCtx: 'Context usage',
862
872
  sbItemQuestions: 'Questions asked',
@@ -869,15 +869,20 @@ export function openSettingsDialog() {
869
869
  // —— “颜色”子页(paneStylesSub3):预设皮肤卡片 + 自定义主题编辑 ——
870
870
  // —— “背景图”子页(paneStylesSub4):无背景 / 图库 / 文件夹轮换 / 网址 ——
871
871
  let themeCfg = structuredClone(config.theme || {});
872
+ const numOrBg = (v, d) => (typeof v === 'number' && Number.isFinite(v) ? v : d);
872
873
  themeCfg.skin = themeCfg.skin || 'default';
873
874
  themeCfg.custom = {
874
875
  light: (themeCfg.custom && themeCfg.custom.light) || {},
875
876
  dark: (themeCfg.custom && themeCfg.custom.dark) || {},
876
877
  };
877
878
  themeCfg.bg = {
878
- mode: 'none', file: '', folder: '', folderRandom: true, url: '', intervalMin: 0, fit: 'cover', visibility: 0.8,
879
879
  ...(themeCfg.bg || {}),
880
+ mode: themeCfg.bg?.mode || 'none',
881
+ maskOpacity: numOrBg(themeCfg.bg?.maskOpacity, 0.82),
882
+ blurPx: numOrBg(themeCfg.bg?.blurPx, 0),
883
+ panelOpacity: numOrBg(themeCfg.bg?.panelOpacity, 0.3),
880
884
  };
885
+ // file/folder/url/fit/folderRandom/intervalMin/regions 由 helper 清洗保证形态,原样保留
881
886
  themeCfg.bg.regions = (themeCfg.bg.regions && typeof themeCfg.bg.regions === 'object') ? themeCfg.bg.regions : {};
882
887
  let bgFiles = [];
883
888
  let bgLoading = false;
@@ -1061,19 +1066,35 @@ export function openSettingsDialog() {
1061
1066
  valueCtrl || h('span', { class: 'text-ui-xs text-foreground-subtle' }, ''));
1062
1067
  };
1063
1068
  const renderThemeBg = () => {
1064
- const vis = Math.round((themeCfg.bg.visibility ?? 0.8) * 100);
1065
- const visLabel = h('span', { class: 'w-10 text-right text-ui-xs tabular-nums text-foreground-subtle' }, vis + '%');
1066
- const visRange = h('input', {
1067
- type: 'range', min: '0', max: '100', step: '5', value: String(vis),
1068
- class: 'zcodepro-range', 'aria-label': L.themeVisibility, style: 'flex:1',
1069
- });
1070
- let visTimer = null;
1071
- visRange.addEventListener('input', () => {
1072
- const v = Number(visRange.value);
1073
- visLabel.textContent = v + '%';
1074
- clearTimeout(visTimer);
1075
- visTimer = setTimeout(() => { void saveTheme({ bg: { visibility: v / 100 } }); }, 300);
1076
- });
1069
+ // 三个调节参数(滑杆行构造器:label + 范围 + 单位 + 提示 + 防抖保存)
1070
+ const sliderRow = (label, title, min, max, step, val, unit, commit, hint) => {
1071
+ // 标签列固定宽(容最长标签六字;内联样式——应用的 Tailwind 按需编译
1072
+ // 不包含 w-[72px] 这类任意值类):多行滑杆的轨道起点对齐同一竖线
1073
+ const lab = h('span', { class: 'shrink-0 whitespace-nowrap text-ui-xs font-medium text-foreground-subtle', title, style: 'width:72px' }, label);
1074
+ const valLabel = h('span', { class: 'w-12 text-right text-ui-xs tabular-nums text-foreground-subtle' }, val + unit);
1075
+ const range = h('input', {
1076
+ type: 'range', min: String(min), max: String(max), step: String(step), value: String(val),
1077
+ class: 'zcodepro-range', 'aria-label': label, style: 'flex:1',
1078
+ });
1079
+ let timer = null;
1080
+ range.addEventListener('input', () => {
1081
+ const v = Number(range.value);
1082
+ valLabel.textContent = v + unit;
1083
+ clearTimeout(timer);
1084
+ timer = setTimeout(() => commit(v), 300);
1085
+ });
1086
+ return h('div', { class: 'flex flex-col gap-0.5' },
1087
+ h('div', { class: 'flex items-center gap-3' }, lab, range, valLabel),
1088
+ hint ? h('span', { class: 'text-ui-xs/relaxed text-foreground-subtle' }, hint) : null);
1089
+ };
1090
+ // 数值兜底:null(旧配置带入)与 undefined 都要落默认,?? 只兜 undefined
1091
+ const numOr = (v, d) => (typeof v === 'number' && Number.isFinite(v) ? v : d);
1092
+ const maskRow = () => sliderRow(L.themeBgMask, L.themeBgMaskHint, 0, 99, 1, Math.round(numOr(themeCfg.bg.maskOpacity, 0.82) * 100), '%',
1093
+ (v) => { void saveTheme({ bg: { maskOpacity: v / 100 } }); }, L.themeBgMaskHint2);
1094
+ const blurRow = () => sliderRow(L.themeBgBlur, '', 0, 24, 1, numOr(themeCfg.bg.blurPx, 0), 'px',
1095
+ (v) => { void saveTheme({ bg: { blurPx: v } }); }, '');
1096
+ const panelRow = () => sliderRow(L.themeBgPanel, '', 0, 100, 1, Math.round(numOr(themeCfg.bg.panelOpacity, 0.3) * 100), '%',
1097
+ (v) => { void saveTheme({ bg: { panelOpacity: v / 100 } }); }, L.themeBgPanelHint);
1077
1098
  const modeBtn = (id, label) => h('button', {
1078
1099
  type: 'button', class: 'zcodepro-tab',
1079
1100
  'data-state': themeCfg.bg.mode === id ? 'active' : 'inactive',
@@ -1184,9 +1205,8 @@ export function openSettingsDialog() {
1184
1205
  h('span', { class: 'text-ui-xs font-medium text-foreground-subtle' }, L.themeBgFit),
1185
1206
  fitBtn('cover', L.themeBgFitCover), fitBtn('contain', L.themeBgFitContain),
1186
1207
  fitBtn('fill', L.themeBgFitFill), fitBtn('tile', L.themeBgFitTile)),
1187
- h('div', { class: 'mt-3 flex items-center gap-3' },
1188
- h('span', { class: 'shrink-0 text-ui-xs font-medium text-foreground-subtle' }, L.themeVisibility),
1189
- visRange, visLabel),
1208
+ h('div', { class: 'mt-3 flex flex-col gap-2' },
1209
+ maskRow(), blurRow(), panelRow()),
1190
1210
  bgInput,
1191
1211
  );
1192
1212
  };
@@ -194,7 +194,7 @@ let skinStyleEl = null; // 皮肤 token 覆盖
194
194
  let bgStyleEl = null; // html 背景层(图 + 遮罩)
195
195
  let injectedKeys = new Set(); // 半透明化写进 root.style 的变量(重算前先撤)
196
196
  let classObserver = null; // 明暗切换(root class 变化)→ 重算遮罩色与透明度
197
- let currentTheme = { skin: 'default', custom: null, bg: { mode: 'none', visibility: 0.8 } };
197
+ let currentTheme = { skin: 'default', custom: null, bg: { mode: 'none', maskOpacity: 0.82, blurPx: 0, panelOpacity: 0.3 } };
198
198
  let lastModeSig = ''; // 观察器只对明暗模式变化重算:applySkin/applyBackground
199
199
  // 自己也会改 root class(zcodepro-skin-*/zcodepro-bg-on),不带这条过滤会自触发循环
200
200
  let bgTimer = null; // 文件夹轮换 / 网址刷新定时器(重应用前先清)
@@ -321,18 +321,23 @@ async function applyBackground(bg) {
321
321
  return;
322
322
  }
323
323
 
324
- const visibility = typeof bg.visibility === 'number' && Number.isFinite(bg.visibility)
325
- ? Math.min(1, Math.max(0, bg.visibility)) : 0.8;
326
- const alpha = 1 - visibility;
324
+ // 三参数(对齐 codeg 语义):maskOpacity 遮罩不透明度(直接作为遮罩 α);
325
+ // blurPx 图片模糊(CSS filter,只糊图片层不糊遮罩);panelOpacity 面板
326
+ // 不透明度(侧栏/面板/标签栏表面混入底色的比例,独立于遮罩)
327
+ const num01 = (v, dflt) => (typeof v === 'number' && Number.isFinite(v) ? Math.min(1, Math.max(0, v)) : dflt);
328
+ const maskOpacity = num01(bg.maskOpacity, 0.82);
329
+ const alpha = Math.min(0.99, maskOpacity);
330
+ const blurPx = typeof bg.blurPx === 'number' && Number.isFinite(bg.blurPx)
331
+ ? Math.min(24, Math.max(0, Math.round(bg.blurPx))) : 0;
332
+ const panelOpacity = num01(bg.panelOpacity, 0.3);
327
333
  // 遮罩色跟随当前明暗的皮肤底色:在皮肤已应用、半透明化未写入时读解析色
328
334
  const resolvedBg = resolvedColor('--color-background');
329
335
  const mHex = resolvedBg.match(/^rgba?\(([\d.]+),\s*([\d.]+),\s*([\d.]+)/);
330
336
  const rgb = mHex
331
337
  ? [Math.round(+mHex[1]), Math.round(+mHex[2]), Math.round(+mHex[3])].join(',')
332
338
  : (isDarkMode() ? '13,13,13' : '248,248,248');
333
- // 表面半透明化:面板不透明度 = 遮罩浓度 + 10% 基础偏移(可见度 100% → 面板
334
- // 10% 底色,0% → 100% 纯色);在设图之前完成,与具体图片无关
335
- const panelMix = Math.min(100, Math.round(alpha * 100) + 10);
339
+ // 表面半透明化:面板不透明度直接用 panelOpacity(0=全透图,1=纯色)
340
+ const panelMix = Math.round(panelOpacity * 100);
336
341
  if (panelMix < 100) {
337
342
  for (const t of SURFACE_TOKENS) {
338
343
  const v = resolvedColor(t);
@@ -342,7 +347,7 @@ async function applyBackground(bg) {
342
347
  }
343
348
  }
344
349
  }
345
- await applyRegionBackgrounds(bg, { mask, rgb, alpha, fit: bg.fit, visibility, seq });
350
+ await applyRegionBackgrounds(bg, { rgb, alpha, fit: bg.fit, seq });
346
351
  if (seq !== applySeq) return; // 分区文件夹列表等待期间配置又变了
347
352
  if (!bgActive) return; // 只有分区背景:到此为止,html 层不设图
348
353
 
@@ -357,10 +362,17 @@ async function applyBackground(bg) {
357
362
  tile: 'background-size:100% 100%,auto !important;background-repeat:no-repeat,repeat !important;',
358
363
  };
359
364
  const fitCss = FIT_CSS[bg.fit] || FIT_CSS.cover;
365
+ // 模糊只作用于背景层:图与遮罩画在 html::before(fixed 铺满视口、z 轴最底),
366
+ // filter 写在伪元素上——写在 html 自身会把整个界面子树一起糊掉。
367
+ // 模糊会把伪元素边缘拉开出现透明缝,按模糊半径外扩 inset 收边
368
+ const blurCss = blurPx > 0 ? `filter:blur(${blurPx}px);` : '';
369
+ const spread = blurPx > 0 ? Math.max(20, blurPx * 2) : 0;
360
370
  const setImage = (image) => {
361
371
  bgStyleEl.textContent =
362
- `html.zcodepro-bg-on{background-color:rgb(${rgb}) !important;background-image:linear-gradient(rgba(${rgb},${alpha}),rgba(${rgb},${alpha})),${image} !important;` +
363
- `${fitCss}background-position:center !important;}`;
372
+ `html.zcodepro-bg-on{background-color:rgb(${rgb}) !important;}` +
373
+ `html.zcodepro-bg-on::before{content:'' !important;position:fixed;inset:-${spread}px;z-index:-1;pointer-events:none;` +
374
+ `background-image:linear-gradient(rgba(${rgb},${alpha}),rgba(${rgb},${alpha})),${image} !important;` +
375
+ `${fitCss}background-position:center !important;${blurCss}}`;
364
376
  root.classList.add('zcodepro-bg-on');
365
377
  };
366
378
  const helperUrl = liveBoot().helperUrl || HELPER_URL;
@@ -413,8 +425,8 @@ async function applyRegionBackgrounds(bg, pre) {
413
425
  regionTimers = [];
414
426
  const rules = new Map(); // region -> 规则文本(轮换时覆盖同分区旧规则,不无限追加)
415
427
  const flush = () => { regionStyleEl.textContent = [...rules.values()].join(''); };
416
- // pre.alpha = 遮罩浓度(= 1 - 可见度),与全局同值,不再反转
417
- const alpha = pre ? pre.alpha : 0.2;
428
+ // pre.alpha = 遮罩不透明度(直接配置值),与全局同值
429
+ const alpha = pre ? pre.alpha : 0.82;
418
430
  const rgb = pre ? pre.rgb : '13,13,13';
419
431
  const FIT_CSS = {
420
432
  cover: '100% 100%,cover', contain: '100% 100%,contain', fill: '100% 100%,100% 100%', tile: '100% 100%,auto',
@@ -468,7 +480,7 @@ function applyAll(theme) {
468
480
 
469
481
  export function startTheme() {
470
482
  if (!document.head && !document.documentElement) return;
471
- applyAll({ skin: 'default', custom: null, bg: { mode: 'none', visibility: 0.8 } });
483
+ applyAll({ skin: 'default', custom: null, bg: { mode: 'none', maskOpacity: 0.82, blurPx: 0, panelOpacity: 0.3 } });
472
484
  void getConfig().then((cfg) => applyAll(cfg.theme)).catch(() => { /* 读不到配置保持应用默认 */ });
473
485
  window.addEventListener('zcodepro:config-changed', () => {
474
486
  void getConfig(true).then((cfg) => applyAll(cfg.theme)).catch(() => { /* ignore */ });
package/src/inject/ui.js CHANGED
@@ -26,8 +26,10 @@ export function showToast(text, kind = 'info') {
26
26
  // 注意:绝不能用 bg-surface 做面板底色——该 token 在应用里是 ~3% 不透明度的着色层(#0d0d0d08),
27
27
  // 会导致整卡透明。关键视觉另在 ensureStyle 里以自有样式表兜底(ID 选择器 + 主题变量 + 字面量回退),
28
28
  // 应用 Tailwind 类随版本更新变动时遮罩/面板仍能正确绘制。
29
- const 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';
30
- const overlayClassBare = 'fixed inset-0 isolate z-50 flex items-center justify-center duration-100 p-4 platform-linux-desktop:top-12';
29
+ // 垂直方向 items-start + 卡片 margin-top:切 tab 内容高度变化时卡片顶边不动
30
+ // (此前 items-center 上下双向伸缩,顶部随内容高度跳动,顶部 tab 没法稳着点)
31
+ const 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';
32
+ const overlayClassBare = 'fixed inset-0 isolate z-50 flex items-start justify-center duration-100 p-4 platform-linux-desktop:top-12';
31
33
  const 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';
32
34
 
33
35
  // overlay: 'dim'(默认,半透明遮罩)| 'none'(透明遮罩)——设置弹窗用 'none',方便边调样式边看会话。
@@ -59,6 +61,7 @@ export function openDialog({ title, description, onMount, onClose, width = 'sm:m
59
61
  role: 'dialog',
60
62
  'aria-modal': 'true',
61
63
  class: contentClass.replace('sm:max-w-md', width) + ' zcodepro-card',
64
+ style: 'margin-top:12vh;max-height:calc(88vh - 3rem);overflow-y:auto;',
62
65
  });
63
66
  const overlayEl = h('div', {
64
67
  'data-overlay': overlay,
@@ -566,6 +569,8 @@ export function ensureStyle() {
566
569
  -webkit-appearance: none;
567
570
  appearance: none;
568
571
  width: 100%;
572
+ /* 不给 padding:padding 会缩小 thumb 的可滑区间(拖不到最左/最右)。
573
+ 轨道起点对齐由弹窗里的固定标签列宽负责 */
569
574
  height: 4px;
570
575
  border-radius: 9999px;
571
576
  background: color-mix(in oklab, var(--color-foreground, #888) 18%, transparent);