diff --git a/docs/project-worklog.md b/docs/project-worklog.md index d85cd1a..42aa603 100644 --- a/docs/project-worklog.md +++ b/docs/project-worklog.md @@ -46,6 +46,52 @@ Future acceptance: ## Timeline +### 2026-07-09: Frontend Workbench Redesign + +Branch: + +```text +feature/evaluation-frontend-workbench +``` + +What changed: + +- Reframed the admin area as a usable workbench instead of concept pages. +- Preserved the existing visual style: calm canvas, fine borders, serif + headings, mono data labels, compact cards, and restrained status colors. +- Replaced primary admin navigation with operational destinations: + Overview, Detections, Evaluations, Models, and Review. +- Added evaluation frontend API bindings. +- Added an evaluation page for creating manifest-based evaluations, running or + retrying them, inspecting metrics, and reviewing wrong samples. +- Added detection history, model registry, and review queue pages backed by the + existing APIs. +- Reworked the overview page to summarize detection history, evaluation runs, + model health, and review workload. + +Why: + +- The backend now has real evaluation execution, so the frontend needs to show + measurable model behavior rather than only demo-style concepts. +- Job-search reviewers should quickly understand the system boundaries: + detection workflow, evaluation workflow, model registry, and human review. +- The redesign intentionally improves usability and information architecture + without changing the recognizable visual identity of the project. + +Verification: + +```powershell +npm run lint +npm run build +npx playwright screenshot --browser=chromium http://127.0.0.1:5174/admin/evaluations artifacts-admin-evaluations.png +``` + +Deferred: + +- Full charting and confusion matrix visualization. +- Pagination and server-side filtering for large datasets. +- Rich image preview for evaluation samples after dataset upload support exists. + ### 2026-07-08: Evaluation Batch Execution Framework Branch: diff --git a/docs/superpowers/plans/2026-07-09-frontend-workbench-redesign.md b/docs/superpowers/plans/2026-07-09-frontend-workbench-redesign.md new file mode 100644 index 0000000..bcb1d13 --- /dev/null +++ b/docs/superpowers/plans/2026-07-09-frontend-workbench-redesign.md @@ -0,0 +1,128 @@ +# Frontend Workbench Redesign Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Rework the admin frontend into a usable AI image authenticity workbench while preserving the current visual style. + +**Architecture:** Keep the existing React/Vite/CSS Modules frontend. Add evaluation API bindings and pages, rename admin information architecture, and reuse the current token-driven dark UI style instead of introducing a new design system. + +**Tech Stack:** React 18, React Router 7, TypeScript, Vite, CSS Modules, existing primitive components. + +## Global Constraints + +- Preserve the current visual style and UI atmosphere. +- Do not redesign the public home, detection upload, or report page in this branch. +- Do not download model weights or require backend changes. +- Keep API calls centralized in `src/api/backend.ts`. +- Prefer dense operational layouts over marketing-style cards. +- Verify with `npm run lint` and `npm run build`. + +--- + +### Task 1: Evaluation API Boundary + +**Files:** +- Modify: `src/api/backend.ts` + +**Interfaces:** +- Produces evaluation response types and API functions for create/list/detail/run/retry/sample filtering. + +- [ ] Add evaluation TypeScript types. +- [ ] Add evaluation API functions. +- [ ] Run `npm run build` and expect TypeScript success. + +### Task 2: Admin Navigation + +**Files:** +- Modify: `src/layouts/AdminLayout.tsx` +- Modify: `src/layouts/AdminLayout.module.css` +- Modify: `src/App.tsx` + +**Interfaces:** +- Produces routes for `/admin/detections`, `/admin/evaluations`, `/admin/models`, and `/admin/review`. + +- [ ] Rename navigation items to operational workbench destinations. +- [ ] Keep the compact sidebar visual style. +- [ ] Add lazy routes for new pages. +- [ ] Run `npm run build` and expect missing page failures until Task 3 creates pages. + +### Task 3: Evaluation Page + +**Files:** +- Create: `src/pages/AdminEvaluations/index.tsx` +- Create: `src/pages/AdminEvaluations/AdminEvaluations.module.css` + +**Interfaces:** +- Consumes evaluation API functions from `src/api/backend.ts`. + +- [ ] Build evaluation list and create form. +- [ ] Add run/retry/refresh actions. +- [ ] Add selected run detail and wrong-sample table. +- [ ] Preserve current visual style. + +### Task 4: Detection History Page + +**Files:** +- Create: `src/pages/AdminDetections/index.tsx` +- Create: `src/pages/AdminDetections/AdminDetections.module.css` + +**Interfaces:** +- Consumes `listDetections`. + +- [ ] Add status filter. +- [ ] Add detection table and report links. +- [ ] Add empty/error states. + +### Task 5: Models Page + +**Files:** +- Create: `src/pages/AdminModels/index.tsx` +- Create: `src/pages/AdminModels/AdminModels.module.css` +- Modify or leave deprecated: `src/pages/AdminExperts/*` + +**Interfaces:** +- Consumes `listModels` and `checkModelHealth`. + +- [ ] Move credible model registry behavior into a clearer Models page. +- [ ] Remove mock expert-training from primary navigation. +- [ ] Keep model health display. + +### Task 6: Review Page + +**Files:** +- Create: `src/pages/AdminReview/index.tsx` +- Create: `src/pages/AdminReview/AdminReview.module.css` + +**Interfaces:** +- Consumes detection history and evaluation sample APIs. + +- [ ] Show failed detections. +- [ ] Show failed evaluations and wrong samples when available. +- [ ] Avoid claiming retraining or data generation. + +### Task 7: Overview Integration + +**Files:** +- Modify: `src/pages/AdminOverview/index.tsx` +- Modify: `src/pages/AdminOverview/AdminOverview.module.css` + +**Interfaces:** +- Consumes detection, model, and evaluation API summaries. + +- [ ] Replace conceptual overview cards with operational status panels. +- [ ] Link panels to new admin routes. +- [ ] Keep the existing typography and card language. + +### Task 8: Documentation, Verification, Push + +**Files:** +- Modify: `docs/project-worklog.md` + +**Interfaces:** +- Records what changed and why the UI style was preserved. + +- [ ] Update worklog. +- [ ] Run `npm run lint`. +- [ ] Run `npm run build`. +- [ ] Commit and push `feature/evaluation-frontend-workbench`. + diff --git a/docs/superpowers/specs/2026-07-09-frontend-workbench-redesign-design.md b/docs/superpowers/specs/2026-07-09-frontend-workbench-redesign-design.md new file mode 100644 index 0000000..2a65a7e --- /dev/null +++ b/docs/superpowers/specs/2026-07-09-frontend-workbench-redesign-design.md @@ -0,0 +1,128 @@ +# Frontend Workbench Redesign Design + +## Goal + +Reshape the admin frontend from a concept demo into a usable AI image authenticity workbench while preserving the current visual language: dark calm canvas, fine rules, serif headings, restrained motion, compact cards, and professional forensic tone. + +## References + +- MLflow: experiment tracking, model registry, and evaluation are organized as lifecycle surfaces rather than unrelated pages. +- Label Studio: model output review works best when uncertain samples become an explicit human-in-the-loop queue. +- ClearML: experiment, model, dataset, and pipeline surfaces are connected through a dense operational dashboard. + +These references inform information architecture only. The project should not copy their visual style. + +## Current Problems + +- Admin navigation names are too conceptual: `Pipeline`, `Experts`, and `Anomaly` do not clearly communicate the job-search project story. +- The backend now has evaluation execution APIs, but the frontend has no evaluation surface. +- The admin overview shows useful detection status, but it does not connect detection, model health, evaluation, and review into one workflow. +- Some existing pages rely on mock concepts such as expert training and anomaly pools. They look interesting but are less credible than model registry, evaluation metrics, and sample review. + +## Product Direction + +The admin area becomes the main workbench: + +- `Overview`: operational snapshot across detection, model health, evaluation, and review. +- `Detections`: detection history and report navigation. +- `Evaluations`: create/run/retry evaluations, inspect metrics, and review wrong samples. +- `Models`: model registry and health checks. +- `Review`: uncertain or failed samples needing human attention. + +The public detection flow and report page keep their current structure for this branch. + +## Visual Constraints + +- Preserve existing colors, tokens, typography, spacing rhythm, border style, and restrained animation. +- Do not introduce a marketing hero, large gradients, decorative blobs, or a new design system. +- Use dense but legible operational layouts: metric rows, tables, compact panels, status dots, and side-by-side detail panels. +- Keep cards at the current radius and border treatment. +- Keep Chinese/English mixed labels concise, but use clear operational English where existing Chinese encoding may be fragile. + +## Data And API Design + +Extend `src/api/backend.ts` with evaluation types and calls: + +- `EvaluationStatus = 'QUEUED' | 'RUNNING' | 'COMPLETED' | 'FAILED'` +- `EvaluationRunResponse` +- `EvaluationDetailResponse` +- `EvaluationSampleResponse` +- `createEvaluation` +- `listEvaluations` +- `getEvaluation` +- `runEvaluation` +- `retryEvaluation` +- `listEvaluationSamples` + +The first frontend slice can create evaluations from a pasted CSV manifest, run queued evaluations, retry failed evaluations, list recent evaluations, and inspect wrong samples. + +## Page Design + +### Admin Layout + +Replace icon-only conceptual navigation with operational destinations while keeping the compact sidebar: + +- Overview +- Detections +- Evaluations +- Models +- Review + +The sidebar stays narrow on desktop and should remain usable on smaller screens. + +### Overview + +Use the existing page style but change the content: + +- API/system status from detection history load. +- Recent detections with status and report links. +- Evaluation snapshot: total runs, completed runs, latest accuracy/F1 if available. +- Model health snapshot: enabled models and health check status. +- Review queue snapshot: failed detections plus failed evaluations. + +### Evaluations + +Add a new page under `/admin/evaluations`: + +- Left/main area: evaluation run table with name, dataset, model, status, attempts, accuracy, precision, recall, F1. +- Right/secondary area: selected evaluation detail with action buttons. +- Create panel: name, dataset, model id, manifest textarea. +- Actions: run, retry, refresh. +- Wrong samples: filename, ground truth, prediction, score, latency, failure reason. + +### Detections + +Create a clearer admin detection history page using existing detection APIs: + +- Status filter. +- Recent detection table. +- Report link when a report exists. +- Basic file metadata. + +### Models + +Keep the useful model registry and health functionality from `AdminExperts`, but rename and simplify the page. Mock expert-training panels are removed from the primary workflow. + +### Review + +Keep the spirit of `AdminAnomaly`, but make it credible: + +- Show failed detections and failed/incorrect evaluation samples. +- Keep the visual sample-review affordance. +- Do not claim retraining or data generation is actually happening. + +## Error Handling + +- Every page that calls an API shows a compact inline error message. +- Loading states use existing subdued text and status dots. +- Empty states should explain the next action: upload a detection, create an evaluation, or check model health. + +## Acceptance Criteria + +- `npm run lint` passes. +- `npm run build` passes. +- Admin navigation exposes Overview, Detections, Evaluations, Models, and Review. +- Evaluation frontend calls the backend APIs added in the previous branch. +- Existing visual style remains recognizable. +- No model weights or backend changes are required. + diff --git a/src/App.tsx b/src/App.tsx index 796ed92..192f2b3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ import { lazy, Suspense, useEffect } from 'react'; -import { BrowserRouter, Route, Routes, useLocation } from 'react-router-dom'; +import { BrowserRouter, Navigate, Route, Routes, useLocation } from 'react-router-dom'; import { AnimatePresence, motion } from 'framer-motion'; import { ToastProvider } from '@/components/primitives/Toast/Toast'; import { AdminLayout } from '@/layouts/AdminLayout'; @@ -10,11 +10,12 @@ const DetectImage = lazy(() => import('@/pages/DetectImage').then((module) => ({ const DetectVideo = lazy(() => import('@/pages/DetectVideo').then((module) => ({ default: module.DetectVideo }))); const Report = lazy(() => import('@/pages/Report').then((module) => ({ default: module.Report }))); const AdminOverview = lazy(() => import('@/pages/AdminOverview').then((module) => ({ default: module.AdminOverview }))); -const AdminPipeline = lazy(() => import('@/pages/AdminPipeline').then((module) => ({ default: module.AdminPipeline }))); +const AdminDetections = lazy(() => import('@/pages/AdminDetections').then((module) => ({ default: module.AdminDetections }))); +const AdminEvaluations = lazy(() => import('@/pages/AdminEvaluations').then((module) => ({ default: module.AdminEvaluations }))); +const AdminModels = lazy(() => import('@/pages/AdminModels').then((module) => ({ default: module.AdminModels }))); +const AdminReview = lazy(() => import('@/pages/AdminReview').then((module) => ({ default: module.AdminReview }))); const VideoShowcase = lazy(() => import('@/pages/AdminPipeline/VideoShowcase').then((module) => ({ default: module.VideoShowcase }))); const ImageShowcase = lazy(() => import('@/pages/AdminPipeline/ImageShowcase').then((module) => ({ default: module.ImageShowcase }))); -const AdminExperts = lazy(() => import('@/pages/AdminExperts').then((module) => ({ default: module.AdminExperts }))); -const AdminAnomaly = lazy(() => import('@/pages/AdminAnomaly').then((module) => ({ default: module.AdminAnomaly }))); const Dev = lazy(() => import('@/pages/Dev').then((module) => ({ default: module.Dev }))); const NotFound = lazy(() => import('@/pages/NotFound/NotFound').then((module) => ({ default: module.NotFound }))); @@ -39,11 +40,15 @@ function AnimatedRoutes() { } /> }> } /> - } /> + } /> + } /> + } /> + } /> + } /> } /> } /> - } /> - } /> + } /> + } /> } /> } /> diff --git a/src/api/backend.ts b/src/api/backend.ts index a9a68c9..8faf6f3 100644 --- a/src/api/backend.ts +++ b/src/api/backend.ts @@ -1,4 +1,5 @@ export type DetectionStatus = 'QUEUED' | 'INFERENCING' | 'COMPLETED' | 'FAILED'; +export type EvaluationStatus = 'QUEUED' | 'RUNNING' | 'COMPLETED' | 'FAILED'; export type ModelLabel = 'AUTHENTIC' | 'SYNTHETIC' | 'UNCERTAIN'; export type ReportVerdict = 'LIKELY_AUTHENTIC' | 'LIKELY_SYNTHETIC' | 'UNCERTAIN'; export type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH'; @@ -94,6 +95,50 @@ export interface ModelHealthResponse { checkedAt: string; } +export interface EvaluationRunResponse { + evaluationId: string; + name: string; + datasetName: string; + modelId: string; + status: EvaluationStatus; + totalSamples: number; + completedSamples: number; + accuracy: number | null; + precision: number | null; + recall: number | null; + f1: number | null; + attemptCount: number; + maxAttempts: number; + createdAt: string; + startedAt: string | null; + completedAt: string | null; + failureReason: string | null; +} + +export interface EvaluationSampleResponse { + sampleId: string; + evaluationId: string; + filename: string; + groundTruthLabel: ModelLabel; + predictedLabel: ModelLabel | null; + score: number | null; + latencyMs: number | null; + correct: boolean | null; + failureReason: string | null; + createdAt: string; +} + +export interface EvaluationDetailResponse extends EvaluationRunResponse { + samples: EvaluationSampleResponse[]; +} + +export interface CreateEvaluationRequest { + name: string; + datasetName: string; + modelId: string; + manifest: string; +} + export class ApiError extends Error { readonly status: number; @@ -157,3 +202,35 @@ export function checkModelHealth(modelId: string) { method: 'POST', }); } + +export function createEvaluation(request: CreateEvaluationRequest) { + return apiRequest('/api/evaluations', { + method: 'POST', + body: JSON.stringify(request), + }); +} + +export function listEvaluations() { + return apiRequest('/api/evaluations'); +} + +export function getEvaluation(evaluationId: string) { + return apiRequest(`/api/evaluations/${encodeURIComponent(evaluationId)}`); +} + +export function runEvaluation(evaluationId: string) { + return apiRequest(`/api/evaluations/${encodeURIComponent(evaluationId)}/run`, { + method: 'POST', + }); +} + +export function retryEvaluation(evaluationId: string) { + return apiRequest(`/api/evaluations/${encodeURIComponent(evaluationId)}/retry`, { + method: 'POST', + }); +} + +export function listEvaluationSamples(evaluationId: string, correct?: boolean) { + const query = typeof correct === 'boolean' ? `?correct=${String(correct)}` : ''; + return apiRequest(`/api/evaluations/${encodeURIComponent(evaluationId)}/samples${query}`); +} diff --git a/src/layouts/AdminLayout.module.css b/src/layouts/AdminLayout.module.css index 53d3256..8c39fb1 100644 --- a/src/layouts/AdminLayout.module.css +++ b/src/layouts/AdminLayout.module.css @@ -1,7 +1,7 @@ .layout { min-height: 100vh; display: grid; - grid-template-columns: 64px 1fr; + grid-template-columns: 188px 1fr; background: var(--canvas); } @@ -18,22 +18,34 @@ display: flex; flex-direction: column; justify-content: center; - gap: var(--space-3); + gap: var(--space-2); + padding: 0 var(--space-4); } .sidebar a { position: relative; - display: grid; - place-items: center; - padding: 18px; + display: flex; + align-items: center; + gap: var(--space-3); + padding: 12px 10px; + border: 1px solid transparent; + border-radius: var(--radius); color: var(--ink-3); + font-family: var(--font-mono); + font-size: var(--text-xs); + letter-spacing: 0.04em; + transition: background 180ms ease-out, border-color 180ms ease-out, color 180ms ease-out; } .sidebar a:hover { + border-color: var(--rule); + background: var(--surface); color: var(--ink); } .sidebar a.active { + border-color: var(--rule); + background: var(--surface); color: var(--accent); } @@ -46,25 +58,41 @@ } .sidebar span { - position: absolute; - left: 58px; - padding: 6px 9px; - background: var(--ink); - color: var(--canvas); - opacity: 0; - pointer-events: none; - font-family: var(--font-mono); - font-size: var(--text-xs); white-space: nowrap; - transform: translateX(-6px); - transition: opacity 160ms ease-out, transform 160ms ease-out; -} - -.sidebar a:hover span { - opacity: 1; - transform: translateX(0); } .main { padding: 96px 0; } + +@media (max-width: 820px) { + .layout { + grid-template-columns: 64px 1fr; + } + + .sidebar nav { + padding: 0 var(--space-2); + } + + .sidebar a { + justify-content: center; + padding: 14px; + } + + .sidebar span { + position: absolute; + left: 58px; + padding: 6px 9px; + background: var(--ink); + color: var(--canvas); + opacity: 0; + pointer-events: none; + transform: translateX(-6px); + transition: opacity 160ms ease-out, transform 160ms ease-out; + } + + .sidebar a:hover span { + opacity: 1; + transform: translateX(0); + } +} diff --git a/src/layouts/AdminLayout.tsx b/src/layouts/AdminLayout.tsx index 4048ced..14d9a86 100644 --- a/src/layouts/AdminLayout.tsx +++ b/src/layouts/AdminLayout.tsx @@ -1,40 +1,50 @@ import { NavLink, Outlet } from 'react-router-dom'; import styles from './AdminLayout.module.css'; -function AdminIcon({ type }: { type: 'overview' | 'pipeline' | 'experts' | 'anomaly' }) { +type AdminIconType = 'overview' | 'detections' | 'evaluations' | 'models' | 'review'; + +function AdminIcon({ type }: { type: AdminIconType }) { return ( ); } const nav = [ - { to: '/admin', label: '总览', icon: 'overview' as const, end: true }, - { to: '/admin/pipeline', label: 'Pipeline', icon: 'pipeline' as const }, - { to: '/admin/experts', label: '专家库', icon: 'experts' as const }, - { to: '/admin/anomaly', label: '异常池', icon: 'anomaly' as const }, + { to: '/admin', label: 'Overview', icon: 'overview' as const, end: true }, + { to: '/admin/detections', label: 'Detections', icon: 'detections' as const }, + { to: '/admin/evaluations', label: 'Evaluations', icon: 'evaluations' as const }, + { to: '/admin/models', label: 'Models', icon: 'models' as const }, + { to: '/admin/review', label: 'Review', icon: 'review' as const }, ]; export function AdminLayout() { return (