import { Badge } from '@/components/badge';
import { Button } from '@/components/button';
import { Spinner } from '@/components/spinner';
import { Switch } from '@/components/switch';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/tooltip';
import { InfoIcon, MoonIcon, SunIcon } from '@/icons';
import { useBridge, useFigmaMode } from '~/figma/use-figma';
import { LANG_LABELS, LANGS, type Messages, isLang, useLang, useMessages } from '~/i18n';
import { useParam } from '~/router';
import { Segmented } from './segmented';
import { useA11y, useA11yMode } from '../hooks/use-a11y';
/**
* Global toggles. They live in the URL hash rather than in React state so a
* particular view — dark theme, a given page — is a link you can send someone.
*
* All three are kit components, which is the rule for the site's chrome. The
* playground's controls are the deliberate exception, and `playground.tsx` says
* why.
*/
/* Only the tint is fixed here — both the short word in the pill and the
sentence in the tooltip come from the catalogue, keyed by the same name. */
const STATUS: Record<
string,
{ palette: 'green' | 'yellow' | 'black'; key: keyof Messages['toolbar']['bridge'] }
> = {
live: { palette: 'green', key: 'live' },
'no-plugin': { palette: 'yellow', key: 'noPlugin' },
off: { palette: 'black', key: 'off' },
idle: { palette: 'black', key: 'idle' },
};
/**
* The bridge indicator, and the switch that turns it off.
*
* It was one button doing both jobs; a Switch says which of the two states you
* are in before you read anything, and the pill beside it reports what the
* bridge actually answered — those are different facts, and conflating them is
* why "off" and "running but nothing polling it" used to look alike.
*
* Rendered in development only, like `A11yToggle` below and for the same
* reason: `useBridge` refuses to poll in a built site, so the switch there
* would be a control that reports nothing and changes nothing.
*/
function FigmaToggle() {
const [mode, setMode] = useFigmaMode();
const bridge = useBridge();
const m = useMessages();
const status = bridge.enabled ? bridge.status : 'idle';
const tone = STATUS[status] ?? STATUS.off;
/* Switched on but never probed is the one state with nothing to report yet,
so it gets the spinner rather than a word that would be wrong a moment
later. Switched *off* reports as `idle` too, which is why `enabled` has to
be in the test — that state is settled, not pending. */
const probing = bridge.enabled && bridge.status === 'idle';
return (
{m.toolbar.bridge[tone.key]}
{bridge.file ? (
<>
{m.toolbar.file}: {bridge.file}
>
) : null}
);
}
/**
* The axe switch — the same shape as the Figma one, and present only in
* development.
*
* `import.meta.env.DEV` is a literal at build time, so this whole component and
* everything it reaches (including the dynamic `import('axe-core')`) is dropped
* from a production `showcase:build` rather than merely hidden. `?a11y=on`
* would do nothing in a built site, which is why there is no control for it
* there either.
*/
function A11yToggle() {
const [mode, setMode] = useA11yMode();
const a11y = useA11y();
const m = useMessages();
const total = a11y.violations.reduce((sum, violation) => sum + violation.nodes.length, 0);
return (
{m.toolbar.a11yHint}
);
}
/**
* Three segments rather than a `