"use client"; import type { ReactNode } from "react"; import { useState } from "react"; import { Check, Copy } from "lucide-react"; import { useTranslation } from "react-i18next"; import { Card, CardFrame, CardFrameAction, CardFrameDescription, CardFrameHeader, CardFrameTitle, } from "@/components/ui/card"; import { Switch } from "@/components/ui/switch"; // A settings section rendered inside the COSS "frame" surface: a titled header // above one or more cards. // // Children are rendered as *direct* children of CardFrame on purpose. CardFrame // styles its cards through direct-child selectors (`*:data-[slot=card]:-m-px`, // the clip-path, `rounded-t/b-xl`, `shadow-none`, `before:hidden`) — it pulls // each card out by a pixel so it sits flush inside the frame's own border. Put // anything between the frame and the card, even an unstyled div, and every one // of those selectors stops matching: the card keeps its own border and shadow // and you get a box inside a box. // // So: no padding here. Body padding belongs on the Card (``), which is where COSS puts it. export function SettingsFrame({ title, description, action, children, className, }: { title: string; description?: string; action?: ReactNode; children: ReactNode; className?: string; }) { return ( {title} {description ? ( {description} ) : null} {action ? {action} : null} {children} ); } // Back-compat wrapper: existing panels compose with SettingsSection, which // renders through the COSS frame surface so the whole settings page shares one // framed look. export function SettingsSection({ title, description, action, children, }: { title: string; description?: string; action?: ReactNode; children: ReactNode; }) { return ( {children} ); } // A card surface used inside settings panels. Rendering a real COSS `Card` (with // `data-slot="card"`) keeps settings cards consistent with the rest of the app // and lets them pick up the frame's card treatment. `Card` is `flex flex-col`, // so row layouts must pass `flex-row` in their className. It carries no padding // of its own — pass `p-5` (the settings default) or a `divide-y` list. export function SettingsCard({ className, children, }: { className?: string; children: ReactNode; }) { return {children}; } export function ToggleRow({ title, description, defaultChecked = false, checked, onCheckedChange, }: { title: string; description?: string; defaultChecked?: boolean; /** Pass `checked` + `onCheckedChange` to make the switch controlled. */ checked?: boolean; onCheckedChange?: (checked: boolean) => void; }) { return (

{title}

{description ? (

{description}

) : null}
); } export function CopyField({ label, description, value, }: { label: string; description?: string; value: string; }) { const { t } = useTranslation(); const [copied, setCopied] = useState(false); const copy = async () => { try { await navigator.clipboard.writeText(value); setCopied(true); setTimeout(() => setCopied(false), 1500); } catch { // Clipboard unavailable (insecure context) — silently ignore. } }; return (

{label}

{description ? (

{description}

) : null}
{value}
); } /** Light/white CTA used for primary actions on the dark settings surface. */ export const whiteButton = "bg-foreground text-background hover:bg-foreground/90"; /** Field label with an optional required asterisk. */ export function FieldLabel({ children, required, }: { children: ReactNode; required?: boolean; }) { return ( {children} {required ? * : null} ); }