@sanity/themer 0.0.0 → 0.2.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.
Files changed (41) hide show
  1. package/README.md +33 -58
  2. package/dist/index.d.ts +51 -23
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +2 -2
  5. package/dist/legacy.d.ts +7 -18
  6. package/dist/legacy.d.ts.map +1 -1
  7. package/dist/legacy.js +39 -39
  8. package/dist/legacy.js.map +1 -1
  9. package/dist/{_chunks/mix.js → mix-BfV6_Ke4.js} +1 -1
  10. package/dist/mix-BfV6_Ke4.js.map +1 -0
  11. package/dist/options-BaqJJgRs.d.ts +59 -0
  12. package/dist/options-BaqJJgRs.d.ts.map +1 -0
  13. package/dist/presets-DWzYcsLg.js +375 -0
  14. package/dist/presets-DWzYcsLg.js.map +1 -0
  15. package/dist/tool.d.ts +22 -15
  16. package/dist/tool.d.ts.map +1 -1
  17. package/dist/tool.js +608 -282
  18. package/dist/tool.js.map +1 -1
  19. package/package.json +24 -39
  20. package/dist/_chunks/mix.cjs +0 -52
  21. package/dist/_chunks/mix.cjs.map +0 -1
  22. package/dist/_chunks/mix.js.map +0 -1
  23. package/dist/_chunks/presets.cjs +0 -215
  24. package/dist/_chunks/presets.cjs.map +0 -1
  25. package/dist/_chunks/presets.js +0 -201
  26. package/dist/_chunks/presets.js.map +0 -1
  27. package/dist/_chunks/types.d.cts +0 -26
  28. package/dist/_chunks/types.d.cts.map +0 -1
  29. package/dist/_chunks/types.d.ts +0 -26
  30. package/dist/_chunks/types.d.ts.map +0 -1
  31. package/dist/index.cjs +0 -3
  32. package/dist/index.d.cts +0 -63
  33. package/dist/index.d.cts.map +0 -1
  34. package/dist/legacy.cjs +0 -1199
  35. package/dist/legacy.cjs.map +0 -1
  36. package/dist/legacy.d.cts +0 -166
  37. package/dist/legacy.d.cts.map +0 -1
  38. package/dist/tool.cjs +0 -407
  39. package/dist/tool.cjs.map +0 -1
  40. package/dist/tool.d.cts +0 -45
  41. package/dist/tool.d.cts.map +0 -1
package/dist/tool.js CHANGED
@@ -1,12 +1,16 @@
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 DEFAULT_BACKGROUND_DARK, c as resolveThemeOptions, i as DEFAULT_ACCENT, n as buildTheme, o as DEFAULT_BACKGROUND_LIGHT, r as buildPalette, s as deriveTextColor, t as presets } from "./presets-DWzYcsLg.js";
2
+ import { t as isColor } from "./mix-BfV6_Ke4.js";
3
+ import { COLOR_TINTS } from "@sanity/color";
4
+ import { c } from "react-compiler-runtime";
4
5
  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";
6
+ import { Box, Button, Card, Code, Flex, Grid, Layer, Stack, Text, ThemeProvider, Tooltip, useToast } from "@sanity/ui";
7
+ import { createContext, useContext, useEffect, useState } from "react";
7
8
  import { ClipboardIcon } from "@sanity/icons/Clipboard";
8
9
  import { CloseIcon } from "@sanity/icons/Close";
9
10
  import { ResetIcon } from "@sanity/icons/Reset";
11
+ import { registerLanguage } from "react-refractor";
12
+ import typescript from "refractor/typescript";
13
+ import { styled } from "styled-components";
10
14
  import { jsx, jsxs } from "react/jsx-runtime";
11
15
  import { ColorWheelIcon } from "@sanity/icons/ColorWheel";
12
16
  /** @internal */
@@ -17,93 +21,112 @@ function useThemer() {
17
21
  if (!context) throw Error("useThemer must be used within the `themerTool` plugin");
18
22
  return context;
19
23
  }
20
- /** @internal */
21
- const THEMER_FIELDS = [
22
- {
23
- key: "primary",
24
- title: "Primary",
25
- description: "Buttons, focus rings and links",
26
- defaultValue: color.blue[500].hex
27
- },
28
- {
29
- key: "gray",
30
- title: "Gray",
31
- description: "Neutral surfaces, borders and text",
32
- defaultValue: color.gray[500].hex
33
- },
34
- {
35
- key: "positive",
36
- title: "Positive",
37
- description: "Success accents",
38
- defaultValue: color.green[500].hex
39
- },
40
- {
41
- key: "caution",
42
- title: "Caution",
43
- description: "Warning accents",
44
- defaultValue: color.yellow[500].hex
45
- },
46
- {
47
- key: "critical",
48
- 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
63
- }
64
- ];
65
24
  /**
66
- * Serializes colors into the `createTheme` call to paste into
67
- * `sanity.config.ts`.
25
+ * Reduces theme options to the minimal object that recreates them through
26
+ * `buildTheme`: the text color is dropped when it matches the one derived
27
+ * from the accent, and backgrounds and contrast are dropped when they match
28
+ * the defaults. Options that boil down to the stock Studio theme reduce to
29
+ * `null` — they need nothing from this package.
68
30
  *
69
31
  * @internal
70
32
  */
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`;
33
+ function minimizeOptions(options) {
34
+ let resolved = resolveThemeOptions(options), minimized = { accent: resolved.accent };
35
+ resolved.text !== deriveTextColor(resolved.accent) && (minimized.text = resolved.text);
36
+ let dark = resolved.background.dark === DEFAULT_BACKGROUND_DARK ? null : resolved.background.dark, light = resolved.background.light === DEFAULT_BACKGROUND_LIGHT ? null : resolved.background.light;
37
+ return (dark !== null || light !== null) && (minimized.background = {}, dark !== null && (minimized.background.dark = dark), light !== null && (minimized.background.light = light)), resolved.contrast !== 85 && (minimized.contrast = resolved.contrast), minimized.accent === DEFAULT_ACCENT && Object.keys(minimized).length === 1 ? null : minimized;
74
38
  }
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));
39
+ /**
40
+ * Whether two sets of theme options generate the same theme, comparing their
41
+ * resolved forms so that derived and explicit values (and hex casing) don't
42
+ * matter.
43
+ *
44
+ * @internal
45
+ */
46
+ function sameOptions(a, b) {
47
+ let left = resolveThemeOptions(a), right = resolveThemeOptions(b);
48
+ return left.accent === right.accent && left.text === right.text && left.background.dark === right.background.dark && left.background.light === right.background.light && left.contrast === right.contrast;
87
49
  }
88
- /** Expands `#abc` to `#aabbcc`, which is the only format `<input type="color">` accepts */
89
- function expandHex(hex) {
90
- return hex.length === 4 ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex;
50
+ /**
51
+ * Serializes theme options into the `buildTheme` call to paste into
52
+ * `sanity.config.ts`, keeping only what differs from the derived defaults.
53
+ *
54
+ * Options that boil down to the stock Studio theme need nothing from this
55
+ * package — so they serialize to a bare `buildTheme()` from `@sanity/ui/theme`
56
+ * instead.
57
+ *
58
+ * @internal
59
+ */
60
+ function createThemeSnippet(options) {
61
+ let minimized = minimizeOptions(options);
62
+ if (minimized === null) return "import {buildTheme} from '@sanity/ui/theme'\n\nexport const theme = buildTheme()\n";
63
+ let entries = [` accent: '${minimized.accent}',`];
64
+ if (minimized.text !== void 0 && entries.push(` text: '${minimized.text}',`), minimized.background) {
65
+ let parts = [];
66
+ minimized.background.dark !== void 0 && parts.push(`dark: '${minimized.background.dark}'`), minimized.background.light !== void 0 && parts.push(`light: '${minimized.background.light}'`), entries.push(` background: {${parts.join(", ")}},`);
67
+ }
68
+ return minimized.contrast !== void 0 && entries.push(` contrast: ${minimized.contrast},`), `import {buildTheme} from '@sanity/themer'\n\nexport const theme = buildTheme({\n${entries.join("\n")}\n})\n`;
91
69
  }
70
+ registerLanguage(typescript);
92
71
  /**
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.
72
+ * `<input type="color">` paints the color into a shadow-DOM swatch that brings
73
+ * its own border and padding, which then sits inside ours as a second border.
74
+ * Stripping that chrome leaves the themed border as the only one.
75
+ */
76
+ const Swatch = styled.input.withConfig({
77
+ displayName: "Swatch",
78
+ componentId: "sc-lj4grb-0"
79
+ })`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;}`, Range = styled.input.withConfig({
80
+ displayName: "Range",
81
+ componentId: "sc-lj4grb-1"
82
+ })`display:block;width:100%;margin:0;accent-color:var(--card-focus-ring-color);`;
83
+ /**
84
+ * The themer sidebar: presets, the accent/text/background pickers and the
85
+ * contrast slider that generate the previewed `buildTheme` theme, plus the
86
+ * `buildTheme` snippet to make it permanent.
96
87
  *
97
88
  * @internal
98
89
  */
99
90
  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) => {
91
+ let $ = c(117), { baseOptions, options, setOptions, setOpen } = useThemer(), toast = useToast(), active = options ?? baseOptions, t0;
92
+ $[0] === active ? t0 = $[1] : (t0 = resolveThemeOptions(active), $[0] = active, $[1] = t0);
93
+ let resolved = t0, t1;
94
+ $[2] === active ? t1 = $[3] : (t1 = buildPalette(active), $[2] = active, $[3] = t1);
95
+ let palette = t1, t2;
96
+ $[4] === active ? t2 = $[5] : (t2 = presets.find((preset) => sameOptions(preset.options, active))?.slug ?? "", $[4] = active, $[5] = t2);
97
+ let activePresetSlug = t2, t3;
98
+ $[6] === active ? t3 = $[7] : (t3 = createThemeSnippet(active), $[6] = active, $[7] = t3);
99
+ let snippet = t3, t4;
100
+ $[8] !== active || $[9] !== setOptions ? (t4 = (changes) => {
101
+ setOptions({
102
+ ...active,
103
+ ...changes
104
+ });
105
+ }, $[8] = active, $[9] = setOptions, $[10] = t4) : t4 = $[10];
106
+ let patch = t4, t5;
107
+ $[11] !== active || $[12] !== setOptions ? (t5 = (changes_0) => {
108
+ setOptions({
109
+ ...active,
110
+ background: {
111
+ ...active.background,
112
+ ...changes_0
113
+ }
114
+ });
115
+ }, $[11] = active, $[12] = setOptions, $[13] = t5) : t5 = $[13];
116
+ let patchBackground = t5, t6;
117
+ $[14] !== active || $[15] !== setOptions ? (t6 = (field) => {
101
118
  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 () => {
119
+ delete next[field], setOptions(next);
120
+ }, $[14] = active, $[15] = setOptions, $[16] = t6) : t6 = $[16];
121
+ let clearField = t6, t7;
122
+ $[17] !== active || $[18] !== setOptions ? (t7 = (key) => {
123
+ let background = { ...active.background };
124
+ delete background[key];
125
+ let next_0 = { ...active };
126
+ background.dark === void 0 && background.light === void 0 ? delete next_0.background : next_0.background = background, setOptions(next_0);
127
+ }, $[17] = active, $[18] = setOptions, $[19] = t7) : t7 = $[19];
128
+ let clearBackground = t7, t8;
129
+ $[20] !== snippet || $[21] !== toast ? (t8 = async () => {
107
130
  try {
108
131
  await navigator.clipboard.writeText(snippet), toast.push({
109
132
  status: "success",
@@ -115,149 +138,397 @@ function ThemerSidebar() {
115
138
  title: "Could not copy the theme"
116
139
  });
117
140
  }
118
- };
119
- return /* @__PURE__ */ jsx(Card, {
141
+ }, $[20] = snippet, $[21] = toast, $[22] = t8) : t8 = $[22];
142
+ let handleCopy = t8, t9;
143
+ $[23] === Symbol.for("react.memo_cache_sentinel") ? (t9 = /* @__PURE__ */ jsx(Box, {
144
+ flex: 1,
145
+ paddingLeft: 1,
146
+ children: /* @__PURE__ */ jsx(Text, {
147
+ size: 1,
148
+ weight: "semibold",
149
+ children: "Themer"
150
+ })
151
+ }), $[23] = t9) : t9 = $[23];
152
+ let t10;
153
+ $[24] === setOpen ? t10 = $[25] : (t10 = /* @__PURE__ */ jsx(Card, {
154
+ borderBottom: !0,
155
+ padding: 3,
156
+ children: /* @__PURE__ */ jsxs(Flex, {
157
+ align: "center",
158
+ gap: 2,
159
+ children: [t9, /* @__PURE__ */ jsx(Button, {
160
+ icon: CloseIcon,
161
+ mode: "bleed",
162
+ onClick: () => setOpen(!1),
163
+ padding: 2,
164
+ title: "Close themer"
165
+ })]
166
+ })
167
+ }), $[24] = setOpen, $[25] = t10);
168
+ let t11;
169
+ $[26] === Symbol.for("react.memo_cache_sentinel") ? (t11 = /* @__PURE__ */ jsx(Text, {
170
+ size: 1,
171
+ weight: "medium",
172
+ children: "Presets"
173
+ }), $[26] = t11) : t11 = $[26];
174
+ let t12;
175
+ $[27] !== activePresetSlug || $[28] !== setOptions ? (t12 = presets.map((preset_0) => /* @__PURE__ */ jsx(PresetButton, {
176
+ active: preset_0.slug === activePresetSlug,
177
+ onClick: () => setOptions(preset_0.options),
178
+ preset: preset_0
179
+ }, preset_0.slug)), $[27] = activePresetSlug, $[28] = setOptions, $[29] = t12) : t12 = $[29];
180
+ let t13;
181
+ $[30] === t12 ? t13 = $[31] : (t13 = /* @__PURE__ */ jsxs(Stack, {
182
+ gap: 3,
183
+ children: [t11, /* @__PURE__ */ jsx(Grid, {
184
+ gap: 2,
185
+ gridTemplateColumns: 2,
186
+ children: t12
187
+ })]
188
+ }), $[30] = t12, $[31] = t13);
189
+ let t14;
190
+ $[32] === Symbol.for("react.memo_cache_sentinel") ? (t14 = /* @__PURE__ */ jsx(Text, {
191
+ size: 1,
192
+ weight: "medium",
193
+ children: "Colors"
194
+ }), $[32] = t14) : t14 = $[32];
195
+ let t15;
196
+ $[33] === patch ? t15 = $[34] : (t15 = (accent) => patch({ accent }), $[33] = patch, $[34] = t15);
197
+ let t16;
198
+ $[35] !== palette.blue || $[36] !== resolved.accent || $[37] !== t15 ? (t16 = /* @__PURE__ */ jsx(ColorRow, {
199
+ onChange: t15,
200
+ tints: palette.blue,
201
+ title: "Accent",
202
+ value: resolved.accent
203
+ }), $[35] = palette.blue, $[36] = resolved.accent, $[37] = t15, $[38] = t16) : t16 = $[38];
204
+ let t17 = active.text === void 0, t18;
205
+ $[39] === patch ? t18 = $[40] : (t18 = (text) => patch({ text }), $[39] = patch, $[40] = t18);
206
+ let t19;
207
+ $[41] === clearField ? t19 = $[42] : (t19 = () => clearField("text"), $[41] = clearField, $[42] = t19);
208
+ let t20;
209
+ $[43] !== palette.gray || $[44] !== resolved.text || $[45] !== t17 || $[46] !== t18 || $[47] !== t19 ? (t20 = /* @__PURE__ */ jsx(ColorRow, {
210
+ auto: t17,
211
+ onChange: t18,
212
+ onClear: t19,
213
+ tints: palette.gray,
214
+ title: "Text",
215
+ value: resolved.text
216
+ }), $[43] = palette.gray, $[44] = resolved.text, $[45] = t17, $[46] = t18, $[47] = t19, $[48] = t20) : t20 = $[48];
217
+ let t21 = active.background?.dark === void 0, t22;
218
+ $[49] === patchBackground ? t22 = $[50] : (t22 = (dark) => patchBackground({ dark }), $[49] = patchBackground, $[50] = t22);
219
+ let t23;
220
+ $[51] === clearBackground ? t23 = $[52] : (t23 = () => clearBackground("dark"), $[51] = clearBackground, $[52] = t23);
221
+ let t24;
222
+ $[53] !== palette.black || $[54] !== resolved.background.dark || $[55] !== t21 || $[56] !== t22 || $[57] !== t23 ? (t24 = /* @__PURE__ */ jsx(ColorRow, {
223
+ adjusted: palette.black,
224
+ auto: t21,
225
+ onChange: t22,
226
+ onClear: t23,
227
+ title: "Background · dark",
228
+ value: resolved.background.dark
229
+ }), $[53] = palette.black, $[54] = resolved.background.dark, $[55] = t21, $[56] = t22, $[57] = t23, $[58] = t24) : t24 = $[58];
230
+ let t25 = active.background?.light === void 0, t26;
231
+ $[59] === patchBackground ? t26 = $[60] : (t26 = (light) => patchBackground({ light }), $[59] = patchBackground, $[60] = t26);
232
+ let t27;
233
+ $[61] === clearBackground ? t27 = $[62] : (t27 = () => clearBackground("light"), $[61] = clearBackground, $[62] = t27);
234
+ let t28;
235
+ $[63] !== palette.white || $[64] !== resolved.background.light || $[65] !== t25 || $[66] !== t26 || $[67] !== t27 ? (t28 = /* @__PURE__ */ jsx(ColorRow, {
236
+ adjusted: palette.white,
237
+ auto: t25,
238
+ onChange: t26,
239
+ onClear: t27,
240
+ title: "Background · light",
241
+ value: resolved.background.light
242
+ }), $[63] = palette.white, $[64] = resolved.background.light, $[65] = t25, $[66] = t26, $[67] = t27, $[68] = t28) : t28 = $[68];
243
+ let t29;
244
+ $[69] !== t16 || $[70] !== t20 || $[71] !== t24 || $[72] !== t28 ? (t29 = /* @__PURE__ */ jsxs(Stack, {
245
+ gap: 3,
246
+ children: [t14, /* @__PURE__ */ jsxs(Stack, {
247
+ gap: 4,
248
+ children: [
249
+ t16,
250
+ t20,
251
+ t24,
252
+ t28
253
+ ]
254
+ })]
255
+ }), $[69] = t16, $[70] = t20, $[71] = t24, $[72] = t28, $[73] = t29) : t29 = $[73];
256
+ let t30;
257
+ $[74] === Symbol.for("react.memo_cache_sentinel") ? (t30 = /* @__PURE__ */ jsx(Text, {
258
+ size: 1,
259
+ weight: "medium",
260
+ children: "Contrast"
261
+ }), $[74] = t30) : t30 = $[74];
262
+ let t31 = active.contrast === void 0 ? " · auto" : "", t32;
263
+ $[75] !== resolved.contrast || $[76] !== t31 ? (t32 = /* @__PURE__ */ jsxs(Stack, {
264
+ flex: 1,
265
+ gap: 2,
266
+ children: [t30, /* @__PURE__ */ jsxs(Text, {
267
+ muted: !0,
268
+ size: 0,
269
+ children: [resolved.contrast, t31]
270
+ })]
271
+ }), $[75] = resolved.contrast, $[76] = t31, $[77] = t32) : t32 = $[77];
272
+ let t33;
273
+ $[78] !== active.contrast || $[79] !== clearField ? (t33 = active.contrast !== void 0 && /* @__PURE__ */ jsx(Button, {
274
+ icon: ResetIcon,
275
+ mode: "bleed",
276
+ onClick: () => clearField("contrast"),
277
+ padding: 2,
278
+ title: "Reset to auto"
279
+ }), $[78] = active.contrast, $[79] = clearField, $[80] = t33) : t33 = $[80];
280
+ let t34;
281
+ $[81] !== t32 || $[82] !== t33 ? (t34 = /* @__PURE__ */ jsxs(Flex, {
282
+ align: "center",
283
+ gap: 2,
284
+ children: [t32, t33]
285
+ }), $[81] = t32, $[82] = t33, $[83] = t34) : t34 = $[83];
286
+ let t35;
287
+ $[84] === patch ? t35 = $[85] : (t35 = (event) => patch({ contrast: Number(event.currentTarget.value) }), $[84] = patch, $[85] = t35);
288
+ let t36;
289
+ $[86] !== resolved.contrast || $[87] !== t35 ? (t36 = /* @__PURE__ */ jsx(Range, {
290
+ "aria-label": "Contrast",
291
+ max: 100,
292
+ min: 15,
293
+ onChange: t35,
294
+ step: 1,
295
+ type: "range",
296
+ value: resolved.contrast
297
+ }), $[86] = resolved.contrast, $[87] = t35, $[88] = t36) : t36 = $[88];
298
+ let t37;
299
+ $[89] === Symbol.for("react.memo_cache_sentinel") ? (t37 = /* @__PURE__ */ jsx(Text, {
300
+ muted: !0,
301
+ size: 0,
302
+ children: "100 keeps text and borders neutral — lower values blend in the accent"
303
+ }), $[89] = t37) : t37 = $[89];
304
+ let t38;
305
+ $[90] !== t34 || $[91] !== t36 ? (t38 = /* @__PURE__ */ jsxs(Stack, {
306
+ gap: 3,
307
+ children: [
308
+ t34,
309
+ t36,
310
+ t37
311
+ ]
312
+ }), $[90] = t34, $[91] = t36, $[92] = t38) : t38 = $[92];
313
+ let t39;
314
+ $[93] === Symbol.for("react.memo_cache_sentinel") ? (t39 = /* @__PURE__ */ jsx(Text, {
315
+ size: 1,
316
+ weight: "medium",
317
+ children: "Add to your config"
318
+ }), $[93] = t39) : t39 = $[93];
319
+ let t40;
320
+ $[94] === snippet ? t40 = $[95] : (t40 = /* @__PURE__ */ jsx(Card, {
321
+ border: !0,
322
+ overflow: "auto",
323
+ padding: 2,
324
+ radius: 2,
325
+ tone: "transparent",
326
+ children: /* @__PURE__ */ jsx(Code, {
327
+ language: "ts",
328
+ size: 0,
329
+ children: snippet
330
+ })
331
+ }), $[94] = snippet, $[95] = t40);
332
+ let t41;
333
+ $[96] === handleCopy ? t41 = $[97] : (t41 = /* @__PURE__ */ jsx(Button, {
334
+ icon: ClipboardIcon,
335
+ mode: "ghost",
336
+ onClick: () => void handleCopy(),
337
+ text: "Copy"
338
+ }), $[96] = handleCopy, $[97] = t41);
339
+ let t42 = options === null, t43;
340
+ $[98] === setOptions ? t43 = $[99] : (t43 = () => setOptions(null), $[98] = setOptions, $[99] = t43);
341
+ let t44;
342
+ $[100] !== t42 || $[101] !== t43 ? (t44 = /* @__PURE__ */ jsx(Button, {
343
+ disabled: t42,
344
+ icon: ResetIcon,
345
+ mode: "ghost",
346
+ onClick: t43,
347
+ text: "Reset",
348
+ tone: "critical"
349
+ }), $[100] = t42, $[101] = t43, $[102] = t44) : t44 = $[102];
350
+ let t45;
351
+ $[103] !== t41 || $[104] !== t44 ? (t45 = /* @__PURE__ */ jsxs(Flex, {
352
+ gap: 2,
353
+ children: [t41, t44]
354
+ }), $[103] = t41, $[104] = t44, $[105] = t45) : t45 = $[105];
355
+ let t46;
356
+ $[106] !== t40 || $[107] !== t45 ? (t46 = /* @__PURE__ */ jsxs(Stack, {
357
+ gap: 3,
358
+ children: [
359
+ t39,
360
+ t40,
361
+ t45
362
+ ]
363
+ }), $[106] = t40, $[107] = t45, $[108] = t46) : t46 = $[108];
364
+ let t47;
365
+ $[109] !== t13 || $[110] !== t29 || $[111] !== t38 || $[112] !== t46 ? (t47 = /* @__PURE__ */ jsx(Box, {
366
+ flex: 1,
367
+ overflow: "auto",
368
+ padding: 3,
369
+ children: /* @__PURE__ */ jsxs(Stack, {
370
+ gap: 4,
371
+ children: [
372
+ t13,
373
+ t29,
374
+ t38,
375
+ t46
376
+ ]
377
+ })
378
+ }), $[109] = t13, $[110] = t29, $[111] = t38, $[112] = t46, $[113] = t47) : t47 = $[113];
379
+ let t48;
380
+ return $[114] !== t10 || $[115] !== t47 ? (t48 = /* @__PURE__ */ jsx(Card, {
120
381
  height: "fill",
121
382
  children: /* @__PURE__ */ jsxs(Flex, {
122
383
  direction: "column",
123
384
  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
- })]
385
+ children: [t10, t47]
220
386
  })
221
- });
387
+ }), $[114] = t10, $[115] = t47, $[116] = t48) : t48 = $[116], t48;
222
388
  }
223
- function ColorField(props) {
224
- let { field, value, onChange } = props;
225
- return /* @__PURE__ */ jsxs(Flex, {
389
+ const paletteStyle = {
390
+ display: "flex",
391
+ gap: 1,
392
+ background: "var(--card-border-color)",
393
+ height: 21,
394
+ borderRadius: 3,
395
+ overflow: "hidden",
396
+ boxShadow: "inset 0 0 0 1px var(--card-border-color)"
397
+ };
398
+ /** A little palette of the colors a preset would apply */
399
+ function PresetButton(props) {
400
+ let $ = c(27), { active, onClick, preset } = props, t0;
401
+ $[0] === preset.options ? t0 = $[1] : (t0 = resolveThemeOptions(preset.options), $[0] = preset.options, $[1] = t0);
402
+ let resolved = t0, t1;
403
+ $[2] === resolved.accent ? t1 = $[3] : (t1 = ["accent", resolved.accent], $[2] = resolved.accent, $[3] = t1);
404
+ let t2;
405
+ $[4] === resolved.text ? t2 = $[5] : (t2 = ["text", resolved.text], $[4] = resolved.text, $[5] = t2);
406
+ let t3;
407
+ $[6] === resolved.background.dark ? t3 = $[7] : (t3 = ["dark", resolved.background.dark], $[6] = resolved.background.dark, $[7] = t3);
408
+ let t4;
409
+ $[8] === resolved.background.light ? t4 = $[9] : (t4 = ["light", resolved.background.light], $[8] = resolved.background.light, $[9] = t4);
410
+ let t5;
411
+ $[10] !== t1 || $[11] !== t2 || $[12] !== t3 || $[13] !== t4 ? (t5 = [
412
+ t1,
413
+ t2,
414
+ t3,
415
+ t4
416
+ ], $[10] = t1, $[11] = t2, $[12] = t3, $[13] = t4, $[14] = t5) : t5 = $[14];
417
+ let swatches = t5, t6;
418
+ $[15] === swatches ? t6 = $[16] : (t6 = /* @__PURE__ */ jsx("span", {
419
+ style: paletteStyle,
420
+ children: swatches.map(_temp$1)
421
+ }), $[15] = swatches, $[16] = t6);
422
+ let t7;
423
+ $[17] === preset.title ? t7 = $[18] : (t7 = /* @__PURE__ */ jsx(Text, {
424
+ align: "left",
425
+ size: 1,
426
+ textOverflow: "ellipsis",
427
+ children: preset.title
428
+ }), $[17] = preset.title, $[18] = t7);
429
+ let t8;
430
+ $[19] !== t6 || $[20] !== t7 ? (t8 = /* @__PURE__ */ jsxs(Stack, {
431
+ as: "span",
432
+ gap: 2,
433
+ children: [t6, t7]
434
+ }), $[19] = t6, $[20] = t7, $[21] = t8) : t8 = $[21];
435
+ let t9;
436
+ return $[22] !== active || $[23] !== onClick || $[24] !== preset.title || $[25] !== t8 ? (t9 = /* @__PURE__ */ jsx(Button, {
437
+ mode: "ghost",
438
+ onClick,
439
+ padding: 2,
440
+ selected: active,
441
+ title: preset.title,
442
+ children: t8
443
+ }), $[22] = active, $[23] = onClick, $[24] = preset.title, $[25] = t8, $[26] = t9) : t9 = $[26], t9;
444
+ }
445
+ function _temp$1(t0) {
446
+ let [key, background] = t0;
447
+ return /* @__PURE__ */ jsx("span", { style: {
448
+ flex: 1,
449
+ background
450
+ } }, key);
451
+ }
452
+ const rampStyle = {
453
+ ...paletteStyle,
454
+ height: 13,
455
+ borderRadius: 2
456
+ };
457
+ /**
458
+ * One color of the theme: a swatch with its value, an optional reset-to-auto
459
+ * button, and the generated 50–950 tint ramp for the scales it anchors.
460
+ */
461
+ function ColorRow(props) {
462
+ let $ = c(27), { adjusted, auto, onChange, onClear, tints, title, value } = props, t0;
463
+ $[0] === title ? t0 = $[1] : (t0 = /* @__PURE__ */ jsx(Text, {
464
+ size: 1,
465
+ children: title
466
+ }), $[0] = title, $[1] = t0);
467
+ let t1 = adjusted !== void 0 && adjusted !== value ? ` → ${adjusted}` : "", t2 = auto ? " · auto" : "", t3;
468
+ $[2] !== t1 || $[3] !== t2 || $[4] !== value ? (t3 = /* @__PURE__ */ jsxs(Text, {
469
+ muted: !0,
470
+ size: 0,
471
+ textOverflow: "ellipsis",
472
+ children: [
473
+ value,
474
+ t1,
475
+ t2
476
+ ]
477
+ }), $[2] = t1, $[3] = t2, $[4] = value, $[5] = t3) : t3 = $[5];
478
+ let t4;
479
+ $[6] !== t0 || $[7] !== t3 ? (t4 = /* @__PURE__ */ jsxs(Stack, {
480
+ flex: 1,
481
+ gap: 2,
482
+ children: [t0, t3]
483
+ }), $[6] = t0, $[7] = t3, $[8] = t4) : t4 = $[8];
484
+ let t5;
485
+ $[9] !== auto || $[10] !== onClear ? (t5 = onClear && !auto && /* @__PURE__ */ jsx(Button, {
486
+ icon: ResetIcon,
487
+ mode: "bleed",
488
+ onClick: onClear,
489
+ padding: 2,
490
+ title: "Reset to auto"
491
+ }), $[9] = auto, $[10] = onClear, $[11] = t5) : t5 = $[11];
492
+ let t6 = `${title} color`, t7;
493
+ $[12] === onChange ? t7 = $[13] : (t7 = (event) => onChange(event.currentTarget.value), $[12] = onChange, $[13] = t7);
494
+ let t8;
495
+ $[14] !== t6 || $[15] !== t7 || $[16] !== value ? (t8 = /* @__PURE__ */ jsx(Swatch, {
496
+ "aria-label": t6,
497
+ onChange: t7,
498
+ type: "color",
499
+ value
500
+ }), $[14] = t6, $[15] = t7, $[16] = value, $[17] = t8) : t8 = $[17];
501
+ let t9;
502
+ $[18] !== t4 || $[19] !== t5 || $[20] !== t8 ? (t9 = /* @__PURE__ */ jsxs(Flex, {
226
503
  align: "center",
227
- gap: 3,
504
+ gap: 2,
228
505
  children: [
229
- /* @__PURE__ */ jsxs(Stack, {
230
- flex: 1,
231
- 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
240
- })]
241
- }),
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}`
248
- }),
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)
255
- })
506
+ t4,
507
+ t5,
508
+ t8
256
509
  ]
257
- });
510
+ }), $[18] = t4, $[19] = t5, $[20] = t8, $[21] = t9) : t9 = $[21];
511
+ let t10;
512
+ $[22] === tints ? t10 = $[23] : (t10 = tints && /* @__PURE__ */ jsx("span", {
513
+ style: rampStyle,
514
+ children: COLOR_TINTS.map((tint) => /* @__PURE__ */ jsx("span", { style: {
515
+ flex: 1,
516
+ background: tints[tint]
517
+ } }, tint))
518
+ }), $[22] = tints, $[23] = t10);
519
+ let t11;
520
+ return $[24] !== t10 || $[25] !== t9 ? (t11 = /* @__PURE__ */ jsxs(Stack, {
521
+ gap: 2,
522
+ children: [t9, t10]
523
+ }), $[24] = t10, $[25] = t9, $[26] = t11) : t11 = $[26], t11;
258
524
  }
525
+ /**
526
+ * Narrow enough to leave the studio preview as much room as possible: it fits
527
+ * the widest picker label next to its swatch, and the code snippet scrolls
528
+ * horizontally rather than widening the sidebar.
529
+ */
259
530
  const sidebarStyle = {
260
- width: 360,
531
+ width: 240,
261
532
  flex: "none",
262
533
  borderLeft: "1px solid var(--card-border-color)",
263
534
  boxSizing: "border-box",
@@ -270,52 +541,67 @@ const sidebarStyle = {
270
541
  * @internal
271
542
  */
272
543
  function ThemerActiveToolLayout(props) {
273
- let { open } = useThemer();
274
- return /* @__PURE__ */ jsxs(Flex, {
544
+ let $ = c(9), { open } = useThemer(), t0;
545
+ $[0] === props ? t0 = $[1] : (t0 = props.renderDefault(props), $[0] = props, $[1] = t0);
546
+ let t1;
547
+ $[2] === t0 ? t1 = $[3] : (t1 = /* @__PURE__ */ jsx(Box, {
548
+ flex: 1,
549
+ height: "fill",
550
+ overflow: "auto",
551
+ children: t0
552
+ }), $[2] = t0, $[3] = t1);
553
+ let t2;
554
+ $[4] === open ? t2 = $[5] : (t2 = open && /* @__PURE__ */ jsx(Layer, {
555
+ height: "fill",
556
+ style: sidebarStyle,
557
+ zOffset: 100,
558
+ children: /* @__PURE__ */ jsx(ThemerSidebar, {})
559
+ }), $[4] = open, $[5] = t2);
560
+ let t3;
561
+ return $[6] !== t1 || $[7] !== t2 ? (t3 = /* @__PURE__ */ jsxs(Flex, {
275
562
  height: "fill",
276
563
  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
- });
564
+ children: [t1, t2]
565
+ }), $[6] = t1, $[7] = t2, $[8] = t3) : t3 = $[8], t3;
566
+ }
567
+ const STORAGE_KEY = "sanityStudio:themer:options";
568
+ function sanitizeColor(value) {
569
+ return typeof value == "string" && isColor(value) ? value.toLowerCase() : null;
570
+ }
571
+ function sanitizeOptions(value) {
572
+ if (!value || typeof value != "object") return null;
573
+ let accent = sanitizeColor(Reflect.get(value, "accent"));
574
+ if (!accent) return null;
575
+ let options = { accent }, text = sanitizeColor(Reflect.get(value, "text"));
576
+ text && (options.text = text);
577
+ let background = Reflect.get(value, "background");
578
+ if (background && typeof background == "object") {
579
+ let dark = sanitizeColor(Reflect.get(background, "dark")), light = sanitizeColor(Reflect.get(background, "light"));
580
+ (dark || light) && (options.background = {}, dark && (options.background.dark = dark), light && (options.background.light = light));
581
+ }
582
+ let contrast = Reflect.get(value, "contrast");
583
+ return typeof contrast == "number" && Number.isFinite(contrast) && (options.contrast = Math.min(100, Math.max(15, contrast))), options;
289
584
  }
290
- const STORAGE_KEY = "sanityStudio:themer:colors";
291
585
  /**
292
- * Restores draft colors from localStorage, so theme drafts survive studio
293
- * reloads.
586
+ * Restores draft theme options from localStorage, so theme drafts survive
587
+ * studio reloads.
294
588
  *
295
589
  * @internal
296
590
  */
297
- function readStoredColors() {
591
+ function readStoredOptions() {
298
592
  try {
299
593
  if (typeof localStorage > "u") return null;
300
594
  let raw = localStorage.getItem(STORAGE_KEY);
301
- if (!raw) return null;
302
- let parsed = JSON.parse(raw);
303
- 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);
308
- }
309
- return colors;
595
+ return raw ? sanitizeOptions(JSON.parse(raw)) : null;
310
596
  } catch {
311
597
  return null;
312
598
  }
313
599
  }
314
600
  /** @internal */
315
- function writeStoredColors(colors) {
601
+ function writeStoredOptions(options) {
316
602
  try {
317
603
  if (typeof localStorage > "u") return;
318
- colors === null ? localStorage.removeItem(STORAGE_KEY) : localStorage.setItem(STORAGE_KEY, JSON.stringify(colors));
604
+ options === null ? localStorage.removeItem(STORAGE_KEY) : localStorage.setItem(STORAGE_KEY, JSON.stringify(options));
319
605
  } catch {}
320
606
  }
321
607
  /**
@@ -330,56 +616,92 @@ function writeStoredColors(colors) {
330
616
  * @internal
331
617
  */
332
618
  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,
619
+ let $ = c(13), { baseOptions, ...layoutProps } = props, [open, setOpen] = useState(!1), [options, setOptions] = useState(readStoredOptions), t0, t1;
620
+ $[0] === options ? (t0 = $[1], t1 = $[2]) : (t0 = () => writeStoredOptions(options), t1 = [options], $[0] = options, $[1] = t0, $[2] = t1), useEffect(t0, t1);
621
+ let t2;
622
+ $[3] === options ? t2 = $[4] : (t2 = options === null ? null : buildTheme(options), $[3] = options, $[4] = t2);
623
+ let theme = t2, t3;
624
+ $[5] !== baseOptions || $[6] !== open || $[7] !== options ? (t3 = {
625
+ baseOptions,
626
+ options,
627
+ setOptions,
342
628
  open,
343
629
  setOpen
344
- }), [
345
- baseColors,
346
- colors,
347
- open
348
- ]);
349
- return /* @__PURE__ */ jsx(ThemerContext.Provider, {
630
+ }, $[5] = baseOptions, $[6] = open, $[7] = options, $[8] = t3) : t3 = $[8];
631
+ let context = t3, t4 = ThemerContext, t5 = theme === null ? layoutProps.renderDefault(layoutProps) : /* @__PURE__ */ jsx(ThemeProvider, {
632
+ theme,
633
+ children: layoutProps.renderDefault(layoutProps)
634
+ }), t6;
635
+ return $[9] !== context || $[10] !== t4.Provider || $[11] !== t5 ? (t6 = /* @__PURE__ */ jsx(t4.Provider, {
350
636
  value: context,
351
- children: theme === null ? layoutProps.renderDefault(layoutProps) : /* @__PURE__ */ jsx(ThemeProvider, {
352
- theme,
353
- children: layoutProps.renderDefault(layoutProps)
637
+ children: t5
638
+ }), $[9] = context, $[10] = t4.Provider, $[11] = t5, $[12] = t6) : t6 = $[12], t6;
639
+ }
640
+ function ThemerNavbarButton() {
641
+ let $ = c(7), { open, setOpen } = useThemer(), t0;
642
+ $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = /* @__PURE__ */ jsx(Text, {
643
+ size: 1,
644
+ children: "Themer"
645
+ }), $[0] = t0) : t0 = $[0];
646
+ let t1;
647
+ $[1] !== open || $[2] !== setOpen ? (t1 = () => setOpen(!open), $[1] = open, $[2] = setOpen, $[3] = t1) : t1 = $[3];
648
+ let t2;
649
+ return $[4] !== open || $[5] !== t1 ? (t2 = /* @__PURE__ */ jsx(Tooltip, {
650
+ content: t0,
651
+ portal: !0,
652
+ children: /* @__PURE__ */ jsx(Button, {
653
+ "aria-label": "Themer",
654
+ icon: ColorWheelIcon,
655
+ mode: "bleed",
656
+ onClick: t1,
657
+ padding: 2,
658
+ selected: open
354
659
  })
355
- });
660
+ }), $[4] = open, $[5] = t1, $[6] = t2) : t2 = $[6], t2;
356
661
  }
357
662
  /**
358
663
  * Adds the toggle that opens and closes the themer sidebar to the Studio
359
- * navbar.
664
+ * navbar — an icon button with a tooltip in the top bar (like the Tasks
665
+ * toggle), and a regular titled action in the narrow-screen sidebar menu.
360
666
  *
361
667
  * @internal
362
668
  */
363
669
  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
- });
670
+ let $ = c(7), { open, setOpen } = useThemer(), t0;
671
+ if ($[0] !== open || $[1] !== props || $[2] !== setOpen) {
672
+ let t1;
673
+ $[4] !== open || $[5] !== setOpen ? (t1 = () => setOpen(!open), $[4] = open, $[5] = setOpen, $[6] = t1) : t1 = $[6], t0 = props.renderDefault({
674
+ ...props,
675
+ __internal_actions: [
676
+ ...props.__internal_actions ?? [],
677
+ {
678
+ location: "topbar",
679
+ name: "themer-topbar",
680
+ render: _temp
681
+ },
682
+ {
683
+ icon: ColorWheelIcon,
684
+ location: "sidebar",
685
+ name: "themer-sidebar",
686
+ onAction: t1,
687
+ selected: open,
688
+ title: "Themer"
689
+ }
690
+ ]
691
+ }), $[0] = open, $[1] = props, $[2] = setOpen, $[3] = t0;
692
+ } else t0 = $[3];
693
+ return t0;
694
+ }
695
+ function _temp() {
696
+ return /* @__PURE__ */ jsx(ThemerNavbarButton, {});
376
697
  }
377
698
  /**
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.
699
+ * A Studio plugin that adds a themer sidebar for `buildTheme` themes: a
700
+ * navbar toggle opens the sidebar next to the active tool, where presets, the
701
+ * accent/text/background pickers and the contrast slider preview a
702
+ * `buildTheme` theme live on the whole Studio while you browse around.
703
+ * Toggle between light and dark mode with the regular appearance menu — the
704
+ * preview follows it.
383
705
  *
384
706
  * ```ts
385
707
  * import {themerTool} from '@sanity/themer/tool'
@@ -391,15 +713,19 @@ function ThemerNavbar(props) {
391
713
  * })
392
714
  * ```
393
715
  *
394
- * @public
716
+ * This is experimental and may change or be removed in any release without
717
+ * notice — use at your own risk.
718
+ *
719
+ * @alpha
395
720
  */
396
721
  const themerTool = definePlugin((options) => {
397
- let baseColors = options?.colors ?? {};
722
+ let baseOptions = options?.config ?? { accent: DEFAULT_ACCENT };
398
723
  function ThemerLayoutWithOptions(props) {
399
- return /* @__PURE__ */ jsx(ThemerLayout, {
724
+ let $ = c(2), t0;
725
+ return $[0] === props ? t0 = $[1] : (t0 = /* @__PURE__ */ jsx(ThemerLayout, {
400
726
  ...props,
401
- baseColors
402
- });
727
+ baseOptions
728
+ }), $[0] = props, $[1] = t0), t0;
403
729
  }
404
730
  return {
405
731
  name: "@sanity/themer/tool",