zcode-pro 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +7 -6
- package/README.md +11 -10
- package/dist/inject.js +446 -108
- package/package.json +1 -1
- package/src/host/helper.mjs +15 -6
- package/src/inject/core.js +70 -48
- package/src/inject/features/composer-provider.js +112 -0
- package/src/inject/features/settings-dialog.js +73 -31
- package/src/inject/features/table-copy.js +148 -0
- package/src/inject/features/theme.js +25 -13
- package/src/inject/index.js +4 -0
- package/src/inject/ui.js +19 -3
package/package.json
CHANGED
package/src/host/helper.mjs
CHANGED
|
@@ -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.
|
|
20
|
+
const VERSION = '1.5.0';
|
|
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 定时刷新);
|
|
72
|
-
//
|
|
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
|
-
|
|
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),
|
|
@@ -154,6 +159,8 @@ export function defaultConfig() {
|
|
|
154
159
|
idleReclaim: true, // 空闲项目内存回收:长时间无活动的项目释放其会话进程(时长 idleReclaimMinutes)
|
|
155
160
|
toolbarIcons: true, // 侧栏菜单并入顶栏:新建任务/搜索/自动化/插件市场收成顶栏图标按钮(原菜单隐藏,点击转发)
|
|
156
161
|
pinnedCollapse: true, // 已置顶分区可折叠:标题可点折叠/展开任务列表(状态记 localStorage)
|
|
162
|
+
tableCopy: true, // 表格工具条"复制表格"按钮:富文本剪贴板,粘贴即表格(table-copy.js)
|
|
163
|
+
composerProvider: true, // 输入框模型名前显示供应商名(composer-provider.js)
|
|
157
164
|
},
|
|
158
165
|
// 空闲项目内存回收的判定时长(分钟,1–1440):项目无运行中会话、无后台命令
|
|
159
166
|
// 且非当前选中,持续满该时长后释放其会话进程
|
|
@@ -515,7 +522,9 @@ export function startHelper({ port, token, dataRoot, state, agentsFile = default
|
|
|
515
522
|
url: bt.bg && bt.bg.url !== undefined ? bt.bg.url : cur.bg.url,
|
|
516
523
|
intervalMin: bt.bg && bt.bg.intervalMin !== undefined ? bt.bg.intervalMin : cur.bg.intervalMin,
|
|
517
524
|
fit: bt.bg && bt.bg.fit !== undefined ? bt.bg.fit : cur.bg.fit,
|
|
518
|
-
|
|
525
|
+
maskOpacity: bt.bg && bt.bg.maskOpacity !== undefined ? bt.bg.maskOpacity : cur.bg.maskOpacity,
|
|
526
|
+
blurPx: bt.bg && bt.bg.blurPx !== undefined ? bt.bg.blurPx : cur.bg.blurPx,
|
|
527
|
+
panelOpacity: bt.bg && bt.bg.panelOpacity !== undefined ? bt.bg.panelOpacity : cur.bg.panelOpacity,
|
|
519
528
|
regions: bt.bg && bt.bg.regions !== undefined ? bt.bg.regions : cur.bg.regions,
|
|
520
529
|
},
|
|
521
530
|
};
|
package/src/inject/core.js
CHANGED
|
@@ -127,7 +127,7 @@ const zh = {
|
|
|
127
127
|
agentsSave: '保存',
|
|
128
128
|
agentsSaved: '全局提示词已保存',
|
|
129
129
|
agentsLoadFailed: '读取全局提示词失败',
|
|
130
|
-
proxyDesc: '为 ZCode Pro 本地助手发起的网络访问设置 HTTP 代理:插件市场的更新与安装、插件依赖软件的安装与升级、WebDAV 同步都可走该代理,用下方开关选择生效范围。不影响 ZCode
|
|
130
|
+
proxyDesc: '为 ZCode Pro 本地助手发起的网络访问设置 HTTP 代理:插件市场的更新与安装、插件依赖软件的安装与升级、WebDAV 同步都可走该代理,用下方开关选择生效范围。不影响 ZCode 应用本身与模型请求。',
|
|
131
131
|
proxyPlaceholder: 'http://127.0.0.1:7890',
|
|
132
132
|
proxySave: '保存',
|
|
133
133
|
proxySaved: '代理设置已保存',
|
|
@@ -141,7 +141,7 @@ const zh = {
|
|
|
141
141
|
proxyScopeDeps: '插件依赖软件',
|
|
142
142
|
proxyScopeDepsDesc: 'headroom 与 rtk 的安装、检查更新与升级(pip 与 GitHub 下载)走代理。',
|
|
143
143
|
proxyScopeSync: '同步',
|
|
144
|
-
proxyScopeSyncDesc: 'WebDAV
|
|
144
|
+
proxyScopeSyncDesc: 'WebDAV 上传与下载走代理。',
|
|
145
145
|
tabSync: '同步',
|
|
146
146
|
tabDebug: '调试',
|
|
147
147
|
testNoSessionHint: '选择“跟随当前会话[当前模型]”时,选择上次会话中使用的供应商[模型]',
|
|
@@ -228,13 +228,13 @@ const zh = {
|
|
|
228
228
|
defaultValue: '默认',
|
|
229
229
|
resetDefault: '恢复默认',
|
|
230
230
|
featureProjectMenu: '项目菜单增强',
|
|
231
|
-
featureProjectMenuDesc: '
|
|
231
|
+
featureProjectMenuDesc: '项目更多菜单:打开文件夹与复制路径(别名与切换文件夹已停用)。',
|
|
232
232
|
featureTaskOrder: '侧边栏会话拖动排序',
|
|
233
|
-
featureTaskOrderDesc: '
|
|
233
|
+
featureTaskOrderDesc: '拖动调整置顶、项目与分组中的会话顺序,重启后不变。',
|
|
234
234
|
featureSessionSwitch: '会话快捷切换',
|
|
235
|
-
featureSessionSwitchDesc: '{last}
|
|
235
|
+
featureSessionSwitchDesc: '{last} 来回上次会话;alt+方向键/快捷键选最近会话;键位可改',
|
|
236
236
|
featureTitleLock: '重命名会话名锁定',
|
|
237
|
-
featureTitleLockDesc: '
|
|
237
|
+
featureTitleLockDesc: '手动重命名过的会话不被自动标题覆盖,侧栏始终显示重命名后的名字。',
|
|
238
238
|
tabTitleGen: '任务名提示词',
|
|
239
239
|
titleGenShortDesc: '新会话开始作答时,按所选供应商与提示词生成任务名并按手动重命名保存,官方自动标题不再覆盖。',
|
|
240
240
|
titleGenEnabled: '启用',
|
|
@@ -260,7 +260,7 @@ const zh = {
|
|
|
260
260
|
titleGenTestOk: '生成结果:',
|
|
261
261
|
titleGenTestFailed: '生成失败',
|
|
262
262
|
featureWsRunning: '折叠时任务运行中提示',
|
|
263
|
-
featureWsRunningDesc: '
|
|
263
|
+
featureWsRunningDesc: '有会话运行时,折叠的项目与任务分区头显示加载图标,全部结束消失。',
|
|
264
264
|
tabPerformance: '性能',
|
|
265
265
|
perfDesc: 'ZCode 为每个打开过的项目常驻一个会话进程,应用自身不会回收,项目多了内存占用就高。开启后,长时间未使用且没有任务在跑的项目会被自动释放:会话记录不受影响,再次打开时自动恢复,仅首次回复有几秒等待。正在使用的项目、运行中的会话与后台命令、定时任务都不会被回收。后台命令检测依赖系统进程信息(Linux / macOS / Windows 各走系统自带工具),检测不可用时功能自动停用。',
|
|
266
266
|
featureIdleReclaim: '空闲项目内存回收',
|
|
@@ -270,17 +270,17 @@ const zh = {
|
|
|
270
270
|
idleReclaimStartupHint: '应用启动会为每个恢复的项目预热进程,到点把未使用的项目一次收掉;0 = 启动后立即回收,该次不受上一项空闲时长限制',
|
|
271
271
|
idleReclaimUnit: '分钟',
|
|
272
272
|
featureToolbarIcons: '侧栏菜单并入顶栏',
|
|
273
|
-
featureToolbarIconsDesc: '
|
|
273
|
+
featureToolbarIconsDesc: '新建任务/搜索/自动化/插件市场收成顶栏图标按钮。',
|
|
274
274
|
tbNewTask: '新建任务',
|
|
275
275
|
tbSearch: '搜索',
|
|
276
276
|
tbAutomations: '自动化',
|
|
277
277
|
tbPluginStore: '插件市场',
|
|
278
278
|
featurePinnedCollapse: '已置顶分区可折叠',
|
|
279
|
-
featurePinnedCollapseDesc: '
|
|
279
|
+
featurePinnedCollapseDesc: '侧边栏已置顶标题可点击折叠/展开其任务列表。',
|
|
280
280
|
featureSandboxReclaim: '插件界面沙箱回收',
|
|
281
|
-
featureSandboxReclaimDesc: '
|
|
281
|
+
featureSandboxReclaimDesc: '沙箱满 64 个时自动释放最早的,新卡片不再打不开;旧卡重新打开所在会话即可恢复。',
|
|
282
282
|
featurePluginCardRetry: '插件卡片自动重试',
|
|
283
|
-
featurePluginCardRetryDesc: '
|
|
283
|
+
featurePluginCardRetryDesc: '因名额上限加载失败的插件卡片自动点击重新加载,名额空出后自动恢复。',
|
|
284
284
|
visionCardUi: '思考过程显示识图卡片',
|
|
285
285
|
pinnedToggleAria: '折叠/展开已置顶',
|
|
286
286
|
sidebarProjectSpacingName: '项目间距',
|
|
@@ -303,7 +303,7 @@ const zh = {
|
|
|
303
303
|
switcherAttentionHint: '运行中 / 运行完未查看的任务 · {prev} / {next} 或 ↑ / ↓ 选择,Enter 或松开 alt 切换',
|
|
304
304
|
switcherAttentionEmpty: '暂无运行中或未查看的任务',
|
|
305
305
|
featureNewtabPanels: '新标签页插件面板入口',
|
|
306
|
-
featureNewtabPanelsDesc: '
|
|
306
|
+
featureNewtabPanelsDesc: '新标签页加入第三方插件(headroom、rtk 等)的面板卡片,点击在侧边面板打开。',
|
|
307
307
|
ntpDescSuffix: '插件面板',
|
|
308
308
|
ntpNeedSession: '先打开一个任务会话,再从新标签页打开插件面板',
|
|
309
309
|
featureStatusBarDesc: '提问输入框右下角和每轮回答最后显示统计项。',
|
|
@@ -317,12 +317,12 @@ const zh = {
|
|
|
317
317
|
themeBgRegionRight: '右侧边栏',
|
|
318
318
|
themeBgPane: '背景图',
|
|
319
319
|
themeSkinTitle: '皮肤',
|
|
320
|
-
themeSkinDesc: '
|
|
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,8 +354,15 @@ const zh = {
|
|
|
354
354
|
themeBgFitContain: '适应',
|
|
355
355
|
themeBgFitFill: '拉伸',
|
|
356
356
|
themeBgFitTile: '平铺',
|
|
357
|
-
|
|
357
|
+
themeBgMask: '遮罩不透明度',
|
|
358
|
+
themeBgMaskHint: '数值越高,图片越向主题背景色淡化,文字对比越清晰',
|
|
359
|
+
themeBgMaskHint2: '图片向主题背景色淡化的程度',
|
|
360
|
+
themeBgBlur: '图片模糊',
|
|
361
|
+
themeBgPanel: '面板不透明度',
|
|
362
|
+
themeBgPanelHint: '侧栏、面板与标签栏的不透明程度。越低,透出的图片越多。',
|
|
358
363
|
themeBgDeletedActive: '当前背景已删除,已自动停用背景',
|
|
364
|
+
tableCopyBtn: '复制表格',
|
|
365
|
+
tableCopied: '表格已复制',
|
|
359
366
|
sbItemCtx: '上下文占用',
|
|
360
367
|
sbItemQuestions: '提问次数',
|
|
361
368
|
sbMaster: '启用统计',
|
|
@@ -408,7 +415,11 @@ const zh = {
|
|
|
408
415
|
featureFileActions: '文件菜单增强',
|
|
409
416
|
featureFileActionsDesc: '在会话中文件链接的右键菜单里新增“默认应用打开”与“打开所在目录”。',
|
|
410
417
|
featureImageCopy: '图片右键复制',
|
|
418
|
+
featureTableCopy: '复制表格',
|
|
419
|
+
featureComposerProvider: '模型名显示供应商',
|
|
411
420
|
featureImageCopyDesc: '右键会话中的图片或点击放大的预览图,可将图片复制到剪贴板。',
|
|
421
|
+
featureTableCopyDesc: '表格工具条新增复制表格,粘贴进 Excel / 富文本编辑器即为干净表格。',
|
|
422
|
+
featureComposerProviderDesc: '模型名前显示供应商名(如 BigModel-Max1 / glm-5.3),区分同名模型。',
|
|
412
423
|
imageCopy: '复制图片',
|
|
413
424
|
imageCopied: '图片已复制到剪贴板',
|
|
414
425
|
imageCopyFailed: '复制图片失败',
|
|
@@ -431,7 +442,7 @@ const zh = {
|
|
|
431
442
|
pluginsUpdatesAvailable: 'zcode-plugins 有 {n} 个插件可更新(ZCode Pro 设置 → 功能)',
|
|
432
443
|
pluginsAutoUpdated: 'zcode-plugins 插件已更新',
|
|
433
444
|
tabVision: 'Vision',
|
|
434
|
-
visionDesc: '主模型不支持图片输入时(如 glm-5.3),自动把消息里的图片交给下面的视觉模型识别,把描述带给主模型(zcode-vision 插件;/vision-*
|
|
445
|
+
visionDesc: '主模型不支持图片输入时(如 glm-5.3),自动把消息里的图片交给下面的视觉模型识别,把描述带给主模型(zcode-vision 插件;/vision-* 命令编辑同一配置)。',
|
|
435
446
|
visionEnabled: '启用',
|
|
436
447
|
visionForceIntercept: '主模型能看图也拦截',
|
|
437
448
|
visionMode: '链模式',
|
|
@@ -462,14 +473,14 @@ const zh = {
|
|
|
462
473
|
visionCompressKBHint: '超过该大小的图先压缩再识别(最长边 2000、JPEG);0 = 不压缩',
|
|
463
474
|
visionLoadFailed: '读取视觉代理配置失败',
|
|
464
475
|
tabRtk: 'rtk',
|
|
465
|
-
rtkDesc: 'rtk 插件:把常见开发命令的输出压缩 60-90% 再进入上下文(/rtk-*
|
|
476
|
+
rtkDesc: 'rtk 插件:把常见开发命令的输出压缩 60-90% 再进入上下文(/rtk-* 命令编辑同一配置)。',
|
|
466
477
|
rtkEnabled: '启用',
|
|
467
478
|
rtkNotInstalled: '未安装 rtk 本体;安装后以下设置生效(会话里执行 /rtk-setup 也可以)',
|
|
468
479
|
rtkInstallBin: '安装 rtk 本体',
|
|
469
480
|
rtkBinInstalled: 'rtk 本体已安装;PATH 需注销重登后对会话生效,插件钩子即时生效',
|
|
470
481
|
rtkWhitelistTitle: '白名单(不压缩直接放行)',
|
|
471
482
|
rtkBuiltinLabel: '内置放行(只读,随插件更新):',
|
|
472
|
-
rtkWhitelistDesc: '每条 name(如 docker)或 git:name(如 git:clone
|
|
483
|
+
rtkWhitelistDesc: '每条 name(如 docker)或 git:name(如 git:clone),加入后不再压缩;内置的 git add/commit/push、mkdir/cp 等不可移除。',
|
|
473
484
|
rtkWhitelistPlaceholder: 'name 或 git:name,回车添加',
|
|
474
485
|
rtkWhitelistAdd: '添加',
|
|
475
486
|
rtkWhitelistClear: '清空',
|
|
@@ -477,18 +488,18 @@ const zh = {
|
|
|
477
488
|
rtkWhitelistInvalid: '条目格式应为 name 或 git:name',
|
|
478
489
|
rtkLoadFailed: '读取 rtk 配置失败',
|
|
479
490
|
tabHeadroom: 'Headroom',
|
|
480
|
-
headroomDesc: 'Headroom 插件:本地压缩代理驻留 127.0.0.1(默认 8787 端口),压缩发往模型的上下文以省 token(/hr-*
|
|
491
|
+
headroomDesc: 'Headroom 插件:本地压缩代理驻留 127.0.0.1(默认 8787 端口),压缩发往模型的上下文以省 token(/hr-* 命令编辑同一配置)。切换设备会重启代理,正在处理的请求可能中断一次。',
|
|
481
492
|
headroomBinMissing: '未找到 headroom 程序,代理无法启动;安装 headroom 后以下设置自动生效',
|
|
482
493
|
hrInstallBin: '安装 headroom 本体',
|
|
483
494
|
hrBinInstalled: 'headroom 本体已安装;重新打开会话后代理可用(SessionStart 钩子自动拉起)',
|
|
484
495
|
headroomInstallTitle: '未安装 headroom 插件',
|
|
485
|
-
headroomInstallDesc: '从 zcode-plugins
|
|
496
|
+
headroomInstallDesc: '从 zcode-plugins 市场安装后即可在此管理压缩代理;重启会话后生效。',
|
|
486
497
|
headroomInstall: '安装',
|
|
487
498
|
headroomInstalling: '安装中…',
|
|
488
499
|
headroomInstalled: '已安装 headroom 插件',
|
|
489
500
|
headroomInstallRetry: '安装未生效,请重试或手动在插件市场安装',
|
|
490
501
|
pluginInstallCardTitle: '未安装 {name} 插件',
|
|
491
|
-
pluginInstallCardDesc: '从 zcode-plugins
|
|
502
|
+
pluginInstallCardDesc: '从 zcode-plugins 市场安装后即可在此管理;重启会话后生效。',
|
|
492
503
|
pluginInstallDone: '已安装 {name} 插件',
|
|
493
504
|
pluginEnable: '启用',
|
|
494
505
|
hrStarting: '正在启动压缩代理…',
|
|
@@ -531,7 +542,7 @@ const zh = {
|
|
|
531
542
|
headroomBackendAuto: '自动 · 显卡优先',
|
|
532
543
|
headroomBackendCpu: '省电 · CPU 优先',
|
|
533
544
|
headroomBackendNative: '(原生值)',
|
|
534
|
-
headroomBackendDesc: '自动:headroom 选最快设备(CUDA/MPS 显卡优先);CPU:强制 CPU 压缩,省电且不占显存。也支持 headroom 原生值(onnx、pytorch_mps
|
|
545
|
+
headroomBackendDesc: '自动:headroom 选最快设备(CUDA/MPS 显卡优先);CPU:强制 CPU 压缩,省电且不占显存。也支持 headroom 原生值(onnx、pytorch_mps 等)。',
|
|
535
546
|
headroomPower: '省电自动切换',
|
|
536
547
|
headroomPowerOff: '关闭',
|
|
537
548
|
headroomPowerOffDesc: '不自动切换,始终按“压缩设备”运行。',
|
|
@@ -541,7 +552,7 @@ const zh = {
|
|
|
541
552
|
headroomPowerSaverDesc: '仅系统进入省电模式 → CPU 压缩,退出切回。',
|
|
542
553
|
hrCpuPinnedHint: '压缩设备已固定为 CPU,省电自动切换不会再改变它。',
|
|
543
554
|
headroomInterval: '监视间隔',
|
|
544
|
-
headroomIntervalDesc: '省电监视器轮询电源状态的间隔(10–3600 秒,默认 60
|
|
555
|
+
headroomIntervalDesc: '省电监视器轮询电源状态的间隔(10–3600 秒,默认 60);保存后从下一轮巡检起生效。',
|
|
545
556
|
headroomSecUnit: '秒',
|
|
546
557
|
upgradeCheck: '检查更新',
|
|
547
558
|
upgradeChecking: '检查中…',
|
|
@@ -629,7 +640,7 @@ const en = {
|
|
|
629
640
|
agentsSave: 'Save',
|
|
630
641
|
agentsSaved: 'Global prompt saved.',
|
|
631
642
|
agentsLoadFailed: 'Failed to load the global prompt',
|
|
632
|
-
proxyDesc: 'Set an HTTP proxy for network access made by the local helper: plugin marketplace updates and installs, plugin dependency installs and upgrades, and WebDAV sync can all use it — pick the scopes with the toggles below. Does not affect the ZCode app itself or model requests.
|
|
643
|
+
proxyDesc: 'Set an HTTP proxy for network access made by the local helper: plugin marketplace updates and installs, plugin dependency installs and upgrades, and WebDAV sync can all use it — pick the scopes with the toggles below. Does not affect the ZCode app itself or model requests.',
|
|
633
644
|
proxyPlaceholder: 'http://127.0.0.1:7890',
|
|
634
645
|
proxySave: 'Save',
|
|
635
646
|
proxySaved: 'Proxy settings saved.',
|
|
@@ -643,7 +654,7 @@ const en = {
|
|
|
643
654
|
proxyScopeDeps: 'Plugin dependencies',
|
|
644
655
|
proxyScopeDepsDesc: 'headroom and rtk installs, update checks and upgrades (pip and GitHub downloads) go through the proxy.',
|
|
645
656
|
proxyScopeSync: 'Sync',
|
|
646
|
-
proxyScopeSyncDesc: 'WebDAV upload and download go through the proxy.
|
|
657
|
+
proxyScopeSyncDesc: 'WebDAV upload and download go through the proxy.',
|
|
647
658
|
tabSync: 'Sync',
|
|
648
659
|
tabDebug: 'Debug',
|
|
649
660
|
testNoSessionHint: 'With “follow current session [current model]” selected, the provider [model] of the last session is used.',
|
|
@@ -730,13 +741,13 @@ const en = {
|
|
|
730
741
|
defaultValue: 'default',
|
|
731
742
|
resetDefault: 'Reset to default',
|
|
732
743
|
featureProjectMenu: 'Project menu actions',
|
|
733
|
-
featureProjectMenuDesc: '
|
|
744
|
+
featureProjectMenuDesc: 'Open folder and copy path in the project More menu (alias and folder switch retired).',
|
|
734
745
|
featureTaskOrder: 'Sidebar session drag ordering',
|
|
735
|
-
featureTaskOrderDesc: '
|
|
746
|
+
featureTaskOrderDesc: 'Drag to reorder sessions in Pinned, Projects and Groups; survives restarts.',
|
|
736
747
|
featureSessionSwitch: 'Session quick switch',
|
|
737
|
-
featureSessionSwitchDesc: '{last} toggles
|
|
748
|
+
featureSessionSwitchDesc: '{last} toggles to the last session; alt + arrows pick recent sessions; keys remappable',
|
|
738
749
|
featureTitleLock: 'Keep renamed session titles',
|
|
739
|
-
featureTitleLockDesc: 'Sessions you renamed keep their custom title
|
|
750
|
+
featureTitleLockDesc: 'Sessions you renamed keep their custom title in the sidebar.',
|
|
740
751
|
tabTitleGen: 'Task name prompt',
|
|
741
752
|
titleGenShortDesc: 'When a new session starts answering, generates the task name with the chosen provider and prompt and saves it as a manual rename the built-in title never overrides.',
|
|
742
753
|
titleGenEnabled: 'Enable',
|
|
@@ -762,7 +773,7 @@ const en = {
|
|
|
762
773
|
titleGenTestOk: 'Result: ',
|
|
763
774
|
titleGenTestFailed: 'Generation failed',
|
|
764
775
|
featureWsRunning: 'Running indicator when collapsed',
|
|
765
|
-
featureWsRunningDesc: '
|
|
776
|
+
featureWsRunningDesc: 'Collapsed project and task headers show a loader while sessions run.',
|
|
766
777
|
tabPerformance: 'Performance',
|
|
767
778
|
perfDesc: 'ZCode keeps a session process resident for every project you have opened and never reclaims it, so memory grows with projects. When enabled, projects unused for a while with nothing running release their memory automatically: sessions are untouched and reload when reopened, with a few seconds wait on the first reply. The project in use, running sessions, background commands and scheduled tasks are never reclaimed. Background-command detection reads system process info via built-in tools (Linux / macOS / Windows); when unavailable, the feature disables itself.',
|
|
768
779
|
featureIdleReclaim: 'Idle project memory reclaim',
|
|
@@ -772,15 +783,15 @@ const en = {
|
|
|
772
783
|
idleReclaimStartupHint: 'Startup pre-spawns a process per restored project; this pass collects the unused ones once. 0 = reclaim right after startup, ignoring the idle minutes above',
|
|
773
784
|
idleReclaimUnit: 'min',
|
|
774
785
|
featureToolbarIcons: 'Sidebar menu in the top bar',
|
|
775
|
-
featureToolbarIconsDesc: '
|
|
786
|
+
featureToolbarIconsDesc: 'New task / search / automations / plugin store become top-bar icon buttons.',
|
|
776
787
|
tbNewTask: 'New task',
|
|
777
788
|
tbSearch: 'Search',
|
|
778
789
|
tbAutomations: 'Automations',
|
|
779
790
|
tbPluginStore: 'Plugin store',
|
|
780
791
|
featurePinnedCollapse: 'Collapsible Pinned section',
|
|
781
|
-
featurePinnedCollapseDesc: 'The
|
|
792
|
+
featurePinnedCollapseDesc: 'The Pinned header collapses/expands its task list.',
|
|
782
793
|
featureSandboxReclaim: 'Plugin card sandbox reclaim',
|
|
783
|
-
featureSandboxReclaimDesc: '
|
|
794
|
+
featureSandboxReclaimDesc: 'Reclaims the oldest sandboxes at the 64 cap so new cards keep opening; released cards come back when their session is reopened.',
|
|
784
795
|
featurePluginCardRetry: 'Plugin card auto-retry',
|
|
785
796
|
featurePluginCardRetryDesc: 'Auto-clicks "Reload" on cards that failed the slot cap; they recover as soon as a slot frees up.',
|
|
786
797
|
visionCardUi: 'Vision cards',
|
|
@@ -805,7 +816,7 @@ const en = {
|
|
|
805
816
|
switcherAttentionHint: 'Running / finished but unseen tasks · {prev} / {next} or ↑ / ↓ to choose, Enter or release alt to switch',
|
|
806
817
|
switcherAttentionEmpty: 'No running or unseen tasks',
|
|
807
818
|
featureNewtabPanels: 'Plugin panels on the new tab page',
|
|
808
|
-
featureNewtabPanelsDesc: "
|
|
819
|
+
featureNewtabPanelsDesc: "Adds third-party plugin panels (headroom, rtk, ...) to the new tab page; click to open in the side pane.",
|
|
809
820
|
ntpDescSuffix: 'plugin panel',
|
|
810
821
|
ntpNeedSession: 'Open a task session first, then open plugin panels from the new tab page',
|
|
811
822
|
featureStatusBarDesc: 'Shows statistics at the bottom-right of the input box and at the end of each answer.',
|
|
@@ -819,12 +830,12 @@ const en = {
|
|
|
819
830
|
themeBgRegionRight: 'Right sidebar',
|
|
820
831
|
themeBgPane: 'Background',
|
|
821
832
|
themeSkinTitle: 'Skins',
|
|
822
|
-
themeSkinDesc: 'Famous editor theme palettes
|
|
833
|
+
themeSkinDesc: 'Famous editor theme palettes, auto-matching ZCode\u2019s light / dark mode; "Default" keeps ZCode as-is, "Custom" is tuned below.',
|
|
823
834
|
themeSkinCustom: 'Custom',
|
|
824
835
|
themeCustomTitle: 'Custom theme',
|
|
825
|
-
themeCustomHint: 'Color changes apply live (auto-switches to the custom skin); light and dark
|
|
826
|
-
themeCustomLight: 'Light
|
|
827
|
-
themeCustomDark: 'Dark
|
|
836
|
+
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.',
|
|
837
|
+
themeCustomLight: 'Light',
|
|
838
|
+
themeCustomDark: 'Dark',
|
|
828
839
|
themeCustomBg: 'Background',
|
|
829
840
|
themeCustomCard: 'Card',
|
|
830
841
|
themeCustomFg: 'Text',
|
|
@@ -856,8 +867,15 @@ const en = {
|
|
|
856
867
|
themeBgFitContain: 'Fit',
|
|
857
868
|
themeBgFitFill: 'Stretch',
|
|
858
869
|
themeBgFitTile: 'Tile',
|
|
859
|
-
|
|
870
|
+
themeBgMask: 'Mask opacity',
|
|
871
|
+
themeBgMaskHint: 'Higher values fade the image toward the theme background color for clearer text',
|
|
872
|
+
themeBgMaskHint2: 'How much the image fades toward the theme background color',
|
|
873
|
+
themeBgBlur: 'Image blur',
|
|
874
|
+
themeBgPanel: 'Panel opacity',
|
|
875
|
+
themeBgPanelHint: 'Opacity of sidebar, panels and tab bar. Lower reveals more of the image.',
|
|
860
876
|
themeBgDeletedActive: 'Active background was deleted; background disabled',
|
|
877
|
+
tableCopyBtn: 'Copy table',
|
|
878
|
+
tableCopied: 'Table copied',
|
|
861
879
|
sbItemCtx: 'Context usage',
|
|
862
880
|
sbItemQuestions: 'Questions asked',
|
|
863
881
|
sbMaster: 'Enable statistics',
|
|
@@ -910,7 +928,11 @@ const en = {
|
|
|
910
928
|
featureFileActions: 'File menu actions',
|
|
911
929
|
featureFileActionsDesc: 'Adds "Open with default app" and "Reveal in file manager" to the right-click menu of file links in chat.',
|
|
912
930
|
featureImageCopy: 'Image right-click copy',
|
|
931
|
+
featureTableCopy: 'Copy table',
|
|
932
|
+
featureComposerProvider: 'Provider in model name',
|
|
913
933
|
featureImageCopyDesc: 'Right-click an image in chat or the enlarged preview to copy it to the clipboard.',
|
|
934
|
+
featureTableCopyDesc: 'Copy-table button on the table toolbar; pastes into Excel / editors as a clean table.',
|
|
935
|
+
featureComposerProviderDesc: 'Shows the provider before the model name (e.g. BigModel-Max1 / glm-5.3).',
|
|
914
936
|
imageCopy: 'Copy image',
|
|
915
937
|
imageCopied: 'Image copied to the clipboard.',
|
|
916
938
|
imageCopyFailed: 'Failed to copy the image.',
|
|
@@ -933,7 +955,7 @@ const en = {
|
|
|
933
955
|
pluginsUpdatesAvailable: '{n} zcode-plugins update(s) available (ZCode Pro settings → Features)',
|
|
934
956
|
pluginsAutoUpdated: 'zcode-plugins updated',
|
|
935
957
|
tabVision: 'Vision',
|
|
936
|
-
visionDesc: 'When the main model cannot see images (e.g. glm-5.3), images in messages are recognized by the vision models below and passed to the main model as text (zcode-vision plugin; /vision-* commands edit the same file).
|
|
958
|
+
visionDesc: 'When the main model cannot see images (e.g. glm-5.3), images in messages are recognized by the vision models below and passed to the main model as text (zcode-vision plugin; /vision-* commands edit the same file).',
|
|
937
959
|
visionEnabled: 'Enable',
|
|
938
960
|
visionForceIntercept: 'Intercept even if the main model sees images',
|
|
939
961
|
visionMode: 'Chain mode',
|
|
@@ -964,14 +986,14 @@ const en = {
|
|
|
964
986
|
visionCompressKBHint: 'Images above this size are compressed first (max edge 2000, JPEG); 0 = never',
|
|
965
987
|
visionLoadFailed: 'Failed to load the vision config',
|
|
966
988
|
tabRtk: 'rtk',
|
|
967
|
-
rtkDesc: 'rtk plugin: compresses common dev command output by 60-90% before it reaches the context (/rtk-* commands edit the same files).
|
|
989
|
+
rtkDesc: 'rtk plugin: compresses common dev command output by 60-90% before it reaches the context (/rtk-* commands edit the same files).',
|
|
968
990
|
rtkEnabled: 'Enable',
|
|
969
991
|
rtkNotInstalled: 'rtk is not installed. The settings below take effect after installation (/rtk-setup in a session).',
|
|
970
992
|
rtkInstallBin: 'Install rtk binary',
|
|
971
993
|
rtkBinInstalled: 'rtk installed; PATH takes effect for sessions after signing out and back in — the plugin hook works immediately',
|
|
972
994
|
rtkWhitelistTitle: 'Whitelist (pass through without compression)',
|
|
973
995
|
rtkBuiltinLabel: 'Built-in exemptions (read-only, follow plugin updates):',
|
|
974
|
-
rtkWhitelistDesc: 'Each entry is name (e.g. docker) or git:name (e.g. git:clone)
|
|
996
|
+
rtkWhitelistDesc: 'Each entry is name (e.g. docker) or git:name (e.g. git:clone); added commands skip compression. Built-ins (git add/commit/push, mkdir/cp, …) cannot be removed.',
|
|
975
997
|
rtkWhitelistPlaceholder: 'name or git:name, press Enter to add',
|
|
976
998
|
rtkWhitelistAdd: 'Add',
|
|
977
999
|
rtkWhitelistClear: 'Clear',
|
|
@@ -979,18 +1001,18 @@ const en = {
|
|
|
979
1001
|
rtkWhitelistInvalid: 'Entry must be name or git:name',
|
|
980
1002
|
rtkLoadFailed: 'Failed to load the rtk config',
|
|
981
1003
|
tabHeadroom: 'Headroom',
|
|
982
|
-
headroomDesc: 'Headroom plugin: a local compression proxy on 127.0.0.1 (port 8787 by default) that compresses context sent to the model to save tokens (/hr-* commands edit the same file).
|
|
1004
|
+
headroomDesc: 'Headroom plugin: a local compression proxy on 127.0.0.1 (port 8787 by default) that compresses context sent to the model to save tokens (/hr-* commands edit the same file). Switching the device restarts the proxy, so requests in progress may be interrupted once.',
|
|
983
1005
|
headroomBinMissing: 'The headroom program was not found, so the proxy cannot start. The settings below take effect once it is installed.',
|
|
984
1006
|
hrInstallBin: 'Install headroom binary',
|
|
985
1007
|
hrBinInstalled: 'headroom installed; reopen a session and the hook will start the proxy automatically',
|
|
986
1008
|
headroomInstallTitle: 'headroom plugin not installed',
|
|
987
|
-
headroomInstallDesc: 'Install it from the zcode-plugins marketplace to manage the compression proxy here;
|
|
1009
|
+
headroomInstallDesc: 'Install it from the zcode-plugins marketplace to manage the compression proxy here; takes effect after a session restart.',
|
|
988
1010
|
headroomInstall: 'Install',
|
|
989
1011
|
headroomInstalling: 'Installing…',
|
|
990
1012
|
headroomInstalled: 'headroom plugin installed',
|
|
991
1013
|
headroomInstallRetry: 'Installation did not take effect; retry or install it from the marketplace manually',
|
|
992
1014
|
pluginInstallCardTitle: '{name} plugin not installed',
|
|
993
|
-
pluginInstallCardDesc: 'Install it from the zcode-plugins marketplace to manage it here;
|
|
1015
|
+
pluginInstallCardDesc: 'Install it from the zcode-plugins marketplace to manage it here; takes effect after a session restart.',
|
|
994
1016
|
pluginInstallDone: '{name} plugin installed',
|
|
995
1017
|
pluginEnable: 'Enable',
|
|
996
1018
|
hrStarting: 'Starting compression proxy…',
|
|
@@ -1043,7 +1065,7 @@ const en = {
|
|
|
1043
1065
|
headroomPowerSaverDesc: 'Only the system power-saver mode → CPU; back when it exits.',
|
|
1044
1066
|
hrCpuPinnedHint: 'The compression device is pinned to CPU, so power-save switching no longer changes anything.',
|
|
1045
1067
|
headroomInterval: 'Watch interval',
|
|
1046
|
-
headroomIntervalDesc: 'Seconds between power checks (10–3600, default 60); applies from the next round
|
|
1068
|
+
headroomIntervalDesc: 'Seconds between power checks (10–3600, default 60); applies from the next round.',
|
|
1047
1069
|
headroomSecUnit: 's',
|
|
1048
1070
|
upgradeCheck: 'Check update',
|
|
1049
1071
|
upgradeChecking: 'Checking…',
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// 输入框模型名前显示供应商名:3.16.0 起官方触发器只显示模型名(如 glm-5.3),
|
|
2
|
+
// 同名模型在不同供应商下无法区分。按钮的 aria-label 本就带全名
|
|
3
|
+
//("BigModel-Max1/glm-5.3 最高"),从那里取 “/” 前的供应商段补到显示文本前,
|
|
4
|
+
// 显示为 “BigModel-Max1 / glm-5.3”。不监听点击、不改官方状态,纯显示增强;
|
|
5
|
+
// 应用重渲染会冲掉文本,MutationObserver 维持。
|
|
6
|
+
import { t, getConfig } from '../core.js';
|
|
7
|
+
|
|
8
|
+
let installed = false;
|
|
9
|
+
let enabled = true; // helper features.composerProvider(默认开)
|
|
10
|
+
|
|
11
|
+
// 从 aria-label 提取 “供应商 / 模型” 显示文本。
|
|
12
|
+
// label 形态:"<供应商>/<模型名> <附加说明(如 最高)>";有的供应商名本身
|
|
13
|
+
// 不含 /、有的官方项可能没有 /(旧版内置项),没有 / 时返回 null 不改
|
|
14
|
+
function labelToDisplay(label) {
|
|
15
|
+
const i = label.indexOf('/');
|
|
16
|
+
if (i <= 0) return null;
|
|
17
|
+
const provider = label.slice(0, i).trim();
|
|
18
|
+
const rest = label.slice(i + 1);
|
|
19
|
+
const model = rest.trim().split(/\s+/)[0];
|
|
20
|
+
if (!provider || !model) return null;
|
|
21
|
+
return `${provider} / ${model}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function apply() {
|
|
25
|
+
if (!enabled) {
|
|
26
|
+
// 关闭时把此前改写的文本还原为官方模型名(aria-label 的 “/” 后段),
|
|
27
|
+
// 不必等应用重渲染
|
|
28
|
+
for (const btn of document.querySelectorAll('[data-testid="chat-model-select-trigger"]')) {
|
|
29
|
+
if (!btn.dataset.zcodeproProviderShown) continue;
|
|
30
|
+
const label = btn.getAttribute('aria-label') || '';
|
|
31
|
+
const i = label.indexOf('/');
|
|
32
|
+
if (i <= 0) continue;
|
|
33
|
+
const model = label.slice(i + 1).trim().split(/\s+/)[0];
|
|
34
|
+
if (!model) continue;
|
|
35
|
+
for (const sp of btn.querySelectorAll('span[title]')) sp.setAttribute('title', model);
|
|
36
|
+
for (const el of btn.querySelectorAll('span[title] span:not([title])')) {
|
|
37
|
+
if (!el.querySelector('span') && el.textContent.includes('/')) el.textContent = model;
|
|
38
|
+
}
|
|
39
|
+
delete btn.dataset.zcodeproProviderShown;
|
|
40
|
+
}
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
for (const btn of document.querySelectorAll('[data-testid="chat-model-select-trigger"]')) {
|
|
44
|
+
// 只处理 a[data-composer-combined-model] 主触发器;aria-label 缺失时跳过
|
|
45
|
+
const label = btn.getAttribute('aria-label') || '';
|
|
46
|
+
const display = labelToDisplay(label);
|
|
47
|
+
if (!display) continue;
|
|
48
|
+
// 显示 span 是嵌套两层的 title 结构(外层裁剪容器 + 内层相对定位层),
|
|
49
|
+
// 文本都在最内层 span;只改文本与 title,不动结构
|
|
50
|
+
// 官方结构:外层 title 容器 > 内层 title 层 > 纯文本 span(模型名)。
|
|
51
|
+
// 只改 title 链内的文本——按“祖先带 title”识别模型名文本;同级的
|
|
52
|
+
// “最高/开启”说明 span(无 title 祖先链上的 title 层)不属于模型名,
|
|
53
|
+
// 不能碰(首版误把它也替换成全名)
|
|
54
|
+
const titleSpans = [...btn.querySelectorAll('span[title]')];
|
|
55
|
+
if (!titleSpans.length) continue;
|
|
56
|
+
const innermost = titleSpans.filter((sp) => !sp.querySelector('span[title]'));
|
|
57
|
+
if (!innermost.length) continue;
|
|
58
|
+
const holders = [];
|
|
59
|
+
for (const sp of innermost) {
|
|
60
|
+
sp.querySelectorAll('span:not([title])').forEach((el) => {
|
|
61
|
+
if (!el.querySelector('span') && el.textContent.trim() && !el.textContent.includes('/')) holders.push(el);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (!holders.length) continue;
|
|
65
|
+
for (const sp of titleSpans) {
|
|
66
|
+
if (sp.getAttribute('title') !== display) sp.setAttribute('title', display);
|
|
67
|
+
}
|
|
68
|
+
for (const el of holders) el.textContent = display;
|
|
69
|
+
btn.dataset.zcodeproProviderShown = '1';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function startComposerProvider() {
|
|
74
|
+
if (installed || typeof document === 'undefined') return;
|
|
75
|
+
installed = true;
|
|
76
|
+
// 先过滤再调度:变更不在输入框工具栏子树(composer 容器)内的批次直接忽略,
|
|
77
|
+
// 连去抖定时器都不排——流式输出的高频文本变更不会叫醒本模块
|
|
78
|
+
const COMPOSER_SEL = '[data-composer-layout], [data-composer-leading-content], [data-testid="chat-model-select-trigger"]';
|
|
79
|
+
const relevant = (muts) => {
|
|
80
|
+
for (const m of muts) {
|
|
81
|
+
if (m.type === 'childList') {
|
|
82
|
+
for (const n of [...m.addedNodes, ...m.removedNodes]) {
|
|
83
|
+
if (n instanceof Element && (n.matches?.(COMPOSER_SEL) || n.querySelector?.(COMPOSER_SEL))) return true;
|
|
84
|
+
}
|
|
85
|
+
} else if (m.type === 'characterData') {
|
|
86
|
+
let el = m.target.parentElement;
|
|
87
|
+
while (el && el !== document.body) {
|
|
88
|
+
if (el.matches?.('[data-composer-layout], [data-composer-leading-actions], [data-composer-leading-content]')) return true;
|
|
89
|
+
el = el.parentElement;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
};
|
|
95
|
+
const schedule = () => {
|
|
96
|
+
clearTimeout(schedule.t);
|
|
97
|
+
schedule.t = setTimeout(apply, 120);
|
|
98
|
+
};
|
|
99
|
+
void getConfig().then((cfg) => { enabled = cfg.features ? cfg.features.composerProvider !== false : true; }).catch(() => {});
|
|
100
|
+
window.addEventListener('zcodepro:config-changed', () => {
|
|
101
|
+
void getConfig(true).then((cfg) => {
|
|
102
|
+
const next = cfg.features ? cfg.features.composerProvider !== false : true;
|
|
103
|
+
if (next !== enabled) {
|
|
104
|
+
enabled = next;
|
|
105
|
+
apply(); // 开关切换即时生效:关时还原官方文本,开时补供应商名
|
|
106
|
+
}
|
|
107
|
+
}).catch(() => {});
|
|
108
|
+
});
|
|
109
|
+
const observer = new MutationObserver((muts) => { if (enabled && relevant(muts)) schedule(); });
|
|
110
|
+
observer.observe(document.body, { childList: true, subtree: true, characterData: true });
|
|
111
|
+
apply();
|
|
112
|
+
}
|