codexmate 0.1.10 → 0.1.13

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.
Files changed (40) hide show
  1. package/README.md +5 -7
  2. package/README.vi.md +4 -7
  3. package/README.zh.md +5 -7
  4. package/cli/agents-files.js +27 -3
  5. package/cli/prompt-history.js +171 -0
  6. package/cli/system-prompt-files.js +20 -2
  7. package/cli.js +162 -5
  8. package/package.json +1 -1
  9. package/web-ui/app.js +95 -4
  10. package/web-ui/index.html +2 -1
  11. package/web-ui/modules/app.methods.agents.mjs +90 -0
  12. package/web-ui/modules/app.methods.index.mjs +2 -0
  13. package/web-ui/modules/app.methods.navigation.mjs +7 -1
  14. package/web-ui/modules/app.methods.pi-config.mjs +371 -0
  15. package/web-ui/modules/app.methods.session-actions.mjs +57 -0
  16. package/web-ui/modules/app.methods.startup-claude.mjs +2 -1
  17. package/web-ui/modules/app.methods.system-prompt.mjs +81 -0
  18. package/web-ui/modules/app.methods.tool-config-permissions.mjs +3 -2
  19. package/web-ui/modules/app.methods.web-ui-preferences.mjs +8 -1
  20. package/web-ui/modules/config-mode.computed.mjs +2 -1
  21. package/web-ui/modules/i18n/locales/en.mjs +52 -0
  22. package/web-ui/modules/i18n/locales/ja.mjs +52 -0
  23. package/web-ui/modules/i18n/locales/vi.mjs +52 -0
  24. package/web-ui/modules/i18n/locales/zh-tw.mjs +52 -0
  25. package/web-ui/modules/i18n/locales/zh.mjs +52 -0
  26. package/web-ui/partials/index/layout-header.html +20 -0
  27. package/web-ui/partials/index/panel-config-claude.html +3 -3
  28. package/web-ui/partials/index/panel-config-codex.html +3 -3
  29. package/web-ui/partials/index/panel-config-kilocode.html +5 -5
  30. package/web-ui/partials/index/panel-config-openclaw.html +3 -3
  31. package/web-ui/partials/index/panel-config-opencode.html +5 -5
  32. package/web-ui/partials/index/panel-config-pi.html +171 -0
  33. package/web-ui/partials/index/panel-prompts.html +52 -0
  34. package/web-ui/partials/index/panel-sessions.html +12 -0
  35. package/web-ui/partials/index/panel-settings.html +42 -0
  36. package/web-ui/res/web-ui-render.precompiled.js +1101 -252
  37. package/web-ui/styles/modals-core.css +86 -0
  38. package/web-ui/styles/sessions-preview.css +38 -0
  39. package/web-ui/styles/settings-panel.css +7 -0
  40. package/web-ui/styles/titles-cards.css +2 -2
@@ -13,6 +13,7 @@ const en = Object.freeze({
13
13
  'common.paste': 'Paste',
14
14
  'common.edit': 'Edit',
15
15
  'common.install': 'Install',
16
+ 'common.optional': 'optional',
16
17
  'common.update': 'Update',
17
18
  'common.uninstall': 'Uninstall',
18
19
  'common.official': 'Official',
@@ -34,6 +35,8 @@ const en = Object.freeze({
34
35
  'common.save': 'Save',
35
36
  'common.saveApply': 'Save & apply',
36
37
  'common.close': 'Close',
38
+ 'common.history': 'History',
39
+ 'common.restoreToEditor': 'Restore to editor',
37
40
  'common.delete': 'Delete',
38
41
  'common.clear': 'Clear',
39
42
  'common.show': 'Show',
@@ -133,6 +136,7 @@ const en = Object.freeze({
133
136
  'tab.config.claude': 'Claude',
134
137
  'tab.config.openclaw': 'OpenClaw',
135
138
  'tab.config.opencode': 'OpenCode',
139
+ 'tab.config.pi': 'Pi',
136
140
  'tab.sessions': 'Sessions',
137
141
  'tab.usage': 'Usage',
138
142
  'tab.market': 'Skills',
@@ -463,6 +467,8 @@ const en = Object.freeze({
463
467
  'toast.health.noFailedProviderSelection': 'Select at least one failed provider first',
464
468
  'toast.health.deleteFailedProvidersDone': 'Deleted {count} failed provider(s)',
465
469
  'toast.noChanges': 'No changes detected',
470
+ 'toast.history.restored': 'Restored to editor, save to confirm',
471
+ 'toast.history.empty': 'No history yet',
466
472
  'toast.template.loadFail': 'Failed to load template',
467
473
  'toast.template.empty': 'Template cannot be empty',
468
474
  'toast.template.applied': 'Template applied',
@@ -692,6 +698,7 @@ const en = Object.freeze({
692
698
  'sessions.preview.importNative.failedWithReason': 'Import to native failed: {reason}',
693
699
  'sessions.preview.copyLink': 'Copy link',
694
700
  'sessions.preview.copyPath': 'Copy path',
701
+ 'sessions.preview.copyMessage': 'Copy message',
695
702
  'sessions.preview.openLink': 'Open link',
696
703
  'sessions.preview.loadingBody': 'Loading session content...',
697
704
  'sessions.preview.emptyMsgs': 'No messages to display',
@@ -1175,6 +1182,10 @@ const en = Object.freeze({
1175
1182
  'settings.timeline.style.dots': 'Dots',
1176
1183
  'settings.timeline.style.bar': 'Bar',
1177
1184
  'settings.timeline.style.hint': 'Dots for quick navigation, Bar for visual progress',
1185
+ 'settings.configTabs.title': 'Config panel visibility',
1186
+ 'settings.configTabs.meta': 'Control which configuration sub-tabs appear in the sidebar',
1187
+ 'settings.configTabs.hint': 'Hidden sub-tabs will no longer be visible; at least one must remain',
1188
+ 'settings.configTabs.minRequired': 'At least one configuration tab must remain visible',
1178
1189
 
1179
1190
  'settings.reset.title': 'Config reset',
1180
1191
  'settings.reset.meta': 'Proceed with caution',
@@ -1310,6 +1321,47 @@ const en = Object.freeze({
1310
1321
  'toolConfig.openclaw.confirmMessage': 'When enabled, actions in the OpenClaw tab can write openclaw.json and OpenClaw workspace files (AGENTS.md, SOUL.md, etc.).',
1311
1322
  'toolConfig.openclaw.lockedTitle': 'OpenClaw is read-only',
1312
1323
  'toolConfig.openclaw.lockedDesc': 'OpenClaw config is not written here. To apply configs or edit workspace files, enable write access on this tab first.',
1324
+ 'side.config.pi': 'Pi',
1325
+ 'side.config.pi.meta': 'Config',
1326
+ 'toolConfig.pi.title': 'Pi config writes',
1327
+ 'toolConfig.pi.desc': 'Read-only by default; enable to sync Pi config.',
1328
+ 'toolConfig.pi.lockedTitle': 'Read-only',
1329
+ 'toolConfig.pi.lockedDesc': 'Enable write access to sync Pi config.',
1330
+ 'toolConfig.pi.confirmMessage': 'When enabled, actions in the Pi tab can write the Pi config file (~/.pi/config.json or ./.pi/config.json).',
1331
+ 'pi.status.readOnly': 'Read-only',
1332
+ 'pi.config.openGlobal': 'Open global config',
1333
+ 'pi.providers.title': 'Pi Providers',
1334
+ 'pi.providers.subtitle': 'Manage Piper local providers and models',
1335
+ 'pi.providers.empty': 'No providers found. Click Add to start.',
1336
+ 'pi.providers.targetFile': 'Active Pi config file: {path}',
1337
+ 'pi.providers.editorTitle': 'Edit Provider',
1338
+ 'pi.providers.add': 'Add Provider',
1339
+ 'pi.providers.active': 'Active',
1340
+ 'pi.providers.setActive': 'Set Active',
1341
+ 'pi.providers.deleteTitle': 'Delete Pi Provider',
1342
+ 'pi.providers.deleteMessage': 'Delete provider "{name}"?',
1343
+ 'pi.providers.fields.name': 'Name',
1344
+ 'pi.providers.fields.baseUrl': 'Base URL',
1345
+ 'pi.providers.fields.api': 'API',
1346
+ 'pi.providers.fields.apiKey': 'API Key',
1347
+ 'pi.providers.fields.models': 'Models',
1348
+ 'pi.providers.fields.configJson': 'Config JSON',
1349
+ 'pi.providers.models.remove': 'Remove',
1350
+ 'pi.providers.models.search': 'Search available models…',
1351
+ 'pi.providers.models.remoteCount': '{count} models available, click to add',
1352
+ 'pi.providers.models.remoteLoading': 'Fetching remote model list…',
1353
+ 'pi.providers.models.remoteFailed': 'You can still type model IDs manually.',
1354
+ 'pi.providers.models.noMatch': 'No matching models',
1355
+ 'pi.providers.delete': 'Delete Provider',
1356
+ 'pi.providers.addModal.title': 'Add Provider',
1357
+ 'pi.providers.addModal.subtitle': 'Create a new Piper provider',
1358
+ 'pi.providers.addModal.fields.baseUrl': 'Base URL',
1359
+ 'pi.providers.addModal.fields.apiKey': 'API Key',
1360
+ 'pi.providers.addModal.api.openaiCompletions': 'OpenAI Chat Completions (most compatible)',
1361
+ 'pi.providers.addModal.api.openaiResponses': 'OpenAI Responses API',
1362
+ 'pi.providers.addModal.api.anthropicMessages': 'Anthropic Messages API',
1363
+ 'pi.providers.addModal.api.googleGenerativeAi': 'Google Generative AI',
1364
+ 'pi.providers.addModal.fields.api': 'API Format',
1313
1365
  'kilocode.providerModel.title': 'KiloCode provider / model',
1314
1366
  'kilocode.connection.title': 'KiloCode connection',
1315
1367
  'kilocode.targetFile': 'Active KiloCode config: {path} · {status}',
@@ -14,6 +14,7 @@ const ja = Object.freeze({
14
14
  'common.paste': 'ペースト',
15
15
  'common.edit': '編集',
16
16
  'common.install': 'インストール',
17
+ 'common.optional': '任意',
17
18
  'common.update': 'アップグレード',
18
19
  'common.uninstall': 'アンインストール',
19
20
  'common.official': '公式',
@@ -35,6 +36,8 @@ const ja = Object.freeze({
35
36
  'common.save': '保存',
36
37
  'common.saveApply': '保存して適用',
37
38
  'common.close': '閉じる',
39
+ 'common.history': '履歴',
40
+ 'common.restoreToEditor': 'エディタに復元',
38
41
  'common.delete': '削除',
39
42
  'common.clear': 'クリア',
40
43
  'common.show': '表示',
@@ -134,6 +137,7 @@ const ja = Object.freeze({
134
137
  'tab.config.claude': 'Claude',
135
138
  'tab.config.openclaw': 'OpenClaw',
136
139
  'tab.config.opencode': 'OpenCode',
140
+ 'tab.config.pi': 'Pi',
137
141
  'tab.sessions': 'セッション',
138
142
  'tab.usage': '使用量',
139
143
  'tab.market': 'Skills',
@@ -469,6 +473,8 @@ const ja = Object.freeze({
469
473
  'toast.health.noFailedProviderSelection': '先に失敗プロバイダーを選択してください',
470
474
  'toast.health.deleteFailedProvidersDone': '失敗プロバイダー {count} 件を削除しました',
471
475
  'toast.noChanges': '変更が検出されませんでした',
476
+ 'toast.history.restored': 'エディタに復元しました。保存すると反映されます',
477
+ 'toast.history.empty': '履歴はまだありません',
472
478
  'toast.template.loadFail': 'テンプレートの読み込みに失敗しました',
473
479
  'toast.template.empty': 'テンプレートは空にできません',
474
480
  'toast.template.applied': 'テンプレートが適用されました',
@@ -695,6 +701,7 @@ const ja = Object.freeze({
695
701
  'sessions.preview.openStandalone': 'スタンドアロンで開く',
696
702
  'sessions.preview.copyLink': 'リンクをコピー',
697
703
  'sessions.preview.copyPath': 'パスをコピー',
704
+ 'sessions.preview.copyMessage': 'メッセージをコピー',
698
705
  'sessions.preview.openLink': 'リンクを開く',
699
706
  'sessions.preview.loadingBody': 'メッセージ読み込み中...',
700
707
  'sessions.preview.emptyMsgs': 'メッセージがありません',
@@ -1177,6 +1184,10 @@ const ja = Object.freeze({
1177
1184
  'settings.timeline.style.dots': 'ドット',
1178
1185
  'settings.timeline.style.bar': 'バー',
1179
1186
  'settings.timeline.style.hint': 'ドットは素早いナビゲーション、バーは進捗を直感的に表示',
1187
+ 'settings.configTabs.title': '設定パネルの表示',
1188
+ 'settings.configTabs.meta': 'サイドバーに表示する設定サブページを制御します',
1189
+ 'settings.configTabs.hint': 'オフにすると対応するサブページが非表示になります。最低1つは必要です',
1190
+ 'settings.configTabs.minRequired': '少なくとも1つの設定ページを表示する必要があります',
1180
1191
 
1181
1192
  'settings.reset.title': '設定リセット',
1182
1193
  'settings.reset.meta': '注意して操作してください',
@@ -1311,6 +1322,47 @@ const ja = Object.freeze({
1311
1322
  'toolConfig.openclaw.confirmMessage': '有効化すると、OpenClaw タブ内の操作が openclaw.json およびワークスペースファイル(AGENTS.md、SOUL.md など)に書き込みできます。',
1312
1323
  'toolConfig.openclaw.lockedTitle': 'OpenClaw は読み取り専用です',
1313
1324
  'toolConfig.openclaw.lockedDesc': 'ここでは OpenClaw 設定を書き込みません。設定を適用またはワークスペースファイルを編集するには、このタブの書き込みスイッチを有効化してください。',
1325
+ 'side.config.pi': 'Pi',
1326
+ 'side.config.pi.meta': '設定',
1327
+ 'toolConfig.pi.title': 'Pi 設定の書き込み',
1328
+ 'toolConfig.pi.desc': '既定は読み取り専用。有効化すると Pi 設定を同期できます。',
1329
+ 'toolConfig.pi.lockedTitle': '読み取り専用',
1330
+ 'toolConfig.pi.lockedDesc': '書き込み権限を有効化すると、Pi 設定を同期できます。',
1331
+ 'toolConfig.pi.confirmMessage': '有効化すると、Pi タブ内の操作で ~/.pi/config.json または ./.pi/config.json に書き込むことができます。',
1332
+ 'pi.status.readOnly': '読み取り専用',
1333
+ 'pi.config.openGlobal': 'グローバル設定を開く',
1334
+ 'pi.providers.title': 'Pi プロバイダ',
1335
+ 'pi.providers.subtitle': 'Piper ローカルプロバイダとモデルを管理',
1336
+ 'pi.providers.empty': 'Piper プロバイダが見つかりません。「追加」をクリックして開始してください。',
1337
+ 'pi.providers.targetFile': 'Pi の有効な設定ファイル:{path}',
1338
+ 'pi.providers.editorTitle': 'プロバイダを編集',
1339
+ 'pi.providers.add': 'プロバイダを追加',
1340
+ 'pi.providers.active': '有効',
1341
+ 'pi.providers.setActive': '使用中に設定',
1342
+ 'pi.providers.deleteTitle': 'Pi 設定を削除',
1343
+ 'pi.providers.deleteMessage': 'プロバイダー「{name}」を削除しますか?',
1344
+ 'pi.providers.fields.name': '名前',
1345
+ 'pi.providers.fields.baseUrl': 'Base URL',
1346
+ 'pi.providers.fields.api': 'API 形式',
1347
+ 'pi.providers.fields.apiKey': 'API Key',
1348
+ 'pi.providers.fields.models': 'モデル',
1349
+ 'pi.providers.fields.configJson': '設定 JSON',
1350
+ 'pi.providers.models.remove': '削除',
1351
+ 'pi.providers.models.search': '利用可能なモデルを検索…',
1352
+ 'pi.providers.models.remoteCount': '{count} 個のモデルを取得しました。クリックで追加',
1353
+ 'pi.providers.models.remoteLoading': 'リモートモデル一覧を取得中…',
1354
+ 'pi.providers.models.remoteFailed': 'モデル ID は手動入力できます。',
1355
+ 'pi.providers.models.noMatch': '一致するモデルがありません',
1356
+ 'pi.providers.delete': 'プロバイダを削除',
1357
+ 'pi.providers.addModal.title': 'プロバイダを追加',
1358
+ 'pi.providers.addModal.subtitle': '新しい Piper プロバイダを作成',
1359
+ 'pi.providers.addModal.fields.baseUrl': 'Base URL',
1360
+ 'pi.providers.addModal.fields.apiKey': 'API Key',
1361
+ 'pi.providers.addModal.api.openaiCompletions': 'OpenAI Chat Completions(最も互換性が高い)',
1362
+ 'pi.providers.addModal.api.openaiResponses': 'OpenAI Responses API',
1363
+ 'pi.providers.addModal.api.anthropicMessages': 'Anthropic Messages API',
1364
+ 'pi.providers.addModal.api.googleGenerativeAi': 'Google Generative AI',
1365
+ 'pi.providers.addModal.fields.api': 'API 形式',
1314
1366
  'kilocode.providerModel.title': 'KiloCode プロバイダー / モデル',
1315
1367
  'kilocode.connection.title': 'KiloCode 接続',
1316
1368
  'kilocode.targetFile': '有効な KiloCode 設定:{path} · {status}',
@@ -22,6 +22,7 @@ const vi = Object.freeze({
22
22
  'common.paste': 'Dán',
23
23
  'common.edit': 'Chỉnh sửa',
24
24
  'common.install': 'Cài đặt',
25
+ 'common.optional': 'không bắt buộc',
25
26
  'common.update': 'Cập nhật',
26
27
  'common.uninstall': 'Gỡ cài đặt',
27
28
  'common.official': 'Chính thức',
@@ -43,6 +44,8 @@ const vi = Object.freeze({
43
44
  'common.save': 'Lưu',
44
45
  'common.saveApply': 'Lưu & áp dụng',
45
46
  'common.close': 'Đóng',
47
+ 'common.history': 'Lịch sử',
48
+ 'common.restoreToEditor': 'Khôi phục vào trình soạn',
46
49
  'common.delete': 'Xóa',
47
50
  'common.clear': 'Xóa',
48
51
  'common.show': 'Hiện',
@@ -93,6 +96,7 @@ const vi = Object.freeze({
93
96
  'tab.docs': 'Tài liệu',
94
97
  'tab.config': 'Cấu hình',
95
98
  'tab.config.opencode': 'OpenCode',
99
+ 'tab.config.pi': 'Pi',
96
100
  'tab.sessions': 'Phiên',
97
101
  'tab.usage': 'Sử dụng',
98
102
  'tab.market': 'Skills',
@@ -410,6 +414,8 @@ const vi = Object.freeze({
410
414
  'toast.health.noFailedProviderSelection': 'Hãy chọn ít nhất một provider lỗi trước',
411
415
  'toast.health.deleteFailedProvidersDone': 'Đã xóa {count} provider lỗi',
412
416
  'toast.noChanges': 'Không phát hiện thay đổi',
417
+ 'toast.history.restored': 'Đã khôi phục vào trình soạn, lưu để xác nhận',
418
+ 'toast.history.empty': 'Chưa có lịch sử',
413
419
  'toast.template.loadFail': 'Tải mẫu thất bại',
414
420
  'toast.template.empty': 'Mẫu không được để trống',
415
421
  'toast.template.applied': 'Đã áp dụng mẫu',
@@ -465,6 +471,10 @@ const vi = Object.freeze({
465
471
  'settings.timeline.style.dots': 'Chấm',
466
472
  'settings.timeline.style.bar': 'Thanh',
467
473
  'settings.timeline.style.hint': 'Chấm để điều hướng nhanh, Thanh để hiển thị tiến trình trực quan',
474
+ 'settings.configTabs.title': 'Hiển thị bảng cấu hình',
475
+ 'settings.configTabs.meta': 'Kiểm soát các sub-tab cấu hình hiển thị trên thanh bên',
476
+ 'settings.configTabs.hint': 'Tab bị ẩn sẽ không hiển thị; cần giữ lại ít nhất một tab',
477
+ 'settings.configTabs.minRequired': 'Phải giữ lại ít nhất một tab cấu hình',
468
478
 
469
479
  // Diff / Agents hints
470
480
  'diff.hint.busy': 'Đang tạo diff hoặc áp dụng. Thao tác tạm thời bị vô hiệu hóa.',
@@ -863,6 +873,7 @@ const vi = Object.freeze({
863
873
  'sessions.preview.importNative.failedWithReason': 'Nhập vào native thất bại: {reason}',
864
874
  'sessions.preview.copyLink': 'Sao chép link',
865
875
  'sessions.preview.copyPath': 'Sao chép đường dẫn',
876
+ 'sessions.preview.copyMessage': 'Sao chép tin nhắn',
866
877
  'sessions.preview.loadingBody': 'Đang tải nội dung phiên...',
867
878
  'sessions.preview.emptyMsgs': 'Không có tin nhắn để hiển thị',
868
879
  'sessions.preview.rendering': 'Đang render nội dung phiên...',
@@ -1279,6 +1290,47 @@ const vi = Object.freeze({
1279
1290
  'toolConfig.openclaw.confirmMessage': 'Khi bật, các thao tác trong tab OpenClaw có thể ghi openclaw.json và các tệp workspace (AGENTS.md, SOUL.md, v.v.).',
1280
1291
  'toolConfig.openclaw.lockedTitle': 'OpenClaw đang ở chế độ chỉ đọc',
1281
1292
  'toolConfig.openclaw.lockedDesc': 'Không ghi cấu hình OpenClaw ở đây. Để áp dụng cấu hình hoặc sửa tệp workspace, hãy bật quyền ghi trên tab này trước.',
1293
+ 'side.config.pi': 'Pi',
1294
+ 'side.config.pi.meta': 'Cấu hình',
1295
+ 'toolConfig.pi.title': 'Ghi cấu hình Pi',
1296
+ 'toolConfig.pi.desc': 'Mặc định chỉ đọc; bật để đồng bộ cấu hình Pi.',
1297
+ 'toolConfig.pi.lockedTitle': 'Chế độ chỉ đọc',
1298
+ 'toolConfig.pi.lockedDesc': 'Bật quyền ghi để đồng bộ cấu hình Pi.',
1299
+ 'toolConfig.pi.confirmMessage': 'Khi bật, các thao tác trong tab Pi có thể ghi file cấu hình Pi (~/.pi/config.json hoặc ./.pi/config.json).',
1300
+ 'pi.status.readOnly': 'Chỉ đọc',
1301
+ 'pi.config.openGlobal': 'Mở cấu hình toàn cục',
1302
+ 'pi.providers.title': 'Nhà cung cấp Pi',
1303
+ 'pi.providers.subtitle': 'Quản lý nhà cung cấp và mô hình Piper cục bộ',
1304
+ 'pi.providers.empty': 'Chưa tìm thấy nhà cung cấp Piper. Nhấn Thêm để bắt đầu.',
1305
+ 'pi.providers.targetFile': 'Tệp cấu hình Pi đang dùng: {path}',
1306
+ 'pi.providers.editorTitle': 'Chỉnh sửa nhà cung cấp',
1307
+ 'pi.providers.add': 'Thêm nhà cung cấp',
1308
+ 'pi.providers.active': 'Đang bật',
1309
+ 'pi.providers.setActive': 'Đặt thành đang dùng',
1310
+ 'pi.providers.deleteTitle': 'Xóa cấu hình Pi',
1311
+ 'pi.providers.deleteMessage': 'Xóa nhà cung cấp "{name}"?',
1312
+ 'pi.providers.fields.name': 'Tên',
1313
+ 'pi.providers.fields.baseUrl': 'Base URL',
1314
+ 'pi.providers.fields.api': 'API',
1315
+ 'pi.providers.fields.apiKey': 'API Key',
1316
+ 'pi.providers.fields.models': 'Mô hình',
1317
+ 'pi.providers.fields.configJson': 'Cấu hình JSON',
1318
+ 'pi.providers.models.remove': 'Xóa',
1319
+ 'pi.providers.models.search': 'Tìm kiếm mô hình khả dụng…',
1320
+ 'pi.providers.models.remoteCount': 'Đã lấy {count} mô hình, bấm để thêm',
1321
+ 'pi.providers.models.remoteLoading': 'Đang tải danh sách mô hình từ xa…',
1322
+ 'pi.providers.models.remoteFailed': 'Bạn vẫn có thể nhập ID mô hình thủ công.',
1323
+ 'pi.providers.models.noMatch': 'Không có mô hình phù hợp',
1324
+ 'pi.providers.delete': 'Xóa nhà cung cấp',
1325
+ 'pi.providers.addModal.title': 'Thêm nhà cung cấp',
1326
+ 'pi.providers.addModal.subtitle': 'Tạo nhà cung cấp Piper mới',
1327
+ 'pi.providers.addModal.fields.baseUrl': 'Base URL',
1328
+ 'pi.providers.addModal.fields.apiKey': 'API Key',
1329
+ 'pi.providers.addModal.api.openaiCompletions': 'OpenAI Chat Completions (tương thích nhất)',
1330
+ 'pi.providers.addModal.api.openaiResponses': 'OpenAI Responses API',
1331
+ 'pi.providers.addModal.api.anthropicMessages': 'Anthropic Messages API',
1332
+ 'pi.providers.addModal.api.googleGenerativeAi': 'Google Generative AI',
1333
+ 'pi.providers.addModal.fields.api': 'Định dạng API',
1282
1334
  'kilocode.providerModel.title': 'Nhà cung cấp / mô hình KiloCode',
1283
1335
  'kilocode.connection.title': 'Kết nối KiloCode',
1284
1336
  'kilocode.targetFile': 'Cấu hình KiloCode đang dùng: {path} · {status}',
@@ -13,6 +13,7 @@ const zhTw = Object.freeze({
13
13
  'common.paste': '粘貼',
14
14
  'common.edit': '編輯',
15
15
  'common.install': '安裝',
16
+ 'common.optional': '選填',
16
17
  'common.update': '升級',
17
18
  'common.uninstall': '解除安裝',
18
19
  'common.official': '官方',
@@ -34,6 +35,8 @@ const zhTw = Object.freeze({
34
35
  'common.save': '保存',
35
36
  'common.saveApply': '保存並應用',
36
37
  'common.close': '關閉',
38
+ 'common.history': '歷史',
39
+ 'common.restoreToEditor': '還原到編輯器',
37
40
  'common.delete': '刪除',
38
41
  'common.clear': '清空',
39
42
  'common.show': '顯示',
@@ -133,6 +136,7 @@ const zhTw = Object.freeze({
133
136
  'tab.config.claude': 'Claude',
134
137
  'tab.config.openclaw': 'OpenClaw',
135
138
  'tab.config.opencode': 'OpenCode',
139
+ 'tab.config.pi': 'Pi',
136
140
  'tab.sessions': '會話',
137
141
  'tab.usage': '用量',
138
142
  'tab.market': 'Skills',
@@ -467,6 +471,8 @@ const zhTw = Object.freeze({
467
471
  'toast.health.noFailedProviderSelection': '請先選擇至少一個失敗提供商',
468
472
  'toast.health.deleteFailedProvidersDone': '已刪除 {count} 個失敗提供商',
469
473
  'toast.noChanges': '未檢測到改動',
474
+ 'toast.history.restored': '已還原到編輯器,儲存後生效',
475
+ 'toast.history.empty': '尚無歷史',
470
476
  'toast.template.loadFail': '載入模板失敗',
471
477
  'toast.template.empty': '模板不能為空',
472
478
  'toast.template.applied': '模板已應用',
@@ -691,6 +697,7 @@ const zhTw = Object.freeze({
691
697
  'sessions.preview.importNative.failedWithReason': '匯入原生目錄失敗:{reason}',
692
698
  'sessions.preview.copyLink': '複製鏈接',
693
699
  'sessions.preview.copyPath': '複製路徑',
700
+ 'sessions.preview.copyMessage': '複製記錄',
694
701
  'sessions.preview.openLink': '打開鏈接',
695
702
  'sessions.preview.loadingBody': '正在載入會話內容...',
696
703
  'sessions.preview.emptyMsgs': '目前會話暫無可展示消息',
@@ -1173,6 +1180,10 @@ const zhTw = Object.freeze({
1173
1180
  'settings.timeline.style.dots': '點狀',
1174
1181
  'settings.timeline.style.bar': '條狀',
1175
1182
  'settings.timeline.style.hint': '點狀適合快速定位,條狀更直觀顯示進度',
1183
+ 'settings.configTabs.title': '配置面板可見性',
1184
+ 'settings.configTabs.meta': '控制側邊欄顯示哪些配置子頁面',
1185
+ 'settings.configTabs.hint': '關閉後對應的配置子頁面將不再顯示,至少需保留一個',
1186
+ 'settings.configTabs.minRequired': '至少需要保留一個可見的配置頁面',
1176
1187
 
1177
1188
  'settings.reset.title': '設定重置',
1178
1189
  'settings.reset.meta': '謹慎操作',
@@ -1308,6 +1319,47 @@ const zhTw = Object.freeze({
1308
1319
  'toolConfig.openclaw.confirmMessage': '開啟後,OpenClaw tab 內的操作會寫入 openclaw.json 及 OpenClaw 工作區檔案(AGENTS.md、SOUL.md 等)。',
1309
1320
  'toolConfig.openclaw.lockedTitle': 'OpenClaw 目前為唯讀',
1310
1321
  'toolConfig.openclaw.lockedDesc': '這裡不會寫入 OpenClaw 設定。要套用設定或編輯工作區檔案,請先開啟本 tab 的寫入開關。',
1322
+ 'side.config.pi': 'Pi',
1323
+ 'side.config.pi.meta': '設定',
1324
+ 'toolConfig.pi.title': 'Pi 設定寫入',
1325
+ 'toolConfig.pi.desc': '預設唯讀;開啟後可同步 Pi 設定。',
1326
+ 'toolConfig.pi.lockedTitle': '唯讀模式',
1327
+ 'toolConfig.pi.lockedDesc': '開啟寫入權限後可同步 Pi 設定。',
1328
+ 'toolConfig.pi.confirmMessage': '開啟後,Pi tab 內的操作會寫入 ~/.pi/config.json 或專案內的 ./.pi/config.json。',
1329
+ 'pi.status.readOnly': '唯讀',
1330
+ 'pi.config.openGlobal': '打開全域設定',
1331
+ 'pi.providers.title': 'Pi 供應商',
1332
+ 'pi.providers.subtitle': '管理 Piper 本地供應商與模型',
1333
+ 'pi.providers.empty': '未找到 Piper 供應商,點擊新增開始。',
1334
+ 'pi.providers.targetFile': 'Pi 生效設定檔:{path}',
1335
+ 'pi.providers.editorTitle': '編輯供應商',
1336
+ 'pi.providers.add': '新增供應商',
1337
+ 'pi.providers.active': '已啟用',
1338
+ 'pi.providers.setActive': '設為使用中',
1339
+ 'pi.providers.deleteTitle': '刪除 Pi 設定',
1340
+ 'pi.providers.deleteMessage': '確定刪除供應商 "{name}"?',
1341
+ 'pi.providers.fields.name': '名稱',
1342
+ 'pi.providers.fields.baseUrl': 'Base URL',
1343
+ 'pi.providers.fields.api': '介面格式',
1344
+ 'pi.providers.fields.apiKey': 'API Key',
1345
+ 'pi.providers.fields.models': '模型',
1346
+ 'pi.providers.fields.configJson': '組態 JSON',
1347
+ 'pi.providers.models.remove': '刪除',
1348
+ 'pi.providers.models.search': '搜尋可用模型…',
1349
+ 'pi.providers.models.remoteCount': '已取得 {count} 個可用模型,點擊新增',
1350
+ 'pi.providers.models.remoteLoading': '正在取得遠端模型清單…',
1351
+ 'pi.providers.models.remoteFailed': '可手動輸入模型 ID。',
1352
+ 'pi.providers.models.noMatch': '無符合模型',
1353
+ 'pi.providers.delete': '刪除供應商',
1354
+ 'pi.providers.addModal.title': '新增供應商',
1355
+ 'pi.providers.addModal.subtitle': '建立新的 Piper 供應商',
1356
+ 'pi.providers.addModal.fields.baseUrl': 'Base URL',
1357
+ 'pi.providers.addModal.fields.apiKey': 'API Key',
1358
+ 'pi.providers.addModal.api.openaiCompletions': 'OpenAI Chat Completions(最相容)',
1359
+ 'pi.providers.addModal.api.openaiResponses': 'OpenAI Responses API',
1360
+ 'pi.providers.addModal.api.anthropicMessages': 'Anthropic Messages API',
1361
+ 'pi.providers.addModal.api.googleGenerativeAi': 'Google Generative AI',
1362
+ 'pi.providers.addModal.fields.api': '介面格式',
1311
1363
  'kilocode.providerModel.title': 'KiloCode provider / 模型',
1312
1364
  'kilocode.connection.title': 'KiloCode 連線',
1313
1365
  'kilocode.targetFile': 'KiloCode 生效設定:{path} · {status}',
@@ -13,6 +13,7 @@ const zh = Object.freeze({
13
13
  'common.paste': '粘贴',
14
14
  'common.edit': '编辑',
15
15
  'common.install': '安装',
16
+ 'common.optional': '选填',
16
17
  'common.update': '升级',
17
18
  'common.uninstall': '卸载',
18
19
  'common.official': '官方',
@@ -34,6 +35,8 @@ const zh = Object.freeze({
34
35
  'common.save': '保存',
35
36
  'common.saveApply': '保存并应用',
36
37
  'common.close': '关闭',
38
+ 'common.history': '历史',
39
+ 'common.restoreToEditor': '恢复到编辑器',
37
40
  'common.delete': '删除',
38
41
  'common.clear': '清空',
39
42
  'common.show': '显示',
@@ -133,6 +136,7 @@ const zh = Object.freeze({
133
136
  'tab.config.claude': 'Claude',
134
137
  'tab.config.openclaw': 'OpenClaw',
135
138
  'tab.config.opencode': 'OpenCode',
139
+ 'tab.config.pi': 'Pi',
136
140
  'tab.sessions': '会话',
137
141
  'tab.usage': '用量',
138
142
  'tab.market': 'Skills',
@@ -467,6 +471,8 @@ const zh = Object.freeze({
467
471
  'toast.health.noFailedProviderSelection': '请先选择至少一个失败提供商',
468
472
  'toast.health.deleteFailedProvidersDone': '已删除 {count} 个失败提供商',
469
473
  'toast.noChanges': '未检测到改动',
474
+ 'toast.history.restored': '已恢复到编辑器,保存后生效',
475
+ 'toast.history.empty': '暂无历史',
470
476
  'toast.template.loadFail': '加载模板失败',
471
477
  'toast.template.empty': '模板不能为空',
472
478
  'toast.template.applied': '模板已应用',
@@ -691,6 +697,7 @@ const zh = Object.freeze({
691
697
  'sessions.preview.importNative.failedWithReason': '导入原生目录失败:{reason}',
692
698
  'sessions.preview.copyLink': '复制链接',
693
699
  'sessions.preview.copyPath': '复制路径',
700
+ 'sessions.preview.copyMessage': '复制记录',
694
701
  'sessions.preview.openLink': '打开链接',
695
702
  'sessions.preview.loadingBody': '正在加载会话内容...',
696
703
  'sessions.preview.emptyMsgs': '当前会话暂无可展示消息',
@@ -1173,6 +1180,10 @@ const zh = Object.freeze({
1173
1180
  'settings.timeline.style.dots': '点状',
1174
1181
  'settings.timeline.style.bar': '条状',
1175
1182
  'settings.timeline.style.hint': '点状适合快速定位,条状更直观显示进度',
1183
+ 'settings.configTabs.title': '配置面板可见性',
1184
+ 'settings.configTabs.meta': '控制侧栏中哪些配置子页面可见',
1185
+ 'settings.configTabs.hint': '关闭后对应的配置子页面将不再显示,至少需保留一个',
1186
+ 'settings.configTabs.minRequired': '至少需要保留一个可见的配置页面',
1176
1187
 
1177
1188
  'settings.reset.title': '配置重置',
1178
1189
  'settings.reset.meta': '谨慎操作',
@@ -1308,6 +1319,47 @@ const zh = Object.freeze({
1308
1319
  'toolConfig.openclaw.confirmMessage': '打开后,OpenClaw tab 内的操作会写入 openclaw.json 及 OpenClaw 工作区文件(AGENTS.md、SOUL.md 等)。',
1309
1320
  'toolConfig.openclaw.lockedTitle': 'OpenClaw 当前为只读',
1310
1321
  'toolConfig.openclaw.lockedDesc': '这里不会写入 OpenClaw 配置。要应用配置或编辑工作区文件,请先打开本 tab 的写入开关。',
1322
+ 'side.config.pi': 'Pi',
1323
+ 'side.config.pi.meta': '配置',
1324
+ 'toolConfig.pi.title': 'Pi 配置写入',
1325
+ 'toolConfig.pi.desc': '默认只读;开启后可同步 Pi 配置。',
1326
+ 'toolConfig.pi.lockedTitle': '只读模式',
1327
+ 'toolConfig.pi.lockedDesc': '开启写入权限后可同步 Pi 配置。',
1328
+ 'toolConfig.pi.confirmMessage': '打开后,Pi tab 内的操作会写入 ~/.pi/config.json 或项目内的 ./.pi/config.json。',
1329
+ 'pi.status.readOnly': '只读',
1330
+ 'pi.config.openGlobal': '打开全局配置',
1331
+ 'pi.providers.title': 'Pi 供应商',
1332
+ 'pi.providers.subtitle': '管理 Piper 本地供应商与模型',
1333
+ 'pi.providers.empty': '未找到 Piper 供应商,点击添加开始。',
1334
+ 'pi.providers.targetFile': 'Pi 生效配置文件:{path}',
1335
+ 'pi.providers.editorTitle': '编辑供应商',
1336
+ 'pi.providers.add': '添加供应商',
1337
+ 'pi.providers.active': '已启用',
1338
+ 'pi.providers.setActive': '设为使用中',
1339
+ 'pi.providers.deleteTitle': '删除 Pi 配置',
1340
+ 'pi.providers.deleteMessage': '确定删除供应商 "{name}"?',
1341
+ 'pi.providers.fields.name': '名称',
1342
+ 'pi.providers.fields.baseUrl': 'Base URL',
1343
+ 'pi.providers.fields.api': '接口格式',
1344
+ 'pi.providers.fields.apiKey': 'API Key',
1345
+ 'pi.providers.fields.models': '模型',
1346
+ 'pi.providers.fields.configJson': '配置 JSON',
1347
+ 'pi.providers.models.remove': '删除',
1348
+ 'pi.providers.models.search': '搜索可用模型…',
1349
+ 'pi.providers.models.remoteCount': '已拉取 {count} 个可用模型,点击添加',
1350
+ 'pi.providers.models.remoteLoading': '正在获取远端模型列表…',
1351
+ 'pi.providers.models.remoteFailed': '可手动输入模型 ID。',
1352
+ 'pi.providers.models.noMatch': '无匹配模型',
1353
+ 'pi.providers.delete': '删除供应商',
1354
+ 'pi.providers.addModal.title': '添加供应商',
1355
+ 'pi.providers.addModal.subtitle': '创建新的 Piper 供应商',
1356
+ 'pi.providers.addModal.fields.baseUrl': 'Base URL',
1357
+ 'pi.providers.addModal.fields.apiKey': 'API Key',
1358
+ 'pi.providers.addModal.api.openaiCompletions': 'OpenAI Chat Completions(最兼容)',
1359
+ 'pi.providers.addModal.api.openaiResponses': 'OpenAI Responses API',
1360
+ 'pi.providers.addModal.api.anthropicMessages': 'Anthropic Messages API',
1361
+ 'pi.providers.addModal.api.googleGenerativeAi': 'Google Generative AI',
1362
+ 'pi.providers.addModal.fields.api': '接口格式',
1311
1363
  'kilocode.providerModel.title': 'KiloCode provider / model',
1312
1364
  'kilocode.connection.title': 'KiloCode 连接',
1313
1365
  'kilocode.targetFile': 'KiloCode 生效配置:{path} · {status}',
@@ -183,6 +183,7 @@
183
183
  <div class="side-section" role="navigation" :aria-label="t('side.config')">
184
184
  <div class="side-section-title">{{ t('side.config') }}</div>
185
185
  <button
186
+ v-if="isConfigModeVisible('codex')"
186
187
  id="side-tab-config-codex"
187
188
  data-main-tab="config"
188
189
  data-config-mode="codex"
@@ -197,6 +198,7 @@
197
198
  </div>
198
199
  </button>
199
200
  <button
201
+ v-if="isConfigModeVisible('claude')"
200
202
  id="side-tab-config-claude"
201
203
  data-main-tab="config"
202
204
  data-config-mode="claude"
@@ -211,6 +213,7 @@
211
213
  </div>
212
214
  </button>
213
215
  <button
216
+ v-if="isConfigModeVisible('openclaw')"
214
217
  id="side-tab-config-openclaw"
215
218
  data-main-tab="config"
216
219
  data-config-mode="openclaw"
@@ -225,6 +228,7 @@
225
228
  </div>
226
229
  </button>
227
230
  <button
231
+ v-if="isConfigModeVisible('opencode')"
228
232
  id="side-tab-config-opencode"
229
233
  data-main-tab="config"
230
234
  data-config-mode="opencode"
@@ -240,6 +244,7 @@
240
244
  </button>
241
245
 
242
246
  <button
247
+ v-if="isConfigModeVisible('kilocode')"
243
248
  id="side-tab-config-kilocode"
244
249
  data-main-tab="config"
245
250
  data-config-mode="kilocode"
@@ -253,6 +258,21 @@
253
258
  <span>{{ t('side.config.kilocode.meta') }}</span>
254
259
  </div>
255
260
  </button>
261
+ <button
262
+ v-if="isConfigModeVisible('pi')"
263
+ id="side-tab-config-pi"
264
+ data-main-tab="config"
265
+ data-config-mode="pi"
266
+ :aria-current="mainTab === 'config' && configMode === 'pi' ? 'page' : null"
267
+ :class="['side-item', 'side-item-compact', { active: isConfigModeNavActive('pi') }]"
268
+ @pointerdown="onConfigTabPointerDown('pi', $event)"
269
+ @click="onConfigTabClick('pi', $event)">
270
+ <span class="side-item-icon" aria-hidden="true">P</span>
271
+ <div class="side-item-title">{{ t('side.config.pi') }}</div>
272
+ <div class="side-item-meta">
273
+ <span>{{ t('side.config.pi.meta') }}</span>
274
+ </div>
275
+ </button>
256
276
  </div>
257
277
 
258
278
 
@@ -6,9 +6,9 @@
6
6
  role="tabpanel"
7
7
  :aria-labelledby="forceCompactLayout ? 'tab-config' : 'side-tab-config-claude'">
8
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>
9
+ <button v-if="isConfigModeVisible('codex')" type="button" :class="['segment', { active: configMode === 'codex' }]" @click="switchConfigMode('codex')">{{ t('tab.config.codex') }}</button>
10
+ <button v-if="isConfigModeVisible('claude')" type="button" :class="['segment', { active: configMode === 'claude' }]" @click="switchConfigMode('claude')">{{ t('tab.config.claude') }}</button>
11
+ <button v-if="isConfigModeVisible('openclaw')" type="button" :class="['segment', { active: configMode === 'openclaw' }]" @click="switchConfigMode('openclaw')">{{ t('tab.config.openclaw') }}</button>
12
12
  </div>
13
13
  <template v-if="shouldShowCliInstallPlaceholder('claude')">
14
14
  <div class="selector-section">
@@ -6,9 +6,9 @@
6
6
  role="tabpanel"
7
7
  :aria-labelledby="forceCompactLayout ? 'tab-config' : ('side-tab-config-' + configMode)">
8
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>
9
+ <button v-if="isConfigModeVisible('codex')" type="button" :class="['segment', { active: configMode === 'codex' }]" @click="switchConfigMode('codex')">{{ t('tab.config.codex') }}</button>
10
+ <button v-if="isConfigModeVisible('claude')" type="button" :class="['segment', { active: configMode === 'claude' }]" @click="switchConfigMode('claude')">{{ t('tab.config.claude') }}</button>
11
+ <button v-if="isConfigModeVisible('openclaw')" type="button" :class="['segment', { active: configMode === 'openclaw' }]" @click="switchConfigMode('openclaw')">{{ t('tab.config.openclaw') }}</button>
12
12
  </div>
13
13
  <template v-if="isCodexConfigMode && shouldShowCliInstallPlaceholder('codex')">
14
14
  <div class="selector-section">
@@ -6,11 +6,11 @@
6
6
  role="tabpanel"
7
7
  :aria-labelledby="forceCompactLayout ? 'tab-config' : 'side-tab-config-kilocode'">
8
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>
9
+ <button v-if="isConfigModeVisible('codex')" type="button" :class="['segment', { active: configMode === 'codex' }]" @click="switchConfigMode('codex')">{{ t('tab.config.codex') }}</button>
10
+ <button v-if="isConfigModeVisible('claude')" type="button" :class="['segment', { active: configMode === 'claude' }]" @click="switchConfigMode('claude')">{{ t('tab.config.claude') }}</button>
11
+ <button v-if="isConfigModeVisible('openclaw')" type="button" :class="['segment', { active: configMode === 'openclaw' }]" @click="switchConfigMode('openclaw')">{{ t('tab.config.openclaw') }}</button>
12
+ <button v-if="isConfigModeVisible('opencode')" type="button" :class="['segment', { active: configMode === 'opencode' }]" @click="switchConfigMode('opencode')">{{ t('tab.config.opencode') }}</button>
13
+ <button v-if="isConfigModeVisible('kilocode')" type="button" :class="['segment', { active: configMode === 'kilocode' }]" @click="switchConfigMode('kilocode')">{{ t('tab.config.kilocode') }}</button>
14
14
  </div>
15
15
 
16
16
  <section class="tool-config-write-card" :aria-label="t('toolConfig.kilocode.title')">
@@ -6,9 +6,9 @@
6
6
  role="tabpanel"
7
7
  :aria-labelledby="forceCompactLayout ? 'tab-config' : 'side-tab-config-openclaw'">
8
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>
9
+ <button v-if="isConfigModeVisible('codex')" type="button" :class="['segment', { active: configMode === 'codex' }]" @click="switchConfigMode('codex')">{{ t('tab.config.codex') }}</button>
10
+ <button v-if="isConfigModeVisible('claude')" type="button" :class="['segment', { active: configMode === 'claude' }]" @click="switchConfigMode('claude')">{{ t('tab.config.claude') }}</button>
11
+ <button v-if="isConfigModeVisible('openclaw')" 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
14
  <section class="tool-config-write-card" :aria-label="t('toolConfig.openclaw.title')">