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.
@@ -2,7 +2,7 @@
2
2
  "id": "principles-disciple",
3
3
  "name": "Principles Disciple",
4
4
  "description": "Evolutionary programming agent framework with strategic guardrails and reflection loops.",
5
- "version": "1.12.0",
5
+ "version": "1.14.0",
6
6
  "skills": [
7
7
  "./skills"
8
8
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "principles-disciple",
3
- "version": "1.12.0",
3
+ "version": "1.14.0",
4
4
  "description": "Native OpenClaw plugin for Principles Disciple",
5
5
  "type": "module",
6
6
  "main": "./dist/bundle.js",
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
- // Empty state
229
- emptyTitle: { zh: '选择一个思维模型', en: 'Select a thinking model' },
230
- emptyDesc: { zh: '点击左侧列表中的模型,查看场景分布和最近事件', en: 'Click a model from the list to inspect scenario coverage and recent events' },
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: '结果统计', en: 'Outcome Stats' },
234
- scenarioDistribution: { zh: '场景分布', en: 'Scenario Distribution' },
235
- recentEvents: { zh: '最近事件', en: 'Recent Events' },
236
- noScenariosYet: { zh: '暂无场景', en: 'No scenarios yet' },
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, { useCallback, useEffect, useMemo, useState } from '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 { Sparkline, DonutChart, GroupedBarChart, TimeRangeSelector, CollapsiblePanel, StatusBadge, EmptyState } from '../charts';
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: '#ef4444',
28
- queued: '#f59e0b',
29
- started: '#3b82f6',
16
+ pain_detected: 'var(--error)',
17
+ queued: 'var(--warning)',
18
+ started: 'var(--info)',
30
19
  analyzing: '#8b5cf6',
31
- principle_generated: '#22c55e',
32
- completed: '#22c55e',
20
+ principle_generated: 'var(--success)',
21
+ completed: 'var(--success)',
33
22
  };
34
23
 
35
- const STAGE_LABELS: Record<string, string> = {
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: '#f59e0b' },
90
- { label: t('evolution.inProgress'), value: stats.inProgress, color: '#3b82f6' },
91
- { label: t('evolution.completed'), value: stats.completed, color: '#22c55e' },
92
- { label: t('evolution.failed'), value: stats.failed, color: '#ef4444' },
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>当前阶段</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 style={{
118
- padding: 'var(--space-2) var(--space-4)',
119
- borderRadius: 'var(--radius-lg)',
120
- fontSize: '15px',
121
- fontWeight: 600,
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)', fontSize: '14px' }}>
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} style={{marginRight: 6, verticalAlign: 'middle'}}/>原则生命周期</h3>
123
+ <h3><BookOpen size={16} />{t('evolution.principleLifecycle')}</h3>
143
124
  <div className="pill-row" style={{ marginBottom: 'var(--space-3)' }}>
144
- <StatusBadge variant="warning">候选: {evoPrinciples.principles.summary.candidate}</StatusBadge>
145
- <StatusBadge variant="info">试用: {evoPrinciples.principles.summary.probation}</StatusBadge>
146
- <StatusBadge variant="success">活跃: {evoPrinciples.principles.summary.active}</StatusBadge>
147
- <StatusBadge variant="error">废弃: {evoPrinciples.principles.summary.deprecated}</StatusBadge>
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>💤 夜间训练状态</h3>
145
+ <h3>💤 {t('evolution.nocturnalTrainingStatus')}</h3>
165
146
  <div className="stack">
166
147
  <div className="row-card">
167
- <strong>训练队列</strong>
168
- <span>待: {evoPrinciples.nocturnalTraining.queue.pending} | 中: {evoPrinciples.nocturnalTraining.queue.inProgress} | 完: {evoPrinciples.nocturnalTraining.queue.completed}</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>Arbiter 通过率</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>ORPO 样本数</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>模型部署</strong>
180
- <span>{evoPrinciples.nocturnalTraining.deployments.length} 个</span>
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>
@@ -136,5 +136,3 @@ export function FeedbackPage() {
136
136
  </div>
137
137
  );
138
138
  }
139
-
140
- // ===== Phase 6: Gate Monitor Page =====
@@ -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: '24px', fontWeight: 700 }}>Stage {gateStats.trust.stage}: {gateStats.trust.status}</div>
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: '24px', fontWeight: 700 }}>{gateStats.evolution.tier} ({gateStats.evolution.status})</div>
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={{ fontSize: '13px', color: 'var(--text-secondary)', marginTop: 'var(--space-1)' }}>
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">Gateway Token</label>
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 style={{ fontSize: '0.8rem', fontWeight: 600, color: 'var(--text-primary)' }}>
200
- 📈 {t('overview.health.gfi')} · 今日趋势
199
+ <span className="text-lg text-semibold">
200
+ {t('overview.health.gfi')} · {t('overview.recentTrend')}
201
201
  </span>
202
- <span style={{ fontSize: '0.7rem', color: 'var(--text-secondary)' }}>
203
- 今日峰值: {h.gfi.peakToday}
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={{ textAlign: 'center', padding: 'var(--space-3)', color: 'var(--text-secondary)', fontSize: '0.85rem' }}>
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={{ textAlign: 'center', padding: 'var(--space-3)', color: 'var(--text-secondary)', fontSize: '0.85rem' }}>
253
- 暂无思维模型使用记录。AI 开始使用后这里会显示数据。
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') || 'No enabled workspaces found'}
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 : 'Review operation failed');
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>