@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.
- package/README.md +1 -1
- package/dist/ResizableSidebar-DD787kQr.js +2050 -0
- package/dist/ResizableSidebar-DD787kQr.js.map +1 -0
- package/dist/ResizableSidebar.css.ts.vanilla-M9EuPu-q.js +422 -0
- package/dist/ResizableSidebar.css.ts.vanilla-M9EuPu-q.js.map +1 -0
- package/dist/bundle.css +555 -555
- package/dist/tool.js +368 -2576
- package/dist/tool.js.map +1 -1
- package/package.json +4 -4
package/dist/tool.js
CHANGED
|
@@ -1,457 +1,18 @@
|
|
|
1
1
|
import "@sanity/themer/bundle.css";
|
|
2
|
-
import {
|
|
2
|
+
import { l as isColor, n as buildTheme, o as SCHEMES, t as presets } from "./presets-CqBgS3A-.js";
|
|
3
|
+
import { a as duplicateTitle, i as displayTitle, l as IMAGE_PALETTE_KEYS, o as initialThemerState, r as createCustomTheme, s as resolveThemes, v as ThemerContext, y as useThemer } from "./ResizableSidebar.css.ts.vanilla-M9EuPu-q.js";
|
|
4
|
+
import { blue, cyan, green, magenta, orange, purple, red, yellow } from "@sanity/color";
|
|
3
5
|
import { c } from "react/compiler-runtime";
|
|
4
6
|
import { definePlugin, useColorSchemeValue } from "sanity";
|
|
5
|
-
import {
|
|
7
|
+
import { Button, Card, Flex, Text, ThemeProvider, useMediaIndex, usePrefersReducedMotion } from "@sanity/ui";
|
|
6
8
|
import { useActor, useSelector } from "@xstate/react";
|
|
7
|
-
import { Activity, ViewTransition, addTransitionType,
|
|
9
|
+
import { Activity, ViewTransition, addTransitionType, lazy, startTransition, useDeferredValue, useEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
8
10
|
import { assign, not, or, setup, stateIn } from "xstate";
|
|
9
|
-
import {
|
|
10
|
-
import { CloseIcon } from "@sanity/icons/Close";
|
|
11
|
-
import { CodeBlockIcon } from "@sanity/icons/CodeBlock";
|
|
12
|
-
import { SplitVerticalIcon } from "@sanity/icons/SplitVertical";
|
|
13
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
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 { Tooltip } from "@sanity/ui/tooltip";
|
|
19
|
-
import { ImageIcon } from "@sanity/icons/Image";
|
|
20
|
-
import { useToast } from "@sanity/ui/toast";
|
|
21
|
-
import { AddIcon } from "@sanity/icons/Add";
|
|
22
|
-
import { ClipboardIcon } from "@sanity/icons/Clipboard";
|
|
23
|
-
import { RestoreIcon } from "@sanity/icons/Restore";
|
|
24
|
-
import { MotionConfig, Reorder, useDragControls } from "motion/react";
|
|
25
|
-
import { ArrowDownIcon } from "@sanity/icons/ArrowDown";
|
|
26
|
-
import { ArrowUpIcon } from "@sanity/icons/ArrowUp";
|
|
27
|
-
import { CopyIcon } from "@sanity/icons/Copy";
|
|
28
|
-
import { EditIcon } from "@sanity/icons/Edit";
|
|
29
|
-
import { EllipsisHorizontalIcon } from "@sanity/icons/EllipsisHorizontal";
|
|
30
|
-
import { ShareIcon } from "@sanity/icons/Share";
|
|
31
|
-
import { Menu, MenuButton, MenuDivider, MenuItem } from "@sanity/ui/menu";
|
|
32
|
-
import scrollIntoView from "scroll-into-view-if-needed";
|
|
33
|
-
import { Code } from "@sanity/ui/code";
|
|
34
|
-
import { registerLanguage } from "react-refractor";
|
|
35
|
-
import typescript from "refractor/typescript";
|
|
11
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
36
12
|
import { ColorWheelIcon } from "@sanity/icons/ColorWheel";
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
function useThemer() {
|
|
41
|
-
let context = useContext(ThemerContext);
|
|
42
|
-
if (!context) throw Error("useThemer must be used within the `themerTool` plugin");
|
|
43
|
-
return context;
|
|
44
|
-
}
|
|
45
|
-
/** @internal */
|
|
46
|
-
const IMAGE_PALETTE_KEYS = [
|
|
47
|
-
"vibrant",
|
|
48
|
-
"lightVibrant",
|
|
49
|
-
"darkVibrant",
|
|
50
|
-
"muted",
|
|
51
|
-
"lightMuted",
|
|
52
|
-
"darkMuted",
|
|
53
|
-
"dominant"
|
|
54
|
-
], IMAGE_PALETTE_VARIANTS = [
|
|
55
|
-
"muted",
|
|
56
|
-
"vibrant",
|
|
57
|
-
"lightMuted",
|
|
58
|
-
"lightVibrant",
|
|
59
|
-
"darkMuted",
|
|
60
|
-
"darkVibrant"
|
|
61
|
-
], IMAGE_PALETTE_TITLES = {
|
|
62
|
-
dominant: "Dominant",
|
|
63
|
-
vibrant: "Vibrant",
|
|
64
|
-
lightVibrant: "Light vibrant",
|
|
65
|
-
darkVibrant: "Dark vibrant",
|
|
66
|
-
muted: "Muted",
|
|
67
|
-
lightMuted: "Light muted",
|
|
68
|
-
darkMuted: "Dark muted"
|
|
69
|
-
}, TARGETS = [
|
|
70
|
-
["vibrant", {
|
|
71
|
-
lightness: [
|
|
72
|
-
.5,
|
|
73
|
-
.3,
|
|
74
|
-
.7
|
|
75
|
-
],
|
|
76
|
-
saturation: [
|
|
77
|
-
1,
|
|
78
|
-
.35,
|
|
79
|
-
1
|
|
80
|
-
]
|
|
81
|
-
}],
|
|
82
|
-
["lightVibrant", {
|
|
83
|
-
lightness: [
|
|
84
|
-
.74,
|
|
85
|
-
.55,
|
|
86
|
-
1
|
|
87
|
-
],
|
|
88
|
-
saturation: [
|
|
89
|
-
1,
|
|
90
|
-
.35,
|
|
91
|
-
1
|
|
92
|
-
]
|
|
93
|
-
}],
|
|
94
|
-
["darkVibrant", {
|
|
95
|
-
lightness: [
|
|
96
|
-
.26,
|
|
97
|
-
0,
|
|
98
|
-
.45
|
|
99
|
-
],
|
|
100
|
-
saturation: [
|
|
101
|
-
1,
|
|
102
|
-
.35,
|
|
103
|
-
1
|
|
104
|
-
]
|
|
105
|
-
}],
|
|
106
|
-
["muted", {
|
|
107
|
-
lightness: [
|
|
108
|
-
.5,
|
|
109
|
-
.3,
|
|
110
|
-
.7
|
|
111
|
-
],
|
|
112
|
-
saturation: [
|
|
113
|
-
.3,
|
|
114
|
-
0,
|
|
115
|
-
.4
|
|
116
|
-
]
|
|
117
|
-
}],
|
|
118
|
-
["lightMuted", {
|
|
119
|
-
lightness: [
|
|
120
|
-
.74,
|
|
121
|
-
.55,
|
|
122
|
-
1
|
|
123
|
-
],
|
|
124
|
-
saturation: [
|
|
125
|
-
.3,
|
|
126
|
-
0,
|
|
127
|
-
.4
|
|
128
|
-
]
|
|
129
|
-
}],
|
|
130
|
-
["darkMuted", {
|
|
131
|
-
lightness: [
|
|
132
|
-
.26,
|
|
133
|
-
0,
|
|
134
|
-
.45
|
|
135
|
-
],
|
|
136
|
-
saturation: [
|
|
137
|
-
.3,
|
|
138
|
-
0,
|
|
139
|
-
.4
|
|
140
|
-
]
|
|
141
|
-
}]
|
|
142
|
-
];
|
|
143
|
-
/**
|
|
144
|
-
* Extracts the color palette of an image from its pixels, the way the Vibrant
|
|
145
|
-
* algorithm does: the colors are quantized into a coarse histogram, and each
|
|
146
|
-
* swatch claims the color that comes closest to its target lightness and
|
|
147
|
-
* saturation — weighted by how much of the image it covers — that no other
|
|
148
|
-
* swatch has claimed yet. The dominant swatch is simply the most common
|
|
149
|
-
* color. Runs anywhere pixels come from, so it can run on device.
|
|
150
|
-
*
|
|
151
|
-
* @internal
|
|
152
|
-
*/
|
|
153
|
-
function extractImagePalette(pixels) {
|
|
154
|
-
let candidates = quantize(pixels), palette = {
|
|
155
|
-
dominant: null,
|
|
156
|
-
vibrant: null,
|
|
157
|
-
lightVibrant: null,
|
|
158
|
-
darkVibrant: null,
|
|
159
|
-
muted: null,
|
|
160
|
-
lightMuted: null,
|
|
161
|
-
darkMuted: null
|
|
162
|
-
};
|
|
163
|
-
if (candidates.length === 0) return palette;
|
|
164
|
-
let maximumPopulation = Math.max(...candidates.map((candidate) => candidate.population)), claimed = /* @__PURE__ */ new Set();
|
|
165
|
-
for (let [key, target] of TARGETS) {
|
|
166
|
-
let best = null, bestScore = -Infinity;
|
|
167
|
-
for (let candidate of candidates) {
|
|
168
|
-
if (claimed.has(candidate.hex) || !matchesTarget(candidate, target)) continue;
|
|
169
|
-
let score = scoreCandidate(candidate, target, maximumPopulation);
|
|
170
|
-
score > bestScore && (best = candidate, bestScore = score);
|
|
171
|
-
}
|
|
172
|
-
best && (palette[key] = best.hex, claimed.add(best.hex));
|
|
173
|
-
}
|
|
174
|
-
return palette.dominant = candidates.reduce((most, candidate) => candidate.population > most.population ? candidate : most).hex, palette;
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Turns the pixels into a histogram of quantized colors, each averaged over
|
|
178
|
-
* the pixels it stands for, leaving out transparent pixels and the near-black
|
|
179
|
-
* and near-white ones that are backdrop rather than color.
|
|
180
|
-
*/
|
|
181
|
-
function quantize(pixels) {
|
|
182
|
-
let { data } = pixels, buckets = /* @__PURE__ */ new Map(), length = Math.min(data.length, pixels.width * pixels.height * 4);
|
|
183
|
-
for (let offset = 0; offset + 3 < length; offset += 4) {
|
|
184
|
-
let r = data[offset], g = data[offset + 1], b = data[offset + 2];
|
|
185
|
-
if (data[offset + 3] < 125) continue;
|
|
186
|
-
let lightness = (Math.max(r, g, b) + Math.min(r, g, b)) / 510;
|
|
187
|
-
if (lightness < .05 || lightness > .95) continue;
|
|
188
|
-
let key = r >> 3 << 10 | g >> 3 << 5 | b >> 3, bucket = buckets.get(key);
|
|
189
|
-
bucket ? (bucket.count++, bucket.r += r, bucket.g += g, bucket.b += b) : buckets.set(key, {
|
|
190
|
-
count: 1,
|
|
191
|
-
r,
|
|
192
|
-
g,
|
|
193
|
-
b
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
let candidates = [];
|
|
197
|
-
for (let bucket of buckets.values()) {
|
|
198
|
-
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);
|
|
199
|
-
candidates.push({
|
|
200
|
-
hex: rgbToHex(r, g, b),
|
|
201
|
-
population: bucket.count,
|
|
202
|
-
saturation,
|
|
203
|
-
lightness
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
return candidates;
|
|
207
|
-
}
|
|
208
|
-
function matchesTarget(candidate, target) {
|
|
209
|
-
return candidate.lightness >= target.lightness[1] && candidate.lightness <= target.lightness[2] && candidate.saturation >= target.saturation[1] && candidate.saturation <= target.saturation[2];
|
|
210
|
-
}
|
|
211
|
-
function scoreCandidate(candidate, target, maximumPopulation) {
|
|
212
|
-
return 3 * (1 - Math.abs(candidate.saturation - target.saturation[0])) + 6.5 * (1 - Math.abs(candidate.lightness - target.lightness[0])) + .5 * (candidate.population / maximumPopulation);
|
|
213
|
-
}
|
|
214
|
-
function saturationAndLightness(r, g, b) {
|
|
215
|
-
let max = Math.max(r, g, b) / 255, min = Math.min(r, g, b) / 255, lightness = (max + min) / 2, delta = max - min;
|
|
216
|
-
return {
|
|
217
|
-
saturation: delta === 0 ? 0 : lightness > .5 ? delta / (2 - max - min) : delta / (max + min),
|
|
218
|
-
lightness
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
function rgbToHex(r, g, b) {
|
|
222
|
-
return `#${[
|
|
223
|
-
r,
|
|
224
|
-
g,
|
|
225
|
-
b
|
|
226
|
-
].map((channel) => channel.toString(16).padStart(2, "0")).join("")}`;
|
|
227
|
-
}
|
|
228
|
-
/** Reads `#rgb` and `#rrggbb` alike — stored palettes may hold either */
|
|
229
|
-
function hexToRgb(hex) {
|
|
230
|
-
let expanded = hex.length === 4 ? `${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex.slice(1);
|
|
231
|
-
return [
|
|
232
|
-
parseInt(expanded.slice(0, 2), 16),
|
|
233
|
-
parseInt(expanded.slice(2, 4), 16),
|
|
234
|
-
parseInt(expanded.slice(4, 6), 16)
|
|
235
|
-
];
|
|
236
|
-
}
|
|
237
|
-
/** Mixes `amount` of `color` into `base` */
|
|
238
|
-
function tint(base, color, amount) {
|
|
239
|
-
let [r1, g1, b1] = hexToRgb(base), [r2, g2, b2] = hexToRgb(color);
|
|
240
|
-
return rgbToHex(Math.round(r1 + (r2 - r1) * amount), Math.round(g1 + (g2 - g1) * amount), Math.round(b1 + (b2 - b1) * amount));
|
|
241
|
-
}
|
|
242
|
-
/** How much of the palette tints each scheme's stock background */
|
|
243
|
-
const BACKGROUND_TINT = {
|
|
244
|
-
light: .08,
|
|
245
|
-
dark: .3
|
|
246
|
-
}, BUTTON_LABEL = {
|
|
247
|
-
light: "#ffffff",
|
|
248
|
-
dark: "#000000"
|
|
249
|
-
}, ACCENT_LIGHTNESS_STEP = .01;
|
|
250
|
-
function contrastRatio(a, b) {
|
|
251
|
-
let [lighter, darker] = [relativeLuminance(a), relativeLuminance(b)].sort((x, y) => y - x);
|
|
252
|
-
return (lighter + .05) / (darker + .05);
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* Makes an accent readable as a button in the given scheme: a light scheme
|
|
256
|
-
* accent is darkened until white text on it reaches AA contrast, a dark
|
|
257
|
-
* scheme accent lightened until black text does — as far as the accent's
|
|
258
|
-
* hue and saturation allow. Accents that already pass come back untouched.
|
|
259
|
-
*
|
|
260
|
-
* @internal
|
|
261
|
-
*/
|
|
262
|
-
function readableAccent(hex, scheme) {
|
|
263
|
-
let label = BUTTON_LABEL[scheme], { h, s } = hexToHsl(hex), { l } = hexToHsl(hex), current = hex;
|
|
264
|
-
for (; contrastRatio(current, label) < 4.5;) {
|
|
265
|
-
let next = scheme === "light" ? l - ACCENT_LIGHTNESS_STEP : l + ACCENT_LIGHTNESS_STEP;
|
|
266
|
-
if (next < 0 || next > 1) break;
|
|
267
|
-
l = next, current = hslToHex({
|
|
268
|
-
h,
|
|
269
|
-
s,
|
|
270
|
-
l
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
return current;
|
|
274
|
-
}
|
|
275
|
-
/** The accent a variant gives a scheme, made readable on a button */
|
|
276
|
-
function variantAccent(palette, variant, scheme) {
|
|
277
|
-
let swatch = (variant ? palette[variant] : null) ?? palette.vibrant ?? (scheme === "light" ? palette.darkVibrant : palette.lightVibrant) ?? palette.dominant;
|
|
278
|
-
return swatch === null ? null : readableAccent(swatch, scheme);
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Derives theme options from an image palette: the accent of both schemes is
|
|
282
|
-
* the swatch of the chosen variant — or, without one, the vibrant swatch,
|
|
283
|
-
* each scheme falling back to the vibrant swatch of its own end of the scale —
|
|
284
|
-
* adjusted until it is readable as a button. The muted swatch becomes the text color, and the light and dark muted
|
|
285
|
-
* swatches tint the stock backgrounds of their scheme — enough to carry the
|
|
286
|
-
* image's tone without giving up a usable canvas. Whatever the palette lacks
|
|
287
|
-
* stays at the theme defaults.
|
|
288
|
-
*
|
|
289
|
-
* @internal
|
|
290
|
-
*/
|
|
291
|
-
function optionsFromImagePalette(palette, variant) {
|
|
292
|
-
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");
|
|
293
|
-
return light && (options.light = light), dark && (options.dark = dark), options;
|
|
294
|
-
}
|
|
295
|
-
function schemeFromPalette(accent, text, backgroundTint, scheme) {
|
|
296
|
-
let options = {};
|
|
297
|
-
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;
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Applies an image palette to existing theme options: the colors the palette
|
|
301
|
-
* provides replace the current ones scheme by scheme, and whatever it does
|
|
302
|
-
* not touch — like the contrast — stays as it is.
|
|
303
|
-
*
|
|
304
|
-
* @internal
|
|
305
|
-
*/
|
|
306
|
-
function applyImagePalette(options, palette, variant) {
|
|
307
|
-
let derived = optionsFromImagePalette(palette, variant), next = { ...options };
|
|
308
|
-
for (let scheme of SCHEMES) derived[scheme] && (next[scheme] = {
|
|
309
|
-
...options[scheme],
|
|
310
|
-
...derived[scheme]
|
|
311
|
-
});
|
|
312
|
-
return next;
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* The variant a theme is built around: the one whose swatch is the theme's
|
|
316
|
-
* accent, or `null` when the accent comes from somewhere else.
|
|
317
|
-
*
|
|
318
|
-
* @internal
|
|
319
|
-
*/
|
|
320
|
-
function currentImageVariant(options, palette) {
|
|
321
|
-
let accent = options.light?.accent;
|
|
322
|
-
return accent ? IMAGE_PALETTE_VARIANTS.find((variant) => palette[variant] !== null && variantAccent(palette, variant, "light") === accent) ?? null : null;
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* How much a swatch has going for it as an accent: saturation first, then
|
|
326
|
-
* closeness to mid lightness — the range where an accent reads best.
|
|
327
|
-
*/
|
|
328
|
-
function interest(hex) {
|
|
329
|
-
let { s, l } = hexToHsl(hex);
|
|
330
|
-
return .1 + .55 * s + .35 * (1 - Math.abs(l - .5) * 2);
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* Picks a variant for the "I'm feeling lucky" button: a random one, weighted
|
|
334
|
-
* by how interesting its swatch is as an accent, and never the one the theme
|
|
335
|
-
* already uses while there are others to pick from.
|
|
336
|
-
*
|
|
337
|
-
* @internal
|
|
338
|
-
*/
|
|
339
|
-
function pickLuckyVariant(palette, options = {}) {
|
|
340
|
-
let { exclude = null, random = Math.random } = options, candidates = IMAGE_PALETTE_VARIANTS.filter((variant) => palette[variant] !== null);
|
|
341
|
-
if (candidates.length > 1 && (candidates = candidates.filter((variant) => variant !== exclude)), candidates.length === 0) return null;
|
|
342
|
-
let weights = candidates.map((variant) => interest(palette[variant])), total = weights.reduce((sum, weight) => sum + weight, 0), pick = random() * total;
|
|
343
|
-
for (let [index, variant] of candidates.entries()) if (pick -= weights[index], pick < 0) return variant;
|
|
344
|
-
return candidates.at(-1) ?? null;
|
|
345
|
-
}
|
|
346
|
-
/** A theme title for the image a palette came from, e.g. `sunset-beach.jpg` → `sunset beach` @internal */
|
|
347
|
-
function titleFromFileName(name) {
|
|
348
|
-
return name.replace(/\.[^.]+$/, "").replace(/[-_]+/g, " ").trim() || "Image theme";
|
|
349
|
-
}
|
|
350
|
-
/**
|
|
351
|
-
* Reduces theme options to the minimal object that recreates them through
|
|
352
|
-
* `buildTheme`: in each scheme the accent is dropped when it is the stock
|
|
353
|
-
* one, the text color when it matches the one derived from the accent, and
|
|
354
|
-
* the background and contrast when they match the defaults — and a scheme
|
|
355
|
-
* with nothing left is dropped altogether. Options that boil down to the
|
|
356
|
-
* stock Studio theme reduce to `null` — they need nothing from this package.
|
|
357
|
-
*
|
|
358
|
-
* @internal
|
|
359
|
-
*/
|
|
360
|
-
function minimizeOptions(options) {
|
|
361
|
-
let resolved = resolveThemeOptions(options), minimized = {};
|
|
362
|
-
for (let scheme of SCHEMES) {
|
|
363
|
-
let values = resolved[scheme], kept = {};
|
|
364
|
-
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);
|
|
365
|
-
}
|
|
366
|
-
return Object.keys(minimized).length === 0 ? null : minimized;
|
|
367
|
-
}
|
|
368
|
-
/**
|
|
369
|
-
* Whether two sets of theme options generate the same theme, comparing their
|
|
370
|
-
* resolved forms so that derived and explicit values (and hex casing) don't
|
|
371
|
-
* matter.
|
|
372
|
-
*
|
|
373
|
-
* @internal
|
|
374
|
-
*/
|
|
375
|
-
function sameOptions(a, b) {
|
|
376
|
-
let left = resolveThemeOptions(a), right = resolveThemeOptions(b);
|
|
377
|
-
return SCHEMES.every((scheme) => sameScheme(left[scheme], right[scheme]));
|
|
378
|
-
}
|
|
379
|
-
function sameScheme(a, b) {
|
|
380
|
-
return a.accent === b.accent && a.text === b.text && a.background === b.background && a.contrast === b.contrast;
|
|
381
|
-
}
|
|
382
|
-
/** The slug of the theme the Studio config was generated from @internal */
|
|
383
|
-
const initialThemerState = {
|
|
384
|
-
active: null,
|
|
385
|
-
custom: [],
|
|
386
|
-
removed: [],
|
|
387
|
-
order: []
|
|
388
|
-
};
|
|
389
|
-
/**
|
|
390
|
-
* Resolves the list of themes: the configured theme first, then the presets
|
|
391
|
-
* (minus the ones that would only repeat the configured theme), then the
|
|
392
|
-
* user's own themes — rearranged into the order the user dragged them into,
|
|
393
|
-
* with themes that order does not know about kept in that default order after
|
|
394
|
-
* the ones it does. Removed themes are set aside so they can be restored, and
|
|
395
|
-
* the active theme falls back to the configured one when its slug no longer
|
|
396
|
-
* resolves.
|
|
397
|
-
*
|
|
398
|
-
* @internal
|
|
399
|
-
*/
|
|
400
|
-
function resolveThemes(state, baseOptions) {
|
|
401
|
-
let config = {
|
|
402
|
-
slug: "config",
|
|
403
|
-
title: "Studio config",
|
|
404
|
-
options: baseOptions,
|
|
405
|
-
source: "config"
|
|
406
|
-
}, all = [config];
|
|
407
|
-
for (let preset of presets) sameOptions(preset.options, baseOptions) || all.push({
|
|
408
|
-
slug: preset.slug,
|
|
409
|
-
title: preset.title,
|
|
410
|
-
options: preset.options,
|
|
411
|
-
source: "preset"
|
|
412
|
-
});
|
|
413
|
-
for (let theme of state.custom) all.push({
|
|
414
|
-
slug: theme.slug,
|
|
415
|
-
title: theme.title,
|
|
416
|
-
options: theme.options,
|
|
417
|
-
source: "custom",
|
|
418
|
-
...theme.palette ? { palette: theme.palette } : {}
|
|
419
|
-
});
|
|
420
|
-
let position = new Map(state.order.map((slug, index) => [slug, index])), rank = (theme) => position.get(theme.slug) ?? state.order.length;
|
|
421
|
-
all.sort((a, b) => rank(a) - rank(b));
|
|
422
|
-
let removedSlugs = new Set(state.removed), themes = all.filter((theme) => !removedSlugs.has(theme.slug));
|
|
423
|
-
return {
|
|
424
|
-
themes,
|
|
425
|
-
removed: all.filter((theme) => removedSlugs.has(theme.slug)),
|
|
426
|
-
active: themes.find((theme) => theme.slug === state.active) ?? config
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
/**
|
|
430
|
-
* Creates a custom theme with a slug that cannot collide with the presets or
|
|
431
|
-
* the configured theme.
|
|
432
|
-
*
|
|
433
|
-
* @internal
|
|
434
|
-
*/
|
|
435
|
-
function createCustomTheme(title, options, palette) {
|
|
436
|
-
return {
|
|
437
|
-
slug: `custom-${randomId()}`,
|
|
438
|
-
title,
|
|
439
|
-
options,
|
|
440
|
-
...palette ? { palette } : {}
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
/** The title of a duplicated theme @internal */
|
|
444
|
-
function duplicateTitle(title) {
|
|
445
|
-
return `${displayTitle(title)} copy`;
|
|
446
|
-
}
|
|
447
|
-
/** The title to show for a theme, falling back when the user cleared it @internal */
|
|
448
|
-
function displayTitle(title) {
|
|
449
|
-
return title.trim() || "Untitled theme";
|
|
450
|
-
}
|
|
451
|
-
function randomId() {
|
|
452
|
-
return typeof crypto < "u" && typeof crypto.randomUUID == "function" ? crypto.randomUUID() : `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
|
|
453
|
-
}
|
|
454
|
-
const STORAGE_KEY = "sanityStudio:themer:state", LEGACY_STORAGE_KEY = "sanityStudio:themer:options";
|
|
13
|
+
import { Tooltip } from "@sanity/ui/tooltip";
|
|
14
|
+
import { animate, motion, useMotionValue, useTransform } from "motion/react";
|
|
15
|
+
const STORAGE_KEY = "sanityStudio:themer:state", LEGACY_STORAGE_KEY = "sanityStudio:themer:options", VISITED_STORAGE_KEY = "sanityStudio:themer:visited";
|
|
455
16
|
function sanitizeColor(value) {
|
|
456
17
|
return typeof value == "string" && isColor(value) ? value.toLowerCase() : null;
|
|
457
18
|
}
|
|
@@ -576,6 +137,31 @@ function writeStoredState(state) {
|
|
|
576
137
|
} catch {}
|
|
577
138
|
}
|
|
578
139
|
/**
|
|
140
|
+
* Whether the themer sidebar has been opened before, in any session — the
|
|
141
|
+
* cue for the navbar to stop introducing the tool.
|
|
142
|
+
*
|
|
143
|
+
* @internal
|
|
144
|
+
*/
|
|
145
|
+
function hasVisited() {
|
|
146
|
+
try {
|
|
147
|
+
return typeof localStorage < "u" && localStorage.getItem(VISITED_STORAGE_KEY) !== null;
|
|
148
|
+
} catch {
|
|
149
|
+
return !1;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Notes that the themer sidebar has been opened, keeping the time it first
|
|
154
|
+
* was.
|
|
155
|
+
*
|
|
156
|
+
* @internal
|
|
157
|
+
*/
|
|
158
|
+
function markVisited() {
|
|
159
|
+
try {
|
|
160
|
+
if (typeof localStorage > "u" || localStorage.getItem(VISITED_STORAGE_KEY) !== null) return;
|
|
161
|
+
localStorage.setItem(VISITED_STORAGE_KEY, (/* @__PURE__ */ new Date()).toISOString());
|
|
162
|
+
} catch {}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
579
165
|
* How long the layout's motions — the panel's and the split preview's, and
|
|
580
166
|
* the cross-fade to another theme — are taken to last when the layout never
|
|
581
167
|
* says: it reports every view transition it starts, so this only ends a
|
|
@@ -1030,2119 +616,118 @@ function selectStoredState(snapshot) {
|
|
|
1030
616
|
order
|
|
1031
617
|
};
|
|
1032
618
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
bottom: 0
|
|
1038
|
-
});
|
|
1039
|
-
/**
|
|
1040
|
-
* The gutter the last scroll area measured. It is the same for every scroll
|
|
1041
|
-
* area (the reserved gutter does not depend on the content), so the next one
|
|
1042
|
-
* starts out with the right padding instead of shifting after its first
|
|
1043
|
-
* measurement — which would also throw off anything scrolling into view as
|
|
1044
|
-
* the content mounts.
|
|
1045
|
-
*/
|
|
1046
|
-
let lastGutter = 0;
|
|
1047
|
-
/**
|
|
1048
|
-
* The scrolling body of a sidebar flow. It always reserves the scrollbar's
|
|
1049
|
-
* space, so the content does not shift when the list grows or shrinks past
|
|
1050
|
-
* the point where it needs to scroll — and it takes that space out of its
|
|
1051
|
-
* right padding, so the content stays centered rather than hugging the left.
|
|
1052
|
-
* With overlay scrollbars there is no gutter and the padding stays even.
|
|
1053
|
-
*
|
|
1054
|
-
* @internal
|
|
1055
|
-
*/
|
|
1056
|
-
function ScrollArea(props) {
|
|
1057
|
-
let $ = c(15), { children, padding } = props, { space } = useTheme_v2(), ref = useRef(null), [gutter, setGutter] = useState(_temp$8), t0, t1;
|
|
1058
|
-
$[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = () => {
|
|
1059
|
-
let element = ref.current;
|
|
1060
|
-
if (!element) return;
|
|
1061
|
-
let measure = () => {
|
|
1062
|
-
lastGutter = element.offsetWidth - element.clientWidth, setGutter(lastGutter);
|
|
1063
|
-
};
|
|
1064
|
-
measure();
|
|
1065
|
-
let observer = new ResizeObserver(measure);
|
|
1066
|
-
return observer.observe(element), () => observer.disconnect();
|
|
1067
|
-
}, t1 = [], $[0] = t0, $[1] = t1) : (t0 = $[0], t1 = $[1]), useEffect(t0, t1);
|
|
1068
|
-
let t2 = space[padding], t3 = Math.max(0, space[padding] - gutter), t4 = space[padding], t5;
|
|
1069
|
-
$[2] !== t2 || $[3] !== t3 || $[4] !== t4 ? (t5 = {
|
|
1070
|
-
left: t2,
|
|
1071
|
-
right: t3,
|
|
1072
|
-
bottom: t4
|
|
1073
|
-
}, $[2] = t2, $[3] = t3, $[4] = t4, $[5] = t5) : t5 = $[5];
|
|
1074
|
-
let inset = t5, t6;
|
|
1075
|
-
$[6] === inset.right ? t6 = $[7] : (t6 = { paddingRight: inset.right }, $[6] = inset.right, $[7] = t6);
|
|
1076
|
-
let t7;
|
|
1077
|
-
$[8] !== children || $[9] !== inset ? (t7 = /* @__PURE__ */ jsx(ScrollAreaInsetContext.Provider, {
|
|
1078
|
-
value: inset,
|
|
1079
|
-
children
|
|
1080
|
-
}), $[8] = children, $[9] = inset, $[10] = t7) : t7 = $[10];
|
|
1081
|
-
let t8;
|
|
1082
|
-
return $[11] !== padding || $[12] !== t6 || $[13] !== t7 ? (t8 = /* @__PURE__ */ jsx(Box, {
|
|
1083
|
-
className: root$2,
|
|
1084
|
-
flex: 1,
|
|
1085
|
-
overflow: "auto",
|
|
1086
|
-
padding,
|
|
1087
|
-
ref,
|
|
1088
|
-
style: t6,
|
|
1089
|
-
children: t7
|
|
1090
|
-
}), $[11] = padding, $[12] = t6, $[13] = t7, $[14] = t8) : t8 = $[14], t8;
|
|
619
|
+
const QUERY = "(prefers-reduced-motion: reduce)";
|
|
620
|
+
function subscribe(onChange) {
|
|
621
|
+
let media = matchMedia(QUERY);
|
|
622
|
+
return media.addEventListener("change", onChange), () => media.removeEventListener("change", onChange);
|
|
1091
623
|
}
|
|
1092
|
-
function
|
|
1093
|
-
return
|
|
624
|
+
function getSnapshot() {
|
|
625
|
+
return matchMedia(QUERY).matches;
|
|
1094
626
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
* instead of sitting inside its padding. It goes last: it runs down to the
|
|
1098
|
-
* bottom edge too.
|
|
1099
|
-
*
|
|
1100
|
-
* @internal
|
|
1101
|
-
*/
|
|
1102
|
-
function ScrollAreaBleed(props) {
|
|
1103
|
-
let $ = c(7), { left, right, bottom } = useContext(ScrollAreaInsetContext), t0 = -left, t1 = -right, t2 = -bottom, t3;
|
|
1104
|
-
$[0] !== t0 || $[1] !== t1 || $[2] !== t2 ? (t3 = {
|
|
1105
|
-
marginLeft: t0,
|
|
1106
|
-
marginRight: t1,
|
|
1107
|
-
marginBottom: t2
|
|
1108
|
-
}, $[0] = t0, $[1] = t1, $[2] = t2, $[3] = t3) : t3 = $[3];
|
|
1109
|
-
let t4;
|
|
1110
|
-
return $[4] !== props.children || $[5] !== t3 ? (t4 = /* @__PURE__ */ jsx("div", {
|
|
1111
|
-
style: t3,
|
|
1112
|
-
children: props.children
|
|
1113
|
-
}), $[4] = props.children, $[5] = t3, $[6] = t4) : t4 = $[6], t4;
|
|
627
|
+
function getServerSnapshot() {
|
|
628
|
+
return !1;
|
|
1114
629
|
}
|
|
1115
|
-
var avatar = "_1aa9huuh", body = "_1aa9huui", button = "_1aa9huuq", colors = {
|
|
1116
|
-
bg: "var(--_1aa9huu0)",
|
|
1117
|
-
fg: "var(--_1aa9huu1)",
|
|
1118
|
-
mutedFg: "var(--_1aa9huu2)",
|
|
1119
|
-
border: "var(--_1aa9huu3)",
|
|
1120
|
-
accent: "var(--_1aa9huu4)",
|
|
1121
|
-
avatar: "var(--_1aa9huu5)",
|
|
1122
|
-
selectedBg: "var(--_1aa9huu6)",
|
|
1123
|
-
selectedFg: "var(--_1aa9huu7)",
|
|
1124
|
-
inputBg: "var(--_1aa9huu8)",
|
|
1125
|
-
inputBorder: "var(--_1aa9huu9)"
|
|
1126
|
-
}, field = "_1aa9huun", footer = "_1aa9huup", form = "_1aa9huum", input = "_1aa9huuo", line = {
|
|
1127
|
-
fg: "_1aa9huus _1aa9huur",
|
|
1128
|
-
muted: "_1aa9huut _1aa9huur",
|
|
1129
|
-
selected: "_1aa9huuu _1aa9huur"
|
|
1130
|
-
}, list = "_1aa9huuj", listRow = "_1aa9huuk", logo = "_1aa9huuf", navbar = "_1aa9huue", pane = "_1aa9huul", root$1 = "_1aa9huua", spacer = "_1aa9huug", studio = {
|
|
1131
|
-
light: "_1aa9huub",
|
|
1132
|
-
dark: "_1aa9huud _1aa9huub"
|
|
1133
|
-
};
|
|
1134
|
-
/** The document rows in the list pane — the widths of their title lines */
|
|
1135
|
-
const LIST_ROWS = [
|
|
1136
|
-
"62%",
|
|
1137
|
-
"46%",
|
|
1138
|
-
"70%",
|
|
1139
|
-
"52%"
|
|
1140
|
-
], FIELDS$1 = ["32%", "24%"];
|
|
1141
630
|
/**
|
|
1142
|
-
*
|
|
1143
|
-
*
|
|
1144
|
-
* the right — in both color schemes at once: the light scheme on the left and
|
|
1145
|
-
* the dark scheme on the right, split diagonally. It only paints colors the
|
|
1146
|
-
* theme options change (backgrounds, text, borders, the accent) so that a
|
|
1147
|
-
* glance shows what the theme does to the Studio — with few and bold enough
|
|
1148
|
-
* elements to still read when scaled down to a thumbnail.
|
|
631
|
+
* Whether the user prefers reduced motion — live, so that turning the
|
|
632
|
+
* setting on while the Studio is open takes effect right away.
|
|
1149
633
|
*
|
|
1150
634
|
* @internal
|
|
1151
635
|
*/
|
|
1152
|
-
function
|
|
1153
|
-
|
|
1154
|
-
$[0] === options ? t0 = $[1] : (t0 = buildTheme(options), $[0] = options, $[1] = t0);
|
|
1155
|
-
let theme = t0, t1;
|
|
1156
|
-
$[2] === Symbol.for("react.memo_cache_sentinel") ? (t1 = /* @__PURE__ */ jsx(MockStudio, { scheme: "light" }), $[2] = t1) : t1 = $[2];
|
|
1157
|
-
let t2;
|
|
1158
|
-
$[3] === theme ? t2 = $[4] : (t2 = /* @__PURE__ */ jsx(ThemeProvider, {
|
|
1159
|
-
scheme: "light",
|
|
1160
|
-
theme,
|
|
1161
|
-
tone: "default",
|
|
1162
|
-
children: t1
|
|
1163
|
-
}), $[3] = theme, $[4] = t2);
|
|
1164
|
-
let t3;
|
|
1165
|
-
$[5] === Symbol.for("react.memo_cache_sentinel") ? (t3 = /* @__PURE__ */ jsx(MockStudio, { scheme: "dark" }), $[5] = t3) : t3 = $[5];
|
|
1166
|
-
let t4;
|
|
1167
|
-
$[6] === theme ? t4 = $[7] : (t4 = /* @__PURE__ */ jsx(ThemeProvider, {
|
|
1168
|
-
scheme: "dark",
|
|
1169
|
-
theme,
|
|
1170
|
-
tone: "default",
|
|
1171
|
-
children: t3
|
|
1172
|
-
}), $[6] = theme, $[7] = t4);
|
|
1173
|
-
let t5;
|
|
1174
|
-
return $[8] !== t2 || $[9] !== t4 ? (t5 = /* @__PURE__ */ jsxs("div", {
|
|
1175
|
-
"aria-hidden": !0,
|
|
1176
|
-
className: root$1,
|
|
1177
|
-
children: [t2, t4]
|
|
1178
|
-
}), $[8] = t2, $[9] = t4, $[10] = t5) : t5 = $[10], t5;
|
|
1179
|
-
}
|
|
1180
|
-
function MockStudio(props) {
|
|
1181
|
-
let $ = c(19), { scheme } = props, { color } = useTheme_v2(), selected = color.selectable.default.selected, inputColor = color.input.default.enabled, t0 = studio[scheme], t1;
|
|
1182
|
-
$[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({
|
|
1183
|
-
[colors.bg]: color.bg,
|
|
1184
|
-
[colors.fg]: color.fg,
|
|
1185
|
-
[colors.mutedFg]: color.muted.fg,
|
|
1186
|
-
[colors.border]: color.border,
|
|
1187
|
-
[colors.accent]: color.button.default.primary.enabled.bg,
|
|
1188
|
-
[colors.avatar]: color.avatar.blue.bg,
|
|
1189
|
-
[colors.selectedBg]: selected.bg,
|
|
1190
|
-
[colors.selectedFg]: selected.fg,
|
|
1191
|
-
[colors.inputBg]: inputColor.bg,
|
|
1192
|
-
[colors.inputBorder]: inputColor.border
|
|
1193
|
-
}), $[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];
|
|
1194
|
-
let t2;
|
|
1195
|
-
$[11] === Symbol.for("react.memo_cache_sentinel") ? (t2 = /* @__PURE__ */ jsx("div", { className: logo }), $[11] = t2) : t2 = $[11];
|
|
1196
|
-
let t3;
|
|
1197
|
-
$[12] === Symbol.for("react.memo_cache_sentinel") ? (t3 = /* @__PURE__ */ jsxs("div", {
|
|
1198
|
-
className: navbar,
|
|
1199
|
-
children: [
|
|
1200
|
-
t2,
|
|
1201
|
-
/* @__PURE__ */ jsx("div", {
|
|
1202
|
-
className: line.fg,
|
|
1203
|
-
style: { width: "22%" }
|
|
1204
|
-
}),
|
|
1205
|
-
/* @__PURE__ */ jsx("div", { className: spacer }),
|
|
1206
|
-
/* @__PURE__ */ jsx("div", { className: avatar })
|
|
1207
|
-
]
|
|
1208
|
-
}), $[12] = t3) : t3 = $[12];
|
|
1209
|
-
let t4;
|
|
1210
|
-
$[13] === Symbol.for("react.memo_cache_sentinel") ? (t4 = /* @__PURE__ */ jsx("div", {
|
|
1211
|
-
className: list,
|
|
1212
|
-
children: LIST_ROWS.map(_temp$7)
|
|
1213
|
-
}), $[13] = t4) : t4 = $[13];
|
|
1214
|
-
let t5;
|
|
1215
|
-
$[14] === Symbol.for("react.memo_cache_sentinel") ? (t5 = /* @__PURE__ */ jsxs("div", {
|
|
1216
|
-
className: form,
|
|
1217
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
1218
|
-
className: line.fg,
|
|
1219
|
-
style: { width: "45%" }
|
|
1220
|
-
}), FIELDS$1.map(_temp2)]
|
|
1221
|
-
}), $[14] = t5) : t5 = $[14];
|
|
1222
|
-
let t6;
|
|
1223
|
-
$[15] === Symbol.for("react.memo_cache_sentinel") ? (t6 = /* @__PURE__ */ jsxs("div", {
|
|
1224
|
-
className: body,
|
|
1225
|
-
children: [t4, /* @__PURE__ */ jsxs("div", {
|
|
1226
|
-
className: pane,
|
|
1227
|
-
children: [t5, /* @__PURE__ */ jsx("div", {
|
|
1228
|
-
className: footer,
|
|
1229
|
-
children: /* @__PURE__ */ jsx("div", { className: button })
|
|
1230
|
-
})]
|
|
1231
|
-
})]
|
|
1232
|
-
}), $[15] = t6) : t6 = $[15];
|
|
1233
|
-
let t7;
|
|
1234
|
-
return $[16] !== t0 || $[17] !== t1 ? (t7 = /* @__PURE__ */ jsxs("div", {
|
|
1235
|
-
className: t0,
|
|
1236
|
-
style: t1,
|
|
1237
|
-
children: [t3, t6]
|
|
1238
|
-
}), $[16] = t0, $[17] = t1, $[18] = t7) : t7 = $[18], t7;
|
|
1239
|
-
}
|
|
1240
|
-
function _temp2(width_0) {
|
|
1241
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
1242
|
-
className: field,
|
|
1243
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
1244
|
-
className: line.muted,
|
|
1245
|
-
style: { width: width_0 }
|
|
1246
|
-
}), /* @__PURE__ */ jsx("div", { className: input })]
|
|
1247
|
-
}, width_0);
|
|
1248
|
-
}
|
|
1249
|
-
function _temp$7(width, index) {
|
|
1250
|
-
let isSelected = index === 1;
|
|
1251
|
-
return /* @__PURE__ */ jsx("div", {
|
|
1252
|
-
className: listRow,
|
|
1253
|
-
"data-selected": isSelected,
|
|
1254
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
1255
|
-
className: isSelected ? line.selected : line.muted,
|
|
1256
|
-
style: { width }
|
|
1257
|
-
})
|
|
1258
|
-
}, width);
|
|
636
|
+
function usePrefersReducedMotion$1() {
|
|
637
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
1259
638
|
}
|
|
1260
639
|
/**
|
|
1261
|
-
* The
|
|
1262
|
-
*
|
|
640
|
+
* The Studio navbar's root, the `Card` that draws its bottom border — its
|
|
641
|
+
* test id is stable and unique to the navbar, unlike its `data-ui` name
|
|
1263
642
|
*
|
|
1264
643
|
* @internal
|
|
1265
644
|
*/
|
|
1266
|
-
|
|
1267
|
-
let $ = c(2), t0;
|
|
1268
|
-
return $[0] === props.options ? t0 = $[1] : (t0 = /* @__PURE__ */ jsx(Card, {
|
|
1269
|
-
overflow: "hidden",
|
|
1270
|
-
radius: 3,
|
|
1271
|
-
shadow: 1,
|
|
1272
|
-
children: /* @__PURE__ */ jsx(ThemePreview, { options: props.options })
|
|
1273
|
-
}), $[0] = props.options, $[1] = t0), t0;
|
|
1274
|
-
}
|
|
1275
|
-
var removedThumbnail = "_1vdkp0v0";
|
|
645
|
+
const NAVBAR_SELECTOR = "[data-testid=\"studio-navbar\"]";
|
|
1276
646
|
/**
|
|
1277
|
-
* The
|
|
1278
|
-
*
|
|
1279
|
-
*
|
|
1280
|
-
* the
|
|
647
|
+
* The height of the Studio navbar inside the given element, in pixels — kept
|
|
648
|
+
* up to date as the navbar renders, remounts and resizes, and `null` until
|
|
649
|
+
* there is one. The themer's header takes this height, so that its bottom
|
|
650
|
+
* border lines up with the navbar's whatever height the Studio version, its
|
|
651
|
+
* breakpoint or a custom navbar gives it.
|
|
1281
652
|
*
|
|
1282
653
|
* @internal
|
|
1283
654
|
*/
|
|
1284
|
-
function
|
|
1285
|
-
let $ = c(
|
|
1286
|
-
|
|
1287
|
-
let
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
gap: 2,
|
|
1303
|
-
justify: "center",
|
|
1304
|
-
children: [/* @__PURE__ */ jsx(Button, {
|
|
1305
|
-
mode: "ghost",
|
|
1306
|
-
onClick: () => send({
|
|
1307
|
-
type: "theme.restore",
|
|
1308
|
-
slug: theme.slug
|
|
1309
|
-
}),
|
|
1310
|
-
padding: 2,
|
|
1311
|
-
text: "Restore"
|
|
1312
|
-
}), theme.source === "custom" && /* @__PURE__ */ jsx(Button, {
|
|
1313
|
-
mode: "ghost",
|
|
1314
|
-
onClick: () => send({
|
|
1315
|
-
type: "theme.delete",
|
|
1316
|
-
slug: theme.slug
|
|
1317
|
-
}),
|
|
1318
|
-
padding: 2,
|
|
1319
|
-
text: "Delete",
|
|
1320
|
-
tone: "critical"
|
|
1321
|
-
})]
|
|
1322
|
-
})
|
|
1323
|
-
]
|
|
1324
|
-
}, theme.slug), $[3] = send, $[4] = t1), t0 = removed.map(t1), $[0] = removed, $[1] = send, $[2] = t0;
|
|
1325
|
-
} else t0 = $[2];
|
|
1326
|
-
let t1;
|
|
1327
|
-
return $[5] === t0 ? t1 = $[6] : (t1 = /* @__PURE__ */ jsx(ScrollArea, {
|
|
1328
|
-
padding: 3,
|
|
1329
|
-
children: /* @__PURE__ */ jsx(Stack, {
|
|
1330
|
-
gap: 5,
|
|
1331
|
-
children: t0
|
|
1332
|
-
})
|
|
1333
|
-
}), $[5] = t0, $[6] = t1), t1;
|
|
655
|
+
function useStudioNavbarHeight(ref) {
|
|
656
|
+
let $ = c(3), [height, setHeight] = useState(null), t0, t1;
|
|
657
|
+
return $[0] === ref ? (t0 = $[1], t1 = $[2]) : (t0 = () => {
|
|
658
|
+
let root = ref.current;
|
|
659
|
+
if (!root) return;
|
|
660
|
+
let navbar = null, resizeObserver = new ResizeObserver(() => {
|
|
661
|
+
navbar && setHeight(navbar.getBoundingClientRect().height);
|
|
662
|
+
}), observeNavbar = () => {
|
|
663
|
+
let next = root.querySelector(NAVBAR_SELECTOR);
|
|
664
|
+
next !== navbar && (navbar && resizeObserver.unobserve(navbar), navbar = next, navbar ? (resizeObserver.observe(navbar), setHeight(navbar.getBoundingClientRect().height)) : setHeight(null));
|
|
665
|
+
}, mutationObserver = new MutationObserver(observeNavbar);
|
|
666
|
+
return mutationObserver.observe(root, {
|
|
667
|
+
childList: !0,
|
|
668
|
+
subtree: !0
|
|
669
|
+
}), observeNavbar(), () => {
|
|
670
|
+
mutationObserver.disconnect(), resizeObserver.disconnect();
|
|
671
|
+
};
|
|
672
|
+
}, t1 = [ref], $[0] = ref, $[1] = t0, $[2] = t1), useEffect(t0, t1), height;
|
|
1334
673
|
}
|
|
1335
674
|
/**
|
|
1336
|
-
*
|
|
1337
|
-
*
|
|
1338
|
-
* the
|
|
675
|
+
* `useDeferredValue` with a transition type: the returned value lags behind
|
|
676
|
+
* `value` like a deferred one — rendered in a transition, which React
|
|
677
|
+
* animates — and the view transition React starts for it carries the given
|
|
678
|
+
* type (`document.startViewTransition({types})`), so a stylesheet tells it
|
|
679
|
+
* from any other transition on the page through
|
|
680
|
+
* `:active-view-transition-type()`.
|
|
1339
681
|
*
|
|
1340
|
-
*
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
}), $[3] = tooltip, $[4] = t0);
|
|
1350
|
-
let t1 = buttonProps.text === void 0 ? tooltip : void 0, t2;
|
|
1351
|
-
$[5] !== buttonProps || $[6] !== t1 ? (t2 = /* @__PURE__ */ jsx(Button, {
|
|
1352
|
-
"aria-label": t1,
|
|
1353
|
-
gap: 2,
|
|
1354
|
-
padding: 2,
|
|
1355
|
-
...buttonProps
|
|
1356
|
-
}), $[5] = buttonProps, $[6] = t1, $[7] = t2) : t2 = $[7];
|
|
1357
|
-
let t3;
|
|
1358
|
-
return $[8] !== t0 || $[9] !== t2 ? (t3 = /* @__PURE__ */ jsx(Tooltip, {
|
|
1359
|
-
animate: !0,
|
|
1360
|
-
content: t0,
|
|
1361
|
-
placement: "bottom",
|
|
1362
|
-
portal: !0,
|
|
1363
|
-
children: t2
|
|
1364
|
-
}), $[8] = t0, $[9] = t2, $[10] = t3) : t3 = $[10], t3;
|
|
1365
|
-
}
|
|
1366
|
-
/**
|
|
1367
|
-
* A button that opens the file picker for an image, and hands the chosen file
|
|
1368
|
-
* over — the picker is a hidden file input, so nothing is uploaded anywhere.
|
|
682
|
+
* React only takes types from `startTransition`, and a deferred render is
|
|
683
|
+
* not one. So while the deferred value lags, a transition of the type joins
|
|
684
|
+
* it, with a state update of its own for the type to belong to: React renders
|
|
685
|
+
* every pending transition in one go, and the commit — the view transition
|
|
686
|
+
* — carries the types of all of them. The value itself stays with
|
|
687
|
+
* `useDeferredValue`, which reads the latest `value` as the deferred render
|
|
688
|
+
* runs: should `value` change back before that render commits, nothing
|
|
689
|
+
* changes and nothing animates — a queued state update would commit the
|
|
690
|
+
* stale value and animate it, only to animate back.
|
|
1369
691
|
*
|
|
1370
692
|
* @internal
|
|
1371
693
|
*/
|
|
1372
|
-
function
|
|
1373
|
-
let $ = c(
|
|
1374
|
-
$[0]
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
hidden: !0,
|
|
1387
|
-
onChange: (event) => {
|
|
1388
|
-
let file = event.currentTarget.files?.[0];
|
|
1389
|
-
event.currentTarget.value = "", file && onFile(file);
|
|
1390
|
-
},
|
|
1391
|
-
ref: inputRef,
|
|
1392
|
-
type: "file"
|
|
1393
|
-
}), $[6] = onFile, $[7] = t2);
|
|
1394
|
-
let t3;
|
|
1395
|
-
return $[8] !== t1 || $[9] !== t2 ? (t3 = /* @__PURE__ */ jsxs(Fragment, { children: [t1, t2] }), $[8] = t1, $[9] = t2, $[10] = t3) : t3 = $[10], t3;
|
|
694
|
+
function useTypedDeferredValue(value, type) {
|
|
695
|
+
let $ = c(5), deferred = useDeferredValue(value), [, setTransitions] = useState(0), t0, t1;
|
|
696
|
+
return $[0] !== deferred || $[1] !== type || $[2] !== value ? (t0 = () => {
|
|
697
|
+
Object.is(deferred, value) || startTransition(() => {
|
|
698
|
+
addTransitionType(type), setTransitions(_temp);
|
|
699
|
+
});
|
|
700
|
+
}, t1 = [
|
|
701
|
+
deferred,
|
|
702
|
+
type,
|
|
703
|
+
value
|
|
704
|
+
], $[0] = deferred, $[1] = type, $[2] = value, $[3] = t0, $[4] = t1) : (t0 = $[3], t1 = $[4]), useEffect(t0, t1), deferred;
|
|
705
|
+
}
|
|
706
|
+
function _temp(count) {
|
|
707
|
+
return count + 1;
|
|
1396
708
|
}
|
|
709
|
+
var layout = "_1yq4ar78", layoutTransitionType = "sanity-themer", panelTransitionClasses = {
|
|
710
|
+
slideIn: "_1yq4ar76",
|
|
711
|
+
slideOut: "_1yq4ar77"
|
|
712
|
+
}, splitTransitionClasses = {
|
|
713
|
+
resize: "_1yq4ar70",
|
|
714
|
+
crossfade: "_1yq4ar71",
|
|
715
|
+
slideIn: "_1yq4ar72",
|
|
716
|
+
slideOut: "_1yq4ar73",
|
|
717
|
+
dropIn: "_1yq4ar74",
|
|
718
|
+
dropOut: "_1yq4ar75"
|
|
719
|
+
}, studioScheme = {
|
|
720
|
+
light: "_1yq4ar79",
|
|
721
|
+
dark: "_1yq4ar7a"
|
|
722
|
+
};
|
|
1397
723
|
/**
|
|
1398
|
-
*
|
|
1399
|
-
*
|
|
1400
|
-
*
|
|
1401
|
-
* @internal
|
|
724
|
+
* Below this media index the Studio collapses its navbar into a drawer, and
|
|
725
|
+
* the themer follows suit: the sidebar covers the Studio instead of standing
|
|
726
|
+
* next to it, and the split preview stacks instead of sitting side by side.
|
|
1402
727
|
*/
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
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");
|
|
1407
|
-
canvas.width = width, canvas.height = height;
|
|
1408
|
-
let context = canvas.getContext("2d", { willReadFrequently: !0 });
|
|
1409
|
-
if (!context) throw Error("Could not draw the image");
|
|
1410
|
-
return context.drawImage(image, 0, 0, width, height), {
|
|
1411
|
-
data: context.getImageData(0, 0, width, height).data,
|
|
1412
|
-
width,
|
|
1413
|
-
height
|
|
1414
|
-
};
|
|
1415
|
-
} finally {
|
|
1416
|
-
typeof ImageBitmap < "u" && image instanceof ImageBitmap && image.close();
|
|
1417
|
-
}
|
|
1418
|
-
}
|
|
1419
|
-
async function decodeImage(file) {
|
|
1420
|
-
if (typeof createImageBitmap == "function") try {
|
|
1421
|
-
return await createImageBitmap(file);
|
|
1422
|
-
} catch {}
|
|
1423
|
-
let url = URL.createObjectURL(file);
|
|
1424
|
-
try {
|
|
1425
|
-
return await new Promise((resolve, reject) => {
|
|
1426
|
-
let image = new Image();
|
|
1427
|
-
image.addEventListener("load", () => resolve(image), { once: !0 }), image.addEventListener("error", () => reject(/* @__PURE__ */ Error("Could not decode the image")), { once: !0 }), image.src = url;
|
|
1428
|
-
});
|
|
1429
|
-
} finally {
|
|
1430
|
-
URL.revokeObjectURL(url);
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
/**
|
|
1434
|
-
* Extracts the palette of image files on device and hands it to `onPalette`,
|
|
1435
|
-
* along with the file it came from. Failures end up as a toast.
|
|
1436
|
-
*
|
|
1437
|
-
* @internal
|
|
1438
|
-
*/
|
|
1439
|
-
function useImagePalette(onPalette) {
|
|
1440
|
-
let $ = c(10), toast = useToast(), [busy, setBusy] = useState(!1), onPaletteRef = useRef(onPalette), mountedRef = useRef(!0), latestPickRef = useRef(0), t0, t1;
|
|
1441
|
-
$[0] === onPalette ? (t0 = $[1], t1 = $[2]) : (t0 = () => {
|
|
1442
|
-
onPaletteRef.current = onPalette;
|
|
1443
|
-
}, t1 = [onPalette], $[0] = onPalette, $[1] = t0, $[2] = t1), useEffect(t0, t1);
|
|
1444
|
-
let t2, t3;
|
|
1445
|
-
$[3] === Symbol.for("react.memo_cache_sentinel") ? (t2 = () => (mountedRef.current = !0, () => {
|
|
1446
|
-
mountedRef.current = !1;
|
|
1447
|
-
}), t3 = [], $[3] = t2, $[4] = t3) : (t2 = $[3], t3 = $[4]), useEffect(t2, t3);
|
|
1448
|
-
let t4;
|
|
1449
|
-
$[5] === toast ? t4 = $[6] : (t4 = (file) => {
|
|
1450
|
-
let pick = latestPickRef.current += 1;
|
|
1451
|
-
setBusy(!0);
|
|
1452
|
-
let isCurrent = () => mountedRef.current && latestPickRef.current === pick, extract = async () => {
|
|
1453
|
-
let palette = extractImagePalette(await readImagePixels(file));
|
|
1454
|
-
isCurrent() && (Object.values(palette).every(_temp$6) ? toast.push({
|
|
1455
|
-
status: "warning",
|
|
1456
|
-
title: "Found no colors in that image"
|
|
1457
|
-
}) : onPaletteRef.current(palette, file));
|
|
1458
|
-
};
|
|
1459
|
-
extract().catch(() => {
|
|
1460
|
-
isCurrent() && toast.push({
|
|
1461
|
-
status: "error",
|
|
1462
|
-
title: "Could not read that image"
|
|
1463
|
-
});
|
|
1464
|
-
}).finally(() => {
|
|
1465
|
-
isCurrent() && setBusy(!1);
|
|
1466
|
-
});
|
|
1467
|
-
}, $[5] = toast, $[6] = t4);
|
|
1468
|
-
let pickImage = t4, t5;
|
|
1469
|
-
return $[7] !== busy || $[8] !== pickImage ? (t5 = {
|
|
1470
|
-
busy,
|
|
1471
|
-
pickImage
|
|
1472
|
-
}, $[7] = busy, $[8] = pickImage, $[9] = t5) : t5 = $[9], t5;
|
|
1473
|
-
}
|
|
1474
|
-
function _temp$6(swatch) {
|
|
1475
|
-
return swatch === null;
|
|
1476
|
-
}
|
|
1477
|
-
var paletteGrid = "_17s80v42", tile = "_17s80v44", variantButton = "_17s80v46", variantFrame = "_17s80v47", variantRow = "_17s80v45", variantScale = "var(--_17s80v41)", variantScaleBox = "_17s80v48", variantWidth = "var(--_17s80v40)";
|
|
1478
|
-
/** The swatches on display: the vibrant family over the muted one */
|
|
1479
|
-
const TILES = [
|
|
1480
|
-
"vibrant",
|
|
1481
|
-
"lightVibrant",
|
|
1482
|
-
"darkVibrant",
|
|
1483
|
-
"muted",
|
|
1484
|
-
"lightMuted",
|
|
1485
|
-
"darkMuted"
|
|
1486
|
-
];
|
|
1487
|
-
/**
|
|
1488
|
-
* Takes the colors of a theme from an image, on device: the image is read
|
|
1489
|
-
* into a canvas and its palette extracted right there — nothing is uploaded.
|
|
1490
|
-
* Shows the image (while the session lasts) next to its palette, offers a
|
|
1491
|
-
* theme variant built around each swatch as a small preview, and a lucky
|
|
1492
|
-
* button that picks an interesting one.
|
|
1493
|
-
*
|
|
1494
|
-
* @internal
|
|
1495
|
-
*/
|
|
1496
|
-
function ImagePaletteSection(props) {
|
|
1497
|
-
let $ = c(38), { imageUrl, onPalette, onVariant, onLucky, options, palette } = props, { busy, pickImage } = useImagePalette(onPalette), [dragging, setDragging] = useState(!1), dragDepth = useRef(0), t0;
|
|
1498
|
-
$[0] !== options || $[1] !== palette ? (t0 = palette ? currentImageVariant(options, palette) : null, $[0] = options, $[1] = palette, $[2] = t0) : t0 = $[2];
|
|
1499
|
-
let current = t0, rowRef = useRef(null), [thumbnailWidth, setThumbnailWidth] = useState(58), t1;
|
|
1500
|
-
$[3] === Symbol.for("react.memo_cache_sentinel") ? (t1 = () => {
|
|
1501
|
-
let row = rowRef.current;
|
|
1502
|
-
if (!row) return;
|
|
1503
|
-
let measure = () => {
|
|
1504
|
-
let visible = row.clientWidth - 12 - 16;
|
|
1505
|
-
setThumbnailWidth(Math.max(24, Math.floor(visible / 2.5) - 8));
|
|
1506
|
-
};
|
|
1507
|
-
measure();
|
|
1508
|
-
let observer = new ResizeObserver(measure);
|
|
1509
|
-
return observer.observe(row), () => observer.disconnect();
|
|
1510
|
-
}, $[3] = t1) : t1 = $[3];
|
|
1511
|
-
let t2;
|
|
1512
|
-
$[4] === palette ? t2 = $[5] : (t2 = [palette], $[4] = palette, $[5] = t2), useEffect(t1, t2);
|
|
1513
|
-
let t3;
|
|
1514
|
-
bb0: {
|
|
1515
|
-
if (!palette) {
|
|
1516
|
-
let t4;
|
|
1517
|
-
$[6] === Symbol.for("react.memo_cache_sentinel") ? (t4 = [], $[6] = t4) : t4 = $[6], t3 = t4;
|
|
1518
|
-
break bb0;
|
|
1519
|
-
}
|
|
1520
|
-
let t4;
|
|
1521
|
-
$[7] !== options || $[8] !== palette ? (t4 = IMAGE_PALETTE_VARIANTS.flatMap((variant) => palette[variant] === null ? [] : [{
|
|
1522
|
-
variant,
|
|
1523
|
-
title: IMAGE_PALETTE_TITLES[variant],
|
|
1524
|
-
options: applyImagePalette(options, palette, variant)
|
|
1525
|
-
}]), $[7] = options, $[8] = palette, $[9] = t4) : t4 = $[9], t3 = t4;
|
|
1526
|
-
}
|
|
1527
|
-
let variants = t3, t4, t5;
|
|
1528
|
-
$[10] === Symbol.for("react.memo_cache_sentinel") ? (t4 = (event) => {
|
|
1529
|
-
event.preventDefault(), dragDepth.current += 1, setDragging(!0);
|
|
1530
|
-
}, t5 = () => {
|
|
1531
|
-
dragDepth.current = Math.max(0, dragDepth.current - 1), dragDepth.current === 0 && setDragging(!1);
|
|
1532
|
-
}, $[10] = t4, $[11] = t5) : (t4 = $[10], t5 = $[11]);
|
|
1533
|
-
let t6;
|
|
1534
|
-
$[12] === pickImage ? t6 = $[13] : (t6 = (event_1) => {
|
|
1535
|
-
event_1.preventDefault(), dragDepth.current = 0, setDragging(!1);
|
|
1536
|
-
let file = event_1.dataTransfer.files[0];
|
|
1537
|
-
file && pickImage(file);
|
|
1538
|
-
}, $[12] = pickImage, $[13] = t6);
|
|
1539
|
-
let t7 = dragging ? "primary" : "default", t8;
|
|
1540
|
-
$[14] === Symbol.for("react.memo_cache_sentinel") ? (t8 = /* @__PURE__ */ jsx(Box, {
|
|
1541
|
-
flex: 1,
|
|
1542
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
1543
|
-
size: 1,
|
|
1544
|
-
weight: "medium",
|
|
1545
|
-
children: "Image palette"
|
|
1546
|
-
})
|
|
1547
|
-
}), $[14] = t8) : t8 = $[14];
|
|
1548
|
-
let t9;
|
|
1549
|
-
$[15] !== busy || $[16] !== palette || $[17] !== pickImage ? (t9 = palette && /* @__PURE__ */ jsx(ImageFileButton, {
|
|
1550
|
-
loading: busy,
|
|
1551
|
-
mode: "bleed",
|
|
1552
|
-
onFile: pickImage,
|
|
1553
|
-
padding: 2,
|
|
1554
|
-
tooltip: "Pick another image"
|
|
1555
|
-
}), $[15] = busy, $[16] = palette, $[17] = pickImage, $[18] = t9) : t9 = $[18];
|
|
1556
|
-
let t10;
|
|
1557
|
-
$[19] === t9 ? t10 = $[20] : (t10 = /* @__PURE__ */ jsxs(Flex, {
|
|
1558
|
-
align: "center",
|
|
1559
|
-
gap: 2,
|
|
1560
|
-
children: [t8, t9]
|
|
1561
|
-
}), $[19] = t9, $[20] = t10);
|
|
1562
|
-
let t11;
|
|
1563
|
-
$[21] !== busy || $[22] !== current || $[23] !== imageUrl || $[24] !== onLucky || $[25] !== onVariant || $[26] !== palette || $[27] !== pickImage || $[28] !== thumbnailWidth || $[29] !== variants ? (t11 = palette ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1564
|
-
/* @__PURE__ */ jsxs("div", {
|
|
1565
|
-
className: paletteGrid,
|
|
1566
|
-
"data-with-image": imageUrl !== void 0,
|
|
1567
|
-
children: [imageUrl && /* @__PURE__ */ jsx("img", {
|
|
1568
|
-
alt: "",
|
|
1569
|
-
className: "_17s80v43",
|
|
1570
|
-
src: imageUrl
|
|
1571
|
-
}), TILES.map((key) => {
|
|
1572
|
-
let hex = palette[key], title = IMAGE_PALETTE_TITLES[key];
|
|
1573
|
-
return /* @__PURE__ */ jsx(Tooltip, {
|
|
1574
|
-
animate: !0,
|
|
1575
|
-
content: /* @__PURE__ */ jsx(Text, {
|
|
1576
|
-
size: 1,
|
|
1577
|
-
children: hex ? `${title} · ${hex}` : `No ${title.toLowerCase()} color`
|
|
1578
|
-
}),
|
|
1579
|
-
placement: "bottom",
|
|
1580
|
-
portal: !0,
|
|
1581
|
-
children: /* @__PURE__ */ jsx("span", {
|
|
1582
|
-
className: tile,
|
|
1583
|
-
"data-empty": hex === null,
|
|
1584
|
-
style: { background: hex ?? void 0 }
|
|
1585
|
-
})
|
|
1586
|
-
}, key);
|
|
1587
|
-
})]
|
|
1588
|
-
}),
|
|
1589
|
-
/* @__PURE__ */ jsxs(Stack, {
|
|
1590
|
-
gap: 2,
|
|
1591
|
-
children: [/* @__PURE__ */ jsx(Text, {
|
|
1592
|
-
muted: !0,
|
|
1593
|
-
size: 0,
|
|
1594
|
-
children: "Pick the swatch to build the theme around"
|
|
1595
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
1596
|
-
className: variantRow,
|
|
1597
|
-
ref: rowRef,
|
|
1598
|
-
style: assignInlineVars({
|
|
1599
|
-
[variantWidth]: `${thumbnailWidth}px`,
|
|
1600
|
-
[variantScale]: String(thumbnailWidth / 144)
|
|
1601
|
-
}),
|
|
1602
|
-
children: variants.map((t12) => {
|
|
1603
|
-
let { variant: variant_0, title: title_0, options: variantOptions } = t12;
|
|
1604
|
-
return /* @__PURE__ */ jsxs("button", {
|
|
1605
|
-
"aria-pressed": variant_0 === current,
|
|
1606
|
-
className: variantButton,
|
|
1607
|
-
onClick: () => onVariant(variant_0),
|
|
1608
|
-
type: "button",
|
|
1609
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
1610
|
-
className: variantFrame,
|
|
1611
|
-
children: /* @__PURE__ */ jsx("span", {
|
|
1612
|
-
className: variantScaleBox,
|
|
1613
|
-
children: /* @__PURE__ */ jsx(ThemeThumbnail, { options: variantOptions })
|
|
1614
|
-
})
|
|
1615
|
-
}), /* @__PURE__ */ jsx(Box, {
|
|
1616
|
-
as: "span",
|
|
1617
|
-
display: "block",
|
|
1618
|
-
paddingTop: 1,
|
|
1619
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
1620
|
-
align: "center",
|
|
1621
|
-
as: "span",
|
|
1622
|
-
size: 0,
|
|
1623
|
-
textOverflow: "ellipsis",
|
|
1624
|
-
weight: variant_0 === current ? "medium" : "regular",
|
|
1625
|
-
children: title_0
|
|
1626
|
-
})
|
|
1627
|
-
})]
|
|
1628
|
-
}, variant_0);
|
|
1629
|
-
})
|
|
1630
|
-
})]
|
|
1631
|
-
}),
|
|
1632
|
-
/* @__PURE__ */ jsx(TooltipButton, {
|
|
1633
|
-
icon: SparklesIcon,
|
|
1634
|
-
mode: "ghost",
|
|
1635
|
-
onClick: onLucky,
|
|
1636
|
-
text: "I'm feeling lucky",
|
|
1637
|
-
tooltip: "Build the theme around a swatch that looks interesting",
|
|
1638
|
-
width: "fill"
|
|
1639
|
-
})
|
|
1640
|
-
] }) : /* @__PURE__ */ jsxs(Stack, {
|
|
1641
|
-
gap: 3,
|
|
1642
|
-
children: [/* @__PURE__ */ jsx(Text, {
|
|
1643
|
-
muted: !0,
|
|
1644
|
-
size: 0,
|
|
1645
|
-
children: "Take the accent, text and background colors from an image — read on device, nothing is uploaded."
|
|
1646
|
-
}), /* @__PURE__ */ jsx(ImageFileButton, {
|
|
1647
|
-
loading: busy,
|
|
1648
|
-
mode: "ghost",
|
|
1649
|
-
onFile: pickImage,
|
|
1650
|
-
text: "Pick an image",
|
|
1651
|
-
tooltip: "Take the colors from an image on your device",
|
|
1652
|
-
width: "fill"
|
|
1653
|
-
})]
|
|
1654
|
-
}), $[21] = busy, $[22] = current, $[23] = imageUrl, $[24] = onLucky, $[25] = onVariant, $[26] = palette, $[27] = pickImage, $[28] = thumbnailWidth, $[29] = variants, $[30] = t11) : t11 = $[30];
|
|
1655
|
-
let t12;
|
|
1656
|
-
$[31] !== t10 || $[32] !== t11 ? (t12 = /* @__PURE__ */ jsxs(Stack, {
|
|
1657
|
-
gap: 3,
|
|
1658
|
-
children: [t10, t11]
|
|
1659
|
-
}), $[31] = t10, $[32] = t11, $[33] = t12) : t12 = $[33];
|
|
1660
|
-
let t13;
|
|
1661
|
-
return $[34] !== t12 || $[35] !== t6 || $[36] !== t7 ? (t13 = /* @__PURE__ */ jsx(Card, {
|
|
1662
|
-
border: !0,
|
|
1663
|
-
onDragEnter: t4,
|
|
1664
|
-
onDragLeave: t5,
|
|
1665
|
-
onDragOver: _temp$5,
|
|
1666
|
-
onDrop: t6,
|
|
1667
|
-
padding: 3,
|
|
1668
|
-
radius: 3,
|
|
1669
|
-
tone: t7,
|
|
1670
|
-
children: t12
|
|
1671
|
-
}), $[34] = t12, $[35] = t6, $[36] = t7, $[37] = t13) : t13 = $[37], t13;
|
|
1672
|
-
}
|
|
1673
|
-
function _temp$5(event_0) {
|
|
1674
|
-
event_0.preventDefault();
|
|
1675
|
-
}
|
|
1676
|
-
var colorRowText = "os3rj2", range = "os3rj1", swatch = "os3rj0";
|
|
1677
|
-
const SCHEME_TITLES = {
|
|
1678
|
-
dark: "Dark mode",
|
|
1679
|
-
light: "Light mode"
|
|
1680
|
-
};
|
|
1681
|
-
/**
|
|
1682
|
-
* Where the split preview shows a scheme: the Studio's own scheme stays next
|
|
1683
|
-
* to the sidebar (or at the bottom, stacked on small screens), the other one
|
|
1684
|
-
* takes the far side
|
|
1685
|
-
*/
|
|
1686
|
-
function splitHint(own, mobile) {
|
|
1687
|
-
return mobile ? own ? "Shown at the bottom" : "Shown at the top" : own ? "Shown on the right, next to the themer" : "Shown on the left";
|
|
1688
|
-
}
|
|
1689
|
-
const SCHEME_OPTION_KEYS = [
|
|
1690
|
-
"accent",
|
|
1691
|
-
"text",
|
|
1692
|
-
"background",
|
|
1693
|
-
"contrast"
|
|
1694
|
-
];
|
|
1695
|
-
/**
|
|
1696
|
-
* `<input type="color">` paints the color into a shadow-DOM swatch that brings
|
|
1697
|
-
* its own border and padding, which then sits inside ours as a second border.
|
|
1698
|
-
* Stripping that chrome leaves the themed border as the only one.
|
|
1699
|
-
*/
|
|
1700
|
-
/**
|
|
1701
|
-
* The flow for editing one of the user's own themes: its title, a live
|
|
1702
|
-
* preview, and a card per color scheme with the accent/text/background
|
|
1703
|
-
* pickers and the contrast slider of that scheme. Every change applies to the
|
|
1704
|
-
* whole Studio right away — in the scheme the Studio is showing, or in both
|
|
1705
|
-
* side by side in the split preview.
|
|
1706
|
-
*
|
|
1707
|
-
* @internal
|
|
1708
|
-
*/
|
|
1709
|
-
function ThemeEditor(props) {
|
|
1710
|
-
let $ = c(42), { focusTitle, slug } = props, { themes, images, split, mobile, send } = useThemer(), t0;
|
|
1711
|
-
if ($[0] !== slug || $[1] !== themes) {
|
|
1712
|
-
let t1;
|
|
1713
|
-
$[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;
|
|
1714
|
-
} else t0 = $[2];
|
|
1715
|
-
let theme = t0;
|
|
1716
|
-
if (!theme) return null;
|
|
1717
|
-
let t1;
|
|
1718
|
-
$[5] !== send || $[6] !== slug || $[7] !== theme.options || $[8] !== theme.palette ? (t1 = (variant) => {
|
|
1719
|
-
theme.palette && variant !== null && send({
|
|
1720
|
-
type: "theme.update",
|
|
1721
|
-
slug,
|
|
1722
|
-
options: applyImagePalette(theme.options, theme.palette, variant)
|
|
1723
|
-
});
|
|
1724
|
-
}, $[5] = send, $[6] = slug, $[7] = theme.options, $[8] = theme.palette, $[9] = t1) : t1 = $[9];
|
|
1725
|
-
let applyVariant = t1, t2 = images[slug], t3;
|
|
1726
|
-
$[10] === send ? t3 = $[11] : (t3 = () => send({ type: "flow.list" }), $[10] = send, $[11] = t3);
|
|
1727
|
-
let t4;
|
|
1728
|
-
$[12] !== applyVariant || $[13] !== theme.options || $[14] !== theme.palette ? (t4 = () => {
|
|
1729
|
-
theme.palette && applyVariant(pickLuckyVariant(theme.palette, { exclude: currentImageVariant(theme.options, theme.palette) }));
|
|
1730
|
-
}, $[12] = applyVariant, $[13] = theme.options, $[14] = theme.palette, $[15] = t4) : t4 = $[15];
|
|
1731
|
-
let t5;
|
|
1732
|
-
$[16] !== send || $[17] !== slug ? (t5 = (options) => send({
|
|
1733
|
-
type: "theme.update",
|
|
1734
|
-
slug,
|
|
1735
|
-
options
|
|
1736
|
-
}), $[16] = send, $[17] = slug, $[18] = t5) : t5 = $[18];
|
|
1737
|
-
let t6;
|
|
1738
|
-
$[19] !== send || $[20] !== slug || $[21] !== theme.options ? (t6 = (palette, file) => send({
|
|
1739
|
-
type: "theme.update",
|
|
1740
|
-
slug,
|
|
1741
|
-
options: applyImagePalette(theme.options, palette),
|
|
1742
|
-
palette,
|
|
1743
|
-
imageUrl: URL.createObjectURL(file)
|
|
1744
|
-
}), $[19] = send, $[20] = slug, $[21] = theme.options, $[22] = t6) : t6 = $[22];
|
|
1745
|
-
let t7, t8;
|
|
1746
|
-
$[23] !== send || $[24] !== slug ? (t7 = () => send({
|
|
1747
|
-
type: "theme.remove",
|
|
1748
|
-
slug
|
|
1749
|
-
}), t8 = (title) => send({
|
|
1750
|
-
type: "theme.update",
|
|
1751
|
-
slug,
|
|
1752
|
-
title
|
|
1753
|
-
}), $[23] = send, $[24] = slug, $[25] = t7, $[26] = t8) : (t7 = $[25], t8 = $[26]);
|
|
1754
|
-
let t9;
|
|
1755
|
-
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, {
|
|
1756
|
-
focusTitle,
|
|
1757
|
-
imageUrl: t2,
|
|
1758
|
-
onDone: t3,
|
|
1759
|
-
onLucky: t4,
|
|
1760
|
-
onOptionsChange: t5,
|
|
1761
|
-
onPalette: t6,
|
|
1762
|
-
onRemove: t7,
|
|
1763
|
-
onTitleChange: t8,
|
|
1764
|
-
onVariant: applyVariant,
|
|
1765
|
-
options: theme.options,
|
|
1766
|
-
palette: theme.palette,
|
|
1767
|
-
split,
|
|
1768
|
-
mobile,
|
|
1769
|
-
title: theme.title
|
|
1770
|
-
}), $[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;
|
|
1771
|
-
}
|
|
1772
|
-
function ThemeEditorForm(props) {
|
|
1773
|
-
let $ = c(50), { focusTitle, imageUrl, onDone, onLucky, onOptionsChange, onPalette, onRemove, onTitleChange, onVariant, options, palette, split, mobile, title } = props, studioScheme = useColorSchemeValue(), t0;
|
|
1774
|
-
$[0] === options ? t0 = $[1] : (t0 = resolveThemeOptions(options), $[0] = options, $[1] = t0);
|
|
1775
|
-
let resolved = t0, t1;
|
|
1776
|
-
$[2] === options ? t1 = $[3] : (t1 = buildPalette(options), $[2] = options, $[3] = t1);
|
|
1777
|
-
let palettes = t1, t2;
|
|
1778
|
-
$[4] !== onOptionsChange || $[5] !== options ? (t2 = (scheme, changes) => {
|
|
1779
|
-
let values = {
|
|
1780
|
-
...options[scheme],
|
|
1781
|
-
...changes
|
|
1782
|
-
};
|
|
1783
|
-
for (let key of SCHEME_OPTION_KEYS) values[key] === void 0 && delete values[key];
|
|
1784
|
-
let next = { ...options };
|
|
1785
|
-
Object.keys(values).length === 0 ? delete next[scheme] : next[scheme] = values, onOptionsChange(next);
|
|
1786
|
-
}, $[4] = onOptionsChange, $[5] = options, $[6] = t2) : t2 = $[6];
|
|
1787
|
-
let patchScheme = t2, t3;
|
|
1788
|
-
$[7] === options ? t3 = $[8] : (t3 = /* @__PURE__ */ jsx(ThemeThumbnail, { options }), $[7] = options, $[8] = t3);
|
|
1789
|
-
let t4;
|
|
1790
|
-
$[9] === Symbol.for("react.memo_cache_sentinel") ? (t4 = /* @__PURE__ */ jsx(Text, {
|
|
1791
|
-
size: 1,
|
|
1792
|
-
weight: "medium",
|
|
1793
|
-
children: "Title"
|
|
1794
|
-
}), $[9] = t4) : t4 = $[9];
|
|
1795
|
-
let t5;
|
|
1796
|
-
$[10] === onTitleChange ? t5 = $[11] : (t5 = (event) => onTitleChange(event.currentTarget.value), $[10] = onTitleChange, $[11] = t5);
|
|
1797
|
-
let t6 = focusTitle ? _temp$4 : void 0, t7;
|
|
1798
|
-
$[12] !== focusTitle || $[13] !== t5 || $[14] !== t6 || $[15] !== title ? (t7 = /* @__PURE__ */ jsxs(Stack, {
|
|
1799
|
-
gap: 2,
|
|
1800
|
-
children: [t4, /* @__PURE__ */ jsx(TextInput, {
|
|
1801
|
-
autoFocus: focusTitle,
|
|
1802
|
-
fontSize: 1,
|
|
1803
|
-
onChange: t5,
|
|
1804
|
-
onFocus: t6,
|
|
1805
|
-
padding: 2,
|
|
1806
|
-
placeholder: "Untitled theme",
|
|
1807
|
-
value: title
|
|
1808
|
-
})]
|
|
1809
|
-
}), $[12] = focusTitle, $[13] = t5, $[14] = t6, $[15] = title, $[16] = t7) : t7 = $[16];
|
|
1810
|
-
let t8;
|
|
1811
|
-
$[17] !== imageUrl || $[18] !== onLucky || $[19] !== onPalette || $[20] !== onVariant || $[21] !== options || $[22] !== palette ? (t8 = /* @__PURE__ */ jsx(ImagePaletteSection, {
|
|
1812
|
-
imageUrl,
|
|
1813
|
-
onLucky,
|
|
1814
|
-
onPalette,
|
|
1815
|
-
onVariant,
|
|
1816
|
-
options,
|
|
1817
|
-
palette
|
|
1818
|
-
}), $[17] = imageUrl, $[18] = onLucky, $[19] = onPalette, $[20] = onVariant, $[21] = options, $[22] = palette, $[23] = t8) : t8 = $[23];
|
|
1819
|
-
let t9;
|
|
1820
|
-
$[24] !== mobile || $[25] !== options || $[26] !== palettes || $[27] !== patchScheme || $[28] !== resolved || $[29] !== split || $[30] !== studioScheme ? (t9 = SCHEMES.map((scheme_0) => /* @__PURE__ */ jsx(SchemeCard, {
|
|
1821
|
-
active: split || scheme_0 === studioScheme,
|
|
1822
|
-
onChange: (changes_0) => patchScheme(scheme_0, changes_0),
|
|
1823
|
-
options: options[scheme_0] ?? {},
|
|
1824
|
-
palette: palettes[scheme_0],
|
|
1825
|
-
resolved: resolved[scheme_0],
|
|
1826
|
-
scheme: scheme_0,
|
|
1827
|
-
split,
|
|
1828
|
-
splitHint: splitHint(scheme_0 === studioScheme, mobile)
|
|
1829
|
-
}, scheme_0)), $[24] = mobile, $[25] = options, $[26] = palettes, $[27] = patchScheme, $[28] = resolved, $[29] = split, $[30] = studioScheme, $[31] = t9) : t9 = $[31];
|
|
1830
|
-
let t10;
|
|
1831
|
-
$[32] === t9 ? t10 = $[33] : (t10 = /* @__PURE__ */ jsx(ScrollAreaBleed, { children: t9 }), $[32] = t9, $[33] = t10);
|
|
1832
|
-
let t11;
|
|
1833
|
-
$[34] !== t10 || $[35] !== t3 || $[36] !== t7 || $[37] !== t8 ? (t11 = /* @__PURE__ */ jsx(ScrollArea, {
|
|
1834
|
-
padding: 3,
|
|
1835
|
-
children: /* @__PURE__ */ jsxs(Stack, {
|
|
1836
|
-
gap: 4,
|
|
1837
|
-
children: [
|
|
1838
|
-
t3,
|
|
1839
|
-
t7,
|
|
1840
|
-
t8,
|
|
1841
|
-
t10
|
|
1842
|
-
]
|
|
1843
|
-
})
|
|
1844
|
-
}), $[34] = t10, $[35] = t3, $[36] = t7, $[37] = t8, $[38] = t11) : t11 = $[38];
|
|
1845
|
-
let t12;
|
|
1846
|
-
$[39] === onRemove ? t12 = $[40] : (t12 = /* @__PURE__ */ jsx(TooltipButton, {
|
|
1847
|
-
icon: TrashIcon,
|
|
1848
|
-
mode: "ghost",
|
|
1849
|
-
onClick: onRemove,
|
|
1850
|
-
text: "Remove",
|
|
1851
|
-
tone: "critical",
|
|
1852
|
-
tooltip: "Remove the theme — it can be restored until it is deleted"
|
|
1853
|
-
}), $[39] = onRemove, $[40] = t12);
|
|
1854
|
-
let t13;
|
|
1855
|
-
$[41] === Symbol.for("react.memo_cache_sentinel") ? (t13 = /* @__PURE__ */ jsx(Box, { flex: 1 }), $[41] = t13) : t13 = $[41];
|
|
1856
|
-
let t14;
|
|
1857
|
-
$[42] === onDone ? t14 = $[43] : (t14 = /* @__PURE__ */ jsx(Button, {
|
|
1858
|
-
mode: "ghost",
|
|
1859
|
-
onClick: onDone,
|
|
1860
|
-
padding: 2,
|
|
1861
|
-
text: "Done"
|
|
1862
|
-
}), $[42] = onDone, $[43] = t14);
|
|
1863
|
-
let t15;
|
|
1864
|
-
$[44] !== t12 || $[45] !== t14 ? (t15 = /* @__PURE__ */ jsx(Card, {
|
|
1865
|
-
borderTop: !0,
|
|
1866
|
-
padding: 3,
|
|
1867
|
-
children: /* @__PURE__ */ jsxs(Flex, {
|
|
1868
|
-
gap: 2,
|
|
1869
|
-
children: [
|
|
1870
|
-
t12,
|
|
1871
|
-
t13,
|
|
1872
|
-
t14
|
|
1873
|
-
]
|
|
1874
|
-
})
|
|
1875
|
-
}), $[44] = t12, $[45] = t14, $[46] = t15) : t15 = $[46];
|
|
1876
|
-
let t16;
|
|
1877
|
-
return $[47] !== t11 || $[48] !== t15 ? (t16 = /* @__PURE__ */ jsxs(Fragment, { children: [t11, t15] }), $[47] = t11, $[48] = t15, $[49] = t16) : t16 = $[49], t16;
|
|
1878
|
-
}
|
|
1879
|
-
function _temp$4(event_0) {
|
|
1880
|
-
return event_0.currentTarget.select();
|
|
1881
|
-
}
|
|
1882
|
-
/**
|
|
1883
|
-
* The colors of one scheme. The cards span the sidebar edge to edge, one
|
|
1884
|
-
* after the other with a border between them, in the sidebar's own scheme —
|
|
1885
|
-
* the swatches show the colors they edit. The scheme the Studio is showing is
|
|
1886
|
-
* marked as active: its changes show up in the Studio right away, the other
|
|
1887
|
-
* scheme's only once the appearance setting switches to it — or both are
|
|
1888
|
-
* active, in the split preview.
|
|
1889
|
-
*/
|
|
1890
|
-
function SchemeCard(props) {
|
|
1891
|
-
let $ = c(66), { active, onChange, options, palette, resolved, scheme, split, splitHint } = props, name = SCHEME_TITLES[scheme], t0;
|
|
1892
|
-
$[0] === name ? t0 = $[1] : (t0 = /* @__PURE__ */ jsx(Box, {
|
|
1893
|
-
flex: 1,
|
|
1894
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
1895
|
-
size: 1,
|
|
1896
|
-
weight: "medium",
|
|
1897
|
-
children: name
|
|
1898
|
-
})
|
|
1899
|
-
}), $[0] = name, $[1] = t0);
|
|
1900
|
-
let t1;
|
|
1901
|
-
$[2] === active ? t1 = $[3] : (t1 = active && /* @__PURE__ */ jsx(Badge, {
|
|
1902
|
-
fontSize: 0,
|
|
1903
|
-
tone: "primary",
|
|
1904
|
-
children: "Active"
|
|
1905
|
-
}), $[2] = active, $[3] = t1);
|
|
1906
|
-
let t2;
|
|
1907
|
-
$[4] !== t0 || $[5] !== t1 ? (t2 = /* @__PURE__ */ jsxs(Flex, {
|
|
1908
|
-
align: "center",
|
|
1909
|
-
gap: 2,
|
|
1910
|
-
children: [t0, t1]
|
|
1911
|
-
}), $[4] = t0, $[5] = t1, $[6] = t2) : t2 = $[6];
|
|
1912
|
-
let t3 = split ? splitHint : active ? "Shown in the Studio right now" : `Shown once the Studio appearance is ${scheme}`, t4;
|
|
1913
|
-
$[7] === t3 ? t4 = $[8] : (t4 = /* @__PURE__ */ jsx(Text, {
|
|
1914
|
-
muted: !0,
|
|
1915
|
-
size: 0,
|
|
1916
|
-
children: t3
|
|
1917
|
-
}), $[7] = t3, $[8] = t4);
|
|
1918
|
-
let t5;
|
|
1919
|
-
$[9] !== t2 || $[10] !== t4 ? (t5 = /* @__PURE__ */ jsxs(Stack, {
|
|
1920
|
-
gap: 2,
|
|
1921
|
-
children: [t2, t4]
|
|
1922
|
-
}), $[9] = t2, $[10] = t4, $[11] = t5) : t5 = $[11];
|
|
1923
|
-
let t6 = options.accent === void 0, t7 = `${name} accent`, t8, t9;
|
|
1924
|
-
$[12] === onChange ? (t8 = $[13], t9 = $[14]) : (t8 = (accent) => onChange({ accent }), t9 = () => onChange({ accent: void 0 }), $[12] = onChange, $[13] = t8, $[14] = t9);
|
|
1925
|
-
let t10;
|
|
1926
|
-
$[15] !== resolved.accent || $[16] !== t6 || $[17] !== t7 || $[18] !== t8 || $[19] !== t9 ? (t10 = /* @__PURE__ */ jsx(ColorRow, {
|
|
1927
|
-
auto: t6,
|
|
1928
|
-
autoLabel: "default",
|
|
1929
|
-
label: t7,
|
|
1930
|
-
onChange: t8,
|
|
1931
|
-
onClear: t9,
|
|
1932
|
-
title: "Accent",
|
|
1933
|
-
value: resolved.accent
|
|
1934
|
-
}), $[15] = resolved.accent, $[16] = t6, $[17] = t7, $[18] = t8, $[19] = t9, $[20] = t10) : t10 = $[20];
|
|
1935
|
-
let t11 = options.text === void 0, t12 = `${name} text`, t13, t14;
|
|
1936
|
-
$[21] === onChange ? (t13 = $[22], t14 = $[23]) : (t13 = (text) => onChange({ text }), t14 = () => onChange({ text: void 0 }), $[21] = onChange, $[22] = t13, $[23] = t14);
|
|
1937
|
-
let t15;
|
|
1938
|
-
$[24] !== resolved.text || $[25] !== t11 || $[26] !== t12 || $[27] !== t13 || $[28] !== t14 ? (t15 = /* @__PURE__ */ jsx(ColorRow, {
|
|
1939
|
-
auto: t11,
|
|
1940
|
-
label: t12,
|
|
1941
|
-
onChange: t13,
|
|
1942
|
-
onClear: t14,
|
|
1943
|
-
title: "Text",
|
|
1944
|
-
value: resolved.text
|
|
1945
|
-
}), $[24] = resolved.text, $[25] = t11, $[26] = t12, $[27] = t13, $[28] = t14, $[29] = t15) : t15 = $[29];
|
|
1946
|
-
let t16 = scheme === "dark" ? palette.black : palette.white, t17 = options.background === void 0, t18 = `${name} background`, t19, t20;
|
|
1947
|
-
$[30] === onChange ? (t19 = $[31], t20 = $[32]) : (t19 = (background) => onChange({ background }), t20 = () => onChange({ background: void 0 }), $[30] = onChange, $[31] = t19, $[32] = t20);
|
|
1948
|
-
let t21;
|
|
1949
|
-
$[33] !== resolved.background || $[34] !== t16 || $[35] !== t17 || $[36] !== t18 || $[37] !== t19 || $[38] !== t20 ? (t21 = /* @__PURE__ */ jsx(ColorRow, {
|
|
1950
|
-
adjusted: t16,
|
|
1951
|
-
auto: t17,
|
|
1952
|
-
label: t18,
|
|
1953
|
-
onChange: t19,
|
|
1954
|
-
onClear: t20,
|
|
1955
|
-
title: "Background",
|
|
1956
|
-
value: resolved.background
|
|
1957
|
-
}), $[33] = resolved.background, $[34] = t16, $[35] = t17, $[36] = t18, $[37] = t19, $[38] = t20, $[39] = t21) : t21 = $[39];
|
|
1958
|
-
let t22;
|
|
1959
|
-
$[40] === Symbol.for("react.memo_cache_sentinel") ? (t22 = /* @__PURE__ */ jsx(Text, {
|
|
1960
|
-
size: 1,
|
|
1961
|
-
children: "Contrast"
|
|
1962
|
-
}), $[40] = t22) : t22 = $[40];
|
|
1963
|
-
let t23 = options.contrast === void 0 ? " · auto" : "", t24;
|
|
1964
|
-
$[41] !== resolved.contrast || $[42] !== t23 ? (t24 = /* @__PURE__ */ jsxs(Stack, {
|
|
1965
|
-
flex: 1,
|
|
1966
|
-
gap: 2,
|
|
1967
|
-
children: [t22, /* @__PURE__ */ jsxs(Text, {
|
|
1968
|
-
muted: !0,
|
|
1969
|
-
size: 0,
|
|
1970
|
-
children: [resolved.contrast, t23]
|
|
1971
|
-
})]
|
|
1972
|
-
}), $[41] = resolved.contrast, $[42] = t23, $[43] = t24) : t24 = $[43];
|
|
1973
|
-
let t25;
|
|
1974
|
-
$[44] !== onChange || $[45] !== options.contrast ? (t25 = options.contrast !== void 0 && /* @__PURE__ */ jsx(TooltipButton, {
|
|
1975
|
-
icon: ResetIcon,
|
|
1976
|
-
mode: "bleed",
|
|
1977
|
-
onClick: () => onChange({ contrast: void 0 }),
|
|
1978
|
-
padding: 2,
|
|
1979
|
-
tooltip: "Reset to auto"
|
|
1980
|
-
}), $[44] = onChange, $[45] = options.contrast, $[46] = t25) : t25 = $[46];
|
|
1981
|
-
let t26;
|
|
1982
|
-
$[47] !== t24 || $[48] !== t25 ? (t26 = /* @__PURE__ */ jsxs(Flex, {
|
|
1983
|
-
align: "center",
|
|
1984
|
-
gap: 2,
|
|
1985
|
-
children: [t24, t25]
|
|
1986
|
-
}), $[47] = t24, $[48] = t25, $[49] = t26) : t26 = $[49];
|
|
1987
|
-
let t27 = `${name} contrast`, t28;
|
|
1988
|
-
$[50] === onChange ? t28 = $[51] : (t28 = (event) => onChange({ contrast: Number(event.currentTarget.value) }), $[50] = onChange, $[51] = t28);
|
|
1989
|
-
let t29;
|
|
1990
|
-
$[52] !== resolved.contrast || $[53] !== t27 || $[54] !== t28 ? (t29 = /* @__PURE__ */ jsx("input", {
|
|
1991
|
-
"aria-label": t27,
|
|
1992
|
-
className: range,
|
|
1993
|
-
max: 100,
|
|
1994
|
-
min: 15,
|
|
1995
|
-
onChange: t28,
|
|
1996
|
-
step: 1,
|
|
1997
|
-
type: "range",
|
|
1998
|
-
value: resolved.contrast
|
|
1999
|
-
}), $[52] = resolved.contrast, $[53] = t27, $[54] = t28, $[55] = t29) : t29 = $[55];
|
|
2000
|
-
let t30;
|
|
2001
|
-
$[56] === Symbol.for("react.memo_cache_sentinel") ? (t30 = /* @__PURE__ */ jsx(Text, {
|
|
2002
|
-
muted: !0,
|
|
2003
|
-
size: 0,
|
|
2004
|
-
children: "100 keeps text and borders neutral — lower values blend in the accent"
|
|
2005
|
-
}), $[56] = t30) : t30 = $[56];
|
|
2006
|
-
let t31;
|
|
2007
|
-
$[57] !== t26 || $[58] !== t29 ? (t31 = /* @__PURE__ */ jsxs(Stack, {
|
|
2008
|
-
gap: 3,
|
|
2009
|
-
children: [
|
|
2010
|
-
t26,
|
|
2011
|
-
t29,
|
|
2012
|
-
t30
|
|
2013
|
-
]
|
|
2014
|
-
}), $[57] = t26, $[58] = t29, $[59] = t31) : t31 = $[59];
|
|
2015
|
-
let t32;
|
|
2016
|
-
return $[60] !== t10 || $[61] !== t15 || $[62] !== t21 || $[63] !== t31 || $[64] !== t5 ? (t32 = /* @__PURE__ */ jsx(Card, {
|
|
2017
|
-
borderTop: !0,
|
|
2018
|
-
padding: 3,
|
|
2019
|
-
children: /* @__PURE__ */ jsxs(Stack, {
|
|
2020
|
-
gap: 4,
|
|
2021
|
-
children: [
|
|
2022
|
-
t5,
|
|
2023
|
-
t10,
|
|
2024
|
-
t15,
|
|
2025
|
-
t21,
|
|
2026
|
-
t31
|
|
2027
|
-
]
|
|
2028
|
-
})
|
|
2029
|
-
}), $[60] = t10, $[61] = t15, $[62] = t21, $[63] = t31, $[64] = t5, $[65] = t32) : t32 = $[65], t32;
|
|
2030
|
-
}
|
|
2031
|
-
/** One color of a scheme: a swatch with its value and an optional reset button */
|
|
2032
|
-
function ColorRow(props) {
|
|
2033
|
-
let $ = c(23), { adjusted, auto, autoLabel: t0, label, onChange, onClear, title, value } = props, autoLabel = t0 === void 0 ? "auto" : t0, t1;
|
|
2034
|
-
$[0] === onChange ? t1 = $[1] : (t1 = (event) => onChange(event.currentTarget.value), $[0] = onChange, $[1] = t1);
|
|
2035
|
-
let t2;
|
|
2036
|
-
$[2] !== label || $[3] !== t1 || $[4] !== value ? (t2 = /* @__PURE__ */ jsx("input", {
|
|
2037
|
-
"aria-label": label,
|
|
2038
|
-
className: swatch,
|
|
2039
|
-
onChange: t1,
|
|
2040
|
-
type: "color",
|
|
2041
|
-
value
|
|
2042
|
-
}), $[2] = label, $[3] = t1, $[4] = value, $[5] = t2) : t2 = $[5];
|
|
2043
|
-
let t3;
|
|
2044
|
-
$[6] === title ? t3 = $[7] : (t3 = /* @__PURE__ */ jsx(Text, {
|
|
2045
|
-
size: 1,
|
|
2046
|
-
children: title
|
|
2047
|
-
}), $[6] = title, $[7] = t3);
|
|
2048
|
-
let t4 = adjusted !== void 0 && adjusted !== value ? ` → ${adjusted}` : "", t5 = auto ? ` · ${autoLabel}` : "", t6;
|
|
2049
|
-
$[8] !== t4 || $[9] !== t5 || $[10] !== value ? (t6 = /* @__PURE__ */ jsxs(Text, {
|
|
2050
|
-
muted: !0,
|
|
2051
|
-
size: 0,
|
|
2052
|
-
textOverflow: "ellipsis",
|
|
2053
|
-
children: [
|
|
2054
|
-
value,
|
|
2055
|
-
t4,
|
|
2056
|
-
t5
|
|
2057
|
-
]
|
|
2058
|
-
}), $[8] = t4, $[9] = t5, $[10] = value, $[11] = t6) : t6 = $[11];
|
|
2059
|
-
let t7;
|
|
2060
|
-
$[12] !== t3 || $[13] !== t6 ? (t7 = /* @__PURE__ */ jsxs(Stack, {
|
|
2061
|
-
className: colorRowText,
|
|
2062
|
-
flex: 1,
|
|
2063
|
-
gap: 2,
|
|
2064
|
-
children: [t3, t6]
|
|
2065
|
-
}), $[12] = t3, $[13] = t6, $[14] = t7) : t7 = $[14];
|
|
2066
|
-
let t8;
|
|
2067
|
-
$[15] !== auto || $[16] !== autoLabel || $[17] !== onClear ? (t8 = onClear && !auto && /* @__PURE__ */ jsx(TooltipButton, {
|
|
2068
|
-
icon: ResetIcon,
|
|
2069
|
-
mode: "bleed",
|
|
2070
|
-
onClick: onClear,
|
|
2071
|
-
padding: 2,
|
|
2072
|
-
tooltip: `Reset to ${autoLabel}`
|
|
2073
|
-
}), $[15] = auto, $[16] = autoLabel, $[17] = onClear, $[18] = t8) : t8 = $[18];
|
|
2074
|
-
let t9;
|
|
2075
|
-
return $[19] !== t2 || $[20] !== t7 || $[21] !== t8 ? (t9 = /* @__PURE__ */ jsxs(Flex, {
|
|
2076
|
-
align: "center",
|
|
2077
|
-
gap: 2,
|
|
2078
|
-
children: [
|
|
2079
|
-
t2,
|
|
2080
|
-
t7,
|
|
2081
|
-
t8
|
|
2082
|
-
]
|
|
2083
|
-
}), $[19] = t2, $[20] = t7, $[21] = t8, $[22] = t9) : t9 = $[22], t9;
|
|
2084
|
-
}
|
|
2085
|
-
/**
|
|
2086
|
-
* The version of the code format, the first byte of every code — bumped if
|
|
2087
|
-
* the layout below ever changes, so older codes can still be told apart
|
|
2088
|
-
*/
|
|
2089
|
-
const FIELDS = [
|
|
2090
|
-
"accent",
|
|
2091
|
-
"text",
|
|
2092
|
-
"background",
|
|
2093
|
-
"contrast"
|
|
2094
|
-
];
|
|
2095
|
-
/**
|
|
2096
|
-
* Turns a theme into a short code to share in a message: `title`, and the
|
|
2097
|
-
* colors and contrasts of both schemes, packed into bytes — one flag byte says
|
|
2098
|
-
* which of the eight fields are set, each color is its three RGB bytes and a
|
|
2099
|
-
* contrast one byte, and the title takes up the rest — then base64url'd, so
|
|
2100
|
-
* it is one word without padding, safe in URLs and chat. A theme setting
|
|
2101
|
-
* everything comes to about 30 bytes plus its title.
|
|
2102
|
-
*
|
|
2103
|
-
* @internal
|
|
2104
|
-
*/
|
|
2105
|
-
function encodeTheme(theme) {
|
|
2106
|
-
let bytes = [1, 0], flags = 0;
|
|
2107
|
-
return SCHEMES.forEach((scheme, schemeIndex) => {
|
|
2108
|
-
let options = theme.options[scheme] ?? {};
|
|
2109
|
-
FIELDS.forEach((field, fieldIndex) => {
|
|
2110
|
-
let value = options[field];
|
|
2111
|
-
if (value !== void 0) {
|
|
2112
|
-
if (field === "contrast") {
|
|
2113
|
-
if (typeof value != "number" || !Number.isFinite(value)) return;
|
|
2114
|
-
bytes.push(Math.round(Math.min(100, Math.max(15, value))));
|
|
2115
|
-
} else {
|
|
2116
|
-
if (typeof value != "string" || !isColor(value)) return;
|
|
2117
|
-
bytes.push(...rgbBytes(value));
|
|
2118
|
-
}
|
|
2119
|
-
flags |= 1 << schemeIndex * FIELDS.length + fieldIndex;
|
|
2120
|
-
}
|
|
2121
|
-
});
|
|
2122
|
-
}), bytes[1] = flags, bytes.push(...new TextEncoder().encode(theme.title.trim())), toBase64Url(Uint8Array.from(bytes));
|
|
2123
|
-
}
|
|
2124
|
-
/**
|
|
2125
|
-
* Reads a theme back out of a code — as pasted, so surrounding whitespace and
|
|
2126
|
-
* words around the code are fine: the first word that is a code counts. `null`
|
|
2127
|
-
* when nothing in the text is one.
|
|
2128
|
-
*
|
|
2129
|
-
* @internal
|
|
2130
|
-
*/
|
|
2131
|
-
function decodeTheme(text) {
|
|
2132
|
-
for (let word of text.trim().split(/\s+/)) {
|
|
2133
|
-
let theme = decodeCode(word);
|
|
2134
|
-
if (theme) return theme;
|
|
2135
|
-
}
|
|
2136
|
-
return null;
|
|
2137
|
-
}
|
|
2138
|
-
function decodeCode(code) {
|
|
2139
|
-
let bytes = fromBase64Url(code);
|
|
2140
|
-
if (!bytes || bytes.length < 2 || bytes[0] !== 1) return null;
|
|
2141
|
-
let flags = bytes[1], options = {}, offset = 2;
|
|
2142
|
-
for (let [schemeIndex, scheme] of SCHEMES.entries()) {
|
|
2143
|
-
let schemeOptions = {};
|
|
2144
|
-
for (let [fieldIndex, field] of FIELDS.entries()) if (flags & 1 << schemeIndex * FIELDS.length + fieldIndex) {
|
|
2145
|
-
if (field === "contrast") {
|
|
2146
|
-
if (offset + 1 > bytes.length) return null;
|
|
2147
|
-
schemeOptions.contrast = Math.min(100, Math.max(15, bytes[offset])), offset += 1;
|
|
2148
|
-
} else {
|
|
2149
|
-
if (offset + 3 > bytes.length) return null;
|
|
2150
|
-
schemeOptions[field] = hexColor(bytes.subarray(offset, offset + 3)), offset += 3;
|
|
2151
|
-
}
|
|
2152
|
-
}
|
|
2153
|
-
Object.keys(schemeOptions).length > 0 && (options[scheme] = schemeOptions);
|
|
2154
|
-
}
|
|
2155
|
-
let title;
|
|
2156
|
-
try {
|
|
2157
|
-
title = new TextDecoder("utf-8", { fatal: !0 }).decode(bytes.subarray(offset));
|
|
2158
|
-
} catch {
|
|
2159
|
-
return null;
|
|
2160
|
-
}
|
|
2161
|
-
return {
|
|
2162
|
-
title: displayTitle(title),
|
|
2163
|
-
options
|
|
2164
|
-
};
|
|
2165
|
-
}
|
|
2166
|
-
/** The three RGB bytes of a `#rgb` or `#rrggbb` color */
|
|
2167
|
-
function rgbBytes(color) {
|
|
2168
|
-
let hex = color.length === 4 ? color.slice(1).replaceAll(/./g, "$&$&") : color.slice(1);
|
|
2169
|
-
return [
|
|
2170
|
-
0,
|
|
2171
|
-
2,
|
|
2172
|
-
4
|
|
2173
|
-
].map((start) => parseInt(hex.slice(start, start + 2), 16));
|
|
2174
|
-
}
|
|
2175
|
-
function hexColor(rgb) {
|
|
2176
|
-
return `#${[...rgb].map((byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
2177
|
-
}
|
|
2178
|
-
function toBase64Url(bytes) {
|
|
2179
|
-
let binary = "";
|
|
2180
|
-
for (let byte of bytes) binary += String.fromCharCode(byte);
|
|
2181
|
-
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
|
|
2182
|
-
}
|
|
2183
|
-
function fromBase64Url(code) {
|
|
2184
|
-
if (!/^[A-Za-z0-9_-]+$/.test(code)) return null;
|
|
2185
|
-
let base64 = code.replaceAll("-", "+").replaceAll("_", "/");
|
|
2186
|
-
try {
|
|
2187
|
-
return Uint8Array.from(atob(base64.padEnd(Math.ceil(base64.length / 4) * 4, "=")), (char) => char.charCodeAt(0));
|
|
2188
|
-
} catch {
|
|
2189
|
-
return null;
|
|
2190
|
-
}
|
|
2191
|
-
}
|
|
2192
|
-
/**
|
|
2193
|
-
* Copying a theme as a code to share, and adding a theme from a code someone
|
|
2194
|
-
* shared — with toasts telling how it went.
|
|
2195
|
-
*
|
|
2196
|
-
* @internal
|
|
2197
|
-
*/
|
|
2198
|
-
function useThemeCodes() {
|
|
2199
|
-
let $ = c(11), { send } = useThemer(), toast = useToast(), t0;
|
|
2200
|
-
$[0] === toast ? t0 = $[1] : (t0 = async (theme) => {
|
|
2201
|
-
let title = displayTitle(theme.title);
|
|
2202
|
-
try {
|
|
2203
|
-
await navigator.clipboard.writeText(encodeTheme(theme)), toast.push({
|
|
2204
|
-
status: "success",
|
|
2205
|
-
title: `Copied ${title} as a code`,
|
|
2206
|
-
description: "Paste it in the themer of another Studio to add the theme there"
|
|
2207
|
-
});
|
|
2208
|
-
} catch {
|
|
2209
|
-
toast.push({
|
|
2210
|
-
status: "error",
|
|
2211
|
-
title: `Could not copy ${title}`
|
|
2212
|
-
});
|
|
2213
|
-
}
|
|
2214
|
-
}, $[0] = toast, $[1] = t0);
|
|
2215
|
-
let copyTheme = t0, t1;
|
|
2216
|
-
$[2] !== send || $[3] !== toast ? (t1 = (text) => {
|
|
2217
|
-
let theme_0 = decodeTheme(text);
|
|
2218
|
-
return theme_0 ? (send({
|
|
2219
|
-
type: "theme.import",
|
|
2220
|
-
title: theme_0.title,
|
|
2221
|
-
options: theme_0.options
|
|
2222
|
-
}), toast.push({
|
|
2223
|
-
status: "success",
|
|
2224
|
-
title: `Added ${theme_0.title}`
|
|
2225
|
-
}), !0) : !1;
|
|
2226
|
-
}, $[2] = send, $[3] = toast, $[4] = t1) : t1 = $[4];
|
|
2227
|
-
let addThemeFromText = t1, t2;
|
|
2228
|
-
$[5] === addThemeFromText ? t2 = $[6] : (t2 = async () => {
|
|
2229
|
-
let text_0;
|
|
2230
|
-
try {
|
|
2231
|
-
text_0 = await navigator.clipboard.readText();
|
|
2232
|
-
} catch {
|
|
2233
|
-
return "no-clipboard";
|
|
2234
|
-
}
|
|
2235
|
-
return addThemeFromText(text_0) ? "added" : "no-theme";
|
|
2236
|
-
}, $[5] = addThemeFromText, $[6] = t2);
|
|
2237
|
-
let addThemeFromClipboard = t2, t3;
|
|
2238
|
-
return $[7] !== addThemeFromClipboard || $[8] !== addThemeFromText || $[9] !== copyTheme ? (t3 = {
|
|
2239
|
-
copyTheme,
|
|
2240
|
-
addThemeFromText,
|
|
2241
|
-
addThemeFromClipboard
|
|
2242
|
-
}, $[7] = addThemeFromClipboard, $[8] = addThemeFromText, $[9] = copyTheme, $[10] = t3) : t3 = $[10], t3;
|
|
2243
|
-
}
|
|
2244
|
-
/**
|
|
2245
|
-
* Adds a theme pasted anywhere in the Studio while the themer's list is
|
|
2246
|
-
* showing — pastes into inputs and other editable things are theirs.
|
|
2247
|
-
*
|
|
2248
|
-
* @internal
|
|
2249
|
-
*/
|
|
2250
|
-
function usePasteThemeCodes() {
|
|
2251
|
-
let $ = c(5), { open, send } = useThemer(), toast = useToast(), t0, t1;
|
|
2252
|
-
$[0] !== open || $[1] !== send || $[2] !== toast ? (t0 = () => {
|
|
2253
|
-
if (!open) return;
|
|
2254
|
-
let handlePaste = (event) => {
|
|
2255
|
-
if (isEditable(event.target)) return;
|
|
2256
|
-
let theme = decodeTheme(event.clipboardData?.getData("text/plain") ?? "");
|
|
2257
|
-
theme && (event.preventDefault(), send({
|
|
2258
|
-
type: "theme.import",
|
|
2259
|
-
title: theme.title,
|
|
2260
|
-
options: theme.options
|
|
2261
|
-
}), toast.push({
|
|
2262
|
-
status: "success",
|
|
2263
|
-
title: `Added ${theme.title}`
|
|
2264
|
-
}));
|
|
2265
|
-
};
|
|
2266
|
-
return document.addEventListener("paste", handlePaste), () => document.removeEventListener("paste", handlePaste);
|
|
2267
|
-
}, t1 = [
|
|
2268
|
-
open,
|
|
2269
|
-
send,
|
|
2270
|
-
toast
|
|
2271
|
-
], $[0] = open, $[1] = send, $[2] = toast, $[3] = t0, $[4] = t1) : (t0 = $[3], t1 = $[4]), useEffect(t0, t1);
|
|
2272
|
-
}
|
|
2273
|
-
function isEditable(target) {
|
|
2274
|
-
return target instanceof HTMLElement && (target.isContentEditable || target.matches("input, textarea, select"));
|
|
2275
|
-
}
|
|
2276
|
-
/**
|
|
2277
|
-
* Where a theme code someone shared gets pasted by hand — for browsers that
|
|
2278
|
-
* do not hand the clipboard over, and for codes that are not on it.
|
|
2279
|
-
*
|
|
2280
|
-
* @internal
|
|
2281
|
-
*/
|
|
2282
|
-
function PasteThemeDialog(props) {
|
|
2283
|
-
let $ = c(35), { onClose } = props, { addThemeFromText } = useThemeCodes(), [text, setText] = useState(""), [invalid, setInvalid] = useState(!1), t0;
|
|
2284
|
-
$[0] !== addThemeFromText || $[1] !== onClose || $[2] !== text ? (t0 = () => {
|
|
2285
|
-
addThemeFromText(text) ? onClose() : setInvalid(!0);
|
|
2286
|
-
}, $[0] = addThemeFromText, $[1] = onClose, $[2] = text, $[3] = t0) : t0 = $[3];
|
|
2287
|
-
let add = t0, t1;
|
|
2288
|
-
$[4] === onClose ? t1 = $[5] : (t1 = /* @__PURE__ */ jsx(Button, {
|
|
2289
|
-
mode: "ghost",
|
|
2290
|
-
onClick: onClose,
|
|
2291
|
-
padding: 2,
|
|
2292
|
-
text: "Cancel"
|
|
2293
|
-
}), $[4] = onClose, $[5] = t1);
|
|
2294
|
-
let t2;
|
|
2295
|
-
$[6] === text ? t2 = $[7] : (t2 = text.trim(), $[6] = text, $[7] = t2);
|
|
2296
|
-
let t3 = t2 === "", t4;
|
|
2297
|
-
$[8] !== add || $[9] !== t3 ? (t4 = /* @__PURE__ */ jsx(Button, {
|
|
2298
|
-
disabled: t3,
|
|
2299
|
-
onClick: add,
|
|
2300
|
-
padding: 2,
|
|
2301
|
-
text: "Add theme",
|
|
2302
|
-
tone: "primary"
|
|
2303
|
-
}), $[8] = add, $[9] = t3, $[10] = t4) : t4 = $[10];
|
|
2304
|
-
let t5;
|
|
2305
|
-
$[11] !== t1 || $[12] !== t4 ? (t5 = /* @__PURE__ */ jsxs(Flex, {
|
|
2306
|
-
gap: 2,
|
|
2307
|
-
justify: "flex-end",
|
|
2308
|
-
padding: 3,
|
|
2309
|
-
children: [t1, t4]
|
|
2310
|
-
}), $[11] = t1, $[12] = t4, $[13] = t5) : t5 = $[13];
|
|
2311
|
-
let t6;
|
|
2312
|
-
$[14] === add ? t6 = $[15] : (t6 = (event) => {
|
|
2313
|
-
event.preventDefault(), add();
|
|
2314
|
-
}, $[14] = add, $[15] = t6);
|
|
2315
|
-
let t7;
|
|
2316
|
-
$[16] === Symbol.for("react.memo_cache_sentinel") ? (t7 = /* @__PURE__ */ jsx(Text, {
|
|
2317
|
-
muted: !0,
|
|
2318
|
-
size: 1,
|
|
2319
|
-
children: "Paste the code someone copied from their themer — the theme is added to your list and applied."
|
|
2320
|
-
}), $[16] = t7) : t7 = $[16];
|
|
2321
|
-
let t8 = invalid ? "themer-paste-error" : void 0, t9 = invalid ? "That is not a theme code" : void 0, t10;
|
|
2322
|
-
$[17] === Symbol.for("react.memo_cache_sentinel") ? (t10 = (event_0) => {
|
|
2323
|
-
setText(event_0.currentTarget.value), setInvalid(!1);
|
|
2324
|
-
}, $[17] = t10) : t10 = $[17];
|
|
2325
|
-
let t11;
|
|
2326
|
-
$[18] !== invalid || $[19] !== t8 || $[20] !== t9 || $[21] !== text ? (t11 = /* @__PURE__ */ jsx(TextInput, {
|
|
2327
|
-
"aria-describedby": t8,
|
|
2328
|
-
"aria-invalid": invalid,
|
|
2329
|
-
"aria-label": "Theme code",
|
|
2330
|
-
autoFocus: !0,
|
|
2331
|
-
customValidity: t9,
|
|
2332
|
-
fontSize: 1,
|
|
2333
|
-
onChange: t10,
|
|
2334
|
-
placeholder: "AQ…",
|
|
2335
|
-
value: text
|
|
2336
|
-
}), $[18] = invalid, $[19] = t8, $[20] = t9, $[21] = text, $[22] = t11) : t11 = $[22];
|
|
2337
|
-
let t12;
|
|
2338
|
-
$[23] === invalid ? t12 = $[24] : (t12 = invalid && /* @__PURE__ */ jsx(Card, {
|
|
2339
|
-
id: "themer-paste-error",
|
|
2340
|
-
padding: 3,
|
|
2341
|
-
radius: 2,
|
|
2342
|
-
role: "alert",
|
|
2343
|
-
tone: "critical",
|
|
2344
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
2345
|
-
size: 1,
|
|
2346
|
-
children: "That is not a theme code — codes are one word, copied from a theme's menu."
|
|
2347
|
-
})
|
|
2348
|
-
}), $[23] = invalid, $[24] = t12);
|
|
2349
|
-
let t13;
|
|
2350
|
-
$[25] !== t11 || $[26] !== t12 ? (t13 = /* @__PURE__ */ jsxs(Stack, {
|
|
2351
|
-
gap: 3,
|
|
2352
|
-
padding: 4,
|
|
2353
|
-
children: [
|
|
2354
|
-
t7,
|
|
2355
|
-
t11,
|
|
2356
|
-
t12
|
|
2357
|
-
]
|
|
2358
|
-
}), $[25] = t11, $[26] = t12, $[27] = t13) : t13 = $[27];
|
|
2359
|
-
let t14;
|
|
2360
|
-
$[28] !== t13 || $[29] !== t6 ? (t14 = /* @__PURE__ */ jsx("form", {
|
|
2361
|
-
onSubmit: t6,
|
|
2362
|
-
children: t13
|
|
2363
|
-
}), $[28] = t13, $[29] = t6, $[30] = t14) : t14 = $[30];
|
|
2364
|
-
let t15;
|
|
2365
|
-
return $[31] !== onClose || $[32] !== t14 || $[33] !== t5 ? (t15 = /* @__PURE__ */ jsx(Dialog, {
|
|
2366
|
-
animate: !0,
|
|
2367
|
-
footer: t5,
|
|
2368
|
-
header: "Add a shared theme",
|
|
2369
|
-
id: "themer-paste",
|
|
2370
|
-
onClickOutside: onClose,
|
|
2371
|
-
onClose,
|
|
2372
|
-
width: 0,
|
|
2373
|
-
children: t14
|
|
2374
|
-
}), $[31] = onClose, $[32] = t14, $[33] = t5, $[34] = t15) : t15 = $[34], t15;
|
|
2375
|
-
}
|
|
2376
|
-
var frame$1 = "_1wsn7882", menuCard = "_1wsn7884", menuSlot = "_1wsn7883", pickButton = "_1wsn7881", root = "_1wsn7880";
|
|
2377
|
-
/**
|
|
2378
|
-
* Scrolls the applied theme's card into view when it is not already, as a
|
|
2379
|
-
* ref: the list scrolls to it as it mounts — coming back from the editor or
|
|
2380
|
-
* the removed themes — and when another theme gets applied
|
|
2381
|
-
*/
|
|
2382
|
-
function scrollRefIntoViewIfNeeded(node) {
|
|
2383
|
-
node && scrollIntoView(node, {
|
|
2384
|
-
block: "nearest",
|
|
2385
|
-
scrollMode: "if-needed"
|
|
2386
|
-
});
|
|
2387
|
-
}
|
|
2388
|
-
/**
|
|
2389
|
-
* One theme in the list: the floating preview with the title below it, like
|
|
2390
|
-
* an appearance option in macOS System Settings. Clicking it applies the
|
|
2391
|
-
* theme to the whole Studio, dragging it moves the theme to another place in
|
|
2392
|
-
* the list; its menu holds the move, edit, duplicate and remove flows that
|
|
2393
|
-
* apply to the theme.
|
|
2394
|
-
*
|
|
2395
|
-
* @internal
|
|
2396
|
-
*/
|
|
2397
|
-
function ThemeCard(props) {
|
|
2398
|
-
let $ = c(85), { active, theme } = props, { themes, send } = useThemer(), { copyTheme } = useThemeCodes(), { scheme } = useRootTheme(), [menuOpen, setMenuOpen] = useState(!1), dragControls = useDragControls(), dragged = useRef(!1), t0;
|
|
2399
|
-
$[0] === theme.title ? t0 = $[1] : (t0 = displayTitle(theme.title), $[0] = theme.title, $[1] = t0);
|
|
2400
|
-
let title = t0, t1;
|
|
2401
|
-
$[2] === theme.slug ? t1 = $[3] : (t1 = (candidate) => candidate.slug === theme.slug, $[2] = theme.slug, $[3] = t1);
|
|
2402
|
-
let index = themes.findIndex(t1), t2;
|
|
2403
|
-
$[4] !== index || $[5] !== send || $[6] !== themes ? (t2 = (to) => {
|
|
2404
|
-
let order = themes.map(_temp$3);
|
|
2405
|
-
order.splice(to, 0, ...order.splice(index, 1)), send({
|
|
2406
|
-
type: "theme.reorder",
|
|
2407
|
-
order
|
|
2408
|
-
});
|
|
2409
|
-
}, $[4] = index, $[5] = send, $[6] = themes, $[7] = t2) : t2 = $[7];
|
|
2410
|
-
let moveTo = t2, t3, t4;
|
|
2411
|
-
$[8] === Symbol.for("react.memo_cache_sentinel") ? (t3 = () => {
|
|
2412
|
-
requestAnimationFrame(() => {
|
|
2413
|
-
dragged.current = !1;
|
|
2414
|
-
});
|
|
2415
|
-
}, t4 = () => {
|
|
2416
|
-
dragged.current = !0;
|
|
2417
|
-
}, $[8] = t3, $[9] = t4) : (t3 = $[8], t4 = $[9]);
|
|
2418
|
-
let t5 = active ? scrollRefIntoViewIfNeeded : void 0, t6;
|
|
2419
|
-
$[10] === Symbol.for("react.memo_cache_sentinel") ? (t6 = { scale: 1.04 }, $[10] = t6) : t6 = $[10];
|
|
2420
|
-
let t7;
|
|
2421
|
-
$[11] !== send || $[12] !== theme.slug ? (t7 = () => {
|
|
2422
|
-
dragged.current || send({
|
|
2423
|
-
type: "theme.pick",
|
|
2424
|
-
slug: theme.slug
|
|
2425
|
-
});
|
|
2426
|
-
}, $[11] = send, $[12] = theme.slug, $[13] = t7) : t7 = $[13];
|
|
2427
|
-
let t8;
|
|
2428
|
-
$[14] === dragControls ? t8 = $[15] : (t8 = (event) => {
|
|
2429
|
-
event.pointerType !== "touch" && dragControls.start(event);
|
|
2430
|
-
}, $[14] = dragControls, $[15] = t8);
|
|
2431
|
-
let t9;
|
|
2432
|
-
$[16] === theme.options ? t9 = $[17] : (t9 = /* @__PURE__ */ jsx("span", {
|
|
2433
|
-
className: frame$1,
|
|
2434
|
-
children: /* @__PURE__ */ jsx(ThemeThumbnail, { options: theme.options })
|
|
2435
|
-
}), $[16] = theme.options, $[17] = t9);
|
|
2436
|
-
let t10 = active ? "medium" : "regular", t11;
|
|
2437
|
-
$[18] !== t10 || $[19] !== title ? (t11 = /* @__PURE__ */ jsx(Box, {
|
|
2438
|
-
as: "span",
|
|
2439
|
-
display: "block",
|
|
2440
|
-
paddingTop: 2,
|
|
2441
|
-
paddingX: 2,
|
|
2442
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
2443
|
-
align: "center",
|
|
2444
|
-
as: "span",
|
|
2445
|
-
size: 1,
|
|
2446
|
-
textOverflow: "ellipsis",
|
|
2447
|
-
weight: t10,
|
|
2448
|
-
children: title
|
|
2449
|
-
})
|
|
2450
|
-
}), $[18] = t10, $[19] = title, $[20] = t11) : t11 = $[20];
|
|
2451
|
-
let t12;
|
|
2452
|
-
$[21] !== active || $[22] !== t11 || $[23] !== t7 || $[24] !== t8 || $[25] !== t9 ? (t12 = /* @__PURE__ */ jsxs("button", {
|
|
2453
|
-
"aria-pressed": active,
|
|
2454
|
-
className: pickButton,
|
|
2455
|
-
onClick: t7,
|
|
2456
|
-
onPointerDown: t8,
|
|
2457
|
-
type: "button",
|
|
2458
|
-
children: [t9, t11]
|
|
2459
|
-
}), $[21] = active, $[22] = t11, $[23] = t7, $[24] = t8, $[25] = t9, $[26] = t12) : t12 = $[26];
|
|
2460
|
-
let t13;
|
|
2461
|
-
$[27] === Symbol.for("react.memo_cache_sentinel") ? (t13 = /* @__PURE__ */ jsx(Text, {
|
|
2462
|
-
size: 1,
|
|
2463
|
-
children: "Show more"
|
|
2464
|
-
}), $[27] = t13) : t13 = $[27];
|
|
2465
|
-
let t14 = `Show more for ${title}`, t15;
|
|
2466
|
-
$[28] === t14 ? t15 = $[29] : (t15 = /* @__PURE__ */ jsx(Button, {
|
|
2467
|
-
"aria-label": t14,
|
|
2468
|
-
fontSize: 1,
|
|
2469
|
-
icon: EllipsisHorizontalIcon,
|
|
2470
|
-
mode: "ghost",
|
|
2471
|
-
padding: 1,
|
|
2472
|
-
radius: 2
|
|
2473
|
-
}), $[28] = t14, $[29] = t15);
|
|
2474
|
-
let t16 = `themer-theme-${theme.slug}`, t17 = index <= 0, t18;
|
|
2475
|
-
$[30] !== index || $[31] !== moveTo ? (t18 = () => moveTo(index - 1), $[30] = index, $[31] = moveTo, $[32] = t18) : t18 = $[32];
|
|
2476
|
-
let t19;
|
|
2477
|
-
$[33] !== t17 || $[34] !== t18 ? (t19 = /* @__PURE__ */ jsx(MenuItem, {
|
|
2478
|
-
disabled: t17,
|
|
2479
|
-
icon: ArrowUpIcon,
|
|
2480
|
-
onClick: t18,
|
|
2481
|
-
text: "Move up"
|
|
2482
|
-
}), $[33] = t17, $[34] = t18, $[35] = t19) : t19 = $[35];
|
|
2483
|
-
let t20 = index >= themes.length - 1, t21;
|
|
2484
|
-
$[36] !== index || $[37] !== moveTo ? (t21 = () => moveTo(index + 1), $[36] = index, $[37] = moveTo, $[38] = t21) : t21 = $[38];
|
|
2485
|
-
let t22;
|
|
2486
|
-
$[39] !== t20 || $[40] !== t21 ? (t22 = /* @__PURE__ */ jsx(MenuItem, {
|
|
2487
|
-
disabled: t20,
|
|
2488
|
-
icon: ArrowDownIcon,
|
|
2489
|
-
onClick: t21,
|
|
2490
|
-
text: "Move down"
|
|
2491
|
-
}), $[39] = t20, $[40] = t21, $[41] = t22) : t22 = $[41];
|
|
2492
|
-
let t23;
|
|
2493
|
-
$[42] === Symbol.for("react.memo_cache_sentinel") ? (t23 = /* @__PURE__ */ jsx(MenuDivider, {}), $[42] = t23) : t23 = $[42];
|
|
2494
|
-
let t24;
|
|
2495
|
-
$[43] !== send || $[44] !== theme.slug || $[45] !== theme.source ? (t24 = theme.source === "custom" && /* @__PURE__ */ jsx(MenuItem, {
|
|
2496
|
-
icon: EditIcon,
|
|
2497
|
-
onClick: () => send({
|
|
2498
|
-
type: "theme.edit",
|
|
2499
|
-
slug: theme.slug
|
|
2500
|
-
}),
|
|
2501
|
-
text: "Edit"
|
|
2502
|
-
}), $[43] = send, $[44] = theme.slug, $[45] = theme.source, $[46] = t24) : t24 = $[46];
|
|
2503
|
-
let t25;
|
|
2504
|
-
$[47] !== send || $[48] !== theme.slug ? (t25 = () => send({
|
|
2505
|
-
type: "theme.duplicate",
|
|
2506
|
-
slug: theme.slug
|
|
2507
|
-
}), $[47] = send, $[48] = theme.slug, $[49] = t25) : t25 = $[49];
|
|
2508
|
-
let t26 = theme.source === "custom" ? "Duplicate" : "Duplicate to edit", t27;
|
|
2509
|
-
$[50] !== t25 || $[51] !== t26 ? (t27 = /* @__PURE__ */ jsx(MenuItem, {
|
|
2510
|
-
icon: CopyIcon,
|
|
2511
|
-
onClick: t25,
|
|
2512
|
-
text: t26
|
|
2513
|
-
}), $[50] = t25, $[51] = t26, $[52] = t27) : t27 = $[52];
|
|
2514
|
-
let t28;
|
|
2515
|
-
$[53] !== copyTheme || $[54] !== theme ? (t28 = /* @__PURE__ */ jsx(MenuItem, {
|
|
2516
|
-
icon: ShareIcon,
|
|
2517
|
-
onClick: () => void copyTheme(theme),
|
|
2518
|
-
text: "Copy to share"
|
|
2519
|
-
}), $[53] = copyTheme, $[54] = theme, $[55] = t28) : t28 = $[55];
|
|
2520
|
-
let t29;
|
|
2521
|
-
$[56] !== send || $[57] !== theme.slug || $[58] !== theme.source ? (t29 = theme.source !== "config" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MenuDivider, {}), /* @__PURE__ */ jsx(MenuItem, {
|
|
2522
|
-
icon: TrashIcon,
|
|
2523
|
-
onClick: () => send({
|
|
2524
|
-
type: "theme.remove",
|
|
2525
|
-
slug: theme.slug
|
|
2526
|
-
}),
|
|
2527
|
-
text: "Remove",
|
|
2528
|
-
tone: "critical"
|
|
2529
|
-
})] }), $[56] = send, $[57] = theme.slug, $[58] = theme.source, $[59] = t29) : t29 = $[59];
|
|
2530
|
-
let t30;
|
|
2531
|
-
$[60] !== t19 || $[61] !== t22 || $[62] !== t24 || $[63] !== t27 || $[64] !== t28 || $[65] !== t29 ? (t30 = /* @__PURE__ */ jsxs(Menu, { children: [
|
|
2532
|
-
t19,
|
|
2533
|
-
t22,
|
|
2534
|
-
t23,
|
|
2535
|
-
t24,
|
|
2536
|
-
t27,
|
|
2537
|
-
t28,
|
|
2538
|
-
t29
|
|
2539
|
-
] }), $[60] = t19, $[61] = t22, $[62] = t24, $[63] = t27, $[64] = t28, $[65] = t29, $[66] = t30) : t30 = $[66];
|
|
2540
|
-
let t31, t32;
|
|
2541
|
-
$[67] === Symbol.for("react.memo_cache_sentinel") ? (t31 = () => setMenuOpen(!1), t32 = () => setMenuOpen(!0), $[67] = t31, $[68] = t32) : (t31 = $[67], t32 = $[68]);
|
|
2542
|
-
let t33;
|
|
2543
|
-
$[69] === scheme ? t33 = $[70] : (t33 = {
|
|
2544
|
-
animate: !0,
|
|
2545
|
-
placement: "bottom-end",
|
|
2546
|
-
portal: !0,
|
|
2547
|
-
scheme
|
|
2548
|
-
}, $[69] = scheme, $[70] = t33);
|
|
2549
|
-
let t34;
|
|
2550
|
-
$[71] !== t15 || $[72] !== t16 || $[73] !== t30 || $[74] !== t33 ? (t34 = /* @__PURE__ */ jsx(Tooltip, {
|
|
2551
|
-
animate: !0,
|
|
2552
|
-
content: t13,
|
|
2553
|
-
placement: "bottom",
|
|
2554
|
-
portal: !0,
|
|
2555
|
-
children: /* @__PURE__ */ jsx(Card, {
|
|
2556
|
-
className: menuCard,
|
|
2557
|
-
radius: 2,
|
|
2558
|
-
scheme: "dark",
|
|
2559
|
-
children: /* @__PURE__ */ jsx(MenuButton, {
|
|
2560
|
-
button: t15,
|
|
2561
|
-
id: t16,
|
|
2562
|
-
menu: t30,
|
|
2563
|
-
onClose: t31,
|
|
2564
|
-
onOpen: t32,
|
|
2565
|
-
popover: t33
|
|
2566
|
-
})
|
|
2567
|
-
})
|
|
2568
|
-
}), $[71] = t15, $[72] = t16, $[73] = t30, $[74] = t33, $[75] = t34) : t34 = $[75];
|
|
2569
|
-
let t35;
|
|
2570
|
-
$[76] !== menuOpen || $[77] !== t34 ? (t35 = /* @__PURE__ */ jsx("div", {
|
|
2571
|
-
className: menuSlot,
|
|
2572
|
-
"data-visible": menuOpen,
|
|
2573
|
-
children: t34
|
|
2574
|
-
}), $[76] = menuOpen, $[77] = t34, $[78] = t35) : t35 = $[78];
|
|
2575
|
-
let t36;
|
|
2576
|
-
return $[79] !== dragControls || $[80] !== t12 || $[81] !== t35 || $[82] !== t5 || $[83] !== theme.slug ? (t36 = /* @__PURE__ */ jsxs(Reorder.Item, {
|
|
2577
|
-
as: "div",
|
|
2578
|
-
className: root,
|
|
2579
|
-
dragControls,
|
|
2580
|
-
dragListener: !1,
|
|
2581
|
-
onDragEnd: t3,
|
|
2582
|
-
onDragStart: t4,
|
|
2583
|
-
ref: t5,
|
|
2584
|
-
value: theme.slug,
|
|
2585
|
-
whileDrag: t6,
|
|
2586
|
-
children: [t12, t35]
|
|
2587
|
-
}), $[79] = dragControls, $[80] = t12, $[81] = t35, $[82] = t5, $[83] = theme.slug, $[84] = t36) : t36 = $[84], t36;
|
|
2588
|
-
}
|
|
2589
|
-
function _temp$3(candidate_0) {
|
|
2590
|
-
return candidate_0.slug;
|
|
2591
|
-
}
|
|
2592
|
-
var cardGrid = "twjqgh0";
|
|
2593
|
-
/**
|
|
2594
|
-
* One column at the sidebar's default width; wider sidebars — and the
|
|
2595
|
-
* overlay on small screens — fit more cards per row
|
|
2596
|
-
*/
|
|
2597
|
-
/**
|
|
2598
|
-
* The flow for picking a theme: a grid of theme cards — the configured theme,
|
|
2599
|
-
* the presets and the user's own themes — that drag into the order the user
|
|
2600
|
-
* wants, with the entry points to the add and restore flows below. A theme
|
|
2601
|
-
* code someone shared pastes right into the list, or through the paste button.
|
|
2602
|
-
*
|
|
2603
|
-
* @internal
|
|
2604
|
-
*/
|
|
2605
|
-
function ThemeList() {
|
|
2606
|
-
let $ = c(38), { themes, removed, active, send } = useThemer(), { addThemeFromClipboard } = useThemeCodes(), [pasting, setPasting] = useState(!1);
|
|
2607
|
-
usePasteThemeCodes();
|
|
2608
|
-
let t0;
|
|
2609
|
-
$[0] === send ? t0 = $[1] : (t0 = (palette, file) => send({
|
|
2610
|
-
type: "theme.add",
|
|
2611
|
-
title: titleFromFileName(file.name),
|
|
2612
|
-
options: optionsFromImagePalette(palette),
|
|
2613
|
-
palette,
|
|
2614
|
-
imageUrl: URL.createObjectURL(file)
|
|
2615
|
-
}), $[0] = send, $[1] = t0);
|
|
2616
|
-
let { busy, pickImage } = useImagePalette(t0), t1;
|
|
2617
|
-
$[2] === send ? t1 = $[3] : (t1 = (order) => send({
|
|
2618
|
-
type: "theme.reorder",
|
|
2619
|
-
order
|
|
2620
|
-
}), $[2] = send, $[3] = t1);
|
|
2621
|
-
let t2;
|
|
2622
|
-
$[4] === themes ? t2 = $[5] : (t2 = themes.map(_temp$2), $[4] = themes, $[5] = t2);
|
|
2623
|
-
let t3;
|
|
2624
|
-
if ($[6] !== active || $[7] !== themes) {
|
|
2625
|
-
let t4;
|
|
2626
|
-
$[9] === active ? t4 = $[10] : (t4 = (theme_0) => /* @__PURE__ */ jsx(ThemeCard, {
|
|
2627
|
-
active: theme_0.slug === active.slug,
|
|
2628
|
-
theme: theme_0
|
|
2629
|
-
}, theme_0.slug), $[9] = active, $[10] = t4), t3 = themes.map(t4), $[6] = active, $[7] = themes, $[8] = t3;
|
|
2630
|
-
} else t3 = $[8];
|
|
2631
|
-
let t4;
|
|
2632
|
-
$[11] !== t1 || $[12] !== t2 || $[13] !== t3 ? (t4 = /* @__PURE__ */ jsx(MotionConfig, {
|
|
2633
|
-
reducedMotion: "user",
|
|
2634
|
-
children: /* @__PURE__ */ jsx(Reorder.Group, {
|
|
2635
|
-
as: "div",
|
|
2636
|
-
className: cardGrid,
|
|
2637
|
-
onReorder: t1,
|
|
2638
|
-
values: t2,
|
|
2639
|
-
children: t3
|
|
2640
|
-
})
|
|
2641
|
-
}), $[11] = t1, $[12] = t2, $[13] = t3, $[14] = t4) : t4 = $[14];
|
|
2642
|
-
let t5;
|
|
2643
|
-
$[15] !== removed.length || $[16] !== send ? (t5 = removed.length > 0 && /* @__PURE__ */ jsx(Button, {
|
|
2644
|
-
gap: 2,
|
|
2645
|
-
icon: RestoreIcon,
|
|
2646
|
-
mode: "bleed",
|
|
2647
|
-
onClick: () => send({ type: "flow.removed" }),
|
|
2648
|
-
padding: 2,
|
|
2649
|
-
text: `Show removed (${removed.length})`,
|
|
2650
|
-
width: "fill"
|
|
2651
|
-
}), $[15] = removed.length, $[16] = send, $[17] = t5) : t5 = $[17];
|
|
2652
|
-
let t6;
|
|
2653
|
-
$[18] !== t4 || $[19] !== t5 ? (t6 = /* @__PURE__ */ jsx(ScrollArea, {
|
|
2654
|
-
padding: 3,
|
|
2655
|
-
children: /* @__PURE__ */ jsxs(Stack, {
|
|
2656
|
-
gap: 4,
|
|
2657
|
-
children: [t4, t5]
|
|
2658
|
-
})
|
|
2659
|
-
}), $[18] = t4, $[19] = t5, $[20] = t6) : t6 = $[20];
|
|
2660
|
-
let t7;
|
|
2661
|
-
$[21] === send ? t7 = $[22] : (t7 = /* @__PURE__ */ jsx(Box, {
|
|
2662
|
-
flex: 1,
|
|
2663
|
-
children: /* @__PURE__ */ jsx(TooltipButton, {
|
|
2664
|
-
icon: AddIcon,
|
|
2665
|
-
mode: "ghost",
|
|
2666
|
-
onClick: () => send({ type: "theme.add" }),
|
|
2667
|
-
text: "Add theme",
|
|
2668
|
-
tooltip: "Add a theme based on the applied one",
|
|
2669
|
-
width: "fill"
|
|
2670
|
-
})
|
|
2671
|
-
}), $[21] = send, $[22] = t7);
|
|
2672
|
-
let t8;
|
|
2673
|
-
$[23] !== busy || $[24] !== pickImage ? (t8 = /* @__PURE__ */ jsx(ImageFileButton, {
|
|
2674
|
-
loading: busy,
|
|
2675
|
-
mode: "ghost",
|
|
2676
|
-
onFile: pickImage,
|
|
2677
|
-
tooltip: "Add a theme from the colors of an image"
|
|
2678
|
-
}), $[23] = busy, $[24] = pickImage, $[25] = t8) : t8 = $[25];
|
|
2679
|
-
let t9;
|
|
2680
|
-
$[26] === addThemeFromClipboard ? t9 = $[27] : (t9 = /* @__PURE__ */ jsx(TooltipButton, {
|
|
2681
|
-
icon: ClipboardIcon,
|
|
2682
|
-
mode: "ghost",
|
|
2683
|
-
onClick: async () => {
|
|
2684
|
-
await addThemeFromClipboard() !== "added" && setPasting(!0);
|
|
2685
|
-
},
|
|
2686
|
-
tooltip: "Add a theme from a code someone shared"
|
|
2687
|
-
}), $[26] = addThemeFromClipboard, $[27] = t9);
|
|
2688
|
-
let t10;
|
|
2689
|
-
$[28] !== t7 || $[29] !== t8 || $[30] !== t9 ? (t10 = /* @__PURE__ */ jsx(Card, {
|
|
2690
|
-
borderTop: !0,
|
|
2691
|
-
padding: 3,
|
|
2692
|
-
children: /* @__PURE__ */ jsxs(Flex, {
|
|
2693
|
-
gap: 2,
|
|
2694
|
-
children: [
|
|
2695
|
-
t7,
|
|
2696
|
-
t8,
|
|
2697
|
-
t9
|
|
2698
|
-
]
|
|
2699
|
-
})
|
|
2700
|
-
}), $[28] = t7, $[29] = t8, $[30] = t9, $[31] = t10) : t10 = $[31];
|
|
2701
|
-
let t11;
|
|
2702
|
-
$[32] === pasting ? t11 = $[33] : (t11 = pasting && /* @__PURE__ */ jsx(PasteThemeDialog, { onClose: () => setPasting(!1) }), $[32] = pasting, $[33] = t11);
|
|
2703
|
-
let t12;
|
|
2704
|
-
return $[34] !== t10 || $[35] !== t11 || $[36] !== t6 ? (t12 = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2705
|
-
t6,
|
|
2706
|
-
t10,
|
|
2707
|
-
t11
|
|
2708
|
-
] }), $[34] = t10, $[35] = t11, $[36] = t6, $[37] = t12) : t12 = $[37], t12;
|
|
2709
|
-
}
|
|
2710
|
-
function _temp$2(theme) {
|
|
2711
|
-
return theme.slug;
|
|
2712
|
-
}
|
|
2713
|
-
/**
|
|
2714
|
-
* Serializes theme options into the `buildTheme` call to paste into
|
|
2715
|
-
* `sanity.config.ts`, keeping only what differs from the derived defaults in
|
|
2716
|
-
* each scheme.
|
|
2717
|
-
*
|
|
2718
|
-
* Options that boil down to the stock Studio theme need nothing from this
|
|
2719
|
-
* package — so they serialize to a bare `buildTheme()` from `@sanity/ui/theme`
|
|
2720
|
-
* instead.
|
|
2721
|
-
*
|
|
2722
|
-
* @internal
|
|
2723
|
-
*/
|
|
2724
|
-
function createThemeSnippet(options) {
|
|
2725
|
-
let minimized = minimizeOptions(options);
|
|
2726
|
-
if (minimized === null) return "import {buildTheme} from '@sanity/ui/theme'\n\nexport const theme = buildTheme()\n";
|
|
2727
|
-
let lines = [];
|
|
2728
|
-
for (let scheme of SCHEMES) {
|
|
2729
|
-
let values = minimized[scheme];
|
|
2730
|
-
values && lines.push(serializeScheme(scheme, values));
|
|
2731
|
-
}
|
|
2732
|
-
return `import {buildTheme} from '@sanity/themer'\n\nexport const theme = buildTheme({\n${lines.join("\n")}\n})\n`;
|
|
2733
|
-
}
|
|
2734
|
-
/** A scheme with up to two options fits on one line, more spread out */
|
|
2735
|
-
function serializeScheme(scheme, values) {
|
|
2736
|
-
let entries = [];
|
|
2737
|
-
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 },`;
|
|
2738
|
-
}
|
|
2739
|
-
registerLanguage(typescript);
|
|
2740
|
-
/**
|
|
2741
|
-
* Shows the `buildTheme` snippet that makes a theme permanent, in a dialog so
|
|
2742
|
-
* that it does not take up sidebar space or make the sidebar scroll as the
|
|
2743
|
-
* snippet changes shape with the theme.
|
|
2744
|
-
*
|
|
2745
|
-
* @internal
|
|
2746
|
-
*/
|
|
2747
|
-
function ThemeSnippetDialog(props) {
|
|
2748
|
-
let $ = c(23), { onClose, theme } = props, toast = useToast(), t0;
|
|
2749
|
-
$[0] === theme.options ? t0 = $[1] : (t0 = createThemeSnippet(theme.options), $[0] = theme.options, $[1] = t0);
|
|
2750
|
-
let snippet = t0, t1;
|
|
2751
|
-
$[2] !== snippet || $[3] !== toast ? (t1 = async () => {
|
|
2752
|
-
try {
|
|
2753
|
-
await navigator.clipboard.writeText(snippet), toast.push({
|
|
2754
|
-
status: "success",
|
|
2755
|
-
title: "Copied the theme to the clipboard"
|
|
2756
|
-
});
|
|
2757
|
-
} catch {
|
|
2758
|
-
toast.push({
|
|
2759
|
-
status: "error",
|
|
2760
|
-
title: "Could not copy the theme"
|
|
2761
|
-
});
|
|
2762
|
-
}
|
|
2763
|
-
}, $[2] = snippet, $[3] = toast, $[4] = t1) : t1 = $[4];
|
|
2764
|
-
let handleCopy = t1, t2;
|
|
2765
|
-
$[5] === handleCopy ? t2 = $[6] : (t2 = /* @__PURE__ */ jsx(Flex, {
|
|
2766
|
-
gap: 2,
|
|
2767
|
-
justify: "flex-end",
|
|
2768
|
-
padding: 3,
|
|
2769
|
-
children: /* @__PURE__ */ jsx(Button, {
|
|
2770
|
-
gap: 2,
|
|
2771
|
-
icon: ClipboardIcon,
|
|
2772
|
-
mode: "ghost",
|
|
2773
|
-
onClick: () => void handleCopy(),
|
|
2774
|
-
padding: 2,
|
|
2775
|
-
text: "Copy"
|
|
2776
|
-
})
|
|
2777
|
-
}), $[5] = handleCopy, $[6] = t2);
|
|
2778
|
-
let t3;
|
|
2779
|
-
$[7] === Symbol.for("react.memo_cache_sentinel") ? (t3 = /* @__PURE__ */ jsx("code", { children: "sanity.config.ts" }), $[7] = t3) : t3 = $[7];
|
|
2780
|
-
let t4;
|
|
2781
|
-
$[8] === Symbol.for("react.memo_cache_sentinel") ? (t4 = /* @__PURE__ */ jsx("code", { children: "theme" }), $[8] = t4) : t4 = $[8];
|
|
2782
|
-
let t5;
|
|
2783
|
-
$[9] === Symbol.for("react.memo_cache_sentinel") ? (t5 = /* @__PURE__ */ jsx("code", { children: "defineConfig" }), $[9] = t5) : t5 = $[9];
|
|
2784
|
-
let t6;
|
|
2785
|
-
$[10] === theme.title ? t6 = $[11] : (t6 = displayTitle(theme.title), $[10] = theme.title, $[11] = t6);
|
|
2786
|
-
let t7;
|
|
2787
|
-
$[12] === t6 ? t7 = $[13] : (t7 = /* @__PURE__ */ jsxs(Text, {
|
|
2788
|
-
muted: !0,
|
|
2789
|
-
size: 1,
|
|
2790
|
-
children: [
|
|
2791
|
-
"Export this from ",
|
|
2792
|
-
t3,
|
|
2793
|
-
" and pass it as the ",
|
|
2794
|
-
t4,
|
|
2795
|
-
" of",
|
|
2796
|
-
" ",
|
|
2797
|
-
t5,
|
|
2798
|
-
" to make “",
|
|
2799
|
-
t6,
|
|
2800
|
-
"” the Studio theme without this tool."
|
|
2801
|
-
]
|
|
2802
|
-
}), $[12] = t6, $[13] = t7);
|
|
2803
|
-
let t8;
|
|
2804
|
-
$[14] === snippet ? t8 = $[15] : (t8 = /* @__PURE__ */ jsx(Card, {
|
|
2805
|
-
border: !0,
|
|
2806
|
-
overflow: "auto",
|
|
2807
|
-
padding: 3,
|
|
2808
|
-
radius: 2,
|
|
2809
|
-
tone: "transparent",
|
|
2810
|
-
children: /* @__PURE__ */ jsx(Code, {
|
|
2811
|
-
language: "ts",
|
|
2812
|
-
size: 1,
|
|
2813
|
-
children: snippet
|
|
2814
|
-
})
|
|
2815
|
-
}), $[14] = snippet, $[15] = t8);
|
|
2816
|
-
let t9;
|
|
2817
|
-
$[16] !== t7 || $[17] !== t8 ? (t9 = /* @__PURE__ */ jsx(Box, {
|
|
2818
|
-
padding: 4,
|
|
2819
|
-
children: /* @__PURE__ */ jsxs(Stack, {
|
|
2820
|
-
gap: 4,
|
|
2821
|
-
children: [t7, t8]
|
|
2822
|
-
})
|
|
2823
|
-
}), $[16] = t7, $[17] = t8, $[18] = t9) : t9 = $[18];
|
|
2824
|
-
let t10;
|
|
2825
|
-
return $[19] !== onClose || $[20] !== t2 || $[21] !== t9 ? (t10 = /* @__PURE__ */ jsx(Dialog, {
|
|
2826
|
-
animate: !0,
|
|
2827
|
-
footer: t2,
|
|
2828
|
-
header: "Add to your config",
|
|
2829
|
-
id: "themer-snippet",
|
|
2830
|
-
onClickOutside: onClose,
|
|
2831
|
-
onClose,
|
|
2832
|
-
width: 1,
|
|
2833
|
-
children: t9
|
|
2834
|
-
}), $[19] = onClose, $[20] = t2, $[21] = t9, $[22] = t10) : t10 = $[22], t10;
|
|
2835
|
-
}
|
|
2836
|
-
var title = "c2pqej0";
|
|
2837
|
-
const VIEW_TITLES = {
|
|
2838
|
-
list: "Themer",
|
|
2839
|
-
edit: "Edit theme",
|
|
2840
|
-
removed: "Removed themes"
|
|
2841
|
-
};
|
|
2842
|
-
/**
|
|
2843
|
-
* The themer sidebar: a header that navigates between the flows and toggles
|
|
2844
|
-
* the split preview, the flow itself — picking a theme, editing one, or
|
|
2845
|
-
* restoring removed ones — and the dialog with the `buildTheme` snippet of
|
|
2846
|
-
* the applied theme.
|
|
2847
|
-
*
|
|
2848
|
-
* @internal
|
|
2849
|
-
*/
|
|
2850
|
-
function ThemerSidebar() {
|
|
2851
|
-
let $ = c(42), { active, split, view, navbarHeight, send } = useThemer(), [snippetOpen, setSnippetOpen] = useState(!1), inList = view.name === "list", t0;
|
|
2852
|
-
$[0] === navbarHeight ? t0 = $[1] : (t0 = navbarHeight === null ? void 0 : { height: navbarHeight }, $[0] = navbarHeight, $[1] = t0);
|
|
2853
|
-
let t1;
|
|
2854
|
-
$[2] !== inList || $[3] !== send ? (t1 = !inList && /* @__PURE__ */ jsx(TooltipButton, {
|
|
2855
|
-
icon: ArrowLeftIcon,
|
|
2856
|
-
mode: "bleed",
|
|
2857
|
-
onClick: () => send({ type: "flow.list" }),
|
|
2858
|
-
padding: 2,
|
|
2859
|
-
tooltip: "Back to the themes"
|
|
2860
|
-
}), $[2] = inList, $[3] = send, $[4] = t1) : t1 = $[4];
|
|
2861
|
-
let t2 = VIEW_TITLES[view.name], t3;
|
|
2862
|
-
$[5] === t2 ? t3 = $[6] : (t3 = /* @__PURE__ */ jsx(Box, {
|
|
2863
|
-
className: title,
|
|
2864
|
-
flex: 1,
|
|
2865
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
2866
|
-
size: 1,
|
|
2867
|
-
textOverflow: "ellipsis",
|
|
2868
|
-
weight: "semibold",
|
|
2869
|
-
children: t2
|
|
2870
|
-
})
|
|
2871
|
-
}), $[5] = t2, $[6] = t3);
|
|
2872
|
-
let t4;
|
|
2873
|
-
$[7] !== send || $[8] !== split || $[9] !== view.name ? (t4 = view.name !== "removed" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(TooltipButton, {
|
|
2874
|
-
"aria-pressed": split,
|
|
2875
|
-
icon: SplitVerticalIcon,
|
|
2876
|
-
mode: "bleed",
|
|
2877
|
-
onClick: () => send({ type: "preview.toggle" }),
|
|
2878
|
-
padding: 2,
|
|
2879
|
-
selected: split,
|
|
2880
|
-
tooltip: "Show light and dark side by side"
|
|
2881
|
-
}), /* @__PURE__ */ jsx(TooltipButton, {
|
|
2882
|
-
icon: CodeBlockIcon,
|
|
2883
|
-
mode: "bleed",
|
|
2884
|
-
onClick: () => setSnippetOpen(!0),
|
|
2885
|
-
padding: 2,
|
|
2886
|
-
tooltip: "Show the code for the applied theme"
|
|
2887
|
-
})] }), $[7] = send, $[8] = split, $[9] = view.name, $[10] = t4) : t4 = $[10];
|
|
2888
|
-
let t5;
|
|
2889
|
-
$[11] === send ? t5 = $[12] : (t5 = /* @__PURE__ */ jsx(TooltipButton, {
|
|
2890
|
-
icon: CloseIcon,
|
|
2891
|
-
mode: "bleed",
|
|
2892
|
-
onClick: () => send({ type: "sidebar.close" }),
|
|
2893
|
-
padding: 2,
|
|
2894
|
-
tooltip: "Close themer"
|
|
2895
|
-
}), $[11] = send, $[12] = t5);
|
|
2896
|
-
let t6;
|
|
2897
|
-
$[13] !== t4 || $[14] !== t5 ? (t6 = /* @__PURE__ */ jsxs(Flex, { children: [t4, t5] }), $[13] = t4, $[14] = t5, $[15] = t6) : t6 = $[15];
|
|
2898
|
-
let t7;
|
|
2899
|
-
$[16] !== t1 || $[17] !== t3 || $[18] !== t6 ? (t7 = /* @__PURE__ */ jsxs(Flex, {
|
|
2900
|
-
align: "center",
|
|
2901
|
-
gap: 1,
|
|
2902
|
-
height: "fill",
|
|
2903
|
-
children: [
|
|
2904
|
-
t1,
|
|
2905
|
-
t3,
|
|
2906
|
-
t6
|
|
2907
|
-
]
|
|
2908
|
-
}), $[16] = t1, $[17] = t3, $[18] = t6, $[19] = t7) : t7 = $[19];
|
|
2909
|
-
let t8;
|
|
2910
|
-
$[20] !== t0 || $[21] !== t7 ? (t8 = /* @__PURE__ */ jsx(Card, {
|
|
2911
|
-
borderBottom: !0,
|
|
2912
|
-
padding: 3,
|
|
2913
|
-
sizing: "border",
|
|
2914
|
-
style: t0,
|
|
2915
|
-
children: t7
|
|
2916
|
-
}), $[20] = t0, $[21] = t7, $[22] = t8) : t8 = $[22];
|
|
2917
|
-
let t9;
|
|
2918
|
-
$[23] === view.name ? t9 = $[24] : (t9 = view.name === "list" && /* @__PURE__ */ jsx(ThemeList, {}), $[23] = view.name, $[24] = t9);
|
|
2919
|
-
let t10;
|
|
2920
|
-
$[25] !== view.focusTitle || $[26] !== view.name || $[27] !== view.slug ? (t10 = view.name === "edit" && /* @__PURE__ */ jsx(ThemeEditor, {
|
|
2921
|
-
focusTitle: view.focusTitle,
|
|
2922
|
-
slug: view.slug
|
|
2923
|
-
}), $[25] = view.focusTitle, $[26] = view.name, $[27] = view.slug, $[28] = t10) : t10 = $[28];
|
|
2924
|
-
let t11;
|
|
2925
|
-
$[29] === view.name ? t11 = $[30] : (t11 = view.name === "removed" && /* @__PURE__ */ jsx(RemovedThemes, {}), $[29] = view.name, $[30] = t11);
|
|
2926
|
-
let t12;
|
|
2927
|
-
$[31] !== t10 || $[32] !== t11 || $[33] !== t8 || $[34] !== t9 ? (t12 = /* @__PURE__ */ jsxs(Flex, {
|
|
2928
|
-
direction: "column",
|
|
2929
|
-
height: "fill",
|
|
2930
|
-
children: [
|
|
2931
|
-
t8,
|
|
2932
|
-
t9,
|
|
2933
|
-
t10,
|
|
2934
|
-
t11
|
|
2935
|
-
]
|
|
2936
|
-
}), $[31] = t10, $[32] = t11, $[33] = t8, $[34] = t9, $[35] = t12) : t12 = $[35];
|
|
2937
|
-
let t13;
|
|
2938
|
-
$[36] !== active || $[37] !== snippetOpen ? (t13 = snippetOpen && /* @__PURE__ */ jsx(ThemeSnippetDialog, {
|
|
2939
|
-
onClose: () => setSnippetOpen(!1),
|
|
2940
|
-
theme: active
|
|
2941
|
-
}), $[36] = active, $[37] = snippetOpen, $[38] = t13) : t13 = $[38];
|
|
2942
|
-
let t14;
|
|
2943
|
-
return $[39] !== t12 || $[40] !== t13 ? (t14 = /* @__PURE__ */ jsxs(Card, {
|
|
2944
|
-
height: "fill",
|
|
2945
|
-
children: [t12, t13]
|
|
2946
|
-
}), $[39] = t12, $[40] = t13, $[41] = t14) : t14 = $[41], t14;
|
|
2947
|
-
}
|
|
2948
|
-
var content = "bgn4np4", frame = "bgn4np3", sidebar = "bgn4np2";
|
|
2949
|
-
/** How far one arrow key press resizes the sidebar */
|
|
2950
|
-
const WIDTH_STORAGE_KEY = "sanityStudio:themer:width";
|
|
2951
|
-
function clampWidth(width) {
|
|
2952
|
-
return Math.min(400, Math.max(200, Math.round(width)));
|
|
2953
|
-
}
|
|
2954
|
-
function readStoredWidth() {
|
|
2955
|
-
try {
|
|
2956
|
-
let stored = Number(localStorage.getItem(WIDTH_STORAGE_KEY));
|
|
2957
|
-
return Number.isFinite(stored) && stored > 0 ? clampWidth(stored) : 200;
|
|
2958
|
-
} catch {
|
|
2959
|
-
return 200;
|
|
2960
|
-
}
|
|
2961
|
-
}
|
|
2962
|
-
function writeStoredWidth(width) {
|
|
2963
|
-
try {
|
|
2964
|
-
width === 200 ? localStorage.removeItem(WIDTH_STORAGE_KEY) : localStorage.setItem(WIDTH_STORAGE_KEY, String(width));
|
|
2965
|
-
} catch {}
|
|
2966
|
-
}
|
|
2967
|
-
/**
|
|
2968
|
-
* The themer sidebar, in a layer along the right edge of the Studio. Its left
|
|
2969
|
-
* edge drags (or arrow-keys) it wider, up to twice its default width — the
|
|
2970
|
-
* width sticks between sessions. As an overlay (small screens) it covers the
|
|
2971
|
-
* Studio edge to edge instead, with nothing to resize.
|
|
2972
|
-
*
|
|
2973
|
-
* @internal
|
|
2974
|
-
*/
|
|
2975
|
-
function ResizableSidebar(props) {
|
|
2976
|
-
let $ = c(24), { overlay } = props, [width, setWidth] = useState(readStoredWidth), [dragging, setDragging] = useState(!1), drag = useRef(null), t0, t1;
|
|
2977
|
-
$[0] === width ? (t0 = $[1], t1 = $[2]) : (t0 = () => writeStoredWidth(width), t1 = [width], $[0] = width, $[1] = t0, $[2] = t1), useEffect(t0, t1);
|
|
2978
|
-
let t2;
|
|
2979
|
-
$[3] === width ? t2 = $[4] : (t2 = (event) => {
|
|
2980
|
-
event.button === 0 && (event.preventDefault(), event.currentTarget.setPointerCapture(event.pointerId), drag.current = {
|
|
2981
|
-
pointerId: event.pointerId,
|
|
2982
|
-
startX: event.clientX,
|
|
2983
|
-
startWidth: width
|
|
2984
|
-
}, setDragging(!0));
|
|
2985
|
-
}, $[3] = width, $[4] = t2);
|
|
2986
|
-
let handlePointerDown = t2, t3;
|
|
2987
|
-
$[5] === Symbol.for("react.memo_cache_sentinel") ? (t3 = (event_0) => {
|
|
2988
|
-
drag.current && drag.current.pointerId === event_0.pointerId && setWidth(clampWidth(drag.current.startWidth - (event_0.clientX - drag.current.startX)));
|
|
2989
|
-
}, $[5] = t3) : t3 = $[5];
|
|
2990
|
-
let handlePointerMove = t3, t4;
|
|
2991
|
-
$[6] === Symbol.for("react.memo_cache_sentinel") ? (t4 = (event_1) => {
|
|
2992
|
-
drag.current && drag.current.pointerId === event_1.pointerId && (drag.current = null, setDragging(!1));
|
|
2993
|
-
}, $[6] = t4) : t4 = $[6];
|
|
2994
|
-
let handlePointerUp = t4, t5;
|
|
2995
|
-
$[7] === Symbol.for("react.memo_cache_sentinel") ? (t5 = (event_2) => {
|
|
2996
|
-
let step = {
|
|
2997
|
-
ArrowLeft: 16,
|
|
2998
|
-
ArrowRight: -16,
|
|
2999
|
-
Home: 400,
|
|
3000
|
-
End: -400
|
|
3001
|
-
}[event_2.key];
|
|
3002
|
-
step !== void 0 && (event_2.preventDefault(), setWidth((current) => clampWidth(current + step)));
|
|
3003
|
-
}, $[7] = t5) : t5 = $[7];
|
|
3004
|
-
let handleKeyDown = t5, t6;
|
|
3005
|
-
$[8] !== overlay || $[9] !== width ? (t6 = overlay ? void 0 : { width }, $[8] = overlay, $[9] = width, $[10] = t6) : t6 = $[10];
|
|
3006
|
-
let t7 = !overlay, t8;
|
|
3007
|
-
$[11] !== dragging || $[12] !== handlePointerDown || $[13] !== overlay || $[14] !== width ? (t8 = !overlay && /* @__PURE__ */ jsx("div", {
|
|
3008
|
-
"aria-label": "Resize the themer",
|
|
3009
|
-
className: "bgn4np5",
|
|
3010
|
-
"aria-orientation": "vertical",
|
|
3011
|
-
"aria-valuemax": 400,
|
|
3012
|
-
"aria-valuemin": 200,
|
|
3013
|
-
"aria-valuenow": width,
|
|
3014
|
-
"data-dragging": dragging,
|
|
3015
|
-
onDoubleClick: () => setWidth(200),
|
|
3016
|
-
onKeyDown: handleKeyDown,
|
|
3017
|
-
onPointerCancel: handlePointerUp,
|
|
3018
|
-
onPointerDown: handlePointerDown,
|
|
3019
|
-
onPointerMove: handlePointerMove,
|
|
3020
|
-
onPointerUp: handlePointerUp,
|
|
3021
|
-
role: "separator",
|
|
3022
|
-
tabIndex: 0
|
|
3023
|
-
}), $[11] = dragging, $[12] = handlePointerDown, $[13] = overlay, $[14] = width, $[15] = t8) : t8 = $[15];
|
|
3024
|
-
let t9;
|
|
3025
|
-
$[16] === Symbol.for("react.memo_cache_sentinel") ? (t9 = /* @__PURE__ */ jsx(Box, {
|
|
3026
|
-
className: content,
|
|
3027
|
-
height: "fill",
|
|
3028
|
-
overflow: "hidden",
|
|
3029
|
-
children: /* @__PURE__ */ jsx(ThemerSidebar, {})
|
|
3030
|
-
}), $[16] = t9) : t9 = $[16];
|
|
3031
|
-
let t10;
|
|
3032
|
-
$[17] !== t7 || $[18] !== t8 ? (t10 = /* @__PURE__ */ jsxs(Card, {
|
|
3033
|
-
borderLeft: t7,
|
|
3034
|
-
className: frame,
|
|
3035
|
-
height: "fill",
|
|
3036
|
-
children: [t8, t9]
|
|
3037
|
-
}), $[17] = t7, $[18] = t8, $[19] = t10) : t10 = $[19];
|
|
3038
|
-
let t11;
|
|
3039
|
-
return $[20] !== overlay || $[21] !== t10 || $[22] !== t6 ? (t11 = /* @__PURE__ */ jsx(Layer, {
|
|
3040
|
-
className: sidebar,
|
|
3041
|
-
"data-overlay": overlay,
|
|
3042
|
-
style: t6,
|
|
3043
|
-
zOffset: 100,
|
|
3044
|
-
children: t10
|
|
3045
|
-
}), $[20] = overlay, $[21] = t10, $[22] = t6, $[23] = t11) : t11 = $[23], t11;
|
|
3046
|
-
}
|
|
3047
|
-
const QUERY = "(prefers-reduced-motion: reduce)";
|
|
3048
|
-
function subscribe(onChange) {
|
|
3049
|
-
let media = matchMedia(QUERY);
|
|
3050
|
-
return media.addEventListener("change", onChange), () => media.removeEventListener("change", onChange);
|
|
3051
|
-
}
|
|
3052
|
-
function getSnapshot() {
|
|
3053
|
-
return matchMedia(QUERY).matches;
|
|
3054
|
-
}
|
|
3055
|
-
function getServerSnapshot() {
|
|
3056
|
-
return !1;
|
|
3057
|
-
}
|
|
3058
|
-
/**
|
|
3059
|
-
* Whether the user prefers reduced motion — live, so that turning the
|
|
3060
|
-
* setting on while the Studio is open takes effect right away.
|
|
3061
|
-
*
|
|
3062
|
-
* @internal
|
|
3063
|
-
*/
|
|
3064
|
-
function usePrefersReducedMotion() {
|
|
3065
|
-
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
3066
|
-
}
|
|
3067
|
-
/**
|
|
3068
|
-
* The height of the Studio navbar inside the given element, in pixels — kept
|
|
3069
|
-
* up to date as the navbar renders, remounts and resizes, and `null` until
|
|
3070
|
-
* there is one. The themer's header takes this height, so that its bottom
|
|
3071
|
-
* border lines up with the navbar's whatever height the Studio version, its
|
|
3072
|
-
* breakpoint or a custom navbar gives it.
|
|
3073
|
-
*
|
|
3074
|
-
* @internal
|
|
3075
|
-
*/
|
|
3076
|
-
function useStudioNavbarHeight(ref) {
|
|
3077
|
-
let $ = c(3), [height, setHeight] = useState(null), t0, t1;
|
|
3078
|
-
return $[0] === ref ? (t0 = $[1], t1 = $[2]) : (t0 = () => {
|
|
3079
|
-
let root = ref.current;
|
|
3080
|
-
if (!root) return;
|
|
3081
|
-
let navbar = null, resizeObserver = new ResizeObserver(() => {
|
|
3082
|
-
navbar && setHeight(navbar.getBoundingClientRect().height);
|
|
3083
|
-
}), observeNavbar = () => {
|
|
3084
|
-
let next = root.querySelector("[data-testid=\"studio-navbar\"]");
|
|
3085
|
-
next !== navbar && (navbar && resizeObserver.unobserve(navbar), navbar = next, navbar ? (resizeObserver.observe(navbar), setHeight(navbar.getBoundingClientRect().height)) : setHeight(null));
|
|
3086
|
-
}, mutationObserver = new MutationObserver(observeNavbar);
|
|
3087
|
-
return mutationObserver.observe(root, {
|
|
3088
|
-
childList: !0,
|
|
3089
|
-
subtree: !0
|
|
3090
|
-
}), observeNavbar(), () => {
|
|
3091
|
-
mutationObserver.disconnect(), resizeObserver.disconnect();
|
|
3092
|
-
};
|
|
3093
|
-
}, t1 = [ref], $[0] = ref, $[1] = t0, $[2] = t1), useEffect(t0, t1), height;
|
|
3094
|
-
}
|
|
3095
|
-
/**
|
|
3096
|
-
* `useDeferredValue` with a transition type: the returned value lags behind
|
|
3097
|
-
* `value` like a deferred one — rendered in a transition, which React
|
|
3098
|
-
* animates — and the view transition React starts for it carries the given
|
|
3099
|
-
* type (`document.startViewTransition({types})`), so a stylesheet tells it
|
|
3100
|
-
* from any other transition on the page through
|
|
3101
|
-
* `:active-view-transition-type()`.
|
|
3102
|
-
*
|
|
3103
|
-
* React only takes types from `startTransition`, and a deferred render is
|
|
3104
|
-
* not one. So while the deferred value lags, a transition of the type joins
|
|
3105
|
-
* it, with a state update of its own for the type to belong to: React renders
|
|
3106
|
-
* every pending transition in one go, and the commit — the view transition
|
|
3107
|
-
* — carries the types of all of them. The value itself stays with
|
|
3108
|
-
* `useDeferredValue`, which reads the latest `value` as the deferred render
|
|
3109
|
-
* runs: should `value` change back before that render commits, nothing
|
|
3110
|
-
* changes and nothing animates — a queued state update would commit the
|
|
3111
|
-
* stale value and animate it, only to animate back.
|
|
3112
|
-
*
|
|
3113
|
-
* @internal
|
|
3114
|
-
*/
|
|
3115
|
-
function useTypedDeferredValue(value, type) {
|
|
3116
|
-
let $ = c(5), deferred = useDeferredValue(value), [, setTransitions] = useState(0), t0, t1;
|
|
3117
|
-
return $[0] !== deferred || $[1] !== type || $[2] !== value ? (t0 = () => {
|
|
3118
|
-
Object.is(deferred, value) || startTransition(() => {
|
|
3119
|
-
addTransitionType(type), setTransitions(_temp$1);
|
|
3120
|
-
});
|
|
3121
|
-
}, t1 = [
|
|
3122
|
-
deferred,
|
|
3123
|
-
type,
|
|
3124
|
-
value
|
|
3125
|
-
], $[0] = deferred, $[1] = type, $[2] = value, $[3] = t0, $[4] = t1) : (t0 = $[3], t1 = $[4]), useEffect(t0, t1), deferred;
|
|
3126
|
-
}
|
|
3127
|
-
function _temp$1(count) {
|
|
3128
|
-
return count + 1;
|
|
3129
|
-
}
|
|
3130
|
-
var layout = "_1yq4ar78", layoutTransitionType = "sanity-themer", panelTransitionClasses = {
|
|
3131
|
-
slideIn: "_1yq4ar76",
|
|
3132
|
-
slideOut: "_1yq4ar77"
|
|
3133
|
-
}, splitTransitionClasses = {
|
|
3134
|
-
resize: "_1yq4ar70",
|
|
3135
|
-
crossfade: "_1yq4ar71",
|
|
3136
|
-
slideIn: "_1yq4ar72",
|
|
3137
|
-
slideOut: "_1yq4ar73",
|
|
3138
|
-
dropIn: "_1yq4ar74",
|
|
3139
|
-
dropOut: "_1yq4ar75"
|
|
3140
|
-
}, studioScheme = {
|
|
3141
|
-
light: "_1yq4ar79",
|
|
3142
|
-
dark: "_1yq4ar7a"
|
|
3143
|
-
};
|
|
3144
|
-
function sameStoredState(a, b) {
|
|
3145
|
-
return a.active === b.active && a.custom === b.custom && a.removed === b.removed && a.order === b.order;
|
|
728
|
+
const ResizableSidebar = lazy(() => import("./ResizableSidebar-DD787kQr.js"));
|
|
729
|
+
function sameStoredState(a, b) {
|
|
730
|
+
return a.active === b.active && a.custom === b.custom && a.removed === b.removed && a.order === b.order;
|
|
3146
731
|
}
|
|
3147
732
|
function selectView(snapshot) {
|
|
3148
733
|
let { editing } = snapshot.context;
|
|
@@ -3184,7 +769,7 @@ function sameView(a, b) {
|
|
|
3184
769
|
* @internal
|
|
3185
770
|
*/
|
|
3186
771
|
function ThemerLayout(props) {
|
|
3187
|
-
let $ = c(
|
|
772
|
+
let $ = c(87), { baseOptions, ...layoutProps } = props, t0;
|
|
3188
773
|
$[0] === baseOptions ? t0 = $[1] : (t0 = () => ({
|
|
3189
774
|
baseOptions,
|
|
3190
775
|
stored: readStoredState()
|
|
@@ -3199,7 +784,9 @@ function ThemerLayout(props) {
|
|
|
3199
784
|
$[8] === snapshot ? t4 = $[9] : (t4 = snapshot.hasTag("moving"), $[8] = snapshot, $[9] = t4);
|
|
3200
785
|
let moving = t4, t5;
|
|
3201
786
|
$[10] === snapshot ? t5 = $[11] : (t5 = snapshot.hasTag("switching"), $[10] = snapshot, $[11] = t5);
|
|
3202
|
-
let switching = t5, { images } = snapshot.context, mobile = useMediaIndex() <= 1, studioRef = useRef(null), navbarHeight = useStudioNavbarHeight(studioRef), reduceMotion = usePrefersReducedMotion(), deferredOpen = useTypedDeferredValue(reduceMotion ? null : open, layoutTransitionType), deferredSplit = useTypedDeferredValue(reduceMotion ? null : split, layoutTransitionType), shownOpen = deferredOpen ?? open, shownSplit = deferredSplit ?? split,
|
|
787
|
+
let switching = t5, { images } = snapshot.context, mobile = useMediaIndex() <= 1, studioRef = useRef(null), navbarHeight = useStudioNavbarHeight(studioRef), reduceMotion = usePrefersReducedMotion$1(), deferredOpen = useTypedDeferredValue(reduceMotion ? null : open, layoutTransitionType), deferredSplit = useTypedDeferredValue(reduceMotion ? null : split, layoutTransitionType), shownOpen = deferredOpen ?? open, shownSplit = deferredSplit ?? split, [panelShown, setPanelShown] = useState(!1), panelWanted = open || panelShown, panelMounted = useTypedDeferredValue(panelWanted, layoutTransitionType);
|
|
788
|
+
panelMounted && !panelShown && setPanelShown(!0);
|
|
789
|
+
let loading = panelWanted !== panelMounted, t6;
|
|
3203
790
|
$[12] !== baseOptions || $[13] !== stored ? (t6 = resolveThemes(stored, baseOptions), $[12] = baseOptions, $[13] = stored, $[14] = t6) : t6 = $[14];
|
|
3204
791
|
let { themes, removed, active } = t6, activeOptions = active.source === "config" ? null : active.options, t7;
|
|
3205
792
|
$[15] === activeOptions ? t7 = $[16] : (t7 = activeOptions === null ? null : buildTheme(activeOptions), $[15] = activeOptions, $[16] = t7);
|
|
@@ -3219,7 +806,7 @@ function ThemerLayout(props) {
|
|
|
3219
806
|
for (let url of Object.values(actorRef.getSnapshot().context.images)) URL.revokeObjectURL(url);
|
|
3220
807
|
}, t11 = [actorRef], $[23] = actorRef, $[24] = t10, $[25] = t11), useEffect(t10, t11);
|
|
3221
808
|
let t12;
|
|
3222
|
-
$[26] !== active || $[27] !== baseOptions || $[28] !== images || $[29] !==
|
|
809
|
+
$[26] !== active || $[27] !== baseOptions || $[28] !== images || $[29] !== loading || $[30] !== mobile || $[31] !== navbarHeight || $[32] !== open || $[33] !== removed || $[34] !== send || $[35] !== split || $[36] !== themes || $[37] !== view ? (t12 = {
|
|
3223
810
|
baseOptions,
|
|
3224
811
|
themes,
|
|
3225
812
|
removed,
|
|
@@ -3227,15 +814,16 @@ function ThemerLayout(props) {
|
|
|
3227
814
|
images,
|
|
3228
815
|
view,
|
|
3229
816
|
open,
|
|
817
|
+
loading,
|
|
3230
818
|
split,
|
|
3231
819
|
mobile,
|
|
3232
820
|
navbarHeight,
|
|
3233
821
|
send
|
|
3234
|
-
}, $[26] = active, $[27] = baseOptions, $[28] = images, $[29] =
|
|
822
|
+
}, $[26] = active, $[27] = baseOptions, $[28] = images, $[29] = loading, $[30] = mobile, $[31] = navbarHeight, $[32] = open, $[33] = removed, $[34] = send, $[35] = split, $[36] = themes, $[37] = view, $[38] = t12) : t12 = $[38];
|
|
3235
823
|
let context = t12, studio = layoutProps.renderDefault(layoutProps), t13;
|
|
3236
|
-
$[
|
|
824
|
+
$[39] === send ? t13 = $[40] : (t13 = () => send({ type: "layout.transitioned" }), $[39] = send, $[40] = t13);
|
|
3237
825
|
let transitioned = t13, t14;
|
|
3238
|
-
$[
|
|
826
|
+
$[41] !== mobile || $[42] !== moving || $[43] !== reduceMotion || $[44] !== switching ? (t14 = reduceMotion ? {
|
|
3239
827
|
copyEnter: "none",
|
|
3240
828
|
copyExit: "none",
|
|
3241
829
|
panelEnter: "none",
|
|
@@ -3249,9 +837,9 @@ function ThemerLayout(props) {
|
|
|
3249
837
|
panelExit: panelTransitionClasses.slideOut,
|
|
3250
838
|
update: switching ? splitTransitionClasses.crossfade : "none",
|
|
3251
839
|
studioUpdate: moving ? splitTransitionClasses.resize : switching ? splitTransitionClasses.crossfade : "none"
|
|
3252
|
-
}, $[
|
|
840
|
+
}, $[41] = mobile, $[42] = moving, $[43] = reduceMotion, $[44] = switching, $[45] = t14) : t14 = $[45];
|
|
3253
841
|
let classes = t14, t15 = mobile ? "column" : "row", t16;
|
|
3254
|
-
$[
|
|
842
|
+
$[46] !== classes.copyEnter || $[47] !== classes.copyExit || $[48] !== classes.update || $[49] !== mobile || $[50] !== oppositeScheme || $[51] !== shownOpen || $[52] !== shownSplit || $[53] !== shownTheme || $[54] !== studio || $[55] !== transitioned ? (t16 = shownOpen && /* @__PURE__ */ jsx(Activity, {
|
|
3255
843
|
mode: shownSplit ? "visible" : "hidden",
|
|
3256
844
|
children: /* @__PURE__ */ jsx(ViewTransition, {
|
|
3257
845
|
enter: classes.copyEnter,
|
|
@@ -3268,28 +856,28 @@ function ThemerLayout(props) {
|
|
|
3268
856
|
children: studio
|
|
3269
857
|
})
|
|
3270
858
|
}, "opposite")
|
|
3271
|
-
}), $[
|
|
859
|
+
}), $[46] = classes.copyEnter, $[47] = classes.copyExit, $[48] = classes.update, $[49] = mobile, $[50] = oppositeScheme, $[51] = shownOpen, $[52] = shownSplit, $[53] = shownTheme, $[54] = studio, $[55] = transitioned, $[56] = t16) : t16 = $[56];
|
|
3272
860
|
let t17;
|
|
3273
|
-
$[
|
|
861
|
+
$[57] !== shownTheme || $[58] !== studio ? (t17 = /* @__PURE__ */ jsx(StudioPreview, {
|
|
3274
862
|
ref: studioRef,
|
|
3275
863
|
theme: shownTheme,
|
|
3276
864
|
children: studio
|
|
3277
|
-
}), $[
|
|
865
|
+
}), $[57] = shownTheme, $[58] = studio, $[59] = t17) : t17 = $[59];
|
|
3278
866
|
let t18;
|
|
3279
|
-
$[
|
|
867
|
+
$[60] !== classes.studioUpdate || $[61] !== t17 || $[62] !== transitioned ? (t18 = /* @__PURE__ */ jsx(ViewTransition, {
|
|
3280
868
|
onUpdate: transitioned,
|
|
3281
869
|
update: classes.studioUpdate,
|
|
3282
870
|
children: t17
|
|
3283
|
-
}, "primary"), $[
|
|
871
|
+
}, "primary"), $[60] = classes.studioUpdate, $[61] = t17, $[62] = transitioned, $[63] = t18) : t18 = $[63];
|
|
3284
872
|
let t19 = shownOpen ? "visible" : "hidden", t20 = shownTheme ?? void 0, t21;
|
|
3285
|
-
$[
|
|
873
|
+
$[64] !== mobile || $[65] !== panelMounted ? (t21 = panelMounted && /* @__PURE__ */ jsx(ResizableSidebar, { overlay: mobile }), $[64] = mobile, $[65] = panelMounted, $[66] = t21) : t21 = $[66];
|
|
3286
874
|
let t22;
|
|
3287
|
-
$[
|
|
875
|
+
$[67] !== t20 || $[68] !== t21 ? (t22 = /* @__PURE__ */ jsx(ThemeProvider, {
|
|
3288
876
|
theme: t20,
|
|
3289
877
|
children: t21
|
|
3290
|
-
}), $[
|
|
878
|
+
}), $[67] = t20, $[68] = t21, $[69] = t22) : t22 = $[69];
|
|
3291
879
|
let t23;
|
|
3292
|
-
$[
|
|
880
|
+
$[70] !== classes.panelEnter || $[71] !== classes.panelExit || $[72] !== classes.update || $[73] !== t22 || $[74] !== transitioned ? (t23 = /* @__PURE__ */ jsx(ViewTransition, {
|
|
3293
881
|
enter: classes.panelEnter,
|
|
3294
882
|
exit: classes.panelExit,
|
|
3295
883
|
onEnter: transitioned,
|
|
@@ -3297,14 +885,14 @@ function ThemerLayout(props) {
|
|
|
3297
885
|
onUpdate: transitioned,
|
|
3298
886
|
update: classes.update,
|
|
3299
887
|
children: t22
|
|
3300
|
-
}, "panel"), $[
|
|
888
|
+
}, "panel"), $[70] = classes.panelEnter, $[71] = classes.panelExit, $[72] = classes.update, $[73] = t22, $[74] = transitioned, $[75] = t23) : t23 = $[75];
|
|
3301
889
|
let t24;
|
|
3302
|
-
$[
|
|
890
|
+
$[76] !== t19 || $[77] !== t23 ? (t24 = /* @__PURE__ */ jsx(Activity, {
|
|
3303
891
|
mode: t19,
|
|
3304
892
|
children: t23
|
|
3305
|
-
}), $[
|
|
893
|
+
}), $[76] = t19, $[77] = t23, $[78] = t24) : t24 = $[78];
|
|
3306
894
|
let t25;
|
|
3307
|
-
$[
|
|
895
|
+
$[79] !== t15 || $[80] !== t16 || $[81] !== t18 || $[82] !== t24 ? (t25 = /* @__PURE__ */ jsxs(Flex, {
|
|
3308
896
|
className: layout,
|
|
3309
897
|
direction: t15,
|
|
3310
898
|
height: "fill",
|
|
@@ -3314,12 +902,12 @@ function ThemerLayout(props) {
|
|
|
3314
902
|
t18,
|
|
3315
903
|
t24
|
|
3316
904
|
]
|
|
3317
|
-
}), $[
|
|
905
|
+
}), $[79] = t15, $[80] = t16, $[81] = t18, $[82] = t24, $[83] = t25) : t25 = $[83];
|
|
3318
906
|
let t26;
|
|
3319
|
-
return $[
|
|
907
|
+
return $[84] !== context || $[85] !== t25 ? (t26 = /* @__PURE__ */ jsx(ThemerContext.Provider, {
|
|
3320
908
|
value: context,
|
|
3321
909
|
children: t25
|
|
3322
|
-
}), $[
|
|
910
|
+
}), $[84] = context, $[85] = t25, $[86] = t26) : t26 = $[86], t26;
|
|
3323
911
|
}
|
|
3324
912
|
/**
|
|
3325
913
|
* One copy of the Studio, in the given color scheme — or, without one, in the
|
|
@@ -3351,28 +939,235 @@ function StudioPreview(props) {
|
|
|
3351
939
|
children: t2
|
|
3352
940
|
}), $[6] = scheme, $[7] = t0, $[8] = t2, $[9] = t3) : t3 = $[9], t3;
|
|
3353
941
|
}
|
|
942
|
+
/**
|
|
943
|
+
* The colors the slices fill with, clockwise from 12 o'clock: the palette's
|
|
944
|
+
* hues in rainbow order, like a real color swatch
|
|
945
|
+
*/
|
|
946
|
+
const SLICE_COLORS = [
|
|
947
|
+
red[500].hex,
|
|
948
|
+
orange[500].hex,
|
|
949
|
+
yellow[400].hex,
|
|
950
|
+
green[500].hex,
|
|
951
|
+
cyan[500].hex,
|
|
952
|
+
blue[500].hex,
|
|
953
|
+
purple[500].hex,
|
|
954
|
+
magenta[500].hex
|
|
955
|
+
], TOTAL_ACTS = 2 + (1 - Math.cbrt(1 / 8)), ANIMATION_DURATION = .55 * TOTAL_ACTS;
|
|
956
|
+
function actAt(progress) {
|
|
957
|
+
return progress * TOTAL_ACTS;
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* How far the wheel has turned at `progress` (0–1), in degrees.
|
|
961
|
+
*
|
|
962
|
+
* It stands still while the top half of the slices pop in. As the 6 o'clock
|
|
963
|
+
* slice pops in it starts spinning — one full turn with a cubic ease-in, from
|
|
964
|
+
* rest to top speed as the bottom half pop in — and it stands still again the
|
|
965
|
+
* moment it is back upright: the clearing of {@link isSliceFilled} takes off
|
|
966
|
+
* at the speed the spin reached, as if the wheel kept turning and eased out
|
|
967
|
+
* over a second turn, so the two read as one movement.
|
|
968
|
+
*/
|
|
969
|
+
function wheelRotation(progress) {
|
|
970
|
+
let spin = actAt(progress) - 1;
|
|
971
|
+
return spin <= 0 || spin >= 1 ? 0 : 360 * spin ** 3;
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Whether slice `index` (clockwise from 12 o'clock) shows its color at
|
|
975
|
+
* `progress` (0–1). A slice pops in whole as the fill reaches its leading
|
|
976
|
+
* edge, and pops out whole as the clearing does — stepped, never partial.
|
|
977
|
+
*
|
|
978
|
+
* The fill goes clockwise at a steady pace over the first two acts, half of
|
|
979
|
+
* the slices in each. The clearing follows in the third act, the same way
|
|
980
|
+
* round, on a cubic ease-out that starts at the speed the spin of
|
|
981
|
+
* {@link wheelRotation} ends with: the slices at first pop out as quickly as
|
|
982
|
+
* they were passing by, then ever more slowly.
|
|
983
|
+
*/
|
|
984
|
+
function isSliceFilled(progress, index) {
|
|
985
|
+
let act = actAt(progress);
|
|
986
|
+
if (act * 8 / 2 <= index) return !1;
|
|
987
|
+
let clearing = act - 2;
|
|
988
|
+
return clearing <= 0 || (1 - (1 - clearing) ** 3) * 8 < index;
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
991
|
+
* The geometry of `ColorWheelIcon` from `@sanity/icons`: a ring between two
|
|
992
|
+
* circles around the center of a 25×25 viewBox, cut into eight slices by the
|
|
993
|
+
* spokes of its outline path
|
|
994
|
+
*/
|
|
995
|
+
const CENTER = 12.5, INNER_RADIUS = 3.5;
|
|
996
|
+
/** A point on a circle around the center, at `degrees` clockwise from 3 o'clock */
|
|
997
|
+
function point(radius, degrees) {
|
|
998
|
+
let radians = degrees * Math.PI / 180;
|
|
999
|
+
return `${CENTER + radius * Math.cos(radians)} ${CENTER + radius * Math.sin(radians)}`;
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* The part of the ring a slice colors in: between its two spokes, from the
|
|
1003
|
+
* inner circle out to the outer. The 12 o'clock slice is centered on 12, so
|
|
1004
|
+
* it starts half a slice before it.
|
|
1005
|
+
*/
|
|
1006
|
+
function slicePath(index) {
|
|
1007
|
+
let start = index * 45 - 45 / 2 - 90, end = start + 45;
|
|
1008
|
+
return [
|
|
1009
|
+
`M${point(8, start)}`,
|
|
1010
|
+
`A8 8 0 0 1 ${point(8, end)}`,
|
|
1011
|
+
`L${point(INNER_RADIUS, end)}`,
|
|
1012
|
+
`A${INNER_RADIUS} ${INNER_RADIUS} 0 0 0 ${point(INNER_RADIUS, start)}`,
|
|
1013
|
+
"Z"
|
|
1014
|
+
].join("");
|
|
1015
|
+
}
|
|
1016
|
+
const SLICE_PATHS = SLICE_COLORS.map((_, index) => slicePath(index));
|
|
1017
|
+
/**
|
|
1018
|
+
* A filled shape, not a stroked one: the icon styles of `@sanity/ui` force
|
|
1019
|
+
* `vector-effect: non-scaling-stroke` on every path in an icon, which fills
|
|
1020
|
+
* are none the wiser about
|
|
1021
|
+
*/
|
|
1022
|
+
function Slice(props) {
|
|
1023
|
+
let $ = c(8), { index, progress } = props, t0;
|
|
1024
|
+
$[0] === index ? t0 = $[1] : (t0 = (value) => +!!isSliceFilled(value, index), $[0] = index, $[1] = t0);
|
|
1025
|
+
let opacity = useTransform(progress, t0), t1 = SLICE_PATHS[index], t2 = SLICE_COLORS[index], t3;
|
|
1026
|
+
$[2] === opacity ? t3 = $[3] : (t3 = { opacity }, $[2] = opacity, $[3] = t3);
|
|
1027
|
+
let t4;
|
|
1028
|
+
return $[4] !== t1 || $[5] !== t2 || $[6] !== t3 ? (t4 = /* @__PURE__ */ jsx(motion.path, {
|
|
1029
|
+
d: t1,
|
|
1030
|
+
fill: t2,
|
|
1031
|
+
style: t3
|
|
1032
|
+
}), $[4] = t1, $[5] = t2, $[6] = t3, $[7] = t4) : t4 = $[7], t4;
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* The `ColorWheelIcon` of `@sanity/icons`, with an animation driven by
|
|
1036
|
+
* `progress` (0–1): the slices pop in with color one at a time, clockwise
|
|
1037
|
+
* from 12 o'clock like the segments of a classic spinner, the wheel spins once
|
|
1038
|
+
* as the 6 o'clock slice pops in, and the colors pop out the same way round,
|
|
1039
|
+
* carrying the spin's speed to a rest (see `colorWheel.ts`). At rest — at
|
|
1040
|
+
* either end — it looks exactly like the icon it stands in for. With
|
|
1041
|
+
* `prefers-reduced-motion: reduce` the wheel keeps still; only the colors come
|
|
1042
|
+
* and go.
|
|
1043
|
+
*
|
|
1044
|
+
* @internal
|
|
1045
|
+
*/
|
|
1046
|
+
function AnimatedColorWheelIcon(props) {
|
|
1047
|
+
let $ = c(10), { progress } = props, prefersReducedMotion = usePrefersReducedMotion(), t0;
|
|
1048
|
+
$[0] === prefersReducedMotion ? t0 = $[1] : (t0 = (value) => prefersReducedMotion ? 0 : wheelRotation(value), $[0] = prefersReducedMotion, $[1] = t0);
|
|
1049
|
+
let rotate = useTransform(progress, t0), t1;
|
|
1050
|
+
$[2] === rotate ? t1 = $[3] : (t1 = { rotate }, $[2] = rotate, $[3] = t1);
|
|
1051
|
+
let t2;
|
|
1052
|
+
$[4] === progress ? t2 = $[5] : (t2 = SLICE_COLORS.map((color, index) => /* @__PURE__ */ jsx(Slice, {
|
|
1053
|
+
index,
|
|
1054
|
+
progress
|
|
1055
|
+
}, color)), $[4] = progress, $[5] = t2);
|
|
1056
|
+
let t3;
|
|
1057
|
+
$[6] === Symbol.for("react.memo_cache_sentinel") ? (t3 = /* @__PURE__ */ jsx("path", {
|
|
1058
|
+
d: "M9.43853 5.10896L11.1606 9.26642M13.8394 15.7336L15.5615 19.891M15.7336 11.1606L19.891 9.43853M9.26642 13.8394L5.10896 15.5615M5.3139 9.52342L9.23359 11.147M15.7664 13.853L19.6861 15.4766M13.853 9.23359L15.4766 5.3139M9.52342 19.6861L11.147 15.7664M20.5 12.5C20.5 16.9183 16.9183 20.5 12.5 20.5C8.08172 20.5 4.5 16.9183 4.5 12.5C4.5 8.08172 8.08172 4.5 12.5 4.5C16.9183 4.5 20.5 8.08172 20.5 12.5ZM16 12.5C16 14.433 14.433 16 12.5 16C10.567 16 9 14.433 9 12.5C9 10.567 10.567 9 12.5 9C14.433 9 16 10.567 16 12.5Z",
|
|
1059
|
+
stroke: "currentColor",
|
|
1060
|
+
strokeLinejoin: "round",
|
|
1061
|
+
strokeWidth: 1.2
|
|
1062
|
+
}), $[6] = t3) : t3 = $[6];
|
|
1063
|
+
let t4;
|
|
1064
|
+
return $[7] !== t1 || $[8] !== t2 ? (t4 = /* @__PURE__ */ jsxs(motion.svg, {
|
|
1065
|
+
"data-sanity-icon": "color-wheel",
|
|
1066
|
+
fill: "none",
|
|
1067
|
+
height: "1em",
|
|
1068
|
+
style: t1,
|
|
1069
|
+
viewBox: "0 0 25 25",
|
|
1070
|
+
width: "1em",
|
|
1071
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1072
|
+
children: [t2, t3]
|
|
1073
|
+
}), $[7] = t1, $[8] = t2, $[9] = t4) : t4 = $[9], t4;
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* Starts loading the sidebar's code as the toggle is about to be pressed —
|
|
1077
|
+
* the same import `React.lazy` makes, which then waits for this one
|
|
1078
|
+
*/
|
|
1079
|
+
function preloadSidebar() {
|
|
1080
|
+
import("./ResizableSidebar-DD787kQr.js").catch(() => {});
|
|
1081
|
+
}
|
|
1082
|
+
/**
|
|
1083
|
+
* Opens or closes the sidebar — and notes the visit, which is what stops the
|
|
1084
|
+
* navbar from introducing the tool
|
|
1085
|
+
*/
|
|
1086
|
+
function toggleSidebar(send) {
|
|
1087
|
+
markVisited(), send({ type: "sidebar.toggle" });
|
|
1088
|
+
}
|
|
1089
|
+
/**
|
|
1090
|
+
* Whether the navbar has had its one chance per page load to introduce the
|
|
1091
|
+
* tool. Module state rather than component state on purpose: the button
|
|
1092
|
+
* remounts with the navbar (switching workspaces, crossing the narrow-screen
|
|
1093
|
+
* breakpoint) and renders once per Studio copy in the split preview, and none
|
|
1094
|
+
* of those should earn another introduction — only loading the page again does.
|
|
1095
|
+
*/
|
|
1096
|
+
let introduced = !1;
|
|
1097
|
+
/**
|
|
1098
|
+
* The topbar toggle, with an icon that plays its color wheel animation: on
|
|
1099
|
+
* hovering the button, every time; on hovering the navbar it sits in, once —
|
|
1100
|
+
* a "hey, look, new tool!" for anyone who has never opened the sidebar, which
|
|
1101
|
+
* the visit noted by {@link toggleSidebar} ends for good; and lap after lap
|
|
1102
|
+
* while the sidebar's code loads, the way a spinner would.
|
|
1103
|
+
*/
|
|
3354
1104
|
function ThemerNavbarButton() {
|
|
3355
|
-
let $ = c(
|
|
3356
|
-
$[0] ===
|
|
1105
|
+
let $ = c(26), { open, loading, send } = useThemer(), progress = useMotionValue(0), [button, setButton] = useState(null), t0;
|
|
1106
|
+
$[0] === progress ? t0 = $[1] : (t0 = () => {
|
|
1107
|
+
progress.isAnimating() || animate(progress, [0, 1], {
|
|
1108
|
+
duration: ANIMATION_DURATION,
|
|
1109
|
+
ease: "linear"
|
|
1110
|
+
});
|
|
1111
|
+
}, $[0] = progress, $[1] = t0);
|
|
1112
|
+
let play = t0, t1, t2;
|
|
1113
|
+
$[2] !== loading || $[3] !== progress ? (t1 = () => {
|
|
1114
|
+
if (!loading) return;
|
|
1115
|
+
let looping = !0, lap = (from) => {
|
|
1116
|
+
animate(progress, [from, 1], {
|
|
1117
|
+
duration: (1 - from) * ANIMATION_DURATION,
|
|
1118
|
+
ease: "linear",
|
|
1119
|
+
onComplete: () => {
|
|
1120
|
+
looping && lap(0);
|
|
1121
|
+
}
|
|
1122
|
+
});
|
|
1123
|
+
};
|
|
1124
|
+
return lap(progress.get()), () => {
|
|
1125
|
+
looping = !1;
|
|
1126
|
+
};
|
|
1127
|
+
}, t2 = [loading, progress], $[2] = loading, $[3] = progress, $[4] = t1, $[5] = t2) : (t1 = $[4], t2 = $[5]), useEffect(t1, t2);
|
|
1128
|
+
let t3, t4;
|
|
1129
|
+
$[6] === progress ? (t3 = $[7], t4 = $[8]) : (t3 = () => () => progress.stop(), t4 = [progress], $[6] = progress, $[7] = t3, $[8] = t4), useEffect(t3, t4);
|
|
1130
|
+
let t5, t6;
|
|
1131
|
+
$[9] !== button || $[10] !== play ? (t5 = () => {
|
|
1132
|
+
let navbar = button?.closest(NAVBAR_SELECTOR);
|
|
1133
|
+
if (!navbar || introduced) return;
|
|
1134
|
+
let introduce = () => {
|
|
1135
|
+
introduced || (introduced = !0, hasVisited() || play());
|
|
1136
|
+
};
|
|
1137
|
+
return navbar.addEventListener("mouseenter", introduce, { once: !0 }), () => navbar.removeEventListener("mouseenter", introduce);
|
|
1138
|
+
}, t6 = [button, play], $[9] = button, $[10] = play, $[11] = t5, $[12] = t6) : (t5 = $[11], t6 = $[12]), useEffect(t5, t6);
|
|
1139
|
+
let t7;
|
|
1140
|
+
$[13] === Symbol.for("react.memo_cache_sentinel") ? (t7 = /* @__PURE__ */ jsx(Text, {
|
|
3357
1141
|
size: 1,
|
|
3358
1142
|
children: "Themer"
|
|
3359
|
-
}), $[
|
|
3360
|
-
let
|
|
3361
|
-
$[
|
|
3362
|
-
let
|
|
3363
|
-
|
|
1143
|
+
}), $[13] = t7) : t7 = $[13];
|
|
1144
|
+
let t8;
|
|
1145
|
+
$[14] === progress ? t8 = $[15] : (t8 = /* @__PURE__ */ jsx(AnimatedColorWheelIcon, { progress }), $[14] = progress, $[15] = t8);
|
|
1146
|
+
let t9;
|
|
1147
|
+
$[16] === send ? t9 = $[17] : (t9 = () => toggleSidebar(send), $[16] = send, $[17] = t9);
|
|
1148
|
+
let t10;
|
|
1149
|
+
$[18] === play ? t10 = $[19] : (t10 = () => {
|
|
1150
|
+
preloadSidebar(), play();
|
|
1151
|
+
}, $[18] = play, $[19] = t10);
|
|
1152
|
+
let t11;
|
|
1153
|
+
return $[20] !== loading || $[21] !== open || $[22] !== t10 || $[23] !== t8 || $[24] !== t9 ? (t11 = /* @__PURE__ */ jsx(Tooltip, {
|
|
3364
1154
|
animate: !0,
|
|
3365
|
-
content:
|
|
1155
|
+
content: t7,
|
|
3366
1156
|
portal: !0,
|
|
3367
1157
|
children: /* @__PURE__ */ jsx(Button, {
|
|
1158
|
+
"aria-busy": loading,
|
|
3368
1159
|
"aria-label": "Themer",
|
|
3369
|
-
|
|
1160
|
+
disabled: loading,
|
|
1161
|
+
icon: t8,
|
|
3370
1162
|
mode: "bleed",
|
|
3371
|
-
onClick:
|
|
1163
|
+
onClick: t9,
|
|
1164
|
+
onFocus: preloadSidebar,
|
|
1165
|
+
onMouseEnter: t10,
|
|
3372
1166
|
padding: 2,
|
|
1167
|
+
ref: setButton,
|
|
3373
1168
|
selected: open
|
|
3374
1169
|
})
|
|
3375
|
-
}), $[
|
|
1170
|
+
}), $[20] = loading, $[21] = open, $[22] = t10, $[23] = t8, $[24] = t9, $[25] = t11) : t11 = $[25], t11;
|
|
3376
1171
|
}
|
|
3377
1172
|
/**
|
|
3378
1173
|
* Adds the toggle that opens and closes the themer sidebar to the Studio
|
|
@@ -3390,14 +1185,14 @@ function ThemerNavbar(props) {
|
|
|
3390
1185
|
let t1;
|
|
3391
1186
|
if ($[2] !== open || $[3] !== props || $[4] !== send || $[5] !== t0) {
|
|
3392
1187
|
let t2;
|
|
3393
|
-
$[7] === send ? t2 = $[8] : (t2 = () => send
|
|
1188
|
+
$[7] === send ? t2 = $[8] : (t2 = () => toggleSidebar(send), $[7] = send, $[8] = t2), t1 = props.renderDefault({
|
|
3394
1189
|
...props,
|
|
3395
1190
|
__internal_actions: [
|
|
3396
1191
|
...t0,
|
|
3397
1192
|
{
|
|
3398
1193
|
location: "topbar",
|
|
3399
1194
|
name: "themer-topbar",
|
|
3400
|
-
render:
|
|
1195
|
+
render: ThemerNavbarButton
|
|
3401
1196
|
},
|
|
3402
1197
|
{
|
|
3403
1198
|
icon: ColorWheelIcon,
|
|
@@ -3412,9 +1207,6 @@ function ThemerNavbar(props) {
|
|
|
3412
1207
|
} else t1 = $[6];
|
|
3413
1208
|
return t1;
|
|
3414
1209
|
}
|
|
3415
|
-
function _temp() {
|
|
3416
|
-
return /* @__PURE__ */ jsx(ThemerNavbarButton, {});
|
|
3417
|
-
}
|
|
3418
1210
|
/**
|
|
3419
1211
|
* A Studio plugin that adds a themer sidebar for `buildTheme` themes: a
|
|
3420
1212
|
* navbar toggle opens the sidebar next to the Studio, with a list of themes —
|