Skip to content

Commit 9b161a6

Browse files
committed
Release v2.11.0
Surface the Mission Control header + live metrics/cooldown on the active Workflow page (extracted into a shared WorkflowHeader component) and fix the v2.10.1 CI lint failure (empty catch in HistoryDetailPage + assorted react-hooks warnings). Pre-commit lint-staged now runs eslint with --max-warnings 0.
1 parent 796e7bb commit 9b161a6

17 files changed

Lines changed: 496 additions & 369 deletions

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
66

7+
## [2.11.0] - 2026-04-27
8+
9+
### Added
10+
- Workflow page now shows the same Mission Control header (status, duration, edit) and Live Metrics strip (avg RT, TPS, last RT) + cooldown countdown that History Detail had — exposed via `liveMetrics` and `cooldown` from `useWorkflow`
11+
- New shared `WorkflowHeader` component used by both the active Workflow page and History Detail
12+
13+
### Changed
14+
- Refactored `HistoryDetailPage` to compose `WorkflowHeader` instead of duplicating header markup (~280 line reduction)
15+
- Tightened pre-commit lint gate: `lint-staged` now runs `eslint --max-warnings 0` on staged frontend files
16+
17+
### Fixed
18+
- CI lint failure on v2.10.1: removed empty `catch {}` block in `HistoryDetailPage` and silenced react-hooks warnings via targeted disables (no behavior change)
19+
- Various react-hooks lint warnings across `ConfigPanel`, `MonitorPage`, `PlaygroundPage`, `WorkflowConfigPanel`, `WorkflowProgress`
20+
721
## [2.10.1] - 2026-04-23
822

923
### Changed

backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "llm-benchmark-backend",
3-
"version": "2.10.1",
3+
"version": "2.11.0",
44
"description": "LLM API Radar - Backend",
55
"main": "dist/index.js",
66
"scripts": {

frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "frontend",
33
"private": true,
4-
"version": "2.10.1",
4+
"version": "2.11.0",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

frontend/src/App.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { HistoryDetailPage } from './components/HistoryDetailPage';
1111
import { WorkflowConfigPanel } from './components/WorkflowConfigPanel';
1212
import { WorkflowProgress } from './components/WorkflowProgress';
1313
import { WorkflowResults } from './components/WorkflowResults';
14+
import { WorkflowHeader } from './components/WorkflowHeader';
1415
import { SettingsPage } from './components/SettingsPage';
1516
import { PlaygroundPage } from './components/PlaygroundPage';
1617
import { MonitorPage } from './components/MonitorPage';
@@ -87,6 +88,8 @@ function App() {
8788
reconnectActiveWorkflow,
8889
workflowsLoaded,
8990
taskProgress,
91+
liveMetrics,
92+
cooldown,
9093
} = useWorkflow();
9194

9295
const navigate = useNavigate();
@@ -496,7 +499,17 @@ function App() {
496499
/>
497500

498501
{/* Live Progress & Results */}
499-
{currentWorkflow && <WorkflowProgress workflow={currentWorkflow} taskProgress={taskProgress} />}
502+
{currentWorkflow && (
503+
<WorkflowHeader workflow={currentWorkflow} onCancel={cancelWorkflow} onExport={exportWorkflow} />
504+
)}
505+
{currentWorkflow && (
506+
<WorkflowProgress
507+
workflow={currentWorkflow}
508+
taskProgress={taskProgress}
509+
liveMetrics={liveMetrics}
510+
cooldown={cooldown}
511+
/>
512+
)}
500513
{currentWorkflow?.summary && <WorkflowResults workflow={currentWorkflow} onExport={exportWorkflow} />}
501514

502515
{/* Recent Workflows */}

frontend/src/components/ConfigPanel.tsx

Lines changed: 49 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,55 @@ interface SelectedModel {
5151
color: string;
5252
}
5353

54+
function QuickButtons({
55+
options,
56+
value,
57+
onChange,
58+
color = 'accent-teal',
59+
}: {
60+
options: { label: string; value: number }[];
61+
value: number;
62+
onChange: (v: number) => void;
63+
color?: string;
64+
}) {
65+
return (
66+
<div className="flex flex-wrap gap-1 mb-1.5">
67+
{options.map((opt) => (
68+
<button
69+
key={opt.value}
70+
onClick={() => onChange(opt.value)}
71+
className={`text-[11px] px-2.5 py-1.5 rounded border transition-all font-medium font-mono ${
72+
value === opt.value
73+
? `border-${color}/40 bg-${color}/8 text-${color}`
74+
: 'border-border text-text-tertiary hover:border-border-hover'
75+
}`}
76+
style={{
77+
...(value === opt.value
78+
? {
79+
borderColor:
80+
color === 'accent-teal'
81+
? 'rgba(115,191,105,0.4)'
82+
: color === 'accent-blue'
83+
? 'rgba(61,113,217,0.4)'
84+
: 'rgba(255,152,48,0.4)',
85+
backgroundColor:
86+
color === 'accent-teal'
87+
? 'rgba(115,191,105,0.08)'
88+
: color === 'accent-blue'
89+
? 'rgba(61,113,217,0.08)'
90+
: 'rgba(255,152,48,0.08)',
91+
color: color === 'accent-teal' ? '#73bf69' : color === 'accent-blue' ? '#4096ff' : '#ff9830',
92+
}
93+
: {}),
94+
}}
95+
>
96+
{opt.label}
97+
</button>
98+
))}
99+
</div>
100+
);
101+
}
102+
54103
export function ConfigPanel({ onStart, isRunning, currentProviders: _currentProviders, onCancel }: ConfigPanelProps) {
55104
const { providers: configuredProviders, loading: providersLoading, fetchProviders } = useProviders();
56105
const [selectedModels, setSelectedModels] = useState<SelectedModel[]>([]);
@@ -110,53 +159,6 @@ export function ConfigPanel({ onStart, isRunning, currentProviders: _currentProv
110159
);
111160
};
112161

113-
const QuickButtons = ({
114-
options,
115-
value,
116-
onChange,
117-
color = 'accent-teal',
118-
}: {
119-
options: { label: string; value: number }[];
120-
value: number;
121-
onChange: (v: number) => void;
122-
color?: string;
123-
}) => (
124-
<div className="flex flex-wrap gap-1 mb-1.5">
125-
{options.map((opt) => (
126-
<button
127-
key={opt.value}
128-
onClick={() => onChange(opt.value)}
129-
className={`text-[11px] px-2.5 py-1.5 rounded border transition-all font-medium font-mono ${
130-
value === opt.value
131-
? `border-${color}/40 bg-${color}/8 text-${color}`
132-
: 'border-border text-text-tertiary hover:border-border-hover'
133-
}`}
134-
style={{
135-
...(value === opt.value
136-
? {
137-
borderColor:
138-
color === 'accent-teal'
139-
? 'rgba(115,191,105,0.4)'
140-
: color === 'accent-blue'
141-
? 'rgba(61,113,217,0.4)'
142-
: 'rgba(255,152,48,0.4)',
143-
backgroundColor:
144-
color === 'accent-teal'
145-
? 'rgba(115,191,105,0.08)'
146-
: color === 'accent-blue'
147-
? 'rgba(61,113,217,0.08)'
148-
: 'rgba(255,152,48,0.08)',
149-
color: color === 'accent-teal' ? '#73bf69' : color === 'accent-blue' ? '#4096ff' : '#ff9830',
150-
}
151-
: {}),
152-
}}
153-
>
154-
{opt.label}
155-
</button>
156-
))}
157-
</div>
158-
);
159-
160162
return (
161163
<motion.div initial={{ opacity: 0, x: -16 }} animate={{ opacity: 1, x: 0 }} className="glass-card p-7 space-y-7">
162164
{/* Header */}

0 commit comments

Comments
 (0)