@sanity/themer 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/tool.d.ts +6 -5
- package/dist/tool.d.ts.map +1 -1
- package/dist/tool.js +552 -428
- package/dist/tool.js.map +1 -1
- package/package.json +2 -2
package/dist/tool.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.js","names":["() => {\n const element = ref.current\n\n if (!element) return undefined\n\n // The reserved gutter is the part of the box the content cannot use\n const measure = () => setGutter(element.offsetWidth - element.clientWidth)\n\n measure()\n\n const observer = new ResizeObserver(measure)\n\n observer.observe(element)\n\n return () => observer.disconnect()\n }","[]","{paddingRight: Math.max(0, space[padding] - gutter)}","Math.max(0, space[padding] - gutter)","<Root\n flex={1}\n overflow=\"auto\"\n padding={padding}\n ref={ref}\n style={{paddingRight: Math.max(0, space[padding] - gutter)}}\n >\n {children}\n </Root>","buildTheme(options)","useMemo(() => buildTheme(options), [options])","<MockStudio />","<ThemeProvider scheme=\"light\" theme={theme} tone=\"default\">\n <MockStudio />\n </ThemeProvider>","<MockStudio style={{clipPath: DARK_CLIP_PATH}} />","<ThemeProvider scheme=\"dark\" theme={theme} tone=\"default\">\n <MockStudio style={{clipPath: DARK_CLIP_PATH}} />\n </ThemeProvider>","<Root aria-hidden>\n <ThemeProvider scheme=\"light\" theme={theme} tone=\"default\">\n <MockStudio />\n </ThemeProvider>\n <ThemeProvider scheme=\"dark\" theme={theme} tone=\"default\">\n <MockStudio style={{clipPath: DARK_CLIP_PATH}} />\n </ThemeProvider>\n </Root>","(width: string, background: string): React.CSSProperties => ({\n flex: 'none',\n width,\n height: LINE,\n borderRadius: LINE / 2,\n background,\n })","{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n flexDirection: 'column',\n background: color.bg,\n ...props.style,\n }","bg","style","{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n gap: 5,\n height: '18%',\n padding: '0 5%',\n borderBottom: border,\n }","<div style={{flex: 'none', width: 8, height: 8, borderRadius: 2, background: primary.bg}} />","<div style={line('22%', color.fg)} />","fg","<div style={{flex: 1}} />","<div\n style={{\n flex: 'none',\n width: 8,\n height: 8,\n borderRadius: '50%',\n background: color.avatar.blue.bg,\n }}\n />","<div\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n gap: 5,\n height: '18%',\n padding: '0 5%',\n borderBottom: border,\n }}\n >\n <div style={{flex: 'none', width: 8, height: 8, borderRadius: 2, background: primary.bg}} />\n <div style={line('22%', color.fg)} />\n <div style={{flex: 1}} />\n <div\n style={{\n flex: 'none',\n width: 8,\n height: 8,\n borderRadius: '50%',\n background: color.avatar.blue.bg,\n }}\n />\n </div>","{flex: 1, display: 'flex', minHeight: 0}","{\n flex: 'none',\n display: 'flex',\n flexDirection: 'column',\n width: '36%',\n borderRight: border,\n }","LIST_ROWS.map((width, index) => {\n const isSelected = index === 1\n\n return (\n <div\n key={width}\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n height: '18%',\n padding: '0 9%',\n background: isSelected ? selected.bg : undefined,\n }}\n >\n <div style={line(width, isSelected ? selected.fg : color.muted.fg)} />\n </div>\n )\n })","muted","selected.bg","width","<div\n style={{\n flex: 'none',\n display: 'flex',\n flexDirection: 'column',\n width: '36%',\n borderRight: border,\n }}\n >\n {LIST_ROWS.map((width, index) => {\n const isSelected = index === 1\n\n return (\n <div\n key={width}\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n height: '18%',\n padding: '0 9%',\n background: isSelected ? selected.bg : undefined,\n }}\n >\n <div style={line(width, isSelected ? selected.fg : color.muted.fg)} />\n </div>\n )\n })}\n </div>","{flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0}","{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n gap: 7,\n padding: '9% 8% 0',\n minHeight: 0,\n overflow: 'hidden',\n }","<div style={line('45%', color.fg)} />","FIELDS.map((width) => (\n <div\n key={width}\n style={{flex: 'none', display: 'flex', flexDirection: 'column', gap: 3}}\n >\n <div style={line(width, color.muted.fg)} />\n <div\n style={{\n height: 12,\n borderRadius: 3,\n background: input.bg,\n boxShadow: `inset 0 0 0 1px ${input.border}`,\n }}\n />\n </div>\n ))","input.border","<div\n style={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n gap: 7,\n padding: '9% 8% 0',\n minHeight: 0,\n overflow: 'hidden',\n }}\n >\n <div style={line('45%', color.fg)} />\n {FIELDS.map((width) => (\n <div\n key={width}\n style={{flex: 'none', display: 'flex', flexDirection: 'column', gap: 3}}\n >\n <div style={line(width, color.muted.fg)} />\n <div\n style={{\n height: 12,\n borderRadius: 3,\n background: input.bg,\n boxShadow: `inset 0 0 0 1px ${input.border}`,\n }}\n />\n </div>\n ))}\n </div>","{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n height: '20%',\n padding: '0 8%',\n borderTop: border,\n }","<div style={{width: '32%', height: '50%', borderRadius: 3, background: primary.bg}} />","<div\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n height: '20%',\n padding: '0 8%',\n borderTop: border,\n }}\n >\n <div style={{width: '32%', height: '50%', borderRadius: 3, background: primary.bg}} />\n </div>","<div style={{flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0}}>\n <div\n style={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n gap: 7,\n padding: '9% 8% 0',\n minHeight: 0,\n overflow: 'hidden',\n }}\n >\n <div style={line('45%', color.fg)} />\n {FIELDS.map((width) => (\n <div\n key={width}\n style={{flex: 'none', display: 'flex', flexDirection: 'column', gap: 3}}\n >\n <div style={line(width, color.muted.fg)} />\n <div\n style={{\n height: 12,\n borderRadius: 3,\n background: input.bg,\n boxShadow: `inset 0 0 0 1px ${input.border}`,\n }}\n />\n </div>\n ))}\n </div>\n <div\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n height: '20%',\n padding: '0 8%',\n borderTop: border,\n }}\n >\n <div style={{width: '32%', height: '50%', borderRadius: 3, background: primary.bg}} />\n </div>\n </div>","<div style={{flex: 1, display: 'flex', minHeight: 0}}>\n <div\n style={{\n flex: 'none',\n display: 'flex',\n flexDirection: 'column',\n width: '36%',\n borderRight: border,\n }}\n >\n {LIST_ROWS.map((width, index) => {\n const isSelected = index === 1\n\n return (\n <div\n key={width}\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n height: '18%',\n padding: '0 9%',\n background: isSelected ? selected.bg : undefined,\n }}\n >\n <div style={line(width, isSelected ? selected.fg : color.muted.fg)} />\n </div>\n )\n })}\n </div>\n\n <div style={{flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0}}>\n <div\n style={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n gap: 7,\n padding: '9% 8% 0',\n minHeight: 0,\n overflow: 'hidden',\n }}\n >\n <div style={line('45%', color.fg)} />\n {FIELDS.map((width) => (\n <div\n key={width}\n style={{flex: 'none', display: 'flex', flexDirection: 'column', gap: 3}}\n >\n <div style={line(width, color.muted.fg)} />\n <div\n style={{\n height: 12,\n borderRadius: 3,\n background: input.bg,\n boxShadow: `inset 0 0 0 1px ${input.border}`,\n }}\n />\n </div>\n ))}\n </div>\n <div\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n height: '20%',\n padding: '0 8%',\n borderTop: border,\n }}\n >\n <div style={{width: '32%', height: '50%', borderRadius: 3, background: primary.bg}} />\n </div>\n </div>\n </div>","<div\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n flexDirection: 'column',\n background: color.bg,\n ...props.style,\n }}\n >\n <div\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n gap: 5,\n height: '18%',\n padding: '0 5%',\n borderBottom: border,\n }}\n >\n <div style={{flex: 'none', width: 8, height: 8, borderRadius: 2, background: primary.bg}} />\n <div style={line('22%', color.fg)} />\n <div style={{flex: 1}} />\n <div\n style={{\n flex: 'none',\n width: 8,\n height: 8,\n borderRadius: '50%',\n background: color.avatar.blue.bg,\n }}\n />\n </div>\n\n <div style={{flex: 1, display: 'flex', minHeight: 0}}>\n <div\n style={{\n flex: 'none',\n display: 'flex',\n flexDirection: 'column',\n width: '36%',\n borderRight: border,\n }}\n >\n {LIST_ROWS.map((width, index) => {\n const isSelected = index === 1\n\n return (\n <div\n key={width}\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n height: '18%',\n padding: '0 9%',\n background: isSelected ? selected.bg : undefined,\n }}\n >\n <div style={line(width, isSelected ? selected.fg : color.muted.fg)} />\n </div>\n )\n })}\n </div>\n\n <div style={{flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0}}>\n <div\n style={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n gap: 7,\n padding: '9% 8% 0',\n minHeight: 0,\n overflow: 'hidden',\n }}\n >\n <div style={line('45%', color.fg)} />\n {FIELDS.map((width) => (\n <div\n key={width}\n style={{flex: 'none', display: 'flex', flexDirection: 'column', gap: 3}}\n >\n <div style={line(width, color.muted.fg)} />\n <div\n style={{\n height: 12,\n borderRadius: 3,\n background: input.bg,\n boxShadow: `inset 0 0 0 1px ${input.border}`,\n }}\n />\n </div>\n ))}\n </div>\n <div\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n height: '20%',\n padding: '0 8%',\n borderTop: border,\n }}\n >\n <div style={{width: '32%', height: '50%', borderRadius: 3, background: primary.bg}} />\n </div>\n </div>\n </div>\n </div>","<Card overflow=\"hidden\" radius={3} shadow={1}>\n <ThemePreview options={props.options} />\n </Card>","options","removed.map((theme) => (\n <Stack gap={3} key={theme.slug}>\n <Box style={{opacity: 0.6}}>\n <ThemeThumbnail options={theme.options} />\n </Box>\n <Text align=\"center\" size={1} textOverflow=\"ellipsis\">\n {displayTitle(theme.title)}\n </Text>\n <Flex gap={2} justify=\"center\">\n <Button\n fontSize={1}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.restore', slug: theme.slug})}\n padding={2}\n text=\"Restore\"\n />\n {theme.source === 'custom' && (\n <Button\n fontSize={1}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.delete', slug: theme.slug})}\n padding={2}\n text=\"Delete\"\n tone=\"critical\"\n />\n )}\n </Flex>\n </Stack>\n ))","(theme) => (\n <Stack gap={3} key={theme.slug}>\n <Box style={{opacity: 0.6}}>\n <ThemeThumbnail options={theme.options} />\n </Box>\n <Text align=\"center\" size={1} textOverflow=\"ellipsis\">\n {displayTitle(theme.title)}\n </Text>\n <Flex gap={2} justify=\"center\">\n <Button\n fontSize={1}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.restore', slug: theme.slug})}\n padding={2}\n text=\"Restore\"\n />\n {theme.source === 'custom' && (\n <Button\n fontSize={1}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.delete', slug: theme.slug})}\n padding={2}\n text=\"Delete\"\n tone=\"critical\"\n />\n )}\n </Flex>\n </Stack>\n )","<ScrollArea padding={3}>\n <Stack gap={5}>\n {removed.map((theme) => (\n <Stack gap={3} key={theme.slug}>\n <Box style={{opacity: 0.6}}>\n <ThemeThumbnail options={theme.options} />\n </Box>\n <Text align=\"center\" size={1} textOverflow=\"ellipsis\">\n {displayTitle(theme.title)}\n </Text>\n <Flex gap={2} justify=\"center\">\n <Button\n fontSize={1}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.restore', slug: theme.slug})}\n padding={2}\n text=\"Restore\"\n />\n {theme.source === 'custom' && (\n <Button\n fontSize={1}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.delete', slug: theme.slug})}\n padding={2}\n text=\"Delete\"\n tone=\"critical\"\n />\n )}\n </Flex>\n </Stack>\n ))}\n </Stack>\n </ScrollArea>","<Text size={1}>{tooltip}</Text>","<Button aria-label={buttonProps.text === undefined ? tooltip : undefined} {...buttonProps} />","buttonProps.text === undefined ? tooltip : undefined","<Tooltip animate content={<Text size={1}>{tooltip}</Text>} placement=\"bottom\" portal>\n <Button aria-label={buttonProps.text === undefined ? tooltip : undefined} {...buttonProps} />\n </Tooltip>","() => inputRef.current?.click()","<TooltipButton {...buttonProps} icon={ImageIcon} onClick={() => inputRef.current?.click()} />","<input\n accept=\"image/*\"\n hidden\n onChange={(event) => {\n const file = event.currentTarget.files?.[0]\n\n // Picking the same file again should work, so the input forgets it\n event.currentTarget.value = ''\n\n if (file) onFile(file)\n }}\n ref={inputRef}\n type=\"file\"\n />","<>\n <TooltipButton {...buttonProps} icon={ImageIcon} onClick={() => inputRef.current?.click()} />\n <input\n accept=\"image/*\"\n hidden\n onChange={(event) => {\n const file = event.currentTarget.files?.[0]\n\n // Picking the same file again should work, so the input forgets it\n event.currentTarget.value = ''\n\n if (file) onFile(file)\n }}\n ref={inputRef}\n type=\"file\"\n />\n </>","() => {\n onPaletteRef.current = onPalette\n }","[onPalette]","() => {\n mountedRef.current = true\n\n return () => {\n mountedRef.current = false\n }\n }","[]","(file: File) => {\n const pick = ++latestPickRef.current\n\n setBusy(true)\n\n // Whether this pick still matters: it is the latest one and the\n // component that asked for it is still around\n const isCurrent = () => mountedRef.current && latestPickRef.current === pick\n\n const extract = async () => {\n const palette = extractImagePalette(await readImagePixels(file))\n\n if (!isCurrent()) return\n\n if (Object.values(palette).every((swatch) => swatch === null)) {\n toast.push({status: 'warning', title: 'Found no colors in that image'})\n } else {\n onPaletteRef.current(palette, file)\n }\n }\n\n extract()\n .catch(() => {\n if (isCurrent()) toast.push({status: 'error', title: 'Could not read that image'})\n })\n .finally(() => {\n if (isCurrent()) setBusy(false)\n })\n }","(swatch) => swatch === null","useCallback(\n (file: File) => {\n const pick = ++latestPickRef.current\n\n setBusy(true)\n\n // Whether this pick still matters: it is the latest one and the\n // component that asked for it is still around\n const isCurrent = () => mountedRef.current && latestPickRef.current === pick\n\n const extract = async () => {\n const palette = extractImagePalette(await readImagePixels(file))\n\n if (!isCurrent()) return\n\n if (Object.values(palette).every((swatch) => swatch === null)) {\n toast.push({status: 'warning', title: 'Found no colors in that image'})\n } else {\n onPaletteRef.current(palette, file)\n }\n }\n\n extract()\n .catch(() => {\n if (isCurrent()) toast.push({status: 'error', title: 'Could not read that image'})\n })\n .finally(() => {\n if (isCurrent()) setBusy(false)\n })\n },\n [toast],\n )","{busy, pickImage}","palette ? currentImageVariant(options, palette) : null","() => {\n const row = rowRef.current\n\n if (!row) return undefined\n\n const measure = () => {\n const visible =\n row.clientWidth - ROW_PADDING - (Math.ceil(VISIBLE_VARIANTS) - 1) * VARIANT_GAP\n\n // Each variant is its thumbnail plus the 4px frame on either side\n setVariantWidth(Math.max(24, Math.floor(visible / VISIBLE_VARIANTS) - 8))\n }\n\n measure()\n\n const observer = new ResizeObserver(measure)\n\n observer.observe(row)\n\n return () => observer.disconnect()\n }","[palette]","useMemo(() => {\n if (!palette) return []\n\n return IMAGE_PALETTE_VARIANTS.flatMap((variant) =>\n palette[variant] === null\n ? []\n : [{variant, options: applyImagePalette(options, palette, variant)}],\n )\n }, [options, palette])","[]","IMAGE_PALETTE_VARIANTS.flatMap((variant) =>\n palette[variant] === null\n ? []\n : [{variant, options: applyImagePalette(options, palette, variant)}],\n )","(event) => {\n event.preventDefault()\n dragDepth.current++\n setDragging(true)\n }","() => {\n dragDepth.current = Math.max(0, dragDepth.current - 1)\n\n if (dragDepth.current === 0) setDragging(false)\n }","(event) => {\n event.preventDefault()\n dragDepth.current = 0\n setDragging(false)\n\n const file = event.dataTransfer.files[0]\n\n if (file) pickImage(file)\n }","event","<Box flex={1}>\n <Text size={1} weight=\"medium\">\n Image palette\n </Text>\n </Box>","palette && (\n <ImageFileButton\n loading={busy}\n mode=\"bleed\"\n onFile={pickImage}\n padding={2}\n tooltip=\"Pick another image\"\n />\n )","<Flex align=\"center\" gap={2}>\n <Box flex={1}>\n <Text size={1} weight=\"medium\">\n Image palette\n </Text>\n </Box>\n {palette && (\n <ImageFileButton\n loading={busy}\n mode=\"bleed\"\n onFile={pickImage}\n padding={2}\n tooltip=\"Pick another image\"\n />\n )}\n </Flex>","palette ? (\n <>\n <PaletteGrid data-with-image={imageUrl !== undefined}>\n {imageUrl && <ImageTile alt=\"\" src={imageUrl} />}\n {TILES.map((key) => {\n const hex = palette[key]\n const title = IMAGE_PALETTE_TITLES[key]\n\n return (\n <Tooltip\n animate\n content={\n <Text size={1}>\n {hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`}\n </Text>\n }\n key={key}\n placement=\"bottom\"\n portal\n >\n <Tile data-empty={hex === null} style={{background: hex ?? undefined}} />\n </Tooltip>\n )\n })}\n </PaletteGrid>\n\n <Stack gap={2}>\n <Text muted size={0}>\n Pick the swatch to build the theme around\n </Text>\n <VariantRow\n ref={rowRef}\n style={{\n ['--variant-width' as string]: `${variantWidth}px`,\n ['--variant-scale' as string]: variantWidth / VARIANT_WIDTH,\n }}\n >\n {variants.map(({variant, options: variantOptions}) => (\n <VariantButton\n aria-pressed={variant === current}\n key={variant}\n onClick={() => onVariant(variant)}\n type=\"button\"\n >\n <VariantFrame>\n <VariantScale>\n <ThemeThumbnail options={variantOptions} />\n </VariantScale>\n </VariantFrame>\n <Box paddingTop={1}>\n <Text\n align=\"center\"\n size={0}\n textOverflow=\"ellipsis\"\n weight={variant === current ? 'medium' : 'regular'}\n >\n {IMAGE_PALETTE_TITLES[variant]}\n </Text>\n </Box>\n </VariantButton>\n ))}\n </VariantRow>\n </Stack>\n\n <TooltipButton\n icon={SparklesIcon}\n mode=\"ghost\"\n onClick={onLucky}\n text=\"I'm feeling lucky\"\n tooltip=\"Build the theme around a swatch that looks interesting\"\n width=\"fill\"\n />\n </>\n ) : (\n <Stack gap={3}>\n <Text muted size={0}>\n Take the accent, text and background colors from an image — read on device, nothing is\n uploaded.\n </Text>\n <ImageFileButton\n loading={busy}\n mode=\"ghost\"\n onFile={pickImage}\n text=\"Pick an image\"\n tooltip=\"Take the colors from an image on your device\"\n width=\"fill\"\n />\n </Stack>\n )","{variant, options: variantOptions}","variant","<Stack gap={3}>\n <Flex align=\"center\" gap={2}>\n <Box flex={1}>\n <Text size={1} weight=\"medium\">\n Image palette\n </Text>\n </Box>\n {palette && (\n <ImageFileButton\n loading={busy}\n mode=\"bleed\"\n onFile={pickImage}\n padding={2}\n tooltip=\"Pick another image\"\n />\n )}\n </Flex>\n\n {palette ? (\n <>\n <PaletteGrid data-with-image={imageUrl !== undefined}>\n {imageUrl && <ImageTile alt=\"\" src={imageUrl} />}\n {TILES.map((key) => {\n const hex = palette[key]\n const title = IMAGE_PALETTE_TITLES[key]\n\n return (\n <Tooltip\n animate\n content={\n <Text size={1}>\n {hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`}\n </Text>\n }\n key={key}\n placement=\"bottom\"\n portal\n >\n <Tile data-empty={hex === null} style={{background: hex ?? undefined}} />\n </Tooltip>\n )\n })}\n </PaletteGrid>\n\n <Stack gap={2}>\n <Text muted size={0}>\n Pick the swatch to build the theme around\n </Text>\n <VariantRow\n ref={rowRef}\n style={{\n ['--variant-width' as string]: `${variantWidth}px`,\n ['--variant-scale' as string]: variantWidth / VARIANT_WIDTH,\n }}\n >\n {variants.map(({variant, options: variantOptions}) => (\n <VariantButton\n aria-pressed={variant === current}\n key={variant}\n onClick={() => onVariant(variant)}\n type=\"button\"\n >\n <VariantFrame>\n <VariantScale>\n <ThemeThumbnail options={variantOptions} />\n </VariantScale>\n </VariantFrame>\n <Box paddingTop={1}>\n <Text\n align=\"center\"\n size={0}\n textOverflow=\"ellipsis\"\n weight={variant === current ? 'medium' : 'regular'}\n >\n {IMAGE_PALETTE_TITLES[variant]}\n </Text>\n </Box>\n </VariantButton>\n ))}\n </VariantRow>\n </Stack>\n\n <TooltipButton\n icon={SparklesIcon}\n mode=\"ghost\"\n onClick={onLucky}\n text=\"I'm feeling lucky\"\n tooltip=\"Build the theme around a swatch that looks interesting\"\n width=\"fill\"\n />\n </>\n ) : (\n <Stack gap={3}>\n <Text muted size={0}>\n Take the accent, text and background colors from an image — read on device, nothing is\n uploaded.\n </Text>\n <ImageFileButton\n loading={busy}\n mode=\"ghost\"\n onFile={pickImage}\n text=\"Pick an image\"\n tooltip=\"Take the colors from an image on your device\"\n width=\"fill\"\n />\n </Stack>\n )}\n </Stack>","<Card\n border\n onDragEnter={(event) => {\n event.preventDefault()\n dragDepth.current++\n setDragging(true)\n }}\n onDragLeave={() => {\n dragDepth.current = Math.max(0, dragDepth.current - 1)\n\n if (dragDepth.current === 0) setDragging(false)\n }}\n onDragOver={(event) => {\n event.preventDefault()\n }}\n onDrop={(event) => {\n event.preventDefault()\n dragDepth.current = 0\n setDragging(false)\n\n const file = event.dataTransfer.files[0]\n\n if (file) pickImage(file)\n }}\n padding={3}\n radius={3}\n tone={dragging ? 'primary' : 'default'}\n >\n <Stack gap={3}>\n <Flex align=\"center\" gap={2}>\n <Box flex={1}>\n <Text size={1} weight=\"medium\">\n Image palette\n </Text>\n </Box>\n {palette && (\n <ImageFileButton\n loading={busy}\n mode=\"bleed\"\n onFile={pickImage}\n padding={2}\n tooltip=\"Pick another image\"\n />\n )}\n </Flex>\n\n {palette ? (\n <>\n <PaletteGrid data-with-image={imageUrl !== undefined}>\n {imageUrl && <ImageTile alt=\"\" src={imageUrl} />}\n {TILES.map((key) => {\n const hex = palette[key]\n const title = IMAGE_PALETTE_TITLES[key]\n\n return (\n <Tooltip\n animate\n content={\n <Text size={1}>\n {hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`}\n </Text>\n }\n key={key}\n placement=\"bottom\"\n portal\n >\n <Tile data-empty={hex === null} style={{background: hex ?? undefined}} />\n </Tooltip>\n )\n })}\n </PaletteGrid>\n\n <Stack gap={2}>\n <Text muted size={0}>\n Pick the swatch to build the theme around\n </Text>\n <VariantRow\n ref={rowRef}\n style={{\n ['--variant-width' as string]: `${variantWidth}px`,\n ['--variant-scale' as string]: variantWidth / VARIANT_WIDTH,\n }}\n >\n {variants.map(({variant, options: variantOptions}) => (\n <VariantButton\n aria-pressed={variant === current}\n key={variant}\n onClick={() => onVariant(variant)}\n type=\"button\"\n >\n <VariantFrame>\n <VariantScale>\n <ThemeThumbnail options={variantOptions} />\n </VariantScale>\n </VariantFrame>\n <Box paddingTop={1}>\n <Text\n align=\"center\"\n size={0}\n textOverflow=\"ellipsis\"\n weight={variant === current ? 'medium' : 'regular'}\n >\n {IMAGE_PALETTE_TITLES[variant]}\n </Text>\n </Box>\n </VariantButton>\n ))}\n </VariantRow>\n </Stack>\n\n <TooltipButton\n icon={SparklesIcon}\n mode=\"ghost\"\n onClick={onLucky}\n text=\"I'm feeling lucky\"\n tooltip=\"Build the theme around a swatch that looks interesting\"\n width=\"fill\"\n />\n </>\n ) : (\n <Stack gap={3}>\n <Text muted size={0}>\n Take the accent, text and background colors from an image — read on device, nothing is\n uploaded.\n </Text>\n <ImageFileButton\n loading={busy}\n mode=\"ghost\"\n onFile={pickImage}\n text=\"Pick an image\"\n tooltip=\"Take the colors from an image on your device\"\n width=\"fill\"\n />\n </Stack>\n )}\n </Stack>\n </Card>","dragging ? 'primary' : 'default'","(event) => {\n event.preventDefault()\n }","themes.find((candidate) => candidate.slug === slug && candidate.source === 'custom')","(candidate) => candidate.slug === slug && candidate.source === 'custom'","(variant: ImagePaletteVariant | null) => {\n if (!theme.palette || variant === null) return\n\n send({\n type: 'theme.update',\n slug,\n options: applyImagePalette(theme.options, theme.palette, variant),\n })\n }","options","palette","() => send({type: 'flow.list'})","() => {\n if (!theme.palette) return\n\n applyVariant(\n pickLuckyVariant(theme.palette, {\n exclude: currentImageVariant(theme.options, theme.palette),\n }),\n )\n }","(options) => send({type: 'theme.update', slug, options})","(palette, file) =>\n send({\n type: 'theme.update',\n slug,\n options: applyImagePalette(theme.options, palette),\n palette,\n imageUrl: URL.createObjectURL(file),\n })","() => send({type: 'theme.remove', slug})","(title) => send({type: 'theme.update', slug, title})","<ThemeEditorForm\n focusTitle={focusTitle}\n imageUrl={images[slug]}\n onDone={() => send({type: 'flow.list'})}\n onLucky={() => {\n if (!theme.palette) return\n\n applyVariant(\n pickLuckyVariant(theme.palette, {\n exclude: currentImageVariant(theme.options, theme.palette),\n }),\n )\n }}\n onOptionsChange={(options) => send({type: 'theme.update', slug, options})}\n onPalette={(palette, file) =>\n send({\n type: 'theme.update',\n slug,\n options: applyImagePalette(theme.options, palette),\n palette,\n imageUrl: URL.createObjectURL(file),\n })\n }\n onRemove={() => send({type: 'theme.remove', slug})}\n onTitleChange={(title) => send({type: 'theme.update', slug, title})}\n onVariant={applyVariant}\n options={theme.options}\n palette={theme.palette}\n title={theme.title}\n />","images[slug]","title","resolveThemeOptions(options)","useMemo(() => resolveThemeOptions(options), [options])","buildPalette(options)","useMemo(() => buildPalette(options), [options])","(scheme: ThemeColorSchemeKey, changes: Partial<SchemeThemeOptions>) => {\n const values: SchemeThemeOptions = {...options[scheme], ...changes}\n\n // Clearing an option means falling back to its default, which is its absence\n for (const key of SCHEME_OPTION_KEYS) {\n if (values[key] === undefined) delete values[key]\n }\n\n const next: BuildThemeOptions = {...options}\n\n if (Object.keys(values).length === 0) {\n delete next[scheme]\n } else {\n next[scheme] = values\n }\n\n onOptionsChange(next)\n }","<ThemeThumbnail options={options} />","<Text size={1} weight=\"medium\">\n Title\n </Text>","(event) => onTitleChange(event.currentTarget.value)","(event) => event.currentTarget.select()","<Stack gap={2}>\n <Text size={1} weight=\"medium\">\n Title\n </Text>\n <TextInput\n autoFocus={focusTitle}\n fontSize={1}\n onChange={(event) => onTitleChange(event.currentTarget.value)}\n onFocus={focusTitle ? (event) => event.currentTarget.select() : undefined}\n padding={2}\n placeholder=\"Untitled theme\"\n value={title}\n />\n </Stack>","focusTitle ? (event) => event.currentTarget.select() : undefined","<ImagePaletteSection\n imageUrl={imageUrl}\n onLucky={onLucky}\n onPalette={onPalette}\n onVariant={onVariant}\n options={options}\n palette={palette}\n />","SCHEMES.map((scheme) => (\n <SchemeCard\n active={scheme === studioScheme}\n key={scheme}\n onChange={(changes) => patchScheme(scheme, changes)}\n options={options[scheme] ?? {}}\n palette={palettes[scheme]}\n resolved={resolved[scheme]}\n scheme={scheme}\n />\n ))","scheme","changes","<ScrollArea padding={3}>\n <Stack gap={4}>\n <ThemeThumbnail options={options} />\n\n <Stack gap={2}>\n <Text size={1} weight=\"medium\">\n Title\n </Text>\n <TextInput\n autoFocus={focusTitle}\n fontSize={1}\n onChange={(event) => onTitleChange(event.currentTarget.value)}\n onFocus={focusTitle ? (event) => event.currentTarget.select() : undefined}\n padding={2}\n placeholder=\"Untitled theme\"\n value={title}\n />\n </Stack>\n\n <ImagePaletteSection\n imageUrl={imageUrl}\n onLucky={onLucky}\n onPalette={onPalette}\n onVariant={onVariant}\n options={options}\n palette={palette}\n />\n\n {SCHEMES.map((scheme) => (\n <SchemeCard\n active={scheme === studioScheme}\n key={scheme}\n onChange={(changes) => patchScheme(scheme, changes)}\n options={options[scheme] ?? {}}\n palette={palettes[scheme]}\n resolved={resolved[scheme]}\n scheme={scheme}\n />\n ))}\n </Stack>\n </ScrollArea>","<TooltipButton\n icon={TrashIcon}\n mode=\"ghost\"\n onClick={onRemove}\n text=\"Remove\"\n tone=\"critical\"\n tooltip=\"Remove the theme — it can be restored until it is deleted\"\n />","<Box flex={1} />","<Button mode=\"ghost\" onClick={onDone} text=\"Done\" />","<Card borderTop padding={3}>\n <Flex gap={2}>\n <TooltipButton\n icon={TrashIcon}\n mode=\"ghost\"\n onClick={onRemove}\n text=\"Remove\"\n tone=\"critical\"\n tooltip=\"Remove the theme — it can be restored until it is deleted\"\n />\n <Box flex={1} />\n <Button mode=\"ghost\" onClick={onDone} text=\"Done\" />\n </Flex>\n </Card>","<>\n <ScrollArea padding={3}>\n <Stack gap={4}>\n <ThemeThumbnail options={options} />\n\n <Stack gap={2}>\n <Text size={1} weight=\"medium\">\n Title\n </Text>\n <TextInput\n autoFocus={focusTitle}\n fontSize={1}\n onChange={(event) => onTitleChange(event.currentTarget.value)}\n onFocus={focusTitle ? (event) => event.currentTarget.select() : undefined}\n padding={2}\n placeholder=\"Untitled theme\"\n value={title}\n />\n </Stack>\n\n <ImagePaletteSection\n imageUrl={imageUrl}\n onLucky={onLucky}\n onPalette={onPalette}\n onVariant={onVariant}\n options={options}\n palette={palette}\n />\n\n {SCHEMES.map((scheme) => (\n <SchemeCard\n active={scheme === studioScheme}\n key={scheme}\n onChange={(changes) => patchScheme(scheme, changes)}\n options={options[scheme] ?? {}}\n palette={palettes[scheme]}\n resolved={resolved[scheme]}\n scheme={scheme}\n />\n ))}\n </Stack>\n </ScrollArea>\n\n <Card borderTop padding={3}>\n <Flex gap={2}>\n <TooltipButton\n icon={TrashIcon}\n mode=\"ghost\"\n onClick={onRemove}\n text=\"Remove\"\n tone=\"critical\"\n tooltip=\"Remove the theme — it can be restored until it is deleted\"\n />\n <Box flex={1} />\n <Button mode=\"ghost\" onClick={onDone} text=\"Done\" />\n </Flex>\n </Card>\n </>","event","<Box flex={1}>\n <Text size={1} weight=\"medium\">\n {name}\n </Text>\n </Box>","active && (\n <Badge fontSize={0} tone=\"primary\">\n Active\n </Badge>\n )","<Flex align=\"center\" gap={2}>\n <Box flex={1}>\n <Text size={1} weight=\"medium\">\n {name}\n </Text>\n </Box>\n {active && (\n <Badge fontSize={0} tone=\"primary\">\n Active\n </Badge>\n )}\n </Flex>","<Text muted size={0}>\n {active\n ? 'Shown in the Studio right now'\n : `Shown once the Studio appearance is ${scheme}`}\n </Text>","active\n ? 'Shown in the Studio right now'\n : `Shown once the Studio appearance is ${scheme}`","<Stack gap={2}>\n <Flex align=\"center\" gap={2}>\n <Box flex={1}>\n <Text size={1} weight=\"medium\">\n {name}\n </Text>\n </Box>\n {active && (\n <Badge fontSize={0} tone=\"primary\">\n Active\n </Badge>\n )}\n </Flex>\n <Text muted size={0}>\n {active\n ? 'Shown in the Studio right now'\n : `Shown once the Studio appearance is ${scheme}`}\n </Text>\n </Stack>","(accent) => onChange({accent})","() => onChange({accent: undefined})","<ColorRow\n auto={options.accent === undefined}\n autoLabel=\"default\"\n label={`${name} accent`}\n onChange={(accent) => onChange({accent})}\n onClear={() => onChange({accent: undefined})}\n title=\"Accent\"\n value={resolved.accent}\n />","accent","options.accent === undefined","`${name} accent`","(text) => onChange({text})","() => onChange({text: undefined})","<ColorRow\n auto={options.text === undefined}\n label={`${name} text`}\n onChange={(text) => onChange({text})}\n onClear={() => onChange({text: undefined})}\n title=\"Text\"\n value={resolved.text}\n />","text","options.text === undefined","`${name} text`","(background) => onChange({background})","() => onChange({background: undefined})","<ColorRow\n adjusted={scheme === 'dark' ? palette.black : palette.white}\n auto={options.background === undefined}\n label={`${name} background`}\n onChange={(background) => onChange({background})}\n onClear={() => onChange({background: undefined})}\n title=\"Background\"\n value={resolved.background}\n />","background","scheme === 'dark' ? palette.black : palette.white","options.background === undefined","`${name} background`","<Text size={1}>Contrast</Text>","<Stack flex={1} gap={2}>\n <Text size={1}>Contrast</Text>\n <Text muted size={0}>\n {resolved.contrast}\n {options.contrast === undefined ? ' · auto' : ''}\n </Text>\n </Stack>","contrast","options.contrast === undefined ? ' · auto' : ''","options.contrast !== undefined && (\n <TooltipButton\n icon={ResetIcon}\n mode=\"bleed\"\n onClick={() => onChange({contrast: undefined})}\n padding={2}\n tooltip=\"Reset to auto\"\n />\n )","<Flex align=\"center\" gap={2}>\n <Stack flex={1} gap={2}>\n <Text size={1}>Contrast</Text>\n <Text muted size={0}>\n {resolved.contrast}\n {options.contrast === undefined ? ' · auto' : ''}\n </Text>\n </Stack>\n {options.contrast !== undefined && (\n <TooltipButton\n icon={ResetIcon}\n mode=\"bleed\"\n onClick={() => onChange({contrast: undefined})}\n padding={2}\n tooltip=\"Reset to auto\"\n />\n )}\n </Flex>","(event) => onChange({contrast: Number(event.currentTarget.value)})","<Range\n aria-label={`${name} contrast`}\n max={100}\n min={15}\n onChange={(event) => onChange({contrast: Number(event.currentTarget.value)})}\n step={1}\n type=\"range\"\n value={resolved.contrast}\n />","`${name} contrast`","<Text muted size={0}>\n 100 keeps text and borders neutral — lower values blend in the accent\n </Text>","<Stack gap={3}>\n <Flex align=\"center\" gap={2}>\n <Stack flex={1} gap={2}>\n <Text size={1}>Contrast</Text>\n <Text muted size={0}>\n {resolved.contrast}\n {options.contrast === undefined ? ' · auto' : ''}\n </Text>\n </Stack>\n {options.contrast !== undefined && (\n <TooltipButton\n icon={ResetIcon}\n mode=\"bleed\"\n onClick={() => onChange({contrast: undefined})}\n padding={2}\n tooltip=\"Reset to auto\"\n />\n )}\n </Flex>\n <Range\n aria-label={`${name} contrast`}\n max={100}\n min={15}\n onChange={(event) => onChange({contrast: Number(event.currentTarget.value)})}\n step={1}\n type=\"range\"\n value={resolved.contrast}\n />\n <Text muted size={0}>\n 100 keeps text and borders neutral — lower values blend in the accent\n </Text>\n </Stack>","<Stack gap={4}>\n <Stack gap={2}>\n <Flex align=\"center\" gap={2}>\n <Box flex={1}>\n <Text size={1} weight=\"medium\">\n {name}\n </Text>\n </Box>\n {active && (\n <Badge fontSize={0} tone=\"primary\">\n Active\n </Badge>\n )}\n </Flex>\n <Text muted size={0}>\n {active\n ? 'Shown in the Studio right now'\n : `Shown once the Studio appearance is ${scheme}`}\n </Text>\n </Stack>\n\n <ColorRow\n auto={options.accent === undefined}\n autoLabel=\"default\"\n label={`${name} accent`}\n onChange={(accent) => onChange({accent})}\n onClear={() => onChange({accent: undefined})}\n title=\"Accent\"\n value={resolved.accent}\n />\n <ColorRow\n auto={options.text === undefined}\n label={`${name} text`}\n onChange={(text) => onChange({text})}\n onClear={() => onChange({text: undefined})}\n title=\"Text\"\n value={resolved.text}\n />\n <ColorRow\n adjusted={scheme === 'dark' ? palette.black : palette.white}\n auto={options.background === undefined}\n label={`${name} background`}\n onChange={(background) => onChange({background})}\n onClear={() => onChange({background: undefined})}\n title=\"Background\"\n value={resolved.background}\n />\n\n <Stack gap={3}>\n <Flex align=\"center\" gap={2}>\n <Stack flex={1} gap={2}>\n <Text size={1}>Contrast</Text>\n <Text muted size={0}>\n {resolved.contrast}\n {options.contrast === undefined ? ' · auto' : ''}\n </Text>\n </Stack>\n {options.contrast !== undefined && (\n <TooltipButton\n icon={ResetIcon}\n mode=\"bleed\"\n onClick={() => onChange({contrast: undefined})}\n padding={2}\n tooltip=\"Reset to auto\"\n />\n )}\n </Flex>\n <Range\n aria-label={`${name} contrast`}\n max={100}\n min={15}\n onChange={(event) => onChange({contrast: Number(event.currentTarget.value)})}\n step={1}\n type=\"range\"\n value={resolved.contrast}\n />\n <Text muted size={0}>\n 100 keeps text and borders neutral — lower values blend in the accent\n </Text>\n </Stack>\n </Stack>","<Card border padding={3} radius={3} scheme={scheme} tone=\"default\">\n <Stack gap={4}>\n <Stack gap={2}>\n <Flex align=\"center\" gap={2}>\n <Box flex={1}>\n <Text size={1} weight=\"medium\">\n {name}\n </Text>\n </Box>\n {active && (\n <Badge fontSize={0} tone=\"primary\">\n Active\n </Badge>\n )}\n </Flex>\n <Text muted size={0}>\n {active\n ? 'Shown in the Studio right now'\n : `Shown once the Studio appearance is ${scheme}`}\n </Text>\n </Stack>\n\n <ColorRow\n auto={options.accent === undefined}\n autoLabel=\"default\"\n label={`${name} accent`}\n onChange={(accent) => onChange({accent})}\n onClear={() => onChange({accent: undefined})}\n title=\"Accent\"\n value={resolved.accent}\n />\n <ColorRow\n auto={options.text === undefined}\n label={`${name} text`}\n onChange={(text) => onChange({text})}\n onClear={() => onChange({text: undefined})}\n title=\"Text\"\n value={resolved.text}\n />\n <ColorRow\n adjusted={scheme === 'dark' ? palette.black : palette.white}\n auto={options.background === undefined}\n label={`${name} background`}\n onChange={(background) => onChange({background})}\n onClear={() => onChange({background: undefined})}\n title=\"Background\"\n value={resolved.background}\n />\n\n <Stack gap={3}>\n <Flex align=\"center\" gap={2}>\n <Stack flex={1} gap={2}>\n <Text size={1}>Contrast</Text>\n <Text muted size={0}>\n {resolved.contrast}\n {options.contrast === undefined ? ' · auto' : ''}\n </Text>\n </Stack>\n {options.contrast !== undefined && (\n <TooltipButton\n icon={ResetIcon}\n mode=\"bleed\"\n onClick={() => onChange({contrast: undefined})}\n padding={2}\n tooltip=\"Reset to auto\"\n />\n )}\n </Flex>\n <Range\n aria-label={`${name} contrast`}\n max={100}\n min={15}\n onChange={(event) => onChange({contrast: Number(event.currentTarget.value)})}\n step={1}\n type=\"range\"\n value={resolved.contrast}\n />\n <Text muted size={0}>\n 100 keeps text and borders neutral — lower values blend in the accent\n </Text>\n </Stack>\n </Stack>\n </Card>","autoLabel = 'auto'","(event) => onChange(event.currentTarget.value)","<Swatch\n aria-label={label}\n onChange={(event) => onChange(event.currentTarget.value)}\n type=\"color\"\n value={value}\n />","{minWidth: 0}","<Text size={1}>{title}</Text>","<Text muted size={0} textOverflow=\"ellipsis\">\n {value}\n {adjusted !== undefined && adjusted !== value ? ` → ${adjusted}` : ''}\n {auto ? ` · ${autoLabel}` : ''}\n </Text>","adjusted !== undefined && adjusted !== value ? ` → ${adjusted}` : ''","auto ? ` · ${autoLabel}` : ''","<Stack flex={1} gap={2} style={{minWidth: 0}}>\n <Text size={1}>{title}</Text>\n <Text muted size={0} textOverflow=\"ellipsis\">\n {value}\n {adjusted !== undefined && adjusted !== value ? ` → ${adjusted}` : ''}\n {auto ? ` · ${autoLabel}` : ''}\n </Text>\n </Stack>","onClear && !auto && (\n <TooltipButton\n icon={ResetIcon}\n mode=\"bleed\"\n onClick={onClear}\n padding={2}\n tooltip={`Reset to ${autoLabel}`}\n />\n )","<Flex align=\"center\" gap={2}>\n <Swatch\n aria-label={label}\n onChange={(event) => onChange(event.currentTarget.value)}\n type=\"color\"\n value={value}\n />\n <Stack flex={1} gap={2} style={{minWidth: 0}}>\n <Text size={1}>{title}</Text>\n <Text muted size={0} textOverflow=\"ellipsis\">\n {value}\n {adjusted !== undefined && adjusted !== value ? ` → ${adjusted}` : ''}\n {auto ? ` · ${autoLabel}` : ''}\n </Text>\n </Stack>\n {onClear && !auto && (\n <TooltipButton\n icon={ResetIcon}\n mode=\"bleed\"\n onClick={onClear}\n padding={2}\n tooltip={`Reset to ${autoLabel}`}\n />\n )}\n </Flex>","displayTitle(theme.title)","title","() => send({type: 'theme.pick', slug: theme.slug})","slug","<Frame>\n <ThemeThumbnail options={theme.options} />\n </Frame>","options","<Box as=\"span\" display=\"block\" paddingTop={2} paddingX={2}>\n <Text\n align=\"center\"\n as=\"span\"\n size={1}\n textOverflow=\"ellipsis\"\n weight={active ? 'medium' : 'regular'}\n >\n {title}\n </Text>\n </Box>","active ? 'medium' : 'regular'","<PickButton\n aria-pressed={active}\n onClick={() => send({type: 'theme.pick', slug: theme.slug})}\n type=\"button\"\n >\n <Frame>\n <ThemeThumbnail options={theme.options} />\n </Frame>\n <Box as=\"span\" display=\"block\" paddingTop={2} paddingX={2}>\n <Text\n align=\"center\"\n as=\"span\"\n size={1}\n textOverflow=\"ellipsis\"\n weight={active ? 'medium' : 'regular'}\n >\n {title}\n </Text>\n </Box>\n </PickButton>","<Text size={1}>Show more</Text>","{display: 'inline-block'}","<Button\n aria-label={`Show more for ${title}`}\n fontSize={1}\n icon={EllipsisHorizontalIcon}\n mode=\"ghost\"\n padding={1}\n radius={2}\n />","`Show more for ${title}`","theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )","source","() => send({type: 'theme.duplicate', slug: theme.slug})","<MenuItem\n icon={CopyIcon}\n onClick={() => send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />","theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'","theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => send({type: 'theme.remove', slug: theme.slug})}\n text=\"Remove\"\n tone=\"critical\"\n />\n </>\n )","<Menu>\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => send({type: 'theme.remove', slug: theme.slug})}\n text=\"Remove\"\n tone=\"critical\"\n />\n </>\n )}\n </Menu>","() => setMenuOpen(false)","() => setMenuOpen(true)","{animate: true, placement: 'bottom-end', portal: true}","<Tooltip animate content={<Text size={1}>Show more</Text>} placement=\"bottom\" portal>\n <span style={{display: 'inline-block'}}>\n <MenuButton\n button={\n <Button\n aria-label={`Show more for ${title}`}\n fontSize={1}\n icon={EllipsisHorizontalIcon}\n mode=\"ghost\"\n padding={1}\n radius={2}\n />\n }\n id={`themer-theme-${theme.slug}`}\n menu={\n <Menu>\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => send({type: 'theme.remove', slug: theme.slug})}\n text=\"Remove\"\n tone=\"critical\"\n />\n </>\n )}\n </Menu>\n }\n onClose={() => setMenuOpen(false)}\n onOpen={() => setMenuOpen(true)}\n popover={{animate: true, placement: 'bottom-end', portal: true}}\n />\n </span>\n </Tooltip>","`themer-theme-${theme.slug}`","<MenuSlot data-visible={menuOpen}>\n <Tooltip animate content={<Text size={1}>Show more</Text>} placement=\"bottom\" portal>\n <span style={{display: 'inline-block'}}>\n <MenuButton\n button={\n <Button\n aria-label={`Show more for ${title}`}\n fontSize={1}\n icon={EllipsisHorizontalIcon}\n mode=\"ghost\"\n padding={1}\n radius={2}\n />\n }\n id={`themer-theme-${theme.slug}`}\n menu={\n <Menu>\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => send({type: 'theme.remove', slug: theme.slug})}\n text=\"Remove\"\n tone=\"critical\"\n />\n </>\n )}\n </Menu>\n }\n onClose={() => setMenuOpen(false)}\n onOpen={() => setMenuOpen(true)}\n popover={{animate: true, placement: 'bottom-end', portal: true}}\n />\n </span>\n </Tooltip>\n </MenuSlot>","<Root>\n <PickButton\n aria-pressed={active}\n onClick={() => send({type: 'theme.pick', slug: theme.slug})}\n type=\"button\"\n >\n <Frame>\n <ThemeThumbnail options={theme.options} />\n </Frame>\n <Box as=\"span\" display=\"block\" paddingTop={2} paddingX={2}>\n <Text\n align=\"center\"\n as=\"span\"\n size={1}\n textOverflow=\"ellipsis\"\n weight={active ? 'medium' : 'regular'}\n >\n {title}\n </Text>\n </Box>\n </PickButton>\n\n {/* The tooltip wraps a span rather than the button: the menu button\n clones its button to wire it up, which a tooltip in between would\n swallow */}\n <MenuSlot data-visible={menuOpen}>\n <Tooltip animate content={<Text size={1}>Show more</Text>} placement=\"bottom\" portal>\n <span style={{display: 'inline-block'}}>\n <MenuButton\n button={\n <Button\n aria-label={`Show more for ${title}`}\n fontSize={1}\n icon={EllipsisHorizontalIcon}\n mode=\"ghost\"\n padding={1}\n radius={2}\n />\n }\n id={`themer-theme-${theme.slug}`}\n menu={\n <Menu>\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => send({type: 'theme.remove', slug: theme.slug})}\n text=\"Remove\"\n tone=\"critical\"\n />\n </>\n )}\n </Menu>\n }\n onClose={() => setMenuOpen(false)}\n onOpen={() => setMenuOpen(true)}\n popover={{animate: true, placement: 'bottom-end', portal: true}}\n />\n </span>\n </Tooltip>\n </MenuSlot>\n </Root>","(palette, file) =>\n send({\n type: 'theme.add',\n title: titleFromFileName(file.name),\n options: optionsFromImagePalette(palette),\n palette,\n imageUrl: URL.createObjectURL(file),\n })","themes.map((theme) => (\n <ThemeCard active={theme.slug === active.slug} key={theme.slug} theme={theme} />\n ))","active.slug","(theme) => (\n <ThemeCard active={theme.slug === active.slug} key={theme.slug} theme={theme} />\n )","removed.length > 0 && (\n <Button\n icon={RestoreIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'flow.removed'})}\n text={`Show removed (${removed.length})`}\n width=\"fill\"\n />\n )","length","<ScrollArea padding={3}>\n <Stack gap={4}>\n {themes.map((theme) => (\n <ThemeCard active={theme.slug === active.slug} key={theme.slug} theme={theme} />\n ))}\n {/* Trails the list rather than sitting in the footer, so the footer\n does not shift when the first theme gets removed */}\n {removed.length > 0 && (\n <Button\n icon={RestoreIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'flow.removed'})}\n text={`Show removed (${removed.length})`}\n width=\"fill\"\n />\n )}\n </Stack>\n </ScrollArea>","<Box flex={1}>\n <TooltipButton\n icon={AddIcon}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.add'})}\n text=\"Add theme\"\n tooltip=\"Add a theme based on the applied one\"\n width=\"fill\"\n />\n </Box>","<ImageFileButton\n loading={busy}\n mode=\"ghost\"\n onFile={pickImage}\n tooltip=\"Add a theme from the colors of an image\"\n />","<Card borderTop padding={3}>\n <Flex gap={2}>\n <Box flex={1}>\n <TooltipButton\n icon={AddIcon}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.add'})}\n text=\"Add theme\"\n tooltip=\"Add a theme based on the applied one\"\n width=\"fill\"\n />\n </Box>\n <ImageFileButton\n loading={busy}\n mode=\"ghost\"\n onFile={pickImage}\n tooltip=\"Add a theme from the colors of an image\"\n />\n </Flex>\n </Card>","<>\n <ScrollArea padding={3}>\n <Stack gap={4}>\n {themes.map((theme) => (\n <ThemeCard active={theme.slug === active.slug} key={theme.slug} theme={theme} />\n ))}\n {/* Trails the list rather than sitting in the footer, so the footer\n does not shift when the first theme gets removed */}\n {removed.length > 0 && (\n <Button\n icon={RestoreIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'flow.removed'})}\n text={`Show removed (${removed.length})`}\n width=\"fill\"\n />\n )}\n </Stack>\n </ScrollArea>\n\n <Card borderTop padding={3}>\n <Flex gap={2}>\n <Box flex={1}>\n <TooltipButton\n icon={AddIcon}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.add'})}\n text=\"Add theme\"\n tooltip=\"Add a theme based on the applied one\"\n width=\"fill\"\n />\n </Box>\n <ImageFileButton\n loading={busy}\n mode=\"ghost\"\n onFile={pickImage}\n tooltip=\"Add a theme from the colors of an image\"\n />\n </Flex>\n </Card>\n </>","createThemeSnippet(theme.options)","options","async () => {\n try {\n await navigator.clipboard.writeText(snippet)\n toast.push({status: 'success', title: 'Copied the theme to the clipboard'})\n } catch {\n toast.push({status: 'error', title: 'Could not copy the theme'})\n }\n }","<Flex gap={2} justify=\"flex-end\" padding={3}>\n <Button icon={ClipboardIcon} mode=\"ghost\" onClick={() => void handleCopy()} text=\"Copy\" />\n </Flex>","<code>sanity.config.ts</code>","<code>theme</code>","<code>defineConfig</code>","displayTitle(theme.title)","title","<Text muted size={1}>\n Export this from <code>sanity.config.ts</code> and pass it as the <code>theme</code> of{' '}\n <code>defineConfig</code> to make “{displayTitle(theme.title)}” the Studio theme without\n this tool.\n </Text>","<Card border overflow=\"auto\" padding={3} radius={2} tone=\"transparent\">\n <Code language=\"ts\" size={1}>\n {snippet}\n </Code>\n </Card>","<Box padding={4}>\n <Stack gap={4}>\n <Text muted size={1}>\n Export this from <code>sanity.config.ts</code> and pass it as the <code>theme</code> of{' '}\n <code>defineConfig</code> to make “{displayTitle(theme.title)}” the Studio theme without\n this tool.\n </Text>\n <Card border overflow=\"auto\" padding={3} radius={2} tone=\"transparent\">\n <Code language=\"ts\" size={1}>\n {snippet}\n </Code>\n </Card>\n </Stack>\n </Box>","<Dialog\n animate\n footer={\n <Flex gap={2} justify=\"flex-end\" padding={3}>\n <Button icon={ClipboardIcon} mode=\"ghost\" onClick={() => void handleCopy()} text=\"Copy\" />\n </Flex>\n }\n header=\"Add to your config\"\n id=\"themer-snippet\"\n onClickOutside={onClose}\n onClose={onClose}\n width={1}\n >\n <Box padding={4}>\n <Stack gap={4}>\n <Text muted size={1}>\n Export this from <code>sanity.config.ts</code> and pass it as the <code>theme</code> of{' '}\n <code>defineConfig</code> to make “{displayTitle(theme.title)}” the Studio theme without\n this tool.\n </Text>\n <Card border overflow=\"auto\" padding={3} radius={2} tone=\"transparent\">\n <Code language=\"ts\" size={1}>\n {snippet}\n </Code>\n </Card>\n </Stack>\n </Box>\n </Dialog>","!inList && (\n <TooltipButton\n icon={ArrowLeftIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'flow.list'})}\n padding={2}\n tooltip=\"Back to the themes\"\n />\n )","{minWidth: 0}","<Text size={1} textOverflow=\"ellipsis\" weight=\"semibold\">\n {VIEW_TITLES[view.name]}\n </Text>","VIEW_TITLES[view.name]","<Box flex={1} paddingLeft={inList ? 1 : 0} style={{minWidth: 0}}>\n <Text size={1} textOverflow=\"ellipsis\" weight=\"semibold\">\n {VIEW_TITLES[view.name]}\n </Text>\n </Box>","inList ? 1 : 0","view.name !== 'removed' && (\n <TooltipButton\n icon={CodeBlockIcon}\n mode=\"bleed\"\n onClick={() => setSnippetOpen(true)}\n padding={2}\n tooltip=\"Show the code for the applied theme\"\n />\n )","name","<TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'sidebar.close'})}\n padding={2}\n tooltip=\"Close themer\"\n />","<Card padding={2}>\n <Flex align=\"center\" gap={1}>\n {!inList && (\n <TooltipButton\n icon={ArrowLeftIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'flow.list'})}\n padding={2}\n tooltip=\"Back to the themes\"\n />\n )}\n <Box flex={1} paddingLeft={inList ? 1 : 0} style={{minWidth: 0}}>\n <Text size={1} textOverflow=\"ellipsis\" weight=\"semibold\">\n {VIEW_TITLES[view.name]}\n </Text>\n </Box>\n {view.name !== 'removed' && (\n <TooltipButton\n icon={CodeBlockIcon}\n mode=\"bleed\"\n onClick={() => setSnippetOpen(true)}\n padding={2}\n tooltip=\"Show the code for the applied theme\"\n />\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'sidebar.close'})}\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>\n </Card>","view.name === 'list' && <ThemeList />","view.name === 'edit' && <ThemeEditor focusTitle={view.focusTitle} slug={view.slug} />","focusTitle","slug","view.name === 'removed' && <RemovedThemes />","<Flex direction=\"column\" height=\"fill\">\n {/* No bottom border: the tools' own toolbars come in different heights, so a\n line here would never line up with theirs */}\n <Card padding={2}>\n <Flex align=\"center\" gap={1}>\n {!inList && (\n <TooltipButton\n icon={ArrowLeftIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'flow.list'})}\n padding={2}\n tooltip=\"Back to the themes\"\n />\n )}\n <Box flex={1} paddingLeft={inList ? 1 : 0} style={{minWidth: 0}}>\n <Text size={1} textOverflow=\"ellipsis\" weight=\"semibold\">\n {VIEW_TITLES[view.name]}\n </Text>\n </Box>\n {view.name !== 'removed' && (\n <TooltipButton\n icon={CodeBlockIcon}\n mode=\"bleed\"\n onClick={() => setSnippetOpen(true)}\n padding={2}\n tooltip=\"Show the code for the applied theme\"\n />\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'sidebar.close'})}\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>\n </Card>\n\n {view.name === 'list' && <ThemeList />}\n {view.name === 'edit' && <ThemeEditor focusTitle={view.focusTitle} slug={view.slug} />}\n {view.name === 'removed' && <RemovedThemes />}\n </Flex>","snippetOpen && <ThemeSnippetDialog onClose={() => setSnippetOpen(false)} theme={active} />","<Card height=\"fill\">\n <Flex direction=\"column\" height=\"fill\">\n {/* No bottom border: the tools' own toolbars come in different heights, so a\n line here would never line up with theirs */}\n <Card padding={2}>\n <Flex align=\"center\" gap={1}>\n {!inList && (\n <TooltipButton\n icon={ArrowLeftIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'flow.list'})}\n padding={2}\n tooltip=\"Back to the themes\"\n />\n )}\n <Box flex={1} paddingLeft={inList ? 1 : 0} style={{minWidth: 0}}>\n <Text size={1} textOverflow=\"ellipsis\" weight=\"semibold\">\n {VIEW_TITLES[view.name]}\n </Text>\n </Box>\n {view.name !== 'removed' && (\n <TooltipButton\n icon={CodeBlockIcon}\n mode=\"bleed\"\n onClick={() => setSnippetOpen(true)}\n padding={2}\n tooltip=\"Show the code for the applied theme\"\n />\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'sidebar.close'})}\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>\n </Card>\n\n {view.name === 'list' && <ThemeList />}\n {view.name === 'edit' && <ThemeEditor focusTitle={view.focusTitle} slug={view.slug} />}\n {view.name === 'removed' && <RemovedThemes />}\n </Flex>\n\n {snippetOpen && <ThemeSnippetDialog onClose={() => setSnippetOpen(false)} theme={active} />}\n </Card>","() => writeStoredWidth(width)","[width]","(event: React.PointerEvent<HTMLDivElement>) => {\n if (event.button !== 0) return\n\n event.preventDefault()\n event.currentTarget.setPointerCapture(event.pointerId)\n drag.current = {pointerId: event.pointerId, startX: event.clientX, startWidth: width}\n setDragging(true)\n }","useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n if (event.button !== 0) return\n\n event.preventDefault()\n event.currentTarget.setPointerCapture(event.pointerId)\n drag.current = {pointerId: event.pointerId, startX: event.clientX, startWidth: width}\n setDragging(true)\n },\n [width],\n )","(event: React.PointerEvent<HTMLDivElement>) => {\n if (!drag.current || drag.current.pointerId !== event.pointerId) return\n\n // The sidebar sits on the right, so dragging left makes it wider\n setWidth(clampWidth(drag.current.startWidth - (event.clientX - drag.current.startX)))\n }","event","useCallback((event: React.PointerEvent<HTMLDivElement>) => {\n if (!drag.current || drag.current.pointerId !== event.pointerId) return\n\n // The sidebar sits on the right, so dragging left makes it wider\n setWidth(clampWidth(drag.current.startWidth - (event.clientX - drag.current.startX)))\n }, [])","(event: React.PointerEvent<HTMLDivElement>) => {\n if (!drag.current || drag.current.pointerId !== event.pointerId) return\n\n drag.current = null\n setDragging(false)\n }","useCallback((event: React.PointerEvent<HTMLDivElement>) => {\n if (!drag.current || drag.current.pointerId !== event.pointerId) return\n\n drag.current = null\n setDragging(false)\n }, [])","(event: React.KeyboardEvent<HTMLDivElement>) => {\n const steps: Record<string, number> = {\n ArrowLeft: KEYBOARD_STEP,\n ArrowRight: -KEYBOARD_STEP,\n Home: MAXIMUM_WIDTH,\n End: -MAXIMUM_WIDTH,\n }\n const step = steps[event.key]\n\n if (step === undefined) return\n\n event.preventDefault()\n setWidth((current) => clampWidth(current + step))\n }","useCallback((event: React.KeyboardEvent<HTMLDivElement>) => {\n const steps: Record<string, number> = {\n ArrowLeft: KEYBOARD_STEP,\n ArrowRight: -KEYBOARD_STEP,\n Home: MAXIMUM_WIDTH,\n End: -MAXIMUM_WIDTH,\n }\n const step = steps[event.key]\n\n if (step === undefined) return\n\n event.preventDefault()\n setWidth((current) => clampWidth(current + step))\n }, [])","props.renderDefault(props)","props.renderDefault","<Box flex={1} height=\"fill\" overflow=\"auto\">\n {props.renderDefault(props)}\n </Box>","open && (\n <Sidebar height=\"fill\" style={{width}} zOffset={100}>\n <ResizeHandle\n aria-label=\"Resize the themer\"\n aria-orientation=\"vertical\"\n aria-valuemax={MAXIMUM_WIDTH}\n aria-valuemin={MINIMUM_WIDTH}\n aria-valuenow={width}\n data-dragging={dragging}\n onDoubleClick={() => setWidth(MINIMUM_WIDTH)}\n onKeyDown={handleKeyDown}\n onPointerCancel={handlePointerUp}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n // oxlint-disable-next-line prefer-tag-over-role -- a window splitter is a focusable, draggable separator, which an hr is not\n role=\"separator\"\n tabIndex={0}\n />\n <Box height=\"fill\" overflow=\"hidden\">\n <ThemerSidebar />\n </Box>\n </Sidebar>\n )","<Flex height=\"fill\" sizing=\"border\">\n <Box flex={1} height=\"fill\" overflow=\"auto\">\n {props.renderDefault(props)}\n </Box>\n\n {open && (\n <Sidebar height=\"fill\" style={{width}} zOffset={100}>\n <ResizeHandle\n aria-label=\"Resize the themer\"\n aria-orientation=\"vertical\"\n aria-valuemax={MAXIMUM_WIDTH}\n aria-valuemin={MINIMUM_WIDTH}\n aria-valuenow={width}\n data-dragging={dragging}\n onDoubleClick={() => setWidth(MINIMUM_WIDTH)}\n onKeyDown={handleKeyDown}\n onPointerCancel={handlePointerUp}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n // oxlint-disable-next-line prefer-tag-over-role -- a window splitter is a focusable, draggable separator, which an hr is not\n role=\"separator\"\n tabIndex={0}\n />\n <Box height=\"fill\" overflow=\"hidden\">\n <ThemerSidebar />\n </Box>\n </Sidebar>\n )}\n </Flex>","() => ({baseOptions, stored: readStoredState()})","{input}","snapshot.matches({sidebar: 'open'})","() => writeStoredState(stored)","[stored]","() => {\n const current = new Set(Object.values(images))\n\n for (const url of Object.values(previousImages.current)) {\n if (!current.has(url)) URL.revokeObjectURL(url)\n }\n\n previousImages.current = images\n }","[images]","() => () => {\n for (const url of Object.values(previousImages.current)) URL.revokeObjectURL(url)\n }","[]","url","resolveThemes(stored, baseOptions)","useMemo(\n () => resolveThemes(stored, baseOptions),\n [stored, baseOptions],\n )","activeOptions === null ? null : buildTheme(activeOptions)","useMemo(\n () => (activeOptions === null ? null : buildTheme(activeOptions)),\n [activeOptions],\n )","() => {\n const background = theme?.v2?.color[scheme].default.bg\n\n if (background === undefined) return undefined\n\n const {style} = document.body\n const previous = style.backgroundColor\n\n style.backgroundColor = background\n\n return () => {\n style.backgroundColor = previous\n }\n }","color","[scheme, theme]","{baseOptions, themes, removed, active, images, view, open, send}","useMemo<ThemerContextValue>(\n () => ({baseOptions, themes, removed, active, images, view, open, send}),\n [active, baseOptions, images, open, removed, send, themes, view],\n )","<ThemerContext.Provider value={context}>\n {theme === null ? (\n layoutProps.renderDefault(layoutProps)\n ) : (\n <ThemeProvider theme={theme}>{layoutProps.renderDefault(layoutProps)}</ThemeProvider>\n )}\n </ThemerContext.Provider>","Provider","theme === null ? (\n layoutProps.renderDefault(layoutProps)\n ) : (\n <ThemeProvider theme={theme}>{layoutProps.renderDefault(layoutProps)}</ThemeProvider>\n )","ThemerContext.Provider","<Text size={1}>Themer</Text>","() => send({type: 'sidebar.toggle'})","<Tooltip animate content={<Text size={1}>Themer</Text>} portal>\n <Button\n aria-label=\"Themer\"\n icon={ColorWheelIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'sidebar.toggle'})}\n // The Studio's own navbar buttons go through a wrapper that pins them\n // to this padding, where `@sanity/ui` defaults to a roomier 3\n padding={2}\n selected={open}\n />\n </Tooltip>","props.__internal_actions ?? []","__internal_actions","props.renderDefault({\n ...props,\n __internal_actions: [\n ...(props.__internal_actions ?? []),\n {\n location: 'topbar',\n name: 'themer-topbar',\n render: () => <ThemerNavbarButton />,\n },\n {\n icon: ColorWheelIcon,\n location: 'sidebar',\n name: 'themer-sidebar',\n onAction: () => send({type: 'sidebar.toggle'}),\n selected: open,\n title: 'Themer',\n },\n ],\n })","() => <ThemerNavbarButton />","<ThemerLayout {...props} baseOptions={baseOptions} />"],"sources":["../src/tool/context.ts","../src/tool/ScrollArea.tsx","../src/tool/options.ts","../src/tool/themes.ts","../src/tool/ThemePreview.tsx","../src/tool/ThemeThumbnail.tsx","../src/tool/RemovedThemes.tsx","../src/tool/imagePalette.ts","../src/tool/TooltipButton.tsx","../src/tool/ImageFileButton.tsx","../src/tool/imageFile.ts","../src/tool/useImagePalette.ts","../src/tool/ImagePaletteSection.tsx","../src/tool/ThemeEditor.tsx","../src/tool/ThemeCard.tsx","../src/tool/ThemeList.tsx","../src/tool/snippet.ts","../src/tool/ThemeSnippetDialog.tsx","../src/tool/ThemerSidebar.tsx","../src/tool/ThemerActiveToolLayout.tsx","../src/tool/machine.ts","../src/tool/storage.ts","../src/tool/ThemerLayout.tsx","../src/tool/ThemerNavbar.tsx","../src/tool/plugin.tsx"],"sourcesContent":["import {createContext, useContext} from 'react'\n\nimport {BuildThemeOptions} from '../theme/options'\nimport {ThemerEvent} from './machine'\nimport {ThemerTheme} from './themes'\n\n/**\n * The flow the sidebar is in: picking a theme from the list, editing one of\n * the user's own themes, or restoring removed ones.\n *\n * @internal\n */\nexport type ThemerView =\n | {name: 'list'}\n | {\n name: 'edit'\n slug: string\n /** Whether the title input should take focus, for themes that were just created */\n focusTitle: boolean\n }\n | {name: 'removed'}\n\n/** @internal */\nexport interface ThemerContextValue {\n /** The theme options the Studio's configured theme was generated from */\n baseOptions: BuildThemeOptions\n /** The themes to pick from, in list order */\n themes: ThemerTheme[]\n /** The removed themes, which can be restored */\n removed: ThemerTheme[]\n /** The applied theme */\n active: ThemerTheme\n /** Object URLs of the images themes took their palette from this session, by slug */\n images: Record<string, string>\n view: ThemerView\n /** Whether the themer sidebar is open */\n open: boolean\n /** Sends an event to the themer machine */\n send: (event: ThemerEvent) => void\n}\n\n/** @internal */\nexport const ThemerContext = createContext<ThemerContextValue | null>(null)\n\n/** @internal */\nexport function useThemer(): ThemerContextValue {\n const context = useContext(ThemerContext)\n\n if (!context) {\n throw new Error('useThemer must be used within the `themerTool` plugin')\n }\n\n return context\n}\n","import {Box, useTheme_v2} from '@sanity/ui'\nimport {useEffect, useRef, useState} from 'react'\nimport {styled} from 'styled-components'\n\nconst Root = styled(Box)`\n scrollbar-gutter: stable;\n`\n\n/**\n * The scrolling body of a sidebar flow. It always reserves the scrollbar's\n * space, so the content does not shift when the list grows or shrinks past\n * the point where it needs to scroll — and it takes that space out of its\n * right padding, so the content stays centered rather than hugging the left.\n * With overlay scrollbars there is no gutter and the padding stays even.\n *\n * @internal\n */\nexport function ScrollArea(props: {children: React.ReactNode; padding: number}) {\n const {children, padding} = props\n const {space} = useTheme_v2()\n const ref = useRef<HTMLDivElement | null>(null)\n const [gutter, setGutter] = useState(0)\n\n useEffect(() => {\n const element = ref.current\n\n if (!element) return undefined\n\n // The reserved gutter is the part of the box the content cannot use\n const measure = () => setGutter(element.offsetWidth - element.clientWidth)\n\n measure()\n\n const observer = new ResizeObserver(measure)\n\n observer.observe(element)\n\n return () => observer.disconnect()\n }, [])\n\n return (\n <Root\n flex={1}\n overflow=\"auto\"\n padding={padding}\n ref={ref}\n style={{paddingRight: Math.max(0, space[padding] - gutter)}}\n >\n {children}\n </Root>\n )\n}\n","import {\n BuildThemeOptions,\n DEFAULT_ACCENT,\n DEFAULT_BACKGROUND,\n DEFAULT_CONTRAST,\n deriveTextColor,\n ResolvedSchemeOptions,\n resolveThemeOptions,\n SCHEMES,\n SchemeThemeOptions,\n} from '../theme/options'\n\n/**\n * Reduces theme options to the minimal object that recreates them through\n * `buildTheme`: in each scheme the accent is dropped when it is the stock\n * one, the text color when it matches the one derived from the accent, and\n * the background and contrast when they match the defaults — and a scheme\n * with nothing left is dropped altogether. Options that boil down to the\n * stock Studio theme reduce to `null` — they need nothing from this package.\n *\n * @internal\n */\nexport function minimizeOptions(options: BuildThemeOptions): BuildThemeOptions | null {\n const resolved = resolveThemeOptions(options)\n const minimized: BuildThemeOptions = {}\n\n for (const scheme of SCHEMES) {\n const values = resolved[scheme]\n const kept: SchemeThemeOptions = {}\n\n if (values.accent !== DEFAULT_ACCENT) kept.accent = values.accent\n if (values.text !== deriveTextColor(values.accent)) kept.text = values.text\n if (values.background !== DEFAULT_BACKGROUND[scheme]) kept.background = values.background\n if (values.contrast !== DEFAULT_CONTRAST) kept.contrast = values.contrast\n\n if (Object.keys(kept).length > 0) minimized[scheme] = kept\n }\n\n return Object.keys(minimized).length === 0 ? null : minimized\n}\n\n/**\n * Whether two sets of theme options generate the same theme, comparing their\n * resolved forms so that derived and explicit values (and hex casing) don't\n * matter.\n *\n * @internal\n */\nexport function sameOptions(a: BuildThemeOptions, b: BuildThemeOptions): boolean {\n const left = resolveThemeOptions(a)\n const right = resolveThemeOptions(b)\n\n return SCHEMES.every((scheme) => sameScheme(left[scheme], right[scheme]))\n}\n\nfunction sameScheme(a: ResolvedSchemeOptions, b: ResolvedSchemeOptions): boolean {\n return (\n a.accent === b.accent &&\n a.text === b.text &&\n a.background === b.background &&\n a.contrast === b.contrast\n )\n}\n","import {BuildThemeOptions} from '../theme/options'\nimport {presets} from '../theme/presets'\nimport {ImagePalette} from './imagePalette'\nimport {sameOptions} from './options'\n\n/**\n * Where a theme in the themer list comes from: the theme the Studio config\n * was generated from, one of the presets defined in code, or one the user\n * added in the tool. Only custom themes are editable — the others are\n * duplicated into a custom theme first.\n */\ntype ThemerThemeSource = 'config' | 'preset' | 'custom'\n\n/** A theme the themer tool lists @internal */\nexport interface ThemerTheme {\n slug: string\n title: string\n options: BuildThemeOptions\n source: ThemerThemeSource\n /** The palette of the image the theme's colors were taken from, for custom themes */\n palette?: ImagePalette\n}\n\n/** A theme the user added in the tool, as persisted @internal */\nexport interface CustomTheme {\n slug: string\n title: string\n options: BuildThemeOptions\n /** The palette of the image the theme's colors were taken from */\n palette?: ImagePalette\n}\n\n/** The persisted themer state @internal */\nexport interface ThemerState {\n /**\n * The slug of the applied theme — `null` applies nothing on top of the\n * Studio's configured theme\n */\n active: string | null\n /** The themes the user added */\n custom: CustomTheme[]\n /** The slugs of the themes the user removed from the list */\n removed: string[]\n}\n\n/** The slug of the theme the Studio config was generated from @internal */\nexport const CONFIG_SLUG = 'config'\n\n/** The title of the theme the Studio config was generated from */\nconst CONFIG_TITLE = 'Studio config'\n\n/** The title new themes start out with @internal */\nexport const UNTITLED_THEME = 'Untitled theme'\n\n/** @internal */\nexport const initialThemerState: ThemerState = {active: null, custom: [], removed: []}\n\n/** The themes the tool works with, derived from the persisted state @internal */\nexport interface ResolvedThemes {\n /** The themes to pick from, in list order */\n themes: ThemerTheme[]\n /** The removed themes, which can be restored */\n removed: ThemerTheme[]\n /** The applied theme */\n active: ThemerTheme\n}\n\n/**\n * Resolves the list of themes: the configured theme first, then the presets\n * (minus the ones that would only repeat the configured theme), then the\n * user's own themes. Removed themes are set aside so they can be restored,\n * and the active theme falls back to the configured one when its slug no\n * longer resolves.\n *\n * @internal\n */\nexport function resolveThemes(state: ThemerState, baseOptions: BuildThemeOptions): ResolvedThemes {\n const config: ThemerTheme = {\n slug: CONFIG_SLUG,\n title: CONFIG_TITLE,\n options: baseOptions,\n source: 'config',\n }\n\n const all: ThemerTheme[] = [config]\n\n for (const preset of presets) {\n if (!sameOptions(preset.options, baseOptions)) {\n all.push({slug: preset.slug, title: preset.title, options: preset.options, source: 'preset'})\n }\n }\n\n for (const theme of state.custom) {\n all.push({\n slug: theme.slug,\n title: theme.title,\n options: theme.options,\n source: 'custom',\n ...(theme.palette ? {palette: theme.palette} : {}),\n })\n }\n\n const removedSlugs = new Set(state.removed)\n const themes = all.filter((theme) => !removedSlugs.has(theme.slug))\n const removed = all.filter((theme) => removedSlugs.has(theme.slug))\n const active = themes.find((theme) => theme.slug === state.active) ?? config\n\n return {themes, removed, active}\n}\n\n/**\n * Creates a custom theme with a slug that cannot collide with the presets or\n * the configured theme.\n *\n * @internal\n */\nexport function createCustomTheme(\n title: string,\n options: BuildThemeOptions,\n palette?: ImagePalette,\n): CustomTheme {\n return {slug: `custom-${randomId()}`, title, options, ...(palette ? {palette} : {})}\n}\n\n/** The title of a duplicated theme @internal */\nexport function duplicateTitle(title: string): string {\n return `${displayTitle(title)} copy`\n}\n\n/** The title to show for a theme, falling back when the user cleared it @internal */\nexport function displayTitle(title: string): string {\n return title.trim() || UNTITLED_THEME\n}\n\nfunction randomId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n\n return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`\n}\n","import {ThemeProvider, useTheme_v2} from '@sanity/ui'\nimport {useMemo} from 'react'\nimport {styled} from 'styled-components'\n\nimport {buildTheme} from '../theme/buildTheme'\nimport {BuildThemeOptions} from '../theme/options'\n\n/**\n * The dark scheme covers the right side of the preview behind a diagonal cut,\n * like the \"Auto\" appearance preview in macOS System Settings shows both\n * schemes at once.\n */\nconst DARK_CLIP_PATH = 'polygon(58% 0, 100% 0, 100% 100%, 42% 100%)'\n\nconst Root = styled.div`\n position: relative;\n aspect-ratio: 16 / 9;\n overflow: hidden;\n`\n\n/** The document rows in the list pane — the widths of their title lines */\nconst LIST_ROWS = ['62%', '46%', '70%', '52%']\n\n/** The fields in the document form — the widths of their labels */\nconst FIELDS = ['32%', '24%']\n\n/** The thickness of a line of text — bold enough to survive a half-size preview */\nconst LINE = 4\n\n/**\n * A tiny, lo-fi mockup of a Studio in the given theme — the navbar over the\n * structure tool with a list of documents on the left and a document form on\n * the right — in both color schemes at once: the light scheme on the left and\n * the dark scheme on the right, split diagonally. It only paints colors the\n * theme options change (backgrounds, text, borders, the accent) so that a\n * glance shows what the theme does to the Studio — with few and bold enough\n * elements to still read when scaled down to a thumbnail.\n *\n * @internal\n */\nexport function ThemePreview(props: {options: BuildThemeOptions}) {\n const {options} = props\n const theme = useMemo(() => buildTheme(options), [options])\n\n return (\n <Root aria-hidden>\n <ThemeProvider scheme=\"light\" theme={theme} tone=\"default\">\n <MockStudio />\n </ThemeProvider>\n <ThemeProvider scheme=\"dark\" theme={theme} tone=\"default\">\n <MockStudio style={{clipPath: DARK_CLIP_PATH}} />\n </ThemeProvider>\n </Root>\n )\n}\n\nfunction MockStudio(props: {style?: React.CSSProperties}) {\n const {color} = useTheme_v2()\n const primary = color.button.default.primary.enabled\n const selected = color.selectable.default.selected\n const input = color.input.default.enabled\n const border = `1px solid ${color.border}`\n\n const line = (width: string, background: string): React.CSSProperties => ({\n flex: 'none',\n width,\n height: LINE,\n borderRadius: LINE / 2,\n background,\n })\n\n return (\n <div\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n flexDirection: 'column',\n background: color.bg,\n ...props.style,\n }}\n >\n <div\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n gap: 5,\n height: '18%',\n padding: '0 5%',\n borderBottom: border,\n }}\n >\n <div style={{flex: 'none', width: 8, height: 8, borderRadius: 2, background: primary.bg}} />\n <div style={line('22%', color.fg)} />\n <div style={{flex: 1}} />\n <div\n style={{\n flex: 'none',\n width: 8,\n height: 8,\n borderRadius: '50%',\n background: color.avatar.blue.bg,\n }}\n />\n </div>\n\n <div style={{flex: 1, display: 'flex', minHeight: 0}}>\n <div\n style={{\n flex: 'none',\n display: 'flex',\n flexDirection: 'column',\n width: '36%',\n borderRight: border,\n }}\n >\n {LIST_ROWS.map((width, index) => {\n const isSelected = index === 1\n\n return (\n <div\n key={width}\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n height: '18%',\n padding: '0 9%',\n background: isSelected ? selected.bg : undefined,\n }}\n >\n <div style={line(width, isSelected ? selected.fg : color.muted.fg)} />\n </div>\n )\n })}\n </div>\n\n <div style={{flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0}}>\n <div\n style={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n gap: 7,\n padding: '9% 8% 0',\n minHeight: 0,\n overflow: 'hidden',\n }}\n >\n <div style={line('45%', color.fg)} />\n {FIELDS.map((width) => (\n <div\n key={width}\n style={{flex: 'none', display: 'flex', flexDirection: 'column', gap: 3}}\n >\n <div style={line(width, color.muted.fg)} />\n <div\n style={{\n height: 12,\n borderRadius: 3,\n background: input.bg,\n boxShadow: `inset 0 0 0 1px ${input.border}`,\n }}\n />\n </div>\n ))}\n </div>\n <div\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n height: '20%',\n padding: '0 8%',\n borderTop: border,\n }}\n >\n <div style={{width: '32%', height: '50%', borderRadius: 3, background: primary.bg}} />\n </div>\n </div>\n </div>\n </div>\n )\n}\n","import {Card} from '@sanity/ui'\n\nimport {BuildThemeOptions} from '../theme/options'\nimport {ThemePreview} from './ThemePreview'\n\n/**\n * The floating preview card of a theme: the lo-fi studio mockup on a raised,\n * rounded card, like the appearance previews in macOS System Settings.\n *\n * @internal\n */\nexport function ThemeThumbnail(props: {options: BuildThemeOptions}) {\n return (\n <Card overflow=\"hidden\" radius={3} shadow={1}>\n <ThemePreview options={props.options} />\n </Card>\n )\n}\n","import {Box, Button, Flex, Stack, Text} from '@sanity/ui'\n\nimport {useThemer} from './context'\nimport {ScrollArea} from './ScrollArea'\nimport {displayTitle} from './themes'\nimport {ThemeThumbnail} from './ThemeThumbnail'\n\n/**\n * The flow for restoring removed themes: the presets and custom themes the\n * user took out of the list, each with a button to put it back — and, for\n * custom themes, one to delete it for good. The machine leaves the flow once\n * the last one is gone.\n *\n * @internal\n */\nexport function RemovedThemes() {\n const {removed, send} = useThemer()\n\n return (\n <ScrollArea padding={3}>\n <Stack gap={5}>\n {removed.map((theme) => (\n <Stack gap={3} key={theme.slug}>\n <Box style={{opacity: 0.6}}>\n <ThemeThumbnail options={theme.options} />\n </Box>\n <Text align=\"center\" size={1} textOverflow=\"ellipsis\">\n {displayTitle(theme.title)}\n </Text>\n <Flex gap={2} justify=\"center\">\n <Button\n fontSize={1}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.restore', slug: theme.slug})}\n padding={2}\n text=\"Restore\"\n />\n {theme.source === 'custom' && (\n <Button\n fontSize={1}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.delete', slug: theme.slug})}\n padding={2}\n text=\"Delete\"\n tone=\"critical\"\n />\n )}\n </Flex>\n </Stack>\n ))}\n </Stack>\n </ScrollArea>\n )\n}\n","import {type ThemeColorSchemeKey} from '@sanity/ui/theme'\n\nimport {hexToHsl, hslToHex, relativeLuminance} from '../theme/hsl'\nimport {BuildThemeOptions, DEFAULT_BACKGROUND, SCHEMES, SchemeThemeOptions} from '../theme/options'\n\n/**\n * The swatches of an image palette — the same set the Sanity image pipeline\n * derives for uploaded assets (`metadata.palette`), extracted on device here.\n *\n * @internal\n */\nexport type ImagePaletteKey =\n | 'dominant'\n | 'vibrant'\n | 'lightVibrant'\n | 'darkVibrant'\n | 'muted'\n | 'lightMuted'\n | 'darkMuted'\n\n/**\n * An image palette: a hex color per swatch, or `null` when the image has no\n * color that qualifies for it.\n *\n * @internal\n */\nexport type ImagePalette = Record<ImagePaletteKey, string | null>\n\n/** @internal */\nexport const IMAGE_PALETTE_KEYS: ImagePaletteKey[] = [\n 'vibrant',\n 'lightVibrant',\n 'darkVibrant',\n 'muted',\n 'lightMuted',\n 'darkMuted',\n 'dominant',\n]\n\n/**\n * The swatches a theme can be built around — every swatch but the dominant\n * one, which only repeats one of them.\n *\n * @internal\n */\nexport type ImagePaletteVariant = Exclude<ImagePaletteKey, 'dominant'>\n\n/** The variants in the order the tool offers them @internal */\nexport const IMAGE_PALETTE_VARIANTS: ImagePaletteVariant[] = [\n 'muted',\n 'vibrant',\n 'lightMuted',\n 'lightVibrant',\n 'darkMuted',\n 'darkVibrant',\n]\n\n/** @internal */\nexport const IMAGE_PALETTE_TITLES: Record<ImagePaletteKey, string> = {\n dominant: 'Dominant',\n vibrant: 'Vibrant',\n lightVibrant: 'Light vibrant',\n darkVibrant: 'Dark vibrant',\n muted: 'Muted',\n lightMuted: 'Light muted',\n darkMuted: 'Dark muted',\n}\n\n/** Pixels the way a canvas hands them out: rows of RGBA bytes @internal */\nexport interface PixelData {\n data: ArrayLike<number>\n width: number\n height: number\n}\n\ninterface Bucket {\n count: number\n r: number\n g: number\n b: number\n}\n\ninterface Candidate {\n hex: string\n population: number\n saturation: number\n lightness: number\n}\n\n/** What a swatch looks for: a lightness and saturation to aim at, within bounds */\ninterface Target {\n lightness: [target: number, min: number, max: number]\n saturation: [target: number, min: number, max: number]\n}\n\n/**\n * The swatch targets of the Vibrant algorithm (the one behind Sanity's image\n * palettes), in the order they get to claim colors.\n */\nconst TARGETS: Array<[key: Exclude<ImagePaletteKey, 'dominant'>, target: Target]> = [\n ['vibrant', {lightness: [0.5, 0.3, 0.7], saturation: [1, 0.35, 1]}],\n ['lightVibrant', {lightness: [0.74, 0.55, 1], saturation: [1, 0.35, 1]}],\n ['darkVibrant', {lightness: [0.26, 0, 0.45], saturation: [1, 0.35, 1]}],\n ['muted', {lightness: [0.5, 0.3, 0.7], saturation: [0.3, 0, 0.4]}],\n ['lightMuted', {lightness: [0.74, 0.55, 1], saturation: [0.3, 0, 0.4]}],\n ['darkMuted', {lightness: [0.26, 0, 0.45], saturation: [0.3, 0, 0.4]}],\n]\n\nconst SATURATION_WEIGHT = 3\nconst LIGHTNESS_WEIGHT = 6.5\nconst POPULATION_WEIGHT = 0.5\n\n/** Pixels this transparent are not part of the picture */\nconst MINIMUM_ALPHA = 125\n\n/** Near-black and near-white pixels are backdrop rather than color */\nconst MINIMUM_LIGHTNESS = 0.05\nconst MAXIMUM_LIGHTNESS = 0.95\n\n/** How many of the 8 bits per channel survive the quantization */\nconst QUANTIZE_BITS = 5\n\n/**\n * Extracts the color palette of an image from its pixels, the way the Vibrant\n * algorithm does: the colors are quantized into a coarse histogram, and each\n * swatch claims the color that comes closest to its target lightness and\n * saturation — weighted by how much of the image it covers — that no other\n * swatch has claimed yet. The dominant swatch is simply the most common\n * color. Runs anywhere pixels come from, so it can run on device.\n *\n * @internal\n */\nexport function extractImagePalette(pixels: PixelData): ImagePalette {\n const candidates = quantize(pixels)\n const palette: ImagePalette = {\n dominant: null,\n vibrant: null,\n lightVibrant: null,\n darkVibrant: null,\n muted: null,\n lightMuted: null,\n darkMuted: null,\n }\n\n if (candidates.length === 0) return palette\n\n const maximumPopulation = Math.max(...candidates.map((candidate) => candidate.population))\n const claimed = new Set<string>()\n\n for (const [key, target] of TARGETS) {\n let best: Candidate | null = null\n let bestScore = -Infinity\n\n for (const candidate of candidates) {\n if (claimed.has(candidate.hex) || !matchesTarget(candidate, target)) continue\n\n const score = scoreCandidate(candidate, target, maximumPopulation)\n\n if (score > bestScore) {\n best = candidate\n bestScore = score\n }\n }\n\n if (best) {\n palette[key] = best.hex\n claimed.add(best.hex)\n }\n }\n\n palette.dominant = candidates.reduce((most, candidate) =>\n candidate.population > most.population ? candidate : most,\n ).hex\n\n return palette\n}\n\n/**\n * Turns the pixels into a histogram of quantized colors, each averaged over\n * the pixels it stands for, leaving out transparent pixels and the near-black\n * and near-white ones that are backdrop rather than color.\n */\nfunction quantize(pixels: PixelData): Candidate[] {\n const {data} = pixels\n const buckets = new Map<number, Bucket>()\n const shift = 8 - QUANTIZE_BITS\n const length = Math.min(data.length, pixels.width * pixels.height * 4)\n\n for (let offset = 0; offset + 3 < length; offset += 4) {\n const r = data[offset]\n const g = data[offset + 1]\n const b = data[offset + 2]\n const a = data[offset + 3]\n\n if (a < MINIMUM_ALPHA) continue\n\n const lightness = (Math.max(r, g, b) + Math.min(r, g, b)) / 510\n\n if (lightness < MINIMUM_LIGHTNESS || lightness > MAXIMUM_LIGHTNESS) continue\n\n const key =\n ((r >> shift) << (QUANTIZE_BITS * 2)) | ((g >> shift) << QUANTIZE_BITS) | (b >> shift)\n const bucket = buckets.get(key)\n\n if (bucket) {\n bucket.count++\n bucket.r += r\n bucket.g += g\n bucket.b += b\n } else {\n buckets.set(key, {count: 1, r, g, b})\n }\n }\n\n const candidates: Candidate[] = []\n\n for (const bucket of buckets.values()) {\n const r = Math.round(bucket.r / bucket.count)\n const g = Math.round(bucket.g / bucket.count)\n const b = Math.round(bucket.b / bucket.count)\n const {saturation, lightness} = saturationAndLightness(r, g, b)\n\n candidates.push({hex: rgbToHex(r, g, b), population: bucket.count, saturation, lightness})\n }\n\n return candidates\n}\n\nfunction matchesTarget(candidate: Candidate, target: Target): boolean {\n return (\n candidate.lightness >= target.lightness[1] &&\n candidate.lightness <= target.lightness[2] &&\n candidate.saturation >= target.saturation[1] &&\n candidate.saturation <= target.saturation[2]\n )\n}\n\nfunction scoreCandidate(candidate: Candidate, target: Target, maximumPopulation: number): number {\n return (\n SATURATION_WEIGHT * (1 - Math.abs(candidate.saturation - target.saturation[0])) +\n LIGHTNESS_WEIGHT * (1 - Math.abs(candidate.lightness - target.lightness[0])) +\n POPULATION_WEIGHT * (candidate.population / maximumPopulation)\n )\n}\n\nfunction saturationAndLightness(r: number, g: number, b: number) {\n const max = Math.max(r, g, b) / 255\n const min = Math.min(r, g, b) / 255\n const lightness = (max + min) / 2\n const delta = max - min\n const saturation =\n delta === 0 ? 0 : lightness > 0.5 ? delta / (2 - max - min) : delta / (max + min)\n\n return {saturation, lightness}\n}\n\nfunction rgbToHex(r: number, g: number, b: number): string {\n return `#${[r, g, b].map((channel) => channel.toString(16).padStart(2, '0')).join('')}`\n}\n\n/** Reads `#rgb` and `#rrggbb` alike — stored palettes may hold either */\nfunction hexToRgb(hex: string): [number, number, number] {\n const expanded =\n hex.length === 4 ? `${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex.slice(1)\n\n return [\n parseInt(expanded.slice(0, 2), 16),\n parseInt(expanded.slice(2, 4), 16),\n parseInt(expanded.slice(4, 6), 16),\n ]\n}\n\n/** Mixes `amount` of `color` into `base` */\nfunction tint(base: string, color: string, amount: number): string {\n const [r1, g1, b1] = hexToRgb(base)\n const [r2, g2, b2] = hexToRgb(color)\n\n return rgbToHex(\n Math.round(r1 + (r2 - r1) * amount),\n Math.round(g1 + (g2 - g1) * amount),\n Math.round(b1 + (b2 - b1) * amount),\n )\n}\n\n/** How much of the palette tints each scheme's stock background */\nconst BACKGROUND_TINT = {light: 0.08, dark: 0.3}\n\n/**\n * The WCAG AA contrast for normal text — what a button label needs against\n * the button. Sanity UI labels solid buttons in white on the accent's 500\n * tint in the light scheme and in black on its 400 tint in the dark scheme\n * (lighter than the accent, so an accent that passes against black passes\n * there too).\n */\nconst MINIMUM_ACCENT_CONTRAST = 4.5\nconst BUTTON_LABEL: Record<ThemeColorSchemeKey, string> = {light: '#ffffff', dark: '#000000'}\nconst ACCENT_LIGHTNESS_STEP = 0.01\n\nfunction contrastRatio(a: string, b: string): number {\n const [lighter, darker] = [relativeLuminance(a), relativeLuminance(b)].sort((x, y) => y - x)\n\n return (lighter + 0.05) / (darker + 0.05)\n}\n\n/**\n * Makes an accent readable as a button in the given scheme: a light scheme\n * accent is darkened until white text on it reaches AA contrast, a dark\n * scheme accent lightened until black text does — as far as the accent's\n * hue and saturation allow. Accents that already pass come back untouched.\n *\n * @internal\n */\nexport function readableAccent(hex: string, scheme: ThemeColorSchemeKey): string {\n const label = BUTTON_LABEL[scheme]\n const {h, s} = hexToHsl(hex)\n let {l} = hexToHsl(hex)\n let current = hex\n\n while (contrastRatio(current, label) < MINIMUM_ACCENT_CONTRAST) {\n const next = scheme === 'light' ? l - ACCENT_LIGHTNESS_STEP : l + ACCENT_LIGHTNESS_STEP\n\n if (next < 0 || next > 1) break\n\n l = next\n current = hslToHex({h, s, l})\n }\n\n return current\n}\n\n/** The accent a variant gives a scheme, made readable on a button */\nfunction variantAccent(\n palette: ImagePalette,\n variant: ImagePaletteVariant | null,\n scheme: ThemeColorSchemeKey,\n): string | null {\n const swatch =\n (variant ? palette[variant] : null) ??\n palette.vibrant ??\n (scheme === 'light' ? palette.darkVibrant : palette.lightVibrant) ??\n palette.dominant\n\n return swatch === null ? null : readableAccent(swatch, scheme)\n}\n\n/**\n * Derives theme options from an image palette: the accent of both schemes is\n * the swatch of the chosen variant — or, without one, the vibrant swatch,\n * each scheme falling back to the vibrant swatch of its own end of the scale —\n * adjusted until it is readable as a button. The muted swatch becomes the text color, and the light and dark muted\n * swatches tint the stock backgrounds of their scheme — enough to carry the\n * image's tone without giving up a usable canvas. Whatever the palette lacks\n * stays at the theme defaults.\n *\n * @internal\n */\nexport function optionsFromImagePalette(\n palette: ImagePalette,\n variant?: ImagePaletteVariant,\n): BuildThemeOptions {\n const options: BuildThemeOptions = {}\n\n const light = schemeFromPalette(\n variantAccent(palette, variant ?? null, 'light'),\n palette.muted ?? palette.darkMuted,\n palette.lightMuted ?? palette.muted ?? palette.lightVibrant,\n 'light',\n )\n const dark = schemeFromPalette(\n variantAccent(palette, variant ?? null, 'dark'),\n palette.muted ?? palette.lightMuted,\n palette.darkMuted ?? palette.muted ?? palette.darkVibrant,\n 'dark',\n )\n\n if (light) options.light = light\n if (dark) options.dark = dark\n\n return options\n}\n\nfunction schemeFromPalette(\n accent: string | null,\n text: string | null,\n backgroundTint: string | null,\n scheme: 'light' | 'dark',\n): SchemeThemeOptions | null {\n const options: SchemeThemeOptions = {}\n\n if (accent) options.accent = accent\n if (text) options.text = text\n if (backgroundTint) {\n options.background = tint(DEFAULT_BACKGROUND[scheme], backgroundTint, BACKGROUND_TINT[scheme])\n }\n\n return Object.keys(options).length > 0 ? options : null\n}\n\n/**\n * Applies an image palette to existing theme options: the colors the palette\n * provides replace the current ones scheme by scheme, and whatever it does\n * not touch — like the contrast — stays as it is.\n *\n * @internal\n */\nexport function applyImagePalette(\n options: BuildThemeOptions,\n palette: ImagePalette,\n variant?: ImagePaletteVariant,\n): BuildThemeOptions {\n const derived = optionsFromImagePalette(palette, variant)\n const next: BuildThemeOptions = {...options}\n\n for (const scheme of SCHEMES) {\n if (derived[scheme]) next[scheme] = {...options[scheme], ...derived[scheme]}\n }\n\n return next\n}\n\n/**\n * The variant a theme is built around: the one whose swatch is the theme's\n * accent, or `null` when the accent comes from somewhere else.\n *\n * @internal\n */\nexport function currentImageVariant(\n options: BuildThemeOptions,\n palette: ImagePalette,\n): ImagePaletteVariant | null {\n const accent = options.light?.accent\n\n if (!accent) return null\n\n return (\n IMAGE_PALETTE_VARIANTS.find(\n (variant) => palette[variant] !== null && variantAccent(palette, variant, 'light') === accent,\n ) ?? null\n )\n}\n\n/**\n * How much a swatch has going for it as an accent: saturation first, then\n * closeness to mid lightness — the range where an accent reads best.\n */\nfunction interest(hex: string): number {\n const {s, l} = hexToHsl(hex)\n\n return 0.1 + 0.55 * s + 0.35 * (1 - Math.abs(l - 0.5) * 2)\n}\n\n/**\n * Picks a variant for the \"I'm feeling lucky\" button: a random one, weighted\n * by how interesting its swatch is as an accent, and never the one the theme\n * already uses while there are others to pick from.\n *\n * @internal\n */\nexport function pickLuckyVariant(\n palette: ImagePalette,\n options: {exclude?: ImagePaletteVariant | null; random?: () => number} = {},\n): ImagePaletteVariant | null {\n const {exclude = null, random = Math.random} = options\n let candidates = IMAGE_PALETTE_VARIANTS.filter((variant) => palette[variant] !== null)\n\n if (candidates.length > 1) {\n candidates = candidates.filter((variant) => variant !== exclude)\n }\n\n if (candidates.length === 0) return null\n\n const weights = candidates.map((variant) => interest(palette[variant]!))\n const total = weights.reduce((sum, weight) => sum + weight, 0)\n let pick = random() * total\n\n for (const [index, variant] of candidates.entries()) {\n pick -= weights[index]\n\n if (pick < 0) return variant\n }\n\n return candidates.at(-1) ?? null\n}\n\n/** A theme title for the image a palette came from, e.g. `sunset-beach.jpg` → `sunset beach` @internal */\nexport function titleFromFileName(name: string): string {\n return (\n name\n .replace(/\\.[^.]+$/, '')\n .replace(/[-_]+/g, ' ')\n .trim() || 'Image theme'\n )\n}\n","import {Button, ButtonProps, Text} from '@sanity/ui'\nimport {Tooltip} from '@sanity/ui/tooltip'\n\n/**\n * A button with an animated tooltip instead of a `title` attribute. Icon-only\n * buttons take the tooltip as their accessible name.\n *\n * @internal\n */\nexport function TooltipButton(props: ButtonProps & {tooltip: string}) {\n const {tooltip, ...buttonProps} = props\n\n return (\n <Tooltip animate content={<Text size={1}>{tooltip}</Text>} placement=\"bottom\" portal>\n <Button aria-label={buttonProps.text === undefined ? tooltip : undefined} {...buttonProps} />\n </Tooltip>\n )\n}\n","import {ImageIcon} from '@sanity/icons/Image'\nimport {ButtonProps} from '@sanity/ui'\nimport {useRef} from 'react'\n\nimport {TooltipButton} from './TooltipButton'\n\n/**\n * A button that opens the file picker for an image, and hands the chosen file\n * over — the picker is a hidden file input, so nothing is uploaded anywhere.\n *\n * @internal\n */\nexport function ImageFileButton(\n props: {onFile: (file: File) => void; tooltip: string} & Pick<\n ButtonProps,\n 'disabled' | 'loading' | 'mode' | 'padding' | 'text' | 'width'\n >,\n) {\n const {onFile, ...buttonProps} = props\n const inputRef = useRef<HTMLInputElement | null>(null)\n\n return (\n <>\n <TooltipButton {...buttonProps} icon={ImageIcon} onClick={() => inputRef.current?.click()} />\n <input\n accept=\"image/*\"\n hidden\n onChange={(event) => {\n const file = event.currentTarget.files?.[0]\n\n // Picking the same file again should work, so the input forgets it\n event.currentTarget.value = ''\n\n if (file) onFile(file)\n }}\n ref={inputRef}\n type=\"file\"\n />\n </>\n )\n}\n","import {PixelData} from './imagePalette'\n\n/**\n * The longest side the image is scaled down to before its colors are read —\n * a few thousand pixels are plenty for a palette, and keep it instant\n */\nconst SAMPLE_SIZE = 96\n\n/**\n * Reads the pixels of an image file on device: the image is decoded, drawn\n * onto a small canvas and read back, so nothing leaves the browser.\n *\n * @internal\n */\nexport async function readImagePixels(file: Blob): Promise<PixelData> {\n const image = await decodeImage(file)\n\n try {\n const scale = Math.min(1, SAMPLE_SIZE / Math.max(image.width, image.height))\n const width = Math.max(1, Math.round(image.width * scale))\n const height = Math.max(1, Math.round(image.height * scale))\n const canvas = document.createElement('canvas')\n\n canvas.width = width\n canvas.height = height\n\n const context = canvas.getContext('2d', {willReadFrequently: true})\n\n if (!context) throw new Error('Could not draw the image')\n\n context.drawImage(image, 0, 0, width, height)\n\n return {data: context.getImageData(0, 0, width, height).data, width, height}\n } finally {\n // Where `createImageBitmap` is missing, `ImageBitmap` may be too\n if (typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap) image.close()\n }\n}\n\nasync function decodeImage(file: Blob): Promise<ImageBitmap | HTMLImageElement> {\n if (typeof createImageBitmap === 'function') {\n try {\n return await createImageBitmap(file)\n } catch {\n // Some browsers cannot decode every format this way (e.g. SVG) — an\n // image element handles whatever the browser can display\n }\n }\n\n const url = URL.createObjectURL(file)\n\n try {\n return await new Promise<HTMLImageElement>((resolve, reject) => {\n const image = new Image()\n\n image.addEventListener('load', () => resolve(image), {once: true})\n image.addEventListener('error', () => reject(new Error('Could not decode the image')), {\n once: true,\n })\n image.src = url\n })\n } finally {\n URL.revokeObjectURL(url)\n }\n}\n","import {useToast} from '@sanity/ui/toast'\nimport {useCallback, useEffect, useRef, useState} from 'react'\n\nimport {readImagePixels} from './imageFile'\nimport {extractImagePalette, ImagePalette} from './imagePalette'\n\n/**\n * Extracts the palette of image files on device and hands it to `onPalette`,\n * along with the file it came from. Failures end up as a toast.\n *\n * @internal\n */\nexport function useImagePalette(onPalette: (palette: ImagePalette, file: File) => void): {\n busy: boolean\n pickImage: (file: File) => void\n} {\n const toast = useToast()\n const [busy, setBusy] = useState(false)\n const onPaletteRef = useRef(onPalette)\n const mountedRef = useRef(true)\n // Picks are numbered so that a slow earlier image cannot overwrite a later\n // pick once its extraction finishes\n const latestPickRef = useRef(0)\n\n useEffect(() => {\n onPaletteRef.current = onPalette\n }, [onPalette])\n\n useEffect(() => {\n mountedRef.current = true\n\n return () => {\n mountedRef.current = false\n }\n }, [])\n\n const pickImage = useCallback(\n (file: File) => {\n const pick = ++latestPickRef.current\n\n setBusy(true)\n\n // Whether this pick still matters: it is the latest one and the\n // component that asked for it is still around\n const isCurrent = () => mountedRef.current && latestPickRef.current === pick\n\n const extract = async () => {\n const palette = extractImagePalette(await readImagePixels(file))\n\n if (!isCurrent()) return\n\n if (Object.values(palette).every((swatch) => swatch === null)) {\n toast.push({status: 'warning', title: 'Found no colors in that image'})\n } else {\n onPaletteRef.current(palette, file)\n }\n }\n\n extract()\n .catch(() => {\n if (isCurrent()) toast.push({status: 'error', title: 'Could not read that image'})\n })\n .finally(() => {\n if (isCurrent()) setBusy(false)\n })\n },\n [toast],\n )\n\n return {busy, pickImage}\n}\n","import {SparklesIcon} from '@sanity/icons/Sparkles'\nimport {Box, Card, Flex, Stack, Text} from '@sanity/ui'\nimport {Tooltip} from '@sanity/ui/tooltip'\nimport {useEffect, useMemo, useRef, useState} from 'react'\nimport {styled} from 'styled-components'\n\nimport {BuildThemeOptions} from '../theme/options'\nimport {ImageFileButton} from './ImageFileButton'\nimport {\n applyImagePalette,\n currentImageVariant,\n IMAGE_PALETTE_TITLES,\n IMAGE_PALETTE_VARIANTS,\n ImagePalette,\n ImagePaletteKey,\n ImagePaletteVariant,\n} from './imagePalette'\nimport {ThemeThumbnail} from './ThemeThumbnail'\nimport {TooltipButton} from './TooltipButton'\nimport {useImagePalette} from './useImagePalette'\n\n/** The swatches on display: the vibrant family over the muted one */\nconst TILES: ImagePaletteKey[] = [\n 'vibrant',\n 'lightVibrant',\n 'darkVibrant',\n 'muted',\n 'lightMuted',\n 'darkMuted',\n]\n\nconst TILE_SIZE = 24\nconst TILE_GAP = 4\n\n/** The image spans both rows of tiles */\nconst IMAGE_SIZE = TILE_SIZE * 2 + TILE_GAP\n\n/** The variant previews are the theme thumbnails, laid out at this width and scaled down */\nconst VARIANT_WIDTH = 144\n\n/**\n * How many variants show at once: two and a half, so that the cut-off third\n * one gives away that the row scrolls\n */\nconst VISIBLE_VARIANTS = 2.5\nconst VARIANT_GAP = 8\nconst ROW_PADDING = 12\n\n/** The width of one variant, before the row has been measured */\nconst FALLBACK_VARIANT_WIDTH = 58\n\nconst PaletteGrid = styled.div`\n display: grid;\n grid-template-columns: repeat(3, ${TILE_SIZE}px);\n grid-auto-rows: ${TILE_SIZE}px;\n gap: ${TILE_GAP}px;\n\n &[data-with-image='true'] {\n grid-template-columns: ${IMAGE_SIZE}px repeat(3, ${TILE_SIZE}px);\n }\n`\n\nconst ImageTile = styled.img`\n display: block;\n grid-row: span 2;\n width: ${IMAGE_SIZE}px;\n height: ${IMAGE_SIZE}px;\n border-radius: 3px;\n object-fit: cover;\n box-shadow: inset 0 0 0 1px var(--card-border-color);\n`\n\nconst Tile = styled.span`\n display: block;\n border-radius: 3px;\n box-shadow: inset 0 0 0 1px var(--card-border-color);\n\n &[data-empty='true'] {\n background: repeating-linear-gradient(\n -45deg,\n transparent 0 3px,\n var(--card-border-color) 3px 4px\n );\n }\n`\n\n/**\n * The variants scroll sideways and snap into place, bleeding into the card's\n * padding so the row runs from edge to edge\n */\nconst VariantRow = styled.div`\n display: flex;\n gap: ${VARIANT_GAP}px;\n margin: 0 -${ROW_PADDING}px;\n padding: 0 ${ROW_PADDING}px;\n overflow-x: auto;\n scroll-snap-type: x mandatory;\n scroll-padding-left: ${ROW_PADDING}px;\n scrollbar-width: thin;\n`\n\nconst VariantButton = styled.button`\n appearance: none;\n flex: none;\n display: block;\n box-sizing: border-box;\n width: calc(var(--variant-width) + 8px);\n margin: 0;\n padding: 0;\n border: 0;\n background: none;\n color: inherit;\n font: inherit;\n text-align: center;\n cursor: pointer;\n scroll-snap-align: start;\n\n &:focus {\n outline: none;\n }\n`\n\n/**\n * Clips the scaled-down thumbnail to its visual size, with room for the ring\n * — drawn inside the frame, like the theme cards', so the scrolling row\n * never clips it\n */\nconst VariantFrame = styled.span`\n display: block;\n box-sizing: border-box;\n width: calc(var(--variant-width) + 8px);\n height: calc(var(--variant-width) * 9 / 16 + 8px);\n padding: 4px;\n border-radius: 7px;\n overflow: hidden;\n transition: box-shadow 100ms;\n\n ${VariantButton}:hover & {\n box-shadow: inset 0 0 0 2px var(--card-border-color);\n }\n\n ${VariantButton}[aria-pressed='true'] & {\n box-shadow: inset 0 0 0 2px var(--card-focus-ring-color);\n }\n\n ${VariantButton}:focus-visible & {\n outline: 2px solid var(--card-focus-ring-color);\n outline-offset: -2px;\n }\n`\n\nconst VariantScale = styled.span`\n display: block;\n width: ${VARIANT_WIDTH}px;\n transform: scale(var(--variant-scale));\n transform-origin: top left;\n`\n\n/**\n * Takes the colors of a theme from an image, on device: the image is read\n * into a canvas and its palette extracted right there — nothing is uploaded.\n * Shows the image (while the session lasts) next to its palette, offers a\n * theme variant built around each swatch as a small preview, and a lucky\n * button that picks an interesting one.\n *\n * @internal\n */\nexport function ImagePaletteSection(props: {\n imageUrl?: string\n onPalette: (palette: ImagePalette, file: File) => void\n onVariant: (variant: ImagePaletteVariant) => void\n onLucky: () => void\n options: BuildThemeOptions\n palette?: ImagePalette\n}) {\n const {imageUrl, onPalette, onVariant, onLucky, options, palette} = props\n const {busy, pickImage} = useImagePalette(onPalette)\n const [dragging, setDragging] = useState(false)\n // `dragleave` also fires when the pointer crosses into a child, so the\n // highlight tracks how deep the drag is rather than the last event\n const dragDepth = useRef(0)\n const current = palette ? currentImageVariant(options, palette) : null\n const rowRef = useRef<HTMLDivElement | null>(null)\n const [variantWidth, setVariantWidth] = useState(FALLBACK_VARIANT_WIDTH)\n\n // Size the variants to the row, so two and a half of them are in view\n useEffect(() => {\n const row = rowRef.current\n\n if (!row) return undefined\n\n const measure = () => {\n const visible =\n row.clientWidth - ROW_PADDING - (Math.ceil(VISIBLE_VARIANTS) - 1) * VARIANT_GAP\n\n // Each variant is its thumbnail plus the 4px frame on either side\n setVariantWidth(Math.max(24, Math.floor(visible / VISIBLE_VARIANTS) - 8))\n }\n\n measure()\n\n const observer = new ResizeObserver(measure)\n\n observer.observe(row)\n\n return () => observer.disconnect()\n }, [palette])\n\n const variants = useMemo(() => {\n if (!palette) return []\n\n return IMAGE_PALETTE_VARIANTS.flatMap((variant) =>\n palette[variant] === null\n ? []\n : [{variant, options: applyImagePalette(options, palette, variant)}],\n )\n }, [options, palette])\n\n return (\n <Card\n border\n onDragEnter={(event) => {\n event.preventDefault()\n dragDepth.current++\n setDragging(true)\n }}\n onDragLeave={() => {\n dragDepth.current = Math.max(0, dragDepth.current - 1)\n\n if (dragDepth.current === 0) setDragging(false)\n }}\n onDragOver={(event) => {\n event.preventDefault()\n }}\n onDrop={(event) => {\n event.preventDefault()\n dragDepth.current = 0\n setDragging(false)\n\n const file = event.dataTransfer.files[0]\n\n if (file) pickImage(file)\n }}\n padding={3}\n radius={3}\n tone={dragging ? 'primary' : 'default'}\n >\n <Stack gap={3}>\n <Flex align=\"center\" gap={2}>\n <Box flex={1}>\n <Text size={1} weight=\"medium\">\n Image palette\n </Text>\n </Box>\n {palette && (\n <ImageFileButton\n loading={busy}\n mode=\"bleed\"\n onFile={pickImage}\n padding={2}\n tooltip=\"Pick another image\"\n />\n )}\n </Flex>\n\n {palette ? (\n <>\n <PaletteGrid data-with-image={imageUrl !== undefined}>\n {imageUrl && <ImageTile alt=\"\" src={imageUrl} />}\n {TILES.map((key) => {\n const hex = palette[key]\n const title = IMAGE_PALETTE_TITLES[key]\n\n return (\n <Tooltip\n animate\n content={\n <Text size={1}>\n {hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`}\n </Text>\n }\n key={key}\n placement=\"bottom\"\n portal\n >\n <Tile data-empty={hex === null} style={{background: hex ?? undefined}} />\n </Tooltip>\n )\n })}\n </PaletteGrid>\n\n <Stack gap={2}>\n <Text muted size={0}>\n Pick the swatch to build the theme around\n </Text>\n <VariantRow\n ref={rowRef}\n style={{\n ['--variant-width' as string]: `${variantWidth}px`,\n ['--variant-scale' as string]: variantWidth / VARIANT_WIDTH,\n }}\n >\n {variants.map(({variant, options: variantOptions}) => (\n <VariantButton\n aria-pressed={variant === current}\n key={variant}\n onClick={() => onVariant(variant)}\n type=\"button\"\n >\n <VariantFrame>\n <VariantScale>\n <ThemeThumbnail options={variantOptions} />\n </VariantScale>\n </VariantFrame>\n <Box paddingTop={1}>\n <Text\n align=\"center\"\n size={0}\n textOverflow=\"ellipsis\"\n weight={variant === current ? 'medium' : 'regular'}\n >\n {IMAGE_PALETTE_TITLES[variant]}\n </Text>\n </Box>\n </VariantButton>\n ))}\n </VariantRow>\n </Stack>\n\n <TooltipButton\n icon={SparklesIcon}\n mode=\"ghost\"\n onClick={onLucky}\n text=\"I'm feeling lucky\"\n tooltip=\"Build the theme around a swatch that looks interesting\"\n width=\"fill\"\n />\n </>\n ) : (\n <Stack gap={3}>\n <Text muted size={0}>\n Take the accent, text and background colors from an image — read on device, nothing is\n uploaded.\n </Text>\n <ImageFileButton\n loading={busy}\n mode=\"ghost\"\n onFile={pickImage}\n text=\"Pick an image\"\n tooltip=\"Take the colors from an image on your device\"\n width=\"fill\"\n />\n </Stack>\n )}\n </Stack>\n </Card>\n )\n}\n","import {ResetIcon} from '@sanity/icons/Reset'\nimport {TrashIcon} from '@sanity/icons/Trash'\nimport {Badge, Box, Button, Card, Flex, Stack, Text, TextInput} from '@sanity/ui'\nimport {type ThemeColorSchemeKey} from '@sanity/ui/theme'\nimport {useMemo} from 'react'\nimport {useColorSchemeValue} from 'sanity'\nimport {styled} from 'styled-components'\n\nimport {buildPalette, GeneratedColorPalette} from '../theme/buildPalette'\nimport {\n BuildThemeOptions,\n ResolvedSchemeOptions,\n resolveThemeOptions,\n SCHEMES,\n SchemeThemeOptions,\n} from '../theme/options'\nimport {useThemer} from './context'\nimport {\n applyImagePalette,\n currentImageVariant,\n ImagePalette,\n ImagePaletteVariant,\n pickLuckyVariant,\n} from './imagePalette'\nimport {ImagePaletteSection} from './ImagePaletteSection'\nimport {ScrollArea} from './ScrollArea'\nimport {ThemeThumbnail} from './ThemeThumbnail'\nimport {TooltipButton} from './TooltipButton'\n\nconst SCHEME_TITLES: Record<ThemeColorSchemeKey, string> = {\n dark: 'Dark mode',\n light: 'Light mode',\n}\n\nconst SCHEME_OPTION_KEYS: Array<keyof SchemeThemeOptions> = [\n 'accent',\n 'text',\n 'background',\n 'contrast',\n]\n\n/**\n * `<input type=\"color\">` paints the color into a shadow-DOM swatch that brings\n * its own border and padding, which then sits inside ours as a second border.\n * Stripping that chrome leaves the themed border as the only one.\n */\nconst Swatch = styled.input`\n box-sizing: border-box;\n flex: none;\n width: 33px;\n height: 33px;\n padding: 0;\n border: 1px solid var(--card-border-color);\n border-radius: 4px;\n background: none;\n cursor: pointer;\n\n &::-webkit-color-swatch-wrapper {\n padding: 0;\n }\n\n &::-webkit-color-swatch {\n border: none;\n border-radius: 3px;\n }\n\n &::-moz-color-swatch {\n border: none;\n border-radius: 3px;\n }\n`\n\n/** A native range input, themed through `accent-color` */\nconst Range = styled.input`\n display: block;\n width: 100%;\n margin: 0;\n accent-color: var(--card-focus-ring-color);\n`\n\n/**\n * The flow for editing one of the user's own themes: its title, a live\n * preview, and a card per color scheme with the accent/text/background\n * pickers and the contrast slider of that scheme. Every change applies to the\n * whole Studio right away — in the scheme the Studio is showing.\n *\n * @internal\n */\nexport function ThemeEditor(props: {focusTitle: boolean; slug: string}) {\n const {focusTitle, slug} = props\n const {themes, images, send} = useThemer()\n const theme = themes.find((candidate) => candidate.slug === slug && candidate.source === 'custom')\n\n // The machine only enters the edit flow for a listed custom theme, and\n // leaves it as soon as the theme is removed or deleted\n if (!theme) return null\n\n const applyVariant = (variant: ImagePaletteVariant | null) => {\n if (!theme.palette || variant === null) return\n\n send({\n type: 'theme.update',\n slug,\n options: applyImagePalette(theme.options, theme.palette, variant),\n })\n }\n\n return (\n <ThemeEditorForm\n focusTitle={focusTitle}\n imageUrl={images[slug]}\n onDone={() => send({type: 'flow.list'})}\n onLucky={() => {\n if (!theme.palette) return\n\n applyVariant(\n pickLuckyVariant(theme.palette, {\n exclude: currentImageVariant(theme.options, theme.palette),\n }),\n )\n }}\n onOptionsChange={(options) => send({type: 'theme.update', slug, options})}\n onPalette={(palette, file) =>\n send({\n type: 'theme.update',\n slug,\n options: applyImagePalette(theme.options, palette),\n palette,\n imageUrl: URL.createObjectURL(file),\n })\n }\n onRemove={() => send({type: 'theme.remove', slug})}\n onTitleChange={(title) => send({type: 'theme.update', slug, title})}\n onVariant={applyVariant}\n options={theme.options}\n palette={theme.palette}\n title={theme.title}\n />\n )\n}\n\n// Split from `ThemeEditor` so that the derived values below can be hooks\n// while the editor bails out early when the theme is gone\nfunction ThemeEditorForm(props: {\n focusTitle: boolean\n imageUrl?: string\n onDone: () => void\n onLucky: () => void\n onOptionsChange: (options: BuildThemeOptions) => void\n onPalette: (palette: ImagePalette, file: File) => void\n onRemove: () => void\n onTitleChange: (title: string) => void\n onVariant: (variant: ImagePaletteVariant) => void\n options: BuildThemeOptions\n palette?: ImagePalette\n title: string\n}) {\n const {\n focusTitle,\n imageUrl,\n onDone,\n onLucky,\n onOptionsChange,\n onPalette,\n onRemove,\n onTitleChange,\n onVariant,\n options,\n palette,\n title,\n } = props\n // The scheme the Studio is showing, with the appearance setting resolved\n const studioScheme = useColorSchemeValue()\n const resolved = useMemo(() => resolveThemeOptions(options), [options])\n const palettes = useMemo(() => buildPalette(options), [options])\n\n const patchScheme = (scheme: ThemeColorSchemeKey, changes: Partial<SchemeThemeOptions>) => {\n const values: SchemeThemeOptions = {...options[scheme], ...changes}\n\n // Clearing an option means falling back to its default, which is its absence\n for (const key of SCHEME_OPTION_KEYS) {\n if (values[key] === undefined) delete values[key]\n }\n\n const next: BuildThemeOptions = {...options}\n\n if (Object.keys(values).length === 0) {\n delete next[scheme]\n } else {\n next[scheme] = values\n }\n\n onOptionsChange(next)\n }\n\n return (\n <>\n <ScrollArea padding={3}>\n <Stack gap={4}>\n <ThemeThumbnail options={options} />\n\n <Stack gap={2}>\n <Text size={1} weight=\"medium\">\n Title\n </Text>\n <TextInput\n autoFocus={focusTitle}\n fontSize={1}\n onChange={(event) => onTitleChange(event.currentTarget.value)}\n onFocus={focusTitle ? (event) => event.currentTarget.select() : undefined}\n padding={2}\n placeholder=\"Untitled theme\"\n value={title}\n />\n </Stack>\n\n <ImagePaletteSection\n imageUrl={imageUrl}\n onLucky={onLucky}\n onPalette={onPalette}\n onVariant={onVariant}\n options={options}\n palette={palette}\n />\n\n {SCHEMES.map((scheme) => (\n <SchemeCard\n active={scheme === studioScheme}\n key={scheme}\n onChange={(changes) => patchScheme(scheme, changes)}\n options={options[scheme] ?? {}}\n palette={palettes[scheme]}\n resolved={resolved[scheme]}\n scheme={scheme}\n />\n ))}\n </Stack>\n </ScrollArea>\n\n <Card borderTop padding={3}>\n <Flex gap={2}>\n <TooltipButton\n icon={TrashIcon}\n mode=\"ghost\"\n onClick={onRemove}\n text=\"Remove\"\n tone=\"critical\"\n tooltip=\"Remove the theme — it can be restored until it is deleted\"\n />\n <Box flex={1} />\n <Button mode=\"ghost\" onClick={onDone} text=\"Done\" />\n </Flex>\n </Card>\n </>\n )\n}\n\n/**\n * The colors of one scheme, on a card painted in that scheme so that each\n * card shows the colors it edits. The scheme the Studio is showing is marked\n * as active: its changes show up in the Studio right away, the other scheme's\n * only once the appearance setting switches to it.\n */\nfunction SchemeCard(props: {\n active: boolean\n onChange: (changes: Partial<SchemeThemeOptions>) => void\n options: SchemeThemeOptions\n palette: GeneratedColorPalette\n resolved: ResolvedSchemeOptions\n scheme: ThemeColorSchemeKey\n}) {\n const {active, onChange, options, palette, resolved, scheme} = props\n const name = SCHEME_TITLES[scheme]\n\n return (\n <Card border padding={3} radius={3} scheme={scheme} tone=\"default\">\n <Stack gap={4}>\n <Stack gap={2}>\n <Flex align=\"center\" gap={2}>\n <Box flex={1}>\n <Text size={1} weight=\"medium\">\n {name}\n </Text>\n </Box>\n {active && (\n <Badge fontSize={0} tone=\"primary\">\n Active\n </Badge>\n )}\n </Flex>\n <Text muted size={0}>\n {active\n ? 'Shown in the Studio right now'\n : `Shown once the Studio appearance is ${scheme}`}\n </Text>\n </Stack>\n\n <ColorRow\n auto={options.accent === undefined}\n autoLabel=\"default\"\n label={`${name} accent`}\n onChange={(accent) => onChange({accent})}\n onClear={() => onChange({accent: undefined})}\n title=\"Accent\"\n value={resolved.accent}\n />\n <ColorRow\n auto={options.text === undefined}\n label={`${name} text`}\n onChange={(text) => onChange({text})}\n onClear={() => onChange({text: undefined})}\n title=\"Text\"\n value={resolved.text}\n />\n <ColorRow\n adjusted={scheme === 'dark' ? palette.black : palette.white}\n auto={options.background === undefined}\n label={`${name} background`}\n onChange={(background) => onChange({background})}\n onClear={() => onChange({background: undefined})}\n title=\"Background\"\n value={resolved.background}\n />\n\n <Stack gap={3}>\n <Flex align=\"center\" gap={2}>\n <Stack flex={1} gap={2}>\n <Text size={1}>Contrast</Text>\n <Text muted size={0}>\n {resolved.contrast}\n {options.contrast === undefined ? ' · auto' : ''}\n </Text>\n </Stack>\n {options.contrast !== undefined && (\n <TooltipButton\n icon={ResetIcon}\n mode=\"bleed\"\n onClick={() => onChange({contrast: undefined})}\n padding={2}\n tooltip=\"Reset to auto\"\n />\n )}\n </Flex>\n <Range\n aria-label={`${name} contrast`}\n max={100}\n min={15}\n onChange={(event) => onChange({contrast: Number(event.currentTarget.value)})}\n step={1}\n type=\"range\"\n value={resolved.contrast}\n />\n <Text muted size={0}>\n 100 keeps text and borders neutral — lower values blend in the accent\n </Text>\n </Stack>\n </Stack>\n </Card>\n )\n}\n\n/** One color of a scheme: a swatch with its value and an optional reset button */\nfunction ColorRow(props: {\n /** The color the generator actually applied, when it may differ from the input */\n adjusted?: string\n /** Whether the value is derived or a default rather than explicitly set */\n auto?: boolean\n /** What the value is when it is not set: derived (`auto`) or the stock one (`default`) */\n autoLabel?: 'auto' | 'default'\n /** The accessible name of the picker */\n label: string\n onChange: (value: string) => void\n onClear?: () => void\n title: string\n value: string\n}) {\n const {adjusted, auto, autoLabel = 'auto', label, onChange, onClear, title, value} = props\n\n return (\n <Flex align=\"center\" gap={2}>\n <Swatch\n aria-label={label}\n onChange={(event) => onChange(event.currentTarget.value)}\n type=\"color\"\n value={value}\n />\n <Stack flex={1} gap={2} style={{minWidth: 0}}>\n <Text size={1}>{title}</Text>\n <Text muted size={0} textOverflow=\"ellipsis\">\n {value}\n {adjusted !== undefined && adjusted !== value ? ` → ${adjusted}` : ''}\n {auto ? ` · ${autoLabel}` : ''}\n </Text>\n </Stack>\n {onClear && !auto && (\n <TooltipButton\n icon={ResetIcon}\n mode=\"bleed\"\n onClick={onClear}\n padding={2}\n tooltip={`Reset to ${autoLabel}`}\n />\n )}\n </Flex>\n )\n}\n","import {CopyIcon} from '@sanity/icons/Copy'\nimport {EditIcon} from '@sanity/icons/Edit'\nimport {EllipsisHorizontalIcon} from '@sanity/icons/EllipsisHorizontal'\nimport {TrashIcon} from '@sanity/icons/Trash'\nimport {Box, Button, Text} from '@sanity/ui'\nimport {Menu, MenuButton, MenuDivider, MenuItem} from '@sanity/ui/menu'\nimport {Tooltip} from '@sanity/ui/tooltip'\nimport {useState} from 'react'\nimport {styled} from 'styled-components'\n\nimport {useThemer} from './context'\nimport {displayTitle, ThemerTheme} from './themes'\nimport {ThemeThumbnail} from './ThemeThumbnail'\n\nconst Root = styled.div`\n position: relative;\n`\n\n/** A bare button, so that the thumbnail and the title can carry the styling */\nconst PickButton = styled.button`\n appearance: none;\n display: block;\n box-sizing: border-box;\n width: 100%;\n margin: 0;\n padding: 0;\n border: 0;\n background: none;\n color: inherit;\n font: inherit;\n text-align: center;\n cursor: pointer;\n\n &:focus {\n outline: none;\n }\n`\n\n/**\n * Wraps the thumbnail with room for the ring that marks the applied theme —\n * hovering shows a faint ring, the applied theme the focus ring color, and\n * keyboard focus an outline. The rings are drawn inside the frame's padding,\n * so nothing sticks out to be clipped by the scrolling list.\n */\nconst Frame = styled.span`\n display: block;\n padding: 4px;\n border-radius: 10px;\n transition: box-shadow 100ms;\n\n ${PickButton}:hover & {\n box-shadow: inset 0 0 0 2px var(--card-border-color);\n }\n\n ${PickButton}[aria-pressed='true'] & {\n box-shadow: inset 0 0 0 2px var(--card-focus-ring-color);\n }\n\n ${PickButton}:focus-visible & {\n outline: 2px solid var(--card-focus-ring-color);\n outline-offset: -2px;\n }\n`\n\n/**\n * The actions menu sits on the thumbnail's top right corner, like the actions\n * of an image input, and only shows on hover, on keyboard focus, and while it\n * is open — its popover is portaled, so an open menu does not count as focus\n * within\n */\nconst MenuSlot = styled.div`\n position: absolute;\n top: 10px;\n right: 10px;\n opacity: 0;\n transition: opacity 100ms;\n\n ${Root}:hover &,\n ${Root}:focus-within &,\n &[data-visible='true'] {\n opacity: 1;\n }\n`\n\n/**\n * One theme in the list: the floating preview with the title below it, like\n * an appearance option in macOS System Settings. Clicking it applies the\n * theme to the whole Studio; its menu holds the edit, duplicate and remove\n * flows that apply to the theme.\n *\n * @internal\n */\nexport function ThemeCard(props: {active: boolean; theme: ThemerTheme}) {\n const {active, theme} = props\n const {send} = useThemer()\n const [menuOpen, setMenuOpen] = useState(false)\n const title = displayTitle(theme.title)\n\n return (\n <Root>\n <PickButton\n aria-pressed={active}\n onClick={() => send({type: 'theme.pick', slug: theme.slug})}\n type=\"button\"\n >\n <Frame>\n <ThemeThumbnail options={theme.options} />\n </Frame>\n <Box as=\"span\" display=\"block\" paddingTop={2} paddingX={2}>\n <Text\n align=\"center\"\n as=\"span\"\n size={1}\n textOverflow=\"ellipsis\"\n weight={active ? 'medium' : 'regular'}\n >\n {title}\n </Text>\n </Box>\n </PickButton>\n\n {/* The tooltip wraps a span rather than the button: the menu button\n clones its button to wire it up, which a tooltip in between would\n swallow */}\n <MenuSlot data-visible={menuOpen}>\n <Tooltip animate content={<Text size={1}>Show more</Text>} placement=\"bottom\" portal>\n <span style={{display: 'inline-block'}}>\n <MenuButton\n button={\n <Button\n aria-label={`Show more for ${title}`}\n fontSize={1}\n icon={EllipsisHorizontalIcon}\n mode=\"ghost\"\n padding={1}\n radius={2}\n />\n }\n id={`themer-theme-${theme.slug}`}\n menu={\n <Menu>\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => send({type: 'theme.remove', slug: theme.slug})}\n text=\"Remove\"\n tone=\"critical\"\n />\n </>\n )}\n </Menu>\n }\n onClose={() => setMenuOpen(false)}\n onOpen={() => setMenuOpen(true)}\n popover={{animate: true, placement: 'bottom-end', portal: true}}\n />\n </span>\n </Tooltip>\n </MenuSlot>\n </Root>\n )\n}\n","import {AddIcon} from '@sanity/icons/Add'\nimport {RestoreIcon} from '@sanity/icons/Restore'\nimport {Box, Button, Card, Flex, Stack} from '@sanity/ui'\n\nimport {useThemer} from './context'\nimport {ImageFileButton} from './ImageFileButton'\nimport {optionsFromImagePalette, titleFromFileName} from './imagePalette'\nimport {ScrollArea} from './ScrollArea'\nimport {ThemeCard} from './ThemeCard'\nimport {TooltipButton} from './TooltipButton'\nimport {useImagePalette} from './useImagePalette'\n\n/**\n * The flow for picking a theme: one column of theme cards — the configured\n * theme, the presets and the user's own themes — with the entry points to\n * the add and restore flows below.\n *\n * @internal\n */\nexport function ThemeList() {\n const {themes, removed, active, send} = useThemer()\n const {busy, pickImage} = useImagePalette((palette, file) =>\n send({\n type: 'theme.add',\n title: titleFromFileName(file.name),\n options: optionsFromImagePalette(palette),\n palette,\n imageUrl: URL.createObjectURL(file),\n }),\n )\n\n return (\n <>\n <ScrollArea padding={3}>\n <Stack gap={4}>\n {themes.map((theme) => (\n <ThemeCard active={theme.slug === active.slug} key={theme.slug} theme={theme} />\n ))}\n {/* Trails the list rather than sitting in the footer, so the footer\n does not shift when the first theme gets removed */}\n {removed.length > 0 && (\n <Button\n icon={RestoreIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'flow.removed'})}\n text={`Show removed (${removed.length})`}\n width=\"fill\"\n />\n )}\n </Stack>\n </ScrollArea>\n\n <Card borderTop padding={3}>\n <Flex gap={2}>\n <Box flex={1}>\n <TooltipButton\n icon={AddIcon}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.add'})}\n text=\"Add theme\"\n tooltip=\"Add a theme based on the applied one\"\n width=\"fill\"\n />\n </Box>\n <ImageFileButton\n loading={busy}\n mode=\"ghost\"\n onFile={pickImage}\n tooltip=\"Add a theme from the colors of an image\"\n />\n </Flex>\n </Card>\n </>\n )\n}\n","import {BuildThemeOptions, SCHEMES, SchemeThemeOptions} from '../theme/options'\nimport {minimizeOptions} from './options'\n\n/**\n * Serializes theme options into the `buildTheme` call to paste into\n * `sanity.config.ts`, keeping only what differs from the derived defaults in\n * each scheme.\n *\n * Options that boil down to the stock Studio theme need nothing from this\n * package — so they serialize to a bare `buildTheme()` from `@sanity/ui/theme`\n * instead.\n *\n * @internal\n */\nexport function createThemeSnippet(options: BuildThemeOptions): string {\n const minimized = minimizeOptions(options)\n\n if (minimized === null) {\n return \"import {buildTheme} from '@sanity/ui/theme'\\n\\nexport const theme = buildTheme()\\n\"\n }\n\n const lines: string[] = []\n\n for (const scheme of SCHEMES) {\n const values = minimized[scheme]\n\n if (values) lines.push(serializeScheme(scheme, values))\n }\n\n return `import {buildTheme} from '@sanity/themer'\\n\\nexport const theme = buildTheme({\\n${lines.join('\\n')}\\n})\\n`\n}\n\n/** A scheme with up to two options fits on one line, more spread out */\nfunction serializeScheme(scheme: string, values: SchemeThemeOptions): string {\n const entries: string[] = []\n\n if (values.accent !== undefined) entries.push(`accent: '${values.accent}'`)\n if (values.text !== undefined) entries.push(`text: '${values.text}'`)\n if (values.background !== undefined) entries.push(`background: '${values.background}'`)\n if (values.contrast !== undefined) entries.push(`contrast: ${values.contrast}`)\n\n if (entries.length <= 2) {\n return ` ${scheme}: {${entries.join(', ')}},`\n }\n\n return ` ${scheme}: {\\n${entries.map((entry) => ` ${entry},`).join('\\n')}\\n },`\n}\n","import {ClipboardIcon} from '@sanity/icons/Clipboard'\nimport {Box, Button, Card, Dialog, Flex, Stack, Text} from '@sanity/ui'\nimport {Code} from '@sanity/ui/code'\nimport {useToast} from '@sanity/ui/toast'\nimport {registerLanguage} from 'react-refractor'\nimport typescript from 'refractor/typescript'\n\nimport {createThemeSnippet} from './snippet'\nimport {displayTitle, ThemerTheme} from './themes'\n\n// `Code` only highlights languages the surrounding app has registered with\n// react-refractor, and the Studio registers its own set from an async import\n// during startup — a race this dialog keeps losing. Registering the one\n// language the snippet needs keeps it highlighted from the first render.\nregisterLanguage(typescript)\n\n/**\n * Shows the `buildTheme` snippet that makes a theme permanent, in a dialog so\n * that it does not take up sidebar space or make the sidebar scroll as the\n * snippet changes shape with the theme.\n *\n * @internal\n */\nexport function ThemeSnippetDialog(props: {onClose: () => void; theme: ThemerTheme}) {\n const {onClose, theme} = props\n const toast = useToast()\n const snippet = createThemeSnippet(theme.options)\n\n const handleCopy = async () => {\n try {\n await navigator.clipboard.writeText(snippet)\n toast.push({status: 'success', title: 'Copied the theme to the clipboard'})\n } catch {\n toast.push({status: 'error', title: 'Could not copy the theme'})\n }\n }\n\n return (\n <Dialog\n animate\n footer={\n <Flex gap={2} justify=\"flex-end\" padding={3}>\n <Button icon={ClipboardIcon} mode=\"ghost\" onClick={() => void handleCopy()} text=\"Copy\" />\n </Flex>\n }\n header=\"Add to your config\"\n id=\"themer-snippet\"\n onClickOutside={onClose}\n onClose={onClose}\n width={1}\n >\n <Box padding={4}>\n <Stack gap={4}>\n <Text muted size={1}>\n Export this from <code>sanity.config.ts</code> and pass it as the <code>theme</code> of{' '}\n <code>defineConfig</code> to make “{displayTitle(theme.title)}” the Studio theme without\n this tool.\n </Text>\n <Card border overflow=\"auto\" padding={3} radius={2} tone=\"transparent\">\n <Code language=\"ts\" size={1}>\n {snippet}\n </Code>\n </Card>\n </Stack>\n </Box>\n </Dialog>\n )\n}\n","import {ArrowLeftIcon} from '@sanity/icons/ArrowLeft'\nimport {CloseIcon} from '@sanity/icons/Close'\nimport {CodeBlockIcon} from '@sanity/icons/CodeBlock'\nimport {Box, Card, Flex, Text} from '@sanity/ui'\nimport {useState} from 'react'\n\nimport {useThemer} from './context'\nimport {RemovedThemes} from './RemovedThemes'\nimport {ThemeEditor} from './ThemeEditor'\nimport {ThemeList} from './ThemeList'\nimport {ThemeSnippetDialog} from './ThemeSnippetDialog'\nimport {TooltipButton} from './TooltipButton'\n\nconst VIEW_TITLES = {\n list: 'Themer',\n edit: 'Edit theme',\n removed: 'Removed themes',\n} as const\n\n/**\n * The themer sidebar: a header that navigates between the flows, the flow\n * itself — picking a theme, editing one, or restoring removed ones — and the\n * dialog with the `buildTheme` snippet of the applied theme.\n *\n * @internal\n */\nexport function ThemerSidebar() {\n const {active, view, send} = useThemer()\n const [snippetOpen, setSnippetOpen] = useState(false)\n const inList = view.name === 'list'\n\n return (\n <Card height=\"fill\">\n <Flex direction=\"column\" height=\"fill\">\n {/* No bottom border: the tools' own toolbars come in different heights, so a\n line here would never line up with theirs */}\n <Card padding={2}>\n <Flex align=\"center\" gap={1}>\n {!inList && (\n <TooltipButton\n icon={ArrowLeftIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'flow.list'})}\n padding={2}\n tooltip=\"Back to the themes\"\n />\n )}\n <Box flex={1} paddingLeft={inList ? 1 : 0} style={{minWidth: 0}}>\n <Text size={1} textOverflow=\"ellipsis\" weight=\"semibold\">\n {VIEW_TITLES[view.name]}\n </Text>\n </Box>\n {view.name !== 'removed' && (\n <TooltipButton\n icon={CodeBlockIcon}\n mode=\"bleed\"\n onClick={() => setSnippetOpen(true)}\n padding={2}\n tooltip=\"Show the code for the applied theme\"\n />\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'sidebar.close'})}\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>\n </Card>\n\n {view.name === 'list' && <ThemeList />}\n {view.name === 'edit' && <ThemeEditor focusTitle={view.focusTitle} slug={view.slug} />}\n {view.name === 'removed' && <RemovedThemes />}\n </Flex>\n\n {snippetOpen && <ThemeSnippetDialog onClose={() => setSnippetOpen(false)} theme={active} />}\n </Card>\n )\n}\n","import {Box, Flex, Layer} from '@sanity/ui'\nimport {useCallback, useEffect, useRef, useState} from 'react'\nimport {type ActiveToolLayoutProps} from 'sanity'\nimport {styled} from 'styled-components'\n\nimport {useThemer} from './context'\nimport {ThemerSidebar} from './ThemerSidebar'\n\n/**\n * The narrowest the sidebar goes — and its default: narrow enough to leave the\n * studio preview as much room as possible, while the themes stack in a single\n * column of preview cards and the editor fits a swatch next to its label.\n */\nconst MINIMUM_WIDTH = 200\n\n/** Twice the default is as wide as the sidebar goes */\nconst MAXIMUM_WIDTH = MINIMUM_WIDTH * 2\n\n/** How far one arrow key press resizes the sidebar */\nconst KEYBOARD_STEP = 16\n\nconst WIDTH_STORAGE_KEY = 'sanityStudio:themer:width'\n\nfunction clampWidth(width: number): number {\n return Math.min(MAXIMUM_WIDTH, Math.max(MINIMUM_WIDTH, Math.round(width)))\n}\n\nfunction readStoredWidth(): number {\n try {\n const stored = Number(localStorage.getItem(WIDTH_STORAGE_KEY))\n\n return Number.isFinite(stored) && stored > 0 ? clampWidth(stored) : MINIMUM_WIDTH\n } catch {\n return MINIMUM_WIDTH\n }\n}\n\nfunction writeStoredWidth(width: number): void {\n try {\n if (width === MINIMUM_WIDTH) {\n localStorage.removeItem(WIDTH_STORAGE_KEY)\n } else {\n localStorage.setItem(WIDTH_STORAGE_KEY, String(width))\n }\n } catch {\n // Storage can be unavailable (e.g. private browsing) — the width just won't persist\n }\n}\n\n/**\n * The sidebar itself does not clip, so the resize handle can straddle its\n * border — the content is clipped one level down instead\n */\nconst Sidebar = styled(Layer)`\n position: relative;\n flex: none;\n box-sizing: border-box;\n border-left: 1px solid var(--card-border-color);\n`\n\n/**\n * The grab area along the sidebar's left edge. It straddles the border so it\n * is easy to hit, and highlights while hovered, focused or dragged.\n */\nconst ResizeHandle = styled.div`\n position: absolute;\n top: 0;\n bottom: 0;\n left: -3px;\n z-index: 1;\n width: 7px;\n cursor: col-resize;\n touch-action: none;\n\n &::after {\n content: '';\n position: absolute;\n top: 0;\n bottom: 0;\n left: 2px;\n width: 3px;\n background: var(--card-focus-ring-color);\n opacity: 0;\n transition: opacity 100ms;\n }\n\n &:hover::after,\n &:focus-visible::after,\n &[data-dragging='true']::after {\n opacity: 1;\n }\n\n &:focus {\n outline: none;\n }\n`\n\n/**\n * Renders the themer sidebar next to the active tool, so the user can browse\n * around their own studio while tweaking the theme. The sidebar's left edge\n * drags (or arrow-keys) it wider, up to twice its default width — the width\n * sticks between sessions.\n *\n * @internal\n */\nexport function ThemerActiveToolLayout(props: ActiveToolLayoutProps) {\n const {open} = useThemer()\n const [width, setWidth] = useState(readStoredWidth)\n const [dragging, setDragging] = useState(false)\n const drag = useRef<{pointerId: number; startX: number; startWidth: number} | null>(null)\n\n useEffect(() => writeStoredWidth(width), [width])\n\n const handlePointerDown = useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n if (event.button !== 0) return\n\n event.preventDefault()\n event.currentTarget.setPointerCapture(event.pointerId)\n drag.current = {pointerId: event.pointerId, startX: event.clientX, startWidth: width}\n setDragging(true)\n },\n [width],\n )\n\n const handlePointerMove = useCallback((event: React.PointerEvent<HTMLDivElement>) => {\n if (!drag.current || drag.current.pointerId !== event.pointerId) return\n\n // The sidebar sits on the right, so dragging left makes it wider\n setWidth(clampWidth(drag.current.startWidth - (event.clientX - drag.current.startX)))\n }, [])\n\n const handlePointerUp = useCallback((event: React.PointerEvent<HTMLDivElement>) => {\n if (!drag.current || drag.current.pointerId !== event.pointerId) return\n\n drag.current = null\n setDragging(false)\n }, [])\n\n const handleKeyDown = useCallback((event: React.KeyboardEvent<HTMLDivElement>) => {\n const steps: Record<string, number> = {\n ArrowLeft: KEYBOARD_STEP,\n ArrowRight: -KEYBOARD_STEP,\n Home: MAXIMUM_WIDTH,\n End: -MAXIMUM_WIDTH,\n }\n const step = steps[event.key]\n\n if (step === undefined) return\n\n event.preventDefault()\n setWidth((current) => clampWidth(current + step))\n }, [])\n\n return (\n <Flex height=\"fill\" sizing=\"border\">\n <Box flex={1} height=\"fill\" overflow=\"auto\">\n {props.renderDefault(props)}\n </Box>\n\n {open && (\n <Sidebar height=\"fill\" style={{width}} zOffset={100}>\n <ResizeHandle\n aria-label=\"Resize the themer\"\n aria-orientation=\"vertical\"\n aria-valuemax={MAXIMUM_WIDTH}\n aria-valuemin={MINIMUM_WIDTH}\n aria-valuenow={width}\n data-dragging={dragging}\n onDoubleClick={() => setWidth(MINIMUM_WIDTH)}\n onKeyDown={handleKeyDown}\n onPointerCancel={handlePointerUp}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n // oxlint-disable-next-line prefer-tag-over-role -- a window splitter is a focusable, draggable separator, which an hr is not\n role=\"separator\"\n tabIndex={0}\n />\n <Box height=\"fill\" overflow=\"hidden\">\n <ThemerSidebar />\n </Box>\n </Sidebar>\n )}\n </Flex>\n )\n}\n","import {assign, not, setup, SnapshotFrom} from 'xstate'\n\nimport {BuildThemeOptions} from '../theme/options'\nimport {ImagePalette} from './imagePalette'\nimport {\n CONFIG_SLUG,\n createCustomTheme,\n duplicateTitle,\n resolveThemes,\n ThemerState,\n UNTITLED_THEME,\n} from './themes'\n\n/** What the themer machine starts from @internal */\nexport interface ThemerInput {\n /** The theme options the Studio's configured theme was generated from */\n baseOptions: BuildThemeOptions\n /** The persisted state of an earlier session */\n stored: ThemerState\n}\n\n/** @internal */\nexport interface ThemerMachineContext extends ThemerState {\n baseOptions: BuildThemeOptions\n /** The theme open in the editor, while the `flow` is `edit` */\n editing: {\n slug: string\n /** Whether the title input should take focus, for themes that were just created */\n focusTitle: boolean\n } | null\n /**\n * Object URLs of the images that themes took their palette from, by theme\n * slug — kept for the session only, so the image can be shown right after\n * it was picked\n */\n images: Record<string, string>\n}\n\n/** @internal */\nexport type ThemerEvent =\n | {type: 'sidebar.toggle'}\n | {type: 'sidebar.close'}\n /** Applies a theme to the whole Studio */\n | {type: 'theme.pick'; slug: string}\n /**\n * Adds a theme and opens it in the editor — based on the applied theme, or\n * on the given title, options and image palette (e.g. from an image)\n */\n | {\n type: 'theme.add'\n title?: string\n options?: BuildThemeOptions\n palette?: ImagePalette\n imageUrl?: string\n }\n /** Adds a copy of a theme and opens it in the editor */\n | {type: 'theme.duplicate'; slug: string}\n /** Opens one of the user's own themes in the editor */\n | {type: 'theme.edit'; slug: string}\n | {\n type: 'theme.update'\n slug: string\n title?: string\n options?: BuildThemeOptions\n palette?: ImagePalette\n imageUrl?: string\n }\n /** Takes a theme out of the list — it can be restored until it is deleted */\n | {type: 'theme.remove'; slug: string}\n /** Puts a removed theme back in the list */\n | {type: 'theme.restore'; slug: string}\n /** Deletes one of the user's own themes for good */\n | {type: 'theme.delete'; slug: string}\n /** Back to picking a theme */\n | {type: 'flow.list'}\n /** On to restoring removed themes */\n | {type: 'flow.removed'}\n\nfunction themesOf(context: ThemerMachineContext) {\n return resolveThemes(context, context.baseOptions)\n}\n\n/**\n * The state of the themer tool: two parallel regions, whether the `sidebar`\n * is open and which `flow` it is in — picking a theme from the `list`,\n * `edit`ing one of the user's own themes, or restoring `removed` ones. The\n * context carries the persisted state (the applied theme, the user's themes\n * and what was removed) alongside what the flows need.\n *\n * Theme operations are handled in every flow, and the flows leave on their\n * own when they lose their subject: the editor when its theme is removed or\n * deleted, and the removed view when its last theme is restored or deleted.\n *\n * @internal\n */\nexport const themerMachine = setup({\n // XState reads `types` for their types only — the values are never used\n types: {\n // oxlint-disable-next-line no-unsafe-type-assertion -- type-level placeholder\n context: {} as ThemerMachineContext,\n // oxlint-disable-next-line no-unsafe-type-assertion -- type-level placeholder\n events: {} as ThemerEvent,\n // oxlint-disable-next-line no-unsafe-type-assertion -- type-level placeholder\n input: {} as ThemerInput,\n },\n guards: {\n isCustomTheme: ({context}, params: {slug: string}) =>\n themesOf(context).themes.some(\n (theme) => theme.slug === params.slug && theme.source === 'custom',\n ),\n isEditingListedTheme: ({context}) =>\n context.editing !== null &&\n themesOf(context).themes.some(\n (theme) => theme.slug === context.editing?.slug && theme.source === 'custom',\n ),\n hasRemovedThemes: ({context}) => themesOf(context).removed.length > 0,\n },\n actions: {\n pick: assign((_, params: {slug: string}) => ({\n active: params.slug === CONFIG_SLUG ? null : params.slug,\n })),\n add: assign(\n (\n {context},\n params: {\n title?: string\n options?: BuildThemeOptions\n palette?: ImagePalette\n imageUrl?: string\n },\n ) => {\n const theme = createCustomTheme(\n params.title ?? UNTITLED_THEME,\n params.options ?? themesOf(context).active.options,\n params.palette,\n )\n\n return {\n active: theme.slug,\n custom: [...context.custom, theme],\n // A theme named after its image needs no renaming right away\n editing: {slug: theme.slug, focusTitle: params.title === undefined},\n images: params.imageUrl\n ? {...context.images, [theme.slug]: params.imageUrl}\n : context.images,\n }\n },\n ),\n duplicate: assign(({context}, params: {slug: string}) => {\n const {themes, removed} = themesOf(context)\n const source = [...themes, ...removed].find((theme) => theme.slug === params.slug)\n\n if (!source) return {}\n\n const theme = createCustomTheme(duplicateTitle(source.title), source.options, source.palette)\n\n return {\n active: theme.slug,\n custom: [...context.custom, theme],\n editing: {slug: theme.slug, focusTitle: true},\n }\n }),\n startEditing: assign((_, params: {slug: string}) => ({\n active: params.slug,\n editing: {slug: params.slug, focusTitle: false},\n })),\n stopEditing: assign({editing: null}),\n update: assign(\n (\n {context},\n params: {\n slug: string\n title?: string\n options?: BuildThemeOptions\n palette?: ImagePalette\n imageUrl?: string\n },\n ) => ({\n custom: context.custom.map((theme) => {\n if (theme.slug !== params.slug) return theme\n\n // Keep the options identity when only the title changes, so the\n // applied theme is not rebuilt on every keystroke\n return {\n ...theme,\n title: params.title ?? theme.title,\n options: params.options ?? theme.options,\n ...(params.palette ? {palette: params.palette} : {}),\n }\n }),\n images:\n params.imageUrl && context.custom.some((theme) => theme.slug === params.slug)\n ? {...context.images, [params.slug]: params.imageUrl}\n : context.images,\n }),\n ),\n remove: assign(({context}, params: {slug: string}) => {\n if (params.slug === CONFIG_SLUG || context.removed.includes(params.slug)) return {}\n\n return {\n active: context.active === params.slug ? null : context.active,\n removed: [...context.removed, params.slug],\n }\n }),\n restore: assign(({context}, params: {slug: string}) => ({\n removed: context.removed.filter((slug) => slug !== params.slug),\n })),\n delete: assign(({context}, params: {slug: string}) => {\n if (!context.custom.some((theme) => theme.slug === params.slug)) return {}\n\n const {[params.slug]: _deleted, ...images} = context.images\n\n return {\n active: context.active === params.slug ? null : context.active,\n custom: context.custom.filter((theme) => theme.slug !== params.slug),\n removed: context.removed.filter((slug) => slug !== params.slug),\n images,\n }\n }),\n },\n}).createMachine({\n id: 'themer',\n context: ({input}) => ({\n ...input.stored,\n baseOptions: input.baseOptions,\n editing: null,\n images: {},\n }),\n type: 'parallel',\n states: {\n sidebar: {\n initial: 'closed',\n states: {\n closed: {\n on: {'sidebar.toggle': 'open'},\n },\n open: {\n on: {'sidebar.toggle': 'closed', 'sidebar.close': 'closed'},\n },\n },\n },\n flow: {\n initial: 'list',\n on: {\n 'theme.pick': {\n actions: {type: 'pick', params: ({event}) => ({slug: event.slug})},\n },\n 'theme.add': {\n target: '.edit',\n actions: {\n type: 'add',\n params: ({event}) => ({\n title: event.title,\n options: event.options,\n palette: event.palette,\n imageUrl: event.imageUrl,\n }),\n },\n },\n 'theme.duplicate': {\n target: '.edit',\n actions: {type: 'duplicate', params: ({event}) => ({slug: event.slug})},\n },\n 'theme.edit': {\n target: '.edit',\n guard: {type: 'isCustomTheme', params: ({event}) => ({slug: event.slug})},\n actions: {type: 'startEditing', params: ({event}) => ({slug: event.slug})},\n },\n 'theme.update': {\n actions: {\n type: 'update',\n params: ({event}) => ({\n slug: event.slug,\n title: event.title,\n options: event.options,\n palette: event.palette,\n imageUrl: event.imageUrl,\n }),\n },\n },\n 'theme.remove': {\n actions: {type: 'remove', params: ({event}) => ({slug: event.slug})},\n },\n 'theme.restore': {\n actions: {type: 'restore', params: ({event}) => ({slug: event.slug})},\n },\n 'theme.delete': {\n actions: {type: 'delete', params: ({event}) => ({slug: event.slug})},\n },\n 'flow.list': '.list',\n 'flow.removed': {\n target: '.removed',\n guard: 'hasRemovedThemes',\n },\n },\n states: {\n list: {},\n edit: {\n exit: 'stopEditing',\n // The theme being edited can go away — removed from the editor, or\n // duplicated from a theme that does not exist — and then there is\n // nothing left to edit\n always: {guard: not('isEditingListedTheme'), target: 'list'},\n },\n removed: {\n always: {guard: not('hasRemovedThemes'), target: 'list'},\n },\n },\n },\n },\n})\n\n/** @internal */\nexport type ThemerSnapshot = SnapshotFrom<typeof themerMachine>\n\n/** The part of the machine context that is persisted between sessions @internal */\nexport function selectStoredState(snapshot: ThemerSnapshot): ThemerState {\n const {active, custom, removed} = snapshot.context\n\n return {active, custom, removed}\n}\n","import {isColor} from '../lib/mix'\nimport {\n BuildThemeOptions,\n MAXIMUM_CONTRAST,\n MINIMUM_CONTRAST,\n SCHEMES,\n SchemeThemeOptions,\n} from '../theme/options'\nimport {presets} from '../theme/presets'\nimport {IMAGE_PALETTE_KEYS, ImagePalette} from './imagePalette'\nimport {\n CONFIG_SLUG,\n createCustomTheme,\n CustomTheme,\n displayTitle,\n initialThemerState,\n ThemerState,\n} from './themes'\n\nconst STORAGE_KEY = 'sanityStudio:themer:state'\n\n/**\n * Where earlier versions of the tool kept their single draft theme — it is\n * migrated into a custom theme, so a draft survives the upgrade.\n */\nconst LEGACY_STORAGE_KEY = 'sanityStudio:themer:options'\n\n/** The title a draft from before themes had titles is imported under */\nconst LEGACY_DRAFT_TITLE = 'Draft theme'\n\nfunction sanitizeColor(value: unknown): string | null {\n return typeof value === 'string' && isColor(value) ? value.toLowerCase() : null\n}\n\nfunction sanitizeContrast(value: unknown): number | null {\n return typeof value === 'number' && Number.isFinite(value)\n ? Math.min(MAXIMUM_CONTRAST, Math.max(MINIMUM_CONTRAST, value))\n : null\n}\n\nfunction sanitizeSchemeOptions(value: unknown): SchemeThemeOptions {\n const options: SchemeThemeOptions = {}\n\n if (!value || typeof value !== 'object') return options\n\n const accent = sanitizeColor(Reflect.get(value, 'accent'))\n const text = sanitizeColor(Reflect.get(value, 'text'))\n const background = sanitizeColor(Reflect.get(value, 'background'))\n const contrast = sanitizeContrast(Reflect.get(value, 'contrast'))\n\n if (accent) options.accent = accent\n if (text) options.text = text\n if (background) options.background = background\n if (contrast !== null) options.contrast = contrast\n\n return options\n}\n\n/**\n * Options from before they were grouped by scheme: one accent, text color and\n * contrast for both schemes, and a background per scheme. Both schemes get\n * the shared colors, so the theme keeps looking the same.\n */\nfunction sanitizeFlatOptions(value: object): BuildThemeOptions | null {\n const accent = sanitizeColor(Reflect.get(value, 'accent'))\n\n if (!accent) return null\n\n const shared: SchemeThemeOptions = {accent}\n const text = sanitizeColor(Reflect.get(value, 'text'))\n const contrast = sanitizeContrast(Reflect.get(value, 'contrast'))\n\n if (text) shared.text = text\n if (contrast !== null) shared.contrast = contrast\n\n const backgrounds: unknown = Reflect.get(value, 'background')\n const options: BuildThemeOptions = {}\n\n for (const scheme of SCHEMES) {\n const background =\n backgrounds && typeof backgrounds === 'object'\n ? sanitizeColor(Reflect.get(backgrounds, scheme))\n : null\n\n options[scheme] = background ? {...shared, background} : {...shared}\n }\n\n return options\n}\n\nfunction sanitizeOptions(value: unknown): BuildThemeOptions | null {\n if (!value || typeof value !== 'object') return null\n\n // The flat shape always had an accent, the scheme shape never has one at the top\n if ('accent' in value) {\n return sanitizeFlatOptions(value)\n }\n\n const options: BuildThemeOptions = {}\n\n for (const scheme of SCHEMES) {\n const schemeOptions = sanitizeSchemeOptions(Reflect.get(value, scheme))\n\n if (Object.keys(schemeOptions).length > 0) options[scheme] = schemeOptions\n }\n\n return options\n}\n\nfunction isReservedSlug(slug: string): boolean {\n return slug === CONFIG_SLUG || presets.some((preset) => preset.slug === slug)\n}\n\nfunction sanitizeCustomTheme(value: unknown): CustomTheme | null {\n if (!value || typeof value !== 'object') return null\n\n const slug: unknown = Reflect.get(value, 'slug')\n\n if (typeof slug !== 'string' || !slug || isReservedSlug(slug)) return null\n\n const options = sanitizeOptions(Reflect.get(value, 'options'))\n\n if (!options) return null\n\n const title: unknown = Reflect.get(value, 'title')\n const palette = sanitizePalette(Reflect.get(value, 'palette'))\n\n return {\n slug,\n title: displayTitle(typeof title === 'string' ? title : ''),\n options,\n ...(palette ? {palette} : {}),\n }\n}\n\nfunction sanitizePalette(value: unknown): ImagePalette | null {\n if (!value || typeof value !== 'object') return null\n\n const palette: Partial<ImagePalette> = {}\n let swatches = 0\n\n for (const key of IMAGE_PALETTE_KEYS) {\n const swatch = sanitizeColor(Reflect.get(value, key))\n\n palette[key] = swatch\n if (swatch) swatches++\n }\n\n // oxlint-disable-next-line no-unsafe-type-assertion -- the loop assigns every key\n return swatches > 0 ? (palette as ImagePalette) : null\n}\n\nfunction sanitizeState(value: unknown): ThemerState {\n if (!value || typeof value !== 'object') return initialThemerState\n\n const custom: CustomTheme[] = []\n const rawCustom: unknown = Reflect.get(value, 'custom')\n\n if (Array.isArray(rawCustom)) {\n for (const item of rawCustom) {\n const theme = sanitizeCustomTheme(item)\n\n if (theme && !custom.some((existing) => existing.slug === theme.slug)) {\n custom.push(theme)\n }\n }\n }\n\n const removed: string[] = []\n const rawRemoved: unknown = Reflect.get(value, 'removed')\n\n if (Array.isArray(rawRemoved)) {\n for (const slug of rawRemoved) {\n const known =\n typeof slug === 'string' &&\n slug !== CONFIG_SLUG &&\n (presets.some((preset) => preset.slug === slug) ||\n custom.some((theme) => theme.slug === slug))\n\n if (known && !removed.includes(slug)) removed.push(slug)\n }\n }\n\n const rawActive: unknown = Reflect.get(value, 'active')\n\n return {active: typeof rawActive === 'string' ? rawActive : null, custom, removed}\n}\n\nfunction readLegacyState(): ThemerState | null {\n const raw = localStorage.getItem(LEGACY_STORAGE_KEY)\n\n if (!raw) return null\n\n const options = sanitizeOptions(JSON.parse(raw))\n\n if (!options) return null\n\n const theme = createCustomTheme(LEGACY_DRAFT_TITLE, options)\n\n return {active: theme.slug, custom: [theme], removed: []}\n}\n\n/**\n * Restores the themer state from localStorage, so the user's themes survive\n * studio reloads. Falls back to the initial state when nothing usable is\n * stored.\n *\n * @internal\n */\nexport function readStoredState(): ThemerState {\n try {\n if (typeof localStorage === 'undefined') return initialThemerState\n\n const raw = localStorage.getItem(STORAGE_KEY)\n\n if (raw) return sanitizeState(JSON.parse(raw))\n\n return readLegacyState() ?? initialThemerState\n } catch {\n return initialThemerState\n }\n}\n\n/** @internal */\nexport function writeStoredState(state: ThemerState): void {\n try {\n if (typeof localStorage === 'undefined') return\n\n localStorage.setItem(STORAGE_KEY, JSON.stringify(state))\n localStorage.removeItem(LEGACY_STORAGE_KEY)\n } catch {\n // Storage can be unavailable (e.g. private browsing) — themes just won't persist\n }\n}\n","import {ThemeProvider} from '@sanity/ui'\nimport {useActor, useSelector} from '@xstate/react'\nimport {useEffect, useMemo, useRef, useState} from 'react'\nimport {type LayoutProps, useColorSchemeValue} from 'sanity'\n\nimport {buildTheme} from '../theme/buildTheme'\nimport {BuildThemeOptions} from '../theme/options'\nimport {ThemerContext, ThemerContextValue, ThemerView} from './context'\nimport {selectStoredState, ThemerInput, themerMachine, ThemerSnapshot} from './machine'\nimport {readStoredState, writeStoredState} from './storage'\nimport {resolveThemes, ThemerState} from './themes'\n\nfunction sameStoredState(a: ThemerState, b: ThemerState): boolean {\n return a.active === b.active && a.custom === b.custom && a.removed === b.removed\n}\n\nfunction selectView(snapshot: ThemerSnapshot): ThemerView {\n const {editing} = snapshot.context\n\n if (snapshot.matches({flow: 'edit'}) && editing) {\n return {name: 'edit', slug: editing.slug, focusTitle: editing.focusTitle}\n }\n\n if (snapshot.matches({flow: 'removed'})) {\n return {name: 'removed'}\n }\n\n return {name: 'list'}\n}\n\nfunction sameView(a: ThemerView, b: ThemerView): boolean {\n if (a.name !== b.name) return false\n\n return (\n a.name !== 'edit' || b.name !== 'edit' || (a.slug === b.slug && a.focusTitle === b.focusTitle)\n )\n}\n\n/**\n * Wraps the whole Studio so that the theme picked in the themer sidebar\n * applies everywhere while the user browses around, and runs the themer\n * machine that the navbar toggle and the sidebar share: the user's themes,\n * which one is applied, and which flow the sidebar is in.\n *\n * The theme provider inherits the color scheme from the Studio, so the\n * preview follows the appearance setting (light/dark/system) like any other\n * theme.\n *\n * @internal\n */\nexport function ThemerLayout(props: LayoutProps & {baseOptions: BuildThemeOptions}) {\n const {baseOptions, ...layoutProps} = props\n const [input] = useState<ThemerInput>(() => ({baseOptions, stored: readStoredState()}))\n const [snapshot, send, actorRef] = useActor(themerMachine, {input})\n const stored = useSelector(actorRef, selectStoredState, sameStoredState)\n const view = useSelector(actorRef, selectView, sameView)\n const open = snapshot.matches({sidebar: 'open'})\n const {images} = snapshot.context\n\n useEffect(() => writeStoredState(stored), [stored])\n\n // The images live in memory as object URLs — release the ones no theme\n // refers to anymore, and all of them on the way out\n const previousImages = useRef(images)\n\n useEffect(() => {\n const current = new Set(Object.values(images))\n\n for (const url of Object.values(previousImages.current)) {\n if (!current.has(url)) URL.revokeObjectURL(url)\n }\n\n previousImages.current = images\n }, [images])\n\n useEffect(\n () => () => {\n for (const url of Object.values(previousImages.current)) URL.revokeObjectURL(url)\n },\n [],\n )\n\n const {themes, removed, active} = useMemo(\n () => resolveThemes(stored, baseOptions),\n [stored, baseOptions],\n )\n\n // The theme identity must be stable between renders: it feeds the\n // styled-components theme context for the whole Studio, and rebuilding it\n // would re-render everything. Keying on the options object keeps it stable\n // across unrelated changes, like renaming a theme.\n const activeOptions = active.source === 'config' ? null : active.options\n const theme = useMemo(\n () => (activeOptions === null ? null : buildTheme(activeOptions)),\n [activeOptions],\n )\n\n // The Studio paints the body with its configured theme from above this\n // layout, and Safari tints its chrome from the body — so the applied theme\n // has to reach the body too, in the scheme the Studio is showing\n const scheme = useColorSchemeValue()\n\n useEffect(() => {\n const background = theme?.v2?.color[scheme].default.bg\n\n if (background === undefined) return undefined\n\n const {style} = document.body\n const previous = style.backgroundColor\n\n style.backgroundColor = background\n\n return () => {\n style.backgroundColor = previous\n }\n }, [scheme, theme])\n\n const context = useMemo<ThemerContextValue>(\n () => ({baseOptions, themes, removed, active, images, view, open, send}),\n [active, baseOptions, images, open, removed, send, themes, view],\n )\n\n return (\n <ThemerContext.Provider value={context}>\n {theme === null ? (\n layoutProps.renderDefault(layoutProps)\n ) : (\n <ThemeProvider theme={theme}>{layoutProps.renderDefault(layoutProps)}</ThemeProvider>\n )}\n </ThemerContext.Provider>\n )\n}\n","import {ColorWheelIcon} from '@sanity/icons/ColorWheel'\nimport {Button, Text} from '@sanity/ui'\nimport {Tooltip} from '@sanity/ui/tooltip'\nimport {type NavbarProps} from 'sanity'\n\nimport {useThemer} from './context'\n\nfunction ThemerNavbarButton() {\n const {open, send} = useThemer()\n\n return (\n <Tooltip animate content={<Text size={1}>Themer</Text>} portal>\n <Button\n aria-label=\"Themer\"\n icon={ColorWheelIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'sidebar.toggle'})}\n // The Studio's own navbar buttons go through a wrapper that pins them\n // to this padding, where `@sanity/ui` defaults to a roomier 3\n padding={2}\n selected={open}\n />\n </Tooltip>\n )\n}\n\n/**\n * Adds the toggle that opens and closes the themer sidebar to the Studio\n * navbar — an icon button with a tooltip in the top bar (like the Tasks\n * toggle), and a regular titled action in the narrow-screen sidebar menu.\n *\n * @internal\n */\nexport function ThemerNavbar(props: NavbarProps) {\n const {open, send} = useThemer()\n\n return props.renderDefault({\n ...props,\n __internal_actions: [\n ...(props.__internal_actions ?? []),\n {\n location: 'topbar',\n name: 'themer-topbar',\n render: () => <ThemerNavbarButton />,\n },\n {\n icon: ColorWheelIcon,\n location: 'sidebar',\n name: 'themer-sidebar',\n onAction: () => send({type: 'sidebar.toggle'}),\n selected: open,\n title: 'Themer',\n },\n ],\n })\n}\n","import {definePlugin, type LayoutProps} from 'sanity'\n\nimport {BuildThemeOptions} from '../theme/options'\nimport {ThemerActiveToolLayout} from './ThemerActiveToolLayout'\nimport {ThemerLayout} from './ThemerLayout'\nimport {ThemerNavbar} from './ThemerNavbar'\n\n/**\n * Options for the {@link themerTool} plugin.\n *\n * This is experimental and may change or be removed in any release without\n * notice — use at your own risk.\n *\n * @alpha\n */\nexport interface ThemerToolOptions {\n /**\n * The `buildTheme` options that the Studio's configured theme was generated\n * from — the themer starts editing from these, so pass the same object that\n * the `theme` in the Studio config uses:\n *\n * ```ts\n * const config: BuildThemeOptions = {light: {accent: '#1cb485'}, dark: {accent: '#22fca8'}}\n *\n * export default defineConfig({\n * theme: buildTheme(config),\n * plugins: [themerTool({config})],\n * })\n * ```\n */\n config?: BuildThemeOptions\n}\n\n/**\n * A Studio plugin that adds a themer sidebar for `buildTheme` themes: a\n * navbar toggle opens the sidebar next to the active tool, with a list of\n * themes — the configured theme, the presets and your own — each previewed\n * as a tiny Studio in both color schemes. Picking one applies it live to the\n * whole Studio while you browse around; your own themes can be edited with\n * accent/text/background pickers and a contrast slider per scheme, added,\n * duplicated, given the colors of an image (its palette is read on device),\n * removed and restored, and a dialog shows the `buildTheme` snippet that\n * makes the applied theme permanent. Toggle between light and dark mode with\n * the regular appearance menu — the preview follows it.\n *\n * ```ts\n * import {themerTool} from '@sanity/themer/tool'\n * import {defineConfig} from 'sanity'\n *\n * export default defineConfig({\n * plugins: [themerTool()],\n * // ...rest of the config\n * })\n * ```\n *\n * This is experimental and may change or be removed in any release without\n * notice — use at your own risk.\n *\n * @alpha\n */\nexport const themerTool = definePlugin<ThemerToolOptions | void>((options) => {\n // No options generate the stock theme, which is what a Studio without a\n // `theme` in its config gets\n const baseOptions = options?.config ?? {}\n\n function ThemerLayoutWithOptions(props: LayoutProps) {\n return <ThemerLayout {...props} baseOptions={baseOptions} />\n }\n\n return {\n name: '@sanity/themer/tool',\n studio: {\n components: {\n layout: ThemerLayoutWithOptions,\n navbar: ThemerNavbar,\n activeToolLayout: ThemerActiveToolLayout,\n },\n },\n }\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tool.js","names":["() => {\n const element = ref.current\n\n if (!element) return undefined\n\n // The reserved gutter is the part of the box the content cannot use\n const measure = () => setGutter(element.offsetWidth - element.clientWidth)\n\n measure()\n\n const observer = new ResizeObserver(measure)\n\n observer.observe(element)\n\n return () => observer.disconnect()\n }","[]","{\n left: space[padding],\n right: Math.max(0, space[padding] - gutter),\n bottom: space[padding],\n }","space[padding]","Math.max(0, space[padding] - gutter)","useMemo<ScrollAreaInset>(\n () => ({\n left: space[padding],\n right: Math.max(0, space[padding] - gutter),\n bottom: space[padding],\n }),\n [gutter, padding, space],\n )","{paddingRight: inset.right}","right","<ScrollAreaInsetContext.Provider value={inset}>{children}</ScrollAreaInsetContext.Provider>","ScrollAreaInsetContext.Provider","<Root flex={1} overflow=\"auto\" padding={padding} ref={ref} style={{paddingRight: inset.right}}>\n <ScrollAreaInsetContext.Provider value={inset}>{children}</ScrollAreaInsetContext.Provider>\n </Root>","{marginLeft: -left, marginRight: -right, marginBottom: -bottom}","-left","-right","-bottom","<div style={{marginLeft: -left, marginRight: -right, marginBottom: -bottom}}>\n {props.children}\n </div>","children","buildTheme(options)","useMemo(() => buildTheme(options), [options])","<MockStudio />","<ThemeProvider scheme=\"light\" theme={theme} tone=\"default\">\n <MockStudio />\n </ThemeProvider>","<MockStudio style={{clipPath: DARK_CLIP_PATH}} />","<ThemeProvider scheme=\"dark\" theme={theme} tone=\"default\">\n <MockStudio style={{clipPath: DARK_CLIP_PATH}} />\n </ThemeProvider>","<Root aria-hidden>\n <ThemeProvider scheme=\"light\" theme={theme} tone=\"default\">\n <MockStudio />\n </ThemeProvider>\n <ThemeProvider scheme=\"dark\" theme={theme} tone=\"default\">\n <MockStudio style={{clipPath: DARK_CLIP_PATH}} />\n </ThemeProvider>\n </Root>","(width: string, background: string): React.CSSProperties => ({\n flex: 'none',\n width,\n height: LINE,\n borderRadius: LINE / 2,\n background,\n })","{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n flexDirection: 'column',\n background: color.bg,\n ...props.style,\n }","bg","style","{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n gap: 5,\n height: '18%',\n padding: '0 5%',\n borderBottom: border,\n }","<div style={{flex: 'none', width: 8, height: 8, borderRadius: 2, background: primary.bg}} />","<div style={line('22%', color.fg)} />","fg","<div style={{flex: 1}} />","<div\n style={{\n flex: 'none',\n width: 8,\n height: 8,\n borderRadius: '50%',\n background: color.avatar.blue.bg,\n }}\n />","<div\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n gap: 5,\n height: '18%',\n padding: '0 5%',\n borderBottom: border,\n }}\n >\n <div style={{flex: 'none', width: 8, height: 8, borderRadius: 2, background: primary.bg}} />\n <div style={line('22%', color.fg)} />\n <div style={{flex: 1}} />\n <div\n style={{\n flex: 'none',\n width: 8,\n height: 8,\n borderRadius: '50%',\n background: color.avatar.blue.bg,\n }}\n />\n </div>","{flex: 1, display: 'flex', minHeight: 0}","{\n flex: 'none',\n display: 'flex',\n flexDirection: 'column',\n width: '36%',\n borderRight: border,\n }","LIST_ROWS.map((width, index) => {\n const isSelected = index === 1\n\n return (\n <div\n key={width}\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n height: '18%',\n padding: '0 9%',\n background: isSelected ? selected.bg : undefined,\n }}\n >\n <div style={line(width, isSelected ? selected.fg : color.muted.fg)} />\n </div>\n )\n })","muted","selected.bg","width","<div\n style={{\n flex: 'none',\n display: 'flex',\n flexDirection: 'column',\n width: '36%',\n borderRight: border,\n }}\n >\n {LIST_ROWS.map((width, index) => {\n const isSelected = index === 1\n\n return (\n <div\n key={width}\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n height: '18%',\n padding: '0 9%',\n background: isSelected ? selected.bg : undefined,\n }}\n >\n <div style={line(width, isSelected ? selected.fg : color.muted.fg)} />\n </div>\n )\n })}\n </div>","{flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0}","{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n gap: 7,\n padding: '9% 8% 0',\n minHeight: 0,\n overflow: 'hidden',\n }","<div style={line('45%', color.fg)} />","FIELDS.map((width) => (\n <div\n key={width}\n style={{flex: 'none', display: 'flex', flexDirection: 'column', gap: 3}}\n >\n <div style={line(width, color.muted.fg)} />\n <div\n style={{\n height: 12,\n borderRadius: 3,\n background: input.bg,\n boxShadow: `inset 0 0 0 1px ${input.border}`,\n }}\n />\n </div>\n ))","input.border","<div\n style={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n gap: 7,\n padding: '9% 8% 0',\n minHeight: 0,\n overflow: 'hidden',\n }}\n >\n <div style={line('45%', color.fg)} />\n {FIELDS.map((width) => (\n <div\n key={width}\n style={{flex: 'none', display: 'flex', flexDirection: 'column', gap: 3}}\n >\n <div style={line(width, color.muted.fg)} />\n <div\n style={{\n height: 12,\n borderRadius: 3,\n background: input.bg,\n boxShadow: `inset 0 0 0 1px ${input.border}`,\n }}\n />\n </div>\n ))}\n </div>","{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n height: '20%',\n padding: '0 8%',\n borderTop: border,\n }","<div style={{width: '32%', height: '50%', borderRadius: 3, background: primary.bg}} />","<div\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n height: '20%',\n padding: '0 8%',\n borderTop: border,\n }}\n >\n <div style={{width: '32%', height: '50%', borderRadius: 3, background: primary.bg}} />\n </div>","<div style={{flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0}}>\n <div\n style={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n gap: 7,\n padding: '9% 8% 0',\n minHeight: 0,\n overflow: 'hidden',\n }}\n >\n <div style={line('45%', color.fg)} />\n {FIELDS.map((width) => (\n <div\n key={width}\n style={{flex: 'none', display: 'flex', flexDirection: 'column', gap: 3}}\n >\n <div style={line(width, color.muted.fg)} />\n <div\n style={{\n height: 12,\n borderRadius: 3,\n background: input.bg,\n boxShadow: `inset 0 0 0 1px ${input.border}`,\n }}\n />\n </div>\n ))}\n </div>\n <div\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n height: '20%',\n padding: '0 8%',\n borderTop: border,\n }}\n >\n <div style={{width: '32%', height: '50%', borderRadius: 3, background: primary.bg}} />\n </div>\n </div>","<div style={{flex: 1, display: 'flex', minHeight: 0}}>\n <div\n style={{\n flex: 'none',\n display: 'flex',\n flexDirection: 'column',\n width: '36%',\n borderRight: border,\n }}\n >\n {LIST_ROWS.map((width, index) => {\n const isSelected = index === 1\n\n return (\n <div\n key={width}\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n height: '18%',\n padding: '0 9%',\n background: isSelected ? selected.bg : undefined,\n }}\n >\n <div style={line(width, isSelected ? selected.fg : color.muted.fg)} />\n </div>\n )\n })}\n </div>\n\n <div style={{flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0}}>\n <div\n style={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n gap: 7,\n padding: '9% 8% 0',\n minHeight: 0,\n overflow: 'hidden',\n }}\n >\n <div style={line('45%', color.fg)} />\n {FIELDS.map((width) => (\n <div\n key={width}\n style={{flex: 'none', display: 'flex', flexDirection: 'column', gap: 3}}\n >\n <div style={line(width, color.muted.fg)} />\n <div\n style={{\n height: 12,\n borderRadius: 3,\n background: input.bg,\n boxShadow: `inset 0 0 0 1px ${input.border}`,\n }}\n />\n </div>\n ))}\n </div>\n <div\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n height: '20%',\n padding: '0 8%',\n borderTop: border,\n }}\n >\n <div style={{width: '32%', height: '50%', borderRadius: 3, background: primary.bg}} />\n </div>\n </div>\n </div>","<div\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n flexDirection: 'column',\n background: color.bg,\n ...props.style,\n }}\n >\n <div\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n gap: 5,\n height: '18%',\n padding: '0 5%',\n borderBottom: border,\n }}\n >\n <div style={{flex: 'none', width: 8, height: 8, borderRadius: 2, background: primary.bg}} />\n <div style={line('22%', color.fg)} />\n <div style={{flex: 1}} />\n <div\n style={{\n flex: 'none',\n width: 8,\n height: 8,\n borderRadius: '50%',\n background: color.avatar.blue.bg,\n }}\n />\n </div>\n\n <div style={{flex: 1, display: 'flex', minHeight: 0}}>\n <div\n style={{\n flex: 'none',\n display: 'flex',\n flexDirection: 'column',\n width: '36%',\n borderRight: border,\n }}\n >\n {LIST_ROWS.map((width, index) => {\n const isSelected = index === 1\n\n return (\n <div\n key={width}\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n height: '18%',\n padding: '0 9%',\n background: isSelected ? selected.bg : undefined,\n }}\n >\n <div style={line(width, isSelected ? selected.fg : color.muted.fg)} />\n </div>\n )\n })}\n </div>\n\n <div style={{flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0}}>\n <div\n style={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n gap: 7,\n padding: '9% 8% 0',\n minHeight: 0,\n overflow: 'hidden',\n }}\n >\n <div style={line('45%', color.fg)} />\n {FIELDS.map((width) => (\n <div\n key={width}\n style={{flex: 'none', display: 'flex', flexDirection: 'column', gap: 3}}\n >\n <div style={line(width, color.muted.fg)} />\n <div\n style={{\n height: 12,\n borderRadius: 3,\n background: input.bg,\n boxShadow: `inset 0 0 0 1px ${input.border}`,\n }}\n />\n </div>\n ))}\n </div>\n <div\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n height: '20%',\n padding: '0 8%',\n borderTop: border,\n }}\n >\n <div style={{width: '32%', height: '50%', borderRadius: 3, background: primary.bg}} />\n </div>\n </div>\n </div>\n </div>","<Card overflow=\"hidden\" radius={3} shadow={1}>\n <ThemePreview options={props.options} />\n </Card>","options","removed.map((theme) => (\n <Stack gap={3} key={theme.slug}>\n <Box style={{opacity: 0.6}}>\n <ThemeThumbnail options={theme.options} />\n </Box>\n <Text align=\"center\" size={1} textOverflow=\"ellipsis\">\n {displayTitle(theme.title)}\n </Text>\n <Flex gap={2} justify=\"center\">\n <Button\n fontSize={1}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.restore', slug: theme.slug})}\n padding={2}\n text=\"Restore\"\n />\n {theme.source === 'custom' && (\n <Button\n fontSize={1}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.delete', slug: theme.slug})}\n padding={2}\n text=\"Delete\"\n tone=\"critical\"\n />\n )}\n </Flex>\n </Stack>\n ))","(theme) => (\n <Stack gap={3} key={theme.slug}>\n <Box style={{opacity: 0.6}}>\n <ThemeThumbnail options={theme.options} />\n </Box>\n <Text align=\"center\" size={1} textOverflow=\"ellipsis\">\n {displayTitle(theme.title)}\n </Text>\n <Flex gap={2} justify=\"center\">\n <Button\n fontSize={1}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.restore', slug: theme.slug})}\n padding={2}\n text=\"Restore\"\n />\n {theme.source === 'custom' && (\n <Button\n fontSize={1}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.delete', slug: theme.slug})}\n padding={2}\n text=\"Delete\"\n tone=\"critical\"\n />\n )}\n </Flex>\n </Stack>\n )","<ScrollArea padding={3}>\n <Stack gap={5}>\n {removed.map((theme) => (\n <Stack gap={3} key={theme.slug}>\n <Box style={{opacity: 0.6}}>\n <ThemeThumbnail options={theme.options} />\n </Box>\n <Text align=\"center\" size={1} textOverflow=\"ellipsis\">\n {displayTitle(theme.title)}\n </Text>\n <Flex gap={2} justify=\"center\">\n <Button\n fontSize={1}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.restore', slug: theme.slug})}\n padding={2}\n text=\"Restore\"\n />\n {theme.source === 'custom' && (\n <Button\n fontSize={1}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.delete', slug: theme.slug})}\n padding={2}\n text=\"Delete\"\n tone=\"critical\"\n />\n )}\n </Flex>\n </Stack>\n ))}\n </Stack>\n </ScrollArea>","<Text size={1}>{tooltip}</Text>","<Button aria-label={buttonProps.text === undefined ? tooltip : undefined} {...buttonProps} />","buttonProps.text === undefined ? tooltip : undefined","<Tooltip animate content={<Text size={1}>{tooltip}</Text>} placement=\"bottom\" portal>\n <Button aria-label={buttonProps.text === undefined ? tooltip : undefined} {...buttonProps} />\n </Tooltip>","() => inputRef.current?.click()","<TooltipButton {...buttonProps} icon={ImageIcon} onClick={() => inputRef.current?.click()} />","<input\n accept=\"image/*\"\n hidden\n onChange={(event) => {\n const file = event.currentTarget.files?.[0]\n\n // Picking the same file again should work, so the input forgets it\n event.currentTarget.value = ''\n\n if (file) onFile(file)\n }}\n ref={inputRef}\n type=\"file\"\n />","<>\n <TooltipButton {...buttonProps} icon={ImageIcon} onClick={() => inputRef.current?.click()} />\n <input\n accept=\"image/*\"\n hidden\n onChange={(event) => {\n const file = event.currentTarget.files?.[0]\n\n // Picking the same file again should work, so the input forgets it\n event.currentTarget.value = ''\n\n if (file) onFile(file)\n }}\n ref={inputRef}\n type=\"file\"\n />\n </>","() => {\n onPaletteRef.current = onPalette\n }","[onPalette]","() => {\n mountedRef.current = true\n\n return () => {\n mountedRef.current = false\n }\n }","[]","(file: File) => {\n const pick = ++latestPickRef.current\n\n setBusy(true)\n\n // Whether this pick still matters: it is the latest one and the\n // component that asked for it is still around\n const isCurrent = () => mountedRef.current && latestPickRef.current === pick\n\n const extract = async () => {\n const palette = extractImagePalette(await readImagePixels(file))\n\n if (!isCurrent()) return\n\n if (Object.values(palette).every((swatch) => swatch === null)) {\n toast.push({status: 'warning', title: 'Found no colors in that image'})\n } else {\n onPaletteRef.current(palette, file)\n }\n }\n\n extract()\n .catch(() => {\n if (isCurrent()) toast.push({status: 'error', title: 'Could not read that image'})\n })\n .finally(() => {\n if (isCurrent()) setBusy(false)\n })\n }","(swatch) => swatch === null","useCallback(\n (file: File) => {\n const pick = ++latestPickRef.current\n\n setBusy(true)\n\n // Whether this pick still matters: it is the latest one and the\n // component that asked for it is still around\n const isCurrent = () => mountedRef.current && latestPickRef.current === pick\n\n const extract = async () => {\n const palette = extractImagePalette(await readImagePixels(file))\n\n if (!isCurrent()) return\n\n if (Object.values(palette).every((swatch) => swatch === null)) {\n toast.push({status: 'warning', title: 'Found no colors in that image'})\n } else {\n onPaletteRef.current(palette, file)\n }\n }\n\n extract()\n .catch(() => {\n if (isCurrent()) toast.push({status: 'error', title: 'Could not read that image'})\n })\n .finally(() => {\n if (isCurrent()) setBusy(false)\n })\n },\n [toast],\n )","{busy, pickImage}","palette ? currentImageVariant(options, palette) : null","() => {\n const row = rowRef.current\n\n if (!row) return undefined\n\n const measure = () => {\n const visible =\n row.clientWidth - ROW_PADDING - (Math.ceil(VISIBLE_VARIANTS) - 1) * VARIANT_GAP\n\n // Each variant is its thumbnail plus the 4px frame on either side\n setVariantWidth(Math.max(24, Math.floor(visible / VISIBLE_VARIANTS) - 8))\n }\n\n measure()\n\n const observer = new ResizeObserver(measure)\n\n observer.observe(row)\n\n return () => observer.disconnect()\n }","[palette]","useMemo(() => {\n if (!palette) return []\n\n return IMAGE_PALETTE_VARIANTS.flatMap((variant) =>\n palette[variant] === null\n ? []\n : [{variant, options: applyImagePalette(options, palette, variant)}],\n )\n }, [options, palette])","[]","IMAGE_PALETTE_VARIANTS.flatMap((variant) =>\n palette[variant] === null\n ? []\n : [{variant, options: applyImagePalette(options, palette, variant)}],\n )","(event) => {\n event.preventDefault()\n dragDepth.current++\n setDragging(true)\n }","() => {\n dragDepth.current = Math.max(0, dragDepth.current - 1)\n\n if (dragDepth.current === 0) setDragging(false)\n }","(event) => {\n event.preventDefault()\n dragDepth.current = 0\n setDragging(false)\n\n const file = event.dataTransfer.files[0]\n\n if (file) pickImage(file)\n }","event","<Box flex={1}>\n <Text size={1} weight=\"medium\">\n Image palette\n </Text>\n </Box>","palette && (\n <ImageFileButton\n loading={busy}\n mode=\"bleed\"\n onFile={pickImage}\n padding={2}\n tooltip=\"Pick another image\"\n />\n )","<Flex align=\"center\" gap={2}>\n <Box flex={1}>\n <Text size={1} weight=\"medium\">\n Image palette\n </Text>\n </Box>\n {palette && (\n <ImageFileButton\n loading={busy}\n mode=\"bleed\"\n onFile={pickImage}\n padding={2}\n tooltip=\"Pick another image\"\n />\n )}\n </Flex>","palette ? (\n <>\n <PaletteGrid data-with-image={imageUrl !== undefined}>\n {imageUrl && <ImageTile alt=\"\" src={imageUrl} />}\n {TILES.map((key) => {\n const hex = palette[key]\n const title = IMAGE_PALETTE_TITLES[key]\n\n return (\n <Tooltip\n animate\n content={\n <Text size={1}>\n {hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`}\n </Text>\n }\n key={key}\n placement=\"bottom\"\n portal\n >\n <Tile data-empty={hex === null} style={{background: hex ?? undefined}} />\n </Tooltip>\n )\n })}\n </PaletteGrid>\n\n <Stack gap={2}>\n <Text muted size={0}>\n Pick the swatch to build the theme around\n </Text>\n <VariantRow\n ref={rowRef}\n style={{\n ['--variant-width' as string]: `${variantWidth}px`,\n ['--variant-scale' as string]: variantWidth / VARIANT_WIDTH,\n }}\n >\n {variants.map(({variant, options: variantOptions}) => (\n <VariantButton\n aria-pressed={variant === current}\n key={variant}\n onClick={() => onVariant(variant)}\n type=\"button\"\n >\n <VariantFrame>\n <VariantScale>\n <ThemeThumbnail options={variantOptions} />\n </VariantScale>\n </VariantFrame>\n <Box paddingTop={1}>\n <Text\n align=\"center\"\n size={0}\n textOverflow=\"ellipsis\"\n weight={variant === current ? 'medium' : 'regular'}\n >\n {IMAGE_PALETTE_TITLES[variant]}\n </Text>\n </Box>\n </VariantButton>\n ))}\n </VariantRow>\n </Stack>\n\n <TooltipButton\n icon={SparklesIcon}\n mode=\"ghost\"\n onClick={onLucky}\n text=\"I'm feeling lucky\"\n tooltip=\"Build the theme around a swatch that looks interesting\"\n width=\"fill\"\n />\n </>\n ) : (\n <Stack gap={3}>\n <Text muted size={0}>\n Take the accent, text and background colors from an image — read on device, nothing is\n uploaded.\n </Text>\n <ImageFileButton\n loading={busy}\n mode=\"ghost\"\n onFile={pickImage}\n text=\"Pick an image\"\n tooltip=\"Take the colors from an image on your device\"\n width=\"fill\"\n />\n </Stack>\n )","{variant, options: variantOptions}","variant","<Stack gap={3}>\n <Flex align=\"center\" gap={2}>\n <Box flex={1}>\n <Text size={1} weight=\"medium\">\n Image palette\n </Text>\n </Box>\n {palette && (\n <ImageFileButton\n loading={busy}\n mode=\"bleed\"\n onFile={pickImage}\n padding={2}\n tooltip=\"Pick another image\"\n />\n )}\n </Flex>\n\n {palette ? (\n <>\n <PaletteGrid data-with-image={imageUrl !== undefined}>\n {imageUrl && <ImageTile alt=\"\" src={imageUrl} />}\n {TILES.map((key) => {\n const hex = palette[key]\n const title = IMAGE_PALETTE_TITLES[key]\n\n return (\n <Tooltip\n animate\n content={\n <Text size={1}>\n {hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`}\n </Text>\n }\n key={key}\n placement=\"bottom\"\n portal\n >\n <Tile data-empty={hex === null} style={{background: hex ?? undefined}} />\n </Tooltip>\n )\n })}\n </PaletteGrid>\n\n <Stack gap={2}>\n <Text muted size={0}>\n Pick the swatch to build the theme around\n </Text>\n <VariantRow\n ref={rowRef}\n style={{\n ['--variant-width' as string]: `${variantWidth}px`,\n ['--variant-scale' as string]: variantWidth / VARIANT_WIDTH,\n }}\n >\n {variants.map(({variant, options: variantOptions}) => (\n <VariantButton\n aria-pressed={variant === current}\n key={variant}\n onClick={() => onVariant(variant)}\n type=\"button\"\n >\n <VariantFrame>\n <VariantScale>\n <ThemeThumbnail options={variantOptions} />\n </VariantScale>\n </VariantFrame>\n <Box paddingTop={1}>\n <Text\n align=\"center\"\n size={0}\n textOverflow=\"ellipsis\"\n weight={variant === current ? 'medium' : 'regular'}\n >\n {IMAGE_PALETTE_TITLES[variant]}\n </Text>\n </Box>\n </VariantButton>\n ))}\n </VariantRow>\n </Stack>\n\n <TooltipButton\n icon={SparklesIcon}\n mode=\"ghost\"\n onClick={onLucky}\n text=\"I'm feeling lucky\"\n tooltip=\"Build the theme around a swatch that looks interesting\"\n width=\"fill\"\n />\n </>\n ) : (\n <Stack gap={3}>\n <Text muted size={0}>\n Take the accent, text and background colors from an image — read on device, nothing is\n uploaded.\n </Text>\n <ImageFileButton\n loading={busy}\n mode=\"ghost\"\n onFile={pickImage}\n text=\"Pick an image\"\n tooltip=\"Take the colors from an image on your device\"\n width=\"fill\"\n />\n </Stack>\n )}\n </Stack>","<Card\n border\n onDragEnter={(event) => {\n event.preventDefault()\n dragDepth.current++\n setDragging(true)\n }}\n onDragLeave={() => {\n dragDepth.current = Math.max(0, dragDepth.current - 1)\n\n if (dragDepth.current === 0) setDragging(false)\n }}\n onDragOver={(event) => {\n event.preventDefault()\n }}\n onDrop={(event) => {\n event.preventDefault()\n dragDepth.current = 0\n setDragging(false)\n\n const file = event.dataTransfer.files[0]\n\n if (file) pickImage(file)\n }}\n padding={3}\n radius={3}\n tone={dragging ? 'primary' : 'default'}\n >\n <Stack gap={3}>\n <Flex align=\"center\" gap={2}>\n <Box flex={1}>\n <Text size={1} weight=\"medium\">\n Image palette\n </Text>\n </Box>\n {palette && (\n <ImageFileButton\n loading={busy}\n mode=\"bleed\"\n onFile={pickImage}\n padding={2}\n tooltip=\"Pick another image\"\n />\n )}\n </Flex>\n\n {palette ? (\n <>\n <PaletteGrid data-with-image={imageUrl !== undefined}>\n {imageUrl && <ImageTile alt=\"\" src={imageUrl} />}\n {TILES.map((key) => {\n const hex = palette[key]\n const title = IMAGE_PALETTE_TITLES[key]\n\n return (\n <Tooltip\n animate\n content={\n <Text size={1}>\n {hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`}\n </Text>\n }\n key={key}\n placement=\"bottom\"\n portal\n >\n <Tile data-empty={hex === null} style={{background: hex ?? undefined}} />\n </Tooltip>\n )\n })}\n </PaletteGrid>\n\n <Stack gap={2}>\n <Text muted size={0}>\n Pick the swatch to build the theme around\n </Text>\n <VariantRow\n ref={rowRef}\n style={{\n ['--variant-width' as string]: `${variantWidth}px`,\n ['--variant-scale' as string]: variantWidth / VARIANT_WIDTH,\n }}\n >\n {variants.map(({variant, options: variantOptions}) => (\n <VariantButton\n aria-pressed={variant === current}\n key={variant}\n onClick={() => onVariant(variant)}\n type=\"button\"\n >\n <VariantFrame>\n <VariantScale>\n <ThemeThumbnail options={variantOptions} />\n </VariantScale>\n </VariantFrame>\n <Box paddingTop={1}>\n <Text\n align=\"center\"\n size={0}\n textOverflow=\"ellipsis\"\n weight={variant === current ? 'medium' : 'regular'}\n >\n {IMAGE_PALETTE_TITLES[variant]}\n </Text>\n </Box>\n </VariantButton>\n ))}\n </VariantRow>\n </Stack>\n\n <TooltipButton\n icon={SparklesIcon}\n mode=\"ghost\"\n onClick={onLucky}\n text=\"I'm feeling lucky\"\n tooltip=\"Build the theme around a swatch that looks interesting\"\n width=\"fill\"\n />\n </>\n ) : (\n <Stack gap={3}>\n <Text muted size={0}>\n Take the accent, text and background colors from an image — read on device, nothing is\n uploaded.\n </Text>\n <ImageFileButton\n loading={busy}\n mode=\"ghost\"\n onFile={pickImage}\n text=\"Pick an image\"\n tooltip=\"Take the colors from an image on your device\"\n width=\"fill\"\n />\n </Stack>\n )}\n </Stack>\n </Card>","dragging ? 'primary' : 'default'","(event) => {\n event.preventDefault()\n }","themes.find((candidate) => candidate.slug === slug && candidate.source === 'custom')","(candidate) => candidate.slug === slug && candidate.source === 'custom'","(variant: ImagePaletteVariant | null) => {\n if (!theme.palette || variant === null) return\n\n send({\n type: 'theme.update',\n slug,\n options: applyImagePalette(theme.options, theme.palette, variant),\n })\n }","options","palette","() => send({type: 'flow.list'})","() => {\n if (!theme.palette) return\n\n applyVariant(\n pickLuckyVariant(theme.palette, {\n exclude: currentImageVariant(theme.options, theme.palette),\n }),\n )\n }","(options) => send({type: 'theme.update', slug, options})","(palette, file) =>\n send({\n type: 'theme.update',\n slug,\n options: applyImagePalette(theme.options, palette),\n palette,\n imageUrl: URL.createObjectURL(file),\n })","() => send({type: 'theme.remove', slug})","(title) => send({type: 'theme.update', slug, title})","<ThemeEditorForm\n focusTitle={focusTitle}\n imageUrl={images[slug]}\n onDone={() => send({type: 'flow.list'})}\n onLucky={() => {\n if (!theme.palette) return\n\n applyVariant(\n pickLuckyVariant(theme.palette, {\n exclude: currentImageVariant(theme.options, theme.palette),\n }),\n )\n }}\n onOptionsChange={(options) => send({type: 'theme.update', slug, options})}\n onPalette={(palette, file) =>\n send({\n type: 'theme.update',\n slug,\n options: applyImagePalette(theme.options, palette),\n palette,\n imageUrl: URL.createObjectURL(file),\n })\n }\n onRemove={() => send({type: 'theme.remove', slug})}\n onTitleChange={(title) => send({type: 'theme.update', slug, title})}\n onVariant={applyVariant}\n options={theme.options}\n palette={theme.palette}\n split={split}\n title={theme.title}\n />","images[slug]","title","resolveThemeOptions(options)","useMemo(() => resolveThemeOptions(options), [options])","buildPalette(options)","useMemo(() => buildPalette(options), [options])","(scheme: ThemeColorSchemeKey, changes: Partial<SchemeThemeOptions>) => {\n const values: SchemeThemeOptions = {...options[scheme], ...changes}\n\n // Clearing an option means falling back to its default, which is its absence\n for (const key of SCHEME_OPTION_KEYS) {\n if (values[key] === undefined) delete values[key]\n }\n\n const next: BuildThemeOptions = {...options}\n\n if (Object.keys(values).length === 0) {\n delete next[scheme]\n } else {\n next[scheme] = values\n }\n\n onOptionsChange(next)\n }","<ThemeThumbnail options={options} />","<Text size={1} weight=\"medium\">\n Title\n </Text>","(event) => onTitleChange(event.currentTarget.value)","(event) => event.currentTarget.select()","<Stack gap={2}>\n <Text size={1} weight=\"medium\">\n Title\n </Text>\n <TextInput\n autoFocus={focusTitle}\n fontSize={1}\n onChange={(event) => onTitleChange(event.currentTarget.value)}\n onFocus={focusTitle ? (event) => event.currentTarget.select() : undefined}\n padding={2}\n placeholder=\"Untitled theme\"\n value={title}\n />\n </Stack>","focusTitle ? (event) => event.currentTarget.select() : undefined","<ImagePaletteSection\n imageUrl={imageUrl}\n onLucky={onLucky}\n onPalette={onPalette}\n onVariant={onVariant}\n options={options}\n palette={palette}\n />","SCHEMES.map((scheme) => (\n <SchemeCard\n active={split || scheme === studioScheme}\n key={scheme}\n onChange={(changes) => patchScheme(scheme, changes)}\n options={options[scheme] ?? {}}\n palette={palettes[scheme]}\n resolved={resolved[scheme]}\n scheme={scheme}\n split={split}\n />\n ))","scheme","changes","<ScrollAreaBleed>\n {SCHEMES.map((scheme) => (\n <SchemeCard\n active={split || scheme === studioScheme}\n key={scheme}\n onChange={(changes) => patchScheme(scheme, changes)}\n options={options[scheme] ?? {}}\n palette={palettes[scheme]}\n resolved={resolved[scheme]}\n scheme={scheme}\n split={split}\n />\n ))}\n </ScrollAreaBleed>","<ScrollArea padding={3}>\n <Stack gap={4}>\n <ThemeThumbnail options={options} />\n\n <Stack gap={2}>\n <Text size={1} weight=\"medium\">\n Title\n </Text>\n <TextInput\n autoFocus={focusTitle}\n fontSize={1}\n onChange={(event) => onTitleChange(event.currentTarget.value)}\n onFocus={focusTitle ? (event) => event.currentTarget.select() : undefined}\n padding={2}\n placeholder=\"Untitled theme\"\n value={title}\n />\n </Stack>\n\n <ImagePaletteSection\n imageUrl={imageUrl}\n onLucky={onLucky}\n onPalette={onPalette}\n onVariant={onVariant}\n options={options}\n palette={palette}\n />\n\n <ScrollAreaBleed>\n {SCHEMES.map((scheme) => (\n <SchemeCard\n active={split || scheme === studioScheme}\n key={scheme}\n onChange={(changes) => patchScheme(scheme, changes)}\n options={options[scheme] ?? {}}\n palette={palettes[scheme]}\n resolved={resolved[scheme]}\n scheme={scheme}\n split={split}\n />\n ))}\n </ScrollAreaBleed>\n </Stack>\n </ScrollArea>","<TooltipButton\n icon={TrashIcon}\n mode=\"ghost\"\n onClick={onRemove}\n text=\"Remove\"\n tone=\"critical\"\n tooltip=\"Remove the theme — it can be restored until it is deleted\"\n />","<Box flex={1} />","<Button mode=\"ghost\" onClick={onDone} text=\"Done\" />","<Card borderTop padding={3}>\n <Flex gap={2}>\n <TooltipButton\n icon={TrashIcon}\n mode=\"ghost\"\n onClick={onRemove}\n text=\"Remove\"\n tone=\"critical\"\n tooltip=\"Remove the theme — it can be restored until it is deleted\"\n />\n <Box flex={1} />\n <Button mode=\"ghost\" onClick={onDone} text=\"Done\" />\n </Flex>\n </Card>","<>\n <ScrollArea padding={3}>\n <Stack gap={4}>\n <ThemeThumbnail options={options} />\n\n <Stack gap={2}>\n <Text size={1} weight=\"medium\">\n Title\n </Text>\n <TextInput\n autoFocus={focusTitle}\n fontSize={1}\n onChange={(event) => onTitleChange(event.currentTarget.value)}\n onFocus={focusTitle ? (event) => event.currentTarget.select() : undefined}\n padding={2}\n placeholder=\"Untitled theme\"\n value={title}\n />\n </Stack>\n\n <ImagePaletteSection\n imageUrl={imageUrl}\n onLucky={onLucky}\n onPalette={onPalette}\n onVariant={onVariant}\n options={options}\n palette={palette}\n />\n\n <ScrollAreaBleed>\n {SCHEMES.map((scheme) => (\n <SchemeCard\n active={split || scheme === studioScheme}\n key={scheme}\n onChange={(changes) => patchScheme(scheme, changes)}\n options={options[scheme] ?? {}}\n palette={palettes[scheme]}\n resolved={resolved[scheme]}\n scheme={scheme}\n split={split}\n />\n ))}\n </ScrollAreaBleed>\n </Stack>\n </ScrollArea>\n\n <Card borderTop padding={3}>\n <Flex gap={2}>\n <TooltipButton\n icon={TrashIcon}\n mode=\"ghost\"\n onClick={onRemove}\n text=\"Remove\"\n tone=\"critical\"\n tooltip=\"Remove the theme — it can be restored until it is deleted\"\n />\n <Box flex={1} />\n <Button mode=\"ghost\" onClick={onDone} text=\"Done\" />\n </Flex>\n </Card>\n </>","event","<Box flex={1}>\n <Text size={1} weight=\"medium\">\n {name}\n </Text>\n </Box>","active && (\n <Badge fontSize={0} tone=\"primary\">\n Active\n </Badge>\n )","<Flex align=\"center\" gap={2}>\n <Box flex={1}>\n <Text size={1} weight=\"medium\">\n {name}\n </Text>\n </Box>\n {active && (\n <Badge fontSize={0} tone=\"primary\">\n Active\n </Badge>\n )}\n </Flex>","<Text muted size={0}>\n {split\n ? SPLIT_HINTS[scheme]\n : active\n ? 'Shown in the Studio right now'\n : `Shown once the Studio appearance is ${scheme}`}\n </Text>","split\n ? SPLIT_HINTS[scheme]\n : active\n ? 'Shown in the Studio right now'\n : `Shown once the Studio appearance is ${scheme}`","<Stack gap={2}>\n <Flex align=\"center\" gap={2}>\n <Box flex={1}>\n <Text size={1} weight=\"medium\">\n {name}\n </Text>\n </Box>\n {active && (\n <Badge fontSize={0} tone=\"primary\">\n Active\n </Badge>\n )}\n </Flex>\n <Text muted size={0}>\n {split\n ? SPLIT_HINTS[scheme]\n : active\n ? 'Shown in the Studio right now'\n : `Shown once the Studio appearance is ${scheme}`}\n </Text>\n </Stack>","(accent) => onChange({accent})","() => onChange({accent: undefined})","<ColorRow\n auto={options.accent === undefined}\n autoLabel=\"default\"\n label={`${name} accent`}\n onChange={(accent) => onChange({accent})}\n onClear={() => onChange({accent: undefined})}\n title=\"Accent\"\n value={resolved.accent}\n />","accent","options.accent === undefined","`${name} accent`","(text) => onChange({text})","() => onChange({text: undefined})","<ColorRow\n auto={options.text === undefined}\n label={`${name} text`}\n onChange={(text) => onChange({text})}\n onClear={() => onChange({text: undefined})}\n title=\"Text\"\n value={resolved.text}\n />","text","options.text === undefined","`${name} text`","(background) => onChange({background})","() => onChange({background: undefined})","<ColorRow\n adjusted={scheme === 'dark' ? palette.black : palette.white}\n auto={options.background === undefined}\n label={`${name} background`}\n onChange={(background) => onChange({background})}\n onClear={() => onChange({background: undefined})}\n title=\"Background\"\n value={resolved.background}\n />","background","scheme === 'dark' ? palette.black : palette.white","options.background === undefined","`${name} background`","<Text size={1}>Contrast</Text>","<Stack flex={1} gap={2}>\n <Text size={1}>Contrast</Text>\n <Text muted size={0}>\n {resolved.contrast}\n {options.contrast === undefined ? ' · auto' : ''}\n </Text>\n </Stack>","contrast","options.contrast === undefined ? ' · auto' : ''","options.contrast !== undefined && (\n <TooltipButton\n icon={ResetIcon}\n mode=\"bleed\"\n onClick={() => onChange({contrast: undefined})}\n padding={2}\n tooltip=\"Reset to auto\"\n />\n )","<Flex align=\"center\" gap={2}>\n <Stack flex={1} gap={2}>\n <Text size={1}>Contrast</Text>\n <Text muted size={0}>\n {resolved.contrast}\n {options.contrast === undefined ? ' · auto' : ''}\n </Text>\n </Stack>\n {options.contrast !== undefined && (\n <TooltipButton\n icon={ResetIcon}\n mode=\"bleed\"\n onClick={() => onChange({contrast: undefined})}\n padding={2}\n tooltip=\"Reset to auto\"\n />\n )}\n </Flex>","(event) => onChange({contrast: Number(event.currentTarget.value)})","<Range\n aria-label={`${name} contrast`}\n max={100}\n min={15}\n onChange={(event) => onChange({contrast: Number(event.currentTarget.value)})}\n step={1}\n type=\"range\"\n value={resolved.contrast}\n />","`${name} contrast`","<Text muted size={0}>\n 100 keeps text and borders neutral — lower values blend in the accent\n </Text>","<Stack gap={3}>\n <Flex align=\"center\" gap={2}>\n <Stack flex={1} gap={2}>\n <Text size={1}>Contrast</Text>\n <Text muted size={0}>\n {resolved.contrast}\n {options.contrast === undefined ? ' · auto' : ''}\n </Text>\n </Stack>\n {options.contrast !== undefined && (\n <TooltipButton\n icon={ResetIcon}\n mode=\"bleed\"\n onClick={() => onChange({contrast: undefined})}\n padding={2}\n tooltip=\"Reset to auto\"\n />\n )}\n </Flex>\n <Range\n aria-label={`${name} contrast`}\n max={100}\n min={15}\n onChange={(event) => onChange({contrast: Number(event.currentTarget.value)})}\n step={1}\n type=\"range\"\n value={resolved.contrast}\n />\n <Text muted size={0}>\n 100 keeps text and borders neutral — lower values blend in the accent\n </Text>\n </Stack>","<Card borderTop padding={3}>\n <Stack gap={4}>\n <Stack gap={2}>\n <Flex align=\"center\" gap={2}>\n <Box flex={1}>\n <Text size={1} weight=\"medium\">\n {name}\n </Text>\n </Box>\n {active && (\n <Badge fontSize={0} tone=\"primary\">\n Active\n </Badge>\n )}\n </Flex>\n <Text muted size={0}>\n {split\n ? SPLIT_HINTS[scheme]\n : active\n ? 'Shown in the Studio right now'\n : `Shown once the Studio appearance is ${scheme}`}\n </Text>\n </Stack>\n\n <ColorRow\n auto={options.accent === undefined}\n autoLabel=\"default\"\n label={`${name} accent`}\n onChange={(accent) => onChange({accent})}\n onClear={() => onChange({accent: undefined})}\n title=\"Accent\"\n value={resolved.accent}\n />\n <ColorRow\n auto={options.text === undefined}\n label={`${name} text`}\n onChange={(text) => onChange({text})}\n onClear={() => onChange({text: undefined})}\n title=\"Text\"\n value={resolved.text}\n />\n <ColorRow\n adjusted={scheme === 'dark' ? palette.black : palette.white}\n auto={options.background === undefined}\n label={`${name} background`}\n onChange={(background) => onChange({background})}\n onClear={() => onChange({background: undefined})}\n title=\"Background\"\n value={resolved.background}\n />\n\n <Stack gap={3}>\n <Flex align=\"center\" gap={2}>\n <Stack flex={1} gap={2}>\n <Text size={1}>Contrast</Text>\n <Text muted size={0}>\n {resolved.contrast}\n {options.contrast === undefined ? ' · auto' : ''}\n </Text>\n </Stack>\n {options.contrast !== undefined && (\n <TooltipButton\n icon={ResetIcon}\n mode=\"bleed\"\n onClick={() => onChange({contrast: undefined})}\n padding={2}\n tooltip=\"Reset to auto\"\n />\n )}\n </Flex>\n <Range\n aria-label={`${name} contrast`}\n max={100}\n min={15}\n onChange={(event) => onChange({contrast: Number(event.currentTarget.value)})}\n step={1}\n type=\"range\"\n value={resolved.contrast}\n />\n <Text muted size={0}>\n 100 keeps text and borders neutral — lower values blend in the accent\n </Text>\n </Stack>\n </Stack>\n </Card>","autoLabel = 'auto'","(event) => onChange(event.currentTarget.value)","<Swatch\n aria-label={label}\n onChange={(event) => onChange(event.currentTarget.value)}\n type=\"color\"\n value={value}\n />","{minWidth: 0}","<Text size={1}>{title}</Text>","<Text muted size={0} textOverflow=\"ellipsis\">\n {value}\n {adjusted !== undefined && adjusted !== value ? ` → ${adjusted}` : ''}\n {auto ? ` · ${autoLabel}` : ''}\n </Text>","adjusted !== undefined && adjusted !== value ? ` → ${adjusted}` : ''","auto ? ` · ${autoLabel}` : ''","<Stack flex={1} gap={2} style={{minWidth: 0}}>\n <Text size={1}>{title}</Text>\n <Text muted size={0} textOverflow=\"ellipsis\">\n {value}\n {adjusted !== undefined && adjusted !== value ? ` → ${adjusted}` : ''}\n {auto ? ` · ${autoLabel}` : ''}\n </Text>\n </Stack>","onClear && !auto && (\n <TooltipButton\n icon={ResetIcon}\n mode=\"bleed\"\n onClick={onClear}\n padding={2}\n tooltip={`Reset to ${autoLabel}`}\n />\n )","<Flex align=\"center\" gap={2}>\n <Swatch\n aria-label={label}\n onChange={(event) => onChange(event.currentTarget.value)}\n type=\"color\"\n value={value}\n />\n <Stack flex={1} gap={2} style={{minWidth: 0}}>\n <Text size={1}>{title}</Text>\n <Text muted size={0} textOverflow=\"ellipsis\">\n {value}\n {adjusted !== undefined && adjusted !== value ? ` → ${adjusted}` : ''}\n {auto ? ` · ${autoLabel}` : ''}\n </Text>\n </Stack>\n {onClear && !auto && (\n <TooltipButton\n icon={ResetIcon}\n mode=\"bleed\"\n onClick={onClear}\n padding={2}\n tooltip={`Reset to ${autoLabel}`}\n />\n )}\n </Flex>","displayTitle(theme.title)","title","() => send({type: 'theme.pick', slug: theme.slug})","slug","<Frame>\n <ThemeThumbnail options={theme.options} />\n </Frame>","options","<Box as=\"span\" display=\"block\" paddingTop={2} paddingX={2}>\n <Text\n align=\"center\"\n as=\"span\"\n size={1}\n textOverflow=\"ellipsis\"\n weight={active ? 'medium' : 'regular'}\n >\n {title}\n </Text>\n </Box>","active ? 'medium' : 'regular'","<PickButton\n aria-pressed={active}\n onClick={() => send({type: 'theme.pick', slug: theme.slug})}\n type=\"button\"\n >\n <Frame>\n <ThemeThumbnail options={theme.options} />\n </Frame>\n <Box as=\"span\" display=\"block\" paddingTop={2} paddingX={2}>\n <Text\n align=\"center\"\n as=\"span\"\n size={1}\n textOverflow=\"ellipsis\"\n weight={active ? 'medium' : 'regular'}\n >\n {title}\n </Text>\n </Box>\n </PickButton>","<Text size={1}>Show more</Text>","{display: 'inline-block'}","<Button\n aria-label={`Show more for ${title}`}\n fontSize={1}\n icon={EllipsisHorizontalIcon}\n mode=\"ghost\"\n padding={1}\n radius={2}\n />","`Show more for ${title}`","theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )","source","() => send({type: 'theme.duplicate', slug: theme.slug})","<MenuItem\n icon={CopyIcon}\n onClick={() => send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />","theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'","theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => send({type: 'theme.remove', slug: theme.slug})}\n text=\"Remove\"\n tone=\"critical\"\n />\n </>\n )","<Menu>\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => send({type: 'theme.remove', slug: theme.slug})}\n text=\"Remove\"\n tone=\"critical\"\n />\n </>\n )}\n </Menu>","() => setMenuOpen(false)","() => setMenuOpen(true)","{animate: true, placement: 'bottom-end', portal: true, scheme}","<Tooltip animate content={<Text size={1}>Show more</Text>} placement=\"bottom\" portal>\n <Card radius={2} scheme=\"dark\" style={{display: 'inline-block'}}>\n <MenuButton\n button={\n <Button\n aria-label={`Show more for ${title}`}\n fontSize={1}\n icon={EllipsisHorizontalIcon}\n mode=\"ghost\"\n padding={1}\n radius={2}\n />\n }\n id={`themer-theme-${theme.slug}`}\n menu={\n <Menu>\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => send({type: 'theme.remove', slug: theme.slug})}\n text=\"Remove\"\n tone=\"critical\"\n />\n </>\n )}\n </Menu>\n }\n onClose={() => setMenuOpen(false)}\n onOpen={() => setMenuOpen(true)}\n popover={{animate: true, placement: 'bottom-end', portal: true, scheme}}\n />\n </Card>\n </Tooltip>","`themer-theme-${theme.slug}`","<MenuSlot data-visible={menuOpen}>\n <Tooltip animate content={<Text size={1}>Show more</Text>} placement=\"bottom\" portal>\n <Card radius={2} scheme=\"dark\" style={{display: 'inline-block'}}>\n <MenuButton\n button={\n <Button\n aria-label={`Show more for ${title}`}\n fontSize={1}\n icon={EllipsisHorizontalIcon}\n mode=\"ghost\"\n padding={1}\n radius={2}\n />\n }\n id={`themer-theme-${theme.slug}`}\n menu={\n <Menu>\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => send({type: 'theme.remove', slug: theme.slug})}\n text=\"Remove\"\n tone=\"critical\"\n />\n </>\n )}\n </Menu>\n }\n onClose={() => setMenuOpen(false)}\n onOpen={() => setMenuOpen(true)}\n popover={{animate: true, placement: 'bottom-end', portal: true, scheme}}\n />\n </Card>\n </Tooltip>\n </MenuSlot>","<Root>\n <PickButton\n aria-pressed={active}\n onClick={() => send({type: 'theme.pick', slug: theme.slug})}\n type=\"button\"\n >\n <Frame>\n <ThemeThumbnail options={theme.options} />\n </Frame>\n <Box as=\"span\" display=\"block\" paddingTop={2} paddingX={2}>\n <Text\n align=\"center\"\n as=\"span\"\n size={1}\n textOverflow=\"ellipsis\"\n weight={active ? 'medium' : 'regular'}\n >\n {title}\n </Text>\n </Box>\n </PickButton>\n\n {/* The tooltip wraps a card rather than the button: the menu button\n clones its button to wire it up, which a tooltip in between would\n swallow. The card keeps the button dark, as it sits on the dark\n half of the thumbnail, while the menu opens in the sidebar's scheme */}\n <MenuSlot data-visible={menuOpen}>\n <Tooltip animate content={<Text size={1}>Show more</Text>} placement=\"bottom\" portal>\n <Card radius={2} scheme=\"dark\" style={{display: 'inline-block'}}>\n <MenuButton\n button={\n <Button\n aria-label={`Show more for ${title}`}\n fontSize={1}\n icon={EllipsisHorizontalIcon}\n mode=\"ghost\"\n padding={1}\n radius={2}\n />\n }\n id={`themer-theme-${theme.slug}`}\n menu={\n <Menu>\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => send({type: 'theme.remove', slug: theme.slug})}\n text=\"Remove\"\n tone=\"critical\"\n />\n </>\n )}\n </Menu>\n }\n onClose={() => setMenuOpen(false)}\n onOpen={() => setMenuOpen(true)}\n popover={{animate: true, placement: 'bottom-end', portal: true, scheme}}\n />\n </Card>\n </Tooltip>\n </MenuSlot>\n </Root>","(palette, file) =>\n send({\n type: 'theme.add',\n title: titleFromFileName(file.name),\n options: optionsFromImagePalette(palette),\n palette,\n imageUrl: URL.createObjectURL(file),\n })","themes.map((theme) => (\n <ThemeCard active={theme.slug === active.slug} key={theme.slug} theme={theme} />\n ))","active.slug","(theme) => (\n <ThemeCard active={theme.slug === active.slug} key={theme.slug} theme={theme} />\n )","removed.length > 0 && (\n <Button\n icon={RestoreIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'flow.removed'})}\n text={`Show removed (${removed.length})`}\n width=\"fill\"\n />\n )","length","<ScrollArea padding={3}>\n <Stack gap={4}>\n {themes.map((theme) => (\n <ThemeCard active={theme.slug === active.slug} key={theme.slug} theme={theme} />\n ))}\n {/* Trails the list rather than sitting in the footer, so the footer\n does not shift when the first theme gets removed */}\n {removed.length > 0 && (\n <Button\n icon={RestoreIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'flow.removed'})}\n text={`Show removed (${removed.length})`}\n width=\"fill\"\n />\n )}\n </Stack>\n </ScrollArea>","<Box flex={1}>\n <TooltipButton\n icon={AddIcon}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.add'})}\n text=\"Add theme\"\n tooltip=\"Add a theme based on the applied one\"\n width=\"fill\"\n />\n </Box>","<ImageFileButton\n loading={busy}\n mode=\"ghost\"\n onFile={pickImage}\n tooltip=\"Add a theme from the colors of an image\"\n />","<Card borderTop padding={3}>\n <Flex gap={2}>\n <Box flex={1}>\n <TooltipButton\n icon={AddIcon}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.add'})}\n text=\"Add theme\"\n tooltip=\"Add a theme based on the applied one\"\n width=\"fill\"\n />\n </Box>\n <ImageFileButton\n loading={busy}\n mode=\"ghost\"\n onFile={pickImage}\n tooltip=\"Add a theme from the colors of an image\"\n />\n </Flex>\n </Card>","<>\n <ScrollArea padding={3}>\n <Stack gap={4}>\n {themes.map((theme) => (\n <ThemeCard active={theme.slug === active.slug} key={theme.slug} theme={theme} />\n ))}\n {/* Trails the list rather than sitting in the footer, so the footer\n does not shift when the first theme gets removed */}\n {removed.length > 0 && (\n <Button\n icon={RestoreIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'flow.removed'})}\n text={`Show removed (${removed.length})`}\n width=\"fill\"\n />\n )}\n </Stack>\n </ScrollArea>\n\n <Card borderTop padding={3}>\n <Flex gap={2}>\n <Box flex={1}>\n <TooltipButton\n icon={AddIcon}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.add'})}\n text=\"Add theme\"\n tooltip=\"Add a theme based on the applied one\"\n width=\"fill\"\n />\n </Box>\n <ImageFileButton\n loading={busy}\n mode=\"ghost\"\n onFile={pickImage}\n tooltip=\"Add a theme from the colors of an image\"\n />\n </Flex>\n </Card>\n </>","createThemeSnippet(theme.options)","options","async () => {\n try {\n await navigator.clipboard.writeText(snippet)\n toast.push({status: 'success', title: 'Copied the theme to the clipboard'})\n } catch {\n toast.push({status: 'error', title: 'Could not copy the theme'})\n }\n }","<Flex gap={2} justify=\"flex-end\" padding={3}>\n <Button icon={ClipboardIcon} mode=\"ghost\" onClick={() => void handleCopy()} text=\"Copy\" />\n </Flex>","<code>sanity.config.ts</code>","<code>theme</code>","<code>defineConfig</code>","displayTitle(theme.title)","title","<Text muted size={1}>\n Export this from <code>sanity.config.ts</code> and pass it as the <code>theme</code> of{' '}\n <code>defineConfig</code> to make “{displayTitle(theme.title)}” the Studio theme without\n this tool.\n </Text>","<Card border overflow=\"auto\" padding={3} radius={2} tone=\"transparent\">\n <Code language=\"ts\" size={1}>\n {snippet}\n </Code>\n </Card>","<Box padding={4}>\n <Stack gap={4}>\n <Text muted size={1}>\n Export this from <code>sanity.config.ts</code> and pass it as the <code>theme</code> of{' '}\n <code>defineConfig</code> to make “{displayTitle(theme.title)}” the Studio theme without\n this tool.\n </Text>\n <Card border overflow=\"auto\" padding={3} radius={2} tone=\"transparent\">\n <Code language=\"ts\" size={1}>\n {snippet}\n </Code>\n </Card>\n </Stack>\n </Box>","<Dialog\n animate\n footer={\n <Flex gap={2} justify=\"flex-end\" padding={3}>\n <Button icon={ClipboardIcon} mode=\"ghost\" onClick={() => void handleCopy()} text=\"Copy\" />\n </Flex>\n }\n header=\"Add to your config\"\n id=\"themer-snippet\"\n onClickOutside={onClose}\n onClose={onClose}\n width={1}\n >\n <Box padding={4}>\n <Stack gap={4}>\n <Text muted size={1}>\n Export this from <code>sanity.config.ts</code> and pass it as the <code>theme</code> of{' '}\n <code>defineConfig</code> to make “{displayTitle(theme.title)}” the Studio theme without\n this tool.\n </Text>\n <Card border overflow=\"auto\" padding={3} radius={2} tone=\"transparent\">\n <Code language=\"ts\" size={1}>\n {snippet}\n </Code>\n </Card>\n </Stack>\n </Box>\n </Dialog>","!inList && (\n <TooltipButton\n icon={ArrowLeftIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'flow.list'})}\n padding={2}\n tooltip=\"Back to the themes\"\n />\n )","{minWidth: 0}","<Text size={1} textOverflow=\"ellipsis\" weight=\"semibold\">\n {VIEW_TITLES[view.name]}\n </Text>","VIEW_TITLES[view.name]","<Box flex={1} paddingLeft={inList ? 1 : 0} style={{minWidth: 0}}>\n <Text size={1} textOverflow=\"ellipsis\" weight=\"semibold\">\n {VIEW_TITLES[view.name]}\n </Text>\n </Box>","inList ? 1 : 0","view.name !== 'removed' && (\n <>\n <TooltipButton\n aria-pressed={split}\n icon={SplitVerticalIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'preview.toggle'})}\n padding={2}\n selected={split}\n tooltip=\"Show light and dark side by side\"\n />\n <TooltipButton\n icon={CodeBlockIcon}\n mode=\"bleed\"\n onClick={() => setSnippetOpen(true)}\n padding={2}\n tooltip=\"Show the code for the applied theme\"\n />\n </>\n )","name","<TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'sidebar.close'})}\n padding={2}\n tooltip=\"Close themer\"\n />","<Flex>\n {view.name !== 'removed' && (\n <>\n <TooltipButton\n aria-pressed={split}\n icon={SplitVerticalIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'preview.toggle'})}\n padding={2}\n selected={split}\n tooltip=\"Show light and dark side by side\"\n />\n <TooltipButton\n icon={CodeBlockIcon}\n mode=\"bleed\"\n onClick={() => setSnippetOpen(true)}\n padding={2}\n tooltip=\"Show the code for the applied theme\"\n />\n </>\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'sidebar.close'})}\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>","<Card padding={2}>\n <Flex align=\"center\" gap={1}>\n {!inList && (\n <TooltipButton\n icon={ArrowLeftIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'flow.list'})}\n padding={2}\n tooltip=\"Back to the themes\"\n />\n )}\n <Box flex={1} paddingLeft={inList ? 1 : 0} style={{minWidth: 0}}>\n <Text size={1} textOverflow=\"ellipsis\" weight=\"semibold\">\n {VIEW_TITLES[view.name]}\n </Text>\n </Box>\n {/* Packed without gaps, so the title still fits next to them at the\n narrowest sidebar width */}\n <Flex>\n {view.name !== 'removed' && (\n <>\n <TooltipButton\n aria-pressed={split}\n icon={SplitVerticalIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'preview.toggle'})}\n padding={2}\n selected={split}\n tooltip=\"Show light and dark side by side\"\n />\n <TooltipButton\n icon={CodeBlockIcon}\n mode=\"bleed\"\n onClick={() => setSnippetOpen(true)}\n padding={2}\n tooltip=\"Show the code for the applied theme\"\n />\n </>\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'sidebar.close'})}\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>\n </Flex>\n </Card>","view.name === 'list' && <ThemeList />","view.name === 'edit' && <ThemeEditor focusTitle={view.focusTitle} slug={view.slug} />","focusTitle","slug","view.name === 'removed' && <RemovedThemes />","<Flex direction=\"column\" height=\"fill\">\n {/* No bottom border: the header sits next to the Studio navbar, whose\n height is the Studio's to decide, so a line here would not line up\n with the navbar's */}\n <Card padding={2}>\n <Flex align=\"center\" gap={1}>\n {!inList && (\n <TooltipButton\n icon={ArrowLeftIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'flow.list'})}\n padding={2}\n tooltip=\"Back to the themes\"\n />\n )}\n <Box flex={1} paddingLeft={inList ? 1 : 0} style={{minWidth: 0}}>\n <Text size={1} textOverflow=\"ellipsis\" weight=\"semibold\">\n {VIEW_TITLES[view.name]}\n </Text>\n </Box>\n {/* Packed without gaps, so the title still fits next to them at the\n narrowest sidebar width */}\n <Flex>\n {view.name !== 'removed' && (\n <>\n <TooltipButton\n aria-pressed={split}\n icon={SplitVerticalIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'preview.toggle'})}\n padding={2}\n selected={split}\n tooltip=\"Show light and dark side by side\"\n />\n <TooltipButton\n icon={CodeBlockIcon}\n mode=\"bleed\"\n onClick={() => setSnippetOpen(true)}\n padding={2}\n tooltip=\"Show the code for the applied theme\"\n />\n </>\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'sidebar.close'})}\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>\n </Flex>\n </Card>\n\n {view.name === 'list' && <ThemeList />}\n {view.name === 'edit' && <ThemeEditor focusTitle={view.focusTitle} slug={view.slug} />}\n {view.name === 'removed' && <RemovedThemes />}\n </Flex>","snippetOpen && <ThemeSnippetDialog onClose={() => setSnippetOpen(false)} theme={active} />","<Card height=\"fill\">\n <Flex direction=\"column\" height=\"fill\">\n {/* No bottom border: the header sits next to the Studio navbar, whose\n height is the Studio's to decide, so a line here would not line up\n with the navbar's */}\n <Card padding={2}>\n <Flex align=\"center\" gap={1}>\n {!inList && (\n <TooltipButton\n icon={ArrowLeftIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'flow.list'})}\n padding={2}\n tooltip=\"Back to the themes\"\n />\n )}\n <Box flex={1} paddingLeft={inList ? 1 : 0} style={{minWidth: 0}}>\n <Text size={1} textOverflow=\"ellipsis\" weight=\"semibold\">\n {VIEW_TITLES[view.name]}\n </Text>\n </Box>\n {/* Packed without gaps, so the title still fits next to them at the\n narrowest sidebar width */}\n <Flex>\n {view.name !== 'removed' && (\n <>\n <TooltipButton\n aria-pressed={split}\n icon={SplitVerticalIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'preview.toggle'})}\n padding={2}\n selected={split}\n tooltip=\"Show light and dark side by side\"\n />\n <TooltipButton\n icon={CodeBlockIcon}\n mode=\"bleed\"\n onClick={() => setSnippetOpen(true)}\n padding={2}\n tooltip=\"Show the code for the applied theme\"\n />\n </>\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'sidebar.close'})}\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>\n </Flex>\n </Card>\n\n {view.name === 'list' && <ThemeList />}\n {view.name === 'edit' && <ThemeEditor focusTitle={view.focusTitle} slug={view.slug} />}\n {view.name === 'removed' && <RemovedThemes />}\n </Flex>\n\n {snippetOpen && <ThemeSnippetDialog onClose={() => setSnippetOpen(false)} theme={active} />}\n </Card>","() => writeStoredWidth(width)","[width]","(event: React.PointerEvent<HTMLDivElement>) => {\n if (event.button !== 0) return\n\n event.preventDefault()\n event.currentTarget.setPointerCapture(event.pointerId)\n drag.current = {pointerId: event.pointerId, startX: event.clientX, startWidth: width}\n setDragging(true)\n }","useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n if (event.button !== 0) return\n\n event.preventDefault()\n event.currentTarget.setPointerCapture(event.pointerId)\n drag.current = {pointerId: event.pointerId, startX: event.clientX, startWidth: width}\n setDragging(true)\n },\n [width],\n )","(event: React.PointerEvent<HTMLDivElement>) => {\n if (!drag.current || drag.current.pointerId !== event.pointerId) return\n\n // The sidebar sits on the right, so dragging left makes it wider\n setWidth(clampWidth(drag.current.startWidth - (event.clientX - drag.current.startX)))\n }","event","useCallback((event: React.PointerEvent<HTMLDivElement>) => {\n if (!drag.current || drag.current.pointerId !== event.pointerId) return\n\n // The sidebar sits on the right, so dragging left makes it wider\n setWidth(clampWidth(drag.current.startWidth - (event.clientX - drag.current.startX)))\n }, [])","(event: React.PointerEvent<HTMLDivElement>) => {\n if (!drag.current || drag.current.pointerId !== event.pointerId) return\n\n drag.current = null\n setDragging(false)\n }","useCallback((event: React.PointerEvent<HTMLDivElement>) => {\n if (!drag.current || drag.current.pointerId !== event.pointerId) return\n\n drag.current = null\n setDragging(false)\n }, [])","(event: React.KeyboardEvent<HTMLDivElement>) => {\n const steps: Record<string, number> = {\n ArrowLeft: KEYBOARD_STEP,\n ArrowRight: -KEYBOARD_STEP,\n Home: MAXIMUM_WIDTH,\n End: -MAXIMUM_WIDTH,\n }\n const step = steps[event.key]\n\n if (step === undefined) return\n\n event.preventDefault()\n setWidth((current) => clampWidth(current + step))\n }","useCallback((event: React.KeyboardEvent<HTMLDivElement>) => {\n const steps: Record<string, number> = {\n ArrowLeft: KEYBOARD_STEP,\n ArrowRight: -KEYBOARD_STEP,\n Home: MAXIMUM_WIDTH,\n End: -MAXIMUM_WIDTH,\n }\n const step = steps[event.key]\n\n if (step === undefined) return\n\n event.preventDefault()\n setWidth((current) => clampWidth(current + step))\n }, [])","{width}","() => setWidth(MINIMUM_WIDTH)","<ResizeHandle\n aria-label=\"Resize the themer\"\n aria-orientation=\"vertical\"\n aria-valuemax={MAXIMUM_WIDTH}\n aria-valuemin={MINIMUM_WIDTH}\n aria-valuenow={width}\n data-dragging={dragging}\n onDoubleClick={() => setWidth(MINIMUM_WIDTH)}\n onKeyDown={handleKeyDown}\n onPointerCancel={handlePointerUp}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n // oxlint-disable-next-line prefer-tag-over-role -- a window splitter is a focusable, draggable separator, which an hr is not\n role=\"separator\"\n tabIndex={0}\n />","<Box height=\"fill\" overflow=\"hidden\">\n <ThemerSidebar />\n </Box>","<Frame borderLeft height=\"fill\">\n <ResizeHandle\n aria-label=\"Resize the themer\"\n aria-orientation=\"vertical\"\n aria-valuemax={MAXIMUM_WIDTH}\n aria-valuemin={MINIMUM_WIDTH}\n aria-valuenow={width}\n data-dragging={dragging}\n onDoubleClick={() => setWidth(MINIMUM_WIDTH)}\n onKeyDown={handleKeyDown}\n onPointerCancel={handlePointerUp}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n // oxlint-disable-next-line prefer-tag-over-role -- a window splitter is a focusable, draggable separator, which an hr is not\n role=\"separator\"\n tabIndex={0}\n />\n <Box height=\"fill\" overflow=\"hidden\">\n <ThemerSidebar />\n </Box>\n </Frame>","<Sidebar style={{width}} zOffset={100}>\n <Frame borderLeft height=\"fill\">\n <ResizeHandle\n aria-label=\"Resize the themer\"\n aria-orientation=\"vertical\"\n aria-valuemax={MAXIMUM_WIDTH}\n aria-valuemin={MINIMUM_WIDTH}\n aria-valuenow={width}\n data-dragging={dragging}\n onDoubleClick={() => setWidth(MINIMUM_WIDTH)}\n onKeyDown={handleKeyDown}\n onPointerCancel={handlePointerUp}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n // oxlint-disable-next-line prefer-tag-over-role -- a window splitter is a focusable, draggable separator, which an hr is not\n role=\"separator\"\n tabIndex={0}\n />\n <Box height=\"fill\" overflow=\"hidden\">\n <ThemerSidebar />\n </Box>\n </Frame>\n </Sidebar>","() => ({baseOptions, stored: readStoredState()})","{input}","snapshot.matches({sidebar: 'open'})","snapshot.matches({preview: 'split'})","() => writeStoredState(stored)","[stored]","() => {\n const current = new Set(Object.values(images))\n\n for (const url of Object.values(previousImages.current)) {\n if (!current.has(url)) URL.revokeObjectURL(url)\n }\n\n previousImages.current = images\n }","[images]","() => () => {\n for (const url of Object.values(previousImages.current)) URL.revokeObjectURL(url)\n }","[]","url","resolveThemes(stored, baseOptions)","useMemo(\n () => resolveThemes(stored, baseOptions),\n [stored, baseOptions],\n )","activeOptions === null ? null : buildTheme(activeOptions)","useMemo(\n () => (activeOptions === null ? null : buildTheme(activeOptions)),\n [activeOptions],\n )","() => {\n const background = theme?.v2?.color[scheme].default.bg\n\n if (background === undefined) return undefined\n\n const {style} = document.body\n const previous = style.backgroundColor\n\n style.backgroundColor = background\n\n return () => {\n style.backgroundColor = previous\n }\n }","color","[scheme, theme]","{baseOptions, themes, removed, active, images, view, open, split, send}","useMemo<ThemerContextValue>(\n () => ({baseOptions, themes, removed, active, images, view, open, split, send}),\n [active, baseOptions, images, open, removed, send, split, themes, view],\n )","<StudioPreview scheme={split ? 'light' : undefined} theme={theme}>\n {studio}\n </StudioPreview>","split ? 'light' : undefined","split && (\n <StudioPreview borderLeft scheme=\"dark\" theme={theme}>\n {studio}\n </StudioPreview>\n )","open && (\n <ThemeProvider theme={theme ?? undefined}>\n <ResizableSidebar />\n </ThemeProvider>\n )","<Flex height=\"fill\" sizing=\"border\">\n {/* The first copy keeps its place when the preview splits, so the Studio\n in it stays mounted and only changes scheme */}\n <StudioPreview scheme={split ? 'light' : undefined} theme={theme}>\n {studio}\n </StudioPreview>\n {split && (\n <StudioPreview borderLeft scheme=\"dark\" theme={theme}>\n {studio}\n </StudioPreview>\n )}\n\n {open && (\n <ThemeProvider theme={theme ?? undefined}>\n <ResizableSidebar />\n </ThemeProvider>\n )}\n </Flex>","<ThemerContext.Provider value={context}>\n <Flex height=\"fill\" sizing=\"border\">\n {/* The first copy keeps its place when the preview splits, so the Studio\n in it stays mounted and only changes scheme */}\n <StudioPreview scheme={split ? 'light' : undefined} theme={theme}>\n {studio}\n </StudioPreview>\n {split && (\n <StudioPreview borderLeft scheme=\"dark\" theme={theme}>\n {studio}\n </StudioPreview>\n )}\n\n {open && (\n <ThemeProvider theme={theme ?? undefined}>\n <ResizableSidebar />\n </ThemeProvider>\n )}\n </Flex>\n </ThemerContext.Provider>","ThemerContext.Provider","scheme ? {colorScheme: scheme} : undefined","<Card\n borderLeft={borderLeft}\n flex={1}\n height=\"fill\"\n overflow=\"hidden\"\n style={scheme ? {colorScheme: scheme} : undefined}\n >\n {children}\n </Card>","<ThemeProvider scheme={scheme} theme={theme ?? undefined}>\n <Card\n borderLeft={borderLeft}\n flex={1}\n height=\"fill\"\n overflow=\"hidden\"\n style={scheme ? {colorScheme: scheme} : undefined}\n >\n {children}\n </Card>\n </ThemeProvider>","theme ?? undefined","<Text size={1}>Themer</Text>","() => send({type: 'sidebar.toggle'})","<Tooltip animate content={<Text size={1}>Themer</Text>} portal>\n <Button\n aria-label=\"Themer\"\n icon={ColorWheelIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'sidebar.toggle'})}\n // The Studio's own navbar buttons go through a wrapper that pins them\n // to this padding, where `@sanity/ui` defaults to a roomier 3\n padding={2}\n selected={open}\n />\n </Tooltip>","props.__internal_actions ?? []","__internal_actions","props.renderDefault({\n ...props,\n __internal_actions: [\n ...(props.__internal_actions ?? []),\n {\n location: 'topbar',\n name: 'themer-topbar',\n render: () => <ThemerNavbarButton />,\n },\n {\n icon: ColorWheelIcon,\n location: 'sidebar',\n name: 'themer-sidebar',\n onAction: () => send({type: 'sidebar.toggle'}),\n selected: open,\n title: 'Themer',\n },\n ],\n })","() => <ThemerNavbarButton />","<ThemerLayout {...props} baseOptions={baseOptions} />"],"sources":["../src/tool/context.ts","../src/tool/options.ts","../src/tool/themes.ts","../src/tool/machine.ts","../src/tool/ScrollArea.tsx","../src/tool/ThemePreview.tsx","../src/tool/ThemeThumbnail.tsx","../src/tool/RemovedThemes.tsx","../src/tool/imagePalette.ts","../src/tool/TooltipButton.tsx","../src/tool/ImageFileButton.tsx","../src/tool/imageFile.ts","../src/tool/useImagePalette.ts","../src/tool/ImagePaletteSection.tsx","../src/tool/ThemeEditor.tsx","../src/tool/ThemeCard.tsx","../src/tool/ThemeList.tsx","../src/tool/snippet.ts","../src/tool/ThemeSnippetDialog.tsx","../src/tool/ThemerSidebar.tsx","../src/tool/ResizableSidebar.tsx","../src/tool/storage.ts","../src/tool/ThemerLayout.tsx","../src/tool/ThemerNavbar.tsx","../src/tool/plugin.tsx"],"sourcesContent":["import {createContext, useContext} from 'react'\n\nimport {BuildThemeOptions} from '../theme/options'\nimport {ThemerEvent} from './machine'\nimport {ThemerTheme} from './themes'\n\n/**\n * The flow the sidebar is in: picking a theme from the list, editing one of\n * the user's own themes, or restoring removed ones.\n *\n * @internal\n */\nexport type ThemerView =\n | {name: 'list'}\n | {\n name: 'edit'\n slug: string\n /** Whether the title input should take focus, for themes that were just created */\n focusTitle: boolean\n }\n | {name: 'removed'}\n\n/** @internal */\nexport interface ThemerContextValue {\n /** The theme options the Studio's configured theme was generated from */\n baseOptions: BuildThemeOptions\n /** The themes to pick from, in list order */\n themes: ThemerTheme[]\n /** The removed themes, which can be restored */\n removed: ThemerTheme[]\n /** The applied theme */\n active: ThemerTheme\n /** Object URLs of the images themes took their palette from this session, by slug */\n images: Record<string, string>\n view: ThemerView\n /** Whether the themer sidebar is open */\n open: boolean\n /** Whether the Studio shows twice side by side, in light and dark */\n split: boolean\n /** Sends an event to the themer machine */\n send: (event: ThemerEvent) => void\n}\n\n/** @internal */\nexport const ThemerContext = createContext<ThemerContextValue | null>(null)\n\n/** @internal */\nexport function useThemer(): ThemerContextValue {\n const context = useContext(ThemerContext)\n\n if (!context) {\n throw new Error('useThemer must be used within the `themerTool` plugin')\n }\n\n return context\n}\n","import {\n BuildThemeOptions,\n DEFAULT_ACCENT,\n DEFAULT_BACKGROUND,\n DEFAULT_CONTRAST,\n deriveTextColor,\n ResolvedSchemeOptions,\n resolveThemeOptions,\n SCHEMES,\n SchemeThemeOptions,\n} from '../theme/options'\n\n/**\n * Reduces theme options to the minimal object that recreates them through\n * `buildTheme`: in each scheme the accent is dropped when it is the stock\n * one, the text color when it matches the one derived from the accent, and\n * the background and contrast when they match the defaults — and a scheme\n * with nothing left is dropped altogether. Options that boil down to the\n * stock Studio theme reduce to `null` — they need nothing from this package.\n *\n * @internal\n */\nexport function minimizeOptions(options: BuildThemeOptions): BuildThemeOptions | null {\n const resolved = resolveThemeOptions(options)\n const minimized: BuildThemeOptions = {}\n\n for (const scheme of SCHEMES) {\n const values = resolved[scheme]\n const kept: SchemeThemeOptions = {}\n\n if (values.accent !== DEFAULT_ACCENT) kept.accent = values.accent\n if (values.text !== deriveTextColor(values.accent)) kept.text = values.text\n if (values.background !== DEFAULT_BACKGROUND[scheme]) kept.background = values.background\n if (values.contrast !== DEFAULT_CONTRAST) kept.contrast = values.contrast\n\n if (Object.keys(kept).length > 0) minimized[scheme] = kept\n }\n\n return Object.keys(minimized).length === 0 ? null : minimized\n}\n\n/**\n * Whether two sets of theme options generate the same theme, comparing their\n * resolved forms so that derived and explicit values (and hex casing) don't\n * matter.\n *\n * @internal\n */\nexport function sameOptions(a: BuildThemeOptions, b: BuildThemeOptions): boolean {\n const left = resolveThemeOptions(a)\n const right = resolveThemeOptions(b)\n\n return SCHEMES.every((scheme) => sameScheme(left[scheme], right[scheme]))\n}\n\nfunction sameScheme(a: ResolvedSchemeOptions, b: ResolvedSchemeOptions): boolean {\n return (\n a.accent === b.accent &&\n a.text === b.text &&\n a.background === b.background &&\n a.contrast === b.contrast\n )\n}\n","import {BuildThemeOptions} from '../theme/options'\nimport {presets} from '../theme/presets'\nimport {ImagePalette} from './imagePalette'\nimport {sameOptions} from './options'\n\n/**\n * Where a theme in the themer list comes from: the theme the Studio config\n * was generated from, one of the presets defined in code, or one the user\n * added in the tool. Only custom themes are editable — the others are\n * duplicated into a custom theme first.\n */\ntype ThemerThemeSource = 'config' | 'preset' | 'custom'\n\n/** A theme the themer tool lists @internal */\nexport interface ThemerTheme {\n slug: string\n title: string\n options: BuildThemeOptions\n source: ThemerThemeSource\n /** The palette of the image the theme's colors were taken from, for custom themes */\n palette?: ImagePalette\n}\n\n/** A theme the user added in the tool, as persisted @internal */\nexport interface CustomTheme {\n slug: string\n title: string\n options: BuildThemeOptions\n /** The palette of the image the theme's colors were taken from */\n palette?: ImagePalette\n}\n\n/** The persisted themer state @internal */\nexport interface ThemerState {\n /**\n * The slug of the applied theme — `null` applies nothing on top of the\n * Studio's configured theme\n */\n active: string | null\n /** The themes the user added */\n custom: CustomTheme[]\n /** The slugs of the themes the user removed from the list */\n removed: string[]\n}\n\n/** The slug of the theme the Studio config was generated from @internal */\nexport const CONFIG_SLUG = 'config'\n\n/** The title of the theme the Studio config was generated from */\nconst CONFIG_TITLE = 'Studio config'\n\n/** The title new themes start out with @internal */\nexport const UNTITLED_THEME = 'Untitled theme'\n\n/** @internal */\nexport const initialThemerState: ThemerState = {active: null, custom: [], removed: []}\n\n/** The themes the tool works with, derived from the persisted state @internal */\nexport interface ResolvedThemes {\n /** The themes to pick from, in list order */\n themes: ThemerTheme[]\n /** The removed themes, which can be restored */\n removed: ThemerTheme[]\n /** The applied theme */\n active: ThemerTheme\n}\n\n/**\n * Resolves the list of themes: the configured theme first, then the presets\n * (minus the ones that would only repeat the configured theme), then the\n * user's own themes. Removed themes are set aside so they can be restored,\n * and the active theme falls back to the configured one when its slug no\n * longer resolves.\n *\n * @internal\n */\nexport function resolveThemes(state: ThemerState, baseOptions: BuildThemeOptions): ResolvedThemes {\n const config: ThemerTheme = {\n slug: CONFIG_SLUG,\n title: CONFIG_TITLE,\n options: baseOptions,\n source: 'config',\n }\n\n const all: ThemerTheme[] = [config]\n\n for (const preset of presets) {\n if (!sameOptions(preset.options, baseOptions)) {\n all.push({slug: preset.slug, title: preset.title, options: preset.options, source: 'preset'})\n }\n }\n\n for (const theme of state.custom) {\n all.push({\n slug: theme.slug,\n title: theme.title,\n options: theme.options,\n source: 'custom',\n ...(theme.palette ? {palette: theme.palette} : {}),\n })\n }\n\n const removedSlugs = new Set(state.removed)\n const themes = all.filter((theme) => !removedSlugs.has(theme.slug))\n const removed = all.filter((theme) => removedSlugs.has(theme.slug))\n const active = themes.find((theme) => theme.slug === state.active) ?? config\n\n return {themes, removed, active}\n}\n\n/**\n * Creates a custom theme with a slug that cannot collide with the presets or\n * the configured theme.\n *\n * @internal\n */\nexport function createCustomTheme(\n title: string,\n options: BuildThemeOptions,\n palette?: ImagePalette,\n): CustomTheme {\n return {slug: `custom-${randomId()}`, title, options, ...(palette ? {palette} : {})}\n}\n\n/** The title of a duplicated theme @internal */\nexport function duplicateTitle(title: string): string {\n return `${displayTitle(title)} copy`\n}\n\n/** The title to show for a theme, falling back when the user cleared it @internal */\nexport function displayTitle(title: string): string {\n return title.trim() || UNTITLED_THEME\n}\n\nfunction randomId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n\n return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`\n}\n","import {assign, not, setup, SnapshotFrom} from 'xstate'\n\nimport {BuildThemeOptions} from '../theme/options'\nimport {ImagePalette} from './imagePalette'\nimport {\n CONFIG_SLUG,\n createCustomTheme,\n duplicateTitle,\n resolveThemes,\n ThemerState,\n UNTITLED_THEME,\n} from './themes'\n\n/** What the themer machine starts from @internal */\nexport interface ThemerInput {\n /** The theme options the Studio's configured theme was generated from */\n baseOptions: BuildThemeOptions\n /** The persisted state of an earlier session */\n stored: ThemerState\n}\n\n/** @internal */\nexport interface ThemerMachineContext extends ThemerState {\n baseOptions: BuildThemeOptions\n /** The theme open in the editor, while the `flow` is `edit` */\n editing: {\n slug: string\n /** Whether the title input should take focus, for themes that were just created */\n focusTitle: boolean\n } | null\n /**\n * Object URLs of the images that themes took their palette from, by theme\n * slug — kept for the session only, so the image can be shown right after\n * it was picked\n */\n images: Record<string, string>\n}\n\n/** @internal */\nexport type ThemerEvent =\n | {type: 'sidebar.toggle'}\n | {type: 'sidebar.close'}\n /**\n * Switches between showing the Studio once, in its own appearance, and\n * twice side by side, in light and dark\n */\n | {type: 'preview.toggle'}\n /** Applies a theme to the whole Studio */\n | {type: 'theme.pick'; slug: string}\n /**\n * Adds a theme and opens it in the editor — based on the applied theme, or\n * on the given title, options and image palette (e.g. from an image)\n */\n | {\n type: 'theme.add'\n title?: string\n options?: BuildThemeOptions\n palette?: ImagePalette\n imageUrl?: string\n }\n /** Adds a copy of a theme and opens it in the editor */\n | {type: 'theme.duplicate'; slug: string}\n /** Opens one of the user's own themes in the editor */\n | {type: 'theme.edit'; slug: string}\n | {\n type: 'theme.update'\n slug: string\n title?: string\n options?: BuildThemeOptions\n palette?: ImagePalette\n imageUrl?: string\n }\n /** Takes a theme out of the list — it can be restored until it is deleted */\n | {type: 'theme.remove'; slug: string}\n /** Puts a removed theme back in the list */\n | {type: 'theme.restore'; slug: string}\n /** Deletes one of the user's own themes for good */\n | {type: 'theme.delete'; slug: string}\n /** Back to picking a theme */\n | {type: 'flow.list'}\n /** On to restoring removed themes */\n | {type: 'flow.removed'}\n\nfunction themesOf(context: ThemerMachineContext) {\n return resolveThemes(context, context.baseOptions)\n}\n\n/**\n * The state of the themer tool: three parallel regions, whether the\n * `sidebar` is open, which `flow` it is in — picking a theme from the `list`,\n * `edit`ing one of the user's own themes, or restoring `removed` ones — and\n * whether the `preview` shows the Studio once (`single`) or in light and dark\n * side by side (`split`). The context carries the persisted state (the\n * applied theme, the user's themes and what was removed) alongside what the\n * flows need.\n *\n * Theme operations are handled in every flow, and the flows leave on their\n * own when they lose their subject: the editor when its theme is removed or\n * deleted, and the removed view when its last theme is restored or deleted.\n *\n * @internal\n */\nexport const themerMachine = setup({\n // XState reads `types` for their types only — the values are never used\n types: {\n // oxlint-disable-next-line no-unsafe-type-assertion -- type-level placeholder\n context: {} as ThemerMachineContext,\n // oxlint-disable-next-line no-unsafe-type-assertion -- type-level placeholder\n events: {} as ThemerEvent,\n // oxlint-disable-next-line no-unsafe-type-assertion -- type-level placeholder\n input: {} as ThemerInput,\n },\n guards: {\n isCustomTheme: ({context}, params: {slug: string}) =>\n themesOf(context).themes.some(\n (theme) => theme.slug === params.slug && theme.source === 'custom',\n ),\n isEditingListedTheme: ({context}) =>\n context.editing !== null &&\n themesOf(context).themes.some(\n (theme) => theme.slug === context.editing?.slug && theme.source === 'custom',\n ),\n hasRemovedThemes: ({context}) => themesOf(context).removed.length > 0,\n },\n actions: {\n pick: assign((_, params: {slug: string}) => ({\n active: params.slug === CONFIG_SLUG ? null : params.slug,\n })),\n add: assign(\n (\n {context},\n params: {\n title?: string\n options?: BuildThemeOptions\n palette?: ImagePalette\n imageUrl?: string\n },\n ) => {\n const theme = createCustomTheme(\n params.title ?? UNTITLED_THEME,\n params.options ?? themesOf(context).active.options,\n params.palette,\n )\n\n return {\n active: theme.slug,\n custom: [...context.custom, theme],\n // A theme named after its image needs no renaming right away\n editing: {slug: theme.slug, focusTitle: params.title === undefined},\n images: params.imageUrl\n ? {...context.images, [theme.slug]: params.imageUrl}\n : context.images,\n }\n },\n ),\n duplicate: assign(({context}, params: {slug: string}) => {\n const {themes, removed} = themesOf(context)\n const source = [...themes, ...removed].find((theme) => theme.slug === params.slug)\n\n if (!source) return {}\n\n const theme = createCustomTheme(duplicateTitle(source.title), source.options, source.palette)\n\n return {\n active: theme.slug,\n custom: [...context.custom, theme],\n editing: {slug: theme.slug, focusTitle: true},\n }\n }),\n startEditing: assign((_, params: {slug: string}) => ({\n active: params.slug,\n editing: {slug: params.slug, focusTitle: false},\n })),\n stopEditing: assign({editing: null}),\n update: assign(\n (\n {context},\n params: {\n slug: string\n title?: string\n options?: BuildThemeOptions\n palette?: ImagePalette\n imageUrl?: string\n },\n ) => ({\n custom: context.custom.map((theme) => {\n if (theme.slug !== params.slug) return theme\n\n // Keep the options identity when only the title changes, so the\n // applied theme is not rebuilt on every keystroke\n return {\n ...theme,\n title: params.title ?? theme.title,\n options: params.options ?? theme.options,\n ...(params.palette ? {palette: params.palette} : {}),\n }\n }),\n images:\n params.imageUrl && context.custom.some((theme) => theme.slug === params.slug)\n ? {...context.images, [params.slug]: params.imageUrl}\n : context.images,\n }),\n ),\n remove: assign(({context}, params: {slug: string}) => {\n if (params.slug === CONFIG_SLUG || context.removed.includes(params.slug)) return {}\n\n return {\n active: context.active === params.slug ? null : context.active,\n removed: [...context.removed, params.slug],\n }\n }),\n restore: assign(({context}, params: {slug: string}) => ({\n removed: context.removed.filter((slug) => slug !== params.slug),\n })),\n delete: assign(({context}, params: {slug: string}) => {\n if (!context.custom.some((theme) => theme.slug === params.slug)) return {}\n\n const {[params.slug]: _deleted, ...images} = context.images\n\n return {\n active: context.active === params.slug ? null : context.active,\n custom: context.custom.filter((theme) => theme.slug !== params.slug),\n removed: context.removed.filter((slug) => slug !== params.slug),\n images,\n }\n }),\n },\n}).createMachine({\n id: 'themer',\n context: ({input}) => ({\n ...input.stored,\n baseOptions: input.baseOptions,\n editing: null,\n images: {},\n }),\n type: 'parallel',\n states: {\n sidebar: {\n initial: 'closed',\n states: {\n closed: {\n on: {'sidebar.toggle': 'open'},\n },\n open: {\n on: {'sidebar.toggle': 'closed', 'sidebar.close': 'closed'},\n },\n },\n },\n // Independent of the sidebar, so the split preview can be looked at\n // without the sidebar taking up room\n preview: {\n initial: 'single',\n states: {\n single: {\n on: {'preview.toggle': 'split'},\n },\n split: {\n on: {'preview.toggle': 'single'},\n },\n },\n },\n flow: {\n initial: 'list',\n on: {\n 'theme.pick': {\n actions: {type: 'pick', params: ({event}) => ({slug: event.slug})},\n },\n 'theme.add': {\n target: '.edit',\n actions: {\n type: 'add',\n params: ({event}) => ({\n title: event.title,\n options: event.options,\n palette: event.palette,\n imageUrl: event.imageUrl,\n }),\n },\n },\n 'theme.duplicate': {\n target: '.edit',\n actions: {type: 'duplicate', params: ({event}) => ({slug: event.slug})},\n },\n 'theme.edit': {\n target: '.edit',\n guard: {type: 'isCustomTheme', params: ({event}) => ({slug: event.slug})},\n actions: {type: 'startEditing', params: ({event}) => ({slug: event.slug})},\n },\n 'theme.update': {\n actions: {\n type: 'update',\n params: ({event}) => ({\n slug: event.slug,\n title: event.title,\n options: event.options,\n palette: event.palette,\n imageUrl: event.imageUrl,\n }),\n },\n },\n 'theme.remove': {\n actions: {type: 'remove', params: ({event}) => ({slug: event.slug})},\n },\n 'theme.restore': {\n actions: {type: 'restore', params: ({event}) => ({slug: event.slug})},\n },\n 'theme.delete': {\n actions: {type: 'delete', params: ({event}) => ({slug: event.slug})},\n },\n 'flow.list': '.list',\n 'flow.removed': {\n target: '.removed',\n guard: 'hasRemovedThemes',\n },\n },\n states: {\n list: {},\n edit: {\n exit: 'stopEditing',\n // The theme being edited can go away — removed from the editor, or\n // duplicated from a theme that does not exist — and then there is\n // nothing left to edit\n always: {guard: not('isEditingListedTheme'), target: 'list'},\n },\n removed: {\n always: {guard: not('hasRemovedThemes'), target: 'list'},\n },\n },\n },\n },\n})\n\n/** @internal */\nexport type ThemerSnapshot = SnapshotFrom<typeof themerMachine>\n\n/** The part of the machine context that is persisted between sessions @internal */\nexport function selectStoredState(snapshot: ThemerSnapshot): ThemerState {\n const {active, custom, removed} = snapshot.context\n\n return {active, custom, removed}\n}\n","import {Box, useTheme_v2} from '@sanity/ui'\nimport {createContext, useContext, useEffect, useMemo, useRef, useState} from 'react'\nimport {styled} from 'styled-components'\n\nconst Root = styled(Box)`\n scrollbar-gutter: stable;\n`\n\n/** How far the content of a scroll area sits from its edges, in pixels */\ninterface ScrollAreaInset {\n left: number\n right: number\n bottom: number\n}\n\nconst ScrollAreaInsetContext = createContext<ScrollAreaInset>({left: 0, right: 0, bottom: 0})\n\n/**\n * The scrolling body of a sidebar flow. It always reserves the scrollbar's\n * space, so the content does not shift when the list grows or shrinks past\n * the point where it needs to scroll — and it takes that space out of its\n * right padding, so the content stays centered rather than hugging the left.\n * With overlay scrollbars there is no gutter and the padding stays even.\n *\n * @internal\n */\nexport function ScrollArea(props: {children: React.ReactNode; padding: number}) {\n const {children, padding} = props\n const {space} = useTheme_v2()\n const ref = useRef<HTMLDivElement | null>(null)\n const [gutter, setGutter] = useState(0)\n\n useEffect(() => {\n const element = ref.current\n\n if (!element) return undefined\n\n // The reserved gutter is the part of the box the content cannot use\n const measure = () => setGutter(element.offsetWidth - element.clientWidth)\n\n measure()\n\n const observer = new ResizeObserver(measure)\n\n observer.observe(element)\n\n return () => observer.disconnect()\n }, [])\n\n const inset = useMemo<ScrollAreaInset>(\n () => ({\n left: space[padding],\n right: Math.max(0, space[padding] - gutter),\n bottom: space[padding],\n }),\n [gutter, padding, space],\n )\n\n return (\n <Root flex={1} overflow=\"auto\" padding={padding} ref={ref} style={{paddingRight: inset.right}}>\n <ScrollAreaInsetContext.Provider value={inset}>{children}</ScrollAreaInsetContext.Provider>\n </Root>\n )\n}\n\n/**\n * Content that runs edge to edge in a scroll area — up to the scrollbar —\n * instead of sitting inside its padding. It goes last: it runs down to the\n * bottom edge too.\n *\n * @internal\n */\nexport function ScrollAreaBleed(props: {children: React.ReactNode}) {\n const {left, right, bottom} = useContext(ScrollAreaInsetContext)\n\n return (\n <div style={{marginLeft: -left, marginRight: -right, marginBottom: -bottom}}>\n {props.children}\n </div>\n )\n}\n","import {ThemeProvider, useTheme_v2} from '@sanity/ui'\nimport {useMemo} from 'react'\nimport {styled} from 'styled-components'\n\nimport {buildTheme} from '../theme/buildTheme'\nimport {BuildThemeOptions} from '../theme/options'\n\n/**\n * The dark scheme covers the right side of the preview behind a diagonal cut,\n * like the \"Auto\" appearance preview in macOS System Settings shows both\n * schemes at once.\n */\nconst DARK_CLIP_PATH = 'polygon(58% 0, 100% 0, 100% 100%, 42% 100%)'\n\nconst Root = styled.div`\n position: relative;\n aspect-ratio: 16 / 9;\n overflow: hidden;\n`\n\n/** The document rows in the list pane — the widths of their title lines */\nconst LIST_ROWS = ['62%', '46%', '70%', '52%']\n\n/** The fields in the document form — the widths of their labels */\nconst FIELDS = ['32%', '24%']\n\n/** The thickness of a line of text — bold enough to survive a half-size preview */\nconst LINE = 4\n\n/**\n * A tiny, lo-fi mockup of a Studio in the given theme — the navbar over the\n * structure tool with a list of documents on the left and a document form on\n * the right — in both color schemes at once: the light scheme on the left and\n * the dark scheme on the right, split diagonally. It only paints colors the\n * theme options change (backgrounds, text, borders, the accent) so that a\n * glance shows what the theme does to the Studio — with few and bold enough\n * elements to still read when scaled down to a thumbnail.\n *\n * @internal\n */\nexport function ThemePreview(props: {options: BuildThemeOptions}) {\n const {options} = props\n const theme = useMemo(() => buildTheme(options), [options])\n\n return (\n <Root aria-hidden>\n <ThemeProvider scheme=\"light\" theme={theme} tone=\"default\">\n <MockStudio />\n </ThemeProvider>\n <ThemeProvider scheme=\"dark\" theme={theme} tone=\"default\">\n <MockStudio style={{clipPath: DARK_CLIP_PATH}} />\n </ThemeProvider>\n </Root>\n )\n}\n\nfunction MockStudio(props: {style?: React.CSSProperties}) {\n const {color} = useTheme_v2()\n const primary = color.button.default.primary.enabled\n const selected = color.selectable.default.selected\n const input = color.input.default.enabled\n const border = `1px solid ${color.border}`\n\n const line = (width: string, background: string): React.CSSProperties => ({\n flex: 'none',\n width,\n height: LINE,\n borderRadius: LINE / 2,\n background,\n })\n\n return (\n <div\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n flexDirection: 'column',\n background: color.bg,\n ...props.style,\n }}\n >\n <div\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n gap: 5,\n height: '18%',\n padding: '0 5%',\n borderBottom: border,\n }}\n >\n <div style={{flex: 'none', width: 8, height: 8, borderRadius: 2, background: primary.bg}} />\n <div style={line('22%', color.fg)} />\n <div style={{flex: 1}} />\n <div\n style={{\n flex: 'none',\n width: 8,\n height: 8,\n borderRadius: '50%',\n background: color.avatar.blue.bg,\n }}\n />\n </div>\n\n <div style={{flex: 1, display: 'flex', minHeight: 0}}>\n <div\n style={{\n flex: 'none',\n display: 'flex',\n flexDirection: 'column',\n width: '36%',\n borderRight: border,\n }}\n >\n {LIST_ROWS.map((width, index) => {\n const isSelected = index === 1\n\n return (\n <div\n key={width}\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n height: '18%',\n padding: '0 9%',\n background: isSelected ? selected.bg : undefined,\n }}\n >\n <div style={line(width, isSelected ? selected.fg : color.muted.fg)} />\n </div>\n )\n })}\n </div>\n\n <div style={{flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0}}>\n <div\n style={{\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n gap: 7,\n padding: '9% 8% 0',\n minHeight: 0,\n overflow: 'hidden',\n }}\n >\n <div style={line('45%', color.fg)} />\n {FIELDS.map((width) => (\n <div\n key={width}\n style={{flex: 'none', display: 'flex', flexDirection: 'column', gap: 3}}\n >\n <div style={line(width, color.muted.fg)} />\n <div\n style={{\n height: 12,\n borderRadius: 3,\n background: input.bg,\n boxShadow: `inset 0 0 0 1px ${input.border}`,\n }}\n />\n </div>\n ))}\n </div>\n <div\n style={{\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n height: '20%',\n padding: '0 8%',\n borderTop: border,\n }}\n >\n <div style={{width: '32%', height: '50%', borderRadius: 3, background: primary.bg}} />\n </div>\n </div>\n </div>\n </div>\n )\n}\n","import {Card} from '@sanity/ui'\n\nimport {BuildThemeOptions} from '../theme/options'\nimport {ThemePreview} from './ThemePreview'\n\n/**\n * The floating preview card of a theme: the lo-fi studio mockup on a raised,\n * rounded card, like the appearance previews in macOS System Settings.\n *\n * @internal\n */\nexport function ThemeThumbnail(props: {options: BuildThemeOptions}) {\n return (\n <Card overflow=\"hidden\" radius={3} shadow={1}>\n <ThemePreview options={props.options} />\n </Card>\n )\n}\n","import {Box, Button, Flex, Stack, Text} from '@sanity/ui'\n\nimport {useThemer} from './context'\nimport {ScrollArea} from './ScrollArea'\nimport {displayTitle} from './themes'\nimport {ThemeThumbnail} from './ThemeThumbnail'\n\n/**\n * The flow for restoring removed themes: the presets and custom themes the\n * user took out of the list, each with a button to put it back — and, for\n * custom themes, one to delete it for good. The machine leaves the flow once\n * the last one is gone.\n *\n * @internal\n */\nexport function RemovedThemes() {\n const {removed, send} = useThemer()\n\n return (\n <ScrollArea padding={3}>\n <Stack gap={5}>\n {removed.map((theme) => (\n <Stack gap={3} key={theme.slug}>\n <Box style={{opacity: 0.6}}>\n <ThemeThumbnail options={theme.options} />\n </Box>\n <Text align=\"center\" size={1} textOverflow=\"ellipsis\">\n {displayTitle(theme.title)}\n </Text>\n <Flex gap={2} justify=\"center\">\n <Button\n fontSize={1}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.restore', slug: theme.slug})}\n padding={2}\n text=\"Restore\"\n />\n {theme.source === 'custom' && (\n <Button\n fontSize={1}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.delete', slug: theme.slug})}\n padding={2}\n text=\"Delete\"\n tone=\"critical\"\n />\n )}\n </Flex>\n </Stack>\n ))}\n </Stack>\n </ScrollArea>\n )\n}\n","import {type ThemeColorSchemeKey} from '@sanity/ui/theme'\n\nimport {hexToHsl, hslToHex, relativeLuminance} from '../theme/hsl'\nimport {BuildThemeOptions, DEFAULT_BACKGROUND, SCHEMES, SchemeThemeOptions} from '../theme/options'\n\n/**\n * The swatches of an image palette — the same set the Sanity image pipeline\n * derives for uploaded assets (`metadata.palette`), extracted on device here.\n *\n * @internal\n */\nexport type ImagePaletteKey =\n | 'dominant'\n | 'vibrant'\n | 'lightVibrant'\n | 'darkVibrant'\n | 'muted'\n | 'lightMuted'\n | 'darkMuted'\n\n/**\n * An image palette: a hex color per swatch, or `null` when the image has no\n * color that qualifies for it.\n *\n * @internal\n */\nexport type ImagePalette = Record<ImagePaletteKey, string | null>\n\n/** @internal */\nexport const IMAGE_PALETTE_KEYS: ImagePaletteKey[] = [\n 'vibrant',\n 'lightVibrant',\n 'darkVibrant',\n 'muted',\n 'lightMuted',\n 'darkMuted',\n 'dominant',\n]\n\n/**\n * The swatches a theme can be built around — every swatch but the dominant\n * one, which only repeats one of them.\n *\n * @internal\n */\nexport type ImagePaletteVariant = Exclude<ImagePaletteKey, 'dominant'>\n\n/** The variants in the order the tool offers them @internal */\nexport const IMAGE_PALETTE_VARIANTS: ImagePaletteVariant[] = [\n 'muted',\n 'vibrant',\n 'lightMuted',\n 'lightVibrant',\n 'darkMuted',\n 'darkVibrant',\n]\n\n/** @internal */\nexport const IMAGE_PALETTE_TITLES: Record<ImagePaletteKey, string> = {\n dominant: 'Dominant',\n vibrant: 'Vibrant',\n lightVibrant: 'Light vibrant',\n darkVibrant: 'Dark vibrant',\n muted: 'Muted',\n lightMuted: 'Light muted',\n darkMuted: 'Dark muted',\n}\n\n/** Pixels the way a canvas hands them out: rows of RGBA bytes @internal */\nexport interface PixelData {\n data: ArrayLike<number>\n width: number\n height: number\n}\n\ninterface Bucket {\n count: number\n r: number\n g: number\n b: number\n}\n\ninterface Candidate {\n hex: string\n population: number\n saturation: number\n lightness: number\n}\n\n/** What a swatch looks for: a lightness and saturation to aim at, within bounds */\ninterface Target {\n lightness: [target: number, min: number, max: number]\n saturation: [target: number, min: number, max: number]\n}\n\n/**\n * The swatch targets of the Vibrant algorithm (the one behind Sanity's image\n * palettes), in the order they get to claim colors.\n */\nconst TARGETS: Array<[key: Exclude<ImagePaletteKey, 'dominant'>, target: Target]> = [\n ['vibrant', {lightness: [0.5, 0.3, 0.7], saturation: [1, 0.35, 1]}],\n ['lightVibrant', {lightness: [0.74, 0.55, 1], saturation: [1, 0.35, 1]}],\n ['darkVibrant', {lightness: [0.26, 0, 0.45], saturation: [1, 0.35, 1]}],\n ['muted', {lightness: [0.5, 0.3, 0.7], saturation: [0.3, 0, 0.4]}],\n ['lightMuted', {lightness: [0.74, 0.55, 1], saturation: [0.3, 0, 0.4]}],\n ['darkMuted', {lightness: [0.26, 0, 0.45], saturation: [0.3, 0, 0.4]}],\n]\n\nconst SATURATION_WEIGHT = 3\nconst LIGHTNESS_WEIGHT = 6.5\nconst POPULATION_WEIGHT = 0.5\n\n/** Pixels this transparent are not part of the picture */\nconst MINIMUM_ALPHA = 125\n\n/** Near-black and near-white pixels are backdrop rather than color */\nconst MINIMUM_LIGHTNESS = 0.05\nconst MAXIMUM_LIGHTNESS = 0.95\n\n/** How many of the 8 bits per channel survive the quantization */\nconst QUANTIZE_BITS = 5\n\n/**\n * Extracts the color palette of an image from its pixels, the way the Vibrant\n * algorithm does: the colors are quantized into a coarse histogram, and each\n * swatch claims the color that comes closest to its target lightness and\n * saturation — weighted by how much of the image it covers — that no other\n * swatch has claimed yet. The dominant swatch is simply the most common\n * color. Runs anywhere pixels come from, so it can run on device.\n *\n * @internal\n */\nexport function extractImagePalette(pixels: PixelData): ImagePalette {\n const candidates = quantize(pixels)\n const palette: ImagePalette = {\n dominant: null,\n vibrant: null,\n lightVibrant: null,\n darkVibrant: null,\n muted: null,\n lightMuted: null,\n darkMuted: null,\n }\n\n if (candidates.length === 0) return palette\n\n const maximumPopulation = Math.max(...candidates.map((candidate) => candidate.population))\n const claimed = new Set<string>()\n\n for (const [key, target] of TARGETS) {\n let best: Candidate | null = null\n let bestScore = -Infinity\n\n for (const candidate of candidates) {\n if (claimed.has(candidate.hex) || !matchesTarget(candidate, target)) continue\n\n const score = scoreCandidate(candidate, target, maximumPopulation)\n\n if (score > bestScore) {\n best = candidate\n bestScore = score\n }\n }\n\n if (best) {\n palette[key] = best.hex\n claimed.add(best.hex)\n }\n }\n\n palette.dominant = candidates.reduce((most, candidate) =>\n candidate.population > most.population ? candidate : most,\n ).hex\n\n return palette\n}\n\n/**\n * Turns the pixels into a histogram of quantized colors, each averaged over\n * the pixels it stands for, leaving out transparent pixels and the near-black\n * and near-white ones that are backdrop rather than color.\n */\nfunction quantize(pixels: PixelData): Candidate[] {\n const {data} = pixels\n const buckets = new Map<number, Bucket>()\n const shift = 8 - QUANTIZE_BITS\n const length = Math.min(data.length, pixels.width * pixels.height * 4)\n\n for (let offset = 0; offset + 3 < length; offset += 4) {\n const r = data[offset]\n const g = data[offset + 1]\n const b = data[offset + 2]\n const a = data[offset + 3]\n\n if (a < MINIMUM_ALPHA) continue\n\n const lightness = (Math.max(r, g, b) + Math.min(r, g, b)) / 510\n\n if (lightness < MINIMUM_LIGHTNESS || lightness > MAXIMUM_LIGHTNESS) continue\n\n const key =\n ((r >> shift) << (QUANTIZE_BITS * 2)) | ((g >> shift) << QUANTIZE_BITS) | (b >> shift)\n const bucket = buckets.get(key)\n\n if (bucket) {\n bucket.count++\n bucket.r += r\n bucket.g += g\n bucket.b += b\n } else {\n buckets.set(key, {count: 1, r, g, b})\n }\n }\n\n const candidates: Candidate[] = []\n\n for (const bucket of buckets.values()) {\n const r = Math.round(bucket.r / bucket.count)\n const g = Math.round(bucket.g / bucket.count)\n const b = Math.round(bucket.b / bucket.count)\n const {saturation, lightness} = saturationAndLightness(r, g, b)\n\n candidates.push({hex: rgbToHex(r, g, b), population: bucket.count, saturation, lightness})\n }\n\n return candidates\n}\n\nfunction matchesTarget(candidate: Candidate, target: Target): boolean {\n return (\n candidate.lightness >= target.lightness[1] &&\n candidate.lightness <= target.lightness[2] &&\n candidate.saturation >= target.saturation[1] &&\n candidate.saturation <= target.saturation[2]\n )\n}\n\nfunction scoreCandidate(candidate: Candidate, target: Target, maximumPopulation: number): number {\n return (\n SATURATION_WEIGHT * (1 - Math.abs(candidate.saturation - target.saturation[0])) +\n LIGHTNESS_WEIGHT * (1 - Math.abs(candidate.lightness - target.lightness[0])) +\n POPULATION_WEIGHT * (candidate.population / maximumPopulation)\n )\n}\n\nfunction saturationAndLightness(r: number, g: number, b: number) {\n const max = Math.max(r, g, b) / 255\n const min = Math.min(r, g, b) / 255\n const lightness = (max + min) / 2\n const delta = max - min\n const saturation =\n delta === 0 ? 0 : lightness > 0.5 ? delta / (2 - max - min) : delta / (max + min)\n\n return {saturation, lightness}\n}\n\nfunction rgbToHex(r: number, g: number, b: number): string {\n return `#${[r, g, b].map((channel) => channel.toString(16).padStart(2, '0')).join('')}`\n}\n\n/** Reads `#rgb` and `#rrggbb` alike — stored palettes may hold either */\nfunction hexToRgb(hex: string): [number, number, number] {\n const expanded =\n hex.length === 4 ? `${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex.slice(1)\n\n return [\n parseInt(expanded.slice(0, 2), 16),\n parseInt(expanded.slice(2, 4), 16),\n parseInt(expanded.slice(4, 6), 16),\n ]\n}\n\n/** Mixes `amount` of `color` into `base` */\nfunction tint(base: string, color: string, amount: number): string {\n const [r1, g1, b1] = hexToRgb(base)\n const [r2, g2, b2] = hexToRgb(color)\n\n return rgbToHex(\n Math.round(r1 + (r2 - r1) * amount),\n Math.round(g1 + (g2 - g1) * amount),\n Math.round(b1 + (b2 - b1) * amount),\n )\n}\n\n/** How much of the palette tints each scheme's stock background */\nconst BACKGROUND_TINT = {light: 0.08, dark: 0.3}\n\n/**\n * The WCAG AA contrast for normal text — what a button label needs against\n * the button. Sanity UI labels solid buttons in white on the accent's 500\n * tint in the light scheme and in black on its 400 tint in the dark scheme\n * (lighter than the accent, so an accent that passes against black passes\n * there too).\n */\nconst MINIMUM_ACCENT_CONTRAST = 4.5\nconst BUTTON_LABEL: Record<ThemeColorSchemeKey, string> = {light: '#ffffff', dark: '#000000'}\nconst ACCENT_LIGHTNESS_STEP = 0.01\n\nfunction contrastRatio(a: string, b: string): number {\n const [lighter, darker] = [relativeLuminance(a), relativeLuminance(b)].sort((x, y) => y - x)\n\n return (lighter + 0.05) / (darker + 0.05)\n}\n\n/**\n * Makes an accent readable as a button in the given scheme: a light scheme\n * accent is darkened until white text on it reaches AA contrast, a dark\n * scheme accent lightened until black text does — as far as the accent's\n * hue and saturation allow. Accents that already pass come back untouched.\n *\n * @internal\n */\nexport function readableAccent(hex: string, scheme: ThemeColorSchemeKey): string {\n const label = BUTTON_LABEL[scheme]\n const {h, s} = hexToHsl(hex)\n let {l} = hexToHsl(hex)\n let current = hex\n\n while (contrastRatio(current, label) < MINIMUM_ACCENT_CONTRAST) {\n const next = scheme === 'light' ? l - ACCENT_LIGHTNESS_STEP : l + ACCENT_LIGHTNESS_STEP\n\n if (next < 0 || next > 1) break\n\n l = next\n current = hslToHex({h, s, l})\n }\n\n return current\n}\n\n/** The accent a variant gives a scheme, made readable on a button */\nfunction variantAccent(\n palette: ImagePalette,\n variant: ImagePaletteVariant | null,\n scheme: ThemeColorSchemeKey,\n): string | null {\n const swatch =\n (variant ? palette[variant] : null) ??\n palette.vibrant ??\n (scheme === 'light' ? palette.darkVibrant : palette.lightVibrant) ??\n palette.dominant\n\n return swatch === null ? null : readableAccent(swatch, scheme)\n}\n\n/**\n * Derives theme options from an image palette: the accent of both schemes is\n * the swatch of the chosen variant — or, without one, the vibrant swatch,\n * each scheme falling back to the vibrant swatch of its own end of the scale —\n * adjusted until it is readable as a button. The muted swatch becomes the text color, and the light and dark muted\n * swatches tint the stock backgrounds of their scheme — enough to carry the\n * image's tone without giving up a usable canvas. Whatever the palette lacks\n * stays at the theme defaults.\n *\n * @internal\n */\nexport function optionsFromImagePalette(\n palette: ImagePalette,\n variant?: ImagePaletteVariant,\n): BuildThemeOptions {\n const options: BuildThemeOptions = {}\n\n const light = schemeFromPalette(\n variantAccent(palette, variant ?? null, 'light'),\n palette.muted ?? palette.darkMuted,\n palette.lightMuted ?? palette.muted ?? palette.lightVibrant,\n 'light',\n )\n const dark = schemeFromPalette(\n variantAccent(palette, variant ?? null, 'dark'),\n palette.muted ?? palette.lightMuted,\n palette.darkMuted ?? palette.muted ?? palette.darkVibrant,\n 'dark',\n )\n\n if (light) options.light = light\n if (dark) options.dark = dark\n\n return options\n}\n\nfunction schemeFromPalette(\n accent: string | null,\n text: string | null,\n backgroundTint: string | null,\n scheme: 'light' | 'dark',\n): SchemeThemeOptions | null {\n const options: SchemeThemeOptions = {}\n\n if (accent) options.accent = accent\n if (text) options.text = text\n if (backgroundTint) {\n options.background = tint(DEFAULT_BACKGROUND[scheme], backgroundTint, BACKGROUND_TINT[scheme])\n }\n\n return Object.keys(options).length > 0 ? options : null\n}\n\n/**\n * Applies an image palette to existing theme options: the colors the palette\n * provides replace the current ones scheme by scheme, and whatever it does\n * not touch — like the contrast — stays as it is.\n *\n * @internal\n */\nexport function applyImagePalette(\n options: BuildThemeOptions,\n palette: ImagePalette,\n variant?: ImagePaletteVariant,\n): BuildThemeOptions {\n const derived = optionsFromImagePalette(palette, variant)\n const next: BuildThemeOptions = {...options}\n\n for (const scheme of SCHEMES) {\n if (derived[scheme]) next[scheme] = {...options[scheme], ...derived[scheme]}\n }\n\n return next\n}\n\n/**\n * The variant a theme is built around: the one whose swatch is the theme's\n * accent, or `null` when the accent comes from somewhere else.\n *\n * @internal\n */\nexport function currentImageVariant(\n options: BuildThemeOptions,\n palette: ImagePalette,\n): ImagePaletteVariant | null {\n const accent = options.light?.accent\n\n if (!accent) return null\n\n return (\n IMAGE_PALETTE_VARIANTS.find(\n (variant) => palette[variant] !== null && variantAccent(palette, variant, 'light') === accent,\n ) ?? null\n )\n}\n\n/**\n * How much a swatch has going for it as an accent: saturation first, then\n * closeness to mid lightness — the range where an accent reads best.\n */\nfunction interest(hex: string): number {\n const {s, l} = hexToHsl(hex)\n\n return 0.1 + 0.55 * s + 0.35 * (1 - Math.abs(l - 0.5) * 2)\n}\n\n/**\n * Picks a variant for the \"I'm feeling lucky\" button: a random one, weighted\n * by how interesting its swatch is as an accent, and never the one the theme\n * already uses while there are others to pick from.\n *\n * @internal\n */\nexport function pickLuckyVariant(\n palette: ImagePalette,\n options: {exclude?: ImagePaletteVariant | null; random?: () => number} = {},\n): ImagePaletteVariant | null {\n const {exclude = null, random = Math.random} = options\n let candidates = IMAGE_PALETTE_VARIANTS.filter((variant) => palette[variant] !== null)\n\n if (candidates.length > 1) {\n candidates = candidates.filter((variant) => variant !== exclude)\n }\n\n if (candidates.length === 0) return null\n\n const weights = candidates.map((variant) => interest(palette[variant]!))\n const total = weights.reduce((sum, weight) => sum + weight, 0)\n let pick = random() * total\n\n for (const [index, variant] of candidates.entries()) {\n pick -= weights[index]\n\n if (pick < 0) return variant\n }\n\n return candidates.at(-1) ?? null\n}\n\n/** A theme title for the image a palette came from, e.g. `sunset-beach.jpg` → `sunset beach` @internal */\nexport function titleFromFileName(name: string): string {\n return (\n name\n .replace(/\\.[^.]+$/, '')\n .replace(/[-_]+/g, ' ')\n .trim() || 'Image theme'\n )\n}\n","import {Button, ButtonProps, Text} from '@sanity/ui'\nimport {Tooltip} from '@sanity/ui/tooltip'\n\n/**\n * A button with an animated tooltip instead of a `title` attribute. Icon-only\n * buttons take the tooltip as their accessible name.\n *\n * @internal\n */\nexport function TooltipButton(props: ButtonProps & {tooltip: string}) {\n const {tooltip, ...buttonProps} = props\n\n return (\n <Tooltip animate content={<Text size={1}>{tooltip}</Text>} placement=\"bottom\" portal>\n <Button aria-label={buttonProps.text === undefined ? tooltip : undefined} {...buttonProps} />\n </Tooltip>\n )\n}\n","import {ImageIcon} from '@sanity/icons/Image'\nimport {ButtonProps} from '@sanity/ui'\nimport {useRef} from 'react'\n\nimport {TooltipButton} from './TooltipButton'\n\n/**\n * A button that opens the file picker for an image, and hands the chosen file\n * over — the picker is a hidden file input, so nothing is uploaded anywhere.\n *\n * @internal\n */\nexport function ImageFileButton(\n props: {onFile: (file: File) => void; tooltip: string} & Pick<\n ButtonProps,\n 'disabled' | 'loading' | 'mode' | 'padding' | 'text' | 'width'\n >,\n) {\n const {onFile, ...buttonProps} = props\n const inputRef = useRef<HTMLInputElement | null>(null)\n\n return (\n <>\n <TooltipButton {...buttonProps} icon={ImageIcon} onClick={() => inputRef.current?.click()} />\n <input\n accept=\"image/*\"\n hidden\n onChange={(event) => {\n const file = event.currentTarget.files?.[0]\n\n // Picking the same file again should work, so the input forgets it\n event.currentTarget.value = ''\n\n if (file) onFile(file)\n }}\n ref={inputRef}\n type=\"file\"\n />\n </>\n )\n}\n","import {PixelData} from './imagePalette'\n\n/**\n * The longest side the image is scaled down to before its colors are read —\n * a few thousand pixels are plenty for a palette, and keep it instant\n */\nconst SAMPLE_SIZE = 96\n\n/**\n * Reads the pixels of an image file on device: the image is decoded, drawn\n * onto a small canvas and read back, so nothing leaves the browser.\n *\n * @internal\n */\nexport async function readImagePixels(file: Blob): Promise<PixelData> {\n const image = await decodeImage(file)\n\n try {\n const scale = Math.min(1, SAMPLE_SIZE / Math.max(image.width, image.height))\n const width = Math.max(1, Math.round(image.width * scale))\n const height = Math.max(1, Math.round(image.height * scale))\n const canvas = document.createElement('canvas')\n\n canvas.width = width\n canvas.height = height\n\n const context = canvas.getContext('2d', {willReadFrequently: true})\n\n if (!context) throw new Error('Could not draw the image')\n\n context.drawImage(image, 0, 0, width, height)\n\n return {data: context.getImageData(0, 0, width, height).data, width, height}\n } finally {\n // Where `createImageBitmap` is missing, `ImageBitmap` may be too\n if (typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap) image.close()\n }\n}\n\nasync function decodeImage(file: Blob): Promise<ImageBitmap | HTMLImageElement> {\n if (typeof createImageBitmap === 'function') {\n try {\n return await createImageBitmap(file)\n } catch {\n // Some browsers cannot decode every format this way (e.g. SVG) — an\n // image element handles whatever the browser can display\n }\n }\n\n const url = URL.createObjectURL(file)\n\n try {\n return await new Promise<HTMLImageElement>((resolve, reject) => {\n const image = new Image()\n\n image.addEventListener('load', () => resolve(image), {once: true})\n image.addEventListener('error', () => reject(new Error('Could not decode the image')), {\n once: true,\n })\n image.src = url\n })\n } finally {\n URL.revokeObjectURL(url)\n }\n}\n","import {useToast} from '@sanity/ui/toast'\nimport {useCallback, useEffect, useRef, useState} from 'react'\n\nimport {readImagePixels} from './imageFile'\nimport {extractImagePalette, ImagePalette} from './imagePalette'\n\n/**\n * Extracts the palette of image files on device and hands it to `onPalette`,\n * along with the file it came from. Failures end up as a toast.\n *\n * @internal\n */\nexport function useImagePalette(onPalette: (palette: ImagePalette, file: File) => void): {\n busy: boolean\n pickImage: (file: File) => void\n} {\n const toast = useToast()\n const [busy, setBusy] = useState(false)\n const onPaletteRef = useRef(onPalette)\n const mountedRef = useRef(true)\n // Picks are numbered so that a slow earlier image cannot overwrite a later\n // pick once its extraction finishes\n const latestPickRef = useRef(0)\n\n useEffect(() => {\n onPaletteRef.current = onPalette\n }, [onPalette])\n\n useEffect(() => {\n mountedRef.current = true\n\n return () => {\n mountedRef.current = false\n }\n }, [])\n\n const pickImage = useCallback(\n (file: File) => {\n const pick = ++latestPickRef.current\n\n setBusy(true)\n\n // Whether this pick still matters: it is the latest one and the\n // component that asked for it is still around\n const isCurrent = () => mountedRef.current && latestPickRef.current === pick\n\n const extract = async () => {\n const palette = extractImagePalette(await readImagePixels(file))\n\n if (!isCurrent()) return\n\n if (Object.values(palette).every((swatch) => swatch === null)) {\n toast.push({status: 'warning', title: 'Found no colors in that image'})\n } else {\n onPaletteRef.current(palette, file)\n }\n }\n\n extract()\n .catch(() => {\n if (isCurrent()) toast.push({status: 'error', title: 'Could not read that image'})\n })\n .finally(() => {\n if (isCurrent()) setBusy(false)\n })\n },\n [toast],\n )\n\n return {busy, pickImage}\n}\n","import {SparklesIcon} from '@sanity/icons/Sparkles'\nimport {Box, Card, Flex, Stack, Text} from '@sanity/ui'\nimport {Tooltip} from '@sanity/ui/tooltip'\nimport {useEffect, useMemo, useRef, useState} from 'react'\nimport {styled} from 'styled-components'\n\nimport {BuildThemeOptions} from '../theme/options'\nimport {ImageFileButton} from './ImageFileButton'\nimport {\n applyImagePalette,\n currentImageVariant,\n IMAGE_PALETTE_TITLES,\n IMAGE_PALETTE_VARIANTS,\n ImagePalette,\n ImagePaletteKey,\n ImagePaletteVariant,\n} from './imagePalette'\nimport {ThemeThumbnail} from './ThemeThumbnail'\nimport {TooltipButton} from './TooltipButton'\nimport {useImagePalette} from './useImagePalette'\n\n/** The swatches on display: the vibrant family over the muted one */\nconst TILES: ImagePaletteKey[] = [\n 'vibrant',\n 'lightVibrant',\n 'darkVibrant',\n 'muted',\n 'lightMuted',\n 'darkMuted',\n]\n\nconst TILE_SIZE = 24\nconst TILE_GAP = 4\n\n/** The image spans both rows of tiles */\nconst IMAGE_SIZE = TILE_SIZE * 2 + TILE_GAP\n\n/** The variant previews are the theme thumbnails, laid out at this width and scaled down */\nconst VARIANT_WIDTH = 144\n\n/**\n * How many variants show at once: two and a half, so that the cut-off third\n * one gives away that the row scrolls\n */\nconst VISIBLE_VARIANTS = 2.5\nconst VARIANT_GAP = 8\nconst ROW_PADDING = 12\n\n/** The width of one variant, before the row has been measured */\nconst FALLBACK_VARIANT_WIDTH = 58\n\nconst PaletteGrid = styled.div`\n display: grid;\n grid-template-columns: repeat(3, ${TILE_SIZE}px);\n grid-auto-rows: ${TILE_SIZE}px;\n gap: ${TILE_GAP}px;\n\n &[data-with-image='true'] {\n grid-template-columns: ${IMAGE_SIZE}px repeat(3, ${TILE_SIZE}px);\n }\n`\n\nconst ImageTile = styled.img`\n display: block;\n grid-row: span 2;\n width: ${IMAGE_SIZE}px;\n height: ${IMAGE_SIZE}px;\n border-radius: 3px;\n object-fit: cover;\n box-shadow: inset 0 0 0 1px var(--card-border-color);\n`\n\nconst Tile = styled.span`\n display: block;\n border-radius: 3px;\n box-shadow: inset 0 0 0 1px var(--card-border-color);\n\n &[data-empty='true'] {\n background: repeating-linear-gradient(\n -45deg,\n transparent 0 3px,\n var(--card-border-color) 3px 4px\n );\n }\n`\n\n/**\n * The variants scroll sideways and snap into place, bleeding into the card's\n * padding so the row runs from edge to edge\n */\nconst VariantRow = styled.div`\n display: flex;\n gap: ${VARIANT_GAP}px;\n margin: 0 -${ROW_PADDING}px;\n padding: 0 ${ROW_PADDING}px;\n overflow-x: auto;\n scroll-snap-type: x mandatory;\n scroll-padding-left: ${ROW_PADDING}px;\n scrollbar-width: thin;\n`\n\nconst VariantButton = styled.button`\n appearance: none;\n flex: none;\n display: block;\n box-sizing: border-box;\n width: calc(var(--variant-width) + 8px);\n margin: 0;\n padding: 0;\n border: 0;\n background: none;\n color: inherit;\n font: inherit;\n text-align: center;\n cursor: pointer;\n scroll-snap-align: start;\n\n &:focus {\n outline: none;\n }\n`\n\n/**\n * Clips the scaled-down thumbnail to its visual size, with room for the ring\n * — drawn inside the frame, like the theme cards', so the scrolling row\n * never clips it\n */\nconst VariantFrame = styled.span`\n display: block;\n box-sizing: border-box;\n width: calc(var(--variant-width) + 8px);\n height: calc(var(--variant-width) * 9 / 16 + 8px);\n padding: 4px;\n border-radius: 7px;\n overflow: hidden;\n transition: box-shadow 100ms;\n\n ${VariantButton}:hover & {\n box-shadow: inset 0 0 0 2px var(--card-border-color);\n }\n\n ${VariantButton}[aria-pressed='true'] & {\n box-shadow: inset 0 0 0 2px var(--card-focus-ring-color);\n }\n\n ${VariantButton}:focus-visible & {\n outline: 2px solid var(--card-focus-ring-color);\n outline-offset: -2px;\n }\n`\n\nconst VariantScale = styled.span`\n display: block;\n width: ${VARIANT_WIDTH}px;\n transform: scale(var(--variant-scale));\n transform-origin: top left;\n`\n\n/**\n * Takes the colors of a theme from an image, on device: the image is read\n * into a canvas and its palette extracted right there — nothing is uploaded.\n * Shows the image (while the session lasts) next to its palette, offers a\n * theme variant built around each swatch as a small preview, and a lucky\n * button that picks an interesting one.\n *\n * @internal\n */\nexport function ImagePaletteSection(props: {\n imageUrl?: string\n onPalette: (palette: ImagePalette, file: File) => void\n onVariant: (variant: ImagePaletteVariant) => void\n onLucky: () => void\n options: BuildThemeOptions\n palette?: ImagePalette\n}) {\n const {imageUrl, onPalette, onVariant, onLucky, options, palette} = props\n const {busy, pickImage} = useImagePalette(onPalette)\n const [dragging, setDragging] = useState(false)\n // `dragleave` also fires when the pointer crosses into a child, so the\n // highlight tracks how deep the drag is rather than the last event\n const dragDepth = useRef(0)\n const current = palette ? currentImageVariant(options, palette) : null\n const rowRef = useRef<HTMLDivElement | null>(null)\n const [variantWidth, setVariantWidth] = useState(FALLBACK_VARIANT_WIDTH)\n\n // Size the variants to the row, so two and a half of them are in view\n useEffect(() => {\n const row = rowRef.current\n\n if (!row) return undefined\n\n const measure = () => {\n const visible =\n row.clientWidth - ROW_PADDING - (Math.ceil(VISIBLE_VARIANTS) - 1) * VARIANT_GAP\n\n // Each variant is its thumbnail plus the 4px frame on either side\n setVariantWidth(Math.max(24, Math.floor(visible / VISIBLE_VARIANTS) - 8))\n }\n\n measure()\n\n const observer = new ResizeObserver(measure)\n\n observer.observe(row)\n\n return () => observer.disconnect()\n }, [palette])\n\n const variants = useMemo(() => {\n if (!palette) return []\n\n return IMAGE_PALETTE_VARIANTS.flatMap((variant) =>\n palette[variant] === null\n ? []\n : [{variant, options: applyImagePalette(options, palette, variant)}],\n )\n }, [options, palette])\n\n return (\n <Card\n border\n onDragEnter={(event) => {\n event.preventDefault()\n dragDepth.current++\n setDragging(true)\n }}\n onDragLeave={() => {\n dragDepth.current = Math.max(0, dragDepth.current - 1)\n\n if (dragDepth.current === 0) setDragging(false)\n }}\n onDragOver={(event) => {\n event.preventDefault()\n }}\n onDrop={(event) => {\n event.preventDefault()\n dragDepth.current = 0\n setDragging(false)\n\n const file = event.dataTransfer.files[0]\n\n if (file) pickImage(file)\n }}\n padding={3}\n radius={3}\n tone={dragging ? 'primary' : 'default'}\n >\n <Stack gap={3}>\n <Flex align=\"center\" gap={2}>\n <Box flex={1}>\n <Text size={1} weight=\"medium\">\n Image palette\n </Text>\n </Box>\n {palette && (\n <ImageFileButton\n loading={busy}\n mode=\"bleed\"\n onFile={pickImage}\n padding={2}\n tooltip=\"Pick another image\"\n />\n )}\n </Flex>\n\n {palette ? (\n <>\n <PaletteGrid data-with-image={imageUrl !== undefined}>\n {imageUrl && <ImageTile alt=\"\" src={imageUrl} />}\n {TILES.map((key) => {\n const hex = palette[key]\n const title = IMAGE_PALETTE_TITLES[key]\n\n return (\n <Tooltip\n animate\n content={\n <Text size={1}>\n {hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`}\n </Text>\n }\n key={key}\n placement=\"bottom\"\n portal\n >\n <Tile data-empty={hex === null} style={{background: hex ?? undefined}} />\n </Tooltip>\n )\n })}\n </PaletteGrid>\n\n <Stack gap={2}>\n <Text muted size={0}>\n Pick the swatch to build the theme around\n </Text>\n <VariantRow\n ref={rowRef}\n style={{\n ['--variant-width' as string]: `${variantWidth}px`,\n ['--variant-scale' as string]: variantWidth / VARIANT_WIDTH,\n }}\n >\n {variants.map(({variant, options: variantOptions}) => (\n <VariantButton\n aria-pressed={variant === current}\n key={variant}\n onClick={() => onVariant(variant)}\n type=\"button\"\n >\n <VariantFrame>\n <VariantScale>\n <ThemeThumbnail options={variantOptions} />\n </VariantScale>\n </VariantFrame>\n <Box paddingTop={1}>\n <Text\n align=\"center\"\n size={0}\n textOverflow=\"ellipsis\"\n weight={variant === current ? 'medium' : 'regular'}\n >\n {IMAGE_PALETTE_TITLES[variant]}\n </Text>\n </Box>\n </VariantButton>\n ))}\n </VariantRow>\n </Stack>\n\n <TooltipButton\n icon={SparklesIcon}\n mode=\"ghost\"\n onClick={onLucky}\n text=\"I'm feeling lucky\"\n tooltip=\"Build the theme around a swatch that looks interesting\"\n width=\"fill\"\n />\n </>\n ) : (\n <Stack gap={3}>\n <Text muted size={0}>\n Take the accent, text and background colors from an image — read on device, nothing is\n uploaded.\n </Text>\n <ImageFileButton\n loading={busy}\n mode=\"ghost\"\n onFile={pickImage}\n text=\"Pick an image\"\n tooltip=\"Take the colors from an image on your device\"\n width=\"fill\"\n />\n </Stack>\n )}\n </Stack>\n </Card>\n )\n}\n","import {ResetIcon} from '@sanity/icons/Reset'\nimport {TrashIcon} from '@sanity/icons/Trash'\nimport {Badge, Box, Button, Card, Flex, Stack, Text, TextInput} from '@sanity/ui'\nimport {type ThemeColorSchemeKey} from '@sanity/ui/theme'\nimport {useMemo} from 'react'\nimport {useColorSchemeValue} from 'sanity'\nimport {styled} from 'styled-components'\n\nimport {buildPalette, GeneratedColorPalette} from '../theme/buildPalette'\nimport {\n BuildThemeOptions,\n ResolvedSchemeOptions,\n resolveThemeOptions,\n SCHEMES,\n SchemeThemeOptions,\n} from '../theme/options'\nimport {useThemer} from './context'\nimport {\n applyImagePalette,\n currentImageVariant,\n ImagePalette,\n ImagePaletteVariant,\n pickLuckyVariant,\n} from './imagePalette'\nimport {ImagePaletteSection} from './ImagePaletteSection'\nimport {ScrollArea, ScrollAreaBleed} from './ScrollArea'\nimport {ThemeThumbnail} from './ThemeThumbnail'\nimport {TooltipButton} from './TooltipButton'\n\nconst SCHEME_TITLES: Record<ThemeColorSchemeKey, string> = {\n dark: 'Dark mode',\n light: 'Light mode',\n}\n\n/** Where the split preview shows each scheme */\nconst SPLIT_HINTS: Record<ThemeColorSchemeKey, string> = {\n dark: 'Shown on the right',\n light: 'Shown on the left',\n}\n\nconst SCHEME_OPTION_KEYS: Array<keyof SchemeThemeOptions> = [\n 'accent',\n 'text',\n 'background',\n 'contrast',\n]\n\n/**\n * `<input type=\"color\">` paints the color into a shadow-DOM swatch that brings\n * its own border and padding, which then sits inside ours as a second border.\n * Stripping that chrome leaves the themed border as the only one.\n */\nconst Swatch = styled.input`\n box-sizing: border-box;\n flex: none;\n width: 33px;\n height: 33px;\n padding: 0;\n border: 1px solid var(--card-border-color);\n border-radius: 4px;\n background: none;\n cursor: pointer;\n\n &::-webkit-color-swatch-wrapper {\n padding: 0;\n }\n\n &::-webkit-color-swatch {\n border: none;\n border-radius: 3px;\n }\n\n &::-moz-color-swatch {\n border: none;\n border-radius: 3px;\n }\n`\n\n/** A native range input, themed through `accent-color` */\nconst Range = styled.input`\n display: block;\n width: 100%;\n margin: 0;\n accent-color: var(--card-focus-ring-color);\n`\n\n/**\n * The flow for editing one of the user's own themes: its title, a live\n * preview, and a card per color scheme with the accent/text/background\n * pickers and the contrast slider of that scheme. Every change applies to the\n * whole Studio right away — in the scheme the Studio is showing, or in both\n * side by side in the split preview.\n *\n * @internal\n */\nexport function ThemeEditor(props: {focusTitle: boolean; slug: string}) {\n const {focusTitle, slug} = props\n const {themes, images, split, send} = useThemer()\n const theme = themes.find((candidate) => candidate.slug === slug && candidate.source === 'custom')\n\n // The machine only enters the edit flow for a listed custom theme, and\n // leaves it as soon as the theme is removed or deleted\n if (!theme) return null\n\n const applyVariant = (variant: ImagePaletteVariant | null) => {\n if (!theme.palette || variant === null) return\n\n send({\n type: 'theme.update',\n slug,\n options: applyImagePalette(theme.options, theme.palette, variant),\n })\n }\n\n return (\n <ThemeEditorForm\n focusTitle={focusTitle}\n imageUrl={images[slug]}\n onDone={() => send({type: 'flow.list'})}\n onLucky={() => {\n if (!theme.palette) return\n\n applyVariant(\n pickLuckyVariant(theme.palette, {\n exclude: currentImageVariant(theme.options, theme.palette),\n }),\n )\n }}\n onOptionsChange={(options) => send({type: 'theme.update', slug, options})}\n onPalette={(palette, file) =>\n send({\n type: 'theme.update',\n slug,\n options: applyImagePalette(theme.options, palette),\n palette,\n imageUrl: URL.createObjectURL(file),\n })\n }\n onRemove={() => send({type: 'theme.remove', slug})}\n onTitleChange={(title) => send({type: 'theme.update', slug, title})}\n onVariant={applyVariant}\n options={theme.options}\n palette={theme.palette}\n split={split}\n title={theme.title}\n />\n )\n}\n\n// Split from `ThemeEditor` so that the derived values below can be hooks\n// while the editor bails out early when the theme is gone\nfunction ThemeEditorForm(props: {\n focusTitle: boolean\n imageUrl?: string\n onDone: () => void\n onLucky: () => void\n onOptionsChange: (options: BuildThemeOptions) => void\n onPalette: (palette: ImagePalette, file: File) => void\n onRemove: () => void\n onTitleChange: (title: string) => void\n onVariant: (variant: ImagePaletteVariant) => void\n options: BuildThemeOptions\n palette?: ImagePalette\n /** Whether the Studio shows in light and dark side by side */\n split: boolean\n title: string\n}) {\n const {\n focusTitle,\n imageUrl,\n onDone,\n onLucky,\n onOptionsChange,\n onPalette,\n onRemove,\n onTitleChange,\n onVariant,\n options,\n palette,\n split,\n title,\n } = props\n // The scheme the Studio is showing, with the appearance setting resolved\n const studioScheme = useColorSchemeValue()\n const resolved = useMemo(() => resolveThemeOptions(options), [options])\n const palettes = useMemo(() => buildPalette(options), [options])\n\n const patchScheme = (scheme: ThemeColorSchemeKey, changes: Partial<SchemeThemeOptions>) => {\n const values: SchemeThemeOptions = {...options[scheme], ...changes}\n\n // Clearing an option means falling back to its default, which is its absence\n for (const key of SCHEME_OPTION_KEYS) {\n if (values[key] === undefined) delete values[key]\n }\n\n const next: BuildThemeOptions = {...options}\n\n if (Object.keys(values).length === 0) {\n delete next[scheme]\n } else {\n next[scheme] = values\n }\n\n onOptionsChange(next)\n }\n\n return (\n <>\n <ScrollArea padding={3}>\n <Stack gap={4}>\n <ThemeThumbnail options={options} />\n\n <Stack gap={2}>\n <Text size={1} weight=\"medium\">\n Title\n </Text>\n <TextInput\n autoFocus={focusTitle}\n fontSize={1}\n onChange={(event) => onTitleChange(event.currentTarget.value)}\n onFocus={focusTitle ? (event) => event.currentTarget.select() : undefined}\n padding={2}\n placeholder=\"Untitled theme\"\n value={title}\n />\n </Stack>\n\n <ImagePaletteSection\n imageUrl={imageUrl}\n onLucky={onLucky}\n onPalette={onPalette}\n onVariant={onVariant}\n options={options}\n palette={palette}\n />\n\n <ScrollAreaBleed>\n {SCHEMES.map((scheme) => (\n <SchemeCard\n active={split || scheme === studioScheme}\n key={scheme}\n onChange={(changes) => patchScheme(scheme, changes)}\n options={options[scheme] ?? {}}\n palette={palettes[scheme]}\n resolved={resolved[scheme]}\n scheme={scheme}\n split={split}\n />\n ))}\n </ScrollAreaBleed>\n </Stack>\n </ScrollArea>\n\n <Card borderTop padding={3}>\n <Flex gap={2}>\n <TooltipButton\n icon={TrashIcon}\n mode=\"ghost\"\n onClick={onRemove}\n text=\"Remove\"\n tone=\"critical\"\n tooltip=\"Remove the theme — it can be restored until it is deleted\"\n />\n <Box flex={1} />\n <Button mode=\"ghost\" onClick={onDone} text=\"Done\" />\n </Flex>\n </Card>\n </>\n )\n}\n\n/**\n * The colors of one scheme. The cards span the sidebar edge to edge, one\n * after the other with a border between them, in the sidebar's own scheme —\n * the swatches show the colors they edit. The scheme the Studio is showing is\n * marked as active: its changes show up in the Studio right away, the other\n * scheme's only once the appearance setting switches to it — or both are\n * active, in the split preview.\n */\nfunction SchemeCard(props: {\n active: boolean\n onChange: (changes: Partial<SchemeThemeOptions>) => void\n options: SchemeThemeOptions\n palette: GeneratedColorPalette\n resolved: ResolvedSchemeOptions\n scheme: ThemeColorSchemeKey\n split: boolean\n}) {\n const {active, onChange, options, palette, resolved, scheme, split} = props\n const name = SCHEME_TITLES[scheme]\n\n return (\n <Card borderTop padding={3}>\n <Stack gap={4}>\n <Stack gap={2}>\n <Flex align=\"center\" gap={2}>\n <Box flex={1}>\n <Text size={1} weight=\"medium\">\n {name}\n </Text>\n </Box>\n {active && (\n <Badge fontSize={0} tone=\"primary\">\n Active\n </Badge>\n )}\n </Flex>\n <Text muted size={0}>\n {split\n ? SPLIT_HINTS[scheme]\n : active\n ? 'Shown in the Studio right now'\n : `Shown once the Studio appearance is ${scheme}`}\n </Text>\n </Stack>\n\n <ColorRow\n auto={options.accent === undefined}\n autoLabel=\"default\"\n label={`${name} accent`}\n onChange={(accent) => onChange({accent})}\n onClear={() => onChange({accent: undefined})}\n title=\"Accent\"\n value={resolved.accent}\n />\n <ColorRow\n auto={options.text === undefined}\n label={`${name} text`}\n onChange={(text) => onChange({text})}\n onClear={() => onChange({text: undefined})}\n title=\"Text\"\n value={resolved.text}\n />\n <ColorRow\n adjusted={scheme === 'dark' ? palette.black : palette.white}\n auto={options.background === undefined}\n label={`${name} background`}\n onChange={(background) => onChange({background})}\n onClear={() => onChange({background: undefined})}\n title=\"Background\"\n value={resolved.background}\n />\n\n <Stack gap={3}>\n <Flex align=\"center\" gap={2}>\n <Stack flex={1} gap={2}>\n <Text size={1}>Contrast</Text>\n <Text muted size={0}>\n {resolved.contrast}\n {options.contrast === undefined ? ' · auto' : ''}\n </Text>\n </Stack>\n {options.contrast !== undefined && (\n <TooltipButton\n icon={ResetIcon}\n mode=\"bleed\"\n onClick={() => onChange({contrast: undefined})}\n padding={2}\n tooltip=\"Reset to auto\"\n />\n )}\n </Flex>\n <Range\n aria-label={`${name} contrast`}\n max={100}\n min={15}\n onChange={(event) => onChange({contrast: Number(event.currentTarget.value)})}\n step={1}\n type=\"range\"\n value={resolved.contrast}\n />\n <Text muted size={0}>\n 100 keeps text and borders neutral — lower values blend in the accent\n </Text>\n </Stack>\n </Stack>\n </Card>\n )\n}\n\n/** One color of a scheme: a swatch with its value and an optional reset button */\nfunction ColorRow(props: {\n /** The color the generator actually applied, when it may differ from the input */\n adjusted?: string\n /** Whether the value is derived or a default rather than explicitly set */\n auto?: boolean\n /** What the value is when it is not set: derived (`auto`) or the stock one (`default`) */\n autoLabel?: 'auto' | 'default'\n /** The accessible name of the picker */\n label: string\n onChange: (value: string) => void\n onClear?: () => void\n title: string\n value: string\n}) {\n const {adjusted, auto, autoLabel = 'auto', label, onChange, onClear, title, value} = props\n\n return (\n <Flex align=\"center\" gap={2}>\n <Swatch\n aria-label={label}\n onChange={(event) => onChange(event.currentTarget.value)}\n type=\"color\"\n value={value}\n />\n <Stack flex={1} gap={2} style={{minWidth: 0}}>\n <Text size={1}>{title}</Text>\n <Text muted size={0} textOverflow=\"ellipsis\">\n {value}\n {adjusted !== undefined && adjusted !== value ? ` → ${adjusted}` : ''}\n {auto ? ` · ${autoLabel}` : ''}\n </Text>\n </Stack>\n {onClear && !auto && (\n <TooltipButton\n icon={ResetIcon}\n mode=\"bleed\"\n onClick={onClear}\n padding={2}\n tooltip={`Reset to ${autoLabel}`}\n />\n )}\n </Flex>\n )\n}\n","import {CopyIcon} from '@sanity/icons/Copy'\nimport {EditIcon} from '@sanity/icons/Edit'\nimport {EllipsisHorizontalIcon} from '@sanity/icons/EllipsisHorizontal'\nimport {TrashIcon} from '@sanity/icons/Trash'\nimport {Box, Button, Card, Text, useRootTheme} from '@sanity/ui'\nimport {Menu, MenuButton, MenuDivider, MenuItem} from '@sanity/ui/menu'\nimport {Tooltip} from '@sanity/ui/tooltip'\nimport {useState} from 'react'\nimport {styled} from 'styled-components'\n\nimport {useThemer} from './context'\nimport {displayTitle, ThemerTheme} from './themes'\nimport {ThemeThumbnail} from './ThemeThumbnail'\n\nconst Root = styled.div`\n position: relative;\n`\n\n/** A bare button, so that the thumbnail and the title can carry the styling */\nconst PickButton = styled.button`\n appearance: none;\n display: block;\n box-sizing: border-box;\n width: 100%;\n margin: 0;\n padding: 0;\n border: 0;\n background: none;\n color: inherit;\n font: inherit;\n text-align: center;\n cursor: pointer;\n\n &:focus {\n outline: none;\n }\n`\n\n/**\n * Wraps the thumbnail with room for the ring that marks the applied theme —\n * hovering shows a faint ring, the applied theme the focus ring color, and\n * keyboard focus an outline. The rings are drawn inside the frame's padding,\n * so nothing sticks out to be clipped by the scrolling list.\n */\nconst Frame = styled.span`\n display: block;\n padding: 4px;\n border-radius: 10px;\n transition: box-shadow 100ms;\n\n ${PickButton}:hover & {\n box-shadow: inset 0 0 0 2px var(--card-border-color);\n }\n\n ${PickButton}[aria-pressed='true'] & {\n box-shadow: inset 0 0 0 2px var(--card-focus-ring-color);\n }\n\n ${PickButton}:focus-visible & {\n outline: 2px solid var(--card-focus-ring-color);\n outline-offset: -2px;\n }\n`\n\n/**\n * The actions menu sits on the thumbnail's top right corner, like the actions\n * of an image input, and only shows on hover, on keyboard focus, and while it\n * is open — its popover is portaled, so an open menu does not count as focus\n * within\n */\nconst MenuSlot = styled.div`\n position: absolute;\n top: 10px;\n right: 10px;\n opacity: 0;\n transition: opacity 100ms;\n\n ${Root}:hover &,\n ${Root}:focus-within &,\n &[data-visible='true'] {\n opacity: 1;\n }\n`\n\n/**\n * One theme in the list: the floating preview with the title below it, like\n * an appearance option in macOS System Settings. Clicking it applies the\n * theme to the whole Studio; its menu holds the edit, duplicate and remove\n * flows that apply to the theme.\n *\n * @internal\n */\nexport function ThemeCard(props: {active: boolean; theme: ThemerTheme}) {\n const {active, theme} = props\n const {send} = useThemer()\n const {scheme} = useRootTheme()\n const [menuOpen, setMenuOpen] = useState(false)\n const title = displayTitle(theme.title)\n\n return (\n <Root>\n <PickButton\n aria-pressed={active}\n onClick={() => send({type: 'theme.pick', slug: theme.slug})}\n type=\"button\"\n >\n <Frame>\n <ThemeThumbnail options={theme.options} />\n </Frame>\n <Box as=\"span\" display=\"block\" paddingTop={2} paddingX={2}>\n <Text\n align=\"center\"\n as=\"span\"\n size={1}\n textOverflow=\"ellipsis\"\n weight={active ? 'medium' : 'regular'}\n >\n {title}\n </Text>\n </Box>\n </PickButton>\n\n {/* The tooltip wraps a card rather than the button: the menu button\n clones its button to wire it up, which a tooltip in between would\n swallow. The card keeps the button dark, as it sits on the dark\n half of the thumbnail, while the menu opens in the sidebar's scheme */}\n <MenuSlot data-visible={menuOpen}>\n <Tooltip animate content={<Text size={1}>Show more</Text>} placement=\"bottom\" portal>\n <Card radius={2} scheme=\"dark\" style={{display: 'inline-block'}}>\n <MenuButton\n button={\n <Button\n aria-label={`Show more for ${title}`}\n fontSize={1}\n icon={EllipsisHorizontalIcon}\n mode=\"ghost\"\n padding={1}\n radius={2}\n />\n }\n id={`themer-theme-${theme.slug}`}\n menu={\n <Menu>\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => send({type: 'theme.remove', slug: theme.slug})}\n text=\"Remove\"\n tone=\"critical\"\n />\n </>\n )}\n </Menu>\n }\n onClose={() => setMenuOpen(false)}\n onOpen={() => setMenuOpen(true)}\n popover={{animate: true, placement: 'bottom-end', portal: true, scheme}}\n />\n </Card>\n </Tooltip>\n </MenuSlot>\n </Root>\n )\n}\n","import {AddIcon} from '@sanity/icons/Add'\nimport {RestoreIcon} from '@sanity/icons/Restore'\nimport {Box, Button, Card, Flex, Stack} from '@sanity/ui'\n\nimport {useThemer} from './context'\nimport {ImageFileButton} from './ImageFileButton'\nimport {optionsFromImagePalette, titleFromFileName} from './imagePalette'\nimport {ScrollArea} from './ScrollArea'\nimport {ThemeCard} from './ThemeCard'\nimport {TooltipButton} from './TooltipButton'\nimport {useImagePalette} from './useImagePalette'\n\n/**\n * The flow for picking a theme: one column of theme cards — the configured\n * theme, the presets and the user's own themes — with the entry points to\n * the add and restore flows below.\n *\n * @internal\n */\nexport function ThemeList() {\n const {themes, removed, active, send} = useThemer()\n const {busy, pickImage} = useImagePalette((palette, file) =>\n send({\n type: 'theme.add',\n title: titleFromFileName(file.name),\n options: optionsFromImagePalette(palette),\n palette,\n imageUrl: URL.createObjectURL(file),\n }),\n )\n\n return (\n <>\n <ScrollArea padding={3}>\n <Stack gap={4}>\n {themes.map((theme) => (\n <ThemeCard active={theme.slug === active.slug} key={theme.slug} theme={theme} />\n ))}\n {/* Trails the list rather than sitting in the footer, so the footer\n does not shift when the first theme gets removed */}\n {removed.length > 0 && (\n <Button\n icon={RestoreIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'flow.removed'})}\n text={`Show removed (${removed.length})`}\n width=\"fill\"\n />\n )}\n </Stack>\n </ScrollArea>\n\n <Card borderTop padding={3}>\n <Flex gap={2}>\n <Box flex={1}>\n <TooltipButton\n icon={AddIcon}\n mode=\"ghost\"\n onClick={() => send({type: 'theme.add'})}\n text=\"Add theme\"\n tooltip=\"Add a theme based on the applied one\"\n width=\"fill\"\n />\n </Box>\n <ImageFileButton\n loading={busy}\n mode=\"ghost\"\n onFile={pickImage}\n tooltip=\"Add a theme from the colors of an image\"\n />\n </Flex>\n </Card>\n </>\n )\n}\n","import {BuildThemeOptions, SCHEMES, SchemeThemeOptions} from '../theme/options'\nimport {minimizeOptions} from './options'\n\n/**\n * Serializes theme options into the `buildTheme` call to paste into\n * `sanity.config.ts`, keeping only what differs from the derived defaults in\n * each scheme.\n *\n * Options that boil down to the stock Studio theme need nothing from this\n * package — so they serialize to a bare `buildTheme()` from `@sanity/ui/theme`\n * instead.\n *\n * @internal\n */\nexport function createThemeSnippet(options: BuildThemeOptions): string {\n const minimized = minimizeOptions(options)\n\n if (minimized === null) {\n return \"import {buildTheme} from '@sanity/ui/theme'\\n\\nexport const theme = buildTheme()\\n\"\n }\n\n const lines: string[] = []\n\n for (const scheme of SCHEMES) {\n const values = minimized[scheme]\n\n if (values) lines.push(serializeScheme(scheme, values))\n }\n\n return `import {buildTheme} from '@sanity/themer'\\n\\nexport const theme = buildTheme({\\n${lines.join('\\n')}\\n})\\n`\n}\n\n/** A scheme with up to two options fits on one line, more spread out */\nfunction serializeScheme(scheme: string, values: SchemeThemeOptions): string {\n const entries: string[] = []\n\n if (values.accent !== undefined) entries.push(`accent: '${values.accent}'`)\n if (values.text !== undefined) entries.push(`text: '${values.text}'`)\n if (values.background !== undefined) entries.push(`background: '${values.background}'`)\n if (values.contrast !== undefined) entries.push(`contrast: ${values.contrast}`)\n\n if (entries.length <= 2) {\n return ` ${scheme}: {${entries.join(', ')}},`\n }\n\n return ` ${scheme}: {\\n${entries.map((entry) => ` ${entry},`).join('\\n')}\\n },`\n}\n","import {ClipboardIcon} from '@sanity/icons/Clipboard'\nimport {Box, Button, Card, Dialog, Flex, Stack, Text} from '@sanity/ui'\nimport {Code} from '@sanity/ui/code'\nimport {useToast} from '@sanity/ui/toast'\nimport {registerLanguage} from 'react-refractor'\nimport typescript from 'refractor/typescript'\n\nimport {createThemeSnippet} from './snippet'\nimport {displayTitle, ThemerTheme} from './themes'\n\n// `Code` only highlights languages the surrounding app has registered with\n// react-refractor, and the Studio registers its own set from an async import\n// during startup — a race this dialog keeps losing. Registering the one\n// language the snippet needs keeps it highlighted from the first render.\nregisterLanguage(typescript)\n\n/**\n * Shows the `buildTheme` snippet that makes a theme permanent, in a dialog so\n * that it does not take up sidebar space or make the sidebar scroll as the\n * snippet changes shape with the theme.\n *\n * @internal\n */\nexport function ThemeSnippetDialog(props: {onClose: () => void; theme: ThemerTheme}) {\n const {onClose, theme} = props\n const toast = useToast()\n const snippet = createThemeSnippet(theme.options)\n\n const handleCopy = async () => {\n try {\n await navigator.clipboard.writeText(snippet)\n toast.push({status: 'success', title: 'Copied the theme to the clipboard'})\n } catch {\n toast.push({status: 'error', title: 'Could not copy the theme'})\n }\n }\n\n return (\n <Dialog\n animate\n footer={\n <Flex gap={2} justify=\"flex-end\" padding={3}>\n <Button icon={ClipboardIcon} mode=\"ghost\" onClick={() => void handleCopy()} text=\"Copy\" />\n </Flex>\n }\n header=\"Add to your config\"\n id=\"themer-snippet\"\n onClickOutside={onClose}\n onClose={onClose}\n width={1}\n >\n <Box padding={4}>\n <Stack gap={4}>\n <Text muted size={1}>\n Export this from <code>sanity.config.ts</code> and pass it as the <code>theme</code> of{' '}\n <code>defineConfig</code> to make “{displayTitle(theme.title)}” the Studio theme without\n this tool.\n </Text>\n <Card border overflow=\"auto\" padding={3} radius={2} tone=\"transparent\">\n <Code language=\"ts\" size={1}>\n {snippet}\n </Code>\n </Card>\n </Stack>\n </Box>\n </Dialog>\n )\n}\n","import {ArrowLeftIcon} from '@sanity/icons/ArrowLeft'\nimport {CloseIcon} from '@sanity/icons/Close'\nimport {CodeBlockIcon} from '@sanity/icons/CodeBlock'\nimport {SplitVerticalIcon} from '@sanity/icons/SplitVertical'\nimport {Box, Card, Flex, Text} from '@sanity/ui'\nimport {useState} from 'react'\n\nimport {useThemer} from './context'\nimport {RemovedThemes} from './RemovedThemes'\nimport {ThemeEditor} from './ThemeEditor'\nimport {ThemeList} from './ThemeList'\nimport {ThemeSnippetDialog} from './ThemeSnippetDialog'\nimport {TooltipButton} from './TooltipButton'\n\nconst VIEW_TITLES = {\n list: 'Themer',\n edit: 'Edit theme',\n removed: 'Removed themes',\n} as const\n\n/**\n * The themer sidebar: a header that navigates between the flows and toggles\n * the split preview, the flow itself — picking a theme, editing one, or\n * restoring removed ones — and the dialog with the `buildTheme` snippet of\n * the applied theme.\n *\n * @internal\n */\nexport function ThemerSidebar() {\n const {active, split, view, send} = useThemer()\n const [snippetOpen, setSnippetOpen] = useState(false)\n const inList = view.name === 'list'\n\n return (\n <Card height=\"fill\">\n <Flex direction=\"column\" height=\"fill\">\n {/* No bottom border: the header sits next to the Studio navbar, whose\n height is the Studio's to decide, so a line here would not line up\n with the navbar's */}\n <Card padding={2}>\n <Flex align=\"center\" gap={1}>\n {!inList && (\n <TooltipButton\n icon={ArrowLeftIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'flow.list'})}\n padding={2}\n tooltip=\"Back to the themes\"\n />\n )}\n <Box flex={1} paddingLeft={inList ? 1 : 0} style={{minWidth: 0}}>\n <Text size={1} textOverflow=\"ellipsis\" weight=\"semibold\">\n {VIEW_TITLES[view.name]}\n </Text>\n </Box>\n {/* Packed without gaps, so the title still fits next to them at the\n narrowest sidebar width */}\n <Flex>\n {view.name !== 'removed' && (\n <>\n <TooltipButton\n aria-pressed={split}\n icon={SplitVerticalIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'preview.toggle'})}\n padding={2}\n selected={split}\n tooltip=\"Show light and dark side by side\"\n />\n <TooltipButton\n icon={CodeBlockIcon}\n mode=\"bleed\"\n onClick={() => setSnippetOpen(true)}\n padding={2}\n tooltip=\"Show the code for the applied theme\"\n />\n </>\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'sidebar.close'})}\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>\n </Flex>\n </Card>\n\n {view.name === 'list' && <ThemeList />}\n {view.name === 'edit' && <ThemeEditor focusTitle={view.focusTitle} slug={view.slug} />}\n {view.name === 'removed' && <RemovedThemes />}\n </Flex>\n\n {snippetOpen && <ThemeSnippetDialog onClose={() => setSnippetOpen(false)} theme={active} />}\n </Card>\n )\n}\n","import {Box, Card, Layer} from '@sanity/ui'\nimport {useCallback, useEffect, useRef, useState} from 'react'\nimport {styled} from 'styled-components'\n\nimport {ThemerSidebar} from './ThemerSidebar'\n\n/**\n * The narrowest the sidebar goes — and its default: narrow enough to leave the\n * studio preview as much room as possible, while the themes stack in a single\n * column of preview cards and the editor fits a swatch next to its label.\n */\nconst MINIMUM_WIDTH = 200\n\n/** Twice the default is as wide as the sidebar goes */\nconst MAXIMUM_WIDTH = MINIMUM_WIDTH * 2\n\n/** How far one arrow key press resizes the sidebar */\nconst KEYBOARD_STEP = 16\n\nconst WIDTH_STORAGE_KEY = 'sanityStudio:themer:width'\n\nfunction clampWidth(width: number): number {\n return Math.min(MAXIMUM_WIDTH, Math.max(MINIMUM_WIDTH, Math.round(width)))\n}\n\nfunction readStoredWidth(): number {\n try {\n const stored = Number(localStorage.getItem(WIDTH_STORAGE_KEY))\n\n return Number.isFinite(stored) && stored > 0 ? clampWidth(stored) : MINIMUM_WIDTH\n } catch {\n return MINIMUM_WIDTH\n }\n}\n\nfunction writeStoredWidth(width: number): void {\n try {\n if (width === MINIMUM_WIDTH) {\n localStorage.removeItem(WIDTH_STORAGE_KEY)\n } else {\n localStorage.setItem(WIDTH_STORAGE_KEY, String(width))\n }\n } catch {\n // Storage can be unavailable (e.g. private browsing) — the width just won't persist\n }\n}\n\nconst Sidebar = styled(Layer)`\n flex: none;\n`\n\n/**\n * Draws the sidebar's border and gives the resize handle its card colors. It\n * does not clip, so the handle can straddle the border — the content is\n * clipped one level down instead.\n */\nconst Frame = styled(Card)`\n position: relative;\n`\n\n/**\n * The grab area along the sidebar's left edge. It straddles the border so it\n * is easy to hit, and highlights while hovered, focused or dragged.\n */\nconst ResizeHandle = styled.div`\n position: absolute;\n top: 0;\n bottom: 0;\n left: -3px;\n z-index: 1;\n width: 7px;\n cursor: col-resize;\n touch-action: none;\n\n &::after {\n content: '';\n position: absolute;\n top: 0;\n bottom: 0;\n left: 2px;\n width: 3px;\n background: var(--card-focus-ring-color);\n opacity: 0;\n transition: opacity 100ms;\n }\n\n &:hover::after,\n &:focus-visible::after,\n &[data-dragging='true']::after {\n opacity: 1;\n }\n\n &:focus {\n outline: none;\n }\n`\n\n/**\n * The themer sidebar, in a layer along the right edge of the Studio. Its left\n * edge drags (or arrow-keys) it wider, up to twice its default width — the\n * width sticks between sessions.\n *\n * @internal\n */\nexport function ResizableSidebar() {\n const [width, setWidth] = useState(readStoredWidth)\n const [dragging, setDragging] = useState(false)\n const drag = useRef<{pointerId: number; startX: number; startWidth: number} | null>(null)\n\n useEffect(() => writeStoredWidth(width), [width])\n\n const handlePointerDown = useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n if (event.button !== 0) return\n\n event.preventDefault()\n event.currentTarget.setPointerCapture(event.pointerId)\n drag.current = {pointerId: event.pointerId, startX: event.clientX, startWidth: width}\n setDragging(true)\n },\n [width],\n )\n\n const handlePointerMove = useCallback((event: React.PointerEvent<HTMLDivElement>) => {\n if (!drag.current || drag.current.pointerId !== event.pointerId) return\n\n // The sidebar sits on the right, so dragging left makes it wider\n setWidth(clampWidth(drag.current.startWidth - (event.clientX - drag.current.startX)))\n }, [])\n\n const handlePointerUp = useCallback((event: React.PointerEvent<HTMLDivElement>) => {\n if (!drag.current || drag.current.pointerId !== event.pointerId) return\n\n drag.current = null\n setDragging(false)\n }, [])\n\n const handleKeyDown = useCallback((event: React.KeyboardEvent<HTMLDivElement>) => {\n const steps: Record<string, number> = {\n ArrowLeft: KEYBOARD_STEP,\n ArrowRight: -KEYBOARD_STEP,\n Home: MAXIMUM_WIDTH,\n End: -MAXIMUM_WIDTH,\n }\n const step = steps[event.key]\n\n if (step === undefined) return\n\n event.preventDefault()\n setWidth((current) => clampWidth(current + step))\n }, [])\n\n return (\n <Sidebar style={{width}} zOffset={100}>\n <Frame borderLeft height=\"fill\">\n <ResizeHandle\n aria-label=\"Resize the themer\"\n aria-orientation=\"vertical\"\n aria-valuemax={MAXIMUM_WIDTH}\n aria-valuemin={MINIMUM_WIDTH}\n aria-valuenow={width}\n data-dragging={dragging}\n onDoubleClick={() => setWidth(MINIMUM_WIDTH)}\n onKeyDown={handleKeyDown}\n onPointerCancel={handlePointerUp}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n // oxlint-disable-next-line prefer-tag-over-role -- a window splitter is a focusable, draggable separator, which an hr is not\n role=\"separator\"\n tabIndex={0}\n />\n <Box height=\"fill\" overflow=\"hidden\">\n <ThemerSidebar />\n </Box>\n </Frame>\n </Sidebar>\n )\n}\n","import {isColor} from '../lib/mix'\nimport {\n BuildThemeOptions,\n MAXIMUM_CONTRAST,\n MINIMUM_CONTRAST,\n SCHEMES,\n SchemeThemeOptions,\n} from '../theme/options'\nimport {presets} from '../theme/presets'\nimport {IMAGE_PALETTE_KEYS, ImagePalette} from './imagePalette'\nimport {\n CONFIG_SLUG,\n createCustomTheme,\n CustomTheme,\n displayTitle,\n initialThemerState,\n ThemerState,\n} from './themes'\n\nconst STORAGE_KEY = 'sanityStudio:themer:state'\n\n/**\n * Where earlier versions of the tool kept their single draft theme — it is\n * migrated into a custom theme, so a draft survives the upgrade.\n */\nconst LEGACY_STORAGE_KEY = 'sanityStudio:themer:options'\n\n/** The title a draft from before themes had titles is imported under */\nconst LEGACY_DRAFT_TITLE = 'Draft theme'\n\nfunction sanitizeColor(value: unknown): string | null {\n return typeof value === 'string' && isColor(value) ? value.toLowerCase() : null\n}\n\nfunction sanitizeContrast(value: unknown): number | null {\n return typeof value === 'number' && Number.isFinite(value)\n ? Math.min(MAXIMUM_CONTRAST, Math.max(MINIMUM_CONTRAST, value))\n : null\n}\n\nfunction sanitizeSchemeOptions(value: unknown): SchemeThemeOptions {\n const options: SchemeThemeOptions = {}\n\n if (!value || typeof value !== 'object') return options\n\n const accent = sanitizeColor(Reflect.get(value, 'accent'))\n const text = sanitizeColor(Reflect.get(value, 'text'))\n const background = sanitizeColor(Reflect.get(value, 'background'))\n const contrast = sanitizeContrast(Reflect.get(value, 'contrast'))\n\n if (accent) options.accent = accent\n if (text) options.text = text\n if (background) options.background = background\n if (contrast !== null) options.contrast = contrast\n\n return options\n}\n\n/**\n * Options from before they were grouped by scheme: one accent, text color and\n * contrast for both schemes, and a background per scheme. Both schemes get\n * the shared colors, so the theme keeps looking the same.\n */\nfunction sanitizeFlatOptions(value: object): BuildThemeOptions | null {\n const accent = sanitizeColor(Reflect.get(value, 'accent'))\n\n if (!accent) return null\n\n const shared: SchemeThemeOptions = {accent}\n const text = sanitizeColor(Reflect.get(value, 'text'))\n const contrast = sanitizeContrast(Reflect.get(value, 'contrast'))\n\n if (text) shared.text = text\n if (contrast !== null) shared.contrast = contrast\n\n const backgrounds: unknown = Reflect.get(value, 'background')\n const options: BuildThemeOptions = {}\n\n for (const scheme of SCHEMES) {\n const background =\n backgrounds && typeof backgrounds === 'object'\n ? sanitizeColor(Reflect.get(backgrounds, scheme))\n : null\n\n options[scheme] = background ? {...shared, background} : {...shared}\n }\n\n return options\n}\n\nfunction sanitizeOptions(value: unknown): BuildThemeOptions | null {\n if (!value || typeof value !== 'object') return null\n\n // The flat shape always had an accent, the scheme shape never has one at the top\n if ('accent' in value) {\n return sanitizeFlatOptions(value)\n }\n\n const options: BuildThemeOptions = {}\n\n for (const scheme of SCHEMES) {\n const schemeOptions = sanitizeSchemeOptions(Reflect.get(value, scheme))\n\n if (Object.keys(schemeOptions).length > 0) options[scheme] = schemeOptions\n }\n\n return options\n}\n\nfunction isReservedSlug(slug: string): boolean {\n return slug === CONFIG_SLUG || presets.some((preset) => preset.slug === slug)\n}\n\nfunction sanitizeCustomTheme(value: unknown): CustomTheme | null {\n if (!value || typeof value !== 'object') return null\n\n const slug: unknown = Reflect.get(value, 'slug')\n\n if (typeof slug !== 'string' || !slug || isReservedSlug(slug)) return null\n\n const options = sanitizeOptions(Reflect.get(value, 'options'))\n\n if (!options) return null\n\n const title: unknown = Reflect.get(value, 'title')\n const palette = sanitizePalette(Reflect.get(value, 'palette'))\n\n return {\n slug,\n title: displayTitle(typeof title === 'string' ? title : ''),\n options,\n ...(palette ? {palette} : {}),\n }\n}\n\nfunction sanitizePalette(value: unknown): ImagePalette | null {\n if (!value || typeof value !== 'object') return null\n\n const palette: Partial<ImagePalette> = {}\n let swatches = 0\n\n for (const key of IMAGE_PALETTE_KEYS) {\n const swatch = sanitizeColor(Reflect.get(value, key))\n\n palette[key] = swatch\n if (swatch) swatches++\n }\n\n // oxlint-disable-next-line no-unsafe-type-assertion -- the loop assigns every key\n return swatches > 0 ? (palette as ImagePalette) : null\n}\n\nfunction sanitizeState(value: unknown): ThemerState {\n if (!value || typeof value !== 'object') return initialThemerState\n\n const custom: CustomTheme[] = []\n const rawCustom: unknown = Reflect.get(value, 'custom')\n\n if (Array.isArray(rawCustom)) {\n for (const item of rawCustom) {\n const theme = sanitizeCustomTheme(item)\n\n if (theme && !custom.some((existing) => existing.slug === theme.slug)) {\n custom.push(theme)\n }\n }\n }\n\n const removed: string[] = []\n const rawRemoved: unknown = Reflect.get(value, 'removed')\n\n if (Array.isArray(rawRemoved)) {\n for (const slug of rawRemoved) {\n const known =\n typeof slug === 'string' &&\n slug !== CONFIG_SLUG &&\n (presets.some((preset) => preset.slug === slug) ||\n custom.some((theme) => theme.slug === slug))\n\n if (known && !removed.includes(slug)) removed.push(slug)\n }\n }\n\n const rawActive: unknown = Reflect.get(value, 'active')\n\n return {active: typeof rawActive === 'string' ? rawActive : null, custom, removed}\n}\n\nfunction readLegacyState(): ThemerState | null {\n const raw = localStorage.getItem(LEGACY_STORAGE_KEY)\n\n if (!raw) return null\n\n const options = sanitizeOptions(JSON.parse(raw))\n\n if (!options) return null\n\n const theme = createCustomTheme(LEGACY_DRAFT_TITLE, options)\n\n return {active: theme.slug, custom: [theme], removed: []}\n}\n\n/**\n * Restores the themer state from localStorage, so the user's themes survive\n * studio reloads. Falls back to the initial state when nothing usable is\n * stored.\n *\n * @internal\n */\nexport function readStoredState(): ThemerState {\n try {\n if (typeof localStorage === 'undefined') return initialThemerState\n\n const raw = localStorage.getItem(STORAGE_KEY)\n\n if (raw) return sanitizeState(JSON.parse(raw))\n\n return readLegacyState() ?? initialThemerState\n } catch {\n return initialThemerState\n }\n}\n\n/** @internal */\nexport function writeStoredState(state: ThemerState): void {\n try {\n if (typeof localStorage === 'undefined') return\n\n localStorage.setItem(STORAGE_KEY, JSON.stringify(state))\n localStorage.removeItem(LEGACY_STORAGE_KEY)\n } catch {\n // Storage can be unavailable (e.g. private browsing) — themes just won't persist\n }\n}\n","import {Card, Flex, ThemeProvider} from '@sanity/ui'\nimport {type RootTheme, type ThemeColorSchemeKey} from '@sanity/ui/theme'\nimport {useActor, useSelector} from '@xstate/react'\nimport {useEffect, useMemo, useRef, useState} from 'react'\nimport {type LayoutProps, useColorSchemeValue} from 'sanity'\n\nimport {buildTheme} from '../theme/buildTheme'\nimport {BuildThemeOptions} from '../theme/options'\nimport {ThemerContext, ThemerContextValue, ThemerView} from './context'\nimport {selectStoredState, ThemerInput, themerMachine, ThemerSnapshot} from './machine'\nimport {ResizableSidebar} from './ResizableSidebar'\nimport {readStoredState, writeStoredState} from './storage'\nimport {resolveThemes, ThemerState} from './themes'\n\nfunction sameStoredState(a: ThemerState, b: ThemerState): boolean {\n return a.active === b.active && a.custom === b.custom && a.removed === b.removed\n}\n\nfunction selectView(snapshot: ThemerSnapshot): ThemerView {\n const {editing} = snapshot.context\n\n if (snapshot.matches({flow: 'edit'}) && editing) {\n return {name: 'edit', slug: editing.slug, focusTitle: editing.focusTitle}\n }\n\n if (snapshot.matches({flow: 'removed'})) {\n return {name: 'removed'}\n }\n\n return {name: 'list'}\n}\n\nfunction sameView(a: ThemerView, b: ThemerView): boolean {\n if (a.name !== b.name) return false\n\n return (\n a.name !== 'edit' || b.name !== 'edit' || (a.slug === b.slug && a.focusTitle === b.focusTitle)\n )\n}\n\n/**\n * Wraps the whole Studio so that the theme picked in the themer sidebar\n * applies everywhere while the user browses around, runs the themer machine\n * that the navbar toggle and the sidebar share — the user's themes, which one\n * is applied, which flow the sidebar is in and how the Studio is previewed —\n * and renders the sidebar next to the Studio. The sidebar sits at the\n * `layout` level rather than in `activeToolLayout` because the split preview\n * renders the Studio twice, and the sidebar must not come along.\n *\n * The single preview inherits the color scheme from the Studio, so it follows\n * the appearance setting (light/dark/system) like any other theme.\n *\n * @internal\n */\nexport function ThemerLayout(props: LayoutProps & {baseOptions: BuildThemeOptions}) {\n const {baseOptions, ...layoutProps} = props\n const [input] = useState<ThemerInput>(() => ({baseOptions, stored: readStoredState()}))\n const [snapshot, send, actorRef] = useActor(themerMachine, {input})\n const stored = useSelector(actorRef, selectStoredState, sameStoredState)\n const view = useSelector(actorRef, selectView, sameView)\n const open = snapshot.matches({sidebar: 'open'})\n const split = snapshot.matches({preview: 'split'})\n const {images} = snapshot.context\n\n useEffect(() => writeStoredState(stored), [stored])\n\n // The images live in memory as object URLs — release the ones no theme\n // refers to anymore, and all of them on the way out\n const previousImages = useRef(images)\n\n useEffect(() => {\n const current = new Set(Object.values(images))\n\n for (const url of Object.values(previousImages.current)) {\n if (!current.has(url)) URL.revokeObjectURL(url)\n }\n\n previousImages.current = images\n }, [images])\n\n useEffect(\n () => () => {\n for (const url of Object.values(previousImages.current)) URL.revokeObjectURL(url)\n },\n [],\n )\n\n const {themes, removed, active} = useMemo(\n () => resolveThemes(stored, baseOptions),\n [stored, baseOptions],\n )\n\n // The theme identity must be stable between renders: it feeds the\n // styled-components theme context for the whole Studio, and rebuilding it\n // would re-render everything. Keying on the options object keeps it stable\n // across unrelated changes, like renaming a theme.\n const activeOptions = active.source === 'config' ? null : active.options\n const theme = useMemo(\n () => (activeOptions === null ? null : buildTheme(activeOptions)),\n [activeOptions],\n )\n\n // The Studio paints the body with its configured theme from above this\n // layout, and Safari tints its chrome from the body — so the applied theme\n // has to reach the body too, in the scheme the Studio is showing\n const scheme = useColorSchemeValue()\n\n useEffect(() => {\n const background = theme?.v2?.color[scheme].default.bg\n\n if (background === undefined) return undefined\n\n const {style} = document.body\n const previous = style.backgroundColor\n\n style.backgroundColor = background\n\n return () => {\n style.backgroundColor = previous\n }\n }, [scheme, theme])\n\n const context = useMemo<ThemerContextValue>(\n () => ({baseOptions, themes, removed, active, images, view, open, split, send}),\n [active, baseOptions, images, open, removed, send, split, themes, view],\n )\n\n const studio = layoutProps.renderDefault(layoutProps)\n\n return (\n <ThemerContext.Provider value={context}>\n <Flex height=\"fill\" sizing=\"border\">\n {/* The first copy keeps its place when the preview splits, so the Studio\n in it stays mounted and only changes scheme */}\n <StudioPreview scheme={split ? 'light' : undefined} theme={theme}>\n {studio}\n </StudioPreview>\n {split && (\n <StudioPreview borderLeft scheme=\"dark\" theme={theme}>\n {studio}\n </StudioPreview>\n )}\n\n {open && (\n <ThemeProvider theme={theme ?? undefined}>\n <ResizableSidebar />\n </ThemeProvider>\n )}\n </Flex>\n </ThemerContext.Provider>\n )\n}\n\n/**\n * One copy of the Studio, in the given color scheme — or, without one, in the\n * scheme the Studio is showing. The configured theme (`null`) goes through\n * the provider too, inheriting the Studio's, so picking a theme swaps it\n * instead of remounting the Studio under a new provider.\n *\n * The two copies of the split preview share the router, the document store\n * and every other provider above the layout — only the scheme differs — so\n * they stay in sync while navigating. The `color-scheme` of a forced scheme\n * keeps native form controls and scrollbars in step with it.\n */\nfunction StudioPreview(props: {\n borderLeft?: boolean\n children: React.ReactNode\n scheme?: ThemeColorSchemeKey\n theme: RootTheme | null\n}) {\n const {borderLeft, children, scheme, theme} = props\n\n return (\n <ThemeProvider scheme={scheme} theme={theme ?? undefined}>\n <Card\n borderLeft={borderLeft}\n flex={1}\n height=\"fill\"\n overflow=\"hidden\"\n style={scheme ? {colorScheme: scheme} : undefined}\n >\n {children}\n </Card>\n </ThemeProvider>\n )\n}\n","import {ColorWheelIcon} from '@sanity/icons/ColorWheel'\nimport {Button, Text} from '@sanity/ui'\nimport {Tooltip} from '@sanity/ui/tooltip'\nimport {type NavbarProps} from 'sanity'\n\nimport {useThemer} from './context'\n\nfunction ThemerNavbarButton() {\n const {open, send} = useThemer()\n\n return (\n <Tooltip animate content={<Text size={1}>Themer</Text>} portal>\n <Button\n aria-label=\"Themer\"\n icon={ColorWheelIcon}\n mode=\"bleed\"\n onClick={() => send({type: 'sidebar.toggle'})}\n // The Studio's own navbar buttons go through a wrapper that pins them\n // to this padding, where `@sanity/ui` defaults to a roomier 3\n padding={2}\n selected={open}\n />\n </Tooltip>\n )\n}\n\n/**\n * Adds the toggle that opens and closes the themer sidebar to the Studio\n * navbar — an icon button with a tooltip in the top bar (like the Tasks\n * toggle), and a regular titled action in the narrow-screen sidebar menu.\n *\n * In the split preview every Studio copy renders its own navbar, so the\n * toggle shows up in both — they drive the same sidebar.\n *\n * @internal\n */\nexport function ThemerNavbar(props: NavbarProps) {\n const {open, send} = useThemer()\n\n return props.renderDefault({\n ...props,\n __internal_actions: [\n ...(props.__internal_actions ?? []),\n {\n location: 'topbar',\n name: 'themer-topbar',\n render: () => <ThemerNavbarButton />,\n },\n {\n icon: ColorWheelIcon,\n location: 'sidebar',\n name: 'themer-sidebar',\n onAction: () => send({type: 'sidebar.toggle'}),\n selected: open,\n title: 'Themer',\n },\n ],\n })\n}\n","import {definePlugin, type LayoutProps} from 'sanity'\n\nimport {BuildThemeOptions} from '../theme/options'\nimport {ThemerLayout} from './ThemerLayout'\nimport {ThemerNavbar} from './ThemerNavbar'\n\n/**\n * Options for the {@link themerTool} plugin.\n *\n * This is experimental and may change or be removed in any release without\n * notice — use at your own risk.\n *\n * @alpha\n */\nexport interface ThemerToolOptions {\n /**\n * The `buildTheme` options that the Studio's configured theme was generated\n * from — the themer starts editing from these, so pass the same object that\n * the `theme` in the Studio config uses:\n *\n * ```ts\n * const config: BuildThemeOptions = {light: {accent: '#1cb485'}, dark: {accent: '#22fca8'}}\n *\n * export default defineConfig({\n * theme: buildTheme(config),\n * plugins: [themerTool({config})],\n * })\n * ```\n */\n config?: BuildThemeOptions\n}\n\n/**\n * A Studio plugin that adds a themer sidebar for `buildTheme` themes: a\n * navbar toggle opens the sidebar next to the Studio, with a list of themes —\n * the configured theme, the presets and your own — each previewed as a tiny\n * Studio in both color schemes. Picking one applies it live to the whole\n * Studio while you browse around; your own themes can be edited with\n * accent/text/background pickers and a contrast slider per scheme, added,\n * duplicated, given the colors of an image (its palette is read on device),\n * removed and restored, and a dialog shows the `buildTheme` snippet that\n * makes the applied theme permanent. Toggle between light and dark mode with\n * the regular appearance menu — the preview follows it — or split the\n * preview to see the whole Studio in light and dark side by side.\n *\n * ```ts\n * import {themerTool} from '@sanity/themer/tool'\n * import {defineConfig} from 'sanity'\n *\n * export default defineConfig({\n * plugins: [themerTool()],\n * // ...rest of the config\n * })\n * ```\n *\n * This is experimental and may change or be removed in any release without\n * notice — use at your own risk.\n *\n * @alpha\n */\nexport const themerTool = definePlugin<ThemerToolOptions | void>((options) => {\n // No options generate the stock theme, which is what a Studio without a\n // `theme` in its config gets\n const baseOptions = options?.config ?? {}\n\n function ThemerLayoutWithOptions(props: LayoutProps) {\n return <ThemerLayout {...props} baseOptions={baseOptions} />\n }\n\n return {\n name: '@sanity/themer/tool',\n studio: {\n components: {\n layout: ThemerLayoutWithOptions,\n navbar: ThemerNavbar,\n },\n },\n }\n})\n"],"mappings":"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"}
|