principles-disciple 1.12.0 → 1.14.0
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.
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/ui/src/i18n/ui.ts +86 -9
- package/ui/src/pages/EvolutionPage.tsx +39 -58
- package/ui/src/pages/FeedbackPage.tsx +0 -2
- package/ui/src/pages/GateMonitorPage.tsx +3 -3
- package/ui/src/pages/LoginPage.tsx +2 -1
- package/ui/src/pages/OverviewPage.tsx +10 -9
- package/ui/src/pages/SamplesPage.tsx +3 -3
- package/ui/src/pages/ThinkingModelsPage.tsx +636 -69
- package/ui/src/styles.css +316 -0
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/ui/src/i18n/ui.ts
CHANGED
|
@@ -52,6 +52,7 @@ export const i18n = {
|
|
|
52
52
|
timeRange: { zh: '时间范围', en: 'Time Range' },
|
|
53
53
|
total: { zh: '共', en: 'Total' },
|
|
54
54
|
items: { zh: '条', en: 'items' },
|
|
55
|
+
search: { zh: '搜索...', en: 'Search...' },
|
|
55
56
|
},
|
|
56
57
|
|
|
57
58
|
// ========================================================================
|
|
@@ -70,6 +71,7 @@ export const i18n = {
|
|
|
70
71
|
checking: { zh: '正在验证身份...', en: 'Verifying identity...' },
|
|
71
72
|
loginTitle: { zh: 'Principles Console', en: 'Principles Console' },
|
|
72
73
|
loginSubtitle: { zh: 'AI Agent 进化流程监控平台', en: 'AI Agent Evolution Monitoring Platform' },
|
|
74
|
+
tokenLabel: { zh: 'Gateway Token', en: 'Gateway Token' },
|
|
73
75
|
tokenPlaceholder: { zh: '请输入您的 Gateway Token', en: 'Enter your Gateway Token' },
|
|
74
76
|
tokenHint: { zh: '在服务器上运行 openclaw config get gateway.auth.token 获取 Token', en: 'Run openclaw config get gateway.auth.token on your server to get the Token' },
|
|
75
77
|
loginButton: { zh: '登 录', en: 'Sign In' },
|
|
@@ -184,6 +186,11 @@ export const i18n = {
|
|
|
184
186
|
createdAt: { zh: '创建时间', en: 'Created At' },
|
|
185
187
|
failureMode: { zh: '失败模式', en: 'Failure Mode' },
|
|
186
188
|
relatedThinking: { zh: '相关思维', en: 'Thinking Hits' },
|
|
189
|
+
|
|
190
|
+
// Missing i18n keys (previously hardcoded in components)
|
|
191
|
+
noGfiToday: { zh: '今日暂无 GFI 记录', en: 'No GFI data today' },
|
|
192
|
+
noWorkspacesFound: { zh: '未找到已启用的工作区', en: 'No enabled workspaces found' },
|
|
193
|
+
refreshing: { zh: '刷新中', en: 'Refreshing' },
|
|
187
194
|
},
|
|
188
195
|
|
|
189
196
|
// ========================================================================
|
|
@@ -213,6 +220,9 @@ export const i18n = {
|
|
|
213
220
|
// Actions
|
|
214
221
|
approve: { zh: '批准', en: 'Approve' },
|
|
215
222
|
reject: { zh: '拒绝', en: 'Reject' },
|
|
223
|
+
|
|
224
|
+
// Error messages (previously hardcoded English)
|
|
225
|
+
reviewFailed: { zh: '审核操作失败', en: 'Review operation failed' },
|
|
216
226
|
},
|
|
217
227
|
|
|
218
228
|
// ========================================================================
|
|
@@ -225,26 +235,75 @@ export const i18n = {
|
|
|
225
235
|
dormant: { zh: '休眠', en: 'Dormant' },
|
|
226
236
|
effective: { zh: '有效', en: 'Effective' },
|
|
227
237
|
|
|
228
|
-
//
|
|
229
|
-
|
|
230
|
-
|
|
238
|
+
// Charts and sections
|
|
239
|
+
coverageTrend: { zh: '覆盖率趋势', en: 'Coverage Trend' },
|
|
240
|
+
emptyCoverageTrend: { zh: '今日暂无覆盖率记录', en: 'No coverage data yet' },
|
|
241
|
+
emptyCoverageTrendDesc: { zh: '当 AI 开始执行任务后,覆盖率会自动记录。', en: 'Coverage is tracked automatically once AI starts working.' },
|
|
242
|
+
scenarioHeatmap: { zh: '场景热力图', en: 'Scenario Heatmap' },
|
|
243
|
+
emptyScenarioMatrix: { zh: '暂无场景数据', en: 'No scenario data yet' },
|
|
244
|
+
emptyScenarioMatrixDesc: { zh: '模型触发场景后会在此显示。', en: 'Scenarios will appear here when models are triggered.' },
|
|
245
|
+
emptyAllActive: { zh: '所有模型都在使用中', en: 'All models are active' },
|
|
246
|
+
emptyAllActiveDesc: { zh: '没有休眠模型。', en: 'No dormant models.' },
|
|
247
|
+
noModelsYet: { zh: '暂无思维模型数据', en: 'No thinking model data yet' },
|
|
248
|
+
noModelsYetDesc: { zh: 'AI 开始使用后,这里会显示思维模型的使用情况。', en: 'Thinking model usage will appear here once AI starts working.' },
|
|
249
|
+
noMatches: { zh: '没有匹配的模型', en: 'No models match your filters.' },
|
|
250
|
+
|
|
251
|
+
// Recommendations
|
|
252
|
+
dormantModels: { zh: '休眠模型', en: 'Dormant Models' },
|
|
253
|
+
reinforce: { zh: '保持', en: 'Reinforce' },
|
|
254
|
+
rework: { zh: '重构', en: 'Rework' },
|
|
255
|
+
archive: { zh: '归档', en: 'Archive' },
|
|
256
|
+
filterByRec: { zh: '按推荐过滤', en: 'Filter by Rec' },
|
|
231
257
|
|
|
232
258
|
// Detail sections
|
|
233
|
-
outcomeStats: { zh: '
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
// Outcome stats
|
|
259
|
+
outcomeStats: { zh: '效果统计', en: 'Outcome Stats' },
|
|
260
|
+
usageTrend: { zh: '使用趋势', en: 'Usage Trend' },
|
|
261
|
+
emptyUsageTrend: { zh: '暂无使用趋势记录', en: 'No usage trend data yet' },
|
|
262
|
+
emptyUsageTrendDesc: { zh: '该模型尚未被触发使用。', en: 'This model has not been triggered yet.' },
|
|
239
263
|
success: { zh: '成功', en: 'Success' },
|
|
240
264
|
failure: { zh: '失败', en: 'Failure' },
|
|
241
265
|
pain: { zh: '痛点', en: 'Pain' },
|
|
242
266
|
correction: { zh: '纠正', en: 'Correction' },
|
|
267
|
+
scenarioDistribution: { zh: '场景分布', en: 'Scenario Distribution' },
|
|
268
|
+
recentEvents: { zh: '最近事件', en: 'Recent Events' },
|
|
269
|
+
toolContext: { zh: '工具上下文', en: 'Tool Context' },
|
|
270
|
+
painContext: { zh: '痛点上下文', en: 'Pain Context' },
|
|
271
|
+
principleContext: { zh: '原则上下文', en: 'Principle Context' },
|
|
272
|
+
trigger: { zh: '触发条件', en: 'Trigger' },
|
|
273
|
+
antiPattern: { zh: '禁止行为', en: 'Anti-Pattern' },
|
|
274
|
+
thinkingOsSource: { zh: '思维模型定义来源', en: 'Thinking Model Source' },
|
|
275
|
+
|
|
276
|
+
// Empty state
|
|
277
|
+
emptyTitle: { zh: '选择一个思维模型', en: 'Select a thinking model' },
|
|
278
|
+
emptyDesc: { zh: '点击左侧列表中的模型,查看场景分布和最近事件', en: 'Click a model from the list to inspect scenario coverage and recent events' },
|
|
279
|
+
noDataTitle: { zh: '思维模型定义', en: 'Thinking Model Definitions' },
|
|
280
|
+
noDataDesc: { zh: '以下是 10 个思维模型的定义。当 AI 开始使用后,这里会显示每个模型的使用统计。', en: 'Below are 10 thinking model definitions. Usage statistics will appear once the AI starts working.' },
|
|
281
|
+
noUsageDataYet: { zh: '该模型暂无使用数据', en: 'No usage data for this model yet' },
|
|
282
|
+
noUsageDataDesc: { zh: '当模型被触发后,使用趋势和事件会在此显示。', en: 'Usage trends and events will appear once the model is triggered.' },
|
|
243
283
|
|
|
244
284
|
// Table
|
|
245
285
|
hits: { zh: '命中', en: 'Hits' },
|
|
246
286
|
successRate: { zh: '成功率', en: 'Success Rate' },
|
|
247
287
|
failureRate: { zh: '失败率', en: 'Failure Rate' },
|
|
288
|
+
|
|
289
|
+
// Comparison mode
|
|
290
|
+
compare: { zh: '对比', en: 'Compare' },
|
|
291
|
+
compareSelected: { zh: '对比选中', en: 'Compare Selected' },
|
|
292
|
+
exitCompare: { zh: '退出对比', en: 'Exit Compare' },
|
|
293
|
+
comparisonTitle: { zh: '模型对比', en: 'Model Comparison' },
|
|
294
|
+
comparisonEmpty: { zh: '请选择至少 2 个模型进行对比', en: 'Select at least 2 models to compare' },
|
|
295
|
+
|
|
296
|
+
// Search and sort
|
|
297
|
+
filterAll: { zh: '全部', en: 'All' },
|
|
298
|
+
searchPlaceholder: { zh: '按名称或场景搜索...', en: 'Search by name or scenario...' },
|
|
299
|
+
sortByHits: { zh: '命中数', en: 'Hits' },
|
|
300
|
+
sortBySuccessRate: { zh: '成功率', en: 'Success Rate' },
|
|
301
|
+
sortByName: { zh: '名称', en: 'Name' },
|
|
302
|
+
|
|
303
|
+
// Loading states
|
|
304
|
+
loadingDetail: { zh: '正在加载模型详情...', en: 'Loading model details...' },
|
|
305
|
+
loadingComparison: { zh: '正在加载对比数据...', en: 'Loading comparison data...' },
|
|
306
|
+
modelLoading: { zh: '加载中', en: 'Loading' },
|
|
248
307
|
},
|
|
249
308
|
|
|
250
309
|
// ========================================================================
|
|
@@ -276,6 +335,11 @@ export const i18n = {
|
|
|
276
335
|
analyzing: { zh: '分析中', en: 'Analyzing' },
|
|
277
336
|
principle_generated: { zh: '原则生成', en: 'Principle Generated' },
|
|
278
337
|
completed: { zh: '已完成', en: 'Completed' },
|
|
338
|
+
// Principle status labels
|
|
339
|
+
candidate: { zh: '候选', en: 'Candidate' },
|
|
340
|
+
probation: { zh: '试用', en: 'Probation' },
|
|
341
|
+
active: { zh: '活跃', en: 'Active' },
|
|
342
|
+
deprecated: { zh: '废弃', en: 'Deprecated' },
|
|
279
343
|
},
|
|
280
344
|
|
|
281
345
|
// Status filter
|
|
@@ -306,6 +370,19 @@ export const i18n = {
|
|
|
306
370
|
evolutionTimeline: { zh: '进化时间线', en: 'Evolution Timeline' },
|
|
307
371
|
detailedEvents: { zh: '详细事件', en: 'Detailed Events' },
|
|
308
372
|
reason: { zh: '原因', en: 'Reason' },
|
|
373
|
+
|
|
374
|
+
// EvolutionPage panel headers (previously hardcoded Chinese)
|
|
375
|
+
currentStage: { zh: '当前阶段', en: 'Current Stage' },
|
|
376
|
+
principleLifecycle: { zh: '原则生命周期', en: 'Principle Lifecycle' },
|
|
377
|
+
nocturnalTrainingStatus: { zh: '夜间训练状态', en: 'Nocturnal Training Status' },
|
|
378
|
+
trainingQueue: { zh: '训练队列', en: 'Training Queue' },
|
|
379
|
+
arbiterPassRate: { zh: 'Arbiter 通过率', en: 'Arbiter Pass Rate' },
|
|
380
|
+
orpoSampleCount: { zh: 'ORPO 样本数', en: 'ORPO Sample Count' },
|
|
381
|
+
modelDeployments: { zh: '模型部署', en: 'Model Deployments' },
|
|
382
|
+
deploymentCount: { zh: '个', en: 'deployments' },
|
|
383
|
+
pendingShort: { zh: '待', en: 'P' },
|
|
384
|
+
inProgressShort: { zh: '中', en: 'I' },
|
|
385
|
+
completedShort: { zh: '完', en: 'C' },
|
|
309
386
|
},
|
|
310
387
|
|
|
311
388
|
// ========================================================================
|
|
@@ -1,45 +1,34 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import { ChevronLeft } from 'lucide-react';
|
|
1
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { ChevronLeft, Clock, Activity, Shield, Zap, BookOpen } from 'lucide-react';
|
|
3
3
|
import { api } from '../api';
|
|
4
4
|
import type {
|
|
5
|
-
OverviewResponse,
|
|
6
|
-
SampleDetailResponse,
|
|
7
|
-
SamplesResponse,
|
|
8
|
-
ThinkingModelDetailResponse,
|
|
9
|
-
ThinkingOverviewResponse,
|
|
10
5
|
EvolutionTasksResponse,
|
|
11
6
|
EvolutionTraceResponse,
|
|
12
7
|
EvolutionStatsResponse,
|
|
13
|
-
OverviewHealthResponse,
|
|
14
8
|
EvolutionPrinciplesResponse,
|
|
15
|
-
FeedbackGfiResponse,
|
|
16
|
-
GateStatsResponse,
|
|
17
|
-
GateBlockItem,
|
|
18
|
-
EmpathyEvent,
|
|
19
|
-
FeedbackGateBlock,
|
|
20
9
|
} from '../types';
|
|
21
|
-
import {
|
|
10
|
+
import { DonutChart, GroupedBarChart, TimeRangeSelector, StatusBadge, EmptyState } from '../charts';
|
|
22
11
|
import { useI18n } from '../i18n/ui';
|
|
23
12
|
import { formatPercent, formatDate, formatDuration } from '../utils/format';
|
|
24
13
|
import { Loading, ErrorState } from '../components';
|
|
25
14
|
|
|
26
15
|
const STAGE_COLORS: Record<string, string> = {
|
|
27
|
-
pain_detected: '
|
|
28
|
-
queued: '
|
|
29
|
-
started: '
|
|
16
|
+
pain_detected: 'var(--error)',
|
|
17
|
+
queued: 'var(--warning)',
|
|
18
|
+
started: 'var(--info)',
|
|
30
19
|
analyzing: '#8b5cf6',
|
|
31
|
-
principle_generated: '
|
|
32
|
-
completed: '
|
|
20
|
+
principle_generated: 'var(--success)',
|
|
21
|
+
completed: 'var(--success)',
|
|
33
22
|
};
|
|
34
23
|
|
|
35
|
-
const
|
|
36
|
-
pain_detected
|
|
37
|
-
queued
|
|
38
|
-
started
|
|
39
|
-
analyzing
|
|
40
|
-
principle_generated
|
|
41
|
-
completed
|
|
42
|
-
|
|
24
|
+
const STAGE_LABEL_KEYS: string[] = [
|
|
25
|
+
'pain_detected',
|
|
26
|
+
'queued',
|
|
27
|
+
'started',
|
|
28
|
+
'analyzing',
|
|
29
|
+
'principle_generated',
|
|
30
|
+
'completed',
|
|
31
|
+
];
|
|
43
32
|
|
|
44
33
|
export function EvolutionPage() {
|
|
45
34
|
const { t } = useI18n();
|
|
@@ -86,10 +75,10 @@ export function EvolutionPage() {
|
|
|
86
75
|
|
|
87
76
|
// Prepare donut chart data
|
|
88
77
|
const statusSegments = [
|
|
89
|
-
{ label: t('evolution.pending'), value: stats.pending, color: '
|
|
90
|
-
{ label: t('evolution.inProgress'), value: stats.inProgress, color: '
|
|
91
|
-
{ label: t('evolution.completed'), value: stats.completed, color: '
|
|
92
|
-
{ label: t('evolution.failed'), value: stats.failed, color: '
|
|
78
|
+
{ label: t('evolution.pending'), value: stats.pending, color: 'var(--warning)' },
|
|
79
|
+
{ label: t('evolution.inProgress'), value: stats.inProgress, color: 'var(--info)' },
|
|
80
|
+
{ label: t('evolution.completed'), value: stats.completed, color: 'var(--success)' },
|
|
81
|
+
{ label: t('evolution.failed'), value: stats.failed, color: 'var(--error)' },
|
|
93
82
|
].filter(s => s.value > 0);
|
|
94
83
|
|
|
95
84
|
return (
|
|
@@ -112,23 +101,15 @@ export function EvolutionPage() {
|
|
|
112
101
|
{/* Current Stage Indicator */}
|
|
113
102
|
{evoPrinciples && (
|
|
114
103
|
<section className="panel" style={{ marginBottom: 'var(--space-5)' }}>
|
|
115
|
-
<h3
|
|
104
|
+
<h3>{t('evolution.currentStage')}</h3>
|
|
116
105
|
<div style={{ display: 'flex', alignItems: 'center', gap: 'var(--space-3)', padding: 'var(--space-3) 0' }}>
|
|
117
|
-
<span
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
background: 'var(--accent)',
|
|
123
|
-
color: '#fff',
|
|
124
|
-
border: '2px solid var(--accent)',
|
|
125
|
-
}}>
|
|
126
|
-
{evoPrinciples.activeStage === 'pending' ? <><Clock size={16} style={{marginRight: 6, verticalAlign: 'middle'}}/>{t('evolution.activeStage.pending')}</> :
|
|
127
|
-
evoPrinciples.activeStage === 'in_progress' ? <><Activity size={16} style={{marginRight: 6, verticalAlign: 'middle'}}/>{t('evolution.activeStage.in_progress')}</> :
|
|
128
|
-
evoPrinciples.activeStage === 'completed' ? <><Shield size={16} style={{marginRight: 6, verticalAlign: 'middle'}}/>{t('evolution.activeStage.completed')}</> :
|
|
129
|
-
evoPrinciples.activeStage === 'idle' ? <><Zap size={16} style={{marginRight: 6, verticalAlign: 'middle'}}/>{t('evolution.activeStage.idle')}</> : evoPrinciples.activeStage}
|
|
106
|
+
<span className="stage-badge">
|
|
107
|
+
{evoPrinciples.activeStage === 'pending' ? <><Clock size={16} /><span>{t('evolution.activeStage.pending')}</span></> :
|
|
108
|
+
evoPrinciples.activeStage === 'in_progress' ? <><Activity size={16} /><span>{t('evolution.activeStage.in_progress')}</span></> :
|
|
109
|
+
evoPrinciples.activeStage === 'completed' ? <><Shield size={16} /><span>{t('evolution.activeStage.completed')}</span></> :
|
|
110
|
+
evoPrinciples.activeStage === 'idle' ? <><Zap size={16} /><span>{t('evolution.activeStage.idle')}</span></> : evoPrinciples.activeStage}
|
|
130
111
|
</span>
|
|
131
|
-
<span style={{ color: 'var(--text-secondary)'
|
|
112
|
+
<span className="text-sm" style={{ color: 'var(--text-secondary)' }}>
|
|
132
113
|
{t('evolution.enhancementLoopStatus')}
|
|
133
114
|
</span>
|
|
134
115
|
</div>
|
|
@@ -139,12 +120,12 @@ export function EvolutionPage() {
|
|
|
139
120
|
{evoPrinciples && (
|
|
140
121
|
<div className="grid two-columns" style={{ marginBottom: 'var(--space-5)' }}>
|
|
141
122
|
<section className="panel">
|
|
142
|
-
<h3><BookOpen size={16}
|
|
123
|
+
<h3><BookOpen size={16} />{t('evolution.principleLifecycle')}</h3>
|
|
143
124
|
<div className="pill-row" style={{ marginBottom: 'var(--space-3)' }}>
|
|
144
|
-
<StatusBadge variant="warning"
|
|
145
|
-
<StatusBadge variant="info"
|
|
146
|
-
<StatusBadge variant="success"
|
|
147
|
-
<StatusBadge variant="error"
|
|
125
|
+
<StatusBadge variant="warning">{t('evolution.stageLabels.candidate')}: {evoPrinciples.principles.summary.candidate}</StatusBadge>
|
|
126
|
+
<StatusBadge variant="info">{t('evolution.stageLabels.probation')}: {evoPrinciples.principles.summary.probation}</StatusBadge>
|
|
127
|
+
<StatusBadge variant="success">{t('evolution.stageLabels.active')}: {evoPrinciples.principles.summary.active}</StatusBadge>
|
|
128
|
+
<StatusBadge variant="error">{t('evolution.stageLabels.deprecated')}: {evoPrinciples.principles.summary.deprecated}</StatusBadge>
|
|
148
129
|
</div>
|
|
149
130
|
{evoPrinciples.principles.recent.length > 0 && (
|
|
150
131
|
<div className="stack">
|
|
@@ -161,23 +142,23 @@ export function EvolutionPage() {
|
|
|
161
142
|
)}
|
|
162
143
|
</section>
|
|
163
144
|
<section className="panel">
|
|
164
|
-
<h3>💤
|
|
145
|
+
<h3>💤 {t('evolution.nocturnalTrainingStatus')}</h3>
|
|
165
146
|
<div className="stack">
|
|
166
147
|
<div className="row-card">
|
|
167
|
-
<strong
|
|
168
|
-
<span
|
|
148
|
+
<strong>{t('evolution.trainingQueue')}</strong>
|
|
149
|
+
<span>{t('evolution.pendingShort')}: {evoPrinciples.nocturnalTraining.queue.pending} | {t('evolution.inProgressShort')}: {evoPrinciples.nocturnalTraining.queue.inProgress} | {t('evolution.completedShort')}: {evoPrinciples.nocturnalTraining.queue.completed}</span>
|
|
169
150
|
</div>
|
|
170
151
|
<div className="row-card">
|
|
171
|
-
<strong>
|
|
152
|
+
<strong>{t('evolution.arbiterPassRate')}</strong>
|
|
172
153
|
<span>{(evoPrinciples.nocturnalTraining.arbiterPassRate * 100).toFixed(1)}%</span>
|
|
173
154
|
</div>
|
|
174
155
|
<div className="row-card">
|
|
175
|
-
<strong>
|
|
156
|
+
<strong>{t('evolution.orpoSampleCount')}</strong>
|
|
176
157
|
<span>{evoPrinciples.nocturnalTraining.orpoSampleCount}</span>
|
|
177
158
|
</div>
|
|
178
159
|
<div className="row-card">
|
|
179
|
-
<strong
|
|
180
|
-
<span>{evoPrinciples.nocturnalTraining.deployments.length}
|
|
160
|
+
<strong>{t('evolution.modelDeployments')}</strong>
|
|
161
|
+
<span>{evoPrinciples.nocturnalTraining.deployments.length} {t('evolution.deploymentCount')}</span>
|
|
181
162
|
</div>
|
|
182
163
|
</div>
|
|
183
164
|
</section>
|
|
@@ -80,7 +80,7 @@ export function GateMonitorPage() {
|
|
|
80
80
|
<section className="panel">
|
|
81
81
|
<h3>🔐 {t('gate.trustEngine')}</h3>
|
|
82
82
|
<div style={{ padding: 'var(--space-3) 0' }}>
|
|
83
|
-
<div style={{ fontSize: '
|
|
83
|
+
<div style={{ fontSize: '1.5rem', fontWeight: 700 }}>Stage {gateStats.trust.stage}: {gateStats.trust.status}</div>
|
|
84
84
|
<div style={{ marginTop: 'var(--space-2)', width: '100%', height: '12px', background: 'var(--bg-sunken)', borderRadius: '6px' }}>
|
|
85
85
|
<div style={{ width: `${gateStats.trust.score}%`, height: '100%', background: 'var(--info)', borderRadius: '6px' }} />
|
|
86
86
|
</div>
|
|
@@ -92,11 +92,11 @@ export function GateMonitorPage() {
|
|
|
92
92
|
<section className="panel">
|
|
93
93
|
<h3>🌱 {t('gate.evolutionEngine')}</h3>
|
|
94
94
|
<div style={{ padding: 'var(--space-3) 0' }}>
|
|
95
|
-
<div style={{ fontSize: '
|
|
95
|
+
<div style={{ fontSize: '1.5rem', fontWeight: 700 }}>{gateStats.evolution.tier} ({gateStats.evolution.status})</div>
|
|
96
96
|
<div style={{ marginTop: 'var(--space-2)', width: '100%', height: '12px', background: 'var(--bg-sunken)', borderRadius: '6px' }}>
|
|
97
97
|
<div style={{ width: `${Math.min(100, gateStats.evolution.points / 10)}%`, height: '100%', background: 'var(--success)', borderRadius: '6px' }} />
|
|
98
98
|
</div>
|
|
99
|
-
<div style={{
|
|
99
|
+
<div className="text-sm" style={{ color: 'var(--text-secondary)', marginTop: 'var(--space-1)' }}>
|
|
100
100
|
{t('gate.points')}: {gateStats.evolution.points}
|
|
101
101
|
</div>
|
|
102
102
|
</div>
|
|
@@ -44,7 +44,7 @@ export function LoginPage() {
|
|
|
44
44
|
|
|
45
45
|
<form className="login-form" onSubmit={handleSubmit}>
|
|
46
46
|
<div className="form-group">
|
|
47
|
-
<label htmlFor="token">
|
|
47
|
+
<label htmlFor="token">{t('auth.tokenLabel')}</label>
|
|
48
48
|
<input
|
|
49
49
|
id="token"
|
|
50
50
|
type="password"
|
|
@@ -52,6 +52,7 @@ export function LoginPage() {
|
|
|
52
52
|
onChange={(e) => setToken(e.target.value)}
|
|
53
53
|
placeholder={t('auth.tokenPlaceholder')}
|
|
54
54
|
autoComplete="off"
|
|
55
|
+
aria-label={t('auth.tokenLabel')}
|
|
55
56
|
/>
|
|
56
57
|
<span className="form-hint">
|
|
57
58
|
{t('auth.tokenHint')}
|
|
@@ -196,11 +196,11 @@ function WorkspaceHealthPanel({ entry }: { entry: WorkspaceHealthEntry }) {
|
|
|
196
196
|
{/* Full-width GFI trend chart */}
|
|
197
197
|
<section style={{ marginTop: 'var(--space-4)', borderTop: '1px solid var(--border)', paddingTop: 'var(--space-4)' }}>
|
|
198
198
|
<div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginBottom: 8 }}>
|
|
199
|
-
<span
|
|
200
|
-
|
|
199
|
+
<span className="text-lg text-semibold">
|
|
200
|
+
{t('overview.health.gfi')} · {t('overview.recentTrend')}
|
|
201
201
|
</span>
|
|
202
|
-
<span style={{
|
|
203
|
-
|
|
202
|
+
<span className="text-sm" style={{ color: 'var(--text-secondary)' }}>
|
|
203
|
+
{t('overview.health.peakToday')}: {h.gfi.peakToday}
|
|
204
204
|
</span>
|
|
205
205
|
</div>
|
|
206
206
|
{h.gfi.trend.length >= 2 ? (
|
|
@@ -236,10 +236,11 @@ function ThinkingModelDistribution({
|
|
|
236
236
|
modelBreakdown?: Array<{ modelId: string; hits: number }>;
|
|
237
237
|
definitions?: Array<{ modelId: string; name: string; description: string }>;
|
|
238
238
|
}) {
|
|
239
|
+
const { t } = useI18n();
|
|
239
240
|
if (!definitions || definitions.length === 0) {
|
|
240
241
|
return (
|
|
241
|
-
<div style={{
|
|
242
|
-
|
|
242
|
+
<div className="text-center" style={{ padding: 'var(--space-3)', color: 'var(--text-secondary)', fontSize: 'var(--text-lg, 0.8rem)' }}>
|
|
243
|
+
{t('overview.noDefinitions')}
|
|
243
244
|
</div>
|
|
244
245
|
);
|
|
245
246
|
}
|
|
@@ -249,8 +250,8 @@ function ThinkingModelDistribution({
|
|
|
249
250
|
|
|
250
251
|
if (!hasAnyHits) {
|
|
251
252
|
return (
|
|
252
|
-
<div style={{
|
|
253
|
-
|
|
253
|
+
<div className="text-center" style={{ padding: 'var(--space-3)', color: 'var(--text-secondary)', fontSize: 'var(--text-lg, 0.8rem)' }}>
|
|
254
|
+
{t('overview.noUsage')}
|
|
254
255
|
</div>
|
|
255
256
|
);
|
|
256
257
|
}
|
|
@@ -472,7 +473,7 @@ export function OverviewPage() {
|
|
|
472
473
|
) : (
|
|
473
474
|
<section className="panel" style={{ marginBottom: 'var(--space-4)' }}>
|
|
474
475
|
<div style={{ textAlign: 'center', padding: 'var(--space-4)', color: 'var(--text-secondary)' }}>
|
|
475
|
-
{t('overview.health.noWorkspaces') || '
|
|
476
|
+
{t('overview.health.noWorkspaces') || t('overview.noWorkspacesFound')}
|
|
476
477
|
</div>
|
|
477
478
|
</section>
|
|
478
479
|
)}
|
|
@@ -46,7 +46,7 @@ export function SamplesPage() {
|
|
|
46
46
|
setData(samples);
|
|
47
47
|
setSelected(detail);
|
|
48
48
|
} catch (err) {
|
|
49
|
-
setError(err instanceof Error ? err.message : '
|
|
49
|
+
setError(err instanceof Error ? err.message : t('samples.reviewFailed'));
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -122,11 +122,11 @@ export function SamplesPage() {
|
|
|
122
122
|
</div>
|
|
123
123
|
<article>
|
|
124
124
|
<h4>{t('samples.badAttempt')}</h4>
|
|
125
|
-
<pre>{selected.badAttempt.rawText || selected.badAttempt.sanitizedText}</pre>
|
|
125
|
+
<pre className="sample-pre">{selected.badAttempt.rawText || selected.badAttempt.sanitizedText}</pre>
|
|
126
126
|
</article>
|
|
127
127
|
<article>
|
|
128
128
|
<h4>{t('samples.userCorrection')}</h4>
|
|
129
|
-
<pre>{selected.userCorrection.rawText}</pre>
|
|
129
|
+
<pre className="sample-pre">{selected.userCorrection.rawText}</pre>
|
|
130
130
|
</article>
|
|
131
131
|
<article>
|
|
132
132
|
<h4>{t('samples.recoveryToolSpan')}</h4>
|