codexmate 0.0.44 → 0.0.47
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 +20 -7
- package/README.zh.md +20 -7
- package/cli.js +473 -3
- package/package.json +2 -1
- package/web-ui/app.js +42 -5
- package/web-ui/index.html +2 -0
- package/web-ui/modules/app.computed.index.mjs +3 -1
- package/web-ui/modules/app.computed.main-tabs.mjs +3 -0
- package/web-ui/modules/app.computed.prompts.mjs +28 -0
- package/web-ui/modules/app.computed.session.mjs +23 -1
- package/web-ui/modules/app.constants.mjs +13 -0
- package/web-ui/modules/app.methods.agents.mjs +50 -4
- package/web-ui/modules/app.methods.index.mjs +6 -0
- package/web-ui/modules/app.methods.navigation.mjs +2 -1
- package/web-ui/modules/app.methods.opencode-config.mjs +228 -0
- package/web-ui/modules/app.methods.session-actions.mjs +10 -0
- package/web-ui/modules/app.methods.startup-claude.mjs +3 -1
- package/web-ui/modules/app.methods.tool-config-permissions.mjs +3 -2
- package/web-ui/modules/config-mode.computed.mjs +17 -1
- package/web-ui/modules/i18n/locales/en.mjs +66 -5
- package/web-ui/modules/i18n/locales/ja.mjs +66 -5
- package/web-ui/modules/i18n/locales/vi.mjs +74 -0
- package/web-ui/modules/i18n/locales/zh-tw.mjs +1269 -0
- package/web-ui/modules/i18n/locales/zh.mjs +66 -5
- package/web-ui/modules/i18n.dict.mjs +2 -0
- package/web-ui/modules/i18n.mjs +3 -2
- package/web-ui/partials/index/layout-footer.html +1 -1
- package/web-ui/partials/index/layout-header.html +70 -2
- package/web-ui/partials/index/modal-config-template-agents.html +12 -13
- package/web-ui/partials/index/panel-config-claude.html +6 -12
- package/web-ui/partials/index/panel-config-codex.html +6 -11
- package/web-ui/partials/index/panel-config-opencode.html +166 -0
- package/web-ui/partials/index/panel-prompts.html +100 -0
- package/web-ui/partials/index/panel-sessions.html +30 -10
- package/web-ui/partials/index/panel-usage.html +34 -18
- package/web-ui/res/web-ui-render.precompiled.js +932 -183
- package/web-ui/styles/controls-forms.css +62 -4
- package/web-ui/styles/modals-core.css +162 -0
- package/web-ui/styles/responsive.css +65 -5
- package/web-ui/styles/sessions-toolbar-trash.css +45 -10
- package/web-ui/styles/sessions-usage.css +31 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const zh = Object.freeze({
|
|
2
2
|
// Global
|
|
3
3
|
'lang.zh': '中文',
|
|
4
|
+
'lang.zh-tw': '繁體中文',
|
|
4
5
|
'lang.en': 'English',
|
|
5
6
|
'lang.vi': '越南语',
|
|
6
7
|
'lang.label': '语言',
|
|
@@ -49,6 +50,8 @@ const zh = Object.freeze({
|
|
|
49
50
|
'common.apply': '应用',
|
|
50
51
|
'common.applying': '应用中...',
|
|
51
52
|
'common.confirming': '确认中...',
|
|
53
|
+
'common.preview': '预览',
|
|
54
|
+
'common.previewing': '预览中...',
|
|
52
55
|
'common.refreshFromText': '从文本刷新',
|
|
53
56
|
'common.backToEdit': '返回编辑',
|
|
54
57
|
'common.selectAll': '全选',
|
|
@@ -63,6 +66,8 @@ const zh = Object.freeze({
|
|
|
63
66
|
'common.none': '暂无',
|
|
64
67
|
'common.configured': '已配置',
|
|
65
68
|
'common.notConfigured': '未配置',
|
|
69
|
+
'common.enabled': '已启用',
|
|
70
|
+
'common.disabled': '已禁用',
|
|
66
71
|
'cli.missing.title': '{name} CLI 未安装',
|
|
67
72
|
'cli.missing.subtitle': '请先安装 {name} CLI 后再继续使用此页面。',
|
|
68
73
|
'cli.missing.openDocs': '打开安装指南',
|
|
@@ -123,18 +128,21 @@ const zh = Object.freeze({
|
|
|
123
128
|
'tab.config.codex': 'Codex',
|
|
124
129
|
'tab.config.claude': 'Claude',
|
|
125
130
|
'tab.config.openclaw': 'OpenClaw',
|
|
131
|
+
'tab.config.opencode': 'OpenCode',
|
|
126
132
|
'tab.sessions': '会话',
|
|
127
133
|
'tab.usage': '用量',
|
|
128
134
|
'tab.orchestration': '任务',
|
|
129
135
|
'tab.market': 'Skills',
|
|
130
136
|
'tab.plugins': '插件',
|
|
131
137
|
'tab.settings': '设置',
|
|
138
|
+
'tab.prompts': 'Prompts',
|
|
132
139
|
|
|
133
140
|
// Side rail section titles
|
|
134
141
|
'side.overview': '概览',
|
|
135
142
|
'side.docs': '文档',
|
|
136
143
|
'side.config': '配置',
|
|
137
144
|
'side.sessions': '会话',
|
|
145
|
+
'side.prompts': 'Prompts',
|
|
138
146
|
'side.plugins': '插件',
|
|
139
147
|
'side.system': '系统',
|
|
140
148
|
'side.orchestration': '任务',
|
|
@@ -162,8 +170,14 @@ const zh = Object.freeze({
|
|
|
162
170
|
'side.config.claude.meta': 'Claude Settings',
|
|
163
171
|
'side.config.openclaw': 'OpenClaw',
|
|
164
172
|
'side.config.openclaw.meta': 'JSON5 / AGENTS',
|
|
173
|
+
'side.config.opencode': 'OpenCode',
|
|
174
|
+
'side.config.opencode.meta': '配置 / Provider',
|
|
165
175
|
'side.sessions.browser': '会话浏览',
|
|
166
176
|
'side.sessions.browser.meta': '浏览 / 导出 / 清理',
|
|
177
|
+
'side.prompts.agents': 'AGENTS.md',
|
|
178
|
+
'side.prompts.agents.meta': 'Codex 指令文件',
|
|
179
|
+
'side.prompts.claude': 'CLAUDE.md',
|
|
180
|
+
'side.prompts.claude.meta': 'Claude 指令文件',
|
|
167
181
|
'side.plugins.tools': '提示词工具',
|
|
168
182
|
'side.plugins.tools.meta': '模板 / 变量',
|
|
169
183
|
'side.plugins.templatesCount': '{count} 个模板',
|
|
@@ -182,6 +196,7 @@ const zh = Object.freeze({
|
|
|
182
196
|
'kicker.docs': 'Docs',
|
|
183
197
|
'kicker.trash': '回收站',
|
|
184
198
|
'kicker.settings': 'Settings',
|
|
199
|
+
'kicker.prompts': 'Prompts',
|
|
185
200
|
|
|
186
201
|
'title.dashboard': 'Dashboard / Doctor',
|
|
187
202
|
'title.config': '本地配置控制台',
|
|
@@ -192,6 +207,7 @@ const zh = Object.freeze({
|
|
|
192
207
|
'title.plugins': '插件与模板',
|
|
193
208
|
'title.docs': 'CLI 安装与文档',
|
|
194
209
|
'title.settings': '系统与数据设置',
|
|
210
|
+
'title.prompts': '指令文件编辑器',
|
|
195
211
|
|
|
196
212
|
'subtitle.dashboard': '聚合状态与诊断入口。',
|
|
197
213
|
'subtitle.config': '管理本地配置与模型。',
|
|
@@ -202,6 +218,9 @@ const zh = Object.freeze({
|
|
|
202
218
|
'subtitle.plugins': '管理模板化 prompt 与可复用插件。',
|
|
203
219
|
'subtitle.docs': '查看 CLI 安装命令与排障。',
|
|
204
220
|
'subtitle.settings': '管理下载、目录与回收站。',
|
|
221
|
+
'subtitle.prompts': '编辑 AGENTS.md 与 CLAUDE.md。',
|
|
222
|
+
'prompts.subTab.codex': 'AGENTS.md (Codex)',
|
|
223
|
+
'prompts.subTab.claude': 'CLAUDE.md (Claude)',
|
|
205
224
|
'dashboard.doctor.title': 'Doctor',
|
|
206
225
|
'dashboard.doctor.runChecks': '运行检查',
|
|
207
226
|
'dashboard.doctor.checking': '检查中...',
|
|
@@ -378,8 +397,13 @@ const zh = Object.freeze({
|
|
|
378
397
|
'toast.copy.empty': '没有可复制内容',
|
|
379
398
|
'toast.copy.ok': '已复制',
|
|
380
399
|
'toast.copy.fail': '复制失败',
|
|
400
|
+
'toast.link.fail': '无法生成链接',
|
|
381
401
|
'toast.save.ok': '已保存',
|
|
382
402
|
'toast.save.fail': '保存失败',
|
|
403
|
+
'toast.agents.saved.agents': 'AGENTS.md 已保存',
|
|
404
|
+
'toast.agents.saved.claudeMd': 'CLAUDE.md 已保存',
|
|
405
|
+
'toast.agents.saved.openclaw': 'OpenClaw AGENTS.md 已保存',
|
|
406
|
+
'toast.agents.saved.workspace': '工作区文件已保存: {name}',
|
|
383
407
|
'toast.delete.ok': '已删除',
|
|
384
408
|
'toast.delete.fail': '删除失败',
|
|
385
409
|
'toast.export.empty': '没有可导出内容',
|
|
@@ -468,7 +492,7 @@ const zh = Object.freeze({
|
|
|
468
492
|
'diff.hint.busy': '正在生成差异或应用中,操作暂不可用。',
|
|
469
493
|
'diff.hint.failedBack': '差异预览失败,请返回编辑后重试。',
|
|
470
494
|
'diff.hint.noChangesBack': '未检测到改动,可返回编辑继续修改或取消退出。',
|
|
471
|
-
'diff.hint.previewMode': '
|
|
495
|
+
'diff.hint.previewMode': '当前为预览模式,可点击”保存”写入或”返回编辑”继续修改。',
|
|
472
496
|
|
|
473
497
|
'modal.agents.export': '导出',
|
|
474
498
|
'modal.agents.copy': '复制',
|
|
@@ -477,13 +501,13 @@ const zh = Object.freeze({
|
|
|
477
501
|
'modal.agents.targetFile': '目标文件',
|
|
478
502
|
'modal.agents.contentLabel': 'AGENTS.md 内容',
|
|
479
503
|
'modal.agents.placeholder': '在这里编辑 AGENTS.md 内容',
|
|
480
|
-
'modal.agents.unsaved.previewModeHint': '
|
|
504
|
+
'modal.agents.unsaved.previewModeHint': '预览模式:当前改动尚未保存,只有点击”保存”后才会写入文件。',
|
|
481
505
|
'modal.agents.unsaved.detectedHint': '检测到未保存改动:关闭页面或应用前请先保存。',
|
|
482
506
|
'modal.agents.hint.shortcuts': '快捷键:Esc(差异预览时返回编辑,编辑时关闭窗口)。',
|
|
483
|
-
'modal.agents.hint.twoStepSave': '
|
|
507
|
+
'modal.agents.hint.twoStepSave': '保存需两步:先点击”预览”查看差异,再点击”保存”写入。',
|
|
484
508
|
'diff.tooLargeSkip': '内容过大,已跳过逐行差异预览',
|
|
485
|
-
'diff.viewHint.preview': '
|
|
486
|
-
'diff.viewHint.truncated': '
|
|
509
|
+
'diff.viewHint.preview': '当前为预览模式,可点击”保存”写入或”返回编辑”继续修改。',
|
|
510
|
+
'diff.viewHint.truncated': '内容过大,已跳过预览,可点击”保存”写入或”返回编辑”继续修改。',
|
|
487
511
|
|
|
488
512
|
// Skills modal
|
|
489
513
|
'modal.skills.title': 'Skills 管理',
|
|
@@ -623,6 +647,7 @@ const zh = Object.freeze({
|
|
|
623
647
|
'sessions.preview.importNative.failedWithReason': '导入原生目录失败:{reason}',
|
|
624
648
|
'sessions.preview.copyLink': '复制链接',
|
|
625
649
|
'sessions.preview.copyPath': '复制路径',
|
|
650
|
+
'sessions.preview.openLink': '打开链接',
|
|
626
651
|
'sessions.preview.loadingBody': '正在加载会话内容...',
|
|
627
652
|
'sessions.preview.emptyMsgs': '当前会话暂无可展示消息',
|
|
628
653
|
'sessions.preview.rendering': '正在渲染会话内容...',
|
|
@@ -785,6 +810,39 @@ const zh = Object.freeze({
|
|
|
785
810
|
'toolConfig.claude.lockedTitle': 'Claude 提供商当前为只读',
|
|
786
811
|
'toolConfig.claude.lockedDesc': '这里不会写入 Claude 配置。要添加、应用、编辑或删除提供商,请先打开本 tab 的写入开关。',
|
|
787
812
|
'toolConfig.claude.confirmMessage': '打开后,Claude tab 内的应用操作会写入 ~/.claude/settings.json 等 Claude 配置。',
|
|
813
|
+
'toolConfig.opencode.title': 'OpenCode 配置写入',
|
|
814
|
+
'toolConfig.opencode.desc': '默认只读;开启后可写入 OpenCode 配置文件。',
|
|
815
|
+
'toolConfig.opencode.lockedTitle': '只读模式',
|
|
816
|
+
'toolConfig.opencode.lockedDesc': '开启写入权限后可保存、导入或应用配置。',
|
|
817
|
+
'toolConfig.opencode.confirmMessage': '打开后,OpenCode tab 内的操作会写入 XDG OpenCode 配置文件(如 ~/.config/opencode/opencode.jsonc)或 OPENCODE_CONFIG 指定文件。',
|
|
818
|
+
'opencode.providerModel.title': 'OpenCode provider / model',
|
|
819
|
+
'opencode.writeAria': 'OpenCode 写入权限',
|
|
820
|
+
'opencode.applySelection': '应用到 OpenCode',
|
|
821
|
+
'opencode.targetFile': 'OpenCode 生效配置:{path} · {status}',
|
|
822
|
+
'opencode.providerStoreFile': 'Provider 草稿库:{path}(应用时写入 OpenCode)',
|
|
823
|
+
'opencode.field.agent': 'Agent',
|
|
824
|
+
'opencode.field.apiKeyKeep': 'API Key(留空则保留已有 key)',
|
|
825
|
+
'opencode.field.maxTokens': 'maxTokens(可选)',
|
|
826
|
+
'opencode.field.reasoningEffort': 'reasoningEffort(可选)',
|
|
827
|
+
'opencode.option.keepUnchanged': '不改动',
|
|
828
|
+
'opencode.option.reasoningLow': 'low',
|
|
829
|
+
'opencode.option.reasoningMedium': 'medium',
|
|
830
|
+
'opencode.option.reasoningHigh': 'high',
|
|
831
|
+
'opencode.applyToCoreAgents': '同步应用到所有核心 agent',
|
|
832
|
+
'opencode.enableAutoCompaction': '启用 compaction.auto',
|
|
833
|
+
'opencode.disableProvider': '禁用该 provider',
|
|
834
|
+
'opencode.configFile.title': 'OpenCode 配置文件',
|
|
835
|
+
'opencode.importParse': '导入/解析文件',
|
|
836
|
+
'opencode.saveConfig': '保存配置',
|
|
837
|
+
'opencode.parsedFile': '已解析:{file}',
|
|
838
|
+
'opencode.textarea.placeholder': '编辑 OpenCode 配置',
|
|
839
|
+
'opencode.configFile.hint': '生效配置预览;草稿保存在独立 Provider 库。',
|
|
840
|
+
'opencode.summary.title': '当前解析摘要',
|
|
841
|
+
'opencode.summary.noApiKey': '未配置 API Key',
|
|
842
|
+
'opencode.summary.noModel': '未设置 model',
|
|
843
|
+
'opencode.summary.agentType': 'agent',
|
|
844
|
+
'opencode.summary.sourceCodexMate': 'CodexMate 库',
|
|
845
|
+
'opencode.summary.sourceOpenCode': 'OpenCode 生效',
|
|
788
846
|
'config.providerTemplate.title': '预设供应商',
|
|
789
847
|
'config.models': '模型',
|
|
790
848
|
'config.modelLoading': '加载中...',
|
|
@@ -1006,6 +1064,9 @@ const zh = Object.freeze({
|
|
|
1006
1064
|
'status.claudeConfig': 'Claude 配置',
|
|
1007
1065
|
'status.claudeModel': 'Claude 模型',
|
|
1008
1066
|
'status.openclawConfig': 'OpenClaw 配置',
|
|
1067
|
+
'status.opencodeProvider': 'OpenCode Provider',
|
|
1068
|
+
'status.opencodeModel': 'OpenCode 模型',
|
|
1069
|
+
'status.opencodeConfig': 'OpenCode 配置',
|
|
1009
1070
|
'status.workspaceFile': '工作区文件',
|
|
1010
1071
|
'status.configMode': '配置模式',
|
|
1011
1072
|
'status.currentProvider': '当前 Provider',
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { zh } from './i18n/locales/zh.mjs';
|
|
2
|
+
import { zhTw } from './i18n/locales/zh-tw.mjs';
|
|
2
3
|
import { en } from './i18n/locales/en.mjs';
|
|
3
4
|
import { ja } from './i18n/locales/ja.mjs';
|
|
4
5
|
import { vi } from './i18n/locales/vi.mjs';
|
|
5
6
|
|
|
6
7
|
const DICT = Object.freeze({
|
|
7
8
|
zh,
|
|
9
|
+
'zh-tw': zhTw,
|
|
8
10
|
en,
|
|
9
11
|
ja,
|
|
10
12
|
vi
|
package/web-ui/modules/i18n.mjs
CHANGED
|
@@ -4,6 +4,7 @@ const I18N_STORAGE_KEY = 'codexmateLang';
|
|
|
4
4
|
|
|
5
5
|
const LANGUAGE_META = Object.freeze([
|
|
6
6
|
Object.freeze({ code: 'zh', nativeName: '中文', englishName: 'Chinese', htmlLang: 'zh-CN', dir: 'ltr' }),
|
|
7
|
+
Object.freeze({ code: 'zh-tw', nativeName: '繁體中文', englishName: 'Chinese-TW', htmlLang: 'zh-TW', dir: 'ltr' }),
|
|
7
8
|
Object.freeze({ code: 'en', nativeName: 'English', englishName: 'English', htmlLang: 'en', dir: 'ltr' }),
|
|
8
9
|
Object.freeze({ code: 'ja', nativeName: '日本語', englishName: 'Japanese', htmlLang: 'ja', dir: 'ltr' }),
|
|
9
10
|
Object.freeze({ code: 'vi', nativeName: 'Tiếng Việt', englishName: 'Vietnamese', htmlLang: 'vi', dir: 'ltr' })
|
|
@@ -102,9 +103,9 @@ export function createI18nMethods() {
|
|
|
102
103
|
t(key, params = null) {
|
|
103
104
|
const lang = normalizeLang(this.lang);
|
|
104
105
|
const table = DICT[lang] || DICT.zh;
|
|
105
|
-
const fallbackEn = DICT.en;
|
|
106
106
|
const fallbackZh = DICT.zh;
|
|
107
|
-
const
|
|
107
|
+
const fallbackEn = DICT.en;
|
|
108
|
+
const raw = (table && table[key]) || (fallbackZh && fallbackZh[key]) || (fallbackEn && fallbackEn[key]) || key;
|
|
108
109
|
return interpolate(raw, params);
|
|
109
110
|
}
|
|
110
111
|
};
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
:data-config-mode="configMode"
|
|
40
40
|
:tabindex="mainTab === 'config' ? 0 : -1"
|
|
41
41
|
:aria-selected="mainTab === 'config'"
|
|
42
|
-
:aria-controls="configMode === 'claude' ? 'panel-config-claude' : (configMode === 'openclaw' ? 'panel-config-openclaw' : 'panel-config-provider')"
|
|
42
|
+
:aria-controls="configMode === 'claude' ? 'panel-config-claude' : (configMode === 'openclaw' ? 'panel-config-openclaw' : (configMode === 'opencode' ? 'panel-config-opencode' : 'panel-config-provider'))"
|
|
43
43
|
:class="{ active: isMainTabNavActive('config') }"
|
|
44
44
|
@pointerdown="onMainTabPointerDown('config', $event)"
|
|
45
45
|
@click="onMainTabClick('config', $event)">{{ t('tab.config') }}</button>
|
|
@@ -93,6 +93,16 @@
|
|
|
93
93
|
:class="{ active: isMainTabNavActive('plugins') }"
|
|
94
94
|
@pointerdown="onMainTabPointerDown('plugins', $event)"
|
|
95
95
|
@click="onMainTabClick('plugins', $event)">{{ t('tab.plugins') }}</button>
|
|
96
|
+
<button type="button" class="top-tab"
|
|
97
|
+
id="tab-prompts"
|
|
98
|
+
role="tab"
|
|
99
|
+
data-main-tab="prompts"
|
|
100
|
+
:tabindex="mainTab === 'prompts' ? 0 : -1"
|
|
101
|
+
:aria-selected="mainTab === 'prompts'"
|
|
102
|
+
aria-controls="panel-prompts"
|
|
103
|
+
:class="{ active: isMainTabNavActive('prompts') }"
|
|
104
|
+
@pointerdown="onMainTabPointerDown('prompts', $event)"
|
|
105
|
+
@click="onMainTabClick('prompts', $event)">{{ t('tab.prompts') }}</button>
|
|
96
106
|
<button type="button" class="top-tab"
|
|
97
107
|
id="tab-settings"
|
|
98
108
|
role="tab"
|
|
@@ -119,7 +129,7 @@
|
|
|
119
129
|
<div class="brand-head">
|
|
120
130
|
<img class="brand-logo" src="/res/logo-pack.webp" alt="Codex Mate logo">
|
|
121
131
|
<div class="brand-copy">
|
|
122
|
-
<div class="brand-kicker">Codex Mate
|
|
132
|
+
<div class="brand-kicker">Codex Mate</div>
|
|
123
133
|
</div>
|
|
124
134
|
</div>
|
|
125
135
|
<button
|
|
@@ -213,6 +223,50 @@
|
|
|
213
223
|
<span v-if="currentOpenclawConfig">{{ t('common.current', { value: currentOpenclawConfig }) }}</span>
|
|
214
224
|
</div>
|
|
215
225
|
</button>
|
|
226
|
+
<button
|
|
227
|
+
id="side-tab-config-opencode"
|
|
228
|
+
data-main-tab="config"
|
|
229
|
+
data-config-mode="opencode"
|
|
230
|
+
:aria-current="mainTab === 'config' && configMode === 'opencode' ? 'page' : null"
|
|
231
|
+
:class="['side-item', { active: isConfigModeNavActive('opencode') }]"
|
|
232
|
+
@pointerdown="onConfigTabPointerDown('opencode', $event)"
|
|
233
|
+
@click="onConfigTabClick('opencode', $event)">
|
|
234
|
+
<div class="side-item-title">{{ t('side.config.opencode') }}</div>
|
|
235
|
+
<div class="side-item-meta">
|
|
236
|
+
<span>{{ t('side.config.opencode.meta') }}</span>
|
|
237
|
+
<span v-if="opencodeModel">{{ t('common.current', { value: opencodeModel }) }}</span>
|
|
238
|
+
</div>
|
|
239
|
+
</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
|
+
<button
|
|
258
|
+
id="side-tab-prompts-claude"
|
|
259
|
+
data-main-tab="prompts"
|
|
260
|
+
data-prompts-sub-tab="claude-md"
|
|
261
|
+
:aria-current="mainTab === 'prompts' && promptsSubTab === 'claude-md' ? 'page' : null"
|
|
262
|
+
:class="['side-item', { active: isMainTabNavActive('prompts') && promptsSubTab === 'claude-md' }]"
|
|
263
|
+
@pointerdown="onMainTabPointerDown('prompts', $event)"
|
|
264
|
+
@click="switchPromptsSubTab('claude-md'); onMainTabClick('prompts')">
|
|
265
|
+
<div class="side-item-title">{{ t('side.prompts.claude') }}</div>
|
|
266
|
+
<div class="side-item-meta">
|
|
267
|
+
<span>{{ t('side.prompts.claude.meta') }}</span>
|
|
268
|
+
</div>
|
|
269
|
+
</button>
|
|
216
270
|
</div>
|
|
217
271
|
|
|
218
272
|
<div class="side-section" role="navigation" :aria-label="t('side.sessions')">
|
|
@@ -379,6 +433,20 @@
|
|
|
379
433
|
<span class="value">{{ openclawWorkspaceFileName || t('common.notSelected') }}</span>
|
|
380
434
|
</div>
|
|
381
435
|
</template>
|
|
436
|
+
<template v-else-if="configMode === 'opencode'">
|
|
437
|
+
<div class="status-chip">
|
|
438
|
+
<span class="label">{{ t('status.opencodeProvider') }}</span>
|
|
439
|
+
<span class="value">{{ opencodeProvider || t('common.notSelected') }}</span>
|
|
440
|
+
</div>
|
|
441
|
+
<div class="status-chip">
|
|
442
|
+
<span class="label">{{ t('status.opencodeModel') }}</span>
|
|
443
|
+
<span class="value">{{ opencodeModel || t('common.notSelected') }}</span>
|
|
444
|
+
</div>
|
|
445
|
+
<div class="status-chip">
|
|
446
|
+
<span class="label">{{ t('status.opencodeConfig') }}</span>
|
|
447
|
+
<span class="value">{{ opencodeConfigPath || t('common.notSelected') }}</span>
|
|
448
|
+
</div>
|
|
449
|
+
</template>
|
|
382
450
|
<template v-else>
|
|
383
451
|
<div class="status-chip">
|
|
384
452
|
<span class="label">{{ t('status.configMode') }}</span>
|
|
@@ -102,6 +102,18 @@
|
|
|
102
102
|
:disabled="agentsLoading || agentsSaving || agentsDiffVisible">
|
|
103
103
|
{{ t('common.paste') }}
|
|
104
104
|
</button>
|
|
105
|
+
<span class="prompts-editor-actions-sep"></span>
|
|
106
|
+
<button class="btn-mini" @click="closeAgentsModal" :disabled="agentsSaving || agentsDiffLoading">{{ t('common.cancel') }}</button>
|
|
107
|
+
<button
|
|
108
|
+
v-if="agentsDiffVisible"
|
|
109
|
+
class="btn-mini"
|
|
110
|
+
@click="resetAgentsDiffState"
|
|
111
|
+
:disabled="agentsSaving || agentsDiffLoading">
|
|
112
|
+
{{ t('common.backToEdit') }}
|
|
113
|
+
</button>
|
|
114
|
+
<button class="btn-mini btn-confirm-mini" @click="applyAgentsContent" :disabled="agentsSaving || agentsLoading || agentsDiffLoading || (agentsDiffVisible && !agentsDiffHasChanges)">
|
|
115
|
+
{{ agentsSaving ? (agentsDiffVisible ? t('common.saving') : t('common.previewing')) : (agentsDiffVisible ? t('common.save') : t('common.preview')) }}
|
|
116
|
+
</button>
|
|
105
117
|
</div>
|
|
106
118
|
</div>
|
|
107
119
|
|
|
@@ -168,18 +180,5 @@
|
|
|
168
180
|
|
|
169
181
|
</div>
|
|
170
182
|
|
|
171
|
-
<div class="btn-group modal-editor-footer">
|
|
172
|
-
<button class="btn btn-cancel" @click="closeAgentsModal" :disabled="agentsSaving || agentsDiffLoading">{{ t('common.cancel') }}</button>
|
|
173
|
-
<button
|
|
174
|
-
v-if="agentsDiffVisible"
|
|
175
|
-
class="btn"
|
|
176
|
-
@click="resetAgentsDiffState"
|
|
177
|
-
:disabled="agentsSaving || agentsDiffLoading">
|
|
178
|
-
{{ t('common.backToEdit') }}
|
|
179
|
-
</button>
|
|
180
|
-
<button class="btn btn-confirm" @click="applyAgentsContent" :disabled="agentsSaving || agentsLoading || agentsDiffLoading || (agentsDiffVisible && !agentsDiffHasChanges)">
|
|
181
|
-
{{ agentsSaving ? (agentsDiffVisible ? t('common.applying') : t('common.confirming')) : (agentsDiffVisible ? t('common.apply') : t('common.confirm')) }}
|
|
182
|
-
</button>
|
|
183
|
-
</div>
|
|
184
183
|
</div>
|
|
185
184
|
</div>
|
|
@@ -108,12 +108,6 @@
|
|
|
108
108
|
<button class="btn-tool btn-template-editor" @click="openClaudeConfigTemplateEditor" :disabled="loading || !!initError">{{ t('config.template.openEditor') }}</button>
|
|
109
109
|
</div>
|
|
110
110
|
|
|
111
|
-
<div class="selector-section">
|
|
112
|
-
<div class="selector-header"><span class="selector-title">CLAUDE.md</span></div>
|
|
113
|
-
<button class="btn-tool" @click="openClaudeMdEditor" :disabled="loading || !!initError || agentsLoading">{{ agentsLoading ? t('config.modelLoading') : t('claude.md.open') }}</button>
|
|
114
|
-
<div class="config-template-hint">{{ t('claude.md.hint') }}</div>
|
|
115
|
-
</div>
|
|
116
|
-
|
|
117
111
|
<div class="selector-section">
|
|
118
112
|
<div class="selector-header"><span class="selector-title">{{ t('config.health.title') }}</span></div>
|
|
119
113
|
<button class="btn-tool" @click="runHealthCheck" :disabled="healthCheckLoading || loading || !!initError">{{ healthCheckLoading ? t('config.health.running') : t('config.health.run') }}</button>
|
|
@@ -176,12 +170,12 @@
|
|
|
176
170
|
</div>
|
|
177
171
|
</div>
|
|
178
172
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
173
|
+
<div v-if="!isToolConfigWriteAllowed('claude')" class="tool-config-write-overlay">
|
|
174
|
+
<div class="tool-config-write-overlay-card">
|
|
175
|
+
<div class="tool-config-write-overlay-title">{{ t('toolConfig.claude.lockedTitle') }}</div>
|
|
176
|
+
<p>{{ t('toolConfig.claude.lockedDesc') }}</p>
|
|
177
|
+
<button type="button" class="btn-tool" @click="setToolConfigPermission('claude', true)" :disabled="toolConfigPermissionSaving.claude">{{ t('toolConfig.enableWrite') }}</button>
|
|
178
|
+
</div>
|
|
185
179
|
</div>
|
|
186
180
|
</div>
|
|
187
181
|
</div>
|
|
@@ -61,11 +61,6 @@
|
|
|
61
61
|
</div>
|
|
62
62
|
</div>
|
|
63
63
|
|
|
64
|
-
<div class="selector-section">
|
|
65
|
-
<div class="selector-header"><span class="selector-title">AGENTS.md</span></div>
|
|
66
|
-
<button class="btn-tool" @click="openAgentsEditor" :disabled="loading || !!initError || agentsLoading">{{ agentsLoading ? t('config.modelLoading') : t('config.agents.open') }}</button>
|
|
67
|
-
</div>
|
|
68
|
-
|
|
69
64
|
<div class="selector-section">
|
|
70
65
|
<div class="selector-header">
|
|
71
66
|
<span class="selector-title">{{ t('config.models') }}</span>
|
|
@@ -194,12 +189,12 @@
|
|
|
194
189
|
</div>
|
|
195
190
|
</div>
|
|
196
191
|
</div>
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
192
|
+
<div v-if="!isToolConfigWriteAllowed('codex')" class="tool-config-write-overlay">
|
|
193
|
+
<div class="tool-config-write-overlay-card">
|
|
194
|
+
<div class="tool-config-write-overlay-title">{{ t('toolConfig.codex.lockedTitle') }}</div>
|
|
195
|
+
<p>{{ t('toolConfig.codex.lockedDesc') }}</p>
|
|
196
|
+
<button type="button" class="btn-tool" @click="setToolConfigPermission('codex', true)" :disabled="toolConfigPermissionSaving.codex">{{ t('toolConfig.enableWrite') }}</button>
|
|
197
|
+
</div>
|
|
203
198
|
</div>
|
|
204
199
|
</div>
|
|
205
200
|
</div>
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
<!-- OpenCode 配置 -->
|
|
2
|
+
<div
|
|
3
|
+
v-show="mainTab === 'config' && configMode === 'opencode'"
|
|
4
|
+
class="mode-content mode-cards"
|
|
5
|
+
id="panel-config-opencode"
|
|
6
|
+
role="tabpanel"
|
|
7
|
+
:aria-labelledby="forceCompactLayout ? 'tab-config' : 'side-tab-config-opencode'">
|
|
8
|
+
<div v-if="forceCompactLayout && !sessionStandalone" class="segmented-control">
|
|
9
|
+
<button type="button" :class="['segment', { active: configMode === 'codex' }]" @click="switchConfigMode('codex')">{{ t('tab.config.codex') }}</button>
|
|
10
|
+
<button type="button" :class="['segment', { active: configMode === 'claude' }]" @click="switchConfigMode('claude')">{{ t('tab.config.claude') }}</button>
|
|
11
|
+
<button type="button" :class="['segment', { active: configMode === 'openclaw' }]" @click="switchConfigMode('openclaw')">{{ t('tab.config.openclaw') }}</button>
|
|
12
|
+
<button type="button" :class="['segment', { active: configMode === 'opencode' }]" @click="switchConfigMode('opencode')">{{ t('tab.config.opencode') }}</button>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<section class="tool-config-write-card" :aria-label="t('opencode.writeAria')">
|
|
16
|
+
<div class="tool-config-write-copy">
|
|
17
|
+
<div class="tool-config-write-title">{{ t('toolConfig.opencode.title') }}</div>
|
|
18
|
+
<p class="tool-config-write-desc">{{ t('toolConfig.opencode.desc') }}</p>
|
|
19
|
+
</div>
|
|
20
|
+
<label class="settings-toggle-row tool-config-write-toggle">
|
|
21
|
+
<input
|
|
22
|
+
type="checkbox"
|
|
23
|
+
autocomplete="off"
|
|
24
|
+
:checked="isToolConfigWriteAllowed('opencode')"
|
|
25
|
+
:disabled="toolConfigPermissionSaving.opencode"
|
|
26
|
+
@change="setToolConfigPermission('opencode', $event.target.checked)">
|
|
27
|
+
<span class="toggle-track">
|
|
28
|
+
<span class="toggle-thumb"></span>
|
|
29
|
+
</span>
|
|
30
|
+
<span>{{ toolConfigPermissionStatusLabel('opencode') }}</span>
|
|
31
|
+
</label>
|
|
32
|
+
</section>
|
|
33
|
+
|
|
34
|
+
<div class="tool-config-write-scope" :class="{ locked: !isToolConfigWriteAllowed('opencode') }">
|
|
35
|
+
<div class="tool-config-write-body">
|
|
36
|
+
<section class="selector-section">
|
|
37
|
+
<div class="selector-header">
|
|
38
|
+
<span class="selector-title">{{ t('opencode.providerModel.title') }}</span>
|
|
39
|
+
<div class="selector-actions opencode-provider-actions">
|
|
40
|
+
<button type="button" class="btn-tool btn-tool-compact" @click="loadOpencodeConfig({ toast: true })" :disabled="opencodeLoading">{{ opencodeLoading ? t('common.refreshing') : t('common.refresh') }}</button>
|
|
41
|
+
<button type="button" class="btn-tool btn-tool-compact" @click="applyOpencodeSelection" :disabled="opencodeApplying || opencodeLoading || !isToolConfigWriteAllowed('opencode')">{{ opencodeApplying ? t('common.applying') : t('opencode.applySelection') }}</button>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="config-template-hint">{{ t('opencode.targetFile', { path: opencodeConfigPath || '~/.config/opencode/opencode.jsonc', status: opencodeConfigExists ? t('common.exists') : t('common.notExistsWillCreateOnSave') }) }}</div>
|
|
45
|
+
<div class="config-template-hint">{{ t('opencode.providerStoreFile', { path: opencodeProviderStorePath || '~/.codexmate/opencode/providers.json' }) }}</div>
|
|
46
|
+
<div class="codex-config-grid">
|
|
47
|
+
<div class="form-group codex-config-field">
|
|
48
|
+
<label class="form-label" for="opencode-provider">{{ t('field.provider') }}</label>
|
|
49
|
+
<input id="opencode-provider" class="form-input" v-model="opencodeProvider" list="opencode-provider-options" autocomplete="off" spellcheck="false" @change="fillOpencodeProvider(opencodeProvider)" @blur="fillOpencodeProvider(opencodeProvider)" placeholder="anthropic / openai / gemini">
|
|
50
|
+
<datalist id="opencode-provider-options">
|
|
51
|
+
<option v-for="provider in opencodeProviderCatalog()" :key="provider" :value="provider"></option>
|
|
52
|
+
</datalist>
|
|
53
|
+
</div>
|
|
54
|
+
<div class="form-group codex-config-field">
|
|
55
|
+
<label class="form-label" for="opencode-model">{{ t('field.model') }}</label>
|
|
56
|
+
<input id="opencode-model" class="form-input" v-model="opencodeModel" list="opencode-model-options" autocomplete="off" spellcheck="false" placeholder="claude-3.7-sonnet / gpt-4.1">
|
|
57
|
+
<datalist id="opencode-model-options">
|
|
58
|
+
<option v-for="model in opencodeModelCatalogForProvider(opencodeProvider)" :key="model" :value="model"></option>
|
|
59
|
+
</datalist>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="form-group codex-config-field">
|
|
62
|
+
<label class="form-label" for="opencode-agent">{{ t('opencode.field.agent') }}</label>
|
|
63
|
+
<input id="opencode-agent" class="form-input" v-model="opencodeAgent" list="opencode-agent-options" autocomplete="off" spellcheck="false" placeholder="build">
|
|
64
|
+
<datalist id="opencode-agent-options">
|
|
65
|
+
<option value="build"></option>
|
|
66
|
+
<option value="plan"></option>
|
|
67
|
+
<option value="general"></option>
|
|
68
|
+
<option value="summary"></option>
|
|
69
|
+
<option value="compaction"></option>
|
|
70
|
+
<option value="title"></option>
|
|
71
|
+
<option v-for="agent in opencodeAgents" :key="agent.name" :value="agent.name"></option>
|
|
72
|
+
</datalist>
|
|
73
|
+
</div>
|
|
74
|
+
<div class="form-group codex-config-field">
|
|
75
|
+
<label class="form-label" for="opencode-api-key">{{ t('opencode.field.apiKeyKeep') }}</label>
|
|
76
|
+
<div class="input-with-toggle">
|
|
77
|
+
<input id="opencode-api-key" class="form-input" v-model="opencodeApiKey" :type="opencodeShowKey ? 'text' : 'password'" autocomplete="off" spellcheck="false" placeholder="sk-...">
|
|
78
|
+
<button type="button" class="input-toggle-btn" @click="opencodeShowKey = !opencodeShowKey" :title="opencodeShowKey ? t('common.hide') : t('common.show')">{{ opencodeShowKey ? t('common.hide') : t('common.show') }}</button>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="form-group codex-config-field">
|
|
82
|
+
<label class="form-label" for="opencode-max-tokens">{{ t('opencode.field.maxTokens') }}</label>
|
|
83
|
+
<input id="opencode-max-tokens" class="form-input" v-model="opencodeMaxTokens" inputmode="numeric" autocomplete="off" placeholder="5000">
|
|
84
|
+
</div>
|
|
85
|
+
<div class="form-group codex-config-field">
|
|
86
|
+
<label class="form-label" for="opencode-reasoning-effort">{{ t('opencode.field.reasoningEffort') }}</label>
|
|
87
|
+
<select id="opencode-reasoning-effort" class="model-select" v-model="opencodeReasoningEffort">
|
|
88
|
+
<option value="">{{ t('opencode.option.keepUnchanged') }}</option>
|
|
89
|
+
<option value="low">{{ t('opencode.option.reasoningLow') }}</option>
|
|
90
|
+
<option value="medium">{{ t('opencode.option.reasoningMedium') }}</option>
|
|
91
|
+
<option value="high">{{ t('opencode.option.reasoningHigh') }}</option>
|
|
92
|
+
</select>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
<label class="settings-toggle-row" style="margin-top: 12px;">
|
|
96
|
+
<input type="checkbox" v-model="opencodeApplyToCoreAgents">
|
|
97
|
+
<span class="toggle-track"><span class="toggle-thumb"></span></span>
|
|
98
|
+
<span>{{ t('opencode.applyToCoreAgents') }}</span>
|
|
99
|
+
</label>
|
|
100
|
+
<label class="settings-toggle-row" style="margin-top: 8px;">
|
|
101
|
+
<input type="checkbox" v-model="opencodeAutoCompact">
|
|
102
|
+
<span class="toggle-track"><span class="toggle-thumb"></span></span>
|
|
103
|
+
<span>{{ t('opencode.enableAutoCompaction') }}</span>
|
|
104
|
+
</label>
|
|
105
|
+
<label class="settings-toggle-row" style="margin-top: 8px;">
|
|
106
|
+
<input type="checkbox" v-model="opencodeProviderDisabled">
|
|
107
|
+
<span class="toggle-track"><span class="toggle-thumb"></span></span>
|
|
108
|
+
<span>{{ t('opencode.disableProvider') }}</span>
|
|
109
|
+
</label>
|
|
110
|
+
</section>
|
|
111
|
+
|
|
112
|
+
<section class="selector-section">
|
|
113
|
+
<div class="selector-header">
|
|
114
|
+
<span class="selector-title">{{ t('opencode.configFile.title') }}</span>
|
|
115
|
+
<div class="selector-actions">
|
|
116
|
+
<button type="button" class="btn-tool btn-tool-compact" @click="$refs.opencodeImportInput && $refs.opencodeImportInput.click()">{{ t('opencode.importParse') }}</button>
|
|
117
|
+
<button type="button" class="btn-tool btn-tool-compact" @click="saveOpencodeConfig" :disabled="opencodeSaving || opencodeLoading || !isToolConfigWriteAllowed('opencode')">{{ opencodeSaving ? t('common.saving') : t('opencode.saveConfig') }}</button>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
<input ref="opencodeImportInput" class="sr-only" type="file" accept=".json,.jsonc,.opencode" @change="handleOpencodeImportChange">
|
|
121
|
+
<div v-if="opencodeImportFileName" class="config-template-hint">{{ t('opencode.parsedFile', { file: opencodeImportFileName }) }}</div>
|
|
122
|
+
<div v-if="opencodeError || opencodeImportError" class="config-template-hint error-text">{{ opencodeError || opencodeImportError }}</div>
|
|
123
|
+
<textarea class="template-textarea" v-model="opencodeContent" spellcheck="false" :readonly="opencodeSaving || opencodeLoading" :placeholder="t('opencode.textarea.placeholder')"></textarea>
|
|
124
|
+
<div class="config-template-hint">{{ t('opencode.configFile.hint') }}</div>
|
|
125
|
+
</section>
|
|
126
|
+
|
|
127
|
+
<section class="selector-section" v-if="opencodeProviders.length || opencodeAgents.length">
|
|
128
|
+
<div class="selector-header"><span class="selector-title">{{ t('opencode.summary.title') }}</span></div>
|
|
129
|
+
<div class="card-list">
|
|
130
|
+
<div v-for="provider in opencodeProviders" :key="provider.name" class="card">
|
|
131
|
+
<div class="card-leading">
|
|
132
|
+
<div class="card-icon">{{ provider.name.charAt(0).toUpperCase() }}</div>
|
|
133
|
+
<div class="card-content">
|
|
134
|
+
<div class="card-title">{{ provider.name }}</div>
|
|
135
|
+
<div class="card-subtitle">{{ provider.hasKey ? provider.apiKey : t('opencode.summary.noApiKey') }}</div>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
<div class="card-trailing">
|
|
139
|
+
<span :class="['pill', provider.disabled ? 'empty' : 'configured']">{{ provider.disabled ? t('common.disabled') : t('common.enabled') }}</span>
|
|
140
|
+
<span class="pill empty">{{ provider.source === 'codexmate' ? t('opencode.summary.sourceCodexMate') : t('opencode.summary.sourceOpenCode') }}</span>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
<div v-for="agent in opencodeAgents" :key="agent.name" class="card">
|
|
144
|
+
<div class="card-leading">
|
|
145
|
+
<div class="card-icon">A</div>
|
|
146
|
+
<div class="card-content">
|
|
147
|
+
<div class="card-title">{{ agent.name }}</div>
|
|
148
|
+
<div class="card-subtitle">{{ agent.model || t('opencode.summary.noModel') }}</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
<div class="card-trailing">
|
|
152
|
+
<span class="pill configured">{{ t('opencode.summary.agentType') }}</span>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
</section>
|
|
157
|
+
<div v-if="!isToolConfigWriteAllowed('opencode')" class="tool-config-write-overlay">
|
|
158
|
+
<div class="tool-config-write-overlay-card">
|
|
159
|
+
<div class="tool-config-write-overlay-title">{{ t('toolConfig.opencode.lockedTitle') }}</div>
|
|
160
|
+
<p>{{ t('toolConfig.opencode.lockedDesc') }}</p>
|
|
161
|
+
<button type="button" class="btn-tool" @click="setToolConfigPermission('opencode', true)" :disabled="toolConfigPermissionSaving.opencode">{{ t('toolConfig.enableWrite') }}</button>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|