@sanity/themer 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tool.js CHANGED
@@ -1,12 +1,17 @@
1
- import { t as isColor } from "./_chunks/mix.js";
2
- import { n as createTheme, t as presets } from "./_chunks/presets.js";
3
- import { color } from "@sanity/color";
1
+ import { a as parseHuesFromUrl, c as isColor, i as createTheme, l as applyHues, o as createTonesFromHues, s as presets, t as hues } from "./defaults-DJtuhI2W.js";
2
+ import { COLOR_TINTS } from "@sanity/color";
3
+ import { c } from "react-compiler-runtime";
4
4
  import { definePlugin } from "sanity";
5
- import { Box, Button, Card, Code, Flex, Layer, Select, Stack, Text, ThemeProvider, useToast } from "@sanity/ui";
6
- import { createContext, useContext, useEffect, useMemo, useState } from "react";
5
+ import { Box, Button, Card, Code, Flex, Grid, Layer, Select, Stack, Text, TextInput, ThemeProvider, Tooltip, useToast } from "@sanity/ui";
6
+ import { createContext, useContext, useEffect, useState } from "react";
7
+ import { ChevronDownIcon } from "@sanity/icons/ChevronDown";
8
+ import { ChevronRightIcon } from "@sanity/icons/ChevronRight";
7
9
  import { ClipboardIcon } from "@sanity/icons/Clipboard";
8
10
  import { CloseIcon } from "@sanity/icons/Close";
9
11
  import { ResetIcon } from "@sanity/icons/Reset";
12
+ import { registerLanguage } from "react-refractor";
13
+ import typescript from "refractor/typescript";
14
+ import { styled } from "styled-components";
10
15
  import { jsx, jsxs } from "react/jsx-runtime";
11
16
  import { ColorWheelIcon } from "@sanity/icons/ColorWheel";
12
17
  /** @internal */
@@ -17,93 +22,193 @@ function useThemer() {
17
22
  if (!context) throw Error("useThemer must be used within the `themerTool` plugin");
18
23
  return context;
19
24
  }
20
- /** @internal */
21
- const THEMER_FIELDS = [
25
+ /**
26
+ * The six hues in the order the hosted Themer service listed them, used for
27
+ * the sidebar's editors and the generated snippet alike.
28
+ *
29
+ * @internal
30
+ */
31
+ const HUE_KEYS = [
32
+ "default",
33
+ "primary",
34
+ "transparent",
35
+ "positive",
36
+ "caution",
37
+ "critical"
38
+ ], HUE_FIELDS = [
39
+ {
40
+ key: "default",
41
+ title: "Default",
42
+ description: "Text, icons and most surfaces"
43
+ },
22
44
  {
23
45
  key: "primary",
24
46
  title: "Primary",
25
- description: "Buttons, focus rings and links",
26
- defaultValue: color.blue[500].hex
47
+ description: "Focus rings, links and primary buttons"
27
48
  },
28
49
  {
29
- key: "gray",
30
- title: "Gray",
31
- description: "Neutral surfaces, borders and text",
32
- defaultValue: color.gray[500].hex
50
+ key: "transparent",
51
+ title: "Transparent",
52
+ description: "The backdrop behind panes"
33
53
  },
34
54
  {
35
55
  key: "positive",
36
56
  title: "Positive",
37
- description: "Success accents",
38
- defaultValue: color.green[500].hex
57
+ description: "Success badges and prompts"
39
58
  },
40
59
  {
41
60
  key: "caution",
42
61
  title: "Caution",
43
- description: "Warning accents",
44
- defaultValue: color.yellow[500].hex
62
+ description: "Warnings and draft indicators"
45
63
  },
46
64
  {
47
65
  key: "critical",
48
66
  title: "Critical",
49
- description: "Errors and destructive actions",
50
- defaultValue: color.red[500].hex
51
- },
52
- {
53
- key: "lightest",
54
- title: "Lightest",
55
- description: "Light mode background",
56
- defaultValue: color.white.hex
57
- },
58
- {
59
- key: "darkest",
60
- title: "Darkest",
61
- description: "Dark mode background",
62
- defaultValue: color.black.hex
67
+ description: "Errors and destructive actions"
63
68
  }
69
+ ], MID_POINTS = [
70
+ 50,
71
+ 100,
72
+ 200,
73
+ 300,
74
+ 400,
75
+ 500,
76
+ 600,
77
+ 700,
78
+ 800,
79
+ 900,
80
+ 950
81
+ ], HUE_PROPERTIES = [
82
+ "mid",
83
+ "midPoint",
84
+ "lightest",
85
+ "darkest"
64
86
  ];
87
+ /** @internal */
88
+ function sameHues(a, b) {
89
+ return HUE_KEYS.every((key) => HUE_PROPERTIES.every((property) => {
90
+ let left = a[key][property], right = b[key][property];
91
+ return typeof left == "string" && typeof right == "string" ? left.toLowerCase() === right.toLowerCase() : left === right;
92
+ }));
93
+ }
65
94
  /**
66
- * Serializes colors into the `createTheme` call to paste into
67
- * `sanity.config.ts`.
95
+ * Reduces resolved hues to the minimal `PartialHues` that recreates them
96
+ * through the legacy `createTheme`. The subtlety is `applyHues`' quirk of
97
+ * resetting a customized `mid`'s mid point to 500: whenever `mid` is included,
98
+ * the baseline for `midPoint` is 500 rather than the hue's default.
68
99
  *
69
100
  * @internal
70
101
  */
71
- function createThemeSnippet(colors) {
72
- let entries = THEMER_FIELDS.filter(({ key }) => colors[key]).map(({ key }) => ` ${key}: '${colors[key]}',`);
73
- return `import {createTheme} from '@sanity/themer'\n\nexport const theme = ${entries.length === 0 ? "createTheme()" : `createTheme({\n${entries.join("\n")}\n})`}\n`;
102
+ function diffHues(hues$1) {
103
+ let diff = {};
104
+ for (let key of HUE_KEYS) {
105
+ let hue = hues$1[key], base = hues[key], patch = {};
106
+ hue.mid.toLowerCase() !== base.mid && (patch.mid = hue.mid.toLowerCase());
107
+ let impliedMidPoint = patch.mid === void 0 ? base.midPoint : 500;
108
+ hue.midPoint !== impliedMidPoint && (patch.midPoint = hue.midPoint), hue.lightest.toLowerCase() !== base.lightest && (patch.lightest = hue.lightest.toLowerCase()), hue.darkest.toLowerCase() !== base.darkest && (patch.darkest = hue.darkest.toLowerCase()), Object.keys(patch).length > 0 && (diff[key] = patch);
109
+ }
110
+ return diff;
74
111
  }
75
- const swatchStyle = {
76
- width: 33,
77
- height: 33,
78
- padding: 0,
79
- border: "1px solid var(--card-border-color)",
80
- borderRadius: 6,
81
- background: "transparent",
82
- cursor: "pointer",
83
- flex: "none"
84
- };
85
- function sameColors(a, b) {
86
- return THEMER_FIELDS.every(({ key }) => (a[key]?.toLowerCase() ?? void 0) === (b[key]?.toLowerCase() ?? void 0));
112
+ function serializeHue(hue) {
113
+ let parts = [];
114
+ return hue.mid !== void 0 && parts.push(`mid: '${hue.mid}'`), hue.midPoint !== void 0 && parts.push(`midPoint: ${hue.midPoint}`), hue.lightest !== void 0 && parts.push(`lightest: '${hue.lightest}'`), hue.darkest !== void 0 && parts.push(`darkest: '${hue.darkest}'`), `{${parts.join(", ")}}`;
87
115
  }
116
+ /**
117
+ * Serializes hues into the `createTheme` call to paste into
118
+ * `sanity.config.ts`, keeping only what differs from the default hues.
119
+ *
120
+ * Hues that match the defaults entirely are the stock Studio theme, which
121
+ * needs nothing from this package — so they serialize to a bare `buildTheme()`
122
+ * instead.
123
+ *
124
+ * @internal
125
+ */
126
+ function createThemeSnippet(hues) {
127
+ let diff = diffHues(hues), entries = [];
128
+ for (let key of HUE_KEYS) {
129
+ let patch = diff[key];
130
+ patch && entries.push(` ${key}: ${serializeHue(patch)},`);
131
+ }
132
+ return entries.length === 0 ? "import {buildTheme} from '@sanity/ui/theme'\n\nexport const theme = buildTheme()\n" : `import {createTheme} from '@sanity/themer/legacy'\n\nexport const theme = createTheme({\n${entries.join("\n")}\n})\n`;
133
+ }
134
+ registerLanguage(typescript);
135
+ /**
136
+ * The tool's take on the hosted service's presets: Tailwind Cyan is hidden and
137
+ * the default "Studio v3" preset reads just "Studio". The legacy `presets`
138
+ * export itself keeps both untouched, for parity with
139
+ * `https://themer.sanity.build/api/hues`.
140
+ */
141
+ const TOOL_PRESETS = [];
142
+ for (let preset of presets) preset.slug !== "tw-cyan" && TOOL_PRESETS.push(preset.slug === "default" ? {
143
+ ...preset,
144
+ title: "Studio"
145
+ } : preset);
146
+ /**
147
+ * `<input type="color">` paints the color into a shadow-DOM swatch that brings
148
+ * its own border and padding, which then sits inside ours as a second border.
149
+ * Stripping that chrome leaves the themed border as the only one.
150
+ */
151
+ const Swatch = styled.input.withConfig({
152
+ displayName: "Swatch",
153
+ componentId: "sc-g4qzjc-0"
154
+ })`box-sizing:border-box;flex:none;width:33px;height:33px;padding:0;border:1px solid var(--card-border-color);border-radius:4px;background:none;cursor:pointer;&::-webkit-color-swatch-wrapper{padding:0;}&::-webkit-color-swatch{border:none;border-radius:3px;}&::-moz-color-swatch{border:none;border-radius:3px;}`;
88
155
  /** Expands `#abc` to `#aabbcc`, which is the only format `<input type="color">` accepts */
89
156
  function expandHex(hex) {
90
157
  return hex.length === 4 ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex;
91
158
  }
159
+ /** Prefixes bare query strings so `parseHuesFromUrl` accepts them too */
160
+ function normalizeImportUrl(input) {
161
+ let trimmed = input.trim();
162
+ return trimmed.startsWith("http") || trimmed.startsWith("?") ? trimmed : `?${trimmed}`;
163
+ }
92
164
  /**
93
- * The themer sidebar: a preset picker and a handful of color pickers that
94
- * generate the previewed theme, plus the `createTheme` snippet to make it
95
- * permanent.
165
+ * The themer sidebar: presets, a themer.sanity.build URL importer and the
166
+ * per-hue editors of the hosted Themer service mid color, mid point,
167
+ * lightest and darkest — that generate the previewed legacy theme, plus the
168
+ * `createTheme` snippet to make it permanent.
96
169
  *
97
170
  * @internal
98
171
  */
99
172
  function ThemerSidebar() {
100
- let { baseColors, colors, setColors, setOpen } = useThemer(), toast = useToast(), active = colors ?? baseColors, activePresetSlug = presets.find((preset) => sameColors(preset.colors, active))?.slug ?? "", snippet = createThemeSnippet(active), handleFieldChange = (key, value) => {
101
- let next = { ...active };
102
- value === void 0 ? delete next[key] : next[key] = value, setColors(next);
103
- }, handlePresetChange = (slug) => {
104
- let preset = presets.find((candidate) => candidate.slug === slug);
105
- preset && setColors({ ...preset.colors });
106
- }, handleCopy = async () => {
173
+ let $ = c(71), { baseHues, hues, setHues, setOpen } = useThemer(), toast = useToast(), [expandedHue, setExpandedHue] = useState(null), [importUrl, setImportUrl] = useState(""), active = hues ?? baseHues, t0;
174
+ $[0] === active ? t0 = $[1] : (t0 = createTonesFromHues(active), $[0] = active, $[1] = t0);
175
+ let tones = t0, t1;
176
+ $[2] === active ? t1 = $[3] : (t1 = TOOL_PRESETS.find((preset) => sameHues(preset.hues, active))?.slug ?? "", $[2] = active, $[3] = t1);
177
+ let activePresetSlug = t1, t2;
178
+ $[4] === active ? t2 = $[5] : (t2 = createThemeSnippet(active), $[4] = active, $[5] = t2);
179
+ let snippet = t2, t3;
180
+ $[6] !== active || $[7] !== setHues ? (t3 = (key, patch) => {
181
+ setHues({
182
+ ...active,
183
+ [key]: {
184
+ ...active[key],
185
+ ...patch
186
+ }
187
+ });
188
+ }, $[6] = active, $[7] = setHues, $[8] = t3) : t3 = $[8];
189
+ let handleHueChange = t3, t4;
190
+ $[9] === setHues ? t4 = $[10] : (t4 = (slug) => {
191
+ let preset_0 = TOOL_PRESETS.find((candidate) => candidate.slug === slug);
192
+ preset_0 && setHues(preset_0.hues);
193
+ }, $[9] = setHues, $[10] = t4);
194
+ let handlePresetChange = t4, t5;
195
+ $[11] !== importUrl || $[12] !== setHues || $[13] !== toast ? (t5 = () => {
196
+ if (importUrl.trim()) try {
197
+ setHues(parseHuesFromUrl(normalizeImportUrl(importUrl))), setImportUrl(""), toast.push({
198
+ status: "success",
199
+ title: "Imported theme from URL"
200
+ });
201
+ } catch (t6) {
202
+ let error = t6;
203
+ toast.push({
204
+ status: "error",
205
+ title: "Could not import the URL",
206
+ description: error instanceof Error ? error.message : String(error)
207
+ });
208
+ }
209
+ }, $[11] = importUrl, $[12] = setHues, $[13] = toast, $[14] = t5) : t5 = $[14];
210
+ let handleImport = t5, t6;
211
+ $[15] !== snippet || $[16] !== toast ? (t6 = async () => {
107
212
  try {
108
213
  await navigator.clipboard.writeText(snippet), toast.push({
109
214
  status: "success",
@@ -115,149 +220,419 @@ function ThemerSidebar() {
115
220
  title: "Could not copy the theme"
116
221
  });
117
222
  }
118
- };
119
- return /* @__PURE__ */ jsx(Card, {
223
+ }, $[15] = snippet, $[16] = toast, $[17] = t6) : t6 = $[17];
224
+ let handleCopy = t6, t7;
225
+ $[18] === Symbol.for("react.memo_cache_sentinel") ? (t7 = /* @__PURE__ */ jsx(Box, {
226
+ flex: 1,
227
+ paddingLeft: 1,
228
+ children: /* @__PURE__ */ jsx(Text, {
229
+ size: 1,
230
+ weight: "semibold",
231
+ children: "Themer"
232
+ })
233
+ }), $[18] = t7) : t7 = $[18];
234
+ let t8;
235
+ $[19] === setOpen ? t8 = $[20] : (t8 = /* @__PURE__ */ jsx(Card, {
236
+ borderBottom: !0,
237
+ padding: 3,
238
+ children: /* @__PURE__ */ jsxs(Flex, {
239
+ align: "center",
240
+ gap: 2,
241
+ children: [t7, /* @__PURE__ */ jsx(Button, {
242
+ icon: CloseIcon,
243
+ mode: "bleed",
244
+ onClick: () => setOpen(!1),
245
+ padding: 2,
246
+ title: "Close themer"
247
+ })]
248
+ })
249
+ }), $[19] = setOpen, $[20] = t8);
250
+ let t9;
251
+ $[21] === Symbol.for("react.memo_cache_sentinel") ? (t9 = /* @__PURE__ */ jsx(Text, {
252
+ size: 1,
253
+ weight: "medium",
254
+ children: "Presets"
255
+ }), $[21] = t9) : t9 = $[21];
256
+ let t10;
257
+ $[22] !== activePresetSlug || $[23] !== handlePresetChange ? (t10 = TOOL_PRESETS.map((preset_1) => /* @__PURE__ */ jsx(PresetButton, {
258
+ active: preset_1.slug === activePresetSlug,
259
+ onClick: handlePresetChange,
260
+ preset: preset_1
261
+ }, preset_1.slug)), $[22] = activePresetSlug, $[23] = handlePresetChange, $[24] = t10) : t10 = $[24];
262
+ let t11;
263
+ $[25] === t10 ? t11 = $[26] : (t11 = /* @__PURE__ */ jsxs(Stack, {
264
+ gap: 3,
265
+ children: [t9, /* @__PURE__ */ jsx(Grid, {
266
+ gap: 2,
267
+ gridTemplateColumns: 2,
268
+ children: t10
269
+ })]
270
+ }), $[25] = t10, $[26] = t11);
271
+ let t12;
272
+ $[27] === Symbol.for("react.memo_cache_sentinel") ? (t12 = /* @__PURE__ */ jsx(Text, {
273
+ size: 1,
274
+ weight: "medium",
275
+ children: "Import"
276
+ }), $[27] = t12) : t12 = $[27];
277
+ let t13;
278
+ $[28] === handleImport ? t13 = $[29] : (t13 = (event) => {
279
+ event.preventDefault(), handleImport();
280
+ }, $[28] = handleImport, $[29] = t13);
281
+ let t14;
282
+ $[30] === Symbol.for("react.memo_cache_sentinel") ? (t14 = (event_0) => setImportUrl(event_0.currentTarget.value), $[30] = t14) : t14 = $[30];
283
+ let t15;
284
+ $[31] === importUrl ? t15 = $[32] : (t15 = /* @__PURE__ */ jsx(Box, {
285
+ flex: 1,
286
+ children: /* @__PURE__ */ jsx(TextInput, {
287
+ "aria-label": "Hosted Themer URL",
288
+ fontSize: 1,
289
+ onChange: t14,
290
+ padding: 2,
291
+ placeholder: "themer.sanity.build URL",
292
+ value: importUrl
293
+ })
294
+ }), $[31] = importUrl, $[32] = t15);
295
+ let t16;
296
+ $[33] === Symbol.for("react.memo_cache_sentinel") ? (t16 = /* @__PURE__ */ jsx(Button, {
297
+ mode: "ghost",
298
+ padding: 2,
299
+ text: "Import",
300
+ type: "submit"
301
+ }), $[33] = t16) : t16 = $[33];
302
+ let t17;
303
+ $[34] === t15 ? t17 = $[35] : (t17 = /* @__PURE__ */ jsxs(Flex, {
304
+ gap: 2,
305
+ children: [t15, t16]
306
+ }), $[34] = t15, $[35] = t17);
307
+ let t18;
308
+ $[36] !== t13 || $[37] !== t17 ? (t18 = /* @__PURE__ */ jsxs(Stack, {
309
+ gap: 3,
310
+ children: [t12, /* @__PURE__ */ jsx("form", {
311
+ onSubmit: t13,
312
+ children: t17
313
+ })]
314
+ }), $[36] = t13, $[37] = t17, $[38] = t18) : t18 = $[38];
315
+ let t19;
316
+ $[39] === Symbol.for("react.memo_cache_sentinel") ? (t19 = /* @__PURE__ */ jsx(Text, {
317
+ size: 1,
318
+ weight: "medium",
319
+ children: "Hues"
320
+ }), $[39] = t19) : t19 = $[39];
321
+ let t20;
322
+ $[40] !== active || $[41] !== expandedHue || $[42] !== handleHueChange || $[43] !== tones ? (t20 = HUE_FIELDS.map((field) => /* @__PURE__ */ jsx(HueSection, {
323
+ expanded: expandedHue === field.key,
324
+ field,
325
+ hue: active[field.key],
326
+ onChange: handleHueChange,
327
+ onToggle: () => setExpandedHue(expandedHue === field.key ? null : field.key),
328
+ tints: tones[field.key]
329
+ }, field.key)), $[40] = active, $[41] = expandedHue, $[42] = handleHueChange, $[43] = tones, $[44] = t20) : t20 = $[44];
330
+ let t21;
331
+ $[45] === t20 ? t21 = $[46] : (t21 = /* @__PURE__ */ jsxs(Stack, {
332
+ gap: 3,
333
+ children: [t19, /* @__PURE__ */ jsx(Stack, {
334
+ gap: 1,
335
+ children: t20
336
+ })]
337
+ }), $[45] = t20, $[46] = t21);
338
+ let t22;
339
+ $[47] === Symbol.for("react.memo_cache_sentinel") ? (t22 = /* @__PURE__ */ jsx(Text, {
340
+ size: 1,
341
+ weight: "medium",
342
+ children: "Add to your config"
343
+ }), $[47] = t22) : t22 = $[47];
344
+ let t23;
345
+ $[48] === snippet ? t23 = $[49] : (t23 = /* @__PURE__ */ jsx(Card, {
346
+ border: !0,
347
+ overflow: "auto",
348
+ padding: 2,
349
+ radius: 2,
350
+ tone: "transparent",
351
+ children: /* @__PURE__ */ jsx(Code, {
352
+ language: "ts",
353
+ size: 0,
354
+ children: snippet
355
+ })
356
+ }), $[48] = snippet, $[49] = t23);
357
+ let t24;
358
+ $[50] === handleCopy ? t24 = $[51] : (t24 = /* @__PURE__ */ jsx(Button, {
359
+ icon: ClipboardIcon,
360
+ mode: "ghost",
361
+ onClick: () => void handleCopy(),
362
+ text: "Copy"
363
+ }), $[50] = handleCopy, $[51] = t24);
364
+ let t25 = hues === null, t26;
365
+ $[52] === setHues ? t26 = $[53] : (t26 = () => setHues(null), $[52] = setHues, $[53] = t26);
366
+ let t27;
367
+ $[54] !== t25 || $[55] !== t26 ? (t27 = /* @__PURE__ */ jsx(Button, {
368
+ disabled: t25,
369
+ icon: ResetIcon,
370
+ mode: "ghost",
371
+ onClick: t26,
372
+ text: "Reset",
373
+ tone: "critical"
374
+ }), $[54] = t25, $[55] = t26, $[56] = t27) : t27 = $[56];
375
+ let t28;
376
+ $[57] !== t24 || $[58] !== t27 ? (t28 = /* @__PURE__ */ jsxs(Flex, {
377
+ gap: 2,
378
+ children: [t24, t27]
379
+ }), $[57] = t24, $[58] = t27, $[59] = t28) : t28 = $[59];
380
+ let t29;
381
+ $[60] !== t23 || $[61] !== t28 ? (t29 = /* @__PURE__ */ jsxs(Stack, {
382
+ gap: 3,
383
+ children: [
384
+ t22,
385
+ t23,
386
+ t28
387
+ ]
388
+ }), $[60] = t23, $[61] = t28, $[62] = t29) : t29 = $[62];
389
+ let t30;
390
+ $[63] !== t11 || $[64] !== t18 || $[65] !== t21 || $[66] !== t29 ? (t30 = /* @__PURE__ */ jsx(Box, {
391
+ flex: 1,
392
+ overflow: "auto",
393
+ padding: 3,
394
+ children: /* @__PURE__ */ jsxs(Stack, {
395
+ gap: 4,
396
+ children: [
397
+ t11,
398
+ t18,
399
+ t21,
400
+ t29
401
+ ]
402
+ })
403
+ }), $[63] = t11, $[64] = t18, $[65] = t21, $[66] = t29, $[67] = t30) : t30 = $[67];
404
+ let t31;
405
+ return $[68] !== t30 || $[69] !== t8 ? (t31 = /* @__PURE__ */ jsx(Card, {
120
406
  height: "fill",
121
407
  children: /* @__PURE__ */ jsxs(Flex, {
122
408
  direction: "column",
123
409
  height: "fill",
124
- children: [/* @__PURE__ */ jsx(Card, {
125
- borderBottom: !0,
126
- padding: 3,
127
- children: /* @__PURE__ */ jsxs(Flex, {
128
- align: "center",
129
- gap: 2,
130
- children: [/* @__PURE__ */ jsx(Box, {
131
- flex: 1,
132
- paddingLeft: 1,
133
- children: /* @__PURE__ */ jsx(Text, {
134
- size: 1,
135
- weight: "semibold",
136
- children: "Themer"
137
- })
138
- }), /* @__PURE__ */ jsx(Button, {
139
- icon: CloseIcon,
140
- mode: "bleed",
141
- onClick: () => setOpen(!1),
142
- padding: 2,
143
- title: "Close themer"
144
- })]
145
- })
146
- }), /* @__PURE__ */ jsx(Box, {
147
- flex: 1,
148
- overflow: "auto",
149
- padding: 4,
150
- children: /* @__PURE__ */ jsxs(Stack, {
151
- gap: 5,
152
- children: [
153
- /* @__PURE__ */ jsxs(Stack, {
154
- gap: 3,
155
- children: [/* @__PURE__ */ jsx(Text, {
156
- size: 1,
157
- weight: "medium",
158
- children: "Preset"
159
- }), /* @__PURE__ */ jsxs(Select, {
160
- onChange: (event) => handlePresetChange(event.currentTarget.value),
161
- value: activePresetSlug,
162
- children: [activePresetSlug === "" && /* @__PURE__ */ jsx("option", {
163
- value: "",
164
- children: "Custom"
165
- }), presets.map((preset) => /* @__PURE__ */ jsx("option", {
166
- value: preset.slug,
167
- children: preset.title
168
- }, preset.slug))]
169
- })]
170
- }),
171
- /* @__PURE__ */ jsx(Stack, {
172
- gap: 4,
173
- children: THEMER_FIELDS.map((field) => /* @__PURE__ */ jsx(ColorField, {
174
- field,
175
- onChange: handleFieldChange,
176
- value: active[field.key]
177
- }, field.key))
178
- }),
179
- /* @__PURE__ */ jsxs(Stack, {
180
- gap: 3,
181
- children: [
182
- /* @__PURE__ */ jsx(Text, {
183
- size: 1,
184
- weight: "medium",
185
- children: "Add to your config"
186
- }),
187
- /* @__PURE__ */ jsx(Card, {
188
- border: !0,
189
- overflow: "auto",
190
- padding: 3,
191
- radius: 2,
192
- tone: "transparent",
193
- children: /* @__PURE__ */ jsx(Code, {
194
- language: "ts",
195
- size: 0,
196
- children: snippet
197
- })
198
- }),
199
- /* @__PURE__ */ jsxs(Flex, {
200
- gap: 2,
201
- children: [/* @__PURE__ */ jsx(Button, {
202
- icon: ClipboardIcon,
203
- mode: "ghost",
204
- onClick: () => void handleCopy(),
205
- text: "Copy"
206
- }), /* @__PURE__ */ jsx(Button, {
207
- disabled: colors === null,
208
- icon: ResetIcon,
209
- mode: "ghost",
210
- onClick: () => setColors(null),
211
- text: "Reset",
212
- tone: "critical"
213
- })]
214
- })
215
- ]
216
- })
217
- ]
218
- })
219
- })]
410
+ children: [t8, t30]
220
411
  })
221
- });
412
+ }), $[68] = t30, $[69] = t8, $[70] = t31) : t31 = $[70], t31;
413
+ }
414
+ const paletteStyle = {
415
+ display: "flex",
416
+ gap: 1,
417
+ background: "var(--card-border-color)",
418
+ height: 21,
419
+ borderRadius: 3,
420
+ overflow: "hidden",
421
+ boxShadow: "inset 0 0 0 1px var(--card-border-color)"
422
+ };
423
+ /** A little color palette of the hue mid colors a preset would apply */
424
+ function PresetButton(props) {
425
+ let $ = c(17), { active, onClick, preset } = props, t0;
426
+ $[0] !== onClick || $[1] !== preset.slug ? (t0 = () => onClick(preset.slug), $[0] = onClick, $[1] = preset.slug, $[2] = t0) : t0 = $[2];
427
+ let t1 = preset.title, t2;
428
+ $[3] === preset.hues ? t2 = $[4] : (t2 = HUE_KEYS.map((key) => /* @__PURE__ */ jsx("span", { style: {
429
+ flex: 1,
430
+ background: preset.hues[key].mid
431
+ } }, key)), $[3] = preset.hues, $[4] = t2);
432
+ let t3;
433
+ $[5] === t2 ? t3 = $[6] : (t3 = /* @__PURE__ */ jsx("span", {
434
+ style: paletteStyle,
435
+ children: t2
436
+ }), $[5] = t2, $[6] = t3);
437
+ let t4;
438
+ $[7] === preset.title ? t4 = $[8] : (t4 = /* @__PURE__ */ jsx(Text, {
439
+ align: "left",
440
+ size: 1,
441
+ textOverflow: "ellipsis",
442
+ children: preset.title
443
+ }), $[7] = preset.title, $[8] = t4);
444
+ let t5;
445
+ $[9] !== t3 || $[10] !== t4 ? (t5 = /* @__PURE__ */ jsxs(Stack, {
446
+ as: "span",
447
+ gap: 2,
448
+ children: [t3, t4]
449
+ }), $[9] = t3, $[10] = t4, $[11] = t5) : t5 = $[11];
450
+ let t6;
451
+ return $[12] !== active || $[13] !== preset.title || $[14] !== t0 || $[15] !== t5 ? (t6 = /* @__PURE__ */ jsx(Button, {
452
+ mode: "ghost",
453
+ onClick: t0,
454
+ padding: 2,
455
+ selected: active,
456
+ title: t1,
457
+ children: t5
458
+ }), $[12] = active, $[13] = preset.title, $[14] = t0, $[15] = t5, $[16] = t6) : t6 = $[16], t6;
222
459
  }
223
- function ColorField(props) {
224
- let { field, value, onChange } = props;
225
- return /* @__PURE__ */ jsxs(Flex, {
460
+ const rampStyle = {
461
+ ...paletteStyle,
462
+ height: 13,
463
+ borderRadius: 2
464
+ };
465
+ /** One hue of the theme: a collapsible header with the generated tint ramp */
466
+ function HueSection(props) {
467
+ let $ = c(35), { expanded, field, hue, onChange, onToggle, tints } = props, t0 = expanded ? "transparent" : void 0, t1 = expanded ? 3 : 0, t2 = expanded ? 3 : 0, t3 = field.description, t4;
468
+ $[0] === expanded ? t4 = $[1] : (t4 = /* @__PURE__ */ jsx(Text, {
469
+ size: 1,
470
+ children: jsx(expanded ? ChevronDownIcon : ChevronRightIcon, {})
471
+ }), $[0] = expanded, $[1] = t4);
472
+ let t5;
473
+ $[2] === Symbol.for("react.memo_cache_sentinel") ? (t5 = { width: 76 }, $[2] = t5) : t5 = $[2];
474
+ let t6;
475
+ $[3] === field.title ? t6 = $[4] : (t6 = /* @__PURE__ */ jsx(Box, {
476
+ as: "span",
477
+ style: t5,
478
+ children: /* @__PURE__ */ jsx(Text, {
479
+ size: 1,
480
+ textOverflow: "ellipsis",
481
+ weight: "medium",
482
+ children: field.title
483
+ })
484
+ }), $[3] = field.title, $[4] = t6);
485
+ let t7;
486
+ $[5] === Symbol.for("react.memo_cache_sentinel") ? (t7 = {
487
+ ...rampStyle,
488
+ flex: 1
489
+ }, $[5] = t7) : t7 = $[5];
490
+ let t8;
491
+ $[6] === tints ? t8 = $[7] : (t8 = COLOR_TINTS.map((tint) => /* @__PURE__ */ jsx("span", { style: {
492
+ flex: 1,
493
+ background: tints[tint].hex
494
+ } }, tint)), $[6] = tints, $[7] = t8);
495
+ let t9;
496
+ $[8] === t8 ? t9 = $[9] : (t9 = /* @__PURE__ */ jsx("span", {
497
+ style: t7,
498
+ children: t8
499
+ }), $[8] = t8, $[9] = t9);
500
+ let t10;
501
+ $[10] !== t4 || $[11] !== t6 || $[12] !== t9 ? (t10 = /* @__PURE__ */ jsxs(Flex, {
226
502
  align: "center",
503
+ as: "span",
504
+ gap: 2,
505
+ children: [
506
+ t4,
507
+ t6,
508
+ t9
509
+ ]
510
+ }), $[10] = t4, $[11] = t6, $[12] = t9, $[13] = t10) : t10 = $[13];
511
+ let t11;
512
+ $[14] !== expanded || $[15] !== field.description || $[16] !== onToggle || $[17] !== t10 ? (t11 = /* @__PURE__ */ jsx(Button, {
513
+ "aria-expanded": expanded,
514
+ mode: "bleed",
515
+ onClick: onToggle,
516
+ padding: 2,
517
+ title: t3,
518
+ children: t10
519
+ }), $[14] = expanded, $[15] = field.description, $[16] = onToggle, $[17] = t10, $[18] = t11) : t11 = $[18];
520
+ let t12;
521
+ $[19] !== expanded || $[20] !== field.description || $[21] !== field.key || $[22] !== field.title || $[23] !== hue || $[24] !== onChange ? (t12 = expanded && /* @__PURE__ */ jsxs(Stack, {
227
522
  gap: 3,
523
+ paddingX: 3,
228
524
  children: [
229
- /* @__PURE__ */ jsxs(Stack, {
230
- flex: 1,
525
+ /* @__PURE__ */ jsx(Text, {
526
+ muted: !0,
527
+ size: 0,
528
+ children: field.description
529
+ }),
530
+ /* @__PURE__ */ jsx(ColorRow, {
531
+ onChange: (mid) => onChange(field.key, { mid }),
532
+ title: "Mid",
533
+ value: hue.mid
534
+ }),
535
+ /* @__PURE__ */ jsxs(Flex, {
536
+ align: "center",
231
537
  gap: 2,
232
- children: [/* @__PURE__ */ jsx(Text, {
233
- size: 1,
234
- weight: "medium",
235
- children: field.title
236
- }), /* @__PURE__ */ jsx(Text, {
237
- muted: !0,
238
- size: 0,
239
- children: value ?? field.description
538
+ children: [/* @__PURE__ */ jsxs(Stack, {
539
+ flex: 1,
540
+ gap: 2,
541
+ children: [/* @__PURE__ */ jsx(Text, {
542
+ size: 1,
543
+ children: "Mid point"
544
+ }), /* @__PURE__ */ jsx(Text, {
545
+ muted: !0,
546
+ size: 0,
547
+ children: "The tint the mid color sits at"
548
+ })]
549
+ }), /* @__PURE__ */ jsx(Select, {
550
+ "aria-label": `${field.title} mid point`,
551
+ fontSize: 1,
552
+ onChange: (event) => {
553
+ let value = Number(event.currentTarget.value), midPoint = MID_POINTS.find((candidate) => candidate === value);
554
+ midPoint !== void 0 && onChange(field.key, { midPoint });
555
+ },
556
+ padding: 2,
557
+ value: hue.midPoint,
558
+ children: MID_POINTS.map(_temp$1)
240
559
  })]
241
560
  }),
242
- value !== void 0 && /* @__PURE__ */ jsx(Button, {
243
- icon: CloseIcon,
244
- mode: "bleed",
245
- onClick: () => onChange(field.key, void 0),
246
- padding: 2,
247
- title: `Reset ${field.title}`
561
+ /* @__PURE__ */ jsx(ColorRow, {
562
+ onChange: (lightest) => onChange(field.key, { lightest }),
563
+ title: "Lightest",
564
+ value: hue.lightest
248
565
  }),
249
- /* @__PURE__ */ jsx("input", {
250
- "aria-label": `${field.title} color`,
251
- onChange: (event) => onChange(field.key, event.currentTarget.value),
252
- style: swatchStyle,
253
- type: "color",
254
- value: expandHex(value ?? field.defaultValue)
566
+ /* @__PURE__ */ jsx(ColorRow, {
567
+ onChange: (darkest) => onChange(field.key, { darkest }),
568
+ title: "Darkest",
569
+ value: hue.darkest
255
570
  })
256
571
  ]
257
- });
572
+ }), $[19] = expanded, $[20] = field.description, $[21] = field.key, $[22] = field.title, $[23] = hue, $[24] = onChange, $[25] = t12) : t12 = $[25];
573
+ let t13;
574
+ $[26] !== t1 || $[27] !== t11 || $[28] !== t12 || $[29] !== t2 ? (t13 = /* @__PURE__ */ jsxs(Stack, {
575
+ gap: t1,
576
+ paddingBottom: t2,
577
+ children: [t11, t12]
578
+ }), $[26] = t1, $[27] = t11, $[28] = t12, $[29] = t2, $[30] = t13) : t13 = $[30];
579
+ let t14;
580
+ return $[31] !== expanded || $[32] !== t0 || $[33] !== t13 ? (t14 = /* @__PURE__ */ jsx(Card, {
581
+ border: expanded,
582
+ radius: 2,
583
+ tone: t0,
584
+ children: t13
585
+ }), $[31] = expanded, $[32] = t0, $[33] = t13, $[34] = t14) : t14 = $[34], t14;
586
+ }
587
+ function _temp$1(midPoint_0) {
588
+ return /* @__PURE__ */ jsx("option", {
589
+ value: midPoint_0,
590
+ children: midPoint_0
591
+ }, midPoint_0);
592
+ }
593
+ function ColorRow(props) {
594
+ let $ = c(18), { onChange, title, value } = props, t0;
595
+ $[0] === title ? t0 = $[1] : (t0 = /* @__PURE__ */ jsx(Text, {
596
+ size: 1,
597
+ children: title
598
+ }), $[0] = title, $[1] = t0);
599
+ let t1;
600
+ $[2] === value ? t1 = $[3] : (t1 = /* @__PURE__ */ jsx(Text, {
601
+ muted: !0,
602
+ size: 0,
603
+ children: value
604
+ }), $[2] = value, $[3] = t1);
605
+ let t2;
606
+ $[4] !== t0 || $[5] !== t1 ? (t2 = /* @__PURE__ */ jsxs(Stack, {
607
+ flex: 1,
608
+ gap: 2,
609
+ children: [t0, t1]
610
+ }), $[4] = t0, $[5] = t1, $[6] = t2) : t2 = $[6];
611
+ let t3 = `${title} color`, t4;
612
+ $[7] === onChange ? t4 = $[8] : (t4 = (event) => onChange(event.currentTarget.value), $[7] = onChange, $[8] = t4);
613
+ let t5;
614
+ $[9] === value ? t5 = $[10] : (t5 = expandHex(value), $[9] = value, $[10] = t5);
615
+ let t6;
616
+ $[11] !== t3 || $[12] !== t4 || $[13] !== t5 ? (t6 = /* @__PURE__ */ jsx(Swatch, {
617
+ "aria-label": t3,
618
+ onChange: t4,
619
+ type: "color",
620
+ value: t5
621
+ }), $[11] = t3, $[12] = t4, $[13] = t5, $[14] = t6) : t6 = $[14];
622
+ let t7;
623
+ return $[15] !== t2 || $[16] !== t6 ? (t7 = /* @__PURE__ */ jsxs(Flex, {
624
+ align: "center",
625
+ gap: 2,
626
+ children: [t2, t6]
627
+ }), $[15] = t2, $[16] = t6, $[17] = t7) : t7 = $[17], t7;
258
628
  }
629
+ /**
630
+ * Narrow enough to leave the studio preview as much room as possible: it fits
631
+ * the widest picker label next to its swatch, and the code snippet scrolls
632
+ * horizontally rather than widening the sidebar.
633
+ */
259
634
  const sidebarStyle = {
260
- width: 360,
635
+ width: 240,
261
636
  flex: "none",
262
637
  borderLeft: "1px solid var(--card-border-color)",
263
638
  boxSizing: "border-box",
@@ -270,52 +645,69 @@ const sidebarStyle = {
270
645
  * @internal
271
646
  */
272
647
  function ThemerActiveToolLayout(props) {
273
- let { open } = useThemer();
274
- return /* @__PURE__ */ jsxs(Flex, {
648
+ let $ = c(9), { open } = useThemer(), t0;
649
+ $[0] === props ? t0 = $[1] : (t0 = props.renderDefault(props), $[0] = props, $[1] = t0);
650
+ let t1;
651
+ $[2] === t0 ? t1 = $[3] : (t1 = /* @__PURE__ */ jsx(Box, {
652
+ flex: 1,
653
+ height: "fill",
654
+ overflow: "auto",
655
+ children: t0
656
+ }), $[2] = t0, $[3] = t1);
657
+ let t2;
658
+ $[4] === open ? t2 = $[5] : (t2 = open && /* @__PURE__ */ jsx(Layer, {
659
+ height: "fill",
660
+ style: sidebarStyle,
661
+ zOffset: 100,
662
+ children: /* @__PURE__ */ jsx(ThemerSidebar, {})
663
+ }), $[4] = open, $[5] = t2);
664
+ let t3;
665
+ return $[6] !== t1 || $[7] !== t2 ? (t3 = /* @__PURE__ */ jsxs(Flex, {
275
666
  height: "fill",
276
667
  sizing: "border",
277
- children: [/* @__PURE__ */ jsx(Box, {
278
- flex: 1,
279
- height: "fill",
280
- overflow: "auto",
281
- children: props.renderDefault(props)
282
- }), open && /* @__PURE__ */ jsx(Layer, {
283
- height: "fill",
284
- style: sidebarStyle,
285
- zOffset: 100,
286
- children: /* @__PURE__ */ jsx(ThemerSidebar, {})
287
- })]
288
- });
668
+ children: [t1, t2]
669
+ }), $[6] = t1, $[7] = t2, $[8] = t3) : t3 = $[8], t3;
670
+ }
671
+ const STORAGE_KEY = "sanityStudio:themer:hues";
672
+ function sanitizeHue(value) {
673
+ if (!value || typeof value != "object") return null;
674
+ let mid = Reflect.get(value, "mid"), midPoint = Reflect.get(value, "midPoint"), lightest = Reflect.get(value, "lightest"), darkest = Reflect.get(value, "darkest");
675
+ return typeof mid != "string" || !isColor(mid) || typeof lightest != "string" || !isColor(lightest) || typeof darkest != "string" || !isColor(darkest) || typeof midPoint != "number" || !MID_POINTS.includes(midPoint) ? null : {
676
+ mid: mid.toLowerCase(),
677
+ midPoint,
678
+ lightest: lightest.toLowerCase(),
679
+ darkest: darkest.toLowerCase()
680
+ };
289
681
  }
290
- const STORAGE_KEY = "sanityStudio:themer:colors";
291
682
  /**
292
- * Restores draft colors from localStorage, so theme drafts survive studio
683
+ * Restores draft hues from localStorage, so theme drafts survive studio
293
684
  * reloads.
294
685
  *
295
686
  * @internal
296
687
  */
297
- function readStoredColors() {
688
+ function readStoredHues() {
298
689
  try {
299
690
  if (typeof localStorage > "u") return null;
300
691
  let raw = localStorage.getItem(STORAGE_KEY);
301
692
  if (!raw) return null;
302
693
  let parsed = JSON.parse(raw);
303
694
  if (!parsed || typeof parsed != "object") return null;
304
- let colors = {};
305
- for (let { key } of THEMER_FIELDS) {
306
- let value = Reflect.get(parsed, key);
307
- typeof value == "string" && isColor(value) && (colors[key] = value);
695
+ let hues = {};
696
+ for (let key of HUE_KEYS) {
697
+ let hue = sanitizeHue(Reflect.get(parsed, key));
698
+ if (!hue) return null;
699
+ hues[key] = hue;
308
700
  }
309
- return colors;
701
+ return hues;
310
702
  } catch {
311
703
  return null;
312
704
  }
313
705
  }
314
706
  /** @internal */
315
- function writeStoredColors(colors) {
707
+ function writeStoredHues(hues) {
316
708
  try {
317
709
  if (typeof localStorage > "u") return;
318
- colors === null ? localStorage.removeItem(STORAGE_KEY) : localStorage.setItem(STORAGE_KEY, JSON.stringify(colors));
710
+ hues === null ? localStorage.removeItem(STORAGE_KEY) : localStorage.setItem(STORAGE_KEY, JSON.stringify(hues));
319
711
  } catch {}
320
712
  }
321
713
  /**
@@ -330,56 +722,92 @@ function writeStoredColors(colors) {
330
722
  * @internal
331
723
  */
332
724
  function ThemerLayout(props) {
333
- let { baseColors, ...layoutProps } = props, [open, setOpen] = useState(!1), [colors, setColors] = useState(readStoredColors);
334
- useEffect(() => writeStoredColors(colors), [colors]);
335
- let theme = useMemo(() => colors === null ? null : createTheme({
336
- ...baseColors,
337
- ...colors
338
- }), [baseColors, colors]), context = useMemo(() => ({
339
- baseColors,
340
- colors,
341
- setColors,
725
+ let $ = c(13), { baseHues, ...layoutProps } = props, [open, setOpen] = useState(!1), [hues, setHues] = useState(readStoredHues), t0, t1;
726
+ $[0] === hues ? (t0 = $[1], t1 = $[2]) : (t0 = () => writeStoredHues(hues), t1 = [hues], $[0] = hues, $[1] = t0, $[2] = t1), useEffect(t0, t1);
727
+ let t2;
728
+ $[3] === hues ? t2 = $[4] : (t2 = hues === null ? null : createTheme(hues), $[3] = hues, $[4] = t2);
729
+ let theme = t2, t3;
730
+ $[5] !== baseHues || $[6] !== hues || $[7] !== open ? (t3 = {
731
+ baseHues,
732
+ hues,
733
+ setHues,
342
734
  open,
343
735
  setOpen
344
- }), [
345
- baseColors,
346
- colors,
347
- open
348
- ]);
349
- return /* @__PURE__ */ jsx(ThemerContext.Provider, {
736
+ }, $[5] = baseHues, $[6] = hues, $[7] = open, $[8] = t3) : t3 = $[8];
737
+ let context = t3, t4 = ThemerContext, t5 = theme === null ? layoutProps.renderDefault(layoutProps) : /* @__PURE__ */ jsx(ThemeProvider, {
738
+ theme,
739
+ children: layoutProps.renderDefault(layoutProps)
740
+ }), t6;
741
+ return $[9] !== context || $[10] !== t4.Provider || $[11] !== t5 ? (t6 = /* @__PURE__ */ jsx(t4.Provider, {
350
742
  value: context,
351
- children: theme === null ? layoutProps.renderDefault(layoutProps) : /* @__PURE__ */ jsx(ThemeProvider, {
352
- theme,
353
- children: layoutProps.renderDefault(layoutProps)
743
+ children: t5
744
+ }), $[9] = context, $[10] = t4.Provider, $[11] = t5, $[12] = t6) : t6 = $[12], t6;
745
+ }
746
+ function ThemerNavbarButton() {
747
+ let $ = c(7), { open, setOpen } = useThemer(), t0;
748
+ $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = /* @__PURE__ */ jsx(Text, {
749
+ size: 1,
750
+ children: "Themer"
751
+ }), $[0] = t0) : t0 = $[0];
752
+ let t1;
753
+ $[1] !== open || $[2] !== setOpen ? (t1 = () => setOpen(!open), $[1] = open, $[2] = setOpen, $[3] = t1) : t1 = $[3];
754
+ let t2;
755
+ return $[4] !== open || $[5] !== t1 ? (t2 = /* @__PURE__ */ jsx(Tooltip, {
756
+ content: t0,
757
+ portal: !0,
758
+ children: /* @__PURE__ */ jsx(Button, {
759
+ "aria-label": "Themer",
760
+ icon: ColorWheelIcon,
761
+ mode: "bleed",
762
+ onClick: t1,
763
+ padding: 2,
764
+ selected: open
354
765
  })
355
- });
766
+ }), $[4] = open, $[5] = t1, $[6] = t2) : t2 = $[6], t2;
356
767
  }
357
768
  /**
358
769
  * Adds the toggle that opens and closes the themer sidebar to the Studio
359
- * navbar.
770
+ * navbar — an icon button with a tooltip in the top bar (like the Tasks
771
+ * toggle), and a regular titled action in the narrow-screen sidebar menu.
360
772
  *
361
773
  * @internal
362
774
  */
363
775
  function ThemerNavbar(props) {
364
- let { open, setOpen } = useThemer();
365
- return props.renderDefault({
366
- ...props,
367
- __internal_actions: [...props.__internal_actions ?? [], {
368
- icon: ColorWheelIcon,
369
- location: "topbar",
370
- name: "themer",
371
- onAction: () => setOpen(!open),
372
- selected: open,
373
- title: "Themer"
374
- }]
375
- });
776
+ let $ = c(7), { open, setOpen } = useThemer(), t0;
777
+ if ($[0] !== open || $[1] !== props || $[2] !== setOpen) {
778
+ let t1;
779
+ $[4] !== open || $[5] !== setOpen ? (t1 = () => setOpen(!open), $[4] = open, $[5] = setOpen, $[6] = t1) : t1 = $[6], t0 = props.renderDefault({
780
+ ...props,
781
+ __internal_actions: [
782
+ ...props.__internal_actions ?? [],
783
+ {
784
+ location: "topbar",
785
+ name: "themer-topbar",
786
+ render: _temp
787
+ },
788
+ {
789
+ icon: ColorWheelIcon,
790
+ location: "sidebar",
791
+ name: "themer-sidebar",
792
+ onAction: t1,
793
+ selected: open,
794
+ title: "Themer"
795
+ }
796
+ ]
797
+ }), $[0] = open, $[1] = props, $[2] = setOpen, $[3] = t0;
798
+ } else t0 = $[3];
799
+ return t0;
800
+ }
801
+ function _temp() {
802
+ return /* @__PURE__ */ jsx(ThemerNavbarButton, {});
376
803
  }
377
804
  /**
378
- * A Studio plugin that adds a themer sidebar for generating Studio themes:
379
- * a navbar toggle opens the sidebar next to the active tool, where presets
380
- * and color pickers preview a `createTheme` theme live on the whole Studio
381
- * while you browse around. Toggle between light and dark mode with the
382
- * regular appearance menu — the preview follows it.
805
+ * A Studio plugin that adds a themer sidebar for the legacy Themer themes:
806
+ * a navbar toggle opens the sidebar next to the active tool, where presets,
807
+ * per-hue editors and pasted themer.sanity.build URLs preview a legacy
808
+ * `createTheme` theme live on the whole Studio while you browse around.
809
+ * Toggle between light and dark mode with the regular appearance menu — the
810
+ * preview follows it.
383
811
  *
384
812
  * ```ts
385
813
  * import {themerTool} from '@sanity/themer/tool'
@@ -391,15 +819,19 @@ function ThemerNavbar(props) {
391
819
  * })
392
820
  * ```
393
821
  *
394
- * @public
822
+ * This is experimental and may change or be removed in any release without
823
+ * notice — use at your own risk.
824
+ *
825
+ * @alpha
395
826
  */
396
827
  const themerTool = definePlugin((options) => {
397
- let baseColors = options?.colors ?? {};
828
+ let baseHues = applyHues(options?.hues ?? {});
398
829
  function ThemerLayoutWithOptions(props) {
399
- return /* @__PURE__ */ jsx(ThemerLayout, {
830
+ let $ = c(2), t0;
831
+ return $[0] === props ? t0 = $[1] : (t0 = /* @__PURE__ */ jsx(ThemerLayout, {
400
832
  ...props,
401
- baseColors
402
- });
833
+ baseHues
834
+ }), $[0] = props, $[1] = t0), t0;
403
835
  }
404
836
  return {
405
837
  name: "@sanity/themer/tool",