codexmate 0.0.20 → 0.0.21

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 (102) hide show
  1. package/README.en.md +349 -259
  2. package/README.md +284 -252
  3. package/cli/agents-files.js +162 -0
  4. package/cli/archive-helpers.js +446 -0
  5. package/cli/auth-profiles.js +359 -0
  6. package/cli/builtin-proxy.js +580 -0
  7. package/cli/claude-proxy.js +998 -0
  8. package/cli/config-bootstrap.js +384 -0
  9. package/cli/config-health.js +338 -338
  10. package/cli/openclaw-config.js +629 -0
  11. package/cli/skills.js +1141 -0
  12. package/cli/zip-commands.js +510 -0
  13. package/cli.js +13101 -13497
  14. package/lib/cli-file-utils.js +151 -151
  15. package/lib/cli-models-utils.js +419 -311
  16. package/lib/cli-network-utils.js +164 -164
  17. package/lib/cli-path-utils.js +69 -0
  18. package/lib/cli-session-utils.js +121 -121
  19. package/lib/cli-sessions.js +386 -0
  20. package/lib/cli-utils.js +155 -155
  21. package/lib/download-artifacts.js +77 -0
  22. package/lib/mcp-stdio.js +440 -440
  23. package/lib/task-orchestrator.js +869 -0
  24. package/lib/text-diff.js +303 -303
  25. package/lib/workflow-engine.js +340 -340
  26. package/package.json +74 -70
  27. package/res/json5.min.js +1 -1
  28. package/res/vue.global.prod.js +13 -0
  29. package/web-ui/app.js +530 -397
  30. package/web-ui/index.html +33 -30
  31. package/web-ui/logic.agents-diff.mjs +386 -386
  32. package/web-ui/logic.claude.mjs +168 -108
  33. package/web-ui/logic.mjs +5 -5
  34. package/web-ui/logic.runtime.mjs +124 -124
  35. package/web-ui/logic.sessions.mjs +581 -263
  36. package/web-ui/modules/api.mjs +90 -69
  37. package/web-ui/modules/app.computed.dashboard.mjs +113 -113
  38. package/web-ui/modules/app.computed.index.mjs +15 -13
  39. package/web-ui/modules/app.computed.main-tabs.mjs +195 -0
  40. package/web-ui/modules/app.computed.session.mjs +507 -141
  41. package/web-ui/modules/app.constants.mjs +15 -15
  42. package/web-ui/modules/app.methods.agents.mjs +493 -493
  43. package/web-ui/modules/app.methods.claude-config.mjs +174 -174
  44. package/web-ui/modules/app.methods.codex-config.mjs +640 -640
  45. package/web-ui/modules/app.methods.index.mjs +88 -86
  46. package/web-ui/modules/app.methods.install.mjs +149 -157
  47. package/web-ui/modules/app.methods.navigation.mjs +619 -478
  48. package/web-ui/modules/app.methods.openclaw-core.mjs +814 -514
  49. package/web-ui/modules/app.methods.openclaw-editing.mjs +372 -337
  50. package/web-ui/modules/app.methods.openclaw-persist.mjs +369 -251
  51. package/web-ui/modules/app.methods.providers.mjs +363 -265
  52. package/web-ui/modules/app.methods.runtime.mjs +323 -323
  53. package/web-ui/modules/app.methods.session-actions.mjs +520 -457
  54. package/web-ui/modules/app.methods.session-browser.mjs +626 -435
  55. package/web-ui/modules/app.methods.session-timeline.mjs +448 -441
  56. package/web-ui/modules/app.methods.session-trash.mjs +422 -419
  57. package/web-ui/modules/app.methods.startup-claude.mjs +412 -406
  58. package/web-ui/modules/app.methods.task-orchestration.mjs +471 -0
  59. package/web-ui/modules/config-mode.computed.mjs +126 -124
  60. package/web-ui/modules/skills.computed.mjs +107 -107
  61. package/web-ui/modules/skills.methods.mjs +481 -481
  62. package/web-ui/partials/index/layout-footer.html +13 -69
  63. package/web-ui/partials/index/layout-header.html +402 -337
  64. package/web-ui/partials/index/modal-config-template-agents.html +125 -125
  65. package/web-ui/partials/index/modal-confirm-toast.html +32 -32
  66. package/web-ui/partials/index/modal-health-check.html +72 -72
  67. package/web-ui/partials/index/modal-openclaw-config.html +280 -275
  68. package/web-ui/partials/index/modal-skills.html +184 -184
  69. package/web-ui/partials/index/modals-basic.html +156 -196
  70. package/web-ui/partials/index/panel-config-claude.html +126 -100
  71. package/web-ui/partials/index/panel-config-codex.html +237 -237
  72. package/web-ui/partials/index/panel-config-openclaw.html +78 -84
  73. package/web-ui/partials/index/panel-docs.html +130 -0
  74. package/web-ui/partials/index/panel-market.html +174 -174
  75. package/web-ui/partials/index/panel-orchestration.html +397 -0
  76. package/web-ui/partials/index/panel-sessions.html +292 -387
  77. package/web-ui/partials/index/panel-settings.html +190 -166
  78. package/web-ui/partials/index/panel-usage.html +213 -0
  79. package/web-ui/session-helpers.mjs +559 -362
  80. package/web-ui/source-bundle.cjs +233 -233
  81. package/web-ui/styles/base-theme.css +271 -373
  82. package/web-ui/styles/controls-forms.css +360 -354
  83. package/web-ui/styles/docs-panel.css +182 -0
  84. package/web-ui/styles/feedback.css +108 -108
  85. package/web-ui/styles/health-check-dialog.css +144 -144
  86. package/web-ui/styles/layout-shell.css +376 -330
  87. package/web-ui/styles/modals-core.css +464 -449
  88. package/web-ui/styles/navigation-panels.css +348 -381
  89. package/web-ui/styles/openclaw-structured.css +266 -266
  90. package/web-ui/styles/responsive.css +450 -416
  91. package/web-ui/styles/sessions-list.css +400 -414
  92. package/web-ui/styles/sessions-preview.css +411 -405
  93. package/web-ui/styles/sessions-toolbar-trash.css +243 -243
  94. package/web-ui/styles/sessions-usage.css +628 -276
  95. package/web-ui/styles/skills-list.css +296 -298
  96. package/web-ui/styles/skills-market.css +335 -335
  97. package/web-ui/styles/task-orchestration.css +776 -0
  98. package/web-ui/styles/titles-cards.css +408 -407
  99. package/web-ui/styles.css +18 -16
  100. package/web-ui.html +17 -17
  101. package/res/screenshot.png +0 -0
  102. package/res/vue.global.js +0 -18552
@@ -1,337 +1,402 @@
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="主导航">
43
- <button class="top-tab"
44
- id="tab-config-codex"
45
- role="tab"
46
- data-main-tab="config"
47
- data-config-mode="codex"
48
- :tabindex="mainTab === 'config' && configMode === 'codex' ? 0 : -1"
49
- :aria-selected="mainTab === 'config' && configMode === 'codex'"
50
- aria-controls="panel-config-provider"
51
- :class="{ active: isConfigModeNavActive('codex') }"
52
- @pointerdown="onConfigTabPointerDown('codex', $event)"
53
- @click="onConfigTabClick('codex', $event)">Codex 配置</button>
54
- <button class="top-tab"
55
- id="tab-config-claude"
56
- role="tab"
57
- data-main-tab="config"
58
- data-config-mode="claude"
59
- :tabindex="mainTab === 'config' && configMode === 'claude' ? 0 : -1"
60
- :aria-selected="mainTab === 'config' && configMode === 'claude'"
61
- aria-controls="panel-config-claude"
62
- :class="{ active: isConfigModeNavActive('claude') }"
63
- @pointerdown="onConfigTabPointerDown('claude', $event)"
64
- @click="onConfigTabClick('claude', $event)">Claude Code 配置</button>
65
- <button class="top-tab"
66
- id="tab-config-openclaw"
67
- role="tab"
68
- data-main-tab="config"
69
- data-config-mode="openclaw"
70
- :tabindex="mainTab === 'config' && configMode === 'openclaw' ? 0 : -1"
71
- :aria-selected="mainTab === 'config' && configMode === 'openclaw'"
72
- aria-controls="panel-config-openclaw"
73
- :class="{ active: isConfigModeNavActive('openclaw') }"
74
- @pointerdown="onConfigTabPointerDown('openclaw', $event)"
75
- @click="onConfigTabClick('openclaw', $event)">OpenClaw 配置</button>
76
- <button class="top-tab"
77
- id="tab-sessions"
78
- role="tab"
79
- data-main-tab="sessions"
80
- :tabindex="mainTab === 'sessions' ? 0 : -1"
81
- :aria-selected="mainTab === 'sessions'"
82
- aria-controls="panel-sessions"
83
- :class="{ active: isMainTabNavActive('sessions') }"
84
- @pointerdown="onMainTabPointerDown('sessions', $event)"
85
- @click="onMainTabClick('sessions', $event)">会话浏览</button>
86
- <button class="top-tab"
87
- id="tab-market"
88
- role="tab"
89
- data-main-tab="market"
90
- :tabindex="mainTab === 'market' ? 0 : -1"
91
- :aria-selected="mainTab === 'market'"
92
- aria-controls="panel-market"
93
- :class="{ active: isMainTabNavActive('market') }"
94
- @pointerdown="onMainTabPointerDown('market', $event)"
95
- @click="onMainTabClick('market', $event)">技能市场</button>
96
- <button class="top-tab"
97
- id="tab-settings"
98
- role="tab"
99
- data-main-tab="settings"
100
- :tabindex="mainTab === 'settings' ? 0 : -1"
101
- :aria-selected="mainTab === 'settings'"
102
- aria-controls="panel-settings"
103
- :class="{ active: isMainTabNavActive('settings') }"
104
- @pointerdown="onMainTabPointerDown('settings', $event)"
105
- @click="onMainTabClick('settings', $event)">设置</button>
106
- </div>
107
-
108
- <div :class="['app-shell', { standalone: sessionStandalone }]">
109
- <aside class="side-rail" v-if="!sessionStandalone">
110
- <div class="brand-block">
111
- <div class="brand-logo-wrap">
112
- <img class="brand-logo" src="/res/logo.png" alt="Codex Mate logo">
113
- </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>
138
- </div>
139
-
140
- <div class="side-section" role="navigation" aria-label="配置管理">
141
- <div class="side-section-title">配置管理</div>
142
- <button
143
- id="side-tab-config-codex"
144
- data-main-tab="config"
145
- data-config-mode="codex"
146
- :aria-current="mainTab === 'config' && configMode === 'codex' ? 'page' : null"
147
- :class="['side-item', { active: isConfigModeNavActive('codex') }]"
148
- @pointerdown="onConfigTabPointerDown('codex', $event)"
149
- @click="onConfigTabClick('codex', $event)">
150
- <div class="side-item-title">Codex 配置</div>
151
- <div class="side-item-meta">
152
- <span>提供商 / 模型</span>
153
- <span v-if="currentProvider">当前 {{ currentProvider }}</span>
154
- </div>
155
- </button>
156
- <button
157
- id="side-tab-config-claude"
158
- data-main-tab="config"
159
- data-config-mode="claude"
160
- :aria-current="mainTab === 'config' && configMode === 'claude' ? 'page' : null"
161
- :class="['side-item', { active: isConfigModeNavActive('claude') }]"
162
- @pointerdown="onConfigTabPointerDown('claude', $event)"
163
- @click="onConfigTabClick('claude', $event)">
164
- <div class="side-item-title">Claude Code 配置</div>
165
- <div class="side-item-meta">
166
- <span>Base URL / Key</span>
167
- <span v-if="currentClaudeConfig">当前 {{ currentClaudeConfig }}</span>
168
- </div>
169
- </button>
170
- <button
171
- id="side-tab-config-openclaw"
172
- data-main-tab="config"
173
- data-config-mode="openclaw"
174
- :aria-current="mainTab === 'config' && configMode === 'openclaw' ? 'page' : null"
175
- :class="['side-item', { active: isConfigModeNavActive('openclaw') }]"
176
- @pointerdown="onConfigTabPointerDown('openclaw', $event)"
177
- @click="onConfigTabClick('openclaw', $event)">
178
- <div class="side-item-title">OpenClaw 配置</div>
179
- <div class="side-item-meta">
180
- <span>JSON5 / Workspace</span>
181
- <span v-if="currentOpenclawConfig">当前 {{ currentOpenclawConfig }}</span>
182
- </div>
183
- </button>
184
- </div>
185
-
186
- <div class="side-section" role="navigation" aria-label="会话管理">
187
- <div class="side-section-title">会话管理</div>
188
- <button
189
- id="side-tab-sessions"
190
- data-main-tab="sessions"
191
- :aria-current="mainTab === 'sessions' ? 'page' : null"
192
- :class="['side-item', { active: isMainTabNavActive('sessions') }]"
193
- @pointerdown="onMainTabPointerDown('sessions', $event)"
194
- @click="onMainTabClick('sessions', $event)">
195
- <div class="side-item-title">会话浏览</div>
196
- <div class="side-item-meta">
197
- <span>快速预览 / 导出</span>
198
- <span>来源:{{ sessionFilterSource === 'all' ? '全部' : (sessionFilterSource === 'codex' ? 'Codex' : 'Claude') }}</span>
199
- </div>
200
- </button>
201
- </div>
202
-
203
- <div class="side-section" role="navigation" aria-label="技能市场">
204
- <div class="side-section-title">技能市场</div>
205
- <button
206
- id="side-tab-market"
207
- data-main-tab="market"
208
- :aria-current="mainTab === 'market' ? 'page' : null"
209
- :class="['side-item', { active: isMainTabNavActive('market') }]"
210
- @pointerdown="onMainTabPointerDown('market', $event)"
211
- @click="onMainTabClick('market', $event)">
212
- <div class="side-item-title">市场</div>
213
- <div class="side-item-meta">
214
- <span>{{ skillsTargetLabel }} / 本地 Skills</span>
215
- <span>已装 {{ skillsList.length }} · 可导入 {{ skillsImportList.length }}</span>
216
- </div>
217
- </button>
218
- </div>
219
-
220
- <div class="side-section" role="navigation" aria-label="设置">
221
- <div class="side-section-title">设置</div>
222
- <button
223
- id="side-tab-settings"
224
- data-main-tab="settings"
225
- :aria-current="mainTab === 'settings' ? 'page' : null"
226
- :class="['side-item', { active: isMainTabNavActive('settings') }]"
227
- @pointerdown="onMainTabPointerDown('settings', $event)"
228
- @click="onMainTabClick('settings', $event)">
229
- <div class="side-item-title">设置</div>
230
- <div class="side-item-meta">
231
- <span>数据管理 / 下载</span>
232
- </div>
233
- </button>
234
- </div>
235
- </aside>
236
- <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>
252
-
253
- <div class="status-strip" v-if="!sessionStandalone && mainTab === 'config'">
254
- <template v-if="isProviderConfigMode">
255
- <div class="status-chip">
256
- <span class="label">{{ activeProviderConfigChipLabel }}</span>
257
- <span class="value">{{ currentProvider || '未选择' }}</span>
258
- </div>
259
- <div class="status-chip">
260
- <span class="label">{{ activeProviderModelChipLabel }}</span>
261
- <span class="value">{{ currentModel || '未选择' }}</span>
262
- </div>
263
- </template>
264
- <template v-else-if="configMode === 'claude'">
265
- <div class="status-chip">
266
- <span class="label">Claude 配置</span>
267
- <span class="value">{{ currentClaudeConfig || '未选择' }}</span>
268
- </div>
269
- <div class="status-chip">
270
- <span class="label">Claude 模型</span>
271
- <span class="value">{{ currentClaudeModel || '未选择' }}</span>
272
- </div>
273
- </template>
274
- <template v-else-if="configMode === 'openclaw'">
275
- <div class="status-chip">
276
- <span class="label">OpenClaw 配置</span>
277
- <span class="value">{{ currentOpenclawConfig || '未选择' }}</span>
278
- </div>
279
- <div class="status-chip">
280
- <span class="label">工作区文件</span>
281
- <span class="value">{{ openclawWorkspaceFileName || '未选择' }}</span>
282
- </div>
283
- </template>
284
- <template v-else>
285
- <div class="status-chip">
286
- <span class="label">配置模式</span>
287
- <span class="value">未选择</span>
288
- </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
- </div>
308
- <div class="status-chip">
309
- <span class="label">本地 Skills</span>
310
- <span class="value">{{ skillsList.length }}</span>
311
- </div>
312
- <div class="status-chip">
313
- <span class="label">可导入</span>
314
- <span class="value">{{ skillsImportList.length }}</span>
315
- </div>
316
- <div class="status-chip">
317
- <span class="label">可直接导入</span>
318
- <span class="value">{{ skillsImportConfiguredCount }}</span>
319
- </div>
320
- </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
-
336
- <!-- 内容包裹器 - 稳定布局 -->
337
- <div class="content-wrapper">
1
+ <div id="app" class="container" v-cloak>
2
+ <div v-if="!sessionStandalone" class="top-tabs" role="tablist" aria-label="主导航">
3
+ <button class="top-tab"
4
+ id="tab-config-codex"
5
+ role="tab"
6
+ data-main-tab="config"
7
+ data-config-mode="codex"
8
+ :tabindex="mainTab === 'config' && configMode === 'codex' ? 0 : -1"
9
+ :aria-selected="mainTab === 'config' && configMode === 'codex'"
10
+ aria-controls="panel-config-provider"
11
+ :class="{ active: isConfigModeNavActive('codex') }"
12
+ @pointerdown="onConfigTabPointerDown('codex', $event)"
13
+ @click="onConfigTabClick('codex', $event)">Codex</button>
14
+ <button class="top-tab"
15
+ id="tab-config-claude"
16
+ role="tab"
17
+ data-main-tab="config"
18
+ data-config-mode="claude"
19
+ :tabindex="mainTab === 'config' && configMode === 'claude' ? 0 : -1"
20
+ :aria-selected="mainTab === 'config' && configMode === 'claude'"
21
+ aria-controls="panel-config-claude"
22
+ :class="{ active: isConfigModeNavActive('claude') }"
23
+ @pointerdown="onConfigTabPointerDown('claude', $event)"
24
+ @click="onConfigTabClick('claude', $event)">Claude</button>
25
+ <button class="top-tab"
26
+ id="tab-config-openclaw"
27
+ role="tab"
28
+ data-main-tab="config"
29
+ data-config-mode="openclaw"
30
+ :tabindex="mainTab === 'config' && configMode === 'openclaw' ? 0 : -1"
31
+ :aria-selected="mainTab === 'config' && configMode === 'openclaw'"
32
+ aria-controls="panel-config-openclaw"
33
+ :class="{ active: isConfigModeNavActive('openclaw') }"
34
+ @pointerdown="onConfigTabPointerDown('openclaw', $event)"
35
+ @click="onConfigTabClick('openclaw', $event)">OpenClaw</button>
36
+ <button class="top-tab"
37
+ id="tab-sessions"
38
+ role="tab"
39
+ data-main-tab="sessions"
40
+ :tabindex="mainTab === 'sessions' ? 0 : -1"
41
+ :aria-selected="mainTab === 'sessions'"
42
+ aria-controls="panel-sessions"
43
+ :class="{ active: isMainTabNavActive('sessions') }"
44
+ @pointerdown="onMainTabPointerDown('sessions', $event)"
45
+ @click="onMainTabClick('sessions', $event)">会话</button>
46
+ <button class="top-tab"
47
+ id="tab-usage"
48
+ role="tab"
49
+ data-main-tab="usage"
50
+ :tabindex="mainTab === 'usage' ? 0 : -1"
51
+ :aria-selected="mainTab === 'usage'"
52
+ aria-controls="panel-usage"
53
+ :class="{ active: isMainTabNavActive('usage') }"
54
+ @pointerdown="onMainTabPointerDown('usage', $event)"
55
+ @click="onMainTabClick('usage', $event)">Usage</button>
56
+ <button v-if="taskOrchestrationTabEnabled" class="top-tab"
57
+ id="tab-orchestration"
58
+ role="tab"
59
+ data-main-tab="orchestration"
60
+ :tabindex="mainTab === 'orchestration' ? 0 : -1"
61
+ :aria-selected="mainTab === 'orchestration'"
62
+ aria-controls="panel-orchestration"
63
+ :class="{ active: isMainTabNavActive('orchestration') }"
64
+ @pointerdown="onMainTabPointerDown('orchestration', $event)"
65
+ @click="onMainTabClick('orchestration', $event)">任务编排</button>
66
+ <button class="top-tab"
67
+ id="tab-market"
68
+ role="tab"
69
+ data-main-tab="market"
70
+ :tabindex="mainTab === 'market' ? 0 : -1"
71
+ :aria-selected="mainTab === 'market'"
72
+ aria-controls="panel-market"
73
+ :class="{ active: isMainTabNavActive('market') }"
74
+ @pointerdown="onMainTabPointerDown('market', $event)"
75
+ @click="onMainTabClick('market', $event)">Skills</button>
76
+ <button class="top-tab"
77
+ id="tab-docs"
78
+ role="tab"
79
+ data-main-tab="docs"
80
+ :tabindex="mainTab === 'docs' ? 0 : -1"
81
+ :aria-selected="mainTab === 'docs'"
82
+ aria-controls="panel-docs"
83
+ :class="{ active: isMainTabNavActive('docs') }"
84
+ @pointerdown="onMainTabPointerDown('docs', $event)"
85
+ @click="onMainTabClick('docs', $event)">文档</button>
86
+ <button class="top-tab"
87
+ id="tab-settings"
88
+ role="tab"
89
+ data-main-tab="settings"
90
+ :tabindex="mainTab === 'settings' ? 0 : -1"
91
+ :aria-selected="mainTab === 'settings'"
92
+ aria-controls="panel-settings"
93
+ :class="{ active: isMainTabNavActive('settings') }"
94
+ @pointerdown="onMainTabPointerDown('settings', $event)"
95
+ @click="onMainTabClick('settings', $event)">设置</button>
96
+ </div>
97
+
98
+ <div :class="['app-shell', { standalone: sessionStandalone }]">
99
+ <aside class="side-rail" v-if="!sessionStandalone">
100
+ <div class="brand-block">
101
+ <div class="brand-head">
102
+ <img class="brand-logo" src="/res/logo.png" alt="Codex Mate logo">
103
+ <div class="brand-copy">
104
+ <div class="brand-kicker">Workspace</div>
105
+ <div class="brand-title">Codex Mate</div>
106
+ </div>
107
+ </div>
108
+ <div class="brand-subtitle">本地配置与会话工作台</div>
109
+ </div>
110
+
111
+ <div class="side-rail-nav">
112
+ <div class="side-section" role="navigation" aria-label="配置管理">
113
+ <div class="side-section-title">配置</div>
114
+ <button
115
+ id="side-tab-config-codex"
116
+ data-main-tab="config"
117
+ data-config-mode="codex"
118
+ :aria-current="mainTab === 'config' && configMode === 'codex' ? 'page' : null"
119
+ :class="['side-item', { active: isConfigModeNavActive('codex') }]"
120
+ @pointerdown="onConfigTabPointerDown('codex', $event)"
121
+ @click="onConfigTabClick('codex', $event)">
122
+ <div class="side-item-title">Codex</div>
123
+ <div class="side-item-meta">
124
+ <span>Provider / Model</span>
125
+ <span v-if="currentProvider">当前 {{ currentProvider }}</span>
126
+ </div>
127
+ </button>
128
+ <button
129
+ id="side-tab-config-claude"
130
+ data-main-tab="config"
131
+ data-config-mode="claude"
132
+ :aria-current="mainTab === 'config' && configMode === 'claude' ? 'page' : null"
133
+ :class="['side-item', { active: isConfigModeNavActive('claude') }]"
134
+ @pointerdown="onConfigTabPointerDown('claude', $event)"
135
+ @click="onConfigTabClick('claude', $event)">
136
+ <div class="side-item-title">Claude Code</div>
137
+ <div class="side-item-meta">
138
+ <span>Claude Settings</span>
139
+ <span v-if="currentClaudeConfig">当前 {{ currentClaudeConfig }}</span>
140
+ </div>
141
+ </button>
142
+ <button
143
+ id="side-tab-config-openclaw"
144
+ data-main-tab="config"
145
+ data-config-mode="openclaw"
146
+ :aria-current="mainTab === 'config' && configMode === 'openclaw' ? 'page' : null"
147
+ :class="['side-item', { active: isConfigModeNavActive('openclaw') }]"
148
+ @pointerdown="onConfigTabPointerDown('openclaw', $event)"
149
+ @click="onConfigTabClick('openclaw', $event)">
150
+ <div class="side-item-title">OpenClaw</div>
151
+ <div class="side-item-meta">
152
+ <span>JSON5 / AGENTS</span>
153
+ <span v-if="currentOpenclawConfig">当前 {{ currentOpenclawConfig }}</span>
154
+ </div>
155
+ </button>
156
+ </div>
157
+
158
+ <div class="side-section" role="navigation" aria-label="会话管理">
159
+ <div class="side-section-title">会话</div>
160
+ <button
161
+ id="side-tab-sessions"
162
+ data-main-tab="sessions"
163
+ :aria-current="mainTab === 'sessions' ? 'page' : null"
164
+ :class="['side-item', { active: isMainTabNavActive('sessions') }]"
165
+ @pointerdown="onMainTabPointerDown('sessions', $event)"
166
+ @click="onMainTabClick('sessions', $event)">
167
+ <div class="side-item-title">会话浏览</div>
168
+ <div class="side-item-meta">
169
+ <span>浏览 / 导出 / 清理</span>
170
+ <span>来源:{{ sessionFilterSource === 'all' ? '全部' : (sessionFilterSource === 'codex' ? 'Codex' : 'Claude') }}</span>
171
+ </div>
172
+ </button>
173
+ <button
174
+ id="side-tab-usage"
175
+ data-main-tab="usage"
176
+ :aria-current="mainTab === 'usage' ? 'page' : null"
177
+ :class="['side-item', { active: isMainTabNavActive('usage') }]"
178
+ @pointerdown="onMainTabPointerDown('usage', $event)"
179
+ @click="onMainTabClick('usage', $event)">
180
+ <div class="side-item-title">Usage 趋势</div>
181
+ <div class="side-item-meta">
182
+ <span>本地统计 / 趋势</span>
183
+ <span>范围:{{ sessionsUsageTimeRange === 'all' ? '全部' : (sessionsUsageTimeRange === '30d' ? '近 30 天' : '近 7 天') }}</span>
184
+ </div>
185
+ </button>
186
+ </div>
187
+
188
+ <div v-if="taskOrchestrationTabEnabled" class="side-section" role="navigation" aria-label="任务编排">
189
+ <div class="side-section-title">任务</div>
190
+ <button
191
+ id="side-tab-orchestration"
192
+ data-main-tab="orchestration"
193
+ :aria-current="mainTab === 'orchestration' ? 'page' : null"
194
+ :class="['side-item', { active: isMainTabNavActive('orchestration') }]"
195
+ @pointerdown="onMainTabPointerDown('orchestration', $event)"
196
+ @click="onMainTabClick('orchestration', $event)">
197
+ <div class="side-item-title">任务编排</div>
198
+ <div class="side-item-meta">
199
+ <span>计划 / 队列 / 运行记录</span>
200
+ <span>队列 {{ taskOrchestrationQueueStats.running }} 运行中 · {{ taskOrchestrationQueueStats.queued }} 等待中</span>
201
+ </div>
202
+ </button>
203
+ </div>
204
+
205
+ <div class="side-section" role="navigation" aria-label="技能市场">
206
+ <div class="side-section-title">扩展</div>
207
+ <button
208
+ id="side-tab-market"
209
+ data-main-tab="market"
210
+ :aria-current="mainTab === 'market' ? 'page' : null"
211
+ :class="['side-item', { active: isMainTabNavActive('market') }]"
212
+ @pointerdown="onMainTabPointerDown('market', $event)"
213
+ @click="onMainTabClick('market', $event)">
214
+ <div class="side-item-title">Skills 市场</div>
215
+ <div class="side-item-meta">
216
+ <span>{{ skillsTargetLabel }} / 本地 Skills</span>
217
+ <span>已装 {{ skillsList.length }} · 可导入 {{ skillsImportList.length }}</span>
218
+ </div>
219
+ </button>
220
+ </div>
221
+
222
+ <div class="side-section" role="navigation" aria-label="文档">
223
+ <div class="side-section-title">文档</div>
224
+ <button
225
+ id="side-tab-docs"
226
+ data-main-tab="docs"
227
+ :aria-current="mainTab === 'docs' ? 'page' : null"
228
+ :class="['side-item', { active: isMainTabNavActive('docs') }]"
229
+ @pointerdown="onMainTabPointerDown('docs', $event)"
230
+ @click="onMainTabClick('docs', $event)">
231
+ <div class="side-item-title">CLI 安装</div>
232
+ <div class="side-item-meta">
233
+ <span>安装 / 升级 / 卸载</span>
234
+ <span>{{ String(installPackageManager || 'npm').toUpperCase() }} · {{ installCommandAction === 'update' ? '升级' : (installCommandAction === 'uninstall' ? '卸载' : '安装') }}</span>
235
+ </div>
236
+ </button>
237
+ </div>
238
+
239
+ <div class="side-section" role="navigation" aria-label="设置">
240
+ <div class="side-section-title">系统</div>
241
+ <button
242
+ id="side-tab-settings"
243
+ data-main-tab="settings"
244
+ :aria-current="mainTab === 'settings' ? 'page' : null"
245
+ :class="['side-item', { active: isMainTabNavActive('settings') }]"
246
+ @pointerdown="onMainTabPointerDown('settings', $event)"
247
+ @click="onMainTabClick('settings', $event)">
248
+ <div class="side-item-title">运行设置</div>
249
+ <div class="side-item-meta">
250
+ <span>数据管理 / 下载</span>
251
+ </div>
252
+ </button>
253
+ </div>
254
+ </div>
255
+
256
+ </aside>
257
+ <main class="main-panel">
258
+ <div class="main-panel-topbar">
259
+ <div class="panel-header panel-header-refined" v-if="!sessionStandalone">
260
+ <div class="panel-header-copy">
261
+ <div class="panel-kicker">{{ mainTabKicker }}</div>
262
+ <h1 class="main-title">{{ mainTabTitle }}</h1>
263
+ <p class="subtitle">{{ mainTabSubtitle }}</p>
264
+ </div>
265
+ </div>
266
+
267
+ <div class="status-strip" v-if="!sessionStandalone && mainTab === 'config'">
268
+ <template v-if="isProviderConfigMode">
269
+ <div class="status-chip">
270
+ <span class="label">{{ activeProviderConfigChipLabel }}</span>
271
+ <span class="value">{{ currentProvider || '未选择' }}</span>
272
+ </div>
273
+ <div class="status-chip">
274
+ <span class="label">{{ activeProviderModelChipLabel }}</span>
275
+ <span class="value">{{ currentModel || '未选择' }}</span>
276
+ </div>
277
+ </template>
278
+ <template v-else-if="configMode === 'claude'">
279
+ <div class="status-chip">
280
+ <span class="label">Claude 配置</span>
281
+ <span class="value">{{ currentClaudeConfig || '未选择' }}</span>
282
+ </div>
283
+ <div class="status-chip">
284
+ <span class="label">Claude 模型</span>
285
+ <span class="value">{{ currentClaudeModel || '未选择' }}</span>
286
+ </div>
287
+ </template>
288
+ <template v-else-if="configMode === 'openclaw'">
289
+ <div class="status-chip">
290
+ <span class="label">OpenClaw 配置</span>
291
+ <span class="value">{{ currentOpenclawConfig || '未选择' }}</span>
292
+ </div>
293
+ <div class="status-chip">
294
+ <span class="label">工作区文件</span>
295
+ <span class="value">{{ openclawWorkspaceFileName || '未选择' }}</span>
296
+ </div>
297
+ </template>
298
+ <template v-else>
299
+ <div class="status-chip">
300
+ <span class="label">配置模式</span>
301
+ <span class="value">未选择</span>
302
+ </div>
303
+ </template>
304
+ </div>
305
+ <div class="status-strip" v-else-if="!sessionStandalone && mainTab === 'sessions'">
306
+ <div class="status-chip">
307
+ <span class="label">当前来源</span>
308
+ <span class="value">
309
+ {{ sessionFilterSource === 'all' ? '全部' : (sessionFilterSource === 'claude' ? 'Claude Code' : 'Codex') }}
310
+ </span>
311
+ </div>
312
+ <div class="status-chip">
313
+ <span class="label">会话数</span>
314
+ <span class="value">{{ sessionsList.length }}</span>
315
+ </div>
316
+ </div>
317
+ <div class="status-strip" v-else-if="!sessionStandalone && mainTab === 'usage'">
318
+ <div class="status-chip">
319
+ <span class="label">统计范围</span>
320
+ <span class="value">{{ sessionsUsageTimeRange === 'all' ? '全部' : (sessionsUsageTimeRange === '30d' ? '近 30 天' : '近 7 天') }}</span>
321
+ </div>
322
+ <div class="status-chip">
323
+ <span class="label">总会话数</span>
324
+ <span class="value">{{ sessionUsageSummaryCards[0] ? sessionUsageSummaryCards[0].value : 0 }}</span>
325
+ </div>
326
+ <div class="status-chip">
327
+ <span class="label">总消息数</span>
328
+ <span class="value">{{ sessionUsageSummaryCards[1] ? sessionUsageSummaryCards[1].value : 0 }}</span>
329
+ </div>
330
+ </div>
331
+ <div class="status-strip" v-else-if="!sessionStandalone && taskOrchestrationTabEnabled && mainTab === 'orchestration'">
332
+ <div class="status-chip">
333
+ <span class="label">引擎</span>
334
+ <span class="value">{{ taskOrchestration.selectedEngine === 'workflow' ? 'Workflow' : 'Codex' }}</span>
335
+ </div>
336
+ <div class="status-chip">
337
+ <span class="label">并发度</span>
338
+ <span class="value">{{ taskOrchestration.concurrency }}</span>
339
+ </div>
340
+ <div class="status-chip">
341
+ <span class="label">运行中</span>
342
+ <span class="value">{{ taskOrchestrationQueueStats.running }}</span>
343
+ </div>
344
+ <div class="status-chip">
345
+ <span class="label">排队中</span>
346
+ <span class="value">{{ taskOrchestrationQueueStats.queued }}</span>
347
+ </div>
348
+ <div class="status-chip">
349
+ <span class="label">历史 Runs</span>
350
+ <span class="value">{{ taskOrchestration.runs.length }}</span>
351
+ </div>
352
+ </div>
353
+ <div class="status-strip" v-else-if="!sessionStandalone && mainTab === 'market'">
354
+ <div class="status-chip">
355
+ <span class="label">当前目标</span>
356
+ <span class="value">{{ skillsTargetLabel }}</span>
357
+ </div>
358
+ <div class="status-chip">
359
+ <span class="label">本地 Skills</span>
360
+ <span class="value">{{ skillsList.length }}</span>
361
+ </div>
362
+ <div class="status-chip">
363
+ <span class="label">可导入</span>
364
+ <span class="value">{{ skillsImportList.length }}</span>
365
+ </div>
366
+ <div class="status-chip">
367
+ <span class="label">可直接导入</span>
368
+ <span class="value">{{ skillsImportConfiguredCount }}</span>
369
+ </div>
370
+ </div>
371
+ <div class="status-strip" v-else-if="!sessionStandalone && mainTab === 'docs'">
372
+ <div class="status-chip">
373
+ <span class="label">包管理器</span>
374
+ <span class="value">{{ String(installPackageManager || 'npm').toUpperCase() }}</span>
375
+ </div>
376
+ <div class="status-chip">
377
+ <span class="label">当前操作</span>
378
+ <span class="value">{{ installCommandAction === 'update' ? '升级' : (installCommandAction === 'uninstall' ? '卸载' : '安装') }}</span>
379
+ </div>
380
+ <div class="status-chip">
381
+ <span class="label">镜像</span>
382
+ <span class="value">{{ installRegistryPreview || '官方默认' }}</span>
383
+ </div>
384
+ </div>
385
+ <div
386
+ v-if="!sessionStandalone && mainTab === 'config' && isProviderConfigMode && forceCompactLayout && !loading && !initError && displayProvidersList.length > 1"
387
+ class="provider-fast-switch">
388
+ <label class="provider-fast-switch-label" for="provider-fast-switch-select">快速切换提供商</label>
389
+ <select
390
+ id="provider-fast-switch-select"
391
+ class="provider-fast-switch-select"
392
+ :value="displayCurrentProvider"
393
+ @change="quickSwitchProvider($event.target.value)">
394
+ <option v-for="provider in displayProvidersList" :key="'quick-switch-' + provider.name" :value="provider.name">
395
+ {{ provider.name }}
396
+ </option>
397
+ </select>
398
+ </div>
399
+ </div>
400
+
401
+ <!-- 内容包裹器 - 稳定布局 -->
402
+ <div class="content-wrapper">