feat: redesign UI to match lovable compact sidebar layout

- Replace green hacker theme with professional blue-toned design
- Light theme default, manual toggle only (no system detection)
- Compact w-60 sidebar with collapsible sections
- New CSS tokens: sidebar, chart, candle, annotation colors
- Tools displayed as compact grid buttons
- Color swatches as inline bar
- Chart top bar with keyboard shortcut hints
- Inter + JetBrains Mono font pairing
- All components updated for compact styling
- Tailwind config extended with sidebar/chart tokens
This commit is contained in:
Marko Djordjevic 2026-02-16 20:50:30 +01:00
parent 2bde38d0bf
commit 4605283d2b
13 changed files with 976 additions and 740 deletions

View file

@ -6,6 +6,8 @@ import FileUpload from '@/components/FileUpload';
import CandleChart, { CandleChartHandle } from '@/components/CandleChart';
import ChartSelector from '@/components/ChartSelector';
import PredictionPanel from '@/components/PredictionPanel';
import SpanAnnotationList from '@/components/SpanAnnotationList';
import { ThemeToggle } from '@/components/ThemeToggle';
import type { PredictionState, PredictionSpan, ModelInfoResponse, Disagreement, DisagreementType, PredictionSummary } from '@/types/predictions';
/**
@ -692,29 +694,25 @@ export default function Home() {
return () => window.removeEventListener('keydown', handleKeyDown);
}, [selectedLabelId]);
const activeChart = charts.find((c) => c.id === activeChartId);
return (
<div className="flex h-screen bg-background">
<div className="flex h-screen w-full overflow-hidden bg-background">
{/* Sidebar */}
<aside className="w-72 flex-shrink-0 flex flex-col border-r border-border bg-card">
<div className="p-6 border-b border-border">
<h1 className="text-2xl font-semibold text-foreground">Candle Annotator</h1>
<p className="text-sm text-muted-foreground mt-1">Chart annotation tool</p>
<div className="mt-3 flex flex-col gap-1">
<a
href="/annotation-types"
className="text-sm text-muted-foreground hover:text-foreground"
>
Manage Annotation Types
</a>
<a
href="/span-label-types"
className="text-sm text-muted-foreground hover:text-foreground"
>
Manage Span Label Types
</a>
<div className="flex flex-col bg-sidebar border-r border-sidebar-border w-60 flex-shrink-0 animate-fade-in">
{/* Sidebar Header */}
<div className="flex items-center justify-between px-4 py-3 border-b border-sidebar-border">
<div>
<h1 className="text-sm font-semibold text-foreground tracking-tight">Candle Annotator</h1>
<p className="text-[10px] text-muted-foreground">Chart annotation tool</p>
</div>
<div className="flex items-center gap-1">
<ThemeToggle />
</div>
</div>
<div className="p-6 pb-3">
{/* Chart Selector */}
<div className="px-3 py-2 border-b border-sidebar-border">
<ChartSelector
charts={charts}
activeChartId={activeChartId}
@ -722,10 +720,14 @@ export default function Home() {
onDeleteChart={handleDeleteChart}
/>
</div>
<div className="px-6 pb-3">
{/* File Upload */}
<div className="px-3 py-2 border-b border-sidebar-border">
<FileUpload onUploadSuccess={handleUploadSuccess} />
</div>
<div className="px-6 pb-6">
{/* Toolbox */}
<div className="px-3 py-2 border-b border-sidebar-border">
<Toolbox
activeTool={activeTool}
onToolChange={setActiveTool}
@ -744,58 +746,103 @@ export default function Home() {
onDeleteSpan={handleDeleteSpan}
/>
</div>
<PredictionPanel
predictionState={predictionState}
onToggleVisibility={togglePredictionVisibility}
onFetchPredictions={handleFetchVisiblePredictions}
onFetchBatchPredictions={handleFetchBatchPredictions}
onConfidenceChange={setConfidenceThreshold}
onToggleLabelSelection={toggleLabelSelection}
predictionSummary={predictionSummary}
isModelOnline={isModelOnline}
showOnlyDisagreements={showOnlyDisagreements}
onToggleShowOnlyDisagreements={toggleShowOnlyDisagreements}
/>
</aside>
{/* Annotations List - scrollable */}
<div className="flex-1 overflow-y-auto scrollbar-thin px-3 py-2 min-h-0">
<SpanAnnotationList
spanAnnotations={spanAnnotations}
spanLabelTypes={spanLabelTypes}
selectedSpanId={selectedSpanId}
onSelectSpan={handleSelectedSpanChange}
onDeleteSpan={handleDeleteSpan}
/>
</div>
{/* Predictions */}
<div className="px-3 py-2 border-t border-sidebar-border">
<PredictionPanel
predictionState={predictionState}
onToggleVisibility={togglePredictionVisibility}
onFetchPredictions={handleFetchVisiblePredictions}
onFetchBatchPredictions={handleFetchBatchPredictions}
onConfidenceChange={setConfidenceThreshold}
onToggleLabelSelection={toggleLabelSelection}
predictionSummary={predictionSummary}
isModelOnline={isModelOnline}
showOnlyDisagreements={showOnlyDisagreements}
onToggleShowOnlyDisagreements={toggleShowOnlyDisagreements}
/>
</div>
{/* Export */}
<div className="px-3 py-2 border-t border-sidebar-border">
<button
onClick={handleExport}
className="w-full flex items-center justify-center gap-1.5 px-2 py-1.5 text-xs rounded bg-primary/10 hover:bg-primary/20 text-primary transition-colors"
>
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>
Export JSON
</button>
</div>
</div>
{/* Main chart area */}
<main className="flex-1 relative bg-background">
{/* Loading overlay for predictions */}
{predictionState.isLoading && (
<div className="absolute inset-0 bg-background/50 backdrop-blur-sm z-50 flex items-center justify-center">
<div className="bg-card border border-border rounded-lg p-6 shadow-lg">
<div className="flex items-center gap-3">
<div className="animate-spin rounded-full h-5 w-5 border-2 border-primary border-t-transparent" />
<span className="text-sm text-foreground">Loading predictions...</span>
<div className="flex-1 flex flex-col min-w-0">
{/* Chart top bar */}
<div className="flex items-center justify-between px-3 py-1.5 border-b border-border bg-card/50">
<div className="flex items-center gap-3">
<span className="font-mono text-sm font-semibold text-foreground">
{activeChart?.name || 'No chart'}
</span>
</div>
<div className="flex items-center gap-2 text-[10px] text-muted-foreground font-mono">
<span className="px-1.5 py-0.5 rounded bg-secondary/50">R Rect</span>
<span className="px-1.5 py-0.5 rounded bg-secondary/50">S Span</span>
<span className="px-1.5 py-0.5 rounded bg-secondary/50">L Line</span>
<span className="px-1.5 py-0.5 rounded bg-secondary/50">D Del</span>
<span className="px-1.5 py-0.5 rounded bg-secondary/50">T Theme</span>
</div>
</div>
{/* Chart */}
<div className="flex-1 min-h-0 relative">
{/* Loading overlay for predictions */}
{predictionState.isLoading && (
<div className="absolute inset-0 bg-background/50 backdrop-blur-sm z-50 flex items-center justify-center">
<div className="bg-card border border-border rounded-lg p-6 shadow-lg">
<div className="flex items-center gap-3">
<div className="animate-spin rounded-full h-5 w-5 border-2 border-primary border-t-transparent" />
<span className="text-sm text-foreground">Loading predictions...</span>
</div>
</div>
</div>
</div>
)}
<CandleChart
ref={chartRef}
activeTool={activeTool}
onAnnotationChange={handleAnnotationChange}
selectedColor={selectedColor}
selectedLabelId={selectedLabelId}
onLabelSelect={handleLabelSelect}
activeChartId={activeChartId}
spanAnnotations={spanAnnotations}
spanLabelTypes={spanLabelTypes}
selectedSpanId={selectedSpanId}
onSpanAnnotationsChange={handleSpanAnnotationsChange}
onSelectedSpanChange={handleSelectedSpanChange}
predictionVisible={predictionState.visible}
perCandlePredictions={predictionState.perCandlePredictions}
predictionSpans={predictionState.spans}
confidenceThreshold={predictionState.confidenceThreshold}
selectedLabels={predictionState.selectedLabels}
modelInfo={predictionState.modelInfo}
predictionSummary={predictionSummary}
showOnlyDisagreements={showOnlyDisagreements}
onPredictionClick={handlePredictionClick}
onPredictionDismiss={handlePredictionDismiss}
/>
</main>
)}
<CandleChart
ref={chartRef}
activeTool={activeTool}
onAnnotationChange={handleAnnotationChange}
selectedColor={selectedColor}
selectedLabelId={selectedLabelId}
onLabelSelect={handleLabelSelect}
activeChartId={activeChartId}
spanAnnotations={spanAnnotations}
spanLabelTypes={spanLabelTypes}
selectedSpanId={selectedSpanId}
onSpanAnnotationsChange={handleSpanAnnotationsChange}
onSelectedSpanChange={handleSelectedSpanChange}
predictionVisible={predictionState.visible}
perCandlePredictions={predictionState.perCandlePredictions}
predictionSpans={predictionState.spans}
confidenceThreshold={predictionState.confidenceThreshold}
selectedLabels={predictionState.selectedLabels}
modelInfo={predictionState.modelInfo}
predictionSummary={predictionSummary}
showOnlyDisagreements={showOnlyDisagreements}
onPredictionClick={handlePredictionClick}
onPredictionDismiss={handlePredictionDismiss}
/>
</div>
</div>
</div>
);
}