/ concept-collection / benchcompress
Sign in
concept-collection / benchcompress
improve ui
Jeremy Magland <jmagland@flatironinstitute.org> committed commit 3b1d188236e8 parent 03f201a Browse files
9 changed files+442−314
web-ui/src/App.tsxmodified+10−2View file
@@ -147,12 +147,20 @@ function App() {
147147 />
148148 <Route
149149 path="/dataset/:datasetName"
150- element={<Dataset datasets={benchmarkData?.datasets || []} />}
150+ element={
151+ <Dataset
152+ datasets={benchmarkData?.datasets || []}
153+ benchmarkData={benchmarkData}
154+ />
155+ }
151156 />
152157 <Route
153158 path="/algorithm/:algorithmName"
154159 element={
155- <Algorithm algorithms={benchmarkData?.algorithms || []} />
160+ <Algorithm
161+ algorithms={benchmarkData?.algorithms || []}
162+ benchmarkData={benchmarkData}
163+ />
156164 }
157165 />
158166 <Route path="/about" element={<About />} />
web-ui/src/components/benchmark/table/BenchmarkTable.tsxmodified+6−119View file
@@ -1,5 +1,3 @@
1-import { useMemo } from "react";
2-import { useSearchParams } from "react-router-dom";
31 import {
42 flexRender,
53 getCoreRowModel,
@@ -10,134 +8,25 @@ import { BenchmarkResult } from "../../../types";
108 import { columns } from "./columns";
119 import { BenchmarkCharts } from "../charts/BenchmarkCharts";
1210 import { exportToCsv } from "../export/csvExport";
11+import { useBenchmarkChartData } from "../../../hooks/useBenchmarkChartData";
1312
1413 interface BenchmarkTableProps {
1514 results: BenchmarkResult[];
16- availableDatasets: string[];
17- availableAlgorithms: string[];
1815 }
1916
20-export function BenchmarkTable({
21- results,
22- availableDatasets,
23- availableAlgorithms,
24-}: BenchmarkTableProps) {
25- const [searchParams, setSearchParams] = useSearchParams();
26- const selectedDataset = searchParams.get("dataset") || "";
27- const selectedAlgorithm = searchParams.get("algorithm") || "";
28-
29- const filteredData = results;
30-
17+export function BenchmarkTable({ results }: BenchmarkTableProps) {
3118 const table = useReactTable({
32- data: filteredData || [],
19+ data: results,
3320 columns,
3421 getCoreRowModel: getCoreRowModel(),
3522 getSortedRowModel: getSortedRowModel(),
3623 });
3724
38- const chartData = useMemo(() => {
39- if (selectedDataset) {
40- return filteredData
41- .filter((row: BenchmarkResult) => row.dataset === selectedDataset)
42- .map((row: BenchmarkResult) => ({
43- algorithm: row.algorithm,
44- compression_ratio: row.compression_ratio,
45- encode_speed: row.encode_mb_per_sec,
46- decode_speed: row.decode_mb_per_sec,
47- }));
48- } else if (selectedAlgorithm) {
49- return filteredData
50- .filter((row: BenchmarkResult) => row.algorithm === selectedAlgorithm)
51- .map((row: BenchmarkResult) => ({
52- algorithm: row.dataset,
53- compression_ratio: row.compression_ratio,
54- encode_speed: row.encode_mb_per_sec,
55- decode_speed: row.decode_mb_per_sec,
56- }));
57- }
58- return [];
59- }, [filteredData, selectedDataset, selectedAlgorithm]);
25+ const chartData = useBenchmarkChartData(results, "", "");
6026
6127 return (
6228 <div className="table-container">
63- <div
64- style={{
65- marginBottom: "12px",
66- display: "flex",
67- alignItems: "center",
68- gap: "8px",
69- justifyContent: "space-between",
70- }}
71- >
72- <div style={{ display: "flex", alignItems: "center", gap: "12px" }}>
73- <div style={{ display: "flex", alignItems: "center", gap: "6px" }}>
74- <label htmlFor="dataset-select">Dataset:</label>
75- <select
76- id="dataset-select"
77- value={selectedDataset}
78- onChange={(e) => {
79- if (e.target.value) {
80- setSearchParams({ dataset: e.target.value });
81- } else {
82- setSearchParams(
83- selectedAlgorithm ? { algorithm: selectedAlgorithm } : {},
84- );
85- }
86- }}
87- style={{
88- padding: "4px 8px",
89- borderRadius: "4px",
90- border: "1px solid #ccc",
91- minWidth: "150px",
92- backgroundColor: "#fff",
93- fontSize: "0.9rem",
94- }}
95- >
96- <option value="">All Datasets</option>
97- {availableDatasets.map((dataset) => (
98- <option key={dataset} value={dataset}>
99- {dataset}
100- </option>
101- ))}
102- </select>
103- </div>
104- <div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
105- <label htmlFor="algorithm-select">Algorithm:</label>
106- <select
107- id="algorithm-select"
108- value={selectedAlgorithm}
109- onChange={(e) => {
110- if (e.target.value) {
111- setSearchParams({ algorithm: e.target.value });
112- } else {
113- setSearchParams(
114- selectedDataset ? { dataset: selectedDataset } : {},
115- );
116- }
117- }}
118- style={{
119- padding: "4px 8px",
120- borderRadius: "4px",
121- border: "1px solid #ccc",
122- minWidth: "150px",
123- backgroundColor: "#fff",
124- fontSize: "0.9rem",
125- }}
126- >
127- <option value="">All Algorithms</option>
128- {availableAlgorithms.map((algorithm) => (
129- <option key={algorithm} value={algorithm}>
130- {algorithm}
131- </option>
132- ))}
133- </select>
134- </div>
135- </div>
136- </div>
137-
138- {(selectedDataset || selectedAlgorithm) && chartData.length > 0 && (
139- <BenchmarkCharts chartData={chartData} />
140- )}
29+ {chartData.length > 0 && <BenchmarkCharts chartData={chartData} />}
14130
14231 <table>
14332 <thead>
@@ -184,9 +73,7 @@ export function BenchmarkTable({
18473 }}
18574 >
18675 <button
187- onClick={() =>
188- exportToCsv(filteredData, selectedDataset || selectedAlgorithm)
189- }
76+ onClick={() => exportToCsv(results, "benchmark_results")}
19077 style={{
19178 padding: "8px 16px",
19279 backgroundColor: "#4CAF50",
web-ui/src/components/benchmark/table/columns.tsxmodified+25−2View file
@@ -1,17 +1,40 @@
11 import { createColumnHelper } from "@tanstack/react-table";
22 import { BenchmarkResult } from "../../../types";
33 import { formatNumber, formatSize } from "../utils/formatters";
4+import { Link } from "react-router-dom";
45
56 const columnHelper = createColumnHelper<BenchmarkResult>();
67
78 export const columns = [
89 columnHelper.accessor("dataset", {
910 header: "Dataset",
10- cell: (info) => info.getValue(),
11+ cell: (info) => (
12+ <Link
13+ to={`/dataset/${info.getValue()}`}
14+ style={{ color: "#2563eb", textDecoration: "none" }}
15+ onMouseEnter={(e) =>
16+ (e.currentTarget.style.textDecoration = "underline")
17+ }
18+ onMouseLeave={(e) => (e.currentTarget.style.textDecoration = "none")}
19+ >
20+ {info.getValue()}
21+ </Link>
22+ ),
1123 }),
1224 columnHelper.accessor("algorithm", {
1325 header: "Algorithm",
14- cell: (info) => info.getValue(),
26+ cell: (info) => (
27+ <Link
28+ to={`/algorithm/${info.getValue()}`}
29+ style={{ color: "#2563eb", textDecoration: "none" }}
30+ onMouseEnter={(e) =>
31+ (e.currentTarget.style.textDecoration = "underline")
32+ }
33+ onMouseLeave={(e) => (e.currentTarget.style.textDecoration = "none")}
34+ >
35+ {info.getValue()}
36+ </Link>
37+ ),
1538 }),
1639 columnHelper.accessor("compression_ratio", {
1740 header: "Compression Ratio",
web-ui/src/components/dataset/TimeseriesView.tsxmodified+84−4View file
@@ -1,4 +1,4 @@
1-import { useEffect, useMemo, useReducer, useState } from "react";
1+import { useEffect, useMemo, useReducer, useState, useCallback } from "react";
22 import { SupportedTypedArray } from "../../hooks/TimeseriesDataClient";
33 import { useTimeseriesDataClient } from "../../hooks/useTimeseriesDataClient";
44 import { Dataset } from "../../types";
@@ -29,6 +29,23 @@ const TimeseriesView: React.FC<TimeseriesViewProps> = ({
2929 useState<HTMLCanvasElement | null>(null);
3030 const [state, dispatch] = useReducer(timeseriesViewReducer, initialState);
3131 const { selectedIndex, isDragging, lastDragX, xRange } = state;
32+ const [isWheelEnabled, setIsWheelEnabled] = useState(false);
33+ const [showHint, setShowHint] = useState(true);
34+
35+ // Hide hint when user interacts with the graph
36+ const hideHint = useCallback(() => {
37+ setShowHint(false);
38+ }, []);
39+
40+ // Auto-hide hint after 4 seconds
41+ useEffect(() => {
42+ if (showHint) {
43+ const timer = setTimeout(() => {
44+ setShowHint(false);
45+ }, 5000);
46+ return () => clearTimeout(timer);
47+ }
48+ }, [showHint]);
3249
3350 const [container, setContainer] = useState<HTMLDivElement | null>(null);
3451 const [worker, setWorker] = useState<Worker | null>(null);
@@ -74,7 +91,7 @@ const TimeseriesView: React.FC<TimeseriesViewProps> = ({
7491 const shape = client.getShape();
7592 dispatch({
7693 type: "SET_X_RANGE",
77- range: { min: 0, max: Math.min(999, shape - 1) },
94+ range: { min: 0, max: Math.min(149, shape - 1) },
7895 });
7996 }
8097 }, [client]);
@@ -84,6 +101,9 @@ const TimeseriesView: React.FC<TimeseriesViewProps> = ({
84101 if (!container || !client) return;
85102
86103 const handleWheel = (e: WheelEvent) => {
104+ if (!isWheelEnabled) {
105+ return; // Allow page scrolling if wheel zoom not enabled
106+ }
87107 e.preventDefault();
88108
89109 const rect = container.getBoundingClientRect();
@@ -115,7 +135,7 @@ const TimeseriesView: React.FC<TimeseriesViewProps> = ({
115135 return () => {
116136 container.removeEventListener("wheel", handleWheel);
117137 };
118- }, [container, client, width, margins, xRange]);
138+ }, [container, client, width, margins, xRange, isWheelEnabled]);
119139
120140 // Set up mouse event listeners for panning
121141 useEffect(() => {
@@ -276,6 +296,12 @@ const TimeseriesView: React.FC<TimeseriesViewProps> = ({
276296
277297 const handleCanvasClick = (e: React.MouseEvent<HTMLDivElement>) => {
278298 if (!overlayCanvasElement || !dataY || isDragging) return;
299+
300+ // Enable wheel zooming on first click
301+ if (!isWheelEnabled) {
302+ setIsWheelEnabled(true);
303+ }
304+
279305 const rect = overlayCanvasElement.getBoundingClientRect();
280306 const x = e.clientX - rect.left;
281307 const xRatio = (x - margins.left) / (width - margins.left - margins.right);
@@ -287,10 +313,64 @@ const TimeseriesView: React.FC<TimeseriesViewProps> = ({
287313
288314 return (
289315 <div style={{ position: "relative", width, height: height + 30 }}>
316+ {showHint && (
317+ <div
318+ style={{
319+ position: "absolute",
320+ top: margins.top + 10,
321+ right: margins.right + 10,
322+ display: "flex",
323+ flexDirection: "column",
324+ alignItems: "flex-end",
325+ gap: "8px",
326+ zIndex: 10,
327+ opacity: showHint ? 0.8 : 0,
328+ transition: "opacity 0.5s ease-out",
329+ pointerEvents: "none",
330+ fontSize: "12px",
331+ color: "#666",
332+ }}
333+ >
334+ <div
335+ style={{
336+ display: "flex",
337+ alignItems: "center",
338+ gap: "4px",
339+ backgroundColor: "rgba(255, 255, 255, 0.9)",
340+ padding: "2px 6px",
341+ borderRadius: "4px",
342+ }}
343+ >
344+ <span>Drag to pan</span>
345+ <svg width="14" height="14" viewBox="0 0 24 24" fill="#666">
346+ <path d="M15 3h2v5h-2V3zm4 0h2v5h-2V3zm-4 7h2v5h-2v-5zm4 0h2v5h-2v-5zm-4 7h2v5h-2v-5zm4 0h2v5h-2v-5z" />
347+ </svg>
348+ </div>
349+ <div
350+ style={{
351+ display: "flex",
352+ alignItems: "center",
353+ gap: "4px",
354+ backgroundColor: "rgba(255, 255, 255, 0.9)",
355+ padding: "2px 6px",
356+ borderRadius: "4px",
357+ }}
358+ >
359+ <span>Scroll to zoom</span>
360+ <svg width="14" height="14" viewBox="0 0 24 24" fill="#666">
361+ <path d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9-4.03-9-9-9zm0 16c-3.86 0-7-3.14-7-7s3.14-7 7-7 7 3.14 7 7-3.14 7-7 7zm1-11h-2v3H8v2h3v3h2v-3h3v-2h-3V8z" />
362+ </svg>
363+ </div>
364+ </div>
365+ )}
290366 <div
291367 ref={setContainer}
292368 style={{ position: "relative", width, height }}
293- onClick={handleCanvasClick}
369+ onClick={(e) => {
370+ handleCanvasClick(e);
371+ hideHint();
372+ }}
373+ onMouseDown={hideHint}
294374 >
295375 <canvas
296376 ref={setCanvasElement}
web-ui/src/components/dataset/timeseriesViewReducer.tsmodified+1−1View file
@@ -13,7 +13,7 @@ export const initialState: TimeseriesViewState = {
1313 selectedIndex: -1,
1414 isDragging: false,
1515 lastDragX: 0,
16- xRange: { min: 0, max: 1 },
16+ xRange: { min: 0, max: 149 },
1717 };
1818
1919 // Action Types Union
web-ui/src/hooks/useBenchmarkChartData.tsadded+31−0View file
@@ -0,0 +1,31 @@
1+import { useMemo } from "react";
2+import { BenchmarkResult } from "../types";
3+
4+export function useBenchmarkChartData(
5+ results: BenchmarkResult[],
6+ selectedDataset?: string | null,
7+ selectedAlgorithm?: string | null,
8+) {
9+ return useMemo(() => {
10+ if (selectedDataset) {
11+ return results
12+ .filter((row) => row.dataset === selectedDataset)
13+ .map((row) => ({
14+ algorithm: row.algorithm,
15+ compression_ratio: row.compression_ratio,
16+ encode_speed: row.encode_mb_per_sec,
17+ decode_speed: row.decode_mb_per_sec,
18+ }));
19+ } else if (selectedAlgorithm) {
20+ return results
21+ .filter((row) => row.algorithm === selectedAlgorithm)
22+ .map((row) => ({
23+ algorithm: row.dataset,
24+ compression_ratio: row.compression_ratio,
25+ encode_speed: row.encode_mb_per_sec,
26+ decode_speed: row.decode_mb_per_sec,
27+ }));
28+ }
29+ return [];
30+ }, [results, selectedDataset, selectedAlgorithm]);
31+}
web-ui/src/pages/Algorithm.tsxmodified+72−51View file
@@ -1,13 +1,22 @@
11 import { useParams } from "react-router-dom";
2-import { Algorithm as AlgorithmType } from "../types";
2+import { Algorithm as AlgorithmType, BenchmarkData } from "../types";
3+import { BenchmarkCharts } from "../components/benchmark/charts/BenchmarkCharts";
4+import { useBenchmarkChartData } from "../hooks/useBenchmarkChartData";
5+import { BenchmarkTable } from "../components/benchmark/table/BenchmarkTable";
36
47 interface AlgorithmProps {
58 algorithms: AlgorithmType[];
9+ benchmarkData: BenchmarkData | null;
610 }
711
8-function Algorithm({ algorithms }: AlgorithmProps) {
12+function Algorithm({ algorithms, benchmarkData }: AlgorithmProps) {
913 const { algorithmName } = useParams<{ algorithmName: string }>();
1014 const algorithm = algorithms.find((a) => a.name === algorithmName);
15+ const chartData = useBenchmarkChartData(
16+ benchmarkData?.results || [],
17+ null,
18+ algorithm?.name || null,
19+ );
1120
1221 if (!algorithm) {
1322 return <div>Algorithm not found</div>;
@@ -31,29 +40,24 @@ function Algorithm({ algorithms }: AlgorithmProps) {
3140 {algorithm.description}
3241 </p>
3342 </div>
34- <div style={{ marginBottom: "1.5rem" }}>
35- <h2
36- style={{
37- fontSize: "1.2rem",
38- fontWeight: "bold",
39- marginBottom: "0.5rem",
40- }}
41- >
42- Version
43- </h2>
44- <p style={{ fontSize: "0.9rem" }}>{algorithm.version}</p>
45- </div>
46- <div style={{ marginBottom: "1.5rem" }}>
47- <h2
48- style={{
49- fontSize: "1.2rem",
50- fontWeight: "bold",
51- marginBottom: "0.5rem",
52- }}
53- >
54- Tags
55- </h2>
43+ <div
44+ style={{
45+ marginBottom: "1.5rem",
46+ display: "flex",
47+ gap: "2rem",
48+ flexWrap: "wrap",
49+ }}
50+ >
5651 <div>
52+ <span style={{ fontWeight: "bold", fontSize: "0.9rem" }}>
53+ Version:{" "}
54+ </span>
55+ <span style={{ fontSize: "0.9rem" }}>{algorithm.version}</span>
56+ </div>
57+ <div>
58+ <span style={{ fontWeight: "bold", fontSize: "0.9rem" }}>
59+ Tags:{" "}
60+ </span>
5761 {algorithm.tags.map((tag) => (
5862 <span
5963 key={tag}
@@ -70,34 +74,51 @@ function Algorithm({ algorithms }: AlgorithmProps) {
7074 </span>
7175 ))}
7276 </div>
77+ {algorithm.source_file && (
78+ <div>
79+ <span style={{ fontWeight: "bold", fontSize: "0.9rem" }}>
80+ Source:{" "}
81+ </span>
82+ <a
83+ href={algorithm.source_file}
84+ target="_blank"
85+ rel="noopener noreferrer"
86+ style={{
87+ color: "#0066cc",
88+ textDecoration: "none",
89+ padding: "2px 6px",
90+ backgroundColor: "#f0f0f0",
91+ borderRadius: "4px",
92+ fontSize: "0.9rem",
93+ }}
94+ >
95+ View
96+ </a>
97+ </div>
98+ )}
7399 </div>
74- {algorithm.source_file && (
75- <div style={{ marginBottom: "1.5rem" }}>
76- <h2
77- style={{
78- fontSize: "1.2rem",
79- fontWeight: "bold",
80- marginBottom: "0.5rem",
81- }}
82- >
83- Source
84- </h2>
85- <a
86- href={algorithm.source_file}
87- target="_blank"
88- rel="noopener noreferrer"
89- style={{
90- color: "#0066cc",
91- textDecoration: "none",
92- padding: "4px 8px",
93- backgroundColor: "#f0f0f0",
94- borderRadius: "4px",
95- fontSize: "0.9rem",
96- }}
97- >
98- View Source
99- </a>
100- </div>
100+ {benchmarkData && (
101+ <>
102+ <div style={{ marginBottom: "1.5rem" }}>
103+ <h2
104+ style={{
105+ fontSize: "1.2rem",
106+ fontWeight: "bold",
107+ marginBottom: "0.5rem",
108+ }}
109+ >
110+ Benchmark Results
111+ </h2>
112+ <BenchmarkCharts chartData={chartData} />
113+ </div>
114+ <div style={{ marginBottom: "1.5rem" }}>
115+ <BenchmarkTable
116+ results={benchmarkData.results.filter(
117+ (result) => result.algorithm === algorithm.name,
118+ )}
119+ />
120+ </div>
121+ </>
101122 )}
102123 </div>
103124 </div>
web-ui/src/pages/Dataset.tsxmodified+116−101View file
@@ -1,13 +1,17 @@
11 import { useParams } from "react-router-dom";
2-import { Dataset as DatasetType } from "../types";
2+import { Dataset as DatasetType, BenchmarkData } from "../types";
33 import TimeseriesView from "../components/dataset/TimeseriesView";
44 import { useEffect, useRef, useState } from "react";
5+import { BenchmarkCharts } from "../components/benchmark/charts/BenchmarkCharts";
6+import { useBenchmarkChartData } from "../hooks/useBenchmarkChartData";
7+import { BenchmarkTable } from "../components/benchmark/table/BenchmarkTable";
58
69 interface DatasetProps {
710 datasets: DatasetType[];
11+ benchmarkData: BenchmarkData | null;
812 }
913
10-function Dataset({ datasets }: DatasetProps) {
14+function Dataset({ datasets, benchmarkData }: DatasetProps) {
1115 const containerRef = useRef<HTMLDivElement>(null);
1216 const [containerWidth, setContainerWidth] = useState(1200);
1317
@@ -30,6 +34,11 @@ function Dataset({ datasets }: DatasetProps) {
3034
3135 const { datasetName } = useParams<{ datasetName: string }>();
3236 const dataset = datasets.find((d) => d.name === datasetName);
37+ const chartData = useBenchmarkChartData(
38+ benchmarkData?.results || [],
39+ dataset?.name || null,
40+ null,
41+ );
3342
3443 if (!dataset) {
3544 return <div>Dataset not found</div>;
@@ -53,47 +62,24 @@ function Dataset({ datasets }: DatasetProps) {
5362 {dataset.description}
5463 </p>
5564 </div>
56- <div style={{ marginBottom: "1.5rem" }}>
57- <div
58- ref={containerRef}
59- style={{
60- width: "100%",
61- height: "300px",
62- backgroundColor: "#f5f5f5",
63- borderRadius: "4px",
64- padding: "1rem",
65- }}
66- >
67- <TimeseriesView
68- width={containerWidth}
69- height={250}
70- dataset={dataset}
71- />
65+ <div
66+ style={{
67+ marginBottom: "1.5rem",
68+ display: "flex",
69+ gap: "2rem",
70+ flexWrap: "wrap",
71+ }}
72+ >
73+ <div>
74+ <span style={{ fontWeight: "bold", fontSize: "0.9rem" }}>
75+ Version:{" "}
76+ </span>
77+ <span style={{ fontSize: "0.9rem" }}>{dataset.version}</span>
7278 </div>
73- </div>
74- <div style={{ marginBottom: "1.5rem" }}>
75- <h2
76- style={{
77- fontSize: "1.2rem",
78- fontWeight: "bold",
79- marginBottom: "0.5rem",
80- }}
81- >
82- Version
83- </h2>
84- <p style={{ fontSize: "0.9rem" }}>{dataset.version}</p>
85- </div>
86- <div style={{ marginBottom: "1.5rem" }}>
87- <h2
88- style={{
89- fontSize: "1.2rem",
90- fontWeight: "bold",
91- marginBottom: "0.5rem",
92- }}
93- >
94- Tags
95- </h2>
9679 <div>
80+ <span style={{ fontWeight: "bold", fontSize: "0.9rem" }}>
81+ Tags:{" "}
82+ </span>
9783 {dataset.tags.map((tag) => (
9884 <span
9985 key={tag}
@@ -110,79 +96,108 @@ function Dataset({ datasets }: DatasetProps) {
11096 </span>
11197 ))}
11298 </div>
113- </div>
114- <div style={{ marginBottom: "1.5rem" }}>
115- <h2
116- style={{
117- fontSize: "1.2rem",
118- fontWeight: "bold",
119- marginBottom: "0.5rem",
120- }}
121- >
122- Downloads
123- </h2>
124- <div style={{ display: "flex", gap: "1rem" }}>
125- {dataset.data_url_npy && (
99+ <div>
100+ <span style={{ fontWeight: "bold", fontSize: "0.9rem" }}>
101+ Download:{" "}
102+ </span>
103+ <span style={{ display: "inline-flex", gap: "0.5rem" }}>
104+ {dataset.data_url_npy && (
105+ <a
106+ href={dataset.data_url_npy}
107+ download={`${dataset.name}-${dataset.version}.npy`}
108+ style={{
109+ color: "#0066cc",
110+ textDecoration: "none",
111+ padding: "2px 6px",
112+ backgroundColor: "#f0f0f0",
113+ borderRadius: "4px",
114+ fontSize: "0.9rem",
115+ }}
116+ >
117+ NPY
118+ </a>
119+ )}
120+ {dataset.data_url_raw && (
121+ <a
122+ href={dataset.data_url_raw}
123+ download={`${dataset.name}-${dataset.version}.dat`}
124+ style={{
125+ color: "#0066cc",
126+ textDecoration: "none",
127+ padding: "2px 6px",
128+ backgroundColor: "#f0f0f0",
129+ borderRadius: "4px",
130+ fontSize: "0.9rem",
131+ }}
132+ >
133+ RAW
134+ </a>
135+ )}
136+ </span>
137+ </div>
138+ {dataset.source_file && (
139+ <div>
140+ <span style={{ fontWeight: "bold", fontSize: "0.9rem" }}>
141+ Source:{" "}
142+ </span>
126143 <a
127- href={dataset.data_url_npy}
128- download={`${dataset.name}-${dataset.version}.npy`}
144+ href={dataset.source_file}
145+ target="_blank"
146+ rel="noopener noreferrer"
129147 style={{
130148 color: "#0066cc",
131149 textDecoration: "none",
132- padding: "4px 8px",
150+ padding: "2px 6px",
133151 backgroundColor: "#f0f0f0",
134152 borderRadius: "4px",
135153 fontSize: "0.9rem",
136154 }}
137155 >
138- Download NPY
156+ View
139157 </a>
140- )}
141- {dataset.data_url_raw && (
142- <a
143- href={dataset.data_url_raw}
144- download={`${dataset.name}-${dataset.version}.dat`}
158+ </div>
159+ )}
160+ </div>
161+ <div style={{ marginBottom: "1.5rem" }}>
162+ <div
163+ ref={containerRef}
164+ style={{
165+ width: "100%",
166+ height: "300px",
167+ backgroundColor: "#f5f5f5",
168+ borderRadius: "4px",
169+ padding: "1rem",
170+ }}
171+ >
172+ <TimeseriesView
173+ width={containerWidth}
174+ height={250}
175+ dataset={dataset}
176+ />
177+ </div>
178+ </div>
179+ {benchmarkData && (
180+ <>
181+ <div style={{ marginBottom: "1.5rem" }}>
182+ <h2
145183 style={{
146- color: "#0066cc",
147- textDecoration: "none",
148- padding: "4px 8px",
149- backgroundColor: "#f0f0f0",
150- borderRadius: "4px",
151- fontSize: "0.9rem",
184+ fontSize: "1.2rem",
185+ fontWeight: "bold",
186+ marginBottom: "0.5rem",
152187 }}
153188 >
154- Download RAW
155- </a>
156- )}
157- </div>
158- </div>
159- {dataset.source_file && (
160- <div style={{ marginBottom: "1.5rem" }}>
161- <h2
162- style={{
163- fontSize: "1.2rem",
164- fontWeight: "bold",
165- marginBottom: "0.5rem",
166- }}
167- >
168- Source
169- </h2>
170- <a
171- href={dataset.source_file}
172- target="_blank"
173- rel="noopener noreferrer"
174- style={{
175- color: "#0066cc",
176- textDecoration: "none",
177- padding: "4px 8px",
178- backgroundColor: "#f0f0f0",
179- borderRadius: "4px",
180- fontSize: "0.9rem",
181- }}
182- >
183- View Source
184- </a>
185- </div>
189+ Benchmark Results
190+ </h2>
191+ <BenchmarkCharts chartData={chartData} />
192+ </div>
193+ <div style={{ marginBottom: "1.5rem" }}>
194+ <BenchmarkTable
195+ results={benchmarkData.results.filter(
196+ (result) => result.dataset === dataset.name,
197+ )}
198+ />
199+ </div>
200+ </>
186201 )}
187202 </div>
188203 </div>
web-ui/src/pages/Home.tsxmodified+97−34View file
@@ -10,7 +10,7 @@ interface HomeProps {
1010 }
1111
1212 function Home({ benchmarkData }: HomeProps) {
13- const [searchParams] = useSearchParams();
13+ const [searchParams, setSearchParams] = useSearchParams();
1414 const selectedDataset = searchParams.get("dataset") || "";
1515 const selectedAlgorithm = searchParams.get("algorithm") || "";
1616
@@ -82,7 +82,7 @@ function Home({ benchmarkData }: HomeProps) {
8282 color: "#333",
8383 }}
8484 >
85- Integer Compression Benchmark
85+ Benchmark Results
8686 </h1>
8787 <p
8888 style={{
@@ -90,44 +90,107 @@ function Home({ benchmarkData }: HomeProps) {
9090 marginTop: "0.5rem",
9191 }}
9292 >
93- Comparing numeric array compression algorithms
93+ Comparing compression algorithms for numeric arrays
9494 </p>
9595 </header>
9696 <main>
97- <div
98- style={{
99- marginBottom: "2rem",
100- display: "flex",
101- flexDirection: "column",
102- gap: "1rem",
103- }}
104- >
105- {!selectedDataset && (
106- <TagFilter
107- availableTags={availableDatasetTags}
108- selectedTags={selectedDatasetTags}
109- onTagToggle={toggleDatasetTag}
110- label="Filter datasets"
111- />
112- )}
97+ <div style={{ marginBottom: "2rem" }}>
98+ <div
99+ style={{
100+ display: "flex",
101+ alignItems: "center",
102+ gap: "12px",
103+ marginBottom: "1rem",
104+ }}
105+ >
106+ <div style={{ display: "flex", alignItems: "center", gap: "6px" }}>
107+ <label htmlFor="dataset-select">Dataset:</label>
108+ <select
109+ id="dataset-select"
110+ value={selectedDataset}
111+ onChange={(e) => {
112+ if (e.target.value) {
113+ setSearchParams({ dataset: e.target.value });
114+ } else {
115+ setSearchParams(
116+ selectedAlgorithm ? { algorithm: selectedAlgorithm } : {},
117+ );
118+ }
119+ }}
120+ style={{
121+ padding: "4px 8px",
122+ borderRadius: "4px",
123+ border: "1px solid #ccc",
124+ minWidth: "150px",
125+ backgroundColor: "#fff",
126+ fontSize: "0.9rem",
127+ }}
128+ >
129+ <option value="">All Datasets</option>
130+ {filteredDatasets.map((dataset) => (
131+ <option key={dataset.name} value={dataset.name}>
132+ {dataset.name}
133+ </option>
134+ ))}
135+ </select>
136+ </div>
137+ <div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
138+ <label htmlFor="algorithm-select">Algorithm:</label>
139+ <select
140+ id="algorithm-select"
141+ value={selectedAlgorithm}
142+ onChange={(e) => {
143+ if (e.target.value) {
144+ setSearchParams({ algorithm: e.target.value });
145+ } else {
146+ setSearchParams(
147+ selectedDataset ? { dataset: selectedDataset } : {},
148+ );
149+ }
150+ }}
151+ style={{
152+ padding: "4px 8px",
153+ borderRadius: "4px",
154+ border: "1px solid #ccc",
155+ minWidth: "150px",
156+ backgroundColor: "#fff",
157+ fontSize: "0.9rem",
158+ }}
159+ >
160+ <option value="">All Algorithms</option>
161+ {filteredAlgorithms.map((algorithm) => (
162+ <option key={algorithm.name} value={algorithm.name}>
163+ {algorithm.name}
164+ </option>
165+ ))}
166+ </select>
167+ </div>
168+ </div>
169+
170+ <div
171+ style={{ display: "flex", flexDirection: "column", gap: "1rem" }}
172+ >
173+ {!selectedDataset && (
174+ <TagFilter
175+ availableTags={availableDatasetTags}
176+ selectedTags={selectedDatasetTags}
177+ onTagToggle={toggleDatasetTag}
178+ label="Filter datasets"
179+ />
180+ )}
113181
114- {!selectedAlgorithm && (
115- <TagFilter
116- availableTags={availableAlgorithmTags}
117- selectedTags={selectedAlgorithmTags}
118- onTagToggle={toggleAlgorithmTag}
119- label="Filter algorithms"
120- />
121- )}
182+ {!selectedAlgorithm && (
183+ <TagFilter
184+ availableTags={availableAlgorithmTags}
185+ selectedTags={selectedAlgorithmTags}
186+ onTagToggle={toggleAlgorithmTag}
187+ label="Filter algorithms"
188+ />
189+ )}
190+ </div>
122191 </div>
123192
124- {benchmarkData && (
125- <BenchmarkTable
126- results={filteredResults}
127- availableDatasets={filteredDatasets.map((d) => d.name)}
128- availableAlgorithms={filteredAlgorithms.map((a) => a.name)}
129- />
130- )}
193+ {benchmarkData && <BenchmarkTable results={filteredResults} />}
131194 </main>
132195 </div>
133196 );
moveopenescclose