Skip to content

Commit 2c5c040

Browse files
nabeelreclaude
andcommitted
Add dataset switcher dropdown
Introduce src/datasets.js as the single place to register datasets. Each entry is either { source: "local", path } or { source: "hf", dataset, config?, split? }. Switching the dropdown tears down the old DataSource, reloads data, and picks a new random star — the rest of the UI is identical regardless of source type. The dropdown is hidden when only one dataset is configured so it doesn't clutter the header for single-dataset setups. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 0efd883 commit 2c5c040

4 files changed

Lines changed: 118 additions & 20 deletions

File tree

src/App.jsx

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { useEffect, useState } from "react";
22
import { createDataSource } from "./data";
3-
import { config } from "./config";
3+
import { DATASETS } from "./datasets.js";
44
import LightCurvePlot from "./components/LightCurvePlot.jsx";
55

66
export default function App() {
7+
const [dataset, setDataset] = useState(DATASETS[0]);
78
const [info, setInfo] = useState(null);
89
const [rows, setRows] = useState([]);
910
const [error, setError] = useState(null);
@@ -12,13 +13,21 @@ export default function App() {
1213

1314
useEffect(() => {
1415
let cancelled = false;
15-
const ds = createDataSource();
16+
setLoading(true);
17+
setError(null);
18+
setRows([]);
19+
setInfo(null);
20+
setSelectedIdx(null);
21+
22+
const ds = createDataSource(dataset);
1623
Promise.all([ds.getInfo(), ds.getRows({ offset: 0, length: 200 })])
1724
.then(([info, rows]) => {
1825
if (cancelled) return;
1926
setInfo(info);
2027
setRows(rows);
21-
setSelectedIdx(Math.floor(Math.random() * rows.length));
28+
if (rows.length > 0) {
29+
setSelectedIdx(Math.floor(Math.random() * rows.length));
30+
}
2231
})
2332
.catch((err) => {
2433
if (cancelled) return;
@@ -28,40 +37,59 @@ export default function App() {
2837
if (cancelled) return;
2938
setLoading(false);
3039
});
40+
3141
return () => {
3242
cancelled = true;
3343
};
34-
}, []);
44+
}, [dataset]);
3545

3646
const pickRandom = () =>
3747
setSelectedIdx(Math.floor(Math.random() * rows.length));
3848

3949
const selectedRow = selectedIdx !== null ? rows[selectedIdx] : null;
4050

41-
if (loading) return <div className="app"><p>Loading…</p></div>;
42-
if (error) return <div className="app"><p className="error">Error: {error}</p></div>;
43-
4451
return (
4552
<div className="app">
4653
<header>
4754
<div className="header-row">
4855
<div>
4956
<h1>StarEmbed Explorer</h1>
5057
<p className="meta">
51-
Source: <code>{config.dataSource}</code>
52-
{" · "}
53-
{info?.numRows ?? rows.length} sources
54-
{" · "}
55-
g_PTF + R_PTF
58+
{info
59+
? `${info.numRows ?? rows.length} sources · ${dataset.source === "hf" ? dataset.dataset : dataset.path}`
60+
: " "}
5661
</p>
5762
</div>
58-
<button className="random-btn" onClick={pickRandom}>
59-
⚄ Random star
60-
</button>
63+
<div className="header-controls">
64+
{DATASETS.length > 1 && (
65+
<select
66+
className="dataset-select"
67+
value={dataset.id}
68+
onChange={(e) =>
69+
setDataset(DATASETS.find((d) => d.id === e.target.value))
70+
}
71+
>
72+
{DATASETS.map((d) => (
73+
<option key={d.id} value={d.id}>
74+
{d.label}
75+
</option>
76+
))}
77+
</select>
78+
)}
79+
<button
80+
className="random-btn"
81+
onClick={pickRandom}
82+
disabled={rows.length === 0}
83+
>
84+
⚄ Random star
85+
</button>
86+
</div>
6187
</div>
6288
</header>
6389

64-
{selectedRow && <SourceDetail row={selectedRow} />}
90+
{loading && <p className="status-msg">Loading…</p>}
91+
{error && <p className="error">Error: {error}</p>}
92+
{!loading && !error && selectedRow && <SourceDetail row={selectedRow} />}
6593
</div>
6694
);
6795
}

src/data/index.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,24 @@ import { config } from "../config.js";
22
import { LocalDataSource } from "./LocalDataSource.js";
33
import { HFDataSource } from "./HFDataSource.js";
44

5-
export function createDataSource() {
6-
if (config.dataSource === "hf") {
7-
return new HFDataSource(config.hf);
5+
/**
6+
* Create a DataSource from a dataset descriptor (from src/datasets.js).
7+
* Falls back to the env-var config when called with no argument.
8+
*/
9+
export function createDataSource(descriptor) {
10+
if (descriptor) {
11+
return descriptor.source === "hf"
12+
? new HFDataSource({
13+
dataset: descriptor.dataset,
14+
config: descriptor.config ?? "default",
15+
split: descriptor.split ?? "train",
16+
})
17+
: new LocalDataSource({ path: descriptor.path });
818
}
9-
return new LocalDataSource(config.local);
19+
// Env-var fallback (production builds / CI)
20+
return config.dataSource === "hf"
21+
? new HFDataSource(config.hf)
22+
: new LocalDataSource(config.local);
1023
}
1124

1225
export { DataSource } from "./DataSource.js";

src/datasets.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* All datasets available in the dropdown.
3+
* The first entry is selected on load.
4+
*
5+
* Local entry:
6+
* { id, label, source: "local", path }
7+
* path is relative to public/ (same as VITE_LOCAL_DATA_PATH)
8+
*
9+
* HuggingFace entry:
10+
* { id, label, source: "hf", dataset, config?, split? }
11+
* dataset is "username/repo-name" on the HF Hub
12+
*/
13+
export const DATASETS = [
14+
{
15+
id: "ptf-north-local",
16+
label: "PTF North — local",
17+
source: "local",
18+
path: "data/ptf_north.jsonl",
19+
},
20+
// Example HuggingFace entry — fill in dataset and uncomment to enable:
21+
// {
22+
// id: "ptf-north-hf",
23+
// label: "PTF North — HuggingFace",
24+
// source: "hf",
25+
// dataset: "your-username/ptf-north",
26+
// config: "default",
27+
// split: "train",
28+
// },
29+
];

src/index.css

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,29 @@ body,
2929
margin-bottom: 1.5rem;
3030
}
3131

32+
.header-controls {
33+
display: flex;
34+
align-items: center;
35+
gap: 0.5rem;
36+
}
37+
38+
.dataset-select {
39+
padding: 0.45rem 0.7rem;
40+
border: 1px solid #ccc;
41+
border-radius: 5px;
42+
background: #fff;
43+
font-size: 0.875rem;
44+
color: #333;
45+
cursor: pointer;
46+
max-width: 260px;
47+
}
48+
49+
.dataset-select:focus {
50+
outline: none;
51+
border-color: #3a7cbf;
52+
box-shadow: 0 0 0 2px rgba(58, 124, 191, 0.15);
53+
}
54+
3255
.app header h1 {
3356
margin: 0 0 0.2rem;
3457
font-size: 1.4rem;
@@ -185,6 +208,11 @@ body,
185208
font-style: italic;
186209
}
187210

211+
.status-msg {
212+
color: #888;
213+
padding: 0.5rem 0;
214+
}
215+
188216
.error {
189217
color: #b00020;
190218
background: #fde7ea;

0 commit comments

Comments
 (0)