diff --git a/cli.js b/cli.js index ab9858f7..ac9aeb53 100644 --- a/cli.js +++ b/cli.js @@ -909,8 +909,8 @@ function isPlainObject(value) { return !!value && typeof value === 'object' && !Array.isArray(value); } -const TOOL_CONFIG_PERMISSION_TARGETS = new Set(['codex', 'claude', 'opencode', 'kilocode']); -const TOOL_CONFIG_PERMISSION_DEFAULTS = Object.freeze({ codex: false, claude: false, opencode: false, kilocode: false }); +const TOOL_CONFIG_PERMISSION_TARGETS = new Set(['codex', 'claude', 'opencode', 'kilocode', 'openclaw']); +const TOOL_CONFIG_PERMISSION_DEFAULTS = Object.freeze({ codex: false, claude: false, opencode: false, kilocode: false, openclaw: false }); let toolConfigWriteGuardDepth = 0; function enterToolConfigWriteGuard() { @@ -938,7 +938,8 @@ function normalizeToolConfigPermissions(value) { codex: source.codex === true, claude: source.claude === true, opencode: source.opencode === true, - kilocode: source.kilocode === true + kilocode: source.kilocode === true, + openclaw: source.openclaw === true }; } @@ -1197,10 +1198,16 @@ function getApiToolConfigWriteTarget(action) { 'apply-kilocode-config', 'start-kilocode' ]); + const openclawWriteActions = new Set([ + 'apply-openclaw-config', + 'apply-openclaw-agents-file', + 'apply-openclaw-workspace-file' + ]); if (codexWriteActions.has(name)) return 'codex'; if (claudeWriteActions.has(name)) return 'claude'; if (opencodeWriteActions.has(name)) return 'opencode'; if (kilocodeWriteActions.has(name)) return 'kilocode'; + if (openclawWriteActions.has(name)) return 'openclaw'; return ''; } diff --git a/package-lock.json b/package-lock.json index cf223c43..2c4da6ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "codexmate", - "version": "0.1.3", + "version": "0.1.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codexmate", - "version": "0.1.3", + "version": "0.1.4", "license": "Apache-2.0", "dependencies": { "@iarna/toml": "^2.2.5", diff --git a/package.json b/package.json index 75074576..f44dc619 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codexmate", - "version": "0.1.3", + "version": "0.1.4", "description": "Codex/Claude Code/OpenClaw 配置、会话与任务编排 CLI + Web 工具", "main": "cli.js", "bin": { diff --git a/tests/e2e/test-openclaw.js b/tests/e2e/test-openclaw.js index ad84b3de..2f1d2e48 100644 --- a/tests/e2e/test-openclaw.js +++ b/tests/e2e/test-openclaw.js @@ -30,6 +30,9 @@ module.exports = async function testOpenclaw(ctx) { assert(openclawReadEmpty.authProfilesByProvider['openai-codex'].editable === true, 'get-openclaw-config missing editable auth profile flag'); // ========== Apply OpenClaw Config Tests ========== + const enableOpenclawWrites = await api('set-tool-config-permission', { target: 'openclaw', allowWrite: true }); + assert(enableOpenclawWrites.success === true, 'set-tool-config-permission(openclaw) should succeed'); + const openclawInvalid = await api('apply-openclaw-config', { content: '', lineEnding: '\n' }); assert(openclawInvalid.success === false, 'apply-openclaw-config should reject empty content'); diff --git a/tests/unit/config-tabs-ui.test.mjs b/tests/unit/config-tabs-ui.test.mjs index af89f49b..c89b8f63 100644 --- a/tests/unit/config-tabs-ui.test.mjs +++ b/tests/unit/config-tabs-ui.test.mjs @@ -501,7 +501,7 @@ test('config template keeps expected config tabs in top and side navigation', () ); assert.match( html, - /:class="\['card', \{ active: currentOpenclawConfig === name \}\]"[\s\S]*@click="applyOpenclawConfig\(name\)"[\s\S]*@keydown\.enter\.self\.prevent="applyOpenclawConfig\(name\)"[\s\S]*@keydown\.space\.self\.prevent="applyOpenclawConfig\(name\)"[\s\S]*tabindex="0"[\s\S]*role="button"[\s\S]*:aria-current="currentOpenclawConfig === name \? 'true' : null"/ + /:class="\['card', \{ active: currentOpenclawConfig === name, disabled: !isToolConfigWriteAllowed\('openclaw'\) \}\]"[\s\S]*@click="isToolConfigWriteAllowed\('openclaw'\) && applyOpenclawConfig\(name\)"[\s\S]*@keydown\.enter\.self\.prevent="isToolConfigWriteAllowed\('openclaw'\) && applyOpenclawConfig\(name\)"[\s\S]*@keydown\.space\.self\.prevent="isToolConfigWriteAllowed\('openclaw'\) && applyOpenclawConfig\(name\)"[\s\S]*:tabindex="isToolConfigWriteAllowed\('openclaw'\) \? 0 : -1"[\s\S]*role="button"[\s\S]*:aria-disabled="!isToolConfigWriteAllowed\('openclaw'\) \? 'true' : null"[\s\S]*:aria-current="currentOpenclawConfig === name \? 'true' : null"/ ); assert.match(html, /class="session-item-copy session-item-pin"/); assert.doesNotMatch(sessionsPanel, /sessionsViewMode/); diff --git a/tests/unit/opencode-config-ui.test.mjs b/tests/unit/opencode-config-ui.test.mjs index afadc829..17e1d563 100644 --- a/tests/unit/opencode-config-ui.test.mjs +++ b/tests/unit/opencode-config-ui.test.mjs @@ -117,7 +117,7 @@ test('opencode backend actions are permission guarded in cli source', () => { const cli = readProjectFile('cli.js'); assert.match(cli, /const OPENCODE_GLOBAL_JSONC_CONFIG_FILE = path\.join\(OPENCODE_CONFIG_DIR, 'opencode\.jsonc'\)/); assert.match(cli, /const OPENCODE_CONFIG_ENV_FILE = process\.env\.OPENCODE_CONFIG/); - assert.match(cli, /const TOOL_CONFIG_PERMISSION_TARGETS = new Set\(\['codex', 'claude', 'opencode', 'kilocode'\]\)/); + assert.match(cli, /const TOOL_CONFIG_PERMISSION_TARGETS = new Set\(\['codex', 'claude', 'opencode', 'kilocode', 'openclaw'\]\)/); assert.match(cli, /function applyOpencodeConfigRaw\(params = \{\}\) \{[\s\S]*assertToolConfigWriteAllowed\('opencode'\)/); assert.match(cli, /function updateOpencodeSelection\(params = \{\}\) \{[\s\S]*assertToolConfigWriteAllowed\('opencode'\)/); assert.match(cli, /case 'get-opencode-config':/); diff --git a/tests/unit/web-ui-behavior-parity.test.mjs b/tests/unit/web-ui-behavior-parity.test.mjs index 2c6e54f5..fe530e95 100644 --- a/tests/unit/web-ui-behavior-parity.test.mjs +++ b/tests/unit/web-ui-behavior-parity.test.mjs @@ -461,6 +461,7 @@ test('captured bundled app skeleton only exposes expected data key drift versus 'showPromptTemplateVarModal', 'brandHovered', 'promptsHint', + 'currentInstalledCommandCards', ]; allowedExtraCurrentKeys.push( 'lang', @@ -991,7 +992,8 @@ test('captured bundled app skeleton only exposes expected data key drift versus 'localProxyUpstreamOptions', 'currentClaudeHaikuModel', 'currentClaudeSonnetModel', - 'currentClaudeOpusModel' + 'currentClaudeOpusModel', + 'currentInstalledCommandCards' ]; if (parityAgainstHead) { const allowedExtraComputedKeySet = new Set(allowedExtraCurrentComputedKeys); diff --git a/tests/unit/web-ui-preferences.test.mjs b/tests/unit/web-ui-preferences.test.mjs index 49741d99..2aa7c62f 100644 --- a/tests/unit/web-ui-preferences.test.mjs +++ b/tests/unit/web-ui-preferences.test.mjs @@ -219,14 +219,15 @@ test('web UI preference debounce preserves nested pending overrides', async () = test('web UI preferences preserve KiloCode write permission on reload', () => { const context = createContext([], createMemoryStorage()); context.applyWebUiPreferences({ - toolConfigPermissions: { codex: false, claude: false, opencode: false, kilocode: true } + toolConfigPermissions: { codex: false, claude: false, opencode: false, kilocode: true, openclaw: false } }, { applyNavigation: false }); assert.deepStrictEqual(context.toolConfigPermissions, { codex: false, claude: false, opencode: false, - kilocode: true + kilocode: true, + openclaw: false }); }); diff --git a/web-ui/app.js b/web-ui/app.js index 3cc152c7..8f2b0562 100644 --- a/web-ui/app.js +++ b/web-ui/app.js @@ -396,8 +396,8 @@ document.addEventListener('DOMContentLoaded', () => { providerCacheError: '', providerCacheRequestSeq: 0, settingsTab: 'general', - toolConfigPermissions: { codex: false, claude: false, opencode: false, kilocode: false }, - toolConfigPermissionSaving: { codex: false, claude: false, opencode: false, kilocode: false }, + toolConfigPermissions: { codex: false, claude: false, opencode: false, kilocode: false, openclaw: false }, + toolConfigPermissionSaving: { codex: false, claude: false, opencode: false, kilocode: false, openclaw: false }, sessionTrashEnabled: true, sessionTrashItems: [], sessionTrashVisibleCount: SESSION_TRASH_PAGE_SIZE, diff --git a/web-ui/modules/app.computed.dashboard.mjs b/web-ui/modules/app.computed.dashboard.mjs index f531f525..92d169d2 100644 --- a/web-ui/modules/app.computed.dashboard.mjs +++ b/web-ui/modules/app.computed.dashboard.mjs @@ -174,10 +174,6 @@ export function createDashboardComputed() { }; }); }, - currentInstalledCommandCards() { - const targets = Array.isArray(this.installTargetCards) ? this.installTargetCards : []; - return targets.filter((target) => target && target.installed === true); - }, installRegistryPreview() { return this.resolveInstallRegistryUrl(this.installRegistryPreset, this.installRegistryCustom); }, diff --git a/web-ui/modules/app.methods.startup-claude.mjs b/web-ui/modules/app.methods.startup-claude.mjs index 07993a25..8a3a46ce 100644 --- a/web-ui/modules/app.methods.startup-claude.mjs +++ b/web-ui/modules/app.methods.startup-claude.mjs @@ -144,7 +144,8 @@ export function createStartupClaudeMethods(options = {}) { codex: statusRes.toolConfigPermissions.codex === true, claude: statusRes.toolConfigPermissions.claude === true, opencode: statusRes.toolConfigPermissions.opencode === true, - kilocode: statusRes.toolConfigPermissions.kilocode === true + kilocode: statusRes.toolConfigPermissions.kilocode === true, + openclaw: statusRes.toolConfigPermissions.openclaw === true }; } this.providersList = listRes.providers; diff --git a/web-ui/modules/app.methods.tool-config-permissions.mjs b/web-ui/modules/app.methods.tool-config-permissions.mjs index 2f64feb2..1fb99697 100644 --- a/web-ui/modules/app.methods.tool-config-permissions.mjs +++ b/web-ui/modules/app.methods.tool-config-permissions.mjs @@ -3,7 +3,7 @@ export function createToolConfigPermissionMethods(options = {}) { function normalizeTarget(value) { const target = typeof value === 'string' ? value.trim().toLowerCase() : ''; - return target === 'codex' || target === 'claude' || target === 'opencode' || target === 'kilocode' ? target : ''; + return target === 'codex' || target === 'claude' || target === 'opencode' || target === 'kilocode' || target === 'openclaw' ? target : ''; } function normalizePermissions(value) { @@ -12,7 +12,8 @@ export function createToolConfigPermissionMethods(options = {}) { codex: source.codex === true, claude: source.claude === true, opencode: source.opencode === true, - kilocode: source.kilocode === true + kilocode: source.kilocode === true, + openclaw: source.openclaw === true }; } diff --git a/web-ui/modules/app.methods.web-ui-preferences.mjs b/web-ui/modules/app.methods.web-ui-preferences.mjs index 510fce67..20164c11 100644 --- a/web-ui/modules/app.methods.web-ui-preferences.mjs +++ b/web-ui/modules/app.methods.web-ui-preferences.mjs @@ -439,7 +439,8 @@ export function createWebUiPreferencesMethods(options = {}) { codex: source.toolConfigPermissions.codex === true, claude: source.toolConfigPermissions.claude === true, opencode: source.toolConfigPermissions.opencode === true, - kilocode: source.toolConfigPermissions.kilocode === true + kilocode: source.toolConfigPermissions.kilocode === true, + openclaw: source.toolConfigPermissions.openclaw === true }; } if (Array.isArray(source.deletedClaudeSettingsImports)) { diff --git a/web-ui/modules/i18n/locales/en.mjs b/web-ui/modules/i18n/locales/en.mjs index 262b2da3..067023e1 100644 --- a/web-ui/modules/i18n/locales/en.mjs +++ b/web-ui/modules/i18n/locales/en.mjs @@ -619,14 +619,8 @@ const en = Object.freeze({ // Docs panel 'docs.title': 'CLI Install', - 'docs.subtitle': 'Install commands for Claude Code / Gemini CLI / CodeBuddy Code / Codex CLI / KiloCode CLI.', 'docs.section.commands': 'Commands', - 'docs.section.commandsNote': 'Copy and run directly.', - 'docs.installed.title': 'Currently installed commands', - 'docs.installed.count': '{count} installed', - 'docs.installed.empty': 'No installed commands detected', 'docs.section.faq': 'FAQ', - 'docs.section.faqNote': 'Common issues and tips.', 'docs.command.aria': '{name} command', 'docs.registryHintPrefix': 'Command will append:', 'docs.registryHintCustom': 'Enter a full URL (http/https) to append as registry.', @@ -634,8 +628,6 @@ const en = Object.freeze({ 'docs.meta.bin': 'bin: {bin}', 'docs.termuxLabel': 'Termux', 'docs.termuxAria': 'Termux Codex CLI command', - 'docs.rule.1': 'Commands depend on package manager, registry and action.', - 'docs.rule.2': 'Custom registry is used for install/update only.', 'docs.tip.win.1': 'If PowerShell reports permission errors (EACCES/EPERM), run the install command as Administrator.', 'docs.tip.win.2': 'If the command is still not found after install, reopen the terminal and run: where codex / where claude / where gemini / where codebuddy.', 'docs.tip.win.3': 'If your network blocks npm, try switching registry presets (npmmirror / Tencent / Custom).', @@ -1544,6 +1536,11 @@ const en = Object.freeze({ 'toolConfig.kilocode.lockedTitle': 'Read-only mode', 'toolConfig.kilocode.lockedDesc': 'Enable write access to auto-sync config or launch KiloCode.', 'toolConfig.kilocode.confirmMessage': 'When enabled, actions in the KiloCode tab can write ~/.config/kilo/kilo.jsonc or an existing kilo.json, and can launch local KiloCode.', + 'toolConfig.openclaw.title': 'OpenClaw config writes', + 'toolConfig.openclaw.desc': 'Read-only by default; enable to apply OpenClaw configs and write AGENTS.md and other workspace files.', + 'toolConfig.openclaw.confirmMessage': 'When enabled, actions in the OpenClaw tab can write openclaw.json and OpenClaw workspace files (AGENTS.md, SOUL.md, etc.).', + 'toolConfig.openclaw.lockedTitle': 'OpenClaw is read-only', + 'toolConfig.openclaw.lockedDesc': 'OpenClaw config is not written here. To apply configs or edit workspace files, enable write access on this tab first.', 'kilocode.providerModel.title': 'KiloCode provider / model', 'kilocode.connection.title': 'KiloCode connection', 'kilocode.targetFile': 'Active KiloCode config: {path} · {status}', diff --git a/web-ui/modules/i18n/locales/ja.mjs b/web-ui/modules/i18n/locales/ja.mjs index fba2ab2a..6df3d364 100644 --- a/web-ui/modules/i18n/locales/ja.mjs +++ b/web-ui/modules/i18n/locales/ja.mjs @@ -621,14 +621,8 @@ const ja = Object.freeze({ // Docs panel 'docs.title': 'CLI インストールドキュメント', - 'docs.subtitle': 'Claude Code / Gemini CLI / CodeBuddy Code / Codex CLI / KiloCode CLI コマンドを表示。', 'docs.section.commands': 'インストールコマンド', - 'docs.section.commandsNote': 'コマンドは直接コピーできます。', - 'docs.installed.title': '現在インストール済みのコマンド', - 'docs.installed.count': '{count} 個インストール済み', - 'docs.installed.empty': 'インストール済みコマンドは検出されていません', 'docs.section.faq': 'よくある質問', - 'docs.section.faqNote': 'よくある質問は以下をご覧ください。', 'docs.command.aria': '{name} コマンド', 'docs.registryHintPrefix': 'コマンドに付加:', 'docs.registryHintCustom': '完全な URL(http/https 含む)を入力してください。コマンドに自動付加されます。', @@ -636,8 +630,6 @@ const ja = Object.freeze({ 'docs.meta.bin': 'bin: {bin}', 'docs.termuxLabel': 'Termux', 'docs.termuxAria': 'Termux Codex CLI command', - 'docs.rule.1': 'コマンドは現在のパッケージマネージャー、ミラー、操作に基づいて生成されます。', - 'docs.rule.2': 'カスタムミラーはインストールとアップグレードにのみ使用されます。', 'docs.tip.win.1': 'PowerShell で権限不足(EACCES/EPERM)エラーが発生した場合は、管理者としてインストールコマンドを実行してください。', 'docs.tip.win.2': 'インストール後にコマンドが見つからない場合は、ターミナルを再起動して実行:where codex / where claude / where gemini / where codebuddy。', 'docs.tip.win.3': '企業ネットワークで制限がある場合は、ミラーソース(npmmirror / Tencent Cloud / カスタム)を切り替えてください。', @@ -1531,6 +1523,11 @@ const ja = Object.freeze({ 'toolConfig.kilocode.lockedTitle': '読み取り専用モード', 'toolConfig.kilocode.lockedDesc': '書き込み権限を有効化すると、設定の自動同期または KiloCode の起動ができます。', 'toolConfig.kilocode.confirmMessage': '有効化すると、KiloCode タブ内の操作が ~/.config/kilo/kilo.jsonc または既存の kilo.json に書き込み、ローカル KiloCode を起動できます。', + 'toolConfig.openclaw.title': 'OpenClaw 設定の書き込み', + 'toolConfig.openclaw.desc': '既定は読み取り専用。有効化すると OpenClaw 設定を適用し、AGENTS.md などのワークスペースファイルを書き込みできます。', + 'toolConfig.openclaw.confirmMessage': '有効化すると、OpenClaw タブ内の操作が openclaw.json およびワークスペースファイル(AGENTS.md、SOUL.md など)に書き込みできます。', + 'toolConfig.openclaw.lockedTitle': 'OpenClaw は読み取り専用です', + 'toolConfig.openclaw.lockedDesc': 'ここでは OpenClaw 設定を書き込みません。設定を適用またはワークスペースファイルを編集するには、このタブの書き込みスイッチを有効化してください。', 'kilocode.providerModel.title': 'KiloCode プロバイダー / モデル', 'kilocode.connection.title': 'KiloCode 接続', 'kilocode.targetFile': '有効な KiloCode 設定:{path} · {status}', diff --git a/web-ui/modules/i18n/locales/vi.mjs b/web-ui/modules/i18n/locales/vi.mjs index a4c882a4..83a6f778 100644 --- a/web-ui/modules/i18n/locales/vi.mjs +++ b/web-ui/modules/i18n/locales/vi.mjs @@ -826,14 +826,8 @@ const vi = Object.freeze({ 'modal.openclaw.quick.optionsHint': 'Khi tắt ghi đè, chỉ điền các trường còn thiếu.', 'modal.openclaw.quick.writeToEditor': 'Ghi vào editor', 'docs.title': 'Cài CLI', - 'docs.subtitle': 'Lệnh cài đặt cho Claude Code / Gemini CLI / CodeBuddy Code / Codex CLI / KiloCode CLI.', 'docs.section.commands': 'Lệnh', - 'docs.section.commandsNote': 'Sao chép và chạy trực tiếp.', - 'docs.installed.title': 'Lệnh hiện đã cài đặt', - 'docs.installed.count': 'Đã cài {count}', - 'docs.installed.empty': 'Chưa phát hiện lệnh đã cài', 'docs.section.faq': 'FAQ', - 'docs.section.faqNote': 'Vấn đề phổ biến và mẹo.', 'docs.command.aria': 'Lệnh {name}', 'docs.registryHintPrefix': 'Lệnh sẽ thêm:', 'docs.registryHintCustom': 'Nhập URL đầy đủ (http/https) để thêm làm registry.', @@ -841,8 +835,6 @@ const vi = Object.freeze({ 'docs.meta.bin': 'bin: {bin}', 'docs.termuxLabel': 'Termux', 'docs.termuxAria': 'Lệnh Codex CLI cho Termux', - 'docs.rule.1': 'Lệnh phụ thuộc vào trình quản lý gói, registry và hành động.', - 'docs.rule.2': 'Registry tùy chỉnh chỉ dùng cho cài đặt/cập nhật.', 'docs.tip.win.1': 'Nếu PowerShell báo lỗi quyền (EACCES/EPERM), chạy lệnh cài đặt với quyền Administrator.', 'docs.tip.win.2': 'Nếu lệnh không tìm thấy sau cài đặt, mở lại terminal và chạy: where codex / where claude.', 'docs.tip.win.3': 'Nếu mạng chặn npm, thử chuyển registry (npmmirror / Tencent / Tùy chỉnh).', @@ -1516,6 +1508,11 @@ const vi = Object.freeze({ 'toolConfig.kilocode.lockedTitle': 'Chế độ chỉ đọc', 'toolConfig.kilocode.lockedDesc': 'Bật quyền ghi để tự động đồng bộ cấu hình hoặc khởi chạy KiloCode.', 'toolConfig.kilocode.confirmMessage': 'Khi bật, các thao tác trong tab KiloCode có thể ghi ~/.config/kilo/kilo.jsonc hoặc kilo.json hiện có, và có thể khởi chạy KiloCode cục bộ.', + 'toolConfig.openclaw.title': 'Ghi cấu hình OpenClaw', + 'toolConfig.openclaw.desc': 'Mặc định chỉ đọc; bật để áp dụng cấu hình OpenClaw và ghi AGENTS.md cùng các tệp workspace khác.', + '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.).', + 'toolConfig.openclaw.lockedTitle': 'OpenClaw đang ở chế độ chỉ đọc', + '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.', 'kilocode.providerModel.title': 'Nhà cung cấp / mô hình KiloCode', 'kilocode.connection.title': 'Kết nối KiloCode', 'kilocode.targetFile': 'Cấu hình KiloCode đang dùng: {path} · {status}', diff --git a/web-ui/modules/i18n/locales/zh-tw.mjs b/web-ui/modules/i18n/locales/zh-tw.mjs index 9f05bae0..a6405f09 100644 --- a/web-ui/modules/i18n/locales/zh-tw.mjs +++ b/web-ui/modules/i18n/locales/zh-tw.mjs @@ -619,14 +619,8 @@ const zhTw = Object.freeze({ // Docs panel 'docs.title': 'CLI 安裝文檔', - 'docs.subtitle': '查看 Claude Code / Gemini CLI / CodeBuddy Code / Codex CLI / KiloCode CLI 命令。', 'docs.section.commands': '安裝命令', - 'docs.section.commandsNote': '命令可直接複製。', - 'docs.installed.title': '目前已安裝命令', - 'docs.installed.count': '已安裝 {count} 個', - 'docs.installed.empty': '未偵測到已安裝命令', 'docs.section.faq': '常見問題', - 'docs.section.faqNote': '常見問題見下。', 'docs.command.aria': '{name} 命令', 'docs.registryHintPrefix': '命令將附加:', 'docs.registryHintCustom': '請輸入完整 URL(含 http/https),將自動附加到命令。', @@ -634,8 +628,6 @@ const zhTw = Object.freeze({ 'docs.meta.bin': 'bin: {bin}', 'docs.termuxLabel': 'Termux', 'docs.termuxAria': 'Termux Codex CLI command', - 'docs.rule.1': '命令按目前包管理器、鏡像與操作生成。', - 'docs.rule.2': '自訂鏡像僅用於安裝與升級。', 'docs.tip.win.1': 'PowerShell 報權限不足(EACCES/EPERM)時,請以管理員身份執行安裝命令。', 'docs.tip.win.2': '安裝後若仍提示找不到命令,重開終端並執行:where codex / where claude / where gemini / where codebuddy。', 'docs.tip.win.3': '公司網路受限時,可先切換鏡像源快捷項(npmmirror / 騰訊雲 / 自訂)。', @@ -1544,6 +1536,11 @@ const zhTw = Object.freeze({ 'toolConfig.kilocode.lockedTitle': '唯讀模式', 'toolConfig.kilocode.lockedDesc': '啟用寫入權限後可自動同步設定或啟動 KiloCode。', 'toolConfig.kilocode.confirmMessage': '開啟後,KiloCode tab 內的操作會寫入 ~/.config/kilo/kilo.jsonc 或既有的 kilo.json,並可啟動本機 KiloCode。', + 'toolConfig.openclaw.title': 'OpenClaw 設定寫入', + 'toolConfig.openclaw.desc': '預設唯讀;啟用後可套用 OpenClaw 設定並寫入 AGENTS.md 與其他工作區檔案。', + 'toolConfig.openclaw.confirmMessage': '開啟後,OpenClaw tab 內的操作會寫入 openclaw.json 及 OpenClaw 工作區檔案(AGENTS.md、SOUL.md 等)。', + 'toolConfig.openclaw.lockedTitle': 'OpenClaw 目前為唯讀', + 'toolConfig.openclaw.lockedDesc': '這裡不會寫入 OpenClaw 設定。要套用設定或編輯工作區檔案,請先開啟本 tab 的寫入開關。', 'kilocode.providerModel.title': 'KiloCode provider / 模型', 'kilocode.connection.title': 'KiloCode 連線', 'kilocode.targetFile': 'KiloCode 生效設定:{path} · {status}', diff --git a/web-ui/modules/i18n/locales/zh.mjs b/web-ui/modules/i18n/locales/zh.mjs index fad8ce5d..e145cf09 100644 --- a/web-ui/modules/i18n/locales/zh.mjs +++ b/web-ui/modules/i18n/locales/zh.mjs @@ -619,14 +619,8 @@ const zh = Object.freeze({ // Docs panel 'docs.title': 'CLI 安装文档', - 'docs.subtitle': '查看 Claude Code / Gemini CLI / CodeBuddy Code / Codex CLI / KiloCode CLI 命令。', 'docs.section.commands': '安装命令', - 'docs.section.commandsNote': '命令可直接复制。', - 'docs.installed.title': '当前已安装命令', - 'docs.installed.count': '已安装 {count} 个', - 'docs.installed.empty': '未检测到已安装命令', 'docs.section.faq': '常见问题', - 'docs.section.faqNote': '常见问题见下。', 'docs.command.aria': '{name} 命令', 'docs.registryHintPrefix': '命令将附加:', 'docs.registryHintCustom': '请输入完整 URL(含 http/https),将自动附加到命令。', @@ -634,8 +628,6 @@ const zh = Object.freeze({ 'docs.meta.bin': 'bin: {bin}', 'docs.termuxLabel': 'Termux', 'docs.termuxAria': 'Termux Codex CLI command', - 'docs.rule.1': '命令按当前包管理器、镜像与操作生成。', - 'docs.rule.2': '自定义镜像仅用于安装与升级。', 'docs.tip.win.1': 'PowerShell 报权限不足(EACCES/EPERM)时,请以管理员身份执行安装命令。', 'docs.tip.win.2': '安装后若仍提示找不到命令,重开终端并执行:where codex / where claude / where gemini / where codebuddy。', 'docs.tip.win.3': '公司网络受限时,可先切换镜像源快捷项(npmmirror / 腾讯云 / 自定义)。', @@ -1544,6 +1536,11 @@ const zh = Object.freeze({ 'toolConfig.kilocode.lockedTitle': '只读模式', 'toolConfig.kilocode.lockedDesc': '开启写入权限后可保存配置或启动 KiloCode。', 'toolConfig.kilocode.confirmMessage': '打开后,KiloCode tab 内的操作会写入 ~/.config/kilo/kilo.jsonc 或已存在的 kilo.json,并可启动本机 KiloCode。', + 'toolConfig.openclaw.title': 'OpenClaw 配置写入', + 'toolConfig.openclaw.desc': '默认只读;开启后可应用 OpenClaw 配置、写入 AGENTS.md 与其他工作区文件。', + 'toolConfig.openclaw.confirmMessage': '打开后,OpenClaw tab 内的操作会写入 openclaw.json 及 OpenClaw 工作区文件(AGENTS.md、SOUL.md 等)。', + 'toolConfig.openclaw.lockedTitle': 'OpenClaw 当前为只读', + 'toolConfig.openclaw.lockedDesc': '这里不会写入 OpenClaw 配置。要应用配置或编辑工作区文件,请先打开本 tab 的写入开关。', 'kilocode.providerModel.title': 'KiloCode provider / model', 'kilocode.connection.title': 'KiloCode 连接', 'kilocode.targetFile': 'KiloCode 生效配置:{path} · {status}', diff --git a/web-ui/partials/index/panel-config-openclaw.html b/web-ui/partials/index/panel-config-openclaw.html index 755da4e0..7b4bf7b2 100644 --- a/web-ui/partials/index/panel-config-openclaw.html +++ b/web-ui/partials/index/panel-config-openclaw.html @@ -11,6 +11,23 @@
+
+
+
{{ t('toolConfig.openclaw.title') }}
+

{{ t('toolConfig.openclaw.desc') }}

+
+ +
+
@@ -32,10 +49,12 @@
+
+
-
+
+
+
{{ t('toolConfig.openclaw.lockedTitle') }}
+

{{ t('toolConfig.openclaw.lockedDesc') }}

+ +
+
+
+
diff --git a/web-ui/partials/index/panel-docs.html b/web-ui/partials/index/panel-docs.html index 45a7a9dc..f6b4bcee 100644 --- a/web-ui/partials/index/panel-docs.html +++ b/web-ui/partials/index/panel-docs.html @@ -7,7 +7,6 @@
{{ t('docs.title') }} -
{{ t('docs.subtitle') }}
@@ -54,28 +53,11 @@ {{ installRegistryPreview || 'npmmirror' }}
- -
-
- {{ t('docs.installed.title') }} - {{ currentInstalledCommandCards.length ? t('docs.installed.count', { count: currentInstalledCommandCards.length }) : t('docs.installed.empty') }} -
-
-
-
- {{ target.name }} - {{ target.version || '—' }} -
- {{ target.commandPath || target.bin }} -
-
-
{{ t('docs.section.commands') }} -
{{ t('docs.section.commandsNote') }}
@@ -108,7 +90,6 @@
{{ t('docs.section.faq') }} -
{{ t('docs.section.faqNote') }}
@@ -118,13 +99,6 @@
  • {{ tip }}
  • -
    -
    {{ t('common.rules') }}
    -
      -
    • {{ t('docs.rule.1') }}
    • -
    • {{ t('docs.rule.2') }}
    • -
    -
    diff --git a/web-ui/res/web-ui-render.precompiled.js b/web-ui/res/web-ui-render.precompiled.js index ab2147b6..e73b3cd0 100644 --- a/web-ui/res/web-ui-render.precompiled.js +++ b/web-ui/res/web-ui-render.precompiled.js @@ -2249,6 +2249,28 @@ return function render(_ctx, _cache) { ])) : _createCommentVNode("v-if", true), _createElementVNode("div", { class: "openclaw-layout" }, [ + _createElementVNode("section", { + class: "tool-config-write-card", + "aria-label": _ctx.t('toolConfig.openclaw.title') + }, [ + _createElementVNode("div", { class: "tool-config-write-copy" }, [ + _createElementVNode("div", { class: "tool-config-write-title" }, _toDisplayString(_ctx.t('toolConfig.openclaw.title')), 1 /* TEXT */), + _createElementVNode("p", { class: "tool-config-write-desc" }, _toDisplayString(_ctx.t('toolConfig.openclaw.desc')), 1 /* TEXT */) + ]), + _createElementVNode("label", { class: "settings-toggle-row tool-config-write-toggle" }, [ + _createElementVNode("input", { + type: "checkbox", + autocomplete: "off", + checked: _ctx.isToolConfigWriteAllowed('openclaw'), + disabled: _ctx.toolConfigPermissionSaving.openclaw, + onChange: $event => (_ctx.setToolConfigPermission('openclaw', $event.target.checked)) + }, null, 40 /* PROPS, NEED_HYDRATION */, ["checked", "disabled", "onChange"]), + _createElementVNode("span", { class: "toggle-track" }, [ + _createElementVNode("span", { class: "toggle-thumb" }) + ]), + _createElementVNode("span", null, _toDisplayString(_ctx.toolConfigPermissionStatusLabel('openclaw')), 1 /* TEXT */) + ]) + ], 8 /* PROPS */, ["aria-label"]), _createElementVNode("section", { class: "settings-card settings-card--wide openclaw-summary-card", "aria-label": "OpenClaw 当前摘要" @@ -2275,126 +2297,114 @@ return function render(_ctx, _cache) { _createElementVNode("div", { class: "openclaw-diagnostic-note" }, " 本页仅管理配置与工作区文件。 ") ]) ]), - _createElementVNode("section", { class: "settings-card settings-card--wide openclaw-workspace-card" }, [ - _createElementVNode("div", { class: "settings-card-body" }, [ - _createElementVNode("div", { class: "openclaw-tools-grid" }, [ - _createElementVNode("button", { - class: "openclaw-tool-btn", - onClick: _ctx.openOpenclawAgentsEditor, - disabled: _ctx.loading || !!_ctx.initError || _ctx.agentsLoading - }, [ - _createElementVNode("div", { class: "tool-icon" }, "📄"), - _createElementVNode("div", { class: "tool-content" }, [ - _createElementVNode("div", { class: "tool-title" }, "AGENTS.md"), - _createElementVNode("div", { class: "tool-meta" }, _toDisplayString(_ctx.agentsLoading ? _ctx.t('config.modelLoading') : 'Workspace file'), 1 /* TEXT */) - ]), - (_openBlock(), _createElementBlock("svg", { - class: "tool-chevron", - viewBox: "0 0 24 24", - fill: "none", - stroke: "currentColor", - "stroke-width": "2" - }, [ - _createElementVNode("path", { d: "M9 18l6-6-6-6" }) - ])) - ], 8 /* PROPS */, ["onClick", "disabled"]), - (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.getOpenclawQuickWorkspaceFiles(), (fileName) => { - return (_openBlock(), _createElementBlock("button", { - key: fileName, - type: "button", - class: "openclaw-tool-btn", - onClick: $event => (_ctx.openOpenclawQuickWorkspaceFile(fileName)), - disabled: _ctx.loading || !!_ctx.initError || _ctx.agentsLoading - }, [ - _createElementVNode("div", { class: "tool-icon" }, "📄"), - _createElementVNode("div", { class: "tool-content" }, [ - _createElementVNode("div", { class: "tool-title" }, _toDisplayString(fileName), 1 /* TEXT */), - _createElementVNode("div", { class: "tool-meta" }, "Workspace file") - ]), - (_openBlock(), _createElementBlock("svg", { - class: "tool-chevron", - viewBox: "0 0 24 24", - fill: "none", - stroke: "currentColor", - "stroke-width": "2" - }, [ - _createElementVNode("path", { d: "M9 18l6-6-6-6" }) - ])) - ], 8 /* PROPS */, ["onClick", "disabled"])) - }), 128 /* KEYED_FRAGMENT */)) - ]) - ]) - ]), - _createElementVNode("section", { - class: "settings-card settings-card--wide openclaw-configs-card", - "aria-labelledby": "openclaw-configs-title" + _createElementVNode("div", { + class: _normalizeClass(["tool-config-write-scope", { locked: !_ctx.isToolConfigWriteAllowed('openclaw') }]) }, [ - _createElementVNode("div", { class: "settings-card-body" }, [ - _createElementVNode("div", { class: "card-list openclaw-card-list" }, [ - (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.openclawConfigs, (config, name) => { - return (_openBlock(), _createElementBlock("div", { - key: name, - class: _normalizeClass(['card', { active: _ctx.currentOpenclawConfig === name }]), - onClick: $event => (_ctx.applyOpenclawConfig(name)), - onKeydown: [ - _withKeys(_withModifiers($event => (_ctx.applyOpenclawConfig(name)), ["self","prevent"]), ["enter"]), - _withKeys(_withModifiers($event => (_ctx.applyOpenclawConfig(name)), ["self","prevent"]), ["space"]) - ], - tabindex: "0", - role: "button", - "aria-label": _ctx.t('openclaw.action.applyAria', { name }), - "aria-current": _ctx.currentOpenclawConfig === name ? 'true' : null - }, [ - _createElementVNode("div", { class: "card-leading" }, [ - _createElementVNode("div", { class: "card-icon" }, _toDisplayString(name.charAt(0).toUpperCase()), 1 /* TEXT */), - _createElementVNode("div", { class: "card-content" }, [ - _createElementVNode("div", { class: "card-title" }, _toDisplayString(name), 1 /* TEXT */), - _createElementVNode("div", { class: "card-subtitle" }, _toDisplayString(_ctx.openclawSubtitle(config)), 1 /* TEXT */), - (_ctx.openclawHasContent(config)) - ? (_openBlock(), _createElementBlock("div", { - key: 0, - class: "openclaw-config-summary-mini" - }, [ - (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.getOpenclawConfigSummary(config).slice(0, 3), (item) => { - return (_openBlock(), _createElementBlock("span", { - key: item.key - }, _toDisplayString(item.label) + ": " + _toDisplayString(item.value), 1 /* TEXT */)) - }), 128 /* KEYED_FRAGMENT */)) - ])) - : _createCommentVNode("v-if", true) - ]) - ]), - _createElementVNode("div", { class: "card-trailing" }, [ - _createElementVNode("span", { - class: _normalizeClass(['pill', _ctx.openclawHasContent(config) ? 'configured' : 'empty']) - }, _toDisplayString(_ctx.openclawHasContent(config) ? _ctx.t('openclaw.configured') : _ctx.t('openclaw.notConfigured')), 3 /* TEXT, CLASS */), - _createElementVNode("div", { - class: "card-actions", - onClick: _withModifiers(() => {}, ["stop"]) + _createElementVNode("div", { class: "tool-config-write-body" }, [ + _createElementVNode("section", { class: "settings-card settings-card--wide openclaw-workspace-card" }, [ + _createElementVNode("div", { class: "settings-card-body" }, [ + _createElementVNode("div", { class: "openclaw-tools-grid" }, [ + _createElementVNode("button", { + class: "openclaw-tool-btn", + onClick: _ctx.openOpenclawAgentsEditor, + disabled: _ctx.loading || !!_ctx.initError || _ctx.agentsLoading || !_ctx.isToolConfigWriteAllowed('openclaw') + }, [ + _createElementVNode("div", { class: "tool-icon" }, "📄"), + _createElementVNode("div", { class: "tool-content" }, [ + _createElementVNode("div", { class: "tool-title" }, "AGENTS.md"), + _createElementVNode("div", { class: "tool-meta" }, _toDisplayString(_ctx.agentsLoading ? _ctx.t('config.modelLoading') : 'Workspace file'), 1 /* TEXT */) + ]), + (_openBlock(), _createElementBlock("svg", { + class: "tool-chevron", + viewBox: "0 0 24 24", + fill: "none", + stroke: "currentColor", + "stroke-width": "2" }, [ - _createElementVNode("button", { - class: "card-action-btn", - onClick: $event => (_ctx.openOpenclawEditModal(name)), - "aria-label": _ctx.t('openclaw.action.editAria', { name }), - title: _ctx.t('openclaw.action.edit') + _createElementVNode("path", { d: "M9 18l6-6-6-6" }) + ])) + ], 8 /* PROPS */, ["onClick", "disabled"]), + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.getOpenclawQuickWorkspaceFiles(), (fileName) => { + return (_openBlock(), _createElementBlock("button", { + key: fileName, + type: "button", + class: "openclaw-tool-btn", + onClick: $event => (_ctx.openOpenclawQuickWorkspaceFile(fileName)), + disabled: _ctx.loading || !!_ctx.initError || _ctx.agentsLoading || !_ctx.isToolConfigWriteAllowed('openclaw') + }, [ + _createElementVNode("div", { class: "tool-icon" }, "📄"), + _createElementVNode("div", { class: "tool-content" }, [ + _createElementVNode("div", { class: "tool-title" }, _toDisplayString(fileName), 1 /* TEXT */), + _createElementVNode("div", { class: "tool-meta" }, "Workspace file") + ]), + (_openBlock(), _createElementBlock("svg", { + class: "tool-chevron", + viewBox: "0 0 24 24", + fill: "none", + stroke: "currentColor", + "stroke-width": "2" }, [ - (_openBlock(), _createElementBlock("svg", { - viewBox: "0 0 24 24", - fill: "none", - stroke: "currentColor", - "stroke-width": "2" + _createElementVNode("path", { d: "M9 18l6-6-6-6" }) + ])) + ], 8 /* PROPS */, ["onClick", "disabled"])) + }), 128 /* KEYED_FRAGMENT */)) + ]) + ]) + ]), + _createElementVNode("section", { + class: "settings-card settings-card--wide openclaw-configs-card", + "aria-labelledby": "openclaw-configs-title" + }, [ + _createElementVNode("div", { class: "settings-card-body" }, [ + _createElementVNode("div", { class: "card-list openclaw-card-list" }, [ + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.openclawConfigs, (config, name) => { + return (_openBlock(), _createElementBlock("div", { + key: name, + class: _normalizeClass(['card', { active: _ctx.currentOpenclawConfig === name, disabled: !_ctx.isToolConfigWriteAllowed('openclaw') }]), + onClick: $event => (_ctx.isToolConfigWriteAllowed('openclaw') && _ctx.applyOpenclawConfig(name)), + onKeydown: [ + _withKeys(_withModifiers($event => (_ctx.isToolConfigWriteAllowed('openclaw') && _ctx.applyOpenclawConfig(name)), ["self","prevent"]), ["enter"]), + _withKeys(_withModifiers($event => (_ctx.isToolConfigWriteAllowed('openclaw') && _ctx.applyOpenclawConfig(name)), ["self","prevent"]), ["space"]) + ], + tabindex: _ctx.isToolConfigWriteAllowed('openclaw') ? 0 : -1, + role: "button", + "aria-label": _ctx.t('openclaw.action.applyAria', { name }), + "aria-disabled": !_ctx.isToolConfigWriteAllowed('openclaw') ? 'true' : null, + "aria-current": _ctx.currentOpenclawConfig === name ? 'true' : null + }, [ + _createElementVNode("div", { class: "card-leading" }, [ + _createElementVNode("div", { class: "card-icon" }, _toDisplayString(name.charAt(0).toUpperCase()), 1 /* TEXT */), + _createElementVNode("div", { class: "card-content" }, [ + _createElementVNode("div", { class: "card-title" }, _toDisplayString(name), 1 /* TEXT */), + _createElementVNode("div", { class: "card-subtitle" }, _toDisplayString(_ctx.openclawSubtitle(config)), 1 /* TEXT */), + (_ctx.openclawHasContent(config)) + ? (_openBlock(), _createElementBlock("div", { + key: 0, + class: "openclaw-config-summary-mini" + }, [ + (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.getOpenclawConfigSummary(config).slice(0, 3), (item) => { + return (_openBlock(), _createElementBlock("span", { + key: item.key + }, _toDisplayString(item.label) + ": " + _toDisplayString(item.value), 1 /* TEXT */)) + }), 128 /* KEYED_FRAGMENT */)) + ])) + : _createCommentVNode("v-if", true) + ]) + ]), + _createElementVNode("div", { class: "card-trailing" }, [ + _createElementVNode("span", { + class: _normalizeClass(['pill', _ctx.openclawHasContent(config) ? 'configured' : 'empty']) + }, _toDisplayString(_ctx.openclawHasContent(config) ? _ctx.t('openclaw.configured') : _ctx.t('openclaw.notConfigured')), 3 /* TEXT, CLASS */), + _createElementVNode("div", { + class: "card-actions", + onClick: _withModifiers(() => {}, ["stop"]) }, [ - _createElementVNode("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }), - _createElementVNode("path", { d: "M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" }) - ])) - ], 8 /* PROPS */, ["onClick", "aria-label", "title"]), - (!_ctx.isDefaultOpenclawConfig(name, config)) - ? (_openBlock(), _createElementBlock("button", { - key: 0, - class: "card-action-btn delete", - onClick: $event => (_ctx.deleteOpenclawConfig(name)), - "aria-label": _ctx.t('openclaw.action.deleteAria', { name }), - title: _ctx.t('openclaw.action.delete') + _createElementVNode("button", { + class: "card-action-btn", + onClick: $event => (_ctx.openOpenclawEditModal(name)), + disabled: !_ctx.isToolConfigWriteAllowed('openclaw'), + "aria-label": _ctx.t('openclaw.action.editAria', { name }), + title: _ctx.t('openclaw.action.edit') }, [ (_openBlock(), _createElementBlock("svg", { viewBox: "0 0 24 24", @@ -2402,18 +2412,55 @@ return function render(_ctx, _cache) { stroke: "currentColor", "stroke-width": "2" }, [ - _createElementVNode("path", { d: "M3 6h18" }), - _createElementVNode("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }) + _createElementVNode("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }), + _createElementVNode("path", { d: "M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" }) ])) - ], 8 /* PROPS */, ["onClick", "aria-label", "title"])) - : _createCommentVNode("v-if", true) - ], 8 /* PROPS */, ["onClick"]) + ], 8 /* PROPS */, ["onClick", "disabled", "aria-label", "title"]), + (!_ctx.isDefaultOpenclawConfig(name, config)) + ? (_openBlock(), _createElementBlock("button", { + key: 0, + class: "card-action-btn delete", + onClick: $event => (_ctx.deleteOpenclawConfig(name)), + "aria-label": _ctx.t('openclaw.action.deleteAria', { name }), + title: _ctx.t('openclaw.action.delete') + }, [ + (_openBlock(), _createElementBlock("svg", { + viewBox: "0 0 24 24", + fill: "none", + stroke: "currentColor", + "stroke-width": "2" + }, [ + _createElementVNode("path", { d: "M3 6h18" }), + _createElementVNode("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }) + ])) + ], 8 /* PROPS */, ["onClick", "aria-label", "title"])) + : _createCommentVNode("v-if", true) + ], 8 /* PROPS */, ["onClick"]) + ]) + ], 42 /* CLASS, PROPS, NEED_HYDRATION */, ["onClick", "onKeydown", "tabindex", "aria-label", "aria-disabled", "aria-current"])) + }), 128 /* KEYED_FRAGMENT */)) + ]) + ]) + ]), + (!_ctx.isToolConfigWriteAllowed('openclaw')) + ? (_openBlock(), _createElementBlock("div", { + key: 0, + class: "tool-config-write-overlay" + }, [ + _createElementVNode("div", { class: "tool-config-write-overlay-card" }, [ + _createElementVNode("div", { class: "tool-config-write-overlay-title" }, _toDisplayString(_ctx.t('toolConfig.openclaw.lockedTitle')), 1 /* TEXT */), + _createElementVNode("p", null, _toDisplayString(_ctx.t('toolConfig.openclaw.lockedDesc')), 1 /* TEXT */), + _createElementVNode("button", { + type: "button", + class: "btn-tool", + onClick: $event => (_ctx.setToolConfigPermission('openclaw', true)), + disabled: _ctx.toolConfigPermissionSaving.openclaw + }, _toDisplayString(_ctx.t('toolConfig.enableWrite')), 9 /* TEXT, PROPS */, ["onClick", "disabled"]) ]) - ], 42 /* CLASS, PROPS, NEED_HYDRATION */, ["onClick", "onKeydown", "aria-label", "aria-current"])) - }), 128 /* KEYED_FRAGMENT */)) - ]) + ])) + : _createCommentVNode("v-if", true) ]) - ]) + ], 2 /* CLASS */) ]) ], 8 /* PROPS */, ["aria-labelledby"]), [ [_vShow, _ctx.mainTab === 'config' && _ctx.configMode === 'openclaw'] @@ -5418,8 +5465,7 @@ return function render(_ctx, _cache) { }, [ _createElementVNode("div", { class: "selector-section" }, [ _createElementVNode("div", { class: "selector-header" }, [ - _createElementVNode("span", { class: "selector-title" }, _toDisplayString(_ctx.t('docs.title')), 1 /* TEXT */), - _createElementVNode("div", { class: "skills-panel-note docs-section-note" }, _toDisplayString(_ctx.t('docs.subtitle')), 1 /* TEXT */) + _createElementVNode("span", { class: "selector-title" }, _toDisplayString(_ctx.t('docs.title')), 1 /* TEXT */) ]), _createElementVNode("div", { class: "docs-toolbar-grid" }, [ _createElementVNode("div", { class: "docs-toolbar-card" }, [ @@ -5514,37 +5560,11 @@ return function render(_ctx, _cache) { _createElementVNode("span", { class: "docs-summary-label" }, _toDisplayString(_ctx.t('common.registry')), 1 /* TEXT */), _createElementVNode("strong", { class: "docs-summary-value" }, _toDisplayString(_ctx.installRegistryPreview || 'npmmirror'), 1 /* TEXT */) ]) - ]), - _createElementVNode("div", { class: "docs-installed-card" }, [ - _createElementVNode("div", { class: "docs-installed-head" }, [ - _createElementVNode("span", { class: "docs-installed-title" }, _toDisplayString(_ctx.t('docs.installed.title')), 1 /* TEXT */), - _createElementVNode("span", { class: "docs-installed-count" }, _toDisplayString(_ctx.currentInstalledCommandCards.length ? _ctx.t('docs.installed.count', { count: _ctx.currentInstalledCommandCards.length }) : _ctx.t('docs.installed.empty')), 1 /* TEXT */) - ]), - (_ctx.currentInstalledCommandCards.length) - ? (_openBlock(), _createElementBlock("div", { - key: 0, - class: "docs-installed-list" - }, [ - (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.currentInstalledCommandCards, (target) => { - return (_openBlock(), _createElementBlock("div", { - key: 'docs-installed-' + target.id, - class: "docs-installed-item" - }, [ - _createElementVNode("div", { class: "docs-installed-main" }, [ - _createElementVNode("span", { class: "docs-installed-name" }, _toDisplayString(target.name), 1 /* TEXT */), - _createElementVNode("span", { class: "docs-installed-version" }, _toDisplayString(target.version || '—'), 1 /* TEXT */) - ]), - _createElementVNode("code", { class: "docs-installed-command" }, _toDisplayString(target.commandPath || target.bin), 1 /* TEXT */) - ])) - }), 128 /* KEYED_FRAGMENT */)) - ])) - : _createCommentVNode("v-if", true) ]) ]), _createElementVNode("div", { class: "selector-section" }, [ _createElementVNode("div", { class: "selector-header" }, [ - _createElementVNode("span", { class: "selector-title" }, _toDisplayString(_ctx.t('docs.section.commands')), 1 /* TEXT */), - _createElementVNode("div", { class: "skills-panel-note docs-section-note" }, _toDisplayString(_ctx.t('docs.section.commandsNote')), 1 /* TEXT */) + _createElementVNode("span", { class: "selector-title" }, _toDisplayString(_ctx.t('docs.section.commands')), 1 /* TEXT */) ]), _createElementVNode("div", { class: "install-list docs-install-list" }, [ (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.installTargetCards, (target) => { @@ -5604,8 +5624,7 @@ return function render(_ctx, _cache) { ]), _createElementVNode("div", { class: "selector-section" }, [ _createElementVNode("div", { class: "selector-header" }, [ - _createElementVNode("span", { class: "selector-title" }, _toDisplayString(_ctx.t('docs.section.faq')), 1 /* TEXT */), - _createElementVNode("div", { class: "skills-panel-note docs-section-note" }, _toDisplayString(_ctx.t('docs.section.faqNote')), 1 /* TEXT */) + _createElementVNode("span", { class: "selector-title" }, _toDisplayString(_ctx.t('docs.section.faq')), 1 /* TEXT */) ]), _createElementVNode("div", { class: "docs-help-grid" }, [ _createElementVNode("div", { class: "docs-note-card" }, [ @@ -5615,13 +5634,6 @@ return function render(_ctx, _cache) { return (_openBlock(), _createElementBlock("li", { key: tip }, _toDisplayString(tip), 1 /* TEXT */)) }), 128 /* KEYED_FRAGMENT */)) ]) - ]), - _createElementVNode("div", { class: "docs-note-card" }, [ - _createElementVNode("div", { class: "docs-note-title" }, _toDisplayString(_ctx.t('common.rules')), 1 /* TEXT */), - _createElementVNode("ul", { class: "docs-static-list" }, [ - _createElementVNode("li", null, _toDisplayString(_ctx.t('docs.rule.1')), 1 /* TEXT */), - _createElementVNode("li", null, _toDisplayString(_ctx.t('docs.rule.2')), 1 /* TEXT */) - ]) ]) ]) ]) diff --git a/web-ui/styles/docs-panel.css b/web-ui/styles/docs-panel.css index a1f4d3cc..4bc3a4f6 100644 --- a/web-ui/styles/docs-panel.css +++ b/web-ui/styles/docs-panel.css @@ -8,15 +8,16 @@ gap: 16px; } -/* ---- Toolbar grid ---- */ +/* ---- Toolbar grid ---- + 三张配置卡(PM / Mirror / Action)等列宽分占,避免 + 此前 span4×span8×span8 带来的重心右偏与左侧留白。 */ .docs-toolbar-grid { display: grid; - grid-template-columns: repeat(12, minmax(0, 1fr)); + grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; } .docs-toolbar-card { - grid-column: span 4; display: flex; flex-direction: column; gap: 8px; @@ -27,13 +28,48 @@ } .docs-toolbar-card-wide { - grid-column: span 8; + /* 历史遗留的宽卡类,等宽网格下不再需要额外跨度, + 保留类名以兼容模板中的引用。 */ + grid-column: auto; } -/* ---- Summary strip ---- */ +/* Segmented control:把 install/registry 的散落 btn-mini + 收敛成带容器边框的成组按钮,弱化“一堆小按钮”观感。 */ +.docs-toolbar-card .install-action-tabs { + display: inline-flex; + flex-wrap: wrap; + gap: 4px; + padding: 4px; + border: 1px solid var(--color-border-soft); + border-radius: var(--radius-sm); + background: var(--color-bg-soft); +} + +.docs-toolbar-card .install-action-tabs .btn-mini { + border-color: transparent; + background: transparent; + box-shadow: none; + padding: 6px 12px; +} + +.docs-toolbar-card .install-action-tabs .btn-mini:hover { + border-color: var(--color-border-soft); + background: var(--color-surface); + transform: none; +} + +.docs-toolbar-card .install-action-tabs .btn-mini.active { + border-color: rgba(200, 121, 99, 0.22); + background: var(--color-surface); + box-shadow: var(--shadow-subtle); +} + +/* ---- Summary strip ---- + 原为 4 列网格但仅有 3 个 item,末列长期空置。改为 + 3 列等宽,composition 直接对齐内容数量。 */ .docs-summary-strip { display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); + grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin-top: 2px; } @@ -48,8 +84,9 @@ background: var(--color-surface); } +/* 三列等宽后不再需要额外跨度,保留类名兼容模板引用。 */ .docs-summary-item-wide { - grid-column: span 2; + grid-column: auto; } .docs-summary-label { @@ -69,57 +106,6 @@ word-break: break-word; } -.docs-installed-card { - margin-top: 12px; - padding: 12px; - border: 1px solid var(--color-border-soft); - border-radius: var(--radius-md); - background: var(--color-bg-soft); -} - -.docs-installed-head, -.docs-installed-main { - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; -} - -.docs-installed-title, -.docs-installed-name { - font-weight: 700; - color: var(--color-text-primary); -} - -.docs-installed-count, -.docs-installed-version { - font-size: 12px; - color: var(--color-text-muted); -} - -.docs-installed-list { - display: grid; - gap: 8px; - margin-top: 10px; -} - -.docs-installed-item { - min-width: 0; - padding: 10px; - border-radius: var(--radius-sm); - background: var(--color-bg-card); -} - -.docs-installed-command { - display: block; - margin-top: 6px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 12px; - color: var(--color-text-secondary); -} - /* ---- Install rows ---- */ .docs-install-list { display: flex; @@ -155,7 +141,7 @@ display: flex; align-items: center; gap: 10px; - padding: 10px 14px; + padding: 8px 8px 8px 14px; border: 1px solid var(--color-border-soft); border-radius: var(--radius-sm); background: var(--color-surface-alt); @@ -163,18 +149,21 @@ max-width: 100%; } +/* 去框中框:box 本体已承担容器层,命令文本不再叠 + 二次背景/边框,降低视觉噪音、与终端观感对齐。 */ .docs-command-box .install-command { flex: 1; min-width: 0; - padding: 8px 12px; - border-radius: var(--radius-sm); - background: var(--color-surface); - border: 1px solid var(--color-border-soft); + padding: 0; + border-radius: 0; + background: transparent; + border: 0; white-space: normal; word-break: break-all; overflow-wrap: break-word; font-size: 13px; line-height: 1.5; + color: var(--color-text-primary); font-family: var(--font-family-mono); user-select: all; } @@ -226,9 +215,17 @@ display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; + align-items: stretch; +} + +/* 仅剩 troubleshooting 卡时,单卡占满整行,避免右侧空置。 */ +.docs-help-grid > .docs-note-card:only-child { + grid-column: 1 / -1; } .docs-note-card { + display: flex; + flex-direction: column; padding: 16px; border: 1px solid var(--color-border-soft); border-radius: var(--radius-md); @@ -246,46 +243,34 @@ margin: 0; } -.docs-static-list { - margin: 0; - padding-left: 18px; - color: var(--color-text-secondary); - font-size: 13px; - line-height: 1.6; -} - -.docs-static-list li + li { - margin-top: 8px; -} - -.docs-section-note { - margin-top: 4px; -} - /* ---- Responsive ---- */ @media (max-width: 1100px) { - .docs-toolbar-card, - .docs-toolbar-card-wide { - grid-column: span 12; - } - - .docs-summary-strip { + .docs-toolbar-grid { + /* PM 单占一行,mirror/action 并排占第二行,避免三卡齐挤窄列。 */ grid-template-columns: repeat(2, minmax(0, 1fr)); } - .docs-summary-item-wide { + .docs-toolbar-grid > .docs-toolbar-card:first-child { grid-column: span 2; } + + .docs-summary-strip { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } } @media (max-width: 720px) { - .docs-summary-strip, - .docs-help-grid { + .docs-toolbar-grid { grid-template-columns: 1fr; } - .docs-summary-item-wide { - grid-column: span 1; + .docs-toolbar-grid > .docs-toolbar-card:first-child { + grid-column: auto; + } + + .docs-summary-strip, + .docs-help-grid { + grid-template-columns: 1fr; } .docs-install-row,