@sanity/themer 0.8.0 → 0.8.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ResizableSidebar.css.ts.vanilla-M9EuPu-q.js","names":[],"sources":["../src/tool/context.ts","../src/tool/imagePalette.ts","../src/tool/options.ts","../src/tool/themes.ts"],"sourcesContent":["import {createContext, useContext} from 'react'\n\nimport {BuildThemeOptions} from '../theme/options'\nimport {ThemerEvent} from './machine'\nimport {ThemerTheme} from './themes'\n\n/**\n * The flow the sidebar is in: picking a theme from the list, editing one of\n * the user's own themes, or restoring removed ones.\n *\n * @internal\n */\nexport type ThemerView =\n | {name: 'list'}\n | {\n name: 'edit'\n slug: string\n /** Whether the title input should take focus, for themes that were just created */\n focusTitle: boolean\n }\n | {name: 'removed'}\n\n/** @internal */\nexport interface ThemerContextValue {\n /** The theme options the Studio's configured theme was generated from */\n baseOptions: BuildThemeOptions\n /** The themes to pick from, in list order */\n themes: ThemerTheme[]\n /** The removed themes, which can be restored */\n removed: ThemerTheme[]\n /** The applied theme */\n active: ThemerTheme\n /** Object URLs of the images themes took their palette from this session, by slug */\n images: Record<string, string>\n view: ThemerView\n /** Whether the themer sidebar is open */\n open: boolean\n /** Whether the sidebar is on its way to opening for the first time, pending while its code loads */\n loading: boolean\n /** Whether the Studio shows twice side by side, in light and dark */\n split: boolean\n /** Whether the Studio is on a small screen, where the sidebar covers it and the split preview stacks */\n mobile: boolean\n /** The height of the Studio navbar, which the sidebar's header matches — `null` until it has rendered */\n navbarHeight: number | null\n /** Sends an event to the themer machine */\n send: (event: ThemerEvent) => void\n}\n\n/** @internal */\nexport const ThemerContext = createContext<ThemerContextValue | null>(null)\n\n/** @internal */\nexport function useThemer(): ThemerContextValue {\n const context = useContext(ThemerContext)\n\n if (!context) {\n throw new Error('useThemer must be used within the `themerTool` plugin')\n }\n\n return context\n}\n","import {type ThemeColorSchemeKey} from '@sanity/ui/theme'\n\nimport {hexToHsl, hslToHex, relativeLuminance} from '../theme/hsl'\nimport {BuildThemeOptions, DEFAULT_BACKGROUND, SCHEMES, SchemeThemeOptions} from '../theme/options'\n\n/**\n * The swatches of an image palette — the same set the Sanity image pipeline\n * derives for uploaded assets (`metadata.palette`), extracted on device here.\n *\n * @internal\n */\nexport type ImagePaletteKey =\n | 'dominant'\n | 'vibrant'\n | 'lightVibrant'\n | 'darkVibrant'\n | 'muted'\n | 'lightMuted'\n | 'darkMuted'\n\n/**\n * An image palette: a hex color per swatch, or `null` when the image has no\n * color that qualifies for it.\n *\n * @internal\n */\nexport type ImagePalette = Record<ImagePaletteKey, string | null>\n\n/** @internal */\nexport const IMAGE_PALETTE_KEYS: ImagePaletteKey[] = [\n 'vibrant',\n 'lightVibrant',\n 'darkVibrant',\n 'muted',\n 'lightMuted',\n 'darkMuted',\n 'dominant',\n]\n\n/**\n * The swatches a theme can be built around — every swatch but the dominant\n * one, which only repeats one of them.\n *\n * @internal\n */\nexport type ImagePaletteVariant = Exclude<ImagePaletteKey, 'dominant'>\n\n/** The variants in the order the tool offers them @internal */\nexport const IMAGE_PALETTE_VARIANTS: ImagePaletteVariant[] = [\n 'muted',\n 'vibrant',\n 'lightMuted',\n 'lightVibrant',\n 'darkMuted',\n 'darkVibrant',\n]\n\n/** @internal */\nexport const IMAGE_PALETTE_TITLES: Record<ImagePaletteKey, string> = {\n dominant: 'Dominant',\n vibrant: 'Vibrant',\n lightVibrant: 'Light vibrant',\n darkVibrant: 'Dark vibrant',\n muted: 'Muted',\n lightMuted: 'Light muted',\n darkMuted: 'Dark muted',\n}\n\n/** Pixels the way a canvas hands them out: rows of RGBA bytes @internal */\nexport interface PixelData {\n data: ArrayLike<number>\n width: number\n height: number\n}\n\ninterface Bucket {\n count: number\n r: number\n g: number\n b: number\n}\n\ninterface Candidate {\n hex: string\n population: number\n saturation: number\n lightness: number\n}\n\n/** What a swatch looks for: a lightness and saturation to aim at, within bounds */\ninterface Target {\n lightness: [target: number, min: number, max: number]\n saturation: [target: number, min: number, max: number]\n}\n\n/**\n * The swatch targets of the Vibrant algorithm (the one behind Sanity's image\n * palettes), in the order they get to claim colors.\n */\nconst TARGETS: Array<[key: Exclude<ImagePaletteKey, 'dominant'>, target: Target]> = [\n ['vibrant', {lightness: [0.5, 0.3, 0.7], saturation: [1, 0.35, 1]}],\n ['lightVibrant', {lightness: [0.74, 0.55, 1], saturation: [1, 0.35, 1]}],\n ['darkVibrant', {lightness: [0.26, 0, 0.45], saturation: [1, 0.35, 1]}],\n ['muted', {lightness: [0.5, 0.3, 0.7], saturation: [0.3, 0, 0.4]}],\n ['lightMuted', {lightness: [0.74, 0.55, 1], saturation: [0.3, 0, 0.4]}],\n ['darkMuted', {lightness: [0.26, 0, 0.45], saturation: [0.3, 0, 0.4]}],\n]\n\nconst SATURATION_WEIGHT = 3\nconst LIGHTNESS_WEIGHT = 6.5\nconst POPULATION_WEIGHT = 0.5\n\n/** Pixels this transparent are not part of the picture */\nconst MINIMUM_ALPHA = 125\n\n/** Near-black and near-white pixels are backdrop rather than color */\nconst MINIMUM_LIGHTNESS = 0.05\nconst MAXIMUM_LIGHTNESS = 0.95\n\n/** How many of the 8 bits per channel survive the quantization */\nconst QUANTIZE_BITS = 5\n\n/**\n * Extracts the color palette of an image from its pixels, the way the Vibrant\n * algorithm does: the colors are quantized into a coarse histogram, and each\n * swatch claims the color that comes closest to its target lightness and\n * saturation — weighted by how much of the image it covers — that no other\n * swatch has claimed yet. The dominant swatch is simply the most common\n * color. Runs anywhere pixels come from, so it can run on device.\n *\n * @internal\n */\nexport function extractImagePalette(pixels: PixelData): ImagePalette {\n const candidates = quantize(pixels)\n const palette: ImagePalette = {\n dominant: null,\n vibrant: null,\n lightVibrant: null,\n darkVibrant: null,\n muted: null,\n lightMuted: null,\n darkMuted: null,\n }\n\n if (candidates.length === 0) return palette\n\n const maximumPopulation = Math.max(...candidates.map((candidate) => candidate.population))\n const claimed = new Set<string>()\n\n for (const [key, target] of TARGETS) {\n let best: Candidate | null = null\n let bestScore = -Infinity\n\n for (const candidate of candidates) {\n if (claimed.has(candidate.hex) || !matchesTarget(candidate, target)) continue\n\n const score = scoreCandidate(candidate, target, maximumPopulation)\n\n if (score > bestScore) {\n best = candidate\n bestScore = score\n }\n }\n\n if (best) {\n palette[key] = best.hex\n claimed.add(best.hex)\n }\n }\n\n palette.dominant = candidates.reduce((most, candidate) =>\n candidate.population > most.population ? candidate : most,\n ).hex\n\n return palette\n}\n\n/**\n * Turns the pixels into a histogram of quantized colors, each averaged over\n * the pixels it stands for, leaving out transparent pixels and the near-black\n * and near-white ones that are backdrop rather than color.\n */\nfunction quantize(pixels: PixelData): Candidate[] {\n const {data} = pixels\n const buckets = new Map<number, Bucket>()\n const shift = 8 - QUANTIZE_BITS\n const length = Math.min(data.length, pixels.width * pixels.height * 4)\n\n for (let offset = 0; offset + 3 < length; offset += 4) {\n const r = data[offset]\n const g = data[offset + 1]\n const b = data[offset + 2]\n const a = data[offset + 3]\n\n if (a < MINIMUM_ALPHA) continue\n\n const lightness = (Math.max(r, g, b) + Math.min(r, g, b)) / 510\n\n if (lightness < MINIMUM_LIGHTNESS || lightness > MAXIMUM_LIGHTNESS) continue\n\n const key =\n ((r >> shift) << (QUANTIZE_BITS * 2)) | ((g >> shift) << QUANTIZE_BITS) | (b >> shift)\n const bucket = buckets.get(key)\n\n if (bucket) {\n bucket.count++\n bucket.r += r\n bucket.g += g\n bucket.b += b\n } else {\n buckets.set(key, {count: 1, r, g, b})\n }\n }\n\n const candidates: Candidate[] = []\n\n for (const bucket of buckets.values()) {\n const r = Math.round(bucket.r / bucket.count)\n const g = Math.round(bucket.g / bucket.count)\n const b = Math.round(bucket.b / bucket.count)\n const {saturation, lightness} = saturationAndLightness(r, g, b)\n\n candidates.push({hex: rgbToHex(r, g, b), population: bucket.count, saturation, lightness})\n }\n\n return candidates\n}\n\nfunction matchesTarget(candidate: Candidate, target: Target): boolean {\n return (\n candidate.lightness >= target.lightness[1] &&\n candidate.lightness <= target.lightness[2] &&\n candidate.saturation >= target.saturation[1] &&\n candidate.saturation <= target.saturation[2]\n )\n}\n\nfunction scoreCandidate(candidate: Candidate, target: Target, maximumPopulation: number): number {\n return (\n SATURATION_WEIGHT * (1 - Math.abs(candidate.saturation - target.saturation[0])) +\n LIGHTNESS_WEIGHT * (1 - Math.abs(candidate.lightness - target.lightness[0])) +\n POPULATION_WEIGHT * (candidate.population / maximumPopulation)\n )\n}\n\nfunction saturationAndLightness(r: number, g: number, b: number) {\n const max = Math.max(r, g, b) / 255\n const min = Math.min(r, g, b) / 255\n const lightness = (max + min) / 2\n const delta = max - min\n const saturation =\n delta === 0 ? 0 : lightness > 0.5 ? delta / (2 - max - min) : delta / (max + min)\n\n return {saturation, lightness}\n}\n\nfunction rgbToHex(r: number, g: number, b: number): string {\n return `#${[r, g, b].map((channel) => channel.toString(16).padStart(2, '0')).join('')}`\n}\n\n/** Reads `#rgb` and `#rrggbb` alike — stored palettes may hold either */\nfunction hexToRgb(hex: string): [number, number, number] {\n const expanded =\n hex.length === 4 ? `${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex.slice(1)\n\n return [\n parseInt(expanded.slice(0, 2), 16),\n parseInt(expanded.slice(2, 4), 16),\n parseInt(expanded.slice(4, 6), 16),\n ]\n}\n\n/** Mixes `amount` of `color` into `base` */\nfunction tint(base: string, color: string, amount: number): string {\n const [r1, g1, b1] = hexToRgb(base)\n const [r2, g2, b2] = hexToRgb(color)\n\n return rgbToHex(\n Math.round(r1 + (r2 - r1) * amount),\n Math.round(g1 + (g2 - g1) * amount),\n Math.round(b1 + (b2 - b1) * amount),\n )\n}\n\n/** How much of the palette tints each scheme's stock background */\nconst BACKGROUND_TINT = {light: 0.08, dark: 0.3}\n\n/**\n * The WCAG AA contrast for normal text — what a button label needs against\n * the button. Sanity UI labels solid buttons in white on the accent's 500\n * tint in the light scheme and in black on its 400 tint in the dark scheme\n * (lighter than the accent, so an accent that passes against black passes\n * there too).\n */\nconst MINIMUM_ACCENT_CONTRAST = 4.5\nconst BUTTON_LABEL: Record<ThemeColorSchemeKey, string> = {light: '#ffffff', dark: '#000000'}\nconst ACCENT_LIGHTNESS_STEP = 0.01\n\nfunction contrastRatio(a: string, b: string): number {\n const [lighter, darker] = [relativeLuminance(a), relativeLuminance(b)].sort((x, y) => y - x)\n\n return (lighter + 0.05) / (darker + 0.05)\n}\n\n/**\n * Makes an accent readable as a button in the given scheme: a light scheme\n * accent is darkened until white text on it reaches AA contrast, a dark\n * scheme accent lightened until black text does — as far as the accent's\n * hue and saturation allow. Accents that already pass come back untouched.\n *\n * @internal\n */\nexport function readableAccent(hex: string, scheme: ThemeColorSchemeKey): string {\n const label = BUTTON_LABEL[scheme]\n const {h, s} = hexToHsl(hex)\n let {l} = hexToHsl(hex)\n let current = hex\n\n while (contrastRatio(current, label) < MINIMUM_ACCENT_CONTRAST) {\n const next = scheme === 'light' ? l - ACCENT_LIGHTNESS_STEP : l + ACCENT_LIGHTNESS_STEP\n\n if (next < 0 || next > 1) break\n\n l = next\n current = hslToHex({h, s, l})\n }\n\n return current\n}\n\n/** The accent a variant gives a scheme, made readable on a button */\nfunction variantAccent(\n palette: ImagePalette,\n variant: ImagePaletteVariant | null,\n scheme: ThemeColorSchemeKey,\n): string | null {\n const swatch =\n (variant ? palette[variant] : null) ??\n palette.vibrant ??\n (scheme === 'light' ? palette.darkVibrant : palette.lightVibrant) ??\n palette.dominant\n\n return swatch === null ? null : readableAccent(swatch, scheme)\n}\n\n/**\n * Derives theme options from an image palette: the accent of both schemes is\n * the swatch of the chosen variant — or, without one, the vibrant swatch,\n * each scheme falling back to the vibrant swatch of its own end of the scale —\n * adjusted until it is readable as a button. The muted swatch becomes the text color, and the light and dark muted\n * swatches tint the stock backgrounds of their scheme — enough to carry the\n * image's tone without giving up a usable canvas. Whatever the palette lacks\n * stays at the theme defaults.\n *\n * @internal\n */\nexport function optionsFromImagePalette(\n palette: ImagePalette,\n variant?: ImagePaletteVariant,\n): BuildThemeOptions {\n const options: BuildThemeOptions = {}\n\n const light = schemeFromPalette(\n variantAccent(palette, variant ?? null, 'light'),\n palette.muted ?? palette.darkMuted,\n palette.lightMuted ?? palette.muted ?? palette.lightVibrant,\n 'light',\n )\n const dark = schemeFromPalette(\n variantAccent(palette, variant ?? null, 'dark'),\n palette.muted ?? palette.lightMuted,\n palette.darkMuted ?? palette.muted ?? palette.darkVibrant,\n 'dark',\n )\n\n if (light) options.light = light\n if (dark) options.dark = dark\n\n return options\n}\n\nfunction schemeFromPalette(\n accent: string | null,\n text: string | null,\n backgroundTint: string | null,\n scheme: 'light' | 'dark',\n): SchemeThemeOptions | null {\n const options: SchemeThemeOptions = {}\n\n if (accent) options.accent = accent\n if (text) options.text = text\n if (backgroundTint) {\n options.background = tint(DEFAULT_BACKGROUND[scheme], backgroundTint, BACKGROUND_TINT[scheme])\n }\n\n return Object.keys(options).length > 0 ? options : null\n}\n\n/**\n * Applies an image palette to existing theme options: the colors the palette\n * provides replace the current ones scheme by scheme, and whatever it does\n * not touch — like the contrast — stays as it is.\n *\n * @internal\n */\nexport function applyImagePalette(\n options: BuildThemeOptions,\n palette: ImagePalette,\n variant?: ImagePaletteVariant,\n): BuildThemeOptions {\n const derived = optionsFromImagePalette(palette, variant)\n const next: BuildThemeOptions = {...options}\n\n for (const scheme of SCHEMES) {\n if (derived[scheme]) next[scheme] = {...options[scheme], ...derived[scheme]}\n }\n\n return next\n}\n\n/**\n * The variant a theme is built around: the one whose swatch is the theme's\n * accent, or `null` when the accent comes from somewhere else.\n *\n * @internal\n */\nexport function currentImageVariant(\n options: BuildThemeOptions,\n palette: ImagePalette,\n): ImagePaletteVariant | null {\n const accent = options.light?.accent\n\n if (!accent) return null\n\n return (\n IMAGE_PALETTE_VARIANTS.find(\n (variant) => palette[variant] !== null && variantAccent(palette, variant, 'light') === accent,\n ) ?? null\n )\n}\n\n/**\n * How much a swatch has going for it as an accent: saturation first, then\n * closeness to mid lightness — the range where an accent reads best.\n */\nfunction interest(hex: string): number {\n const {s, l} = hexToHsl(hex)\n\n return 0.1 + 0.55 * s + 0.35 * (1 - Math.abs(l - 0.5) * 2)\n}\n\n/**\n * Picks a variant for the \"I'm feeling lucky\" button: a random one, weighted\n * by how interesting its swatch is as an accent, and never the one the theme\n * already uses while there are others to pick from.\n *\n * @internal\n */\nexport function pickLuckyVariant(\n palette: ImagePalette,\n options: {exclude?: ImagePaletteVariant | null; random?: () => number} = {},\n): ImagePaletteVariant | null {\n const {exclude = null, random = Math.random} = options\n let candidates = IMAGE_PALETTE_VARIANTS.filter((variant) => palette[variant] !== null)\n\n if (candidates.length > 1) {\n candidates = candidates.filter((variant) => variant !== exclude)\n }\n\n if (candidates.length === 0) return null\n\n const weights = candidates.map((variant) => interest(palette[variant]!))\n const total = weights.reduce((sum, weight) => sum + weight, 0)\n let pick = random() * total\n\n for (const [index, variant] of candidates.entries()) {\n pick -= weights[index]\n\n if (pick < 0) return variant\n }\n\n return candidates.at(-1) ?? null\n}\n\n/** A theme title for the image a palette came from, e.g. `sunset-beach.jpg` → `sunset beach` @internal */\nexport function titleFromFileName(name: string): string {\n return (\n name\n .replace(/\\.[^.]+$/, '')\n .replace(/[-_]+/g, ' ')\n .trim() || 'Image theme'\n )\n}\n","import {\n BuildThemeOptions,\n DEFAULT_ACCENT,\n DEFAULT_BACKGROUND,\n DEFAULT_CONTRAST,\n deriveTextColor,\n ResolvedSchemeOptions,\n resolveThemeOptions,\n SCHEMES,\n SchemeThemeOptions,\n} from '../theme/options'\n\n/**\n * Reduces theme options to the minimal object that recreates them through\n * `buildTheme`: in each scheme the accent is dropped when it is the stock\n * one, the text color when it matches the one derived from the accent, and\n * the background and contrast when they match the defaults — and a scheme\n * with nothing left is dropped altogether. Options that boil down to the\n * stock Studio theme reduce to `null` — they need nothing from this package.\n *\n * @internal\n */\nexport function minimizeOptions(options: BuildThemeOptions): BuildThemeOptions | null {\n const resolved = resolveThemeOptions(options)\n const minimized: BuildThemeOptions = {}\n\n for (const scheme of SCHEMES) {\n const values = resolved[scheme]\n const kept: SchemeThemeOptions = {}\n\n if (values.accent !== DEFAULT_ACCENT) kept.accent = values.accent\n if (values.text !== deriveTextColor(values.accent)) kept.text = values.text\n if (values.background !== DEFAULT_BACKGROUND[scheme]) kept.background = values.background\n if (values.contrast !== DEFAULT_CONTRAST) kept.contrast = values.contrast\n\n if (Object.keys(kept).length > 0) minimized[scheme] = kept\n }\n\n return Object.keys(minimized).length === 0 ? null : minimized\n}\n\n/**\n * Whether two sets of theme options generate the same theme, comparing their\n * resolved forms so that derived and explicit values (and hex casing) don't\n * matter.\n *\n * @internal\n */\nexport function sameOptions(a: BuildThemeOptions, b: BuildThemeOptions): boolean {\n const left = resolveThemeOptions(a)\n const right = resolveThemeOptions(b)\n\n return SCHEMES.every((scheme) => sameScheme(left[scheme], right[scheme]))\n}\n\nfunction sameScheme(a: ResolvedSchemeOptions, b: ResolvedSchemeOptions): boolean {\n return (\n a.accent === b.accent &&\n a.text === b.text &&\n a.background === b.background &&\n a.contrast === b.contrast\n )\n}\n","import {BuildThemeOptions} from '../theme/options'\nimport {presets} from '../theme/presets'\nimport {ImagePalette} from './imagePalette'\nimport {sameOptions} from './options'\n\n/**\n * Where a theme in the themer list comes from: the theme the Studio config\n * was generated from, one of the presets defined in code, or one the user\n * added in the tool. Only custom themes are editable — the others are\n * duplicated into a custom theme first.\n */\ntype ThemerThemeSource = 'config' | 'preset' | 'custom'\n\n/** A theme the themer tool lists @internal */\nexport interface ThemerTheme {\n slug: string\n title: string\n options: BuildThemeOptions\n source: ThemerThemeSource\n /** The palette of the image the theme's colors were taken from, for custom themes */\n palette?: ImagePalette\n}\n\n/** A theme the user added in the tool, as persisted @internal */\nexport interface CustomTheme {\n slug: string\n title: string\n options: BuildThemeOptions\n /** The palette of the image the theme's colors were taken from */\n palette?: ImagePalette\n}\n\n/** The persisted themer state @internal */\nexport interface ThemerState {\n /**\n * The slug of the applied theme — `null` applies nothing on top of the\n * Studio's configured theme\n */\n active: string | null\n /** The themes the user added */\n custom: CustomTheme[]\n /** The slugs of the themes the user removed from the list */\n removed: string[]\n /**\n * The slugs of the themes in the order the user arranged them — themes not\n * in it (new presets, themes added since) follow in their default order\n */\n order: string[]\n}\n\n/** The slug of the theme the Studio config was generated from @internal */\nexport const CONFIG_SLUG = 'config'\n\n/** The title of the theme the Studio config was generated from */\nconst CONFIG_TITLE = 'Studio config'\n\n/** The title new themes start out with @internal */\nexport const UNTITLED_THEME = 'Untitled theme'\n\n/** @internal */\nexport const initialThemerState: ThemerState = {active: null, custom: [], removed: [], order: []}\n\n/** The themes the tool works with, derived from the persisted state @internal */\nexport interface ResolvedThemes {\n /** The themes to pick from, in list order */\n themes: ThemerTheme[]\n /** The removed themes, which can be restored */\n removed: ThemerTheme[]\n /** The applied theme */\n active: ThemerTheme\n}\n\n/**\n * Resolves the list of themes: the configured theme first, then the presets\n * (minus the ones that would only repeat the configured theme), then the\n * user's own themes — rearranged into the order the user dragged them into,\n * with themes that order does not know about kept in that default order after\n * the ones it does. Removed themes are set aside so they can be restored, and\n * the active theme falls back to the configured one when its slug no longer\n * resolves.\n *\n * @internal\n */\nexport function resolveThemes(state: ThemerState, baseOptions: BuildThemeOptions): ResolvedThemes {\n const config: ThemerTheme = {\n slug: CONFIG_SLUG,\n title: CONFIG_TITLE,\n options: baseOptions,\n source: 'config',\n }\n\n const all: ThemerTheme[] = [config]\n\n for (const preset of presets) {\n if (!sameOptions(preset.options, baseOptions)) {\n all.push({slug: preset.slug, title: preset.title, options: preset.options, source: 'preset'})\n }\n }\n\n for (const theme of state.custom) {\n all.push({\n slug: theme.slug,\n title: theme.title,\n options: theme.options,\n source: 'custom',\n ...(theme.palette ? {palette: theme.palette} : {}),\n })\n }\n\n const position = new Map(state.order.map((slug, index) => [slug, index]))\n const rank = (theme: ThemerTheme) => position.get(theme.slug) ?? state.order.length\n\n // A stable sort: the themes the order does not know about keep their default order\n all.sort((a, b) => rank(a) - rank(b))\n\n const removedSlugs = new Set(state.removed)\n const themes = all.filter((theme) => !removedSlugs.has(theme.slug))\n const removed = all.filter((theme) => removedSlugs.has(theme.slug))\n const active = themes.find((theme) => theme.slug === state.active) ?? config\n\n return {themes, removed, active}\n}\n\n/**\n * Creates a custom theme with a slug that cannot collide with the presets or\n * the configured theme.\n *\n * @internal\n */\nexport function createCustomTheme(\n title: string,\n options: BuildThemeOptions,\n palette?: ImagePalette,\n): CustomTheme {\n return {slug: `custom-${randomId()}`, title, options, ...(palette ? {palette} : {})}\n}\n\n/** The title of a duplicated theme @internal */\nexport function duplicateTitle(title: string): string {\n return `${displayTitle(title)} copy`\n}\n\n/** The title to show for a theme, falling back when the user cleared it @internal */\nexport function displayTitle(title: string): string {\n return title.trim() || UNTITLED_THEME\n}\n\nfunction randomId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n\n return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ResizableSidebar.css.ts.vanilla-M9EuPu-q.js","names":[],"sources":["../src/tool/context.ts","../src/tool/imagePalette.ts","../src/tool/options.ts","../src/tool/themes.ts"],"sourcesContent":["import {createContext, useContext} from 'react'\n\nimport type {BuildThemeOptions} from '../theme/options'\nimport type {ThemerEvent} from './machine'\nimport type {ThemerTheme} from './themes'\n\n/**\n * The flow the sidebar is in: picking a theme from the list, editing one of\n * the user's own themes, or restoring removed ones.\n *\n * @internal\n */\nexport type ThemerView =\n | {name: 'list'}\n | {\n name: 'edit'\n slug: string\n /** Whether the title input should take focus, for themes that were just created */\n focusTitle: boolean\n }\n | {name: 'removed'}\n\n/** @internal */\nexport interface ThemerContextValue {\n /** The theme options the Studio's configured theme was generated from */\n baseOptions: BuildThemeOptions\n /** The themes to pick from, in list order */\n themes: ThemerTheme[]\n /** The removed themes, which can be restored */\n removed: ThemerTheme[]\n /** The applied theme */\n active: ThemerTheme\n /** Object URLs of the images themes took their palette from this session, by slug */\n images: Record<string, string>\n view: ThemerView\n /** Whether the themer sidebar is open */\n open: boolean\n /** Whether the sidebar is on its way to opening for the first time, pending while its code loads */\n loading: boolean\n /** Whether the Studio shows twice side by side, in light and dark */\n split: boolean\n /** Whether the Studio is on a small screen, where the sidebar covers it and the split preview stacks */\n mobile: boolean\n /** The height of the Studio navbar, which the sidebar's header matches — `null` until it has rendered */\n navbarHeight: number | null\n /** Sends an event to the themer machine */\n send: (event: ThemerEvent) => void\n}\n\n/** @internal */\nexport const ThemerContext = createContext<ThemerContextValue | null>(null)\n\n/** @internal */\nexport function useThemer(): ThemerContextValue {\n const context = useContext(ThemerContext)\n\n if (!context) {\n throw new Error('useThemer must be used within the `themerTool` plugin')\n }\n\n return context\n}\n","import {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/** @internal */\nexport const IMAGE_PALETTE_KEYS: ImagePaletteKey[] = [\n 'vibrant',\n 'lightVibrant',\n 'darkVibrant',\n 'muted',\n 'lightMuted',\n 'darkMuted',\n 'dominant',\n]\n\n/**\n * The swatches a theme can be built around — every swatch but the dominant\n * one, which only repeats one of them.\n *\n * @internal\n */\nexport type ImagePaletteVariant = Exclude<ImagePaletteKey, 'dominant'>\n\n/** The variants in the order the tool offers them @internal */\nexport const IMAGE_PALETTE_VARIANTS: ImagePaletteVariant[] = [\n 'muted',\n 'vibrant',\n 'lightMuted',\n 'lightVibrant',\n 'darkMuted',\n 'darkVibrant',\n]\n\n/** @internal */\nexport const IMAGE_PALETTE_TITLES: Record<ImagePaletteKey, string> = {\n dominant: 'Dominant',\n vibrant: 'Vibrant',\n lightVibrant: 'Light vibrant',\n darkVibrant: 'Dark vibrant',\n muted: 'Muted',\n lightMuted: 'Light muted',\n darkMuted: 'Dark muted',\n}\n\n/** Pixels the way a canvas hands them out: rows of RGBA bytes @internal */\nexport interface PixelData {\n data: ArrayLike<number>\n width: number\n height: number\n}\n\ninterface Bucket {\n count: number\n r: number\n g: number\n b: number\n}\n\ninterface Candidate {\n hex: string\n population: number\n saturation: number\n lightness: number\n}\n\n/** What a swatch looks for: a lightness and saturation to aim at, within bounds */\ninterface Target {\n lightness: [target: number, min: number, max: number]\n saturation: [target: number, min: number, max: number]\n}\n\n/**\n * The swatch targets of the Vibrant algorithm (the one behind Sanity's image\n * palettes), in the order they get to claim colors.\n */\nconst TARGETS: Array<[key: Exclude<ImagePaletteKey, 'dominant'>, target: Target]> = [\n ['vibrant', {lightness: [0.5, 0.3, 0.7], saturation: [1, 0.35, 1]}],\n ['lightVibrant', {lightness: [0.74, 0.55, 1], saturation: [1, 0.35, 1]}],\n ['darkVibrant', {lightness: [0.26, 0, 0.45], saturation: [1, 0.35, 1]}],\n ['muted', {lightness: [0.5, 0.3, 0.7], saturation: [0.3, 0, 0.4]}],\n ['lightMuted', {lightness: [0.74, 0.55, 1], saturation: [0.3, 0, 0.4]}],\n ['darkMuted', {lightness: [0.26, 0, 0.45], saturation: [0.3, 0, 0.4]}],\n]\n\nconst SATURATION_WEIGHT = 3\nconst LIGHTNESS_WEIGHT = 6.5\nconst POPULATION_WEIGHT = 0.5\n\n/** Pixels this transparent are not part of the picture */\nconst MINIMUM_ALPHA = 125\n\n/** Near-black and near-white pixels are backdrop rather than color */\nconst MINIMUM_LIGHTNESS = 0.05\nconst MAXIMUM_LIGHTNESS = 0.95\n\n/** How many of the 8 bits per channel survive the quantization */\nconst QUANTIZE_BITS = 5\n\n/**\n * Extracts the color palette of an image from its pixels, the way the Vibrant\n * algorithm does: the colors are quantized into a coarse histogram, and each\n * swatch claims the color that comes closest to its target lightness and\n * saturation — weighted by how much of the image it covers — that no other\n * swatch has claimed yet. The dominant swatch is simply the most common\n * color. Runs anywhere pixels come from, so it can run on device.\n *\n * @internal\n */\nexport function extractImagePalette(pixels: PixelData): ImagePalette {\n const candidates = quantize(pixels)\n const palette: ImagePalette = {\n dominant: null,\n vibrant: null,\n lightVibrant: null,\n darkVibrant: null,\n muted: null,\n lightMuted: null,\n darkMuted: null,\n }\n\n if (candidates.length === 0) return palette\n\n const maximumPopulation = Math.max(...candidates.map((candidate) => candidate.population))\n const claimed = new Set<string>()\n\n for (const [key, target] of TARGETS) {\n let best: Candidate | null = null\n let bestScore = -Infinity\n\n for (const candidate of candidates) {\n if (claimed.has(candidate.hex) || !matchesTarget(candidate, target)) continue\n\n const score = scoreCandidate(candidate, target, maximumPopulation)\n\n if (score > bestScore) {\n best = candidate\n bestScore = score\n }\n }\n\n if (best) {\n palette[key] = best.hex\n claimed.add(best.hex)\n }\n }\n\n palette.dominant = candidates.reduce((most, candidate) =>\n candidate.population > most.population ? candidate : most,\n ).hex\n\n return palette\n}\n\n/**\n * Turns the pixels into a histogram of quantized colors, each averaged over\n * the pixels it stands for, leaving out transparent pixels and the near-black\n * and near-white ones that are backdrop rather than color.\n */\nfunction quantize(pixels: PixelData): Candidate[] {\n const {data} = pixels\n const buckets = new Map<number, Bucket>()\n const shift = 8 - QUANTIZE_BITS\n const length = Math.min(data.length, pixels.width * pixels.height * 4)\n\n for (let offset = 0; offset + 3 < length; offset += 4) {\n const r = data[offset]\n const g = data[offset + 1]\n const b = data[offset + 2]\n const a = data[offset + 3]\n\n if (a < MINIMUM_ALPHA) continue\n\n const lightness = (Math.max(r, g, b) + Math.min(r, g, b)) / 510\n\n if (lightness < MINIMUM_LIGHTNESS || lightness > MAXIMUM_LIGHTNESS) continue\n\n const key =\n ((r >> shift) << (QUANTIZE_BITS * 2)) | ((g >> shift) << QUANTIZE_BITS) | (b >> shift)\n const bucket = buckets.get(key)\n\n if (bucket) {\n bucket.count++\n bucket.r += r\n bucket.g += g\n bucket.b += b\n } else {\n buckets.set(key, {count: 1, r, g, b})\n }\n }\n\n const candidates: Candidate[] = []\n\n for (const bucket of buckets.values()) {\n const r = Math.round(bucket.r / bucket.count)\n const g = Math.round(bucket.g / bucket.count)\n const b = Math.round(bucket.b / bucket.count)\n const {saturation, lightness} = saturationAndLightness(r, g, b)\n\n candidates.push({hex: rgbToHex(r, g, b), population: bucket.count, saturation, lightness})\n }\n\n return candidates\n}\n\nfunction matchesTarget(candidate: Candidate, target: Target): boolean {\n return (\n candidate.lightness >= target.lightness[1] &&\n candidate.lightness <= target.lightness[2] &&\n candidate.saturation >= target.saturation[1] &&\n candidate.saturation <= target.saturation[2]\n )\n}\n\nfunction scoreCandidate(candidate: Candidate, target: Target, maximumPopulation: number): number {\n return (\n SATURATION_WEIGHT * (1 - Math.abs(candidate.saturation - target.saturation[0])) +\n LIGHTNESS_WEIGHT * (1 - Math.abs(candidate.lightness - target.lightness[0])) +\n POPULATION_WEIGHT * (candidate.population / maximumPopulation)\n )\n}\n\nfunction saturationAndLightness(r: number, g: number, b: number) {\n const max = Math.max(r, g, b) / 255\n const min = Math.min(r, g, b) / 255\n const lightness = (max + min) / 2\n const delta = max - min\n const saturation =\n delta === 0 ? 0 : lightness > 0.5 ? delta / (2 - max - min) : delta / (max + min)\n\n return {saturation, lightness}\n}\n\nfunction rgbToHex(r: number, g: number, b: number): string {\n return `#${[r, g, b].map((channel) => channel.toString(16).padStart(2, '0')).join('')}`\n}\n\n/** Reads `#rgb` and `#rrggbb` alike — stored palettes may hold either */\nfunction hexToRgb(hex: string): [number, number, number] {\n const expanded =\n hex.length === 4 ? `${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex.slice(1)\n\n return [\n parseInt(expanded.slice(0, 2), 16),\n parseInt(expanded.slice(2, 4), 16),\n parseInt(expanded.slice(4, 6), 16),\n ]\n}\n\n/** Mixes `amount` of `color` into `base` */\nfunction tint(base: string, color: string, amount: number): string {\n const [r1, g1, b1] = hexToRgb(base)\n const [r2, g2, b2] = hexToRgb(color)\n\n return rgbToHex(\n Math.round(r1 + (r2 - r1) * amount),\n Math.round(g1 + (g2 - g1) * amount),\n Math.round(b1 + (b2 - b1) * amount),\n )\n}\n\n/** How much of the palette tints each scheme's stock background */\nconst BACKGROUND_TINT = {light: 0.08, dark: 0.3}\n\n/**\n * The WCAG AA contrast for normal text — what a button label needs against\n * the button. Sanity UI labels solid buttons in white on the accent's 500\n * tint in the light scheme and in black on its 400 tint in the dark scheme\n * (lighter than the accent, so an accent that passes against black passes\n * there too).\n */\nconst MINIMUM_ACCENT_CONTRAST = 4.5\nconst BUTTON_LABEL: Record<ThemeColorSchemeKey, string> = {light: '#ffffff', dark: '#000000'}\nconst ACCENT_LIGHTNESS_STEP = 0.01\n\nfunction contrastRatio(a: string, b: string): number {\n const [lighter, darker] = [relativeLuminance(a), relativeLuminance(b)].sort((x, y) => y - x)\n\n return (lighter + 0.05) / (darker + 0.05)\n}\n\n/**\n * Makes an accent readable as a button in the given scheme: a light scheme\n * accent is darkened until white text on it reaches AA contrast, a dark\n * scheme accent lightened until black text does — as far as the accent's\n * hue and saturation allow. Accents that already pass come back untouched.\n *\n * @internal\n */\nexport function readableAccent(hex: string, scheme: ThemeColorSchemeKey): string {\n const label = BUTTON_LABEL[scheme]\n const {h, s} = hexToHsl(hex)\n let {l} = hexToHsl(hex)\n let current = hex\n\n while (contrastRatio(current, label) < MINIMUM_ACCENT_CONTRAST) {\n const next = scheme === 'light' ? l - ACCENT_LIGHTNESS_STEP : l + ACCENT_LIGHTNESS_STEP\n\n if (next < 0 || next > 1) break\n\n l = next\n current = hslToHex({h, s, l})\n }\n\n return current\n}\n\n/** The accent a variant gives a scheme, made readable on a button */\nfunction variantAccent(\n palette: ImagePalette,\n variant: ImagePaletteVariant | null,\n scheme: ThemeColorSchemeKey,\n): string | null {\n const swatch =\n (variant ? palette[variant] : null) ??\n palette.vibrant ??\n (scheme === 'light' ? palette.darkVibrant : palette.lightVibrant) ??\n palette.dominant\n\n return swatch === null ? null : readableAccent(swatch, scheme)\n}\n\n/**\n * Derives theme options from an image palette: the accent of both schemes is\n * the swatch of the chosen variant — or, without one, the vibrant swatch,\n * each scheme falling back to the vibrant swatch of its own end of the scale —\n * adjusted until it is readable as a button. The muted swatch becomes the text color, and the light and dark muted\n * swatches tint the stock backgrounds of their scheme — enough to carry the\n * image's tone without giving up a usable canvas. Whatever the palette lacks\n * stays at the theme defaults.\n *\n * @internal\n */\nexport function optionsFromImagePalette(\n palette: ImagePalette,\n variant?: ImagePaletteVariant,\n): BuildThemeOptions {\n const options: BuildThemeOptions = {}\n\n const light = schemeFromPalette(\n variantAccent(palette, variant ?? null, 'light'),\n palette.muted ?? palette.darkMuted,\n palette.lightMuted ?? palette.muted ?? palette.lightVibrant,\n 'light',\n )\n const dark = schemeFromPalette(\n variantAccent(palette, variant ?? null, 'dark'),\n palette.muted ?? palette.lightMuted,\n palette.darkMuted ?? palette.muted ?? palette.darkVibrant,\n 'dark',\n )\n\n if (light) options.light = light\n if (dark) options.dark = dark\n\n return options\n}\n\nfunction schemeFromPalette(\n accent: string | null,\n text: string | null,\n backgroundTint: string | null,\n scheme: 'light' | 'dark',\n): SchemeThemeOptions | null {\n const options: SchemeThemeOptions = {}\n\n if (accent) options.accent = accent\n if (text) options.text = text\n if (backgroundTint) {\n options.background = tint(DEFAULT_BACKGROUND[scheme], backgroundTint, BACKGROUND_TINT[scheme])\n }\n\n return Object.keys(options).length > 0 ? options : null\n}\n\n/**\n * Applies an image palette to existing theme options: the colors the palette\n * provides replace the current ones scheme by scheme, and whatever it does\n * not touch — like the contrast — stays as it is.\n *\n * @internal\n */\nexport function applyImagePalette(\n options: BuildThemeOptions,\n palette: ImagePalette,\n variant?: ImagePaletteVariant,\n): BuildThemeOptions {\n const derived = optionsFromImagePalette(palette, variant)\n const next: BuildThemeOptions = {...options}\n\n for (const scheme of SCHEMES) {\n if (derived[scheme]) next[scheme] = {...options[scheme], ...derived[scheme]}\n }\n\n return next\n}\n\n/**\n * The variant a theme is built around: the one whose swatch is the theme's\n * accent, or `null` when the accent comes from somewhere else.\n *\n * @internal\n */\nexport function currentImageVariant(\n options: BuildThemeOptions,\n palette: ImagePalette,\n): ImagePaletteVariant | null {\n const accent = options.light?.accent\n\n if (!accent) return null\n\n return (\n IMAGE_PALETTE_VARIANTS.find(\n (variant) => palette[variant] !== null && variantAccent(palette, variant, 'light') === accent,\n ) ?? null\n )\n}\n\n/**\n * How much a swatch has going for it as an accent: saturation first, then\n * closeness to mid lightness — the range where an accent reads best.\n */\nfunction interest(hex: string): number {\n const {s, l} = hexToHsl(hex)\n\n return 0.1 + 0.55 * s + 0.35 * (1 - Math.abs(l - 0.5) * 2)\n}\n\n/**\n * Picks a variant for the \"I'm feeling lucky\" button: a random one, weighted\n * by how interesting its swatch is as an accent, and never the one the theme\n * already uses while there are others to pick from.\n *\n * @internal\n */\nexport function pickLuckyVariant(\n palette: ImagePalette,\n options: {exclude?: ImagePaletteVariant | null; random?: () => number} = {},\n): ImagePaletteVariant | null {\n const {exclude = null, random = Math.random} = options\n let candidates = IMAGE_PALETTE_VARIANTS.filter((variant) => palette[variant] !== null)\n\n if (candidates.length > 1) {\n candidates = candidates.filter((variant) => variant !== exclude)\n }\n\n if (candidates.length === 0) return null\n\n const weights = candidates.map((variant) => interest(palette[variant]!))\n const total = weights.reduce((sum, weight) => sum + weight, 0)\n let pick = random() * total\n\n for (const [index, variant] of candidates.entries()) {\n pick -= weights[index]\n\n if (pick < 0) return variant\n }\n\n return candidates.at(-1) ?? null\n}\n\n/** A theme title for the image a palette came from, e.g. `sunset-beach.jpg` → `sunset beach` @internal */\nexport function titleFromFileName(name: string): string {\n return (\n name\n .replace(/\\.[^.]+$/, '')\n .replace(/[-_]+/g, ' ')\n .trim() || 'Image theme'\n )\n}\n","import {\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} from '../theme/options'\nimport {presets} from '../theme/presets'\nimport type {ImagePalette} from './imagePalette'\nimport {sameOptions} from './options'\n\n/**\n * Where a theme in the themer list comes from: the theme the Studio config\n * was generated from, one of the presets defined in code, or one the user\n * added in the tool. Only custom themes are editable — the others are\n * duplicated into a custom theme first.\n */\ntype ThemerThemeSource = 'config' | 'preset' | 'custom'\n\n/** A theme the themer tool lists @internal */\nexport interface ThemerTheme {\n slug: string\n title: string\n options: BuildThemeOptions\n source: ThemerThemeSource\n /** The palette of the image the theme's colors were taken from, for custom themes */\n palette?: ImagePalette\n}\n\n/** A theme the user added in the tool, as persisted @internal */\nexport interface CustomTheme {\n slug: string\n title: string\n options: BuildThemeOptions\n /** The palette of the image the theme's colors were taken from */\n palette?: ImagePalette\n}\n\n/** The persisted themer state @internal */\nexport interface ThemerState {\n /**\n * The slug of the applied theme — `null` applies nothing on top of the\n * Studio's configured theme\n */\n active: string | null\n /** The themes the user added */\n custom: CustomTheme[]\n /** The slugs of the themes the user removed from the list */\n removed: string[]\n /**\n * The slugs of the themes in the order the user arranged them — themes not\n * in it (new presets, themes added since) follow in their default order\n */\n order: string[]\n}\n\n/** The slug of the theme the Studio config was generated from @internal */\nexport const CONFIG_SLUG = 'config'\n\n/** The title of the theme the Studio config was generated from */\nconst CONFIG_TITLE = 'Studio config'\n\n/** The title new themes start out with @internal */\nexport const UNTITLED_THEME = 'Untitled theme'\n\n/** @internal */\nexport const initialThemerState: ThemerState = {active: null, custom: [], removed: [], order: []}\n\n/** The themes the tool works with, derived from the persisted state @internal */\nexport interface ResolvedThemes {\n /** The themes to pick from, in list order */\n themes: ThemerTheme[]\n /** The removed themes, which can be restored */\n removed: ThemerTheme[]\n /** The applied theme */\n active: ThemerTheme\n}\n\n/**\n * Resolves the list of themes: the configured theme first, then the presets\n * (minus the ones that would only repeat the configured theme), then the\n * user's own themes — rearranged into the order the user dragged them into,\n * with themes that order does not know about kept in that default order after\n * the ones it does. Removed themes are set aside so they can be restored, and\n * the active theme falls back to the configured one when its slug no longer\n * resolves.\n *\n * @internal\n */\nexport function resolveThemes(state: ThemerState, baseOptions: BuildThemeOptions): ResolvedThemes {\n const config: ThemerTheme = {\n slug: CONFIG_SLUG,\n title: CONFIG_TITLE,\n options: baseOptions,\n source: 'config',\n }\n\n const all: ThemerTheme[] = [config]\n\n for (const preset of presets) {\n if (!sameOptions(preset.options, baseOptions)) {\n all.push({slug: preset.slug, title: preset.title, options: preset.options, source: 'preset'})\n }\n }\n\n for (const theme of state.custom) {\n all.push({\n slug: theme.slug,\n title: theme.title,\n options: theme.options,\n source: 'custom',\n ...(theme.palette ? {palette: theme.palette} : {}),\n })\n }\n\n const position = new Map(state.order.map((slug, index) => [slug, index]))\n const rank = (theme: ThemerTheme) => position.get(theme.slug) ?? state.order.length\n\n // A stable sort: the themes the order does not know about keep their default order\n all.sort((a, b) => rank(a) - rank(b))\n\n const removedSlugs = new Set(state.removed)\n const themes = all.filter((theme) => !removedSlugs.has(theme.slug))\n const removed = all.filter((theme) => removedSlugs.has(theme.slug))\n const active = themes.find((theme) => theme.slug === state.active) ?? config\n\n return {themes, removed, active}\n}\n\n/**\n * Creates a custom theme with a slug that cannot collide with the presets or\n * the configured theme.\n *\n * @internal\n */\nexport function createCustomTheme(\n title: string,\n options: BuildThemeOptions,\n palette?: ImagePalette,\n): CustomTheme {\n return {slug: `custom-${randomId()}`, title, options, ...(palette ? {palette} : {})}\n}\n\n/** The title of a duplicated theme @internal */\nexport function duplicateTitle(title: string): string {\n return `${displayTitle(title)} copy`\n}\n\n/** The title to show for a theme, falling back when the user cleared it @internal */\nexport function displayTitle(title: string): string {\n return title.trim() || UNTITLED_THEME\n}\n\nfunction randomId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID()\n }\n\n return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`\n}\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -33,7 +33,7 @@ type GeneratedColorPalettes = Record<ThemeColorSchemeKey, GeneratedColorPalette>
33
33
  *
34
34
  * @public
35
35
  */
36
- export declare function buildPalette(options?: BuildThemeOptions): GeneratedColorPalettes;
36
+ declare function buildPalette(options?: BuildThemeOptions): GeneratedColorPalettes;
37
37
  /**
38
38
  * Generates a Studio theme from a handful of colors per color scheme, by
39
39
  * replacing the color palette that the `buildTheme` from `@sanity/ui/theme`
@@ -73,7 +73,7 @@ export declare function buildPalette(options?: BuildThemeOptions): GeneratedColo
73
73
  *
74
74
  * @public
75
75
  */
76
- export declare function buildTheme(options?: BuildThemeOptions): RootTheme;
76
+ declare function buildTheme(options?: BuildThemeOptions): RootTheme;
77
77
  /**
78
78
  * A preset theme: a named set of {@link BuildThemeOptions} ready to pass to
79
79
  * `buildTheme`.
@@ -101,6 +101,6 @@ interface ThemePreset {
101
101
  *
102
102
  * @public
103
103
  */
104
- export declare const presets: ThemePreset[];
105
- export type { BuildThemeOptions, GeneratedColorPalette, GeneratedColorPalettes, SchemeThemeOptions, ThemePreset };
104
+ declare const presets: ThemePreset[];
105
+ export { type BuildThemeOptions, type GeneratedColorPalette, type GeneratedColorPalettes, type SchemeThemeOptions, type ThemePreset, buildPalette, buildTheme, presets };
106
106
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/theme/buildPalette.ts","../src/theme/buildTheme.ts","../src/theme/presets.ts"],"mappings":";;;;;;;;;KAuCY;EAAyB;EAAe;IAAiB,OACnE,aACA,OAAO;;;;;;KAQG,yBAAyB,OAAO,qBAAqB;;;;;;;;;;;;;;;;;wBAkBjD,aAAa,UAAS,oBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBCjB/C,WAAW,UAAS,oBAAyB;;;;;;;UC1C5C;EACf;EACA;;EAEA,SAAS;;;;;;;;;;;;;;;;;qBA6BE,SAAS"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/theme/buildPalette.ts","../src/theme/buildTheme.ts","../src/theme/presets.ts"],"mappings":";;;;;;;;;KA6CY;EAAyB;EAAe;IAAiB,OACnE,aACA,OAAO;;;;;;KAQG,yBAAyB,OAAO,qBAAqB;;;;;;;;;;;;;;;;;iBAkBjD,aAAa,UAAS,oBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBCvB/C,WAAW,UAAS,oBAAyB;;;;;;;UC1C5C;EACf;EACA;;EAEA,SAAS;;;;;;;;;;;;;;;;;cA6BE,SAAS"}
package/dist/legacy.d.ts CHANGED
@@ -5,76 +5,77 @@ import * as themerLegacy from "@sanity/themer-legacy";
5
5
  * in `@sanity/themer@1.0`.
6
6
  * @public
7
7
  */
8
- export declare const buildThemeFromUrl: typeof themerLegacy.buildThemeFromUrl;
8
+ declare const buildThemeFromUrl: typeof themerLegacy.buildThemeFromUrl;
9
9
  /**
10
10
  * @deprecated Import `createTheme` from `@sanity/themer-legacy` instead.
11
11
  * `@sanity/themer/legacy` re-exports it unchanged for now and will be removed
12
12
  * in `@sanity/themer@1.0`.
13
13
  * @public
14
14
  */
15
- export declare const createTheme: typeof themerLegacy.createTheme;
15
+ declare const createTheme: typeof themerLegacy.createTheme;
16
16
  /**
17
17
  * @deprecated Import `parseHuesFromUrl` from `@sanity/themer-legacy` instead.
18
18
  * `@sanity/themer/legacy` re-exports it unchanged for now and will be removed
19
19
  * in `@sanity/themer@1.0`.
20
20
  * @public
21
21
  */
22
- export declare const parseHuesFromUrl: typeof themerLegacy.parseHuesFromUrl;
22
+ declare const parseHuesFromUrl: typeof themerLegacy.parseHuesFromUrl;
23
23
  /**
24
24
  * @deprecated Import `hues` from `@sanity/themer-legacy` instead.
25
25
  * `@sanity/themer/legacy` re-exports it unchanged for now and will be removed
26
26
  * in `@sanity/themer@1.0`.
27
27
  * @public
28
28
  */
29
- export declare const hues: typeof themerLegacy.hues;
29
+ declare const hues: typeof themerLegacy.hues;
30
30
  /**
31
31
  * @deprecated Import `theme` from `@sanity/themer-legacy` instead.
32
32
  * `@sanity/themer/legacy` re-exports it unchanged for now and will be removed
33
33
  * in `@sanity/themer@1.0`.
34
34
  * @public
35
35
  */
36
- export declare const theme: typeof themerLegacy.theme;
36
+ declare const theme: typeof themerLegacy.theme;
37
37
  /**
38
38
  * @deprecated Import `presets` from `@sanity/themer-legacy` instead.
39
39
  * `@sanity/themer/legacy` re-exports it unchanged for now and will be removed
40
40
  * in `@sanity/themer@1.0`.
41
41
  * @public
42
42
  */
43
- export declare const presets: typeof themerLegacy.presets;
43
+ declare const presets: typeof themerLegacy.presets;
44
44
  /**
45
45
  * @deprecated Import `Hue` from `@sanity/themer-legacy` instead.
46
46
  * `@sanity/themer/legacy` will be removed in `@sanity/themer@1.0`.
47
47
  * @public
48
48
  */
49
- export type Hue = themerLegacy.Hue;
49
+ type Hue = themerLegacy.Hue;
50
50
  /**
51
51
  * @deprecated Import `HueMidPoint` from `@sanity/themer-legacy` instead.
52
52
  * `@sanity/themer/legacy` will be removed in `@sanity/themer@1.0`.
53
53
  * @public
54
54
  */
55
- export type HueMidPoint = themerLegacy.HueMidPoint;
55
+ type HueMidPoint = themerLegacy.HueMidPoint;
56
56
  /**
57
57
  * @deprecated Import `Hues` from `@sanity/themer-legacy` instead.
58
58
  * `@sanity/themer/legacy` will be removed in `@sanity/themer@1.0`.
59
59
  * @public
60
60
  */
61
- export type Hues = themerLegacy.Hues;
61
+ type Hues = themerLegacy.Hues;
62
62
  /**
63
63
  * @deprecated Import `LegacyTheme` from `@sanity/themer-legacy` instead.
64
64
  * `@sanity/themer/legacy` will be removed in `@sanity/themer@1.0`.
65
65
  * @public
66
66
  */
67
- export type LegacyTheme = themerLegacy.LegacyTheme;
67
+ type LegacyTheme = themerLegacy.LegacyTheme;
68
68
  /**
69
69
  * @deprecated Import `PartialHues` from `@sanity/themer-legacy` instead.
70
70
  * `@sanity/themer/legacy` will be removed in `@sanity/themer@1.0`.
71
71
  * @public
72
72
  */
73
- export type PartialHues = themerLegacy.PartialHues;
73
+ type PartialHues = themerLegacy.PartialHues;
74
74
  /**
75
75
  * @deprecated Import `ThemePreset` from `@sanity/themer-legacy` instead.
76
76
  * `@sanity/themer/legacy` will be removed in `@sanity/themer@1.0`.
77
77
  * @public
78
78
  */
79
- export type ThemePreset = themerLegacy.ThemePreset;
79
+ type ThemePreset = themerLegacy.ThemePreset;
80
+ export { Hue, HueMidPoint, Hues, LegacyTheme, PartialHues, ThemePreset, buildThemeFromUrl, createTheme, hues, parseHuesFromUrl, presets, theme };
80
81
  //# sourceMappingURL=legacy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"legacy.d.ts","names":[],"sources":["../src/legacy/index.ts"],"mappings":";;;;;;;qBAYa,0BAA0B,aAAa;;;;;;;qBASvC,oBAAoB,aAAa;;;;;;;qBAQjC,yBAAyB,aAAa;;;;;;;qBAQtC,aAAa,aAAa;;;;;;;qBAQ1B,cAAc,aAAa;;;;;;;qBAQ3B,gBAAgB,aAAa;;;;;;YAO9B,MAAM,aAAa;;;;;;YAOnB,cAAc,aAAa;;;;;;YAO3B,OAAO,aAAa;;;;;;YAOpB,cAAc,aAAa;;;;;;YAO3B,cAAc,aAAa;;;;;;YAO3B,cAAc,aAAa"}
1
+ {"version":3,"file":"legacy.d.ts","names":[],"sources":["../src/legacy/index.ts"],"mappings":";;;;;;;cAYa,0BAA0B,aAAa;;;;;;;cASvC,oBAAoB,aAAa;;;;;;;cAQjC,yBAAyB,aAAa;;;;;;;cAQtC,aAAa,aAAa;;;;;;;cAQ1B,cAAc,aAAa;;;;;;;cAQ3B,gBAAgB,aAAa;;;;;;KAO9B,MAAM,aAAa;;;;;;KAOnB,cAAc,aAAa;;;;;;KAO3B,OAAO,aAAa;;;;;;KAOpB,cAAc,aAAa;;;;;;KAO3B,cAAc,aAAa;;;;;;KAO3B,cAAc,aAAa"}
@@ -1 +1 @@
1
- {"version":3,"file":"presets-CqBgS3A-.js","names":["buildTheme","buildUITheme"],"sources":["../src/theme/hsl.ts","../src/lib/mix.ts","../src/theme/options.ts","../src/theme/buildPalette.ts","../src/theme/buildTheme.ts","../src/theme/presets.ts"],"sourcesContent":["/**\n * Color math for the palette generator, kept at full float precision: unlike\n * the HSL helpers in `@sanity/ui/theme`, nothing is rounded between the two\n * conversions, so `hslToHex(hexToHsl(hex))` returns `hex` unchanged for every\n * color. `buildTheme` relies on that to reproduce the stock `@sanity/color`\n * palette byte-for-byte when it is called with the stock anchor colors.\n */\n\n/**\n * A color in HSL space: `h` in degrees within `[0, 360)`, `s` and `l` as\n * fractions within `[0, 1]`.\n *\n * @internal\n */\nexport interface Hsl {\n h: number\n s: number\n l: number\n}\n\n/** @internal */\nexport function hexToHsl(hex: string): Hsl {\n const [r, g, b] = parseHex(hex)\n\n const max = Math.max(r, g, b)\n const min = Math.min(r, g, b)\n const delta = max - min\n\n const l = (max + min) / 2\n\n if (delta === 0) {\n return {h: 0, s: 0, l}\n }\n\n const s = delta / (1 - Math.abs(2 * l - 1))\n\n let h: number\n\n if (max === r) {\n h = ((g - b) / delta) % 6\n } else if (max === g) {\n h = (b - r) / delta + 2\n } else {\n h = (r - g) / delta + 4\n }\n\n return {h: normalizeHue(h * 60), s, l}\n}\n\n/** @internal */\nexport function hslToHex(hsl: Hsl): string {\n const h = normalizeHue(hsl.h)\n const s = clamp01(hsl.s)\n const l = clamp01(hsl.l)\n\n const c = (1 - Math.abs(2 * l - 1)) * s\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1))\n const m = l - c / 2\n\n let r = 0\n let g = 0\n let b = 0\n\n if (h < 60) {\n r = c\n g = x\n } else if (h < 120) {\n r = x\n g = c\n } else if (h < 180) {\n g = c\n b = x\n } else if (h < 240) {\n g = x\n b = c\n } else if (h < 300) {\n r = x\n b = c\n } else {\n r = c\n b = x\n }\n\n return `#${channelToHex(r + m)}${channelToHex(g + m)}${channelToHex(b + m)}`\n}\n\n/**\n * Rotates `from` towards `to` along the shortest arc of the hue circle.\n *\n * @internal\n */\nexport function mixHue(from: number, to: number, amount: number): number {\n const delta = ((((to - from) % 360) + 540) % 360) - 180\n\n return normalizeHue(from + delta * amount)\n}\n\n/**\n * The WCAG relative luminance of a hex color, between 0 (black) and 1 (white).\n *\n * @internal\n */\nexport function relativeLuminance(hex: string): number {\n const [r, g, b] = parseHex(hex)\n\n return 0.2126 * linearChannel(r) + 0.7152 * linearChannel(g) + 0.0722 * linearChannel(b)\n}\n\nfunction linearChannel(value: number): number {\n return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4\n}\n\n/** Parses `#abc` and `#aabbcc` hex colors into channel fractions of 1 */\nfunction parseHex(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) / 255,\n parseInt(expanded.slice(2, 4), 16) / 255,\n parseInt(expanded.slice(4, 6), 16) / 255,\n ]\n}\n\nfunction channelToHex(value: number): string {\n return Math.round(clamp01(value) * 255)\n .toString(16)\n .padStart(2, '0')\n}\n\nfunction normalizeHue(hue: number): number {\n return ((hue % 360) + 360) % 360\n}\n\nfunction clamp01(value: number): number {\n return Math.min(1, Math.max(0, value))\n}\n","/**\n * Matches `#abc` and `#aabbcc` hex colors (the only formats the theme\n * generators accept), same as the hosted Themer service did.\n *\n * The `mix` function this module was named for moved to `@sanity/themer-legacy`\n * along with the legacy generator; `isColor` stays here for `theme/options.ts`\n * and `tool/storage.ts`.\n *\n * @internal\n */\nexport function isColor(input: string): boolean {\n return /^#(?:[0-9a-f]{3}){1,2}$/i.test(input)\n}\n","import {black, blue, gray, white} from '@sanity/color'\nimport {type ThemeColorSchemeKey} from '@sanity/ui/theme'\n\nimport {isColor} from '../lib/mix'\nimport {hexToHsl, hslToHex} from './hsl'\n\n/**\n * The colors of one color scheme, for {@link BuildThemeOptions}. Every color\n * is a hex color (`#rgb` or `#rrggbb`), and everything is optional: omitted\n * options fall back to the stock Studio colors, so an empty object reproduces\n * the stock scheme exactly.\n *\n * @public\n */\nexport interface SchemeThemeOptions {\n /**\n * The accent color — the brand color of the scheme. Replaces the `blue`\n * scale of the palette, which Sanity UI uses for primary buttons, focus\n * rings and links.\n *\n * The stock accent is `#556bfc` (`blue/500` of `@sanity/color`).\n */\n accent?: string\n\n /**\n * The background of the scheme — every other color of the scheme is\n * blended onto it. The dark scheme's background replaces `black` in the\n * palette and the light scheme's replaces `white`.\n *\n * It is kept far enough from the text and accent colors to stay usable:\n * a dark background is made darker until it has enough contrast with them\n * (it can never be lighter than either), and a light background can never\n * be darker than either.\n *\n * The stock backgrounds are `#0d0e12` (dark) and `#ffffff` (light).\n */\n background?: string\n\n /**\n * How strongly text and borders separate from the accent color, between 15\n * and 100. The default is 85, which uses the text color as-is. Above 85 the\n * text scale loses its tint until it is fully neutral at 100 — a high\n * contrast scheme with no mixing of the text and accent colors. Below 85\n * more and more of the accent color mixes into the text scale, giving text\n * and borders more color, until the scale fully adopts the accent hue and\n * saturation at 15.\n */\n contrast?: number\n\n /**\n * The text color. Replaces the `gray` scale of the palette, which Sanity UI\n * uses for text, icons, borders and neutral surfaces. When omitted, it is\n * derived from the accent color: a mostly desaturated version of it, the\n * way the stock `gray` scale carries a hint of the stock blue.\n *\n * The stock text color is `#727892` (`gray/500` of `@sanity/color`).\n */\n text?: string\n}\n\n/**\n * Options for {@link buildTheme} and {@link buildPalette}: the colors of the\n * dark and the light scheme, each with its own accent, text, background and\n * contrast — so the two schemes can differ in more than their background.\n * Both schemes are optional, and so is every color: whatever is omitted\n * falls back to the stock Studio colors, so `buildTheme({})` matches\n * `buildTheme()` from `@sanity/ui/theme` exactly.\n *\n * @public\n */\nexport interface BuildThemeOptions {\n /** The colors of the dark scheme */\n dark?: SchemeThemeOptions\n /** The colors of the light scheme */\n light?: SchemeThemeOptions\n}\n\n/**\n * {@link SchemeThemeOptions} with every option filled in: colors normalized\n * to lowercase `#rrggbb`, the text color derived from the accent when it was\n * omitted, and the contrast clamped into its valid range.\n *\n * @internal\n */\nexport interface ResolvedSchemeOptions {\n accent: string\n background: string\n contrast: number\n text: string\n}\n\n/** {@link BuildThemeOptions} with both schemes resolved @internal */\nexport type ResolvedThemeOptions = Record<ThemeColorSchemeKey, ResolvedSchemeOptions>\n\n/** The color schemes, in the order the tool presents them @internal */\nexport const SCHEMES: ThemeColorSchemeKey[] = ['light', 'dark']\n\n/** The stock accent color — `blue/500` of `@sanity/color` @internal */\nexport const DEFAULT_ACCENT: string = blue[500].hex\n\n/** The stock text color — `gray/500` of `@sanity/color` @internal */\nexport const DEFAULT_TEXT: string = gray[500].hex\n\n/** The stock backgrounds — `black` and `white` of `@sanity/color` @internal */\nexport const DEFAULT_BACKGROUND: Record<ThemeColorSchemeKey, string> = {\n dark: black.hex,\n light: white.hex,\n}\n\n/** The contrast that applies the text color as-is @internal */\nexport const DEFAULT_CONTRAST = 85\n\n/** @internal */\nexport const MINIMUM_CONTRAST = 15\n\n/** @internal */\nexport const MAXIMUM_CONTRAST = 100\n\n/** The keys of the options shape before they were grouped by scheme */\nconst FLAT_OPTION_KEYS = ['accent', 'text', 'background', 'contrast']\n\n/** @internal */\nexport function resolveThemeOptions(options: BuildThemeOptions = {}): ResolvedThemeOptions {\n if (FLAT_OPTION_KEYS.some((key) => key in options)) {\n throw new TypeError(\n 'The `buildTheme` options are grouped by color scheme: pass `{light: {accent, text, background, contrast}, dark: {…}}` instead of one set of colors for both',\n )\n }\n\n return {\n dark: resolveSchemeOptions(options.dark ?? {}, 'dark'),\n light: resolveSchemeOptions(options.light ?? {}, 'light'),\n }\n}\n\nfunction resolveSchemeOptions(\n options: SchemeThemeOptions,\n scheme: ThemeColorSchemeKey,\n): ResolvedSchemeOptions {\n const accent =\n options.accent === undefined\n ? DEFAULT_ACCENT\n : normalizeColor(options.accent, `${scheme}.accent`)\n\n return {\n accent,\n background:\n options.background === undefined\n ? DEFAULT_BACKGROUND[scheme]\n : normalizeColor(options.background, `${scheme}.background`),\n contrast: normalizeContrast(options.contrast, scheme),\n text:\n options.text === undefined\n ? deriveTextColor(accent)\n : normalizeColor(options.text, `${scheme}.text`),\n }\n}\n\n/**\n * Derives the text color from an accent color, keeping the relationship the\n * stock palette has between `gray/500` and `blue/500`: the text color takes\n * the accent's hue (offset the way the stock gray is offset from the stock\n * blue), carries about an eighth of its saturation, and keeps the stock text\n * lightness. Deriving from the stock accent returns the stock text color.\n *\n * @internal\n */\nexport function deriveTextColor(accent: string): string {\n const accentHsl = hexToHsl(normalizeColor(accent, 'accent'))\n const defaultAccent = hexToHsl(DEFAULT_ACCENT)\n const defaultText = hexToHsl(DEFAULT_TEXT)\n\n return hslToHex({\n h: defaultText.h + (accentHsl.h - defaultAccent.h),\n s: Math.min(1, defaultText.s * (accentHsl.s / defaultAccent.s)),\n l: defaultText.l,\n })\n}\n\nfunction normalizeColor(value: string, name: string): string {\n if (typeof value !== 'string' || !isColor(value)) {\n throw new TypeError(\n `Invalid \\`${name}\\` color: ${JSON.stringify(value)} — expected a hex color like #556bfc`,\n )\n }\n\n const lower = value.toLowerCase()\n\n return lower.length === 4\n ? `#${lower[1]}${lower[1]}${lower[2]}${lower[2]}${lower[3]}${lower[3]}`\n : lower\n}\n\nfunction normalizeContrast(value: number | undefined, scheme: ThemeColorSchemeKey): number {\n if (value === undefined) {\n return DEFAULT_CONTRAST\n }\n\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n throw new TypeError(\n `Invalid \\`${scheme}.contrast\\`: ${JSON.stringify(value)} — expected a number between ${MINIMUM_CONTRAST} and ${MAXIMUM_CONTRAST}`,\n )\n }\n\n return Math.min(MAXIMUM_CONTRAST, Math.max(MINIMUM_CONTRAST, value))\n}\n","import {color, COLOR_TINTS, ColorHueKey, ColorTintKey, ColorTints} from '@sanity/color'\nimport {type ThemeColorSchemeKey} from '@sanity/ui/theme'\n\nimport {hexToHsl, Hsl, hslToHex, mixHue, relativeLuminance} from './hsl'\nimport {\n BuildThemeOptions,\n DEFAULT_ACCENT,\n DEFAULT_CONTRAST,\n DEFAULT_TEXT,\n MAXIMUM_CONTRAST,\n MINIMUM_CONTRAST,\n ResolvedSchemeOptions,\n resolveThemeOptions,\n} from './options'\n\n/**\n * The lightness band the accent and text colors are clamped into — anchors\n * outside it would squeeze most of their scale's tints into an\n * indistinguishable sliver, messing with the rest of the scale.\n */\nconst MINIMUM_ANCHOR_LIGHTNESS = 0.25\nconst MAXIMUM_ANCHOR_LIGHTNESS = 0.75\n\n/**\n * The contrast ratio the dark background must reach against the 400 tints of\n * the text and accent scales — the tints Sanity UI paints muted text and\n * solid buttons with on dark backgrounds. The stock palette sits at ~6.8.\n */\nconst MINIMUM_DARK_BACKGROUND_CONTRAST = 4.5\n\n/** How much lightness each step of a background adjustment removes or adds */\nconst BACKGROUND_LIGHTNESS_STEP = 0.005\n\n/**\n * A color palette generated by {@link buildPalette}: the `palette` config of\n * `buildTheme` from `@sanity/ui/theme`, with every color as a hex string.\n *\n * @public\n */\nexport type GeneratedColorPalette = {black: string; white: string} & Record<\n ColorHueKey,\n Record<ColorTintKey, string>\n>\n\n/**\n * The palettes generated by {@link buildPalette} — one per color scheme.\n *\n * @public\n */\nexport type GeneratedColorPalettes = Record<ThemeColorSchemeKey, GeneratedColorPalette>\n\n/**\n * Generates a Sanity UI color palette for each color scheme from the theme\n * options: in each palette the scheme's accent color replaces the `blue`\n * scale, its text color the `gray` scale, and the backgrounds replace `black`\n * and `white`. All other hues keep their stock `@sanity/color` values. Called\n * with the stock colors, both palettes come back as the stock palette\n * byte-for-byte.\n *\n * ```ts\n * import {buildPalette} from '@sanity/themer'\n *\n * const {light, dark} = buildPalette({light: {accent: '#556bfc'}})\n * ```\n *\n * @public\n */\nexport function buildPalette(options: BuildThemeOptions = {}): GeneratedColorPalettes {\n const resolved = resolveThemeOptions(options)\n\n return {\n dark: buildSchemePalette('dark', resolved.dark, resolved.light.background),\n light: buildSchemePalette('light', resolved.light, resolved.dark.background),\n }\n}\n\n/**\n * Generates the palette of one scheme. The scheme's own background takes the\n * slot the scheme paints with (`black` for dark, `white` for light), while\n * the other slot — which a scheme only touches for a few inverted details —\n * takes the other scheme's background, so the theme stays one palette.\n */\nfunction buildSchemePalette(\n key: ThemeColorSchemeKey,\n scheme: ResolvedSchemeOptions,\n otherBackground: string,\n): GeneratedColorPalette {\n const accentAnchor = clampAnchorLightness(hexToHsl(scheme.accent))\n const textAnchor = clampAnchorLightness(hexToHsl(scheme.text))\n const grayAnchor = applyContrast(textAnchor, accentAnchor, scheme.contrast)\n\n const blueTints = remapTints(color.blue, hexToHsl(DEFAULT_ACCENT), accentAnchor)\n const grayTints = remapTints(color.gray, hexToHsl(DEFAULT_TEXT), grayAnchor)\n\n const anchorLuminances = [\n relativeLuminance(hslToHex(grayAnchor)),\n relativeLuminance(hslToHex(accentAnchor)),\n ]\n\n const darkBackground = key === 'dark' ? scheme.background : otherBackground\n const lightBackground = key === 'light' ? scheme.background : otherBackground\n\n return {\n black: darkenBackground(darkBackground, {\n anchorLuminances,\n tints: [grayTints['400'], blueTints['400']],\n }),\n white: lightenBackground(lightBackground, anchorLuminances),\n gray: grayTints,\n blue: blueTints,\n purple: tintHexes(color.purple),\n magenta: tintHexes(color.magenta),\n red: tintHexes(color.red),\n orange: tintHexes(color.orange),\n yellow: tintHexes(color.yellow),\n green: tintHexes(color.green),\n cyan: tintHexes(color.cyan),\n }\n}\n\n/**\n * Mixes the accent color into the text color according to the contrast: 85\n * applies the text color as-is, 100 removes its tint entirely, and lower\n * values rotate and saturate it towards the accent.\n */\nfunction applyContrast(text: Hsl, accent: Hsl, contrast: number): Hsl {\n if (contrast === DEFAULT_CONTRAST) {\n return text\n }\n\n if (contrast > DEFAULT_CONTRAST) {\n const amount = (contrast - DEFAULT_CONTRAST) / (MAXIMUM_CONTRAST - DEFAULT_CONTRAST)\n\n return {h: text.h, s: text.s * (1 - amount), l: text.l}\n }\n\n const amount = (DEFAULT_CONTRAST - contrast) / (DEFAULT_CONTRAST - MINIMUM_CONTRAST)\n\n return {\n h: mixHue(text.h, accent.h, amount),\n s: text.s + (accent.s - text.s) * amount,\n l: text.l,\n }\n}\n\n/**\n * Rebuilds a stock tint scale around a new anchor color, where `from` is the\n * scale's stock 500 tint: hues rotate along, saturation scales\n * proportionally, and lightness is remapped piecewise so the anchor takes the\n * new lightness while the scale keeps spanning light to dark. When the anchor\n * matches the stock one, every tint comes back unchanged.\n */\nfunction remapTints(tints: ColorTints, from: Hsl, to: Hsl): Record<ColorTintKey, string> {\n const rotation = to.h - from.h\n const result: Partial<Record<ColorTintKey, string>> = {}\n\n for (const tint of COLOR_TINTS) {\n const value = hexToHsl(tints[tint].hex)\n\n result[tint] = hslToHex({\n h: value.h + rotation,\n s: remapSaturation(value.s, from.s, to.s),\n l: remapLightness(value.l, from.l, to.l),\n })\n }\n\n // oxlint-disable-next-line no-unsafe-type-assertion -- the loop assigns every tint key\n return result as Record<ColorTintKey, string>\n}\n\nfunction remapSaturation(value: number, from: number, to: number): number {\n if (from === to) {\n return value\n }\n\n return Math.min(1, value * (to / from))\n}\n\nfunction remapLightness(value: number, from: number, to: number): number {\n if (from === to) {\n return value\n }\n\n if (value <= from) {\n return value * (to / from)\n }\n\n return to + ((value - from) * (1 - to)) / (1 - from)\n}\n\nfunction clampAnchorLightness(anchor: Hsl): Hsl {\n const l = Math.min(MAXIMUM_ANCHOR_LIGHTNESS, Math.max(MINIMUM_ANCHOR_LIGHTNESS, anchor.l))\n\n return l === anchor.l ? anchor : {...anchor, l}\n}\n\n/**\n * Enforces the dark background rules: it can never be lighter than the text\n * or accent colors, and it must reach enough contrast with the 400 tints of\n * their scales — it is made darker until it does (or bottoms out at black).\n */\nfunction darkenBackground(\n hex: string,\n rules: {anchorLuminances: number[]; tints: string[]},\n): string {\n const maximumLuminance = Math.min(...rules.anchorLuminances)\n const tintLuminances = rules.tints.map((tint) => relativeLuminance(tint))\n\n const isDarkEnough = (background: string): boolean => {\n const luminance = relativeLuminance(background)\n\n return (\n luminance < maximumLuminance &&\n tintLuminances.every(\n (tintLuminance) =>\n (tintLuminance + 0.05) / (luminance + 0.05) >= MINIMUM_DARK_BACKGROUND_CONTRAST,\n )\n )\n }\n\n const {h, s, l} = hexToHsl(hex)\n let lightness = l\n let current = hex\n\n while (!isDarkEnough(current) && lightness > 0) {\n lightness = Math.max(0, lightness - BACKGROUND_LIGHTNESS_STEP)\n current = hslToHex({h, s, l: lightness})\n }\n\n return current\n}\n\n/**\n * Enforces the light background rule: it can never be darker than the text\n * or accent colors — it is made lighter until it is (or tops out at white).\n */\nfunction lightenBackground(hex: string, anchorLuminances: number[]): string {\n const minimumLuminance = Math.max(...anchorLuminances)\n\n const {h, s, l} = hexToHsl(hex)\n let lightness = l\n let current = hex\n\n while (relativeLuminance(current) <= minimumLuminance && lightness < 1) {\n lightness = Math.min(1, lightness + BACKGROUND_LIGHTNESS_STEP)\n current = hslToHex({h, s, l: lightness})\n }\n\n return current\n}\n\nfunction tintHexes(tints: ColorTints): Record<ColorTintKey, string> {\n const result: Partial<Record<ColorTintKey, string>> = {}\n\n for (const tint of COLOR_TINTS) {\n result[tint] = tints[tint].hex\n }\n\n // oxlint-disable-next-line no-unsafe-type-assertion -- the loop assigns every tint key\n return result as Record<ColorTintKey, string>\n}\n","import {\n buildTheme as buildUITheme,\n RootTheme,\n ThemeColorSchemes_v2,\n v0_v2,\n v2_v0,\n} from '@sanity/ui/theme'\n\nimport {buildPalette} from './buildPalette'\nimport {BuildThemeOptions} from './options'\n\n/**\n * Generates a Studio theme from a handful of colors per color scheme, by\n * replacing the color palette that the `buildTheme` from `@sanity/ui/theme`\n * otherwise fills with `@sanity/color`: in each scheme the accent color\n * replaces the `blue` scale (primary buttons, focus rings, links), the\n * optional text color the `gray` scale (text, icons, borders), and the\n * optional background replaces `black` (dark) or `white` (light). The result\n * is the same type of theme that `@sanity/ui/theme` builds, ready for the\n * `theme` property of a Studio config.\n *\n * ```ts\n * import {buildTheme} from '@sanity/themer'\n * import {defineConfig} from 'sanity'\n *\n * export const theme = buildTheme({\n * light: {\n * accent: '#556bfc',\n * text: '#727892', // optional — derived from `accent` when omitted\n * background: '#ffffff', // optional\n * contrast: 85, // optional, 15–100\n * },\n * dark: {\n * accent: '#556bfc',\n * background: '#0d0e12',\n * },\n * })\n *\n * export default defineConfig({\n * theme,\n * // ...rest of the config\n * })\n * ```\n *\n * Every option is optional and falls back to the stock Studio colors: called\n * with the stock colors — like the example — or with no options at all, it\n * returns the exact same colors as `buildTheme()` from `@sanity/ui/theme`.\n *\n * @public\n */\nexport function buildTheme(options: BuildThemeOptions = {}): RootTheme {\n const palettes = buildPalette(options)\n\n // Sanity UI renders both schemes of a theme from one palette, so each\n // scheme gets a theme of its own palette, and the two are composed into one\n // theme that takes its light scheme from the one and its dark scheme from\n // the other. The scheme getters keep Sanity UI's lazy rendering: a scheme is\n // only computed once something reads it.\n const light = v0_v2(buildUITheme({palette: palettes.light}))\n const dark = v0_v2(buildUITheme({palette: palettes.dark}))\n\n const color: ThemeColorSchemes_v2 = {\n get light() {\n return light.color.light\n },\n get dark() {\n return dark.color.dark\n },\n }\n\n return v2_v0({...light, color})\n}\n","import {BuildThemeOptions} from './options'\n\n/**\n * A preset theme: a named set of {@link BuildThemeOptions} ready to pass to\n * `buildTheme`.\n *\n * @public\n */\nexport interface ThemePreset {\n slug: string\n title: string\n /** The preset's theme options, ready to pass to `buildTheme` */\n options: BuildThemeOptions\n}\n\n/**\n * The hosted Themer presets had one accent and one text color for both\n * schemes, and a background per scheme\n */\nfunction scheme(accent: string, text: string, backgrounds: {dark: string; light: string}) {\n return {\n dark: {accent, text, background: backgrounds.dark},\n light: {accent, text, background: backgrounds.light},\n } satisfies BuildThemeOptions\n}\n\n/**\n * Preset themes for `buildTheme`, carried over from the hosted Themer service\n * (themer.sanity.build) presets: each one maps the legacy preset's primary\n * hue to `accent`, its default hue to `text` and its lightest/darkest colors\n * to the backgrounds of the light and dark scheme.\n *\n * ```ts\n * import {buildTheme, presets} from '@sanity/themer'\n *\n * const verdant = presets.find((preset) => preset.slug === 'verdant')\n * const theme = buildTheme(verdant.options)\n * ```\n *\n * @public\n */\nexport const presets: ThemePreset[] = [\n {\n slug: 'studio',\n title: 'Studio',\n options: {},\n },\n {\n slug: 'dew',\n title: 'Dew',\n options: scheme('#d1a308', '#5e63b4', {dark: '#0d0d15', light: '#fcfcfd'}),\n },\n {\n slug: 'pink-synth',\n title: 'Pink Synth',\n options: scheme('#ec4899', '#8b6584', {dark: '#171721', light: '#f7f2f5'}),\n },\n {\n slug: 'pixel-art',\n title: 'Pixel Art',\n options: scheme('#f10784', '#57619c', {dark: '#0d0e15', light: '#fcfcfd'}),\n },\n {\n slug: 'retro-colonial',\n title: 'Retro Colonial',\n options: scheme('#fa7a78', '#8bb9b5', {dark: '#0d1515', light: '#fcfdfd'}),\n },\n {\n slug: 'rosabel',\n title: 'Rosabel',\n options: scheme('#ed2555', '#9d8966', {dark: '#15120d', light: '#fdfdfc'}),\n },\n {\n slug: 'stereofidelic',\n title: 'Stereofidelic',\n options: scheme('#f13009', '#678e9a', {dark: '#0e1315', light: '#fcfdfd'}),\n },\n {\n slug: 'verdant',\n title: 'Verdant',\n options: scheme('#1cb485', '#5c9199', {dark: '#0d1415', light: '#fcfdfd'}),\n },\n]\n"],"mappings":";;;AAqBA,SAAgB,SAAS,KAAkB;CACzC,IAAM,CAAC,GAAG,GAAG,KAAK,SAAS,GAAG,GAExB,MAAM,KAAK,IAAI,GAAG,GAAG,CAAC,GACtB,MAAM,KAAK,IAAI,GAAG,GAAG,CAAC,GACtB,QAAQ,MAAM,KAEd,KAAK,MAAM,OAAO;CAExB,IAAI,UAAU,GACZ,OAAO;EAAC,GAAG;EAAG,GAAG;EAAG;CAAC;CAGvB,IAAM,IAAI,SAAS,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAErC;CAUJ,OARA,AAKE,IALE,QAAQ,KACJ,IAAI,KAAK,QAAS,IACf,QAAQ,KACZ,IAAI,KAAK,QAAQ,KAEjB,IAAI,KAAK,QAAQ,GAGjB;EAAC,GAAG,aAAa,IAAI,EAAE;EAAG;EAAG;CAAC;AACvC;;AAGA,SAAgB,SAAS,KAAkB;CACzC,IAAM,IAAI,aAAa,IAAI,CAAC,GACtB,IAAI,QAAQ,IAAI,CAAC,GACjB,IAAI,QAAQ,IAAI,CAAC,GAEjB,KAAK,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,KAAK,GAChC,IAAI,KAAK,IAAI,KAAK,IAAM,IAAI,KAAM,IAAK,CAAC,IACxC,IAAI,IAAI,IAAI,GAEd,IAAI,GACJ,IAAI,GACJ,IAAI;CAsBR,OApBI,IAAI,MACN,IAAI,GACJ,IAAI,KACK,IAAI,OACb,IAAI,GACJ,IAAI,KACK,IAAI,OACb,IAAI,GACJ,IAAI,KACK,IAAI,OACb,IAAI,GACJ,IAAI,KACK,IAAI,OACb,IAAI,GACJ,IAAI,MAEJ,IAAI,GACJ,IAAI,IAGC,IAAI,aAAa,IAAI,CAAC,IAAI,aAAa,IAAI,CAAC,IAAI,aAAa,IAAI,CAAC;AAC3E;;;;;;AAOA,SAAgB,OAAO,MAAc,IAAY,QAAwB;CAGvE,OAAO,aAAa,UAFF,KAAK,QAAQ,MAAO,OAAO,MAAO,OAEjB,MAAM;AAC3C;;;;;;AAOA,SAAgB,kBAAkB,KAAqB;CACrD,IAAM,CAAC,GAAG,GAAG,KAAK,SAAS,GAAG;CAE9B,OAAO,QAAS,cAAc,CAAC,IAAI,QAAS,cAAc,CAAC,IAAI,QAAS,cAAc,CAAC;AACzF;AAEA,SAAS,cAAc,OAAuB;CAC5C,OAAO,SAAS,SAAU,QAAQ,UAAU,QAAQ,QAAS,UAAU;AACzE;;AAGA,SAAS,SAAS,KAAuC;CACvD,IAAM,WACJ,IAAI,WAAW,IAAI,GAAG,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,OAAO,IAAI,MAAM,CAAC;CAE3F,OAAO;EACL,SAAS,SAAS,MAAM,GAAG,CAAC,GAAG,EAAE,IAAI;EACrC,SAAS,SAAS,MAAM,GAAG,CAAC,GAAG,EAAE,IAAI;EACrC,SAAS,SAAS,MAAM,GAAG,CAAC,GAAG,EAAE,IAAI;CACvC;AACF;AAEA,SAAS,aAAa,OAAuB;CAC3C,OAAO,KAAK,MAAM,QAAQ,KAAK,IAAI,GAAG,CAAC,CACpC,SAAS,EAAE,CAAC,CACZ,SAAS,GAAG,GAAG;AACpB;AAEA,SAAS,aAAa,KAAqB;CACzC,QAAS,MAAM,MAAO,OAAO;AAC/B;AAEA,SAAS,QAAQ,OAAuB;CACtC,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,CAAC;AACvC;;;;;;;;;;;AC9HA,SAAgB,QAAQ,OAAwB;CAC9C,OAAO,2BAA2B,KAAK,KAAK;AAC9C;;ACmFA,MAAa,UAAiC,CAAC,SAAS,MAAM,GAGjD,iBAAyB,KAAK,IAAI,CAAC,KAGnC,eAAuB,KAAK,IAAI,CAAC,KAGjC,qBAA0D;CACrE,MAAM,MAAM;CACZ,OAAO,MAAM;AACf,GAYM,mBAAmB;CAAC;CAAU;CAAQ;CAAc;AAAU;;AAGpE,SAAgB,oBAAoB,UAA6B,CAAC,GAAyB;CACzF,IAAI,iBAAiB,MAAM,QAAQ,OAAO,OAAO,GAC/C,MAAU,UACR,6JACF;CAGF,OAAO;EACL,MAAM,qBAAqB,QAAQ,QAAQ,CAAC,GAAG,MAAM;EACrD,OAAO,qBAAqB,QAAQ,SAAS,CAAC,GAAG,OAAO;CAC1D;AACF;AAEA,SAAS,qBACP,SACA,QACuB;CACvB,IAAM,SACJ,QAAQ,WAAW,KAAA,IACf,iBACA,eAAe,QAAQ,QAAQ,GAAG,OAAO,QAAQ;CAEvD,OAAO;EACL;EACA,YACE,QAAQ,eAAe,KAAA,IACnB,mBAAmB,UACnB,eAAe,QAAQ,YAAY,GAAG,OAAO,YAAY;EAC/D,UAAU,kBAAkB,QAAQ,UAAU,MAAM;EACpD,MACE,QAAQ,SAAS,KAAA,IACb,gBAAgB,MAAM,IACtB,eAAe,QAAQ,MAAM,GAAG,OAAO,MAAM;CACrD;AACF;;;;;;;;;;AAWA,SAAgB,gBAAgB,QAAwB;CACtD,IAAM,YAAY,SAAS,eAAe,QAAQ,QAAQ,CAAC,GACrD,gBAAgB,SAAS,cAAc,GACvC,cAAc,SAAS,YAAY;CAEzC,OAAO,SAAS;EACd,GAAG,YAAY,KAAK,UAAU,IAAI,cAAc;EAChD,GAAG,KAAK,IAAI,GAAG,YAAY,KAAK,UAAU,IAAI,cAAc,EAAE;EAC9D,GAAG,YAAY;CACjB,CAAC;AACH;AAEA,SAAS,eAAe,OAAe,MAAsB;CAC3D,IAAI,OAAO,SAAU,YAAY,CAAC,QAAQ,KAAK,GAC7C,MAAU,UACR,aAAa,KAAK,YAAY,KAAK,UAAU,KAAK,EAAE,qCACtD;CAGF,IAAM,QAAQ,MAAM,YAAY;CAEhC,OAAO,MAAM,WAAW,IACpB,IAAI,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,OACjE;AACN;AAEA,SAAS,kBAAkB,OAA2B,QAAqC;CACzF,IAAI,UAAU,KAAA,GACZ,OAAA;CAGF,IAAI,OAAO,SAAU,YAAY,CAAC,OAAO,SAAS,KAAK,GACrD,MAAU,UACR,aAAa,OAAO,eAAe,KAAK,UAAU,KAAK,EAAE,wCAC3D;CAGF,OAAO,KAAK,IAAA,KAAsB,KAAK,IAAA,IAAsB,KAAK,CAAC;AACrE;;;;;;ACzLA,MAWM,4BAA4B;;;;;;;;;;;;;;;;;AAoClC,SAAgB,aAAa,UAA6B,CAAC,GAA2B;CACpF,IAAM,WAAW,oBAAoB,OAAO;CAE5C,OAAO;EACL,MAAM,mBAAmB,QAAQ,SAAS,MAAM,SAAS,MAAM,UAAU;EACzE,OAAO,mBAAmB,SAAS,SAAS,OAAO,SAAS,KAAK,UAAU;CAC7E;AACF;;;;;;;AAQA,SAAS,mBACP,KACA,QACA,iBACuB;CACvB,IAAM,eAAe,qBAAqB,SAAS,OAAO,MAAM,CAAC,GAE3D,aAAa,cADA,qBAAqB,SAAS,OAAO,IAAI,CAC3B,GAAY,cAAc,OAAO,QAAQ,GAEpE,YAAY,WAAW,MAAM,MAAM,SAAS,cAAc,GAAG,YAAY,GACzE,YAAY,WAAW,MAAM,MAAM,SAAS,YAAY,GAAG,UAAU,GAErE,mBAAmB,CACvB,kBAAkB,SAAS,UAAU,CAAC,GACtC,kBAAkB,SAAS,YAAY,CAAC,CAC1C,GAEM,iBAAiB,QAAQ,SAAS,OAAO,aAAa,iBACtD,kBAAkB,QAAQ,UAAU,OAAO,aAAa;CAE9D,OAAO;EACL,OAAO,iBAAiB,gBAAgB;GACtC;GACA,OAAO,CAAC,UAAU,MAAQ,UAAU,IAAM;EAC5C,CAAC;EACD,OAAO,kBAAkB,iBAAiB,gBAAgB;EAC1D,MAAM;EACN,MAAM;EACN,QAAQ,UAAU,MAAM,MAAM;EAC9B,SAAS,UAAU,MAAM,OAAO;EAChC,KAAK,UAAU,MAAM,GAAG;EACxB,QAAQ,UAAU,MAAM,MAAM;EAC9B,QAAQ,UAAU,MAAM,MAAM;EAC9B,OAAO,UAAU,MAAM,KAAK;EAC5B,MAAM,UAAU,MAAM,IAAI;CAC5B;AACF;;;;;;AAOA,SAAS,cAAc,MAAW,QAAa,UAAuB;CACpE,IAAI,aAAA,IACF,OAAO;CAGT,IAAI,WAAA,IAA6B;EAC/B,IAAM,UAAU,WAAA,MAAA;EAEhB,OAAO;GAAC,GAAG,KAAK;GAAG,GAAG,KAAK,KAAK,IAAI;GAAS,GAAG,KAAK;EAAC;CACxD;CAEA,IAAM,UAAA,KAA6B,YAAA;CAEnC,OAAO;EACL,GAAG,OAAO,KAAK,GAAG,OAAO,GAAG,MAAM;EAClC,GAAG,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK;EAClC,GAAG,KAAK;CACV;AACF;;;;;;;;AASA,SAAS,WAAW,OAAmB,MAAW,IAAuC;CACvF,IAAM,WAAW,GAAG,IAAI,KAAK,GACvB,SAAgD,CAAC;CAEvD,KAAK,IAAM,QAAQ,aAAa;EAC9B,IAAM,QAAQ,SAAS,MAAM,KAAK,CAAC,GAAG;EAEtC,OAAO,QAAQ,SAAS;GACtB,GAAG,MAAM,IAAI;GACb,GAAG,gBAAgB,MAAM,GAAG,KAAK,GAAG,GAAG,CAAC;GACxC,GAAG,eAAe,MAAM,GAAG,KAAK,GAAG,GAAG,CAAC;EACzC,CAAC;CACH;CAGA,OAAO;AACT;AAEA,SAAS,gBAAgB,OAAe,MAAc,IAAoB;CAKxE,OAJI,SAAS,KACJ,QAGF,KAAK,IAAI,GAAY,KAAK,OAAd,KAAmB;AACxC;AAEA,SAAS,eAAe,OAAe,MAAc,IAAoB;CASvE,OARI,SAAS,KACJ,QAGL,SAAS,OACK,KAAK,OAAd,QAGF,MAAO,QAAQ,SAAS,IAAI,OAAQ,IAAI;AACjD;AAEA,SAAS,qBAAqB,QAAkB;CAC9C,IAAM,IAAI,KAAK,IAAI,KAA0B,KAAK,IAAI,KAA0B,OAAO,CAAC,CAAC;CAEzF,OAAO,MAAM,OAAO,IAAI,SAAS;EAAC,GAAG;EAAQ;CAAC;AAChD;;;;;;AAOA,SAAS,iBACP,KACA,OACQ;CACR,IAAM,mBAAmB,KAAK,IAAI,GAAG,MAAM,gBAAgB,GACrD,iBAAiB,MAAM,MAAM,KAAK,SAAS,kBAAkB,IAAI,CAAC,GAElE,gBAAgB,eAAgC;EACpD,IAAM,YAAY,kBAAkB,UAAU;EAE9C,OACE,YAAY,oBACZ,eAAe,OACZ,mBACE,gBAAgB,QAAS,YAAY,QAAS,GACnD;CAEJ,GAEM,EAAC,GAAG,GAAG,MAAK,SAAS,GAAG,GAC1B,YAAY,GACZ,UAAU;CAEd,OAAO,CAAC,aAAa,OAAO,KAAK,YAAY,IAE3C,AADA,YAAY,KAAK,IAAI,GAAG,YAAY,yBAAyB,GAC7D,UAAU,SAAS;EAAC;EAAG;EAAG,GAAG;CAAS,CAAC;CAGzC,OAAO;AACT;;;;;AAMA,SAAS,kBAAkB,KAAa,kBAAoC;CAC1E,IAAM,mBAAmB,KAAK,IAAI,GAAG,gBAAgB,GAE/C,EAAC,GAAG,GAAG,MAAK,SAAS,GAAG,GAC1B,YAAY,GACZ,UAAU;CAEd,OAAO,kBAAkB,OAAO,KAAK,oBAAoB,YAAY,IAEnE,AADA,YAAY,KAAK,IAAI,GAAG,YAAY,yBAAyB,GAC7D,UAAU,SAAS;EAAC;EAAG;EAAG,GAAG;CAAS,CAAC;CAGzC,OAAO;AACT;AAEA,SAAS,UAAU,OAAiD;CAClE,IAAM,SAAgD,CAAC;CAEvD,KAAK,IAAM,QAAQ,aACjB,OAAO,QAAQ,MAAM,KAAK,CAAC;CAI7B,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AClNA,SAAgBA,aAAW,UAA6B,CAAC,GAAc;CACrE,IAAM,WAAW,aAAa,OAAO,GAO/B,QAAQ,MAAMC,WAAa,EAAC,SAAS,SAAS,MAAK,CAAC,CAAC,GACrD,OAAO,MAAMA,WAAa,EAAC,SAAS,SAAS,KAAI,CAAC,CAAC,GAEnD,QAA8B;EAClC,IAAI,QAAQ;GACV,OAAO,MAAM,MAAM;EACrB;EACA,IAAI,OAAO;GACT,OAAO,KAAK,MAAM;EACpB;CACF;CAEA,OAAO,MAAM;EAAC,GAAG;EAAO;CAAK,CAAC;AAChC;;;;;ACpDA,SAAS,OAAO,QAAgB,MAAc,aAA4C;CACxF,OAAO;EACL,MAAM;GAAC;GAAQ;GAAM,YAAY,YAAY;EAAI;EACjD,OAAO;GAAC;GAAQ;GAAM,YAAY,YAAY;EAAK;CACrD;AACF;;;;;;;;;;;;;;;;AAiBA,MAAa,UAAyB;CACpC;EACE,MAAM;EACN,OAAO;EACP,SAAS,CAAC;CACZ;CACA;EACE,MAAM;EACN,OAAO;EACP,SAAS,OAAO,WAAW,WAAW;GAAC,MAAM;GAAW,OAAO;EAAS,CAAC;CAC3E;CACA;EACE,MAAM;EACN,OAAO;EACP,SAAS,OAAO,WAAW,WAAW;GAAC,MAAM;GAAW,OAAO;EAAS,CAAC;CAC3E;CACA;EACE,MAAM;EACN,OAAO;EACP,SAAS,OAAO,WAAW,WAAW;GAAC,MAAM;GAAW,OAAO;EAAS,CAAC;CAC3E;CACA;EACE,MAAM;EACN,OAAO;EACP,SAAS,OAAO,WAAW,WAAW;GAAC,MAAM;GAAW,OAAO;EAAS,CAAC;CAC3E;CACA;EACE,MAAM;EACN,OAAO;EACP,SAAS,OAAO,WAAW,WAAW;GAAC,MAAM;GAAW,OAAO;EAAS,CAAC;CAC3E;CACA;EACE,MAAM;EACN,OAAO;EACP,SAAS,OAAO,WAAW,WAAW;GAAC,MAAM;GAAW,OAAO;EAAS,CAAC;CAC3E;CACA;EACE,MAAM;EACN,OAAO;EACP,SAAS,OAAO,WAAW,WAAW;GAAC,MAAM;GAAW,OAAO;EAAS,CAAC;CAC3E;AACF"}
1
+ {"version":3,"file":"presets-CqBgS3A-.js","names":["buildTheme","buildUITheme"],"sources":["../src/theme/hsl.ts","../src/lib/mix.ts","../src/theme/options.ts","../src/theme/buildPalette.ts","../src/theme/buildTheme.ts","../src/theme/presets.ts"],"sourcesContent":["/**\n * Color math for the palette generator, kept at full float precision: unlike\n * the HSL helpers in `@sanity/ui/theme`, nothing is rounded between the two\n * conversions, so `hslToHex(hexToHsl(hex))` returns `hex` unchanged for every\n * color. `buildTheme` relies on that to reproduce the stock `@sanity/color`\n * palette byte-for-byte when it is called with the stock anchor colors.\n */\n\n/**\n * A color in HSL space: `h` in degrees within `[0, 360)`, `s` and `l` as\n * fractions within `[0, 1]`.\n *\n * @internal\n */\nexport interface Hsl {\n h: number\n s: number\n l: number\n}\n\n/** @internal */\nexport function hexToHsl(hex: string): Hsl {\n const [r, g, b] = parseHex(hex)\n\n const max = Math.max(r, g, b)\n const min = Math.min(r, g, b)\n const delta = max - min\n\n const l = (max + min) / 2\n\n if (delta === 0) {\n return {h: 0, s: 0, l}\n }\n\n const s = delta / (1 - Math.abs(2 * l - 1))\n\n let h: number\n\n if (max === r) {\n h = ((g - b) / delta) % 6\n } else if (max === g) {\n h = (b - r) / delta + 2\n } else {\n h = (r - g) / delta + 4\n }\n\n return {h: normalizeHue(h * 60), s, l}\n}\n\n/** @internal */\nexport function hslToHex(hsl: Hsl): string {\n const h = normalizeHue(hsl.h)\n const s = clamp01(hsl.s)\n const l = clamp01(hsl.l)\n\n const c = (1 - Math.abs(2 * l - 1)) * s\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1))\n const m = l - c / 2\n\n let r = 0\n let g = 0\n let b = 0\n\n if (h < 60) {\n r = c\n g = x\n } else if (h < 120) {\n r = x\n g = c\n } else if (h < 180) {\n g = c\n b = x\n } else if (h < 240) {\n g = x\n b = c\n } else if (h < 300) {\n r = x\n b = c\n } else {\n r = c\n b = x\n }\n\n return `#${channelToHex(r + m)}${channelToHex(g + m)}${channelToHex(b + m)}`\n}\n\n/**\n * Rotates `from` towards `to` along the shortest arc of the hue circle.\n *\n * @internal\n */\nexport function mixHue(from: number, to: number, amount: number): number {\n const delta = ((((to - from) % 360) + 540) % 360) - 180\n\n return normalizeHue(from + delta * amount)\n}\n\n/**\n * The WCAG relative luminance of a hex color, between 0 (black) and 1 (white).\n *\n * @internal\n */\nexport function relativeLuminance(hex: string): number {\n const [r, g, b] = parseHex(hex)\n\n return 0.2126 * linearChannel(r) + 0.7152 * linearChannel(g) + 0.0722 * linearChannel(b)\n}\n\nfunction linearChannel(value: number): number {\n return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4\n}\n\n/** Parses `#abc` and `#aabbcc` hex colors into channel fractions of 1 */\nfunction parseHex(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) / 255,\n parseInt(expanded.slice(2, 4), 16) / 255,\n parseInt(expanded.slice(4, 6), 16) / 255,\n ]\n}\n\nfunction channelToHex(value: number): string {\n return Math.round(clamp01(value) * 255)\n .toString(16)\n .padStart(2, '0')\n}\n\nfunction normalizeHue(hue: number): number {\n return ((hue % 360) + 360) % 360\n}\n\nfunction clamp01(value: number): number {\n return Math.min(1, Math.max(0, value))\n}\n","/**\n * Matches `#abc` and `#aabbcc` hex colors (the only formats the theme\n * generators accept), same as the hosted Themer service did.\n *\n * The `mix` function this module was named for moved to `@sanity/themer-legacy`\n * along with the legacy generator; `isColor` stays here for `theme/options.ts`\n * and `tool/storage.ts`.\n *\n * @internal\n */\nexport function isColor(input: string): boolean {\n return /^#(?:[0-9a-f]{3}){1,2}$/i.test(input)\n}\n","import {black, blue, gray, white} from '@sanity/color'\nimport {type ThemeColorSchemeKey} from '@sanity/ui/theme'\n\nimport {isColor} from '../lib/mix'\nimport {hexToHsl, hslToHex} from './hsl'\n\n/**\n * The colors of one color scheme, for {@link BuildThemeOptions}. Every color\n * is a hex color (`#rgb` or `#rrggbb`), and everything is optional: omitted\n * options fall back to the stock Studio colors, so an empty object reproduces\n * the stock scheme exactly.\n *\n * @public\n */\nexport interface SchemeThemeOptions {\n /**\n * The accent color — the brand color of the scheme. Replaces the `blue`\n * scale of the palette, which Sanity UI uses for primary buttons, focus\n * rings and links.\n *\n * The stock accent is `#556bfc` (`blue/500` of `@sanity/color`).\n */\n accent?: string\n\n /**\n * The background of the scheme — every other color of the scheme is\n * blended onto it. The dark scheme's background replaces `black` in the\n * palette and the light scheme's replaces `white`.\n *\n * It is kept far enough from the text and accent colors to stay usable:\n * a dark background is made darker until it has enough contrast with them\n * (it can never be lighter than either), and a light background can never\n * be darker than either.\n *\n * The stock backgrounds are `#0d0e12` (dark) and `#ffffff` (light).\n */\n background?: string\n\n /**\n * How strongly text and borders separate from the accent color, between 15\n * and 100. The default is 85, which uses the text color as-is. Above 85 the\n * text scale loses its tint until it is fully neutral at 100 — a high\n * contrast scheme with no mixing of the text and accent colors. Below 85\n * more and more of the accent color mixes into the text scale, giving text\n * and borders more color, until the scale fully adopts the accent hue and\n * saturation at 15.\n */\n contrast?: number\n\n /**\n * The text color. Replaces the `gray` scale of the palette, which Sanity UI\n * uses for text, icons, borders and neutral surfaces. When omitted, it is\n * derived from the accent color: a mostly desaturated version of it, the\n * way the stock `gray` scale carries a hint of the stock blue.\n *\n * The stock text color is `#727892` (`gray/500` of `@sanity/color`).\n */\n text?: string\n}\n\n/**\n * Options for {@link buildTheme} and {@link buildPalette}: the colors of the\n * dark and the light scheme, each with its own accent, text, background and\n * contrast — so the two schemes can differ in more than their background.\n * Both schemes are optional, and so is every color: whatever is omitted\n * falls back to the stock Studio colors, so `buildTheme({})` matches\n * `buildTheme()` from `@sanity/ui/theme` exactly.\n *\n * @public\n */\nexport interface BuildThemeOptions {\n /** The colors of the dark scheme */\n dark?: SchemeThemeOptions\n /** The colors of the light scheme */\n light?: SchemeThemeOptions\n}\n\n/**\n * {@link SchemeThemeOptions} with every option filled in: colors normalized\n * to lowercase `#rrggbb`, the text color derived from the accent when it was\n * omitted, and the contrast clamped into its valid range.\n *\n * @internal\n */\nexport interface ResolvedSchemeOptions {\n accent: string\n background: string\n contrast: number\n text: string\n}\n\n/** {@link BuildThemeOptions} with both schemes resolved @internal */\nexport type ResolvedThemeOptions = Record<ThemeColorSchemeKey, ResolvedSchemeOptions>\n\n/** The color schemes, in the order the tool presents them @internal */\nexport const SCHEMES: ThemeColorSchemeKey[] = ['light', 'dark']\n\n/** The stock accent color — `blue/500` of `@sanity/color` @internal */\nexport const DEFAULT_ACCENT: string = blue[500].hex\n\n/** The stock text color — `gray/500` of `@sanity/color` @internal */\nexport const DEFAULT_TEXT: string = gray[500].hex\n\n/** The stock backgrounds — `black` and `white` of `@sanity/color` @internal */\nexport const DEFAULT_BACKGROUND: Record<ThemeColorSchemeKey, string> = {\n dark: black.hex,\n light: white.hex,\n}\n\n/** The contrast that applies the text color as-is @internal */\nexport const DEFAULT_CONTRAST = 85\n\n/** @internal */\nexport const MINIMUM_CONTRAST = 15\n\n/** @internal */\nexport const MAXIMUM_CONTRAST = 100\n\n/** The keys of the options shape before they were grouped by scheme */\nconst FLAT_OPTION_KEYS = ['accent', 'text', 'background', 'contrast']\n\n/** @internal */\nexport function resolveThemeOptions(options: BuildThemeOptions = {}): ResolvedThemeOptions {\n if (FLAT_OPTION_KEYS.some((key) => key in options)) {\n throw new TypeError(\n 'The `buildTheme` options are grouped by color scheme: pass `{light: {accent, text, background, contrast}, dark: {…}}` instead of one set of colors for both',\n )\n }\n\n return {\n dark: resolveSchemeOptions(options.dark ?? {}, 'dark'),\n light: resolveSchemeOptions(options.light ?? {}, 'light'),\n }\n}\n\nfunction resolveSchemeOptions(\n options: SchemeThemeOptions,\n scheme: ThemeColorSchemeKey,\n): ResolvedSchemeOptions {\n const accent =\n options.accent === undefined\n ? DEFAULT_ACCENT\n : normalizeColor(options.accent, `${scheme}.accent`)\n\n return {\n accent,\n background:\n options.background === undefined\n ? DEFAULT_BACKGROUND[scheme]\n : normalizeColor(options.background, `${scheme}.background`),\n contrast: normalizeContrast(options.contrast, scheme),\n text:\n options.text === undefined\n ? deriveTextColor(accent)\n : normalizeColor(options.text, `${scheme}.text`),\n }\n}\n\n/**\n * Derives the text color from an accent color, keeping the relationship the\n * stock palette has between `gray/500` and `blue/500`: the text color takes\n * the accent's hue (offset the way the stock gray is offset from the stock\n * blue), carries about an eighth of its saturation, and keeps the stock text\n * lightness. Deriving from the stock accent returns the stock text color.\n *\n * @internal\n */\nexport function deriveTextColor(accent: string): string {\n const accentHsl = hexToHsl(normalizeColor(accent, 'accent'))\n const defaultAccent = hexToHsl(DEFAULT_ACCENT)\n const defaultText = hexToHsl(DEFAULT_TEXT)\n\n return hslToHex({\n h: defaultText.h + (accentHsl.h - defaultAccent.h),\n s: Math.min(1, defaultText.s * (accentHsl.s / defaultAccent.s)),\n l: defaultText.l,\n })\n}\n\nfunction normalizeColor(value: string, name: string): string {\n if (typeof value !== 'string' || !isColor(value)) {\n throw new TypeError(\n `Invalid \\`${name}\\` color: ${JSON.stringify(value)} — expected a hex color like #556bfc`,\n )\n }\n\n const lower = value.toLowerCase()\n\n return lower.length === 4\n ? `#${lower[1]}${lower[1]}${lower[2]}${lower[2]}${lower[3]}${lower[3]}`\n : lower\n}\n\nfunction normalizeContrast(value: number | undefined, scheme: ThemeColorSchemeKey): number {\n if (value === undefined) {\n return DEFAULT_CONTRAST\n }\n\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n throw new TypeError(\n `Invalid \\`${scheme}.contrast\\`: ${JSON.stringify(value)} — expected a number between ${MINIMUM_CONTRAST} and ${MAXIMUM_CONTRAST}`,\n )\n }\n\n return Math.min(MAXIMUM_CONTRAST, Math.max(MINIMUM_CONTRAST, value))\n}\n","import {\n color,\n COLOR_TINTS,\n type ColorHueKey,\n type ColorTintKey,\n type ColorTints,\n} from '@sanity/color'\nimport {type ThemeColorSchemeKey} from '@sanity/ui/theme'\n\nimport {hexToHsl, type Hsl, hslToHex, mixHue, relativeLuminance} from './hsl'\nimport {\n type BuildThemeOptions,\n DEFAULT_ACCENT,\n DEFAULT_CONTRAST,\n DEFAULT_TEXT,\n MAXIMUM_CONTRAST,\n MINIMUM_CONTRAST,\n type ResolvedSchemeOptions,\n resolveThemeOptions,\n} from './options'\n\n/**\n * The lightness band the accent and text colors are clamped into — anchors\n * outside it would squeeze most of their scale's tints into an\n * indistinguishable sliver, messing with the rest of the scale.\n */\nconst MINIMUM_ANCHOR_LIGHTNESS = 0.25\nconst MAXIMUM_ANCHOR_LIGHTNESS = 0.75\n\n/**\n * The contrast ratio the dark background must reach against the 400 tints of\n * the text and accent scales — the tints Sanity UI paints muted text and\n * solid buttons with on dark backgrounds. The stock palette sits at ~6.8.\n */\nconst MINIMUM_DARK_BACKGROUND_CONTRAST = 4.5\n\n/** How much lightness each step of a background adjustment removes or adds */\nconst BACKGROUND_LIGHTNESS_STEP = 0.005\n\n/**\n * A color palette generated by {@link buildPalette}: the `palette` config of\n * `buildTheme` from `@sanity/ui/theme`, with every color as a hex string.\n *\n * @public\n */\nexport type GeneratedColorPalette = {black: string; white: string} & Record<\n ColorHueKey,\n Record<ColorTintKey, string>\n>\n\n/**\n * The palettes generated by {@link buildPalette} — one per color scheme.\n *\n * @public\n */\nexport type GeneratedColorPalettes = Record<ThemeColorSchemeKey, GeneratedColorPalette>\n\n/**\n * Generates a Sanity UI color palette for each color scheme from the theme\n * options: in each palette the scheme's accent color replaces the `blue`\n * scale, its text color the `gray` scale, and the backgrounds replace `black`\n * and `white`. All other hues keep their stock `@sanity/color` values. Called\n * with the stock colors, both palettes come back as the stock palette\n * byte-for-byte.\n *\n * ```ts\n * import {buildPalette} from '@sanity/themer'\n *\n * const {light, dark} = buildPalette({light: {accent: '#556bfc'}})\n * ```\n *\n * @public\n */\nexport function buildPalette(options: BuildThemeOptions = {}): GeneratedColorPalettes {\n const resolved = resolveThemeOptions(options)\n\n return {\n dark: buildSchemePalette('dark', resolved.dark, resolved.light.background),\n light: buildSchemePalette('light', resolved.light, resolved.dark.background),\n }\n}\n\n/**\n * Generates the palette of one scheme. The scheme's own background takes the\n * slot the scheme paints with (`black` for dark, `white` for light), while\n * the other slot — which a scheme only touches for a few inverted details —\n * takes the other scheme's background, so the theme stays one palette.\n */\nfunction buildSchemePalette(\n key: ThemeColorSchemeKey,\n scheme: ResolvedSchemeOptions,\n otherBackground: string,\n): GeneratedColorPalette {\n const accentAnchor = clampAnchorLightness(hexToHsl(scheme.accent))\n const textAnchor = clampAnchorLightness(hexToHsl(scheme.text))\n const grayAnchor = applyContrast(textAnchor, accentAnchor, scheme.contrast)\n\n const blueTints = remapTints(color.blue, hexToHsl(DEFAULT_ACCENT), accentAnchor)\n const grayTints = remapTints(color.gray, hexToHsl(DEFAULT_TEXT), grayAnchor)\n\n const anchorLuminances = [\n relativeLuminance(hslToHex(grayAnchor)),\n relativeLuminance(hslToHex(accentAnchor)),\n ]\n\n const darkBackground = key === 'dark' ? scheme.background : otherBackground\n const lightBackground = key === 'light' ? scheme.background : otherBackground\n\n return {\n black: darkenBackground(darkBackground, {\n anchorLuminances,\n tints: [grayTints['400'], blueTints['400']],\n }),\n white: lightenBackground(lightBackground, anchorLuminances),\n gray: grayTints,\n blue: blueTints,\n purple: tintHexes(color.purple),\n magenta: tintHexes(color.magenta),\n red: tintHexes(color.red),\n orange: tintHexes(color.orange),\n yellow: tintHexes(color.yellow),\n green: tintHexes(color.green),\n cyan: tintHexes(color.cyan),\n }\n}\n\n/**\n * Mixes the accent color into the text color according to the contrast: 85\n * applies the text color as-is, 100 removes its tint entirely, and lower\n * values rotate and saturate it towards the accent.\n */\nfunction applyContrast(text: Hsl, accent: Hsl, contrast: number): Hsl {\n if (contrast === DEFAULT_CONTRAST) {\n return text\n }\n\n if (contrast > DEFAULT_CONTRAST) {\n const amount = (contrast - DEFAULT_CONTRAST) / (MAXIMUM_CONTRAST - DEFAULT_CONTRAST)\n\n return {h: text.h, s: text.s * (1 - amount), l: text.l}\n }\n\n const amount = (DEFAULT_CONTRAST - contrast) / (DEFAULT_CONTRAST - MINIMUM_CONTRAST)\n\n return {\n h: mixHue(text.h, accent.h, amount),\n s: text.s + (accent.s - text.s) * amount,\n l: text.l,\n }\n}\n\n/**\n * Rebuilds a stock tint scale around a new anchor color, where `from` is the\n * scale's stock 500 tint: hues rotate along, saturation scales\n * proportionally, and lightness is remapped piecewise so the anchor takes the\n * new lightness while the scale keeps spanning light to dark. When the anchor\n * matches the stock one, every tint comes back unchanged.\n */\nfunction remapTints(tints: ColorTints, from: Hsl, to: Hsl): Record<ColorTintKey, string> {\n const rotation = to.h - from.h\n const result: Partial<Record<ColorTintKey, string>> = {}\n\n for (const tint of COLOR_TINTS) {\n const value = hexToHsl(tints[tint].hex)\n\n result[tint] = hslToHex({\n h: value.h + rotation,\n s: remapSaturation(value.s, from.s, to.s),\n l: remapLightness(value.l, from.l, to.l),\n })\n }\n\n // oxlint-disable-next-line no-unsafe-type-assertion -- the loop assigns every tint key\n return result as Record<ColorTintKey, string>\n}\n\nfunction remapSaturation(value: number, from: number, to: number): number {\n if (from === to) {\n return value\n }\n\n return Math.min(1, value * (to / from))\n}\n\nfunction remapLightness(value: number, from: number, to: number): number {\n if (from === to) {\n return value\n }\n\n if (value <= from) {\n return value * (to / from)\n }\n\n return to + ((value - from) * (1 - to)) / (1 - from)\n}\n\nfunction clampAnchorLightness(anchor: Hsl): Hsl {\n const l = Math.min(MAXIMUM_ANCHOR_LIGHTNESS, Math.max(MINIMUM_ANCHOR_LIGHTNESS, anchor.l))\n\n return l === anchor.l ? anchor : {...anchor, l}\n}\n\n/**\n * Enforces the dark background rules: it can never be lighter than the text\n * or accent colors, and it must reach enough contrast with the 400 tints of\n * their scales — it is made darker until it does (or bottoms out at black).\n */\nfunction darkenBackground(\n hex: string,\n rules: {anchorLuminances: number[]; tints: string[]},\n): string {\n const maximumLuminance = Math.min(...rules.anchorLuminances)\n const tintLuminances = rules.tints.map((tint) => relativeLuminance(tint))\n\n const isDarkEnough = (background: string): boolean => {\n const luminance = relativeLuminance(background)\n\n return (\n luminance < maximumLuminance &&\n tintLuminances.every(\n (tintLuminance) =>\n (tintLuminance + 0.05) / (luminance + 0.05) >= MINIMUM_DARK_BACKGROUND_CONTRAST,\n )\n )\n }\n\n const {h, s, l} = hexToHsl(hex)\n let lightness = l\n let current = hex\n\n while (!isDarkEnough(current) && lightness > 0) {\n lightness = Math.max(0, lightness - BACKGROUND_LIGHTNESS_STEP)\n current = hslToHex({h, s, l: lightness})\n }\n\n return current\n}\n\n/**\n * Enforces the light background rule: it can never be darker than the text\n * or accent colors — it is made lighter until it is (or tops out at white).\n */\nfunction lightenBackground(hex: string, anchorLuminances: number[]): string {\n const minimumLuminance = Math.max(...anchorLuminances)\n\n const {h, s, l} = hexToHsl(hex)\n let lightness = l\n let current = hex\n\n while (relativeLuminance(current) <= minimumLuminance && lightness < 1) {\n lightness = Math.min(1, lightness + BACKGROUND_LIGHTNESS_STEP)\n current = hslToHex({h, s, l: lightness})\n }\n\n return current\n}\n\nfunction tintHexes(tints: ColorTints): Record<ColorTintKey, string> {\n const result: Partial<Record<ColorTintKey, string>> = {}\n\n for (const tint of COLOR_TINTS) {\n result[tint] = tints[tint].hex\n }\n\n // oxlint-disable-next-line no-unsafe-type-assertion -- the loop assigns every tint key\n return result as Record<ColorTintKey, string>\n}\n","import {\n buildTheme as buildUITheme,\n type RootTheme,\n type ThemeColorSchemes_v2,\n v0_v2,\n v2_v0,\n} from '@sanity/ui/theme'\n\nimport {buildPalette} from './buildPalette'\nimport type {BuildThemeOptions} from './options'\n\n/**\n * Generates a Studio theme from a handful of colors per color scheme, by\n * replacing the color palette that the `buildTheme` from `@sanity/ui/theme`\n * otherwise fills with `@sanity/color`: in each scheme the accent color\n * replaces the `blue` scale (primary buttons, focus rings, links), the\n * optional text color the `gray` scale (text, icons, borders), and the\n * optional background replaces `black` (dark) or `white` (light). The result\n * is the same type of theme that `@sanity/ui/theme` builds, ready for the\n * `theme` property of a Studio config.\n *\n * ```ts\n * import {buildTheme} from '@sanity/themer'\n * import {defineConfig} from 'sanity'\n *\n * export const theme = buildTheme({\n * light: {\n * accent: '#556bfc',\n * text: '#727892', // optional — derived from `accent` when omitted\n * background: '#ffffff', // optional\n * contrast: 85, // optional, 15–100\n * },\n * dark: {\n * accent: '#556bfc',\n * background: '#0d0e12',\n * },\n * })\n *\n * export default defineConfig({\n * theme,\n * // ...rest of the config\n * })\n * ```\n *\n * Every option is optional and falls back to the stock Studio colors: called\n * with the stock colors — like the example — or with no options at all, it\n * returns the exact same colors as `buildTheme()` from `@sanity/ui/theme`.\n *\n * @public\n */\nexport function buildTheme(options: BuildThemeOptions = {}): RootTheme {\n const palettes = buildPalette(options)\n\n // Sanity UI renders both schemes of a theme from one palette, so each\n // scheme gets a theme of its own palette, and the two are composed into one\n // theme that takes its light scheme from the one and its dark scheme from\n // the other. The scheme getters keep Sanity UI's lazy rendering: a scheme is\n // only computed once something reads it.\n const light = v0_v2(buildUITheme({palette: palettes.light}))\n const dark = v0_v2(buildUITheme({palette: palettes.dark}))\n\n const color: ThemeColorSchemes_v2 = {\n get light() {\n return light.color.light\n },\n get dark() {\n return dark.color.dark\n },\n }\n\n return v2_v0({...light, color})\n}\n","import type {BuildThemeOptions} from './options'\n\n/**\n * A preset theme: a named set of {@link BuildThemeOptions} ready to pass to\n * `buildTheme`.\n *\n * @public\n */\nexport interface ThemePreset {\n slug: string\n title: string\n /** The preset's theme options, ready to pass to `buildTheme` */\n options: BuildThemeOptions\n}\n\n/**\n * The hosted Themer presets had one accent and one text color for both\n * schemes, and a background per scheme\n */\nfunction scheme(accent: string, text: string, backgrounds: {dark: string; light: string}) {\n return {\n dark: {accent, text, background: backgrounds.dark},\n light: {accent, text, background: backgrounds.light},\n } satisfies BuildThemeOptions\n}\n\n/**\n * Preset themes for `buildTheme`, carried over from the hosted Themer service\n * (themer.sanity.build) presets: each one maps the legacy preset's primary\n * hue to `accent`, its default hue to `text` and its lightest/darkest colors\n * to the backgrounds of the light and dark scheme.\n *\n * ```ts\n * import {buildTheme, presets} from '@sanity/themer'\n *\n * const verdant = presets.find((preset) => preset.slug === 'verdant')\n * const theme = buildTheme(verdant.options)\n * ```\n *\n * @public\n */\nexport const presets: ThemePreset[] = [\n {\n slug: 'studio',\n title: 'Studio',\n options: {},\n },\n {\n slug: 'dew',\n title: 'Dew',\n options: scheme('#d1a308', '#5e63b4', {dark: '#0d0d15', light: '#fcfcfd'}),\n },\n {\n slug: 'pink-synth',\n title: 'Pink Synth',\n options: scheme('#ec4899', '#8b6584', {dark: '#171721', light: '#f7f2f5'}),\n },\n {\n slug: 'pixel-art',\n title: 'Pixel Art',\n options: scheme('#f10784', '#57619c', {dark: '#0d0e15', light: '#fcfcfd'}),\n },\n {\n slug: 'retro-colonial',\n title: 'Retro Colonial',\n options: scheme('#fa7a78', '#8bb9b5', {dark: '#0d1515', light: '#fcfdfd'}),\n },\n {\n slug: 'rosabel',\n title: 'Rosabel',\n options: scheme('#ed2555', '#9d8966', {dark: '#15120d', light: '#fdfdfc'}),\n },\n {\n slug: 'stereofidelic',\n title: 'Stereofidelic',\n options: scheme('#f13009', '#678e9a', {dark: '#0e1315', light: '#fcfdfd'}),\n },\n {\n slug: 'verdant',\n title: 'Verdant',\n options: scheme('#1cb485', '#5c9199', {dark: '#0d1415', light: '#fcfdfd'}),\n },\n]\n"],"mappings":"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"}
package/dist/tool.d.ts CHANGED
@@ -52,6 +52,6 @@ interface ThemerToolOptions {
52
52
  *
53
53
  * @alpha
54
54
  */
55
- export declare const themerTool: import("sanity").Plugin<void | ThemerToolOptions>;
56
- export type { ThemerToolOptions };
55
+ declare const themerTool: import("sanity").Plugin<void | ThemerToolOptions>;
56
+ export { type ThemerToolOptions, themerTool };
57
57
  //# sourceMappingURL=tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","names":[],"sources":["../src/tool/plugin.tsx"],"mappings":";;;;;;;;;UAciB;;;;;;;;;;;;;;;EAef,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA+BE,6BAAU,cAAA"}
1
+ {"version":3,"file":"tool.d.ts","names":[],"sources":["../src/tool/plugin.tsx"],"mappings":";;;;;;;;;UAciB;;;;;;;;;;;;;;;EAef,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA+BE,6BAAU,cAAA"}