long descriptions
12 changed files+424−267
benchcompress/MANIFEST.inadded+1−0View file
@@ -0,0 +1 @@
1+recursive-include src/benchcompress *.md
benchcompress/src/benchcompress/datasets/bernoulli/__init__.pymodified+16−0View file
@@ -1,9 +1,20 @@
11 import numpy as np
2+import os
23
34
45 SOURCE_FILE = "bernoulli/__init__.py"
56
67
8+def _load_long_description():
9+ current_dir = os.path.dirname(os.path.abspath(__file__))
10+ md_path = os.path.join(current_dir, "bernoulli.md")
11+ with open(md_path, "r", encoding="utf-8") as f:
12+ return f.read()
13+
14+
15+LONG_DESCRIPTION = _load_long_description()
16+
17+
718 def create_bernoulli(*, n_samples: int, p: float, seed: int) -> np.ndarray:
819 rng = np.random.default_rng(seed)
920 x = rng.binomial(1, p, n_samples).astype(np.uint8)
@@ -20,6 +31,7 @@ datasets = [
2031 "description": "Binary sequence with 10% probability of ones.",
2132 "tags": tags,
2233 "source_file": SOURCE_FILE,
34+ "long_description": LONG_DESCRIPTION,
2335 },
2436 {
2537 "name": "bernoulli-0.2",
@@ -28,6 +40,7 @@ datasets = [
2840 "description": "Binary sequence with 20% probability of ones.",
2941 "tags": tags,
3042 "source_file": SOURCE_FILE,
43+ "long_description": LONG_DESCRIPTION,
3144 },
3245 {
3346 "name": "bernoulli-0.3",
@@ -36,6 +49,7 @@ datasets = [
3649 "description": "Binary sequence with 30% probability of ones.",
3750 "tags": tags,
3851 "source_file": SOURCE_FILE,
52+ "long_description": LONG_DESCRIPTION,
3953 },
4054 {
4155 "name": "bernoulli-0.4",
@@ -44,6 +58,7 @@ datasets = [
4458 "description": "Binary sequence with 40% probability of ones.",
4559 "tags": tags,
4660 "source_file": SOURCE_FILE,
61+ "long_description": LONG_DESCRIPTION,
4762 },
4863 {
4964 "name": "bernoulli-0.5",
@@ -52,5 +67,6 @@ datasets = [
5267 "description": "Binary sequence with 50% probability of ones and 50% probability of zeros.",
5368 "tags": tags,
5469 "source_file": SOURCE_FILE,
70+ "long_description": LONG_DESCRIPTION,
5571 },
5672 ]
benchcompress/src/benchcompress/datasets/bernoulli/bernoulli.mdadded+21−0View file
@@ -0,0 +1,21 @@
1+# Bernoulli Dataset
2+
3+A Bernoulli dataset is a sequence of binary values (0s and 1s) where each value is independently generated with a fixed probability `p` of being 1. These datasets are useful for testing compression algorithms because they have well-defined theoretical properties.
4+
5+## Variants
6+
7+We provide five variants with different probabilities of generating a 1:
8+- bernoulli-0.1 (10% ones)
9+- bernoulli-0.2 (20% ones)
10+- bernoulli-0.3 (30% ones)
11+- bernoulli-0.4 (40% ones)
12+- bernoulli-0.5 (50% ones)
13+
14+## Theoretical Properties
15+
16+The theoretical entropy H(p) provides a lower bound for compression:
17+```
18+H(p) = -p log₂(p) - (1-p) log₂(1-p) bits/symbol
19+```
20+
21+For example, at p = 0.1 the theoretical minimum is 0.469 bits/symbol, while at p = 0.5 it reaches the maximum of 1.000 bits/symbol.
web-ui/src/components/algorithm/AlgorithmContent.cssdeleted+0−15View file
@@ -1,15 +0,0 @@
1-.algorithm-tag {
2- display: inline-block;
3- background-color: #e1e1e1;
4- padding: 2px 6px;
5- border-radius: 3px;
6- margin: 2px;
7- font-size: 0.8rem;
8- cursor: pointer;
9- transition: all 0.2s ease;
10-}
11-
12-.algorithm-tag:hover {
13- background-color: #d1d1d1;
14- transform: translateY(-1px);
15-}
web-ui/src/components/algorithm/AlgorithmContent.tsxmodified+15−93View file
@@ -1,20 +1,16 @@
11 import { Algorithm, BenchmarkData } from "../../types";
2-import "./AlgorithmContent.css";
3-import { useNavigate } from "react-router-dom";
4-import { BenchmarkCharts } from "../benchmark/charts/BenchmarkCharts";
5-import { BenchmarkTable } from "../benchmark/table/BenchmarkTable";
6-
7-type ChartData = Array<{
8- algorithm: string;
9- compression_ratio: number;
10- encode_speed: number;
11- decode_speed: number;
12-}>;
2+import { BaseContent } from "../shared/BaseContent";
3+import "../shared/ContentStyles.css";
134
145 interface AlgorithmContentProps {
156 algorithm: Algorithm;
167 benchmarkData: BenchmarkData | null;
17- chartData: ChartData;
8+ chartData: Array<{
9+ algorithm: string;
10+ compression_ratio: number;
11+ encode_speed: number;
12+ decode_speed: number;
13+ }>;
1814 }
1915
2016 export const AlgorithmContent = ({
@@ -22,87 +18,13 @@ export const AlgorithmContent = ({
2218 benchmarkData,
2319 chartData,
2420 }: AlgorithmContentProps) => {
25- const navigate = useNavigate();
26-
2721 return (
28- <div>
29- <div style={{ marginBottom: "1.5rem" }}>
30- <p style={{ fontSize: "0.9rem", lineHeight: "1.5" }}>
31- {algorithm.description}
32- </p>
33- </div>
34- <div
35- style={{
36- marginBottom: "1.5rem",
37- display: "flex",
38- gap: "2rem",
39- flexWrap: "wrap",
40- }}
41- >
42- <div>
43- <span style={{ fontWeight: "bold", fontSize: "0.9rem" }}>
44- Version:{" "}
45- </span>
46- <span style={{ fontSize: "0.9rem" }}>{algorithm.version}</span>
47- </div>
48- <div>
49- <span style={{ fontWeight: "bold", fontSize: "0.9rem" }}>Tags: </span>
50- {algorithm.tags.map((tag) => (
51- <span
52- onClick={() => navigate(`/algorithms?tag=${tag}`)}
53- key={tag}
54- className="algorithm-tag"
55- >
56- {tag}
57- </span>
58- ))}
59- </div>
60- {algorithm.source_file && (
61- <div>
62- <span style={{ fontWeight: "bold", fontSize: "0.9rem" }}>
63- Source:{" "}
64- </span>
65- <a
66- href={algorithm.source_file}
67- target="_blank"
68- rel="noopener noreferrer"
69- style={{
70- color: "#0066cc",
71- textDecoration: "none",
72- padding: "2px 6px",
73- backgroundColor: "#f0f0f0",
74- borderRadius: "4px",
75- fontSize: "0.9rem",
76- }}
77- >
78- View
79- </a>
80- </div>
81- )}
82- </div>
83- {benchmarkData && (
84- <>
85- <div style={{ marginBottom: "1.5rem" }}>
86- <h2
87- style={{
88- fontSize: "1.2rem",
89- fontWeight: "bold",
90- marginBottom: "0.5rem",
91- }}
92- >
93- Benchmark Results
94- </h2>
95- <BenchmarkCharts chartData={chartData} />
96- </div>
97- <div style={{ marginBottom: "1.5rem" }}>
98- <BenchmarkTable
99- results={benchmarkData.results.filter(
100- (result) => result.algorithm === algorithm.name,
101- )}
102- />
103- </div>
104- </>
105- )}
106- </div>
22+ <BaseContent
23+ item={algorithm}
24+ benchmarkData={benchmarkData}
25+ chartData={chartData}
26+ tagNavigationPrefix="/algorithms"
27+ filterKey="algorithm"
28+ />
10729 );
10830 };
web-ui/src/components/dataset/DatasetContent.cssdeleted+0−15View file
@@ -1,15 +0,0 @@
1-.dataset-tag {
2- display: inline-block;
3- background-color: #e1e1e1;
4- padding: 2px 6px;
5- border-radius: 3px;
6- margin: 2px;
7- font-size: 0.8rem;
8- cursor: pointer;
9- transition: all 0.2s ease;
10-}
11-
12-.dataset-tag:hover {
13- background-color: #d1d1d1;
14- transform: translateY(-1px);
15-}
web-ui/src/components/dataset/DatasetContent.tsxmodified+56−143View file
@@ -1,21 +1,18 @@
11 import { Dataset, BenchmarkData } from "../../types";
2-import { useNavigate } from "react-router-dom";
3-import "./DatasetContent.css";
4-import TimeseriesView from "./TimeseriesView";
5-import { BenchmarkCharts } from "../benchmark/charts/BenchmarkCharts";
6-import { BenchmarkTable } from "../benchmark/table/BenchmarkTable";
72 import { useEffect, useRef, useState } from "react";
8-type ChartData = Array<{
9- algorithm: string;
10- compression_ratio: number;
11- encode_speed: number;
12- decode_speed: number;
13-}>;
3+import TimeseriesView from "./TimeseriesView";
4+import { BaseContent } from "../shared/BaseContent";
5+import "../shared/ContentStyles.css";
146
157 interface DatasetContentProps {
168 dataset: Dataset;
179 benchmarkData: BenchmarkData | null;
18- chartData: ChartData;
10+ chartData: Array<{
11+ algorithm: string;
12+ compression_ratio: number;
13+ encode_speed: number;
14+ decode_speed: number;
15+ }>;
1916 }
2017
2118 export const DatasetContent = ({
@@ -23,7 +20,6 @@ export const DatasetContent = ({
2320 benchmarkData,
2421 chartData,
2522 }: DatasetContentProps) => {
26- const navigate = useNavigate();
2723 const containerRef = useRef<HTMLDivElement>(null);
2824 const [containerWidth, setContainerWidth] = useState(1200);
2925
@@ -43,142 +39,59 @@ export const DatasetContent = ({
4339 };
4440 }, []);
4541
46- return (
47- <div>
48- <div style={{ marginBottom: "1.5rem" }}>
49- <p style={{ fontSize: "0.9rem", lineHeight: "1.5" }}>
50- {dataset.description}
51- </p>
52- </div>
53- <div
54- style={{
55- marginBottom: "1.5rem",
56- display: "flex",
57- gap: "2rem",
58- flexWrap: "wrap",
59- }}
60- >
61- <div>
62- <span style={{ fontWeight: "bold", fontSize: "0.9rem" }}>
63- Version:{" "}
64- </span>
65- <span style={{ fontSize: "0.9rem" }}>{dataset.version}</span>
66- </div>
67- <div>
68- <span style={{ fontWeight: "bold", fontSize: "0.9rem" }}>Tags: </span>
69- {dataset.tags.map((tag) => (
70- <span
71- key={tag}
72- className="dataset-tag"
73- onClick={() => navigate(`/datasets?tag=${tag}`)}
42+ const downloadSection =
43+ dataset.data_url_npy || dataset.data_url_raw ? (
44+ <div>
45+ <span className="metadata-label">Download: </span>
46+ <span style={{ display: "inline-flex", gap: "0.5rem" }}>
47+ {dataset.data_url_npy && (
48+ <a
49+ href={dataset.data_url_npy}
50+ download={`${dataset.name}-${dataset.version}.npy`}
51+ className="download-link"
7452 >
75- {tag}
76- </span>
77- ))}
78- </div>
79- <div>
80- <span style={{ fontWeight: "bold", fontSize: "0.9rem" }}>
81- Download:{" "}
82- </span>
83- <span style={{ display: "inline-flex", gap: "0.5rem" }}>
84- {dataset.data_url_npy && (
85- <a
86- href={dataset.data_url_npy}
87- download={`${dataset.name}-${dataset.version}.npy`}
88- style={{
89- color: "#0066cc",
90- textDecoration: "none",
91- padding: "2px 6px",
92- backgroundColor: "#f0f0f0",
93- borderRadius: "4px",
94- fontSize: "0.9rem",
95- }}
96- >
97- NPY
98- </a>
99- )}
100- {dataset.data_url_raw && (
101- <a
102- href={dataset.data_url_raw}
103- download={`${dataset.name}-${dataset.version}.dat`}
104- style={{
105- color: "#0066cc",
106- textDecoration: "none",
107- padding: "2px 6px",
108- backgroundColor: "#f0f0f0",
109- borderRadius: "4px",
110- fontSize: "0.9rem",
111- }}
112- >
113- RAW
114- </a>
115- )}
116- </span>
117- </div>
118- {dataset.source_file && (
119- <div>
120- <span style={{ fontWeight: "bold", fontSize: "0.9rem" }}>
121- Source:{" "}
122- </span>
53+ NPY
54+ </a>
55+ )}
56+ {dataset.data_url_raw && (
12357 <a
124- href={dataset.source_file}
125- target="_blank"
126- rel="noopener noreferrer"
127- style={{
128- color: "#0066cc",
129- textDecoration: "none",
130- padding: "2px 6px",
131- backgroundColor: "#f0f0f0",
132- borderRadius: "4px",
133- fontSize: "0.9rem",
134- }}
58+ href={dataset.data_url_raw}
59+ download={`${dataset.name}-${dataset.version}.dat`}
60+ className="download-link"
13561 >
136- View
62+ RAW
13763 </a>
138- </div>
139- )}
64+ )}
65+ </span>
14066 </div>
141- <div style={{ marginBottom: "1.5rem" }}>
142- <div
143- ref={containerRef}
144- style={{
145- width: "100%",
146- height: "300px",
147- backgroundColor: "#f5f5f5",
148- borderRadius: "4px",
149- padding: "1rem",
150- }}
151- >
152- <TimeseriesView
153- width={containerWidth}
154- height={250}
155- dataset={dataset}
156- />
157- </div>
67+ ) : null;
68+
69+ const timeseriesSection = (
70+ <div className="content-container">
71+ <div
72+ ref={containerRef}
73+ style={{
74+ width: "100%",
75+ height: "300px",
76+ backgroundColor: "#f5f5f5",
77+ borderRadius: "4px",
78+ padding: "1rem",
79+ }}
80+ >
81+ <TimeseriesView width={containerWidth} height={250} dataset={dataset} />
15882 </div>
159- {benchmarkData && (
160- <>
161- <div style={{ marginBottom: "1.5rem" }}>
162- <h2
163- style={{
164- fontSize: "1.2rem",
165- fontWeight: "bold",
166- marginBottom: "0.5rem",
167- }}
168- >
169- Benchmark Results
170- </h2>
171- <BenchmarkCharts chartData={chartData} />
172- </div>
173- <div style={{ marginBottom: "1.5rem" }}>
174- <BenchmarkTable
175- results={benchmarkData.results.filter(
176- (result) => result.dataset === dataset.name,
177- )}
178- />
179- </div>
180- </>
181- )}
18283 </div>
18384 );
85+
86+ return (
87+ <BaseContent
88+ item={dataset}
89+ benchmarkData={benchmarkData}
90+ chartData={chartData}
91+ tagNavigationPrefix="/datasets"
92+ filterKey="dataset"
93+ downloadSection={downloadSection}
94+ additionalContent={timeseriesSection}
95+ />
96+ );
18497 };
web-ui/src/components/dataset/TimeseriesNavigationBar.tsxadded+98−0View file
@@ -0,0 +1,98 @@
1+import React, { useRef } from "react";
2+import { Range } from "./WorkerTypes";
3+
4+interface TimeseriesNavigationBarProps {
5+ width: number;
6+ height: number;
7+ totalRange: Range;
8+ viewRange: Range;
9+ onViewRangeChange: (range: Range) => void;
10+}
11+
12+const TimeseriesNavigationBar: React.FC<TimeseriesNavigationBarProps> = ({
13+ width,
14+ height,
15+ totalRange,
16+ viewRange,
17+ onViewRangeChange,
18+}) => {
19+ const containerRef = useRef<HTMLDivElement>(null);
20+
21+ // Constants
22+ const minMarkerWidth = 25; // Minimum width of the marker in pixels
23+ const padding = 10; // Padding on left and right
24+ const barWidth = width - 2 * padding;
25+
26+ // Convert data range to pixel coordinates
27+ const rangeToPixel = (value: number): number => {
28+ const ratio = (value - totalRange.min) / (totalRange.max - totalRange.min);
29+ return padding + ratio * barWidth;
30+ };
31+
32+ // Convert pixel coordinates to data range
33+ const pixelToRange = (pixel: number): number => {
34+ const ratio = (pixel - padding) / barWidth;
35+ return totalRange.min + ratio * (totalRange.max - totalRange.min);
36+ };
37+
38+ // Calculate marker position and width
39+ const markerLeft = rangeToPixel(viewRange.min);
40+ const rawMarkerWidth = rangeToPixel(viewRange.max) - markerLeft;
41+ const markerWidth = Math.max(rawMarkerWidth, minMarkerWidth);
42+
43+ const handleClick = (e: React.MouseEvent) => {
44+ if (!containerRef.current) return;
45+
46+ const rect = containerRef.current.getBoundingClientRect();
47+ const clickX = e.clientX - rect.left;
48+
49+ // Click on the bar - center the view on click position
50+ const clickedValue = pixelToRange(clickX);
51+ const currentSize = viewRange.max - viewRange.min;
52+ const halfSize = currentSize / 2;
53+
54+ let newMin = clickedValue - halfSize;
55+ let newMax = clickedValue + halfSize;
56+
57+ // Clamp to total range bounds
58+ if (newMin < totalRange.min) {
59+ newMin = totalRange.min;
60+ newMax = newMin + currentSize;
61+ }
62+ if (newMax > totalRange.max) {
63+ newMax = totalRange.max;
64+ newMin = newMax - currentSize;
65+ }
66+
67+ onViewRangeChange({ min: newMin, max: newMax });
68+ };
69+
70+ return (
71+ <div
72+ ref={containerRef}
73+ style={{
74+ width,
75+ height,
76+ position: "relative",
77+ backgroundColor: "#f0f0f0",
78+ borderRadius: 4,
79+ cursor: "pointer",
80+ }}
81+ onClick={handleClick}
82+ >
83+ <div
84+ style={{
85+ position: "absolute",
86+ left: markerLeft,
87+ width: markerWidth,
88+ height: "100%",
89+ backgroundColor: "#007bff",
90+ borderRadius: 4,
91+ pointerEvents: "none",
92+ }}
93+ />
94+ </div>
95+ );
96+};
97+
98+export default TimeseriesNavigationBar;
web-ui/src/components/dataset/TimeseriesView.tsxmodified+13−1View file
@@ -1,4 +1,5 @@
11 import { useEffect, useMemo, useReducer, useState, useCallback } from "react";
2+import TimeseriesNavigationBar from "./TimeseriesNavigationBar";
23 import { SupportedTypedArray } from "../../hooks/TimeseriesDataClient";
34 import { useTimeseriesDataClient } from "../../hooks/useTimeseriesDataClient";
45 import { Dataset } from "../../types";
@@ -310,7 +311,18 @@ const TimeseriesView: React.FC<TimeseriesViewProps> = ({
310311 };
311312
312313 return (
313- <div style={{ position: "relative", width, height: height + 30 }}>
314+ <div style={{ position: "relative", width, height: height + 50 }}>
315+ <div style={{ marginBottom: 10, height: 20 }}>
316+ <TimeseriesNavigationBar
317+ width={width}
318+ height={20}
319+ totalRange={{ min: 0, max: client ? client.getShape() - 1 : 999 }}
320+ viewRange={xRange}
321+ onViewRangeChange={(range) =>
322+ dispatch({ type: "SET_X_RANGE", range })
323+ }
324+ />
325+ </div>
314326 {showHint && (
315327 <div
316328 style={{
web-ui/src/components/shared/BaseContent.tsxadded+114−0View file
@@ -0,0 +1,114 @@
1+import { useNavigate } from "react-router-dom";
2+import { useState } from "react";
3+import { BenchmarkData } from "../../types";
4+import { BenchmarkCharts } from "../benchmark/charts/BenchmarkCharts";
5+import { BenchmarkTable } from "../benchmark/table/BenchmarkTable";
6+import "./ContentStyles.css";
7+
8+export interface BaseItem {
9+ name: string;
10+ description: string;
11+ long_description?: string;
12+ version: string;
13+ tags: string[];
14+ source_file?: string;
15+}
16+
17+interface BaseContentProps {
18+ item: BaseItem;
19+ benchmarkData: BenchmarkData | null;
20+ chartData: Array<{
21+ algorithm: string;
22+ compression_ratio: number;
23+ encode_speed: number;
24+ decode_speed: number;
25+ }>;
26+ tagNavigationPrefix: string;
27+ filterKey: "dataset" | "algorithm";
28+ downloadSection?: React.ReactNode;
29+ additionalContent?: React.ReactNode;
30+}
31+
32+export const BaseContent = ({
33+ item,
34+ benchmarkData,
35+ chartData,
36+ tagNavigationPrefix,
37+ filterKey,
38+ downloadSection,
39+ additionalContent,
40+}: BaseContentProps) => {
41+ const navigate = useNavigate();
42+ const [isExpanded, setIsExpanded] = useState(false);
43+
44+ return (
45+ <div>
46+ <div className="content-container">
47+ <p className="content-header">
48+ <strong>{item.name}</strong> | {item.description}
49+ </p>
50+ {item.long_description && (
51+ <>
52+ <button
53+ className="description-toggle"
54+ onClick={() => setIsExpanded(!isExpanded)}
55+ >
56+ {isExpanded ? "View less" : "Read more"}
57+ </button>
58+ {isExpanded && (
59+ <div className="long-description">{item.long_description}</div>
60+ )}
61+ </>
62+ )}
63+ </div>
64+ <div className="metadata-section">
65+ <div>
66+ <span className="metadata-label">Version: </span>
67+ <span className="metadata-value">{item.version}</span>
68+ </div>
69+ <div>
70+ <span className="metadata-label">Tags: </span>
71+ {item.tags.map((tag) => (
72+ <span
73+ key={tag}
74+ className="tag"
75+ onClick={() => navigate(`${tagNavigationPrefix}?tag=${tag}`)}
76+ >
77+ {tag}
78+ </span>
79+ ))}
80+ </div>
81+ {downloadSection}
82+ {item.source_file && (
83+ <div>
84+ <span className="metadata-label">Source: </span>
85+ <a
86+ href={item.source_file}
87+ target="_blank"
88+ rel="noopener noreferrer"
89+ className="source-link"
90+ >
91+ View
92+ </a>
93+ </div>
94+ )}
95+ </div>
96+ {additionalContent}
97+ {benchmarkData && (
98+ <>
99+ <div className="benchmark-section">
100+ <h2 className="benchmark-title">Benchmark Results</h2>
101+ <BenchmarkCharts chartData={chartData} />
102+ </div>
103+ <div className="benchmark-section">
104+ <BenchmarkTable
105+ results={benchmarkData.results.filter(
106+ (result) => result[filterKey] === item.name,
107+ )}
108+ />
109+ </div>
110+ </>
111+ )}
112+ </div>
113+ );
114+};
web-ui/src/components/shared/ContentStyles.cssadded+88−0View file
@@ -0,0 +1,88 @@
1+.content-container {
2+ margin-bottom: 1.5rem;
3+}
4+
5+.content-header {
6+ font-size: 0.9rem;
7+ line-height: 1.5;
8+ margin-bottom: 0.5rem;
9+}
10+
11+.description-toggle {
12+ color: #0066cc;
13+ text-decoration: none;
14+ font-size: 0.8rem;
15+ cursor: pointer;
16+ background: none;
17+ border: none;
18+ padding: 0;
19+ margin-top: 0.25rem;
20+ display: block;
21+}
22+
23+.description-toggle:hover {
24+ text-decoration: underline;
25+}
26+
27+.long-description {
28+ font-size: 0.9rem;
29+ line-height: 1.5;
30+ margin-top: 0.5rem;
31+ padding: 0.5rem;
32+ background-color: #f8f8f8;
33+ border-radius: 4px;
34+}
35+
36+.metadata-section {
37+ margin-bottom: 1.5rem;
38+ display: flex;
39+ gap: 2rem;
40+ flex-wrap: wrap;
41+}
42+
43+.metadata-label {
44+ font-weight: bold;
45+ font-size: 0.9rem;
46+}
47+
48+.metadata-value {
49+ font-size: 0.9rem;
50+}
51+
52+.tag {
53+ color: #0066cc;
54+ text-decoration: none;
55+ padding: 2px 6px;
56+ background-color: #f0f0f0;
57+ border-radius: 4px;
58+ font-size: 0.9rem;
59+ cursor: pointer;
60+}
61+
62+.source-link {
63+ color: #0066cc;
64+ text-decoration: none;
65+ padding: 2px 6px;
66+ background-color: #f0f0f0;
67+ border-radius: 4px;
68+ font-size: 0.9rem;
69+}
70+
71+.benchmark-section {
72+ margin-bottom: 1.5rem;
73+}
74+
75+.benchmark-title {
76+ font-size: 1.2rem;
77+ font-weight: bold;
78+ margin-bottom: 0.5rem;
79+}
80+
81+.download-link {
82+ color: #0066cc;
83+ text-decoration: none;
84+ padding: 2px 6px;
85+ background-color: #f0f0f0;
86+ border-radius: 4px;
87+ font-size: 0.9rem;
88+}
web-ui/src/types.tsmodified+2−0View file
@@ -19,6 +19,7 @@ export interface BenchmarkResult {
1919 export interface Algorithm {
2020 name: string;
2121 description: string;
22+ long_description?: string;
2223 version: string;
2324 tags: string[];
2425 source_file?: string;
@@ -27,6 +28,7 @@ export interface Algorithm {
2728 export interface Dataset {
2829 name: string;
2930 description: string;
31+ long_description?: string;
3032 version: string;
3133 tags: string[];
3234 source_file?: string;