"use client";
import React from "react";
import { setTheme, useTheme } from "@/utils/theme";
/**
* Shared building blocks for the analysis tabs (Statistics, Filtering, Frames, Action Insights), so
* their headings, cards, loading states and toggles look the same everywhere.
*/
/** Tab title and one-line description, with an optional control on the right. */
export function PageHeader({
title,
description,
children,
}: {
title: string;
description?: React.ReactNode;
children?: React.ReactNode;
}) {
return (
{title}
{description && (
{description}
)}
{children &&
{children}
}
);
}
/** A labelled number: the label styled like the sidebar's, the value large and tabular. */
export function StatCard({
label,
value,
}: {
label: string;
value: React.ReactNode;
}) {
return (
{label}
{value}
);
}
export function Spinner({ className = "h-4 w-4" }: { className?: string }) {
return (
);
}
/** A spinner with a label, for a section that is still loading. */
export function InlineLoading({ label }: { label: string }) {
return (
{label}
);
}
/** Two-state switch with a label on each side; the active side is highlighted. */
export function Switch({
checked,
onChange,
offLabel,
onLabel,
ariaLabel,
}: {
checked: boolean;
onChange: (checked: boolean) => void;
offLabel: React.ReactNode;
onLabel: React.ReactNode;
ariaLabel: string;
}) {
const labelClass = (active: boolean) =>
`text-sm ${active ? "text-fg font-medium" : "text-fg-faint"}`;
return (
{offLabel}{onLabel}
);
}
/** Repo and episode above the videos, on every tab that plays an episode. */
export function EpisodeHeader({
repoId,
episodeId,
}: {
repoId: string;
episodeId: number;
}) {
return (
);
}
/** Switches between the light and dark theme; the choice is remembered. */
export function ThemeToggle() {
const theme = useTheme();
const next = theme === "dark" ? "light" : "dark";
return (
);
}