codexmate 0.0.56 → 0.1.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.md +2 -2
- package/README.vi.md +1 -0
- package/README.zh.md +2 -2
- package/cli/openai-bridge-retry.js +62 -0
- package/cli/openai-bridge-runtime.js +1819 -0
- package/cli/openai-bridge.js +137 -2048
- package/cli.js +749 -133
- package/lib/task-orchestrator.js +90 -21
- package/lib/task-workspace-chat.js +292 -0
- package/package.json +2 -2
- package/web-ui/app.js +67 -131
- package/web-ui/modules/app.computed.main-tabs.mjs +304 -7
- package/web-ui/modules/app.methods.agents.mjs +192 -1
- package/web-ui/modules/app.methods.claude-config.mjs +65 -25
- package/web-ui/modules/app.methods.navigation.mjs +67 -83
- package/web-ui/modules/app.methods.openclaw-editing.mjs +3 -6
- package/web-ui/modules/app.methods.providers.mjs +40 -10
- package/web-ui/modules/app.methods.session-actions.mjs +1 -12
- package/web-ui/modules/app.methods.session-browser.mjs +23 -54
- package/web-ui/modules/app.methods.session-trash.mjs +0 -1
- package/web-ui/modules/app.methods.startup-claude.mjs +16 -31
- package/web-ui/modules/app.methods.task-orchestration.mjs +347 -8
- package/web-ui/modules/app.methods.tool-config-permissions.mjs +0 -3
- package/web-ui/modules/app.methods.web-ui-preferences.mjs +442 -68
- package/web-ui/modules/config-template-confirm-pref.mjs +2 -3
- package/web-ui/modules/i18n/locales/en.mjs +181 -30
- package/web-ui/modules/i18n/locales/ja.mjs +178 -27
- package/web-ui/modules/i18n/locales/vi.mjs +180 -29
- package/web-ui/modules/i18n/locales/zh-tw.mjs +181 -30
- package/web-ui/modules/i18n/locales/zh.mjs +183 -32
- package/web-ui/modules/i18n.mjs +5 -12
- package/web-ui/modules/sessions-filters-url.mjs +1 -2
- package/web-ui/partials/index/layout-header.html +44 -38
- package/web-ui/partials/index/modals-basic.html +26 -0
- package/web-ui/partials/index/panel-orchestration.html +489 -282
- package/web-ui/partials/index/panel-prompts.html +66 -3
- package/web-ui/res/web-ui-render.precompiled.js +1181 -604
- package/web-ui/styles/layout-shell.css +157 -1
- package/web-ui/styles/modals-core.css +173 -0
- package/web-ui/styles/navigation-panels.css +11 -0
- package/web-ui/styles/responsive.css +32 -0
- package/web-ui/styles/task-orchestration.css +2161 -4
|
@@ -143,10 +143,13 @@ const zh = Object.freeze({
|
|
|
143
143
|
'side.config': '配置',
|
|
144
144
|
'side.sessions': '会话',
|
|
145
145
|
'side.prompts': 'Prompts',
|
|
146
|
+
'side.prompts.meta': 'Prompt 文件与预设',
|
|
146
147
|
'side.plugins': '插件',
|
|
147
148
|
'side.system': '系统',
|
|
148
149
|
'side.orchestration': '任务',
|
|
149
150
|
'side.skills': 'Skills',
|
|
151
|
+
'side.collapse': '收起侧边栏',
|
|
152
|
+
'side.expand': '展开侧边栏',
|
|
150
153
|
|
|
151
154
|
// Side rail items
|
|
152
155
|
'side.overview.doctor': 'Doctor 面板',
|
|
@@ -165,19 +168,21 @@ const zh = Object.freeze({
|
|
|
165
168
|
'side.update.currentOnly': '当前 v{current}',
|
|
166
169
|
'side.update.retry': '重试版本检查',
|
|
167
170
|
'side.config.codex': 'Codex',
|
|
168
|
-
'side.config.codex.meta': 'Provider
|
|
171
|
+
'side.config.codex.meta': 'Provider · Model',
|
|
169
172
|
'side.config.claude': 'Claude Code',
|
|
170
|
-
'side.config.claude.meta': '
|
|
173
|
+
'side.config.claude.meta': 'Settings',
|
|
171
174
|
'side.config.openclaw': 'OpenClaw',
|
|
172
|
-
'side.config.openclaw.meta': 'JSON5
|
|
175
|
+
'side.config.openclaw.meta': 'JSON5 · AGENTS',
|
|
173
176
|
'side.config.opencode': 'OpenCode',
|
|
174
|
-
'side.config.opencode.meta': '
|
|
177
|
+
'side.config.opencode.meta': 'Provider',
|
|
175
178
|
'side.sessions.browser': '会话浏览',
|
|
176
179
|
'side.sessions.browser.meta': '浏览 / 导出 / 清理',
|
|
177
180
|
'side.prompts.agents': 'AGENTS.md',
|
|
178
181
|
'side.prompts.agents.meta': 'Codex 指令文件',
|
|
179
182
|
'side.prompts.claude': 'CLAUDE.md',
|
|
180
183
|
'side.prompts.claude.meta': 'Claude 指令文件',
|
|
184
|
+
'side.prompts.presets': '预设池',
|
|
185
|
+
'side.prompts.presets.meta': '保存和粘贴预设',
|
|
181
186
|
'side.plugins.tools': '提示词工具',
|
|
182
187
|
'side.plugins.tools.meta': '模板 / 变量',
|
|
183
188
|
'side.plugins.templatesCount': '{count} 个模板',
|
|
@@ -202,7 +207,7 @@ const zh = Object.freeze({
|
|
|
202
207
|
'title.config': '本地配置控制台',
|
|
203
208
|
'title.sessions': '会话与导出',
|
|
204
209
|
'title.usage': '本地用量与趋势',
|
|
205
|
-
'title.orchestration': '
|
|
210
|
+
'title.orchestration': '任务线程',
|
|
206
211
|
'title.market': 'Skills 安装与同步',
|
|
207
212
|
'title.plugins': '插件与模板',
|
|
208
213
|
'title.docs': 'CLI 安装与文档',
|
|
@@ -213,7 +218,7 @@ const zh = Object.freeze({
|
|
|
213
218
|
'subtitle.config': '管理本地配置与模型。',
|
|
214
219
|
'subtitle.sessions': '浏览与导出会话。',
|
|
215
220
|
'subtitle.usage': '查看近 7 / 30 天用量。',
|
|
216
|
-
'subtitle.orchestration': '
|
|
221
|
+
'subtitle.orchestration': '像聊天一样整理任务,准备好后发送 /plan。',
|
|
217
222
|
'subtitle.market': '管理本地 Skills。',
|
|
218
223
|
'subtitle.plugins': '管理模板化 prompt 与可复用插件。',
|
|
219
224
|
'subtitle.docs': '查看 CLI 安装命令与排障。',
|
|
@@ -221,6 +226,32 @@ const zh = Object.freeze({
|
|
|
221
226
|
'subtitle.prompts': '编辑 AGENTS.md 与 CLAUDE.md。',
|
|
222
227
|
'prompts.subTab.codex': 'AGENTS.md (Codex)',
|
|
223
228
|
'prompts.subTab.project': 'CLAUDE.md',
|
|
229
|
+
'prompts.subTab.presets': '预设池',
|
|
230
|
+
'prompts.presets.title': '预设池',
|
|
231
|
+
'prompts.presets.hint': '管理预设。',
|
|
232
|
+
'prompts.presets.namePlaceholder': '预设名称',
|
|
233
|
+
'prompts.presets.selectPlaceholder': '选择预设…',
|
|
234
|
+
'prompts.presets.saveCurrent': '存为预设',
|
|
235
|
+
'prompts.presets.empty': '暂无预设',
|
|
236
|
+
'prompts.presets.updatedAt': '更新时间:{time}',
|
|
237
|
+
'prompts.presets.renameAria': '重命名 prompt 预设',
|
|
238
|
+
'prompts.presets.open': '打开预设池',
|
|
239
|
+
'prompts.presets.addCurrent': '添加到预设池',
|
|
240
|
+
'prompts.presets.defaultName.project': '项目 CLAUDE.md - {path}',
|
|
241
|
+
'prompts.presets.error.emptyName': '预设名称不能为空',
|
|
242
|
+
'prompts.presets.error.emptyContent': '预设内容不能为空',
|
|
243
|
+
'prompts.presets.error.duplicateName': '已存在同名预设',
|
|
244
|
+
'prompts.presets.confirm.addCurrentTitle': '添加到预设池?',
|
|
245
|
+
'prompts.presets.confirm.addCurrentMessage': '保存为“{name}”。',
|
|
246
|
+
'prompts.presets.confirm.overwriteTitle': '覆盖预设?',
|
|
247
|
+
'prompts.presets.confirm.overwriteMessage': '名为“{name}”的预设已存在。要用当前编辑器内容覆盖它吗?',
|
|
248
|
+
'prompts.presets.confirm.overwriteConfirm': '覆盖',
|
|
249
|
+
'prompts.presets.confirm.deleteTitle': '删除预设?',
|
|
250
|
+
'prompts.presets.confirm.deleteMessage': '删除预设“{name}”?此操作不可撤销。',
|
|
251
|
+
'prompts.presets.toast.saved': '预设已保存',
|
|
252
|
+
'prompts.presets.toast.pasted': '已粘贴,未保存。',
|
|
253
|
+
'prompts.presets.toast.renamed': '预设已重命名',
|
|
254
|
+
'prompts.presets.toast.deleted': '预设已删除',
|
|
224
255
|
'prompts.project.pathLabel': '项目路径(可选)',
|
|
225
256
|
'prompts.project.selectPlaceholder': '-- 全局 (~/.claude/CLAUDE.md) --',
|
|
226
257
|
'prompts.project.manualPlaceholder': '或手动输入项目根路径...',
|
|
@@ -486,6 +517,8 @@ const zh = Object.freeze({
|
|
|
486
517
|
'modal.claudeConfigEdit.title': '编辑 Claude Code 配置',
|
|
487
518
|
'field.useBuiltinTransform': '使用内建转换(兼容 OpenAI 格式)',
|
|
488
519
|
'hint.useBuiltinTransform': '开启后:写入的 base_url 会指向 codexmate 内建转换服务;Codex 使用的令牌固定为 codexmate。',
|
|
520
|
+
'field.transformMaxRetries': '内建转换重试次数',
|
|
521
|
+
'hint.transformMaxRetries': '默认 2,最小 2;表示失败后最多重试的次数。',
|
|
489
522
|
|
|
490
523
|
// Config template / agents modals
|
|
491
524
|
'modal.configTemplate.label': 'config.toml 模板',
|
|
@@ -957,19 +990,105 @@ const zh = Object.freeze({
|
|
|
957
990
|
'usage.range.7d.short': '近 7 天',
|
|
958
991
|
'usage.range.30d.short': '近 30 天',
|
|
959
992
|
'orchestration.queueStats': '队列 {running} 运行中 · {queued} 等待中',
|
|
993
|
+
'orchestration.rebuilding.status': '暂停开放',
|
|
994
|
+
'orchestration.rebuilding.title': '重建中',
|
|
995
|
+
'orchestration.rebuilding.body': '任务编排入口暂时保留展示,但当前不可点击。',
|
|
960
996
|
'orchestration.hero.kicker': '任务编排',
|
|
961
|
-
'orchestration.hero.title': '
|
|
962
|
-
'orchestration.hero.subtitle': '
|
|
997
|
+
'orchestration.hero.title': '任务编排',
|
|
998
|
+
'orchestration.hero.subtitle': '规划、执行并追踪任务,保留工作区与线程上下文。',
|
|
999
|
+
'orchestration.quick.kicker': '任务输入',
|
|
1000
|
+
'orchestration.quick.title': '输入任务目标',
|
|
1001
|
+
'orchestration.quick.subtitle': '描述目标、约束和验收标准。执行前可用 /plan 预览计划。',
|
|
1002
|
+
'orchestration.quick.caption': 'Enter 追加需求;/plan 预览计划;开始执行会生成计划并运行。',
|
|
1003
|
+
'orchestration.quick.templates.reviewFix.meta': '处理 review 反馈并补回归验证。',
|
|
1004
|
+
'orchestration.quick.templates.planOnly.meta': '先排查规划,不写入文件。',
|
|
1005
|
+
'orchestration.quick.templates.workflowBatch.meta': '复用本地 Workflow 跑固定检查。',
|
|
1006
|
+
'orchestration.chat.input.label': '任务消息',
|
|
1007
|
+
'orchestration.chat.input.placeholder': '描述目标、约束和验收标准;输入 /plan 预览计划。',
|
|
1008
|
+
'orchestration.chat.input.hint': '工作区、线程和运行模式可在高级设置中调整。',
|
|
1009
|
+
'orchestration.chat.context.aria': '任务上下文',
|
|
1010
|
+
'orchestration.chat.context.workspace.auto': '工作区自动推断',
|
|
1011
|
+
'orchestration.chat.context.workspace.value': '工作区 {value}',
|
|
1012
|
+
'orchestration.chat.context.thread.auto': '线程自动生成',
|
|
1013
|
+
'orchestration.chat.context.thread.value': '线程 {value}',
|
|
1014
|
+
'orchestration.chat.context.sequence.empty': '顺序:等待需求 1',
|
|
1015
|
+
'orchestration.chat.context.sequence.value': '顺序:{count} 条需求',
|
|
1016
|
+
'orchestration.privacy.workspace.selected': '已选择工作区',
|
|
1017
|
+
'orchestration.privacy.thread.selected': '已选择线程',
|
|
1018
|
+
'orchestration.chat.thread.aria': '任务对话',
|
|
1019
|
+
'orchestration.chat.input.send': '追加需求',
|
|
1020
|
+
'orchestration.chat.input.discuss': '预览计划',
|
|
1021
|
+
'orchestration.chat.input.work': '开始执行',
|
|
1022
|
+
'orchestration.chat.input.firstHint': '这会成为需求 1。',
|
|
1023
|
+
'orchestration.chat.input.workHint': '执行前会先合并当前输入。',
|
|
1024
|
+
'orchestration.chat.input.workCaption': 'Enter 追加需求;/plan 只预览;开始执行会规划并运行。',
|
|
1025
|
+
'orchestration.chat.input.sequenceHint': '现在发送会成为需求 {count}。',
|
|
1026
|
+
'orchestration.chat.assistant.contextLabel': '上一轮上下文',
|
|
1027
|
+
'orchestration.chat.assistant.contextFallback': '已选中一个历史任务,继续时会继承它的线程和工作区。',
|
|
1028
|
+
'orchestration.chat.assistant.resultLabel': '实现结果',
|
|
1029
|
+
'orchestration.chat.assistant.filesSummary': '文件操作:{files}',
|
|
1030
|
+
'orchestration.chat.assistant.readyLabel': 'Codexmate',
|
|
1031
|
+
'orchestration.chat.assistant.empty': '输入任务目标。多条需求会按顺序执行并保留上下文。',
|
|
1032
|
+
'orchestration.chat.assistant.planLabel': '计划预览',
|
|
1033
|
+
'orchestration.chat.assistant.planSummary': '计划已生成:{nodes} 个节点,{waves} 个批次。',
|
|
1034
|
+
'orchestration.chat.assistant.sequenceReady': '已收到多条需求;执行时会先完成需求 1,再带着上下文继续后续需求。',
|
|
1035
|
+
'orchestration.chat.assistant.singleReady': '已收到需求。可继续追加,或输入 /plan 预览计划。',
|
|
1036
|
+
'orchestration.chat.user.step': '需求 {count}',
|
|
1037
|
+
'orchestration.chat.meta.thread': '线程 {value}',
|
|
1038
|
+
'orchestration.chat.meta.workspace': '工作区 {value}',
|
|
1039
|
+
'orchestration.chat.meta.order': '顺序执行 · 保留上下文',
|
|
1040
|
+
'orchestration.chat.meta.first': '先完成这一条',
|
|
1041
|
+
'orchestration.chat.meta.afterPrevious': '等待前一条完成后继续',
|
|
1042
|
+
'orchestration.chat.meta.contextKept': '上下文会随线程保留',
|
|
1043
|
+
'orchestration.chat.meta.previewNext': '下一步:/plan 预览',
|
|
1044
|
+
'orchestration.quick.checklist.title': '执行检查',
|
|
1045
|
+
'orchestration.quick.checklist.subtitle': '只保留真正影响执行的阻塞项。',
|
|
1046
|
+
'orchestration.agent.aria': '任务编排状态',
|
|
1047
|
+
'orchestration.agent.kicker': '任务编排',
|
|
1048
|
+
'orchestration.agent.title': '任务编排控制台',
|
|
1049
|
+
'orchestration.agent.subtitle': '选择工作区与会话,执行任务,并查看运行详情。',
|
|
1050
|
+
'orchestration.agent.state.ready': 'Ready',
|
|
1051
|
+
'orchestration.agent.state.planning': 'Planning',
|
|
1052
|
+
'orchestration.agent.state.running': 'Running',
|
|
1053
|
+
'orchestration.agent.surface.workspace': 'Workspace',
|
|
1054
|
+
'orchestration.agent.surface.session': '上下文',
|
|
1055
|
+
'orchestration.agent.surface.mode': 'Mode',
|
|
1056
|
+
'orchestration.agent.surface.trace': 'Trace',
|
|
1057
|
+
'orchestration.agent.surface.runs': 'runs',
|
|
1058
|
+
'orchestration.agent.surface.queue': 'queued',
|
|
1059
|
+
'orchestration.agent.trace.title': 'Live trace',
|
|
1060
|
+
'orchestration.agent.trace.plan': 'Plan nodes',
|
|
1061
|
+
'orchestration.agent.trace.queue': 'Queue',
|
|
1062
|
+
'orchestration.agent.trace.runs': 'Runs',
|
|
1063
|
+
'orchestration.agent.trace.nodes': 'Run nodes',
|
|
1064
|
+
'orchestration.quick.status.title': '当前草稿',
|
|
1065
|
+
'orchestration.workspace.aria': '工作区与会话',
|
|
1066
|
+
'orchestration.workspace.title': '工作区',
|
|
1067
|
+
'orchestration.workspace.subtitle': '选择项目,再为该项目新建或恢复会话。',
|
|
1068
|
+
'orchestration.workspace.selector': '工作区选择',
|
|
1069
|
+
'orchestration.workspace.counts': '{runs} runs · {queue} queue',
|
|
1070
|
+
'orchestration.workspace.newSession': '+ 新建',
|
|
1071
|
+
'orchestration.workspace.sessions.title': '记录',
|
|
1072
|
+
'orchestration.workspace.sessions.empty.title': '暂无记录',
|
|
1073
|
+
'orchestration.workspace.sessions.empty.subtitle': '新建记录,或刷新后选择已有运行记录。',
|
|
1074
|
+
'orchestration.workspace.sessions.queueTitle': '排队项',
|
|
1075
|
+
'orchestration.workspace.sessions.runTitle': '运行记录',
|
|
1076
|
+
'orchestration.workspace.sessions.detailsHidden': '详情已隐藏',
|
|
1077
|
+
'orchestration.workspace.pathHidden': '路径已隐藏',
|
|
963
1078
|
'orchestration.draft.reset': '重置草稿',
|
|
964
1079
|
'orchestration.summary.aria': '任务编排概览',
|
|
965
1080
|
'orchestration.summary.running': '运行中',
|
|
966
1081
|
'orchestration.summary.queued': '排队中',
|
|
967
1082
|
'orchestration.summary.runs': '运行记录',
|
|
968
|
-
'orchestration.readiness.target.label': '
|
|
969
|
-
'orchestration.readiness.target.done': '
|
|
970
|
-
'orchestration.readiness.target.missing': '
|
|
1083
|
+
'orchestration.readiness.target.label': '任务目标',
|
|
1084
|
+
'orchestration.readiness.target.done': '已填写目标',
|
|
1085
|
+
'orchestration.readiness.target.missing': '未填写目标',
|
|
1086
|
+
'orchestration.readiness.sequence.label': '顺序',
|
|
1087
|
+
'orchestration.readiness.sequence.missing': '先发送需求 1',
|
|
1088
|
+
'orchestration.readiness.sequence.single': '当前只有需求 1;继续发送会变成需求 2。',
|
|
1089
|
+
'orchestration.readiness.sequence.multiple': '{count} 条需求会按顺序执行:先完成需求 1,再继续需求 2。',
|
|
971
1090
|
'orchestration.readiness.engine.label': '执行策略',
|
|
972
|
-
'orchestration.readiness.engine.
|
|
1091
|
+
'orchestration.readiness.engine.openaiChat': '使用 OpenAI Chat-compatible 节点',
|
|
973
1092
|
'orchestration.readiness.workflow.done': '已选 {count} 个 Workflow',
|
|
974
1093
|
'orchestration.readiness.workflow.missing': '还没选 Workflow ID',
|
|
975
1094
|
'orchestration.readiness.scope.label': '边界',
|
|
@@ -977,24 +1096,25 @@ const zh = Object.freeze({
|
|
|
977
1096
|
'orchestration.readiness.scope.writeHint': '建议补说明后再写入',
|
|
978
1097
|
'orchestration.readiness.scope.readonlyHint': '当前是只读,可直接试',
|
|
979
1098
|
'orchestration.readiness.preview.label': '预览',
|
|
980
|
-
'orchestration.readiness.preview.missing': '
|
|
1099
|
+
'orchestration.readiness.preview.missing': '还没发送 /plan',
|
|
981
1100
|
'orchestration.readiness.preview.blocked': '有 {count} 个阻塞项',
|
|
982
1101
|
'orchestration.readiness.preview.ready': '计划可用,{count} 个节点',
|
|
983
|
-
'orchestration.readiness.empty.title': '
|
|
984
|
-
'orchestration.readiness.empty.summary': '
|
|
1102
|
+
'orchestration.readiness.empty.title': '填写任务目标',
|
|
1103
|
+
'orchestration.readiness.empty.summary': '先明确目标、约束和验收标准。',
|
|
985
1104
|
'orchestration.readiness.workflow.title': '缺少 Workflow',
|
|
986
1105
|
'orchestration.readiness.workflow.summary': '你已经选了 Workflow 模式,但还没指定可复用流程。',
|
|
987
|
-
'orchestration.readiness.preview.title': '
|
|
988
|
-
'orchestration.readiness.preview.summary': '
|
|
1106
|
+
'orchestration.readiness.preview.title': '建议预览计划',
|
|
1107
|
+
'orchestration.readiness.preview.summary': '目标已填写。可用 /plan 确认节点和依赖。',
|
|
1108
|
+
'orchestration.readiness.preview.sequenceSummary': '草稿已成形,已锁定 {count} 条顺序需求:先完成需求 1,再继续需求 2。',
|
|
989
1109
|
'orchestration.readiness.blocked.title': '预览有阻塞',
|
|
990
1110
|
'orchestration.readiness.blocked.summary': '当前计划里还有 {count} 个阻塞项,先处理它们。',
|
|
991
1111
|
'orchestration.readiness.warn.title': '可以执行,但有提醒',
|
|
992
|
-
'orchestration.readiness.warn.summary': '
|
|
1112
|
+
'orchestration.readiness.warn.summary': '/plan 方案已生成,但还有 {count} 条提醒值得先看一眼。',
|
|
993
1113
|
'orchestration.readiness.dryRun.title': '适合先预演',
|
|
994
1114
|
'orchestration.readiness.dryRun.summary': '现在可以安全地跑一次仅预演,先看结果再决定是否真实执行。',
|
|
995
1115
|
'orchestration.readiness.ready.title': '可以执行',
|
|
996
|
-
'orchestration.readiness.ready.withFollowUps': '
|
|
997
|
-
'orchestration.readiness.ready.summary': '
|
|
1116
|
+
'orchestration.readiness.ready.withFollowUps': '已锁定 {count} 条顺序需求:先完成需求 1,再带上下文继续需求 2。',
|
|
1117
|
+
'orchestration.readiness.ready.summary': '目标清晰,可以执行或入队。',
|
|
998
1118
|
'orchestration.step1.title': '先把结果写清楚',
|
|
999
1119
|
'orchestration.step1.subtitle': '只写会影响执行的内容。',
|
|
1000
1120
|
'orchestration.templates.title': '快捷示例',
|
|
@@ -1009,14 +1129,15 @@ const zh = Object.freeze({
|
|
|
1009
1129
|
'orchestration.templates.workflowBatch.target': '用 Workflow 跑一组固定检查并整理结果',
|
|
1010
1130
|
'orchestration.templates.workflowBatch.workflowIds': 'diagnose-config\nsafe-provider-switch',
|
|
1011
1131
|
'orchestration.templates.workflowBatch.notes': '输出统一结论,避免重复描述',
|
|
1012
|
-
'orchestration.fields.target': '
|
|
1013
|
-
'orchestration.fields.target.placeholder': '例如:修复当前 PR review
|
|
1014
|
-
'orchestration.fields.target.hint': '
|
|
1015
|
-
'orchestration.engine.
|
|
1132
|
+
'orchestration.fields.target': '任务消息',
|
|
1133
|
+
'orchestration.fields.target.placeholder': '例如:修复当前 PR review 评论,补对应回归测试,并避免改无关模块',
|
|
1134
|
+
'orchestration.fields.target.hint': '像发消息一样写清结果、边界和验收标准就够了。',
|
|
1135
|
+
'orchestration.engine.openaiChat': 'OpenAI Chat',
|
|
1016
1136
|
'orchestration.engine.workflow': 'Workflow',
|
|
1017
1137
|
'orchestration.runMode.write': '写入',
|
|
1018
1138
|
'orchestration.runMode.readOnly': '只读',
|
|
1019
1139
|
'orchestration.runMode.dryRun': '仅预演',
|
|
1140
|
+
'orchestration.pills.sequenceCount': '顺序需求 {count}',
|
|
1020
1141
|
'orchestration.pills.hasTitle': '标题已设',
|
|
1021
1142
|
'orchestration.pills.workflowCount': 'Workflow {count}',
|
|
1022
1143
|
'orchestration.pills.planNodes': '计划 {count} 节点',
|
|
@@ -1025,11 +1146,18 @@ const zh = Object.freeze({
|
|
|
1025
1146
|
'orchestration.fields.engine': '引擎',
|
|
1026
1147
|
'orchestration.fields.runMode': '运行模式',
|
|
1027
1148
|
'orchestration.advanced.title': '高级设置',
|
|
1149
|
+
'orchestration.advanced.open': '设置',
|
|
1028
1150
|
'orchestration.fields.title': '标题',
|
|
1029
1151
|
'orchestration.fields.title.placeholder': '可选,默认从目标自动提取',
|
|
1030
1152
|
'orchestration.fields.notes': '说明',
|
|
1031
1153
|
'orchestration.fields.notes.placeholder': '例如:不要重写现有架构,只做增量实现',
|
|
1032
1154
|
'orchestration.fields.notes.hint': '补边界、禁区、风格要求或验证要求。',
|
|
1155
|
+
'orchestration.fields.workspacePath': '工作区路径',
|
|
1156
|
+
'orchestration.fields.workspacePath.placeholder': '例如:/home/me/project',
|
|
1157
|
+
'orchestration.fields.workspacePath.hint': '计划、执行和队列都会使用这个 cwd;留空则使用服务当前目录。',
|
|
1158
|
+
'orchestration.fields.threadId': '任务线程 ID',
|
|
1159
|
+
'orchestration.fields.threadId.placeholder': '可选;留空自动生成',
|
|
1160
|
+
'orchestration.fields.threadId.hint': '同一线程用于连续追加要求、重试和队列追踪。',
|
|
1033
1161
|
'orchestration.fields.followUps': '后续动作(每行一条)',
|
|
1034
1162
|
'orchestration.fields.followUps.placeholder': '例如:\n继续处理 review 评论\n最后补回归测试',
|
|
1035
1163
|
'orchestration.fields.concurrency': '并发',
|
|
@@ -1045,33 +1173,37 @@ const zh = Object.freeze({
|
|
|
1045
1173
|
'orchestration.actions.planning': '规划中...',
|
|
1046
1174
|
'orchestration.actions.previewOnly': '仅预览',
|
|
1047
1175
|
'orchestration.actions.preparing': '准备中...',
|
|
1048
|
-
'orchestration.actions.generatePlan': '
|
|
1049
|
-
'orchestration.actions.planAndRun': '
|
|
1176
|
+
'orchestration.actions.generatePlan': '发送 /plan',
|
|
1177
|
+
'orchestration.actions.planAndRun': '预览 /plan 并执行',
|
|
1050
1178
|
'orchestration.actions.processing': '处理中...',
|
|
1051
1179
|
'orchestration.actions.queueAndStart': '入队并开始',
|
|
1052
|
-
'orchestration.actions.caption': '
|
|
1053
|
-
'orchestration.stage.title': '
|
|
1054
|
-
'orchestration.stage.subtitle': '
|
|
1055
|
-
'orchestration.stage.pill.target': '
|
|
1180
|
+
'orchestration.actions.caption': '执行前会按需刷新计划;批量任务可入队运行。',
|
|
1181
|
+
'orchestration.stage.title': '任务阶段',
|
|
1182
|
+
'orchestration.stage.subtitle': '填写目标、预览计划,然后执行。',
|
|
1183
|
+
'orchestration.stage.pill.target': '写任务消息',
|
|
1056
1184
|
'orchestration.stage.pill.preview': '预览',
|
|
1057
1185
|
'orchestration.stage.pill.run': '执行或入队',
|
|
1058
1186
|
'orchestration.plan.title': '计划预览',
|
|
1059
|
-
'orchestration.plan.subtitle': '
|
|
1187
|
+
'orchestration.plan.subtitle': '确认节点、批次和依赖关系。',
|
|
1060
1188
|
'orchestration.plan.summary.nodes': '节点数',
|
|
1061
1189
|
'orchestration.plan.summary.waves': '波次',
|
|
1062
1190
|
'orchestration.plan.summary.engine': '引擎',
|
|
1191
|
+
'orchestration.plan.summary.threadId': '线程 ID',
|
|
1192
|
+
'orchestration.plan.summary.cwd': '工作区',
|
|
1063
1193
|
'orchestration.plan.node.write': 'write',
|
|
1064
1194
|
'orchestration.plan.node.readOnly': 'read-only',
|
|
1065
1195
|
'orchestration.labels.dependencies': '依赖:',
|
|
1066
1196
|
'orchestration.labels.error': '错误:',
|
|
1067
1197
|
'orchestration.workbench.title': '执行工作台',
|
|
1068
|
-
'orchestration.workbench.subtitle': '
|
|
1198
|
+
'orchestration.workbench.subtitle': '显示队列、运行记录和节点详情。',
|
|
1069
1199
|
'orchestration.queue.start': '开始队列',
|
|
1070
1200
|
'orchestration.queue.starting': '启动中...',
|
|
1071
1201
|
'orchestration.workbench.tabs.aria': '任务编排工作台视图',
|
|
1072
1202
|
'orchestration.workbench.tabs.queue': '队列 {count}',
|
|
1073
1203
|
'orchestration.workbench.tabs.runs': '运行记录 {count}',
|
|
1074
1204
|
'orchestration.workbench.tabs.detail': '运行详情',
|
|
1205
|
+
'orchestration.workbench.queueCount': '{count} 个排队中',
|
|
1206
|
+
'orchestration.workbench.ready': '就绪',
|
|
1075
1207
|
'orchestration.queue.empty.title': '当前没有排队任务',
|
|
1076
1208
|
'orchestration.queue.empty.subtitle': '批量任务可先入队,再启动队列。',
|
|
1077
1209
|
'orchestration.runs.empty.title': '还没有运行记录',
|
|
@@ -1079,13 +1211,32 @@ const zh = Object.freeze({
|
|
|
1079
1211
|
'orchestration.detail.refresh': '刷新详情',
|
|
1080
1212
|
'orchestration.detail.retry': '重试',
|
|
1081
1213
|
'orchestration.detail.retrying': '重试中...',
|
|
1214
|
+
'orchestration.detail.continueThread': '继续此线程',
|
|
1082
1215
|
'orchestration.detail.empty.title': '选择一条运行记录查看详情',
|
|
1083
1216
|
'orchestration.detail.empty.subtitle': '这里会显示节点状态、摘要和日志。',
|
|
1084
1217
|
'orchestration.detail.summary.status': '状态',
|
|
1085
1218
|
'orchestration.detail.summary.duration': '耗时',
|
|
1086
1219
|
'orchestration.detail.summary.nodes': '节点数',
|
|
1087
1220
|
'orchestration.detail.summary.summary': '摘要',
|
|
1221
|
+
'orchestration.detail.context.aria': '任务运行上下文',
|
|
1222
|
+
'orchestration.detail.context.run': 'Run ID',
|
|
1088
1223
|
'orchestration.detail.node.meta': '{id} · attempts {attempts} · auto-fix {autoFix}',
|
|
1224
|
+
'orchestration.openai.status.title': 'OpenAI Chat 运行配置',
|
|
1225
|
+
'orchestration.openai.status.subtitle': '任务节点使用当前 OpenClaw provider;执行前会检查 base URL、API key 和模型。',
|
|
1226
|
+
'orchestration.openai.status.provider': 'Provider',
|
|
1227
|
+
'orchestration.openai.status.model': '模型',
|
|
1228
|
+
'orchestration.openai.status.endpoint': 'Endpoint',
|
|
1229
|
+
'orchestration.openai.status.apiKey': 'API Key',
|
|
1230
|
+
'orchestration.openai.status.headers': '额外 headers',
|
|
1231
|
+
'orchestration.openai.status.configured': '已配置',
|
|
1232
|
+
'orchestration.openai.status.missing': '缺失',
|
|
1233
|
+
'orchestration.openai.status.notSet': '未设置',
|
|
1234
|
+
'orchestration.openai.status.notLoaded': '刷新后加载 OpenAI Chat 就绪状态。',
|
|
1235
|
+
'orchestration.openai.status.configure': '配置 OpenAI Chat',
|
|
1236
|
+
'orchestration.detail.node.output': 'AI 输出',
|
|
1237
|
+
'orchestration.detail.node.endpoint': 'Endpoint:',
|
|
1238
|
+
'orchestration.detail.node.materializedFiles': '物化文件:',
|
|
1239
|
+
'orchestration.detail.node.workspaceFiles': '工作区文件:',
|
|
1089
1240
|
'skills.localLabel': '{target} / 本地 Skills',
|
|
1090
1241
|
'skills.counts': '已装 {installed} · 可导入 {importable}',
|
|
1091
1242
|
|
package/web-ui/modules/i18n.mjs
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { DICT } from './i18n.dict.mjs';
|
|
2
2
|
|
|
3
|
-
const I18N_STORAGE_KEY = 'codexmateLang';
|
|
4
|
-
|
|
5
3
|
const LANGUAGE_META = Object.freeze([
|
|
6
4
|
Object.freeze({ code: 'zh', nativeName: '中文', englishName: 'Chinese', htmlLang: 'zh-CN', dir: 'ltr' }),
|
|
7
5
|
Object.freeze({ code: 'zh-tw', nativeName: '繁體中文', englishName: 'Chinese-TW', htmlLang: 'zh-TW', dir: 'ltr' }),
|
|
@@ -52,10 +50,7 @@ export function createI18nMethods() {
|
|
|
52
50
|
return {
|
|
53
51
|
normalizeLang,
|
|
54
52
|
initI18n() {
|
|
55
|
-
const
|
|
56
|
-
? localStorage.getItem(I18N_STORAGE_KEY)
|
|
57
|
-
: '';
|
|
58
|
-
const next = normalizeLang(saved);
|
|
53
|
+
const next = normalizeLang(this.lang);
|
|
59
54
|
this.lang = next;
|
|
60
55
|
applyDocumentLanguage(next);
|
|
61
56
|
},
|
|
@@ -90,15 +85,13 @@ export function createI18nMethods() {
|
|
|
90
85
|
}
|
|
91
86
|
});
|
|
92
87
|
},
|
|
93
|
-
setLang(nextLang) {
|
|
88
|
+
setLang(nextLang, options = {}) {
|
|
94
89
|
const next = normalizeLang(nextLang);
|
|
95
90
|
this.lang = next;
|
|
96
|
-
try {
|
|
97
|
-
if (typeof localStorage !== 'undefined') {
|
|
98
|
-
localStorage.setItem(I18N_STORAGE_KEY, next);
|
|
99
|
-
}
|
|
100
|
-
} catch (_) {}
|
|
101
91
|
applyDocumentLanguage(next);
|
|
92
|
+
if (!(options && options.persist === false) && typeof this.persistWebUiPreferences === 'function') {
|
|
93
|
+
this.persistWebUiPreferences({ language: next });
|
|
94
|
+
}
|
|
102
95
|
},
|
|
103
96
|
t(key, params = null) {
|
|
104
97
|
const lang = normalizeLang(this.lang);
|
|
@@ -63,16 +63,15 @@
|
|
|
63
63
|
:class="{ active: isMainTabNavActive('usage') }"
|
|
64
64
|
@pointerdown="onMainTabPointerDown('usage', $event)"
|
|
65
65
|
@click="onMainTabClick('usage', $event)">{{ t('tab.usage') }}</button>
|
|
66
|
-
<button v-if="taskOrchestrationTabEnabled" type="button" class="top-tab"
|
|
66
|
+
<button v-if="taskOrchestrationTabEnabled" type="button" class="top-tab top-tab-disabled"
|
|
67
67
|
id="tab-orchestration"
|
|
68
68
|
role="tab"
|
|
69
69
|
data-main-tab="orchestration"
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
aria-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
@click="onMainTabClick('orchestration', $event)">{{ t('tab.orchestration') }}</button>
|
|
70
|
+
tabindex="-1"
|
|
71
|
+
aria-selected="false"
|
|
72
|
+
aria-disabled="true"
|
|
73
|
+
disabled
|
|
74
|
+
:title="t('orchestration.rebuilding.body')">{{ t('tab.orchestration') }}</button>
|
|
76
75
|
<button type="button" class="top-tab"
|
|
77
76
|
id="tab-market"
|
|
78
77
|
role="tab"
|
|
@@ -123,8 +122,17 @@
|
|
|
123
122
|
@click="openLanguageSettings">{{ t('settings.language.sideLabel', { language: currentLanguageLabel() }) }}</button>
|
|
124
123
|
</div>
|
|
125
124
|
|
|
126
|
-
<div :class="['app-shell', { standalone: sessionStandalone }]">
|
|
125
|
+
<div :class="['app-shell', { standalone: sessionStandalone, 'sidebar-collapsed': sidebarCollapsed }]">
|
|
127
126
|
<aside class="side-rail" v-if="!sessionStandalone">
|
|
127
|
+
<button
|
|
128
|
+
type="button"
|
|
129
|
+
class="side-rail-collapse-toggle"
|
|
130
|
+
:aria-pressed="sidebarCollapsed ? 'true' : 'false'"
|
|
131
|
+
:aria-label="sidebarCollapsed ? t('side.expand') : t('side.collapse')"
|
|
132
|
+
:title="sidebarCollapsed ? t('side.expand') : t('side.collapse')"
|
|
133
|
+
@click="toggleSidebarCollapsed">
|
|
134
|
+
<span aria-hidden="true">{{ sidebarCollapsed ? '›' : '‹' }}</span>
|
|
135
|
+
</button>
|
|
128
136
|
<div class="brand-block">
|
|
129
137
|
<div class="brand-head">
|
|
130
138
|
<img class="brand-logo" src="/res/logo-pack.webp" alt="Codex Mate logo">
|
|
@@ -156,6 +164,7 @@
|
|
|
156
164
|
:class="['side-item', { active: isMainTabNavActive('dashboard') }]"
|
|
157
165
|
@pointerdown="onMainTabPointerDown('dashboard', $event)"
|
|
158
166
|
@click="onMainTabClick('dashboard', $event)">
|
|
167
|
+
<span class="side-item-icon" aria-hidden="true">⌂</span>
|
|
159
168
|
<div class="side-item-title">{{ t('side.overview.doctor') }}</div>
|
|
160
169
|
<div class="side-item-meta">
|
|
161
170
|
<span>{{ t('side.overview.doctor.meta') }}</span>
|
|
@@ -172,6 +181,7 @@
|
|
|
172
181
|
:class="['side-item', { active: isMainTabNavActive('docs') }]"
|
|
173
182
|
@pointerdown="onMainTabPointerDown('docs', $event)"
|
|
174
183
|
@click="onMainTabClick('docs', $event)">
|
|
184
|
+
<span class="side-item-icon" aria-hidden="true">?</span>
|
|
175
185
|
<div class="side-item-title">{{ t('side.docs.cliInstall') }}</div>
|
|
176
186
|
<div class="side-item-meta">
|
|
177
187
|
<span>{{ t('side.docs.cliInstall.meta') }}</span>
|
|
@@ -189,6 +199,7 @@
|
|
|
189
199
|
:class="['side-item', { active: isConfigModeNavActive('codex') }]"
|
|
190
200
|
@pointerdown="onConfigTabPointerDown('codex', $event)"
|
|
191
201
|
@click="onConfigTabClick('codex', $event)">
|
|
202
|
+
<span class="side-item-icon" aria-hidden="true">C</span>
|
|
192
203
|
<div class="side-item-title">{{ t('side.config.codex') }}</div>
|
|
193
204
|
<div class="side-item-meta">
|
|
194
205
|
<span>{{ t('side.config.codex.meta') }}</span>
|
|
@@ -203,6 +214,7 @@
|
|
|
203
214
|
:class="['side-item', { active: isConfigModeNavActive('claude') }]"
|
|
204
215
|
@pointerdown="onConfigTabPointerDown('claude', $event)"
|
|
205
216
|
@click="onConfigTabClick('claude', $event)">
|
|
217
|
+
<span class="side-item-icon" aria-hidden="true">A</span>
|
|
206
218
|
<div class="side-item-title">{{ t('side.config.claude') }}</div>
|
|
207
219
|
<div class="side-item-meta">
|
|
208
220
|
<span>{{ t('side.config.claude.meta') }}</span>
|
|
@@ -217,6 +229,7 @@
|
|
|
217
229
|
:class="['side-item', { active: isConfigModeNavActive('openclaw') }]"
|
|
218
230
|
@pointerdown="onConfigTabPointerDown('openclaw', $event)"
|
|
219
231
|
@click="onConfigTabClick('openclaw', $event)">
|
|
232
|
+
<span class="side-item-icon" aria-hidden="true">O</span>
|
|
220
233
|
<div class="side-item-title">{{ t('side.config.openclaw') }}</div>
|
|
221
234
|
<div class="side-item-meta">
|
|
222
235
|
<span>{{ t('side.config.openclaw.meta') }}</span>
|
|
@@ -231,44 +244,30 @@
|
|
|
231
244
|
:class="['side-item', { active: isConfigModeNavActive('opencode') }]"
|
|
232
245
|
@pointerdown="onConfigTabPointerDown('opencode', $event)"
|
|
233
246
|
@click="onConfigTabClick('opencode', $event)">
|
|
247
|
+
<span class="side-item-icon" aria-hidden="true">N</span>
|
|
234
248
|
<div class="side-item-title">{{ t('side.config.opencode') }}</div>
|
|
235
249
|
<div class="side-item-meta">
|
|
236
250
|
<span>{{ t('side.config.opencode.meta') }}</span>
|
|
237
251
|
<span v-if="opencodeModel">{{ t('common.current', { value: opencodeModel }) }}</span>
|
|
238
252
|
</div>
|
|
239
253
|
</button>
|
|
240
|
-
</div>
|
|
241
|
-
|
|
242
|
-
<div class="side-section" role="navigation" :aria-label="t('side.prompts')">
|
|
243
|
-
<div class="side-section-title">{{ t('side.prompts') }}</div>
|
|
244
|
-
<button
|
|
245
|
-
id="side-tab-prompts-agents"
|
|
246
|
-
data-main-tab="prompts"
|
|
247
|
-
data-prompts-sub-tab="codex"
|
|
248
|
-
:aria-current="mainTab === 'prompts' && promptsSubTab === 'codex' ? 'page' : null"
|
|
249
|
-
:class="['side-item', { active: isMainTabNavActive('prompts') && promptsSubTab === 'codex' }]"
|
|
250
|
-
@pointerdown="onMainTabPointerDown('prompts', $event)"
|
|
251
|
-
@click="switchPromptsSubTab('codex'); onMainTabClick('prompts')">
|
|
252
|
-
<div class="side-item-title">{{ t('side.prompts.agents') }}</div>
|
|
253
|
-
<div class="side-item-meta">
|
|
254
|
-
<span>{{ t('side.prompts.agents.meta') }}</span>
|
|
255
|
-
</div>
|
|
256
|
-
</button>
|
|
257
254
|
<button
|
|
258
|
-
id="side-tab-prompts
|
|
255
|
+
id="side-tab-prompts"
|
|
259
256
|
data-main-tab="prompts"
|
|
260
|
-
|
|
261
|
-
:
|
|
262
|
-
:class="['side-item', { active: isMainTabNavActive('prompts') && promptsSubTab === 'claude-project' }]"
|
|
257
|
+
:aria-current="mainTab === 'prompts' ? 'page' : null"
|
|
258
|
+
:class="['side-item', { active: isMainTabNavActive('prompts') }]"
|
|
263
259
|
@pointerdown="onMainTabPointerDown('prompts', $event)"
|
|
264
|
-
@click="
|
|
265
|
-
<
|
|
260
|
+
@click="onMainTabClick('prompts', $event)">
|
|
261
|
+
<span class="side-item-icon" aria-hidden="true">P</span>
|
|
262
|
+
<div class="side-item-title">{{ t('side.prompts') }}</div>
|
|
266
263
|
<div class="side-item-meta">
|
|
267
|
-
<span>{{ t('side.prompts.
|
|
264
|
+
<span>{{ t('side.prompts.meta') }}</span>
|
|
268
265
|
</div>
|
|
269
266
|
</button>
|
|
267
|
+
|
|
270
268
|
</div>
|
|
271
269
|
|
|
270
|
+
|
|
272
271
|
<div class="side-section" role="navigation" :aria-label="t('side.sessions')">
|
|
273
272
|
<div class="side-section-title">{{ t('side.sessions') }}</div>
|
|
274
273
|
<button
|
|
@@ -278,6 +277,7 @@
|
|
|
278
277
|
:class="['side-item', { active: isMainTabNavActive('sessions') }]"
|
|
279
278
|
@pointerdown="onMainTabPointerDown('sessions', $event)"
|
|
280
279
|
@click="onMainTabClick('sessions', $event)">
|
|
280
|
+
<span class="side-item-icon" aria-hidden="true">S</span>
|
|
281
281
|
<div class="side-item-title">{{ t('side.sessions.browser') }}</div>
|
|
282
282
|
<div class="side-item-meta">
|
|
283
283
|
<span>{{ t('side.sessions.browser.meta') }}</span>
|
|
@@ -291,6 +291,7 @@
|
|
|
291
291
|
:class="['side-item', { active: isMainTabNavActive('usage') }]"
|
|
292
292
|
@pointerdown="onMainTabPointerDown('usage', $event)"
|
|
293
293
|
@click="onMainTabClick('usage', $event)">
|
|
294
|
+
<span class="side-item-icon" aria-hidden="true">↗</span>
|
|
294
295
|
<div class="side-item-title">{{ t('tab.usage') }}</div>
|
|
295
296
|
<div class="side-item-meta">
|
|
296
297
|
<span>{{ t('side.usage.meta') }}</span>
|
|
@@ -304,14 +305,15 @@
|
|
|
304
305
|
<button
|
|
305
306
|
id="side-tab-orchestration"
|
|
306
307
|
data-main-tab="orchestration"
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
308
|
+
class="side-item side-item-disabled"
|
|
309
|
+
aria-disabled="true"
|
|
310
|
+
disabled
|
|
311
|
+
:title="t('orchestration.rebuilding.body')">
|
|
312
|
+
<span class="side-item-icon" aria-hidden="true">T</span>
|
|
311
313
|
<div class="side-item-title">{{ t('tab.orchestration') }}</div>
|
|
312
314
|
<div class="side-item-meta">
|
|
313
|
-
<span>{{ t('
|
|
314
|
-
<span>{{ t('orchestration.
|
|
315
|
+
<span>{{ t('orchestration.rebuilding.status') }}</span>
|
|
316
|
+
<span>{{ t('orchestration.rebuilding.title') }}</span>
|
|
315
317
|
</div>
|
|
316
318
|
</button>
|
|
317
319
|
</div>
|
|
@@ -325,6 +327,7 @@
|
|
|
325
327
|
:class="['side-item', { active: isMainTabNavActive('market') }]"
|
|
326
328
|
@pointerdown="onMainTabPointerDown('market', $event)"
|
|
327
329
|
@click="onMainTabClick('market', $event)">
|
|
330
|
+
<span class="side-item-icon" aria-hidden="true">★</span>
|
|
328
331
|
<div class="side-item-title">{{ t('tab.market') }}</div>
|
|
329
332
|
<div class="side-item-meta">
|
|
330
333
|
<span>{{ t('skills.localLabel', { target: skillsTargetLabel }) }}</span>
|
|
@@ -342,6 +345,7 @@
|
|
|
342
345
|
:class="['side-item', { active: isMainTabNavActive('plugins') }]"
|
|
343
346
|
@pointerdown="onMainTabPointerDown('plugins', $event)"
|
|
344
347
|
@click="onMainTabClick('plugins', $event)">
|
|
348
|
+
<span class="side-item-icon" aria-hidden="true">◇</span>
|
|
345
349
|
<div class="side-item-title">{{ t('side.plugins.tools') }}</div>
|
|
346
350
|
<div class="side-item-meta">
|
|
347
351
|
<span>{{ t('side.plugins.tools.meta') }}</span>
|
|
@@ -359,6 +363,7 @@
|
|
|
359
363
|
:class="['side-item', { active: isMainTabNavActive('settings') }]"
|
|
360
364
|
@pointerdown="onMainTabPointerDown('settings', $event)"
|
|
361
365
|
@click="onMainTabClick('settings', $event)">
|
|
366
|
+
<span class="side-item-icon" aria-hidden="true">⚙</span>
|
|
362
367
|
<div class="side-item-title">{{ t('side.system.settings') }}</div>
|
|
363
368
|
<div class="side-item-meta">
|
|
364
369
|
<span>{{ t('side.system.settings.meta') }}</span>
|
|
@@ -371,6 +376,7 @@
|
|
|
371
376
|
:class="['side-item', { active: isMainTabNavActive('trash') }]"
|
|
372
377
|
@pointerdown="onMainTabPointerDown('trash', $event)"
|
|
373
378
|
@click="onMainTabClick('trash', $event)">
|
|
379
|
+
<span class="side-item-icon" aria-hidden="true">⌫</span>
|
|
374
380
|
<div class="side-item-title">{{ t('settings.trash.title') }}</div>
|
|
375
381
|
<div class="side-item-meta">
|
|
376
382
|
<span>{{ t('settings.trash.meta') }}</span>
|