codexmate 0.0.20 → 0.0.22

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 (96) hide show
  1. package/README.md +289 -152
  2. package/README.zh.md +321 -0
  3. package/cli/agents-files.js +224 -0
  4. package/cli/archive-helpers.js +446 -0
  5. package/cli/auth-profiles.js +359 -0
  6. package/cli/builtin-proxy.js +1044 -0
  7. package/cli/claude-proxy.js +998 -0
  8. package/cli/config-bootstrap.js +384 -0
  9. package/cli/openai-bridge.js +950 -0
  10. package/cli/openclaw-config.js +629 -0
  11. package/cli/session-usage.concurrent.js +28 -0
  12. package/cli/session-usage.js +112 -0
  13. package/cli/session-usage.models.js +176 -0
  14. package/cli/skills.js +1141 -0
  15. package/cli/zip-commands.js +510 -0
  16. package/cli.js +9408 -9719
  17. package/lib/cli-models-utils.js +109 -1
  18. package/lib/cli-path-utils.js +69 -0
  19. package/lib/cli-sessions.js +386 -0
  20. package/lib/download-artifacts.js +77 -0
  21. package/lib/task-orchestrator.js +869 -0
  22. package/package.json +14 -10
  23. package/res/logo.png +0 -0
  24. package/res/vue.global.prod.js +13 -0
  25. package/web-ui/app.js +193 -15
  26. package/web-ui/index.html +5 -1
  27. package/web-ui/logic.agents-diff.mjs +1 -1
  28. package/web-ui/logic.claude.mjs +60 -0
  29. package/web-ui/logic.runtime.mjs +11 -7
  30. package/web-ui/logic.sessions.mjs +372 -21
  31. package/web-ui/modules/api.mjs +22 -1
  32. package/web-ui/modules/app.computed.dashboard.mjs +23 -10
  33. package/web-ui/modules/app.computed.index.mjs +4 -0
  34. package/web-ui/modules/app.computed.main-tabs.mjs +198 -0
  35. package/web-ui/modules/app.computed.session.mjs +521 -9
  36. package/web-ui/modules/app.methods.agents.mjs +62 -11
  37. package/web-ui/modules/app.methods.codex-config.mjs +189 -34
  38. package/web-ui/modules/app.methods.index.mjs +7 -1
  39. package/web-ui/modules/app.methods.install.mjs +24 -20
  40. package/web-ui/modules/app.methods.navigation.mjs +142 -1
  41. package/web-ui/modules/app.methods.openclaw-core.mjs +339 -39
  42. package/web-ui/modules/app.methods.openclaw-editing.mjs +39 -4
  43. package/web-ui/modules/app.methods.openclaw-persist.mjs +122 -4
  44. package/web-ui/modules/app.methods.providers.mjs +192 -53
  45. package/web-ui/modules/app.methods.session-actions.mjs +99 -19
  46. package/web-ui/modules/app.methods.session-browser.mjs +196 -5
  47. package/web-ui/modules/app.methods.session-timeline.mjs +22 -15
  48. package/web-ui/modules/app.methods.session-trash.mjs +3 -0
  49. package/web-ui/modules/app.methods.startup-claude.mjs +70 -71
  50. package/web-ui/modules/app.methods.task-orchestration.mjs +471 -0
  51. package/web-ui/modules/config-mode.computed.mjs +2 -0
  52. package/web-ui/modules/config-template-confirm-pref.mjs +33 -0
  53. package/web-ui/modules/i18n.mjs +1609 -0
  54. package/web-ui/modules/plugins.computed.mjs +220 -0
  55. package/web-ui/modules/plugins.methods.mjs +620 -0
  56. package/web-ui/modules/plugins.storage.mjs +37 -0
  57. package/web-ui/partials/index/layout-footer.html +1 -57
  58. package/web-ui/partials/index/layout-header.html +299 -175
  59. package/web-ui/partials/index/modal-config-template-agents.html +79 -29
  60. package/web-ui/partials/index/modal-confirm-toast.html +1 -1
  61. package/web-ui/partials/index/modal-health-check.html +14 -14
  62. package/web-ui/partials/index/modal-openclaw-config.html +47 -42
  63. package/web-ui/partials/index/modal-skills.html +130 -114
  64. package/web-ui/partials/index/modals-basic.html +71 -102
  65. package/web-ui/partials/index/panel-config-claude.html +50 -12
  66. package/web-ui/partials/index/panel-config-codex.html +34 -37
  67. package/web-ui/partials/index/panel-config-openclaw.html +10 -16
  68. package/web-ui/partials/index/panel-docs.html +147 -0
  69. package/web-ui/partials/index/panel-market.html +38 -38
  70. package/web-ui/partials/index/panel-orchestration.html +397 -0
  71. package/web-ui/partials/index/panel-plugins.html +243 -0
  72. package/web-ui/partials/index/panel-sessions.html +51 -146
  73. package/web-ui/partials/index/panel-settings.html +188 -96
  74. package/web-ui/partials/index/panel-usage.html +353 -0
  75. package/web-ui/session-helpers.mjs +221 -10
  76. package/web-ui/styles/base-theme.css +120 -229
  77. package/web-ui/styles/controls-forms.css +59 -51
  78. package/web-ui/styles/docs-panel.css +247 -0
  79. package/web-ui/styles/layout-shell.css +394 -128
  80. package/web-ui/styles/modals-core.css +18 -3
  81. package/web-ui/styles/navigation-panels.css +184 -183
  82. package/web-ui/styles/plugins-panel.css +518 -0
  83. package/web-ui/styles/responsive.css +102 -62
  84. package/web-ui/styles/sessions-list.css +13 -27
  85. package/web-ui/styles/sessions-preview.css +13 -7
  86. package/web-ui/styles/sessions-toolbar-trash.css +25 -0
  87. package/web-ui/styles/sessions-usage.css +581 -6
  88. package/web-ui/styles/settings-panel.css +166 -0
  89. package/web-ui/styles/skills-list.css +16 -11
  90. package/web-ui/styles/skills-market.css +63 -2
  91. package/web-ui/styles/task-orchestration.css +776 -0
  92. package/web-ui/styles/titles-cards.css +67 -66
  93. package/web-ui/styles.css +4 -0
  94. package/README.en.md +0 -259
  95. package/res/screenshot.png +0 -0
  96. package/res/vue.global.js +0 -18552
@@ -1,45 +1,15 @@
1
1
  <div id="app" class="container" v-cloak>
2
- <button class="fab-install" @click="openInstallModal" aria-label="安装 CLI">
3
- <span class="fab-install-icon" aria-hidden="true">
4
- <svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.8">
5
- <path d="M10 4v8M6.5 8.5 10 12l3.5-3.5" stroke-linecap="round" stroke-linejoin="round"/>
6
- <path d="M4 14.5h12" stroke-linecap="round"/>
7
- </svg>
8
- </span>
9
- </button>
10
-
11
- <div class="hero" v-if="!sessionStandalone">
12
- <img class="hero-logo" src="/res/logo.png" alt="Codex Mate logo">
13
- <div class="hero-title">
14
- Codex <span class="accent">Mate.</span>
15
- </div>
16
- <div class="hero-subtitle">
17
- 配置中枢:管理 Codex / Claude / OpenClaw / 会话
18
- <span class="sr-only">本地配置中枢,统一管理 Codex / Claude Code / OpenClaw / 会话。</span>
19
- </div>
20
- </div>
21
- <div v-if="!sessionStandalone" class="hero-github">
22
- <a
23
- class="github-badge github-badge-mobile"
24
- href="https://github.com/SakuraByteCore/codexmate"
25
- target="_blank"
26
- rel="noopener"
27
- title="打开 GitHub 仓库">
28
- <span class="github-badge-left">
29
- <svg class="github-badge-icon" viewBox="0 0 24 24" aria-hidden="true">
30
- <path fill="currentColor" d="M12 2C6.48 2 2 6.58 2 12.26c0 4.5 2.87 8.32 6.84 9.67.5.1.68-.22.68-.49 0-.24-.01-.88-.01-1.73-2.78.62-3.37-1.38-3.37-1.38-.45-1.19-1.11-1.5-1.11-1.5-.91-.64.07-.63.07-.63 1 .07 1.53 1.06 1.53 1.06.9 1.57 2.36 1.12 2.94.86.09-.66.35-1.12.63-1.38-2.22-.26-4.56-1.14-4.56-5.09 0-1.13.39-2.06 1.03-2.79-.1-.26-.45-1.31.1-2.73 0 0 .84-.28 2.75 1.06A9.36 9.36 0 0 1 12 6.8c.85 0 1.7.12 2.5.34 1.9-1.34 2.74-1.06 2.74-1.06.55 1.42.2 2.47.1 2.73.64.73 1.03 1.66 1.03 2.79 0 3.96-2.35 4.83-4.58 5.08.36.32.68.95.68 1.92 0 1.38-.01 2.5-.01 2.84 0 .27.18.59.69.49A10.04 10.04 0 0 0 22 12.26C22 6.58 17.52 2 12 2z"/>
31
- </svg>
32
- <span class="github-badge-label">GitHub</span>
33
- </span>
34
- <span class="github-badge-text" title="SakuraByteCore/codexmate">
35
- <span class="github-owner">SakuraByteCore</span>
36
- <span class="github-sep">/</span>
37
- <span class="github-repo">codexmate</span>
38
- </span>
39
- </a>
40
- </div>
41
-
42
- <div v-if="!sessionStandalone" class="top-tabs" role="tablist" aria-label="主导航">
2
+ <div v-if="!sessionStandalone" class="top-tabs" role="tablist" aria-label="Navigation">
3
+ <button class="top-tab"
4
+ id="tab-docs"
5
+ role="tab"
6
+ data-main-tab="docs"
7
+ :tabindex="mainTab === 'docs' ? 0 : -1"
8
+ :aria-selected="mainTab === 'docs'"
9
+ aria-controls="panel-docs"
10
+ :class="{ active: isMainTabNavActive('docs') }"
11
+ @pointerdown="onMainTabPointerDown('docs', $event)"
12
+ @click="onMainTabClick('docs', $event)">{{ t('tab.docs') }}</button>
43
13
  <button class="top-tab"
44
14
  id="tab-config-codex"
45
15
  role="tab"
@@ -50,7 +20,7 @@
50
20
  aria-controls="panel-config-provider"
51
21
  :class="{ active: isConfigModeNavActive('codex') }"
52
22
  @pointerdown="onConfigTabPointerDown('codex', $event)"
53
- @click="onConfigTabClick('codex', $event)">Codex 配置</button>
23
+ @click="onConfigTabClick('codex', $event)">{{ t('tab.config.codex') }}</button>
54
24
  <button class="top-tab"
55
25
  id="tab-config-claude"
56
26
  role="tab"
@@ -61,7 +31,7 @@
61
31
  aria-controls="panel-config-claude"
62
32
  :class="{ active: isConfigModeNavActive('claude') }"
63
33
  @pointerdown="onConfigTabPointerDown('claude', $event)"
64
- @click="onConfigTabClick('claude', $event)">Claude Code 配置</button>
34
+ @click="onConfigTabClick('claude', $event)">{{ t('tab.config.claude') }}</button>
65
35
  <button class="top-tab"
66
36
  id="tab-config-openclaw"
67
37
  role="tab"
@@ -72,7 +42,7 @@
72
42
  aria-controls="panel-config-openclaw"
73
43
  :class="{ active: isConfigModeNavActive('openclaw') }"
74
44
  @pointerdown="onConfigTabPointerDown('openclaw', $event)"
75
- @click="onConfigTabClick('openclaw', $event)">OpenClaw 配置</button>
45
+ @click="onConfigTabClick('openclaw', $event)">{{ t('tab.config.openclaw') }}</button>
76
46
  <button class="top-tab"
77
47
  id="tab-sessions"
78
48
  role="tab"
@@ -82,7 +52,27 @@
82
52
  aria-controls="panel-sessions"
83
53
  :class="{ active: isMainTabNavActive('sessions') }"
84
54
  @pointerdown="onMainTabPointerDown('sessions', $event)"
85
- @click="onMainTabClick('sessions', $event)">会话浏览</button>
55
+ @click="onMainTabClick('sessions', $event)">{{ t('tab.sessions') }}</button>
56
+ <button class="top-tab"
57
+ id="tab-usage"
58
+ role="tab"
59
+ data-main-tab="usage"
60
+ :tabindex="mainTab === 'usage' ? 0 : -1"
61
+ :aria-selected="mainTab === 'usage'"
62
+ aria-controls="panel-usage"
63
+ :class="{ active: isMainTabNavActive('usage') }"
64
+ @pointerdown="onMainTabPointerDown('usage', $event)"
65
+ @click="onMainTabClick('usage', $event)">{{ t('tab.usage') }}</button>
66
+ <button v-if="taskOrchestrationTabEnabled" class="top-tab"
67
+ id="tab-orchestration"
68
+ role="tab"
69
+ data-main-tab="orchestration"
70
+ :tabindex="mainTab === 'orchestration' ? 0 : -1"
71
+ :aria-selected="mainTab === 'orchestration'"
72
+ aria-controls="panel-orchestration"
73
+ :class="{ active: isMainTabNavActive('orchestration') }"
74
+ @pointerdown="onMainTabPointerDown('orchestration', $event)"
75
+ @click="onMainTabClick('orchestration', $event)">{{ t('tab.orchestration') }}</button>
86
76
  <button class="top-tab"
87
77
  id="tab-market"
88
78
  role="tab"
@@ -92,7 +82,17 @@
92
82
  aria-controls="panel-market"
93
83
  :class="{ active: isMainTabNavActive('market') }"
94
84
  @pointerdown="onMainTabPointerDown('market', $event)"
95
- @click="onMainTabClick('market', $event)">技能市场</button>
85
+ @click="onMainTabClick('market', $event)">{{ t('tab.market') }}</button>
86
+ <button class="top-tab"
87
+ id="tab-plugins"
88
+ role="tab"
89
+ data-main-tab="plugins"
90
+ :tabindex="mainTab === 'plugins' ? 0 : -1"
91
+ :aria-selected="mainTab === 'plugins'"
92
+ aria-controls="panel-plugins"
93
+ :class="{ active: isMainTabNavActive('plugins') }"
94
+ @pointerdown="onMainTabPointerDown('plugins', $event)"
95
+ @click="onMainTabClick('plugins', $event)">{{ t('tab.plugins') }}</button>
96
96
  <button class="top-tab"
97
97
  id="tab-settings"
98
98
  role="tab"
@@ -102,43 +102,58 @@
102
102
  aria-controls="panel-settings"
103
103
  :class="{ active: isMainTabNavActive('settings') }"
104
104
  @pointerdown="onMainTabPointerDown('settings', $event)"
105
- @click="onMainTabClick('settings', $event)">设置</button>
105
+ @click="onMainTabClick('settings', $event)">{{ t('tab.settings') }}</button>
106
+ </div>
107
+
108
+ <div v-if="!sessionStandalone" class="lang-fab" role="group" :aria-label="t('lang.label')">
109
+ <div class="lang-choice" role="group" :aria-label="t('lang.label')">
110
+ <button
111
+ type="button"
112
+ class="lang-choice-btn"
113
+ :aria-pressed="(lang || 'zh') === 'zh'"
114
+ :class="{ active: (lang || 'zh') === 'zh' }"
115
+ @click="setLang('zh')">ZH</button>
116
+ <button
117
+ type="button"
118
+ class="lang-choice-btn"
119
+ :aria-pressed="(lang || 'zh') === 'en'"
120
+ :class="{ active: (lang || 'zh') === 'en' }"
121
+ @click="setLang('en')">EN</button>
122
+ </div>
106
123
  </div>
107
124
 
108
125
  <div :class="['app-shell', { standalone: sessionStandalone }]">
109
126
  <aside class="side-rail" v-if="!sessionStandalone">
110
127
  <div class="brand-block">
111
- <div class="brand-logo-wrap">
128
+ <div class="brand-head">
112
129
  <img class="brand-logo" src="/res/logo.png" alt="Codex Mate logo">
130
+ <div class="brand-copy">
131
+ <div class="brand-kicker">Workspace</div>
132
+ <div class="brand-title">Codex Mate</div>
133
+ </div>
113
134
  </div>
114
- <div class="brand-title">
115
- Codex <span class="accent">Mate</span>
116
- </div>
117
- <div class="brand-subtitle">
118
- 配置 / 会话切换器
119
- </div>
120
- <a
121
- class="github-badge github-badge-rail"
122
- href="https://github.com/SakuraByteCore/codexmate"
123
- target="_blank"
124
- rel="noopener"
125
- title="打开 GitHub 仓库">
126
- <span class="github-badge-left">
127
- <svg class="github-badge-icon" viewBox="0 0 24 24" aria-hidden="true">
128
- <path fill="currentColor" d="M12 2C6.48 2 2 6.58 2 12.26c0 4.5 2.87 8.32 6.84 9.67.5.1.68-.22.68-.49 0-.24-.01-.88-.01-1.73-2.78.62-3.37-1.38-3.37-1.38-.45-1.19-1.11-1.5-1.11-1.5-.91-.64.07-.63.07-.63 1 .07 1.53 1.06 1.53 1.06.9 1.57 2.36 1.12 2.94.86.09-.66.35-1.12.63-1.38-2.22-.26-4.56-1.14-4.56-5.09 0-1.13.39-2.06 1.03-2.79-.1-.26-.45-1.31.1-2.73 0 0 .84-.28 2.75 1.06A9.36 9.36 0 0 1 12 6.8c.85 0 1.7.12 2.5.34 1.9-1.34 2.74-1.06 2.74-1.06.55 1.42.2 2.47.1 2.73.64.73 1.03 1.66 1.03 2.79 0 3.96-2.35 4.83-4.58 5.08.36.32.68.95.68 1.92 0 1.38-.01 2.5-.01 2.84 0 .27.18.59.69.49A10.04 10.04 0 0 0 22 12.26C22 6.58 17.52 2 12 2z"/>
129
- </svg>
130
- <span class="github-badge-label">GitHub</span>
131
- </span>
132
- <span class="github-badge-text" title="SakuraByteCore/codexmate">
133
- <span class="github-owner">SakuraByteCore</span>
134
- <span class="github-sep">/</span>
135
- <span class="github-repo">codexmate</span>
136
- </span>
137
- </a>
135
+ <div class="brand-subtitle">Local config & sessions workspace</div>
138
136
  </div>
139
137
 
140
- <div class="side-section" role="navigation" aria-label="配置管理">
141
- <div class="side-section-title">配置管理</div>
138
+ <div class="side-rail-nav">
139
+ <div class="side-section" role="navigation" :aria-label="t('side.docs')">
140
+ <div class="side-section-title">{{ t('side.docs') }}</div>
141
+ <button
142
+ id="side-tab-docs"
143
+ data-main-tab="docs"
144
+ :aria-current="mainTab === 'docs' ? 'page' : null"
145
+ :class="['side-item', { active: isMainTabNavActive('docs') }]"
146
+ @pointerdown="onMainTabPointerDown('docs', $event)"
147
+ @click="onMainTabClick('docs', $event)">
148
+ <div class="side-item-title">{{ t('side.docs.cliInstall') }}</div>
149
+ <div class="side-item-meta">
150
+ <span>{{ t('side.docs.cliInstall.meta') }}</span>
151
+ <span>{{ String(installPackageManager || 'npm').toUpperCase() }} · {{ installCommandAction === 'update' ? t('common.update') : (installCommandAction === 'uninstall' ? t('common.uninstall') : t('common.install')) }}</span>
152
+ </div>
153
+ </button>
154
+ </div>
155
+ <div class="side-section" role="navigation" :aria-label="t('side.config')">
156
+ <div class="side-section-title">{{ t('side.config') }}</div>
142
157
  <button
143
158
  id="side-tab-config-codex"
144
159
  data-main-tab="config"
@@ -147,10 +162,10 @@
147
162
  :class="['side-item', { active: isConfigModeNavActive('codex') }]"
148
163
  @pointerdown="onConfigTabPointerDown('codex', $event)"
149
164
  @click="onConfigTabClick('codex', $event)">
150
- <div class="side-item-title">Codex 配置</div>
165
+ <div class="side-item-title">{{ t('side.config.codex') }}</div>
151
166
  <div class="side-item-meta">
152
- <span>提供商 / 模型</span>
153
- <span v-if="currentProvider">当前 {{ currentProvider }}</span>
167
+ <span>{{ t('side.config.codex.meta') }}</span>
168
+ <span v-if="currentProvider">{{ t('common.current', { value: currentProvider }) }}</span>
154
169
  </div>
155
170
  </button>
156
171
  <button
@@ -161,10 +176,10 @@
161
176
  :class="['side-item', { active: isConfigModeNavActive('claude') }]"
162
177
  @pointerdown="onConfigTabPointerDown('claude', $event)"
163
178
  @click="onConfigTabClick('claude', $event)">
164
- <div class="side-item-title">Claude Code 配置</div>
179
+ <div class="side-item-title">{{ t('side.config.claude') }}</div>
165
180
  <div class="side-item-meta">
166
- <span>Base URL / Key</span>
167
- <span v-if="currentClaudeConfig">当前 {{ currentClaudeConfig }}</span>
181
+ <span>{{ t('side.config.claude.meta') }}</span>
182
+ <span v-if="currentClaudeConfig">{{ t('common.current', { value: currentClaudeConfig }) }}</span>
168
183
  </div>
169
184
  </button>
170
185
  <button
@@ -175,16 +190,16 @@
175
190
  :class="['side-item', { active: isConfigModeNavActive('openclaw') }]"
176
191
  @pointerdown="onConfigTabPointerDown('openclaw', $event)"
177
192
  @click="onConfigTabClick('openclaw', $event)">
178
- <div class="side-item-title">OpenClaw 配置</div>
193
+ <div class="side-item-title">{{ t('side.config.openclaw') }}</div>
179
194
  <div class="side-item-meta">
180
- <span>JSON5 / Workspace</span>
181
- <span v-if="currentOpenclawConfig">当前 {{ currentOpenclawConfig }}</span>
195
+ <span>{{ t('side.config.openclaw.meta') }}</span>
196
+ <span v-if="currentOpenclawConfig">{{ t('common.current', { value: currentOpenclawConfig }) }}</span>
182
197
  </div>
183
198
  </button>
184
199
  </div>
185
200
 
186
- <div class="side-section" role="navigation" aria-label="会话管理">
187
- <div class="side-section-title">会话管理</div>
201
+ <div class="side-section" role="navigation" :aria-label="t('side.sessions')">
202
+ <div class="side-section-title">{{ t('side.sessions') }}</div>
188
203
  <button
189
204
  id="side-tab-sessions"
190
205
  data-main-tab="sessions"
@@ -192,16 +207,46 @@
192
207
  :class="['side-item', { active: isMainTabNavActive('sessions') }]"
193
208
  @pointerdown="onMainTabPointerDown('sessions', $event)"
194
209
  @click="onMainTabClick('sessions', $event)">
195
- <div class="side-item-title">会话浏览</div>
210
+ <div class="side-item-title">{{ t('side.sessions.browser') }}</div>
196
211
  <div class="side-item-meta">
197
- <span>快速预览 / 导出</span>
198
- <span>来源:{{ sessionFilterSource === 'all' ? '全部' : (sessionFilterSource === 'codex' ? 'Codex' : 'Claude') }}</span>
212
+ <span>{{ t('side.sessions.browser.meta') }}</span>
213
+ <span>{{ t('sessions.sourceLabel', { value: (sessionFilterSource === 'all' ? t('sessions.source.all') : (sessionFilterSource === 'codex' ? 'Codex' : 'Claude')) }) }}</span>
214
+ </div>
215
+ </button>
216
+ <button
217
+ id="side-tab-usage"
218
+ data-main-tab="usage"
219
+ :aria-current="mainTab === 'usage' ? 'page' : null"
220
+ :class="['side-item', { active: isMainTabNavActive('usage') }]"
221
+ @pointerdown="onMainTabPointerDown('usage', $event)"
222
+ @click="onMainTabClick('usage', $event)">
223
+ <div class="side-item-title">{{ t('tab.usage') }}</div>
224
+ <div class="side-item-meta">
225
+ <span>{{ t('side.usage.meta') }}</span>
226
+ <span>{{ t('usage.rangeLabel', { value: (sessionsUsageTimeRange === 'all' ? t('usage.range.all') : (sessionsUsageTimeRange === '30d' ? t('usage.range.30d.short') : t('usage.range.7d.short'))) }) }}</span>
199
227
  </div>
200
228
  </button>
201
229
  </div>
202
230
 
203
- <div class="side-section" role="navigation" aria-label="技能市场">
204
- <div class="side-section-title">技能市场</div>
231
+ <div v-if="taskOrchestrationTabEnabled" class="side-section" role="navigation" :aria-label="t('side.orchestration')">
232
+ <div class="side-section-title">{{ t('side.orchestration') }}</div>
233
+ <button
234
+ id="side-tab-orchestration"
235
+ data-main-tab="orchestration"
236
+ :aria-current="mainTab === 'orchestration' ? 'page' : null"
237
+ :class="['side-item', { active: isMainTabNavActive('orchestration') }]"
238
+ @pointerdown="onMainTabPointerDown('orchestration', $event)"
239
+ @click="onMainTabClick('orchestration', $event)">
240
+ <div class="side-item-title">{{ t('tab.orchestration') }}</div>
241
+ <div class="side-item-meta">
242
+ <span>{{ t('side.orchestration.meta') }}</span>
243
+ <span>{{ t('orchestration.queueStats', { running: taskOrchestrationQueueStats.running, queued: taskOrchestrationQueueStats.queued }) }}</span>
244
+ </div>
245
+ </button>
246
+ </div>
247
+
248
+ <div class="side-section" role="navigation" :aria-label="t('side.skills')">
249
+ <div class="side-section-title">{{ t('side.skills') }}</div>
205
250
  <button
206
251
  id="side-tab-market"
207
252
  data-main-tab="market"
@@ -209,16 +254,33 @@
209
254
  :class="['side-item', { active: isMainTabNavActive('market') }]"
210
255
  @pointerdown="onMainTabPointerDown('market', $event)"
211
256
  @click="onMainTabClick('market', $event)">
212
- <div class="side-item-title">市场</div>
257
+ <div class="side-item-title">{{ t('tab.market') }}</div>
213
258
  <div class="side-item-meta">
214
- <span>{{ skillsTargetLabel }} / 本地 Skills</span>
215
- <span>已装 {{ skillsList.length }} · 可导入 {{ skillsImportList.length }}</span>
259
+ <span>{{ t('skills.localLabel', { target: skillsTargetLabel }) }}</span>
260
+ <span>{{ t('skills.counts', { installed: skillsList.length, importable: skillsImportList.length }) }}</span>
216
261
  </div>
217
262
  </button>
218
263
  </div>
219
264
 
220
- <div class="side-section" role="navigation" aria-label="设置">
221
- <div class="side-section-title">设置</div>
265
+ <div class="side-section" role="navigation" :aria-label="t('side.plugins')">
266
+ <div class="side-section-title">{{ t('side.plugins') }}</div>
267
+ <button
268
+ id="side-tab-plugins"
269
+ data-main-tab="plugins"
270
+ :aria-current="mainTab === 'plugins' ? 'page' : null"
271
+ :class="['side-item', { active: isMainTabNavActive('plugins') }]"
272
+ @pointerdown="onMainTabPointerDown('plugins', $event)"
273
+ @click="onMainTabClick('plugins', $event)">
274
+ <div class="side-item-title">{{ t('side.plugins.tools') }}</div>
275
+ <div class="side-item-meta">
276
+ <span>{{ t('side.plugins.tools.meta') }}</span>
277
+ <span>{{ promptTemplatesList.length }} templates</span>
278
+ </div>
279
+ </button>
280
+ </div>
281
+
282
+ <div class="side-section" role="navigation" :aria-label="t('side.system')">
283
+ <div class="side-section-title">{{ t('side.system') }}</div>
222
284
  <button
223
285
  id="side-tab-settings"
224
286
  data-main-tab="settings"
@@ -226,112 +288,174 @@
226
288
  :class="['side-item', { active: isMainTabNavActive('settings') }]"
227
289
  @pointerdown="onMainTabPointerDown('settings', $event)"
228
290
  @click="onMainTabClick('settings', $event)">
229
- <div class="side-item-title">设置</div>
291
+ <div class="side-item-title">{{ t('side.system.settings') }}</div>
230
292
  <div class="side-item-meta">
231
- <span>数据管理 / 下载</span>
293
+ <span>{{ t('side.system.settings.meta') }}</span>
232
294
  </div>
233
295
  </button>
234
296
  </div>
297
+ </div>
298
+
299
+ <div class="side-rail-lang" role="group" :aria-label="t('lang.label')">
300
+ <div class="lang-choice" role="group" :aria-label="t('lang.label')">
301
+ <button
302
+ type="button"
303
+ class="lang-choice-btn"
304
+ :aria-pressed="(lang || 'zh') === 'zh'"
305
+ :class="{ active: (lang || 'zh') === 'zh' }"
306
+ @click="setLang('zh')">ZH</button>
307
+ <button
308
+ type="button"
309
+ class="lang-choice-btn"
310
+ :aria-pressed="(lang || 'zh') === 'en'"
311
+ :class="{ active: (lang || 'zh') === 'en' }"
312
+ @click="setLang('en')">EN</button>
313
+ </div>
314
+ </div>
235
315
  </aside>
236
316
  <main class="main-panel">
237
- <div class="panel-header" v-if="!sessionStandalone">
238
- <h1 class="main-title">
239
- {{ mainTab === 'config' ? '配置中心' : (mainTab === 'sessions' ? '会话浏览' : (mainTab === 'market' ? '技能市场' : '设置')) }}
240
- </h1>
241
- <p class="subtitle" v-if="mainTab === 'config'">
242
- 配置中枢:管理 Codex / Claude / OpenClaw
243
- <span class="sr-only">本地配置中枢,统一管理 Codex / Claude Code / OpenClaw。</span>
244
- </p>
245
- <p class="subtitle" v-else-if="mainTab === 'sessions'">
246
- 统一查看与导出 Codex / Claude 会话。
247
- </p>
248
- <p class="subtitle" v-else-if="mainTab === 'market'">
249
- 统一管理 Codex / Claude Skills,并聚焦本地导入与分发。
250
- </p>
251
- </div>
317
+ <div class="main-panel-topbar">
318
+ <div class="panel-header panel-header-refined" v-if="!sessionStandalone">
319
+ <div class="panel-header-copy">
320
+ <div class="panel-kicker">{{ mainTabKicker }}</div>
321
+ <h1 class="main-title">{{ mainTabTitle }}</h1>
322
+ <p class="subtitle">{{ mainTabSubtitle }}</p>
323
+ </div>
324
+ </div>
252
325
 
253
- <div class="status-strip" v-if="!sessionStandalone && mainTab === 'config'">
254
- <template v-if="isProviderConfigMode">
326
+ <div class="status-strip" v-if="!sessionStandalone && mainTab === 'config'">
327
+ <template v-if="isProviderConfigMode">
328
+ <div class="status-chip">
329
+ <span class="label">{{ activeProviderConfigChipLabel }}</span>
330
+ <span class="value">{{ currentProvider || t('common.notSelected') }}</span>
331
+ </div>
332
+ <div class="status-chip">
333
+ <span class="label">{{ activeProviderModelChipLabel }}</span>
334
+ <span class="value">{{ currentModel || t('common.notSelected') }}</span>
335
+ </div>
336
+ </template>
337
+ <template v-else-if="configMode === 'claude'">
338
+ <div class="status-chip">
339
+ <span class="label">{{ t('status.claudeConfig') }}</span>
340
+ <span class="value">{{ currentClaudeConfig || t('common.notSelected') }}</span>
341
+ </div>
342
+ <div class="status-chip">
343
+ <span class="label">{{ t('status.claudeModel') }}</span>
344
+ <span class="value">{{ currentClaudeModel || t('common.notSelected') }}</span>
345
+ </div>
346
+ </template>
347
+ <template v-else-if="configMode === 'openclaw'">
348
+ <div class="status-chip">
349
+ <span class="label">{{ t('status.openclawConfig') }}</span>
350
+ <span class="value">{{ currentOpenclawConfig || t('common.notSelected') }}</span>
351
+ </div>
352
+ <div class="status-chip">
353
+ <span class="label">{{ t('status.workspaceFile') }}</span>
354
+ <span class="value">{{ openclawWorkspaceFileName || t('common.notSelected') }}</span>
355
+ </div>
356
+ </template>
357
+ <template v-else>
358
+ <div class="status-chip">
359
+ <span class="label">{{ t('status.configMode') }}</span>
360
+ <span class="value">{{ t('common.notSelected') }}</span>
361
+ </div>
362
+ </template>
363
+ </div>
364
+ <div class="status-strip" v-else-if="!sessionStandalone && mainTab === 'sessions'">
255
365
  <div class="status-chip">
256
- <span class="label">{{ activeProviderConfigChipLabel }}</span>
257
- <span class="value">{{ currentProvider || '未选择' }}</span>
366
+ <span class="label">{{ t('status.currentSource') }}</span>
367
+ <span class="value">
368
+ {{ sessionFilterSource === 'all' ? t('sessions.source.all') : (sessionFilterSource === 'claude' ? 'Claude Code' : 'Codex') }}
369
+ </span>
258
370
  </div>
259
371
  <div class="status-chip">
260
- <span class="label">{{ activeProviderModelChipLabel }}</span>
261
- <span class="value">{{ currentModel || '未选择' }}</span>
372
+ <span class="label">{{ t('status.sessionCount') }}</span>
373
+ <span class="value">{{ sessionsList.length }}</span>
262
374
  </div>
263
- </template>
264
- <template v-else-if="configMode === 'claude'">
375
+ </div>
376
+ <div class="status-strip" v-else-if="!sessionStandalone && mainTab === 'usage'">
265
377
  <div class="status-chip">
266
- <span class="label">Claude 配置</span>
267
- <span class="value">{{ currentClaudeConfig || '未选择' }}</span>
378
+ <span class="label">{{ t('status.range') }}</span>
379
+ <span class="value">{{ sessionsUsageTimeRange === 'all' ? t('usage.range.all') : (sessionsUsageTimeRange === '30d' ? t('usage.range.30d.short') : t('usage.range.7d.short')) }}</span>
268
380
  </div>
269
381
  <div class="status-chip">
270
- <span class="label">Claude 模型</span>
271
- <span class="value">{{ currentClaudeModel || '未选择' }}</span>
382
+ <span class="label">{{ t('status.totalSessions') }}</span>
383
+ <span class="value">{{ sessionUsageSummaryCards[0] ? sessionUsageSummaryCards[0].value : 0 }}</span>
272
384
  </div>
273
- </template>
274
- <template v-else-if="configMode === 'openclaw'">
275
385
  <div class="status-chip">
276
- <span class="label">OpenClaw 配置</span>
277
- <span class="value">{{ currentOpenclawConfig || '未选择' }}</span>
386
+ <span class="label">{{ t('status.totalMessages') }}</span>
387
+ <span class="value">{{ sessionUsageSummaryCards[1] ? sessionUsageSummaryCards[1].value : 0 }}</span>
278
388
  </div>
389
+ </div>
390
+ <div class="status-strip" v-else-if="!sessionStandalone && taskOrchestrationTabEnabled && mainTab === 'orchestration'">
279
391
  <div class="status-chip">
280
- <span class="label">工作区文件</span>
281
- <span class="value">{{ openclawWorkspaceFileName || '未选择' }}</span>
392
+ <span class="label">{{ t('status.engine') }}</span>
393
+ <span class="value">{{ taskOrchestration.selectedEngine === 'workflow' ? 'Workflow' : 'Codex' }}</span>
282
394
  </div>
283
- </template>
284
- <template v-else>
285
395
  <div class="status-chip">
286
- <span class="label">配置模式</span>
287
- <span class="value">未选择</span>
396
+ <span class="label">{{ t('status.concurrency') }}</span>
397
+ <span class="value">{{ taskOrchestration.concurrency }}</span>
398
+ </div>
399
+ <div class="status-chip">
400
+ <span class="label">{{ t('status.running') }}</span>
401
+ <span class="value">{{ taskOrchestrationQueueStats.running }}</span>
402
+ </div>
403
+ <div class="status-chip">
404
+ <span class="label">{{ t('status.queued') }}</span>
405
+ <span class="value">{{ taskOrchestrationQueueStats.queued }}</span>
406
+ </div>
407
+ <div class="status-chip">
408
+ <span class="label">{{ t('status.runs') }}</span>
409
+ <span class="value">{{ taskOrchestration.runs.length }}</span>
288
410
  </div>
289
- </template>
290
- </div>
291
- <div class="status-strip" v-else-if="!sessionStandalone && mainTab === 'sessions'">
292
- <div class="status-chip">
293
- <span class="label">当前来源</span>
294
- <span class="value">
295
- {{ sessionFilterSource === 'all' ? '全部' : (sessionFilterSource === 'claude' ? 'Claude Code' : 'Codex') }}
296
- </span>
297
- </div>
298
- <div class="status-chip">
299
- <span class="label">会话数</span>
300
- <span class="value">{{ sessionsList.length }}</span>
301
- </div>
302
- </div>
303
- <div class="status-strip" v-else-if="!sessionStandalone && mainTab === 'market'">
304
- <div class="status-chip">
305
- <span class="label">当前目标</span>
306
- <span class="value">{{ skillsTargetLabel }}</span>
307
411
  </div>
308
- <div class="status-chip">
309
- <span class="label">本地 Skills</span>
310
- <span class="value">{{ skillsList.length }}</span>
412
+ <div class="status-strip" v-else-if="!sessionStandalone && mainTab === 'market'">
413
+ <div class="status-chip">
414
+ <span class="label">{{ t('status.currentTarget') }}</span>
415
+ <span class="value">{{ skillsTargetLabel }}</span>
416
+ </div>
417
+ <div class="status-chip">
418
+ <span class="label">{{ t('status.localSkills') }}</span>
419
+ <span class="value">{{ skillsList.length }}</span>
420
+ </div>
421
+ <div class="status-chip">
422
+ <span class="label">{{ t('status.importable') }}</span>
423
+ <span class="value">{{ skillsImportList.length }}</span>
424
+ </div>
425
+ <div class="status-chip">
426
+ <span class="label">{{ t('status.importableDirect') }}</span>
427
+ <span class="value">{{ skillsImportConfiguredCount }}</span>
428
+ </div>
311
429
  </div>
312
- <div class="status-chip">
313
- <span class="label">可导入</span>
314
- <span class="value">{{ skillsImportList.length }}</span>
430
+ <div class="status-strip" v-else-if="!sessionStandalone && mainTab === 'docs'">
431
+ <div class="status-chip">
432
+ <span class="label">{{ t('status.pm') }}</span>
433
+ <span class="value">{{ String(installPackageManager || 'npm').toUpperCase() }}</span>
434
+ </div>
435
+ <div class="status-chip">
436
+ <span class="label">{{ t('status.action') }}</span>
437
+ <span class="value">{{ installCommandAction === 'update' ? t('common.update') : (installCommandAction === 'uninstall' ? t('common.uninstall') : t('common.install')) }}</span>
438
+ </div>
439
+ <div class="status-chip">
440
+ <span class="label">{{ t('status.registry') }}</span>
441
+ <span class="value">{{ installRegistryPreview || t('common.defaultOfficial') }}</span>
442
+ </div>
315
443
  </div>
316
- <div class="status-chip">
317
- <span class="label">可直接导入</span>
318
- <span class="value">{{ skillsImportConfiguredCount }}</span>
444
+ <div
445
+ v-if="!sessionStandalone && mainTab === 'config' && isProviderConfigMode && forceCompactLayout && !loading && !initError && displayProvidersList.length > 1"
446
+ class="provider-fast-switch">
447
+ <label class="provider-fast-switch-label" for="provider-fast-switch-select">{{ t('status.quickSwitchProvider') }}</label>
448
+ <select
449
+ id="provider-fast-switch-select"
450
+ class="provider-fast-switch-select"
451
+ :value="displayCurrentProvider"
452
+ @change="quickSwitchProvider($event.target.value)">
453
+ <option v-for="provider in displayProvidersList" :key="'quick-switch-' + provider.name" :value="provider.name">
454
+ {{ provider.name }}
455
+ </option>
456
+ </select>
319
457
  </div>
320
458
  </div>
321
- <div
322
- v-if="!sessionStandalone && mainTab === 'config' && isProviderConfigMode && forceCompactLayout && !loading && !initError && displayProvidersList.length > 1"
323
- class="provider-fast-switch">
324
- <label class="provider-fast-switch-label" for="provider-fast-switch-select">快速切换提供商</label>
325
- <select
326
- id="provider-fast-switch-select"
327
- class="provider-fast-switch-select"
328
- :value="displayCurrentProvider"
329
- @change="quickSwitchProvider($event.target.value)">
330
- <option v-for="provider in displayProvidersList" :key="'quick-switch-' + provider.name" :value="provider.name">
331
- {{ provider.name }}
332
- </option>
333
- </select>
334
- </div>
335
459
 
336
460
  <!-- 内容包裹器 - 稳定布局 -->
337
461
  <div class="content-wrapper">