@sanity/themer 0.7.1 → 0.8.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.
@@ -0,0 +1,2050 @@
1
+ import { c as resolveThemeOptions, l as isColor, n as buildTheme, o as SCHEMES, r as buildPalette } from "./presets-CqBgS3A-.js";
2
+ import { _ as titleFromFileName, c as minimizeOptions, d as IMAGE_PALETTE_VARIANTS, f as applyImagePalette, g as pickLuckyVariant, h as optionsFromImagePalette, i as displayTitle, m as extractImagePalette, p as currentImageVariant, u as IMAGE_PALETTE_TITLES, y as useThemer } from "./ResizableSidebar.css.ts.vanilla-M9EuPu-q.js";
3
+ import { c } from "react/compiler-runtime";
4
+ import { useColorSchemeValue } from "sanity";
5
+ import { Badge, Box, Button, Card, Dialog, Flex, Layer, Stack, Text, TextInput, ThemeProvider, useRootTheme, useTheme_v2 } from "@sanity/ui";
6
+ import { createContext, useContext, useEffect, useRef, useState } from "react";
7
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ import { Tooltip } from "@sanity/ui/tooltip";
9
+ import { MotionConfig, Reorder, useDragControls } from "motion/react";
10
+ import { ArrowLeftIcon } from "@sanity/icons/ArrowLeft";
11
+ import { CloseIcon } from "@sanity/icons/Close";
12
+ import { CodeBlockIcon } from "@sanity/icons/CodeBlock";
13
+ import { SplitVerticalIcon } from "@sanity/icons/SplitVertical";
14
+ import { assignInlineVars } from "@vanilla-extract/dynamic";
15
+ import { ResetIcon } from "@sanity/icons/Reset";
16
+ import { TrashIcon } from "@sanity/icons/Trash";
17
+ import { SparklesIcon } from "@sanity/icons/Sparkles";
18
+ import { ImageIcon } from "@sanity/icons/Image";
19
+ import { useToast } from "@sanity/ui/toast";
20
+ import { AddIcon } from "@sanity/icons/Add";
21
+ import { ClipboardIcon } from "@sanity/icons/Clipboard";
22
+ import { RestoreIcon } from "@sanity/icons/Restore";
23
+ import { ArrowDownIcon } from "@sanity/icons/ArrowDown";
24
+ import { ArrowUpIcon } from "@sanity/icons/ArrowUp";
25
+ import { CopyIcon } from "@sanity/icons/Copy";
26
+ import { EditIcon } from "@sanity/icons/Edit";
27
+ import { EllipsisHorizontalIcon } from "@sanity/icons/EllipsisHorizontal";
28
+ import { ShareIcon } from "@sanity/icons/Share";
29
+ import { Menu, MenuButton, MenuDivider, MenuItem } from "@sanity/ui/menu";
30
+ import scrollIntoView from "scroll-into-view-if-needed";
31
+ import { Code } from "@sanity/ui/code";
32
+ import { registerLanguage } from "react-refractor";
33
+ import typescript from "refractor/typescript";
34
+ var root$2 = "_1o50s7t0";
35
+ const ScrollAreaInsetContext = createContext({
36
+ left: 0,
37
+ right: 0,
38
+ bottom: 0
39
+ });
40
+ /**
41
+ * The gutter the last scroll area measured. It is the same for every scroll
42
+ * area (the reserved gutter does not depend on the content), so the next one
43
+ * starts out with the right padding instead of shifting after its first
44
+ * measurement — which would also throw off anything scrolling into view as
45
+ * the content mounts.
46
+ */
47
+ let lastGutter = 0;
48
+ /**
49
+ * The scrolling body of a sidebar flow. It always reserves the scrollbar's
50
+ * space, so the content does not shift when the list grows or shrinks past
51
+ * the point where it needs to scroll — and it takes that space out of its
52
+ * right padding, so the content stays centered rather than hugging the left.
53
+ * With overlay scrollbars there is no gutter and the padding stays even.
54
+ *
55
+ * @internal
56
+ */
57
+ function ScrollArea(props) {
58
+ let $ = c(15), { children, padding } = props, { space } = useTheme_v2(), ref = useRef(null), [gutter, setGutter] = useState(_temp$6), t0, t1;
59
+ $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = () => {
60
+ let element = ref.current;
61
+ if (!element) return;
62
+ let measure = () => {
63
+ lastGutter = element.offsetWidth - element.clientWidth, setGutter(lastGutter);
64
+ };
65
+ measure();
66
+ let observer = new ResizeObserver(measure);
67
+ return observer.observe(element), () => observer.disconnect();
68
+ }, t1 = [], $[0] = t0, $[1] = t1) : (t0 = $[0], t1 = $[1]), useEffect(t0, t1);
69
+ let t2 = space[padding], t3 = Math.max(0, space[padding] - gutter), t4 = space[padding], t5;
70
+ $[2] !== t2 || $[3] !== t3 || $[4] !== t4 ? (t5 = {
71
+ left: t2,
72
+ right: t3,
73
+ bottom: t4
74
+ }, $[2] = t2, $[3] = t3, $[4] = t4, $[5] = t5) : t5 = $[5];
75
+ let inset = t5, t6;
76
+ $[6] === inset.right ? t6 = $[7] : (t6 = { paddingRight: inset.right }, $[6] = inset.right, $[7] = t6);
77
+ let t7;
78
+ $[8] !== children || $[9] !== inset ? (t7 = /* @__PURE__ */ jsx(ScrollAreaInsetContext.Provider, {
79
+ value: inset,
80
+ children
81
+ }), $[8] = children, $[9] = inset, $[10] = t7) : t7 = $[10];
82
+ let t8;
83
+ return $[11] !== padding || $[12] !== t6 || $[13] !== t7 ? (t8 = /* @__PURE__ */ jsx(Box, {
84
+ className: root$2,
85
+ flex: 1,
86
+ overflow: "auto",
87
+ padding,
88
+ ref,
89
+ style: t6,
90
+ children: t7
91
+ }), $[11] = padding, $[12] = t6, $[13] = t7, $[14] = t8) : t8 = $[14], t8;
92
+ }
93
+ function _temp$6() {
94
+ return lastGutter;
95
+ }
96
+ /**
97
+ * Content that runs edge to edge in a scroll area — up to the scrollbar —
98
+ * instead of sitting inside its padding. It goes last: it runs down to the
99
+ * bottom edge too.
100
+ *
101
+ * @internal
102
+ */
103
+ function ScrollAreaBleed(props) {
104
+ let $ = c(7), { left, right, bottom } = useContext(ScrollAreaInsetContext), t0 = -left, t1 = -right, t2 = -bottom, t3;
105
+ $[0] !== t0 || $[1] !== t1 || $[2] !== t2 ? (t3 = {
106
+ marginLeft: t0,
107
+ marginRight: t1,
108
+ marginBottom: t2
109
+ }, $[0] = t0, $[1] = t1, $[2] = t2, $[3] = t3) : t3 = $[3];
110
+ let t4;
111
+ return $[4] !== props.children || $[5] !== t3 ? (t4 = /* @__PURE__ */ jsx("div", {
112
+ style: t3,
113
+ children: props.children
114
+ }), $[4] = props.children, $[5] = t3, $[6] = t4) : t4 = $[6], t4;
115
+ }
116
+ var avatar = "_1aa9huuh", body = "_1aa9huui", button = "_1aa9huuq", colors = {
117
+ bg: "var(--_1aa9huu0)",
118
+ fg: "var(--_1aa9huu1)",
119
+ mutedFg: "var(--_1aa9huu2)",
120
+ border: "var(--_1aa9huu3)",
121
+ accent: "var(--_1aa9huu4)",
122
+ avatar: "var(--_1aa9huu5)",
123
+ selectedBg: "var(--_1aa9huu6)",
124
+ selectedFg: "var(--_1aa9huu7)",
125
+ inputBg: "var(--_1aa9huu8)",
126
+ inputBorder: "var(--_1aa9huu9)"
127
+ }, field = "_1aa9huun", footer = "_1aa9huup", form = "_1aa9huum", input = "_1aa9huuo", line = {
128
+ fg: "_1aa9huus _1aa9huur",
129
+ muted: "_1aa9huut _1aa9huur",
130
+ selected: "_1aa9huuu _1aa9huur"
131
+ }, list = "_1aa9huuj", listRow = "_1aa9huuk", logo = "_1aa9huuf", navbar = "_1aa9huue", pane = "_1aa9huul", root$1 = "_1aa9huua", spacer = "_1aa9huug", studio = {
132
+ light: "_1aa9huub",
133
+ dark: "_1aa9huud _1aa9huub"
134
+ };
135
+ /** The document rows in the list pane — the widths of their title lines */
136
+ const LIST_ROWS = [
137
+ "62%",
138
+ "46%",
139
+ "70%",
140
+ "52%"
141
+ ], FIELDS$1 = ["32%", "24%"];
142
+ /**
143
+ * A tiny, lo-fi mockup of a Studio in the given theme — the navbar over the
144
+ * structure tool with a list of documents on the left and a document form on
145
+ * the right — in both color schemes at once: the light scheme on the left and
146
+ * the dark scheme on the right, split diagonally. It only paints colors the
147
+ * theme options change (backgrounds, text, borders, the accent) so that a
148
+ * glance shows what the theme does to the Studio — with few and bold enough
149
+ * elements to still read when scaled down to a thumbnail.
150
+ *
151
+ * @internal
152
+ */
153
+ function ThemePreview(props) {
154
+ let $ = c(11), { options } = props, t0;
155
+ $[0] === options ? t0 = $[1] : (t0 = buildTheme(options), $[0] = options, $[1] = t0);
156
+ let theme = t0, t1;
157
+ $[2] === Symbol.for("react.memo_cache_sentinel") ? (t1 = /* @__PURE__ */ jsx(MockStudio, { scheme: "light" }), $[2] = t1) : t1 = $[2];
158
+ let t2;
159
+ $[3] === theme ? t2 = $[4] : (t2 = /* @__PURE__ */ jsx(ThemeProvider, {
160
+ scheme: "light",
161
+ theme,
162
+ tone: "default",
163
+ children: t1
164
+ }), $[3] = theme, $[4] = t2);
165
+ let t3;
166
+ $[5] === Symbol.for("react.memo_cache_sentinel") ? (t3 = /* @__PURE__ */ jsx(MockStudio, { scheme: "dark" }), $[5] = t3) : t3 = $[5];
167
+ let t4;
168
+ $[6] === theme ? t4 = $[7] : (t4 = /* @__PURE__ */ jsx(ThemeProvider, {
169
+ scheme: "dark",
170
+ theme,
171
+ tone: "default",
172
+ children: t3
173
+ }), $[6] = theme, $[7] = t4);
174
+ let t5;
175
+ return $[8] !== t2 || $[9] !== t4 ? (t5 = /* @__PURE__ */ jsxs("div", {
176
+ "aria-hidden": !0,
177
+ className: root$1,
178
+ children: [t2, t4]
179
+ }), $[8] = t2, $[9] = t4, $[10] = t5) : t5 = $[10], t5;
180
+ }
181
+ function MockStudio(props) {
182
+ let $ = c(19), { scheme } = props, { color } = useTheme_v2(), selected = color.selectable.default.selected, inputColor = color.input.default.enabled, t0 = studio[scheme], t1;
183
+ $[0] !== color.avatar.blue.bg || $[1] !== color.bg || $[2] !== color.border || $[3] !== color.button.default.primary.enabled.bg || $[4] !== color.fg || $[5] !== color.muted.fg || $[6] !== inputColor.bg || $[7] !== inputColor.border || $[8] !== selected.bg || $[9] !== selected.fg ? (t1 = assignInlineVars({
184
+ [colors.bg]: color.bg,
185
+ [colors.fg]: color.fg,
186
+ [colors.mutedFg]: color.muted.fg,
187
+ [colors.border]: color.border,
188
+ [colors.accent]: color.button.default.primary.enabled.bg,
189
+ [colors.avatar]: color.avatar.blue.bg,
190
+ [colors.selectedBg]: selected.bg,
191
+ [colors.selectedFg]: selected.fg,
192
+ [colors.inputBg]: inputColor.bg,
193
+ [colors.inputBorder]: inputColor.border
194
+ }), $[0] = color.avatar.blue.bg, $[1] = color.bg, $[2] = color.border, $[3] = color.button.default.primary.enabled.bg, $[4] = color.fg, $[5] = color.muted.fg, $[6] = inputColor.bg, $[7] = inputColor.border, $[8] = selected.bg, $[9] = selected.fg, $[10] = t1) : t1 = $[10];
195
+ let t2;
196
+ $[11] === Symbol.for("react.memo_cache_sentinel") ? (t2 = /* @__PURE__ */ jsx("div", { className: logo }), $[11] = t2) : t2 = $[11];
197
+ let t3;
198
+ $[12] === Symbol.for("react.memo_cache_sentinel") ? (t3 = /* @__PURE__ */ jsxs("div", {
199
+ className: navbar,
200
+ children: [
201
+ t2,
202
+ /* @__PURE__ */ jsx("div", {
203
+ className: line.fg,
204
+ style: { width: "22%" }
205
+ }),
206
+ /* @__PURE__ */ jsx("div", { className: spacer }),
207
+ /* @__PURE__ */ jsx("div", { className: avatar })
208
+ ]
209
+ }), $[12] = t3) : t3 = $[12];
210
+ let t4;
211
+ $[13] === Symbol.for("react.memo_cache_sentinel") ? (t4 = /* @__PURE__ */ jsx("div", {
212
+ className: list,
213
+ children: LIST_ROWS.map(_temp$5)
214
+ }), $[13] = t4) : t4 = $[13];
215
+ let t5;
216
+ $[14] === Symbol.for("react.memo_cache_sentinel") ? (t5 = /* @__PURE__ */ jsxs("div", {
217
+ className: form,
218
+ children: [/* @__PURE__ */ jsx("div", {
219
+ className: line.fg,
220
+ style: { width: "45%" }
221
+ }), FIELDS$1.map(_temp2)]
222
+ }), $[14] = t5) : t5 = $[14];
223
+ let t6;
224
+ $[15] === Symbol.for("react.memo_cache_sentinel") ? (t6 = /* @__PURE__ */ jsxs("div", {
225
+ className: body,
226
+ children: [t4, /* @__PURE__ */ jsxs("div", {
227
+ className: pane,
228
+ children: [t5, /* @__PURE__ */ jsx("div", {
229
+ className: footer,
230
+ children: /* @__PURE__ */ jsx("div", { className: button })
231
+ })]
232
+ })]
233
+ }), $[15] = t6) : t6 = $[15];
234
+ let t7;
235
+ return $[16] !== t0 || $[17] !== t1 ? (t7 = /* @__PURE__ */ jsxs("div", {
236
+ className: t0,
237
+ style: t1,
238
+ children: [t3, t6]
239
+ }), $[16] = t0, $[17] = t1, $[18] = t7) : t7 = $[18], t7;
240
+ }
241
+ function _temp2(width_0) {
242
+ return /* @__PURE__ */ jsxs("div", {
243
+ className: field,
244
+ children: [/* @__PURE__ */ jsx("div", {
245
+ className: line.muted,
246
+ style: { width: width_0 }
247
+ }), /* @__PURE__ */ jsx("div", { className: input })]
248
+ }, width_0);
249
+ }
250
+ function _temp$5(width, index) {
251
+ let isSelected = index === 1;
252
+ return /* @__PURE__ */ jsx("div", {
253
+ className: listRow,
254
+ "data-selected": isSelected,
255
+ children: /* @__PURE__ */ jsx("div", {
256
+ className: isSelected ? line.selected : line.muted,
257
+ style: { width }
258
+ })
259
+ }, width);
260
+ }
261
+ /**
262
+ * The floating preview card of a theme: the lo-fi studio mockup on a raised,
263
+ * rounded card, like the appearance previews in macOS System Settings.
264
+ *
265
+ * @internal
266
+ */
267
+ function ThemeThumbnail(props) {
268
+ let $ = c(2), t0;
269
+ return $[0] === props.options ? t0 = $[1] : (t0 = /* @__PURE__ */ jsx(Card, {
270
+ overflow: "hidden",
271
+ radius: 3,
272
+ shadow: 1,
273
+ children: /* @__PURE__ */ jsx(ThemePreview, { options: props.options })
274
+ }), $[0] = props.options, $[1] = t0), t0;
275
+ }
276
+ var removedThumbnail = "_1vdkp0v0";
277
+ /**
278
+ * The flow for restoring removed themes: the presets and custom themes the
279
+ * user took out of the list, each with a button to put it back — and, for
280
+ * custom themes, one to delete it for good. The machine leaves the flow once
281
+ * the last one is gone.
282
+ *
283
+ * @internal
284
+ */
285
+ function RemovedThemes() {
286
+ let $ = c(7), { removed, send } = useThemer(), t0;
287
+ if ($[0] !== removed || $[1] !== send) {
288
+ let t1;
289
+ $[3] === send ? t1 = $[4] : (t1 = (theme) => /* @__PURE__ */ jsxs(Stack, {
290
+ gap: 3,
291
+ children: [
292
+ /* @__PURE__ */ jsx(Box, {
293
+ className: removedThumbnail,
294
+ children: /* @__PURE__ */ jsx(ThemeThumbnail, { options: theme.options })
295
+ }),
296
+ /* @__PURE__ */ jsx(Text, {
297
+ align: "center",
298
+ size: 1,
299
+ textOverflow: "ellipsis",
300
+ children: displayTitle(theme.title)
301
+ }),
302
+ /* @__PURE__ */ jsxs(Flex, {
303
+ gap: 2,
304
+ justify: "center",
305
+ children: [/* @__PURE__ */ jsx(Button, {
306
+ mode: "ghost",
307
+ onClick: () => send({
308
+ type: "theme.restore",
309
+ slug: theme.slug
310
+ }),
311
+ padding: 2,
312
+ text: "Restore"
313
+ }), theme.source === "custom" && /* @__PURE__ */ jsx(Button, {
314
+ mode: "ghost",
315
+ onClick: () => send({
316
+ type: "theme.delete",
317
+ slug: theme.slug
318
+ }),
319
+ padding: 2,
320
+ text: "Delete",
321
+ tone: "critical"
322
+ })]
323
+ })
324
+ ]
325
+ }, theme.slug), $[3] = send, $[4] = t1), t0 = removed.map(t1), $[0] = removed, $[1] = send, $[2] = t0;
326
+ } else t0 = $[2];
327
+ let t1;
328
+ return $[5] === t0 ? t1 = $[6] : (t1 = /* @__PURE__ */ jsx(ScrollArea, {
329
+ padding: 3,
330
+ children: /* @__PURE__ */ jsx(Stack, {
331
+ gap: 5,
332
+ children: t0
333
+ })
334
+ }), $[5] = t0, $[6] = t1), t1;
335
+ }
336
+ /**
337
+ * A button with an animated tooltip instead of a `title` attribute, sized like
338
+ * the Studio's own buttons (`padding` and `gap` of 2). Icon-only buttons take
339
+ * the tooltip as their accessible name.
340
+ *
341
+ * @internal
342
+ */
343
+ function TooltipButton(props) {
344
+ let $ = c(11), buttonProps, tooltip;
345
+ $[0] === props ? (buttonProps = $[1], tooltip = $[2]) : ({tooltip, ...buttonProps} = props, $[0] = props, $[1] = buttonProps, $[2] = tooltip);
346
+ let t0;
347
+ $[3] === tooltip ? t0 = $[4] : (t0 = /* @__PURE__ */ jsx(Text, {
348
+ size: 1,
349
+ children: tooltip
350
+ }), $[3] = tooltip, $[4] = t0);
351
+ let t1 = buttonProps.text === void 0 ? tooltip : void 0, t2;
352
+ $[5] !== buttonProps || $[6] !== t1 ? (t2 = /* @__PURE__ */ jsx(Button, {
353
+ "aria-label": t1,
354
+ gap: 2,
355
+ padding: 2,
356
+ ...buttonProps
357
+ }), $[5] = buttonProps, $[6] = t1, $[7] = t2) : t2 = $[7];
358
+ let t3;
359
+ return $[8] !== t0 || $[9] !== t2 ? (t3 = /* @__PURE__ */ jsx(Tooltip, {
360
+ animate: !0,
361
+ content: t0,
362
+ placement: "bottom",
363
+ portal: !0,
364
+ children: t2
365
+ }), $[8] = t0, $[9] = t2, $[10] = t3) : t3 = $[10], t3;
366
+ }
367
+ /**
368
+ * A button that opens the file picker for an image, and hands the chosen file
369
+ * over — the picker is a hidden file input, so nothing is uploaded anywhere.
370
+ *
371
+ * @internal
372
+ */
373
+ function ImageFileButton(props) {
374
+ let $ = c(11), buttonProps, onFile;
375
+ $[0] === props ? (buttonProps = $[1], onFile = $[2]) : ({onFile, ...buttonProps} = props, $[0] = props, $[1] = buttonProps, $[2] = onFile);
376
+ let inputRef = useRef(null), t0;
377
+ $[3] === Symbol.for("react.memo_cache_sentinel") ? (t0 = () => inputRef.current?.click(), $[3] = t0) : t0 = $[3];
378
+ let t1;
379
+ $[4] === buttonProps ? t1 = $[5] : (t1 = /* @__PURE__ */ jsx(TooltipButton, {
380
+ ...buttonProps,
381
+ icon: ImageIcon,
382
+ onClick: t0
383
+ }), $[4] = buttonProps, $[5] = t1);
384
+ let t2;
385
+ $[6] === onFile ? t2 = $[7] : (t2 = /* @__PURE__ */ jsx("input", {
386
+ accept: "image/*",
387
+ hidden: !0,
388
+ onChange: (event) => {
389
+ let file = event.currentTarget.files?.[0];
390
+ event.currentTarget.value = "", file && onFile(file);
391
+ },
392
+ ref: inputRef,
393
+ type: "file"
394
+ }), $[6] = onFile, $[7] = t2);
395
+ let t3;
396
+ return $[8] !== t1 || $[9] !== t2 ? (t3 = /* @__PURE__ */ jsxs(Fragment, { children: [t1, t2] }), $[8] = t1, $[9] = t2, $[10] = t3) : t3 = $[10], t3;
397
+ }
398
+ /**
399
+ * Reads the pixels of an image file on device: the image is decoded, drawn
400
+ * onto a small canvas and read back, so nothing leaves the browser.
401
+ *
402
+ * @internal
403
+ */
404
+ async function readImagePixels(file) {
405
+ let image = await decodeImage(file);
406
+ try {
407
+ 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");
408
+ canvas.width = width, canvas.height = height;
409
+ let context = canvas.getContext("2d", { willReadFrequently: !0 });
410
+ if (!context) throw Error("Could not draw the image");
411
+ return context.drawImage(image, 0, 0, width, height), {
412
+ data: context.getImageData(0, 0, width, height).data,
413
+ width,
414
+ height
415
+ };
416
+ } finally {
417
+ typeof ImageBitmap < "u" && image instanceof ImageBitmap && image.close();
418
+ }
419
+ }
420
+ async function decodeImage(file) {
421
+ if (typeof createImageBitmap == "function") try {
422
+ return await createImageBitmap(file);
423
+ } catch {}
424
+ let url = URL.createObjectURL(file);
425
+ try {
426
+ return await new Promise((resolve, reject) => {
427
+ let image = new Image();
428
+ image.addEventListener("load", () => resolve(image), { once: !0 }), image.addEventListener("error", () => reject(/* @__PURE__ */ Error("Could not decode the image")), { once: !0 }), image.src = url;
429
+ });
430
+ } finally {
431
+ URL.revokeObjectURL(url);
432
+ }
433
+ }
434
+ /**
435
+ * Extracts the palette of image files on device and hands it to `onPalette`,
436
+ * along with the file it came from. Failures end up as a toast.
437
+ *
438
+ * @internal
439
+ */
440
+ function useImagePalette(onPalette) {
441
+ let $ = c(10), toast = useToast(), [busy, setBusy] = useState(!1), onPaletteRef = useRef(onPalette), mountedRef = useRef(!0), latestPickRef = useRef(0), t0, t1;
442
+ $[0] === onPalette ? (t0 = $[1], t1 = $[2]) : (t0 = () => {
443
+ onPaletteRef.current = onPalette;
444
+ }, t1 = [onPalette], $[0] = onPalette, $[1] = t0, $[2] = t1), useEffect(t0, t1);
445
+ let t2, t3;
446
+ $[3] === Symbol.for("react.memo_cache_sentinel") ? (t2 = () => (mountedRef.current = !0, () => {
447
+ mountedRef.current = !1;
448
+ }), t3 = [], $[3] = t2, $[4] = t3) : (t2 = $[3], t3 = $[4]), useEffect(t2, t3);
449
+ let t4;
450
+ $[5] === toast ? t4 = $[6] : (t4 = (file) => {
451
+ let pick = latestPickRef.current += 1;
452
+ setBusy(!0);
453
+ let isCurrent = () => mountedRef.current && latestPickRef.current === pick, extract = async () => {
454
+ let palette = extractImagePalette(await readImagePixels(file));
455
+ isCurrent() && (Object.values(palette).every(_temp$4) ? toast.push({
456
+ status: "warning",
457
+ title: "Found no colors in that image"
458
+ }) : onPaletteRef.current(palette, file));
459
+ };
460
+ extract().catch(() => {
461
+ isCurrent() && toast.push({
462
+ status: "error",
463
+ title: "Could not read that image"
464
+ });
465
+ }).finally(() => {
466
+ isCurrent() && setBusy(!1);
467
+ });
468
+ }, $[5] = toast, $[6] = t4);
469
+ let pickImage = t4, t5;
470
+ return $[7] !== busy || $[8] !== pickImage ? (t5 = {
471
+ busy,
472
+ pickImage
473
+ }, $[7] = busy, $[8] = pickImage, $[9] = t5) : t5 = $[9], t5;
474
+ }
475
+ function _temp$4(swatch) {
476
+ return swatch === null;
477
+ }
478
+ var paletteGrid = "_17s80v42", tile = "_17s80v44", variantButton = "_17s80v46", variantFrame = "_17s80v47", variantRow = "_17s80v45", variantScale = "var(--_17s80v41)", variantScaleBox = "_17s80v48", variantWidth = "var(--_17s80v40)";
479
+ /** The swatches on display: the vibrant family over the muted one */
480
+ const TILES = [
481
+ "vibrant",
482
+ "lightVibrant",
483
+ "darkVibrant",
484
+ "muted",
485
+ "lightMuted",
486
+ "darkMuted"
487
+ ];
488
+ /**
489
+ * Takes the colors of a theme from an image, on device: the image is read
490
+ * into a canvas and its palette extracted right there — nothing is uploaded.
491
+ * Shows the image (while the session lasts) next to its palette, offers a
492
+ * theme variant built around each swatch as a small preview, and a lucky
493
+ * button that picks an interesting one.
494
+ *
495
+ * @internal
496
+ */
497
+ function ImagePaletteSection(props) {
498
+ let $ = c(38), { imageUrl, onPalette, onVariant, onLucky, options, palette } = props, { busy, pickImage } = useImagePalette(onPalette), [dragging, setDragging] = useState(!1), dragDepth = useRef(0), t0;
499
+ $[0] !== options || $[1] !== palette ? (t0 = palette ? currentImageVariant(options, palette) : null, $[0] = options, $[1] = palette, $[2] = t0) : t0 = $[2];
500
+ let current = t0, rowRef = useRef(null), [thumbnailWidth, setThumbnailWidth] = useState(58), t1;
501
+ $[3] === Symbol.for("react.memo_cache_sentinel") ? (t1 = () => {
502
+ let row = rowRef.current;
503
+ if (!row) return;
504
+ let measure = () => {
505
+ let visible = row.clientWidth - 12 - 16;
506
+ setThumbnailWidth(Math.max(24, Math.floor(visible / 2.5) - 8));
507
+ };
508
+ measure();
509
+ let observer = new ResizeObserver(measure);
510
+ return observer.observe(row), () => observer.disconnect();
511
+ }, $[3] = t1) : t1 = $[3];
512
+ let t2;
513
+ $[4] === palette ? t2 = $[5] : (t2 = [palette], $[4] = palette, $[5] = t2), useEffect(t1, t2);
514
+ let t3;
515
+ bb0: {
516
+ if (!palette) {
517
+ let t4;
518
+ $[6] === Symbol.for("react.memo_cache_sentinel") ? (t4 = [], $[6] = t4) : t4 = $[6], t3 = t4;
519
+ break bb0;
520
+ }
521
+ let t4;
522
+ $[7] !== options || $[8] !== palette ? (t4 = IMAGE_PALETTE_VARIANTS.flatMap((variant) => palette[variant] === null ? [] : [{
523
+ variant,
524
+ title: IMAGE_PALETTE_TITLES[variant],
525
+ options: applyImagePalette(options, palette, variant)
526
+ }]), $[7] = options, $[8] = palette, $[9] = t4) : t4 = $[9], t3 = t4;
527
+ }
528
+ let variants = t3, t4, t5;
529
+ $[10] === Symbol.for("react.memo_cache_sentinel") ? (t4 = (event) => {
530
+ event.preventDefault(), dragDepth.current += 1, setDragging(!0);
531
+ }, t5 = () => {
532
+ dragDepth.current = Math.max(0, dragDepth.current - 1), dragDepth.current === 0 && setDragging(!1);
533
+ }, $[10] = t4, $[11] = t5) : (t4 = $[10], t5 = $[11]);
534
+ let t6;
535
+ $[12] === pickImage ? t6 = $[13] : (t6 = (event_1) => {
536
+ event_1.preventDefault(), dragDepth.current = 0, setDragging(!1);
537
+ let file = event_1.dataTransfer.files[0];
538
+ file && pickImage(file);
539
+ }, $[12] = pickImage, $[13] = t6);
540
+ let t7 = dragging ? "primary" : "default", t8;
541
+ $[14] === Symbol.for("react.memo_cache_sentinel") ? (t8 = /* @__PURE__ */ jsx(Box, {
542
+ flex: 1,
543
+ children: /* @__PURE__ */ jsx(Text, {
544
+ size: 1,
545
+ weight: "medium",
546
+ children: "Image palette"
547
+ })
548
+ }), $[14] = t8) : t8 = $[14];
549
+ let t9;
550
+ $[15] !== busy || $[16] !== palette || $[17] !== pickImage ? (t9 = palette && /* @__PURE__ */ jsx(ImageFileButton, {
551
+ loading: busy,
552
+ mode: "bleed",
553
+ onFile: pickImage,
554
+ padding: 2,
555
+ tooltip: "Pick another image"
556
+ }), $[15] = busy, $[16] = palette, $[17] = pickImage, $[18] = t9) : t9 = $[18];
557
+ let t10;
558
+ $[19] === t9 ? t10 = $[20] : (t10 = /* @__PURE__ */ jsxs(Flex, {
559
+ align: "center",
560
+ gap: 2,
561
+ children: [t8, t9]
562
+ }), $[19] = t9, $[20] = t10);
563
+ let t11;
564
+ $[21] !== busy || $[22] !== current || $[23] !== imageUrl || $[24] !== onLucky || $[25] !== onVariant || $[26] !== palette || $[27] !== pickImage || $[28] !== thumbnailWidth || $[29] !== variants ? (t11 = palette ? /* @__PURE__ */ jsxs(Fragment, { children: [
565
+ /* @__PURE__ */ jsxs("div", {
566
+ className: paletteGrid,
567
+ "data-with-image": imageUrl !== void 0,
568
+ children: [imageUrl && /* @__PURE__ */ jsx("img", {
569
+ alt: "",
570
+ className: "_17s80v43",
571
+ src: imageUrl
572
+ }), TILES.map((key) => {
573
+ let hex = palette[key], title = IMAGE_PALETTE_TITLES[key];
574
+ return /* @__PURE__ */ jsx(Tooltip, {
575
+ animate: !0,
576
+ content: /* @__PURE__ */ jsx(Text, {
577
+ size: 1,
578
+ children: hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`
579
+ }),
580
+ placement: "bottom",
581
+ portal: !0,
582
+ children: /* @__PURE__ */ jsx("span", {
583
+ className: tile,
584
+ "data-empty": hex === null,
585
+ style: { background: hex ?? void 0 }
586
+ })
587
+ }, key);
588
+ })]
589
+ }),
590
+ /* @__PURE__ */ jsxs(Stack, {
591
+ gap: 2,
592
+ children: [/* @__PURE__ */ jsx(Text, {
593
+ muted: !0,
594
+ size: 0,
595
+ children: "Pick the swatch to build the theme around"
596
+ }), /* @__PURE__ */ jsx("div", {
597
+ className: variantRow,
598
+ ref: rowRef,
599
+ style: assignInlineVars({
600
+ [variantWidth]: `${thumbnailWidth}px`,
601
+ [variantScale]: String(thumbnailWidth / 144)
602
+ }),
603
+ children: variants.map((t12) => {
604
+ let { variant: variant_0, title: title_0, options: variantOptions } = t12;
605
+ return /* @__PURE__ */ jsxs("button", {
606
+ "aria-pressed": variant_0 === current,
607
+ className: variantButton,
608
+ onClick: () => onVariant(variant_0),
609
+ type: "button",
610
+ children: [/* @__PURE__ */ jsx("span", {
611
+ className: variantFrame,
612
+ children: /* @__PURE__ */ jsx("span", {
613
+ className: variantScaleBox,
614
+ children: /* @__PURE__ */ jsx(ThemeThumbnail, { options: variantOptions })
615
+ })
616
+ }), /* @__PURE__ */ jsx(Box, {
617
+ as: "span",
618
+ display: "block",
619
+ paddingTop: 1,
620
+ children: /* @__PURE__ */ jsx(Text, {
621
+ align: "center",
622
+ as: "span",
623
+ size: 0,
624
+ textOverflow: "ellipsis",
625
+ weight: variant_0 === current ? "medium" : "regular",
626
+ children: title_0
627
+ })
628
+ })]
629
+ }, variant_0);
630
+ })
631
+ })]
632
+ }),
633
+ /* @__PURE__ */ jsx(TooltipButton, {
634
+ icon: SparklesIcon,
635
+ mode: "ghost",
636
+ onClick: onLucky,
637
+ text: "I'm feeling lucky",
638
+ tooltip: "Build the theme around a swatch that looks interesting",
639
+ width: "fill"
640
+ })
641
+ ] }) : /* @__PURE__ */ jsxs(Stack, {
642
+ gap: 3,
643
+ children: [/* @__PURE__ */ jsx(Text, {
644
+ muted: !0,
645
+ size: 0,
646
+ children: "Take the accent, text and background colors from an image — read on device, nothing is uploaded."
647
+ }), /* @__PURE__ */ jsx(ImageFileButton, {
648
+ loading: busy,
649
+ mode: "ghost",
650
+ onFile: pickImage,
651
+ text: "Pick an image",
652
+ tooltip: "Take the colors from an image on your device",
653
+ width: "fill"
654
+ })]
655
+ }), $[21] = busy, $[22] = current, $[23] = imageUrl, $[24] = onLucky, $[25] = onVariant, $[26] = palette, $[27] = pickImage, $[28] = thumbnailWidth, $[29] = variants, $[30] = t11) : t11 = $[30];
656
+ let t12;
657
+ $[31] !== t10 || $[32] !== t11 ? (t12 = /* @__PURE__ */ jsxs(Stack, {
658
+ gap: 3,
659
+ children: [t10, t11]
660
+ }), $[31] = t10, $[32] = t11, $[33] = t12) : t12 = $[33];
661
+ let t13;
662
+ return $[34] !== t12 || $[35] !== t6 || $[36] !== t7 ? (t13 = /* @__PURE__ */ jsx(Card, {
663
+ border: !0,
664
+ onDragEnter: t4,
665
+ onDragLeave: t5,
666
+ onDragOver: _temp$3,
667
+ onDrop: t6,
668
+ padding: 3,
669
+ radius: 3,
670
+ tone: t7,
671
+ children: t12
672
+ }), $[34] = t12, $[35] = t6, $[36] = t7, $[37] = t13) : t13 = $[37], t13;
673
+ }
674
+ function _temp$3(event_0) {
675
+ event_0.preventDefault();
676
+ }
677
+ var colorRowText = "os3rj2", range = "os3rj1", swatch = "os3rj0";
678
+ const SCHEME_TITLES = {
679
+ dark: "Dark mode",
680
+ light: "Light mode"
681
+ };
682
+ /**
683
+ * Where the split preview shows a scheme: the Studio's own scheme stays next
684
+ * to the sidebar (or at the bottom, stacked on small screens), the other one
685
+ * takes the far side
686
+ */
687
+ function splitHint(own, mobile) {
688
+ return mobile ? own ? "Shown at the bottom" : "Shown at the top" : own ? "Shown on the right, next to the themer" : "Shown on the left";
689
+ }
690
+ const SCHEME_OPTION_KEYS = [
691
+ "accent",
692
+ "text",
693
+ "background",
694
+ "contrast"
695
+ ];
696
+ /**
697
+ * `<input type="color">` paints the color into a shadow-DOM swatch that brings
698
+ * its own border and padding, which then sits inside ours as a second border.
699
+ * Stripping that chrome leaves the themed border as the only one.
700
+ */
701
+ /**
702
+ * The flow for editing one of the user's own themes: its title, a live
703
+ * preview, and a card per color scheme with the accent/text/background
704
+ * pickers and the contrast slider of that scheme. Every change applies to the
705
+ * whole Studio right away — in the scheme the Studio is showing, or in both
706
+ * side by side in the split preview.
707
+ *
708
+ * @internal
709
+ */
710
+ function ThemeEditor(props) {
711
+ let $ = c(42), { focusTitle, slug } = props, { themes, images, split, mobile, send } = useThemer(), t0;
712
+ if ($[0] !== slug || $[1] !== themes) {
713
+ let t1;
714
+ $[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;
715
+ } else t0 = $[2];
716
+ let theme = t0;
717
+ if (!theme) return null;
718
+ let t1;
719
+ $[5] !== send || $[6] !== slug || $[7] !== theme.options || $[8] !== theme.palette ? (t1 = (variant) => {
720
+ theme.palette && variant !== null && send({
721
+ type: "theme.update",
722
+ slug,
723
+ options: applyImagePalette(theme.options, theme.palette, variant)
724
+ });
725
+ }, $[5] = send, $[6] = slug, $[7] = theme.options, $[8] = theme.palette, $[9] = t1) : t1 = $[9];
726
+ let applyVariant = t1, t2 = images[slug], t3;
727
+ $[10] === send ? t3 = $[11] : (t3 = () => send({ type: "flow.list" }), $[10] = send, $[11] = t3);
728
+ let t4;
729
+ $[12] !== applyVariant || $[13] !== theme.options || $[14] !== theme.palette ? (t4 = () => {
730
+ theme.palette && applyVariant(pickLuckyVariant(theme.palette, { exclude: currentImageVariant(theme.options, theme.palette) }));
731
+ }, $[12] = applyVariant, $[13] = theme.options, $[14] = theme.palette, $[15] = t4) : t4 = $[15];
732
+ let t5;
733
+ $[16] !== send || $[17] !== slug ? (t5 = (options) => send({
734
+ type: "theme.update",
735
+ slug,
736
+ options
737
+ }), $[16] = send, $[17] = slug, $[18] = t5) : t5 = $[18];
738
+ let t6;
739
+ $[19] !== send || $[20] !== slug || $[21] !== theme.options ? (t6 = (palette, file) => send({
740
+ type: "theme.update",
741
+ slug,
742
+ options: applyImagePalette(theme.options, palette),
743
+ palette,
744
+ imageUrl: URL.createObjectURL(file)
745
+ }), $[19] = send, $[20] = slug, $[21] = theme.options, $[22] = t6) : t6 = $[22];
746
+ let t7, t8;
747
+ $[23] !== send || $[24] !== slug ? (t7 = () => send({
748
+ type: "theme.remove",
749
+ slug
750
+ }), t8 = (title) => send({
751
+ type: "theme.update",
752
+ slug,
753
+ title
754
+ }), $[23] = send, $[24] = slug, $[25] = t7, $[26] = t8) : (t7 = $[25], t8 = $[26]);
755
+ let t9;
756
+ return $[27] !== applyVariant || $[28] !== focusTitle || $[29] !== mobile || $[30] !== split || $[31] !== t2 || $[32] !== t3 || $[33] !== t4 || $[34] !== t5 || $[35] !== t6 || $[36] !== t7 || $[37] !== t8 || $[38] !== theme.options || $[39] !== theme.palette || $[40] !== theme.title ? (t9 = /* @__PURE__ */ jsx(ThemeEditorForm, {
757
+ focusTitle,
758
+ imageUrl: t2,
759
+ onDone: t3,
760
+ onLucky: t4,
761
+ onOptionsChange: t5,
762
+ onPalette: t6,
763
+ onRemove: t7,
764
+ onTitleChange: t8,
765
+ onVariant: applyVariant,
766
+ options: theme.options,
767
+ palette: theme.palette,
768
+ split,
769
+ mobile,
770
+ title: theme.title
771
+ }), $[27] = applyVariant, $[28] = focusTitle, $[29] = mobile, $[30] = split, $[31] = t2, $[32] = t3, $[33] = t4, $[34] = t5, $[35] = t6, $[36] = t7, $[37] = t8, $[38] = theme.options, $[39] = theme.palette, $[40] = theme.title, $[41] = t9) : t9 = $[41], t9;
772
+ }
773
+ function ThemeEditorForm(props) {
774
+ let $ = c(50), { focusTitle, imageUrl, onDone, onLucky, onOptionsChange, onPalette, onRemove, onTitleChange, onVariant, options, palette, split, mobile, title } = props, studioScheme = useColorSchemeValue(), t0;
775
+ $[0] === options ? t0 = $[1] : (t0 = resolveThemeOptions(options), $[0] = options, $[1] = t0);
776
+ let resolved = t0, t1;
777
+ $[2] === options ? t1 = $[3] : (t1 = buildPalette(options), $[2] = options, $[3] = t1);
778
+ let palettes = t1, t2;
779
+ $[4] !== onOptionsChange || $[5] !== options ? (t2 = (scheme, changes) => {
780
+ let values = {
781
+ ...options[scheme],
782
+ ...changes
783
+ };
784
+ for (let key of SCHEME_OPTION_KEYS) values[key] === void 0 && delete values[key];
785
+ let next = { ...options };
786
+ Object.keys(values).length === 0 ? delete next[scheme] : next[scheme] = values, onOptionsChange(next);
787
+ }, $[4] = onOptionsChange, $[5] = options, $[6] = t2) : t2 = $[6];
788
+ let patchScheme = t2, t3;
789
+ $[7] === options ? t3 = $[8] : (t3 = /* @__PURE__ */ jsx(ThemeThumbnail, { options }), $[7] = options, $[8] = t3);
790
+ let t4;
791
+ $[9] === Symbol.for("react.memo_cache_sentinel") ? (t4 = /* @__PURE__ */ jsx(Text, {
792
+ size: 1,
793
+ weight: "medium",
794
+ children: "Title"
795
+ }), $[9] = t4) : t4 = $[9];
796
+ let t5;
797
+ $[10] === onTitleChange ? t5 = $[11] : (t5 = (event) => onTitleChange(event.currentTarget.value), $[10] = onTitleChange, $[11] = t5);
798
+ let t6 = focusTitle ? _temp$2 : void 0, t7;
799
+ $[12] !== focusTitle || $[13] !== t5 || $[14] !== t6 || $[15] !== title ? (t7 = /* @__PURE__ */ jsxs(Stack, {
800
+ gap: 2,
801
+ children: [t4, /* @__PURE__ */ jsx(TextInput, {
802
+ autoFocus: focusTitle,
803
+ fontSize: 1,
804
+ onChange: t5,
805
+ onFocus: t6,
806
+ padding: 2,
807
+ placeholder: "Untitled theme",
808
+ value: title
809
+ })]
810
+ }), $[12] = focusTitle, $[13] = t5, $[14] = t6, $[15] = title, $[16] = t7) : t7 = $[16];
811
+ let t8;
812
+ $[17] !== imageUrl || $[18] !== onLucky || $[19] !== onPalette || $[20] !== onVariant || $[21] !== options || $[22] !== palette ? (t8 = /* @__PURE__ */ jsx(ImagePaletteSection, {
813
+ imageUrl,
814
+ onLucky,
815
+ onPalette,
816
+ onVariant,
817
+ options,
818
+ palette
819
+ }), $[17] = imageUrl, $[18] = onLucky, $[19] = onPalette, $[20] = onVariant, $[21] = options, $[22] = palette, $[23] = t8) : t8 = $[23];
820
+ let t9;
821
+ $[24] !== mobile || $[25] !== options || $[26] !== palettes || $[27] !== patchScheme || $[28] !== resolved || $[29] !== split || $[30] !== studioScheme ? (t9 = SCHEMES.map((scheme_0) => /* @__PURE__ */ jsx(SchemeCard, {
822
+ active: split || scheme_0 === studioScheme,
823
+ onChange: (changes_0) => patchScheme(scheme_0, changes_0),
824
+ options: options[scheme_0] ?? {},
825
+ palette: palettes[scheme_0],
826
+ resolved: resolved[scheme_0],
827
+ scheme: scheme_0,
828
+ split,
829
+ splitHint: splitHint(scheme_0 === studioScheme, mobile)
830
+ }, scheme_0)), $[24] = mobile, $[25] = options, $[26] = palettes, $[27] = patchScheme, $[28] = resolved, $[29] = split, $[30] = studioScheme, $[31] = t9) : t9 = $[31];
831
+ let t10;
832
+ $[32] === t9 ? t10 = $[33] : (t10 = /* @__PURE__ */ jsx(ScrollAreaBleed, { children: t9 }), $[32] = t9, $[33] = t10);
833
+ let t11;
834
+ $[34] !== t10 || $[35] !== t3 || $[36] !== t7 || $[37] !== t8 ? (t11 = /* @__PURE__ */ jsx(ScrollArea, {
835
+ padding: 3,
836
+ children: /* @__PURE__ */ jsxs(Stack, {
837
+ gap: 4,
838
+ children: [
839
+ t3,
840
+ t7,
841
+ t8,
842
+ t10
843
+ ]
844
+ })
845
+ }), $[34] = t10, $[35] = t3, $[36] = t7, $[37] = t8, $[38] = t11) : t11 = $[38];
846
+ let t12;
847
+ $[39] === onRemove ? t12 = $[40] : (t12 = /* @__PURE__ */ jsx(TooltipButton, {
848
+ icon: TrashIcon,
849
+ mode: "ghost",
850
+ onClick: onRemove,
851
+ text: "Remove",
852
+ tone: "critical",
853
+ tooltip: "Remove the theme — it can be restored until it is deleted"
854
+ }), $[39] = onRemove, $[40] = t12);
855
+ let t13;
856
+ $[41] === Symbol.for("react.memo_cache_sentinel") ? (t13 = /* @__PURE__ */ jsx(Box, { flex: 1 }), $[41] = t13) : t13 = $[41];
857
+ let t14;
858
+ $[42] === onDone ? t14 = $[43] : (t14 = /* @__PURE__ */ jsx(Button, {
859
+ mode: "ghost",
860
+ onClick: onDone,
861
+ padding: 2,
862
+ text: "Done"
863
+ }), $[42] = onDone, $[43] = t14);
864
+ let t15;
865
+ $[44] !== t12 || $[45] !== t14 ? (t15 = /* @__PURE__ */ jsx(Card, {
866
+ borderTop: !0,
867
+ padding: 3,
868
+ children: /* @__PURE__ */ jsxs(Flex, {
869
+ gap: 2,
870
+ children: [
871
+ t12,
872
+ t13,
873
+ t14
874
+ ]
875
+ })
876
+ }), $[44] = t12, $[45] = t14, $[46] = t15) : t15 = $[46];
877
+ let t16;
878
+ return $[47] !== t11 || $[48] !== t15 ? (t16 = /* @__PURE__ */ jsxs(Fragment, { children: [t11, t15] }), $[47] = t11, $[48] = t15, $[49] = t16) : t16 = $[49], t16;
879
+ }
880
+ function _temp$2(event_0) {
881
+ return event_0.currentTarget.select();
882
+ }
883
+ /**
884
+ * The colors of one scheme. The cards span the sidebar edge to edge, one
885
+ * after the other with a border between them, in the sidebar's own scheme —
886
+ * the swatches show the colors they edit. The scheme the Studio is showing is
887
+ * marked as active: its changes show up in the Studio right away, the other
888
+ * scheme's only once the appearance setting switches to it — or both are
889
+ * active, in the split preview.
890
+ */
891
+ function SchemeCard(props) {
892
+ let $ = c(66), { active, onChange, options, palette, resolved, scheme, split, splitHint } = props, name = SCHEME_TITLES[scheme], t0;
893
+ $[0] === name ? t0 = $[1] : (t0 = /* @__PURE__ */ jsx(Box, {
894
+ flex: 1,
895
+ children: /* @__PURE__ */ jsx(Text, {
896
+ size: 1,
897
+ weight: "medium",
898
+ children: name
899
+ })
900
+ }), $[0] = name, $[1] = t0);
901
+ let t1;
902
+ $[2] === active ? t1 = $[3] : (t1 = active && /* @__PURE__ */ jsx(Badge, {
903
+ fontSize: 0,
904
+ tone: "primary",
905
+ children: "Active"
906
+ }), $[2] = active, $[3] = t1);
907
+ let t2;
908
+ $[4] !== t0 || $[5] !== t1 ? (t2 = /* @__PURE__ */ jsxs(Flex, {
909
+ align: "center",
910
+ gap: 2,
911
+ children: [t0, t1]
912
+ }), $[4] = t0, $[5] = t1, $[6] = t2) : t2 = $[6];
913
+ let t3 = split ? splitHint : active ? "Shown in the Studio right now" : `Shown once the Studio appearance is ${scheme}`, t4;
914
+ $[7] === t3 ? t4 = $[8] : (t4 = /* @__PURE__ */ jsx(Text, {
915
+ muted: !0,
916
+ size: 0,
917
+ children: t3
918
+ }), $[7] = t3, $[8] = t4);
919
+ let t5;
920
+ $[9] !== t2 || $[10] !== t4 ? (t5 = /* @__PURE__ */ jsxs(Stack, {
921
+ gap: 2,
922
+ children: [t2, t4]
923
+ }), $[9] = t2, $[10] = t4, $[11] = t5) : t5 = $[11];
924
+ let t6 = options.accent === void 0, t7 = `${name} accent`, t8, t9;
925
+ $[12] === onChange ? (t8 = $[13], t9 = $[14]) : (t8 = (accent) => onChange({ accent }), t9 = () => onChange({ accent: void 0 }), $[12] = onChange, $[13] = t8, $[14] = t9);
926
+ let t10;
927
+ $[15] !== resolved.accent || $[16] !== t6 || $[17] !== t7 || $[18] !== t8 || $[19] !== t9 ? (t10 = /* @__PURE__ */ jsx(ColorRow, {
928
+ auto: t6,
929
+ autoLabel: "default",
930
+ label: t7,
931
+ onChange: t8,
932
+ onClear: t9,
933
+ title: "Accent",
934
+ value: resolved.accent
935
+ }), $[15] = resolved.accent, $[16] = t6, $[17] = t7, $[18] = t8, $[19] = t9, $[20] = t10) : t10 = $[20];
936
+ let t11 = options.text === void 0, t12 = `${name} text`, t13, t14;
937
+ $[21] === onChange ? (t13 = $[22], t14 = $[23]) : (t13 = (text) => onChange({ text }), t14 = () => onChange({ text: void 0 }), $[21] = onChange, $[22] = t13, $[23] = t14);
938
+ let t15;
939
+ $[24] !== resolved.text || $[25] !== t11 || $[26] !== t12 || $[27] !== t13 || $[28] !== t14 ? (t15 = /* @__PURE__ */ jsx(ColorRow, {
940
+ auto: t11,
941
+ label: t12,
942
+ onChange: t13,
943
+ onClear: t14,
944
+ title: "Text",
945
+ value: resolved.text
946
+ }), $[24] = resolved.text, $[25] = t11, $[26] = t12, $[27] = t13, $[28] = t14, $[29] = t15) : t15 = $[29];
947
+ let t16 = scheme === "dark" ? palette.black : palette.white, t17 = options.background === void 0, t18 = `${name} background`, t19, t20;
948
+ $[30] === onChange ? (t19 = $[31], t20 = $[32]) : (t19 = (background) => onChange({ background }), t20 = () => onChange({ background: void 0 }), $[30] = onChange, $[31] = t19, $[32] = t20);
949
+ let t21;
950
+ $[33] !== resolved.background || $[34] !== t16 || $[35] !== t17 || $[36] !== t18 || $[37] !== t19 || $[38] !== t20 ? (t21 = /* @__PURE__ */ jsx(ColorRow, {
951
+ adjusted: t16,
952
+ auto: t17,
953
+ label: t18,
954
+ onChange: t19,
955
+ onClear: t20,
956
+ title: "Background",
957
+ value: resolved.background
958
+ }), $[33] = resolved.background, $[34] = t16, $[35] = t17, $[36] = t18, $[37] = t19, $[38] = t20, $[39] = t21) : t21 = $[39];
959
+ let t22;
960
+ $[40] === Symbol.for("react.memo_cache_sentinel") ? (t22 = /* @__PURE__ */ jsx(Text, {
961
+ size: 1,
962
+ children: "Contrast"
963
+ }), $[40] = t22) : t22 = $[40];
964
+ let t23 = options.contrast === void 0 ? " · auto" : "", t24;
965
+ $[41] !== resolved.contrast || $[42] !== t23 ? (t24 = /* @__PURE__ */ jsxs(Stack, {
966
+ flex: 1,
967
+ gap: 2,
968
+ children: [t22, /* @__PURE__ */ jsxs(Text, {
969
+ muted: !0,
970
+ size: 0,
971
+ children: [resolved.contrast, t23]
972
+ })]
973
+ }), $[41] = resolved.contrast, $[42] = t23, $[43] = t24) : t24 = $[43];
974
+ let t25;
975
+ $[44] !== onChange || $[45] !== options.contrast ? (t25 = options.contrast !== void 0 && /* @__PURE__ */ jsx(TooltipButton, {
976
+ icon: ResetIcon,
977
+ mode: "bleed",
978
+ onClick: () => onChange({ contrast: void 0 }),
979
+ padding: 2,
980
+ tooltip: "Reset to auto"
981
+ }), $[44] = onChange, $[45] = options.contrast, $[46] = t25) : t25 = $[46];
982
+ let t26;
983
+ $[47] !== t24 || $[48] !== t25 ? (t26 = /* @__PURE__ */ jsxs(Flex, {
984
+ align: "center",
985
+ gap: 2,
986
+ children: [t24, t25]
987
+ }), $[47] = t24, $[48] = t25, $[49] = t26) : t26 = $[49];
988
+ let t27 = `${name} contrast`, t28;
989
+ $[50] === onChange ? t28 = $[51] : (t28 = (event) => onChange({ contrast: Number(event.currentTarget.value) }), $[50] = onChange, $[51] = t28);
990
+ let t29;
991
+ $[52] !== resolved.contrast || $[53] !== t27 || $[54] !== t28 ? (t29 = /* @__PURE__ */ jsx("input", {
992
+ "aria-label": t27,
993
+ className: range,
994
+ max: 100,
995
+ min: 15,
996
+ onChange: t28,
997
+ step: 1,
998
+ type: "range",
999
+ value: resolved.contrast
1000
+ }), $[52] = resolved.contrast, $[53] = t27, $[54] = t28, $[55] = t29) : t29 = $[55];
1001
+ let t30;
1002
+ $[56] === Symbol.for("react.memo_cache_sentinel") ? (t30 = /* @__PURE__ */ jsx(Text, {
1003
+ muted: !0,
1004
+ size: 0,
1005
+ children: "100 keeps text and borders neutral — lower values blend in the accent"
1006
+ }), $[56] = t30) : t30 = $[56];
1007
+ let t31;
1008
+ $[57] !== t26 || $[58] !== t29 ? (t31 = /* @__PURE__ */ jsxs(Stack, {
1009
+ gap: 3,
1010
+ children: [
1011
+ t26,
1012
+ t29,
1013
+ t30
1014
+ ]
1015
+ }), $[57] = t26, $[58] = t29, $[59] = t31) : t31 = $[59];
1016
+ let t32;
1017
+ return $[60] !== t10 || $[61] !== t15 || $[62] !== t21 || $[63] !== t31 || $[64] !== t5 ? (t32 = /* @__PURE__ */ jsx(Card, {
1018
+ borderTop: !0,
1019
+ padding: 3,
1020
+ children: /* @__PURE__ */ jsxs(Stack, {
1021
+ gap: 4,
1022
+ children: [
1023
+ t5,
1024
+ t10,
1025
+ t15,
1026
+ t21,
1027
+ t31
1028
+ ]
1029
+ })
1030
+ }), $[60] = t10, $[61] = t15, $[62] = t21, $[63] = t31, $[64] = t5, $[65] = t32) : t32 = $[65], t32;
1031
+ }
1032
+ /** One color of a scheme: a swatch with its value and an optional reset button */
1033
+ function ColorRow(props) {
1034
+ let $ = c(23), { adjusted, auto, autoLabel: t0, label, onChange, onClear, title, value } = props, autoLabel = t0 === void 0 ? "auto" : t0, t1;
1035
+ $[0] === onChange ? t1 = $[1] : (t1 = (event) => onChange(event.currentTarget.value), $[0] = onChange, $[1] = t1);
1036
+ let t2;
1037
+ $[2] !== label || $[3] !== t1 || $[4] !== value ? (t2 = /* @__PURE__ */ jsx("input", {
1038
+ "aria-label": label,
1039
+ className: swatch,
1040
+ onChange: t1,
1041
+ type: "color",
1042
+ value
1043
+ }), $[2] = label, $[3] = t1, $[4] = value, $[5] = t2) : t2 = $[5];
1044
+ let t3;
1045
+ $[6] === title ? t3 = $[7] : (t3 = /* @__PURE__ */ jsx(Text, {
1046
+ size: 1,
1047
+ children: title
1048
+ }), $[6] = title, $[7] = t3);
1049
+ let t4 = adjusted !== void 0 && adjusted !== value ? ` → ${adjusted}` : "", t5 = auto ? ` · ${autoLabel}` : "", t6;
1050
+ $[8] !== t4 || $[9] !== t5 || $[10] !== value ? (t6 = /* @__PURE__ */ jsxs(Text, {
1051
+ muted: !0,
1052
+ size: 0,
1053
+ textOverflow: "ellipsis",
1054
+ children: [
1055
+ value,
1056
+ t4,
1057
+ t5
1058
+ ]
1059
+ }), $[8] = t4, $[9] = t5, $[10] = value, $[11] = t6) : t6 = $[11];
1060
+ let t7;
1061
+ $[12] !== t3 || $[13] !== t6 ? (t7 = /* @__PURE__ */ jsxs(Stack, {
1062
+ className: colorRowText,
1063
+ flex: 1,
1064
+ gap: 2,
1065
+ children: [t3, t6]
1066
+ }), $[12] = t3, $[13] = t6, $[14] = t7) : t7 = $[14];
1067
+ let t8;
1068
+ $[15] !== auto || $[16] !== autoLabel || $[17] !== onClear ? (t8 = onClear && !auto && /* @__PURE__ */ jsx(TooltipButton, {
1069
+ icon: ResetIcon,
1070
+ mode: "bleed",
1071
+ onClick: onClear,
1072
+ padding: 2,
1073
+ tooltip: `Reset to ${autoLabel}`
1074
+ }), $[15] = auto, $[16] = autoLabel, $[17] = onClear, $[18] = t8) : t8 = $[18];
1075
+ let t9;
1076
+ return $[19] !== t2 || $[20] !== t7 || $[21] !== t8 ? (t9 = /* @__PURE__ */ jsxs(Flex, {
1077
+ align: "center",
1078
+ gap: 2,
1079
+ children: [
1080
+ t2,
1081
+ t7,
1082
+ t8
1083
+ ]
1084
+ }), $[19] = t2, $[20] = t7, $[21] = t8, $[22] = t9) : t9 = $[22], t9;
1085
+ }
1086
+ /**
1087
+ * The version of the code format, the first byte of every code — bumped if
1088
+ * the layout below ever changes, so older codes can still be told apart
1089
+ */
1090
+ const FIELDS = [
1091
+ "accent",
1092
+ "text",
1093
+ "background",
1094
+ "contrast"
1095
+ ];
1096
+ /**
1097
+ * Turns a theme into a short code to share in a message: `title`, and the
1098
+ * colors and contrasts of both schemes, packed into bytes — one flag byte says
1099
+ * which of the eight fields are set, each color is its three RGB bytes and a
1100
+ * contrast one byte, and the title takes up the rest — then base64url'd, so
1101
+ * it is one word without padding, safe in URLs and chat. A theme setting
1102
+ * everything comes to about 30 bytes plus its title.
1103
+ *
1104
+ * @internal
1105
+ */
1106
+ function encodeTheme(theme) {
1107
+ let bytes = [1, 0], flags = 0;
1108
+ return SCHEMES.forEach((scheme, schemeIndex) => {
1109
+ let options = theme.options[scheme] ?? {};
1110
+ FIELDS.forEach((field, fieldIndex) => {
1111
+ let value = options[field];
1112
+ if (value !== void 0) {
1113
+ if (field === "contrast") {
1114
+ if (typeof value != "number" || !Number.isFinite(value)) return;
1115
+ bytes.push(Math.round(Math.min(100, Math.max(15, value))));
1116
+ } else {
1117
+ if (typeof value != "string" || !isColor(value)) return;
1118
+ bytes.push(...rgbBytes(value));
1119
+ }
1120
+ flags |= 1 << schemeIndex * FIELDS.length + fieldIndex;
1121
+ }
1122
+ });
1123
+ }), bytes[1] = flags, bytes.push(...new TextEncoder().encode(theme.title.trim())), toBase64Url(Uint8Array.from(bytes));
1124
+ }
1125
+ /**
1126
+ * Reads a theme back out of a code — as pasted, so surrounding whitespace and
1127
+ * words around the code are fine: the first word that is a code counts. `null`
1128
+ * when nothing in the text is one.
1129
+ *
1130
+ * @internal
1131
+ */
1132
+ function decodeTheme(text) {
1133
+ for (let word of text.trim().split(/\s+/)) {
1134
+ let theme = decodeCode(word);
1135
+ if (theme) return theme;
1136
+ }
1137
+ return null;
1138
+ }
1139
+ function decodeCode(code) {
1140
+ let bytes = fromBase64Url(code);
1141
+ if (!bytes || bytes.length < 2 || bytes[0] !== 1) return null;
1142
+ let flags = bytes[1], options = {}, offset = 2;
1143
+ for (let [schemeIndex, scheme] of SCHEMES.entries()) {
1144
+ let schemeOptions = {};
1145
+ for (let [fieldIndex, field] of FIELDS.entries()) if (flags & 1 << schemeIndex * FIELDS.length + fieldIndex) {
1146
+ if (field === "contrast") {
1147
+ if (offset + 1 > bytes.length) return null;
1148
+ schemeOptions.contrast = Math.min(100, Math.max(15, bytes[offset])), offset += 1;
1149
+ } else {
1150
+ if (offset + 3 > bytes.length) return null;
1151
+ schemeOptions[field] = hexColor(bytes.subarray(offset, offset + 3)), offset += 3;
1152
+ }
1153
+ }
1154
+ Object.keys(schemeOptions).length > 0 && (options[scheme] = schemeOptions);
1155
+ }
1156
+ let title;
1157
+ try {
1158
+ title = new TextDecoder("utf-8", { fatal: !0 }).decode(bytes.subarray(offset));
1159
+ } catch {
1160
+ return null;
1161
+ }
1162
+ return {
1163
+ title: displayTitle(title),
1164
+ options
1165
+ };
1166
+ }
1167
+ /** The three RGB bytes of a `#rgb` or `#rrggbb` color */
1168
+ function rgbBytes(color) {
1169
+ let hex = color.length === 4 ? color.slice(1).replaceAll(/./g, "$&$&") : color.slice(1);
1170
+ return [
1171
+ 0,
1172
+ 2,
1173
+ 4
1174
+ ].map((start) => parseInt(hex.slice(start, start + 2), 16));
1175
+ }
1176
+ function hexColor(rgb) {
1177
+ return `#${[...rgb].map((byte) => byte.toString(16).padStart(2, "0")).join("")}`;
1178
+ }
1179
+ function toBase64Url(bytes) {
1180
+ let binary = "";
1181
+ for (let byte of bytes) binary += String.fromCharCode(byte);
1182
+ return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
1183
+ }
1184
+ function fromBase64Url(code) {
1185
+ if (!/^[A-Za-z0-9_-]+$/.test(code)) return null;
1186
+ let base64 = code.replaceAll("-", "+").replaceAll("_", "/");
1187
+ try {
1188
+ return Uint8Array.from(atob(base64.padEnd(Math.ceil(base64.length / 4) * 4, "=")), (char) => char.charCodeAt(0));
1189
+ } catch {
1190
+ return null;
1191
+ }
1192
+ }
1193
+ /**
1194
+ * Copying a theme as a code to share, and adding a theme from a code someone
1195
+ * shared — with toasts telling how it went.
1196
+ *
1197
+ * @internal
1198
+ */
1199
+ function useThemeCodes() {
1200
+ let $ = c(11), { send } = useThemer(), toast = useToast(), t0;
1201
+ $[0] === toast ? t0 = $[1] : (t0 = async (theme) => {
1202
+ let title = displayTitle(theme.title);
1203
+ try {
1204
+ await navigator.clipboard.writeText(encodeTheme(theme)), toast.push({
1205
+ status: "success",
1206
+ title: `Copied ${title} as a code`,
1207
+ description: "Paste it in the themer of another Studio to add the theme there"
1208
+ });
1209
+ } catch {
1210
+ toast.push({
1211
+ status: "error",
1212
+ title: `Could not copy ${title}`
1213
+ });
1214
+ }
1215
+ }, $[0] = toast, $[1] = t0);
1216
+ let copyTheme = t0, t1;
1217
+ $[2] !== send || $[3] !== toast ? (t1 = (text) => {
1218
+ let theme_0 = decodeTheme(text);
1219
+ return theme_0 ? (send({
1220
+ type: "theme.import",
1221
+ title: theme_0.title,
1222
+ options: theme_0.options
1223
+ }), toast.push({
1224
+ status: "success",
1225
+ title: `Added ${theme_0.title}`
1226
+ }), !0) : !1;
1227
+ }, $[2] = send, $[3] = toast, $[4] = t1) : t1 = $[4];
1228
+ let addThemeFromText = t1, t2;
1229
+ $[5] === addThemeFromText ? t2 = $[6] : (t2 = async () => {
1230
+ let text_0;
1231
+ try {
1232
+ text_0 = await navigator.clipboard.readText();
1233
+ } catch {
1234
+ return "no-clipboard";
1235
+ }
1236
+ return addThemeFromText(text_0) ? "added" : "no-theme";
1237
+ }, $[5] = addThemeFromText, $[6] = t2);
1238
+ let addThemeFromClipboard = t2, t3;
1239
+ return $[7] !== addThemeFromClipboard || $[8] !== addThemeFromText || $[9] !== copyTheme ? (t3 = {
1240
+ copyTheme,
1241
+ addThemeFromText,
1242
+ addThemeFromClipboard
1243
+ }, $[7] = addThemeFromClipboard, $[8] = addThemeFromText, $[9] = copyTheme, $[10] = t3) : t3 = $[10], t3;
1244
+ }
1245
+ /**
1246
+ * Adds a theme pasted anywhere in the Studio while the themer's list is
1247
+ * showing — pastes into inputs and other editable things are theirs.
1248
+ *
1249
+ * @internal
1250
+ */
1251
+ function usePasteThemeCodes() {
1252
+ let $ = c(5), { open, send } = useThemer(), toast = useToast(), t0, t1;
1253
+ $[0] !== open || $[1] !== send || $[2] !== toast ? (t0 = () => {
1254
+ if (!open) return;
1255
+ let handlePaste = (event) => {
1256
+ if (isEditable(event.target)) return;
1257
+ let theme = decodeTheme(event.clipboardData?.getData("text/plain") ?? "");
1258
+ theme && (event.preventDefault(), send({
1259
+ type: "theme.import",
1260
+ title: theme.title,
1261
+ options: theme.options
1262
+ }), toast.push({
1263
+ status: "success",
1264
+ title: `Added ${theme.title}`
1265
+ }));
1266
+ };
1267
+ return document.addEventListener("paste", handlePaste), () => document.removeEventListener("paste", handlePaste);
1268
+ }, t1 = [
1269
+ open,
1270
+ send,
1271
+ toast
1272
+ ], $[0] = open, $[1] = send, $[2] = toast, $[3] = t0, $[4] = t1) : (t0 = $[3], t1 = $[4]), useEffect(t0, t1);
1273
+ }
1274
+ function isEditable(target) {
1275
+ return target instanceof HTMLElement && (target.isContentEditable || target.matches("input, textarea, select"));
1276
+ }
1277
+ /**
1278
+ * Where a theme code someone shared gets pasted by hand — for browsers that
1279
+ * do not hand the clipboard over, and for codes that are not on it.
1280
+ *
1281
+ * @internal
1282
+ */
1283
+ function PasteThemeDialog(props) {
1284
+ let $ = c(35), { onClose } = props, { addThemeFromText } = useThemeCodes(), [text, setText] = useState(""), [invalid, setInvalid] = useState(!1), t0;
1285
+ $[0] !== addThemeFromText || $[1] !== onClose || $[2] !== text ? (t0 = () => {
1286
+ addThemeFromText(text) ? onClose() : setInvalid(!0);
1287
+ }, $[0] = addThemeFromText, $[1] = onClose, $[2] = text, $[3] = t0) : t0 = $[3];
1288
+ let add = t0, t1;
1289
+ $[4] === onClose ? t1 = $[5] : (t1 = /* @__PURE__ */ jsx(Button, {
1290
+ mode: "ghost",
1291
+ onClick: onClose,
1292
+ padding: 2,
1293
+ text: "Cancel"
1294
+ }), $[4] = onClose, $[5] = t1);
1295
+ let t2;
1296
+ $[6] === text ? t2 = $[7] : (t2 = text.trim(), $[6] = text, $[7] = t2);
1297
+ let t3 = t2 === "", t4;
1298
+ $[8] !== add || $[9] !== t3 ? (t4 = /* @__PURE__ */ jsx(Button, {
1299
+ disabled: t3,
1300
+ onClick: add,
1301
+ padding: 2,
1302
+ text: "Add theme",
1303
+ tone: "primary"
1304
+ }), $[8] = add, $[9] = t3, $[10] = t4) : t4 = $[10];
1305
+ let t5;
1306
+ $[11] !== t1 || $[12] !== t4 ? (t5 = /* @__PURE__ */ jsxs(Flex, {
1307
+ gap: 2,
1308
+ justify: "flex-end",
1309
+ padding: 3,
1310
+ children: [t1, t4]
1311
+ }), $[11] = t1, $[12] = t4, $[13] = t5) : t5 = $[13];
1312
+ let t6;
1313
+ $[14] === add ? t6 = $[15] : (t6 = (event) => {
1314
+ event.preventDefault(), add();
1315
+ }, $[14] = add, $[15] = t6);
1316
+ let t7;
1317
+ $[16] === Symbol.for("react.memo_cache_sentinel") ? (t7 = /* @__PURE__ */ jsx(Text, {
1318
+ muted: !0,
1319
+ size: 1,
1320
+ children: "Paste the code someone copied from their themer — the theme is added to your list and applied."
1321
+ }), $[16] = t7) : t7 = $[16];
1322
+ let t8 = invalid ? "themer-paste-error" : void 0, t9 = invalid ? "That is not a theme code" : void 0, t10;
1323
+ $[17] === Symbol.for("react.memo_cache_sentinel") ? (t10 = (event_0) => {
1324
+ setText(event_0.currentTarget.value), setInvalid(!1);
1325
+ }, $[17] = t10) : t10 = $[17];
1326
+ let t11;
1327
+ $[18] !== invalid || $[19] !== t8 || $[20] !== t9 || $[21] !== text ? (t11 = /* @__PURE__ */ jsx(TextInput, {
1328
+ "aria-describedby": t8,
1329
+ "aria-invalid": invalid,
1330
+ "aria-label": "Theme code",
1331
+ autoFocus: !0,
1332
+ customValidity: t9,
1333
+ fontSize: 1,
1334
+ onChange: t10,
1335
+ placeholder: "AQ…",
1336
+ value: text
1337
+ }), $[18] = invalid, $[19] = t8, $[20] = t9, $[21] = text, $[22] = t11) : t11 = $[22];
1338
+ let t12;
1339
+ $[23] === invalid ? t12 = $[24] : (t12 = invalid && /* @__PURE__ */ jsx(Card, {
1340
+ id: "themer-paste-error",
1341
+ padding: 3,
1342
+ radius: 2,
1343
+ role: "alert",
1344
+ tone: "critical",
1345
+ children: /* @__PURE__ */ jsx(Text, {
1346
+ size: 1,
1347
+ children: "That is not a theme code — codes are one word, copied from a theme's menu."
1348
+ })
1349
+ }), $[23] = invalid, $[24] = t12);
1350
+ let t13;
1351
+ $[25] !== t11 || $[26] !== t12 ? (t13 = /* @__PURE__ */ jsxs(Stack, {
1352
+ gap: 3,
1353
+ padding: 4,
1354
+ children: [
1355
+ t7,
1356
+ t11,
1357
+ t12
1358
+ ]
1359
+ }), $[25] = t11, $[26] = t12, $[27] = t13) : t13 = $[27];
1360
+ let t14;
1361
+ $[28] !== t13 || $[29] !== t6 ? (t14 = /* @__PURE__ */ jsx("form", {
1362
+ onSubmit: t6,
1363
+ children: t13
1364
+ }), $[28] = t13, $[29] = t6, $[30] = t14) : t14 = $[30];
1365
+ let t15;
1366
+ return $[31] !== onClose || $[32] !== t14 || $[33] !== t5 ? (t15 = /* @__PURE__ */ jsx(Dialog, {
1367
+ animate: !0,
1368
+ footer: t5,
1369
+ header: "Add a shared theme",
1370
+ id: "themer-paste",
1371
+ onClickOutside: onClose,
1372
+ onClose,
1373
+ width: 0,
1374
+ children: t14
1375
+ }), $[31] = onClose, $[32] = t14, $[33] = t5, $[34] = t15) : t15 = $[34], t15;
1376
+ }
1377
+ var frame$1 = "_1wsn7882", menuCard = "_1wsn7884", menuSlot = "_1wsn7883", pickButton = "_1wsn7881", root = "_1wsn7880";
1378
+ /**
1379
+ * Scrolls the applied theme's card into view when it is not already, as a
1380
+ * ref: the list scrolls to it as it mounts — coming back from the editor or
1381
+ * the removed themes — and when another theme gets applied
1382
+ */
1383
+ function scrollRefIntoViewIfNeeded(node) {
1384
+ node && scrollIntoView(node, {
1385
+ block: "nearest",
1386
+ scrollMode: "if-needed"
1387
+ });
1388
+ }
1389
+ /**
1390
+ * One theme in the list: the floating preview with the title below it, like
1391
+ * an appearance option in macOS System Settings. Clicking it applies the
1392
+ * theme to the whole Studio, dragging it moves the theme to another place in
1393
+ * the list; its menu holds the move, edit, duplicate and remove flows that
1394
+ * apply to the theme.
1395
+ *
1396
+ * @internal
1397
+ */
1398
+ function ThemeCard(props) {
1399
+ let $ = c(85), { active, theme } = props, { themes, send } = useThemer(), { copyTheme } = useThemeCodes(), { scheme } = useRootTheme(), [menuOpen, setMenuOpen] = useState(!1), dragControls = useDragControls(), dragged = useRef(!1), t0;
1400
+ $[0] === theme.title ? t0 = $[1] : (t0 = displayTitle(theme.title), $[0] = theme.title, $[1] = t0);
1401
+ let title = t0, t1;
1402
+ $[2] === theme.slug ? t1 = $[3] : (t1 = (candidate) => candidate.slug === theme.slug, $[2] = theme.slug, $[3] = t1);
1403
+ let index = themes.findIndex(t1), t2;
1404
+ $[4] !== index || $[5] !== send || $[6] !== themes ? (t2 = (to) => {
1405
+ let order = themes.map(_temp$1);
1406
+ order.splice(to, 0, ...order.splice(index, 1)), send({
1407
+ type: "theme.reorder",
1408
+ order
1409
+ });
1410
+ }, $[4] = index, $[5] = send, $[6] = themes, $[7] = t2) : t2 = $[7];
1411
+ let moveTo = t2, t3, t4;
1412
+ $[8] === Symbol.for("react.memo_cache_sentinel") ? (t3 = () => {
1413
+ requestAnimationFrame(() => {
1414
+ dragged.current = !1;
1415
+ });
1416
+ }, t4 = () => {
1417
+ dragged.current = !0;
1418
+ }, $[8] = t3, $[9] = t4) : (t3 = $[8], t4 = $[9]);
1419
+ let t5 = active ? scrollRefIntoViewIfNeeded : void 0, t6;
1420
+ $[10] === Symbol.for("react.memo_cache_sentinel") ? (t6 = { scale: 1.04 }, $[10] = t6) : t6 = $[10];
1421
+ let t7;
1422
+ $[11] !== send || $[12] !== theme.slug ? (t7 = () => {
1423
+ dragged.current || send({
1424
+ type: "theme.pick",
1425
+ slug: theme.slug
1426
+ });
1427
+ }, $[11] = send, $[12] = theme.slug, $[13] = t7) : t7 = $[13];
1428
+ let t8;
1429
+ $[14] === dragControls ? t8 = $[15] : (t8 = (event) => {
1430
+ event.pointerType !== "touch" && dragControls.start(event);
1431
+ }, $[14] = dragControls, $[15] = t8);
1432
+ let t9;
1433
+ $[16] === theme.options ? t9 = $[17] : (t9 = /* @__PURE__ */ jsx("span", {
1434
+ className: frame$1,
1435
+ children: /* @__PURE__ */ jsx(ThemeThumbnail, { options: theme.options })
1436
+ }), $[16] = theme.options, $[17] = t9);
1437
+ let t10 = active ? "medium" : "regular", t11;
1438
+ $[18] !== t10 || $[19] !== title ? (t11 = /* @__PURE__ */ jsx(Box, {
1439
+ as: "span",
1440
+ display: "block",
1441
+ paddingTop: 2,
1442
+ paddingX: 2,
1443
+ children: /* @__PURE__ */ jsx(Text, {
1444
+ align: "center",
1445
+ as: "span",
1446
+ size: 1,
1447
+ textOverflow: "ellipsis",
1448
+ weight: t10,
1449
+ children: title
1450
+ })
1451
+ }), $[18] = t10, $[19] = title, $[20] = t11) : t11 = $[20];
1452
+ let t12;
1453
+ $[21] !== active || $[22] !== t11 || $[23] !== t7 || $[24] !== t8 || $[25] !== t9 ? (t12 = /* @__PURE__ */ jsxs("button", {
1454
+ "aria-pressed": active,
1455
+ className: pickButton,
1456
+ onClick: t7,
1457
+ onPointerDown: t8,
1458
+ type: "button",
1459
+ children: [t9, t11]
1460
+ }), $[21] = active, $[22] = t11, $[23] = t7, $[24] = t8, $[25] = t9, $[26] = t12) : t12 = $[26];
1461
+ let t13;
1462
+ $[27] === Symbol.for("react.memo_cache_sentinel") ? (t13 = /* @__PURE__ */ jsx(Text, {
1463
+ size: 1,
1464
+ children: "Show more"
1465
+ }), $[27] = t13) : t13 = $[27];
1466
+ let t14 = `Show more for ${title}`, t15;
1467
+ $[28] === t14 ? t15 = $[29] : (t15 = /* @__PURE__ */ jsx(Button, {
1468
+ "aria-label": t14,
1469
+ fontSize: 1,
1470
+ icon: EllipsisHorizontalIcon,
1471
+ mode: "ghost",
1472
+ padding: 1,
1473
+ radius: 2
1474
+ }), $[28] = t14, $[29] = t15);
1475
+ let t16 = `themer-theme-${theme.slug}`, t17 = index <= 0, t18;
1476
+ $[30] !== index || $[31] !== moveTo ? (t18 = () => moveTo(index - 1), $[30] = index, $[31] = moveTo, $[32] = t18) : t18 = $[32];
1477
+ let t19;
1478
+ $[33] !== t17 || $[34] !== t18 ? (t19 = /* @__PURE__ */ jsx(MenuItem, {
1479
+ disabled: t17,
1480
+ icon: ArrowUpIcon,
1481
+ onClick: t18,
1482
+ text: "Move up"
1483
+ }), $[33] = t17, $[34] = t18, $[35] = t19) : t19 = $[35];
1484
+ let t20 = index >= themes.length - 1, t21;
1485
+ $[36] !== index || $[37] !== moveTo ? (t21 = () => moveTo(index + 1), $[36] = index, $[37] = moveTo, $[38] = t21) : t21 = $[38];
1486
+ let t22;
1487
+ $[39] !== t20 || $[40] !== t21 ? (t22 = /* @__PURE__ */ jsx(MenuItem, {
1488
+ disabled: t20,
1489
+ icon: ArrowDownIcon,
1490
+ onClick: t21,
1491
+ text: "Move down"
1492
+ }), $[39] = t20, $[40] = t21, $[41] = t22) : t22 = $[41];
1493
+ let t23;
1494
+ $[42] === Symbol.for("react.memo_cache_sentinel") ? (t23 = /* @__PURE__ */ jsx(MenuDivider, {}), $[42] = t23) : t23 = $[42];
1495
+ let t24;
1496
+ $[43] !== send || $[44] !== theme.slug || $[45] !== theme.source ? (t24 = theme.source === "custom" && /* @__PURE__ */ jsx(MenuItem, {
1497
+ icon: EditIcon,
1498
+ onClick: () => send({
1499
+ type: "theme.edit",
1500
+ slug: theme.slug
1501
+ }),
1502
+ text: "Edit"
1503
+ }), $[43] = send, $[44] = theme.slug, $[45] = theme.source, $[46] = t24) : t24 = $[46];
1504
+ let t25;
1505
+ $[47] !== send || $[48] !== theme.slug ? (t25 = () => send({
1506
+ type: "theme.duplicate",
1507
+ slug: theme.slug
1508
+ }), $[47] = send, $[48] = theme.slug, $[49] = t25) : t25 = $[49];
1509
+ let t26 = theme.source === "custom" ? "Duplicate" : "Duplicate to edit", t27;
1510
+ $[50] !== t25 || $[51] !== t26 ? (t27 = /* @__PURE__ */ jsx(MenuItem, {
1511
+ icon: CopyIcon,
1512
+ onClick: t25,
1513
+ text: t26
1514
+ }), $[50] = t25, $[51] = t26, $[52] = t27) : t27 = $[52];
1515
+ let t28;
1516
+ $[53] !== copyTheme || $[54] !== theme ? (t28 = /* @__PURE__ */ jsx(MenuItem, {
1517
+ icon: ShareIcon,
1518
+ onClick: () => void copyTheme(theme),
1519
+ text: "Copy to share"
1520
+ }), $[53] = copyTheme, $[54] = theme, $[55] = t28) : t28 = $[55];
1521
+ let t29;
1522
+ $[56] !== send || $[57] !== theme.slug || $[58] !== theme.source ? (t29 = theme.source !== "config" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MenuDivider, {}), /* @__PURE__ */ jsx(MenuItem, {
1523
+ icon: TrashIcon,
1524
+ onClick: () => send({
1525
+ type: "theme.remove",
1526
+ slug: theme.slug
1527
+ }),
1528
+ text: "Remove",
1529
+ tone: "critical"
1530
+ })] }), $[56] = send, $[57] = theme.slug, $[58] = theme.source, $[59] = t29) : t29 = $[59];
1531
+ let t30;
1532
+ $[60] !== t19 || $[61] !== t22 || $[62] !== t24 || $[63] !== t27 || $[64] !== t28 || $[65] !== t29 ? (t30 = /* @__PURE__ */ jsxs(Menu, { children: [
1533
+ t19,
1534
+ t22,
1535
+ t23,
1536
+ t24,
1537
+ t27,
1538
+ t28,
1539
+ t29
1540
+ ] }), $[60] = t19, $[61] = t22, $[62] = t24, $[63] = t27, $[64] = t28, $[65] = t29, $[66] = t30) : t30 = $[66];
1541
+ let t31, t32;
1542
+ $[67] === Symbol.for("react.memo_cache_sentinel") ? (t31 = () => setMenuOpen(!1), t32 = () => setMenuOpen(!0), $[67] = t31, $[68] = t32) : (t31 = $[67], t32 = $[68]);
1543
+ let t33;
1544
+ $[69] === scheme ? t33 = $[70] : (t33 = {
1545
+ animate: !0,
1546
+ placement: "bottom-end",
1547
+ portal: !0,
1548
+ scheme
1549
+ }, $[69] = scheme, $[70] = t33);
1550
+ let t34;
1551
+ $[71] !== t15 || $[72] !== t16 || $[73] !== t30 || $[74] !== t33 ? (t34 = /* @__PURE__ */ jsx(Tooltip, {
1552
+ animate: !0,
1553
+ content: t13,
1554
+ placement: "bottom",
1555
+ portal: !0,
1556
+ children: /* @__PURE__ */ jsx(Card, {
1557
+ className: menuCard,
1558
+ radius: 2,
1559
+ scheme: "dark",
1560
+ children: /* @__PURE__ */ jsx(MenuButton, {
1561
+ button: t15,
1562
+ id: t16,
1563
+ menu: t30,
1564
+ onClose: t31,
1565
+ onOpen: t32,
1566
+ popover: t33
1567
+ })
1568
+ })
1569
+ }), $[71] = t15, $[72] = t16, $[73] = t30, $[74] = t33, $[75] = t34) : t34 = $[75];
1570
+ let t35;
1571
+ $[76] !== menuOpen || $[77] !== t34 ? (t35 = /* @__PURE__ */ jsx("div", {
1572
+ className: menuSlot,
1573
+ "data-visible": menuOpen,
1574
+ children: t34
1575
+ }), $[76] = menuOpen, $[77] = t34, $[78] = t35) : t35 = $[78];
1576
+ let t36;
1577
+ return $[79] !== dragControls || $[80] !== t12 || $[81] !== t35 || $[82] !== t5 || $[83] !== theme.slug ? (t36 = /* @__PURE__ */ jsxs(Reorder.Item, {
1578
+ as: "div",
1579
+ className: root,
1580
+ dragControls,
1581
+ dragListener: !1,
1582
+ onDragEnd: t3,
1583
+ onDragStart: t4,
1584
+ ref: t5,
1585
+ value: theme.slug,
1586
+ whileDrag: t6,
1587
+ children: [t12, t35]
1588
+ }), $[79] = dragControls, $[80] = t12, $[81] = t35, $[82] = t5, $[83] = theme.slug, $[84] = t36) : t36 = $[84], t36;
1589
+ }
1590
+ function _temp$1(candidate_0) {
1591
+ return candidate_0.slug;
1592
+ }
1593
+ var cardGrid = "twjqgh0";
1594
+ /**
1595
+ * One column at the sidebar's default width; wider sidebars — and the
1596
+ * overlay on small screens — fit more cards per row
1597
+ */
1598
+ /**
1599
+ * The flow for picking a theme: a grid of theme cards — the configured theme,
1600
+ * the presets and the user's own themes — that drag into the order the user
1601
+ * wants, with the entry points to the add and restore flows below. A theme
1602
+ * code someone shared pastes right into the list, or through the paste button.
1603
+ *
1604
+ * @internal
1605
+ */
1606
+ function ThemeList() {
1607
+ let $ = c(38), { themes, removed, active, send } = useThemer(), { addThemeFromClipboard } = useThemeCodes(), [pasting, setPasting] = useState(!1);
1608
+ usePasteThemeCodes();
1609
+ let t0;
1610
+ $[0] === send ? t0 = $[1] : (t0 = (palette, file) => send({
1611
+ type: "theme.add",
1612
+ title: titleFromFileName(file.name),
1613
+ options: optionsFromImagePalette(palette),
1614
+ palette,
1615
+ imageUrl: URL.createObjectURL(file)
1616
+ }), $[0] = send, $[1] = t0);
1617
+ let { busy, pickImage } = useImagePalette(t0), t1;
1618
+ $[2] === send ? t1 = $[3] : (t1 = (order) => send({
1619
+ type: "theme.reorder",
1620
+ order
1621
+ }), $[2] = send, $[3] = t1);
1622
+ let t2;
1623
+ $[4] === themes ? t2 = $[5] : (t2 = themes.map(_temp), $[4] = themes, $[5] = t2);
1624
+ let t3;
1625
+ if ($[6] !== active || $[7] !== themes) {
1626
+ let t4;
1627
+ $[9] === active ? t4 = $[10] : (t4 = (theme_0) => /* @__PURE__ */ jsx(ThemeCard, {
1628
+ active: theme_0.slug === active.slug,
1629
+ theme: theme_0
1630
+ }, theme_0.slug), $[9] = active, $[10] = t4), t3 = themes.map(t4), $[6] = active, $[7] = themes, $[8] = t3;
1631
+ } else t3 = $[8];
1632
+ let t4;
1633
+ $[11] !== t1 || $[12] !== t2 || $[13] !== t3 ? (t4 = /* @__PURE__ */ jsx(MotionConfig, {
1634
+ reducedMotion: "user",
1635
+ children: /* @__PURE__ */ jsx(Reorder.Group, {
1636
+ as: "div",
1637
+ className: cardGrid,
1638
+ onReorder: t1,
1639
+ values: t2,
1640
+ children: t3
1641
+ })
1642
+ }), $[11] = t1, $[12] = t2, $[13] = t3, $[14] = t4) : t4 = $[14];
1643
+ let t5;
1644
+ $[15] !== removed.length || $[16] !== send ? (t5 = removed.length > 0 && /* @__PURE__ */ jsx(Button, {
1645
+ gap: 2,
1646
+ icon: RestoreIcon,
1647
+ mode: "bleed",
1648
+ onClick: () => send({ type: "flow.removed" }),
1649
+ padding: 2,
1650
+ text: `Show removed (${removed.length})`,
1651
+ width: "fill"
1652
+ }), $[15] = removed.length, $[16] = send, $[17] = t5) : t5 = $[17];
1653
+ let t6;
1654
+ $[18] !== t4 || $[19] !== t5 ? (t6 = /* @__PURE__ */ jsx(ScrollArea, {
1655
+ padding: 3,
1656
+ children: /* @__PURE__ */ jsxs(Stack, {
1657
+ gap: 4,
1658
+ children: [t4, t5]
1659
+ })
1660
+ }), $[18] = t4, $[19] = t5, $[20] = t6) : t6 = $[20];
1661
+ let t7;
1662
+ $[21] === send ? t7 = $[22] : (t7 = /* @__PURE__ */ jsx(Box, {
1663
+ flex: 1,
1664
+ children: /* @__PURE__ */ jsx(TooltipButton, {
1665
+ icon: AddIcon,
1666
+ mode: "ghost",
1667
+ onClick: () => send({ type: "theme.add" }),
1668
+ text: "Add theme",
1669
+ tooltip: "Add a theme based on the applied one",
1670
+ width: "fill"
1671
+ })
1672
+ }), $[21] = send, $[22] = t7);
1673
+ let t8;
1674
+ $[23] !== busy || $[24] !== pickImage ? (t8 = /* @__PURE__ */ jsx(ImageFileButton, {
1675
+ loading: busy,
1676
+ mode: "ghost",
1677
+ onFile: pickImage,
1678
+ tooltip: "Add a theme from the colors of an image"
1679
+ }), $[23] = busy, $[24] = pickImage, $[25] = t8) : t8 = $[25];
1680
+ let t9;
1681
+ $[26] === addThemeFromClipboard ? t9 = $[27] : (t9 = /* @__PURE__ */ jsx(TooltipButton, {
1682
+ icon: ClipboardIcon,
1683
+ mode: "ghost",
1684
+ onClick: async () => {
1685
+ await addThemeFromClipboard() !== "added" && setPasting(!0);
1686
+ },
1687
+ tooltip: "Add a theme from a code someone shared"
1688
+ }), $[26] = addThemeFromClipboard, $[27] = t9);
1689
+ let t10;
1690
+ $[28] !== t7 || $[29] !== t8 || $[30] !== t9 ? (t10 = /* @__PURE__ */ jsx(Card, {
1691
+ borderTop: !0,
1692
+ padding: 3,
1693
+ children: /* @__PURE__ */ jsxs(Flex, {
1694
+ gap: 2,
1695
+ children: [
1696
+ t7,
1697
+ t8,
1698
+ t9
1699
+ ]
1700
+ })
1701
+ }), $[28] = t7, $[29] = t8, $[30] = t9, $[31] = t10) : t10 = $[31];
1702
+ let t11;
1703
+ $[32] === pasting ? t11 = $[33] : (t11 = pasting && /* @__PURE__ */ jsx(PasteThemeDialog, { onClose: () => setPasting(!1) }), $[32] = pasting, $[33] = t11);
1704
+ let t12;
1705
+ return $[34] !== t10 || $[35] !== t11 || $[36] !== t6 ? (t12 = /* @__PURE__ */ jsxs(Fragment, { children: [
1706
+ t6,
1707
+ t10,
1708
+ t11
1709
+ ] }), $[34] = t10, $[35] = t11, $[36] = t6, $[37] = t12) : t12 = $[37], t12;
1710
+ }
1711
+ function _temp(theme) {
1712
+ return theme.slug;
1713
+ }
1714
+ /**
1715
+ * Serializes theme options into the `buildTheme` call to paste into
1716
+ * `sanity.config.ts`, keeping only what differs from the derived defaults in
1717
+ * each scheme.
1718
+ *
1719
+ * Options that boil down to the stock Studio theme need nothing from this
1720
+ * package — so they serialize to a bare `buildTheme()` from `@sanity/ui/theme`
1721
+ * instead.
1722
+ *
1723
+ * @internal
1724
+ */
1725
+ function createThemeSnippet(options) {
1726
+ let minimized = minimizeOptions(options);
1727
+ if (minimized === null) return "import {buildTheme} from '@sanity/ui/theme'\n\nexport const theme = buildTheme()\n";
1728
+ let lines = [];
1729
+ for (let scheme of SCHEMES) {
1730
+ let values = minimized[scheme];
1731
+ values && lines.push(serializeScheme(scheme, values));
1732
+ }
1733
+ return `import {buildTheme} from '@sanity/themer'\n\nexport const theme = buildTheme({\n${lines.join("\n")}\n})\n`;
1734
+ }
1735
+ /** A scheme with up to two options fits on one line, more spread out */
1736
+ function serializeScheme(scheme, values) {
1737
+ let entries = [];
1738
+ 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 },`;
1739
+ }
1740
+ registerLanguage(typescript);
1741
+ /**
1742
+ * Shows the `buildTheme` snippet that makes a theme permanent, in a dialog so
1743
+ * that it does not take up sidebar space or make the sidebar scroll as the
1744
+ * snippet changes shape with the theme.
1745
+ *
1746
+ * @internal
1747
+ */
1748
+ function ThemeSnippetDialog(props) {
1749
+ let $ = c(23), { onClose, theme } = props, toast = useToast(), t0;
1750
+ $[0] === theme.options ? t0 = $[1] : (t0 = createThemeSnippet(theme.options), $[0] = theme.options, $[1] = t0);
1751
+ let snippet = t0, t1;
1752
+ $[2] !== snippet || $[3] !== toast ? (t1 = async () => {
1753
+ try {
1754
+ await navigator.clipboard.writeText(snippet), toast.push({
1755
+ status: "success",
1756
+ title: "Copied the theme to the clipboard"
1757
+ });
1758
+ } catch {
1759
+ toast.push({
1760
+ status: "error",
1761
+ title: "Could not copy the theme"
1762
+ });
1763
+ }
1764
+ }, $[2] = snippet, $[3] = toast, $[4] = t1) : t1 = $[4];
1765
+ let handleCopy = t1, t2;
1766
+ $[5] === handleCopy ? t2 = $[6] : (t2 = /* @__PURE__ */ jsx(Flex, {
1767
+ gap: 2,
1768
+ justify: "flex-end",
1769
+ padding: 3,
1770
+ children: /* @__PURE__ */ jsx(Button, {
1771
+ gap: 2,
1772
+ icon: ClipboardIcon,
1773
+ mode: "ghost",
1774
+ onClick: () => void handleCopy(),
1775
+ padding: 2,
1776
+ text: "Copy"
1777
+ })
1778
+ }), $[5] = handleCopy, $[6] = t2);
1779
+ let t3;
1780
+ $[7] === Symbol.for("react.memo_cache_sentinel") ? (t3 = /* @__PURE__ */ jsx("code", { children: "sanity.config.ts" }), $[7] = t3) : t3 = $[7];
1781
+ let t4;
1782
+ $[8] === Symbol.for("react.memo_cache_sentinel") ? (t4 = /* @__PURE__ */ jsx("code", { children: "theme" }), $[8] = t4) : t4 = $[8];
1783
+ let t5;
1784
+ $[9] === Symbol.for("react.memo_cache_sentinel") ? (t5 = /* @__PURE__ */ jsx("code", { children: "defineConfig" }), $[9] = t5) : t5 = $[9];
1785
+ let t6;
1786
+ $[10] === theme.title ? t6 = $[11] : (t6 = displayTitle(theme.title), $[10] = theme.title, $[11] = t6);
1787
+ let t7;
1788
+ $[12] === t6 ? t7 = $[13] : (t7 = /* @__PURE__ */ jsxs(Text, {
1789
+ muted: !0,
1790
+ size: 1,
1791
+ children: [
1792
+ "Export this from ",
1793
+ t3,
1794
+ " and pass it as the ",
1795
+ t4,
1796
+ " of",
1797
+ " ",
1798
+ t5,
1799
+ " to make “",
1800
+ t6,
1801
+ "” the Studio theme without this tool."
1802
+ ]
1803
+ }), $[12] = t6, $[13] = t7);
1804
+ let t8;
1805
+ $[14] === snippet ? t8 = $[15] : (t8 = /* @__PURE__ */ jsx(Card, {
1806
+ border: !0,
1807
+ overflow: "auto",
1808
+ padding: 3,
1809
+ radius: 2,
1810
+ tone: "transparent",
1811
+ children: /* @__PURE__ */ jsx(Code, {
1812
+ language: "ts",
1813
+ size: 1,
1814
+ children: snippet
1815
+ })
1816
+ }), $[14] = snippet, $[15] = t8);
1817
+ let t9;
1818
+ $[16] !== t7 || $[17] !== t8 ? (t9 = /* @__PURE__ */ jsx(Box, {
1819
+ padding: 4,
1820
+ children: /* @__PURE__ */ jsxs(Stack, {
1821
+ gap: 4,
1822
+ children: [t7, t8]
1823
+ })
1824
+ }), $[16] = t7, $[17] = t8, $[18] = t9) : t9 = $[18];
1825
+ let t10;
1826
+ return $[19] !== onClose || $[20] !== t2 || $[21] !== t9 ? (t10 = /* @__PURE__ */ jsx(Dialog, {
1827
+ animate: !0,
1828
+ footer: t2,
1829
+ header: "Add to your config",
1830
+ id: "themer-snippet",
1831
+ onClickOutside: onClose,
1832
+ onClose,
1833
+ width: 1,
1834
+ children: t9
1835
+ }), $[19] = onClose, $[20] = t2, $[21] = t9, $[22] = t10) : t10 = $[22], t10;
1836
+ }
1837
+ var title = "c2pqej0";
1838
+ const VIEW_TITLES = {
1839
+ list: "Themer",
1840
+ edit: "Edit theme",
1841
+ removed: "Removed themes"
1842
+ };
1843
+ /**
1844
+ * The themer sidebar: a header that navigates between the flows and toggles
1845
+ * the split preview, the flow itself — picking a theme, editing one, or
1846
+ * restoring removed ones — and the dialog with the `buildTheme` snippet of
1847
+ * the applied theme.
1848
+ *
1849
+ * @internal
1850
+ */
1851
+ function ThemerSidebar() {
1852
+ let $ = c(42), { active, split, view, navbarHeight, send } = useThemer(), [snippetOpen, setSnippetOpen] = useState(!1), inList = view.name === "list", t0;
1853
+ $[0] === navbarHeight ? t0 = $[1] : (t0 = navbarHeight === null ? void 0 : { height: navbarHeight }, $[0] = navbarHeight, $[1] = t0);
1854
+ let t1;
1855
+ $[2] !== inList || $[3] !== send ? (t1 = !inList && /* @__PURE__ */ jsx(TooltipButton, {
1856
+ icon: ArrowLeftIcon,
1857
+ mode: "bleed",
1858
+ onClick: () => send({ type: "flow.list" }),
1859
+ padding: 2,
1860
+ tooltip: "Back to the themes"
1861
+ }), $[2] = inList, $[3] = send, $[4] = t1) : t1 = $[4];
1862
+ let t2 = VIEW_TITLES[view.name], t3;
1863
+ $[5] === t2 ? t3 = $[6] : (t3 = /* @__PURE__ */ jsx(Box, {
1864
+ className: title,
1865
+ flex: 1,
1866
+ children: /* @__PURE__ */ jsx(Text, {
1867
+ size: 1,
1868
+ textOverflow: "ellipsis",
1869
+ weight: "semibold",
1870
+ children: t2
1871
+ })
1872
+ }), $[5] = t2, $[6] = t3);
1873
+ let t4;
1874
+ $[7] !== send || $[8] !== split || $[9] !== view.name ? (t4 = view.name !== "removed" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(TooltipButton, {
1875
+ "aria-pressed": split,
1876
+ icon: SplitVerticalIcon,
1877
+ mode: "bleed",
1878
+ onClick: () => send({ type: "preview.toggle" }),
1879
+ padding: 2,
1880
+ selected: split,
1881
+ tooltip: "Show light and dark side by side"
1882
+ }), /* @__PURE__ */ jsx(TooltipButton, {
1883
+ icon: CodeBlockIcon,
1884
+ mode: "bleed",
1885
+ onClick: () => setSnippetOpen(!0),
1886
+ padding: 2,
1887
+ tooltip: "Show the code for the applied theme"
1888
+ })] }), $[7] = send, $[8] = split, $[9] = view.name, $[10] = t4) : t4 = $[10];
1889
+ let t5;
1890
+ $[11] === send ? t5 = $[12] : (t5 = /* @__PURE__ */ jsx(TooltipButton, {
1891
+ icon: CloseIcon,
1892
+ mode: "bleed",
1893
+ onClick: () => send({ type: "sidebar.close" }),
1894
+ padding: 2,
1895
+ tooltip: "Close themer"
1896
+ }), $[11] = send, $[12] = t5);
1897
+ let t6;
1898
+ $[13] !== t4 || $[14] !== t5 ? (t6 = /* @__PURE__ */ jsxs(Flex, { children: [t4, t5] }), $[13] = t4, $[14] = t5, $[15] = t6) : t6 = $[15];
1899
+ let t7;
1900
+ $[16] !== t1 || $[17] !== t3 || $[18] !== t6 ? (t7 = /* @__PURE__ */ jsxs(Flex, {
1901
+ align: "center",
1902
+ gap: 1,
1903
+ height: "fill",
1904
+ children: [
1905
+ t1,
1906
+ t3,
1907
+ t6
1908
+ ]
1909
+ }), $[16] = t1, $[17] = t3, $[18] = t6, $[19] = t7) : t7 = $[19];
1910
+ let t8;
1911
+ $[20] !== t0 || $[21] !== t7 ? (t8 = /* @__PURE__ */ jsx(Card, {
1912
+ borderBottom: !0,
1913
+ padding: 3,
1914
+ sizing: "border",
1915
+ style: t0,
1916
+ children: t7
1917
+ }), $[20] = t0, $[21] = t7, $[22] = t8) : t8 = $[22];
1918
+ let t9;
1919
+ $[23] === view.name ? t9 = $[24] : (t9 = view.name === "list" && /* @__PURE__ */ jsx(ThemeList, {}), $[23] = view.name, $[24] = t9);
1920
+ let t10;
1921
+ $[25] !== view.focusTitle || $[26] !== view.name || $[27] !== view.slug ? (t10 = view.name === "edit" && /* @__PURE__ */ jsx(ThemeEditor, {
1922
+ focusTitle: view.focusTitle,
1923
+ slug: view.slug
1924
+ }), $[25] = view.focusTitle, $[26] = view.name, $[27] = view.slug, $[28] = t10) : t10 = $[28];
1925
+ let t11;
1926
+ $[29] === view.name ? t11 = $[30] : (t11 = view.name === "removed" && /* @__PURE__ */ jsx(RemovedThemes, {}), $[29] = view.name, $[30] = t11);
1927
+ let t12;
1928
+ $[31] !== t10 || $[32] !== t11 || $[33] !== t8 || $[34] !== t9 ? (t12 = /* @__PURE__ */ jsxs(Flex, {
1929
+ direction: "column",
1930
+ height: "fill",
1931
+ children: [
1932
+ t8,
1933
+ t9,
1934
+ t10,
1935
+ t11
1936
+ ]
1937
+ }), $[31] = t10, $[32] = t11, $[33] = t8, $[34] = t9, $[35] = t12) : t12 = $[35];
1938
+ let t13;
1939
+ $[36] !== active || $[37] !== snippetOpen ? (t13 = snippetOpen && /* @__PURE__ */ jsx(ThemeSnippetDialog, {
1940
+ onClose: () => setSnippetOpen(!1),
1941
+ theme: active
1942
+ }), $[36] = active, $[37] = snippetOpen, $[38] = t13) : t13 = $[38];
1943
+ let t14;
1944
+ return $[39] !== t12 || $[40] !== t13 ? (t14 = /* @__PURE__ */ jsxs(Card, {
1945
+ height: "fill",
1946
+ children: [t12, t13]
1947
+ }), $[39] = t12, $[40] = t13, $[41] = t14) : t14 = $[41], t14;
1948
+ }
1949
+ var content = "bgn4np4", frame = "bgn4np3", sidebar = "bgn4np2";
1950
+ /** How far one arrow key press resizes the sidebar */
1951
+ const WIDTH_STORAGE_KEY = "sanityStudio:themer:width";
1952
+ function clampWidth(width) {
1953
+ return Math.min(400, Math.max(200, Math.round(width)));
1954
+ }
1955
+ function readStoredWidth() {
1956
+ try {
1957
+ let stored = Number(localStorage.getItem(WIDTH_STORAGE_KEY));
1958
+ return Number.isFinite(stored) && stored > 0 ? clampWidth(stored) : 200;
1959
+ } catch {
1960
+ return 200;
1961
+ }
1962
+ }
1963
+ function writeStoredWidth(width) {
1964
+ try {
1965
+ width === 200 ? localStorage.removeItem(WIDTH_STORAGE_KEY) : localStorage.setItem(WIDTH_STORAGE_KEY, String(width));
1966
+ } catch {}
1967
+ }
1968
+ /**
1969
+ * The themer sidebar, in a layer along the right edge of the Studio. Its left
1970
+ * edge drags (or arrow-keys) it wider, up to twice its default width — the
1971
+ * width sticks between sessions. As an overlay (small screens) it covers the
1972
+ * Studio edge to edge instead, with nothing to resize.
1973
+ *
1974
+ * @internal
1975
+ */
1976
+ function ResizableSidebar(props) {
1977
+ let $ = c(24), { overlay } = props, [width, setWidth] = useState(readStoredWidth), [dragging, setDragging] = useState(!1), drag = useRef(null), t0, t1;
1978
+ $[0] === width ? (t0 = $[1], t1 = $[2]) : (t0 = () => writeStoredWidth(width), t1 = [width], $[0] = width, $[1] = t0, $[2] = t1), useEffect(t0, t1);
1979
+ let t2;
1980
+ $[3] === width ? t2 = $[4] : (t2 = (event) => {
1981
+ event.button === 0 && (event.preventDefault(), event.currentTarget.setPointerCapture(event.pointerId), drag.current = {
1982
+ pointerId: event.pointerId,
1983
+ startX: event.clientX,
1984
+ startWidth: width
1985
+ }, setDragging(!0));
1986
+ }, $[3] = width, $[4] = t2);
1987
+ let handlePointerDown = t2, t3;
1988
+ $[5] === Symbol.for("react.memo_cache_sentinel") ? (t3 = (event_0) => {
1989
+ drag.current && drag.current.pointerId === event_0.pointerId && setWidth(clampWidth(drag.current.startWidth - (event_0.clientX - drag.current.startX)));
1990
+ }, $[5] = t3) : t3 = $[5];
1991
+ let handlePointerMove = t3, t4;
1992
+ $[6] === Symbol.for("react.memo_cache_sentinel") ? (t4 = (event_1) => {
1993
+ drag.current && drag.current.pointerId === event_1.pointerId && (drag.current = null, setDragging(!1));
1994
+ }, $[6] = t4) : t4 = $[6];
1995
+ let handlePointerUp = t4, t5;
1996
+ $[7] === Symbol.for("react.memo_cache_sentinel") ? (t5 = (event_2) => {
1997
+ let step = {
1998
+ ArrowLeft: 16,
1999
+ ArrowRight: -16,
2000
+ Home: 400,
2001
+ End: -400
2002
+ }[event_2.key];
2003
+ step !== void 0 && (event_2.preventDefault(), setWidth((current) => clampWidth(current + step)));
2004
+ }, $[7] = t5) : t5 = $[7];
2005
+ let handleKeyDown = t5, t6;
2006
+ $[8] !== overlay || $[9] !== width ? (t6 = overlay ? void 0 : { width }, $[8] = overlay, $[9] = width, $[10] = t6) : t6 = $[10];
2007
+ let t7 = !overlay, t8;
2008
+ $[11] !== dragging || $[12] !== handlePointerDown || $[13] !== overlay || $[14] !== width ? (t8 = !overlay && /* @__PURE__ */ jsx("div", {
2009
+ "aria-label": "Resize the themer",
2010
+ className: "bgn4np5",
2011
+ "aria-orientation": "vertical",
2012
+ "aria-valuemax": 400,
2013
+ "aria-valuemin": 200,
2014
+ "aria-valuenow": width,
2015
+ "data-dragging": dragging,
2016
+ onDoubleClick: () => setWidth(200),
2017
+ onKeyDown: handleKeyDown,
2018
+ onPointerCancel: handlePointerUp,
2019
+ onPointerDown: handlePointerDown,
2020
+ onPointerMove: handlePointerMove,
2021
+ onPointerUp: handlePointerUp,
2022
+ role: "separator",
2023
+ tabIndex: 0
2024
+ }), $[11] = dragging, $[12] = handlePointerDown, $[13] = overlay, $[14] = width, $[15] = t8) : t8 = $[15];
2025
+ let t9;
2026
+ $[16] === Symbol.for("react.memo_cache_sentinel") ? (t9 = /* @__PURE__ */ jsx(Box, {
2027
+ className: content,
2028
+ height: "fill",
2029
+ overflow: "hidden",
2030
+ children: /* @__PURE__ */ jsx(ThemerSidebar, {})
2031
+ }), $[16] = t9) : t9 = $[16];
2032
+ let t10;
2033
+ $[17] !== t7 || $[18] !== t8 ? (t10 = /* @__PURE__ */ jsxs(Card, {
2034
+ borderLeft: t7,
2035
+ className: frame,
2036
+ height: "fill",
2037
+ children: [t8, t9]
2038
+ }), $[17] = t7, $[18] = t8, $[19] = t10) : t10 = $[19];
2039
+ let t11;
2040
+ return $[20] !== overlay || $[21] !== t10 || $[22] !== t6 ? (t11 = /* @__PURE__ */ jsx(Layer, {
2041
+ className: sidebar,
2042
+ "data-overlay": overlay,
2043
+ style: t6,
2044
+ zOffset: 100,
2045
+ children: t10
2046
+ }), $[20] = overlay, $[21] = t10, $[22] = t6, $[23] = t11) : t11 = $[23], t11;
2047
+ }
2048
+ export { ResizableSidebar as default };
2049
+
2050
+ //# sourceMappingURL=ResizableSidebar-DD787kQr.js.map