@sanity/themer 0.3.15 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -54
- package/dist/index.d.ts +42 -27
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/legacy.d.ts +43 -118
- package/dist/legacy.d.ts.map +1 -1
- package/dist/legacy.js +5 -1205
- package/dist/legacy.js.map +1 -1
- package/dist/options-CU-emn_p.d.ts +69 -0
- package/dist/options-CU-emn_p.d.ts.map +1 -0
- package/dist/{presets-DWzYcsLg.js → presets-CqBgS3A-.js} +146 -99
- package/dist/presets-CqBgS3A-.js.map +1 -0
- package/dist/tool.d.ts +11 -7
- package/dist/tool.d.ts.map +1 -1
- package/dist/tool.js +2333 -514
- package/dist/tool.js.map +1 -1
- package/package.json +7 -4
- package/dist/mix-BfV6_Ke4.js +0 -42
- package/dist/mix-BfV6_Ke4.js.map +0 -1
- package/dist/options-BaqJJgRs.d.ts +0 -59
- package/dist/options-BaqJJgRs.d.ts.map +0 -1
- package/dist/presets-DWzYcsLg.js.map +0 -1
package/dist/tool.js
CHANGED
|
@@ -1,21 +1,32 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { t as isColor } from "./mix-BfV6_Ke4.js";
|
|
3
|
-
import { COLOR_TINTS } from "@sanity/color";
|
|
1
|
+
import { a as DEFAULT_BACKGROUND, c as resolveThemeOptions, d as hslToHex, f as relativeLuminance, i as DEFAULT_ACCENT, l as isColor, n as buildTheme, o as SCHEMES, r as buildPalette, s as deriveTextColor, t as presets, u as hexToHsl } from "./presets-CqBgS3A-.js";
|
|
4
2
|
import { c } from "react-compiler-runtime";
|
|
5
|
-
import { definePlugin } from "sanity";
|
|
6
|
-
import { Box, Button, Card,
|
|
7
|
-
import { createContext, useContext, useEffect, useState } from "react";
|
|
8
|
-
import {
|
|
3
|
+
import { definePlugin, useColorSchemeValue } from "sanity";
|
|
4
|
+
import { Badge, Box, Button, Card, Dialog, Flex, Layer, Stack, Text, TextInput, ThemeProvider, useTheme_v2 } from "@sanity/ui";
|
|
5
|
+
import { createContext, useContext, useEffect, useRef, useState } from "react";
|
|
6
|
+
import { styled } from "styled-components";
|
|
7
|
+
import { ArrowLeftIcon } from "@sanity/icons/ArrowLeft";
|
|
9
8
|
import { CloseIcon } from "@sanity/icons/Close";
|
|
9
|
+
import { CodeBlockIcon } from "@sanity/icons/CodeBlock";
|
|
10
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
10
11
|
import { ResetIcon } from "@sanity/icons/Reset";
|
|
11
|
-
import {
|
|
12
|
+
import { TrashIcon } from "@sanity/icons/Trash";
|
|
13
|
+
import { SparklesIcon } from "@sanity/icons/Sparkles";
|
|
14
|
+
import { Tooltip } from "@sanity/ui/tooltip";
|
|
15
|
+
import { ImageIcon } from "@sanity/icons/Image";
|
|
12
16
|
import { useToast } from "@sanity/ui/toast";
|
|
17
|
+
import { AddIcon } from "@sanity/icons/Add";
|
|
18
|
+
import { RestoreIcon } from "@sanity/icons/Restore";
|
|
19
|
+
import { CopyIcon } from "@sanity/icons/Copy";
|
|
20
|
+
import { EditIcon } from "@sanity/icons/Edit";
|
|
21
|
+
import { EllipsisHorizontalIcon } from "@sanity/icons/EllipsisHorizontal";
|
|
22
|
+
import { Menu, MenuButton, MenuDivider, MenuItem } from "@sanity/ui/menu";
|
|
23
|
+
import { ClipboardIcon } from "@sanity/icons/Clipboard";
|
|
24
|
+
import { Code } from "@sanity/ui/code";
|
|
13
25
|
import { registerLanguage } from "react-refractor";
|
|
14
26
|
import typescript from "refractor/typescript";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
27
|
+
import { useActor, useSelector } from "@xstate/react";
|
|
28
|
+
import { assign, not, setup } from "xstate";
|
|
17
29
|
import { ColorWheelIcon } from "@sanity/icons/ColorWheel";
|
|
18
|
-
import { Tooltip } from "@sanity/ui/tooltip";
|
|
19
30
|
/** @internal */
|
|
20
31
|
const ThemerContext = createContext(null);
|
|
21
32
|
/** @internal */
|
|
@@ -24,20 +35,58 @@ function useThemer() {
|
|
|
24
35
|
if (!context) throw Error("useThemer must be used within the `themerTool` plugin");
|
|
25
36
|
return context;
|
|
26
37
|
}
|
|
38
|
+
const Root$2 = styled(Box).withConfig({
|
|
39
|
+
displayName: "Root",
|
|
40
|
+
componentId: "sc-6zqwg0-0"
|
|
41
|
+
})`scrollbar-gutter:stable;`;
|
|
42
|
+
/**
|
|
43
|
+
* The scrolling body of a sidebar flow. It always reserves the scrollbar's
|
|
44
|
+
* space, so the content does not shift when the list grows or shrinks past
|
|
45
|
+
* the point where it needs to scroll — and it takes that space out of its
|
|
46
|
+
* right padding, so the content stays centered rather than hugging the left.
|
|
47
|
+
* With overlay scrollbars there is no gutter and the padding stays even.
|
|
48
|
+
*
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
function ScrollArea(props) {
|
|
52
|
+
let $ = c(8), { children, padding } = props, { space } = useTheme_v2(), ref = useRef(null), [gutter, setGutter] = useState(0), t0, t1;
|
|
53
|
+
$[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = () => {
|
|
54
|
+
let element = ref.current;
|
|
55
|
+
if (!element) return;
|
|
56
|
+
let measure = () => setGutter(element.offsetWidth - element.clientWidth);
|
|
57
|
+
measure();
|
|
58
|
+
let observer = new ResizeObserver(measure);
|
|
59
|
+
return observer.observe(element), () => observer.disconnect();
|
|
60
|
+
}, t1 = [], $[0] = t0, $[1] = t1) : (t0 = $[0], t1 = $[1]), useEffect(t0, t1);
|
|
61
|
+
let t2 = Math.max(0, space[padding] - gutter), t3;
|
|
62
|
+
$[2] === t2 ? t3 = $[3] : (t3 = { paddingRight: t2 }, $[2] = t2, $[3] = t3);
|
|
63
|
+
let t4;
|
|
64
|
+
return $[4] !== children || $[5] !== padding || $[6] !== t3 ? (t4 = /* @__PURE__ */ jsx(Root$2, {
|
|
65
|
+
flex: 1,
|
|
66
|
+
overflow: "auto",
|
|
67
|
+
padding,
|
|
68
|
+
ref,
|
|
69
|
+
style: t3,
|
|
70
|
+
children
|
|
71
|
+
}), $[4] = children, $[5] = padding, $[6] = t3, $[7] = t4) : t4 = $[7], t4;
|
|
72
|
+
}
|
|
27
73
|
/**
|
|
28
74
|
* Reduces theme options to the minimal object that recreates them through
|
|
29
|
-
* `buildTheme`: the
|
|
30
|
-
*
|
|
31
|
-
* the
|
|
32
|
-
*
|
|
75
|
+
* `buildTheme`: in each scheme the accent is dropped when it is the stock
|
|
76
|
+
* one, the text color when it matches the one derived from the accent, and
|
|
77
|
+
* the background and contrast when they match the defaults — and a scheme
|
|
78
|
+
* with nothing left is dropped altogether. Options that boil down to the
|
|
79
|
+
* stock Studio theme reduce to `null` — they need nothing from this package.
|
|
33
80
|
*
|
|
34
81
|
* @internal
|
|
35
82
|
*/
|
|
36
83
|
function minimizeOptions(options) {
|
|
37
|
-
let resolved = resolveThemeOptions(options), minimized = {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
84
|
+
let resolved = resolveThemeOptions(options), minimized = {};
|
|
85
|
+
for (let scheme of SCHEMES) {
|
|
86
|
+
let values = resolved[scheme], kept = {};
|
|
87
|
+
values.accent !== DEFAULT_ACCENT && (kept.accent = values.accent), values.text !== deriveTextColor(values.accent) && (kept.text = values.text), values.background !== DEFAULT_BACKGROUND[scheme] && (kept.background = values.background), values.contrast !== 85 && (kept.contrast = values.contrast), Object.keys(kept).length > 0 && (minimized[scheme] = kept);
|
|
88
|
+
}
|
|
89
|
+
return Object.keys(minimized).length === 0 ? null : minimized;
|
|
41
90
|
}
|
|
42
91
|
/**
|
|
43
92
|
* Whether two sets of theme options generate the same theme, comparing their
|
|
@@ -48,11 +97,1629 @@ function minimizeOptions(options) {
|
|
|
48
97
|
*/
|
|
49
98
|
function sameOptions(a, b) {
|
|
50
99
|
let left = resolveThemeOptions(a), right = resolveThemeOptions(b);
|
|
51
|
-
return
|
|
100
|
+
return SCHEMES.every((scheme) => sameScheme(left[scheme], right[scheme]));
|
|
101
|
+
}
|
|
102
|
+
function sameScheme(a, b) {
|
|
103
|
+
return a.accent === b.accent && a.text === b.text && a.background === b.background && a.contrast === b.contrast;
|
|
104
|
+
}
|
|
105
|
+
/** The slug of the theme the Studio config was generated from @internal */
|
|
106
|
+
const initialThemerState = {
|
|
107
|
+
active: null,
|
|
108
|
+
custom: [],
|
|
109
|
+
removed: []
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Resolves the list of themes: the configured theme first, then the presets
|
|
113
|
+
* (minus the ones that would only repeat the configured theme), then the
|
|
114
|
+
* user's own themes. Removed themes are set aside so they can be restored,
|
|
115
|
+
* and the active theme falls back to the configured one when its slug no
|
|
116
|
+
* longer resolves.
|
|
117
|
+
*
|
|
118
|
+
* @internal
|
|
119
|
+
*/
|
|
120
|
+
function resolveThemes(state, baseOptions) {
|
|
121
|
+
let config = {
|
|
122
|
+
slug: "config",
|
|
123
|
+
title: "Studio config",
|
|
124
|
+
options: baseOptions,
|
|
125
|
+
source: "config"
|
|
126
|
+
}, all = [config];
|
|
127
|
+
for (let preset of presets) sameOptions(preset.options, baseOptions) || all.push({
|
|
128
|
+
slug: preset.slug,
|
|
129
|
+
title: preset.title,
|
|
130
|
+
options: preset.options,
|
|
131
|
+
source: "preset"
|
|
132
|
+
});
|
|
133
|
+
for (let theme of state.custom) all.push({
|
|
134
|
+
slug: theme.slug,
|
|
135
|
+
title: theme.title,
|
|
136
|
+
options: theme.options,
|
|
137
|
+
source: "custom",
|
|
138
|
+
...theme.palette ? { palette: theme.palette } : {}
|
|
139
|
+
});
|
|
140
|
+
let removedSlugs = new Set(state.removed), themes = all.filter((theme) => !removedSlugs.has(theme.slug));
|
|
141
|
+
return {
|
|
142
|
+
themes,
|
|
143
|
+
removed: all.filter((theme) => removedSlugs.has(theme.slug)),
|
|
144
|
+
active: themes.find((theme) => theme.slug === state.active) ?? config
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Creates a custom theme with a slug that cannot collide with the presets or
|
|
149
|
+
* the configured theme.
|
|
150
|
+
*
|
|
151
|
+
* @internal
|
|
152
|
+
*/
|
|
153
|
+
function createCustomTheme(title, options, palette) {
|
|
154
|
+
return {
|
|
155
|
+
slug: `custom-${randomId()}`,
|
|
156
|
+
title,
|
|
157
|
+
options,
|
|
158
|
+
...palette ? { palette } : {}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/** The title of a duplicated theme @internal */
|
|
162
|
+
function duplicateTitle(title) {
|
|
163
|
+
return `${displayTitle(title)} copy`;
|
|
164
|
+
}
|
|
165
|
+
/** The title to show for a theme, falling back when the user cleared it @internal */
|
|
166
|
+
function displayTitle(title) {
|
|
167
|
+
return title.trim() || "Untitled theme";
|
|
168
|
+
}
|
|
169
|
+
function randomId() {
|
|
170
|
+
return typeof crypto < "u" && typeof crypto.randomUUID == "function" ? crypto.randomUUID() : `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* The dark scheme covers the right side of the preview behind a diagonal cut,
|
|
174
|
+
* like the "Auto" appearance preview in macOS System Settings shows both
|
|
175
|
+
* schemes at once.
|
|
176
|
+
*/
|
|
177
|
+
const Root$1 = styled.div.withConfig({
|
|
178
|
+
displayName: "Root",
|
|
179
|
+
componentId: "sc-w55yz9-0"
|
|
180
|
+
})`position:relative;aspect-ratio:16 / 9;overflow:hidden;`, LIST_ROWS = [
|
|
181
|
+
"62%",
|
|
182
|
+
"46%",
|
|
183
|
+
"70%",
|
|
184
|
+
"52%"
|
|
185
|
+
], FIELDS = ["32%", "24%"];
|
|
186
|
+
/**
|
|
187
|
+
* A tiny, lo-fi mockup of a Studio in the given theme — the navbar over the
|
|
188
|
+
* structure tool with a list of documents on the left and a document form on
|
|
189
|
+
* the right — in both color schemes at once: the light scheme on the left and
|
|
190
|
+
* the dark scheme on the right, split diagonally. It only paints colors the
|
|
191
|
+
* theme options change (backgrounds, text, borders, the accent) so that a
|
|
192
|
+
* glance shows what the theme does to the Studio — with few and bold enough
|
|
193
|
+
* elements to still read when scaled down to a thumbnail.
|
|
194
|
+
*
|
|
195
|
+
* @internal
|
|
196
|
+
*/
|
|
197
|
+
function ThemePreview(props) {
|
|
198
|
+
let $ = c(11), { options } = props, t0;
|
|
199
|
+
$[0] === options ? t0 = $[1] : (t0 = buildTheme(options), $[0] = options, $[1] = t0);
|
|
200
|
+
let theme = t0, t1;
|
|
201
|
+
$[2] === Symbol.for("react.memo_cache_sentinel") ? (t1 = /* @__PURE__ */ jsx(MockStudio, {}), $[2] = t1) : t1 = $[2];
|
|
202
|
+
let t2;
|
|
203
|
+
$[3] === theme ? t2 = $[4] : (t2 = /* @__PURE__ */ jsx(ThemeProvider, {
|
|
204
|
+
scheme: "light",
|
|
205
|
+
theme,
|
|
206
|
+
tone: "default",
|
|
207
|
+
children: t1
|
|
208
|
+
}), $[3] = theme, $[4] = t2);
|
|
209
|
+
let t3;
|
|
210
|
+
$[5] === Symbol.for("react.memo_cache_sentinel") ? (t3 = /* @__PURE__ */ jsx(MockStudio, { style: { clipPath: "polygon(58% 0, 100% 0, 100% 100%, 42% 100%)" } }), $[5] = t3) : t3 = $[5];
|
|
211
|
+
let t4;
|
|
212
|
+
$[6] === theme ? t4 = $[7] : (t4 = /* @__PURE__ */ jsx(ThemeProvider, {
|
|
213
|
+
scheme: "dark",
|
|
214
|
+
theme,
|
|
215
|
+
tone: "default",
|
|
216
|
+
children: t3
|
|
217
|
+
}), $[6] = theme, $[7] = t4);
|
|
218
|
+
let t5;
|
|
219
|
+
return $[8] !== t2 || $[9] !== t4 ? (t5 = /* @__PURE__ */ jsxs(Root$1, {
|
|
220
|
+
"aria-hidden": !0,
|
|
221
|
+
children: [t2, t4]
|
|
222
|
+
}), $[8] = t2, $[9] = t4, $[10] = t5) : t5 = $[10], t5;
|
|
223
|
+
}
|
|
224
|
+
function MockStudio(props) {
|
|
225
|
+
let $ = c(53), { color } = useTheme_v2(), primary = color.button.default.primary.enabled, selected = color.selectable.default.selected, input = color.input.default.enabled, border = `1px solid ${color.border}`, line = _temp$4, t0;
|
|
226
|
+
$[0] !== color.bg || $[1] !== props.style ? (t0 = {
|
|
227
|
+
position: "absolute",
|
|
228
|
+
inset: 0,
|
|
229
|
+
display: "flex",
|
|
230
|
+
flexDirection: "column",
|
|
231
|
+
background: color.bg,
|
|
232
|
+
...props.style
|
|
233
|
+
}, $[0] = color.bg, $[1] = props.style, $[2] = t0) : t0 = $[2];
|
|
234
|
+
let t1;
|
|
235
|
+
$[3] === border ? t1 = $[4] : (t1 = {
|
|
236
|
+
flex: "none",
|
|
237
|
+
display: "flex",
|
|
238
|
+
alignItems: "center",
|
|
239
|
+
gap: 5,
|
|
240
|
+
height: "18%",
|
|
241
|
+
padding: "0 5%",
|
|
242
|
+
borderBottom: border
|
|
243
|
+
}, $[3] = border, $[4] = t1);
|
|
244
|
+
let t2;
|
|
245
|
+
$[5] === primary.bg ? t2 = $[6] : (t2 = /* @__PURE__ */ jsx("div", { style: {
|
|
246
|
+
flex: "none",
|
|
247
|
+
width: 8,
|
|
248
|
+
height: 8,
|
|
249
|
+
borderRadius: 2,
|
|
250
|
+
background: primary.bg
|
|
251
|
+
} }), $[5] = primary.bg, $[6] = t2);
|
|
252
|
+
let t3;
|
|
253
|
+
$[7] === color.fg ? t3 = $[8] : (t3 = /* @__PURE__ */ jsx("div", { style: line("22%", color.fg) }), $[7] = color.fg, $[8] = t3);
|
|
254
|
+
let t4;
|
|
255
|
+
$[9] === Symbol.for("react.memo_cache_sentinel") ? (t4 = /* @__PURE__ */ jsx("div", { style: { flex: 1 } }), $[9] = t4) : t4 = $[9];
|
|
256
|
+
let t5;
|
|
257
|
+
$[10] === color.avatar.blue.bg ? t5 = $[11] : (t5 = /* @__PURE__ */ jsx("div", { style: {
|
|
258
|
+
flex: "none",
|
|
259
|
+
width: 8,
|
|
260
|
+
height: 8,
|
|
261
|
+
borderRadius: "50%",
|
|
262
|
+
background: color.avatar.blue.bg
|
|
263
|
+
} }), $[10] = color.avatar.blue.bg, $[11] = t5);
|
|
264
|
+
let t6;
|
|
265
|
+
$[12] !== t1 || $[13] !== t2 || $[14] !== t3 || $[15] !== t5 ? (t6 = /* @__PURE__ */ jsxs("div", {
|
|
266
|
+
style: t1,
|
|
267
|
+
children: [
|
|
268
|
+
t2,
|
|
269
|
+
t3,
|
|
270
|
+
t4,
|
|
271
|
+
t5
|
|
272
|
+
]
|
|
273
|
+
}), $[12] = t1, $[13] = t2, $[14] = t3, $[15] = t5, $[16] = t6) : t6 = $[16];
|
|
274
|
+
let t7;
|
|
275
|
+
$[17] === Symbol.for("react.memo_cache_sentinel") ? (t7 = {
|
|
276
|
+
flex: 1,
|
|
277
|
+
display: "flex",
|
|
278
|
+
minHeight: 0
|
|
279
|
+
}, $[17] = t7) : t7 = $[17];
|
|
280
|
+
let t8;
|
|
281
|
+
$[18] === border ? t8 = $[19] : (t8 = {
|
|
282
|
+
flex: "none",
|
|
283
|
+
display: "flex",
|
|
284
|
+
flexDirection: "column",
|
|
285
|
+
width: "36%",
|
|
286
|
+
borderRight: border
|
|
287
|
+
}, $[18] = border, $[19] = t8);
|
|
288
|
+
let t9;
|
|
289
|
+
$[20] !== color.muted || $[21] !== selected ? (t9 = LIST_ROWS.map((width_0, index) => {
|
|
290
|
+
let isSelected = index === 1;
|
|
291
|
+
return /* @__PURE__ */ jsx("div", {
|
|
292
|
+
style: {
|
|
293
|
+
flex: "none",
|
|
294
|
+
display: "flex",
|
|
295
|
+
alignItems: "center",
|
|
296
|
+
height: "18%",
|
|
297
|
+
padding: "0 9%",
|
|
298
|
+
background: isSelected ? selected.bg : void 0
|
|
299
|
+
},
|
|
300
|
+
children: /* @__PURE__ */ jsx("div", { style: line(width_0, isSelected ? selected.fg : color.muted.fg) })
|
|
301
|
+
}, width_0);
|
|
302
|
+
}), $[20] = color.muted, $[21] = selected, $[22] = t9) : t9 = $[22];
|
|
303
|
+
let t10;
|
|
304
|
+
$[23] !== t8 || $[24] !== t9 ? (t10 = /* @__PURE__ */ jsx("div", {
|
|
305
|
+
style: t8,
|
|
306
|
+
children: t9
|
|
307
|
+
}), $[23] = t8, $[24] = t9, $[25] = t10) : t10 = $[25];
|
|
308
|
+
let t11, t12;
|
|
309
|
+
$[26] === Symbol.for("react.memo_cache_sentinel") ? (t11 = {
|
|
310
|
+
flex: 1,
|
|
311
|
+
display: "flex",
|
|
312
|
+
flexDirection: "column",
|
|
313
|
+
minWidth: 0
|
|
314
|
+
}, t12 = {
|
|
315
|
+
flex: 1,
|
|
316
|
+
display: "flex",
|
|
317
|
+
flexDirection: "column",
|
|
318
|
+
gap: 7,
|
|
319
|
+
padding: "9% 8% 0",
|
|
320
|
+
minHeight: 0,
|
|
321
|
+
overflow: "hidden"
|
|
322
|
+
}, $[26] = t11, $[27] = t12) : (t11 = $[26], t12 = $[27]);
|
|
323
|
+
let t13;
|
|
324
|
+
$[28] === color.fg ? t13 = $[29] : (t13 = /* @__PURE__ */ jsx("div", { style: line("45%", color.fg) }), $[28] = color.fg, $[29] = t13);
|
|
325
|
+
let t14;
|
|
326
|
+
$[30] !== color.muted || $[31] !== input ? (t14 = FIELDS.map((width_1) => /* @__PURE__ */ jsxs("div", {
|
|
327
|
+
style: {
|
|
328
|
+
flex: "none",
|
|
329
|
+
display: "flex",
|
|
330
|
+
flexDirection: "column",
|
|
331
|
+
gap: 3
|
|
332
|
+
},
|
|
333
|
+
children: [/* @__PURE__ */ jsx("div", { style: line(width_1, color.muted.fg) }), /* @__PURE__ */ jsx("div", { style: {
|
|
334
|
+
height: 12,
|
|
335
|
+
borderRadius: 3,
|
|
336
|
+
background: input.bg,
|
|
337
|
+
boxShadow: `inset 0 0 0 1px ${input.border}`
|
|
338
|
+
} })]
|
|
339
|
+
}, width_1)), $[30] = color.muted, $[31] = input, $[32] = t14) : t14 = $[32];
|
|
340
|
+
let t15;
|
|
341
|
+
$[33] !== t13 || $[34] !== t14 ? (t15 = /* @__PURE__ */ jsxs("div", {
|
|
342
|
+
style: t12,
|
|
343
|
+
children: [t13, t14]
|
|
344
|
+
}), $[33] = t13, $[34] = t14, $[35] = t15) : t15 = $[35];
|
|
345
|
+
let t16;
|
|
346
|
+
$[36] === border ? t16 = $[37] : (t16 = {
|
|
347
|
+
flex: "none",
|
|
348
|
+
display: "flex",
|
|
349
|
+
alignItems: "center",
|
|
350
|
+
justifyContent: "flex-end",
|
|
351
|
+
height: "20%",
|
|
352
|
+
padding: "0 8%",
|
|
353
|
+
borderTop: border
|
|
354
|
+
}, $[36] = border, $[37] = t16);
|
|
355
|
+
let t17;
|
|
356
|
+
$[38] === primary.bg ? t17 = $[39] : (t17 = /* @__PURE__ */ jsx("div", { style: {
|
|
357
|
+
width: "32%",
|
|
358
|
+
height: "50%",
|
|
359
|
+
borderRadius: 3,
|
|
360
|
+
background: primary.bg
|
|
361
|
+
} }), $[38] = primary.bg, $[39] = t17);
|
|
362
|
+
let t18;
|
|
363
|
+
$[40] !== t16 || $[41] !== t17 ? (t18 = /* @__PURE__ */ jsx("div", {
|
|
364
|
+
style: t16,
|
|
365
|
+
children: t17
|
|
366
|
+
}), $[40] = t16, $[41] = t17, $[42] = t18) : t18 = $[42];
|
|
367
|
+
let t19;
|
|
368
|
+
$[43] !== t15 || $[44] !== t18 ? (t19 = /* @__PURE__ */ jsxs("div", {
|
|
369
|
+
style: t11,
|
|
370
|
+
children: [t15, t18]
|
|
371
|
+
}), $[43] = t15, $[44] = t18, $[45] = t19) : t19 = $[45];
|
|
372
|
+
let t20;
|
|
373
|
+
$[46] !== t10 || $[47] !== t19 ? (t20 = /* @__PURE__ */ jsxs("div", {
|
|
374
|
+
style: t7,
|
|
375
|
+
children: [t10, t19]
|
|
376
|
+
}), $[46] = t10, $[47] = t19, $[48] = t20) : t20 = $[48];
|
|
377
|
+
let t21;
|
|
378
|
+
return $[49] !== t0 || $[50] !== t20 || $[51] !== t6 ? (t21 = /* @__PURE__ */ jsxs("div", {
|
|
379
|
+
style: t0,
|
|
380
|
+
children: [t6, t20]
|
|
381
|
+
}), $[49] = t0, $[50] = t20, $[51] = t6, $[52] = t21) : t21 = $[52], t21;
|
|
382
|
+
}
|
|
383
|
+
function _temp$4(width, background) {
|
|
384
|
+
return {
|
|
385
|
+
flex: "none",
|
|
386
|
+
width,
|
|
387
|
+
height: 4,
|
|
388
|
+
borderRadius: 2,
|
|
389
|
+
background
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* The floating preview card of a theme: the lo-fi studio mockup on a raised,
|
|
394
|
+
* rounded card, like the appearance previews in macOS System Settings.
|
|
395
|
+
*
|
|
396
|
+
* @internal
|
|
397
|
+
*/
|
|
398
|
+
function ThemeThumbnail(props) {
|
|
399
|
+
let $ = c(2), t0;
|
|
400
|
+
return $[0] === props.options ? t0 = $[1] : (t0 = /* @__PURE__ */ jsx(Card, {
|
|
401
|
+
overflow: "hidden",
|
|
402
|
+
radius: 3,
|
|
403
|
+
shadow: 1,
|
|
404
|
+
children: /* @__PURE__ */ jsx(ThemePreview, { options: props.options })
|
|
405
|
+
}), $[0] = props.options, $[1] = t0), t0;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* The flow for restoring removed themes: the presets and custom themes the
|
|
409
|
+
* user took out of the list, each with a button to put it back — and, for
|
|
410
|
+
* custom themes, one to delete it for good. The machine leaves the flow once
|
|
411
|
+
* the last one is gone.
|
|
412
|
+
*
|
|
413
|
+
* @internal
|
|
414
|
+
*/
|
|
415
|
+
function RemovedThemes() {
|
|
416
|
+
let $ = c(7), { removed, send } = useThemer(), t0;
|
|
417
|
+
if ($[0] !== removed || $[1] !== send) {
|
|
418
|
+
let t1;
|
|
419
|
+
$[3] === send ? t1 = $[4] : (t1 = (theme) => /* @__PURE__ */ jsxs(Stack, {
|
|
420
|
+
gap: 3,
|
|
421
|
+
children: [
|
|
422
|
+
/* @__PURE__ */ jsx(Box, {
|
|
423
|
+
style: { opacity: .6 },
|
|
424
|
+
children: /* @__PURE__ */ jsx(ThemeThumbnail, { options: theme.options })
|
|
425
|
+
}),
|
|
426
|
+
/* @__PURE__ */ jsx(Text, {
|
|
427
|
+
align: "center",
|
|
428
|
+
size: 1,
|
|
429
|
+
textOverflow: "ellipsis",
|
|
430
|
+
children: displayTitle(theme.title)
|
|
431
|
+
}),
|
|
432
|
+
/* @__PURE__ */ jsxs(Flex, {
|
|
433
|
+
gap: 2,
|
|
434
|
+
justify: "center",
|
|
435
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
436
|
+
fontSize: 1,
|
|
437
|
+
mode: "ghost",
|
|
438
|
+
onClick: () => send({
|
|
439
|
+
type: "theme.restore",
|
|
440
|
+
slug: theme.slug
|
|
441
|
+
}),
|
|
442
|
+
padding: 2,
|
|
443
|
+
text: "Restore"
|
|
444
|
+
}), theme.source === "custom" && /* @__PURE__ */ jsx(Button, {
|
|
445
|
+
fontSize: 1,
|
|
446
|
+
mode: "ghost",
|
|
447
|
+
onClick: () => send({
|
|
448
|
+
type: "theme.delete",
|
|
449
|
+
slug: theme.slug
|
|
450
|
+
}),
|
|
451
|
+
padding: 2,
|
|
452
|
+
text: "Delete",
|
|
453
|
+
tone: "critical"
|
|
454
|
+
})]
|
|
455
|
+
})
|
|
456
|
+
]
|
|
457
|
+
}, theme.slug), $[3] = send, $[4] = t1), t0 = removed.map(t1), $[0] = removed, $[1] = send, $[2] = t0;
|
|
458
|
+
} else t0 = $[2];
|
|
459
|
+
let t1;
|
|
460
|
+
return $[5] === t0 ? t1 = $[6] : (t1 = /* @__PURE__ */ jsx(ScrollArea, {
|
|
461
|
+
padding: 3,
|
|
462
|
+
children: /* @__PURE__ */ jsx(Stack, {
|
|
463
|
+
gap: 5,
|
|
464
|
+
children: t0
|
|
465
|
+
})
|
|
466
|
+
}), $[5] = t0, $[6] = t1), t1;
|
|
467
|
+
}
|
|
468
|
+
/** @internal */
|
|
469
|
+
const IMAGE_PALETTE_KEYS = [
|
|
470
|
+
"vibrant",
|
|
471
|
+
"lightVibrant",
|
|
472
|
+
"darkVibrant",
|
|
473
|
+
"muted",
|
|
474
|
+
"lightMuted",
|
|
475
|
+
"darkMuted",
|
|
476
|
+
"dominant"
|
|
477
|
+
], IMAGE_PALETTE_VARIANTS = [
|
|
478
|
+
"muted",
|
|
479
|
+
"vibrant",
|
|
480
|
+
"lightMuted",
|
|
481
|
+
"lightVibrant",
|
|
482
|
+
"darkMuted",
|
|
483
|
+
"darkVibrant"
|
|
484
|
+
], IMAGE_PALETTE_TITLES = {
|
|
485
|
+
dominant: "Dominant",
|
|
486
|
+
vibrant: "Vibrant",
|
|
487
|
+
lightVibrant: "Light vibrant",
|
|
488
|
+
darkVibrant: "Dark vibrant",
|
|
489
|
+
muted: "Muted",
|
|
490
|
+
lightMuted: "Light muted",
|
|
491
|
+
darkMuted: "Dark muted"
|
|
492
|
+
}, TARGETS = [
|
|
493
|
+
["vibrant", {
|
|
494
|
+
lightness: [
|
|
495
|
+
.5,
|
|
496
|
+
.3,
|
|
497
|
+
.7
|
|
498
|
+
],
|
|
499
|
+
saturation: [
|
|
500
|
+
1,
|
|
501
|
+
.35,
|
|
502
|
+
1
|
|
503
|
+
]
|
|
504
|
+
}],
|
|
505
|
+
["lightVibrant", {
|
|
506
|
+
lightness: [
|
|
507
|
+
.74,
|
|
508
|
+
.55,
|
|
509
|
+
1
|
|
510
|
+
],
|
|
511
|
+
saturation: [
|
|
512
|
+
1,
|
|
513
|
+
.35,
|
|
514
|
+
1
|
|
515
|
+
]
|
|
516
|
+
}],
|
|
517
|
+
["darkVibrant", {
|
|
518
|
+
lightness: [
|
|
519
|
+
.26,
|
|
520
|
+
0,
|
|
521
|
+
.45
|
|
522
|
+
],
|
|
523
|
+
saturation: [
|
|
524
|
+
1,
|
|
525
|
+
.35,
|
|
526
|
+
1
|
|
527
|
+
]
|
|
528
|
+
}],
|
|
529
|
+
["muted", {
|
|
530
|
+
lightness: [
|
|
531
|
+
.5,
|
|
532
|
+
.3,
|
|
533
|
+
.7
|
|
534
|
+
],
|
|
535
|
+
saturation: [
|
|
536
|
+
.3,
|
|
537
|
+
0,
|
|
538
|
+
.4
|
|
539
|
+
]
|
|
540
|
+
}],
|
|
541
|
+
["lightMuted", {
|
|
542
|
+
lightness: [
|
|
543
|
+
.74,
|
|
544
|
+
.55,
|
|
545
|
+
1
|
|
546
|
+
],
|
|
547
|
+
saturation: [
|
|
548
|
+
.3,
|
|
549
|
+
0,
|
|
550
|
+
.4
|
|
551
|
+
]
|
|
552
|
+
}],
|
|
553
|
+
["darkMuted", {
|
|
554
|
+
lightness: [
|
|
555
|
+
.26,
|
|
556
|
+
0,
|
|
557
|
+
.45
|
|
558
|
+
],
|
|
559
|
+
saturation: [
|
|
560
|
+
.3,
|
|
561
|
+
0,
|
|
562
|
+
.4
|
|
563
|
+
]
|
|
564
|
+
}]
|
|
565
|
+
];
|
|
566
|
+
/**
|
|
567
|
+
* Extracts the color palette of an image from its pixels, the way the Vibrant
|
|
568
|
+
* algorithm does: the colors are quantized into a coarse histogram, and each
|
|
569
|
+
* swatch claims the color that comes closest to its target lightness and
|
|
570
|
+
* saturation — weighted by how much of the image it covers — that no other
|
|
571
|
+
* swatch has claimed yet. The dominant swatch is simply the most common
|
|
572
|
+
* color. Runs anywhere pixels come from, so it can run on device.
|
|
573
|
+
*
|
|
574
|
+
* @internal
|
|
575
|
+
*/
|
|
576
|
+
function extractImagePalette(pixels) {
|
|
577
|
+
let candidates = quantize(pixels), palette = {
|
|
578
|
+
dominant: null,
|
|
579
|
+
vibrant: null,
|
|
580
|
+
lightVibrant: null,
|
|
581
|
+
darkVibrant: null,
|
|
582
|
+
muted: null,
|
|
583
|
+
lightMuted: null,
|
|
584
|
+
darkMuted: null
|
|
585
|
+
};
|
|
586
|
+
if (candidates.length === 0) return palette;
|
|
587
|
+
let maximumPopulation = Math.max(...candidates.map((candidate) => candidate.population)), claimed = /* @__PURE__ */ new Set();
|
|
588
|
+
for (let [key, target] of TARGETS) {
|
|
589
|
+
let best = null, bestScore = -Infinity;
|
|
590
|
+
for (let candidate of candidates) {
|
|
591
|
+
if (claimed.has(candidate.hex) || !matchesTarget(candidate, target)) continue;
|
|
592
|
+
let score = scoreCandidate(candidate, target, maximumPopulation);
|
|
593
|
+
score > bestScore && (best = candidate, bestScore = score);
|
|
594
|
+
}
|
|
595
|
+
best && (palette[key] = best.hex, claimed.add(best.hex));
|
|
596
|
+
}
|
|
597
|
+
return palette.dominant = candidates.reduce((most, candidate) => candidate.population > most.population ? candidate : most).hex, palette;
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Turns the pixels into a histogram of quantized colors, each averaged over
|
|
601
|
+
* the pixels it stands for, leaving out transparent pixels and the near-black
|
|
602
|
+
* and near-white ones that are backdrop rather than color.
|
|
603
|
+
*/
|
|
604
|
+
function quantize(pixels) {
|
|
605
|
+
let { data } = pixels, buckets = /* @__PURE__ */ new Map(), length = Math.min(data.length, pixels.width * pixels.height * 4);
|
|
606
|
+
for (let offset = 0; offset + 3 < length; offset += 4) {
|
|
607
|
+
let r = data[offset], g = data[offset + 1], b = data[offset + 2];
|
|
608
|
+
if (data[offset + 3] < 125) continue;
|
|
609
|
+
let lightness = (Math.max(r, g, b) + Math.min(r, g, b)) / 510;
|
|
610
|
+
if (lightness < .05 || lightness > .95) continue;
|
|
611
|
+
let key = r >> 3 << 10 | g >> 3 << 5 | b >> 3, bucket = buckets.get(key);
|
|
612
|
+
bucket ? (bucket.count++, bucket.r += r, bucket.g += g, bucket.b += b) : buckets.set(key, {
|
|
613
|
+
count: 1,
|
|
614
|
+
r,
|
|
615
|
+
g,
|
|
616
|
+
b
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
let candidates = [];
|
|
620
|
+
for (let bucket of buckets.values()) {
|
|
621
|
+
let r = Math.round(bucket.r / bucket.count), g = Math.round(bucket.g / bucket.count), b = Math.round(bucket.b / bucket.count), { saturation, lightness } = saturationAndLightness(r, g, b);
|
|
622
|
+
candidates.push({
|
|
623
|
+
hex: rgbToHex(r, g, b),
|
|
624
|
+
population: bucket.count,
|
|
625
|
+
saturation,
|
|
626
|
+
lightness
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
return candidates;
|
|
630
|
+
}
|
|
631
|
+
function matchesTarget(candidate, target) {
|
|
632
|
+
return candidate.lightness >= target.lightness[1] && candidate.lightness <= target.lightness[2] && candidate.saturation >= target.saturation[1] && candidate.saturation <= target.saturation[2];
|
|
633
|
+
}
|
|
634
|
+
function scoreCandidate(candidate, target, maximumPopulation) {
|
|
635
|
+
return 3 * (1 - Math.abs(candidate.saturation - target.saturation[0])) + 6.5 * (1 - Math.abs(candidate.lightness - target.lightness[0])) + .5 * (candidate.population / maximumPopulation);
|
|
636
|
+
}
|
|
637
|
+
function saturationAndLightness(r, g, b) {
|
|
638
|
+
let max = Math.max(r, g, b) / 255, min = Math.min(r, g, b) / 255, lightness = (max + min) / 2, delta = max - min;
|
|
639
|
+
return {
|
|
640
|
+
saturation: delta === 0 ? 0 : lightness > .5 ? delta / (2 - max - min) : delta / (max + min),
|
|
641
|
+
lightness
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
function rgbToHex(r, g, b) {
|
|
645
|
+
return `#${[
|
|
646
|
+
r,
|
|
647
|
+
g,
|
|
648
|
+
b
|
|
649
|
+
].map((channel) => channel.toString(16).padStart(2, "0")).join("")}`;
|
|
650
|
+
}
|
|
651
|
+
/** Reads `#rgb` and `#rrggbb` alike — stored palettes may hold either */
|
|
652
|
+
function hexToRgb(hex) {
|
|
653
|
+
let expanded = hex.length === 4 ? `${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex.slice(1);
|
|
654
|
+
return [
|
|
655
|
+
parseInt(expanded.slice(0, 2), 16),
|
|
656
|
+
parseInt(expanded.slice(2, 4), 16),
|
|
657
|
+
parseInt(expanded.slice(4, 6), 16)
|
|
658
|
+
];
|
|
659
|
+
}
|
|
660
|
+
/** Mixes `amount` of `color` into `base` */
|
|
661
|
+
function tint(base, color, amount) {
|
|
662
|
+
let [r1, g1, b1] = hexToRgb(base), [r2, g2, b2] = hexToRgb(color);
|
|
663
|
+
return rgbToHex(Math.round(r1 + (r2 - r1) * amount), Math.round(g1 + (g2 - g1) * amount), Math.round(b1 + (b2 - b1) * amount));
|
|
664
|
+
}
|
|
665
|
+
/** How much of the palette tints each scheme's stock background */
|
|
666
|
+
const BACKGROUND_TINT = {
|
|
667
|
+
light: .08,
|
|
668
|
+
dark: .3
|
|
669
|
+
}, BUTTON_LABEL = {
|
|
670
|
+
light: "#ffffff",
|
|
671
|
+
dark: "#000000"
|
|
672
|
+
}, ACCENT_LIGHTNESS_STEP = .01;
|
|
673
|
+
function contrastRatio(a, b) {
|
|
674
|
+
let [lighter, darker] = [relativeLuminance(a), relativeLuminance(b)].sort((x, y) => y - x);
|
|
675
|
+
return (lighter + .05) / (darker + .05);
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Makes an accent readable as a button in the given scheme: a light scheme
|
|
679
|
+
* accent is darkened until white text on it reaches AA contrast, a dark
|
|
680
|
+
* scheme accent lightened until black text does — as far as the accent's
|
|
681
|
+
* hue and saturation allow. Accents that already pass come back untouched.
|
|
682
|
+
*
|
|
683
|
+
* @internal
|
|
684
|
+
*/
|
|
685
|
+
function readableAccent(hex, scheme) {
|
|
686
|
+
let label = BUTTON_LABEL[scheme], { h, s } = hexToHsl(hex), { l } = hexToHsl(hex), current = hex;
|
|
687
|
+
for (; contrastRatio(current, label) < 4.5;) {
|
|
688
|
+
let next = scheme === "light" ? l - ACCENT_LIGHTNESS_STEP : l + ACCENT_LIGHTNESS_STEP;
|
|
689
|
+
if (next < 0 || next > 1) break;
|
|
690
|
+
l = next, current = hslToHex({
|
|
691
|
+
h,
|
|
692
|
+
s,
|
|
693
|
+
l
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
return current;
|
|
697
|
+
}
|
|
698
|
+
/** The accent a variant gives a scheme, made readable on a button */
|
|
699
|
+
function variantAccent(palette, variant, scheme) {
|
|
700
|
+
let swatch = (variant ? palette[variant] : null) ?? palette.vibrant ?? (scheme === "light" ? palette.darkVibrant : palette.lightVibrant) ?? palette.dominant;
|
|
701
|
+
return swatch === null ? null : readableAccent(swatch, scheme);
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Derives theme options from an image palette: the accent of both schemes is
|
|
705
|
+
* the swatch of the chosen variant — or, without one, the vibrant swatch,
|
|
706
|
+
* each scheme falling back to the vibrant swatch of its own end of the scale —
|
|
707
|
+
* adjusted until it is readable as a button. The muted swatch becomes the text color, and the light and dark muted
|
|
708
|
+
* swatches tint the stock backgrounds of their scheme — enough to carry the
|
|
709
|
+
* image's tone without giving up a usable canvas. Whatever the palette lacks
|
|
710
|
+
* stays at the theme defaults.
|
|
711
|
+
*
|
|
712
|
+
* @internal
|
|
713
|
+
*/
|
|
714
|
+
function optionsFromImagePalette(palette, variant) {
|
|
715
|
+
let options = {}, light = schemeFromPalette(variantAccent(palette, variant ?? null, "light"), palette.muted ?? palette.darkMuted, palette.lightMuted ?? palette.muted ?? palette.lightVibrant, "light"), dark = schemeFromPalette(variantAccent(palette, variant ?? null, "dark"), palette.muted ?? palette.lightMuted, palette.darkMuted ?? palette.muted ?? palette.darkVibrant, "dark");
|
|
716
|
+
return light && (options.light = light), dark && (options.dark = dark), options;
|
|
717
|
+
}
|
|
718
|
+
function schemeFromPalette(accent, text, backgroundTint, scheme) {
|
|
719
|
+
let options = {};
|
|
720
|
+
return accent && (options.accent = accent), text && (options.text = text), backgroundTint && (options.background = tint(DEFAULT_BACKGROUND[scheme], backgroundTint, BACKGROUND_TINT[scheme])), Object.keys(options).length > 0 ? options : null;
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Applies an image palette to existing theme options: the colors the palette
|
|
724
|
+
* provides replace the current ones scheme by scheme, and whatever it does
|
|
725
|
+
* not touch — like the contrast — stays as it is.
|
|
726
|
+
*
|
|
727
|
+
* @internal
|
|
728
|
+
*/
|
|
729
|
+
function applyImagePalette(options, palette, variant) {
|
|
730
|
+
let derived = optionsFromImagePalette(palette, variant), next = { ...options };
|
|
731
|
+
for (let scheme of SCHEMES) derived[scheme] && (next[scheme] = {
|
|
732
|
+
...options[scheme],
|
|
733
|
+
...derived[scheme]
|
|
734
|
+
});
|
|
735
|
+
return next;
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* The variant a theme is built around: the one whose swatch is the theme's
|
|
739
|
+
* accent, or `null` when the accent comes from somewhere else.
|
|
740
|
+
*
|
|
741
|
+
* @internal
|
|
742
|
+
*/
|
|
743
|
+
function currentImageVariant(options, palette) {
|
|
744
|
+
let accent = options.light?.accent;
|
|
745
|
+
return accent ? IMAGE_PALETTE_VARIANTS.find((variant) => palette[variant] !== null && variantAccent(palette, variant, "light") === accent) ?? null : null;
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* How much a swatch has going for it as an accent: saturation first, then
|
|
749
|
+
* closeness to mid lightness — the range where an accent reads best.
|
|
750
|
+
*/
|
|
751
|
+
function interest(hex) {
|
|
752
|
+
let { s, l } = hexToHsl(hex);
|
|
753
|
+
return .1 + .55 * s + .35 * (1 - Math.abs(l - .5) * 2);
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Picks a variant for the "I'm feeling lucky" button: a random one, weighted
|
|
757
|
+
* by how interesting its swatch is as an accent, and never the one the theme
|
|
758
|
+
* already uses while there are others to pick from.
|
|
759
|
+
*
|
|
760
|
+
* @internal
|
|
761
|
+
*/
|
|
762
|
+
function pickLuckyVariant(palette, options = {}) {
|
|
763
|
+
let { exclude = null, random = Math.random } = options, candidates = IMAGE_PALETTE_VARIANTS.filter((variant) => palette[variant] !== null);
|
|
764
|
+
if (candidates.length > 1 && (candidates = candidates.filter((variant) => variant !== exclude)), candidates.length === 0) return null;
|
|
765
|
+
let weights = candidates.map((variant) => interest(palette[variant])), total = weights.reduce((sum, weight) => sum + weight, 0), pick = random() * total;
|
|
766
|
+
for (let [index, variant] of candidates.entries()) if (pick -= weights[index], pick < 0) return variant;
|
|
767
|
+
return candidates.at(-1) ?? null;
|
|
768
|
+
}
|
|
769
|
+
/** A theme title for the image a palette came from, e.g. `sunset-beach.jpg` → `sunset beach` @internal */
|
|
770
|
+
function titleFromFileName(name) {
|
|
771
|
+
return name.replace(/\.[^.]+$/, "").replace(/[-_]+/g, " ").trim() || "Image theme";
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* A button with an animated tooltip instead of a `title` attribute. Icon-only
|
|
775
|
+
* buttons take the tooltip as their accessible name.
|
|
776
|
+
*
|
|
777
|
+
* @internal
|
|
778
|
+
*/
|
|
779
|
+
function TooltipButton(props) {
|
|
780
|
+
let $ = c(11), buttonProps, tooltip;
|
|
781
|
+
$[0] === props ? (buttonProps = $[1], tooltip = $[2]) : ({tooltip, ...buttonProps} = props, $[0] = props, $[1] = buttonProps, $[2] = tooltip);
|
|
782
|
+
let t0;
|
|
783
|
+
$[3] === tooltip ? t0 = $[4] : (t0 = /* @__PURE__ */ jsx(Text, {
|
|
784
|
+
size: 1,
|
|
785
|
+
children: tooltip
|
|
786
|
+
}), $[3] = tooltip, $[4] = t0);
|
|
787
|
+
let t1 = buttonProps.text === void 0 ? tooltip : void 0, t2;
|
|
788
|
+
$[5] !== buttonProps || $[6] !== t1 ? (t2 = /* @__PURE__ */ jsx(Button, {
|
|
789
|
+
"aria-label": t1,
|
|
790
|
+
...buttonProps
|
|
791
|
+
}), $[5] = buttonProps, $[6] = t1, $[7] = t2) : t2 = $[7];
|
|
792
|
+
let t3;
|
|
793
|
+
return $[8] !== t0 || $[9] !== t2 ? (t3 = /* @__PURE__ */ jsx(Tooltip, {
|
|
794
|
+
animate: !0,
|
|
795
|
+
content: t0,
|
|
796
|
+
placement: "bottom",
|
|
797
|
+
portal: !0,
|
|
798
|
+
children: t2
|
|
799
|
+
}), $[8] = t0, $[9] = t2, $[10] = t3) : t3 = $[10], t3;
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* A button that opens the file picker for an image, and hands the chosen file
|
|
803
|
+
* over — the picker is a hidden file input, so nothing is uploaded anywhere.
|
|
804
|
+
*
|
|
805
|
+
* @internal
|
|
806
|
+
*/
|
|
807
|
+
function ImageFileButton(props) {
|
|
808
|
+
let $ = c(11), buttonProps, onFile;
|
|
809
|
+
$[0] === props ? (buttonProps = $[1], onFile = $[2]) : ({onFile, ...buttonProps} = props, $[0] = props, $[1] = buttonProps, $[2] = onFile);
|
|
810
|
+
let inputRef = useRef(null), t0;
|
|
811
|
+
$[3] === Symbol.for("react.memo_cache_sentinel") ? (t0 = () => inputRef.current?.click(), $[3] = t0) : t0 = $[3];
|
|
812
|
+
let t1;
|
|
813
|
+
$[4] === buttonProps ? t1 = $[5] : (t1 = /* @__PURE__ */ jsx(TooltipButton, {
|
|
814
|
+
...buttonProps,
|
|
815
|
+
icon: ImageIcon,
|
|
816
|
+
onClick: t0
|
|
817
|
+
}), $[4] = buttonProps, $[5] = t1);
|
|
818
|
+
let t2;
|
|
819
|
+
$[6] === onFile ? t2 = $[7] : (t2 = /* @__PURE__ */ jsx("input", {
|
|
820
|
+
accept: "image/*",
|
|
821
|
+
hidden: !0,
|
|
822
|
+
onChange: (event) => {
|
|
823
|
+
let file = event.currentTarget.files?.[0];
|
|
824
|
+
event.currentTarget.value = "", file && onFile(file);
|
|
825
|
+
},
|
|
826
|
+
ref: inputRef,
|
|
827
|
+
type: "file"
|
|
828
|
+
}), $[6] = onFile, $[7] = t2);
|
|
829
|
+
let t3;
|
|
830
|
+
return $[8] !== t1 || $[9] !== t2 ? (t3 = /* @__PURE__ */ jsxs(Fragment, { children: [t1, t2] }), $[8] = t1, $[9] = t2, $[10] = t3) : t3 = $[10], t3;
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Reads the pixels of an image file on device: the image is decoded, drawn
|
|
834
|
+
* onto a small canvas and read back, so nothing leaves the browser.
|
|
835
|
+
*
|
|
836
|
+
* @internal
|
|
837
|
+
*/
|
|
838
|
+
async function readImagePixels(file) {
|
|
839
|
+
let image = await decodeImage(file);
|
|
840
|
+
try {
|
|
841
|
+
let scale = Math.min(1, 96 / Math.max(image.width, image.height)), width = Math.max(1, Math.round(image.width * scale)), height = Math.max(1, Math.round(image.height * scale)), canvas = document.createElement("canvas");
|
|
842
|
+
canvas.width = width, canvas.height = height;
|
|
843
|
+
let context = canvas.getContext("2d", { willReadFrequently: !0 });
|
|
844
|
+
if (!context) throw Error("Could not draw the image");
|
|
845
|
+
return context.drawImage(image, 0, 0, width, height), {
|
|
846
|
+
data: context.getImageData(0, 0, width, height).data,
|
|
847
|
+
width,
|
|
848
|
+
height
|
|
849
|
+
};
|
|
850
|
+
} finally {
|
|
851
|
+
typeof ImageBitmap < "u" && image instanceof ImageBitmap && image.close();
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
async function decodeImage(file) {
|
|
855
|
+
if (typeof createImageBitmap == "function") try {
|
|
856
|
+
return await createImageBitmap(file);
|
|
857
|
+
} catch {}
|
|
858
|
+
let url = URL.createObjectURL(file);
|
|
859
|
+
try {
|
|
860
|
+
return await new Promise((resolve, reject) => {
|
|
861
|
+
let image = new Image();
|
|
862
|
+
image.addEventListener("load", () => resolve(image), { once: !0 }), image.addEventListener("error", () => reject(/* @__PURE__ */ Error("Could not decode the image")), { once: !0 }), image.src = url;
|
|
863
|
+
});
|
|
864
|
+
} finally {
|
|
865
|
+
URL.revokeObjectURL(url);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Extracts the palette of image files on device and hands it to `onPalette`,
|
|
870
|
+
* along with the file it came from. Failures end up as a toast.
|
|
871
|
+
*
|
|
872
|
+
* @internal
|
|
873
|
+
*/
|
|
874
|
+
function useImagePalette(onPalette) {
|
|
875
|
+
let $ = c(10), toast = useToast(), [busy, setBusy] = useState(!1), onPaletteRef = useRef(onPalette), mountedRef = useRef(!0), latestPickRef = useRef(0), t0, t1;
|
|
876
|
+
$[0] === onPalette ? (t0 = $[1], t1 = $[2]) : (t0 = () => {
|
|
877
|
+
onPaletteRef.current = onPalette;
|
|
878
|
+
}, t1 = [onPalette], $[0] = onPalette, $[1] = t0, $[2] = t1), useEffect(t0, t1);
|
|
879
|
+
let t2, t3;
|
|
880
|
+
$[3] === Symbol.for("react.memo_cache_sentinel") ? (t2 = () => (mountedRef.current = !0, () => {
|
|
881
|
+
mountedRef.current = !1;
|
|
882
|
+
}), t3 = [], $[3] = t2, $[4] = t3) : (t2 = $[3], t3 = $[4]), useEffect(t2, t3);
|
|
883
|
+
let t4;
|
|
884
|
+
$[5] === toast ? t4 = $[6] : (t4 = (file) => {
|
|
885
|
+
let pick = latestPickRef.current += 1;
|
|
886
|
+
setBusy(!0);
|
|
887
|
+
let isCurrent = () => mountedRef.current && latestPickRef.current === pick, extract = async () => {
|
|
888
|
+
let palette = extractImagePalette(await readImagePixels(file));
|
|
889
|
+
isCurrent() && (Object.values(palette).every(_temp$3) ? toast.push({
|
|
890
|
+
status: "warning",
|
|
891
|
+
title: "Found no colors in that image"
|
|
892
|
+
}) : onPaletteRef.current(palette, file));
|
|
893
|
+
};
|
|
894
|
+
extract().catch(() => {
|
|
895
|
+
isCurrent() && toast.push({
|
|
896
|
+
status: "error",
|
|
897
|
+
title: "Could not read that image"
|
|
898
|
+
});
|
|
899
|
+
}).finally(() => {
|
|
900
|
+
isCurrent() && setBusy(!1);
|
|
901
|
+
});
|
|
902
|
+
}, $[5] = toast, $[6] = t4);
|
|
903
|
+
let pickImage = t4, t5;
|
|
904
|
+
return $[7] !== busy || $[8] !== pickImage ? (t5 = {
|
|
905
|
+
busy,
|
|
906
|
+
pickImage
|
|
907
|
+
}, $[7] = busy, $[8] = pickImage, $[9] = t5) : t5 = $[9], t5;
|
|
908
|
+
}
|
|
909
|
+
function _temp$3(swatch) {
|
|
910
|
+
return swatch === null;
|
|
911
|
+
}
|
|
912
|
+
/** The swatches on display: the vibrant family over the muted one */
|
|
913
|
+
const TILES = [
|
|
914
|
+
"vibrant",
|
|
915
|
+
"lightVibrant",
|
|
916
|
+
"darkVibrant",
|
|
917
|
+
"muted",
|
|
918
|
+
"lightMuted",
|
|
919
|
+
"darkMuted"
|
|
920
|
+
], PaletteGrid = styled.div.withConfig({
|
|
921
|
+
displayName: "PaletteGrid",
|
|
922
|
+
componentId: "sc-j2tqre-0"
|
|
923
|
+
})`display:grid;grid-template-columns:repeat(3,${24}px);grid-auto-rows:${24}px;gap:${4}px;&[data-with-image='true']{grid-template-columns:${52}px repeat(3,${24}px);}`, ImageTile = styled.img.withConfig({
|
|
924
|
+
displayName: "ImageTile",
|
|
925
|
+
componentId: "sc-j2tqre-1"
|
|
926
|
+
})`display:block;grid-row:span 2;width:${52}px;height:${52}px;border-radius:3px;object-fit:cover;box-shadow:inset 0 0 0 1px var(--card-border-color);`, Tile = styled.span.withConfig({
|
|
927
|
+
displayName: "Tile",
|
|
928
|
+
componentId: "sc-j2tqre-2"
|
|
929
|
+
})`display:block;border-radius:3px;box-shadow:inset 0 0 0 1px var(--card-border-color);&[data-empty='true']{background:repeating-linear-gradient( -45deg,transparent 0 3px,var(--card-border-color) 3px 4px );}`, VariantRow = styled.div.withConfig({
|
|
930
|
+
displayName: "VariantRow",
|
|
931
|
+
componentId: "sc-j2tqre-3"
|
|
932
|
+
})`display:flex;gap:${8}px;margin:0 -${12}px;padding:0 ${12}px;overflow-x:auto;scroll-snap-type:x mandatory;scroll-padding-left:${12}px;scrollbar-width:thin;`, VariantButton = styled.button.withConfig({
|
|
933
|
+
displayName: "VariantButton",
|
|
934
|
+
componentId: "sc-j2tqre-4"
|
|
935
|
+
})`appearance:none;flex:none;display:block;box-sizing:border-box;width:calc(var(--variant-width) + 8px);margin:0;padding:0;border:0;background:none;color:inherit;font:inherit;text-align:center;cursor:pointer;scroll-snap-align:start;&:focus{outline:none;}`, VariantFrame = styled.span.withConfig({
|
|
936
|
+
displayName: "VariantFrame",
|
|
937
|
+
componentId: "sc-j2tqre-5"
|
|
938
|
+
})`display:block;box-sizing:border-box;width:calc(var(--variant-width) + 8px);height:calc(var(--variant-width) * 9 / 16 + 8px);padding:4px;border-radius:7px;overflow:hidden;transition:box-shadow 100ms;${VariantButton}:hover &{box-shadow:inset 0 0 0 2px var(--card-border-color);}${VariantButton}[aria-pressed='true'] &{box-shadow:inset 0 0 0 2px var(--card-focus-ring-color);}${VariantButton}:focus-visible &{outline:2px solid var(--card-focus-ring-color);outline-offset:-2px;}`, VariantScale = styled.span.withConfig({
|
|
939
|
+
displayName: "VariantScale",
|
|
940
|
+
componentId: "sc-j2tqre-6"
|
|
941
|
+
})`display:block;width:${144}px;transform:scale(var(--variant-scale));transform-origin:top left;`;
|
|
942
|
+
/**
|
|
943
|
+
* Takes the colors of a theme from an image, on device: the image is read
|
|
944
|
+
* into a canvas and its palette extracted right there — nothing is uploaded.
|
|
945
|
+
* Shows the image (while the session lasts) next to its palette, offers a
|
|
946
|
+
* theme variant built around each swatch as a small preview, and a lucky
|
|
947
|
+
* button that picks an interesting one.
|
|
948
|
+
*
|
|
949
|
+
* @internal
|
|
950
|
+
*/
|
|
951
|
+
function ImagePaletteSection(props) {
|
|
952
|
+
let $ = c(38), { imageUrl, onPalette, onVariant, onLucky, options, palette } = props, { busy, pickImage } = useImagePalette(onPalette), [dragging, setDragging] = useState(!1), dragDepth = useRef(0), t0;
|
|
953
|
+
$[0] !== options || $[1] !== palette ? (t0 = palette ? currentImageVariant(options, palette) : null, $[0] = options, $[1] = palette, $[2] = t0) : t0 = $[2];
|
|
954
|
+
let current = t0, rowRef = useRef(null), [variantWidth, setVariantWidth] = useState(58), t1;
|
|
955
|
+
$[3] === Symbol.for("react.memo_cache_sentinel") ? (t1 = () => {
|
|
956
|
+
let row = rowRef.current;
|
|
957
|
+
if (!row) return;
|
|
958
|
+
let measure = () => {
|
|
959
|
+
let visible = row.clientWidth - 12 - 16;
|
|
960
|
+
setVariantWidth(Math.max(24, Math.floor(visible / 2.5) - 8));
|
|
961
|
+
};
|
|
962
|
+
measure();
|
|
963
|
+
let observer = new ResizeObserver(measure);
|
|
964
|
+
return observer.observe(row), () => observer.disconnect();
|
|
965
|
+
}, $[3] = t1) : t1 = $[3];
|
|
966
|
+
let t2;
|
|
967
|
+
$[4] === palette ? t2 = $[5] : (t2 = [palette], $[4] = palette, $[5] = t2), useEffect(t1, t2);
|
|
968
|
+
let t3;
|
|
969
|
+
bb0: {
|
|
970
|
+
if (!palette) {
|
|
971
|
+
let t4;
|
|
972
|
+
$[6] === Symbol.for("react.memo_cache_sentinel") ? (t4 = [], $[6] = t4) : t4 = $[6], t3 = t4;
|
|
973
|
+
break bb0;
|
|
974
|
+
}
|
|
975
|
+
let t4;
|
|
976
|
+
$[7] !== options || $[8] !== palette ? (t4 = IMAGE_PALETTE_VARIANTS.flatMap((variant) => palette[variant] === null ? [] : [{
|
|
977
|
+
variant,
|
|
978
|
+
options: applyImagePalette(options, palette, variant)
|
|
979
|
+
}]), $[7] = options, $[8] = palette, $[9] = t4) : t4 = $[9], t3 = t4;
|
|
980
|
+
}
|
|
981
|
+
let variants = t3, t4, t5;
|
|
982
|
+
$[10] === Symbol.for("react.memo_cache_sentinel") ? (t4 = (event) => {
|
|
983
|
+
event.preventDefault(), dragDepth.current += 1, setDragging(!0);
|
|
984
|
+
}, t5 = () => {
|
|
985
|
+
dragDepth.current = Math.max(0, dragDepth.current - 1), dragDepth.current === 0 && setDragging(!1);
|
|
986
|
+
}, $[10] = t4, $[11] = t5) : (t4 = $[10], t5 = $[11]);
|
|
987
|
+
let t6;
|
|
988
|
+
$[12] === pickImage ? t6 = $[13] : (t6 = (event_1) => {
|
|
989
|
+
event_1.preventDefault(), dragDepth.current = 0, setDragging(!1);
|
|
990
|
+
let file = event_1.dataTransfer.files[0];
|
|
991
|
+
file && pickImage(file);
|
|
992
|
+
}, $[12] = pickImage, $[13] = t6);
|
|
993
|
+
let t7 = dragging ? "primary" : "default", t8;
|
|
994
|
+
$[14] === Symbol.for("react.memo_cache_sentinel") ? (t8 = /* @__PURE__ */ jsx(Box, {
|
|
995
|
+
flex: 1,
|
|
996
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
997
|
+
size: 1,
|
|
998
|
+
weight: "medium",
|
|
999
|
+
children: "Image palette"
|
|
1000
|
+
})
|
|
1001
|
+
}), $[14] = t8) : t8 = $[14];
|
|
1002
|
+
let t9;
|
|
1003
|
+
$[15] !== busy || $[16] !== palette || $[17] !== pickImage ? (t9 = palette && /* @__PURE__ */ jsx(ImageFileButton, {
|
|
1004
|
+
loading: busy,
|
|
1005
|
+
mode: "bleed",
|
|
1006
|
+
onFile: pickImage,
|
|
1007
|
+
padding: 2,
|
|
1008
|
+
tooltip: "Pick another image"
|
|
1009
|
+
}), $[15] = busy, $[16] = palette, $[17] = pickImage, $[18] = t9) : t9 = $[18];
|
|
1010
|
+
let t10;
|
|
1011
|
+
$[19] === t9 ? t10 = $[20] : (t10 = /* @__PURE__ */ jsxs(Flex, {
|
|
1012
|
+
align: "center",
|
|
1013
|
+
gap: 2,
|
|
1014
|
+
children: [t8, t9]
|
|
1015
|
+
}), $[19] = t9, $[20] = t10);
|
|
1016
|
+
let t11;
|
|
1017
|
+
$[21] !== busy || $[22] !== current || $[23] !== imageUrl || $[24] !== onLucky || $[25] !== onVariant || $[26] !== palette || $[27] !== pickImage || $[28] !== variantWidth || $[29] !== variants ? (t11 = palette ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1018
|
+
/* @__PURE__ */ jsxs(PaletteGrid, {
|
|
1019
|
+
"data-with-image": imageUrl !== void 0,
|
|
1020
|
+
children: [imageUrl && /* @__PURE__ */ jsx(ImageTile, {
|
|
1021
|
+
alt: "",
|
|
1022
|
+
src: imageUrl
|
|
1023
|
+
}), TILES.map((key) => {
|
|
1024
|
+
let hex = palette[key], title = IMAGE_PALETTE_TITLES[key];
|
|
1025
|
+
return /* @__PURE__ */ jsx(Tooltip, {
|
|
1026
|
+
animate: !0,
|
|
1027
|
+
content: /* @__PURE__ */ jsx(Text, {
|
|
1028
|
+
size: 1,
|
|
1029
|
+
children: hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`
|
|
1030
|
+
}),
|
|
1031
|
+
placement: "bottom",
|
|
1032
|
+
portal: !0,
|
|
1033
|
+
children: /* @__PURE__ */ jsx(Tile, {
|
|
1034
|
+
"data-empty": hex === null,
|
|
1035
|
+
style: { background: hex ?? void 0 }
|
|
1036
|
+
})
|
|
1037
|
+
}, key);
|
|
1038
|
+
})]
|
|
1039
|
+
}),
|
|
1040
|
+
/* @__PURE__ */ jsxs(Stack, {
|
|
1041
|
+
gap: 2,
|
|
1042
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
1043
|
+
muted: !0,
|
|
1044
|
+
size: 0,
|
|
1045
|
+
children: "Pick the swatch to build the theme around"
|
|
1046
|
+
}), /* @__PURE__ */ jsx(VariantRow, {
|
|
1047
|
+
ref: rowRef,
|
|
1048
|
+
style: {
|
|
1049
|
+
"--variant-width": `${variantWidth}px`,
|
|
1050
|
+
"--variant-scale": variantWidth / 144
|
|
1051
|
+
},
|
|
1052
|
+
children: variants.map((t12) => {
|
|
1053
|
+
let { variant: variant_0, options: variantOptions } = t12;
|
|
1054
|
+
return /* @__PURE__ */ jsxs(VariantButton, {
|
|
1055
|
+
"aria-pressed": variant_0 === current,
|
|
1056
|
+
onClick: () => onVariant(variant_0),
|
|
1057
|
+
type: "button",
|
|
1058
|
+
children: [/* @__PURE__ */ jsx(VariantFrame, { children: /* @__PURE__ */ jsx(VariantScale, { children: /* @__PURE__ */ jsx(ThemeThumbnail, { options: variantOptions }) }) }), /* @__PURE__ */ jsx(Box, {
|
|
1059
|
+
paddingTop: 1,
|
|
1060
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1061
|
+
align: "center",
|
|
1062
|
+
size: 0,
|
|
1063
|
+
textOverflow: "ellipsis",
|
|
1064
|
+
weight: variant_0 === current ? "medium" : "regular",
|
|
1065
|
+
children: IMAGE_PALETTE_TITLES[variant_0]
|
|
1066
|
+
})
|
|
1067
|
+
})]
|
|
1068
|
+
}, variant_0);
|
|
1069
|
+
})
|
|
1070
|
+
})]
|
|
1071
|
+
}),
|
|
1072
|
+
/* @__PURE__ */ jsx(TooltipButton, {
|
|
1073
|
+
icon: SparklesIcon,
|
|
1074
|
+
mode: "ghost",
|
|
1075
|
+
onClick: onLucky,
|
|
1076
|
+
text: "I'm feeling lucky",
|
|
1077
|
+
tooltip: "Build the theme around a swatch that looks interesting",
|
|
1078
|
+
width: "fill"
|
|
1079
|
+
})
|
|
1080
|
+
] }) : /* @__PURE__ */ jsxs(Stack, {
|
|
1081
|
+
gap: 3,
|
|
1082
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
1083
|
+
muted: !0,
|
|
1084
|
+
size: 0,
|
|
1085
|
+
children: "Take the accent, text and background colors from an image — read on device, nothing is uploaded."
|
|
1086
|
+
}), /* @__PURE__ */ jsx(ImageFileButton, {
|
|
1087
|
+
loading: busy,
|
|
1088
|
+
mode: "ghost",
|
|
1089
|
+
onFile: pickImage,
|
|
1090
|
+
text: "Pick an image",
|
|
1091
|
+
tooltip: "Take the colors from an image on your device",
|
|
1092
|
+
width: "fill"
|
|
1093
|
+
})]
|
|
1094
|
+
}), $[21] = busy, $[22] = current, $[23] = imageUrl, $[24] = onLucky, $[25] = onVariant, $[26] = palette, $[27] = pickImage, $[28] = variantWidth, $[29] = variants, $[30] = t11) : t11 = $[30];
|
|
1095
|
+
let t12;
|
|
1096
|
+
$[31] !== t10 || $[32] !== t11 ? (t12 = /* @__PURE__ */ jsxs(Stack, {
|
|
1097
|
+
gap: 3,
|
|
1098
|
+
children: [t10, t11]
|
|
1099
|
+
}), $[31] = t10, $[32] = t11, $[33] = t12) : t12 = $[33];
|
|
1100
|
+
let t13;
|
|
1101
|
+
return $[34] !== t12 || $[35] !== t6 || $[36] !== t7 ? (t13 = /* @__PURE__ */ jsx(Card, {
|
|
1102
|
+
border: !0,
|
|
1103
|
+
onDragEnter: t4,
|
|
1104
|
+
onDragLeave: t5,
|
|
1105
|
+
onDragOver: _temp$2,
|
|
1106
|
+
onDrop: t6,
|
|
1107
|
+
padding: 3,
|
|
1108
|
+
radius: 3,
|
|
1109
|
+
tone: t7,
|
|
1110
|
+
children: t12
|
|
1111
|
+
}), $[34] = t12, $[35] = t6, $[36] = t7, $[37] = t13) : t13 = $[37], t13;
|
|
1112
|
+
}
|
|
1113
|
+
function _temp$2(event_0) {
|
|
1114
|
+
event_0.preventDefault();
|
|
1115
|
+
}
|
|
1116
|
+
const SCHEME_TITLES = {
|
|
1117
|
+
dark: "Dark mode",
|
|
1118
|
+
light: "Light mode"
|
|
1119
|
+
}, SCHEME_OPTION_KEYS = [
|
|
1120
|
+
"accent",
|
|
1121
|
+
"text",
|
|
1122
|
+
"background",
|
|
1123
|
+
"contrast"
|
|
1124
|
+
], Swatch = styled.input.withConfig({
|
|
1125
|
+
displayName: "Swatch",
|
|
1126
|
+
componentId: "sc-vs5143-0"
|
|
1127
|
+
})`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({
|
|
1128
|
+
displayName: "Range",
|
|
1129
|
+
componentId: "sc-vs5143-1"
|
|
1130
|
+
})`display:block;width:100%;margin:0;accent-color:var(--card-focus-ring-color);`;
|
|
1131
|
+
/**
|
|
1132
|
+
* The flow for editing one of the user's own themes: its title, a live
|
|
1133
|
+
* preview, and a card per color scheme with the accent/text/background
|
|
1134
|
+
* pickers and the contrast slider of that scheme. Every change applies to the
|
|
1135
|
+
* whole Studio right away — in the scheme the Studio is showing.
|
|
1136
|
+
*
|
|
1137
|
+
* @internal
|
|
1138
|
+
*/
|
|
1139
|
+
function ThemeEditor(props) {
|
|
1140
|
+
let $ = c(40), { focusTitle, slug } = props, { themes, images, send } = useThemer(), t0;
|
|
1141
|
+
if ($[0] !== slug || $[1] !== themes) {
|
|
1142
|
+
let t1;
|
|
1143
|
+
$[3] === slug ? t1 = $[4] : (t1 = (candidate) => candidate.slug === slug && candidate.source === "custom", $[3] = slug, $[4] = t1), t0 = themes.find(t1), $[0] = slug, $[1] = themes, $[2] = t0;
|
|
1144
|
+
} else t0 = $[2];
|
|
1145
|
+
let theme = t0;
|
|
1146
|
+
if (!theme) return null;
|
|
1147
|
+
let t1;
|
|
1148
|
+
$[5] !== send || $[6] !== slug || $[7] !== theme.options || $[8] !== theme.palette ? (t1 = (variant) => {
|
|
1149
|
+
theme.palette && variant !== null && send({
|
|
1150
|
+
type: "theme.update",
|
|
1151
|
+
slug,
|
|
1152
|
+
options: applyImagePalette(theme.options, theme.palette, variant)
|
|
1153
|
+
});
|
|
1154
|
+
}, $[5] = send, $[6] = slug, $[7] = theme.options, $[8] = theme.palette, $[9] = t1) : t1 = $[9];
|
|
1155
|
+
let applyVariant = t1, t2 = images[slug], t3;
|
|
1156
|
+
$[10] === send ? t3 = $[11] : (t3 = () => send({ type: "flow.list" }), $[10] = send, $[11] = t3);
|
|
1157
|
+
let t4;
|
|
1158
|
+
$[12] !== applyVariant || $[13] !== theme.options || $[14] !== theme.palette ? (t4 = () => {
|
|
1159
|
+
theme.palette && applyVariant(pickLuckyVariant(theme.palette, { exclude: currentImageVariant(theme.options, theme.palette) }));
|
|
1160
|
+
}, $[12] = applyVariant, $[13] = theme.options, $[14] = theme.palette, $[15] = t4) : t4 = $[15];
|
|
1161
|
+
let t5;
|
|
1162
|
+
$[16] !== send || $[17] !== slug ? (t5 = (options) => send({
|
|
1163
|
+
type: "theme.update",
|
|
1164
|
+
slug,
|
|
1165
|
+
options
|
|
1166
|
+
}), $[16] = send, $[17] = slug, $[18] = t5) : t5 = $[18];
|
|
1167
|
+
let t6;
|
|
1168
|
+
$[19] !== send || $[20] !== slug || $[21] !== theme.options ? (t6 = (palette, file) => send({
|
|
1169
|
+
type: "theme.update",
|
|
1170
|
+
slug,
|
|
1171
|
+
options: applyImagePalette(theme.options, palette),
|
|
1172
|
+
palette,
|
|
1173
|
+
imageUrl: URL.createObjectURL(file)
|
|
1174
|
+
}), $[19] = send, $[20] = slug, $[21] = theme.options, $[22] = t6) : t6 = $[22];
|
|
1175
|
+
let t7, t8;
|
|
1176
|
+
$[23] !== send || $[24] !== slug ? (t7 = () => send({
|
|
1177
|
+
type: "theme.remove",
|
|
1178
|
+
slug
|
|
1179
|
+
}), t8 = (title) => send({
|
|
1180
|
+
type: "theme.update",
|
|
1181
|
+
slug,
|
|
1182
|
+
title
|
|
1183
|
+
}), $[23] = send, $[24] = slug, $[25] = t7, $[26] = t8) : (t7 = $[25], t8 = $[26]);
|
|
1184
|
+
let t9;
|
|
1185
|
+
return $[27] !== applyVariant || $[28] !== focusTitle || $[29] !== t2 || $[30] !== t3 || $[31] !== t4 || $[32] !== t5 || $[33] !== t6 || $[34] !== t7 || $[35] !== t8 || $[36] !== theme.options || $[37] !== theme.palette || $[38] !== theme.title ? (t9 = /* @__PURE__ */ jsx(ThemeEditorForm, {
|
|
1186
|
+
focusTitle,
|
|
1187
|
+
imageUrl: t2,
|
|
1188
|
+
onDone: t3,
|
|
1189
|
+
onLucky: t4,
|
|
1190
|
+
onOptionsChange: t5,
|
|
1191
|
+
onPalette: t6,
|
|
1192
|
+
onRemove: t7,
|
|
1193
|
+
onTitleChange: t8,
|
|
1194
|
+
onVariant: applyVariant,
|
|
1195
|
+
options: theme.options,
|
|
1196
|
+
palette: theme.palette,
|
|
1197
|
+
title: theme.title
|
|
1198
|
+
}), $[27] = applyVariant, $[28] = focusTitle, $[29] = t2, $[30] = t3, $[31] = t4, $[32] = t5, $[33] = t6, $[34] = t7, $[35] = t8, $[36] = theme.options, $[37] = theme.palette, $[38] = theme.title, $[39] = t9) : t9 = $[39], t9;
|
|
1199
|
+
}
|
|
1200
|
+
function ThemeEditorForm(props) {
|
|
1201
|
+
let $ = c(46), { focusTitle, imageUrl, onDone, onLucky, onOptionsChange, onPalette, onRemove, onTitleChange, onVariant, options, palette, title } = props, studioScheme = useColorSchemeValue(), t0;
|
|
1202
|
+
$[0] === options ? t0 = $[1] : (t0 = resolveThemeOptions(options), $[0] = options, $[1] = t0);
|
|
1203
|
+
let resolved = t0, t1;
|
|
1204
|
+
$[2] === options ? t1 = $[3] : (t1 = buildPalette(options), $[2] = options, $[3] = t1);
|
|
1205
|
+
let palettes = t1, t2;
|
|
1206
|
+
$[4] !== onOptionsChange || $[5] !== options ? (t2 = (scheme, changes) => {
|
|
1207
|
+
let values = {
|
|
1208
|
+
...options[scheme],
|
|
1209
|
+
...changes
|
|
1210
|
+
};
|
|
1211
|
+
for (let key of SCHEME_OPTION_KEYS) values[key] === void 0 && delete values[key];
|
|
1212
|
+
let next = { ...options };
|
|
1213
|
+
Object.keys(values).length === 0 ? delete next[scheme] : next[scheme] = values, onOptionsChange(next);
|
|
1214
|
+
}, $[4] = onOptionsChange, $[5] = options, $[6] = t2) : t2 = $[6];
|
|
1215
|
+
let patchScheme = t2, t3;
|
|
1216
|
+
$[7] === options ? t3 = $[8] : (t3 = /* @__PURE__ */ jsx(ThemeThumbnail, { options }), $[7] = options, $[8] = t3);
|
|
1217
|
+
let t4;
|
|
1218
|
+
$[9] === Symbol.for("react.memo_cache_sentinel") ? (t4 = /* @__PURE__ */ jsx(Text, {
|
|
1219
|
+
size: 1,
|
|
1220
|
+
weight: "medium",
|
|
1221
|
+
children: "Title"
|
|
1222
|
+
}), $[9] = t4) : t4 = $[9];
|
|
1223
|
+
let t5;
|
|
1224
|
+
$[10] === onTitleChange ? t5 = $[11] : (t5 = (event) => onTitleChange(event.currentTarget.value), $[10] = onTitleChange, $[11] = t5);
|
|
1225
|
+
let t6 = focusTitle ? _temp$1 : void 0, t7;
|
|
1226
|
+
$[12] !== focusTitle || $[13] !== t5 || $[14] !== t6 || $[15] !== title ? (t7 = /* @__PURE__ */ jsxs(Stack, {
|
|
1227
|
+
gap: 2,
|
|
1228
|
+
children: [t4, /* @__PURE__ */ jsx(TextInput, {
|
|
1229
|
+
autoFocus: focusTitle,
|
|
1230
|
+
fontSize: 1,
|
|
1231
|
+
onChange: t5,
|
|
1232
|
+
onFocus: t6,
|
|
1233
|
+
padding: 2,
|
|
1234
|
+
placeholder: "Untitled theme",
|
|
1235
|
+
value: title
|
|
1236
|
+
})]
|
|
1237
|
+
}), $[12] = focusTitle, $[13] = t5, $[14] = t6, $[15] = title, $[16] = t7) : t7 = $[16];
|
|
1238
|
+
let t8;
|
|
1239
|
+
$[17] !== imageUrl || $[18] !== onLucky || $[19] !== onPalette || $[20] !== onVariant || $[21] !== options || $[22] !== palette ? (t8 = /* @__PURE__ */ jsx(ImagePaletteSection, {
|
|
1240
|
+
imageUrl,
|
|
1241
|
+
onLucky,
|
|
1242
|
+
onPalette,
|
|
1243
|
+
onVariant,
|
|
1244
|
+
options,
|
|
1245
|
+
palette
|
|
1246
|
+
}), $[17] = imageUrl, $[18] = onLucky, $[19] = onPalette, $[20] = onVariant, $[21] = options, $[22] = palette, $[23] = t8) : t8 = $[23];
|
|
1247
|
+
let t9;
|
|
1248
|
+
$[24] !== options || $[25] !== palettes || $[26] !== patchScheme || $[27] !== resolved || $[28] !== studioScheme ? (t9 = SCHEMES.map((scheme_0) => /* @__PURE__ */ jsx(SchemeCard, {
|
|
1249
|
+
active: scheme_0 === studioScheme,
|
|
1250
|
+
onChange: (changes_0) => patchScheme(scheme_0, changes_0),
|
|
1251
|
+
options: options[scheme_0] ?? {},
|
|
1252
|
+
palette: palettes[scheme_0],
|
|
1253
|
+
resolved: resolved[scheme_0],
|
|
1254
|
+
scheme: scheme_0
|
|
1255
|
+
}, scheme_0)), $[24] = options, $[25] = palettes, $[26] = patchScheme, $[27] = resolved, $[28] = studioScheme, $[29] = t9) : t9 = $[29];
|
|
1256
|
+
let t10;
|
|
1257
|
+
$[30] !== t3 || $[31] !== t7 || $[32] !== t8 || $[33] !== t9 ? (t10 = /* @__PURE__ */ jsx(ScrollArea, {
|
|
1258
|
+
padding: 3,
|
|
1259
|
+
children: /* @__PURE__ */ jsxs(Stack, {
|
|
1260
|
+
gap: 4,
|
|
1261
|
+
children: [
|
|
1262
|
+
t3,
|
|
1263
|
+
t7,
|
|
1264
|
+
t8,
|
|
1265
|
+
t9
|
|
1266
|
+
]
|
|
1267
|
+
})
|
|
1268
|
+
}), $[30] = t3, $[31] = t7, $[32] = t8, $[33] = t9, $[34] = t10) : t10 = $[34];
|
|
1269
|
+
let t11;
|
|
1270
|
+
$[35] === onRemove ? t11 = $[36] : (t11 = /* @__PURE__ */ jsx(TooltipButton, {
|
|
1271
|
+
icon: TrashIcon,
|
|
1272
|
+
mode: "ghost",
|
|
1273
|
+
onClick: onRemove,
|
|
1274
|
+
text: "Remove",
|
|
1275
|
+
tone: "critical",
|
|
1276
|
+
tooltip: "Remove the theme — it can be restored until it is deleted"
|
|
1277
|
+
}), $[35] = onRemove, $[36] = t11);
|
|
1278
|
+
let t12;
|
|
1279
|
+
$[37] === Symbol.for("react.memo_cache_sentinel") ? (t12 = /* @__PURE__ */ jsx(Box, { flex: 1 }), $[37] = t12) : t12 = $[37];
|
|
1280
|
+
let t13;
|
|
1281
|
+
$[38] === onDone ? t13 = $[39] : (t13 = /* @__PURE__ */ jsx(Button, {
|
|
1282
|
+
mode: "ghost",
|
|
1283
|
+
onClick: onDone,
|
|
1284
|
+
text: "Done"
|
|
1285
|
+
}), $[38] = onDone, $[39] = t13);
|
|
1286
|
+
let t14;
|
|
1287
|
+
$[40] !== t11 || $[41] !== t13 ? (t14 = /* @__PURE__ */ jsx(Card, {
|
|
1288
|
+
borderTop: !0,
|
|
1289
|
+
padding: 3,
|
|
1290
|
+
children: /* @__PURE__ */ jsxs(Flex, {
|
|
1291
|
+
gap: 2,
|
|
1292
|
+
children: [
|
|
1293
|
+
t11,
|
|
1294
|
+
t12,
|
|
1295
|
+
t13
|
|
1296
|
+
]
|
|
1297
|
+
})
|
|
1298
|
+
}), $[40] = t11, $[41] = t13, $[42] = t14) : t14 = $[42];
|
|
1299
|
+
let t15;
|
|
1300
|
+
return $[43] !== t10 || $[44] !== t14 ? (t15 = /* @__PURE__ */ jsxs(Fragment, { children: [t10, t14] }), $[43] = t10, $[44] = t14, $[45] = t15) : t15 = $[45], t15;
|
|
1301
|
+
}
|
|
1302
|
+
function _temp$1(event_0) {
|
|
1303
|
+
return event_0.currentTarget.select();
|
|
1304
|
+
}
|
|
1305
|
+
/**
|
|
1306
|
+
* The colors of one scheme, on a card painted in that scheme so that each
|
|
1307
|
+
* card shows the colors it edits. The scheme the Studio is showing is marked
|
|
1308
|
+
* as active: its changes show up in the Studio right away, the other scheme's
|
|
1309
|
+
* only once the appearance setting switches to it.
|
|
1310
|
+
*/
|
|
1311
|
+
function SchemeCard(props) {
|
|
1312
|
+
let $ = c(69), { active, onChange, options, palette, resolved, scheme } = props, name = SCHEME_TITLES[scheme], t0;
|
|
1313
|
+
$[0] === name ? t0 = $[1] : (t0 = /* @__PURE__ */ jsx(Box, {
|
|
1314
|
+
flex: 1,
|
|
1315
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1316
|
+
size: 1,
|
|
1317
|
+
weight: "medium",
|
|
1318
|
+
children: name
|
|
1319
|
+
})
|
|
1320
|
+
}), $[0] = name, $[1] = t0);
|
|
1321
|
+
let t1;
|
|
1322
|
+
$[2] === active ? t1 = $[3] : (t1 = active && /* @__PURE__ */ jsx(Badge, {
|
|
1323
|
+
fontSize: 0,
|
|
1324
|
+
tone: "primary",
|
|
1325
|
+
children: "Active"
|
|
1326
|
+
}), $[2] = active, $[3] = t1);
|
|
1327
|
+
let t2;
|
|
1328
|
+
$[4] !== t0 || $[5] !== t1 ? (t2 = /* @__PURE__ */ jsxs(Flex, {
|
|
1329
|
+
align: "center",
|
|
1330
|
+
gap: 2,
|
|
1331
|
+
children: [t0, t1]
|
|
1332
|
+
}), $[4] = t0, $[5] = t1, $[6] = t2) : t2 = $[6];
|
|
1333
|
+
let t3 = active ? "Shown in the Studio right now" : `Shown once the Studio appearance is ${scheme}`, t4;
|
|
1334
|
+
$[7] === t3 ? t4 = $[8] : (t4 = /* @__PURE__ */ jsx(Text, {
|
|
1335
|
+
muted: !0,
|
|
1336
|
+
size: 0,
|
|
1337
|
+
children: t3
|
|
1338
|
+
}), $[7] = t3, $[8] = t4);
|
|
1339
|
+
let t5;
|
|
1340
|
+
$[9] !== t2 || $[10] !== t4 ? (t5 = /* @__PURE__ */ jsxs(Stack, {
|
|
1341
|
+
gap: 2,
|
|
1342
|
+
children: [t2, t4]
|
|
1343
|
+
}), $[9] = t2, $[10] = t4, $[11] = t5) : t5 = $[11];
|
|
1344
|
+
let t6 = options.accent === void 0, t7 = `${name} accent`, t8, t9;
|
|
1345
|
+
$[12] === onChange ? (t8 = $[13], t9 = $[14]) : (t8 = (accent) => onChange({ accent }), t9 = () => onChange({ accent: void 0 }), $[12] = onChange, $[13] = t8, $[14] = t9);
|
|
1346
|
+
let t10;
|
|
1347
|
+
$[15] !== resolved.accent || $[16] !== t6 || $[17] !== t7 || $[18] !== t8 || $[19] !== t9 ? (t10 = /* @__PURE__ */ jsx(ColorRow, {
|
|
1348
|
+
auto: t6,
|
|
1349
|
+
autoLabel: "default",
|
|
1350
|
+
label: t7,
|
|
1351
|
+
onChange: t8,
|
|
1352
|
+
onClear: t9,
|
|
1353
|
+
title: "Accent",
|
|
1354
|
+
value: resolved.accent
|
|
1355
|
+
}), $[15] = resolved.accent, $[16] = t6, $[17] = t7, $[18] = t8, $[19] = t9, $[20] = t10) : t10 = $[20];
|
|
1356
|
+
let t11 = options.text === void 0, t12 = `${name} text`, t13, t14;
|
|
1357
|
+
$[21] === onChange ? (t13 = $[22], t14 = $[23]) : (t13 = (text) => onChange({ text }), t14 = () => onChange({ text: void 0 }), $[21] = onChange, $[22] = t13, $[23] = t14);
|
|
1358
|
+
let t15;
|
|
1359
|
+
$[24] !== resolved.text || $[25] !== t11 || $[26] !== t12 || $[27] !== t13 || $[28] !== t14 ? (t15 = /* @__PURE__ */ jsx(ColorRow, {
|
|
1360
|
+
auto: t11,
|
|
1361
|
+
label: t12,
|
|
1362
|
+
onChange: t13,
|
|
1363
|
+
onClear: t14,
|
|
1364
|
+
title: "Text",
|
|
1365
|
+
value: resolved.text
|
|
1366
|
+
}), $[24] = resolved.text, $[25] = t11, $[26] = t12, $[27] = t13, $[28] = t14, $[29] = t15) : t15 = $[29];
|
|
1367
|
+
let t16 = scheme === "dark" ? palette.black : palette.white, t17 = options.background === void 0, t18 = `${name} background`, t19, t20;
|
|
1368
|
+
$[30] === onChange ? (t19 = $[31], t20 = $[32]) : (t19 = (background) => onChange({ background }), t20 = () => onChange({ background: void 0 }), $[30] = onChange, $[31] = t19, $[32] = t20);
|
|
1369
|
+
let t21;
|
|
1370
|
+
$[33] !== resolved.background || $[34] !== t16 || $[35] !== t17 || $[36] !== t18 || $[37] !== t19 || $[38] !== t20 ? (t21 = /* @__PURE__ */ jsx(ColorRow, {
|
|
1371
|
+
adjusted: t16,
|
|
1372
|
+
auto: t17,
|
|
1373
|
+
label: t18,
|
|
1374
|
+
onChange: t19,
|
|
1375
|
+
onClear: t20,
|
|
1376
|
+
title: "Background",
|
|
1377
|
+
value: resolved.background
|
|
1378
|
+
}), $[33] = resolved.background, $[34] = t16, $[35] = t17, $[36] = t18, $[37] = t19, $[38] = t20, $[39] = t21) : t21 = $[39];
|
|
1379
|
+
let t22;
|
|
1380
|
+
$[40] === Symbol.for("react.memo_cache_sentinel") ? (t22 = /* @__PURE__ */ jsx(Text, {
|
|
1381
|
+
size: 1,
|
|
1382
|
+
children: "Contrast"
|
|
1383
|
+
}), $[40] = t22) : t22 = $[40];
|
|
1384
|
+
let t23 = options.contrast === void 0 ? " · auto" : "", t24;
|
|
1385
|
+
$[41] !== resolved.contrast || $[42] !== t23 ? (t24 = /* @__PURE__ */ jsxs(Stack, {
|
|
1386
|
+
flex: 1,
|
|
1387
|
+
gap: 2,
|
|
1388
|
+
children: [t22, /* @__PURE__ */ jsxs(Text, {
|
|
1389
|
+
muted: !0,
|
|
1390
|
+
size: 0,
|
|
1391
|
+
children: [resolved.contrast, t23]
|
|
1392
|
+
})]
|
|
1393
|
+
}), $[41] = resolved.contrast, $[42] = t23, $[43] = t24) : t24 = $[43];
|
|
1394
|
+
let t25;
|
|
1395
|
+
$[44] !== onChange || $[45] !== options.contrast ? (t25 = options.contrast !== void 0 && /* @__PURE__ */ jsx(TooltipButton, {
|
|
1396
|
+
icon: ResetIcon,
|
|
1397
|
+
mode: "bleed",
|
|
1398
|
+
onClick: () => onChange({ contrast: void 0 }),
|
|
1399
|
+
padding: 2,
|
|
1400
|
+
tooltip: "Reset to auto"
|
|
1401
|
+
}), $[44] = onChange, $[45] = options.contrast, $[46] = t25) : t25 = $[46];
|
|
1402
|
+
let t26;
|
|
1403
|
+
$[47] !== t24 || $[48] !== t25 ? (t26 = /* @__PURE__ */ jsxs(Flex, {
|
|
1404
|
+
align: "center",
|
|
1405
|
+
gap: 2,
|
|
1406
|
+
children: [t24, t25]
|
|
1407
|
+
}), $[47] = t24, $[48] = t25, $[49] = t26) : t26 = $[49];
|
|
1408
|
+
let t27 = `${name} contrast`, t28;
|
|
1409
|
+
$[50] === onChange ? t28 = $[51] : (t28 = (event) => onChange({ contrast: Number(event.currentTarget.value) }), $[50] = onChange, $[51] = t28);
|
|
1410
|
+
let t29;
|
|
1411
|
+
$[52] !== resolved.contrast || $[53] !== t27 || $[54] !== t28 ? (t29 = /* @__PURE__ */ jsx(Range, {
|
|
1412
|
+
"aria-label": t27,
|
|
1413
|
+
max: 100,
|
|
1414
|
+
min: 15,
|
|
1415
|
+
onChange: t28,
|
|
1416
|
+
step: 1,
|
|
1417
|
+
type: "range",
|
|
1418
|
+
value: resolved.contrast
|
|
1419
|
+
}), $[52] = resolved.contrast, $[53] = t27, $[54] = t28, $[55] = t29) : t29 = $[55];
|
|
1420
|
+
let t30;
|
|
1421
|
+
$[56] === Symbol.for("react.memo_cache_sentinel") ? (t30 = /* @__PURE__ */ jsx(Text, {
|
|
1422
|
+
muted: !0,
|
|
1423
|
+
size: 0,
|
|
1424
|
+
children: "100 keeps text and borders neutral — lower values blend in the accent"
|
|
1425
|
+
}), $[56] = t30) : t30 = $[56];
|
|
1426
|
+
let t31;
|
|
1427
|
+
$[57] !== t26 || $[58] !== t29 ? (t31 = /* @__PURE__ */ jsxs(Stack, {
|
|
1428
|
+
gap: 3,
|
|
1429
|
+
children: [
|
|
1430
|
+
t26,
|
|
1431
|
+
t29,
|
|
1432
|
+
t30
|
|
1433
|
+
]
|
|
1434
|
+
}), $[57] = t26, $[58] = t29, $[59] = t31) : t31 = $[59];
|
|
1435
|
+
let t32;
|
|
1436
|
+
$[60] !== t10 || $[61] !== t15 || $[62] !== t21 || $[63] !== t31 || $[64] !== t5 ? (t32 = /* @__PURE__ */ jsxs(Stack, {
|
|
1437
|
+
gap: 4,
|
|
1438
|
+
children: [
|
|
1439
|
+
t5,
|
|
1440
|
+
t10,
|
|
1441
|
+
t15,
|
|
1442
|
+
t21,
|
|
1443
|
+
t31
|
|
1444
|
+
]
|
|
1445
|
+
}), $[60] = t10, $[61] = t15, $[62] = t21, $[63] = t31, $[64] = t5, $[65] = t32) : t32 = $[65];
|
|
1446
|
+
let t33;
|
|
1447
|
+
return $[66] !== scheme || $[67] !== t32 ? (t33 = /* @__PURE__ */ jsx(Card, {
|
|
1448
|
+
border: !0,
|
|
1449
|
+
padding: 3,
|
|
1450
|
+
radius: 3,
|
|
1451
|
+
scheme,
|
|
1452
|
+
tone: "default",
|
|
1453
|
+
children: t32
|
|
1454
|
+
}), $[66] = scheme, $[67] = t32, $[68] = t33) : t33 = $[68], t33;
|
|
1455
|
+
}
|
|
1456
|
+
/** One color of a scheme: a swatch with its value and an optional reset button */
|
|
1457
|
+
function ColorRow(props) {
|
|
1458
|
+
let $ = c(24), { adjusted, auto, autoLabel: t0, label, onChange, onClear, title, value } = props, autoLabel = t0 === void 0 ? "auto" : t0, t1;
|
|
1459
|
+
$[0] === onChange ? t1 = $[1] : (t1 = (event) => onChange(event.currentTarget.value), $[0] = onChange, $[1] = t1);
|
|
1460
|
+
let t2;
|
|
1461
|
+
$[2] !== label || $[3] !== t1 || $[4] !== value ? (t2 = /* @__PURE__ */ jsx(Swatch, {
|
|
1462
|
+
"aria-label": label,
|
|
1463
|
+
onChange: t1,
|
|
1464
|
+
type: "color",
|
|
1465
|
+
value
|
|
1466
|
+
}), $[2] = label, $[3] = t1, $[4] = value, $[5] = t2) : t2 = $[5];
|
|
1467
|
+
let t3;
|
|
1468
|
+
$[6] === Symbol.for("react.memo_cache_sentinel") ? (t3 = { minWidth: 0 }, $[6] = t3) : t3 = $[6];
|
|
1469
|
+
let t4;
|
|
1470
|
+
$[7] === title ? t4 = $[8] : (t4 = /* @__PURE__ */ jsx(Text, {
|
|
1471
|
+
size: 1,
|
|
1472
|
+
children: title
|
|
1473
|
+
}), $[7] = title, $[8] = t4);
|
|
1474
|
+
let t5 = adjusted !== void 0 && adjusted !== value ? ` → ${adjusted}` : "", t6 = auto ? ` · ${autoLabel}` : "", t7;
|
|
1475
|
+
$[9] !== t5 || $[10] !== t6 || $[11] !== value ? (t7 = /* @__PURE__ */ jsxs(Text, {
|
|
1476
|
+
muted: !0,
|
|
1477
|
+
size: 0,
|
|
1478
|
+
textOverflow: "ellipsis",
|
|
1479
|
+
children: [
|
|
1480
|
+
value,
|
|
1481
|
+
t5,
|
|
1482
|
+
t6
|
|
1483
|
+
]
|
|
1484
|
+
}), $[9] = t5, $[10] = t6, $[11] = value, $[12] = t7) : t7 = $[12];
|
|
1485
|
+
let t8;
|
|
1486
|
+
$[13] !== t4 || $[14] !== t7 ? (t8 = /* @__PURE__ */ jsxs(Stack, {
|
|
1487
|
+
flex: 1,
|
|
1488
|
+
gap: 2,
|
|
1489
|
+
style: t3,
|
|
1490
|
+
children: [t4, t7]
|
|
1491
|
+
}), $[13] = t4, $[14] = t7, $[15] = t8) : t8 = $[15];
|
|
1492
|
+
let t9;
|
|
1493
|
+
$[16] !== auto || $[17] !== autoLabel || $[18] !== onClear ? (t9 = onClear && !auto && /* @__PURE__ */ jsx(TooltipButton, {
|
|
1494
|
+
icon: ResetIcon,
|
|
1495
|
+
mode: "bleed",
|
|
1496
|
+
onClick: onClear,
|
|
1497
|
+
padding: 2,
|
|
1498
|
+
tooltip: `Reset to ${autoLabel}`
|
|
1499
|
+
}), $[16] = auto, $[17] = autoLabel, $[18] = onClear, $[19] = t9) : t9 = $[19];
|
|
1500
|
+
let t10;
|
|
1501
|
+
return $[20] !== t2 || $[21] !== t8 || $[22] !== t9 ? (t10 = /* @__PURE__ */ jsxs(Flex, {
|
|
1502
|
+
align: "center",
|
|
1503
|
+
gap: 2,
|
|
1504
|
+
children: [
|
|
1505
|
+
t2,
|
|
1506
|
+
t8,
|
|
1507
|
+
t9
|
|
1508
|
+
]
|
|
1509
|
+
}), $[20] = t2, $[21] = t8, $[22] = t9, $[23] = t10) : t10 = $[23], t10;
|
|
1510
|
+
}
|
|
1511
|
+
const Root = styled.div.withConfig({
|
|
1512
|
+
displayName: "Root",
|
|
1513
|
+
componentId: "sc-a1a6d4-0"
|
|
1514
|
+
})`position:relative;`, PickButton = styled.button.withConfig({
|
|
1515
|
+
displayName: "PickButton",
|
|
1516
|
+
componentId: "sc-a1a6d4-1"
|
|
1517
|
+
})`appearance:none;display:block;box-sizing:border-box;width:100%;margin:0;padding:0;border:0;background:none;color:inherit;font:inherit;text-align:center;cursor:pointer;&:focus{outline:none;}`, Frame = styled.span.withConfig({
|
|
1518
|
+
displayName: "Frame",
|
|
1519
|
+
componentId: "sc-a1a6d4-2"
|
|
1520
|
+
})`display:block;padding:4px;border-radius:10px;transition:box-shadow 100ms;${PickButton}:hover &{box-shadow:inset 0 0 0 2px var(--card-border-color);}${PickButton}[aria-pressed='true'] &{box-shadow:inset 0 0 0 2px var(--card-focus-ring-color);}${PickButton}:focus-visible &{outline:2px solid var(--card-focus-ring-color);outline-offset:-2px;}`, MenuSlot = styled.div.withConfig({
|
|
1521
|
+
displayName: "MenuSlot",
|
|
1522
|
+
componentId: "sc-a1a6d4-3"
|
|
1523
|
+
})`position:absolute;top:10px;right:10px;opacity:0;transition:opacity 100ms;${Root}:hover &,${Root}:focus-within &,&[data-visible='true']{opacity:1;}`;
|
|
1524
|
+
/**
|
|
1525
|
+
* One theme in the list: the floating preview with the title below it, like
|
|
1526
|
+
* an appearance option in macOS System Settings. Clicking it applies the
|
|
1527
|
+
* theme to the whole Studio; its menu holds the edit, duplicate and remove
|
|
1528
|
+
* flows that apply to the theme.
|
|
1529
|
+
*
|
|
1530
|
+
* @internal
|
|
1531
|
+
*/
|
|
1532
|
+
function ThemeCard(props) {
|
|
1533
|
+
let $ = c(50), { active, theme } = props, { send } = useThemer(), [menuOpen, setMenuOpen] = useState(!1), t0;
|
|
1534
|
+
$[0] === theme.title ? t0 = $[1] : (t0 = displayTitle(theme.title), $[0] = theme.title, $[1] = t0);
|
|
1535
|
+
let title = t0, t1;
|
|
1536
|
+
$[2] !== send || $[3] !== theme.slug ? (t1 = () => send({
|
|
1537
|
+
type: "theme.pick",
|
|
1538
|
+
slug: theme.slug
|
|
1539
|
+
}), $[2] = send, $[3] = theme.slug, $[4] = t1) : t1 = $[4];
|
|
1540
|
+
let t2;
|
|
1541
|
+
$[5] === theme.options ? t2 = $[6] : (t2 = /* @__PURE__ */ jsx(Frame, { children: /* @__PURE__ */ jsx(ThemeThumbnail, { options: theme.options }) }), $[5] = theme.options, $[6] = t2);
|
|
1542
|
+
let t3 = active ? "medium" : "regular", t4;
|
|
1543
|
+
$[7] !== t3 || $[8] !== title ? (t4 = /* @__PURE__ */ jsx(Box, {
|
|
1544
|
+
as: "span",
|
|
1545
|
+
display: "block",
|
|
1546
|
+
paddingTop: 2,
|
|
1547
|
+
paddingX: 2,
|
|
1548
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1549
|
+
align: "center",
|
|
1550
|
+
as: "span",
|
|
1551
|
+
size: 1,
|
|
1552
|
+
textOverflow: "ellipsis",
|
|
1553
|
+
weight: t3,
|
|
1554
|
+
children: title
|
|
1555
|
+
})
|
|
1556
|
+
}), $[7] = t3, $[8] = title, $[9] = t4) : t4 = $[9];
|
|
1557
|
+
let t5;
|
|
1558
|
+
$[10] !== active || $[11] !== t1 || $[12] !== t2 || $[13] !== t4 ? (t5 = /* @__PURE__ */ jsxs(PickButton, {
|
|
1559
|
+
"aria-pressed": active,
|
|
1560
|
+
onClick: t1,
|
|
1561
|
+
type: "button",
|
|
1562
|
+
children: [t2, t4]
|
|
1563
|
+
}), $[10] = active, $[11] = t1, $[12] = t2, $[13] = t4, $[14] = t5) : t5 = $[14];
|
|
1564
|
+
let t6;
|
|
1565
|
+
$[15] === Symbol.for("react.memo_cache_sentinel") ? (t6 = /* @__PURE__ */ jsx(Text, {
|
|
1566
|
+
size: 1,
|
|
1567
|
+
children: "Show more"
|
|
1568
|
+
}), $[15] = t6) : t6 = $[15];
|
|
1569
|
+
let t7;
|
|
1570
|
+
$[16] === Symbol.for("react.memo_cache_sentinel") ? (t7 = { display: "inline-block" }, $[16] = t7) : t7 = $[16];
|
|
1571
|
+
let t8 = `Show more for ${title}`, t9;
|
|
1572
|
+
$[17] === t8 ? t9 = $[18] : (t9 = /* @__PURE__ */ jsx(Button, {
|
|
1573
|
+
"aria-label": t8,
|
|
1574
|
+
fontSize: 1,
|
|
1575
|
+
icon: EllipsisHorizontalIcon,
|
|
1576
|
+
mode: "ghost",
|
|
1577
|
+
padding: 1,
|
|
1578
|
+
radius: 2
|
|
1579
|
+
}), $[17] = t8, $[18] = t9);
|
|
1580
|
+
let t10 = `themer-theme-${theme.slug}`, t11;
|
|
1581
|
+
$[19] !== send || $[20] !== theme.slug || $[21] !== theme.source ? (t11 = theme.source === "custom" && /* @__PURE__ */ jsx(MenuItem, {
|
|
1582
|
+
icon: EditIcon,
|
|
1583
|
+
onClick: () => send({
|
|
1584
|
+
type: "theme.edit",
|
|
1585
|
+
slug: theme.slug
|
|
1586
|
+
}),
|
|
1587
|
+
text: "Edit"
|
|
1588
|
+
}), $[19] = send, $[20] = theme.slug, $[21] = theme.source, $[22] = t11) : t11 = $[22];
|
|
1589
|
+
let t12;
|
|
1590
|
+
$[23] !== send || $[24] !== theme.slug ? (t12 = () => send({
|
|
1591
|
+
type: "theme.duplicate",
|
|
1592
|
+
slug: theme.slug
|
|
1593
|
+
}), $[23] = send, $[24] = theme.slug, $[25] = t12) : t12 = $[25];
|
|
1594
|
+
let t13 = theme.source === "custom" ? "Duplicate" : "Duplicate to edit", t14;
|
|
1595
|
+
$[26] !== t12 || $[27] !== t13 ? (t14 = /* @__PURE__ */ jsx(MenuItem, {
|
|
1596
|
+
icon: CopyIcon,
|
|
1597
|
+
onClick: t12,
|
|
1598
|
+
text: t13
|
|
1599
|
+
}), $[26] = t12, $[27] = t13, $[28] = t14) : t14 = $[28];
|
|
1600
|
+
let t15;
|
|
1601
|
+
$[29] !== send || $[30] !== theme.slug || $[31] !== theme.source ? (t15 = theme.source !== "config" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MenuDivider, {}), /* @__PURE__ */ jsx(MenuItem, {
|
|
1602
|
+
icon: TrashIcon,
|
|
1603
|
+
onClick: () => send({
|
|
1604
|
+
type: "theme.remove",
|
|
1605
|
+
slug: theme.slug
|
|
1606
|
+
}),
|
|
1607
|
+
text: "Remove",
|
|
1608
|
+
tone: "critical"
|
|
1609
|
+
})] }), $[29] = send, $[30] = theme.slug, $[31] = theme.source, $[32] = t15) : t15 = $[32];
|
|
1610
|
+
let t16;
|
|
1611
|
+
$[33] !== t11 || $[34] !== t14 || $[35] !== t15 ? (t16 = /* @__PURE__ */ jsxs(Menu, { children: [
|
|
1612
|
+
t11,
|
|
1613
|
+
t14,
|
|
1614
|
+
t15
|
|
1615
|
+
] }), $[33] = t11, $[34] = t14, $[35] = t15, $[36] = t16) : t16 = $[36];
|
|
1616
|
+
let t17, t18, t19;
|
|
1617
|
+
$[37] === Symbol.for("react.memo_cache_sentinel") ? (t17 = () => setMenuOpen(!1), t18 = () => setMenuOpen(!0), t19 = {
|
|
1618
|
+
animate: !0,
|
|
1619
|
+
placement: "bottom-end",
|
|
1620
|
+
portal: !0
|
|
1621
|
+
}, $[37] = t17, $[38] = t18, $[39] = t19) : (t17 = $[37], t18 = $[38], t19 = $[39]);
|
|
1622
|
+
let t20;
|
|
1623
|
+
$[40] !== t10 || $[41] !== t16 || $[42] !== t9 ? (t20 = /* @__PURE__ */ jsx(Tooltip, {
|
|
1624
|
+
animate: !0,
|
|
1625
|
+
content: t6,
|
|
1626
|
+
placement: "bottom",
|
|
1627
|
+
portal: !0,
|
|
1628
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
1629
|
+
style: t7,
|
|
1630
|
+
children: /* @__PURE__ */ jsx(MenuButton, {
|
|
1631
|
+
button: t9,
|
|
1632
|
+
id: t10,
|
|
1633
|
+
menu: t16,
|
|
1634
|
+
onClose: t17,
|
|
1635
|
+
onOpen: t18,
|
|
1636
|
+
popover: t19
|
|
1637
|
+
})
|
|
1638
|
+
})
|
|
1639
|
+
}), $[40] = t10, $[41] = t16, $[42] = t9, $[43] = t20) : t20 = $[43];
|
|
1640
|
+
let t21;
|
|
1641
|
+
$[44] !== menuOpen || $[45] !== t20 ? (t21 = /* @__PURE__ */ jsx(MenuSlot, {
|
|
1642
|
+
"data-visible": menuOpen,
|
|
1643
|
+
children: t20
|
|
1644
|
+
}), $[44] = menuOpen, $[45] = t20, $[46] = t21) : t21 = $[46];
|
|
1645
|
+
let t22;
|
|
1646
|
+
return $[47] !== t21 || $[48] !== t5 ? (t22 = /* @__PURE__ */ jsxs(Root, { children: [t5, t21] }), $[47] = t21, $[48] = t5, $[49] = t22) : t22 = $[49], t22;
|
|
1647
|
+
}
|
|
1648
|
+
/**
|
|
1649
|
+
* The flow for picking a theme: one column of theme cards — the configured
|
|
1650
|
+
* theme, the presets and the user's own themes — with the entry points to
|
|
1651
|
+
* the add and restore flows below.
|
|
1652
|
+
*
|
|
1653
|
+
* @internal
|
|
1654
|
+
*/
|
|
1655
|
+
function ThemeList() {
|
|
1656
|
+
let $ = c(24), { themes, removed, active, send } = useThemer(), t0;
|
|
1657
|
+
$[0] === send ? t0 = $[1] : (t0 = (palette, file) => send({
|
|
1658
|
+
type: "theme.add",
|
|
1659
|
+
title: titleFromFileName(file.name),
|
|
1660
|
+
options: optionsFromImagePalette(palette),
|
|
1661
|
+
palette,
|
|
1662
|
+
imageUrl: URL.createObjectURL(file)
|
|
1663
|
+
}), $[0] = send, $[1] = t0);
|
|
1664
|
+
let { busy, pickImage } = useImagePalette(t0), t1;
|
|
1665
|
+
if ($[2] !== active || $[3] !== themes) {
|
|
1666
|
+
let t2;
|
|
1667
|
+
$[5] === active ? t2 = $[6] : (t2 = (theme) => /* @__PURE__ */ jsx(ThemeCard, {
|
|
1668
|
+
active: theme.slug === active.slug,
|
|
1669
|
+
theme
|
|
1670
|
+
}, theme.slug), $[5] = active, $[6] = t2), t1 = themes.map(t2), $[2] = active, $[3] = themes, $[4] = t1;
|
|
1671
|
+
} else t1 = $[4];
|
|
1672
|
+
let t2;
|
|
1673
|
+
$[7] !== removed.length || $[8] !== send ? (t2 = removed.length > 0 && /* @__PURE__ */ jsx(Button, {
|
|
1674
|
+
icon: RestoreIcon,
|
|
1675
|
+
mode: "bleed",
|
|
1676
|
+
onClick: () => send({ type: "flow.removed" }),
|
|
1677
|
+
text: `Show removed (${removed.length})`,
|
|
1678
|
+
width: "fill"
|
|
1679
|
+
}), $[7] = removed.length, $[8] = send, $[9] = t2) : t2 = $[9];
|
|
1680
|
+
let t3;
|
|
1681
|
+
$[10] !== t1 || $[11] !== t2 ? (t3 = /* @__PURE__ */ jsx(ScrollArea, {
|
|
1682
|
+
padding: 3,
|
|
1683
|
+
children: /* @__PURE__ */ jsxs(Stack, {
|
|
1684
|
+
gap: 4,
|
|
1685
|
+
children: [t1, t2]
|
|
1686
|
+
})
|
|
1687
|
+
}), $[10] = t1, $[11] = t2, $[12] = t3) : t3 = $[12];
|
|
1688
|
+
let t4;
|
|
1689
|
+
$[13] === send ? t4 = $[14] : (t4 = /* @__PURE__ */ jsx(Box, {
|
|
1690
|
+
flex: 1,
|
|
1691
|
+
children: /* @__PURE__ */ jsx(TooltipButton, {
|
|
1692
|
+
icon: AddIcon,
|
|
1693
|
+
mode: "ghost",
|
|
1694
|
+
onClick: () => send({ type: "theme.add" }),
|
|
1695
|
+
text: "Add theme",
|
|
1696
|
+
tooltip: "Add a theme based on the applied one",
|
|
1697
|
+
width: "fill"
|
|
1698
|
+
})
|
|
1699
|
+
}), $[13] = send, $[14] = t4);
|
|
1700
|
+
let t5;
|
|
1701
|
+
$[15] !== busy || $[16] !== pickImage ? (t5 = /* @__PURE__ */ jsx(ImageFileButton, {
|
|
1702
|
+
loading: busy,
|
|
1703
|
+
mode: "ghost",
|
|
1704
|
+
onFile: pickImage,
|
|
1705
|
+
tooltip: "Add a theme from the colors of an image"
|
|
1706
|
+
}), $[15] = busy, $[16] = pickImage, $[17] = t5) : t5 = $[17];
|
|
1707
|
+
let t6;
|
|
1708
|
+
$[18] !== t4 || $[19] !== t5 ? (t6 = /* @__PURE__ */ jsx(Card, {
|
|
1709
|
+
borderTop: !0,
|
|
1710
|
+
padding: 3,
|
|
1711
|
+
children: /* @__PURE__ */ jsxs(Flex, {
|
|
1712
|
+
gap: 2,
|
|
1713
|
+
children: [t4, t5]
|
|
1714
|
+
})
|
|
1715
|
+
}), $[18] = t4, $[19] = t5, $[20] = t6) : t6 = $[20];
|
|
1716
|
+
let t7;
|
|
1717
|
+
return $[21] !== t3 || $[22] !== t6 ? (t7 = /* @__PURE__ */ jsxs(Fragment, { children: [t3, t6] }), $[21] = t3, $[22] = t6, $[23] = t7) : t7 = $[23], t7;
|
|
52
1718
|
}
|
|
53
1719
|
/**
|
|
54
1720
|
* Serializes theme options into the `buildTheme` call to paste into
|
|
55
|
-
* `sanity.config.ts`, keeping only what differs from the derived defaults
|
|
1721
|
+
* `sanity.config.ts`, keeping only what differs from the derived defaults in
|
|
1722
|
+
* each scheme.
|
|
56
1723
|
*
|
|
57
1724
|
* Options that boil down to the stock Studio theme need nothing from this
|
|
58
1725
|
* package — so they serialize to a bare `buildTheme()` from `@sanity/ui/theme`
|
|
@@ -63,77 +1730,35 @@ function sameOptions(a, b) {
|
|
|
63
1730
|
function createThemeSnippet(options) {
|
|
64
1731
|
let minimized = minimizeOptions(options);
|
|
65
1732
|
if (minimized === null) return "import {buildTheme} from '@sanity/ui/theme'\n\nexport const theme = buildTheme()\n";
|
|
66
|
-
let
|
|
67
|
-
|
|
68
|
-
let
|
|
69
|
-
|
|
1733
|
+
let lines = [];
|
|
1734
|
+
for (let scheme of SCHEMES) {
|
|
1735
|
+
let values = minimized[scheme];
|
|
1736
|
+
values && lines.push(serializeScheme(scheme, values));
|
|
70
1737
|
}
|
|
71
|
-
return
|
|
1738
|
+
return `import {buildTheme} from '@sanity/themer'\n\nexport const theme = buildTheme({\n${lines.join("\n")}\n})\n`;
|
|
1739
|
+
}
|
|
1740
|
+
/** A scheme with up to two options fits on one line, more spread out */
|
|
1741
|
+
function serializeScheme(scheme, values) {
|
|
1742
|
+
let entries = [];
|
|
1743
|
+
return values.accent !== void 0 && entries.push(`accent: '${values.accent}'`), values.text !== void 0 && entries.push(`text: '${values.text}'`), values.background !== void 0 && entries.push(`background: '${values.background}'`), values.contrast !== void 0 && entries.push(`contrast: ${values.contrast}`), entries.length <= 2 ? ` ${scheme}: {${entries.join(", ")}},` : ` ${scheme}: {\n${entries.map((entry) => ` ${entry},`).join("\n")}\n },`;
|
|
72
1744
|
}
|
|
73
1745
|
registerLanguage(typescript);
|
|
74
1746
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*/
|
|
79
|
-
const Swatch = styled.input.withConfig({
|
|
80
|
-
displayName: "Swatch",
|
|
81
|
-
componentId: "sc-7kf8xr-0"
|
|
82
|
-
})`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({
|
|
83
|
-
displayName: "Range",
|
|
84
|
-
componentId: "sc-7kf8xr-1"
|
|
85
|
-
})`display:block;width:100%;margin:0;accent-color:var(--card-focus-ring-color);`;
|
|
86
|
-
/**
|
|
87
|
-
* The themer sidebar: presets, the accent/text/background pickers and the
|
|
88
|
-
* contrast slider that generate the previewed `buildTheme` theme, plus the
|
|
89
|
-
* `buildTheme` snippet to make it permanent.
|
|
1747
|
+
* Shows the `buildTheme` snippet that makes a theme permanent, in a dialog so
|
|
1748
|
+
* that it does not take up sidebar space or make the sidebar scroll as the
|
|
1749
|
+
* snippet changes shape with the theme.
|
|
90
1750
|
*
|
|
91
1751
|
* @internal
|
|
92
1752
|
*/
|
|
93
|
-
function
|
|
94
|
-
let $ = c(
|
|
95
|
-
$[0] ===
|
|
96
|
-
let
|
|
97
|
-
$[2]
|
|
98
|
-
let palette = t1, t2;
|
|
99
|
-
$[4] === active ? t2 = $[5] : (t2 = presets.find((preset) => sameOptions(preset.options, active))?.slug ?? "", $[4] = active, $[5] = t2);
|
|
100
|
-
let activePresetSlug = t2, t3;
|
|
101
|
-
$[6] === active ? t3 = $[7] : (t3 = createThemeSnippet(active), $[6] = active, $[7] = t3);
|
|
102
|
-
let snippet = t3, t4;
|
|
103
|
-
$[8] !== active || $[9] !== setOptions ? (t4 = (changes) => {
|
|
104
|
-
setOptions({
|
|
105
|
-
...active,
|
|
106
|
-
...changes
|
|
107
|
-
});
|
|
108
|
-
}, $[8] = active, $[9] = setOptions, $[10] = t4) : t4 = $[10];
|
|
109
|
-
let patch = t4, t5;
|
|
110
|
-
$[11] !== active || $[12] !== setOptions ? (t5 = (changes_0) => {
|
|
111
|
-
setOptions({
|
|
112
|
-
...active,
|
|
113
|
-
background: {
|
|
114
|
-
...active.background,
|
|
115
|
-
...changes_0
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
}, $[11] = active, $[12] = setOptions, $[13] = t5) : t5 = $[13];
|
|
119
|
-
let patchBackground = t5, t6;
|
|
120
|
-
$[14] !== active || $[15] !== setOptions ? (t6 = (field) => {
|
|
121
|
-
let next = { ...active };
|
|
122
|
-
delete next[field], setOptions(next);
|
|
123
|
-
}, $[14] = active, $[15] = setOptions, $[16] = t6) : t6 = $[16];
|
|
124
|
-
let clearField = t6, t7;
|
|
125
|
-
$[17] !== active || $[18] !== setOptions ? (t7 = (key) => {
|
|
126
|
-
let background = { ...active.background };
|
|
127
|
-
delete background[key];
|
|
128
|
-
let next_0 = { ...active };
|
|
129
|
-
background.dark === void 0 && background.light === void 0 ? delete next_0.background : next_0.background = background, setOptions(next_0);
|
|
130
|
-
}, $[17] = active, $[18] = setOptions, $[19] = t7) : t7 = $[19];
|
|
131
|
-
let clearBackground = t7, t8;
|
|
132
|
-
$[20] !== snippet || $[21] !== toast ? (t8 = async () => {
|
|
1753
|
+
function ThemeSnippetDialog(props) {
|
|
1754
|
+
let $ = c(23), { onClose, theme } = props, toast = useToast(), t0;
|
|
1755
|
+
$[0] === theme.options ? t0 = $[1] : (t0 = createThemeSnippet(theme.options), $[0] = theme.options, $[1] = t0);
|
|
1756
|
+
let snippet = t0, t1;
|
|
1757
|
+
$[2] !== snippet || $[3] !== toast ? (t1 = async () => {
|
|
133
1758
|
try {
|
|
134
1759
|
await navigator.clipboard.writeText(snippet), toast.push({
|
|
135
1760
|
status: "success",
|
|
136
|
-
title: "Copied theme to the clipboard"
|
|
1761
|
+
title: "Copied the theme to the clipboard"
|
|
137
1762
|
});
|
|
138
1763
|
} catch {
|
|
139
1764
|
toast.push({
|
|
@@ -141,515 +1766,705 @@ function ThemerSidebar() {
|
|
|
141
1766
|
title: "Could not copy the theme"
|
|
142
1767
|
});
|
|
143
1768
|
}
|
|
144
|
-
}, $[
|
|
145
|
-
let handleCopy =
|
|
146
|
-
$[
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
150
|
-
size: 1,
|
|
151
|
-
weight: "semibold",
|
|
152
|
-
children: "Themer"
|
|
153
|
-
})
|
|
154
|
-
}), $[23] = t9) : t9 = $[23];
|
|
155
|
-
let t10;
|
|
156
|
-
$[24] === setOpen ? t10 = $[25] : (t10 = /* @__PURE__ */ jsx(Card, {
|
|
157
|
-
borderBottom: !0,
|
|
1769
|
+
}, $[2] = snippet, $[3] = toast, $[4] = t1) : t1 = $[4];
|
|
1770
|
+
let handleCopy = t1, t2;
|
|
1771
|
+
$[5] === handleCopy ? t2 = $[6] : (t2 = /* @__PURE__ */ jsx(Flex, {
|
|
1772
|
+
gap: 2,
|
|
1773
|
+
justify: "flex-end",
|
|
158
1774
|
padding: 3,
|
|
159
|
-
children: /* @__PURE__ */
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
mode: "bleed",
|
|
165
|
-
onClick: () => setOpen(!1),
|
|
166
|
-
padding: 2,
|
|
167
|
-
title: "Close themer"
|
|
168
|
-
})]
|
|
1775
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
1776
|
+
icon: ClipboardIcon,
|
|
1777
|
+
mode: "ghost",
|
|
1778
|
+
onClick: () => void handleCopy(),
|
|
1779
|
+
text: "Copy"
|
|
169
1780
|
})
|
|
170
|
-
}), $[
|
|
171
|
-
let
|
|
172
|
-
$[
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}), $[
|
|
177
|
-
let
|
|
178
|
-
$[
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
preset: preset_0
|
|
182
|
-
}, preset_0.slug)), $[27] = activePresetSlug, $[28] = setOptions, $[29] = t12) : t12 = $[29];
|
|
183
|
-
let t13;
|
|
184
|
-
$[30] === t12 ? t13 = $[31] : (t13 = /* @__PURE__ */ jsxs(Stack, {
|
|
185
|
-
gap: 3,
|
|
186
|
-
children: [t11, /* @__PURE__ */ jsx(Grid, {
|
|
187
|
-
gap: 2,
|
|
188
|
-
gridTemplateColumns: 2,
|
|
189
|
-
children: t12
|
|
190
|
-
})]
|
|
191
|
-
}), $[30] = t12, $[31] = t13);
|
|
192
|
-
let t14;
|
|
193
|
-
$[32] === Symbol.for("react.memo_cache_sentinel") ? (t14 = /* @__PURE__ */ jsx(Text, {
|
|
194
|
-
size: 1,
|
|
195
|
-
weight: "medium",
|
|
196
|
-
children: "Colors"
|
|
197
|
-
}), $[32] = t14) : t14 = $[32];
|
|
198
|
-
let t15;
|
|
199
|
-
$[33] === patch ? t15 = $[34] : (t15 = (accent) => patch({ accent }), $[33] = patch, $[34] = t15);
|
|
200
|
-
let t16;
|
|
201
|
-
$[35] !== palette.blue || $[36] !== resolved.accent || $[37] !== t15 ? (t16 = /* @__PURE__ */ jsx(ColorRow, {
|
|
202
|
-
onChange: t15,
|
|
203
|
-
tints: palette.blue,
|
|
204
|
-
title: "Accent",
|
|
205
|
-
value: resolved.accent
|
|
206
|
-
}), $[35] = palette.blue, $[36] = resolved.accent, $[37] = t15, $[38] = t16) : t16 = $[38];
|
|
207
|
-
let t17 = active.text === void 0, t18;
|
|
208
|
-
$[39] === patch ? t18 = $[40] : (t18 = (text) => patch({ text }), $[39] = patch, $[40] = t18);
|
|
209
|
-
let t19;
|
|
210
|
-
$[41] === clearField ? t19 = $[42] : (t19 = () => clearField("text"), $[41] = clearField, $[42] = t19);
|
|
211
|
-
let t20;
|
|
212
|
-
$[43] !== palette.gray || $[44] !== resolved.text || $[45] !== t17 || $[46] !== t18 || $[47] !== t19 ? (t20 = /* @__PURE__ */ jsx(ColorRow, {
|
|
213
|
-
auto: t17,
|
|
214
|
-
onChange: t18,
|
|
215
|
-
onClear: t19,
|
|
216
|
-
tints: palette.gray,
|
|
217
|
-
title: "Text",
|
|
218
|
-
value: resolved.text
|
|
219
|
-
}), $[43] = palette.gray, $[44] = resolved.text, $[45] = t17, $[46] = t18, $[47] = t19, $[48] = t20) : t20 = $[48];
|
|
220
|
-
let t21 = active.background?.dark === void 0, t22;
|
|
221
|
-
$[49] === patchBackground ? t22 = $[50] : (t22 = (dark) => patchBackground({ dark }), $[49] = patchBackground, $[50] = t22);
|
|
222
|
-
let t23;
|
|
223
|
-
$[51] === clearBackground ? t23 = $[52] : (t23 = () => clearBackground("dark"), $[51] = clearBackground, $[52] = t23);
|
|
224
|
-
let t24;
|
|
225
|
-
$[53] !== palette.black || $[54] !== resolved.background.dark || $[55] !== t21 || $[56] !== t22 || $[57] !== t23 ? (t24 = /* @__PURE__ */ jsx(ColorRow, {
|
|
226
|
-
adjusted: palette.black,
|
|
227
|
-
auto: t21,
|
|
228
|
-
onChange: t22,
|
|
229
|
-
onClear: t23,
|
|
230
|
-
title: "Background · dark",
|
|
231
|
-
value: resolved.background.dark
|
|
232
|
-
}), $[53] = palette.black, $[54] = resolved.background.dark, $[55] = t21, $[56] = t22, $[57] = t23, $[58] = t24) : t24 = $[58];
|
|
233
|
-
let t25 = active.background?.light === void 0, t26;
|
|
234
|
-
$[59] === patchBackground ? t26 = $[60] : (t26 = (light) => patchBackground({ light }), $[59] = patchBackground, $[60] = t26);
|
|
235
|
-
let t27;
|
|
236
|
-
$[61] === clearBackground ? t27 = $[62] : (t27 = () => clearBackground("light"), $[61] = clearBackground, $[62] = t27);
|
|
237
|
-
let t28;
|
|
238
|
-
$[63] !== palette.white || $[64] !== resolved.background.light || $[65] !== t25 || $[66] !== t26 || $[67] !== t27 ? (t28 = /* @__PURE__ */ jsx(ColorRow, {
|
|
239
|
-
adjusted: palette.white,
|
|
240
|
-
auto: t25,
|
|
241
|
-
onChange: t26,
|
|
242
|
-
onClear: t27,
|
|
243
|
-
title: "Background · light",
|
|
244
|
-
value: resolved.background.light
|
|
245
|
-
}), $[63] = palette.white, $[64] = resolved.background.light, $[65] = t25, $[66] = t26, $[67] = t27, $[68] = t28) : t28 = $[68];
|
|
246
|
-
let t29;
|
|
247
|
-
$[69] !== t16 || $[70] !== t20 || $[71] !== t24 || $[72] !== t28 ? (t29 = /* @__PURE__ */ jsxs(Stack, {
|
|
248
|
-
gap: 3,
|
|
249
|
-
children: [t14, /* @__PURE__ */ jsxs(Stack, {
|
|
250
|
-
gap: 4,
|
|
251
|
-
children: [
|
|
252
|
-
t16,
|
|
253
|
-
t20,
|
|
254
|
-
t24,
|
|
255
|
-
t28
|
|
256
|
-
]
|
|
257
|
-
})]
|
|
258
|
-
}), $[69] = t16, $[70] = t20, $[71] = t24, $[72] = t28, $[73] = t29) : t29 = $[73];
|
|
259
|
-
let t30;
|
|
260
|
-
$[74] === Symbol.for("react.memo_cache_sentinel") ? (t30 = /* @__PURE__ */ jsx(Text, {
|
|
261
|
-
size: 1,
|
|
262
|
-
weight: "medium",
|
|
263
|
-
children: "Contrast"
|
|
264
|
-
}), $[74] = t30) : t30 = $[74];
|
|
265
|
-
let t31 = active.contrast === void 0 ? " · auto" : "", t32;
|
|
266
|
-
$[75] !== resolved.contrast || $[76] !== t31 ? (t32 = /* @__PURE__ */ jsxs(Stack, {
|
|
267
|
-
flex: 1,
|
|
268
|
-
gap: 2,
|
|
269
|
-
children: [t30, /* @__PURE__ */ jsxs(Text, {
|
|
270
|
-
muted: !0,
|
|
271
|
-
size: 0,
|
|
272
|
-
children: [resolved.contrast, t31]
|
|
273
|
-
})]
|
|
274
|
-
}), $[75] = resolved.contrast, $[76] = t31, $[77] = t32) : t32 = $[77];
|
|
275
|
-
let t33;
|
|
276
|
-
$[78] !== active.contrast || $[79] !== clearField ? (t33 = active.contrast !== void 0 && /* @__PURE__ */ jsx(Button, {
|
|
277
|
-
icon: ResetIcon,
|
|
278
|
-
mode: "bleed",
|
|
279
|
-
onClick: () => clearField("contrast"),
|
|
280
|
-
padding: 2,
|
|
281
|
-
title: "Reset to auto"
|
|
282
|
-
}), $[78] = active.contrast, $[79] = clearField, $[80] = t33) : t33 = $[80];
|
|
283
|
-
let t34;
|
|
284
|
-
$[81] !== t32 || $[82] !== t33 ? (t34 = /* @__PURE__ */ jsxs(Flex, {
|
|
285
|
-
align: "center",
|
|
286
|
-
gap: 2,
|
|
287
|
-
children: [t32, t33]
|
|
288
|
-
}), $[81] = t32, $[82] = t33, $[83] = t34) : t34 = $[83];
|
|
289
|
-
let t35;
|
|
290
|
-
$[84] === patch ? t35 = $[85] : (t35 = (event) => patch({ contrast: Number(event.currentTarget.value) }), $[84] = patch, $[85] = t35);
|
|
291
|
-
let t36;
|
|
292
|
-
$[86] !== resolved.contrast || $[87] !== t35 ? (t36 = /* @__PURE__ */ jsx(Range, {
|
|
293
|
-
"aria-label": "Contrast",
|
|
294
|
-
max: 100,
|
|
295
|
-
min: 15,
|
|
296
|
-
onChange: t35,
|
|
297
|
-
step: 1,
|
|
298
|
-
type: "range",
|
|
299
|
-
value: resolved.contrast
|
|
300
|
-
}), $[86] = resolved.contrast, $[87] = t35, $[88] = t36) : t36 = $[88];
|
|
301
|
-
let t37;
|
|
302
|
-
$[89] === Symbol.for("react.memo_cache_sentinel") ? (t37 = /* @__PURE__ */ jsx(Text, {
|
|
1781
|
+
}), $[5] = handleCopy, $[6] = t2);
|
|
1782
|
+
let t3;
|
|
1783
|
+
$[7] === Symbol.for("react.memo_cache_sentinel") ? (t3 = /* @__PURE__ */ jsx("code", { children: "sanity.config.ts" }), $[7] = t3) : t3 = $[7];
|
|
1784
|
+
let t4;
|
|
1785
|
+
$[8] === Symbol.for("react.memo_cache_sentinel") ? (t4 = /* @__PURE__ */ jsx("code", { children: "theme" }), $[8] = t4) : t4 = $[8];
|
|
1786
|
+
let t5;
|
|
1787
|
+
$[9] === Symbol.for("react.memo_cache_sentinel") ? (t5 = /* @__PURE__ */ jsx("code", { children: "defineConfig" }), $[9] = t5) : t5 = $[9];
|
|
1788
|
+
let t6;
|
|
1789
|
+
$[10] === theme.title ? t6 = $[11] : (t6 = displayTitle(theme.title), $[10] = theme.title, $[11] = t6);
|
|
1790
|
+
let t7;
|
|
1791
|
+
$[12] === t6 ? t7 = $[13] : (t7 = /* @__PURE__ */ jsxs(Text, {
|
|
303
1792
|
muted: !0,
|
|
304
|
-
size:
|
|
305
|
-
children: "100 keeps text and borders neutral — lower values blend in the accent"
|
|
306
|
-
}), $[89] = t37) : t37 = $[89];
|
|
307
|
-
let t38;
|
|
308
|
-
$[90] !== t34 || $[91] !== t36 ? (t38 = /* @__PURE__ */ jsxs(Stack, {
|
|
309
|
-
gap: 3,
|
|
1793
|
+
size: 1,
|
|
310
1794
|
children: [
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
1795
|
+
"Export this from ",
|
|
1796
|
+
t3,
|
|
1797
|
+
" and pass it as the ",
|
|
1798
|
+
t4,
|
|
1799
|
+
" of",
|
|
1800
|
+
" ",
|
|
1801
|
+
t5,
|
|
1802
|
+
" to make “",
|
|
1803
|
+
t6,
|
|
1804
|
+
"” the Studio theme without this tool."
|
|
314
1805
|
]
|
|
315
|
-
}), $[
|
|
316
|
-
let
|
|
317
|
-
$[
|
|
318
|
-
size: 1,
|
|
319
|
-
weight: "medium",
|
|
320
|
-
children: "Add to your config"
|
|
321
|
-
}), $[93] = t39) : t39 = $[93];
|
|
322
|
-
let t40;
|
|
323
|
-
$[94] === snippet ? t40 = $[95] : (t40 = /* @__PURE__ */ jsx(Card, {
|
|
1806
|
+
}), $[12] = t6, $[13] = t7);
|
|
1807
|
+
let t8;
|
|
1808
|
+
$[14] === snippet ? t8 = $[15] : (t8 = /* @__PURE__ */ jsx(Card, {
|
|
324
1809
|
border: !0,
|
|
325
1810
|
overflow: "auto",
|
|
326
|
-
padding:
|
|
1811
|
+
padding: 3,
|
|
327
1812
|
radius: 2,
|
|
328
1813
|
tone: "transparent",
|
|
329
1814
|
children: /* @__PURE__ */ jsx(Code, {
|
|
330
1815
|
language: "ts",
|
|
331
|
-
size:
|
|
1816
|
+
size: 1,
|
|
332
1817
|
children: snippet
|
|
333
1818
|
})
|
|
334
|
-
}), $[
|
|
335
|
-
let
|
|
336
|
-
$[
|
|
337
|
-
|
|
338
|
-
mode: "ghost",
|
|
339
|
-
onClick: () => void handleCopy(),
|
|
340
|
-
text: "Copy"
|
|
341
|
-
}), $[96] = handleCopy, $[97] = t41);
|
|
342
|
-
let t42 = options === null, t43;
|
|
343
|
-
$[98] === setOptions ? t43 = $[99] : (t43 = () => setOptions(null), $[98] = setOptions, $[99] = t43);
|
|
344
|
-
let t44;
|
|
345
|
-
$[100] !== t42 || $[101] !== t43 ? (t44 = /* @__PURE__ */ jsx(Button, {
|
|
346
|
-
disabled: t42,
|
|
347
|
-
icon: ResetIcon,
|
|
348
|
-
mode: "ghost",
|
|
349
|
-
onClick: t43,
|
|
350
|
-
text: "Reset",
|
|
351
|
-
tone: "critical"
|
|
352
|
-
}), $[100] = t42, $[101] = t43, $[102] = t44) : t44 = $[102];
|
|
353
|
-
let t45;
|
|
354
|
-
$[103] !== t41 || $[104] !== t44 ? (t45 = /* @__PURE__ */ jsxs(Flex, {
|
|
355
|
-
gap: 2,
|
|
356
|
-
children: [t41, t44]
|
|
357
|
-
}), $[103] = t41, $[104] = t44, $[105] = t45) : t45 = $[105];
|
|
358
|
-
let t46;
|
|
359
|
-
$[106] !== t40 || $[107] !== t45 ? (t46 = /* @__PURE__ */ jsxs(Stack, {
|
|
360
|
-
gap: 3,
|
|
361
|
-
children: [
|
|
362
|
-
t39,
|
|
363
|
-
t40,
|
|
364
|
-
t45
|
|
365
|
-
]
|
|
366
|
-
}), $[106] = t40, $[107] = t45, $[108] = t46) : t46 = $[108];
|
|
367
|
-
let t47;
|
|
368
|
-
$[109] !== t13 || $[110] !== t29 || $[111] !== t38 || $[112] !== t46 ? (t47 = /* @__PURE__ */ jsx(Box, {
|
|
369
|
-
flex: 1,
|
|
370
|
-
overflow: "auto",
|
|
371
|
-
padding: 3,
|
|
1819
|
+
}), $[14] = snippet, $[15] = t8);
|
|
1820
|
+
let t9;
|
|
1821
|
+
$[16] !== t7 || $[17] !== t8 ? (t9 = /* @__PURE__ */ jsx(Box, {
|
|
1822
|
+
padding: 4,
|
|
372
1823
|
children: /* @__PURE__ */ jsxs(Stack, {
|
|
373
1824
|
gap: 4,
|
|
374
|
-
children: [
|
|
375
|
-
t13,
|
|
376
|
-
t29,
|
|
377
|
-
t38,
|
|
378
|
-
t46
|
|
379
|
-
]
|
|
380
|
-
})
|
|
381
|
-
}), $[109] = t13, $[110] = t29, $[111] = t38, $[112] = t46, $[113] = t47) : t47 = $[113];
|
|
382
|
-
let t48;
|
|
383
|
-
return $[114] !== t10 || $[115] !== t47 ? (t48 = /* @__PURE__ */ jsx(Card, {
|
|
384
|
-
height: "fill",
|
|
385
|
-
children: /* @__PURE__ */ jsxs(Flex, {
|
|
386
|
-
direction: "column",
|
|
387
|
-
height: "fill",
|
|
388
|
-
children: [t10, t47]
|
|
1825
|
+
children: [t7, t8]
|
|
389
1826
|
})
|
|
390
|
-
}), $[
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
function PresetButton(props) {
|
|
403
|
-
let $ = c(27), { active, onClick, preset } = props, t0;
|
|
404
|
-
$[0] === preset.options ? t0 = $[1] : (t0 = resolveThemeOptions(preset.options), $[0] = preset.options, $[1] = t0);
|
|
405
|
-
let resolved = t0, t1;
|
|
406
|
-
$[2] === resolved.accent ? t1 = $[3] : (t1 = ["accent", resolved.accent], $[2] = resolved.accent, $[3] = t1);
|
|
407
|
-
let t2;
|
|
408
|
-
$[4] === resolved.text ? t2 = $[5] : (t2 = ["text", resolved.text], $[4] = resolved.text, $[5] = t2);
|
|
409
|
-
let t3;
|
|
410
|
-
$[6] === resolved.background.dark ? t3 = $[7] : (t3 = ["dark", resolved.background.dark], $[6] = resolved.background.dark, $[7] = t3);
|
|
411
|
-
let t4;
|
|
412
|
-
$[8] === resolved.background.light ? t4 = $[9] : (t4 = ["light", resolved.background.light], $[8] = resolved.background.light, $[9] = t4);
|
|
413
|
-
let t5;
|
|
414
|
-
$[10] !== t1 || $[11] !== t2 || $[12] !== t3 || $[13] !== t4 ? (t5 = [
|
|
415
|
-
t1,
|
|
416
|
-
t2,
|
|
417
|
-
t3,
|
|
418
|
-
t4
|
|
419
|
-
], $[10] = t1, $[11] = t2, $[12] = t3, $[13] = t4, $[14] = t5) : t5 = $[14];
|
|
420
|
-
let swatches = t5, t6;
|
|
421
|
-
$[15] === swatches ? t6 = $[16] : (t6 = /* @__PURE__ */ jsx("span", {
|
|
422
|
-
style: paletteStyle,
|
|
423
|
-
children: swatches.map(_temp$1)
|
|
424
|
-
}), $[15] = swatches, $[16] = t6);
|
|
425
|
-
let t7;
|
|
426
|
-
$[17] === preset.title ? t7 = $[18] : (t7 = /* @__PURE__ */ jsx(Text, {
|
|
427
|
-
align: "left",
|
|
428
|
-
size: 1,
|
|
429
|
-
textOverflow: "ellipsis",
|
|
430
|
-
children: preset.title
|
|
431
|
-
}), $[17] = preset.title, $[18] = t7);
|
|
432
|
-
let t8;
|
|
433
|
-
$[19] !== t6 || $[20] !== t7 ? (t8 = /* @__PURE__ */ jsxs(Stack, {
|
|
434
|
-
as: "span",
|
|
435
|
-
gap: 2,
|
|
436
|
-
children: [t6, t7]
|
|
437
|
-
}), $[19] = t6, $[20] = t7, $[21] = t8) : t8 = $[21];
|
|
438
|
-
let t9;
|
|
439
|
-
return $[22] !== active || $[23] !== onClick || $[24] !== preset.title || $[25] !== t8 ? (t9 = /* @__PURE__ */ jsx(Button, {
|
|
440
|
-
mode: "ghost",
|
|
441
|
-
onClick,
|
|
442
|
-
padding: 2,
|
|
443
|
-
selected: active,
|
|
444
|
-
title: preset.title,
|
|
445
|
-
children: t8
|
|
446
|
-
}), $[22] = active, $[23] = onClick, $[24] = preset.title, $[25] = t8, $[26] = t9) : t9 = $[26], t9;
|
|
447
|
-
}
|
|
448
|
-
function _temp$1(t0) {
|
|
449
|
-
let [key, background] = t0;
|
|
450
|
-
return /* @__PURE__ */ jsx("span", { style: {
|
|
451
|
-
flex: 1,
|
|
452
|
-
background
|
|
453
|
-
} }, key);
|
|
1827
|
+
}), $[16] = t7, $[17] = t8, $[18] = t9) : t9 = $[18];
|
|
1828
|
+
let t10;
|
|
1829
|
+
return $[19] !== onClose || $[20] !== t2 || $[21] !== t9 ? (t10 = /* @__PURE__ */ jsx(Dialog, {
|
|
1830
|
+
animate: !0,
|
|
1831
|
+
footer: t2,
|
|
1832
|
+
header: "Add to your config",
|
|
1833
|
+
id: "themer-snippet",
|
|
1834
|
+
onClickOutside: onClose,
|
|
1835
|
+
onClose,
|
|
1836
|
+
width: 1,
|
|
1837
|
+
children: t9
|
|
1838
|
+
}), $[19] = onClose, $[20] = t2, $[21] = t9, $[22] = t10) : t10 = $[22], t10;
|
|
454
1839
|
}
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
1840
|
+
const VIEW_TITLES = {
|
|
1841
|
+
list: "Themer",
|
|
1842
|
+
edit: "Edit theme",
|
|
1843
|
+
removed: "Removed themes"
|
|
459
1844
|
};
|
|
460
1845
|
/**
|
|
461
|
-
*
|
|
462
|
-
*
|
|
1846
|
+
* The themer sidebar: a header that navigates between the flows, the flow
|
|
1847
|
+
* itself — picking a theme, editing one, or restoring removed ones — and the
|
|
1848
|
+
* dialog with the `buildTheme` snippet of the applied theme.
|
|
1849
|
+
*
|
|
1850
|
+
* @internal
|
|
463
1851
|
*/
|
|
464
|
-
function
|
|
465
|
-
let $ = c(
|
|
466
|
-
$[0]
|
|
1852
|
+
function ThemerSidebar() {
|
|
1853
|
+
let $ = c(37), { active, view, send } = useThemer(), [snippetOpen, setSnippetOpen] = useState(!1), inList = view.name === "list", t0;
|
|
1854
|
+
$[0] !== inList || $[1] !== send ? (t0 = !inList && /* @__PURE__ */ jsx(TooltipButton, {
|
|
1855
|
+
icon: ArrowLeftIcon,
|
|
1856
|
+
mode: "bleed",
|
|
1857
|
+
onClick: () => send({ type: "flow.list" }),
|
|
1858
|
+
padding: 2,
|
|
1859
|
+
tooltip: "Back to the themes"
|
|
1860
|
+
}), $[0] = inList, $[1] = send, $[2] = t0) : t0 = $[2];
|
|
1861
|
+
let t1 = +!!inList, t2;
|
|
1862
|
+
$[3] === Symbol.for("react.memo_cache_sentinel") ? (t2 = { minWidth: 0 }, $[3] = t2) : t2 = $[3];
|
|
1863
|
+
let t3 = VIEW_TITLES[view.name], t4;
|
|
1864
|
+
$[4] === t3 ? t4 = $[5] : (t4 = /* @__PURE__ */ jsx(Text, {
|
|
467
1865
|
size: 1,
|
|
468
|
-
children: title
|
|
469
|
-
}), $[0] = title, $[1] = t0);
|
|
470
|
-
let t1 = adjusted !== void 0 && adjusted !== value ? ` → ${adjusted}` : "", t2 = auto ? " · auto" : "", t3;
|
|
471
|
-
$[2] !== t1 || $[3] !== t2 || $[4] !== value ? (t3 = /* @__PURE__ */ jsxs(Text, {
|
|
472
|
-
muted: !0,
|
|
473
|
-
size: 0,
|
|
474
1866
|
textOverflow: "ellipsis",
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
t2
|
|
479
|
-
]
|
|
480
|
-
}), $[2] = t1, $[3] = t2, $[4] = value, $[5] = t3) : t3 = $[5];
|
|
481
|
-
let t4;
|
|
482
|
-
$[6] !== t0 || $[7] !== t3 ? (t4 = /* @__PURE__ */ jsxs(Stack, {
|
|
483
|
-
flex: 1,
|
|
484
|
-
gap: 2,
|
|
485
|
-
children: [t0, t3]
|
|
486
|
-
}), $[6] = t0, $[7] = t3, $[8] = t4) : t4 = $[8];
|
|
1867
|
+
weight: "semibold",
|
|
1868
|
+
children: t3
|
|
1869
|
+
}), $[4] = t3, $[5] = t4);
|
|
487
1870
|
let t5;
|
|
488
|
-
$[
|
|
489
|
-
|
|
1871
|
+
$[6] !== t1 || $[7] !== t4 ? (t5 = /* @__PURE__ */ jsx(Box, {
|
|
1872
|
+
flex: 1,
|
|
1873
|
+
paddingLeft: t1,
|
|
1874
|
+
style: t2,
|
|
1875
|
+
children: t4
|
|
1876
|
+
}), $[6] = t1, $[7] = t4, $[8] = t5) : t5 = $[8];
|
|
1877
|
+
let t6;
|
|
1878
|
+
$[9] === view.name ? t6 = $[10] : (t6 = view.name !== "removed" && /* @__PURE__ */ jsx(TooltipButton, {
|
|
1879
|
+
icon: CodeBlockIcon,
|
|
490
1880
|
mode: "bleed",
|
|
491
|
-
onClick:
|
|
1881
|
+
onClick: () => setSnippetOpen(!0),
|
|
492
1882
|
padding: 2,
|
|
493
|
-
|
|
494
|
-
}), $[9] =
|
|
495
|
-
let
|
|
496
|
-
$[
|
|
1883
|
+
tooltip: "Show the code for the applied theme"
|
|
1884
|
+
}), $[9] = view.name, $[10] = t6);
|
|
1885
|
+
let t7;
|
|
1886
|
+
$[11] === send ? t7 = $[12] : (t7 = /* @__PURE__ */ jsx(TooltipButton, {
|
|
1887
|
+
icon: CloseIcon,
|
|
1888
|
+
mode: "bleed",
|
|
1889
|
+
onClick: () => send({ type: "sidebar.close" }),
|
|
1890
|
+
padding: 2,
|
|
1891
|
+
tooltip: "Close themer"
|
|
1892
|
+
}), $[11] = send, $[12] = t7);
|
|
497
1893
|
let t8;
|
|
498
|
-
$[14] !==
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
1894
|
+
$[13] !== t0 || $[14] !== t5 || $[15] !== t6 || $[16] !== t7 ? (t8 = /* @__PURE__ */ jsx(Card, {
|
|
1895
|
+
padding: 2,
|
|
1896
|
+
children: /* @__PURE__ */ jsxs(Flex, {
|
|
1897
|
+
align: "center",
|
|
1898
|
+
gap: 1,
|
|
1899
|
+
children: [
|
|
1900
|
+
t0,
|
|
1901
|
+
t5,
|
|
1902
|
+
t6,
|
|
1903
|
+
t7
|
|
1904
|
+
]
|
|
1905
|
+
})
|
|
1906
|
+
}), $[13] = t0, $[14] = t5, $[15] = t6, $[16] = t7, $[17] = t8) : t8 = $[17];
|
|
504
1907
|
let t9;
|
|
505
|
-
$[18]
|
|
506
|
-
align: "center",
|
|
507
|
-
gap: 2,
|
|
508
|
-
children: [
|
|
509
|
-
t4,
|
|
510
|
-
t5,
|
|
511
|
-
t8
|
|
512
|
-
]
|
|
513
|
-
}), $[18] = t4, $[19] = t5, $[20] = t8, $[21] = t9) : t9 = $[21];
|
|
1908
|
+
$[18] === view.name ? t9 = $[19] : (t9 = view.name === "list" && /* @__PURE__ */ jsx(ThemeList, {}), $[18] = view.name, $[19] = t9);
|
|
514
1909
|
let t10;
|
|
515
|
-
$[
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
background: tints[tint]
|
|
520
|
-
} }, tint))
|
|
521
|
-
}), $[22] = tints, $[23] = t10);
|
|
1910
|
+
$[20] !== view.focusTitle || $[21] !== view.name || $[22] !== view.slug ? (t10 = view.name === "edit" && /* @__PURE__ */ jsx(ThemeEditor, {
|
|
1911
|
+
focusTitle: view.focusTitle,
|
|
1912
|
+
slug: view.slug
|
|
1913
|
+
}), $[20] = view.focusTitle, $[21] = view.name, $[22] = view.slug, $[23] = t10) : t10 = $[23];
|
|
522
1914
|
let t11;
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
1915
|
+
$[24] === view.name ? t11 = $[25] : (t11 = view.name === "removed" && /* @__PURE__ */ jsx(RemovedThemes, {}), $[24] = view.name, $[25] = t11);
|
|
1916
|
+
let t12;
|
|
1917
|
+
$[26] !== t10 || $[27] !== t11 || $[28] !== t8 || $[29] !== t9 ? (t12 = /* @__PURE__ */ jsxs(Flex, {
|
|
1918
|
+
direction: "column",
|
|
1919
|
+
height: "fill",
|
|
1920
|
+
children: [
|
|
1921
|
+
t8,
|
|
1922
|
+
t9,
|
|
1923
|
+
t10,
|
|
1924
|
+
t11
|
|
1925
|
+
]
|
|
1926
|
+
}), $[26] = t10, $[27] = t11, $[28] = t8, $[29] = t9, $[30] = t12) : t12 = $[30];
|
|
1927
|
+
let t13;
|
|
1928
|
+
$[31] !== active || $[32] !== snippetOpen ? (t13 = snippetOpen && /* @__PURE__ */ jsx(ThemeSnippetDialog, {
|
|
1929
|
+
onClose: () => setSnippetOpen(!1),
|
|
1930
|
+
theme: active
|
|
1931
|
+
}), $[31] = active, $[32] = snippetOpen, $[33] = t13) : t13 = $[33];
|
|
1932
|
+
let t14;
|
|
1933
|
+
return $[34] !== t12 || $[35] !== t13 ? (t14 = /* @__PURE__ */ jsxs(Card, {
|
|
1934
|
+
height: "fill",
|
|
1935
|
+
children: [t12, t13]
|
|
1936
|
+
}), $[34] = t12, $[35] = t13, $[36] = t14) : t14 = $[36], t14;
|
|
527
1937
|
}
|
|
528
1938
|
/**
|
|
529
|
-
*
|
|
530
|
-
*
|
|
531
|
-
*
|
|
1939
|
+
* The narrowest the sidebar goes — and its default: narrow enough to leave the
|
|
1940
|
+
* studio preview as much room as possible, while the themes stack in a single
|
|
1941
|
+
* column of preview cards and the editor fits a swatch next to its label.
|
|
532
1942
|
*/
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
1943
|
+
const WIDTH_STORAGE_KEY = "sanityStudio:themer:width";
|
|
1944
|
+
function clampWidth(width) {
|
|
1945
|
+
return Math.min(400, Math.max(200, Math.round(width)));
|
|
1946
|
+
}
|
|
1947
|
+
function readStoredWidth() {
|
|
1948
|
+
try {
|
|
1949
|
+
let stored = Number(localStorage.getItem(WIDTH_STORAGE_KEY));
|
|
1950
|
+
return Number.isFinite(stored) && stored > 0 ? clampWidth(stored) : 200;
|
|
1951
|
+
} catch {
|
|
1952
|
+
return 200;
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
function writeStoredWidth(width) {
|
|
1956
|
+
try {
|
|
1957
|
+
width === 200 ? localStorage.removeItem(WIDTH_STORAGE_KEY) : localStorage.setItem(WIDTH_STORAGE_KEY, String(width));
|
|
1958
|
+
} catch {}
|
|
1959
|
+
}
|
|
1960
|
+
/**
|
|
1961
|
+
* The sidebar itself does not clip, so the resize handle can straddle its
|
|
1962
|
+
* border — the content is clipped one level down instead
|
|
1963
|
+
*/
|
|
1964
|
+
const Sidebar = styled(Layer).withConfig({
|
|
1965
|
+
displayName: "Sidebar",
|
|
1966
|
+
componentId: "sc-xkgsr1-0"
|
|
1967
|
+
})`position:relative;flex:none;box-sizing:border-box;border-left:1px solid var(--card-border-color);`, ResizeHandle = styled.div.withConfig({
|
|
1968
|
+
displayName: "ResizeHandle",
|
|
1969
|
+
componentId: "sc-xkgsr1-1"
|
|
1970
|
+
})`position:absolute;top:0;bottom:0;left:-3px;z-index:1;width:7px;cursor:col-resize;touch-action:none;&::after{content:'';position:absolute;top:0;bottom:0;left:2px;width:3px;background:var(--card-focus-ring-color);opacity:0;transition:opacity 100ms;}&:hover::after,&:focus-visible::after,&[data-dragging='true']::after{opacity:1;}&:focus{outline:none;}`;
|
|
540
1971
|
/**
|
|
541
1972
|
* Renders the themer sidebar next to the active tool, so the user can browse
|
|
542
|
-
* around their own studio while tweaking the theme.
|
|
1973
|
+
* around their own studio while tweaking the theme. The sidebar's left edge
|
|
1974
|
+
* drags (or arrow-keys) it wider, up to twice its default width — the width
|
|
1975
|
+
* sticks between sessions.
|
|
543
1976
|
*
|
|
544
1977
|
* @internal
|
|
545
1978
|
*/
|
|
546
1979
|
function ThemerActiveToolLayout(props) {
|
|
547
|
-
let $ = c(
|
|
548
|
-
$[0] ===
|
|
549
|
-
let
|
|
550
|
-
$[
|
|
1980
|
+
let $ = c(20), { open } = useThemer(), [width, setWidth] = useState(readStoredWidth), [dragging, setDragging] = useState(!1), drag = useRef(null), t0, t1;
|
|
1981
|
+
$[0] === width ? (t0 = $[1], t1 = $[2]) : (t0 = () => writeStoredWidth(width), t1 = [width], $[0] = width, $[1] = t0, $[2] = t1), useEffect(t0, t1);
|
|
1982
|
+
let t2;
|
|
1983
|
+
$[3] === width ? t2 = $[4] : (t2 = (event) => {
|
|
1984
|
+
event.button === 0 && (event.preventDefault(), event.currentTarget.setPointerCapture(event.pointerId), drag.current = {
|
|
1985
|
+
pointerId: event.pointerId,
|
|
1986
|
+
startX: event.clientX,
|
|
1987
|
+
startWidth: width
|
|
1988
|
+
}, setDragging(!0));
|
|
1989
|
+
}, $[3] = width, $[4] = t2);
|
|
1990
|
+
let handlePointerDown = t2, t3;
|
|
1991
|
+
$[5] === Symbol.for("react.memo_cache_sentinel") ? (t3 = (event_0) => {
|
|
1992
|
+
drag.current && drag.current.pointerId === event_0.pointerId && setWidth(clampWidth(drag.current.startWidth - (event_0.clientX - drag.current.startX)));
|
|
1993
|
+
}, $[5] = t3) : t3 = $[5];
|
|
1994
|
+
let handlePointerMove = t3, t4;
|
|
1995
|
+
$[6] === Symbol.for("react.memo_cache_sentinel") ? (t4 = (event_1) => {
|
|
1996
|
+
drag.current && drag.current.pointerId === event_1.pointerId && (drag.current = null, setDragging(!1));
|
|
1997
|
+
}, $[6] = t4) : t4 = $[6];
|
|
1998
|
+
let handlePointerUp = t4, t5;
|
|
1999
|
+
$[7] === Symbol.for("react.memo_cache_sentinel") ? (t5 = (event_2) => {
|
|
2000
|
+
let step = {
|
|
2001
|
+
ArrowLeft: 16,
|
|
2002
|
+
ArrowRight: -16,
|
|
2003
|
+
Home: 400,
|
|
2004
|
+
End: -400
|
|
2005
|
+
}[event_2.key];
|
|
2006
|
+
step !== void 0 && (event_2.preventDefault(), setWidth((current) => clampWidth(current + step)));
|
|
2007
|
+
}, $[7] = t5) : t5 = $[7];
|
|
2008
|
+
let handleKeyDown = t5, t6;
|
|
2009
|
+
$[8] === props ? t6 = $[9] : (t6 = props.renderDefault(props), $[8] = props, $[9] = t6);
|
|
2010
|
+
let t7;
|
|
2011
|
+
$[10] === t6 ? t7 = $[11] : (t7 = /* @__PURE__ */ jsx(Box, {
|
|
551
2012
|
flex: 1,
|
|
552
2013
|
height: "fill",
|
|
553
2014
|
overflow: "auto",
|
|
554
|
-
children:
|
|
555
|
-
}), $[
|
|
556
|
-
let
|
|
557
|
-
$[
|
|
2015
|
+
children: t6
|
|
2016
|
+
}), $[10] = t6, $[11] = t7);
|
|
2017
|
+
let t8;
|
|
2018
|
+
$[12] !== dragging || $[13] !== handlePointerDown || $[14] !== open || $[15] !== width ? (t8 = open && /* @__PURE__ */ jsxs(Sidebar, {
|
|
558
2019
|
height: "fill",
|
|
559
|
-
style:
|
|
2020
|
+
style: { width },
|
|
560
2021
|
zOffset: 100,
|
|
561
|
-
children: /* @__PURE__ */ jsx(
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
2022
|
+
children: [/* @__PURE__ */ jsx(ResizeHandle, {
|
|
2023
|
+
"aria-label": "Resize the themer",
|
|
2024
|
+
"aria-orientation": "vertical",
|
|
2025
|
+
"aria-valuemax": 400,
|
|
2026
|
+
"aria-valuemin": 200,
|
|
2027
|
+
"aria-valuenow": width,
|
|
2028
|
+
"data-dragging": dragging,
|
|
2029
|
+
onDoubleClick: () => setWidth(200),
|
|
2030
|
+
onKeyDown: handleKeyDown,
|
|
2031
|
+
onPointerCancel: handlePointerUp,
|
|
2032
|
+
onPointerDown: handlePointerDown,
|
|
2033
|
+
onPointerMove: handlePointerMove,
|
|
2034
|
+
onPointerUp: handlePointerUp,
|
|
2035
|
+
role: "separator",
|
|
2036
|
+
tabIndex: 0
|
|
2037
|
+
}), /* @__PURE__ */ jsx(Box, {
|
|
2038
|
+
height: "fill",
|
|
2039
|
+
overflow: "hidden",
|
|
2040
|
+
children: /* @__PURE__ */ jsx(ThemerSidebar, {})
|
|
2041
|
+
})]
|
|
2042
|
+
}), $[12] = dragging, $[13] = handlePointerDown, $[14] = open, $[15] = width, $[16] = t8) : t8 = $[16];
|
|
2043
|
+
let t9;
|
|
2044
|
+
return $[17] !== t7 || $[18] !== t8 ? (t9 = /* @__PURE__ */ jsxs(Flex, {
|
|
565
2045
|
height: "fill",
|
|
566
2046
|
sizing: "border",
|
|
567
|
-
children: [
|
|
568
|
-
}), $[
|
|
2047
|
+
children: [t7, t8]
|
|
2048
|
+
}), $[17] = t7, $[18] = t8, $[19] = t9) : t9 = $[19], t9;
|
|
2049
|
+
}
|
|
2050
|
+
function themesOf(context) {
|
|
2051
|
+
return resolveThemes(context, context.baseOptions);
|
|
2052
|
+
}
|
|
2053
|
+
/**
|
|
2054
|
+
* The state of the themer tool: two parallel regions, whether the `sidebar`
|
|
2055
|
+
* is open and which `flow` it is in — picking a theme from the `list`,
|
|
2056
|
+
* `edit`ing one of the user's own themes, or restoring `removed` ones. The
|
|
2057
|
+
* context carries the persisted state (the applied theme, the user's themes
|
|
2058
|
+
* and what was removed) alongside what the flows need.
|
|
2059
|
+
*
|
|
2060
|
+
* Theme operations are handled in every flow, and the flows leave on their
|
|
2061
|
+
* own when they lose their subject: the editor when its theme is removed or
|
|
2062
|
+
* deleted, and the removed view when its last theme is restored or deleted.
|
|
2063
|
+
*
|
|
2064
|
+
* @internal
|
|
2065
|
+
*/
|
|
2066
|
+
const themerMachine = setup({
|
|
2067
|
+
types: {
|
|
2068
|
+
context: {},
|
|
2069
|
+
events: {},
|
|
2070
|
+
input: {}
|
|
2071
|
+
},
|
|
2072
|
+
guards: {
|
|
2073
|
+
isCustomTheme: ({ context }, params) => themesOf(context).themes.some((theme) => theme.slug === params.slug && theme.source === "custom"),
|
|
2074
|
+
isEditingListedTheme: ({ context }) => context.editing !== null && themesOf(context).themes.some((theme) => theme.slug === context.editing?.slug && theme.source === "custom"),
|
|
2075
|
+
hasRemovedThemes: ({ context }) => themesOf(context).removed.length > 0
|
|
2076
|
+
},
|
|
2077
|
+
actions: {
|
|
2078
|
+
pick: assign((_, params) => ({ active: params.slug === "config" ? null : params.slug })),
|
|
2079
|
+
add: assign(({ context }, params) => {
|
|
2080
|
+
let theme = createCustomTheme(params.title ?? "Untitled theme", params.options ?? themesOf(context).active.options, params.palette);
|
|
2081
|
+
return {
|
|
2082
|
+
active: theme.slug,
|
|
2083
|
+
custom: [...context.custom, theme],
|
|
2084
|
+
editing: {
|
|
2085
|
+
slug: theme.slug,
|
|
2086
|
+
focusTitle: params.title === void 0
|
|
2087
|
+
},
|
|
2088
|
+
images: params.imageUrl ? {
|
|
2089
|
+
...context.images,
|
|
2090
|
+
[theme.slug]: params.imageUrl
|
|
2091
|
+
} : context.images
|
|
2092
|
+
};
|
|
2093
|
+
}),
|
|
2094
|
+
duplicate: assign(({ context }, params) => {
|
|
2095
|
+
let { themes, removed } = themesOf(context), source = [...themes, ...removed].find((theme) => theme.slug === params.slug);
|
|
2096
|
+
if (!source) return {};
|
|
2097
|
+
let theme = createCustomTheme(duplicateTitle(source.title), source.options, source.palette);
|
|
2098
|
+
return {
|
|
2099
|
+
active: theme.slug,
|
|
2100
|
+
custom: [...context.custom, theme],
|
|
2101
|
+
editing: {
|
|
2102
|
+
slug: theme.slug,
|
|
2103
|
+
focusTitle: !0
|
|
2104
|
+
}
|
|
2105
|
+
};
|
|
2106
|
+
}),
|
|
2107
|
+
startEditing: assign((_, params) => ({
|
|
2108
|
+
active: params.slug,
|
|
2109
|
+
editing: {
|
|
2110
|
+
slug: params.slug,
|
|
2111
|
+
focusTitle: !1
|
|
2112
|
+
}
|
|
2113
|
+
})),
|
|
2114
|
+
stopEditing: assign({ editing: null }),
|
|
2115
|
+
update: assign(({ context }, params) => ({
|
|
2116
|
+
custom: context.custom.map((theme) => theme.slug === params.slug ? {
|
|
2117
|
+
...theme,
|
|
2118
|
+
title: params.title ?? theme.title,
|
|
2119
|
+
options: params.options ?? theme.options,
|
|
2120
|
+
...params.palette ? { palette: params.palette } : {}
|
|
2121
|
+
} : theme),
|
|
2122
|
+
images: params.imageUrl && context.custom.some((theme) => theme.slug === params.slug) ? {
|
|
2123
|
+
...context.images,
|
|
2124
|
+
[params.slug]: params.imageUrl
|
|
2125
|
+
} : context.images
|
|
2126
|
+
})),
|
|
2127
|
+
remove: assign(({ context }, params) => params.slug === "config" || context.removed.includes(params.slug) ? {} : {
|
|
2128
|
+
active: context.active === params.slug ? null : context.active,
|
|
2129
|
+
removed: [...context.removed, params.slug]
|
|
2130
|
+
}),
|
|
2131
|
+
restore: assign(({ context }, params) => ({ removed: context.removed.filter((slug) => slug !== params.slug) })),
|
|
2132
|
+
delete: assign(({ context }, params) => {
|
|
2133
|
+
if (!context.custom.some((theme) => theme.slug === params.slug)) return {};
|
|
2134
|
+
let { [params.slug]: _deleted, ...images } = context.images;
|
|
2135
|
+
return {
|
|
2136
|
+
active: context.active === params.slug ? null : context.active,
|
|
2137
|
+
custom: context.custom.filter((theme) => theme.slug !== params.slug),
|
|
2138
|
+
removed: context.removed.filter((slug) => slug !== params.slug),
|
|
2139
|
+
images
|
|
2140
|
+
};
|
|
2141
|
+
})
|
|
2142
|
+
}
|
|
2143
|
+
}).createMachine({
|
|
2144
|
+
id: "themer",
|
|
2145
|
+
context: ({ input }) => ({
|
|
2146
|
+
...input.stored,
|
|
2147
|
+
baseOptions: input.baseOptions,
|
|
2148
|
+
editing: null,
|
|
2149
|
+
images: {}
|
|
2150
|
+
}),
|
|
2151
|
+
type: "parallel",
|
|
2152
|
+
states: {
|
|
2153
|
+
sidebar: {
|
|
2154
|
+
initial: "closed",
|
|
2155
|
+
states: {
|
|
2156
|
+
closed: { on: { "sidebar.toggle": "open" } },
|
|
2157
|
+
open: { on: {
|
|
2158
|
+
"sidebar.toggle": "closed",
|
|
2159
|
+
"sidebar.close": "closed"
|
|
2160
|
+
} }
|
|
2161
|
+
}
|
|
2162
|
+
},
|
|
2163
|
+
flow: {
|
|
2164
|
+
initial: "list",
|
|
2165
|
+
on: {
|
|
2166
|
+
"theme.pick": { actions: {
|
|
2167
|
+
type: "pick",
|
|
2168
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
2169
|
+
} },
|
|
2170
|
+
"theme.add": {
|
|
2171
|
+
target: ".edit",
|
|
2172
|
+
actions: {
|
|
2173
|
+
type: "add",
|
|
2174
|
+
params: ({ event }) => ({
|
|
2175
|
+
title: event.title,
|
|
2176
|
+
options: event.options,
|
|
2177
|
+
palette: event.palette,
|
|
2178
|
+
imageUrl: event.imageUrl
|
|
2179
|
+
})
|
|
2180
|
+
}
|
|
2181
|
+
},
|
|
2182
|
+
"theme.duplicate": {
|
|
2183
|
+
target: ".edit",
|
|
2184
|
+
actions: {
|
|
2185
|
+
type: "duplicate",
|
|
2186
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
2187
|
+
}
|
|
2188
|
+
},
|
|
2189
|
+
"theme.edit": {
|
|
2190
|
+
target: ".edit",
|
|
2191
|
+
guard: {
|
|
2192
|
+
type: "isCustomTheme",
|
|
2193
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
2194
|
+
},
|
|
2195
|
+
actions: {
|
|
2196
|
+
type: "startEditing",
|
|
2197
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
2198
|
+
}
|
|
2199
|
+
},
|
|
2200
|
+
"theme.update": { actions: {
|
|
2201
|
+
type: "update",
|
|
2202
|
+
params: ({ event }) => ({
|
|
2203
|
+
slug: event.slug,
|
|
2204
|
+
title: event.title,
|
|
2205
|
+
options: event.options,
|
|
2206
|
+
palette: event.palette,
|
|
2207
|
+
imageUrl: event.imageUrl
|
|
2208
|
+
})
|
|
2209
|
+
} },
|
|
2210
|
+
"theme.remove": { actions: {
|
|
2211
|
+
type: "remove",
|
|
2212
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
2213
|
+
} },
|
|
2214
|
+
"theme.restore": { actions: {
|
|
2215
|
+
type: "restore",
|
|
2216
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
2217
|
+
} },
|
|
2218
|
+
"theme.delete": { actions: {
|
|
2219
|
+
type: "delete",
|
|
2220
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
2221
|
+
} },
|
|
2222
|
+
"flow.list": ".list",
|
|
2223
|
+
"flow.removed": {
|
|
2224
|
+
target: ".removed",
|
|
2225
|
+
guard: "hasRemovedThemes"
|
|
2226
|
+
}
|
|
2227
|
+
},
|
|
2228
|
+
states: {
|
|
2229
|
+
list: {},
|
|
2230
|
+
edit: {
|
|
2231
|
+
exit: "stopEditing",
|
|
2232
|
+
always: {
|
|
2233
|
+
guard: not("isEditingListedTheme"),
|
|
2234
|
+
target: "list"
|
|
2235
|
+
}
|
|
2236
|
+
},
|
|
2237
|
+
removed: { always: {
|
|
2238
|
+
guard: not("hasRemovedThemes"),
|
|
2239
|
+
target: "list"
|
|
2240
|
+
} }
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
});
|
|
2245
|
+
/** The part of the machine context that is persisted between sessions @internal */
|
|
2246
|
+
function selectStoredState(snapshot) {
|
|
2247
|
+
let { active, custom, removed } = snapshot.context;
|
|
2248
|
+
return {
|
|
2249
|
+
active,
|
|
2250
|
+
custom,
|
|
2251
|
+
removed
|
|
2252
|
+
};
|
|
569
2253
|
}
|
|
570
|
-
const STORAGE_KEY = "sanityStudio:themer:options";
|
|
2254
|
+
const STORAGE_KEY = "sanityStudio:themer:state", LEGACY_STORAGE_KEY = "sanityStudio:themer:options";
|
|
571
2255
|
function sanitizeColor(value) {
|
|
572
2256
|
return typeof value == "string" && isColor(value) ? value.toLowerCase() : null;
|
|
573
2257
|
}
|
|
574
|
-
function
|
|
575
|
-
|
|
2258
|
+
function sanitizeContrast(value) {
|
|
2259
|
+
return typeof value == "number" && Number.isFinite(value) ? Math.min(100, Math.max(15, value)) : null;
|
|
2260
|
+
}
|
|
2261
|
+
function sanitizeSchemeOptions(value) {
|
|
2262
|
+
let options = {};
|
|
2263
|
+
if (!value || typeof value != "object") return options;
|
|
2264
|
+
let accent = sanitizeColor(Reflect.get(value, "accent")), text = sanitizeColor(Reflect.get(value, "text")), background = sanitizeColor(Reflect.get(value, "background")), contrast = sanitizeContrast(Reflect.get(value, "contrast"));
|
|
2265
|
+
return accent && (options.accent = accent), text && (options.text = text), background && (options.background = background), contrast !== null && (options.contrast = contrast), options;
|
|
2266
|
+
}
|
|
2267
|
+
/**
|
|
2268
|
+
* Options from before they were grouped by scheme: one accent, text color and
|
|
2269
|
+
* contrast for both schemes, and a background per scheme. Both schemes get
|
|
2270
|
+
* the shared colors, so the theme keeps looking the same.
|
|
2271
|
+
*/
|
|
2272
|
+
function sanitizeFlatOptions(value) {
|
|
576
2273
|
let accent = sanitizeColor(Reflect.get(value, "accent"));
|
|
577
2274
|
if (!accent) return null;
|
|
578
|
-
let
|
|
579
|
-
text && (
|
|
580
|
-
let
|
|
581
|
-
|
|
582
|
-
let
|
|
583
|
-
|
|
2275
|
+
let shared = { accent }, text = sanitizeColor(Reflect.get(value, "text")), contrast = sanitizeContrast(Reflect.get(value, "contrast"));
|
|
2276
|
+
text && (shared.text = text), contrast !== null && (shared.contrast = contrast);
|
|
2277
|
+
let backgrounds = Reflect.get(value, "background"), options = {};
|
|
2278
|
+
for (let scheme of SCHEMES) {
|
|
2279
|
+
let background = backgrounds && typeof backgrounds == "object" ? sanitizeColor(Reflect.get(backgrounds, scheme)) : null;
|
|
2280
|
+
options[scheme] = background ? {
|
|
2281
|
+
...shared,
|
|
2282
|
+
background
|
|
2283
|
+
} : { ...shared };
|
|
2284
|
+
}
|
|
2285
|
+
return options;
|
|
2286
|
+
}
|
|
2287
|
+
function sanitizeOptions(value) {
|
|
2288
|
+
if (!value || typeof value != "object") return null;
|
|
2289
|
+
if ("accent" in value) return sanitizeFlatOptions(value);
|
|
2290
|
+
let options = {};
|
|
2291
|
+
for (let scheme of SCHEMES) {
|
|
2292
|
+
let schemeOptions = sanitizeSchemeOptions(Reflect.get(value, scheme));
|
|
2293
|
+
Object.keys(schemeOptions).length > 0 && (options[scheme] = schemeOptions);
|
|
2294
|
+
}
|
|
2295
|
+
return options;
|
|
2296
|
+
}
|
|
2297
|
+
function isReservedSlug(slug) {
|
|
2298
|
+
return slug === "config" || presets.some((preset) => preset.slug === slug);
|
|
2299
|
+
}
|
|
2300
|
+
function sanitizeCustomTheme(value) {
|
|
2301
|
+
if (!value || typeof value != "object") return null;
|
|
2302
|
+
let slug = Reflect.get(value, "slug");
|
|
2303
|
+
if (typeof slug != "string" || !slug || isReservedSlug(slug)) return null;
|
|
2304
|
+
let options = sanitizeOptions(Reflect.get(value, "options"));
|
|
2305
|
+
if (!options) return null;
|
|
2306
|
+
let title = Reflect.get(value, "title"), palette = sanitizePalette(Reflect.get(value, "palette"));
|
|
2307
|
+
return {
|
|
2308
|
+
slug,
|
|
2309
|
+
title: displayTitle(typeof title == "string" ? title : ""),
|
|
2310
|
+
options,
|
|
2311
|
+
...palette ? { palette } : {}
|
|
2312
|
+
};
|
|
2313
|
+
}
|
|
2314
|
+
function sanitizePalette(value) {
|
|
2315
|
+
if (!value || typeof value != "object") return null;
|
|
2316
|
+
let palette = {}, swatches = 0;
|
|
2317
|
+
for (let key of IMAGE_PALETTE_KEYS) {
|
|
2318
|
+
let swatch = sanitizeColor(Reflect.get(value, key));
|
|
2319
|
+
palette[key] = swatch, swatch && swatches++;
|
|
584
2320
|
}
|
|
585
|
-
|
|
586
|
-
|
|
2321
|
+
return swatches > 0 ? palette : null;
|
|
2322
|
+
}
|
|
2323
|
+
function sanitizeState(value) {
|
|
2324
|
+
if (!value || typeof value != "object") return initialThemerState;
|
|
2325
|
+
let custom = [], rawCustom = Reflect.get(value, "custom");
|
|
2326
|
+
if (Array.isArray(rawCustom)) for (let item of rawCustom) {
|
|
2327
|
+
let theme = sanitizeCustomTheme(item);
|
|
2328
|
+
theme && !custom.some((existing) => existing.slug === theme.slug) && custom.push(theme);
|
|
2329
|
+
}
|
|
2330
|
+
let removed = [], rawRemoved = Reflect.get(value, "removed");
|
|
2331
|
+
if (Array.isArray(rawRemoved)) for (let slug of rawRemoved) typeof slug == "string" && slug !== "config" && (presets.some((preset) => preset.slug === slug) || custom.some((theme) => theme.slug === slug)) && !removed.includes(slug) && removed.push(slug);
|
|
2332
|
+
let rawActive = Reflect.get(value, "active");
|
|
2333
|
+
return {
|
|
2334
|
+
active: typeof rawActive == "string" ? rawActive : null,
|
|
2335
|
+
custom,
|
|
2336
|
+
removed
|
|
2337
|
+
};
|
|
2338
|
+
}
|
|
2339
|
+
function readLegacyState() {
|
|
2340
|
+
let raw = localStorage.getItem(LEGACY_STORAGE_KEY);
|
|
2341
|
+
if (!raw) return null;
|
|
2342
|
+
let options = sanitizeOptions(JSON.parse(raw));
|
|
2343
|
+
if (!options) return null;
|
|
2344
|
+
let theme = createCustomTheme("Draft theme", options);
|
|
2345
|
+
return {
|
|
2346
|
+
active: theme.slug,
|
|
2347
|
+
custom: [theme],
|
|
2348
|
+
removed: []
|
|
2349
|
+
};
|
|
587
2350
|
}
|
|
588
2351
|
/**
|
|
589
|
-
* Restores
|
|
590
|
-
* studio reloads.
|
|
2352
|
+
* Restores the themer state from localStorage, so the user's themes survive
|
|
2353
|
+
* studio reloads. Falls back to the initial state when nothing usable is
|
|
2354
|
+
* stored.
|
|
591
2355
|
*
|
|
592
2356
|
* @internal
|
|
593
2357
|
*/
|
|
594
|
-
function
|
|
2358
|
+
function readStoredState() {
|
|
595
2359
|
try {
|
|
596
|
-
if (typeof localStorage > "u") return
|
|
2360
|
+
if (typeof localStorage > "u") return initialThemerState;
|
|
597
2361
|
let raw = localStorage.getItem(STORAGE_KEY);
|
|
598
|
-
return raw ?
|
|
2362
|
+
return raw ? sanitizeState(JSON.parse(raw)) : readLegacyState() ?? initialThemerState;
|
|
599
2363
|
} catch {
|
|
600
|
-
return
|
|
2364
|
+
return initialThemerState;
|
|
601
2365
|
}
|
|
602
2366
|
}
|
|
603
2367
|
/** @internal */
|
|
604
|
-
function
|
|
2368
|
+
function writeStoredState(state) {
|
|
605
2369
|
try {
|
|
606
2370
|
if (typeof localStorage > "u") return;
|
|
607
|
-
|
|
2371
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(state)), localStorage.removeItem(LEGACY_STORAGE_KEY);
|
|
608
2372
|
} catch {}
|
|
609
2373
|
}
|
|
2374
|
+
function sameStoredState(a, b) {
|
|
2375
|
+
return a.active === b.active && a.custom === b.custom && a.removed === b.removed;
|
|
2376
|
+
}
|
|
2377
|
+
function selectView(snapshot) {
|
|
2378
|
+
let { editing } = snapshot.context;
|
|
2379
|
+
return snapshot.matches({ flow: "edit" }) && editing ? {
|
|
2380
|
+
name: "edit",
|
|
2381
|
+
slug: editing.slug,
|
|
2382
|
+
focusTitle: editing.focusTitle
|
|
2383
|
+
} : snapshot.matches({ flow: "removed" }) ? { name: "removed" } : { name: "list" };
|
|
2384
|
+
}
|
|
2385
|
+
function sameView(a, b) {
|
|
2386
|
+
return a.name === b.name ? a.name !== "edit" || b.name !== "edit" || a.slug === b.slug && a.focusTitle === b.focusTitle : !1;
|
|
2387
|
+
}
|
|
610
2388
|
/**
|
|
611
|
-
* Wraps the whole Studio so that
|
|
612
|
-
*
|
|
613
|
-
* navbar toggle and the sidebar share
|
|
2389
|
+
* Wraps the whole Studio so that the theme picked in the themer sidebar
|
|
2390
|
+
* applies everywhere while the user browses around, and runs the themer
|
|
2391
|
+
* machine that the navbar toggle and the sidebar share: the user's themes,
|
|
2392
|
+
* which one is applied, and which flow the sidebar is in.
|
|
614
2393
|
*
|
|
615
|
-
* The
|
|
2394
|
+
* The theme provider inherits the color scheme from the Studio, so the
|
|
616
2395
|
* preview follows the appearance setting (light/dark/system) like any other
|
|
617
2396
|
* theme.
|
|
618
2397
|
*
|
|
619
2398
|
* @internal
|
|
620
2399
|
*/
|
|
621
2400
|
function ThemerLayout(props) {
|
|
622
|
-
let $ = c(
|
|
623
|
-
$[0] ===
|
|
624
|
-
let t2;
|
|
625
|
-
$[3] === options ? t2 = $[4] : (t2 = options === null ? null : buildTheme(options), $[3] = options, $[4] = t2);
|
|
626
|
-
let theme = t2, t3;
|
|
627
|
-
$[5] !== baseOptions || $[6] !== open || $[7] !== options ? (t3 = {
|
|
2401
|
+
let $ = c(38), { baseOptions, ...layoutProps } = props, t0;
|
|
2402
|
+
$[0] === baseOptions ? t0 = $[1] : (t0 = () => ({
|
|
628
2403
|
baseOptions,
|
|
629
|
-
|
|
630
|
-
|
|
2404
|
+
stored: readStoredState()
|
|
2405
|
+
}), $[0] = baseOptions, $[1] = t0);
|
|
2406
|
+
let [input] = useState(t0), t1;
|
|
2407
|
+
$[2] === input ? t1 = $[3] : (t1 = { input }, $[2] = input, $[3] = t1);
|
|
2408
|
+
let [snapshot, send, actorRef] = useActor(themerMachine, t1), stored = useSelector(actorRef, selectStoredState, sameStoredState), view = useSelector(actorRef, selectView, sameView), t2;
|
|
2409
|
+
$[4] === snapshot ? t2 = $[5] : (t2 = snapshot.matches({ sidebar: "open" }), $[4] = snapshot, $[5] = t2);
|
|
2410
|
+
let open = t2, { images } = snapshot.context, t3, t4;
|
|
2411
|
+
$[6] === stored ? (t3 = $[7], t4 = $[8]) : (t3 = () => writeStoredState(stored), t4 = [stored], $[6] = stored, $[7] = t3, $[8] = t4), useEffect(t3, t4);
|
|
2412
|
+
let previousImages = useRef(images), t5, t6;
|
|
2413
|
+
$[9] === images ? (t5 = $[10], t6 = $[11]) : (t5 = () => {
|
|
2414
|
+
let current = new Set(Object.values(images));
|
|
2415
|
+
for (let url of Object.values(previousImages.current)) current.has(url) || URL.revokeObjectURL(url);
|
|
2416
|
+
previousImages.current = images;
|
|
2417
|
+
}, t6 = [images], $[9] = images, $[10] = t5, $[11] = t6), useEffect(t5, t6);
|
|
2418
|
+
let t7, t8;
|
|
2419
|
+
$[12] === Symbol.for("react.memo_cache_sentinel") ? (t7 = () => () => {
|
|
2420
|
+
for (let url_0 of Object.values(previousImages.current)) URL.revokeObjectURL(url_0);
|
|
2421
|
+
}, t8 = [], $[12] = t7, $[13] = t8) : (t7 = $[12], t8 = $[13]), useEffect(t7, t8);
|
|
2422
|
+
let t9;
|
|
2423
|
+
$[14] !== baseOptions || $[15] !== stored ? (t9 = resolveThemes(stored, baseOptions), $[14] = baseOptions, $[15] = stored, $[16] = t9) : t9 = $[16];
|
|
2424
|
+
let { themes, removed, active } = t9, activeOptions = active.source === "config" ? null : active.options, t10;
|
|
2425
|
+
$[17] === activeOptions ? t10 = $[18] : (t10 = activeOptions === null ? null : buildTheme(activeOptions), $[17] = activeOptions, $[18] = t10);
|
|
2426
|
+
let theme = t10, scheme = useColorSchemeValue(), t11;
|
|
2427
|
+
$[19] !== scheme || $[20] !== theme?.v2?.color ? (t11 = () => {
|
|
2428
|
+
let background = theme?.v2?.color[scheme].default.bg;
|
|
2429
|
+
if (background === void 0) return;
|
|
2430
|
+
let { style } = document.body, previous = style.backgroundColor;
|
|
2431
|
+
return style.backgroundColor = background, () => {
|
|
2432
|
+
style.backgroundColor = previous;
|
|
2433
|
+
};
|
|
2434
|
+
}, $[19] = scheme, $[20] = theme?.v2?.color, $[21] = t11) : t11 = $[21];
|
|
2435
|
+
let t12;
|
|
2436
|
+
$[22] !== scheme || $[23] !== theme ? (t12 = [scheme, theme], $[22] = scheme, $[23] = theme, $[24] = t12) : t12 = $[24], useEffect(t11, t12);
|
|
2437
|
+
let t13;
|
|
2438
|
+
$[25] !== active || $[26] !== baseOptions || $[27] !== images || $[28] !== open || $[29] !== removed || $[30] !== send || $[31] !== themes || $[32] !== view ? (t13 = {
|
|
2439
|
+
baseOptions,
|
|
2440
|
+
themes,
|
|
2441
|
+
removed,
|
|
2442
|
+
active,
|
|
2443
|
+
images,
|
|
2444
|
+
view,
|
|
631
2445
|
open,
|
|
632
|
-
|
|
633
|
-
}, $[
|
|
634
|
-
let context =
|
|
2446
|
+
send
|
|
2447
|
+
}, $[25] = active, $[26] = baseOptions, $[27] = images, $[28] = open, $[29] = removed, $[30] = send, $[31] = themes, $[32] = view, $[33] = t13) : t13 = $[33];
|
|
2448
|
+
let context = t13, t14 = ThemerContext, t15 = theme === null ? layoutProps.renderDefault(layoutProps) : /* @__PURE__ */ jsx(ThemeProvider, {
|
|
635
2449
|
theme,
|
|
636
2450
|
children: layoutProps.renderDefault(layoutProps)
|
|
637
|
-
}),
|
|
638
|
-
return $[
|
|
2451
|
+
}), t16;
|
|
2452
|
+
return $[34] !== context || $[35] !== t14.Provider || $[36] !== t15 ? (t16 = /* @__PURE__ */ jsx(t14.Provider, {
|
|
639
2453
|
value: context,
|
|
640
|
-
children:
|
|
641
|
-
}), $[
|
|
2454
|
+
children: t15
|
|
2455
|
+
}), $[34] = context, $[35] = t14.Provider, $[36] = t15, $[37] = t16) : t16 = $[37], t16;
|
|
642
2456
|
}
|
|
643
2457
|
function ThemerNavbarButton() {
|
|
644
|
-
let $ = c(
|
|
2458
|
+
let $ = c(6), { open, send } = useThemer(), t0;
|
|
645
2459
|
$[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = /* @__PURE__ */ jsx(Text, {
|
|
646
2460
|
size: 1,
|
|
647
2461
|
children: "Themer"
|
|
648
2462
|
}), $[0] = t0) : t0 = $[0];
|
|
649
2463
|
let t1;
|
|
650
|
-
$[1]
|
|
2464
|
+
$[1] === send ? t1 = $[2] : (t1 = () => send({ type: "sidebar.toggle" }), $[1] = send, $[2] = t1);
|
|
651
2465
|
let t2;
|
|
652
|
-
return $[
|
|
2466
|
+
return $[3] !== open || $[4] !== t1 ? (t2 = /* @__PURE__ */ jsx(Tooltip, {
|
|
2467
|
+
animate: !0,
|
|
653
2468
|
content: t0,
|
|
654
2469
|
portal: !0,
|
|
655
2470
|
children: /* @__PURE__ */ jsx(Button, {
|
|
@@ -660,7 +2475,7 @@ function ThemerNavbarButton() {
|
|
|
660
2475
|
padding: 2,
|
|
661
2476
|
selected: open
|
|
662
2477
|
})
|
|
663
|
-
}), $[
|
|
2478
|
+
}), $[3] = open, $[4] = t1, $[5] = t2) : t2 = $[5], t2;
|
|
664
2479
|
}
|
|
665
2480
|
/**
|
|
666
2481
|
* Adds the toggle that opens and closes the themer sidebar to the Studio
|
|
@@ -670,12 +2485,12 @@ function ThemerNavbarButton() {
|
|
|
670
2485
|
* @internal
|
|
671
2486
|
*/
|
|
672
2487
|
function ThemerNavbar(props) {
|
|
673
|
-
let $ = c(
|
|
2488
|
+
let $ = c(9), { open, send } = useThemer(), t0;
|
|
674
2489
|
$[0] === props.__internal_actions ? t0 = $[1] : (t0 = props.__internal_actions ?? [], $[0] = props.__internal_actions, $[1] = t0);
|
|
675
2490
|
let t1;
|
|
676
|
-
if ($[2] !== open || $[3] !== props || $[4] !==
|
|
2491
|
+
if ($[2] !== open || $[3] !== props || $[4] !== send || $[5] !== t0) {
|
|
677
2492
|
let t2;
|
|
678
|
-
$[7]
|
|
2493
|
+
$[7] === send ? t2 = $[8] : (t2 = () => send({ type: "sidebar.toggle" }), $[7] = send, $[8] = t2), t1 = props.renderDefault({
|
|
679
2494
|
...props,
|
|
680
2495
|
__internal_actions: [
|
|
681
2496
|
...t0,
|
|
@@ -693,7 +2508,7 @@ function ThemerNavbar(props) {
|
|
|
693
2508
|
title: "Themer"
|
|
694
2509
|
}
|
|
695
2510
|
]
|
|
696
|
-
}), $[2] = open, $[3] = props, $[4] =
|
|
2511
|
+
}), $[2] = open, $[3] = props, $[4] = send, $[5] = t0, $[6] = t1;
|
|
697
2512
|
} else t1 = $[6];
|
|
698
2513
|
return t1;
|
|
699
2514
|
}
|
|
@@ -702,11 +2517,15 @@ function _temp() {
|
|
|
702
2517
|
}
|
|
703
2518
|
/**
|
|
704
2519
|
* A Studio plugin that adds a themer sidebar for `buildTheme` themes: a
|
|
705
|
-
* navbar toggle opens the sidebar next to the active tool,
|
|
706
|
-
*
|
|
707
|
-
*
|
|
708
|
-
*
|
|
709
|
-
*
|
|
2520
|
+
* navbar toggle opens the sidebar next to the active tool, with a list of
|
|
2521
|
+
* themes — the configured theme, the presets and your own — each previewed
|
|
2522
|
+
* as a tiny Studio in both color schemes. Picking one applies it live to the
|
|
2523
|
+
* whole Studio while you browse around; your own themes can be edited with
|
|
2524
|
+
* accent/text/background pickers and a contrast slider per scheme, added,
|
|
2525
|
+
* duplicated, given the colors of an image (its palette is read on device),
|
|
2526
|
+
* removed and restored, and a dialog shows the `buildTheme` snippet that
|
|
2527
|
+
* makes the applied theme permanent. Toggle between light and dark mode with
|
|
2528
|
+
* the regular appearance menu — the preview follows it.
|
|
710
2529
|
*
|
|
711
2530
|
* ```ts
|
|
712
2531
|
* import {themerTool} from '@sanity/themer/tool'
|
|
@@ -724,7 +2543,7 @@ function _temp() {
|
|
|
724
2543
|
* @alpha
|
|
725
2544
|
*/
|
|
726
2545
|
const themerTool = definePlugin((options) => {
|
|
727
|
-
let baseOptions = options?.config ?? {
|
|
2546
|
+
let baseOptions = options?.config ?? {};
|
|
728
2547
|
function ThemerLayoutWithOptions(props) {
|
|
729
2548
|
let $ = c(2), t0;
|
|
730
2549
|
return $[0] === props ? t0 = $[1] : (t0 = /* @__PURE__ */ jsx(ThemerLayout, {
|