zcode-pro 1.4.1 → 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 +10 -9
- package/dist/inject.js +330 -53
- package/package.json +1 -1
- package/src/host/helper.mjs +3 -1
- package/src/inject/core.js +50 -38
- package/src/inject/features/composer-provider.js +112 -0
- package/src/inject/features/settings-dialog.js +36 -14
- package/src/inject/features/table-copy.js +148 -0
- package/src/inject/index.js +4 -0
- package/src/inject/ui.js +12 -1
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(数据根迁走时全局指令仍在原位)。
|
|
@@ -159,6 +159,8 @@ export function defaultConfig() {
|
|
|
159
159
|
idleReclaim: true, // 空闲项目内存回收:长时间无活动的项目释放其会话进程(时长 idleReclaimMinutes)
|
|
160
160
|
toolbarIcons: true, // 侧栏菜单并入顶栏:新建任务/搜索/自动化/插件市场收成顶栏图标按钮(原菜单隐藏,点击转发)
|
|
161
161
|
pinnedCollapse: true, // 已置顶分区可折叠:标题可点折叠/展开任务列表(状态记 localStorage)
|
|
162
|
+
tableCopy: true, // 表格工具条"复制表格"按钮:富文本剪贴板,粘贴即表格(table-copy.js)
|
|
163
|
+
composerProvider: true, // 输入框模型名前显示供应商名(composer-provider.js)
|
|
162
164
|
},
|
|
163
165
|
// 空闲项目内存回收的判定时长(分钟,1–1440):项目无运行中会话、无后台命令
|
|
164
166
|
// 且非当前选中,持续满该时长后释放其会话进程
|
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: '提问输入框右下角和每轮回答最后显示统计项。',
|
|
@@ -361,6 +361,8 @@ const zh = {
|
|
|
361
361
|
themeBgPanel: '面板不透明度',
|
|
362
362
|
themeBgPanelHint: '侧栏、面板与标签栏的不透明程度。越低,透出的图片越多。',
|
|
363
363
|
themeBgDeletedActive: '当前背景已删除,已自动停用背景',
|
|
364
|
+
tableCopyBtn: '复制表格',
|
|
365
|
+
tableCopied: '表格已复制',
|
|
364
366
|
sbItemCtx: '上下文占用',
|
|
365
367
|
sbItemQuestions: '提问次数',
|
|
366
368
|
sbMaster: '启用统计',
|
|
@@ -413,7 +415,11 @@ const zh = {
|
|
|
413
415
|
featureFileActions: '文件菜单增强',
|
|
414
416
|
featureFileActionsDesc: '在会话中文件链接的右键菜单里新增“默认应用打开”与“打开所在目录”。',
|
|
415
417
|
featureImageCopy: '图片右键复制',
|
|
418
|
+
featureTableCopy: '复制表格',
|
|
419
|
+
featureComposerProvider: '模型名显示供应商',
|
|
416
420
|
featureImageCopyDesc: '右键会话中的图片或点击放大的预览图,可将图片复制到剪贴板。',
|
|
421
|
+
featureTableCopyDesc: '表格工具条新增复制表格,粘贴进 Excel / 富文本编辑器即为干净表格。',
|
|
422
|
+
featureComposerProviderDesc: '模型名前显示供应商名(如 BigModel-Max1 / glm-5.3),区分同名模型。',
|
|
417
423
|
imageCopy: '复制图片',
|
|
418
424
|
imageCopied: '图片已复制到剪贴板',
|
|
419
425
|
imageCopyFailed: '复制图片失败',
|
|
@@ -436,7 +442,7 @@ const zh = {
|
|
|
436
442
|
pluginsUpdatesAvailable: 'zcode-plugins 有 {n} 个插件可更新(ZCode Pro 设置 → 功能)',
|
|
437
443
|
pluginsAutoUpdated: 'zcode-plugins 插件已更新',
|
|
438
444
|
tabVision: 'Vision',
|
|
439
|
-
visionDesc: '主模型不支持图片输入时(如 glm-5.3),自动把消息里的图片交给下面的视觉模型识别,把描述带给主模型(zcode-vision 插件;/vision-*
|
|
445
|
+
visionDesc: '主模型不支持图片输入时(如 glm-5.3),自动把消息里的图片交给下面的视觉模型识别,把描述带给主模型(zcode-vision 插件;/vision-* 命令编辑同一配置)。',
|
|
440
446
|
visionEnabled: '启用',
|
|
441
447
|
visionForceIntercept: '主模型能看图也拦截',
|
|
442
448
|
visionMode: '链模式',
|
|
@@ -467,14 +473,14 @@ const zh = {
|
|
|
467
473
|
visionCompressKBHint: '超过该大小的图先压缩再识别(最长边 2000、JPEG);0 = 不压缩',
|
|
468
474
|
visionLoadFailed: '读取视觉代理配置失败',
|
|
469
475
|
tabRtk: 'rtk',
|
|
470
|
-
rtkDesc: 'rtk 插件:把常见开发命令的输出压缩 60-90% 再进入上下文(/rtk-*
|
|
476
|
+
rtkDesc: 'rtk 插件:把常见开发命令的输出压缩 60-90% 再进入上下文(/rtk-* 命令编辑同一配置)。',
|
|
471
477
|
rtkEnabled: '启用',
|
|
472
478
|
rtkNotInstalled: '未安装 rtk 本体;安装后以下设置生效(会话里执行 /rtk-setup 也可以)',
|
|
473
479
|
rtkInstallBin: '安装 rtk 本体',
|
|
474
480
|
rtkBinInstalled: 'rtk 本体已安装;PATH 需注销重登后对会话生效,插件钩子即时生效',
|
|
475
481
|
rtkWhitelistTitle: '白名单(不压缩直接放行)',
|
|
476
482
|
rtkBuiltinLabel: '内置放行(只读,随插件更新):',
|
|
477
|
-
rtkWhitelistDesc: '每条 name(如 docker)或 git:name(如 git:clone
|
|
483
|
+
rtkWhitelistDesc: '每条 name(如 docker)或 git:name(如 git:clone),加入后不再压缩;内置的 git add/commit/push、mkdir/cp 等不可移除。',
|
|
478
484
|
rtkWhitelistPlaceholder: 'name 或 git:name,回车添加',
|
|
479
485
|
rtkWhitelistAdd: '添加',
|
|
480
486
|
rtkWhitelistClear: '清空',
|
|
@@ -482,18 +488,18 @@ const zh = {
|
|
|
482
488
|
rtkWhitelistInvalid: '条目格式应为 name 或 git:name',
|
|
483
489
|
rtkLoadFailed: '读取 rtk 配置失败',
|
|
484
490
|
tabHeadroom: 'Headroom',
|
|
485
|
-
headroomDesc: 'Headroom 插件:本地压缩代理驻留 127.0.0.1(默认 8787 端口),压缩发往模型的上下文以省 token(/hr-*
|
|
491
|
+
headroomDesc: 'Headroom 插件:本地压缩代理驻留 127.0.0.1(默认 8787 端口),压缩发往模型的上下文以省 token(/hr-* 命令编辑同一配置)。切换设备会重启代理,正在处理的请求可能中断一次。',
|
|
486
492
|
headroomBinMissing: '未找到 headroom 程序,代理无法启动;安装 headroom 后以下设置自动生效',
|
|
487
493
|
hrInstallBin: '安装 headroom 本体',
|
|
488
494
|
hrBinInstalled: 'headroom 本体已安装;重新打开会话后代理可用(SessionStart 钩子自动拉起)',
|
|
489
495
|
headroomInstallTitle: '未安装 headroom 插件',
|
|
490
|
-
headroomInstallDesc: '从 zcode-plugins
|
|
496
|
+
headroomInstallDesc: '从 zcode-plugins 市场安装后即可在此管理压缩代理;重启会话后生效。',
|
|
491
497
|
headroomInstall: '安装',
|
|
492
498
|
headroomInstalling: '安装中…',
|
|
493
499
|
headroomInstalled: '已安装 headroom 插件',
|
|
494
500
|
headroomInstallRetry: '安装未生效,请重试或手动在插件市场安装',
|
|
495
501
|
pluginInstallCardTitle: '未安装 {name} 插件',
|
|
496
|
-
pluginInstallCardDesc: '从 zcode-plugins
|
|
502
|
+
pluginInstallCardDesc: '从 zcode-plugins 市场安装后即可在此管理;重启会话后生效。',
|
|
497
503
|
pluginInstallDone: '已安装 {name} 插件',
|
|
498
504
|
pluginEnable: '启用',
|
|
499
505
|
hrStarting: '正在启动压缩代理…',
|
|
@@ -536,7 +542,7 @@ const zh = {
|
|
|
536
542
|
headroomBackendAuto: '自动 · 显卡优先',
|
|
537
543
|
headroomBackendCpu: '省电 · CPU 优先',
|
|
538
544
|
headroomBackendNative: '(原生值)',
|
|
539
|
-
headroomBackendDesc: '自动:headroom 选最快设备(CUDA/MPS 显卡优先);CPU:强制 CPU 压缩,省电且不占显存。也支持 headroom 原生值(onnx、pytorch_mps
|
|
545
|
+
headroomBackendDesc: '自动:headroom 选最快设备(CUDA/MPS 显卡优先);CPU:强制 CPU 压缩,省电且不占显存。也支持 headroom 原生值(onnx、pytorch_mps 等)。',
|
|
540
546
|
headroomPower: '省电自动切换',
|
|
541
547
|
headroomPowerOff: '关闭',
|
|
542
548
|
headroomPowerOffDesc: '不自动切换,始终按“压缩设备”运行。',
|
|
@@ -546,7 +552,7 @@ const zh = {
|
|
|
546
552
|
headroomPowerSaverDesc: '仅系统进入省电模式 → CPU 压缩,退出切回。',
|
|
547
553
|
hrCpuPinnedHint: '压缩设备已固定为 CPU,省电自动切换不会再改变它。',
|
|
548
554
|
headroomInterval: '监视间隔',
|
|
549
|
-
headroomIntervalDesc: '省电监视器轮询电源状态的间隔(10–3600 秒,默认 60
|
|
555
|
+
headroomIntervalDesc: '省电监视器轮询电源状态的间隔(10–3600 秒,默认 60);保存后从下一轮巡检起生效。',
|
|
550
556
|
headroomSecUnit: '秒',
|
|
551
557
|
upgradeCheck: '检查更新',
|
|
552
558
|
upgradeChecking: '检查中…',
|
|
@@ -634,7 +640,7 @@ const en = {
|
|
|
634
640
|
agentsSave: 'Save',
|
|
635
641
|
agentsSaved: 'Global prompt saved.',
|
|
636
642
|
agentsLoadFailed: 'Failed to load the global prompt',
|
|
637
|
-
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.',
|
|
638
644
|
proxyPlaceholder: 'http://127.0.0.1:7890',
|
|
639
645
|
proxySave: 'Save',
|
|
640
646
|
proxySaved: 'Proxy settings saved.',
|
|
@@ -648,7 +654,7 @@ const en = {
|
|
|
648
654
|
proxyScopeDeps: 'Plugin dependencies',
|
|
649
655
|
proxyScopeDepsDesc: 'headroom and rtk installs, update checks and upgrades (pip and GitHub downloads) go through the proxy.',
|
|
650
656
|
proxyScopeSync: 'Sync',
|
|
651
|
-
proxyScopeSyncDesc: 'WebDAV upload and download go through the proxy.
|
|
657
|
+
proxyScopeSyncDesc: 'WebDAV upload and download go through the proxy.',
|
|
652
658
|
tabSync: 'Sync',
|
|
653
659
|
tabDebug: 'Debug',
|
|
654
660
|
testNoSessionHint: 'With “follow current session [current model]” selected, the provider [model] of the last session is used.',
|
|
@@ -735,13 +741,13 @@ const en = {
|
|
|
735
741
|
defaultValue: 'default',
|
|
736
742
|
resetDefault: 'Reset to default',
|
|
737
743
|
featureProjectMenu: 'Project menu actions',
|
|
738
|
-
featureProjectMenuDesc: '
|
|
744
|
+
featureProjectMenuDesc: 'Open folder and copy path in the project More menu (alias and folder switch retired).',
|
|
739
745
|
featureTaskOrder: 'Sidebar session drag ordering',
|
|
740
|
-
featureTaskOrderDesc: '
|
|
746
|
+
featureTaskOrderDesc: 'Drag to reorder sessions in Pinned, Projects and Groups; survives restarts.',
|
|
741
747
|
featureSessionSwitch: 'Session quick switch',
|
|
742
|
-
featureSessionSwitchDesc: '{last} toggles
|
|
748
|
+
featureSessionSwitchDesc: '{last} toggles to the last session; alt + arrows pick recent sessions; keys remappable',
|
|
743
749
|
featureTitleLock: 'Keep renamed session titles',
|
|
744
|
-
featureTitleLockDesc: 'Sessions you renamed keep their custom title
|
|
750
|
+
featureTitleLockDesc: 'Sessions you renamed keep their custom title in the sidebar.',
|
|
745
751
|
tabTitleGen: 'Task name prompt',
|
|
746
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.',
|
|
747
753
|
titleGenEnabled: 'Enable',
|
|
@@ -767,7 +773,7 @@ const en = {
|
|
|
767
773
|
titleGenTestOk: 'Result: ',
|
|
768
774
|
titleGenTestFailed: 'Generation failed',
|
|
769
775
|
featureWsRunning: 'Running indicator when collapsed',
|
|
770
|
-
featureWsRunningDesc: '
|
|
776
|
+
featureWsRunningDesc: 'Collapsed project and task headers show a loader while sessions run.',
|
|
771
777
|
tabPerformance: 'Performance',
|
|
772
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.',
|
|
773
779
|
featureIdleReclaim: 'Idle project memory reclaim',
|
|
@@ -777,15 +783,15 @@ const en = {
|
|
|
777
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',
|
|
778
784
|
idleReclaimUnit: 'min',
|
|
779
785
|
featureToolbarIcons: 'Sidebar menu in the top bar',
|
|
780
|
-
featureToolbarIconsDesc: '
|
|
786
|
+
featureToolbarIconsDesc: 'New task / search / automations / plugin store become top-bar icon buttons.',
|
|
781
787
|
tbNewTask: 'New task',
|
|
782
788
|
tbSearch: 'Search',
|
|
783
789
|
tbAutomations: 'Automations',
|
|
784
790
|
tbPluginStore: 'Plugin store',
|
|
785
791
|
featurePinnedCollapse: 'Collapsible Pinned section',
|
|
786
|
-
featurePinnedCollapseDesc: 'The
|
|
792
|
+
featurePinnedCollapseDesc: 'The Pinned header collapses/expands its task list.',
|
|
787
793
|
featureSandboxReclaim: 'Plugin card sandbox reclaim',
|
|
788
|
-
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.',
|
|
789
795
|
featurePluginCardRetry: 'Plugin card auto-retry',
|
|
790
796
|
featurePluginCardRetryDesc: 'Auto-clicks "Reload" on cards that failed the slot cap; they recover as soon as a slot frees up.',
|
|
791
797
|
visionCardUi: 'Vision cards',
|
|
@@ -810,7 +816,7 @@ const en = {
|
|
|
810
816
|
switcherAttentionHint: 'Running / finished but unseen tasks · {prev} / {next} or ↑ / ↓ to choose, Enter or release alt to switch',
|
|
811
817
|
switcherAttentionEmpty: 'No running or unseen tasks',
|
|
812
818
|
featureNewtabPanels: 'Plugin panels on the new tab page',
|
|
813
|
-
featureNewtabPanelsDesc: "
|
|
819
|
+
featureNewtabPanelsDesc: "Adds third-party plugin panels (headroom, rtk, ...) to the new tab page; click to open in the side pane.",
|
|
814
820
|
ntpDescSuffix: 'plugin panel',
|
|
815
821
|
ntpNeedSession: 'Open a task session first, then open plugin panels from the new tab page',
|
|
816
822
|
featureStatusBarDesc: 'Shows statistics at the bottom-right of the input box and at the end of each answer.',
|
|
@@ -868,6 +874,8 @@ const en = {
|
|
|
868
874
|
themeBgPanel: 'Panel opacity',
|
|
869
875
|
themeBgPanelHint: 'Opacity of sidebar, panels and tab bar. Lower reveals more of the image.',
|
|
870
876
|
themeBgDeletedActive: 'Active background was deleted; background disabled',
|
|
877
|
+
tableCopyBtn: 'Copy table',
|
|
878
|
+
tableCopied: 'Table copied',
|
|
871
879
|
sbItemCtx: 'Context usage',
|
|
872
880
|
sbItemQuestions: 'Questions asked',
|
|
873
881
|
sbMaster: 'Enable statistics',
|
|
@@ -920,7 +928,11 @@ const en = {
|
|
|
920
928
|
featureFileActions: 'File menu actions',
|
|
921
929
|
featureFileActionsDesc: 'Adds "Open with default app" and "Reveal in file manager" to the right-click menu of file links in chat.',
|
|
922
930
|
featureImageCopy: 'Image right-click copy',
|
|
931
|
+
featureTableCopy: 'Copy table',
|
|
932
|
+
featureComposerProvider: 'Provider in model name',
|
|
923
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).',
|
|
924
936
|
imageCopy: 'Copy image',
|
|
925
937
|
imageCopied: 'Image copied to the clipboard.',
|
|
926
938
|
imageCopyFailed: 'Failed to copy the image.',
|
|
@@ -943,7 +955,7 @@ const en = {
|
|
|
943
955
|
pluginsUpdatesAvailable: '{n} zcode-plugins update(s) available (ZCode Pro settings → Features)',
|
|
944
956
|
pluginsAutoUpdated: 'zcode-plugins updated',
|
|
945
957
|
tabVision: 'Vision',
|
|
946
|
-
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).',
|
|
947
959
|
visionEnabled: 'Enable',
|
|
948
960
|
visionForceIntercept: 'Intercept even if the main model sees images',
|
|
949
961
|
visionMode: 'Chain mode',
|
|
@@ -974,14 +986,14 @@ const en = {
|
|
|
974
986
|
visionCompressKBHint: 'Images above this size are compressed first (max edge 2000, JPEG); 0 = never',
|
|
975
987
|
visionLoadFailed: 'Failed to load the vision config',
|
|
976
988
|
tabRtk: 'rtk',
|
|
977
|
-
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).',
|
|
978
990
|
rtkEnabled: 'Enable',
|
|
979
991
|
rtkNotInstalled: 'rtk is not installed. The settings below take effect after installation (/rtk-setup in a session).',
|
|
980
992
|
rtkInstallBin: 'Install rtk binary',
|
|
981
993
|
rtkBinInstalled: 'rtk installed; PATH takes effect for sessions after signing out and back in — the plugin hook works immediately',
|
|
982
994
|
rtkWhitelistTitle: 'Whitelist (pass through without compression)',
|
|
983
995
|
rtkBuiltinLabel: 'Built-in exemptions (read-only, follow plugin updates):',
|
|
984
|
-
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.',
|
|
985
997
|
rtkWhitelistPlaceholder: 'name or git:name, press Enter to add',
|
|
986
998
|
rtkWhitelistAdd: 'Add',
|
|
987
999
|
rtkWhitelistClear: 'Clear',
|
|
@@ -989,18 +1001,18 @@ const en = {
|
|
|
989
1001
|
rtkWhitelistInvalid: 'Entry must be name or git:name',
|
|
990
1002
|
rtkLoadFailed: 'Failed to load the rtk config',
|
|
991
1003
|
tabHeadroom: 'Headroom',
|
|
992
|
-
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.',
|
|
993
1005
|
headroomBinMissing: 'The headroom program was not found, so the proxy cannot start. The settings below take effect once it is installed.',
|
|
994
1006
|
hrInstallBin: 'Install headroom binary',
|
|
995
1007
|
hrBinInstalled: 'headroom installed; reopen a session and the hook will start the proxy automatically',
|
|
996
1008
|
headroomInstallTitle: 'headroom plugin not installed',
|
|
997
|
-
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.',
|
|
998
1010
|
headroomInstall: 'Install',
|
|
999
1011
|
headroomInstalling: 'Installing…',
|
|
1000
1012
|
headroomInstalled: 'headroom plugin installed',
|
|
1001
1013
|
headroomInstallRetry: 'Installation did not take effect; retry or install it from the marketplace manually',
|
|
1002
1014
|
pluginInstallCardTitle: '{name} plugin not installed',
|
|
1003
|
-
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.',
|
|
1004
1016
|
pluginInstallDone: '{name} plugin installed',
|
|
1005
1017
|
pluginEnable: 'Enable',
|
|
1006
1018
|
hrStarting: 'Starting compression proxy…',
|
|
@@ -1053,7 +1065,7 @@ const en = {
|
|
|
1053
1065
|
headroomPowerSaverDesc: 'Only the system power-saver mode → CPU; back when it exits.',
|
|
1054
1066
|
hrCpuPinnedHint: 'The compression device is pinned to CPU, so power-save switching no longer changes anything.',
|
|
1055
1067
|
headroomInterval: 'Watch interval',
|
|
1056
|
-
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.',
|
|
1057
1069
|
headroomSecUnit: 's',
|
|
1058
1070
|
upgradeCheck: 'Check update',
|
|
1059
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
|
+
}
|
|
@@ -244,8 +244,17 @@ export function openSettingsDialog() {
|
|
|
244
244
|
h('span', { class: 'text-foreground-subtle/70' }, `${health.version} · ${HELPER_URL}`)
|
|
245
245
|
);
|
|
246
246
|
|
|
247
|
-
|
|
247
|
+
// 双列网格:功能开关行短小(名称+开关+折行描述),两列排布把 15+ 行压到
|
|
248
|
+
// 一屏内。列间加分隔线、行距拉开(gap-y-1),格内不留底 padding——
|
|
249
|
+
// 悬停底色由行自身圆角承担,避免一格一格的补丁感
|
|
250
|
+
const rows = h('div', { class: 'grid grid-cols-2 gap-x-0 gap-y-1 rounded-xl border border-border p-2 divide-x divide-border' });
|
|
248
251
|
const pluginsStatusLine = h('span', { class: 'max-w-40 truncate text-ui-xs/relaxed text-foreground-subtle' }, '…');
|
|
252
|
+
// 状态文字写入统一入口:文本与悬停 title 一起更新(描述位被动态内容替换后
|
|
253
|
+
// 仍保持悬停全文约定)
|
|
254
|
+
const setPluginsStatus = (text) => {
|
|
255
|
+
setPluginsStatus(text);
|
|
256
|
+
pluginsStatusLine.setAttribute('title', text);
|
|
257
|
+
};
|
|
249
258
|
const pluginsBtn = btnSmall(L.pluginsCheckNow, () => { void runPluginsUpdate(); });
|
|
250
259
|
let pluginsUpdating = false;
|
|
251
260
|
const runPluginsUpdate = async () => {
|
|
@@ -253,18 +262,18 @@ export function openSettingsDialog() {
|
|
|
253
262
|
if (pluginsUpdating) return;
|
|
254
263
|
pluginsUpdating = true;
|
|
255
264
|
pluginsBtn.disabled = true;
|
|
256
|
-
|
|
265
|
+
setPluginsStatus(L.pluginsUpdating);
|
|
257
266
|
const res = await rpc('/plugins/update', { method: 'POST', body: {} });
|
|
258
267
|
pluginsUpdating = false;
|
|
259
268
|
pluginsBtn.disabled = false;
|
|
260
269
|
if (!res.ok) {
|
|
261
|
-
|
|
270
|
+
setPluginsStatus('');
|
|
262
271
|
showToast(L.pluginsUpdateFailed + ': ' + errText(res), 'error');
|
|
263
272
|
return;
|
|
264
273
|
}
|
|
265
|
-
|
|
274
|
+
setPluginsStatus(res.updated > 0
|
|
266
275
|
? L.pluginsUpdatedDone.replaceAll('{n}', String(res.updated))
|
|
267
|
-
: L.pluginsUpToDate;
|
|
276
|
+
: L.pluginsUpToDate);
|
|
268
277
|
showToast(pluginsStatusLine.textContent, 'success');
|
|
269
278
|
};
|
|
270
279
|
void (async () => {
|
|
@@ -272,15 +281,15 @@ export function openSettingsDialog() {
|
|
|
272
281
|
// 状态行如实显示过程与结果;开关关闭时只检查并显示“N 个可更新”
|
|
273
282
|
const cfg = await getConfig(true).catch(() => null);
|
|
274
283
|
const res = await rpc('/plugins/status');
|
|
275
|
-
if (!res.ok) {
|
|
284
|
+
if (!res.ok) { setPluginsStatus(''); return; }
|
|
276
285
|
const n = (res.updates || []).length;
|
|
277
286
|
if (cfg && (cfg.features || {}).autoUpdatePlugins === true && n > 0) {
|
|
278
287
|
void runPluginsUpdate();
|
|
279
288
|
return;
|
|
280
289
|
}
|
|
281
|
-
|
|
290
|
+
setPluginsStatus(n > 0
|
|
282
291
|
? L.pluginsUpdatesFound.replaceAll('{n}', String(n))
|
|
283
|
-
: L.pluginsUpToDate;
|
|
292
|
+
: L.pluginsUpToDate);
|
|
284
293
|
})();
|
|
285
294
|
|
|
286
295
|
// 会话快捷切换:入口键位(“快捷键”标签页可改);此处定义在 refreshRows 之前,
|
|
@@ -360,6 +369,16 @@ export function openSettingsDialog() {
|
|
|
360
369
|
if (await setFeature('imageCopy', next)) f.imageCopy = next;
|
|
361
370
|
refreshRows();
|
|
362
371
|
}),
|
|
372
|
+
settingRow(L.featureTableCopy, L.featureTableCopyDesc, f.tableCopy !== false, async () => {
|
|
373
|
+
const next = !(f.tableCopy !== false);
|
|
374
|
+
if (await setFeature('tableCopy', next)) f.tableCopy = next;
|
|
375
|
+
refreshRows();
|
|
376
|
+
}),
|
|
377
|
+
settingRow(L.featureComposerProvider, L.featureComposerProviderDesc, f.composerProvider !== false, async () => {
|
|
378
|
+
const next = !(f.composerProvider !== false);
|
|
379
|
+
if (await setFeature('composerProvider', next)) f.composerProvider = next;
|
|
380
|
+
refreshRows();
|
|
381
|
+
}),
|
|
363
382
|
settingRow(L.featureSandboxReclaim, L.featureSandboxReclaimDesc, f.sandboxReclaim !== false, async () => {
|
|
364
383
|
const next = !(f.sandboxReclaim !== false);
|
|
365
384
|
if (await setFeature('sandboxReclaim', next)) f.sandboxReclaim = next;
|
|
@@ -381,7 +400,7 @@ export function openSettingsDialog() {
|
|
|
381
400
|
refreshRows();
|
|
382
401
|
}),
|
|
383
402
|
(() => {
|
|
384
|
-
// 标题 = “自动更新 duanluan/zcode-plugins
|
|
403
|
+
// 标题 = “自动更新 duanluan/zcode-plugins”,仓库名可点击跳转仓库
|
|
385
404
|
const repoLink = h('span', {
|
|
386
405
|
class: 'cursor-pointer underline-offset-4 hover:underline',
|
|
387
406
|
title: PLUGINS_REPO_URL,
|
|
@@ -392,17 +411,19 @@ export function openSettingsDialog() {
|
|
|
392
411
|
if (await setFeature('autoUpdatePlugins', next)) f.autoUpdatePlugins = next;
|
|
393
412
|
refreshRows();
|
|
394
413
|
}, h('div', { class: 'flex items-center gap-2' }, pluginsBtn));
|
|
395
|
-
// 标题行:自动更新
|
|
414
|
+
// 标题行:自动更新 <链接>;描述位放状态字(检查结果/更新进度)
|
|
396
415
|
const nameEl = row.querySelector('.text-ui-sm.font-medium');
|
|
397
416
|
if (nameEl) {
|
|
398
417
|
nameEl.replaceChildren(
|
|
399
418
|
document.createTextNode(L.featureAutoUpdatePlugins + ' '),
|
|
400
419
|
repoLink,
|
|
401
|
-
document.createTextNode(' 插件'),
|
|
402
420
|
);
|
|
403
421
|
}
|
|
404
422
|
const descEl = row.querySelector('[data-zcodepro-row-desc]');
|
|
405
|
-
if (descEl)
|
|
423
|
+
if (descEl) {
|
|
424
|
+
descEl.replaceChildren(pluginsStatusLine);
|
|
425
|
+
descEl.setAttribute('title', descEl.textContent.trim());
|
|
426
|
+
}
|
|
406
427
|
return row;
|
|
407
428
|
})(),
|
|
408
429
|
);
|
|
@@ -560,12 +581,13 @@ export function openSettingsDialog() {
|
|
|
560
581
|
const paneTitleGen = h('div', { role: 'tabpanel', class: 'mt-4', style: 'display:none' });
|
|
561
582
|
const paneDebug = h('div', { role: 'tabpanel', class: 'mt-4', style: 'display:none' });
|
|
562
583
|
const panes = { features: paneFeatures, shortcuts: paneShortcuts, styles: paneStyles, perf: panePerf, agents: paneAgents, proxy: paneProxy, sync: paneSync, vision: paneVision, headroom: paneHeadroom, rtk: paneRtk, debug: paneDebug };
|
|
584
|
+
// 一级 tab 顺序:功能 → 外观 → 行为 → 性能 → 快捷键 → 代理/同步/插件页
|
|
563
585
|
const tabDefs = [
|
|
564
586
|
['features', L.tabFeatures],
|
|
565
|
-
['shortcuts', L.tabShortcuts],
|
|
566
|
-
['agents', L.tabBehavior],
|
|
567
587
|
['styles', L.tabStyles],
|
|
588
|
+
['agents', L.tabBehavior],
|
|
568
589
|
['perf', L.tabPerformance],
|
|
590
|
+
['shortcuts', L.tabShortcuts],
|
|
569
591
|
['proxy', L.tabProxy],
|
|
570
592
|
['sync', L.tabSync],
|
|
571
593
|
['vision', L.tabVision],
|