'use client';
import type { ReactNode } from 'react';
import Link from 'next/link';
import * as Icons from '../Icons';
// Direct import from the theme module, not from '@/lib/config': the config
// index pulls in `fs` and cannot be bundled into a client component.
import { resolveTheme } from '@/lib/config/theme';
import type { SettingsYaml } from '@/lib/config/schema';
import type { Settings } from './types';
/**
* One labelled block of related switches.
*
* The features panel was a single flat grid of identical cards, so nothing said
* which switches belonged together — and a switch that publishes private notes
* looked exactly like one that controls a fade (#510).
*/
export function FeatureGroup({
icon,
title,
description,
chip,
children,
}: {
icon: ReactNode;
title: string;
description: string;
/** Short marker for a group that needs its own weight, e.g. what visitors see. */
chip?: string;
children: ReactNode;
}) {
return (
{icon}
{title}
{chip && {chip}}
{description}
{children}
);
}
/**
* A switch inside a group panel. Deliberately lighter than a toggle card: these
* are settings *of* the panel they sit in, and rendering them as peers of the
* top-level cards is what hid the structure in the first place.
*/
export function SettingRow({
title,
description,
checked,
onToggle,
disabled,
hint,
indented,
}: {
title: string;
description: string;
checked: boolean;
onToggle: () => void;
/** A row whose parent switch is off — shown, but inert and explained. */
disabled?: boolean;
hint?: string;
indented?: boolean;
}) {
return (
);
}
/**
* One switch in a feature list: a single dense line instead of a card. Cards
* gave every switch the same weight, so a fade and a whole page looked alike;
* rows grouped by meaning let the group heading carry that weight instead.
* The optional link sits beside the switch, not inside it — a link nested in
* a button is invalid and swallows the click.
*/
export function FeatureRow({
icon,
title,
description,
checked,
onToggle,
href,
}: {
icon: ReactNode;
title: string;
description: string;
checked: boolean;
onToggle: () => void;
/** Where this feature is configured beyond on/off. */
href?: string;
}) {
return (
{/* Reserved even without a link, so every switch lines up in one column. */}
{href ? (
Configure →
) : (
)}
);
}
/**
* The theme as the site resolves it. A preset name the form does not know (a
* hand-edited typo) falls back to the default instead of taking the panel down.
*/
export function effectiveTheme(raw: Settings['theme']) {
try {
return resolveTheme(raw as SettingsYaml['theme']);
} catch {
return resolveTheme(undefined);
}
}