codexmate 0.0.22 → 0.0.23

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.
@@ -8,23 +8,23 @@
8
8
  <section class="selector-section task-hero-card">
9
9
  <div class="task-hero-main">
10
10
  <div>
11
- <div class="task-hero-kicker">Task orchestration</div>
12
- <div class="selector-title">把需求拆成可执行步骤</div>
13
- <div class="skills-panel-note task-hero-copy">先写目标,再预览,再执行。</div>
11
+ <div class="task-hero-kicker">{{ t('orchestration.hero.kicker') }}</div>
12
+ <div class="selector-title">{{ t('orchestration.hero.title') }}</div>
13
+ <div class="skills-panel-note task-hero-copy">{{ t('orchestration.hero.subtitle') }}</div>
14
14
  </div>
15
15
  <div class="task-hero-actions settings-tab-actions task-header-actions">
16
16
  <button type="button" class="btn-tool btn-tool-compact" @click="loadTaskOrchestrationOverview({ forceRefresh: true, includeDetail: true })" :disabled="taskOrchestration.loading">
17
- {{ taskOrchestration.loading ? '刷新中...' : '刷新' }}
17
+ {{ taskOrchestration.loading ? t('common.refreshing') : t('common.refresh') }}
18
18
  </button>
19
19
  <button type="button" class="btn-tool btn-tool-compact" @click="resetTaskOrchestrationDraft()" :disabled="taskOrchestration.running || taskOrchestration.queueAdding || taskOrchestration.planning">
20
- 重置草稿
20
+ {{ t('orchestration.draft.reset') }}
21
21
  </button>
22
22
  </div>
23
23
  </div>
24
- <div v-if="taskOrchestrationQueueStats.running || taskOrchestrationQueueStats.queued || taskOrchestration.runs.length" class="task-hero-meta-strip" aria-label="任务编排概览">
25
- <div class="task-hero-meta">运行中 <strong>{{ taskOrchestrationQueueStats.running }}</strong></div>
26
- <div class="task-hero-meta">排队中 <strong>{{ taskOrchestrationQueueStats.queued }}</strong></div>
27
- <div class="task-hero-meta">运行记录 <strong>{{ taskOrchestration.runs.length }}</strong></div>
24
+ <div v-if="taskOrchestrationQueueStats.running || taskOrchestrationQueueStats.queued || taskOrchestration.runs.length" class="task-hero-meta-strip" :aria-label="t('orchestration.summary.aria')">
25
+ <div class="task-hero-meta">{{ t('orchestration.summary.running') }} <strong>{{ taskOrchestrationQueueStats.running }}</strong></div>
26
+ <div class="task-hero-meta">{{ t('orchestration.summary.queued') }} <strong>{{ taskOrchestrationQueueStats.queued }}</strong></div>
27
+ <div class="task-hero-meta">{{ t('orchestration.summary.runs') }} <strong>{{ taskOrchestration.runs.length }}</strong></div>
28
28
  </div>
29
29
  </section>
30
30
 
@@ -34,27 +34,27 @@
34
34
  <div class="task-flow-head">
35
35
  <div class="task-flow-step">1</div>
36
36
  <div>
37
- <div class="task-flow-title">先把结果写清楚</div>
38
- <div class="task-flow-copy">只写会影响执行的内容。</div>
37
+ <div class="task-flow-title">{{ t('orchestration.step1.title') }}</div>
38
+ <div class="task-flow-copy">{{ t('orchestration.step1.subtitle') }}</div>
39
39
  </div>
40
40
  </div>
41
41
 
42
42
  <details v-if="!taskOrchestration.target.trim()" class="task-template-panel">
43
- <summary class="task-advanced-summary">快捷示例</summary>
43
+ <summary class="task-advanced-summary">{{ t('orchestration.templates.title') }}</summary>
44
44
  <div class="task-template-block">
45
45
  <div class="task-template-chip-group">
46
- <button type="button" class="task-template-chip" @click="taskOrchestration.target = '修复当前 PR review 评论,并补对应回归测试'; taskOrchestration.selectedEngine = 'codex'; taskOrchestration.workflowIdsText = ''; taskOrchestration.notes = '不要改动无关模块;需要给出验证结果'; taskOrchestration.followUpsText = '继续处理新增 review 评论\n最后更新 PR 摘要'">修 review + 回归</button>
47
- <button type="button" class="task-template-chip" @click="taskOrchestration.target = '先排查问题根因并给出执行计划,不直接修改代码'; taskOrchestration.selectedEngine = 'codex'; taskOrchestration.workflowIdsText = ''; taskOrchestration.notes = '优先定位根因、影响范围和风险点'; taskOrchestration.followUpsText = ''">只做排查规划</button>
48
- <button type="button" class="task-template-chip" @click="taskOrchestration.target = '用 Workflow 跑一组固定检查并整理结果'; taskOrchestration.selectedEngine = 'workflow'; taskOrchestration.workflowIdsText = 'diagnose-config\nsafe-provider-switch'; taskOrchestration.notes = '输出统一结论,避免重复描述'; taskOrchestration.followUpsText = ''">Workflow 批处理</button>
46
+ <button type="button" class="task-template-chip" @click="taskOrchestration.target = t('orchestration.templates.reviewFix.target'); taskOrchestration.selectedEngine = 'codex'; taskOrchestration.workflowIdsText = ''; taskOrchestration.notes = t('orchestration.templates.reviewFix.notes'); taskOrchestration.followUpsText = t('orchestration.templates.reviewFix.followUps')">{{ t('orchestration.templates.reviewFix.label') }}</button>
47
+ <button type="button" class="task-template-chip" @click="taskOrchestration.target = t('orchestration.templates.planOnly.target'); taskOrchestration.selectedEngine = 'codex'; taskOrchestration.workflowIdsText = ''; taskOrchestration.notes = t('orchestration.templates.planOnly.notes'); taskOrchestration.followUpsText = ''">{{ t('orchestration.templates.planOnly.label') }}</button>
48
+ <button type="button" class="task-template-chip" @click="taskOrchestration.target = t('orchestration.templates.workflowBatch.target'); taskOrchestration.selectedEngine = 'workflow'; taskOrchestration.workflowIdsText = t('orchestration.templates.workflowBatch.workflowIds'); taskOrchestration.notes = t('orchestration.templates.workflowBatch.notes'); taskOrchestration.followUpsText = ''">{{ t('orchestration.templates.workflowBatch.label') }}</button>
49
49
  </div>
50
50
  </div>
51
51
  </details>
52
52
 
53
53
  <div class="selector-grid task-composer-grid task-composer-grid-primary">
54
54
  <label class="selector-field task-field task-field-wide task-goal-field">
55
- <span class="selector-label">目标</span>
56
- <textarea v-model="taskOrchestration.target" class="task-textarea task-textarea-goal" rows="5" placeholder="例如:修复当前 PR review 评论,并补对应回归测试;不要改无关模块"></textarea>
57
- <span class="task-field-hint">一句话写清结果、边界和验收标准就够了。</span>
55
+ <span class="selector-label">{{ t('orchestration.fields.target') }}</span>
56
+ <textarea v-model="taskOrchestration.target" class="task-textarea task-textarea-goal" rows="5" :placeholder="t('orchestration.fields.target.placeholder')"></textarea>
57
+ <span class="task-field-hint">{{ t('orchestration.fields.target.hint') }}</span>
58
58
  </label>
59
59
  </div>
60
60
 
@@ -64,12 +64,11 @@
64
64
  <div class="task-readiness-copy task-draft-inline-copy">{{ taskOrchestrationDraftReadiness.summary }}</div>
65
65
  </div>
66
66
  <div class="task-config-strip">
67
- <div class="task-config-pill">{{ taskOrchestration.selectedEngine === 'workflow' ? 'Workflow' : 'Codex' }}</div>
68
- <div class="task-config-pill">{{ taskOrchestration.allowWrite ? '允许写入' : '只读' }}</div>
69
- <div class="task-config-pill">{{ taskOrchestration.dryRun ? '仅预演' : '真实执行' }}</div>
70
- <div v-if="taskOrchestration.title.trim()" class="task-config-pill">标题已设</div>
71
- <div v-if="taskOrchestration.selectedEngine === 'workflow' && taskOrchestrationDraftMetrics.workflowCount > 0" class="task-config-pill">Workflow {{ taskOrchestrationDraftMetrics.workflowCount }}</div>
72
- <div v-if="taskOrchestration.plan" class="task-config-pill">计划 {{ taskOrchestrationDraftMetrics.planNodeCount }} 节点</div>
67
+ <div class="task-config-pill">{{ taskOrchestration.selectedEngine === 'workflow' ? t('orchestration.engine.workflow') : t('orchestration.engine.codex') }}</div>
68
+ <div class="task-config-pill">{{ taskOrchestration.runMode === 'dry-run' ? t('orchestration.runMode.dryRun') : (taskOrchestration.runMode === 'read' ? t('orchestration.runMode.readOnly') : t('orchestration.runMode.write')) }}</div>
69
+ <div v-if="taskOrchestration.title.trim()" class="task-config-pill">{{ t('orchestration.pills.hasTitle') }}</div>
70
+ <div v-if="taskOrchestration.selectedEngine === 'workflow' && taskOrchestrationDraftMetrics.workflowCount > 0" class="task-config-pill">{{ t('orchestration.pills.workflowCount', { count: taskOrchestrationDraftMetrics.workflowCount }) }}</div>
71
+ <div v-if="taskOrchestration.plan" class="task-config-pill">{{ t('orchestration.pills.planNodes', { count: taskOrchestrationDraftMetrics.planNodeCount }) }}</div>
73
72
  </div>
74
73
  </div>
75
74
  </div>
@@ -78,64 +77,59 @@
78
77
  <div class="task-flow-head">
79
78
  <div class="task-flow-step">2</div>
80
79
  <div>
81
- <div class="task-flow-title">选择执行方式</div>
82
- <div class="task-flow-copy">常用项默认展开,其余放高级设置。</div>
80
+ <div class="task-flow-title">{{ t('orchestration.step2.title') }}</div>
81
+ <div class="task-flow-copy">{{ t('orchestration.step2.subtitle') }}</div>
83
82
  </div>
84
83
  </div>
85
84
 
86
- <div class="selector-grid task-composer-grid task-composer-grid-compact">
85
+ <div class="selector-grid task-composer-grid task-composer-grid-compact task-composer-grid-inline">
87
86
  <label class="selector-field">
88
- <span class="selector-label">引擎</span>
89
- <select v-model="taskOrchestration.selectedEngine">
90
- <option value="codex">Codex</option>
91
- <option value="workflow">Workflow</option>
87
+ <span class="selector-label">{{ t('orchestration.fields.engine') }}</span>
88
+ <select v-model="taskOrchestration.selectedEngine" class="provider-fast-switch-select" disabled>
89
+ <option value="codex">{{ t('orchestration.engine.codex') }}</option>
90
+ <option value="workflow">{{ t('orchestration.engine.workflow') }}</option>
91
+ </select>
92
+ </label>
93
+ <label class="selector-field">
94
+ <span class="selector-label">{{ t('orchestration.fields.runMode') }}</span>
95
+ <select v-model="taskOrchestration.runMode" class="provider-fast-switch-select" disabled>
96
+ <option value="write">{{ t('orchestration.runMode.write') }}</option>
97
+ <option value="read">{{ t('orchestration.runMode.readOnly') }}</option>
98
+ <option value="dry-run">{{ t('orchestration.runMode.dryRun') }}</option>
92
99
  </select>
93
100
  </label>
94
- <div class="selector-field task-toggle-card task-toggle-card-inline">
95
- <span class="selector-label">运行模式</span>
96
- <div class="task-toggle-inline">
97
- <label class="task-toggle">
98
- <input v-model="taskOrchestration.allowWrite" type="checkbox">
99
- <span>允许写入</span>
100
- </label>
101
- <label class="task-toggle">
102
- <input v-model="taskOrchestration.dryRun" type="checkbox">
103
- <span>仅预演</span>
104
- </label>
105
- </div>
106
- </div>
107
101
  </div>
108
102
 
109
103
  <details class="task-advanced-panel">
110
- <summary class="task-advanced-summary">高级设置</summary>
104
+ <summary class="task-advanced-summary">{{ t('orchestration.advanced.title') }}</summary>
111
105
  <div class="selector-grid task-composer-grid task-composer-grid-secondary">
112
106
  <label class="selector-field task-field-wide">
113
- <span class="selector-label">标题</span>
114
- <input v-model="taskOrchestration.title" type="text" placeholder="可选,默认从目标自动提取">
107
+ <span class="selector-label">{{ t('orchestration.fields.title') }}</span>
108
+ <input v-model="taskOrchestration.title" class="model-input" type="text" :placeholder="t('orchestration.fields.title.placeholder')">
115
109
  </label>
116
110
  <label class="selector-field task-field-wide">
117
- <span class="selector-label">说明</span>
118
- <textarea v-model="taskOrchestration.notes" class="task-textarea" rows="3" placeholder="例如:不要重写现有架构,只做增量实现"></textarea>
119
- <span class="task-field-hint">补边界、禁区、风格要求或验证要求。</span>
111
+ <span class="selector-label">{{ t('orchestration.fields.notes') }}</span>
112
+ <textarea v-model="taskOrchestration.notes" class="task-textarea" rows="3" :placeholder="t('orchestration.fields.notes.placeholder')"></textarea>
113
+ <span class="task-field-hint">{{ t('orchestration.fields.notes.hint') }}</span>
120
114
  </label>
121
115
  <label class="selector-field task-field-wide">
122
- <span class="selector-label">后续动作(每行一条)</span>
123
- <textarea v-model="taskOrchestration.followUpsText" class="task-textarea" rows="3" placeholder="例如:\n继续处理 review 评论\n最后补回归测试"></textarea>
116
+ <span class="selector-label">{{ t('orchestration.fields.followUps') }}</span>
117
+ <textarea v-model="taskOrchestration.followUpsText" class="task-textarea" rows="3" :placeholder="t('orchestration.fields.followUps.placeholder')"></textarea>
124
118
  </label>
125
119
  <label class="selector-field">
126
- <span class="selector-label">并发</span>
127
- <input v-model="taskOrchestration.concurrency" type="number" min="1" max="8">
128
- <span class="task-field-hint">复杂任务先从 1~2 开始更稳。</span>
120
+ <span class="selector-label">{{ t('orchestration.fields.concurrency') }}</span>
121
+ <input v-model="taskOrchestration.concurrency" class="model-input" type="number" min="1" max="8">
122
+ <span class="task-field-hint">{{ t('orchestration.fields.concurrency.hint') }}</span>
129
123
  </label>
130
124
  <label class="selector-field">
131
- <span class="selector-label">自动修复</span>
132
- <input v-model="taskOrchestration.autoFixRounds" type="number" min="0" max="5">
133
- <span class="task-field-hint">失败后自动再试几轮。</span>
125
+ <span class="selector-label">{{ t('orchestration.fields.autoFixRounds') }}</span>
126
+ <input v-model="taskOrchestration.autoFixRounds" class="model-input" type="number" min="0" max="5">
127
+ <span class="task-field-hint">{{ t('orchestration.fields.autoFixRounds.hint') }}</span>
134
128
  </label>
135
129
  <label v-if="taskOrchestration.selectedEngine === 'workflow'" class="selector-field task-field-wide">
136
- <span class="selector-label">Workflow ID(每行一条)</span>
137
- <textarea v-model="taskOrchestration.workflowIdsText" class="task-textarea" rows="3" placeholder="例如:\ndiagnose-config\nsafe-provider-switch"></textarea>
138
- <span class="task-field-hint">仅 Workflow 模式需要。当前本地可用 {{ taskOrchestration.workflows.length }} 个。</span>
130
+ <span class="selector-label">{{ t('orchestration.fields.workflowIds') }}</span>
131
+ <textarea v-model="taskOrchestration.workflowIdsText" class="task-textarea" rows="3" :placeholder="t('orchestration.fields.workflowIds.placeholder')"></textarea>
132
+ <span class="task-field-hint">{{ t('orchestration.fields.workflowIds.hint', { count: taskOrchestration.workflows.length }) }}</span>
139
133
  <div v-if="taskOrchestration.workflows.length" class="task-workflow-suggestions">
140
134
  <button
141
135
  v-for="workflow in taskOrchestration.workflows"
@@ -144,7 +138,7 @@
144
138
  class="task-workflow-chip"
145
139
  @click="appendTaskWorkflowId(workflow.id || workflow.name)">
146
140
  <span>{{ workflow.name || workflow.id }}</span>
147
- <small v-if="workflow.stepCount">{{ workflow.stepCount }} 步</small>
141
+ <small v-if="workflow.stepCount">{{ t('orchestration.workflow.stepCount', { count: workflow.stepCount }) }}</small>
148
142
  </button>
149
143
  </div>
150
144
  </label>
@@ -156,27 +150,24 @@
156
150
  <div class="task-flow-head">
157
151
  <div class="task-flow-step">3</div>
158
152
  <div>
159
- <div class="task-flow-title">先预览,再执行</div>
160
- <div class="task-flow-copy">先确认计划,再决定立即执行还是入队。</div>
153
+ <div class="task-flow-title">{{ t('orchestration.step3.title') }}</div>
154
+ <div class="task-flow-copy">{{ t('orchestration.step3.subtitle') }}</div>
161
155
  </div>
162
156
  </div>
163
157
  <div class="task-action-row task-action-row-prominent">
164
158
  <button type="button" class="btn-tool task-action-preview" @click="previewTaskPlan()" :disabled="taskOrchestration.planning || taskOrchestration.running || !taskOrchestration.target.trim()">
165
- {{ taskOrchestration.planning ? '规划中...' : '预览计划' }}
159
+ {{ taskOrchestration.planning ? t('orchestration.actions.planning') : t('orchestration.actions.previewOnly') }}
166
160
  </button>
167
161
  <div class="task-action-row-right task-action-row-right-prominent">
168
- <button type="button" class="btn-tool btn-primary" @click="runTaskOrchestration()" :disabled="taskOrchestration.running || !taskOrchestration.target.trim()">
169
- {{ taskOrchestration.running ? '启动中...' : '立即执行' }}
170
- </button>
171
- <button type="button" class="btn-tool" @click="addTaskOrchestrationToQueue()" :disabled="taskOrchestration.queueAdding || !taskOrchestration.target.trim()">
172
- {{ taskOrchestration.queueAdding ? '加入中...' : '加入队列' }}
162
+ <button type="button" class="btn-tool btn-primary" @click="planAndRunTaskOrchestration()" :disabled="taskOrchestration.running || taskOrchestration.planning || !taskOrchestration.target.trim()">
163
+ {{ (taskOrchestration.running || taskOrchestration.planning) ? t('orchestration.actions.preparing') : (taskOrchestration.runMode === 'dry-run' ? t('orchestration.actions.generatePlan') : t('orchestration.actions.planAndRun')) }}
173
164
  </button>
174
- <button v-if="taskOrchestration.queue.length" type="button" class="btn-tool" @click="startTaskQueueRunner()" :disabled="taskOrchestration.queueStarting">
175
- {{ taskOrchestration.queueStarting ? '启动中...' : '开始队列' }}
165
+ <button type="button" class="btn-tool" @click="queueTaskOrchestrationAndStart()" :disabled="taskOrchestration.queueAdding || taskOrchestration.queueStarting || taskOrchestration.planning || !taskOrchestration.target.trim()">
166
+ {{ (taskOrchestration.queueAdding || taskOrchestration.queueStarting) ? t('orchestration.actions.processing') : t('orchestration.actions.queueAndStart') }}
176
167
  </button>
177
168
  </div>
178
169
  </div>
179
- <div class="task-action-caption">预览不会执行。批量任务再入队。</div>
170
+ <div class="task-action-caption">{{ t('orchestration.actions.caption') }}</div>
180
171
  </div>
181
172
  </section>
182
173
  </div>
@@ -186,13 +177,13 @@
186
177
  class="selector-section task-stage-card">
187
178
  <div class="task-stage-empty">
188
179
  <div>
189
- <div class="selector-title">有内容时再展开工作区</div>
190
- <div class="skills-panel-note">先写目标,再预览计划。</div>
180
+ <div class="selector-title">{{ t('orchestration.stage.title') }}</div>
181
+ <div class="skills-panel-note">{{ t('orchestration.stage.subtitle') }}</div>
191
182
  </div>
192
183
  <div class="task-stage-strip">
193
- <div class="task-stage-pill">写目标</div>
194
- <div class="task-stage-pill">预览</div>
195
- <div class="task-stage-pill">执行或入队</div>
184
+ <div class="task-stage-pill">{{ t('orchestration.stage.pill.target') }}</div>
185
+ <div class="task-stage-pill">{{ t('orchestration.stage.pill.preview') }}</div>
186
+ <div class="task-stage-pill">{{ t('orchestration.stage.pill.run') }}</div>
196
187
  </div>
197
188
  </div>
198
189
  </section>
@@ -206,8 +197,8 @@
206
197
  <div v-if="taskOrchestration.lastError" class="task-issue-item">{{ taskOrchestration.lastError }}</div>
207
198
  <div class="selector-header task-section-header">
208
199
  <div>
209
- <span class="selector-title">计划预览</span>
210
- <div class="skills-panel-note">先确认节点、波次和依赖。</div>
200
+ <span class="selector-title">{{ t('orchestration.plan.title') }}</span>
201
+ <div class="skills-panel-note">{{ t('orchestration.plan.subtitle') }}</div>
211
202
  </div>
212
203
  </div>
213
204
  <div v-if="taskOrchestration.planIssues.length" class="task-issues-list">
@@ -223,15 +214,15 @@
223
214
  <template v-if="taskOrchestration.plan">
224
215
  <div class="task-plan-summary-strip">
225
216
  <div class="task-plan-summary-item">
226
- <span class="task-plan-summary-label">节点数</span>
217
+ <span class="task-plan-summary-label">{{ t('orchestration.plan.summary.nodes') }}</span>
227
218
  <strong>{{ taskOrchestration.plan.nodes.length }}</strong>
228
219
  </div>
229
220
  <div class="task-plan-summary-item">
230
- <span class="task-plan-summary-label">波次</span>
221
+ <span class="task-plan-summary-label">{{ t('orchestration.plan.summary.waves') }}</span>
231
222
  <strong>{{ taskOrchestration.plan.waves.length }}</strong>
232
223
  </div>
233
224
  <div class="task-plan-summary-item">
234
- <span class="task-plan-summary-label">引擎</span>
225
+ <span class="task-plan-summary-label">{{ t('orchestration.plan.summary.engine') }}</span>
235
226
  <strong>{{ taskOrchestration.plan.engine }}</strong>
236
227
  </div>
237
228
  </div>
@@ -248,9 +239,9 @@
248
239
  <div class="task-node-title">{{ node.title || node.id }}</div>
249
240
  <div class="task-node-meta">{{ node.id }} · {{ node.kind }}<span v-if="node.workflowId"> · {{ node.workflowId }}</span></div>
250
241
  </div>
251
- <span :class="['pill', node.write ? 'configured' : 'empty']">{{ node.write ? 'write' : 'read-only' }}</span>
242
+ <span :class="['pill', node.write ? 'configured' : 'empty']">{{ node.write ? t('orchestration.plan.node.write') : t('orchestration.plan.node.readOnly') }}</span>
252
243
  </div>
253
- <div class="task-node-deps">依赖:{{ formatTaskNodeDependencies(node) }}</div>
244
+ <div class="task-node-deps">{{ t('orchestration.labels.dependencies') }}{{ formatTaskNodeDependencies(node) }}</div>
254
245
  </div>
255
246
  </div>
256
247
  </template>
@@ -261,15 +252,15 @@
261
252
  class="selector-section task-workbench-card">
262
253
  <div class="selector-header task-section-header">
263
254
  <div>
264
- <span class="selector-title">执行工作台</span>
265
- <div class="skills-panel-note">有内容时才展开。</div>
255
+ <span class="selector-title">{{ t('orchestration.workbench.title') }}</span>
256
+ <div class="skills-panel-note">{{ t('orchestration.workbench.subtitle') }}</div>
266
257
  </div>
267
258
  <div class="settings-tab-actions task-header-actions">
268
259
  <button type="button" class="btn-tool btn-tool-compact" @click="loadTaskOrchestrationOverview({ forceRefresh: true, includeDetail: true })" :disabled="taskOrchestration.loading">
269
- {{ taskOrchestration.loading ? '刷新中...' : '刷新' }}
260
+ {{ taskOrchestration.loading ? t('common.refreshing') : t('common.refresh') }}
270
261
  </button>
271
262
  <button v-if="taskOrchestration.queue.length" type="button" class="btn-tool btn-tool-compact" @click="startTaskQueueRunner()" :disabled="taskOrchestration.queueStarting">
272
- {{ taskOrchestration.queueStarting ? '启动中...' : '开始队列' }}
263
+ {{ taskOrchestration.queueStarting ? t('orchestration.queue.starting') : t('orchestration.queue.start') }}
273
264
  </button>
274
265
  </div>
275
266
  </div>
@@ -278,16 +269,16 @@
278
269
  v-if="(taskOrchestration.queue.length ? 1 : 0) + (taskOrchestration.runs.length ? 1 : 0) + ((taskOrchestration.selectedRunId || taskOrchestration.selectedRunError) ? 1 : 0) > 1"
279
270
  class="task-workbench-tabs"
280
271
  role="group"
281
- aria-label="任务编排工作台视图">
282
- <button v-if="taskOrchestration.queue.length" type="button" class="task-workbench-tab" :class="{ active: taskOrchestration.workspaceTab === 'queue' }" @click="taskOrchestration.workspaceTab = 'queue'">队列 {{ taskOrchestration.queue.length }}</button>
283
- <button v-if="taskOrchestration.runs.length" type="button" class="task-workbench-tab" :class="{ active: taskOrchestration.workspaceTab === 'runs' }" @click="taskOrchestration.workspaceTab = 'runs'">运行记录 {{ taskOrchestration.runs.length }}</button>
284
- <button v-if="taskOrchestration.selectedRunId || taskOrchestration.selectedRunError" type="button" class="task-workbench-tab" :class="{ active: taskOrchestration.workspaceTab === 'detail' }" @click="taskOrchestration.workspaceTab = 'detail'">运行详情</button>
272
+ :aria-label="t('orchestration.workbench.tabs.aria')">
273
+ <button v-if="taskOrchestration.queue.length" type="button" class="task-workbench-tab" :class="{ active: taskOrchestration.workspaceTab === 'queue' }" @click="taskOrchestration.workspaceTab = 'queue'">{{ t('orchestration.workbench.tabs.queue', { count: taskOrchestration.queue.length }) }}</button>
274
+ <button v-if="taskOrchestration.runs.length" type="button" class="task-workbench-tab" :class="{ active: taskOrchestration.workspaceTab === 'runs' }" @click="taskOrchestration.workspaceTab = 'runs'">{{ t('orchestration.workbench.tabs.runs', { count: taskOrchestration.runs.length }) }}</button>
275
+ <button v-if="taskOrchestration.selectedRunId || taskOrchestration.selectedRunError" type="button" class="task-workbench-tab" :class="{ active: taskOrchestration.workspaceTab === 'detail' }" @click="taskOrchestration.workspaceTab = 'detail'">{{ t('orchestration.workbench.tabs.detail') }}</button>
285
276
  </div>
286
277
 
287
278
  <div v-if="taskOrchestration.workspaceTab === 'queue' || (!taskOrchestration.runs.length && !taskOrchestration.selectedRunId && !taskOrchestration.selectedRunError)" class="task-workbench-panel">
288
279
  <div v-if="!taskOrchestration.queue.length" class="task-empty-state">
289
- <div class="task-empty-title">当前没有排队任务</div>
290
- <div class="task-empty-copy">批量任务可先入队,再启动队列。</div>
280
+ <div class="task-empty-title">{{ t('orchestration.queue.empty.title') }}</div>
281
+ <div class="task-empty-copy">{{ t('orchestration.queue.empty.subtitle') }}</div>
291
282
  </div>
292
283
  <div v-else class="task-runtime-list">
293
284
  <div
@@ -307,7 +298,7 @@
307
298
  </div>
308
299
  <div class="task-runtime-item-actions">
309
300
  <span :class="['pill', taskRunStatusTone(item.status)]">{{ item.status }}</span>
310
- <button type="button" class="btn-mini" @click.stop="cancelTaskRunFromUi(item.taskId)" :disabled="item.status !== 'queued' && item.status !== 'running'">取消</button>
301
+ <button type="button" class="btn-mini" @click.stop="cancelTaskRunFromUi(item.taskId)" :disabled="item.status !== 'queued' && item.status !== 'running'">{{ t('common.cancel') }}</button>
311
302
  </div>
312
303
  </div>
313
304
  </div>
@@ -315,8 +306,8 @@
315
306
 
316
307
  <div v-else-if="taskOrchestration.workspaceTab === 'runs' || (!taskOrchestration.queue.length && taskOrchestration.runs.length && !taskOrchestration.selectedRunId && !taskOrchestration.selectedRunError)" class="task-workbench-panel">
317
308
  <div v-if="!taskOrchestration.runs.length" class="task-empty-state">
318
- <div class="task-empty-title">还没有运行记录</div>
319
- <div class="task-empty-copy">执行后会显示最近运行记录。</div>
309
+ <div class="task-empty-title">{{ t('orchestration.runs.empty.title') }}</div>
310
+ <div class="task-empty-copy">{{ t('orchestration.runs.empty.subtitle') }}</div>
320
311
  </div>
321
312
  <div v-else class="task-runtime-list">
322
313
  <button
@@ -340,38 +331,38 @@
340
331
  <div v-else class="task-workbench-panel">
341
332
  <div class="task-detail-toolbar settings-tab-actions">
342
333
  <button type="button" class="btn-tool btn-tool-compact" @click="taskOrchestration.selectedRunId ? loadTaskRunDetail(taskOrchestration.selectedRunId) : null" :disabled="!taskOrchestration.selectedRunId || taskOrchestration.selectedRunLoading">
343
- {{ taskOrchestration.selectedRunLoading ? '刷新中...' : '刷新详情' }}
334
+ {{ taskOrchestration.selectedRunLoading ? t('common.refreshing') : t('orchestration.detail.refresh') }}
344
335
  </button>
345
336
  <button type="button" class="btn-tool btn-tool-compact" @click="retryTaskRunFromUi(taskOrchestration.selectedRunId)" :disabled="!taskOrchestration.selectedRunId || taskOrchestration.retrying">
346
- {{ taskOrchestration.retrying ? '重试中...' : '重试' }}
337
+ {{ taskOrchestration.retrying ? t('orchestration.detail.retrying') : t('orchestration.detail.retry') }}
347
338
  </button>
348
339
  <button type="button" class="btn-tool btn-tool-compact" @click="cancelTaskRunFromUi(taskOrchestration.selectedRunId)" :disabled="!taskOrchestrationSelectedRun || !taskOrchestrationSelectedRun.run || !isTaskRunActive(taskOrchestrationSelectedRun.run.status)">
349
- 取消
340
+ {{ t('common.cancel') }}
350
341
  </button>
351
342
  </div>
352
343
 
353
344
  <div v-if="taskOrchestration.selectedRunError" class="task-issue-item">{{ taskOrchestration.selectedRunError }}</div>
354
345
  <div v-if="!taskOrchestrationSelectedRun" class="task-empty-state">
355
- <div class="task-empty-title">选择一条运行记录查看详情</div>
356
- <div class="task-empty-copy">这里会显示节点状态、摘要和日志。</div>
346
+ <div class="task-empty-title">{{ t('orchestration.detail.empty.title') }}</div>
347
+ <div class="task-empty-copy">{{ t('orchestration.detail.empty.subtitle') }}</div>
357
348
  </div>
358
349
  <template v-else>
359
350
  <div class="task-detail-summary-strip">
360
351
  <div class="task-plan-summary-item">
361
- <span class="task-plan-summary-label">状态</span>
352
+ <span class="task-plan-summary-label">{{ t('orchestration.detail.summary.status') }}</span>
362
353
  <strong>{{ taskOrchestrationSelectedRun.run.status }}</strong>
363
354
  </div>
364
355
  <div class="task-plan-summary-item">
365
- <span class="task-plan-summary-label">耗时</span>
356
+ <span class="task-plan-summary-label">{{ t('orchestration.detail.summary.duration') }}</span>
366
357
  <strong>{{ taskOrchestrationSelectedRun.run.durationMs || 0 }}ms</strong>
367
358
  </div>
368
359
  <div class="task-plan-summary-item">
369
- <span class="task-plan-summary-label">节点数</span>
360
+ <span class="task-plan-summary-label">{{ t('orchestration.detail.summary.nodes') }}</span>
370
361
  <strong>{{ taskOrchestrationSelectedRunNodes.length }}</strong>
371
362
  </div>
372
363
  <div class="task-plan-summary-item">
373
- <span class="task-plan-summary-label">摘要</span>
374
- <strong>{{ taskOrchestrationSelectedRun.run.summary || '暂无' }}</strong>
364
+ <span class="task-plan-summary-label">{{ t('orchestration.detail.summary.summary') }}</span>
365
+ <strong>{{ taskOrchestrationSelectedRun.run.summary || t('common.none') }}</strong>
375
366
  </div>
376
367
  </div>
377
368
  <div v-if="taskOrchestrationSelectedRun.run.error" class="task-issue-item">{{ taskOrchestrationSelectedRun.run.error }}</div>
@@ -380,13 +371,13 @@
380
371
  <div class="task-node-head">
381
372
  <div>
382
373
  <div class="task-node-title">{{ node.title || node.id }}</div>
383
- <div class="task-node-meta">{{ node.id }} · attempts {{ node.attemptCount || 0 }} · auto-fix {{ node.autoFixRounds || 0 }}</div>
374
+ <div class="task-node-meta">{{ t('orchestration.detail.node.meta', { id: node.id, attempts: (node.attemptCount || 0), autoFix: (node.autoFixRounds || 0) }) }}</div>
384
375
  </div>
385
376
  <span :class="['pill', taskRunStatusTone(node.status)]">{{ node.status }}</span>
386
377
  </div>
387
378
  <div v-if="node.summary" class="task-runtime-item-summary">{{ node.summary }}</div>
388
- <div v-if="node.error && node.error !== node.summary" class="task-node-deps">错误:{{ node.error }}</div>
389
- <div class="task-node-deps">依赖:{{ formatTaskNodeDependencies(node) }}</div>
379
+ <div v-if="node.error && node.error !== node.summary" class="task-node-deps">{{ t('orchestration.labels.error') }}{{ node.error }}</div>
380
+ <div class="task-node-deps">{{ t('orchestration.labels.dependencies') }}{{ formatTaskNodeDependencies(node) }}</div>
390
381
  <pre class="task-log-block">{{ formatTaskNodeLogs(node.logs) }}</pre>
391
382
  </div>
392
383
  </div>
@@ -37,7 +37,12 @@
37
37
  </aside>
38
38
 
39
39
  <section class="plugins-main" :aria-label="t('plugins.main.ariaWorkspace')">
40
- <div v-if="pluginsActiveId === 'prompt-templates'" class="plugins-panel">
40
+ <div v-if="pluginsLoading" class="skills-empty-state">{{ t('common.loading') }}</div>
41
+ <div v-else-if="pluginsError" class="skills-empty-state">
42
+ <div class="plugins-panel-note">{{ pluginsError }}</div>
43
+ <button type="button" class="btn-mini" @click="loadPluginsOverview({ forceRefresh: true, silent: false })" :disabled="loading || !!initError || pluginsLoading">{{ t('common.refresh') }}</button>
44
+ </div>
45
+ <div v-else-if="pluginsActiveId === 'prompt-templates'" class="plugins-panel">
41
46
  <div class="plugins-panel-head">
42
47
  <div class="plugins-panel-title">{{ t('plugins.promptTemplates.title') }}</div>
43
48
  <div class="plugins-panel-subtitle">{{ t('plugins.promptTemplates.subtitle') }}</div>
@@ -78,8 +83,8 @@
78
83
  </select>
79
84
 
80
85
  <div v-if="!promptComposerActiveTemplate" class="plugins-panel-note">{{ t('plugins.promptTemplates.compose.empty') }}</div>
81
- <div v-else class="plugins-panel-note">{{ t('plugins.promptTemplates.compose.varsHint') }}</div>
82
- <div class="prompt-compose-actions">
86
+ <div v-else-if="!promptComposerActiveTemplate.isBuiltin" class="plugins-panel-note">{{ t('plugins.promptTemplates.compose.varsHint') }}</div>
87
+ <div v-if="promptComposerActiveTemplate && !promptComposerActiveTemplate.isBuiltin" class="prompt-compose-actions">
83
88
  <button
84
89
  type="button"
85
90
  class="btn-mini"
@@ -88,6 +93,31 @@
88
93
  </div>
89
94
  </div>
90
95
 
96
+ <div v-if="promptComposerActiveTemplate && promptComposerActiveTemplate.vars && promptComposerActiveTemplate.vars.length" class="prompt-vars-block">
97
+ <div class="prompt-vars-head">
98
+ <div>
99
+ <div class="prompt-vars-title">{{ t('plugins.promptTemplates.vars.title') }}</div>
100
+ <div v-if="!promptComposerActiveTemplate.isBuiltin" class="plugins-panel-note">{{ t('plugins.promptTemplates.compose.varsHint') }}</div>
101
+ <div v-if="promptComposerMissingVars.length" class="plugins-panel-note">未填 {{ promptComposerMissingVars.length }} 项</div>
102
+ </div>
103
+ <div class="prompt-editor-actions">
104
+ <button v-if="promptComposerMissingVars.length" type="button" class="btn-mini" @click="focusPromptComposerFirstMissingVar" :disabled="pluginsLoading">跳到未填</button>
105
+ <button type="button" class="btn-mini" @click="resetPromptComposerVarValues" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.vars.reset') }}</button>
106
+ </div>
107
+ </div>
108
+ <div class="prompt-vars-grid">
109
+ <label v-for="(name, idx) in promptComposerActiveTemplate.vars" :key="'prompt-compose-var-' + name" class="prompt-var-row">
110
+ <span class="prompt-var-label mono">{{ name }}</span>
111
+ <template v-if="idx === 0">
112
+ <input ref="promptComposerFirstField" :class="['form-input', 'prompt-var-input', { 'is-missing': promptComposerMissingVars.includes(name) }]" type="text" :value="promptComposerVarValues[name] || ''" @input="setPromptComposerVarValue(name, $event.target.value)" :placeholder="t('plugins.promptTemplates.vars.valuePlaceholder', { name })">
113
+ </template>
114
+ <template v-else>
115
+ <input :class="['form-input', 'prompt-var-input', { 'is-missing': promptComposerMissingVars.includes(name) }]" type="text" :value="promptComposerVarValues[name] || ''" @input="setPromptComposerVarValue(name, $event.target.value)" :placeholder="t('plugins.promptTemplates.vars.valuePlaceholder', { name })">
116
+ </template>
117
+ </label>
118
+ </div>
119
+ </div>
120
+
91
121
  <div class="prompt-preview-block prompt-compose-preview">
92
122
  <div class="prompt-vars-head">
93
123
  <div>
@@ -183,7 +213,7 @@
183
213
  <div v-else class="prompt-vars-grid">
184
214
  <label v-for="name in promptTemplateVars" :key="'prompt-var-' + name" class="prompt-var-row">
185
215
  <span class="prompt-var-label mono">{{ name }}</span>
186
- <input class="form-input prompt-var-input" type="text" :disabled="promptTemplateDraft.isBuiltin" :value="promptTemplateVarValues[name] || ''" @input="setPromptVariableValue(name, $event.target.value)" :placeholder="t('plugins.promptTemplates.vars.valuePlaceholder', { name })">
216
+ <input class="form-input prompt-var-input" type="text" :readonly="promptTemplateDraft.isBuiltin" :value="promptTemplateVarValues[name] || ''" @input="setPromptVariableValue(name, $event.target.value)" :placeholder="t('plugins.promptTemplates.vars.valuePlaceholder', { name })">
187
217
  </label>
188
218
  </div>
189
219
  </div>
@@ -51,7 +51,7 @@
51
51
  <input
52
52
  class="session-query-input"
53
53
  v-model="sessionQuery"
54
- @keyup.enter="loadSessions"
54
+ @keyup.enter="onSessionFilterChange"
55
55
  :disabled="sessionsLoading || !isSessionQueryEnabled"
56
56
  :placeholder="sessionQueryPlaceholder">
57
57
  </div>
@@ -60,7 +60,7 @@
60
60
  class="session-role-select"
61
61
  v-model="sessionRoleFilter"
62
62
  @change="onSessionFilterChange"
63
- disabled>
63
+ :disabled="sessionsLoading">
64
64
  <option value="all">{{ t('sessions.role.all') }}</option>
65
65
  <option value="user">{{ t('sessions.role.user') }}</option>
66
66
  <option value="assistant">{{ t('sessions.role.assistant') }}</option>
@@ -70,12 +70,18 @@
70
70
  class="session-time-select"
71
71
  v-model="sessionTimePreset"
72
72
  @change="onSessionFilterChange"
73
- disabled>
73
+ :disabled="sessionsLoading">
74
74
  <option value="all">{{ t('sessions.time.all') }}</option>
75
75
  <option value="7d">{{ t('sessions.time.7d') }}</option>
76
76
  <option value="30d">{{ t('sessions.time.30d') }}</option>
77
77
  <option value="90d">{{ t('sessions.time.90d') }}</option>
78
78
  </select>
79
+ <button class="btn-tool btn-tool-compact" type="button" @click="copySessionsFilterShareUrl" :disabled="sessionsLoading">
80
+ 复制筛选链接
81
+ </button>
82
+ <button class="btn-tool btn-tool-compact" type="button" @click="clearSessionFilters" :disabled="sessionsLoading">
83
+ {{ t('common.resetFilters') }}
84
+ </button>
79
85
  </div>
80
86
  </div>
81
87
  <div class="session-toolbar-footer">
@@ -10,10 +10,10 @@
10
10
  <span class="selector-title">{{ t('usage.overview') }}</span>
11
11
  </div>
12
12
  <div class="usage-range-group" role="group" :aria-label="t('usage.range.aria')">
13
- <button type="button" class="usage-range-btn" :class="{ active: sessionsUsageTimeRange === '7d' }" @click="sessionsUsageTimeRange = '7d'">{{ t('usage.range.7d') }}</button>
14
- <button type="button" class="usage-range-btn" :class="{ active: sessionsUsageTimeRange === '30d' }" @click="sessionsUsageTimeRange = '30d'">{{ t('usage.range.30d') }}</button>
15
- <button type="button" class="usage-range-btn" :class="{ active: sessionsUsageTimeRange === 'all' }" @click="sessionsUsageTimeRange = 'all'">{{ t('usage.range.all') }}</button>
16
- <button type="button" class="usage-range-btn" @click="loadSessionsUsage({ forceRefresh: true })" :disabled="sessionsUsageLoading">{{ sessionsUsageLoading ? t('usage.refreshing') : t('usage.refresh') }}</button>
13
+ <button type="button" class="usage-range-btn" :class="{ active: sessionsUsageTimeRange === '7d' }" @click="setSessionsUsageTimeRange('7d')">{{ t('usage.range.7d') }}</button>
14
+ <button type="button" class="usage-range-btn" :class="{ active: sessionsUsageTimeRange === '30d' }" @click="setSessionsUsageTimeRange('30d')">{{ t('usage.range.30d') }}</button>
15
+ <button type="button" class="usage-range-btn" :class="{ active: sessionsUsageTimeRange === 'all' }" @click="setSessionsUsageTimeRange('all')">{{ t('usage.range.all') }}</button>
16
+ <button type="button" class="usage-range-btn" @click="loadSessionsUsage({ forceRefresh: true, range: sessionsUsageTimeRange })" :disabled="sessionsUsageLoading">{{ sessionsUsageLoading ? t('usage.refreshing') : t('usage.refresh') }}</button>
17
17
  </div>
18
18
  </div>
19
19
 
@@ -302,7 +302,7 @@
302
302
  <section class="usage-card">
303
303
  <div class="usage-card-title">{{ t('usage.paths.title') }}</div>
304
304
  <div v-if="!sessionUsageCharts.topPaths.length" class="usage-list-value">{{ t('usage.paths.empty') }}</div>
305
- <div v-else class="usage-list">
305
+ <div v-else class="usage-list usage-list-scroll usage-list-top-paths">
306
306
  <div v-for="item in sessionUsageCharts.topPaths" :key="item.path" class="usage-list-row">
307
307
  <div class="usage-list-label">{{ t('usage.paths.count', { count: item.count }) }}</div>
308
308
  <div class="usage-progress"><div class="usage-progress-fill" :style="{ width: ((item.count / Math.max((sessionUsageCharts.topPaths.length ? sessionUsageCharts.topPaths[0].count : 1), 1)) * 100) + '%' }"></div></div>