import { useState, useEffect, useCallback } from "react"; import { HF_ORG } from "../../../config"; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- interface PlotlyViewerProps { datasetRepo: string; split?: string; onClose: () => void; } interface HfRow { [key: string]: unknown; } interface ColumnStats { name: string; min: number; max: number; mean: number; count: number; } type ViewerMode = "plotly_json" | "numeric_summary" | "empty"; // --------------------------------------------------------------------------- // Constants // --------------------------------------------------------------------------- const PLOTLY_COLUMN_NAMES = ["plotly_json", "chart_data", "figure_json"]; const HF_DATASETS_API = "https://datasets-server.huggingface.co"; // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- function detectPlotlyColumn(row: HfRow): string | null { for (const col of PLOTLY_COLUMN_NAMES) { if (col in row) return col; } return null; } function extractNumericColumns(rows: HfRow[]): ColumnStats[] { if (rows.length === 0) return []; const firstRow = rows[0]; const stats: ColumnStats[] = []; for (const key of Object.keys(firstRow)) { const values = rows .map((r) => r[key]) .filter((v) => typeof v === "number" && isFinite(v as number)) as number[]; if (values.length === 0) continue; const min = Math.min(...values); const max = Math.max(...values); const mean = values.reduce((a, b) => a + b, 0) / values.length; stats.push({ name: key, min, max, mean, count: values.length }); } return stats; } function tryParseJson(raw: unknown): object | null { if (typeof raw === "object" && raw !== null) return raw as object; if (typeof raw === "string") { try { const parsed = JSON.parse(raw); if (typeof parsed === "object" && parsed !== null) return parsed; } catch { // not valid JSON } } return null; } // --------------------------------------------------------------------------- // Sub-components // --------------------------------------------------------------------------- function CopyButton({ text }: { text: string }) { const [copied, setCopied] = useState(false); const handleCopy = async () => { try { await navigator.clipboard.writeText(text); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch { // clipboard not available } }; return ( ); } function PlotlyJsonView({ jsonData }: { jsonData: object }) { const formatted = JSON.stringify(jsonData, null, 2); // Detect chart type for a helpful header badge const chartType = (jsonData as Record).type || ( (jsonData as Record).data?.[0] as | Record | undefined )?.type || null; return (
{/* Info bar */}
plotly JSON {chartType && ( type:{" "} {String(chartType)} )} Install{" "} react-plotly.js {" "} for interactive rendering
{/* JSON code block */}
          {formatted}
        
); } function NumericSummaryView({ stats }: { stats: ColumnStats[] }) { if (stats.length === 0) { return (

No numeric columns found in dataset.

); } return (

No Plotly JSON column detected ( {PLOTLY_COLUMN_NAMES.join(", ")} ). Showing numeric column summary.

{stats.map((col) => ( ))}
Column Count Min Max Mean
{col.name} {col.count} {col.min.toLocaleString(undefined, { maximumFractionDigits: 4, })} {col.max.toLocaleString(undefined, { maximumFractionDigits: 4, })} {col.mean.toLocaleString(undefined, { maximumFractionDigits: 4, })}
); } // --------------------------------------------------------------------------- // Main Component // --------------------------------------------------------------------------- export default function PlotlyViewer({ datasetRepo, split = "train", onClose, }: PlotlyViewerProps) { // Ensure dataset repo has org prefix for HF API calls const fullRepo = datasetRepo.includes("/") ? datasetRepo : `${HF_ORG}/${datasetRepo}`; const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [mode, setMode] = useState("empty"); const [plotlyJson, setPlotlyJson] = useState(null); const [numericStats, setNumericStats] = useState([]); const [rowCount, setRowCount] = useState(0); const shortName = datasetRepo.split("/").pop() ?? datasetRepo; const fetchData = useCallback(async () => { setLoading(true); setError(null); try { // Fetch first page of rows (up to 100 for numeric summary) const url = `${HF_DATASETS_API}/rows?dataset=${encodeURIComponent( fullRepo )}&config=default&split=${encodeURIComponent(split)}&offset=0&length=100`; const resp = await fetch(url); if (!resp.ok) { const text = await resp.text(); throw new Error(`HF API error ${resp.status}: ${text.slice(0, 200)}`); } const data = (await resp.json()) as { rows?: { row: HfRow }[]; num_rows_total?: number; }; const rows: HfRow[] = (data.rows ?? []).map((r) => r.row); setRowCount(data.num_rows_total ?? rows.length); if (rows.length === 0) { setMode("empty"); return; } // Try to find a plotly JSON column const plotlyCol = detectPlotlyColumn(rows[0]); if (plotlyCol !== null) { // Use the first row's plotly JSON const parsed = tryParseJson(rows[0][plotlyCol]); if (parsed !== null) { setPlotlyJson(parsed); setMode("plotly_json"); return; } } // Fallback: numeric summary const stats = extractNumericColumns(rows); setNumericStats(stats); setMode(stats.length > 0 ? "numeric_summary" : "empty"); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { setLoading(false); } }, [fullRepo, split]); useEffect(() => { fetchData(); }, [fetchData]); return (
{/* Header */}
{shortName} {datasetRepo.includes("/") && ( {datasetRepo.split("/")[0]}/ )} {split} {!loading && rowCount > 0 && ( {rowCount.toLocaleString()} rows )}
{/* Body */}
{loading && (

Loading dataset...

)} {!loading && error && (

Failed to load dataset

{error}

)} {!loading && !error && mode === "empty" && (

Dataset is empty or has no renderable columns.

Expected columns for Plotly:{" "} {PLOTLY_COLUMN_NAMES.join(", ")}

)} {!loading && !error && mode === "plotly_json" && plotlyJson && ( )} {!loading && !error && mode === "numeric_summary" && ( )}
); }