@sanity/themer 0.10.2 → 0.10.4
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/dist/AnimatedColorWheelIcon-CwWOVAsw.js.map +1 -1
- package/dist/{ResizableSidebar-C1nr3Ls2.js → ResizableSidebar-Qo2gLzlm.js} +29 -29
- package/dist/{ResizableSidebar-C1nr3Ls2.js.map → ResizableSidebar-Qo2gLzlm.js.map} +1 -1
- package/dist/bundle.css +109 -109
- package/dist/{themes-au1BLKmY.js → themes-C_-85qf1.js} +29 -29
- package/dist/{themes-au1BLKmY.js.map → themes-C_-85qf1.js.map} +1 -1
- package/dist/tool.js +2 -2
- package/dist/tool.js.map +1 -1
- package/package.json +10 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResizableSidebar-C1nr3Ls2.js","names":["root","() => lastGutter","() => {\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 = () => {\n lastGutter = element.offsetWidth - element.clientWidth\n setGutter(lastGutter)\n }\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 value={inset}>{children}</ScrollAreaInsetContext>","<Box\n className={root}\n flex={1}\n overflow=\"auto\"\n padding={padding}\n ref={ref}\n style={{paddingRight: inset.right}}\n >\n <ScrollAreaInsetContext value={inset}>{children}</ScrollAreaInsetContext>\n </Box>","{marginLeft: -left, marginRight: -right, marginBottom: -bottom}","-left","-right","-bottom","<div style={{marginLeft: -left, marginRight: -right, marginBottom: -bottom}}>\n {props.children}\n </div>","children","_c","_temp","root","root","buildTheme(options)","useMemo(() => buildTheme(options), [options])","<MockStudio scheme=\"light\" />","<ThemeProvider scheme=\"light\" theme={theme} tone=\"default\">\n <MockStudio scheme=\"light\" />\n </ThemeProvider>","<MockStudio scheme=\"dark\" />","<ThemeProvider scheme=\"dark\" theme={theme} tone=\"default\">\n <MockStudio scheme=\"dark\" />\n </ThemeProvider>","<div aria-hidden className={root}>\n <ThemeProvider scheme=\"light\" theme={theme} tone=\"default\">\n <MockStudio scheme=\"light\" />\n </ThemeProvider>\n <ThemeProvider scheme=\"dark\" theme={theme} tone=\"default\">\n <MockStudio scheme=\"dark\" />\n </ThemeProvider>\n </div>","assignInlineVars({\n [colors.bg]: color.bg,\n [colors.fg]: color.fg,\n [colors.mutedFg]: color.muted.fg,\n [colors.border]: color.border,\n [colors.accent]: color.button.default.primary.enabled.bg,\n [colors.avatar]: color.avatar.blue.bg,\n [colors.selectedBg]: selected.bg,\n [colors.selectedFg]: selected.fg,\n [colors.inputBg]: inputColor.bg,\n [colors.inputBorder]: inputColor.border,\n })","bg","border","fg","<div className={logo} />","<div className={navbar}>\n <div className={logo} />\n <div className={line.fg} style={{width: '22%'}} />\n <div className={spacer} />\n <div className={avatar} />\n </div>","<div className={list}>\n {LIST_ROWS.map((width, index) => {\n const isSelected = index === 1\n\n return (\n <div key={width} className={listRow} data-selected={isSelected}>\n <div className={isSelected ? line.selected : line.muted} style={{width}} />\n </div>\n )\n })}\n </div>","(width, index) => {\n const isSelected = index === 1\n\n return (\n <div key={width} className={listRow} data-selected={isSelected}>\n <div className={isSelected ? line.selected : line.muted} style={{width}} />\n </div>\n )\n }","<div className={form}>\n <div className={line.fg} style={{width: '45%'}} />\n {FIELDS.map((width) => (\n <div key={width} className={field}>\n <div className={line.muted} style={{width}} />\n <div className={input} />\n </div>\n ))}\n </div>","(width) => (\n <div key={width} className={field}>\n <div className={line.muted} style={{width}} />\n <div className={input} />\n </div>\n )","<div className={body}>\n <div className={list}>\n {LIST_ROWS.map((width, index) => {\n const isSelected = index === 1\n\n return (\n <div key={width} className={listRow} data-selected={isSelected}>\n <div className={isSelected ? line.selected : line.muted} style={{width}} />\n </div>\n )\n })}\n </div>\n\n <div className={pane}>\n <div className={form}>\n <div className={line.fg} style={{width: '45%'}} />\n {FIELDS.map((width) => (\n <div key={width} className={field}>\n <div className={line.muted} style={{width}} />\n <div className={input} />\n </div>\n ))}\n </div>\n <div className={footer}>\n <div className={button} />\n </div>\n </div>\n </div>","<div\n className={studio[scheme]}\n style={assignInlineVars({\n [colors.bg]: color.bg,\n [colors.fg]: color.fg,\n [colors.mutedFg]: color.muted.fg,\n [colors.border]: color.border,\n [colors.accent]: color.button.default.primary.enabled.bg,\n [colors.avatar]: color.avatar.blue.bg,\n [colors.selectedBg]: selected.bg,\n [colors.selectedFg]: selected.fg,\n [colors.inputBg]: inputColor.bg,\n [colors.inputBorder]: inputColor.border,\n })}\n >\n <div className={navbar}>\n <div className={logo} />\n <div className={line.fg} style={{width: '22%'}} />\n <div className={spacer} />\n <div className={avatar} />\n </div>\n\n <div className={body}>\n <div className={list}>\n {LIST_ROWS.map((width, index) => {\n const isSelected = index === 1\n\n return (\n <div key={width} className={listRow} data-selected={isSelected}>\n <div className={isSelected ? line.selected : line.muted} style={{width}} />\n </div>\n )\n })}\n </div>\n\n <div className={pane}>\n <div className={form}>\n <div className={line.fg} style={{width: '45%'}} />\n {FIELDS.map((width) => (\n <div key={width} className={field}>\n <div className={line.muted} style={{width}} />\n <div className={input} />\n </div>\n ))}\n </div>\n <div className={footer}>\n <div className={button} />\n </div>\n </div>\n </div>\n </div>","studio[scheme]","width","FIELDS","_c","root","_temp","_temp2","<Card overflow=\"hidden\" radius={3} shadow={1}>\n <ThemePreview options={props.options} />\n </Card>","options","_c","{actorRef, removed}: Pick<ThemerProps, 'actorRef' | 'removed'>","removed.map((theme) => (\n <Stack key={theme.slug} gap={3}>\n <Box className={removedThumbnail}>\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 mode=\"ghost\"\n onClick={() => actorRef.send({type: 'theme.restore', slug: theme.slug})}\n padding={2}\n text=\"Restore\"\n />\n {theme.source === 'custom' && (\n <Button\n mode=\"ghost\"\n onClick={() => actorRef.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 key={theme.slug} gap={3}>\n <Box className={removedThumbnail}>\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 mode=\"ghost\"\n onClick={() => actorRef.send({type: 'theme.restore', slug: theme.slug})}\n padding={2}\n text=\"Restore\"\n />\n {theme.source === 'custom' && (\n <Button\n mode=\"ghost\"\n onClick={() => actorRef.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 key={theme.slug} gap={3}>\n <Box className={removedThumbnail}>\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 mode=\"ghost\"\n onClick={() => actorRef.send({type: 'theme.restore', slug: theme.slug})}\n padding={2}\n text=\"Restore\"\n />\n {theme.source === 'custom' && (\n <Button\n mode=\"ghost\"\n onClick={() => actorRef.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>","<Card borderTop padding={3}>\n <Flex justify=\"flex-end\">\n <Button\n gap={2}\n icon={CheckmarkIcon}\n onClick={() => actorRef.send({type: 'flow.list'})}\n padding={2}\n text=\"Done\"\n mode=\"ghost\"\n />\n </Flex>\n </Card>","<>\n <ScrollArea padding={3}>\n <Stack gap={5}>\n {removed.map((theme) => (\n <Stack key={theme.slug} gap={3}>\n <Box className={removedThumbnail}>\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 mode=\"ghost\"\n onClick={() => actorRef.send({type: 'theme.restore', slug: theme.slug})}\n padding={2}\n text=\"Restore\"\n />\n {theme.source === 'custom' && (\n <Button\n mode=\"ghost\"\n onClick={() => actorRef.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 {/* In the size of the Studio's own buttons (padding and gap of 2) */}\n <Card borderTop padding={3}>\n <Flex justify=\"flex-end\">\n <Button\n gap={2}\n icon={CheckmarkIcon}\n onClick={() => actorRef.send({type: 'flow.list'})}\n padding={2}\n text=\"Done\"\n mode=\"ghost\"\n />\n </Flex>\n </Card>\n </>","_c","_Fragment","<Text size={1}>{tooltip}</Text>","<Button\n aria-label={buttonProps.text === undefined ? tooltip : undefined}\n gap={2}\n padding={2}\n {...buttonProps}\n />","buttonProps.text === undefined ? tooltip : undefined","<Tooltip animate content={<Text size={1}>{tooltip}</Text>} placement=\"bottom\" portal>\n <Button\n aria-label={buttonProps.text === undefined ? tooltip : undefined}\n gap={2}\n padding={2}\n {...buttonProps}\n />\n </Tooltip>","_c","{\n onFile,\n ref,\n}: {\n onFile: (file: File) => void\n ref: React.Ref<HTMLInputElement>\n}","(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 }","<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={ref}\n type=\"file\"\n />","() => inputRef.current?.click()","<TooltipButton {...buttonProps} icon={ImageIcon} onClick={() => inputRef.current?.click()} />","<ImageFileInput onFile={onFile} ref={inputRef} />","<>\n <TooltipButton {...buttonProps} icon={ImageIcon} onClick={() => inputRef.current?.click()} />\n <ImageFileInput onFile={onFile} ref={inputRef} />\n </>","_c","_Fragment","() => {\n onPaletteRef.current = onPalette\n }","[onPalette]","(file: File) => {\n const pick = ++latestPickRef.current\n\n setBusy(true)\n\n // Whether this pick is still the latest one. Nothing checks whether the\n // component still shows: adding the theme opens the editor, which hides\n // the list in an `Activity` — its state survives that, and the flag\n // must clear before the list shows again\n const isCurrent = () => 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 is still the latest one. Nothing checks whether the\n // component still shows: adding the theme opens the editor, which hides\n // the list in an `Activity` — its state survives that, and the flag\n // must clear before the list shows again\n const isCurrent = () => 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}","_c","_temp","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 frame on either side\n setThumbnailWidth(Math.max(24, Math.floor(visible / VISIBLE_VARIANTS) - VARIANT_FRAME * 2))\n }\n\n measure()\n\n const observer = new ResizeObserver(measure)\n\n observer.observe(row)\n\n return () => observer.disconnect()\n // oxlint-disable-next-line react/exhaustive-effect-dependencies -- the row only renders with a palette, so a new palette has a new row to observe\n }","[palette]","useMemo(() => {\n if (!palette) return []\n\n return IMAGE_PALETTE_VARIANTS.flatMap((variant) =>\n palette[variant] === null\n ? []\n : [\n {\n variant,\n title: IMAGE_PALETTE_TITLES[variant],\n options: applyImagePalette(options, palette, variant),\n },\n ],\n )\n }, [options, palette])","[]","IMAGE_PALETTE_VARIANTS.flatMap((variant) =>\n palette[variant] === null\n ? []\n : [\n {\n variant,\n title: IMAGE_PALETTE_TITLES[variant],\n options: applyImagePalette(options, palette, variant),\n },\n ],\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 <div className={paletteGrid} data-with-image={imageUrl !== undefined}>\n {imageUrl && <img alt=\"\" className={imageTile} src={imageUrl} />}\n {TILES.map((key) => {\n const hex = palette[key]\n const title = IMAGE_PALETTE_TITLES[key]\n\n return (\n <Tooltip\n key={key}\n animate\n content={\n <Text size={1}>\n {hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`}\n </Text>\n }\n\n placement=\"bottom\"\n portal\n >\n <span\n className={tile}\n data-empty={hex === null}\n style={{background: hex ?? undefined}}\n />\n </Tooltip>\n )\n })}\n </div>\n\n <Stack gap={2}>\n <Text muted size={0}>\n Pick the swatch to build the theme around\n </Text>\n <div\n className={variantRow}\n ref={rowRef}\n style={assignInlineVars({\n [variantWidth]: `${thumbnailWidth}px`,\n [variantScale]: String(thumbnailWidth / VARIANT_WIDTH),\n })}\n >\n {variants.map(({variant, title, options: variantOptions}) => (\n // oxlint-disable-next-line control-has-associated-label -- the title below the thumbnail is the label, deeper than the rule looks\n <button\n key={variant}\n aria-pressed={variant === current}\n className={variantButton}\n onClick={() => onVariant(variant)}\n type=\"button\"\n >\n <span className={variantFrame}>\n <span className={variantScaleBox}>\n <ThemeThumbnail options={variantOptions} />\n </span>\n </span>\n <Box as=\"span\" display=\"block\" paddingTop={1}>\n <Text\n align=\"center\"\n as=\"span\"\n size={0}\n textOverflow=\"ellipsis\"\n weight={variant === current ? 'medium' : 'regular'}\n >\n {title}\n </Text>\n </Box>\n </button>\n ))}\n </div>\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, title, options: variantOptions}","variant","title","<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 <div className={paletteGrid} data-with-image={imageUrl !== undefined}>\n {imageUrl && <img alt=\"\" className={imageTile} src={imageUrl} />}\n {TILES.map((key) => {\n const hex = palette[key]\n const title = IMAGE_PALETTE_TITLES[key]\n\n return (\n <Tooltip\n key={key}\n animate\n content={\n <Text size={1}>\n {hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`}\n </Text>\n }\n\n placement=\"bottom\"\n portal\n >\n <span\n className={tile}\n data-empty={hex === null}\n style={{background: hex ?? undefined}}\n />\n </Tooltip>\n )\n })}\n </div>\n\n <Stack gap={2}>\n <Text muted size={0}>\n Pick the swatch to build the theme around\n </Text>\n <div\n className={variantRow}\n ref={rowRef}\n style={assignInlineVars({\n [variantWidth]: `${thumbnailWidth}px`,\n [variantScale]: String(thumbnailWidth / VARIANT_WIDTH),\n })}\n >\n {variants.map(({variant, title, options: variantOptions}) => (\n // oxlint-disable-next-line control-has-associated-label -- the title below the thumbnail is the label, deeper than the rule looks\n <button\n key={variant}\n aria-pressed={variant === current}\n className={variantButton}\n onClick={() => onVariant(variant)}\n type=\"button\"\n >\n <span className={variantFrame}>\n <span className={variantScaleBox}>\n <ThemeThumbnail options={variantOptions} />\n </span>\n </span>\n <Box as=\"span\" display=\"block\" paddingTop={1}>\n <Text\n align=\"center\"\n as=\"span\"\n size={0}\n textOverflow=\"ellipsis\"\n weight={variant === current ? 'medium' : 'regular'}\n >\n {title}\n </Text>\n </Box>\n </button>\n ))}\n </div>\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 <div className={paletteGrid} data-with-image={imageUrl !== undefined}>\n {imageUrl && <img alt=\"\" className={imageTile} src={imageUrl} />}\n {TILES.map((key) => {\n const hex = palette[key]\n const title = IMAGE_PALETTE_TITLES[key]\n\n return (\n <Tooltip\n key={key}\n animate\n content={\n <Text size={1}>\n {hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`}\n </Text>\n }\n\n placement=\"bottom\"\n portal\n >\n <span\n className={tile}\n data-empty={hex === null}\n style={{background: hex ?? undefined}}\n />\n </Tooltip>\n )\n })}\n </div>\n\n <Stack gap={2}>\n <Text muted size={0}>\n Pick the swatch to build the theme around\n </Text>\n <div\n className={variantRow}\n ref={rowRef}\n style={assignInlineVars({\n [variantWidth]: `${thumbnailWidth}px`,\n [variantScale]: String(thumbnailWidth / VARIANT_WIDTH),\n })}\n >\n {variants.map(({variant, title, options: variantOptions}) => (\n // oxlint-disable-next-line control-has-associated-label -- the title below the thumbnail is the label, deeper than the rule looks\n <button\n key={variant}\n aria-pressed={variant === current}\n className={variantButton}\n onClick={() => onVariant(variant)}\n type=\"button\"\n >\n <span className={variantFrame}>\n <span className={variantScaleBox}>\n <ThemeThumbnail options={variantOptions} />\n </span>\n </span>\n <Box as=\"span\" display=\"block\" paddingTop={1}>\n <Text\n align=\"center\"\n as=\"span\"\n size={0}\n textOverflow=\"ellipsis\"\n weight={variant === current ? 'medium' : 'regular'}\n >\n {title}\n </Text>\n </Box>\n </button>\n ))}\n </div>\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 }","_c","_Fragment","_temp","{\n actorRef,\n focusTitle,\n slug,\n split,\n themes,\n}: {focusTitle: boolean; slug: string} & Pick<ThemerProps, 'actorRef' | 'split' | 'themes'>","(snapshot) => snapshot.context.images","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 actorRef.send({\n type: 'theme.update',\n slug,\n options: applyImagePalette(theme.options, theme.palette, variant),\n })\n }","options","palette","() => actorRef.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) => actorRef.send({type: 'theme.update', slug, options})","(palette, file) =>\n actorRef.send({\n type: 'theme.update',\n slug,\n options: applyImagePalette(theme.options, palette),\n palette,\n imageUrl: URL.createObjectURL(file),\n })","() => actorRef.send({type: 'theme.remove', slug})","(title) => actorRef.send({type: 'theme.update', slug, title})","<ThemeEditorForm\n focusTitle={focusTitle}\n imageUrl={images[slug]}\n onDone={() => actorRef.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) => actorRef.send({type: 'theme.update', slug, options})}\n onPalette={(palette, file) =>\n actorRef.send({\n type: 'theme.update',\n slug,\n options: applyImagePalette(theme.options, palette),\n palette,\n imageUrl: URL.createObjectURL(file),\n })\n }\n onRemove={() => actorRef.send({type: 'theme.remove', slug})}\n onTitleChange={(title) => actorRef.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 key={scheme}\n active={split || scheme === studioScheme}\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 splitHint={splitHint(scheme === studioScheme)}\n />\n ))","scheme","changes","<ScrollAreaBleed>\n {SCHEMES.map((scheme) => (\n <SchemeCard\n key={scheme}\n active={split || scheme === studioScheme}\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 splitHint={splitHint(scheme === studioScheme)}\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 key={scheme}\n active={split || scheme === studioScheme}\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 splitHint={splitHint(scheme === studioScheme)}\n />\n ))}\n </ScrollAreaBleed>\n </Stack>\n </ScrollArea>","<Button\n gap={2}\n icon={CheckmarkIcon}\n onClick={onDone}\n padding={2}\n text=\"Done\"\n mode=\"ghost\"\n />","<Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n mode=\"bleed\"\n padding={2}\n />","<Menu>\n {/* The theme can be restored until it is deleted from the removed themes */}\n <MenuItem icon={TrashIcon} onClick={onRemove} text=\"Remove\" tone=\"critical\" />\n </Menu>","{animate: true, placement: 'top-end', portal: true}","<MenuButton\n button={\n <Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n mode=\"bleed\"\n padding={2}\n />\n }\n id={actionsId}\n menu={\n <Menu>\n {/* The theme can be restored until it is deleted from the removed themes */}\n <MenuItem icon={TrashIcon} onClick={onRemove} text=\"Remove\" tone=\"critical\" />\n </Menu>\n }\n popover={{animate: true, placement: 'top-end', portal: true}}\n />","<Card borderTop padding={3}>\n <Flex align=\"center\" gap={2} justify=\"flex-end\">\n <Button\n gap={2}\n icon={CheckmarkIcon}\n onClick={onDone}\n padding={2}\n text=\"Done\"\n mode=\"ghost\"\n />\n <MenuButton\n button={\n <Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n mode=\"bleed\"\n padding={2}\n />\n }\n id={actionsId}\n menu={\n <Menu>\n {/* The theme can be restored until it is deleted from the removed themes */}\n <MenuItem icon={TrashIcon} onClick={onRemove} text=\"Remove\" tone=\"critical\" />\n </Menu>\n }\n popover={{animate: true, placement: 'top-end', portal: true}}\n />\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 key={scheme}\n active={split || scheme === studioScheme}\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 splitHint={splitHint(scheme === studioScheme)}\n />\n ))}\n </ScrollAreaBleed>\n </Stack>\n </ScrollArea>\n\n {/* Like the Studio's document footer: one primary action, the rest in\n the menu, in the size of the Studio's own buttons (padding and gap of 2) */}\n <Card borderTop padding={3}>\n <Flex align=\"center\" gap={2} justify=\"flex-end\">\n <Button\n gap={2}\n icon={CheckmarkIcon}\n onClick={onDone}\n padding={2}\n text=\"Done\"\n mode=\"ghost\"\n />\n <MenuButton\n button={\n <Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n mode=\"bleed\"\n padding={2}\n />\n }\n id={actionsId}\n menu={\n <Menu>\n {/* The theme can be restored until it is deleted from the removed themes */}\n <MenuItem icon={TrashIcon} onClick={onRemove} text=\"Remove\" tone=\"critical\" />\n </Menu>\n }\n popover={{animate: true, placement: 'top-end', portal: true}}\n />\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 ? splitHint\n : active\n ? 'Shown in the Studio right now'\n : `Shown once the Studio appearance is ${scheme}`}\n </Text>","split\n ? splitHint\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 ? splitHint\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)})","<input\n aria-label={`${name} contrast`}\n className={range}\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 <input\n aria-label={`${name} contrast`}\n className={range}\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 ? splitHint\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 <input\n aria-label={`${name} contrast`}\n className={range}\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)","<input\n aria-label={label}\n className={swatch}\n onChange={(event) => onChange(event.currentTarget.value)}\n type=\"color\"\n value={value}\n />","<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 className={colorRowText} flex={1} gap={2}>\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 <input\n aria-label={label}\n className={swatch}\n onChange={(event) => onChange(event.currentTarget.value)}\n type=\"color\"\n value={value}\n />\n <Stack className={colorRowText} flex={1} gap={2}>\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>","_c","_temp","_Fragment","{actorRef}: Pick<ThemerProps, 'actorRef'>","async (theme: SharedTheme) => {\n const title = displayTitle(theme.title)\n\n try {\n await navigator.clipboard.writeText(encodeTheme(theme))\n toast.push({\n status: 'success',\n title: `Copied ${title} as a code`,\n description: 'Paste it in the themer of another Studio to add the theme there',\n })\n } catch {\n toast.push({status: 'error', title: `Could not copy ${title}`})\n }\n }","(text: string) => {\n const theme = decodeTheme(text)\n\n if (!theme) return false\n\n actorRef.send({type: 'theme.import', title: theme.title, options: theme.options})\n toast.push({status: 'success', title: `Added ${theme.title}`})\n\n return true\n }","theme","async () => {\n let text: string\n\n try {\n text = await navigator.clipboard.readText()\n } catch {\n return 'no-clipboard'\n }\n\n return addThemeFromText(text) ? 'added' : 'no-theme'\n }","text","{copyTheme, addThemeFromText, addThemeFromClipboard}","() => {\n const handlePaste = (event: ClipboardEvent) => {\n if (isEditable(event.target)) return\n\n const theme = decodeTheme(event.clipboardData?.getData('text/plain') ?? '')\n\n if (!theme) return\n\n event.preventDefault()\n actorRef.send({type: 'theme.import', title: theme.title, options: theme.options})\n toast.push({status: 'success', title: `Added ${theme.title}`})\n }\n\n document.addEventListener('paste', handlePaste)\n\n return () => document.removeEventListener('paste', handlePaste)\n }","[actorRef, toast]","_c","{actorRef}","() => {\n if (addThemeFromText(text)) {\n onClose()\n } else {\n setInvalid(true)\n }\n }","<Button mode=\"ghost\" onClick={onClose} padding={2} text=\"Cancel\" />","text.trim()","<Button\n disabled={text.trim() === ''}\n onClick={add}\n padding={2}\n text=\"Add theme\"\n tone=\"primary\"\n />","text.trim() === ''","<Flex gap={2} justify=\"flex-end\" padding={3}>\n <Button mode=\"ghost\" onClick={onClose} padding={2} text=\"Cancel\" />\n <Button\n disabled={text.trim() === ''}\n onClick={add}\n padding={2}\n text=\"Add theme\"\n tone=\"primary\"\n />\n </Flex>","(event) => {\n event.preventDefault()\n add()\n }","<Text muted size={1}>\n Paste the code someone copied from their themer — the theme is added to your list and\n applied.\n </Text>","(event) => {\n setText(event.currentTarget.value)\n setInvalid(false)\n }","event","<TextInput\n aria-describedby={invalid ? errorId : undefined}\n aria-invalid={invalid}\n aria-label=\"Theme code\"\n autoFocus\n customValidity={invalid ? 'That is not a theme code' : undefined}\n fontSize={1}\n onChange={(event) => {\n setText(event.currentTarget.value)\n setInvalid(false)\n }}\n placeholder=\"AQ…\"\n value={text}\n />","invalid ? 'That is not a theme code' : undefined","invalid ? errorId : undefined","invalid && (\n <Card id={errorId} padding={3} radius={2} role=\"alert\" tone=\"critical\">\n <Text size={1}>\n That is not a theme code — codes are one word, copied from a theme's menu.\n </Text>\n </Card>\n )","<Stack gap={3} padding={4}>\n <Text muted size={1}>\n Paste the code someone copied from their themer — the theme is added to your list and\n applied.\n </Text>\n <TextInput\n aria-describedby={invalid ? errorId : undefined}\n aria-invalid={invalid}\n aria-label=\"Theme code\"\n autoFocus\n customValidity={invalid ? 'That is not a theme code' : undefined}\n fontSize={1}\n onChange={(event) => {\n setText(event.currentTarget.value)\n setInvalid(false)\n }}\n placeholder=\"AQ…\"\n value={text}\n />\n {invalid && (\n <Card id={errorId} padding={3} radius={2} role=\"alert\" tone=\"critical\">\n <Text size={1}>\n That is not a theme code — codes are one word, copied from a theme's menu.\n </Text>\n </Card>\n )}\n </Stack>","<form\n onSubmit={(event) => {\n event.preventDefault()\n add()\n }}\n >\n <Stack gap={3} padding={4}>\n <Text muted size={1}>\n Paste the code someone copied from their themer — the theme is added to your list and\n applied.\n </Text>\n <TextInput\n aria-describedby={invalid ? errorId : undefined}\n aria-invalid={invalid}\n aria-label=\"Theme code\"\n autoFocus\n customValidity={invalid ? 'That is not a theme code' : undefined}\n fontSize={1}\n onChange={(event) => {\n setText(event.currentTarget.value)\n setInvalid(false)\n }}\n placeholder=\"AQ…\"\n value={text}\n />\n {invalid && (\n <Card id={errorId} padding={3} radius={2} role=\"alert\" tone=\"critical\">\n <Text size={1}>\n That is not a theme code — codes are one word, copied from a theme's menu.\n </Text>\n </Card>\n )}\n </Stack>\n </form>","<Dialog\n animate\n footer={\n <Flex gap={2} justify=\"flex-end\" padding={3}>\n <Button mode=\"ghost\" onClick={onClose} padding={2} text=\"Cancel\" />\n <Button\n disabled={text.trim() === ''}\n onClick={add}\n padding={2}\n text=\"Add theme\"\n tone=\"primary\"\n />\n </Flex>\n }\n header=\"Add a shared theme\"\n id={id}\n onClickOutside={onClose}\n onClose={onClose}\n width={0}\n >\n <form\n onSubmit={(event) => {\n event.preventDefault()\n add()\n }}\n >\n <Stack gap={3} padding={4}>\n <Text muted size={1}>\n Paste the code someone copied from their themer — the theme is added to your list and\n applied.\n </Text>\n <TextInput\n aria-describedby={invalid ? errorId : undefined}\n aria-invalid={invalid}\n aria-label=\"Theme code\"\n autoFocus\n customValidity={invalid ? 'That is not a theme code' : undefined}\n fontSize={1}\n onChange={(event) => {\n setText(event.currentTarget.value)\n setInvalid(false)\n }}\n placeholder=\"AQ…\"\n value={text}\n />\n {invalid && (\n <Card id={errorId} padding={3} radius={2} role=\"alert\" tone=\"critical\">\n <Text size={1}>\n That is not a theme code — codes are one word, copied from a theme's menu.\n </Text>\n </Card>\n )}\n </Stack>\n </form>\n </Dialog>","_c","frame","{\n actorRef,\n active,\n theme,\n themes,\n}: {active: boolean; theme: ThemerTheme} & Pick<ThemerProps, 'actorRef' | 'themes'>","{actorRef}","displayTitle(theme.title)","title","(candidate) => candidate.slug === theme.slug","slug","(to: number) => {\n const order = themes.map((candidate) => candidate.slug)\n\n order.splice(to, 0, ...order.splice(index, 1))\n actorRef.send({type: 'theme.reorder', order})\n }","(candidate) => candidate.slug","() => {\n // The click lands before the next frame\n requestAnimationFrame(() => {\n dragged.current = false\n })\n }","() => {\n dragged.current = true\n }","{scale: 1.04}","() => {\n if (!dragged.current) actorRef.send({type: 'theme.pick', slug: theme.slug})\n }","(event) => {\n // A mouse or pen drags the card off the button; a finger scrolls the\n // list instead, and moves themes from the menu\n if (listed && event.pointerType !== 'touch') dragControls.start(event)\n }","<span className={frame}>\n <ThemeThumbnail options={theme.options} />\n </span>","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'","<button\n aria-pressed={active}\n className={pickButton}\n onClick={() => {\n if (!dragged.current) actorRef.send({type: 'theme.pick', slug: theme.slug})\n }}\n onPointerDown={(event) => {\n // A mouse or pen drags the card off the button; a finger scrolls the\n // list instead, and moves themes from the menu\n if (listed && event.pointerType !== 'touch') dragControls.start(event)\n }}\n type=\"button\"\n >\n <span className={frame}>\n <ThemeThumbnail options={theme.options} />\n </span>\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 </button>","<Text size={1}>Show more</Text>","<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}`","listed && (\n <>\n <MenuItem\n disabled={index <= 0}\n icon={ArrowUpIcon}\n onClick={() => moveTo(index - 1)}\n text=\"Move up\"\n />\n <MenuItem\n disabled={index >= themes.length - 1}\n icon={ArrowDownIcon}\n onClick={() => moveTo(index + 1)}\n text=\"Move down\"\n />\n <MenuDivider />\n </>\n )","length","theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )","source","() => actorRef.send({type: 'theme.duplicate', slug: theme.slug})","<MenuItem\n icon={CopyIcon}\n onClick={() => actorRef.send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />","theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'","<MenuItem\n icon={ShareIcon}\n onClick={() => void copyTheme(theme)}\n text=\"Copy to share\"\n />","theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => actorRef.send({type: 'theme.remove', slug: theme.slug})}\n text=\"Remove\"\n tone=\"critical\"\n />\n </>\n )","<Menu>\n {listed && (\n <>\n <MenuItem\n disabled={index <= 0}\n icon={ArrowUpIcon}\n onClick={() => moveTo(index - 1)}\n text=\"Move up\"\n />\n <MenuItem\n disabled={index >= themes.length - 1}\n icon={ArrowDownIcon}\n onClick={() => moveTo(index + 1)}\n text=\"Move down\"\n />\n <MenuDivider />\n </>\n )}\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => actorRef.send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n <MenuItem\n icon={ShareIcon}\n onClick={() => void copyTheme(theme)}\n text=\"Copy to share\"\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => actorRef.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 className={menuCard} radius={2} scheme=\"dark\">\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={menuId}\n menu={\n <Menu>\n {listed && (\n <>\n <MenuItem\n disabled={index <= 0}\n icon={ArrowUpIcon}\n onClick={() => moveTo(index - 1)}\n text=\"Move up\"\n />\n <MenuItem\n disabled={index >= themes.length - 1}\n icon={ArrowDownIcon}\n onClick={() => moveTo(index + 1)}\n text=\"Move down\"\n />\n <MenuDivider />\n </>\n )}\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => actorRef.send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n <MenuItem\n icon={ShareIcon}\n onClick={() => void copyTheme(theme)}\n text=\"Copy to share\"\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => actorRef.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>","<div className={menuSlot} data-visible={menuOpen}>\n <Tooltip animate content={<Text size={1}>Show more</Text>} placement=\"bottom\" portal>\n <Card className={menuCard} radius={2} scheme=\"dark\">\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={menuId}\n menu={\n <Menu>\n {listed && (\n <>\n <MenuItem\n disabled={index <= 0}\n icon={ArrowUpIcon}\n onClick={() => moveTo(index - 1)}\n text=\"Move up\"\n />\n <MenuItem\n disabled={index >= themes.length - 1}\n icon={ArrowDownIcon}\n onClick={() => moveTo(index + 1)}\n text=\"Move down\"\n />\n <MenuDivider />\n </>\n )}\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => actorRef.send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n <MenuItem\n icon={ShareIcon}\n onClick={() => void copyTheme(theme)}\n text=\"Copy to share\"\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => actorRef.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 </div>","<Reorder.Item\n as=\"div\"\n className={root}\n dragControls={dragControls}\n dragListener={false}\n onDragEnd={() => {\n // The click lands before the next frame\n requestAnimationFrame(() => {\n dragged.current = false\n })\n }}\n onDragStart={() => {\n dragged.current = true\n }}\n ref={active ? scrollRefIntoViewIfNeeded : undefined}\n value={theme.slug}\n whileDrag={{scale: 1.04}}\n >\n <button\n aria-pressed={active}\n className={pickButton}\n onClick={() => {\n if (!dragged.current) actorRef.send({type: 'theme.pick', slug: theme.slug})\n }}\n onPointerDown={(event) => {\n // A mouse or pen drags the card off the button; a finger scrolls the\n // list instead, and moves themes from the menu\n if (listed && event.pointerType !== 'touch') dragControls.start(event)\n }}\n type=\"button\"\n >\n <span className={frame}>\n <ThemeThumbnail options={theme.options} />\n </span>\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 </button>\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 <div className={menuSlot} data-visible={menuOpen}>\n <Tooltip animate content={<Text size={1}>Show more</Text>} placement=\"bottom\" portal>\n <Card className={menuCard} radius={2} scheme=\"dark\">\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={menuId}\n menu={\n <Menu>\n {listed && (\n <>\n <MenuItem\n disabled={index <= 0}\n icon={ArrowUpIcon}\n onClick={() => moveTo(index - 1)}\n text=\"Move up\"\n />\n <MenuItem\n disabled={index >= themes.length - 1}\n icon={ArrowDownIcon}\n onClick={() => moveTo(index + 1)}\n text=\"Move down\"\n />\n <MenuDivider />\n </>\n )}\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => actorRef.send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n <MenuItem\n icon={ShareIcon}\n onClick={() => void copyTheme(theme)}\n text=\"Copy to share\"\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => actorRef.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 </div>\n </Reorder.Item>","active ? scrollRefIntoViewIfNeeded : undefined","Reorder.Item","candidate","_c","_temp","frame","_Fragment","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\n gap={2}\n icon={ClipboardIcon}\n mode=\"ghost\"\n onClick={() => void handleCopy()}\n padding={2}\n text=\"Copy\"\n />\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\n gap={2}\n icon={ClipboardIcon}\n mode=\"ghost\"\n onClick={() => void handleCopy()}\n padding={2}\n text=\"Copy\"\n />\n </Flex>\n }\n header=\"Add to your config\"\n id={id}\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>","_c","{\n actorRef,\n themes,\n removed,\n active,\n}: Pick<ThemerProps, 'actorRef' | 'themes' | 'removed' | 'active'>","{actorRef}","(palette, file) =>\n actorRef.send({\n type: 'theme.add',\n title: titleFromFileName(file.name),\n options: optionsFromImagePalette(palette),\n palette,\n imageUrl: URL.createObjectURL(file),\n })","(order: string[]) => actorRef.send({type: 'theme.reorder', order})","themes.map((theme) => theme.slug)","(theme) => theme.slug","{\n slug: CONFIG_SLUG,\n title: CONFIG_TITLE,\n options: {},\n source: 'config',\n }","<ThemeCard\n actorRef={actorRef}\n active={!active}\n theme={{\n slug: CONFIG_SLUG,\n title: CONFIG_TITLE,\n options: {},\n source: 'config',\n }}\n themes={themes}\n />","!active","themes.map((theme) => (\n <ThemeCard\n key={theme.slug}\n actorRef={actorRef}\n active={theme.slug === active?.slug}\n theme={theme}\n themes={themes}\n />\n ))","slug","theme","<MotionConfig reducedMotion=\"user\">\n <Reorder.Group\n as=\"div\"\n className={cardGrid}\n onReorder={(order: string[]) => actorRef.send({type: 'theme.reorder', order})}\n values={themes.map((theme) => theme.slug)}\n >\n <ThemeCard\n actorRef={actorRef}\n active={!active}\n theme={{\n slug: CONFIG_SLUG,\n title: CONFIG_TITLE,\n options: {},\n source: 'config',\n }}\n themes={themes}\n />\n {themes.map((theme) => (\n <ThemeCard\n key={theme.slug}\n actorRef={actorRef}\n active={theme.slug === active?.slug}\n theme={theme}\n themes={themes}\n />\n ))}\n </Reorder.Group>\n </MotionConfig>","Reorder.Group","removed.length > 0 && (\n <Button\n gap={2}\n icon={RestoreIcon}\n mode=\"bleed\"\n onClick={() => actorRef.send({type: 'flow.removed'})}\n padding={2}\n text={`Show removed (${removed.length})`}\n width=\"fill\"\n />\n )","length","<ScrollArea padding={3}>\n <Stack gap={4}>\n {/* The group tells a column from a grid by measuring the cards, and\n lets go of the layout animations for users who prefer reduced motion */}\n <MotionConfig reducedMotion=\"user\">\n <Reorder.Group\n as=\"div\"\n className={cardGrid}\n onReorder={(order: string[]) => actorRef.send({type: 'theme.reorder', order})}\n values={themes.map((theme) => theme.slug)}\n >\n <ThemeCard\n actorRef={actorRef}\n active={!active}\n theme={{\n slug: CONFIG_SLUG,\n title: CONFIG_TITLE,\n options: {},\n source: 'config',\n }}\n themes={themes}\n />\n {themes.map((theme) => (\n <ThemeCard\n key={theme.slug}\n actorRef={actorRef}\n active={theme.slug === active?.slug}\n theme={theme}\n themes={themes}\n />\n ))}\n </Reorder.Group>\n </MotionConfig>\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 gap={2}\n icon={RestoreIcon}\n mode=\"bleed\"\n onClick={() => actorRef.send({type: 'flow.removed'})}\n padding={2}\n text={`Show removed (${removed.length})`}\n width=\"fill\"\n />\n )}\n </Stack>\n </ScrollArea>","active?.source === 'custom' ? (\n <Button\n gap={2}\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: active.slug})}\n padding={2}\n text=\"Edit\"\n mode=\"ghost\"\n />\n ) : (\n <Button\n gap={2}\n icon={CopyIcon}\n onClick={() =>\n actorRef.send({type: 'theme.duplicate', slug: active?.slug ?? CONFIG_SLUG})\n }\n padding={2}\n text=\"Duplicate & Edit\"\n mode=\"ghost\"\n />\n )","active?.source","<Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n // The image's colors are read on device, which the menu button shows while it lasts\n loading={busy}\n mode=\"bleed\"\n padding={2}\n />","<MenuItem\n icon={AddIcon}\n onClick={() => actorRef.send({type: 'theme.add'})}\n text=\"Add theme\"\n />","<MenuItem\n icon={ImageIcon}\n onClick={() => imageInputRef.current?.click()}\n text=\"Add from image\"\n />","<MenuItem\n icon={ClipboardIcon}\n onClick={async () => {\n // Straight from the clipboard where the browser allows; by hand otherwise\n if ((await addThemeFromClipboard()) !== 'added') {\n actorRef.send({type: 'dialog.paste'})\n }\n }}\n text=\"Add from code\"\n />","active && (\n <>\n <MenuDivider />\n <MenuItem\n icon={CodeBlockIcon}\n onClick={() => actorRef.send({type: 'dialog.snippet'})}\n text=\"Show theme code\"\n />\n </>\n )","<Menu>\n <MenuItem\n icon={AddIcon}\n onClick={() => actorRef.send({type: 'theme.add'})}\n text=\"Add theme\"\n />\n <MenuItem\n icon={ImageIcon}\n onClick={() => imageInputRef.current?.click()}\n text=\"Add from image\"\n />\n <MenuItem\n icon={ClipboardIcon}\n onClick={async () => {\n // Straight from the clipboard where the browser allows; by hand otherwise\n if ((await addThemeFromClipboard()) !== 'added') {\n actorRef.send({type: 'dialog.paste'})\n }\n }}\n text=\"Add from code\"\n />\n {active && (\n <>\n <MenuDivider />\n <MenuItem\n icon={CodeBlockIcon}\n onClick={() => actorRef.send({type: 'dialog.snippet'})}\n text=\"Show theme code\"\n />\n </>\n )}\n </Menu>","{animate: true, placement: 'top-end', portal: true}","<MenuButton\n button={\n <Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n // The image's colors are read on device, which the menu button shows while it lasts\n loading={busy}\n mode=\"bleed\"\n padding={2}\n />\n }\n id={actionsId}\n menu={\n // Short labels: the menu is confined to the sidebar, which can be narrow\n <Menu>\n <MenuItem\n icon={AddIcon}\n onClick={() => actorRef.send({type: 'theme.add'})}\n text=\"Add theme\"\n />\n <MenuItem\n icon={ImageIcon}\n onClick={() => imageInputRef.current?.click()}\n text=\"Add from image\"\n />\n <MenuItem\n icon={ClipboardIcon}\n onClick={async () => {\n // Straight from the clipboard where the browser allows; by hand otherwise\n if ((await addThemeFromClipboard()) !== 'added') {\n actorRef.send({type: 'dialog.paste'})\n }\n }}\n text=\"Add from code\"\n />\n {active && (\n <>\n <MenuDivider />\n <MenuItem\n icon={CodeBlockIcon}\n onClick={() => actorRef.send({type: 'dialog.snippet'})}\n text=\"Show theme code\"\n />\n </>\n )}\n </Menu>\n }\n popover={{animate: true, placement: 'top-end', portal: true}}\n />","<Card borderTop padding={3}>\n <Flex align=\"center\" gap={2} justify=\"flex-end\">\n {active?.source === 'custom' ? (\n <Button\n gap={2}\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: active.slug})}\n padding={2}\n text=\"Edit\"\n mode=\"ghost\"\n />\n ) : (\n <Button\n gap={2}\n icon={CopyIcon}\n onClick={() =>\n actorRef.send({type: 'theme.duplicate', slug: active?.slug ?? CONFIG_SLUG})\n }\n padding={2}\n text=\"Duplicate & Edit\"\n mode=\"ghost\"\n />\n )}\n <MenuButton\n button={\n <Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n // The image's colors are read on device, which the menu button shows while it lasts\n loading={busy}\n mode=\"bleed\"\n padding={2}\n />\n }\n id={actionsId}\n menu={\n // Short labels: the menu is confined to the sidebar, which can be narrow\n <Menu>\n <MenuItem\n icon={AddIcon}\n onClick={() => actorRef.send({type: 'theme.add'})}\n text=\"Add theme\"\n />\n <MenuItem\n icon={ImageIcon}\n onClick={() => imageInputRef.current?.click()}\n text=\"Add from image\"\n />\n <MenuItem\n icon={ClipboardIcon}\n onClick={async () => {\n // Straight from the clipboard where the browser allows; by hand otherwise\n if ((await addThemeFromClipboard()) !== 'added') {\n actorRef.send({type: 'dialog.paste'})\n }\n }}\n text=\"Add from code\"\n />\n {active && (\n <>\n <MenuDivider />\n <MenuItem\n icon={CodeBlockIcon}\n onClick={() => actorRef.send({type: 'dialog.snippet'})}\n text=\"Show theme code\"\n />\n </>\n )}\n </Menu>\n }\n popover={{animate: true, placement: 'top-end', portal: true}}\n />\n </Flex>\n </Card>","<ImageFileInput onFile={pickImage} ref={imageInputRef} />","dialog === 'paste' && (\n <PasteThemeDialog\n actorRef={actorRef}\n onClose={() => actorRef.send({type: 'dialog.close'})}\n />\n )","dialog === 'snippet' && active && (\n <ThemeSnippetDialog onClose={() => actorRef.send({type: 'dialog.close'})} theme={active} />\n )","<>\n <ScrollArea padding={3}>\n <Stack gap={4}>\n {/* The group tells a column from a grid by measuring the cards, and\n lets go of the layout animations for users who prefer reduced motion */}\n <MotionConfig reducedMotion=\"user\">\n <Reorder.Group\n as=\"div\"\n className={cardGrid}\n onReorder={(order: string[]) => actorRef.send({type: 'theme.reorder', order})}\n values={themes.map((theme) => theme.slug)}\n >\n <ThemeCard\n actorRef={actorRef}\n active={!active}\n theme={{\n slug: CONFIG_SLUG,\n title: CONFIG_TITLE,\n options: {},\n source: 'config',\n }}\n themes={themes}\n />\n {themes.map((theme) => (\n <ThemeCard\n key={theme.slug}\n actorRef={actorRef}\n active={theme.slug === active?.slug}\n theme={theme}\n themes={themes}\n />\n ))}\n </Reorder.Group>\n </MotionConfig>\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 gap={2}\n icon={RestoreIcon}\n mode=\"bleed\"\n onClick={() => actorRef.send({type: 'flow.removed'})}\n padding={2}\n text={`Show removed (${removed.length})`}\n width=\"fill\"\n />\n )}\n </Stack>\n </ScrollArea>\n\n {/* Like the Studio's document footer: one primary action, the rest in\n the menu, in the size of the Studio's own buttons (padding and gap of 2) */}\n <Card borderTop padding={3}>\n <Flex align=\"center\" gap={2} justify=\"flex-end\">\n {active?.source === 'custom' ? (\n <Button\n gap={2}\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: active.slug})}\n padding={2}\n text=\"Edit\"\n mode=\"ghost\"\n />\n ) : (\n <Button\n gap={2}\n icon={CopyIcon}\n onClick={() =>\n actorRef.send({type: 'theme.duplicate', slug: active?.slug ?? CONFIG_SLUG})\n }\n padding={2}\n text=\"Duplicate & Edit\"\n mode=\"ghost\"\n />\n )}\n <MenuButton\n button={\n <Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n // The image's colors are read on device, which the menu button shows while it lasts\n loading={busy}\n mode=\"bleed\"\n padding={2}\n />\n }\n id={actionsId}\n menu={\n // Short labels: the menu is confined to the sidebar, which can be narrow\n <Menu>\n <MenuItem\n icon={AddIcon}\n onClick={() => actorRef.send({type: 'theme.add'})}\n text=\"Add theme\"\n />\n <MenuItem\n icon={ImageIcon}\n onClick={() => imageInputRef.current?.click()}\n text=\"Add from image\"\n />\n <MenuItem\n icon={ClipboardIcon}\n onClick={async () => {\n // Straight from the clipboard where the browser allows; by hand otherwise\n if ((await addThemeFromClipboard()) !== 'added') {\n actorRef.send({type: 'dialog.paste'})\n }\n }}\n text=\"Add from code\"\n />\n {active && (\n <>\n <MenuDivider />\n <MenuItem\n icon={CodeBlockIcon}\n onClick={() => actorRef.send({type: 'dialog.snippet'})}\n text=\"Show theme code\"\n />\n </>\n )}\n </Menu>\n }\n popover={{animate: true, placement: 'top-end', portal: true}}\n />\n </Flex>\n </Card>\n\n <ImageFileInput onFile={pickImage} ref={imageInputRef} />\n\n {dialog === 'paste' && (\n <PasteThemeDialog\n actorRef={actorRef}\n onClose={() => actorRef.send({type: 'dialog.close'})}\n />\n )}\n {dialog === 'snippet' && active && (\n <ThemeSnippetDialog onClose={() => actorRef.send({type: 'dialog.close'})} theme={active} />\n )}\n </>","_c","_temp","_Fragment","{\n actorRef,\n dispatch,\n navbarHeight,\n split,\n}: Pick<ThemerProps, 'actorRef' | 'dispatch' | 'navbarHeight' | 'split'>","(snapshot) => resolveThemes(snapshot.context)","navbarHeight === null ? undefined : {height: navbarHeight}","<Box className={title} flex={1}>\n <Text size={1} textOverflow=\"ellipsis\" weight=\"semibold\">\n {VIEW_TITLES[view.name]}\n </Text>\n </Box>","VIEW_TITLES[view.name]","!isTooSmallForSplitScreen && (\n <TooltipButton\n aria-pressed={split}\n icon={SplitVerticalIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n const type = split ? 'split-screen:close' : 'split-screen:open'\n addThemerTransitionType(type)\n dispatch({type})\n })\n }\n onMouseEnter={() =>\n startTransition(() => {\n dispatch({type: 'split-screen:prerender'})\n })\n }\n padding={2}\n selected={split}\n tooltip=\"Show light and dark side by side\"\n />\n )","<TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n if (split) addThemerTransitionType('split-screen:close')\n addThemerTransitionType('close')\n dispatch({type: 'close'})\n })\n }\n padding={2}\n tooltip=\"Close themer\"\n />","<Flex>\n {!isTooSmallForSplitScreen && (\n <TooltipButton\n aria-pressed={split}\n icon={SplitVerticalIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n const type = split ? 'split-screen:close' : 'split-screen:open'\n addThemerTransitionType(type)\n dispatch({type})\n })\n }\n onMouseEnter={() =>\n startTransition(() => {\n dispatch({type: 'split-screen:prerender'})\n })\n }\n padding={2}\n selected={split}\n tooltip=\"Show light and dark side by side\"\n />\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n if (split) addThemerTransitionType('split-screen:close')\n addThemerTransitionType('close')\n dispatch({type: 'close'})\n })\n }\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>","<Flex align=\"center\" gap={1} height=\"fill\">\n <Box className={title} flex={1}>\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 {!isTooSmallForSplitScreen && (\n <TooltipButton\n aria-pressed={split}\n icon={SplitVerticalIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n const type = split ? 'split-screen:close' : 'split-screen:open'\n addThemerTransitionType(type)\n dispatch({type})\n })\n }\n onMouseEnter={() =>\n startTransition(() => {\n dispatch({type: 'split-screen:prerender'})\n })\n }\n padding={2}\n selected={split}\n tooltip=\"Show light and dark side by side\"\n />\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n if (split) addThemerTransitionType('split-screen:close')\n addThemerTransitionType('close')\n dispatch({type: 'close'})\n })\n }\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>\n </Flex>","<Card\n borderBottom\n padding={3}\n sizing=\"border\"\n style={navbarHeight === null ? undefined : {height: navbarHeight}}\n >\n <Flex align=\"center\" gap={1} height=\"fill\">\n <Box className={title} flex={1}>\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 {!isTooSmallForSplitScreen && (\n <TooltipButton\n aria-pressed={split}\n icon={SplitVerticalIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n const type = split ? 'split-screen:close' : 'split-screen:open'\n addThemerTransitionType(type)\n dispatch({type})\n })\n }\n onMouseEnter={() =>\n startTransition(() => {\n dispatch({type: 'split-screen:prerender'})\n })\n }\n padding={2}\n selected={split}\n tooltip=\"Show light and dark side by side\"\n />\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n if (split) addThemerTransitionType('split-screen:close')\n addThemerTransitionType('close')\n dispatch({type: 'close'})\n })\n }\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>\n </Flex>\n </Card>","<ThemeList actorRef={actorRef} active={active} removed={removed} themes={themes} />","<Activity key=\"list\" mode={view.name === 'list' ? 'visible' : 'hidden'}>\n <ThemeList actorRef={actorRef} active={active} removed={removed} themes={themes} />\n </Activity>","view.name === 'list' ? 'visible' : 'hidden'","view.name === 'edit' && (\n <ThemeEditor\n actorRef={actorRef}\n focusTitle={view.focusTitle}\n slug={view.slug}\n split={split}\n themes={themes}\n />\n )","focusTitle","name","slug","<RemovedThemes actorRef={actorRef} removed={removed} />","<Activity key=\"removed\" mode={view.name === 'removed' ? 'visible' : 'hidden'}>\n <RemovedThemes actorRef={actorRef} removed={removed} />\n </Activity>","view.name === 'removed' ? 'visible' : 'hidden'","<Card height=\"fill\">\n <Flex direction=\"column\" height=\"fill\">\n {/* The header sits next to the Studio navbar and takes its height, so\n the two bottom borders line up — whatever height the Studio, its\n breakpoint or a custom navbar gives it */}\n <Card\n borderBottom\n padding={3}\n sizing=\"border\"\n style={navbarHeight === null ? undefined : {height: navbarHeight}}\n >\n <Flex align=\"center\" gap={1} height=\"fill\">\n <Box className={title} flex={1}>\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 {!isTooSmallForSplitScreen && (\n <TooltipButton\n aria-pressed={split}\n icon={SplitVerticalIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n const type = split ? 'split-screen:close' : 'split-screen:open'\n addThemerTransitionType(type)\n dispatch({type})\n })\n }\n onMouseEnter={() =>\n startTransition(() => {\n dispatch({type: 'split-screen:prerender'})\n })\n }\n padding={2}\n selected={split}\n tooltip=\"Show light and dark side by side\"\n />\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n if (split) addThemerTransitionType('split-screen:close')\n addThemerTransitionType('close')\n dispatch({type: 'close'})\n })\n }\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>\n </Flex>\n </Card>\n\n <Activity key=\"list\" mode={view.name === 'list' ? 'visible' : 'hidden'}>\n <ThemeList actorRef={actorRef} active={active} removed={removed} themes={themes} />\n </Activity>\n {view.name === 'edit' && (\n <ThemeEditor\n actorRef={actorRef}\n focusTitle={view.focusTitle}\n slug={view.slug}\n split={split}\n themes={themes}\n />\n )}\n <Activity key=\"removed\" mode={view.name === 'removed' ? 'visible' : 'hidden'}>\n <RemovedThemes actorRef={actorRef} removed={removed} />\n </Activity>\n </Flex>\n </Card>","_c","_temp","{\n actorRef,\n dispatch,\n navbarHeight,\n split,\n}: Pick<ThemerProps, 'actorRef' | 'dispatch' | 'navbarHeight' | 'split'>","() => 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}","(boundaryElement) => {\n startTransition(() => setBoundaryElement(boundaryElement))\n // Intentionally not unsetting the element as it would create unnecessary work when <Activity> unhides\n return () => {}\n }","boundaryElement","() => {}","() => setWidth(MINIMUM_WIDTH)","<div\n aria-label=\"Resize the themer\"\n className={resizeHandle}\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 />","<ThemerSidebar\n split={split}\n navbarHeight={navbarHeight}\n actorRef={actorRef}\n dispatch={dispatch}\n />","<Box className={content} height=\"fill\" overflow=\"hidden\">\n <BoundaryElementProvider element={boundaryElement}>\n <ThemerSidebar\n split={split}\n navbarHeight={navbarHeight}\n actorRef={actorRef}\n dispatch={dispatch}\n />\n </BoundaryElementProvider>\n </Box>","<Card\n borderLeft\n className={frame}\n height=\"fill\"\n ref={(boundaryElement) => {\n startTransition(() => setBoundaryElement(boundaryElement))\n // Intentionally not unsetting the element as it would create unnecessary work when <Activity> unhides\n return () => {}\n }}\n >\n <div\n aria-label=\"Resize the themer\"\n className={resizeHandle}\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 className={content} height=\"fill\" overflow=\"hidden\">\n <BoundaryElementProvider element={boundaryElement}>\n <ThemerSidebar\n split={split}\n navbarHeight={navbarHeight}\n actorRef={actorRef}\n dispatch={dispatch}\n />\n </BoundaryElementProvider>\n </Box>\n </Card>","<Layer className={sidebar} style={{width}} zOffset={100}>\n <Card\n borderLeft\n className={frame}\n height=\"fill\"\n ref={(boundaryElement) => {\n startTransition(() => setBoundaryElement(boundaryElement))\n // Intentionally not unsetting the element as it would create unnecessary work when <Activity> unhides\n return () => {}\n }}\n >\n <div\n aria-label=\"Resize the themer\"\n className={resizeHandle}\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 className={content} height=\"fill\" overflow=\"hidden\">\n <BoundaryElementProvider element={boundaryElement}>\n <ThemerSidebar\n split={split}\n navbarHeight={navbarHeight}\n actorRef={actorRef}\n dispatch={dispatch}\n />\n </BoundaryElementProvider>\n </Box>\n </Card>\n </Layer>","_c"],"sources":["../src/tool/ScrollArea.css.ts","../src/tool/ScrollArea.tsx","../src/tool/ThemePreview.css.ts","../src/tool/ThemePreview.tsx","../src/tool/ThemeThumbnail.tsx","../src/tool/RemovedThemes.css.ts","../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.css.ts","../src/tool/ImagePaletteSection.tsx","../src/tool/ThemeEditor.css.ts","../src/tool/ThemeEditor.tsx","../src/tool/share.ts","../src/tool/useThemeCodes.ts","../src/tool/PasteThemeDialog.tsx","../src/tool/ThemeCard.css.ts","../src/tool/ThemeCard.tsx","../src/tool/options.ts","../src/tool/snippet.ts","../src/tool/ThemeSnippetDialog.tsx","../src/tool/ThemeList.css.ts","../src/tool/ThemeList.tsx","../src/tool/useIsTooSmallForSplitScreen.ts","../src/tool/ThemerSidebar.css.ts","../src/tool/ThemerSidebar.tsx","../src/tool/ResizableSidebar.css.ts","../src/tool/ResizableSidebar.tsx"],"sourcesContent":["import {style} from '@vanilla-extract/css'\n\n/** Always reserves the scrollbar's space, so the content never shifts */\nexport const root = style({\n scrollbarGutter: 'stable',\n})\n","import {Box, useTheme_v2} from '@sanity/ui'\nimport {createContext, useContext, useEffect, useMemo, useRef, useState} from 'react'\n\nimport {root} from './ScrollArea.css'\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 gutter the last scroll area measured. It is the same for every scroll\n * area (the reserved gutter does not depend on the content), so the next one\n * starts out with the right padding instead of shifting after its first\n * measurement — which would also throw off anything scrolling into view as\n * the content mounts.\n */\nlet lastGutter = 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(() => lastGutter)\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 = () => {\n lastGutter = element.offsetWidth - element.clientWidth\n setGutter(lastGutter)\n }\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 <Box\n className={root}\n flex={1}\n overflow=\"auto\"\n padding={padding}\n ref={ref}\n style={{paddingRight: inset.right}}\n >\n <ScrollAreaInsetContext value={inset}>{children}</ScrollAreaInsetContext>\n </Box>\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 {createVar, style, styleVariants} from '@vanilla-extract/css'\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\n/** The thickness of a line of text — bold enough to survive a half-size preview */\nconst LINE = 4\n\n/**\n * The colors a mockup paints with — only the ones the theme options change —\n * assigned per color scheme from the theme it previews\n */\nexport const colors = {\n bg: createVar(),\n fg: createVar(),\n mutedFg: createVar(),\n border: createVar(),\n accent: createVar(),\n avatar: createVar(),\n selectedBg: createVar(),\n selectedFg: createVar(),\n inputBg: createVar(),\n inputBorder: createVar(),\n}\n\nexport const root = style({\n position: 'relative',\n aspectRatio: '16 / 9',\n overflow: 'hidden',\n})\n\nconst studioBase = style({\n position: 'absolute',\n inset: 0,\n display: 'flex',\n flexDirection: 'column',\n background: colors.bg,\n})\n\nexport const studio = styleVariants({\n light: [studioBase],\n dark: [studioBase, {clipPath: DARK_CLIP_PATH}],\n})\n\nexport const navbar = style({\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n gap: 5,\n height: '18%',\n padding: '0 5%',\n borderBottom: `1px solid ${colors.border}`,\n})\n\nexport const logo = style({\n flex: 'none',\n width: 8,\n height: 8,\n borderRadius: 2,\n background: colors.accent,\n})\n\nexport const spacer = style({\n flex: 1,\n})\n\nexport const avatar = style({\n flex: 'none',\n width: 8,\n height: 8,\n borderRadius: '50%',\n background: colors.avatar,\n})\n\nexport const body = style({\n flex: 1,\n display: 'flex',\n minHeight: 0,\n})\n\nexport const list = style({\n flex: 'none',\n display: 'flex',\n flexDirection: 'column',\n width: '36%',\n borderRight: `1px solid ${colors.border}`,\n})\n\nexport const listRow = style({\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n height: '18%',\n padding: '0 9%',\n selectors: {\n \"&[data-selected='true']\": {\n background: colors.selectedBg,\n },\n },\n})\n\nexport const pane = style({\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n minWidth: 0,\n})\n\nexport const form = 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\nexport const field = style({\n flex: 'none',\n display: 'flex',\n flexDirection: 'column',\n gap: 3,\n})\n\nexport const input = style({\n height: 12,\n borderRadius: 3,\n background: colors.inputBg,\n boxShadow: `inset 0 0 0 1px ${colors.inputBorder}`,\n})\n\nexport const footer = style({\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n height: '20%',\n padding: '0 8%',\n borderTop: `1px solid ${colors.border}`,\n})\n\nexport const button = style({\n width: '32%',\n height: '50%',\n borderRadius: 3,\n background: colors.accent,\n})\n\nconst lineBase = style({\n flex: 'none',\n height: LINE,\n borderRadius: LINE / 2,\n})\n\n/** A line of text, in one of the text colors — its width is set inline */\nexport const line = styleVariants({\n fg: [lineBase, {background: colors.fg}],\n muted: [lineBase, {background: colors.mutedFg}],\n selected: [lineBase, {background: colors.selectedFg}],\n})\n","import {ThemeProvider, useTheme_v2} from '@sanity/ui'\nimport {type ThemeColorSchemeKey} from '@sanity/ui/theme'\nimport {assignInlineVars} from '@vanilla-extract/dynamic'\nimport {useMemo} from 'react'\n\nimport {buildTheme} from '../theme/buildTheme'\nimport type {BuildThemeOptions} from '../theme/options'\n\nimport {\n avatar,\n body,\n button,\n colors,\n field,\n footer,\n form,\n input,\n line,\n list,\n listRow,\n logo,\n navbar,\n pane,\n root,\n spacer,\n studio,\n} from './ThemePreview.css'\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/**\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 <div aria-hidden className={root}>\n <ThemeProvider scheme=\"light\" theme={theme} tone=\"default\">\n <MockStudio scheme=\"light\" />\n </ThemeProvider>\n <ThemeProvider scheme=\"dark\" theme={theme} tone=\"default\">\n <MockStudio scheme=\"dark\" />\n </ThemeProvider>\n </div>\n )\n}\n\nfunction MockStudio(props: {scheme: ThemeColorSchemeKey}) {\n const {scheme} = props\n const {color} = useTheme_v2()\n const selected = color.selectable.default.selected\n const inputColor = color.input.default.enabled\n\n return (\n <div\n className={studio[scheme]}\n style={assignInlineVars({\n [colors.bg]: color.bg,\n [colors.fg]: color.fg,\n [colors.mutedFg]: color.muted.fg,\n [colors.border]: color.border,\n [colors.accent]: color.button.default.primary.enabled.bg,\n [colors.avatar]: color.avatar.blue.bg,\n [colors.selectedBg]: selected.bg,\n [colors.selectedFg]: selected.fg,\n [colors.inputBg]: inputColor.bg,\n [colors.inputBorder]: inputColor.border,\n })}\n >\n <div className={navbar}>\n <div className={logo} />\n <div className={line.fg} style={{width: '22%'}} />\n <div className={spacer} />\n <div className={avatar} />\n </div>\n\n <div className={body}>\n <div className={list}>\n {LIST_ROWS.map((width, index) => {\n const isSelected = index === 1\n\n return (\n <div key={width} className={listRow} data-selected={isSelected}>\n <div className={isSelected ? line.selected : line.muted} style={{width}} />\n </div>\n )\n })}\n </div>\n\n <div className={pane}>\n <div className={form}>\n <div className={line.fg} style={{width: '45%'}} />\n {FIELDS.map((width) => (\n <div key={width} className={field}>\n <div className={line.muted} style={{width}} />\n <div className={input} />\n </div>\n ))}\n </div>\n <div className={footer}>\n <div className={button} />\n </div>\n </div>\n </div>\n </div>\n )\n}\n","import {Card} from '@sanity/ui'\n\nimport type {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 {style} from '@vanilla-extract/css'\n\n/** A removed theme's thumbnail, dimmed to read as removed */\nexport const removedThumbnail = style({\n opacity: 0.6,\n})\n","import {CheckmarkIcon} from '@sanity/icons/Checkmark'\nimport {Box, Button, Card, Flex, Stack, Text} from '@sanity/ui'\n\nimport type {ThemerProps} from '#types'\n\nimport {ScrollArea} from './ScrollArea'\nimport {displayTitle} from './themes'\nimport {ThemeThumbnail} from './ThemeThumbnail'\n\nimport {removedThumbnail} from './RemovedThemes.css'\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 footer leads back to the\n * list, and the machine leaves the flow on its own once the last theme is\n * gone.\n *\n * @internal\n */\nexport function RemovedThemes({actorRef, removed}: Pick<ThemerProps, 'actorRef' | 'removed'>) {\n return (\n <>\n <ScrollArea padding={3}>\n <Stack gap={5}>\n {removed.map((theme) => (\n <Stack key={theme.slug} gap={3}>\n <Box className={removedThumbnail}>\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 mode=\"ghost\"\n onClick={() => actorRef.send({type: 'theme.restore', slug: theme.slug})}\n padding={2}\n text=\"Restore\"\n />\n {theme.source === 'custom' && (\n <Button\n mode=\"ghost\"\n onClick={() => actorRef.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 {/* In the size of the Studio's own buttons (padding and gap of 2) */}\n <Card borderTop padding={3}>\n <Flex justify=\"flex-end\">\n <Button\n gap={2}\n icon={CheckmarkIcon}\n onClick={() => actorRef.send({type: 'flow.list'})}\n padding={2}\n text=\"Done\"\n mode=\"ghost\"\n />\n </Flex>\n </Card>\n </>\n )\n}\n","import {type ThemeColorSchemeKey} from '@sanity/ui/theme'\n\nimport {hexToHsl, hslToHex, relativeLuminance} from '../theme/hsl'\nimport {\n type BuildThemeOptions,\n DEFAULT_BACKGROUND,\n SCHEMES,\n type SchemeThemeOptions,\n} 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/**\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, type 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, sized like\n * the Studio's own buttons (`padding` and `gap` of 2). Icon-only buttons take\n * 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\n aria-label={buttonProps.text === undefined ? tooltip : undefined}\n gap={2}\n padding={2}\n {...buttonProps}\n />\n </Tooltip>\n )\n}\n","import {ImageIcon} from '@sanity/icons/Image'\nimport type {ButtonProps} from '@sanity/ui'\nimport {useRef} from 'react'\n\nimport {TooltipButton} from './TooltipButton'\n\n/**\n * The file picker for an image, as a hidden file input that a button or menu\n * item clicks: the chosen file goes to `onFile`, and nowhere else — nothing\n * is uploaded.\n *\n * @internal\n */\nexport function ImageFileInput({\n onFile,\n ref,\n}: {\n onFile: (file: File) => void\n ref: React.Ref<HTMLInputElement>\n}) {\n return (\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={ref}\n type=\"file\"\n />\n )\n}\n\n/**\n * A button that opens the file picker for an image, and hands the chosen file\n * over.\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 <ImageFileInput onFile={onFile} ref={inputRef} />\n </>\n )\n}\n","import type {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, type 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 // 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 const pickImage = useCallback(\n (file: File) => {\n const pick = ++latestPickRef.current\n\n setBusy(true)\n\n // Whether this pick is still the latest one. Nothing checks whether the\n // component still shows: adding the theme opens the editor, which hides\n // the list in an `Activity` — its state survives that, and the flag\n // must clear before the list shows again\n const isCurrent = () => 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 {createVar, style} from '@vanilla-extract/css'\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 */\nexport const VARIANT_WIDTH = 144\n\nexport const VARIANT_GAP = 8\nexport const ROW_PADDING = 12\n\n/** The frame around a variant's thumbnail, on either side */\nexport const VARIANT_FRAME = 4\n\n/** The width of one variant's thumbnail, set on the row once it is measured */\nexport const variantWidth = createVar()\n\n/** How much a variant's thumbnail is scaled down from `VARIANT_WIDTH` */\nexport const variantScale = createVar()\n\nexport const paletteGrid = style({\n display: 'grid',\n gridTemplateColumns: `repeat(3, ${TILE_SIZE}px)`,\n gridAutoRows: TILE_SIZE,\n gap: TILE_GAP,\n selectors: {\n \"&[data-with-image='true']\": {\n gridTemplateColumns: `${IMAGE_SIZE}px repeat(3, ${TILE_SIZE}px)`,\n },\n },\n})\n\nexport const imageTile = style({\n display: 'block',\n gridRow: 'span 2',\n width: IMAGE_SIZE,\n height: IMAGE_SIZE,\n borderRadius: 3,\n objectFit: 'cover',\n boxShadow: 'inset 0 0 0 1px var(--card-border-color)',\n})\n\nexport const tile = style({\n display: 'block',\n borderRadius: 3,\n boxShadow: 'inset 0 0 0 1px var(--card-border-color)',\n selectors: {\n \"&[data-empty='true']\": {\n background:\n 'repeating-linear-gradient(-45deg, transparent 0 3px, 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 */\nexport const variantRow = style({\n display: 'flex',\n gap: VARIANT_GAP,\n margin: `0 -${ROW_PADDING}px`,\n padding: `0 ${ROW_PADDING}px`,\n overflowX: 'auto',\n scrollSnapType: 'x mandatory',\n scrollPaddingLeft: ROW_PADDING,\n scrollbarWidth: 'thin',\n})\n\nexport const variantButton = style({\n 'appearance': 'none',\n 'flex': 'none',\n 'display': 'block',\n 'boxSizing': 'border-box',\n 'width': `calc(${variantWidth} + ${VARIANT_FRAME * 2}px)`,\n 'margin': 0,\n 'padding': 0,\n 'border': 0,\n 'background': 'none',\n 'color': 'inherit',\n 'font': 'inherit',\n 'textAlign': 'center',\n 'cursor': 'pointer',\n 'scrollSnapAlign': 'start',\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 */\nexport const variantFrame = style({\n display: 'block',\n boxSizing: 'border-box',\n width: `calc(${variantWidth} + ${VARIANT_FRAME * 2}px)`,\n height: `calc(${variantWidth} * 9 / 16 + ${VARIANT_FRAME * 2}px)`,\n padding: VARIANT_FRAME,\n borderRadius: 7,\n overflow: 'hidden',\n transition: 'box-shadow 100ms',\n selectors: {\n [`${variantButton}:hover &`]: {\n boxShadow: 'inset 0 0 0 2px var(--card-border-color)',\n },\n [`${variantButton}[aria-pressed='true'] &`]: {\n boxShadow: 'inset 0 0 0 2px var(--card-focus-ring-color)',\n },\n [`${variantButton}:focus-visible &`]: {\n outline: '2px solid var(--card-focus-ring-color)',\n outlineOffset: -2,\n },\n },\n})\n\nexport const variantScaleBox = style({\n display: 'block',\n width: VARIANT_WIDTH,\n transform: `scale(${variantScale})`,\n transformOrigin: 'top left',\n})\n","import {SparklesIcon} from '@sanity/icons/Sparkles'\nimport {Box, Card, Flex, Stack, Text} from '@sanity/ui'\nimport {Tooltip} from '@sanity/ui/tooltip'\nimport {assignInlineVars} from '@vanilla-extract/dynamic'\nimport {useEffect, useMemo, useRef, useState} from 'react'\n\nimport type {BuildThemeOptions} from '../theme/options'\nimport {ImageFileButton} from './ImageFileButton'\nimport {\n applyImagePalette,\n currentImageVariant,\n IMAGE_PALETTE_TITLES,\n IMAGE_PALETTE_VARIANTS,\n type ImagePalette,\n type ImagePaletteKey,\n type ImagePaletteVariant,\n} from './imagePalette'\nimport {ThemeThumbnail} from './ThemeThumbnail'\nimport {TooltipButton} from './TooltipButton'\nimport {useImagePalette} from './useImagePalette'\n\nimport {\n imageTile,\n paletteGrid,\n ROW_PADDING,\n tile,\n VARIANT_FRAME,\n VARIANT_GAP,\n VARIANT_WIDTH,\n variantButton,\n variantFrame,\n variantRow,\n variantScale,\n variantScaleBox,\n variantWidth,\n} from './ImagePaletteSection.css'\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\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\n\n/** The width of one variant, before the row has been measured */\nconst FALLBACK_VARIANT_WIDTH = 58\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 [thumbnailWidth, setThumbnailWidth] = 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 frame on either side\n setThumbnailWidth(Math.max(24, Math.floor(visible / VISIBLE_VARIANTS) - VARIANT_FRAME * 2))\n }\n\n measure()\n\n const observer = new ResizeObserver(measure)\n\n observer.observe(row)\n\n return () => observer.disconnect()\n // oxlint-disable-next-line react/exhaustive-effect-dependencies -- the row only renders with a palette, so a new palette has a new row to observe\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 : [\n {\n variant,\n title: IMAGE_PALETTE_TITLES[variant],\n options: applyImagePalette(options, palette, variant),\n },\n ],\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 <div className={paletteGrid} data-with-image={imageUrl !== undefined}>\n {imageUrl && <img alt=\"\" className={imageTile} src={imageUrl} />}\n {TILES.map((key) => {\n const hex = palette[key]\n const title = IMAGE_PALETTE_TITLES[key]\n\n return (\n <Tooltip\n key={key}\n animate\n content={\n <Text size={1}>\n {hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`}\n </Text>\n }\n\n placement=\"bottom\"\n portal\n >\n <span\n className={tile}\n data-empty={hex === null}\n style={{background: hex ?? undefined}}\n />\n </Tooltip>\n )\n })}\n </div>\n\n <Stack gap={2}>\n <Text muted size={0}>\n Pick the swatch to build the theme around\n </Text>\n <div\n className={variantRow}\n ref={rowRef}\n style={assignInlineVars({\n [variantWidth]: `${thumbnailWidth}px`,\n [variantScale]: String(thumbnailWidth / VARIANT_WIDTH),\n })}\n >\n {variants.map(({variant, title, options: variantOptions}) => (\n // oxlint-disable-next-line control-has-associated-label -- the title below the thumbnail is the label, deeper than the rule looks\n <button\n key={variant}\n aria-pressed={variant === current}\n className={variantButton}\n onClick={() => onVariant(variant)}\n type=\"button\"\n >\n <span className={variantFrame}>\n <span className={variantScaleBox}>\n <ThemeThumbnail options={variantOptions} />\n </span>\n </span>\n <Box as=\"span\" display=\"block\" paddingTop={1}>\n <Text\n align=\"center\"\n as=\"span\"\n size={0}\n textOverflow=\"ellipsis\"\n weight={variant === current ? 'medium' : 'regular'}\n >\n {title}\n </Text>\n </Box>\n </button>\n ))}\n </div>\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 {style} from '@vanilla-extract/css'\n\n/** A native color input, trimmed down to its swatch */\nexport const swatch = style({\n boxSizing: 'border-box',\n flex: 'none',\n width: 33,\n height: 33,\n padding: 0,\n border: '1px solid var(--card-border-color)',\n borderRadius: 4,\n background: 'none',\n cursor: 'pointer',\n selectors: {\n '&::-webkit-color-swatch-wrapper': {\n padding: 0,\n },\n '&::-webkit-color-swatch': {\n border: 'none',\n borderRadius: 3,\n },\n '&::-moz-color-swatch': {\n border: 'none',\n borderRadius: 3,\n },\n },\n})\n\n/** A native range input, themed through `accent-color` */\nexport const range = style({\n display: 'block',\n width: '100%',\n margin: 0,\n accentColor: 'var(--card-focus-ring-color)',\n})\n\n/** Lets the color row's text shrink and truncate instead of widening the row */\nexport const colorRowText = style({\n minWidth: 0,\n})\n","import {CheckmarkIcon} from '@sanity/icons/Checkmark'\nimport {EllipsisHorizontalIcon} from '@sanity/icons/EllipsisHorizontal'\nimport {ResetIcon} from '@sanity/icons/Reset'\nimport {TrashIcon} from '@sanity/icons/Trash'\nimport {Badge, Box, Button, Card, Flex, Stack, Text, TextInput} from '@sanity/ui'\nimport {Menu, MenuButton, MenuItem} from '@sanity/ui/menu'\nimport {type ThemeColorSchemeKey} from '@sanity/ui/theme'\nimport {useSelector} from '@xstate/react'\nimport {useId, useMemo} from 'react'\nimport {useColorSchemeValue} from 'sanity'\n\nimport type {ThemerProps} from '#types'\n\nimport {buildPalette, type GeneratedColorPalette} from '../theme/buildPalette'\nimport {\n type BuildThemeOptions,\n type ResolvedSchemeOptions,\n resolveThemeOptions,\n SCHEMES,\n type SchemeThemeOptions,\n} from '../theme/options'\nimport {\n applyImagePalette,\n currentImageVariant,\n type ImagePalette,\n type ImagePaletteVariant,\n pickLuckyVariant,\n} from './imagePalette'\nimport {ImagePaletteSection} from './ImagePaletteSection'\nimport {ScrollArea, ScrollAreaBleed} from './ScrollArea'\nimport {ThemeThumbnail} from './ThemeThumbnail'\nimport {TooltipButton} from './TooltipButton'\n\nimport {colorRowText, range, swatch} from './ThemeEditor.css'\n\nconst SCHEME_TITLES: Record<ThemeColorSchemeKey, string> = {\n dark: 'Dark mode',\n light: 'Light mode',\n}\n\n/**\n * Where the split preview shows a scheme: the Studio's own scheme stays next\n * to the sidebar (or at the bottom, stacked on small screens), the other one\n * takes the far side\n */\nfunction splitHint(own: boolean): string {\n return own ? 'Shown on the right, next to the themer' : '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 */\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({\n actorRef,\n focusTitle,\n slug,\n split,\n themes,\n}: {focusTitle: boolean; slug: string} & Pick<ThemerProps, 'actorRef' | 'split' | 'themes'>) {\n const images = useSelector(actorRef, (snapshot) => snapshot.context.images)\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 actorRef.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={() => actorRef.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) => actorRef.send({type: 'theme.update', slug, options})}\n onPalette={(palette, file) =>\n actorRef.send({\n type: 'theme.update',\n slug,\n options: applyImagePalette(theme.options, palette),\n palette,\n imageUrl: URL.createObjectURL(file),\n })\n }\n onRemove={() => actorRef.send({type: 'theme.remove', slug})}\n onTitleChange={(title) => actorRef.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 actionsId = useId()\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 key={scheme}\n active={split || scheme === studioScheme}\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 splitHint={splitHint(scheme === studioScheme)}\n />\n ))}\n </ScrollAreaBleed>\n </Stack>\n </ScrollArea>\n\n {/* Like the Studio's document footer: one primary action, the rest in\n the menu, in the size of the Studio's own buttons (padding and gap of 2) */}\n <Card borderTop padding={3}>\n <Flex align=\"center\" gap={2} justify=\"flex-end\">\n <Button\n gap={2}\n icon={CheckmarkIcon}\n onClick={onDone}\n padding={2}\n text=\"Done\"\n mode=\"ghost\"\n />\n <MenuButton\n button={\n <Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n mode=\"bleed\"\n padding={2}\n />\n }\n id={actionsId}\n menu={\n <Menu>\n {/* The theme can be restored until it is deleted from the removed themes */}\n <MenuItem icon={TrashIcon} onClick={onRemove} text=\"Remove\" tone=\"critical\" />\n </Menu>\n }\n popover={{animate: true, placement: 'top-end', portal: true}}\n />\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 /** Where the split preview shows this scheme */\n splitHint: string\n}) {\n const {active, onChange, options, palette, resolved, scheme, split, splitHint} = 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 ? splitHint\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 <input\n aria-label={`${name} contrast`}\n className={range}\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 <input\n aria-label={label}\n className={swatch}\n onChange={(event) => onChange(event.currentTarget.value)}\n type=\"color\"\n value={value}\n />\n <Stack className={colorRowText} flex={1} gap={2}>\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 {isColor} from '../lib/mix'\nimport {\n type BuildThemeOptions,\n MAXIMUM_CONTRAST,\n MINIMUM_CONTRAST,\n SCHEMES,\n type SchemeThemeOptions,\n} from '../theme/options'\nimport {displayTitle} from './themes'\n\n/** What a theme code carries: a theme's title and its options @internal */\nexport interface SharedTheme {\n title: string\n options: BuildThemeOptions\n}\n\n/**\n * The version of the code format, the first byte of every code — bumped if\n * the layout below ever changes, so older codes can still be told apart\n */\nconst VERSION = 1\n\n/** The fields a scheme's options can set, in the order their bytes go */\nconst FIELDS = ['accent', 'text', 'background', 'contrast'] as const\n\n/**\n * Turns a theme into a short code to share in a message: `title`, and the\n * colors and contrasts of both schemes, packed into bytes — one flag byte says\n * which of the eight fields are set, each color is its three RGB bytes and a\n * contrast one byte, and the title takes up the rest — then base64url'd, so\n * it is one word without padding, safe in URLs and chat. A theme setting\n * everything comes to about 30 bytes plus its title.\n *\n * @internal\n */\nexport function encodeTheme(theme: SharedTheme): string {\n const bytes: number[] = [VERSION, 0]\n let flags = 0\n\n SCHEMES.forEach((scheme, schemeIndex) => {\n const options: SchemeThemeOptions = theme.options[scheme] ?? {}\n\n FIELDS.forEach((field, fieldIndex) => {\n const value = options[field]\n\n if (value === undefined) return\n\n if (field === 'contrast') {\n if (typeof value !== 'number' || !Number.isFinite(value)) return\n\n bytes.push(Math.round(Math.min(MAXIMUM_CONTRAST, Math.max(MINIMUM_CONTRAST, value))))\n } else {\n if (typeof value !== 'string' || !isColor(value)) return\n\n bytes.push(...rgbBytes(value))\n }\n\n flags |= 1 << (schemeIndex * FIELDS.length + fieldIndex)\n })\n })\n\n bytes[1] = flags\n bytes.push(...new TextEncoder().encode(theme.title.trim()))\n\n return toBase64Url(Uint8Array.from(bytes))\n}\n\n/**\n * Reads a theme back out of a code — as pasted, so surrounding whitespace and\n * words around the code are fine: the first word that is a code counts. `null`\n * when nothing in the text is one.\n *\n * @internal\n */\nexport function decodeTheme(text: string): SharedTheme | null {\n for (const word of text.trim().split(/\\s+/)) {\n const theme = decodeCode(word)\n\n if (theme) return theme\n }\n\n return null\n}\n\nfunction decodeCode(code: string): SharedTheme | null {\n const bytes = fromBase64Url(code)\n\n if (!bytes || bytes.length < 2 || bytes[0] !== VERSION) return null\n\n const flags = bytes[1]\n const options: BuildThemeOptions = {}\n let offset = 2\n\n for (const [schemeIndex, scheme] of SCHEMES.entries()) {\n const schemeOptions: SchemeThemeOptions = {}\n\n for (const [fieldIndex, field] of FIELDS.entries()) {\n if (!(flags & (1 << (schemeIndex * FIELDS.length + fieldIndex)))) continue\n\n if (field === 'contrast') {\n if (offset + 1 > bytes.length) return null\n\n schemeOptions.contrast = Math.min(\n MAXIMUM_CONTRAST,\n Math.max(MINIMUM_CONTRAST, bytes[offset]),\n )\n offset += 1\n } else {\n if (offset + 3 > bytes.length) return null\n\n schemeOptions[field] = hexColor(bytes.subarray(offset, offset + 3))\n offset += 3\n }\n }\n\n if (Object.keys(schemeOptions).length > 0) options[scheme] = schemeOptions\n }\n\n let title: string\n\n try {\n title = new TextDecoder('utf-8', {fatal: true}).decode(bytes.subarray(offset))\n } catch {\n return null\n }\n\n return {title: displayTitle(title), options}\n}\n\n/** The three RGB bytes of a `#rgb` or `#rrggbb` color */\nfunction rgbBytes(color: string): number[] {\n const hex = color.length === 4 ? color.slice(1).replaceAll(/./g, '$&$&') : color.slice(1)\n\n return [0, 2, 4].map((start) => parseInt(hex.slice(start, start + 2), 16))\n}\n\nfunction hexColor(rgb: Uint8Array): string {\n return `#${[...rgb].map((byte) => byte.toString(16).padStart(2, '0')).join('')}`\n}\n\nfunction toBase64Url(bytes: Uint8Array): string {\n let binary = ''\n\n for (const byte of bytes) binary += String.fromCharCode(byte)\n\n return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '')\n}\n\nfunction fromBase64Url(code: string): Uint8Array | null {\n if (!/^[A-Za-z0-9_-]+$/.test(code)) return null\n\n const base64 = code.replaceAll('-', '+').replaceAll('_', '/')\n\n try {\n return Uint8Array.from(atob(base64.padEnd(Math.ceil(base64.length / 4) * 4, '=')), (char) =>\n char.charCodeAt(0),\n )\n } catch {\n return null\n }\n}\n","import {useToast} from '@sanity/ui/toast'\nimport {useEffect} from 'react'\n\nimport type {ThemerProps} from '#types'\n\nimport {decodeTheme, encodeTheme, type SharedTheme} from './share'\nimport {displayTitle} from './themes'\n\n/**\n * Copying a theme as a code to share, and adding a theme from a code someone\n * shared — with toasts telling how it went.\n *\n * @internal\n */\nexport function useThemeCodes({actorRef}: Pick<ThemerProps, 'actorRef'>): {\n copyTheme: (theme: SharedTheme) => Promise<void>\n /** Adds the theme in the text, if there is one — the result says whether there was */\n addThemeFromText: (text: string) => boolean\n /** Adds the theme on the clipboard, if there is one and the browser hands the clipboard over */\n addThemeFromClipboard: () => Promise<'added' | 'no-theme' | 'no-clipboard'>\n} {\n const toast = useToast()\n\n const copyTheme = async (theme: SharedTheme) => {\n const title = displayTitle(theme.title)\n\n try {\n await navigator.clipboard.writeText(encodeTheme(theme))\n toast.push({\n status: 'success',\n title: `Copied ${title} as a code`,\n description: 'Paste it in the themer of another Studio to add the theme there',\n })\n } catch {\n toast.push({status: 'error', title: `Could not copy ${title}`})\n }\n }\n\n const addThemeFromText = (text: string) => {\n const theme = decodeTheme(text)\n\n if (!theme) return false\n\n actorRef.send({type: 'theme.import', title: theme.title, options: theme.options})\n toast.push({status: 'success', title: `Added ${theme.title}`})\n\n return true\n }\n\n const addThemeFromClipboard = async () => {\n let text: string\n\n try {\n text = await navigator.clipboard.readText()\n } catch {\n return 'no-clipboard'\n }\n\n return addThemeFromText(text) ? 'added' : 'no-theme'\n }\n\n return {copyTheme, addThemeFromText, addThemeFromClipboard}\n}\n\n/**\n * Adds a theme pasted anywhere in the Studio while the themer's list is\n * showing — pastes into inputs and other editable things are theirs.\n *\n * @internal\n */\nexport function usePasteThemeCodes({actorRef}: Pick<ThemerProps, 'actorRef'>): void {\n const toast = useToast()\n\n useEffect(() => {\n const handlePaste = (event: ClipboardEvent) => {\n if (isEditable(event.target)) return\n\n const theme = decodeTheme(event.clipboardData?.getData('text/plain') ?? '')\n\n if (!theme) return\n\n event.preventDefault()\n actorRef.send({type: 'theme.import', title: theme.title, options: theme.options})\n toast.push({status: 'success', title: `Added ${theme.title}`})\n }\n\n document.addEventListener('paste', handlePaste)\n\n return () => document.removeEventListener('paste', handlePaste)\n }, [actorRef, toast])\n}\n\nfunction isEditable(target: EventTarget | null): boolean {\n return (\n target instanceof HTMLElement &&\n (target.isContentEditable || target.matches('input, textarea, select'))\n )\n}\n","import {Button, Card, Dialog, Flex, Stack, Text, TextInput} from '@sanity/ui'\nimport {useId, useState} from 'react'\n\nimport type {ThemerProps} from '#types'\n\nimport {useThemeCodes} from './useThemeCodes'\n\n/**\n * Where a theme code someone shared gets pasted by hand — for browsers that\n * do not hand the clipboard over, and for codes that are not on it.\n *\n * @internal\n */\nexport function PasteThemeDialog(props: {onClose: () => void} & Pick<ThemerProps, 'actorRef'>) {\n const {actorRef, onClose} = props\n const {addThemeFromText} = useThemeCodes({actorRef})\n const [text, setText] = useState('')\n const [invalid, setInvalid] = useState(false)\n const id = useId()\n const errorId = `${id}-error`\n\n const add = () => {\n if (addThemeFromText(text)) {\n onClose()\n } else {\n setInvalid(true)\n }\n }\n\n return (\n <Dialog\n animate\n footer={\n <Flex gap={2} justify=\"flex-end\" padding={3}>\n <Button mode=\"ghost\" onClick={onClose} padding={2} text=\"Cancel\" />\n <Button\n disabled={text.trim() === ''}\n onClick={add}\n padding={2}\n text=\"Add theme\"\n tone=\"primary\"\n />\n </Flex>\n }\n header=\"Add a shared theme\"\n id={id}\n onClickOutside={onClose}\n onClose={onClose}\n width={0}\n >\n <form\n onSubmit={(event) => {\n event.preventDefault()\n add()\n }}\n >\n <Stack gap={3} padding={4}>\n <Text muted size={1}>\n Paste the code someone copied from their themer — the theme is added to your list and\n applied.\n </Text>\n <TextInput\n aria-describedby={invalid ? errorId : undefined}\n aria-invalid={invalid}\n aria-label=\"Theme code\"\n autoFocus\n customValidity={invalid ? 'That is not a theme code' : undefined}\n fontSize={1}\n onChange={(event) => {\n setText(event.currentTarget.value)\n setInvalid(false)\n }}\n placeholder=\"AQ…\"\n value={text}\n />\n {invalid && (\n <Card id={errorId} padding={3} radius={2} role=\"alert\" tone=\"critical\">\n <Text size={1}>\n That is not a theme code — codes are one word, copied from a theme's menu.\n </Text>\n </Card>\n )}\n </Stack>\n </form>\n </Dialog>\n )\n}\n","import {style} from '@vanilla-extract/css'\n\nexport const root = style({\n position: 'relative',\n})\n\n/** A bare button, so that the thumbnail and the title can carry the styling */\nexport const pickButton = style({\n 'appearance': 'none',\n 'display': 'block',\n 'boxSizing': 'border-box',\n 'width': '100%',\n 'margin': 0,\n 'padding': 0,\n 'border': 0,\n 'background': 'none',\n 'color': 'inherit',\n 'font': 'inherit',\n 'textAlign': 'center',\n 'cursor': 'pointer',\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 */\nexport const frame = style({\n display: 'block',\n padding: 4,\n borderRadius: 10,\n transition: 'box-shadow 100ms',\n selectors: {\n [`${pickButton}:hover &`]: {\n boxShadow: 'inset 0 0 0 2px var(--card-border-color)',\n },\n [`${pickButton}[aria-pressed='true'] &`]: {\n boxShadow: 'inset 0 0 0 2px var(--card-focus-ring-color)',\n },\n [`${pickButton}:focus-visible &`]: {\n outline: '2px solid var(--card-focus-ring-color)',\n outlineOffset: -2,\n },\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 */\nexport const menuSlot = style({\n position: 'absolute',\n top: 10,\n right: 10,\n opacity: 0,\n transition: 'opacity 100ms',\n selectors: {\n [`${root}:hover &, ${root}:focus-within &, &[data-visible='true']`]: {\n opacity: 1,\n },\n },\n})\n\n/** The dark card behind the menu button sizes to the button, not the slot */\nexport const menuCard = style({\n display: 'inline-block',\n})\n","import {ArrowDownIcon} from '@sanity/icons/ArrowDown'\nimport {ArrowUpIcon} from '@sanity/icons/ArrowUp'\nimport {CopyIcon} from '@sanity/icons/Copy'\nimport {EditIcon} from '@sanity/icons/Edit'\nimport {EllipsisHorizontalIcon} from '@sanity/icons/EllipsisHorizontal'\nimport {ShareIcon} from '@sanity/icons/Share'\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 {Reorder, useDragControls} from 'motion/react'\nimport {useId, useRef, useState} from 'react'\nimport scrollIntoView from 'scroll-into-view-if-needed'\n\nimport type {ThemerProps} from '#types'\n\nimport {displayTitle, type ThemerTheme} from './themes'\nimport {ThemeThumbnail} from './ThemeThumbnail'\nimport {useThemeCodes} from './useThemeCodes'\n\nimport {frame, menuCard, menuSlot, pickButton, root} from './ThemeCard.css'\n\n/**\n * Scrolls the applied theme's card into view when it is not already, as a\n * ref: the list scrolls to it as it mounts — coming back from the editor or\n * the removed themes — and when another theme gets applied\n */\nfunction scrollRefIntoViewIfNeeded(node: HTMLElement | null) {\n if (node) scrollIntoView(node, {block: 'nearest', scrollMode: 'if-needed'})\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, dragging it moves the theme to another place in\n * the list; its menu holds the move, edit, duplicate and remove flows that\n * apply to the theme.\n *\n * @internal\n */\nexport function ThemeCard({\n actorRef,\n active,\n theme,\n themes,\n}: {active: boolean; theme: ThemerTheme} & Pick<ThemerProps, 'actorRef' | 'themes'>) {\n const {copyTheme} = useThemeCodes({actorRef})\n const {scheme} = useRootTheme()\n const [menuOpen, setMenuOpen] = useState(false)\n const dragControls = useDragControls()\n const menuId = useId()\n // Letting go of a drag also clicks the card, and that click must not pick\n const dragged = useRef(false)\n const title = displayTitle(theme.title)\n const index = themes.findIndex((candidate) => candidate.slug === theme.slug)\n // The configured theme sits ahead of the list, and does not move within it\n const listed = index !== -1\n\n const moveTo = (to: number) => {\n const order = themes.map((candidate) => candidate.slug)\n\n order.splice(to, 0, ...order.splice(index, 1))\n actorRef.send({type: 'theme.reorder', order})\n }\n\n return (\n <Reorder.Item\n as=\"div\"\n className={root}\n dragControls={dragControls}\n dragListener={false}\n onDragEnd={() => {\n // The click lands before the next frame\n requestAnimationFrame(() => {\n dragged.current = false\n })\n }}\n onDragStart={() => {\n dragged.current = true\n }}\n ref={active ? scrollRefIntoViewIfNeeded : undefined}\n value={theme.slug}\n whileDrag={{scale: 1.04}}\n >\n <button\n aria-pressed={active}\n className={pickButton}\n onClick={() => {\n if (!dragged.current) actorRef.send({type: 'theme.pick', slug: theme.slug})\n }}\n onPointerDown={(event) => {\n // A mouse or pen drags the card off the button; a finger scrolls the\n // list instead, and moves themes from the menu\n if (listed && event.pointerType !== 'touch') dragControls.start(event)\n }}\n type=\"button\"\n >\n <span className={frame}>\n <ThemeThumbnail options={theme.options} />\n </span>\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 </button>\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 <div className={menuSlot} data-visible={menuOpen}>\n <Tooltip animate content={<Text size={1}>Show more</Text>} placement=\"bottom\" portal>\n <Card className={menuCard} radius={2} scheme=\"dark\">\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={menuId}\n menu={\n <Menu>\n {listed && (\n <>\n <MenuItem\n disabled={index <= 0}\n icon={ArrowUpIcon}\n onClick={() => moveTo(index - 1)}\n text=\"Move up\"\n />\n <MenuItem\n disabled={index >= themes.length - 1}\n icon={ArrowDownIcon}\n onClick={() => moveTo(index + 1)}\n text=\"Move down\"\n />\n <MenuDivider />\n </>\n )}\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => actorRef.send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n <MenuItem\n icon={ShareIcon}\n onClick={() => void copyTheme(theme)}\n text=\"Copy to share\"\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => actorRef.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 </div>\n </Reorder.Item>\n )\n}\n","import {\n type BuildThemeOptions,\n DEFAULT_ACCENT,\n DEFAULT_BACKGROUND,\n DEFAULT_CONTRAST,\n deriveTextColor,\n type ResolvedSchemeOptions,\n resolveThemeOptions,\n SCHEMES,\n type 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 {type BuildThemeOptions, SCHEMES, type 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 {useId} from 'react'\nimport {registerLanguage} from 'react-refractor'\nimport typescript from 'refractor/typescript'\n\nimport {createThemeSnippet} from './snippet'\nimport {displayTitle, type 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 id = useId()\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\n gap={2}\n icon={ClipboardIcon}\n mode=\"ghost\"\n onClick={() => void handleCopy()}\n padding={2}\n text=\"Copy\"\n />\n </Flex>\n }\n header=\"Add to your config\"\n id={id}\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 {style} from '@vanilla-extract/css'\n\nimport {MAXIMUM_WIDTH, MINIMUM_WIDTH} from './sidebarWidth'\n\nimport {sidebarContent} from './ResizableSidebar.css'\n\nconst CARD_GAP = 16\n\n/**\n * The theme cards flow into two columns once the sidebar has been dragged a\n * third of the way from its narrowest to its widest. The breakpoints are\n * container queries on the sidebar's content (`ResizableSidebar`), which is\n * as wide as the sidebar itself.\n */\nconst TWO_COLUMNS_FROM = MINIMUM_WIDTH + (MAXIMUM_WIDTH - MINIMUM_WIDTH) * 0.3\n\n/**\n * The columns share the width equally whatever the titles: a plain `1fr` may\n * not shrink below a card's min-content width, and a card's title does not\n * wrap, so one long title would widen its column past the sidebar\n */\nconst COLUMN = 'minmax(0, 1fr)'\n\nexport const cardGrid = style({\n 'display': 'grid',\n 'gridTemplateColumns': COLUMN,\n 'gap': CARD_GAP,\n '@container': {\n [`${sidebarContent} (min-width: ${TWO_COLUMNS_FROM}px)`]: {\n gridTemplateColumns: `repeat(2, ${COLUMN})`,\n },\n },\n})\n","import {AddIcon} from '@sanity/icons/Add'\nimport {ClipboardIcon} from '@sanity/icons/Clipboard'\nimport {CodeBlockIcon} from '@sanity/icons/CodeBlock'\nimport {CopyIcon} from '@sanity/icons/Copy'\nimport {EditIcon} from '@sanity/icons/Edit'\nimport {EllipsisHorizontalIcon} from '@sanity/icons/EllipsisHorizontal'\nimport {ImageIcon} from '@sanity/icons/Image'\nimport {RestoreIcon} from '@sanity/icons/Restore'\nimport {Button, Card, Flex, Stack} from '@sanity/ui'\nimport {Menu, MenuButton, MenuDivider, MenuItem} from '@sanity/ui/menu'\nimport {useSelector} from '@xstate/react'\nimport {MotionConfig, Reorder} from 'motion/react'\nimport {useId, useRef} from 'react'\n\nimport type {ThemerProps} from '#types'\n\nimport {ImageFileInput} from './ImageFileButton'\nimport {optionsFromImagePalette, titleFromFileName} from './imagePalette'\nimport type {ThemerSnapshot} from './machine'\nimport {PasteThemeDialog} from './PasteThemeDialog'\nimport {ScrollArea} from './ScrollArea'\nimport {ThemeCard} from './ThemeCard'\nimport {CONFIG_SLUG, CONFIG_TITLE} from './themes'\nimport {ThemeSnippetDialog} from './ThemeSnippetDialog'\nimport {useImagePalette} from './useImagePalette'\nimport {usePasteThemeCodes, useThemeCodes} from './useThemeCodes'\n\nimport {cardGrid} from './ThemeList.css'\n\n/**\n * The flow for picking a theme: a grid of theme cards — the configured theme,\n * the presets and the user's own themes — that drag into the order the user\n * wants, with the entry point to the restore flow below. The footer takes the\n * applied theme on to the editor — a copy of it, for a preset or the\n * configured theme — and keeps the ways to add a theme and the applied\n * theme's code behind its menu. A theme code someone shared pastes right into\n * the list too.\n *\n * @internal\n */\nexport function ThemeList({\n actorRef,\n themes,\n removed,\n active,\n}: Pick<ThemerProps, 'actorRef' | 'themes' | 'removed' | 'active'>) {\n const {addThemeFromClipboard} = useThemeCodes({actorRef})\n // Which of the list's dialogs is open is the machine's: a state of the list flow\n const dialog = useSelector(actorRef, selectDialog)\n const imageInputRef = useRef<HTMLInputElement | null>(null)\n const actionsId = useId()\n\n usePasteThemeCodes({actorRef})\n\n const {busy, pickImage} = useImagePalette((palette, file) =>\n actorRef.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 {/* The group tells a column from a grid by measuring the cards, and\n lets go of the layout animations for users who prefer reduced motion */}\n <MotionConfig reducedMotion=\"user\">\n <Reorder.Group\n as=\"div\"\n className={cardGrid}\n onReorder={(order: string[]) => actorRef.send({type: 'theme.reorder', order})}\n values={themes.map((theme) => theme.slug)}\n >\n <ThemeCard\n actorRef={actorRef}\n active={!active}\n theme={{\n slug: CONFIG_SLUG,\n title: CONFIG_TITLE,\n options: {},\n source: 'config',\n }}\n themes={themes}\n />\n {themes.map((theme) => (\n <ThemeCard\n key={theme.slug}\n actorRef={actorRef}\n active={theme.slug === active?.slug}\n theme={theme}\n themes={themes}\n />\n ))}\n </Reorder.Group>\n </MotionConfig>\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 gap={2}\n icon={RestoreIcon}\n mode=\"bleed\"\n onClick={() => actorRef.send({type: 'flow.removed'})}\n padding={2}\n text={`Show removed (${removed.length})`}\n width=\"fill\"\n />\n )}\n </Stack>\n </ScrollArea>\n\n {/* Like the Studio's document footer: one primary action, the rest in\n the menu, in the size of the Studio's own buttons (padding and gap of 2) */}\n <Card borderTop padding={3}>\n <Flex align=\"center\" gap={2} justify=\"flex-end\">\n {active?.source === 'custom' ? (\n <Button\n gap={2}\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: active.slug})}\n padding={2}\n text=\"Edit\"\n mode=\"ghost\"\n />\n ) : (\n <Button\n gap={2}\n icon={CopyIcon}\n onClick={() =>\n actorRef.send({type: 'theme.duplicate', slug: active?.slug ?? CONFIG_SLUG})\n }\n padding={2}\n text=\"Duplicate & Edit\"\n mode=\"ghost\"\n />\n )}\n <MenuButton\n button={\n <Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n // The image's colors are read on device, which the menu button shows while it lasts\n loading={busy}\n mode=\"bleed\"\n padding={2}\n />\n }\n id={actionsId}\n menu={\n // Short labels: the menu is confined to the sidebar, which can be narrow\n <Menu>\n <MenuItem\n icon={AddIcon}\n onClick={() => actorRef.send({type: 'theme.add'})}\n text=\"Add theme\"\n />\n <MenuItem\n icon={ImageIcon}\n onClick={() => imageInputRef.current?.click()}\n text=\"Add from image\"\n />\n <MenuItem\n icon={ClipboardIcon}\n onClick={async () => {\n // Straight from the clipboard where the browser allows; by hand otherwise\n if ((await addThemeFromClipboard()) !== 'added') {\n actorRef.send({type: 'dialog.paste'})\n }\n }}\n text=\"Add from code\"\n />\n {active && (\n <>\n <MenuDivider />\n <MenuItem\n icon={CodeBlockIcon}\n onClick={() => actorRef.send({type: 'dialog.snippet'})}\n text=\"Show theme code\"\n />\n </>\n )}\n </Menu>\n }\n popover={{animate: true, placement: 'top-end', portal: true}}\n />\n </Flex>\n </Card>\n\n <ImageFileInput onFile={pickImage} ref={imageInputRef} />\n\n {dialog === 'paste' && (\n <PasteThemeDialog\n actorRef={actorRef}\n onClose={() => actorRef.send({type: 'dialog.close'})}\n />\n )}\n {dialog === 'snippet' && active && (\n <ThemeSnippetDialog onClose={() => actorRef.send({type: 'dialog.close'})} theme={active} />\n )}\n </>\n )\n}\n\n/** The dialog the list has open, if any — a state of the machine's list flow */\nfunction selectDialog(snapshot: ThemerSnapshot): 'paste' | 'snippet' | null {\n if (snapshot.matches({flow: {list: 'pasting'}})) return 'paste'\n if (snapshot.matches({flow: {list: 'snippet'}})) return 'snippet'\n\n return null\n}\n","import {useMediaIndex} from '@sanity/ui'\n\nimport {MIN_MEDIA_INDEX_FOR_SPLIT_SCREEN} from '#constants'\n\nexport function useIsTooSmallForSplitScreen() {\n const index = useMediaIndex()\n return index <= MIN_MEDIA_INDEX_FOR_SPLIT_SCREEN\n}\n","import {style} from '@vanilla-extract/css'\n\n/** Lets the title shrink and truncate instead of pushing the buttons out */\nexport const title = style({\n minWidth: 0,\n})\n","import {CloseIcon} from '@sanity/icons/Close'\nimport {SplitVerticalIcon} from '@sanity/icons/SplitVertical'\nimport {Box, Card, Flex, Text} from '@sanity/ui'\nimport {useSelector} from '@xstate/react'\nimport {dequal} from 'dequal/lite'\nimport {Activity, startTransition} from 'react'\n\nimport type {ThemerView} from '#context'\nimport type {ThemerProps} from '#types'\n\nimport {addThemerTransitionType} from './addThemerTransitionType'\nimport type {ThemerSnapshot} from './machine'\nimport {RemovedThemes} from './RemovedThemes'\nimport {ThemeEditor} from './ThemeEditor'\nimport {ThemeList} from './ThemeList'\nimport {resolveThemes} from './themes'\nimport {TooltipButton} from './TooltipButton'\nimport {useIsTooSmallForSplitScreen} from './useIsTooSmallForSplitScreen'\n\nimport {title} from './ThemerSidebar.css'\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 with the flow's title, the split preview\n * toggle and the close button, and the flow itself — picking a theme, editing\n * one, or restoring removed ones. Each flow ends in a footer like the\n * Studio's document footer: one primary action, with the rest of its actions\n * behind a menu button, and the way back to the list where the flow is not\n * the list.\n *\n * @internal\n */\nexport function ThemerSidebar({\n actorRef,\n dispatch,\n navbarHeight,\n split,\n}: Pick<ThemerProps, 'actorRef' | 'dispatch' | 'navbarHeight' | 'split'>) {\n const view = useSelector(actorRef, selectView, sameView)\n const isTooSmallForSplitScreen = useIsTooSmallForSplitScreen()\n\n const {themes, removed, active} = useSelector(\n actorRef,\n (snapshot) => resolveThemes(snapshot.context),\n dequal,\n )\n\n return (\n <Card height=\"fill\">\n <Flex direction=\"column\" height=\"fill\">\n {/* The header sits next to the Studio navbar and takes its height, so\n the two bottom borders line up — whatever height the Studio, its\n breakpoint or a custom navbar gives it */}\n <Card\n borderBottom\n padding={3}\n sizing=\"border\"\n style={navbarHeight === null ? undefined : {height: navbarHeight}}\n >\n <Flex align=\"center\" gap={1} height=\"fill\">\n <Box className={title} flex={1}>\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 {!isTooSmallForSplitScreen && (\n <TooltipButton\n aria-pressed={split}\n icon={SplitVerticalIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n const type = split ? 'split-screen:close' : 'split-screen:open'\n addThemerTransitionType(type)\n dispatch({type})\n })\n }\n onMouseEnter={() =>\n startTransition(() => {\n dispatch({type: 'split-screen:prerender'})\n })\n }\n padding={2}\n selected={split}\n tooltip=\"Show light and dark side by side\"\n />\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n if (split) addThemerTransitionType('split-screen:close')\n addThemerTransitionType('close')\n dispatch({type: 'close'})\n })\n }\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>\n </Flex>\n </Card>\n\n <Activity key=\"list\" mode={view.name === 'list' ? 'visible' : 'hidden'}>\n <ThemeList actorRef={actorRef} active={active} removed={removed} themes={themes} />\n </Activity>\n {view.name === 'edit' && (\n <ThemeEditor\n actorRef={actorRef}\n focusTitle={view.focusTitle}\n slug={view.slug}\n split={split}\n themes={themes}\n />\n )}\n <Activity key=\"removed\" mode={view.name === 'removed' ? 'visible' : 'hidden'}>\n <RemovedThemes actorRef={actorRef} removed={removed} />\n </Activity>\n </Flex>\n </Card>\n )\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","import {createContainer, style} from '@vanilla-extract/css'\n\n/** The sidebar's content, which the content's layout queries — as wide as the sidebar itself */\nexport const sidebarContent = createContainer()\n\nexport const sidebar = style({\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 */\nexport const frame = style({\n position: 'relative',\n})\n\n/**\n * Clips the content, and is the container its layout queries: as wide as\n * the sidebar, whether or not a scrollbar takes room inside — see the theme\n * grid's columns in `ThemeList.css.ts`\n */\nexport const content = style({\n containerType: 'inline-size',\n containerName: sidebarContent,\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 */\nexport const resizeHandle = style({\n 'position': 'absolute',\n 'top': 0,\n 'bottom': 0,\n 'left': -3,\n 'zIndex': 1,\n 'width': 7,\n 'cursor': 'col-resize',\n 'touchAction': 'none',\n '::after': {\n content: '',\n position: 'absolute',\n top: 0,\n bottom: 0,\n left: 2,\n width: 3,\n background: 'var(--card-focus-ring-color)',\n opacity: 0,\n transition: 'opacity 100ms',\n },\n ':focus': {\n outline: 'none',\n },\n 'selectors': {\n \"&:hover::after, &:focus-visible::after, &[data-dragging='true']::after\": {\n opacity: 1,\n },\n },\n})\n","import {BoundaryElementProvider, Box, Card, Layer} from '@sanity/ui'\nimport {startTransition, useCallback, useEffect, useRef, useState} from 'react'\n\nimport type {ThemerProps} from '#types'\n\nimport {MAXIMUM_WIDTH, MINIMUM_WIDTH} from './sidebarWidth'\nimport {ThemerSidebar} from './ThemerSidebar'\n\nimport {content, frame, resizeHandle, sidebar} from './ResizableSidebar.css'\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 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. As an overlay (small screens) it covers the\n * Studio edge to edge instead, with nothing to resize.\n *\n * @internal\n */\nexport default function ResizableSidebar({\n actorRef,\n dispatch,\n navbarHeight,\n split,\n}: Pick<ThemerProps, 'actorRef' | 'dispatch' | 'navbarHeight' | 'split'>) {\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 // @TODO schedule the write to localStorage to avoid blocking the main thread\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 const [boundaryElement, setBoundaryElement] = useState<HTMLDivElement | null>(null)\n\n return (\n <Layer className={sidebar} style={{width}} zOffset={100}>\n <Card\n borderLeft\n className={frame}\n height=\"fill\"\n ref={(boundaryElement) => {\n startTransition(() => setBoundaryElement(boundaryElement))\n // Intentionally not unsetting the element as it would create unnecessary work when <Activity> unhides\n return () => {}\n }}\n >\n <div\n aria-label=\"Resize the themer\"\n className={resizeHandle}\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 className={content} height=\"fill\" overflow=\"hidden\">\n <BoundaryElementProvider element={boundaryElement}>\n <ThemerSidebar\n split={split}\n navbarHeight={navbarHeight}\n actorRef={actorRef}\n dispatch={dispatch}\n />\n </BoundaryElementProvider>\n </Box>\n </Card>\n </Layer>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ResizableSidebar-Qo2gLzlm.js","names":["root","() => lastGutter","() => {\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 = () => {\n lastGutter = element.offsetWidth - element.clientWidth\n setGutter(lastGutter)\n }\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 value={inset}>{children}</ScrollAreaInsetContext>","<Box\n className={root}\n flex={1}\n overflow=\"auto\"\n padding={padding}\n ref={ref}\n style={{paddingRight: inset.right}}\n >\n <ScrollAreaInsetContext value={inset}>{children}</ScrollAreaInsetContext>\n </Box>","{marginLeft: -left, marginRight: -right, marginBottom: -bottom}","-left","-right","-bottom","<div style={{marginLeft: -left, marginRight: -right, marginBottom: -bottom}}>\n {props.children}\n </div>","children","_c","_temp","root","root","buildTheme(options)","useMemo(() => buildTheme(options), [options])","<MockStudio scheme=\"light\" />","<ThemeProvider scheme=\"light\" theme={theme} tone=\"default\">\n <MockStudio scheme=\"light\" />\n </ThemeProvider>","<MockStudio scheme=\"dark\" />","<ThemeProvider scheme=\"dark\" theme={theme} tone=\"default\">\n <MockStudio scheme=\"dark\" />\n </ThemeProvider>","<div aria-hidden className={root}>\n <ThemeProvider scheme=\"light\" theme={theme} tone=\"default\">\n <MockStudio scheme=\"light\" />\n </ThemeProvider>\n <ThemeProvider scheme=\"dark\" theme={theme} tone=\"default\">\n <MockStudio scheme=\"dark\" />\n </ThemeProvider>\n </div>","assignInlineVars({\n [colors.bg]: color.bg,\n [colors.fg]: color.fg,\n [colors.mutedFg]: color.muted.fg,\n [colors.border]: color.border,\n [colors.accent]: color.button.default.primary.enabled.bg,\n [colors.avatar]: color.avatar.blue.bg,\n [colors.selectedBg]: selected.bg,\n [colors.selectedFg]: selected.fg,\n [colors.inputBg]: inputColor.bg,\n [colors.inputBorder]: inputColor.border,\n })","bg","border","fg","<div className={logo} />","<div className={navbar}>\n <div className={logo} />\n <div className={line.fg} style={{width: '22%'}} />\n <div className={spacer} />\n <div className={avatar} />\n </div>","<div className={list}>\n {LIST_ROWS.map((width, index) => {\n const isSelected = index === 1\n\n return (\n <div key={width} className={listRow} data-selected={isSelected}>\n <div className={isSelected ? line.selected : line.muted} style={{width}} />\n </div>\n )\n })}\n </div>","(width, index) => {\n const isSelected = index === 1\n\n return (\n <div key={width} className={listRow} data-selected={isSelected}>\n <div className={isSelected ? line.selected : line.muted} style={{width}} />\n </div>\n )\n }","<div className={form}>\n <div className={line.fg} style={{width: '45%'}} />\n {FIELDS.map((width) => (\n <div key={width} className={field}>\n <div className={line.muted} style={{width}} />\n <div className={input} />\n </div>\n ))}\n </div>","(width) => (\n <div key={width} className={field}>\n <div className={line.muted} style={{width}} />\n <div className={input} />\n </div>\n )","<div className={body}>\n <div className={list}>\n {LIST_ROWS.map((width, index) => {\n const isSelected = index === 1\n\n return (\n <div key={width} className={listRow} data-selected={isSelected}>\n <div className={isSelected ? line.selected : line.muted} style={{width}} />\n </div>\n )\n })}\n </div>\n\n <div className={pane}>\n <div className={form}>\n <div className={line.fg} style={{width: '45%'}} />\n {FIELDS.map((width) => (\n <div key={width} className={field}>\n <div className={line.muted} style={{width}} />\n <div className={input} />\n </div>\n ))}\n </div>\n <div className={footer}>\n <div className={button} />\n </div>\n </div>\n </div>","<div\n className={studio[scheme]}\n style={assignInlineVars({\n [colors.bg]: color.bg,\n [colors.fg]: color.fg,\n [colors.mutedFg]: color.muted.fg,\n [colors.border]: color.border,\n [colors.accent]: color.button.default.primary.enabled.bg,\n [colors.avatar]: color.avatar.blue.bg,\n [colors.selectedBg]: selected.bg,\n [colors.selectedFg]: selected.fg,\n [colors.inputBg]: inputColor.bg,\n [colors.inputBorder]: inputColor.border,\n })}\n >\n <div className={navbar}>\n <div className={logo} />\n <div className={line.fg} style={{width: '22%'}} />\n <div className={spacer} />\n <div className={avatar} />\n </div>\n\n <div className={body}>\n <div className={list}>\n {LIST_ROWS.map((width, index) => {\n const isSelected = index === 1\n\n return (\n <div key={width} className={listRow} data-selected={isSelected}>\n <div className={isSelected ? line.selected : line.muted} style={{width}} />\n </div>\n )\n })}\n </div>\n\n <div className={pane}>\n <div className={form}>\n <div className={line.fg} style={{width: '45%'}} />\n {FIELDS.map((width) => (\n <div key={width} className={field}>\n <div className={line.muted} style={{width}} />\n <div className={input} />\n </div>\n ))}\n </div>\n <div className={footer}>\n <div className={button} />\n </div>\n </div>\n </div>\n </div>","studio[scheme]","width","FIELDS","_c","root","_temp","_temp2","<Card overflow=\"hidden\" radius={3} shadow={1}>\n <ThemePreview options={props.options} />\n </Card>","options","_c","{actorRef, removed}: Pick<ThemerProps, 'actorRef' | 'removed'>","removed.map((theme) => (\n <Stack key={theme.slug} gap={3}>\n <Box className={removedThumbnail}>\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 mode=\"ghost\"\n onClick={() => actorRef.send({type: 'theme.restore', slug: theme.slug})}\n padding={2}\n text=\"Restore\"\n />\n {theme.source === 'custom' && (\n <Button\n mode=\"ghost\"\n onClick={() => actorRef.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 key={theme.slug} gap={3}>\n <Box className={removedThumbnail}>\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 mode=\"ghost\"\n onClick={() => actorRef.send({type: 'theme.restore', slug: theme.slug})}\n padding={2}\n text=\"Restore\"\n />\n {theme.source === 'custom' && (\n <Button\n mode=\"ghost\"\n onClick={() => actorRef.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 key={theme.slug} gap={3}>\n <Box className={removedThumbnail}>\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 mode=\"ghost\"\n onClick={() => actorRef.send({type: 'theme.restore', slug: theme.slug})}\n padding={2}\n text=\"Restore\"\n />\n {theme.source === 'custom' && (\n <Button\n mode=\"ghost\"\n onClick={() => actorRef.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>","<Card borderTop padding={3}>\n <Flex justify=\"flex-end\">\n <Button\n gap={2}\n icon={CheckmarkIcon}\n onClick={() => actorRef.send({type: 'flow.list'})}\n padding={2}\n text=\"Done\"\n mode=\"ghost\"\n />\n </Flex>\n </Card>","<>\n <ScrollArea padding={3}>\n <Stack gap={5}>\n {removed.map((theme) => (\n <Stack key={theme.slug} gap={3}>\n <Box className={removedThumbnail}>\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 mode=\"ghost\"\n onClick={() => actorRef.send({type: 'theme.restore', slug: theme.slug})}\n padding={2}\n text=\"Restore\"\n />\n {theme.source === 'custom' && (\n <Button\n mode=\"ghost\"\n onClick={() => actorRef.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 {/* In the size of the Studio's own buttons (padding and gap of 2) */}\n <Card borderTop padding={3}>\n <Flex justify=\"flex-end\">\n <Button\n gap={2}\n icon={CheckmarkIcon}\n onClick={() => actorRef.send({type: 'flow.list'})}\n padding={2}\n text=\"Done\"\n mode=\"ghost\"\n />\n </Flex>\n </Card>\n </>","_c","_Fragment","<Text size={1}>{tooltip}</Text>","<Button\n aria-label={buttonProps.text === undefined ? tooltip : undefined}\n gap={2}\n padding={2}\n {...buttonProps}\n />","buttonProps.text === undefined ? tooltip : undefined","<Tooltip animate content={<Text size={1}>{tooltip}</Text>} placement=\"bottom\" portal>\n <Button\n aria-label={buttonProps.text === undefined ? tooltip : undefined}\n gap={2}\n padding={2}\n {...buttonProps}\n />\n </Tooltip>","_c","{\n onFile,\n ref,\n}: {\n onFile: (file: File) => void\n ref: React.Ref<HTMLInputElement>\n}","(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 }","<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={ref}\n type=\"file\"\n />","() => inputRef.current?.click()","<TooltipButton {...buttonProps} icon={ImageIcon} onClick={() => inputRef.current?.click()} />","<ImageFileInput onFile={onFile} ref={inputRef} />","<>\n <TooltipButton {...buttonProps} icon={ImageIcon} onClick={() => inputRef.current?.click()} />\n <ImageFileInput onFile={onFile} ref={inputRef} />\n </>","_c","_Fragment","() => {\n onPaletteRef.current = onPalette\n }","[onPalette]","(file: File) => {\n const pick = ++latestPickRef.current\n\n setBusy(true)\n\n // Whether this pick is still the latest one. Nothing checks whether the\n // component still shows: adding the theme opens the editor, which hides\n // the list in an `Activity` — its state survives that, and the flag\n // must clear before the list shows again\n const isCurrent = () => 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 is still the latest one. Nothing checks whether the\n // component still shows: adding the theme opens the editor, which hides\n // the list in an `Activity` — its state survives that, and the flag\n // must clear before the list shows again\n const isCurrent = () => 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}","_c","_temp","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 frame on either side\n setThumbnailWidth(Math.max(24, Math.floor(visible / VISIBLE_VARIANTS) - VARIANT_FRAME * 2))\n }\n\n measure()\n\n const observer = new ResizeObserver(measure)\n\n observer.observe(row)\n\n return () => observer.disconnect()\n // oxlint-disable-next-line react/exhaustive-effect-dependencies -- the row only renders with a palette, so a new palette has a new row to observe\n }","[palette]","useMemo(() => {\n if (!palette) return []\n\n return IMAGE_PALETTE_VARIANTS.flatMap((variant) =>\n palette[variant] === null\n ? []\n : [\n {\n variant,\n title: IMAGE_PALETTE_TITLES[variant],\n options: applyImagePalette(options, palette, variant),\n },\n ],\n )\n }, [options, palette])","[]","IMAGE_PALETTE_VARIANTS.flatMap((variant) =>\n palette[variant] === null\n ? []\n : [\n {\n variant,\n title: IMAGE_PALETTE_TITLES[variant],\n options: applyImagePalette(options, palette, variant),\n },\n ],\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 <div className={paletteGrid} data-with-image={imageUrl !== undefined}>\n {imageUrl && <img alt=\"\" className={imageTile} src={imageUrl} />}\n {TILES.map((key) => {\n const hex = palette[key]\n const title = IMAGE_PALETTE_TITLES[key]\n\n return (\n <Tooltip\n key={key}\n animate\n content={\n <Text size={1}>\n {hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`}\n </Text>\n }\n\n placement=\"bottom\"\n portal\n >\n <span\n className={tile}\n data-empty={hex === null}\n style={{background: hex ?? undefined}}\n />\n </Tooltip>\n )\n })}\n </div>\n\n <Stack gap={2}>\n <Text muted size={0}>\n Pick the swatch to build the theme around\n </Text>\n <div\n className={variantRow}\n ref={rowRef}\n style={assignInlineVars({\n [variantWidth]: `${thumbnailWidth}px`,\n [variantScale]: String(thumbnailWidth / VARIANT_WIDTH),\n })}\n >\n {variants.map(({variant, title, options: variantOptions}) => (\n // oxlint-disable-next-line control-has-associated-label -- the title below the thumbnail is the label, deeper than the rule looks\n <button\n key={variant}\n aria-pressed={variant === current}\n className={variantButton}\n onClick={() => onVariant(variant)}\n type=\"button\"\n >\n <span className={variantFrame}>\n <span className={variantScaleBox}>\n <ThemeThumbnail options={variantOptions} />\n </span>\n </span>\n <Box as=\"span\" display=\"block\" paddingTop={1}>\n <Text\n align=\"center\"\n as=\"span\"\n size={0}\n textOverflow=\"ellipsis\"\n weight={variant === current ? 'medium' : 'regular'}\n >\n {title}\n </Text>\n </Box>\n </button>\n ))}\n </div>\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, title, options: variantOptions}","variant","title","<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 <div className={paletteGrid} data-with-image={imageUrl !== undefined}>\n {imageUrl && <img alt=\"\" className={imageTile} src={imageUrl} />}\n {TILES.map((key) => {\n const hex = palette[key]\n const title = IMAGE_PALETTE_TITLES[key]\n\n return (\n <Tooltip\n key={key}\n animate\n content={\n <Text size={1}>\n {hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`}\n </Text>\n }\n\n placement=\"bottom\"\n portal\n >\n <span\n className={tile}\n data-empty={hex === null}\n style={{background: hex ?? undefined}}\n />\n </Tooltip>\n )\n })}\n </div>\n\n <Stack gap={2}>\n <Text muted size={0}>\n Pick the swatch to build the theme around\n </Text>\n <div\n className={variantRow}\n ref={rowRef}\n style={assignInlineVars({\n [variantWidth]: `${thumbnailWidth}px`,\n [variantScale]: String(thumbnailWidth / VARIANT_WIDTH),\n })}\n >\n {variants.map(({variant, title, options: variantOptions}) => (\n // oxlint-disable-next-line control-has-associated-label -- the title below the thumbnail is the label, deeper than the rule looks\n <button\n key={variant}\n aria-pressed={variant === current}\n className={variantButton}\n onClick={() => onVariant(variant)}\n type=\"button\"\n >\n <span className={variantFrame}>\n <span className={variantScaleBox}>\n <ThemeThumbnail options={variantOptions} />\n </span>\n </span>\n <Box as=\"span\" display=\"block\" paddingTop={1}>\n <Text\n align=\"center\"\n as=\"span\"\n size={0}\n textOverflow=\"ellipsis\"\n weight={variant === current ? 'medium' : 'regular'}\n >\n {title}\n </Text>\n </Box>\n </button>\n ))}\n </div>\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 <div className={paletteGrid} data-with-image={imageUrl !== undefined}>\n {imageUrl && <img alt=\"\" className={imageTile} src={imageUrl} />}\n {TILES.map((key) => {\n const hex = palette[key]\n const title = IMAGE_PALETTE_TITLES[key]\n\n return (\n <Tooltip\n key={key}\n animate\n content={\n <Text size={1}>\n {hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`}\n </Text>\n }\n\n placement=\"bottom\"\n portal\n >\n <span\n className={tile}\n data-empty={hex === null}\n style={{background: hex ?? undefined}}\n />\n </Tooltip>\n )\n })}\n </div>\n\n <Stack gap={2}>\n <Text muted size={0}>\n Pick the swatch to build the theme around\n </Text>\n <div\n className={variantRow}\n ref={rowRef}\n style={assignInlineVars({\n [variantWidth]: `${thumbnailWidth}px`,\n [variantScale]: String(thumbnailWidth / VARIANT_WIDTH),\n })}\n >\n {variants.map(({variant, title, options: variantOptions}) => (\n // oxlint-disable-next-line control-has-associated-label -- the title below the thumbnail is the label, deeper than the rule looks\n <button\n key={variant}\n aria-pressed={variant === current}\n className={variantButton}\n onClick={() => onVariant(variant)}\n type=\"button\"\n >\n <span className={variantFrame}>\n <span className={variantScaleBox}>\n <ThemeThumbnail options={variantOptions} />\n </span>\n </span>\n <Box as=\"span\" display=\"block\" paddingTop={1}>\n <Text\n align=\"center\"\n as=\"span\"\n size={0}\n textOverflow=\"ellipsis\"\n weight={variant === current ? 'medium' : 'regular'}\n >\n {title}\n </Text>\n </Box>\n </button>\n ))}\n </div>\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 }","_c","_Fragment","_temp","{\n actorRef,\n focusTitle,\n slug,\n split,\n themes,\n}: {focusTitle: boolean; slug: string} & Pick<ThemerProps, 'actorRef' | 'split' | 'themes'>","(snapshot) => snapshot.context.images","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 actorRef.send({\n type: 'theme.update',\n slug,\n options: applyImagePalette(theme.options, theme.palette, variant),\n })\n }","options","palette","() => actorRef.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) => actorRef.send({type: 'theme.update', slug, options})","(palette, file) =>\n actorRef.send({\n type: 'theme.update',\n slug,\n options: applyImagePalette(theme.options, palette),\n palette,\n imageUrl: URL.createObjectURL(file),\n })","() => actorRef.send({type: 'theme.remove', slug})","(title) => actorRef.send({type: 'theme.update', slug, title})","<ThemeEditorForm\n focusTitle={focusTitle}\n imageUrl={images[slug]}\n onDone={() => actorRef.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) => actorRef.send({type: 'theme.update', slug, options})}\n onPalette={(palette, file) =>\n actorRef.send({\n type: 'theme.update',\n slug,\n options: applyImagePalette(theme.options, palette),\n palette,\n imageUrl: URL.createObjectURL(file),\n })\n }\n onRemove={() => actorRef.send({type: 'theme.remove', slug})}\n onTitleChange={(title) => actorRef.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 key={scheme}\n active={split || scheme === studioScheme}\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 splitHint={splitHint(scheme === studioScheme)}\n />\n ))","scheme","changes","<ScrollAreaBleed>\n {SCHEMES.map((scheme) => (\n <SchemeCard\n key={scheme}\n active={split || scheme === studioScheme}\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 splitHint={splitHint(scheme === studioScheme)}\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 key={scheme}\n active={split || scheme === studioScheme}\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 splitHint={splitHint(scheme === studioScheme)}\n />\n ))}\n </ScrollAreaBleed>\n </Stack>\n </ScrollArea>","<Button\n gap={2}\n icon={CheckmarkIcon}\n onClick={onDone}\n padding={2}\n text=\"Done\"\n mode=\"ghost\"\n />","<Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n mode=\"bleed\"\n padding={2}\n />","<Menu>\n {/* The theme can be restored until it is deleted from the removed themes */}\n <MenuItem icon={TrashIcon} onClick={onRemove} text=\"Remove\" tone=\"critical\" />\n </Menu>","{animate: true, placement: 'top-end', portal: true}","<MenuButton\n button={\n <Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n mode=\"bleed\"\n padding={2}\n />\n }\n id={actionsId}\n menu={\n <Menu>\n {/* The theme can be restored until it is deleted from the removed themes */}\n <MenuItem icon={TrashIcon} onClick={onRemove} text=\"Remove\" tone=\"critical\" />\n </Menu>\n }\n popover={{animate: true, placement: 'top-end', portal: true}}\n />","<Card borderTop padding={3}>\n <Flex align=\"center\" gap={2} justify=\"flex-end\">\n <Button\n gap={2}\n icon={CheckmarkIcon}\n onClick={onDone}\n padding={2}\n text=\"Done\"\n mode=\"ghost\"\n />\n <MenuButton\n button={\n <Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n mode=\"bleed\"\n padding={2}\n />\n }\n id={actionsId}\n menu={\n <Menu>\n {/* The theme can be restored until it is deleted from the removed themes */}\n <MenuItem icon={TrashIcon} onClick={onRemove} text=\"Remove\" tone=\"critical\" />\n </Menu>\n }\n popover={{animate: true, placement: 'top-end', portal: true}}\n />\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 key={scheme}\n active={split || scheme === studioScheme}\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 splitHint={splitHint(scheme === studioScheme)}\n />\n ))}\n </ScrollAreaBleed>\n </Stack>\n </ScrollArea>\n\n {/* Like the Studio's document footer: one primary action, the rest in\n the menu, in the size of the Studio's own buttons (padding and gap of 2) */}\n <Card borderTop padding={3}>\n <Flex align=\"center\" gap={2} justify=\"flex-end\">\n <Button\n gap={2}\n icon={CheckmarkIcon}\n onClick={onDone}\n padding={2}\n text=\"Done\"\n mode=\"ghost\"\n />\n <MenuButton\n button={\n <Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n mode=\"bleed\"\n padding={2}\n />\n }\n id={actionsId}\n menu={\n <Menu>\n {/* The theme can be restored until it is deleted from the removed themes */}\n <MenuItem icon={TrashIcon} onClick={onRemove} text=\"Remove\" tone=\"critical\" />\n </Menu>\n }\n popover={{animate: true, placement: 'top-end', portal: true}}\n />\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 ? splitHint\n : active\n ? 'Shown in the Studio right now'\n : `Shown once the Studio appearance is ${scheme}`}\n </Text>","split\n ? splitHint\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 ? splitHint\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)})","<input\n aria-label={`${name} contrast`}\n className={range}\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 <input\n aria-label={`${name} contrast`}\n className={range}\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 ? splitHint\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 <input\n aria-label={`${name} contrast`}\n className={range}\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)","<input\n aria-label={label}\n className={swatch}\n onChange={(event) => onChange(event.currentTarget.value)}\n type=\"color\"\n value={value}\n />","<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 className={colorRowText} flex={1} gap={2}>\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 <input\n aria-label={label}\n className={swatch}\n onChange={(event) => onChange(event.currentTarget.value)}\n type=\"color\"\n value={value}\n />\n <Stack className={colorRowText} flex={1} gap={2}>\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>","_c","_temp","_Fragment","{actorRef}: Pick<ThemerProps, 'actorRef'>","async (theme: SharedTheme) => {\n const title = displayTitle(theme.title)\n\n try {\n await navigator.clipboard.writeText(encodeTheme(theme))\n toast.push({\n status: 'success',\n title: `Copied ${title} as a code`,\n description: 'Paste it in the themer of another Studio to add the theme there',\n })\n } catch {\n toast.push({status: 'error', title: `Could not copy ${title}`})\n }\n }","(text: string) => {\n const theme = decodeTheme(text)\n\n if (!theme) return false\n\n actorRef.send({type: 'theme.import', title: theme.title, options: theme.options})\n toast.push({status: 'success', title: `Added ${theme.title}`})\n\n return true\n }","theme","async () => {\n let text: string\n\n try {\n text = await navigator.clipboard.readText()\n } catch {\n return 'no-clipboard'\n }\n\n return addThemeFromText(text) ? 'added' : 'no-theme'\n }","text","{copyTheme, addThemeFromText, addThemeFromClipboard}","() => {\n const handlePaste = (event: ClipboardEvent) => {\n if (isEditable(event.target)) return\n\n const theme = decodeTheme(event.clipboardData?.getData('text/plain') ?? '')\n\n if (!theme) return\n\n event.preventDefault()\n actorRef.send({type: 'theme.import', title: theme.title, options: theme.options})\n toast.push({status: 'success', title: `Added ${theme.title}`})\n }\n\n document.addEventListener('paste', handlePaste)\n\n return () => document.removeEventListener('paste', handlePaste)\n }","[actorRef, toast]","_c","{actorRef}","() => {\n if (addThemeFromText(text)) {\n onClose()\n } else {\n setInvalid(true)\n }\n }","<Button mode=\"ghost\" onClick={onClose} padding={2} text=\"Cancel\" />","text.trim()","<Button\n disabled={text.trim() === ''}\n onClick={add}\n padding={2}\n text=\"Add theme\"\n tone=\"primary\"\n />","text.trim() === ''","<Flex gap={2} justify=\"flex-end\" padding={3}>\n <Button mode=\"ghost\" onClick={onClose} padding={2} text=\"Cancel\" />\n <Button\n disabled={text.trim() === ''}\n onClick={add}\n padding={2}\n text=\"Add theme\"\n tone=\"primary\"\n />\n </Flex>","(event) => {\n event.preventDefault()\n add()\n }","<Text muted size={1}>\n Paste the code someone copied from their themer — the theme is added to your list and\n applied.\n </Text>","(event) => {\n setText(event.currentTarget.value)\n setInvalid(false)\n }","event","<TextInput\n aria-describedby={invalid ? errorId : undefined}\n aria-invalid={invalid}\n aria-label=\"Theme code\"\n autoFocus\n customValidity={invalid ? 'That is not a theme code' : undefined}\n fontSize={1}\n onChange={(event) => {\n setText(event.currentTarget.value)\n setInvalid(false)\n }}\n placeholder=\"AQ…\"\n value={text}\n />","invalid ? 'That is not a theme code' : undefined","invalid ? errorId : undefined","invalid && (\n <Card id={errorId} padding={3} radius={2} role=\"alert\" tone=\"critical\">\n <Text size={1}>\n That is not a theme code — codes are one word, copied from a theme's menu.\n </Text>\n </Card>\n )","<Stack gap={3} padding={4}>\n <Text muted size={1}>\n Paste the code someone copied from their themer — the theme is added to your list and\n applied.\n </Text>\n <TextInput\n aria-describedby={invalid ? errorId : undefined}\n aria-invalid={invalid}\n aria-label=\"Theme code\"\n autoFocus\n customValidity={invalid ? 'That is not a theme code' : undefined}\n fontSize={1}\n onChange={(event) => {\n setText(event.currentTarget.value)\n setInvalid(false)\n }}\n placeholder=\"AQ…\"\n value={text}\n />\n {invalid && (\n <Card id={errorId} padding={3} radius={2} role=\"alert\" tone=\"critical\">\n <Text size={1}>\n That is not a theme code — codes are one word, copied from a theme's menu.\n </Text>\n </Card>\n )}\n </Stack>","<form\n onSubmit={(event) => {\n event.preventDefault()\n add()\n }}\n >\n <Stack gap={3} padding={4}>\n <Text muted size={1}>\n Paste the code someone copied from their themer — the theme is added to your list and\n applied.\n </Text>\n <TextInput\n aria-describedby={invalid ? errorId : undefined}\n aria-invalid={invalid}\n aria-label=\"Theme code\"\n autoFocus\n customValidity={invalid ? 'That is not a theme code' : undefined}\n fontSize={1}\n onChange={(event) => {\n setText(event.currentTarget.value)\n setInvalid(false)\n }}\n placeholder=\"AQ…\"\n value={text}\n />\n {invalid && (\n <Card id={errorId} padding={3} radius={2} role=\"alert\" tone=\"critical\">\n <Text size={1}>\n That is not a theme code — codes are one word, copied from a theme's menu.\n </Text>\n </Card>\n )}\n </Stack>\n </form>","<Dialog\n animate\n footer={\n <Flex gap={2} justify=\"flex-end\" padding={3}>\n <Button mode=\"ghost\" onClick={onClose} padding={2} text=\"Cancel\" />\n <Button\n disabled={text.trim() === ''}\n onClick={add}\n padding={2}\n text=\"Add theme\"\n tone=\"primary\"\n />\n </Flex>\n }\n header=\"Add a shared theme\"\n id={id}\n onClickOutside={onClose}\n onClose={onClose}\n width={0}\n >\n <form\n onSubmit={(event) => {\n event.preventDefault()\n add()\n }}\n >\n <Stack gap={3} padding={4}>\n <Text muted size={1}>\n Paste the code someone copied from their themer — the theme is added to your list and\n applied.\n </Text>\n <TextInput\n aria-describedby={invalid ? errorId : undefined}\n aria-invalid={invalid}\n aria-label=\"Theme code\"\n autoFocus\n customValidity={invalid ? 'That is not a theme code' : undefined}\n fontSize={1}\n onChange={(event) => {\n setText(event.currentTarget.value)\n setInvalid(false)\n }}\n placeholder=\"AQ…\"\n value={text}\n />\n {invalid && (\n <Card id={errorId} padding={3} radius={2} role=\"alert\" tone=\"critical\">\n <Text size={1}>\n That is not a theme code — codes are one word, copied from a theme's menu.\n </Text>\n </Card>\n )}\n </Stack>\n </form>\n </Dialog>","_c","frame","{\n actorRef,\n active,\n theme,\n themes,\n}: {active: boolean; theme: ThemerTheme} & Pick<ThemerProps, 'actorRef' | 'themes'>","{actorRef}","displayTitle(theme.title)","title","(candidate) => candidate.slug === theme.slug","slug","(to: number) => {\n const order = themes.map((candidate) => candidate.slug)\n\n order.splice(to, 0, ...order.splice(index, 1))\n actorRef.send({type: 'theme.reorder', order})\n }","(candidate) => candidate.slug","() => {\n // The click lands before the next frame\n requestAnimationFrame(() => {\n dragged.current = false\n })\n }","() => {\n dragged.current = true\n }","{scale: 1.04}","() => {\n if (!dragged.current) actorRef.send({type: 'theme.pick', slug: theme.slug})\n }","(event) => {\n // A mouse or pen drags the card off the button; a finger scrolls the\n // list instead, and moves themes from the menu\n if (listed && event.pointerType !== 'touch') dragControls.start(event)\n }","<span className={frame}>\n <ThemeThumbnail options={theme.options} />\n </span>","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'","<button\n aria-pressed={active}\n className={pickButton}\n onClick={() => {\n if (!dragged.current) actorRef.send({type: 'theme.pick', slug: theme.slug})\n }}\n onPointerDown={(event) => {\n // A mouse or pen drags the card off the button; a finger scrolls the\n // list instead, and moves themes from the menu\n if (listed && event.pointerType !== 'touch') dragControls.start(event)\n }}\n type=\"button\"\n >\n <span className={frame}>\n <ThemeThumbnail options={theme.options} />\n </span>\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 </button>","<Text size={1}>Show more</Text>","<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}`","listed && (\n <>\n <MenuItem\n disabled={index <= 0}\n icon={ArrowUpIcon}\n onClick={() => moveTo(index - 1)}\n text=\"Move up\"\n />\n <MenuItem\n disabled={index >= themes.length - 1}\n icon={ArrowDownIcon}\n onClick={() => moveTo(index + 1)}\n text=\"Move down\"\n />\n <MenuDivider />\n </>\n )","length","theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )","source","() => actorRef.send({type: 'theme.duplicate', slug: theme.slug})","<MenuItem\n icon={CopyIcon}\n onClick={() => actorRef.send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />","theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'","<MenuItem\n icon={ShareIcon}\n onClick={() => void copyTheme(theme)}\n text=\"Copy to share\"\n />","theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => actorRef.send({type: 'theme.remove', slug: theme.slug})}\n text=\"Remove\"\n tone=\"critical\"\n />\n </>\n )","<Menu>\n {listed && (\n <>\n <MenuItem\n disabled={index <= 0}\n icon={ArrowUpIcon}\n onClick={() => moveTo(index - 1)}\n text=\"Move up\"\n />\n <MenuItem\n disabled={index >= themes.length - 1}\n icon={ArrowDownIcon}\n onClick={() => moveTo(index + 1)}\n text=\"Move down\"\n />\n <MenuDivider />\n </>\n )}\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => actorRef.send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n <MenuItem\n icon={ShareIcon}\n onClick={() => void copyTheme(theme)}\n text=\"Copy to share\"\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => actorRef.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 className={menuCard} radius={2} scheme=\"dark\">\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={menuId}\n menu={\n <Menu>\n {listed && (\n <>\n <MenuItem\n disabled={index <= 0}\n icon={ArrowUpIcon}\n onClick={() => moveTo(index - 1)}\n text=\"Move up\"\n />\n <MenuItem\n disabled={index >= themes.length - 1}\n icon={ArrowDownIcon}\n onClick={() => moveTo(index + 1)}\n text=\"Move down\"\n />\n <MenuDivider />\n </>\n )}\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => actorRef.send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n <MenuItem\n icon={ShareIcon}\n onClick={() => void copyTheme(theme)}\n text=\"Copy to share\"\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => actorRef.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>","<div className={menuSlot} data-visible={menuOpen}>\n <Tooltip animate content={<Text size={1}>Show more</Text>} placement=\"bottom\" portal>\n <Card className={menuCard} radius={2} scheme=\"dark\">\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={menuId}\n menu={\n <Menu>\n {listed && (\n <>\n <MenuItem\n disabled={index <= 0}\n icon={ArrowUpIcon}\n onClick={() => moveTo(index - 1)}\n text=\"Move up\"\n />\n <MenuItem\n disabled={index >= themes.length - 1}\n icon={ArrowDownIcon}\n onClick={() => moveTo(index + 1)}\n text=\"Move down\"\n />\n <MenuDivider />\n </>\n )}\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => actorRef.send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n <MenuItem\n icon={ShareIcon}\n onClick={() => void copyTheme(theme)}\n text=\"Copy to share\"\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => actorRef.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 </div>","<Reorder.Item\n as=\"div\"\n className={root}\n dragControls={dragControls}\n dragListener={false}\n onDragEnd={() => {\n // The click lands before the next frame\n requestAnimationFrame(() => {\n dragged.current = false\n })\n }}\n onDragStart={() => {\n dragged.current = true\n }}\n ref={active ? scrollRefIntoViewIfNeeded : undefined}\n value={theme.slug}\n whileDrag={{scale: 1.04}}\n >\n <button\n aria-pressed={active}\n className={pickButton}\n onClick={() => {\n if (!dragged.current) actorRef.send({type: 'theme.pick', slug: theme.slug})\n }}\n onPointerDown={(event) => {\n // A mouse or pen drags the card off the button; a finger scrolls the\n // list instead, and moves themes from the menu\n if (listed && event.pointerType !== 'touch') dragControls.start(event)\n }}\n type=\"button\"\n >\n <span className={frame}>\n <ThemeThumbnail options={theme.options} />\n </span>\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 </button>\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 <div className={menuSlot} data-visible={menuOpen}>\n <Tooltip animate content={<Text size={1}>Show more</Text>} placement=\"bottom\" portal>\n <Card className={menuCard} radius={2} scheme=\"dark\">\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={menuId}\n menu={\n <Menu>\n {listed && (\n <>\n <MenuItem\n disabled={index <= 0}\n icon={ArrowUpIcon}\n onClick={() => moveTo(index - 1)}\n text=\"Move up\"\n />\n <MenuItem\n disabled={index >= themes.length - 1}\n icon={ArrowDownIcon}\n onClick={() => moveTo(index + 1)}\n text=\"Move down\"\n />\n <MenuDivider />\n </>\n )}\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => actorRef.send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n <MenuItem\n icon={ShareIcon}\n onClick={() => void copyTheme(theme)}\n text=\"Copy to share\"\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => actorRef.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 </div>\n </Reorder.Item>","active ? scrollRefIntoViewIfNeeded : undefined","Reorder.Item","candidate","_c","_temp","frame","_Fragment","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\n gap={2}\n icon={ClipboardIcon}\n mode=\"ghost\"\n onClick={() => void handleCopy()}\n padding={2}\n text=\"Copy\"\n />\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\n gap={2}\n icon={ClipboardIcon}\n mode=\"ghost\"\n onClick={() => void handleCopy()}\n padding={2}\n text=\"Copy\"\n />\n </Flex>\n }\n header=\"Add to your config\"\n id={id}\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>","_c","{\n actorRef,\n themes,\n removed,\n active,\n}: Pick<ThemerProps, 'actorRef' | 'themes' | 'removed' | 'active'>","{actorRef}","(palette, file) =>\n actorRef.send({\n type: 'theme.add',\n title: titleFromFileName(file.name),\n options: optionsFromImagePalette(palette),\n palette,\n imageUrl: URL.createObjectURL(file),\n })","(order: string[]) => actorRef.send({type: 'theme.reorder', order})","themes.map((theme) => theme.slug)","(theme) => theme.slug","{\n slug: CONFIG_SLUG,\n title: CONFIG_TITLE,\n options: {},\n source: 'config',\n }","<ThemeCard\n actorRef={actorRef}\n active={!active}\n theme={{\n slug: CONFIG_SLUG,\n title: CONFIG_TITLE,\n options: {},\n source: 'config',\n }}\n themes={themes}\n />","!active","themes.map((theme) => (\n <ThemeCard\n key={theme.slug}\n actorRef={actorRef}\n active={theme.slug === active?.slug}\n theme={theme}\n themes={themes}\n />\n ))","slug","theme","<MotionConfig reducedMotion=\"user\">\n <Reorder.Group\n as=\"div\"\n className={cardGrid}\n onReorder={(order: string[]) => actorRef.send({type: 'theme.reorder', order})}\n values={themes.map((theme) => theme.slug)}\n >\n <ThemeCard\n actorRef={actorRef}\n active={!active}\n theme={{\n slug: CONFIG_SLUG,\n title: CONFIG_TITLE,\n options: {},\n source: 'config',\n }}\n themes={themes}\n />\n {themes.map((theme) => (\n <ThemeCard\n key={theme.slug}\n actorRef={actorRef}\n active={theme.slug === active?.slug}\n theme={theme}\n themes={themes}\n />\n ))}\n </Reorder.Group>\n </MotionConfig>","Reorder.Group","removed.length > 0 && (\n <Button\n gap={2}\n icon={RestoreIcon}\n mode=\"bleed\"\n onClick={() => actorRef.send({type: 'flow.removed'})}\n padding={2}\n text={`Show removed (${removed.length})`}\n width=\"fill\"\n />\n )","length","<ScrollArea padding={3}>\n <Stack gap={4}>\n {/* The group tells a column from a grid by measuring the cards, and\n lets go of the layout animations for users who prefer reduced motion */}\n <MotionConfig reducedMotion=\"user\">\n <Reorder.Group\n as=\"div\"\n className={cardGrid}\n onReorder={(order: string[]) => actorRef.send({type: 'theme.reorder', order})}\n values={themes.map((theme) => theme.slug)}\n >\n <ThemeCard\n actorRef={actorRef}\n active={!active}\n theme={{\n slug: CONFIG_SLUG,\n title: CONFIG_TITLE,\n options: {},\n source: 'config',\n }}\n themes={themes}\n />\n {themes.map((theme) => (\n <ThemeCard\n key={theme.slug}\n actorRef={actorRef}\n active={theme.slug === active?.slug}\n theme={theme}\n themes={themes}\n />\n ))}\n </Reorder.Group>\n </MotionConfig>\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 gap={2}\n icon={RestoreIcon}\n mode=\"bleed\"\n onClick={() => actorRef.send({type: 'flow.removed'})}\n padding={2}\n text={`Show removed (${removed.length})`}\n width=\"fill\"\n />\n )}\n </Stack>\n </ScrollArea>","active?.source === 'custom' ? (\n <Button\n gap={2}\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: active.slug})}\n padding={2}\n text=\"Edit\"\n mode=\"ghost\"\n />\n ) : (\n <Button\n gap={2}\n icon={CopyIcon}\n onClick={() =>\n actorRef.send({type: 'theme.duplicate', slug: active?.slug ?? CONFIG_SLUG})\n }\n padding={2}\n text=\"Duplicate & Edit\"\n mode=\"ghost\"\n />\n )","active?.source","<Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n // The image's colors are read on device, which the menu button shows while it lasts\n loading={busy}\n mode=\"bleed\"\n padding={2}\n />","<MenuItem\n icon={AddIcon}\n onClick={() => actorRef.send({type: 'theme.add'})}\n text=\"Add theme\"\n />","<MenuItem\n icon={ImageIcon}\n onClick={() => imageInputRef.current?.click()}\n text=\"Add from image\"\n />","<MenuItem\n icon={ClipboardIcon}\n onClick={async () => {\n // Straight from the clipboard where the browser allows; by hand otherwise\n if ((await addThemeFromClipboard()) !== 'added') {\n actorRef.send({type: 'dialog.paste'})\n }\n }}\n text=\"Add from code\"\n />","active && (\n <>\n <MenuDivider />\n <MenuItem\n icon={CodeBlockIcon}\n onClick={() => actorRef.send({type: 'dialog.snippet'})}\n text=\"Show theme code\"\n />\n </>\n )","<Menu>\n <MenuItem\n icon={AddIcon}\n onClick={() => actorRef.send({type: 'theme.add'})}\n text=\"Add theme\"\n />\n <MenuItem\n icon={ImageIcon}\n onClick={() => imageInputRef.current?.click()}\n text=\"Add from image\"\n />\n <MenuItem\n icon={ClipboardIcon}\n onClick={async () => {\n // Straight from the clipboard where the browser allows; by hand otherwise\n if ((await addThemeFromClipboard()) !== 'added') {\n actorRef.send({type: 'dialog.paste'})\n }\n }}\n text=\"Add from code\"\n />\n {active && (\n <>\n <MenuDivider />\n <MenuItem\n icon={CodeBlockIcon}\n onClick={() => actorRef.send({type: 'dialog.snippet'})}\n text=\"Show theme code\"\n />\n </>\n )}\n </Menu>","{animate: true, placement: 'top-end', portal: true}","<MenuButton\n button={\n <Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n // The image's colors are read on device, which the menu button shows while it lasts\n loading={busy}\n mode=\"bleed\"\n padding={2}\n />\n }\n id={actionsId}\n menu={\n // Short labels: the menu is confined to the sidebar, which can be narrow\n <Menu>\n <MenuItem\n icon={AddIcon}\n onClick={() => actorRef.send({type: 'theme.add'})}\n text=\"Add theme\"\n />\n <MenuItem\n icon={ImageIcon}\n onClick={() => imageInputRef.current?.click()}\n text=\"Add from image\"\n />\n <MenuItem\n icon={ClipboardIcon}\n onClick={async () => {\n // Straight from the clipboard where the browser allows; by hand otherwise\n if ((await addThemeFromClipboard()) !== 'added') {\n actorRef.send({type: 'dialog.paste'})\n }\n }}\n text=\"Add from code\"\n />\n {active && (\n <>\n <MenuDivider />\n <MenuItem\n icon={CodeBlockIcon}\n onClick={() => actorRef.send({type: 'dialog.snippet'})}\n text=\"Show theme code\"\n />\n </>\n )}\n </Menu>\n }\n popover={{animate: true, placement: 'top-end', portal: true}}\n />","<Card borderTop padding={3}>\n <Flex align=\"center\" gap={2} justify=\"flex-end\">\n {active?.source === 'custom' ? (\n <Button\n gap={2}\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: active.slug})}\n padding={2}\n text=\"Edit\"\n mode=\"ghost\"\n />\n ) : (\n <Button\n gap={2}\n icon={CopyIcon}\n onClick={() =>\n actorRef.send({type: 'theme.duplicate', slug: active?.slug ?? CONFIG_SLUG})\n }\n padding={2}\n text=\"Duplicate & Edit\"\n mode=\"ghost\"\n />\n )}\n <MenuButton\n button={\n <Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n // The image's colors are read on device, which the menu button shows while it lasts\n loading={busy}\n mode=\"bleed\"\n padding={2}\n />\n }\n id={actionsId}\n menu={\n // Short labels: the menu is confined to the sidebar, which can be narrow\n <Menu>\n <MenuItem\n icon={AddIcon}\n onClick={() => actorRef.send({type: 'theme.add'})}\n text=\"Add theme\"\n />\n <MenuItem\n icon={ImageIcon}\n onClick={() => imageInputRef.current?.click()}\n text=\"Add from image\"\n />\n <MenuItem\n icon={ClipboardIcon}\n onClick={async () => {\n // Straight from the clipboard where the browser allows; by hand otherwise\n if ((await addThemeFromClipboard()) !== 'added') {\n actorRef.send({type: 'dialog.paste'})\n }\n }}\n text=\"Add from code\"\n />\n {active && (\n <>\n <MenuDivider />\n <MenuItem\n icon={CodeBlockIcon}\n onClick={() => actorRef.send({type: 'dialog.snippet'})}\n text=\"Show theme code\"\n />\n </>\n )}\n </Menu>\n }\n popover={{animate: true, placement: 'top-end', portal: true}}\n />\n </Flex>\n </Card>","<ImageFileInput onFile={pickImage} ref={imageInputRef} />","dialog === 'paste' && (\n <PasteThemeDialog\n actorRef={actorRef}\n onClose={() => actorRef.send({type: 'dialog.close'})}\n />\n )","dialog === 'snippet' && active && (\n <ThemeSnippetDialog onClose={() => actorRef.send({type: 'dialog.close'})} theme={active} />\n )","<>\n <ScrollArea padding={3}>\n <Stack gap={4}>\n {/* The group tells a column from a grid by measuring the cards, and\n lets go of the layout animations for users who prefer reduced motion */}\n <MotionConfig reducedMotion=\"user\">\n <Reorder.Group\n as=\"div\"\n className={cardGrid}\n onReorder={(order: string[]) => actorRef.send({type: 'theme.reorder', order})}\n values={themes.map((theme) => theme.slug)}\n >\n <ThemeCard\n actorRef={actorRef}\n active={!active}\n theme={{\n slug: CONFIG_SLUG,\n title: CONFIG_TITLE,\n options: {},\n source: 'config',\n }}\n themes={themes}\n />\n {themes.map((theme) => (\n <ThemeCard\n key={theme.slug}\n actorRef={actorRef}\n active={theme.slug === active?.slug}\n theme={theme}\n themes={themes}\n />\n ))}\n </Reorder.Group>\n </MotionConfig>\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 gap={2}\n icon={RestoreIcon}\n mode=\"bleed\"\n onClick={() => actorRef.send({type: 'flow.removed'})}\n padding={2}\n text={`Show removed (${removed.length})`}\n width=\"fill\"\n />\n )}\n </Stack>\n </ScrollArea>\n\n {/* Like the Studio's document footer: one primary action, the rest in\n the menu, in the size of the Studio's own buttons (padding and gap of 2) */}\n <Card borderTop padding={3}>\n <Flex align=\"center\" gap={2} justify=\"flex-end\">\n {active?.source === 'custom' ? (\n <Button\n gap={2}\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: active.slug})}\n padding={2}\n text=\"Edit\"\n mode=\"ghost\"\n />\n ) : (\n <Button\n gap={2}\n icon={CopyIcon}\n onClick={() =>\n actorRef.send({type: 'theme.duplicate', slug: active?.slug ?? CONFIG_SLUG})\n }\n padding={2}\n text=\"Duplicate & Edit\"\n mode=\"ghost\"\n />\n )}\n <MenuButton\n button={\n <Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n // The image's colors are read on device, which the menu button shows while it lasts\n loading={busy}\n mode=\"bleed\"\n padding={2}\n />\n }\n id={actionsId}\n menu={\n // Short labels: the menu is confined to the sidebar, which can be narrow\n <Menu>\n <MenuItem\n icon={AddIcon}\n onClick={() => actorRef.send({type: 'theme.add'})}\n text=\"Add theme\"\n />\n <MenuItem\n icon={ImageIcon}\n onClick={() => imageInputRef.current?.click()}\n text=\"Add from image\"\n />\n <MenuItem\n icon={ClipboardIcon}\n onClick={async () => {\n // Straight from the clipboard where the browser allows; by hand otherwise\n if ((await addThemeFromClipboard()) !== 'added') {\n actorRef.send({type: 'dialog.paste'})\n }\n }}\n text=\"Add from code\"\n />\n {active && (\n <>\n <MenuDivider />\n <MenuItem\n icon={CodeBlockIcon}\n onClick={() => actorRef.send({type: 'dialog.snippet'})}\n text=\"Show theme code\"\n />\n </>\n )}\n </Menu>\n }\n popover={{animate: true, placement: 'top-end', portal: true}}\n />\n </Flex>\n </Card>\n\n <ImageFileInput onFile={pickImage} ref={imageInputRef} />\n\n {dialog === 'paste' && (\n <PasteThemeDialog\n actorRef={actorRef}\n onClose={() => actorRef.send({type: 'dialog.close'})}\n />\n )}\n {dialog === 'snippet' && active && (\n <ThemeSnippetDialog onClose={() => actorRef.send({type: 'dialog.close'})} theme={active} />\n )}\n </>","_c","_temp","_Fragment","{\n actorRef,\n dispatch,\n navbarHeight,\n split,\n}: Pick<ThemerProps, 'actorRef' | 'dispatch' | 'navbarHeight' | 'split'>","(snapshot) => resolveThemes(snapshot.context)","navbarHeight === null ? undefined : {height: navbarHeight}","<Box className={title} flex={1}>\n <Text size={1} textOverflow=\"ellipsis\" weight=\"semibold\">\n {VIEW_TITLES[view.name]}\n </Text>\n </Box>","VIEW_TITLES[view.name]","!isTooSmallForSplitScreen && (\n <TooltipButton\n aria-pressed={split}\n icon={SplitVerticalIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n const type = split ? 'split-screen:close' : 'split-screen:open'\n addThemerTransitionType(type)\n dispatch({type})\n })\n }\n onMouseEnter={() =>\n startTransition(() => {\n dispatch({type: 'split-screen:prerender'})\n })\n }\n padding={2}\n selected={split}\n tooltip=\"Show light and dark side by side\"\n />\n )","<TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n if (split) addThemerTransitionType('split-screen:close')\n addThemerTransitionType('close')\n dispatch({type: 'close'})\n })\n }\n padding={2}\n tooltip=\"Close themer\"\n />","<Flex>\n {!isTooSmallForSplitScreen && (\n <TooltipButton\n aria-pressed={split}\n icon={SplitVerticalIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n const type = split ? 'split-screen:close' : 'split-screen:open'\n addThemerTransitionType(type)\n dispatch({type})\n })\n }\n onMouseEnter={() =>\n startTransition(() => {\n dispatch({type: 'split-screen:prerender'})\n })\n }\n padding={2}\n selected={split}\n tooltip=\"Show light and dark side by side\"\n />\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n if (split) addThemerTransitionType('split-screen:close')\n addThemerTransitionType('close')\n dispatch({type: 'close'})\n })\n }\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>","<Flex align=\"center\" gap={1} height=\"fill\">\n <Box className={title} flex={1}>\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 {!isTooSmallForSplitScreen && (\n <TooltipButton\n aria-pressed={split}\n icon={SplitVerticalIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n const type = split ? 'split-screen:close' : 'split-screen:open'\n addThemerTransitionType(type)\n dispatch({type})\n })\n }\n onMouseEnter={() =>\n startTransition(() => {\n dispatch({type: 'split-screen:prerender'})\n })\n }\n padding={2}\n selected={split}\n tooltip=\"Show light and dark side by side\"\n />\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n if (split) addThemerTransitionType('split-screen:close')\n addThemerTransitionType('close')\n dispatch({type: 'close'})\n })\n }\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>\n </Flex>","<Card\n borderBottom\n padding={3}\n sizing=\"border\"\n style={navbarHeight === null ? undefined : {height: navbarHeight}}\n >\n <Flex align=\"center\" gap={1} height=\"fill\">\n <Box className={title} flex={1}>\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 {!isTooSmallForSplitScreen && (\n <TooltipButton\n aria-pressed={split}\n icon={SplitVerticalIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n const type = split ? 'split-screen:close' : 'split-screen:open'\n addThemerTransitionType(type)\n dispatch({type})\n })\n }\n onMouseEnter={() =>\n startTransition(() => {\n dispatch({type: 'split-screen:prerender'})\n })\n }\n padding={2}\n selected={split}\n tooltip=\"Show light and dark side by side\"\n />\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n if (split) addThemerTransitionType('split-screen:close')\n addThemerTransitionType('close')\n dispatch({type: 'close'})\n })\n }\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>\n </Flex>\n </Card>","<ThemeList actorRef={actorRef} active={active} removed={removed} themes={themes} />","<Activity key=\"list\" mode={view.name === 'list' ? 'visible' : 'hidden'}>\n <ThemeList actorRef={actorRef} active={active} removed={removed} themes={themes} />\n </Activity>","view.name === 'list' ? 'visible' : 'hidden'","view.name === 'edit' && (\n <ThemeEditor\n actorRef={actorRef}\n focusTitle={view.focusTitle}\n slug={view.slug}\n split={split}\n themes={themes}\n />\n )","focusTitle","name","slug","<RemovedThemes actorRef={actorRef} removed={removed} />","<Activity key=\"removed\" mode={view.name === 'removed' ? 'visible' : 'hidden'}>\n <RemovedThemes actorRef={actorRef} removed={removed} />\n </Activity>","view.name === 'removed' ? 'visible' : 'hidden'","<Card height=\"fill\">\n <Flex direction=\"column\" height=\"fill\">\n {/* The header sits next to the Studio navbar and takes its height, so\n the two bottom borders line up — whatever height the Studio, its\n breakpoint or a custom navbar gives it */}\n <Card\n borderBottom\n padding={3}\n sizing=\"border\"\n style={navbarHeight === null ? undefined : {height: navbarHeight}}\n >\n <Flex align=\"center\" gap={1} height=\"fill\">\n <Box className={title} flex={1}>\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 {!isTooSmallForSplitScreen && (\n <TooltipButton\n aria-pressed={split}\n icon={SplitVerticalIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n const type = split ? 'split-screen:close' : 'split-screen:open'\n addThemerTransitionType(type)\n dispatch({type})\n })\n }\n onMouseEnter={() =>\n startTransition(() => {\n dispatch({type: 'split-screen:prerender'})\n })\n }\n padding={2}\n selected={split}\n tooltip=\"Show light and dark side by side\"\n />\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n if (split) addThemerTransitionType('split-screen:close')\n addThemerTransitionType('close')\n dispatch({type: 'close'})\n })\n }\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>\n </Flex>\n </Card>\n\n <Activity key=\"list\" mode={view.name === 'list' ? 'visible' : 'hidden'}>\n <ThemeList actorRef={actorRef} active={active} removed={removed} themes={themes} />\n </Activity>\n {view.name === 'edit' && (\n <ThemeEditor\n actorRef={actorRef}\n focusTitle={view.focusTitle}\n slug={view.slug}\n split={split}\n themes={themes}\n />\n )}\n <Activity key=\"removed\" mode={view.name === 'removed' ? 'visible' : 'hidden'}>\n <RemovedThemes actorRef={actorRef} removed={removed} />\n </Activity>\n </Flex>\n </Card>","_c","_temp","{\n actorRef,\n dispatch,\n navbarHeight,\n split,\n}: Pick<ThemerProps, 'actorRef' | 'dispatch' | 'navbarHeight' | 'split'>","() => 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}","(boundaryElement) => {\n startTransition(() => setBoundaryElement(boundaryElement))\n // Intentionally not unsetting the element as it would create unnecessary work when <Activity> unhides\n return () => {}\n }","boundaryElement","() => {}","() => setWidth(MINIMUM_WIDTH)","<div\n aria-label=\"Resize the themer\"\n className={resizeHandle}\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 />","<ThemerSidebar\n split={split}\n navbarHeight={navbarHeight}\n actorRef={actorRef}\n dispatch={dispatch}\n />","<Box className={content} height=\"fill\" overflow=\"hidden\">\n <BoundaryElementProvider element={boundaryElement}>\n <ThemerSidebar\n split={split}\n navbarHeight={navbarHeight}\n actorRef={actorRef}\n dispatch={dispatch}\n />\n </BoundaryElementProvider>\n </Box>","<Card\n borderLeft\n className={frame}\n height=\"fill\"\n ref={(boundaryElement) => {\n startTransition(() => setBoundaryElement(boundaryElement))\n // Intentionally not unsetting the element as it would create unnecessary work when <Activity> unhides\n return () => {}\n }}\n >\n <div\n aria-label=\"Resize the themer\"\n className={resizeHandle}\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 className={content} height=\"fill\" overflow=\"hidden\">\n <BoundaryElementProvider element={boundaryElement}>\n <ThemerSidebar\n split={split}\n navbarHeight={navbarHeight}\n actorRef={actorRef}\n dispatch={dispatch}\n />\n </BoundaryElementProvider>\n </Box>\n </Card>","<Layer className={sidebar} style={{width}} zOffset={100}>\n <Card\n borderLeft\n className={frame}\n height=\"fill\"\n ref={(boundaryElement) => {\n startTransition(() => setBoundaryElement(boundaryElement))\n // Intentionally not unsetting the element as it would create unnecessary work when <Activity> unhides\n return () => {}\n }}\n >\n <div\n aria-label=\"Resize the themer\"\n className={resizeHandle}\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 className={content} height=\"fill\" overflow=\"hidden\">\n <BoundaryElementProvider element={boundaryElement}>\n <ThemerSidebar\n split={split}\n navbarHeight={navbarHeight}\n actorRef={actorRef}\n dispatch={dispatch}\n />\n </BoundaryElementProvider>\n </Box>\n </Card>\n </Layer>","_c"],"sources":["../src/tool/ScrollArea.css.ts","../src/tool/ScrollArea.tsx","../src/tool/ThemePreview.css.ts","../src/tool/ThemePreview.tsx","../src/tool/ThemeThumbnail.tsx","../src/tool/RemovedThemes.css.ts","../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.css.ts","../src/tool/ImagePaletteSection.tsx","../src/tool/ThemeEditor.css.ts","../src/tool/ThemeEditor.tsx","../src/tool/share.ts","../src/tool/useThemeCodes.ts","../src/tool/PasteThemeDialog.tsx","../src/tool/ThemeCard.css.ts","../src/tool/ThemeCard.tsx","../src/tool/options.ts","../src/tool/snippet.ts","../src/tool/ThemeSnippetDialog.tsx","../src/tool/ThemeList.css.ts","../src/tool/ThemeList.tsx","../src/tool/useIsTooSmallForSplitScreen.ts","../src/tool/ThemerSidebar.css.ts","../src/tool/ThemerSidebar.tsx","../src/tool/ResizableSidebar.css.ts","../src/tool/ResizableSidebar.tsx"],"sourcesContent":["import {style} from '@vanilla-extract/css'\n\n/** Always reserves the scrollbar's space, so the content never shifts */\nexport const root = style({\n scrollbarGutter: 'stable',\n})\n","import {Box, useTheme_v2} from '@sanity/ui'\nimport {createContext, useContext, useEffect, useMemo, useRef, useState} from 'react'\n\nimport {root} from './ScrollArea.css'\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 gutter the last scroll area measured. It is the same for every scroll\n * area (the reserved gutter does not depend on the content), so the next one\n * starts out with the right padding instead of shifting after its first\n * measurement — which would also throw off anything scrolling into view as\n * the content mounts.\n */\nlet lastGutter = 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(() => lastGutter)\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 = () => {\n lastGutter = element.offsetWidth - element.clientWidth\n setGutter(lastGutter)\n }\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 <Box\n className={root}\n flex={1}\n overflow=\"auto\"\n padding={padding}\n ref={ref}\n style={{paddingRight: inset.right}}\n >\n <ScrollAreaInsetContext value={inset}>{children}</ScrollAreaInsetContext>\n </Box>\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 {createVar, style, styleVariants} from '@vanilla-extract/css'\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\n/** The thickness of a line of text — bold enough to survive a half-size preview */\nconst LINE = 4\n\n/**\n * The colors a mockup paints with — only the ones the theme options change —\n * assigned per color scheme from the theme it previews\n */\nexport const colors = {\n bg: createVar(),\n fg: createVar(),\n mutedFg: createVar(),\n border: createVar(),\n accent: createVar(),\n avatar: createVar(),\n selectedBg: createVar(),\n selectedFg: createVar(),\n inputBg: createVar(),\n inputBorder: createVar(),\n}\n\nexport const root = style({\n position: 'relative',\n aspectRatio: '16 / 9',\n overflow: 'hidden',\n})\n\nconst studioBase = style({\n position: 'absolute',\n inset: 0,\n display: 'flex',\n flexDirection: 'column',\n background: colors.bg,\n})\n\nexport const studio = styleVariants({\n light: [studioBase],\n dark: [studioBase, {clipPath: DARK_CLIP_PATH}],\n})\n\nexport const navbar = style({\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n gap: 5,\n height: '18%',\n padding: '0 5%',\n borderBottom: `1px solid ${colors.border}`,\n})\n\nexport const logo = style({\n flex: 'none',\n width: 8,\n height: 8,\n borderRadius: 2,\n background: colors.accent,\n})\n\nexport const spacer = style({\n flex: 1,\n})\n\nexport const avatar = style({\n flex: 'none',\n width: 8,\n height: 8,\n borderRadius: '50%',\n background: colors.avatar,\n})\n\nexport const body = style({\n flex: 1,\n display: 'flex',\n minHeight: 0,\n})\n\nexport const list = style({\n flex: 'none',\n display: 'flex',\n flexDirection: 'column',\n width: '36%',\n borderRight: `1px solid ${colors.border}`,\n})\n\nexport const listRow = style({\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n height: '18%',\n padding: '0 9%',\n selectors: {\n \"&[data-selected='true']\": {\n background: colors.selectedBg,\n },\n },\n})\n\nexport const pane = style({\n flex: 1,\n display: 'flex',\n flexDirection: 'column',\n minWidth: 0,\n})\n\nexport const form = 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\nexport const field = style({\n flex: 'none',\n display: 'flex',\n flexDirection: 'column',\n gap: 3,\n})\n\nexport const input = style({\n height: 12,\n borderRadius: 3,\n background: colors.inputBg,\n boxShadow: `inset 0 0 0 1px ${colors.inputBorder}`,\n})\n\nexport const footer = style({\n flex: 'none',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'flex-end',\n height: '20%',\n padding: '0 8%',\n borderTop: `1px solid ${colors.border}`,\n})\n\nexport const button = style({\n width: '32%',\n height: '50%',\n borderRadius: 3,\n background: colors.accent,\n})\n\nconst lineBase = style({\n flex: 'none',\n height: LINE,\n borderRadius: LINE / 2,\n})\n\n/** A line of text, in one of the text colors — its width is set inline */\nexport const line = styleVariants({\n fg: [lineBase, {background: colors.fg}],\n muted: [lineBase, {background: colors.mutedFg}],\n selected: [lineBase, {background: colors.selectedFg}],\n})\n","import {ThemeProvider, useTheme_v2} from '@sanity/ui'\nimport {type ThemeColorSchemeKey} from '@sanity/ui/theme'\nimport {assignInlineVars} from '@vanilla-extract/dynamic'\nimport {useMemo} from 'react'\n\nimport {buildTheme} from '../theme/buildTheme'\nimport type {BuildThemeOptions} from '../theme/options'\n\nimport {\n avatar,\n body,\n button,\n colors,\n field,\n footer,\n form,\n input,\n line,\n list,\n listRow,\n logo,\n navbar,\n pane,\n root,\n spacer,\n studio,\n} from './ThemePreview.css'\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/**\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 <div aria-hidden className={root}>\n <ThemeProvider scheme=\"light\" theme={theme} tone=\"default\">\n <MockStudio scheme=\"light\" />\n </ThemeProvider>\n <ThemeProvider scheme=\"dark\" theme={theme} tone=\"default\">\n <MockStudio scheme=\"dark\" />\n </ThemeProvider>\n </div>\n )\n}\n\nfunction MockStudio(props: {scheme: ThemeColorSchemeKey}) {\n const {scheme} = props\n const {color} = useTheme_v2()\n const selected = color.selectable.default.selected\n const inputColor = color.input.default.enabled\n\n return (\n <div\n className={studio[scheme]}\n style={assignInlineVars({\n [colors.bg]: color.bg,\n [colors.fg]: color.fg,\n [colors.mutedFg]: color.muted.fg,\n [colors.border]: color.border,\n [colors.accent]: color.button.default.primary.enabled.bg,\n [colors.avatar]: color.avatar.blue.bg,\n [colors.selectedBg]: selected.bg,\n [colors.selectedFg]: selected.fg,\n [colors.inputBg]: inputColor.bg,\n [colors.inputBorder]: inputColor.border,\n })}\n >\n <div className={navbar}>\n <div className={logo} />\n <div className={line.fg} style={{width: '22%'}} />\n <div className={spacer} />\n <div className={avatar} />\n </div>\n\n <div className={body}>\n <div className={list}>\n {LIST_ROWS.map((width, index) => {\n const isSelected = index === 1\n\n return (\n <div key={width} className={listRow} data-selected={isSelected}>\n <div className={isSelected ? line.selected : line.muted} style={{width}} />\n </div>\n )\n })}\n </div>\n\n <div className={pane}>\n <div className={form}>\n <div className={line.fg} style={{width: '45%'}} />\n {FIELDS.map((width) => (\n <div key={width} className={field}>\n <div className={line.muted} style={{width}} />\n <div className={input} />\n </div>\n ))}\n </div>\n <div className={footer}>\n <div className={button} />\n </div>\n </div>\n </div>\n </div>\n )\n}\n","import {Card} from '@sanity/ui'\n\nimport type {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 {style} from '@vanilla-extract/css'\n\n/** A removed theme's thumbnail, dimmed to read as removed */\nexport const removedThumbnail = style({\n opacity: 0.6,\n})\n","import {CheckmarkIcon} from '@sanity/icons/Checkmark'\nimport {Box, Button, Card, Flex, Stack, Text} from '@sanity/ui'\n\nimport type {ThemerProps} from '#types'\n\nimport {ScrollArea} from './ScrollArea'\nimport {displayTitle} from './themes'\nimport {ThemeThumbnail} from './ThemeThumbnail'\n\nimport {removedThumbnail} from './RemovedThemes.css'\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 footer leads back to the\n * list, and the machine leaves the flow on its own once the last theme is\n * gone.\n *\n * @internal\n */\nexport function RemovedThemes({actorRef, removed}: Pick<ThemerProps, 'actorRef' | 'removed'>) {\n return (\n <>\n <ScrollArea padding={3}>\n <Stack gap={5}>\n {removed.map((theme) => (\n <Stack key={theme.slug} gap={3}>\n <Box className={removedThumbnail}>\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 mode=\"ghost\"\n onClick={() => actorRef.send({type: 'theme.restore', slug: theme.slug})}\n padding={2}\n text=\"Restore\"\n />\n {theme.source === 'custom' && (\n <Button\n mode=\"ghost\"\n onClick={() => actorRef.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 {/* In the size of the Studio's own buttons (padding and gap of 2) */}\n <Card borderTop padding={3}>\n <Flex justify=\"flex-end\">\n <Button\n gap={2}\n icon={CheckmarkIcon}\n onClick={() => actorRef.send({type: 'flow.list'})}\n padding={2}\n text=\"Done\"\n mode=\"ghost\"\n />\n </Flex>\n </Card>\n </>\n )\n}\n","import {type ThemeColorSchemeKey} from '@sanity/ui/theme'\n\nimport {hexToHsl, hslToHex, relativeLuminance} from '../theme/hsl'\nimport {\n type BuildThemeOptions,\n DEFAULT_BACKGROUND,\n SCHEMES,\n type SchemeThemeOptions,\n} 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/**\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, type 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, sized like\n * the Studio's own buttons (`padding` and `gap` of 2). Icon-only buttons take\n * 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\n aria-label={buttonProps.text === undefined ? tooltip : undefined}\n gap={2}\n padding={2}\n {...buttonProps}\n />\n </Tooltip>\n )\n}\n","import {ImageIcon} from '@sanity/icons/Image'\nimport type {ButtonProps} from '@sanity/ui'\nimport {useRef} from 'react'\n\nimport {TooltipButton} from './TooltipButton'\n\n/**\n * The file picker for an image, as a hidden file input that a button or menu\n * item clicks: the chosen file goes to `onFile`, and nowhere else — nothing\n * is uploaded.\n *\n * @internal\n */\nexport function ImageFileInput({\n onFile,\n ref,\n}: {\n onFile: (file: File) => void\n ref: React.Ref<HTMLInputElement>\n}) {\n return (\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={ref}\n type=\"file\"\n />\n )\n}\n\n/**\n * A button that opens the file picker for an image, and hands the chosen file\n * over.\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 <ImageFileInput onFile={onFile} ref={inputRef} />\n </>\n )\n}\n","import type {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, type 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 // 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 const pickImage = useCallback(\n (file: File) => {\n const pick = ++latestPickRef.current\n\n setBusy(true)\n\n // Whether this pick is still the latest one. Nothing checks whether the\n // component still shows: adding the theme opens the editor, which hides\n // the list in an `Activity` — its state survives that, and the flag\n // must clear before the list shows again\n const isCurrent = () => 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 {createVar, style} from '@vanilla-extract/css'\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 */\nexport const VARIANT_WIDTH = 144\n\nexport const VARIANT_GAP = 8\nexport const ROW_PADDING = 12\n\n/** The frame around a variant's thumbnail, on either side */\nexport const VARIANT_FRAME = 4\n\n/** The width of one variant's thumbnail, set on the row once it is measured */\nexport const variantWidth = createVar()\n\n/** How much a variant's thumbnail is scaled down from `VARIANT_WIDTH` */\nexport const variantScale = createVar()\n\nexport const paletteGrid = style({\n display: 'grid',\n gridTemplateColumns: `repeat(3, ${TILE_SIZE}px)`,\n gridAutoRows: TILE_SIZE,\n gap: TILE_GAP,\n selectors: {\n \"&[data-with-image='true']\": {\n gridTemplateColumns: `${IMAGE_SIZE}px repeat(3, ${TILE_SIZE}px)`,\n },\n },\n})\n\nexport const imageTile = style({\n display: 'block',\n gridRow: 'span 2',\n width: IMAGE_SIZE,\n height: IMAGE_SIZE,\n borderRadius: 3,\n objectFit: 'cover',\n boxShadow: 'inset 0 0 0 1px var(--card-border-color)',\n})\n\nexport const tile = style({\n display: 'block',\n borderRadius: 3,\n boxShadow: 'inset 0 0 0 1px var(--card-border-color)',\n selectors: {\n \"&[data-empty='true']\": {\n background:\n 'repeating-linear-gradient(-45deg, transparent 0 3px, 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 */\nexport const variantRow = style({\n display: 'flex',\n gap: VARIANT_GAP,\n margin: `0 -${ROW_PADDING}px`,\n padding: `0 ${ROW_PADDING}px`,\n overflowX: 'auto',\n scrollSnapType: 'x mandatory',\n scrollPaddingLeft: ROW_PADDING,\n scrollbarWidth: 'thin',\n})\n\nexport const variantButton = style({\n 'appearance': 'none',\n 'flex': 'none',\n 'display': 'block',\n 'boxSizing': 'border-box',\n 'width': `calc(${variantWidth} + ${VARIANT_FRAME * 2}px)`,\n 'margin': 0,\n 'padding': 0,\n 'border': 0,\n 'background': 'none',\n 'color': 'inherit',\n 'font': 'inherit',\n 'textAlign': 'center',\n 'cursor': 'pointer',\n 'scrollSnapAlign': 'start',\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 */\nexport const variantFrame = style({\n display: 'block',\n boxSizing: 'border-box',\n width: `calc(${variantWidth} + ${VARIANT_FRAME * 2}px)`,\n height: `calc(${variantWidth} * 9 / 16 + ${VARIANT_FRAME * 2}px)`,\n padding: VARIANT_FRAME,\n borderRadius: 7,\n overflow: 'hidden',\n transition: 'box-shadow 100ms',\n selectors: {\n [`${variantButton}:hover &`]: {\n boxShadow: 'inset 0 0 0 2px var(--card-border-color)',\n },\n [`${variantButton}[aria-pressed='true'] &`]: {\n boxShadow: 'inset 0 0 0 2px var(--card-focus-ring-color)',\n },\n [`${variantButton}:focus-visible &`]: {\n outline: '2px solid var(--card-focus-ring-color)',\n outlineOffset: -2,\n },\n },\n})\n\nexport const variantScaleBox = style({\n display: 'block',\n width: VARIANT_WIDTH,\n transform: `scale(${variantScale})`,\n transformOrigin: 'top left',\n})\n","import {SparklesIcon} from '@sanity/icons/Sparkles'\nimport {Box, Card, Flex, Stack, Text} from '@sanity/ui'\nimport {Tooltip} from '@sanity/ui/tooltip'\nimport {assignInlineVars} from '@vanilla-extract/dynamic'\nimport {useEffect, useMemo, useRef, useState} from 'react'\n\nimport type {BuildThemeOptions} from '../theme/options'\nimport {ImageFileButton} from './ImageFileButton'\nimport {\n applyImagePalette,\n currentImageVariant,\n IMAGE_PALETTE_TITLES,\n IMAGE_PALETTE_VARIANTS,\n type ImagePalette,\n type ImagePaletteKey,\n type ImagePaletteVariant,\n} from './imagePalette'\nimport {ThemeThumbnail} from './ThemeThumbnail'\nimport {TooltipButton} from './TooltipButton'\nimport {useImagePalette} from './useImagePalette'\n\nimport {\n imageTile,\n paletteGrid,\n ROW_PADDING,\n tile,\n VARIANT_FRAME,\n VARIANT_GAP,\n VARIANT_WIDTH,\n variantButton,\n variantFrame,\n variantRow,\n variantScale,\n variantScaleBox,\n variantWidth,\n} from './ImagePaletteSection.css'\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\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\n\n/** The width of one variant, before the row has been measured */\nconst FALLBACK_VARIANT_WIDTH = 58\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 [thumbnailWidth, setThumbnailWidth] = 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 frame on either side\n setThumbnailWidth(Math.max(24, Math.floor(visible / VISIBLE_VARIANTS) - VARIANT_FRAME * 2))\n }\n\n measure()\n\n const observer = new ResizeObserver(measure)\n\n observer.observe(row)\n\n return () => observer.disconnect()\n // oxlint-disable-next-line react/exhaustive-effect-dependencies -- the row only renders with a palette, so a new palette has a new row to observe\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 : [\n {\n variant,\n title: IMAGE_PALETTE_TITLES[variant],\n options: applyImagePalette(options, palette, variant),\n },\n ],\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 <div className={paletteGrid} data-with-image={imageUrl !== undefined}>\n {imageUrl && <img alt=\"\" className={imageTile} src={imageUrl} />}\n {TILES.map((key) => {\n const hex = palette[key]\n const title = IMAGE_PALETTE_TITLES[key]\n\n return (\n <Tooltip\n key={key}\n animate\n content={\n <Text size={1}>\n {hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`}\n </Text>\n }\n\n placement=\"bottom\"\n portal\n >\n <span\n className={tile}\n data-empty={hex === null}\n style={{background: hex ?? undefined}}\n />\n </Tooltip>\n )\n })}\n </div>\n\n <Stack gap={2}>\n <Text muted size={0}>\n Pick the swatch to build the theme around\n </Text>\n <div\n className={variantRow}\n ref={rowRef}\n style={assignInlineVars({\n [variantWidth]: `${thumbnailWidth}px`,\n [variantScale]: String(thumbnailWidth / VARIANT_WIDTH),\n })}\n >\n {variants.map(({variant, title, options: variantOptions}) => (\n // oxlint-disable-next-line control-has-associated-label -- the title below the thumbnail is the label, deeper than the rule looks\n <button\n key={variant}\n aria-pressed={variant === current}\n className={variantButton}\n onClick={() => onVariant(variant)}\n type=\"button\"\n >\n <span className={variantFrame}>\n <span className={variantScaleBox}>\n <ThemeThumbnail options={variantOptions} />\n </span>\n </span>\n <Box as=\"span\" display=\"block\" paddingTop={1}>\n <Text\n align=\"center\"\n as=\"span\"\n size={0}\n textOverflow=\"ellipsis\"\n weight={variant === current ? 'medium' : 'regular'}\n >\n {title}\n </Text>\n </Box>\n </button>\n ))}\n </div>\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 {style} from '@vanilla-extract/css'\n\n/** A native color input, trimmed down to its swatch */\nexport const swatch = style({\n boxSizing: 'border-box',\n flex: 'none',\n width: 33,\n height: 33,\n padding: 0,\n border: '1px solid var(--card-border-color)',\n borderRadius: 4,\n background: 'none',\n cursor: 'pointer',\n selectors: {\n '&::-webkit-color-swatch-wrapper': {\n padding: 0,\n },\n '&::-webkit-color-swatch': {\n border: 'none',\n borderRadius: 3,\n },\n '&::-moz-color-swatch': {\n border: 'none',\n borderRadius: 3,\n },\n },\n})\n\n/** A native range input, themed through `accent-color` */\nexport const range = style({\n display: 'block',\n width: '100%',\n margin: 0,\n accentColor: 'var(--card-focus-ring-color)',\n})\n\n/** Lets the color row's text shrink and truncate instead of widening the row */\nexport const colorRowText = style({\n minWidth: 0,\n})\n","import {CheckmarkIcon} from '@sanity/icons/Checkmark'\nimport {EllipsisHorizontalIcon} from '@sanity/icons/EllipsisHorizontal'\nimport {ResetIcon} from '@sanity/icons/Reset'\nimport {TrashIcon} from '@sanity/icons/Trash'\nimport {Badge, Box, Button, Card, Flex, Stack, Text, TextInput} from '@sanity/ui'\nimport {Menu, MenuButton, MenuItem} from '@sanity/ui/menu'\nimport {type ThemeColorSchemeKey} from '@sanity/ui/theme'\nimport {useSelector} from '@xstate/react'\nimport {useId, useMemo} from 'react'\nimport {useColorSchemeValue} from 'sanity'\n\nimport type {ThemerProps} from '#types'\n\nimport {buildPalette, type GeneratedColorPalette} from '../theme/buildPalette'\nimport {\n type BuildThemeOptions,\n type ResolvedSchemeOptions,\n resolveThemeOptions,\n SCHEMES,\n type SchemeThemeOptions,\n} from '../theme/options'\nimport {\n applyImagePalette,\n currentImageVariant,\n type ImagePalette,\n type ImagePaletteVariant,\n pickLuckyVariant,\n} from './imagePalette'\nimport {ImagePaletteSection} from './ImagePaletteSection'\nimport {ScrollArea, ScrollAreaBleed} from './ScrollArea'\nimport {ThemeThumbnail} from './ThemeThumbnail'\nimport {TooltipButton} from './TooltipButton'\n\nimport {colorRowText, range, swatch} from './ThemeEditor.css'\n\nconst SCHEME_TITLES: Record<ThemeColorSchemeKey, string> = {\n dark: 'Dark mode',\n light: 'Light mode',\n}\n\n/**\n * Where the split preview shows a scheme: the Studio's own scheme stays next\n * to the sidebar (or at the bottom, stacked on small screens), the other one\n * takes the far side\n */\nfunction splitHint(own: boolean): string {\n return own ? 'Shown on the right, next to the themer' : '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 */\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({\n actorRef,\n focusTitle,\n slug,\n split,\n themes,\n}: {focusTitle: boolean; slug: string} & Pick<ThemerProps, 'actorRef' | 'split' | 'themes'>) {\n const images = useSelector(actorRef, (snapshot) => snapshot.context.images)\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 actorRef.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={() => actorRef.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) => actorRef.send({type: 'theme.update', slug, options})}\n onPalette={(palette, file) =>\n actorRef.send({\n type: 'theme.update',\n slug,\n options: applyImagePalette(theme.options, palette),\n palette,\n imageUrl: URL.createObjectURL(file),\n })\n }\n onRemove={() => actorRef.send({type: 'theme.remove', slug})}\n onTitleChange={(title) => actorRef.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 actionsId = useId()\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 key={scheme}\n active={split || scheme === studioScheme}\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 splitHint={splitHint(scheme === studioScheme)}\n />\n ))}\n </ScrollAreaBleed>\n </Stack>\n </ScrollArea>\n\n {/* Like the Studio's document footer: one primary action, the rest in\n the menu, in the size of the Studio's own buttons (padding and gap of 2) */}\n <Card borderTop padding={3}>\n <Flex align=\"center\" gap={2} justify=\"flex-end\">\n <Button\n gap={2}\n icon={CheckmarkIcon}\n onClick={onDone}\n padding={2}\n text=\"Done\"\n mode=\"ghost\"\n />\n <MenuButton\n button={\n <Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n mode=\"bleed\"\n padding={2}\n />\n }\n id={actionsId}\n menu={\n <Menu>\n {/* The theme can be restored until it is deleted from the removed themes */}\n <MenuItem icon={TrashIcon} onClick={onRemove} text=\"Remove\" tone=\"critical\" />\n </Menu>\n }\n popover={{animate: true, placement: 'top-end', portal: true}}\n />\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 /** Where the split preview shows this scheme */\n splitHint: string\n}) {\n const {active, onChange, options, palette, resolved, scheme, split, splitHint} = 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 ? splitHint\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 <input\n aria-label={`${name} contrast`}\n className={range}\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 <input\n aria-label={label}\n className={swatch}\n onChange={(event) => onChange(event.currentTarget.value)}\n type=\"color\"\n value={value}\n />\n <Stack className={colorRowText} flex={1} gap={2}>\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 {isColor} from '../lib/mix'\nimport {\n type BuildThemeOptions,\n MAXIMUM_CONTRAST,\n MINIMUM_CONTRAST,\n SCHEMES,\n type SchemeThemeOptions,\n} from '../theme/options'\nimport {displayTitle} from './themes'\n\n/** What a theme code carries: a theme's title and its options @internal */\nexport interface SharedTheme {\n title: string\n options: BuildThemeOptions\n}\n\n/**\n * The version of the code format, the first byte of every code — bumped if\n * the layout below ever changes, so older codes can still be told apart\n */\nconst VERSION = 1\n\n/** The fields a scheme's options can set, in the order their bytes go */\nconst FIELDS = ['accent', 'text', 'background', 'contrast'] as const\n\n/**\n * Turns a theme into a short code to share in a message: `title`, and the\n * colors and contrasts of both schemes, packed into bytes — one flag byte says\n * which of the eight fields are set, each color is its three RGB bytes and a\n * contrast one byte, and the title takes up the rest — then base64url'd, so\n * it is one word without padding, safe in URLs and chat. A theme setting\n * everything comes to about 30 bytes plus its title.\n *\n * @internal\n */\nexport function encodeTheme(theme: SharedTheme): string {\n const bytes: number[] = [VERSION, 0]\n let flags = 0\n\n SCHEMES.forEach((scheme, schemeIndex) => {\n const options: SchemeThemeOptions = theme.options[scheme] ?? {}\n\n FIELDS.forEach((field, fieldIndex) => {\n const value = options[field]\n\n if (value === undefined) return\n\n if (field === 'contrast') {\n if (typeof value !== 'number' || !Number.isFinite(value)) return\n\n bytes.push(Math.round(Math.min(MAXIMUM_CONTRAST, Math.max(MINIMUM_CONTRAST, value))))\n } else {\n if (typeof value !== 'string' || !isColor(value)) return\n\n bytes.push(...rgbBytes(value))\n }\n\n flags |= 1 << (schemeIndex * FIELDS.length + fieldIndex)\n })\n })\n\n bytes[1] = flags\n bytes.push(...new TextEncoder().encode(theme.title.trim()))\n\n return toBase64Url(Uint8Array.from(bytes))\n}\n\n/**\n * Reads a theme back out of a code — as pasted, so surrounding whitespace and\n * words around the code are fine: the first word that is a code counts. `null`\n * when nothing in the text is one.\n *\n * @internal\n */\nexport function decodeTheme(text: string): SharedTheme | null {\n for (const word of text.trim().split(/\\s+/)) {\n const theme = decodeCode(word)\n\n if (theme) return theme\n }\n\n return null\n}\n\nfunction decodeCode(code: string): SharedTheme | null {\n const bytes = fromBase64Url(code)\n\n if (!bytes || bytes.length < 2 || bytes[0] !== VERSION) return null\n\n const flags = bytes[1]\n const options: BuildThemeOptions = {}\n let offset = 2\n\n for (const [schemeIndex, scheme] of SCHEMES.entries()) {\n const schemeOptions: SchemeThemeOptions = {}\n\n for (const [fieldIndex, field] of FIELDS.entries()) {\n if (!(flags & (1 << (schemeIndex * FIELDS.length + fieldIndex)))) continue\n\n if (field === 'contrast') {\n if (offset + 1 > bytes.length) return null\n\n schemeOptions.contrast = Math.min(\n MAXIMUM_CONTRAST,\n Math.max(MINIMUM_CONTRAST, bytes[offset]),\n )\n offset += 1\n } else {\n if (offset + 3 > bytes.length) return null\n\n schemeOptions[field] = hexColor(bytes.subarray(offset, offset + 3))\n offset += 3\n }\n }\n\n if (Object.keys(schemeOptions).length > 0) options[scheme] = schemeOptions\n }\n\n let title: string\n\n try {\n title = new TextDecoder('utf-8', {fatal: true}).decode(bytes.subarray(offset))\n } catch {\n return null\n }\n\n return {title: displayTitle(title), options}\n}\n\n/** The three RGB bytes of a `#rgb` or `#rrggbb` color */\nfunction rgbBytes(color: string): number[] {\n const hex = color.length === 4 ? color.slice(1).replaceAll(/./g, '$&$&') : color.slice(1)\n\n return [0, 2, 4].map((start) => parseInt(hex.slice(start, start + 2), 16))\n}\n\nfunction hexColor(rgb: Uint8Array): string {\n return `#${[...rgb].map((byte) => byte.toString(16).padStart(2, '0')).join('')}`\n}\n\nfunction toBase64Url(bytes: Uint8Array): string {\n let binary = ''\n\n for (const byte of bytes) binary += String.fromCharCode(byte)\n\n return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '')\n}\n\nfunction fromBase64Url(code: string): Uint8Array | null {\n if (!/^[A-Za-z0-9_-]+$/.test(code)) return null\n\n const base64 = code.replaceAll('-', '+').replaceAll('_', '/')\n\n try {\n return Uint8Array.from(atob(base64.padEnd(Math.ceil(base64.length / 4) * 4, '=')), (char) =>\n char.charCodeAt(0),\n )\n } catch {\n return null\n }\n}\n","import {useToast} from '@sanity/ui/toast'\nimport {useEffect} from 'react'\n\nimport type {ThemerProps} from '#types'\n\nimport {decodeTheme, encodeTheme, type SharedTheme} from './share'\nimport {displayTitle} from './themes'\n\n/**\n * Copying a theme as a code to share, and adding a theme from a code someone\n * shared — with toasts telling how it went.\n *\n * @internal\n */\nexport function useThemeCodes({actorRef}: Pick<ThemerProps, 'actorRef'>): {\n copyTheme: (theme: SharedTheme) => Promise<void>\n /** Adds the theme in the text, if there is one — the result says whether there was */\n addThemeFromText: (text: string) => boolean\n /** Adds the theme on the clipboard, if there is one and the browser hands the clipboard over */\n addThemeFromClipboard: () => Promise<'added' | 'no-theme' | 'no-clipboard'>\n} {\n const toast = useToast()\n\n const copyTheme = async (theme: SharedTheme) => {\n const title = displayTitle(theme.title)\n\n try {\n await navigator.clipboard.writeText(encodeTheme(theme))\n toast.push({\n status: 'success',\n title: `Copied ${title} as a code`,\n description: 'Paste it in the themer of another Studio to add the theme there',\n })\n } catch {\n toast.push({status: 'error', title: `Could not copy ${title}`})\n }\n }\n\n const addThemeFromText = (text: string) => {\n const theme = decodeTheme(text)\n\n if (!theme) return false\n\n actorRef.send({type: 'theme.import', title: theme.title, options: theme.options})\n toast.push({status: 'success', title: `Added ${theme.title}`})\n\n return true\n }\n\n const addThemeFromClipboard = async () => {\n let text: string\n\n try {\n text = await navigator.clipboard.readText()\n } catch {\n return 'no-clipboard'\n }\n\n return addThemeFromText(text) ? 'added' : 'no-theme'\n }\n\n return {copyTheme, addThemeFromText, addThemeFromClipboard}\n}\n\n/**\n * Adds a theme pasted anywhere in the Studio while the themer's list is\n * showing — pastes into inputs and other editable things are theirs.\n *\n * @internal\n */\nexport function usePasteThemeCodes({actorRef}: Pick<ThemerProps, 'actorRef'>): void {\n const toast = useToast()\n\n useEffect(() => {\n const handlePaste = (event: ClipboardEvent) => {\n if (isEditable(event.target)) return\n\n const theme = decodeTheme(event.clipboardData?.getData('text/plain') ?? '')\n\n if (!theme) return\n\n event.preventDefault()\n actorRef.send({type: 'theme.import', title: theme.title, options: theme.options})\n toast.push({status: 'success', title: `Added ${theme.title}`})\n }\n\n document.addEventListener('paste', handlePaste)\n\n return () => document.removeEventListener('paste', handlePaste)\n }, [actorRef, toast])\n}\n\nfunction isEditable(target: EventTarget | null): boolean {\n return (\n target instanceof HTMLElement &&\n (target.isContentEditable || target.matches('input, textarea, select'))\n )\n}\n","import {Button, Card, Dialog, Flex, Stack, Text, TextInput} from '@sanity/ui'\nimport {useId, useState} from 'react'\n\nimport type {ThemerProps} from '#types'\n\nimport {useThemeCodes} from './useThemeCodes'\n\n/**\n * Where a theme code someone shared gets pasted by hand — for browsers that\n * do not hand the clipboard over, and for codes that are not on it.\n *\n * @internal\n */\nexport function PasteThemeDialog(props: {onClose: () => void} & Pick<ThemerProps, 'actorRef'>) {\n const {actorRef, onClose} = props\n const {addThemeFromText} = useThemeCodes({actorRef})\n const [text, setText] = useState('')\n const [invalid, setInvalid] = useState(false)\n const id = useId()\n const errorId = `${id}-error`\n\n const add = () => {\n if (addThemeFromText(text)) {\n onClose()\n } else {\n setInvalid(true)\n }\n }\n\n return (\n <Dialog\n animate\n footer={\n <Flex gap={2} justify=\"flex-end\" padding={3}>\n <Button mode=\"ghost\" onClick={onClose} padding={2} text=\"Cancel\" />\n <Button\n disabled={text.trim() === ''}\n onClick={add}\n padding={2}\n text=\"Add theme\"\n tone=\"primary\"\n />\n </Flex>\n }\n header=\"Add a shared theme\"\n id={id}\n onClickOutside={onClose}\n onClose={onClose}\n width={0}\n >\n <form\n onSubmit={(event) => {\n event.preventDefault()\n add()\n }}\n >\n <Stack gap={3} padding={4}>\n <Text muted size={1}>\n Paste the code someone copied from their themer — the theme is added to your list and\n applied.\n </Text>\n <TextInput\n aria-describedby={invalid ? errorId : undefined}\n aria-invalid={invalid}\n aria-label=\"Theme code\"\n autoFocus\n customValidity={invalid ? 'That is not a theme code' : undefined}\n fontSize={1}\n onChange={(event) => {\n setText(event.currentTarget.value)\n setInvalid(false)\n }}\n placeholder=\"AQ…\"\n value={text}\n />\n {invalid && (\n <Card id={errorId} padding={3} radius={2} role=\"alert\" tone=\"critical\">\n <Text size={1}>\n That is not a theme code — codes are one word, copied from a theme's menu.\n </Text>\n </Card>\n )}\n </Stack>\n </form>\n </Dialog>\n )\n}\n","import {style} from '@vanilla-extract/css'\n\nexport const root = style({\n position: 'relative',\n})\n\n/** A bare button, so that the thumbnail and the title can carry the styling */\nexport const pickButton = style({\n 'appearance': 'none',\n 'display': 'block',\n 'boxSizing': 'border-box',\n 'width': '100%',\n 'margin': 0,\n 'padding': 0,\n 'border': 0,\n 'background': 'none',\n 'color': 'inherit',\n 'font': 'inherit',\n 'textAlign': 'center',\n 'cursor': 'pointer',\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 */\nexport const frame = style({\n display: 'block',\n padding: 4,\n borderRadius: 10,\n transition: 'box-shadow 100ms',\n selectors: {\n [`${pickButton}:hover &`]: {\n boxShadow: 'inset 0 0 0 2px var(--card-border-color)',\n },\n [`${pickButton}[aria-pressed='true'] &`]: {\n boxShadow: 'inset 0 0 0 2px var(--card-focus-ring-color)',\n },\n [`${pickButton}:focus-visible &`]: {\n outline: '2px solid var(--card-focus-ring-color)',\n outlineOffset: -2,\n },\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 */\nexport const menuSlot = style({\n position: 'absolute',\n top: 10,\n right: 10,\n opacity: 0,\n transition: 'opacity 100ms',\n selectors: {\n [`${root}:hover &, ${root}:focus-within &, &[data-visible='true']`]: {\n opacity: 1,\n },\n },\n})\n\n/** The dark card behind the menu button sizes to the button, not the slot */\nexport const menuCard = style({\n display: 'inline-block',\n})\n","import {ArrowDownIcon} from '@sanity/icons/ArrowDown'\nimport {ArrowUpIcon} from '@sanity/icons/ArrowUp'\nimport {CopyIcon} from '@sanity/icons/Copy'\nimport {EditIcon} from '@sanity/icons/Edit'\nimport {EllipsisHorizontalIcon} from '@sanity/icons/EllipsisHorizontal'\nimport {ShareIcon} from '@sanity/icons/Share'\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 {Reorder, useDragControls} from 'motion/react'\nimport {useId, useRef, useState} from 'react'\nimport scrollIntoView from 'scroll-into-view-if-needed'\n\nimport type {ThemerProps} from '#types'\n\nimport {displayTitle, type ThemerTheme} from './themes'\nimport {ThemeThumbnail} from './ThemeThumbnail'\nimport {useThemeCodes} from './useThemeCodes'\n\nimport {frame, menuCard, menuSlot, pickButton, root} from './ThemeCard.css'\n\n/**\n * Scrolls the applied theme's card into view when it is not already, as a\n * ref: the list scrolls to it as it mounts — coming back from the editor or\n * the removed themes — and when another theme gets applied\n */\nfunction scrollRefIntoViewIfNeeded(node: HTMLElement | null) {\n if (node) scrollIntoView(node, {block: 'nearest', scrollMode: 'if-needed'})\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, dragging it moves the theme to another place in\n * the list; its menu holds the move, edit, duplicate and remove flows that\n * apply to the theme.\n *\n * @internal\n */\nexport function ThemeCard({\n actorRef,\n active,\n theme,\n themes,\n}: {active: boolean; theme: ThemerTheme} & Pick<ThemerProps, 'actorRef' | 'themes'>) {\n const {copyTheme} = useThemeCodes({actorRef})\n const {scheme} = useRootTheme()\n const [menuOpen, setMenuOpen] = useState(false)\n const dragControls = useDragControls()\n const menuId = useId()\n // Letting go of a drag also clicks the card, and that click must not pick\n const dragged = useRef(false)\n const title = displayTitle(theme.title)\n const index = themes.findIndex((candidate) => candidate.slug === theme.slug)\n // The configured theme sits ahead of the list, and does not move within it\n const listed = index !== -1\n\n const moveTo = (to: number) => {\n const order = themes.map((candidate) => candidate.slug)\n\n order.splice(to, 0, ...order.splice(index, 1))\n actorRef.send({type: 'theme.reorder', order})\n }\n\n return (\n <Reorder.Item\n as=\"div\"\n className={root}\n dragControls={dragControls}\n dragListener={false}\n onDragEnd={() => {\n // The click lands before the next frame\n requestAnimationFrame(() => {\n dragged.current = false\n })\n }}\n onDragStart={() => {\n dragged.current = true\n }}\n ref={active ? scrollRefIntoViewIfNeeded : undefined}\n value={theme.slug}\n whileDrag={{scale: 1.04}}\n >\n <button\n aria-pressed={active}\n className={pickButton}\n onClick={() => {\n if (!dragged.current) actorRef.send({type: 'theme.pick', slug: theme.slug})\n }}\n onPointerDown={(event) => {\n // A mouse or pen drags the card off the button; a finger scrolls the\n // list instead, and moves themes from the menu\n if (listed && event.pointerType !== 'touch') dragControls.start(event)\n }}\n type=\"button\"\n >\n <span className={frame}>\n <ThemeThumbnail options={theme.options} />\n </span>\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 </button>\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 <div className={menuSlot} data-visible={menuOpen}>\n <Tooltip animate content={<Text size={1}>Show more</Text>} placement=\"bottom\" portal>\n <Card className={menuCard} radius={2} scheme=\"dark\">\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={menuId}\n menu={\n <Menu>\n {listed && (\n <>\n <MenuItem\n disabled={index <= 0}\n icon={ArrowUpIcon}\n onClick={() => moveTo(index - 1)}\n text=\"Move up\"\n />\n <MenuItem\n disabled={index >= themes.length - 1}\n icon={ArrowDownIcon}\n onClick={() => moveTo(index + 1)}\n text=\"Move down\"\n />\n <MenuDivider />\n </>\n )}\n {theme.source === 'custom' && (\n <MenuItem\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: theme.slug})}\n text=\"Edit\"\n />\n )}\n <MenuItem\n icon={CopyIcon}\n onClick={() => actorRef.send({type: 'theme.duplicate', slug: theme.slug})}\n text={theme.source === 'custom' ? 'Duplicate' : 'Duplicate to edit'}\n />\n <MenuItem\n icon={ShareIcon}\n onClick={() => void copyTheme(theme)}\n text=\"Copy to share\"\n />\n {theme.source !== 'config' && (\n <>\n <MenuDivider />\n <MenuItem\n icon={TrashIcon}\n onClick={() => actorRef.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 </div>\n </Reorder.Item>\n )\n}\n","import {\n type BuildThemeOptions,\n DEFAULT_ACCENT,\n DEFAULT_BACKGROUND,\n DEFAULT_CONTRAST,\n deriveTextColor,\n type ResolvedSchemeOptions,\n resolveThemeOptions,\n SCHEMES,\n type 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 {type BuildThemeOptions, SCHEMES, type 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 {useId} from 'react'\nimport {registerLanguage} from 'react-refractor'\nimport typescript from 'refractor/typescript'\n\nimport {createThemeSnippet} from './snippet'\nimport {displayTitle, type 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 id = useId()\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\n gap={2}\n icon={ClipboardIcon}\n mode=\"ghost\"\n onClick={() => void handleCopy()}\n padding={2}\n text=\"Copy\"\n />\n </Flex>\n }\n header=\"Add to your config\"\n id={id}\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 {style} from '@vanilla-extract/css'\n\nimport {MAXIMUM_WIDTH, MINIMUM_WIDTH} from './sidebarWidth'\n\nimport {sidebarContent} from './ResizableSidebar.css'\n\nconst CARD_GAP = 16\n\n/**\n * The theme cards flow into two columns once the sidebar has been dragged a\n * third of the way from its narrowest to its widest. The breakpoints are\n * container queries on the sidebar's content (`ResizableSidebar`), which is\n * as wide as the sidebar itself.\n */\nconst TWO_COLUMNS_FROM = MINIMUM_WIDTH + (MAXIMUM_WIDTH - MINIMUM_WIDTH) * 0.3\n\n/**\n * The columns share the width equally whatever the titles: a plain `1fr` may\n * not shrink below a card's min-content width, and a card's title does not\n * wrap, so one long title would widen its column past the sidebar\n */\nconst COLUMN = 'minmax(0, 1fr)'\n\nexport const cardGrid = style({\n 'display': 'grid',\n 'gridTemplateColumns': COLUMN,\n 'gap': CARD_GAP,\n '@container': {\n [`${sidebarContent} (min-width: ${TWO_COLUMNS_FROM}px)`]: {\n gridTemplateColumns: `repeat(2, ${COLUMN})`,\n },\n },\n})\n","import {AddIcon} from '@sanity/icons/Add'\nimport {ClipboardIcon} from '@sanity/icons/Clipboard'\nimport {CodeBlockIcon} from '@sanity/icons/CodeBlock'\nimport {CopyIcon} from '@sanity/icons/Copy'\nimport {EditIcon} from '@sanity/icons/Edit'\nimport {EllipsisHorizontalIcon} from '@sanity/icons/EllipsisHorizontal'\nimport {ImageIcon} from '@sanity/icons/Image'\nimport {RestoreIcon} from '@sanity/icons/Restore'\nimport {Button, Card, Flex, Stack} from '@sanity/ui'\nimport {Menu, MenuButton, MenuDivider, MenuItem} from '@sanity/ui/menu'\nimport {useSelector} from '@xstate/react'\nimport {MotionConfig, Reorder} from 'motion/react'\nimport {useId, useRef} from 'react'\n\nimport type {ThemerProps} from '#types'\n\nimport {ImageFileInput} from './ImageFileButton'\nimport {optionsFromImagePalette, titleFromFileName} from './imagePalette'\nimport type {ThemerSnapshot} from './machine'\nimport {PasteThemeDialog} from './PasteThemeDialog'\nimport {ScrollArea} from './ScrollArea'\nimport {ThemeCard} from './ThemeCard'\nimport {CONFIG_SLUG, CONFIG_TITLE} from './themes'\nimport {ThemeSnippetDialog} from './ThemeSnippetDialog'\nimport {useImagePalette} from './useImagePalette'\nimport {usePasteThemeCodes, useThemeCodes} from './useThemeCodes'\n\nimport {cardGrid} from './ThemeList.css'\n\n/**\n * The flow for picking a theme: a grid of theme cards — the configured theme,\n * the presets and the user's own themes — that drag into the order the user\n * wants, with the entry point to the restore flow below. The footer takes the\n * applied theme on to the editor — a copy of it, for a preset or the\n * configured theme — and keeps the ways to add a theme and the applied\n * theme's code behind its menu. A theme code someone shared pastes right into\n * the list too.\n *\n * @internal\n */\nexport function ThemeList({\n actorRef,\n themes,\n removed,\n active,\n}: Pick<ThemerProps, 'actorRef' | 'themes' | 'removed' | 'active'>) {\n const {addThemeFromClipboard} = useThemeCodes({actorRef})\n // Which of the list's dialogs is open is the machine's: a state of the list flow\n const dialog = useSelector(actorRef, selectDialog)\n const imageInputRef = useRef<HTMLInputElement | null>(null)\n const actionsId = useId()\n\n usePasteThemeCodes({actorRef})\n\n const {busy, pickImage} = useImagePalette((palette, file) =>\n actorRef.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 {/* The group tells a column from a grid by measuring the cards, and\n lets go of the layout animations for users who prefer reduced motion */}\n <MotionConfig reducedMotion=\"user\">\n <Reorder.Group\n as=\"div\"\n className={cardGrid}\n onReorder={(order: string[]) => actorRef.send({type: 'theme.reorder', order})}\n values={themes.map((theme) => theme.slug)}\n >\n <ThemeCard\n actorRef={actorRef}\n active={!active}\n theme={{\n slug: CONFIG_SLUG,\n title: CONFIG_TITLE,\n options: {},\n source: 'config',\n }}\n themes={themes}\n />\n {themes.map((theme) => (\n <ThemeCard\n key={theme.slug}\n actorRef={actorRef}\n active={theme.slug === active?.slug}\n theme={theme}\n themes={themes}\n />\n ))}\n </Reorder.Group>\n </MotionConfig>\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 gap={2}\n icon={RestoreIcon}\n mode=\"bleed\"\n onClick={() => actorRef.send({type: 'flow.removed'})}\n padding={2}\n text={`Show removed (${removed.length})`}\n width=\"fill\"\n />\n )}\n </Stack>\n </ScrollArea>\n\n {/* Like the Studio's document footer: one primary action, the rest in\n the menu, in the size of the Studio's own buttons (padding and gap of 2) */}\n <Card borderTop padding={3}>\n <Flex align=\"center\" gap={2} justify=\"flex-end\">\n {active?.source === 'custom' ? (\n <Button\n gap={2}\n icon={EditIcon}\n onClick={() => actorRef.send({type: 'theme.edit', slug: active.slug})}\n padding={2}\n text=\"Edit\"\n mode=\"ghost\"\n />\n ) : (\n <Button\n gap={2}\n icon={CopyIcon}\n onClick={() =>\n actorRef.send({type: 'theme.duplicate', slug: active?.slug ?? CONFIG_SLUG})\n }\n padding={2}\n text=\"Duplicate & Edit\"\n mode=\"ghost\"\n />\n )}\n <MenuButton\n button={\n <Button\n aria-label=\"More actions\"\n icon={EllipsisHorizontalIcon}\n // The image's colors are read on device, which the menu button shows while it lasts\n loading={busy}\n mode=\"bleed\"\n padding={2}\n />\n }\n id={actionsId}\n menu={\n // Short labels: the menu is confined to the sidebar, which can be narrow\n <Menu>\n <MenuItem\n icon={AddIcon}\n onClick={() => actorRef.send({type: 'theme.add'})}\n text=\"Add theme\"\n />\n <MenuItem\n icon={ImageIcon}\n onClick={() => imageInputRef.current?.click()}\n text=\"Add from image\"\n />\n <MenuItem\n icon={ClipboardIcon}\n onClick={async () => {\n // Straight from the clipboard where the browser allows; by hand otherwise\n if ((await addThemeFromClipboard()) !== 'added') {\n actorRef.send({type: 'dialog.paste'})\n }\n }}\n text=\"Add from code\"\n />\n {active && (\n <>\n <MenuDivider />\n <MenuItem\n icon={CodeBlockIcon}\n onClick={() => actorRef.send({type: 'dialog.snippet'})}\n text=\"Show theme code\"\n />\n </>\n )}\n </Menu>\n }\n popover={{animate: true, placement: 'top-end', portal: true}}\n />\n </Flex>\n </Card>\n\n <ImageFileInput onFile={pickImage} ref={imageInputRef} />\n\n {dialog === 'paste' && (\n <PasteThemeDialog\n actorRef={actorRef}\n onClose={() => actorRef.send({type: 'dialog.close'})}\n />\n )}\n {dialog === 'snippet' && active && (\n <ThemeSnippetDialog onClose={() => actorRef.send({type: 'dialog.close'})} theme={active} />\n )}\n </>\n )\n}\n\n/** The dialog the list has open, if any — a state of the machine's list flow */\nfunction selectDialog(snapshot: ThemerSnapshot): 'paste' | 'snippet' | null {\n if (snapshot.matches({flow: {list: 'pasting'}})) return 'paste'\n if (snapshot.matches({flow: {list: 'snippet'}})) return 'snippet'\n\n return null\n}\n","import {useMediaIndex} from '@sanity/ui'\n\nimport {MIN_MEDIA_INDEX_FOR_SPLIT_SCREEN} from '#constants'\n\nexport function useIsTooSmallForSplitScreen() {\n const index = useMediaIndex()\n return index <= MIN_MEDIA_INDEX_FOR_SPLIT_SCREEN\n}\n","import {style} from '@vanilla-extract/css'\n\n/** Lets the title shrink and truncate instead of pushing the buttons out */\nexport const title = style({\n minWidth: 0,\n})\n","import {CloseIcon} from '@sanity/icons/Close'\nimport {SplitVerticalIcon} from '@sanity/icons/SplitVertical'\nimport {Box, Card, Flex, Text} from '@sanity/ui'\nimport {useSelector} from '@xstate/react'\nimport {dequal} from 'dequal/lite'\nimport {Activity, startTransition} from 'react'\n\nimport type {ThemerView} from '#context'\nimport type {ThemerProps} from '#types'\n\nimport {addThemerTransitionType} from './addThemerTransitionType'\nimport type {ThemerSnapshot} from './machine'\nimport {RemovedThemes} from './RemovedThemes'\nimport {ThemeEditor} from './ThemeEditor'\nimport {ThemeList} from './ThemeList'\nimport {resolveThemes} from './themes'\nimport {TooltipButton} from './TooltipButton'\nimport {useIsTooSmallForSplitScreen} from './useIsTooSmallForSplitScreen'\n\nimport {title} from './ThemerSidebar.css'\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 with the flow's title, the split preview\n * toggle and the close button, and the flow itself — picking a theme, editing\n * one, or restoring removed ones. Each flow ends in a footer like the\n * Studio's document footer: one primary action, with the rest of its actions\n * behind a menu button, and the way back to the list where the flow is not\n * the list.\n *\n * @internal\n */\nexport function ThemerSidebar({\n actorRef,\n dispatch,\n navbarHeight,\n split,\n}: Pick<ThemerProps, 'actorRef' | 'dispatch' | 'navbarHeight' | 'split'>) {\n const view = useSelector(actorRef, selectView, sameView)\n const isTooSmallForSplitScreen = useIsTooSmallForSplitScreen()\n\n const {themes, removed, active} = useSelector(\n actorRef,\n (snapshot) => resolveThemes(snapshot.context),\n dequal,\n )\n\n return (\n <Card height=\"fill\">\n <Flex direction=\"column\" height=\"fill\">\n {/* The header sits next to the Studio navbar and takes its height, so\n the two bottom borders line up — whatever height the Studio, its\n breakpoint or a custom navbar gives it */}\n <Card\n borderBottom\n padding={3}\n sizing=\"border\"\n style={navbarHeight === null ? undefined : {height: navbarHeight}}\n >\n <Flex align=\"center\" gap={1} height=\"fill\">\n <Box className={title} flex={1}>\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 {!isTooSmallForSplitScreen && (\n <TooltipButton\n aria-pressed={split}\n icon={SplitVerticalIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n const type = split ? 'split-screen:close' : 'split-screen:open'\n addThemerTransitionType(type)\n dispatch({type})\n })\n }\n onMouseEnter={() =>\n startTransition(() => {\n dispatch({type: 'split-screen:prerender'})\n })\n }\n padding={2}\n selected={split}\n tooltip=\"Show light and dark side by side\"\n />\n )}\n <TooltipButton\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() =>\n startTransition(() => {\n if (split) addThemerTransitionType('split-screen:close')\n addThemerTransitionType('close')\n dispatch({type: 'close'})\n })\n }\n padding={2}\n tooltip=\"Close themer\"\n />\n </Flex>\n </Flex>\n </Card>\n\n <Activity key=\"list\" mode={view.name === 'list' ? 'visible' : 'hidden'}>\n <ThemeList actorRef={actorRef} active={active} removed={removed} themes={themes} />\n </Activity>\n {view.name === 'edit' && (\n <ThemeEditor\n actorRef={actorRef}\n focusTitle={view.focusTitle}\n slug={view.slug}\n split={split}\n themes={themes}\n />\n )}\n <Activity key=\"removed\" mode={view.name === 'removed' ? 'visible' : 'hidden'}>\n <RemovedThemes actorRef={actorRef} removed={removed} />\n </Activity>\n </Flex>\n </Card>\n )\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","import {createContainer, style} from '@vanilla-extract/css'\n\n/** The sidebar's content, which the content's layout queries — as wide as the sidebar itself */\nexport const sidebarContent = createContainer()\n\nexport const sidebar = style({\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 */\nexport const frame = style({\n position: 'relative',\n})\n\n/**\n * Clips the content, and is the container its layout queries: as wide as\n * the sidebar, whether or not a scrollbar takes room inside — see the theme\n * grid's columns in `ThemeList.css.ts`\n */\nexport const content = style({\n containerType: 'inline-size',\n containerName: sidebarContent,\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 */\nexport const resizeHandle = style({\n 'position': 'absolute',\n 'top': 0,\n 'bottom': 0,\n 'left': -3,\n 'zIndex': 1,\n 'width': 7,\n 'cursor': 'col-resize',\n 'touchAction': 'none',\n '::after': {\n content: '',\n position: 'absolute',\n top: 0,\n bottom: 0,\n left: 2,\n width: 3,\n background: 'var(--card-focus-ring-color)',\n opacity: 0,\n transition: 'opacity 100ms',\n },\n ':focus': {\n outline: 'none',\n },\n 'selectors': {\n \"&:hover::after, &:focus-visible::after, &[data-dragging='true']::after\": {\n opacity: 1,\n },\n },\n})\n","import {BoundaryElementProvider, Box, Card, Layer} from '@sanity/ui'\nimport {startTransition, useCallback, useEffect, useRef, useState} from 'react'\n\nimport type {ThemerProps} from '#types'\n\nimport {MAXIMUM_WIDTH, MINIMUM_WIDTH} from './sidebarWidth'\nimport {ThemerSidebar} from './ThemerSidebar'\n\nimport {content, frame, resizeHandle, sidebar} from './ResizableSidebar.css'\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 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. As an overlay (small screens) it covers the\n * Studio edge to edge instead, with nothing to resize.\n *\n * @internal\n */\nexport default function ResizableSidebar({\n actorRef,\n dispatch,\n navbarHeight,\n split,\n}: Pick<ThemerProps, 'actorRef' | 'dispatch' | 'navbarHeight' | 'split'>) {\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 // @TODO schedule the write to localStorage to avoid blocking the main thread\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 const [boundaryElement, setBoundaryElement] = useState<HTMLDivElement | null>(null)\n\n return (\n <Layer className={sidebar} style={{width}} zOffset={100}>\n <Card\n borderLeft\n className={frame}\n height=\"fill\"\n ref={(boundaryElement) => {\n startTransition(() => setBoundaryElement(boundaryElement))\n // Intentionally not unsetting the element as it would create unnecessary work when <Activity> unhides\n return () => {}\n }}\n >\n <div\n aria-label=\"Resize the themer\"\n className={resizeHandle}\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 className={content} height=\"fill\" overflow=\"hidden\">\n <BoundaryElementProvider element={boundaryElement}>\n <ThemerSidebar\n split={split}\n navbarHeight={navbarHeight}\n actorRef={actorRef}\n dispatch={dispatch}\n />\n </BoundaryElementProvider>\n </Box>\n </Card>\n </Layer>\n )\n}\n"],"mappings":"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"}
|