codexmate 0.1.2 → 0.1.4

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.
@@ -142,8 +142,9 @@ const zh = Object.freeze({
142
142
  'side.docs': '文档',
143
143
  'side.config': '配置',
144
144
  'side.sessions': '会话',
145
- 'side.prompts': 'Prompts',
146
- 'side.prompts.meta': 'Prompt 文件与预设',
145
+ 'side.workspace': '工作区',
146
+ 'side.prompts': '提示词',
147
+ 'side.prompts.meta': '提示词文件与预设',
147
148
  'side.plugins': '插件',
148
149
  'side.system': '系统',
149
150
  'side.orchestration': '任务',
@@ -168,13 +169,13 @@ const zh = Object.freeze({
168
169
  'side.update.currentOnly': '当前 v{current}',
169
170
  'side.update.retry': '重试版本检查',
170
171
  'side.config.codex': 'Codex',
171
- 'side.config.codex.meta': 'Provider · Model',
172
+ 'side.config.codex.meta': '提供商 · 模型',
172
173
  'side.config.claude': 'Claude Code',
173
- 'side.config.claude.meta': 'Settings',
174
+ 'side.config.claude.meta': '设置',
174
175
  'side.config.openclaw': 'OpenClaw',
175
- 'side.config.openclaw.meta': 'JSON5 · AGENTS',
176
+ 'side.config.openclaw.meta': '配置',
176
177
  'side.config.opencode': 'OpenCode',
177
- 'side.config.opencode.meta': 'Provider',
178
+ 'side.config.opencode.meta': '提供商',
178
179
  'side.sessions.browser': '会话浏览',
179
180
  'side.sessions.browser.meta': '浏览 / 导出 / 清理',
180
181
  'side.prompts.agents': 'AGENTS.md',
@@ -618,11 +619,8 @@ const zh = Object.freeze({
618
619
 
619
620
  // Docs panel
620
621
  'docs.title': 'CLI 安装文档',
621
- 'docs.subtitle': '查看 Claude Code / Gemini CLI / CodeBuddy Code / Codex CLI 命令。',
622
622
  'docs.section.commands': '安装命令',
623
- 'docs.section.commandsNote': '命令可直接复制。',
624
623
  'docs.section.faq': '常见问题',
625
- 'docs.section.faqNote': '常见问题见下。',
626
624
  'docs.command.aria': '{name} 命令',
627
625
  'docs.registryHintPrefix': '命令将附加:',
628
626
  'docs.registryHintCustom': '请输入完整 URL(含 http/https),将自动附加到命令。',
@@ -630,8 +628,6 @@ const zh = Object.freeze({
630
628
  'docs.meta.bin': 'bin: {bin}',
631
629
  'docs.termuxLabel': 'Termux',
632
630
  'docs.termuxAria': 'Termux Codex CLI command',
633
- 'docs.rule.1': '命令按当前包管理器、镜像与操作生成。',
634
- 'docs.rule.2': '自定义镜像仅用于安装与升级。',
635
631
  'docs.tip.win.1': 'PowerShell 报权限不足(EACCES/EPERM)时,请以管理员身份执行安装命令。',
636
632
  'docs.tip.win.2': '安装后若仍提示找不到命令,重开终端并执行:where codex / where claude / where gemini / where codebuddy。',
637
633
  'docs.tip.win.3': '公司网络受限时,可先切换镜像源快捷项(npmmirror / 腾讯云 / 自定义)。',
@@ -1530,6 +1526,36 @@ const zh = Object.freeze({
1530
1526
  'openclaw.action.applyAria': '应用 OpenClaw 配置:{name}',
1531
1527
  'openclaw.action.editAria': '编辑 OpenClaw 配置:{name}',
1532
1528
  'openclaw.action.deleteAria': '删除 OpenClaw 配置:{name}'
1529
+ ,
1530
+
1531
+ 'tab.config.kilocode': 'KiloCode',
1532
+ 'side.config.kilocode': 'KiloCode',
1533
+ 'side.config.kilocode.meta': '提供商',
1534
+ 'toolConfig.kilocode.title': 'KiloCode 配置写入与启动',
1535
+ 'toolConfig.kilocode.desc': '默认只读;开启后可写入 KiloCode 配置并从 Web 启动。',
1536
+ 'toolConfig.kilocode.lockedTitle': '只读模式',
1537
+ 'toolConfig.kilocode.lockedDesc': '开启写入权限后可保存配置或启动 KiloCode。',
1538
+ 'toolConfig.kilocode.confirmMessage': '打开后,KiloCode tab 内的操作会写入 ~/.config/kilo/kilo.jsonc 或已存在的 kilo.json,并可启动本机 KiloCode。',
1539
+ 'toolConfig.openclaw.title': 'OpenClaw 配置写入',
1540
+ 'toolConfig.openclaw.desc': '默认只读;开启后可应用 OpenClaw 配置、写入 AGENTS.md 与其他工作区文件。',
1541
+ 'toolConfig.openclaw.confirmMessage': '打开后,OpenClaw tab 内的操作会写入 openclaw.json 及 OpenClaw 工作区文件(AGENTS.md、SOUL.md 等)。',
1542
+ 'toolConfig.openclaw.lockedTitle': 'OpenClaw 当前为只读',
1543
+ 'toolConfig.openclaw.lockedDesc': '这里不会写入 OpenClaw 配置。要应用配置或编辑工作区文件,请先打开本 tab 的写入开关。',
1544
+ 'kilocode.providerModel.title': 'KiloCode provider / model',
1545
+ 'kilocode.connection.title': 'KiloCode 连接',
1546
+ 'kilocode.targetFile': 'KiloCode 生效配置:{path} · {status}',
1547
+ 'kilocode.saveConfig': '保存配置',
1548
+ 'kilocode.start': '启动 KiloCode',
1549
+ 'kilocode.starting': '启动中',
1550
+ 'kilocode.saveAndStart': '保存并启动',
1551
+ 'kilocode.summary.title': '当前 KiloCode provider',
1552
+ 'kilocode.configPreview.title': 'KiloCode 配置预览',
1553
+ 'kilocode.configPreview.hint': 'API Key 已脱敏;保存会写入真实 key。',
1554
+ 'kilocode.saveFailed': '保存 KiloCode 配置失败',
1555
+ 'kilocode.fillRequired': '请填写 KiloCode URL 和 API Key',
1556
+ 'kilocode.writeRequired': '请先打开 KiloCode 写入开关',
1557
+ 'kilocode.saved': 'KiloCode 配置已保存',
1558
+ 'kilocode.autoSaved': 'KiloCode 配置已同步',
1533
1559
  });
1534
1560
 
1535
1561
  export { zh };
@@ -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' : (configMode === 'opencode' ? 'panel-config-opencode' : 'panel-config-provider'))"
42
+ :aria-controls="configMode === 'claude' ? 'panel-config-claude' : (configMode === 'openclaw' ? 'panel-config-openclaw' : (configMode === 'opencode' ? 'panel-config-opencode' : (configMode === 'kilocode' ? 'panel-config-kilocode' : '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>
@@ -196,14 +196,13 @@
196
196
  data-main-tab="config"
197
197
  data-config-mode="codex"
198
198
  :aria-current="mainTab === 'config' && configMode === 'codex' ? 'page' : null"
199
- :class="['side-item', { active: isConfigModeNavActive('codex') }]"
199
+ :class="['side-item', 'side-item-compact', { active: isConfigModeNavActive('codex') }]"
200
200
  @pointerdown="onConfigTabPointerDown('codex', $event)"
201
201
  @click="onConfigTabClick('codex', $event)">
202
202
  <span class="side-item-icon" aria-hidden="true">C</span>
203
203
  <div class="side-item-title">{{ t('side.config.codex') }}</div>
204
204
  <div class="side-item-meta">
205
205
  <span>{{ t('side.config.codex.meta') }}</span>
206
- <span v-if="currentProvider">{{ t('common.current', { value: currentProvider }) }}</span>
207
206
  </div>
208
207
  </button>
209
208
  <button
@@ -211,14 +210,13 @@
211
210
  data-main-tab="config"
212
211
  data-config-mode="claude"
213
212
  :aria-current="mainTab === 'config' && configMode === 'claude' ? 'page' : null"
214
- :class="['side-item', { active: isConfigModeNavActive('claude') }]"
213
+ :class="['side-item', 'side-item-compact', { active: isConfigModeNavActive('claude') }]"
215
214
  @pointerdown="onConfigTabPointerDown('claude', $event)"
216
215
  @click="onConfigTabClick('claude', $event)">
217
216
  <span class="side-item-icon" aria-hidden="true">A</span>
218
217
  <div class="side-item-title">{{ t('side.config.claude') }}</div>
219
218
  <div class="side-item-meta">
220
219
  <span>{{ t('side.config.claude.meta') }}</span>
221
- <span v-if="currentClaudeConfig">{{ t('common.current', { value: currentClaudeConfig }) }}</span>
222
220
  </div>
223
221
  </button>
224
222
  <button
@@ -226,14 +224,13 @@
226
224
  data-main-tab="config"
227
225
  data-config-mode="openclaw"
228
226
  :aria-current="mainTab === 'config' && configMode === 'openclaw' ? 'page' : null"
229
- :class="['side-item', { active: isConfigModeNavActive('openclaw') }]"
227
+ :class="['side-item', 'side-item-compact', { active: isConfigModeNavActive('openclaw') }]"
230
228
  @pointerdown="onConfigTabPointerDown('openclaw', $event)"
231
229
  @click="onConfigTabClick('openclaw', $event)">
232
230
  <span class="side-item-icon" aria-hidden="true">O</span>
233
231
  <div class="side-item-title">{{ t('side.config.openclaw') }}</div>
234
232
  <div class="side-item-meta">
235
233
  <span>{{ t('side.config.openclaw.meta') }}</span>
236
- <span v-if="currentOpenclawConfig">{{ t('common.current', { value: currentOpenclawConfig }) }}</span>
237
234
  </div>
238
235
  </button>
239
236
  <button
@@ -241,35 +238,35 @@
241
238
  data-main-tab="config"
242
239
  data-config-mode="opencode"
243
240
  :aria-current="mainTab === 'config' && configMode === 'opencode' ? 'page' : null"
244
- :class="['side-item', { active: isConfigModeNavActive('opencode') }]"
241
+ :class="['side-item', 'side-item-compact', { active: isConfigModeNavActive('opencode') }]"
245
242
  @pointerdown="onConfigTabPointerDown('opencode', $event)"
246
243
  @click="onConfigTabClick('opencode', $event)">
247
244
  <span class="side-item-icon" aria-hidden="true">N</span>
248
245
  <div class="side-item-title">{{ t('side.config.opencode') }}</div>
249
246
  <div class="side-item-meta">
250
247
  <span>{{ t('side.config.opencode.meta') }}</span>
251
- <span v-if="opencodeModel">{{ t('common.current', { value: opencodeModel }) }}</span>
252
248
  </div>
253
249
  </button>
250
+
254
251
  <button
255
- id="side-tab-prompts"
256
- data-main-tab="prompts"
257
- :aria-current="mainTab === 'prompts' ? 'page' : null"
258
- :class="['side-item', { active: isMainTabNavActive('prompts') }]"
259
- @pointerdown="onMainTabPointerDown('prompts', $event)"
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>
252
+ id="side-tab-config-kilocode"
253
+ data-main-tab="config"
254
+ data-config-mode="kilocode"
255
+ :aria-current="mainTab === 'config' && configMode === 'kilocode' ? 'page' : null"
256
+ :class="['side-item', 'side-item-compact', { active: isConfigModeNavActive('kilocode') }]"
257
+ @pointerdown="onConfigTabPointerDown('kilocode', $event)"
258
+ @click="onConfigTabClick('kilocode', $event)">
259
+ <span class="side-item-icon" aria-hidden="true">K</span>
260
+ <div class="side-item-title">{{ t('side.config.kilocode') }}</div>
263
261
  <div class="side-item-meta">
264
- <span>{{ t('side.prompts.meta') }}</span>
262
+ <span>{{ t('side.config.kilocode.meta') }}</span>
265
263
  </div>
266
264
  </button>
267
-
268
265
  </div>
269
266
 
270
267
 
271
- <div class="side-section" role="navigation" :aria-label="t('side.sessions')">
272
- <div class="side-section-title">{{ t('side.sessions') }}</div>
268
+ <div class="side-section" role="navigation" :aria-label="t('side.workspace')">
269
+ <div class="side-section-title">{{ t('side.workspace') }}</div>
273
270
  <button
274
271
  id="side-tab-sessions"
275
272
  data-main-tab="sessions"
@@ -284,6 +281,20 @@
284
281
  <span>{{ t('sessions.sourceLabel', { value: (sessionFilterSource === 'all' ? t('sessions.source.all') : (sessionFilterSource === 'claude' ? 'Claude Code' : (sessionFilterSource === 'gemini' ? 'Gemini CLI' : (sessionFilterSource === 'codebuddy' ? 'CodeBuddy Code' : 'Codex')))) }) }}</span>
285
282
  </div>
286
283
  </button>
284
+ <button
285
+ id="side-tab-prompts"
286
+ data-main-tab="prompts"
287
+ :aria-current="mainTab === 'prompts' ? 'page' : null"
288
+ :class="['side-item', { active: isMainTabNavActive('prompts') }]"
289
+ @pointerdown="onMainTabPointerDown('prompts', $event)"
290
+ @click="onMainTabClick('prompts', $event)">
291
+ <span class="side-item-icon" aria-hidden="true">P</span>
292
+ <div class="side-item-title">{{ t('side.prompts') }}</div>
293
+ <div class="side-item-meta">
294
+ <span>{{ t('side.prompts.meta') }}</span>
295
+ </div>
296
+ </button>
297
+
287
298
  <button
288
299
  id="side-tab-usage"
289
300
  data-main-tab="usage"
@@ -0,0 +1,101 @@
1
+ <!-- KiloCode 配置 -->
2
+ <div
3
+ v-show="mainTab === 'config' && configMode === 'kilocode'"
4
+ class="mode-content mode-cards"
5
+ id="panel-config-kilocode"
6
+ role="tabpanel"
7
+ :aria-labelledby="forceCompactLayout ? 'tab-config' : 'side-tab-config-kilocode'">
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
+ <button type="button" :class="['segment', { active: configMode === 'kilocode' }]" @click="switchConfigMode('kilocode')">{{ t('tab.config.kilocode') }}</button>
14
+ </div>
15
+
16
+ <section class="tool-config-write-card" :aria-label="t('toolConfig.kilocode.title')">
17
+ <div class="tool-config-write-copy">
18
+ <div class="tool-config-write-title">{{ t('toolConfig.kilocode.title') }}</div>
19
+ <p class="tool-config-write-desc">{{ t('toolConfig.kilocode.desc') }}</p>
20
+ </div>
21
+ <label class="settings-toggle-row tool-config-write-toggle">
22
+ <input
23
+ type="checkbox"
24
+ autocomplete="off"
25
+ :checked="isToolConfigWriteAllowed('kilocode')"
26
+ :disabled="toolConfigPermissionSaving.kilocode"
27
+ @change="setToolConfigPermission('kilocode', $event.target.checked)">
28
+ <span class="toggle-track"><span class="toggle-thumb"></span></span>
29
+ <span>{{ toolConfigPermissionStatusLabel('kilocode') }}</span>
30
+ </label>
31
+ </section>
32
+
33
+ <div class="tool-config-write-scope" :class="{ locked: !isToolConfigWriteAllowed('kilocode') }">
34
+ <div class="tool-config-write-body">
35
+ <section class="selector-section">
36
+ <div class="selector-header">
37
+ <span class="selector-title">{{ t('kilocode.providerModel.title') }}</span>
38
+ </div>
39
+ <div class="config-template-hint">{{ t('kilocode.targetFile', { path: kilocodeConfigPath || '~/.config/kilo/kilo.jsonc', status: kilocodeConfigExists ? t('common.exists') : t('common.notExistsWillCreateOnSave') }) }}</div>
40
+ <div class="codex-config-grid">
41
+ <div class="form-group codex-config-field">
42
+ <label class="form-label" for="kilocode-provider">{{ t('field.provider') }}</label>
43
+ <input id="kilocode-provider" class="form-input" v-model="kilocodeProvider" autocomplete="off" spellcheck="false" placeholder="codexmate" @blur="autoSaveKilocodeConfig">
44
+ </div>
45
+ <div class="form-group codex-config-field">
46
+ <label class="form-label" for="kilocode-base-url">{{ t('field.url') }}</label>
47
+ <input id="kilocode-base-url" class="form-input" v-model="kilocodeBaseUrl" autocomplete="off" spellcheck="false" placeholder="https://api.example.com/v1" @blur="autoSaveKilocodeConfig">
48
+ </div>
49
+ <div class="form-group codex-config-field">
50
+ <label class="form-label" for="kilocode-model">{{ t('field.model') }}</label>
51
+ <input id="kilocode-model" class="form-input" v-model="kilocodeModel" autocomplete="off" spellcheck="false" placeholder="gpt-5.3" @blur="autoSaveKilocodeConfig">
52
+ </div>
53
+ <div class="form-group codex-config-field">
54
+ <label class="form-label" for="kilocode-api-key">{{ t('opencode.field.apiKeyKeep') }}</label>
55
+ <div class="input-with-toggle">
56
+ <input id="kilocode-api-key" class="form-input" v-model="kilocodeApiKey" :type="kilocodeShowKey ? 'text' : 'password'" autocomplete="off" spellcheck="false" placeholder="sk-..." @blur="autoSaveKilocodeConfig">
57
+ <button type="button" class="input-toggle-btn" @mousedown.prevent @click="kilocodeShowKey = !kilocodeShowKey" :title="kilocodeShowKey ? t('common.hide') : t('common.show')" :aria-label="kilocodeShowKey ? t('common.hide') : t('common.show')">
58
+ <svg v-if="!kilocodeShowKey" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" width="16" height="16"><path d="M10 4C5 4 1.73 8.11 1 10c.73 1.89 4 6 9 6s8.27-4.11 9-6c-.73-1.89-4-6-9-6z"/><circle cx="10" cy="10" r="3"/></svg>
59
+ <svg v-else viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" width="16" height="16"><path d="M2 2l16 16M8.2 4.2A9.9 9.9 0 0 1 10 4c5 0 8.27 4.11 9 6-.44.94-1.5 2.7-3.2 4.2M14.5 14.5A5.9 5.9 0 0 1 10 16c-5 0-8.27-4.11-9-6 .76-1.66 2.2-3.6 4.3-5"/></svg>
60
+ </button>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ <div v-if="kilocodeError" class="config-template-hint error-text">{{ kilocodeError }}</div>
65
+ </section>
66
+
67
+ <section class="selector-section" v-if="kilocodeProviders.length">
68
+ <div class="selector-header"><span class="selector-title">{{ t('kilocode.summary.title') }}</span></div>
69
+ <div class="card-list">
70
+ <div v-for="provider in kilocodeProviders" :key="provider.name" :class="['card', { active: normalizeKilocodeProviderNameForUi(provider.name) === normalizeKilocodeProviderNameForUi(kilocodeProvider) }]" @dblclick="selectKilocodeProvider(provider)" @keydown.enter.self.prevent="selectKilocodeProvider(provider)" tabindex="0" role="button" :aria-current="normalizeKilocodeProviderNameForUi(provider.name) === normalizeKilocodeProviderNameForUi(kilocodeProvider) ? 'true' : null">
71
+ <div class="card-leading">
72
+ <div class="card-icon">{{ provider.name.charAt(0).toUpperCase() }}</div>
73
+ <div class="card-content">
74
+ <div class="card-title">{{ provider.name }}</div>
75
+ <div class="card-subtitle">{{ provider.baseURL || provider.api || t('config.url.unset') }}</div>
76
+ <div class="card-subtitle">{{ (provider.models || []).join(', ') || t('opencode.summary.noModel') }}</div>
77
+ </div>
78
+ </div>
79
+ <div class="card-trailing">
80
+ <span :class="['pill', provider.hasKey ? 'configured' : 'empty']">{{ provider.hasKey ? t('common.configured') : t('common.notConfigured') }}</span>
81
+ </div>
82
+ </div>
83
+ </div>
84
+ </section>
85
+
86
+ <section class="selector-section">
87
+ <div class="selector-header"><span class="selector-title">{{ t('kilocode.configPreview.title') }}</span></div>
88
+ <textarea class="template-textarea" :value="kilocodeContent" spellcheck="false" readonly aria-readonly="true"></textarea>
89
+ <div class="config-template-hint">{{ t('kilocode.configPreview.hint') }}</div>
90
+ </section>
91
+
92
+ <div v-if="!isToolConfigWriteAllowed('kilocode')" class="tool-config-write-overlay">
93
+ <div class="tool-config-write-overlay-card">
94
+ <div class="tool-config-write-overlay-title">{{ t('toolConfig.kilocode.lockedTitle') }}</div>
95
+ <p>{{ t('toolConfig.kilocode.lockedDesc') }}</p>
96
+ <button type="button" class="btn-tool" @click="setToolConfigPermission('kilocode', true)" :disabled="toolConfigPermissionSaving.kilocode">{{ t('toolConfig.enableWrite') }}</button>
97
+ </div>
98
+ </div>
99
+ </div>
100
+ </div>
101
+ </div>
@@ -11,6 +11,23 @@
11
11
  <button type="button" :class="['segment', { active: configMode === 'openclaw' }]" @click="switchConfigMode('openclaw')">{{ t('tab.config.openclaw') }}</button>
12
12
  </div>
13
13
  <div class="openclaw-layout">
14
+ <section class="tool-config-write-card" :aria-label="t('toolConfig.openclaw.title')">
15
+ <div class="tool-config-write-copy">
16
+ <div class="tool-config-write-title">{{ t('toolConfig.openclaw.title') }}</div>
17
+ <p class="tool-config-write-desc">{{ t('toolConfig.openclaw.desc') }}</p>
18
+ </div>
19
+ <label class="settings-toggle-row tool-config-write-toggle">
20
+ <input
21
+ type="checkbox"
22
+ autocomplete="off"
23
+ :checked="isToolConfigWriteAllowed('openclaw')"
24
+ :disabled="toolConfigPermissionSaving.openclaw"
25
+ @change="setToolConfigPermission('openclaw', $event.target.checked)">
26
+ <span class="toggle-track"><span class="toggle-thumb"></span></span>
27
+ <span>{{ toolConfigPermissionStatusLabel('openclaw') }}</span>
28
+ </label>
29
+ </section>
30
+
14
31
  <section class="settings-card settings-card--wide openclaw-summary-card" aria-label="OpenClaw 当前摘要">
15
32
  <div class="settings-card-body">
16
33
  <div class="openclaw-summary-header">
@@ -32,10 +49,12 @@
32
49
  </div>
33
50
  </section>
34
51
 
52
+ <div class="tool-config-write-scope" :class="{ locked: !isToolConfigWriteAllowed('openclaw') }">
53
+ <div class="tool-config-write-body">
35
54
  <section class="settings-card settings-card--wide openclaw-workspace-card">
36
55
  <div class="settings-card-body">
37
56
  <div class="openclaw-tools-grid">
38
- <button class="openclaw-tool-btn" @click="openOpenclawAgentsEditor" :disabled="loading || !!initError || agentsLoading">
57
+ <button class="openclaw-tool-btn" @click="openOpenclawAgentsEditor" :disabled="loading || !!initError || agentsLoading || !isToolConfigWriteAllowed('openclaw')">
39
58
  <div class="tool-icon">📄</div>
40
59
  <div class="tool-content">
41
60
  <div class="tool-title">AGENTS.md</div>
@@ -52,7 +71,7 @@
52
71
  type="button"
53
72
  class="openclaw-tool-btn"
54
73
  @click="openOpenclawQuickWorkspaceFile(fileName)"
55
- :disabled="loading || !!initError || agentsLoading">
74
+ :disabled="loading || !!initError || agentsLoading || !isToolConfigWriteAllowed('openclaw')">
56
75
  <div class="tool-icon">📄</div>
57
76
  <div class="tool-content">
58
77
  <div class="tool-title">{{ fileName }}</div>
@@ -70,13 +89,14 @@
70
89
  <div class="settings-card-body">
71
90
  <div class="card-list openclaw-card-list">
72
91
  <div v-for="(config, name) in openclawConfigs" :key="name"
73
- :class="['card', { active: currentOpenclawConfig === name }]"
74
- @click="applyOpenclawConfig(name)"
75
- @keydown.enter.self.prevent="applyOpenclawConfig(name)"
76
- @keydown.space.self.prevent="applyOpenclawConfig(name)"
77
- tabindex="0"
92
+ :class="['card', { active: currentOpenclawConfig === name, disabled: !isToolConfigWriteAllowed('openclaw') }]"
93
+ @click="isToolConfigWriteAllowed('openclaw') && applyOpenclawConfig(name)"
94
+ @keydown.enter.self.prevent="isToolConfigWriteAllowed('openclaw') && applyOpenclawConfig(name)"
95
+ @keydown.space.self.prevent="isToolConfigWriteAllowed('openclaw') && applyOpenclawConfig(name)"
96
+ :tabindex="isToolConfigWriteAllowed('openclaw') ? 0 : -1"
78
97
  role="button"
79
98
  :aria-label="t('openclaw.action.applyAria', { name })"
99
+ :aria-disabled="!isToolConfigWriteAllowed('openclaw') ? 'true' : null"
80
100
  :aria-current="currentOpenclawConfig === name ? 'true' : null">
81
101
  <div class="card-leading">
82
102
  <div class="card-icon">{{ name.charAt(0).toUpperCase() }}</div>
@@ -95,7 +115,7 @@
95
115
  {{ openclawHasContent(config) ? t('openclaw.configured') : t('openclaw.notConfigured') }}
96
116
  </span>
97
117
  <div class="card-actions" @click.stop>
98
- <button class="card-action-btn" @click="openOpenclawEditModal(name)" :aria-label="t('openclaw.action.editAria', { name })" :title="t('openclaw.action.edit')">
118
+ <button class="card-action-btn" @click="openOpenclawEditModal(name)" :disabled="!isToolConfigWriteAllowed('openclaw')" :aria-label="t('openclaw.action.editAria', { name })" :title="t('openclaw.action.edit')">
99
119
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
100
120
  <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
101
121
  <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
@@ -113,5 +133,14 @@
113
133
  </div>
114
134
  </div>
115
135
  </section>
136
+ <div v-if="!isToolConfigWriteAllowed('openclaw')" class="tool-config-write-overlay">
137
+ <div class="tool-config-write-overlay-card">
138
+ <div class="tool-config-write-overlay-title">{{ t('toolConfig.openclaw.lockedTitle') }}</div>
139
+ <p>{{ t('toolConfig.openclaw.lockedDesc') }}</p>
140
+ <button type="button" class="btn-tool" @click="setToolConfigPermission('openclaw', true)" :disabled="toolConfigPermissionSaving.openclaw">{{ t('toolConfig.enableWrite') }}</button>
141
+ </div>
142
+ </div>
143
+ </div>
144
+ </div>
116
145
  </div>
117
146
  </div>
@@ -10,6 +10,7 @@
10
10
  <button type="button" :class="['segment', { active: configMode === 'claude' }]" @click="switchConfigMode('claude')">{{ t('tab.config.claude') }}</button>
11
11
  <button type="button" :class="['segment', { active: configMode === 'openclaw' }]" @click="switchConfigMode('openclaw')">{{ t('tab.config.openclaw') }}</button>
12
12
  <button type="button" :class="['segment', { active: configMode === 'opencode' }]" @click="switchConfigMode('opencode')">{{ t('tab.config.opencode') }}</button>
13
+ <button type="button" :class="['segment', { active: configMode === 'kilocode' }]" @click="switchConfigMode('kilocode')">{{ t('tab.config.kilocode') }}</button>
13
14
  </div>
14
15
 
15
16
  <section class="tool-config-write-card" :aria-label="t('opencode.writeAria')">
@@ -7,7 +7,6 @@
7
7
  <div class="selector-section">
8
8
  <div class="selector-header">
9
9
  <span class="selector-title">{{ t('docs.title') }}</span>
10
- <div class="skills-panel-note docs-section-note">{{ t('docs.subtitle') }}</div>
11
10
  </div>
12
11
 
13
12
  <div class="docs-toolbar-grid">
@@ -59,7 +58,6 @@
59
58
  <div class="selector-section">
60
59
  <div class="selector-header">
61
60
  <span class="selector-title">{{ t('docs.section.commands') }}</span>
62
- <div class="skills-panel-note docs-section-note">{{ t('docs.section.commandsNote') }}</div>
63
61
  </div>
64
62
 
65
63
  <div class="install-list docs-install-list">
@@ -92,7 +90,6 @@
92
90
  <div class="selector-section">
93
91
  <div class="selector-header">
94
92
  <span class="selector-title">{{ t('docs.section.faq') }}</span>
95
- <div class="skills-panel-note docs-section-note">{{ t('docs.section.faqNote') }}</div>
96
93
  </div>
97
94
 
98
95
  <div class="docs-help-grid">
@@ -102,13 +99,6 @@
102
99
  <li v-for="tip in installTroubleshootingTips" :key="tip">{{ tip }}</li>
103
100
  </ul>
104
101
  </div>
105
- <div class="docs-note-card">
106
- <div class="docs-note-title">{{ t('common.rules') }}</div>
107
- <ul class="docs-static-list">
108
- <li>{{ t('docs.rule.1') }}</li>
109
- <li>{{ t('docs.rule.2') }}</li>
110
- </ul>
111
- </div>
112
102
  </div>
113
103
  </div>
114
- </div>
104
+ </div>