# Control Panel

> Mac OS 9 Appearance control panel with Themes, Appearance, Fonts, Desktop, Sound and Options tabs built from group boxes, pop-ups, sliders, checkboxes, radios and little-arrows fields.

A shadcn/ui block from the [@nostalgia-ui](https://nostalgia-ui.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/nostalgia-ui/control-panel).

- Page, with a live preview: https://shadcn.exchange/r/nostalgia-ui/control-panel
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/nostalgia-ui/control-panel-light-32c1616f15716445.png
- Registry item: https://nostalgia-ui.com/r/control-panel.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://nostalgia-ui.com/r/control-panel.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `https://nostalgia-ui.com/r/nostalgia-theme.json`, `https://nostalgia-ui.com/r/retro-button.json`, `https://nostalgia-ui.com/r/retro-checkbox.json`, `https://nostalgia-ui.com/r/retro-group-box.json`, `https://nostalgia-ui.com/r/retro-label.json`, `https://nostalgia-ui.com/r/retro-list-box.json`, `https://nostalgia-ui.com/r/retro-little-arrows.json`, `https://nostalgia-ui.com/r/retro-radio.json`, `https://nostalgia-ui.com/r/retro-select.json`, `https://nostalgia-ui.com/r/retro-slider.json`, `https://nostalgia-ui.com/r/retro-tabs.json`, `https://nostalgia-ui.com/r/retro-window.json`

## Files

### `registry/new-york/blocks/control-panel.tsx`

```tsx
"use client"

import * as React from "react"

import { cn } from "@/lib/utils"
import { RetroButton } from "@/registry/new-york/ui/retro-button"
import { RetroCheckbox } from "@/registry/new-york/ui/retro-checkbox"
import { RetroGroupBox } from "@/registry/new-york/ui/retro-group-box"
import { RetroLabel } from "@/registry/new-york/ui/retro-label"
import { RetroNumberField } from "@/registry/new-york/ui/retro-little-arrows"
import { RetroListBox, RetroListBoxItem } from "@/registry/new-york/ui/retro-list-box"
import { RetroRadioGroup, RetroRadioGroupItem } from "@/registry/new-york/ui/retro-radio"
import {
  RetroSelect,
  RetroSelectContent,
  RetroSelectItem,
  RetroSelectTrigger,
  RetroSelectValue,
} from "@/registry/new-york/ui/retro-select"
import { RetroSlider } from "@/registry/new-york/ui/retro-slider"
import {
  RetroTabs,
  RetroTabsContent,
  RetroTabsList,
  RetroTabsTrigger,
} from "@/registry/new-york/ui/retro-tabs"
import { RetroWindow } from "@/registry/new-york/ui/retro-window"

/* ------------------------------------------------------------------ */
/*  Data                                                               */
/* ------------------------------------------------------------------ */

type ControlPanelTab = "themes" | "appearance" | "fonts" | "desktop" | "sound" | "options"

const TABS: { value: ControlPanelTab; label: string }[] = [
  { value: "themes", label: "Themes" },
  { value: "appearance", label: "Appearance" },
  { value: "fonts", label: "Fonts" },
  { value: "desktop", label: "Desktop" },
  { value: "sound", label: "Sound" },
  { value: "options", label: "Options" },
]

/** Highlight colours are user content, so they carry their own values. */
const HIGHLIGHT_COLORS = [
  { id: "lavender", name: "Lavender", color: "var(--os9-lavender)" },
  { id: "blue", name: "Blue", color: "#99ccff" },
  { id: "gold", name: "Gold", color: "#ffcc66" },
  { id: "graphite", name: "Graphite", color: "var(--os9-gray-400)" },
  { id: "green", name: "Green", color: "#99ee99" },
  { id: "purple", name: "Purple", color: "#cc99ff" },
  { id: "red", name: "Red", color: "#ff9999" },
  { id: "silver", name: "Silver", color: "var(--os9-gray-300)" },
] as const

const DESKTOP_PATTERNS = [
  {
    id: "mac-os-default",
    name: "Mac OS Default",
    style: {
      backgroundColor: "var(--os9-gray-400)",
      backgroundImage: "radial-gradient(var(--os9-gray-700) 0.8px, transparent 0.8px)",
      backgroundSize: "4px 4px",
    },
  },
  {
    id: "azul-dots",
    name: "Azul Dots",
    style: {
      backgroundColor: "var(--os9-azul)",
      backgroundImage: "radial-gradient(var(--os9-focus) 1px, transparent 1px)",
      backgroundSize: "6px 6px",
    },
  },
  {
    id: "lavender-stripes",
    name: "Lavender Stripes",
    style: {
      backgroundColor: "var(--os9-lavender)",
      backgroundImage:
        "repeating-linear-gradient(90deg, transparent 0 3px, var(--os9-white) 3px 4px)",
    },
  },
  {
    id: "platinum-grid",
    name: "Platinum Grid",
    style: {
      backgroundColor: "var(--os9-gray-300)",
      backgroundImage:
        "linear-gradient(var(--os9-gray-500) 1px, transparent 1px), linear-gradient(90deg, var(--os9-gray-500) 1px, transparent 1px)",
      backgroundSize: "8px 8px",
    },
  },
  {
    id: "checkerboard",
    name: "Checkerboard",
    style: {
      backgroundColor: "var(--os9-gray-200)",
      backgroundImage:
        "conic-gradient(var(--os9-gray-600) 25%, transparent 0 50%, var(--os9-gray-600) 0 75%, transparent 0)",
      backgroundSize: "4px 4px",
    },
  },
  {
    id: "solid-gray",
    name: "Solid Gray",
    style: { backgroundColor: "var(--os9-gray-600)" },
  },
] as const satisfies readonly { id: string; name: string; style: React.CSSProperties }[]

const THEMES = [
  { id: "mac-os-default", name: "Mac OS Default", highlight: "lavender", pattern: "mac-os-default" },
  { id: "hicontrast", name: "Mac OS HiContrast", highlight: "silver", pattern: "solid-gray" },
  { id: "drawing-board", name: "Drawing Board", highlight: "gold", pattern: "platinum-grid" },
  { id: "gizmo", name: "Gizmo", highlight: "purple", pattern: "azul-dots" },
  { id: "sensible", name: "Sensible", highlight: "green", pattern: "lavender-stripes" },
  { id: "cafe", name: "Cafe", highlight: "red", pattern: "checkerboard" },
] as const

const LARGE_FONTS = ["Charcoal", "Chicago", "Gadget", "Sand", "Techno", "Textile"]
const SMALL_FONTS = ["Geneva", "Helvetica", "Monaco"]

/* ------------------------------------------------------------------ */
/*  Settings                                                           */
/* ------------------------------------------------------------------ */

interface ControlPanelSettings {
  theme: string
  appearance: string
  highlightColor: string
  variation: string
  largeFont: string
  smallFont: string
  viewsFont: string
  viewsFontSize: number
  smoothFonts: boolean
  smoothFontsMinSize: number
  desktopPattern: string
  soundTrack: string
  sounds: { menus: boolean; windows: boolean; controls: boolean; finder: boolean }
  volume: number
  smartScrolling: boolean
  collapseOnDoubleClick: boolean
  scrollArrows: "together" | "both"
  menuBlinks: number
}

const DEFAULT_SETTINGS: ControlPanelSettings = {
  theme: "mac-os-default",
  appearance: "platinum",
  highlightColor: "lavender",
  variation: "blue",
  largeFont: "Charcoal",
  smallFont: "Geneva",
  viewsFont: "Geneva",
  viewsFontSize: 10,
  smoothFonts: true,
  smoothFontsMinSize: 12,
  desktopPattern: "mac-os-default",
  soundTrack: "platinum",
  sounds: { menus: true, windows: true, controls: true, finder: false },
  volume: 60,
  smartScrolling: true,
  collapseOnDoubleClick: true,
  scrollArrows: "together",
  menuBlinks: 1,
}

interface ControlPanelBlockProps
  extends Omit<
    React.ComponentPropsWithoutRef<typeof RetroWindow>,
    "title" | "children" | "onChange" | "defaultValue"
  > {
  /** Initially selected tab. Defaults to "themes". */
  defaultTab?: ControlPanelTab
  /** Initial settings (merged over the Mac OS defaults). */
  defaultSettings?: Partial<ControlPanelSettings>
  /** Fired with the full settings object after every change. */
  onChange?: (settings: ControlPanelSettings) => void
  /** Window title. Defaults to "Appearance". */
  title?: string
}

/* ------------------------------------------------------------------ */
/*  Small building blocks                                              */
/* ------------------------------------------------------------------ */

const TEXT = "font-[family-name:var(--os9-font-sans)] text-[10px] leading-[1.4] text-os9-black"

/** Label column + control column; stacks on narrow widths. */
function Row({
  label,
  htmlFor,
  labelId,
  children,
}: {
  label: string
  htmlFor?: string
  labelId?: string
  children: React.ReactNode
}) {
  return (
    <div className="grid grid-cols-[140px_minmax(0,1fr)] items-center gap-x-2 max-[480px]:grid-cols-1 max-[480px]:gap-y-1">
      <RetroLabel
        size="lg"
        htmlFor={htmlFor}
        id={labelId}
        className="text-right max-[480px]:text-left"
      >
        {label}
      </RetroLabel>
      <div className="flex min-w-0 items-center gap-2">{children}</div>
    </div>
  )
}

function PopUp({
  value,
  onValueChange,
  labelledBy,
  className,
  children,
}: {
  value: string
  onValueChange: (value: string) => void
  labelledBy: string
  className?: string
  children: React.ReactNode
}) {
  return (
    <RetroSelect value={value} onValueChange={onValueChange}>
      <RetroSelectTrigger aria-labelledby={labelledBy} className={cn("w-[200px] max-w-full", className)}>
        <RetroSelectValue />
      </RetroSelectTrigger>
      <RetroSelectContent>{children}</RetroSelectContent>
    </RetroSelect>
  )
}

function CheckRow({
  id,
  checked,
  onCheckedChange,
  children,
}: {
  id: string
  checked: boolean
  onCheckedChange: (checked: boolean) => void
  children: React.ReactNode
}) {
  return (
    <div className="flex items-center gap-2">
      <RetroCheckbox id={id} checked={checked} onCheckedChange={(c) => onCheckedChange(c === true)} />
      <RetroLabel htmlFor={id} className="text-[10px]">
        {children}
      </RetroLabel>
    </div>
  )
}

function Swatch({ color }: { color: string }) {
  return (
    <span
      aria-hidden="true"
      className="inline-block size-[10px] shrink-0 border border-os9-black align-[-1px]"
      style={{ background: color }}
    />
  )
}

function patternStyle(id: string): React.CSSProperties {
  return (DESKTOP_PATTERNS.find((p) => p.id === id) ?? DESKTOP_PATTERNS[0]).style
}

function highlightValue(id: string) {
  return (HIGHLIGHT_COLORS.find((c) => c.id === id) ?? HIGHLIGHT_COLORS[0]).color
}

/** Tiny desktop-with-window preview used by theme tiles. */
function ThemePreview({ pattern, highlight }: { pattern: string; highlight: string }) {
  return (
    <span
      aria-hidden="true"
      className="relative block h-[48px] w-[72px] border border-os9-black"
      style={patternStyle(pattern)}
    >
      <span className="absolute inset-x-0 top-0 h-[5px] border-b border-os9-black bg-os9-gray-300" />
      <span className="absolute left-[10px] top-[12px] h-[28px] w-[44px] border border-os9-black bg-os9-white shadow-[1px_1px_0_var(--os9-black)]">
        <span className="os9-stripes block h-[5px] border-b border-os9-black" />
        <span className="mx-[4px] mt-[4px] block h-[4px]" style={{ background: highlightValue(highlight) }} />
        <span className="mx-[4px] mt-[3px] block h-px bg-os9-gray-600" />
        <span className="mx-[4px] mt-[3px] block h-px w-[20px] bg-os9-gray-600" />
      </span>
    </span>
  )
}

/* ------------------------------------------------------------------ */
/*  ControlPanelBlock                                                  */
/* ------------------------------------------------------------------ */

const ControlPanelBlock = React.forwardRef<HTMLDivElement, ControlPanelBlockProps>(
  function ControlPanelBlock(
    {
      defaultTab = "themes",
      defaultSettings,
      onChange,
      title = "Appearance",
      className,
      contentClassName,
      ...props
    },
    ref
  ) {
    const id = React.useId()
    const [tab, setTab] = React.useState<ControlPanelTab>(defaultTab)
    const [settings, setSettings] = React.useState<ControlPanelSettings>(() => ({
      ...DEFAULT_SETTINGS,
      ...defaultSettings,
    }))

    const update = (patch: Partial<ControlPanelSettings>) => {
      const next = { ...settings, ...patch }
      setSettings(next)
      onChange?.(next)
    }
    const s = settings

    return (
      <RetroWindow
        ref={ref}
        title={title}
        className={cn("w-full max-w-[560px]", className)}
        contentClassName={cn("p-[12px] max-[480px]:p-[6px]", contentClassName)}
        {...props}
      >
        <RetroTabs value={tab} onValueChange={(v) => setTab(v as ControlPanelTab)}>
          <RetroTabsList className="overflow-x-auto overflow-y-hidden pt-[3px] [scrollbar-width:none]">
            {TABS.map((t) => (
              <RetroTabsTrigger key={t.value} value={t.value} className="shrink-0 max-[480px]:px-[8px]">
                {t.label}
              </RetroTabsTrigger>
            ))}
          </RetroTabsList>

          {/* ---------------- Themes ---------------- */}
          <RetroTabsContent value="themes" className="flex min-h-[300px] flex-col gap-3">
            <p className={TEXT}>
              Click a theme to change the appearance of your computer. A theme
              sets the highlight colour, desktop pattern, fonts and sounds.
            </p>
            <RetroListBox
              aria-label="Themes"
              value={s.theme}
              onValueChange={(v) => {
                const theme = THEMES.find((t) => t.id === v)
                if (!theme) return
                update({ theme: theme.id, highlightColor: theme.highlight, desktopPattern: theme.pattern })
              }}
              className="grid grid-cols-[repeat(auto-fill,minmax(88px,1fr))] gap-[6px] overflow-y-auto p-[6px] max-h-[220px]"
            >
              {THEMES.map((t) => (
                <RetroListBoxItem
                  key={t.id}
                  value={t.id}
                  textValue={t.name}
                  className="group/theme h-auto flex-col gap-[4px] whitespace-normal bg-transparent p-[4px] data-[selected]:bg-transparent data-[selected]:text-os9-black"
                >
                  <span className="p-[2px] group-data-[selected]/theme:bg-os9-azul">
                    <ThemePreview pattern={t.pattern} highlight={t.highlight} />
                  </span>
                  <span className="px-[2px] text-center leading-[1.3] group-data-[selected]/theme:bg-os9-azul group-data-[selected]/theme:text-os9-white">
                    {t.name}
                  </span>
                </RetroListBoxItem>
              ))}
            </RetroListBox>
          </RetroTabsContent>

          {/* ---------------- Appearance ---------------- */}
          <RetroTabsContent value="appearance" className="flex min-h-[300px] flex-col gap-3">
            <RetroGroupBox title="Appearance" contentClassName="flex flex-col gap-2">
              <Row label="Appearance:" labelId={`${id}-appearance`}>
                <PopUp value={s.appearance} onValueChange={(v) => update({ appearance: v })} labelledBy={`${id}-appearance`}>
                  <RetroSelectItem value="platinum">Apple Platinum</RetroSelectItem>
                </PopUp>
              </Row>
            </RetroGroupBox>

            <RetroGroupBox title="Colors" contentClassName="flex flex-col gap-2">
              <Row label="Highlight Color:" labelId={`${id}-highlight`}>
                <RetroSelect value={s.highlightColor} onValueChange={(v) => update({ highlightColor: v })}>
                  <RetroSelectTrigger aria-labelledby={`${id}-highlight`} className="w-[200px] max-w-full">
                    <RetroSelectValue />
                  </RetroSelectTrigger>
                  <RetroSelectContent>
                    {HIGHLIGHT_COLORS.map((c) => (
                      <RetroSelectItem key={c.id} value={c.id}>
                        <span className="flex items-center gap-[6px]">
                          <Swatch color={c.color} />
                          {c.name}
                        </span>
                      </RetroSelectItem>
                    ))}
                  </RetroSelectContent>
                </RetroSelect>
              </Row>
              <Row label="Variation:" labelId={`${id}-variation`}>
                <PopUp value={s.variation} onValueChange={(v) => update({ variation: v })} labelledBy={`${id}-variation`}>
                  <RetroSelectItem value="blue">Blue</RetroSelectItem>
                  <RetroSelectItem value="graphite">Graphite</RetroSelectItem>
                </PopUp>
              </Row>
              <Row label="Sample:">
                <p className={cn(TEXT, "truncate")}>
                  Some{" "}
                  <span className="px-[1px]" style={{ background: highlightValue(s.highlightColor) }}>
                    selected text
                  </span>{" "}
                  in a document.
                </p>
              </Row>
            </RetroGroupBox>
          </RetroTabsContent>

          {/* ---------------- Fonts ---------------- */}
          <RetroTabsContent value="fonts" className="flex min-h-[300px] flex-col gap-3">
            <RetroGroupBox title="System Fonts" contentClassName="flex flex-col gap-2">
              <Row label="Large System Font:" labelId={`${id}-large`}>
                <PopUp value={s.largeFont} onValueChange={(v) => update({ largeFont: v })} labelledBy={`${id}-large`}>
                  {LARGE_FONTS.map((f) => (
                    <RetroSelectItem key={f} value={f}>{f}</RetroSelectItem>
                  ))}
                </PopUp>
              </Row>
              <Row label="Small System Font:" labelId={`${id}-small`}>
                <PopUp value={s.smallFont} onValueChange={(v) => update({ smallFont: v })} labelledBy={`${id}-small`}>
                  {SMALL_FONTS.map((f) => (
                    <RetroSelectItem key={f} value={f}>{f}</RetroSelectItem>
                  ))}
                </PopUp>
              </Row>
              <Row label="Views Font:" labelId={`${id}-views`}>
                <PopUp
                  value={s.viewsFont}
                  onValueChange={(v) => update({ viewsFont: v })}
                  labelledBy={`${id}-views`}
                  className="w-[130px]"
                >
                  {SMALL_FONTS.map((f) => (
                    <RetroSelectItem key={f} value={f}>{f}</RetroSelectItem>
                  ))}
                </PopUp>
                <RetroLabel htmlFor={`${id}-views-size`} className="shrink-0">Size:</RetroLabel>
                <RetroNumberField
                  id={`${id}-views-size`}
                  size="sm"
                  min={9}
                  max={24}
                  value={s.viewsFontSize}
                  onValueChange={(v) => update({ viewsFontSize: v })}
                  inputClassName="w-[36px]"
                />
              </Row>
            </RetroGroupBox>

            <RetroGroupBox
              title={
                <CheckRow
                  id={`${id}-smooth`}
                  checked={s.smoothFonts}
                  onCheckedChange={(c) => update({ smoothFonts: c })}
                >
                  <span className="font-[family-name:var(--os9-font-heading)] text-[12px] tracking-[0.42px]">
                    Smooth all fonts on screen
                  </span>
                </CheckRow>
              }
              contentClassName="flex flex-col gap-2"
            >
              <div className={cn("flex flex-wrap items-center gap-2", !s.smoothFonts && "opacity-50")}>
                <RetroLabel htmlFor={`${id}-smooth-size`}>Smooth fonts at sizes</RetroLabel>
                <RetroNumberField
                  id={`${id}-smooth-size`}
                  size="sm"
                  min={9}
                  max={24}
                  disabled={!s.smoothFonts}
                  value={s.smoothFontsMinSize}
                  onValueChange={(v) => update({ smoothFontsMinSize: v })}
                  inputClassName="w-[36px]"
                />
                <span className={TEXT}>and above</span>
              </div>
            </RetroGroupBox>
          </RetroTabsContent>

          {/* ---------------- Desktop ---------------- */}
          <RetroTabsContent value="desktop" className="flex min-h-[300px] flex-wrap gap-4">
            <div className="flex flex-col items-center gap-2">
              {/* Monitor preview */}
              <div className="border border-os9-black bg-os9-gray-300 p-[6px] pb-[14px] shadow-[var(--os9-shadow-raised)]">
                <div
                  aria-label="Desktop preview"
                  role="img"
                  className="h-[96px] w-[128px] border border-os9-black"
                  style={patternStyle(s.desktopPattern)}
                />
              </div>
              <p className={cn(TEXT, "text-center")}>
                {DESKTOP_PATTERNS.find((p) => p.id === s.desktopPattern)?.name}
              </p>
            </div>
            <div className="flex min-w-[180px] flex-1 flex-col gap-2">
              <RetroLabel size="lg" id={`${id}-patterns`}>Patterns</RetroLabel>
              <RetroListBox
                aria-labelledby={`${id}-patterns`}
                value={s.desktopPattern}
                onValueChange={(v) => v && update({ desktopPattern: v })}
                className="h-[140px]"
              >
                {DESKTOP_PATTERNS.map((p) => (
                  <RetroListBoxItem key={p.id} value={p.id}>
                    <span aria-hidden="true" className="size-[10px] shrink-0 border border-os9-black" style={p.style} />
                    {p.name}
                  </RetroListBoxItem>
                ))}
              </RetroListBox>
              <div className="flex flex-wrap gap-2">
                <RetroButton size="sm" type="button" disabled>Place Picture…</RetroButton>
                <RetroButton size="sm" type="button" disabled>Remove Picture</RetroButton>
              </div>
            </div>
          </RetroTabsContent>

          {/* ---------------- Sound ---------------- */}
          <RetroTabsContent value="sound" className="flex min-h-[300px] flex-col gap-3">
            <Row label="Sound track:" labelId={`${id}-track`}>
              <PopUp value={s.soundTrack} onValueChange={(v) => update({ soundTrack: v })} labelledBy={`${id}-track`}>
                <RetroSelectItem value="platinum">Platinum Sounds</RetroSelectItem>
                <RetroSelectItem value="none">None</RetroSelectItem>
              </PopUp>
            </Row>
            <RetroGroupBox title="Play sounds for:" contentClassName="grid grid-cols-2 gap-2 max-[420px]:grid-cols-1">
              {(
                [
                  ["menus", "Menus"],
                  ["windows", "Windows"],
                  ["controls", "Controls"],
                  ["finder", "Finder"],
                ] as const
              ).map(([key, label]) => (
                <CheckRow
                  key={key}
                  id={`${id}-sound-${key}`}
                  checked={s.sounds[key] && s.soundTrack !== "none"}
                  onCheckedChange={(c) => update({ sounds: { ...s.sounds, [key]: c } })}
                >
                  {label}
                </CheckRow>
              ))}
            </RetroGroupBox>
            <RetroGroupBox title="Volume" contentClassName="flex items-center gap-3">
              <span className={TEXT} aria-hidden="true">Quiet</span>
              <RetroSlider
                aria-label="Volume"
                min={0}
                max={100}
                step={1}
                value={[s.volume]}
                onValueChange={([v]) => update({ volume: v })}
                className="flex-1"
              />
              <span className={TEXT} aria-hidden="true">Loud</span>
            </RetroGroupBox>
          </RetroTabsContent>

          {/* ---------------- Options ---------------- */}
          <RetroTabsContent value="options" className="flex min-h-[300px] flex-col gap-3">
            <RetroGroupBox title="Scrolling" contentClassName="flex flex-col gap-2">
              <CheckRow
                id={`${id}-smart`}
                checked={s.smartScrolling}
                onCheckedChange={(c) => update({ smartScrolling: c })}
              >
                Smart Scrolling (proportional scroll boxes)
              </CheckRow>
              <p className={cn(TEXT, "mt-1")} id={`${id}-arrows`}>Scroll arrows:</p>
              <RetroRadioGroup
                aria-labelledby={`${id}-arrows`}
                value={s.scrollArrows}
                onValueChange={(v) => update({ scrollArrows: v as ControlPanelSettings["scrollArrows"] })}
                className="gap-1.5 pl-3"
              >
                <div className="flex items-center gap-2">
                  <RetroRadioGroupItem id={`${id}-arrows-together`} value="together" />
                  <RetroLabel htmlFor={`${id}-arrows-together`}>Together</RetroLabel>
                </div>
                <div className="flex items-center gap-2">
                  <RetroRadioGroupItem id={`${id}-arrows-both`} value="both" />
                  <RetroLabel htmlFor={`${id}-arrows-both`}>At top and bottom</RetroLabel>
                </div>
              </RetroRadioGroup>
            </RetroGroupBox>
            <RetroGroupBox title="Windows & Menus" contentClassName="flex flex-col gap-2">
              <CheckRow
                id={`${id}-collapse`}
                checked={s.collapseOnDoubleClick}
                onCheckedChange={(c) => update({ collapseOnDoubleClick: c })}
              >
                Double-click title bar to collapse windows
              </CheckRow>
              <div className="flex items-center gap-2">
                <RetroLabel htmlFor={`${id}-blinks`}>Menu blinking:</RetroLabel>
                <RetroNumberField
                  id={`${id}-blinks`}
                  size="sm"
                  min={0}
                  max={3}
                  value={s.menuBlinks}
                  onValueChange={(v) => update({ menuBlinks: v })}
                  formatValue={(v) => (v === 0 ? "Off" : String(v))}
                  inputClassName="w-[36px]"
                />
                <span className={TEXT}>{s.menuBlinks === 1 ? "time" : "times"}</span>
              </div>
            </RetroGroupBox>
          </RetroTabsContent>
        </RetroTabs>
      </RetroWindow>
    )
  }
)
ControlPanelBlock.displayName = "ControlPanelBlock"

export { ControlPanelBlock, DEFAULT_SETTINGS as CONTROL_PANEL_DEFAULT_SETTINGS }
export type { ControlPanelBlockProps, ControlPanelSettings, ControlPanelTab }
```
