@sanity/themer 0.6.1 → 0.7.1
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/bundle.css +745 -1
- package/dist/presets-CqBgS3A-.js.map +1 -1
- package/dist/tool.js +1795 -1183
- package/dist/tool.js.map +1 -1
- package/package.json +6 -2
package/dist/tool.js
CHANGED
|
@@ -4,8 +4,8 @@ import { c } from "react/compiler-runtime";
|
|
|
4
4
|
import { definePlugin, useColorSchemeValue } from "sanity";
|
|
5
5
|
import { Badge, Box, Button, Card, Dialog, Flex, Layer, Stack, Text, TextInput, ThemeProvider, useMediaIndex, useRootTheme, useTheme_v2 } from "@sanity/ui";
|
|
6
6
|
import { useActor, useSelector } from "@xstate/react";
|
|
7
|
-
import { Activity, ViewTransition, addTransitionType, createContext, startTransition, useContext, useEffect, useRef, useState } from "react";
|
|
8
|
-
import { assign, not, setup, stateIn } from "xstate";
|
|
7
|
+
import { Activity, ViewTransition, addTransitionType, createContext, startTransition, useContext, useDeferredValue, useEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
8
|
+
import { assign, not, or, setup, stateIn } from "xstate";
|
|
9
9
|
import { ArrowLeftIcon } from "@sanity/icons/ArrowLeft";
|
|
10
10
|
import { CloseIcon } from "@sanity/icons/Close";
|
|
11
11
|
import { CodeBlockIcon } from "@sanity/icons/CodeBlock";
|
|
@@ -19,6 +19,7 @@ import { Tooltip } from "@sanity/ui/tooltip";
|
|
|
19
19
|
import { ImageIcon } from "@sanity/icons/Image";
|
|
20
20
|
import { useToast } from "@sanity/ui/toast";
|
|
21
21
|
import { AddIcon } from "@sanity/icons/Add";
|
|
22
|
+
import { ClipboardIcon } from "@sanity/icons/Clipboard";
|
|
22
23
|
import { RestoreIcon } from "@sanity/icons/Restore";
|
|
23
24
|
import { MotionConfig, Reorder, useDragControls } from "motion/react";
|
|
24
25
|
import { ArrowDownIcon } from "@sanity/icons/ArrowDown";
|
|
@@ -26,9 +27,9 @@ import { ArrowUpIcon } from "@sanity/icons/ArrowUp";
|
|
|
26
27
|
import { CopyIcon } from "@sanity/icons/Copy";
|
|
27
28
|
import { EditIcon } from "@sanity/icons/Edit";
|
|
28
29
|
import { EllipsisHorizontalIcon } from "@sanity/icons/EllipsisHorizontal";
|
|
30
|
+
import { ShareIcon } from "@sanity/icons/Share";
|
|
29
31
|
import { Menu, MenuButton, MenuDivider, MenuItem } from "@sanity/ui/menu";
|
|
30
32
|
import scrollIntoView from "scroll-into-view-if-needed";
|
|
31
|
-
import { ClipboardIcon } from "@sanity/icons/Clipboard";
|
|
32
33
|
import { Code } from "@sanity/ui/code";
|
|
33
34
|
import { registerLanguage } from "react-refractor";
|
|
34
35
|
import typescript from "refractor/typescript";
|
|
@@ -41,951 +42,1300 @@ function useThemer() {
|
|
|
41
42
|
if (!context) throw Error("useThemer must be used within the `themerTool` plugin");
|
|
42
43
|
return context;
|
|
43
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
|
+
];
|
|
44
143
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
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.
|
|
51
150
|
*
|
|
52
151
|
* @internal
|
|
53
152
|
*/
|
|
54
|
-
function
|
|
55
|
-
let
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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));
|
|
59
173
|
}
|
|
60
|
-
return
|
|
174
|
+
return palette.dominant = candidates.reduce((most, candidate) => candidate.population > most.population ? candidate : most).hex, palette;
|
|
61
175
|
}
|
|
62
176
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* @internal
|
|
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.
|
|
68
180
|
*/
|
|
69
|
-
function
|
|
70
|
-
let
|
|
71
|
-
|
|
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;
|
|
72
207
|
}
|
|
73
|
-
function
|
|
74
|
-
return
|
|
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];
|
|
75
210
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
active: null,
|
|
79
|
-
custom: [],
|
|
80
|
-
removed: [],
|
|
81
|
-
order: []
|
|
82
|
-
};
|
|
83
|
-
/**
|
|
84
|
-
* Resolves the list of themes: the configured theme first, then the presets
|
|
85
|
-
* (minus the ones that would only repeat the configured theme), then the
|
|
86
|
-
* user's own themes — rearranged into the order the user dragged them into,
|
|
87
|
-
* with themes that order does not know about kept in that default order after
|
|
88
|
-
* the ones it does. Removed themes are set aside so they can be restored, and
|
|
89
|
-
* the active theme falls back to the configured one when its slug no longer
|
|
90
|
-
* resolves.
|
|
91
|
-
*
|
|
92
|
-
* @internal
|
|
93
|
-
*/
|
|
94
|
-
function resolveThemes(state, baseOptions) {
|
|
95
|
-
let config = {
|
|
96
|
-
slug: "config",
|
|
97
|
-
title: "Studio config",
|
|
98
|
-
options: baseOptions,
|
|
99
|
-
source: "config"
|
|
100
|
-
}, all = [config];
|
|
101
|
-
for (let preset of presets) sameOptions(preset.options, baseOptions) || all.push({
|
|
102
|
-
slug: preset.slug,
|
|
103
|
-
title: preset.title,
|
|
104
|
-
options: preset.options,
|
|
105
|
-
source: "preset"
|
|
106
|
-
});
|
|
107
|
-
for (let theme of state.custom) all.push({
|
|
108
|
-
slug: theme.slug,
|
|
109
|
-
title: theme.title,
|
|
110
|
-
options: theme.options,
|
|
111
|
-
source: "custom",
|
|
112
|
-
...theme.palette ? { palette: theme.palette } : {}
|
|
113
|
-
});
|
|
114
|
-
let position = new Map(state.order.map((slug, index) => [slug, index])), rank = (theme) => position.get(theme.slug) ?? state.order.length;
|
|
115
|
-
all.sort((a, b) => rank(a) - rank(b));
|
|
116
|
-
let removedSlugs = new Set(state.removed), themes = all.filter((theme) => !removedSlugs.has(theme.slug));
|
|
117
|
-
return {
|
|
118
|
-
themes,
|
|
119
|
-
removed: all.filter((theme) => removedSlugs.has(theme.slug)),
|
|
120
|
-
active: themes.find((theme) => theme.slug === state.active) ?? config
|
|
121
|
-
};
|
|
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);
|
|
122
213
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
* the configured theme.
|
|
126
|
-
*
|
|
127
|
-
* @internal
|
|
128
|
-
*/
|
|
129
|
-
function createCustomTheme(title, options, palette) {
|
|
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;
|
|
130
216
|
return {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
options,
|
|
134
|
-
...palette ? { palette } : {}
|
|
217
|
+
saturation: delta === 0 ? 0 : lightness > .5 ? delta / (2 - max - min) : delta / (max + min),
|
|
218
|
+
lightness
|
|
135
219
|
};
|
|
136
220
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
return title.trim() || "Untitled theme";
|
|
221
|
+
function rgbToHex(r, g, b) {
|
|
222
|
+
return `#${[
|
|
223
|
+
r,
|
|
224
|
+
g,
|
|
225
|
+
b
|
|
226
|
+
].map((channel) => channel.toString(16).padStart(2, "0")).join("")}`;
|
|
144
227
|
}
|
|
145
|
-
|
|
146
|
-
|
|
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
|
+
];
|
|
147
236
|
}
|
|
148
|
-
|
|
149
|
-
|
|
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);
|
|
150
253
|
}
|
|
151
254
|
/**
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
* side by side (`split`). The context carries the persisted state (the
|
|
157
|
-
* applied theme, the user's themes and what was removed) alongside what the
|
|
158
|
-
* flows need.
|
|
159
|
-
*
|
|
160
|
-
* Theme operations are handled in every flow, and the flows leave on their
|
|
161
|
-
* own when they lose their subject: the editor when its theme is removed or
|
|
162
|
-
* deleted, and the removed view when its last theme is restored or deleted.
|
|
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.
|
|
163
259
|
*
|
|
164
260
|
* @internal
|
|
165
261
|
*/
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
},
|
|
177
|
-
actions: {
|
|
178
|
-
pick: assign((_, params) => ({ active: params.slug === "config" ? null : params.slug })),
|
|
179
|
-
add: assign(({ context }, params) => {
|
|
180
|
-
let theme = createCustomTheme(params.title ?? "Untitled theme", params.options ?? themesOf(context).active.options, params.palette);
|
|
181
|
-
return {
|
|
182
|
-
active: theme.slug,
|
|
183
|
-
custom: [...context.custom, theme],
|
|
184
|
-
editing: {
|
|
185
|
-
slug: theme.slug,
|
|
186
|
-
focusTitle: params.title === void 0
|
|
187
|
-
},
|
|
188
|
-
images: params.imageUrl ? {
|
|
189
|
-
...context.images,
|
|
190
|
-
[theme.slug]: params.imageUrl
|
|
191
|
-
} : context.images
|
|
192
|
-
};
|
|
193
|
-
}),
|
|
194
|
-
duplicate: assign(({ context }, params) => {
|
|
195
|
-
let { themes, removed } = themesOf(context), source = [...themes, ...removed].find((theme) => theme.slug === params.slug);
|
|
196
|
-
if (!source) return {};
|
|
197
|
-
let theme = createCustomTheme(duplicateTitle(source.title), source.options, source.palette);
|
|
198
|
-
return {
|
|
199
|
-
active: theme.slug,
|
|
200
|
-
custom: [...context.custom, theme],
|
|
201
|
-
editing: {
|
|
202
|
-
slug: theme.slug,
|
|
203
|
-
focusTitle: !0
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
}),
|
|
207
|
-
startEditing: assign((_, params) => ({
|
|
208
|
-
active: params.slug,
|
|
209
|
-
editing: {
|
|
210
|
-
slug: params.slug,
|
|
211
|
-
focusTitle: !1
|
|
212
|
-
}
|
|
213
|
-
})),
|
|
214
|
-
stopEditing: assign({ editing: null }),
|
|
215
|
-
update: assign(({ context }, params) => ({
|
|
216
|
-
custom: context.custom.map((theme) => theme.slug === params.slug ? {
|
|
217
|
-
...theme,
|
|
218
|
-
title: params.title ?? theme.title,
|
|
219
|
-
options: params.options ?? theme.options,
|
|
220
|
-
...params.palette ? { palette: params.palette } : {}
|
|
221
|
-
} : theme),
|
|
222
|
-
images: params.imageUrl && context.custom.some((theme) => theme.slug === params.slug) ? {
|
|
223
|
-
...context.images,
|
|
224
|
-
[params.slug]: params.imageUrl
|
|
225
|
-
} : context.images
|
|
226
|
-
})),
|
|
227
|
-
remove: assign(({ context }, params) => params.slug === "config" || context.removed.includes(params.slug) ? {} : {
|
|
228
|
-
active: context.active === params.slug ? null : context.active,
|
|
229
|
-
removed: [...context.removed, params.slug]
|
|
230
|
-
}),
|
|
231
|
-
restore: assign(({ context }, params) => ({ removed: context.removed.filter((slug) => slug !== params.slug) })),
|
|
232
|
-
reorder: assign(({ context }, params) => {
|
|
233
|
-
let listed = new Set(themesOf(context).themes.map((theme) => theme.slug)), order = params.order.filter((slug) => listed.has(slug));
|
|
234
|
-
return { order: [...order, ...context.order.filter((slug) => !order.includes(slug))] };
|
|
235
|
-
}),
|
|
236
|
-
delete: assign(({ context }, params) => {
|
|
237
|
-
if (!context.custom.some((theme) => theme.slug === params.slug)) return {};
|
|
238
|
-
let { [params.slug]: _deleted, ...images } = context.images;
|
|
239
|
-
return {
|
|
240
|
-
active: context.active === params.slug ? null : context.active,
|
|
241
|
-
custom: context.custom.filter((theme) => theme.slug !== params.slug),
|
|
242
|
-
removed: context.removed.filter((slug) => slug !== params.slug),
|
|
243
|
-
images
|
|
244
|
-
};
|
|
245
|
-
})
|
|
246
|
-
}
|
|
247
|
-
}).createMachine({
|
|
248
|
-
id: "themer",
|
|
249
|
-
context: ({ input }) => ({
|
|
250
|
-
...input.stored,
|
|
251
|
-
baseOptions: input.baseOptions,
|
|
252
|
-
editing: null,
|
|
253
|
-
images: {}
|
|
254
|
-
}),
|
|
255
|
-
type: "parallel",
|
|
256
|
-
states: {
|
|
257
|
-
sidebar: {
|
|
258
|
-
initial: "closed",
|
|
259
|
-
states: {
|
|
260
|
-
closed: { on: { "sidebar.toggle": "open" } },
|
|
261
|
-
open: { on: {
|
|
262
|
-
"sidebar.toggle": "closed",
|
|
263
|
-
"sidebar.close": "closed"
|
|
264
|
-
} }
|
|
265
|
-
}
|
|
266
|
-
},
|
|
267
|
-
preview: {
|
|
268
|
-
initial: "single",
|
|
269
|
-
states: {
|
|
270
|
-
single: { on: { "preview.toggle": "split" } },
|
|
271
|
-
split: { on: {
|
|
272
|
-
"preview.toggle": "single",
|
|
273
|
-
"sidebar.close": "single",
|
|
274
|
-
"sidebar.toggle": {
|
|
275
|
-
guard: stateIn({ sidebar: "open" }),
|
|
276
|
-
target: "single"
|
|
277
|
-
}
|
|
278
|
-
} }
|
|
279
|
-
}
|
|
280
|
-
},
|
|
281
|
-
flow: {
|
|
282
|
-
initial: "list",
|
|
283
|
-
on: {
|
|
284
|
-
"theme.pick": { actions: {
|
|
285
|
-
type: "pick",
|
|
286
|
-
params: ({ event }) => ({ slug: event.slug })
|
|
287
|
-
} },
|
|
288
|
-
"theme.add": {
|
|
289
|
-
target: ".edit",
|
|
290
|
-
actions: {
|
|
291
|
-
type: "add",
|
|
292
|
-
params: ({ event }) => ({
|
|
293
|
-
title: event.title,
|
|
294
|
-
options: event.options,
|
|
295
|
-
palette: event.palette,
|
|
296
|
-
imageUrl: event.imageUrl
|
|
297
|
-
})
|
|
298
|
-
}
|
|
299
|
-
},
|
|
300
|
-
"theme.duplicate": {
|
|
301
|
-
target: ".edit",
|
|
302
|
-
actions: {
|
|
303
|
-
type: "duplicate",
|
|
304
|
-
params: ({ event }) => ({ slug: event.slug })
|
|
305
|
-
}
|
|
306
|
-
},
|
|
307
|
-
"theme.edit": {
|
|
308
|
-
target: ".edit",
|
|
309
|
-
guard: {
|
|
310
|
-
type: "isCustomTheme",
|
|
311
|
-
params: ({ event }) => ({ slug: event.slug })
|
|
312
|
-
},
|
|
313
|
-
actions: {
|
|
314
|
-
type: "startEditing",
|
|
315
|
-
params: ({ event }) => ({ slug: event.slug })
|
|
316
|
-
}
|
|
317
|
-
},
|
|
318
|
-
"theme.update": { actions: {
|
|
319
|
-
type: "update",
|
|
320
|
-
params: ({ event }) => ({
|
|
321
|
-
slug: event.slug,
|
|
322
|
-
title: event.title,
|
|
323
|
-
options: event.options,
|
|
324
|
-
palette: event.palette,
|
|
325
|
-
imageUrl: event.imageUrl
|
|
326
|
-
})
|
|
327
|
-
} },
|
|
328
|
-
"theme.remove": { actions: {
|
|
329
|
-
type: "remove",
|
|
330
|
-
params: ({ event }) => ({ slug: event.slug })
|
|
331
|
-
} },
|
|
332
|
-
"theme.restore": { actions: {
|
|
333
|
-
type: "restore",
|
|
334
|
-
params: ({ event }) => ({ slug: event.slug })
|
|
335
|
-
} },
|
|
336
|
-
"theme.delete": { actions: {
|
|
337
|
-
type: "delete",
|
|
338
|
-
params: ({ event }) => ({ slug: event.slug })
|
|
339
|
-
} },
|
|
340
|
-
"theme.reorder": { actions: {
|
|
341
|
-
type: "reorder",
|
|
342
|
-
params: ({ event }) => ({ order: event.order })
|
|
343
|
-
} },
|
|
344
|
-
"flow.list": ".list",
|
|
345
|
-
"flow.removed": {
|
|
346
|
-
target: ".removed",
|
|
347
|
-
guard: "hasRemovedThemes"
|
|
348
|
-
}
|
|
349
|
-
},
|
|
350
|
-
states: {
|
|
351
|
-
list: {},
|
|
352
|
-
edit: {
|
|
353
|
-
exit: "stopEditing",
|
|
354
|
-
always: {
|
|
355
|
-
guard: not("isEditingListedTheme"),
|
|
356
|
-
target: "list"
|
|
357
|
-
}
|
|
358
|
-
},
|
|
359
|
-
removed: { always: {
|
|
360
|
-
guard: not("hasRemovedThemes"),
|
|
361
|
-
target: "list"
|
|
362
|
-
} }
|
|
363
|
-
}
|
|
364
|
-
}
|
|
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
|
+
});
|
|
365
272
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
custom,
|
|
373
|
-
removed,
|
|
374
|
-
order
|
|
375
|
-
};
|
|
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);
|
|
376
279
|
}
|
|
377
|
-
var root$2 = "_1o50s7t0";
|
|
378
|
-
const ScrollAreaInsetContext = createContext({
|
|
379
|
-
left: 0,
|
|
380
|
-
right: 0,
|
|
381
|
-
bottom: 0
|
|
382
|
-
});
|
|
383
|
-
/**
|
|
384
|
-
* The gutter the last scroll area measured. It is the same for every scroll
|
|
385
|
-
* area (the reserved gutter does not depend on the content), so the next one
|
|
386
|
-
* starts out with the right padding instead of shifting after its first
|
|
387
|
-
* measurement — which would also throw off anything scrolling into view as
|
|
388
|
-
* the content mounts.
|
|
389
|
-
*/
|
|
390
|
-
let lastGutter = 0;
|
|
391
280
|
/**
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
*
|
|
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.
|
|
397
288
|
*
|
|
398
289
|
* @internal
|
|
399
290
|
*/
|
|
400
|
-
function
|
|
401
|
-
let
|
|
402
|
-
|
|
403
|
-
let element = ref.current;
|
|
404
|
-
if (!element) return;
|
|
405
|
-
let measure = () => {
|
|
406
|
-
lastGutter = element.offsetWidth - element.clientWidth, setGutter(lastGutter);
|
|
407
|
-
};
|
|
408
|
-
measure();
|
|
409
|
-
let observer = new ResizeObserver(measure);
|
|
410
|
-
return observer.observe(element), () => observer.disconnect();
|
|
411
|
-
}, t1 = [], $[0] = t0, $[1] = t1) : (t0 = $[0], t1 = $[1]), useEffect(t0, t1);
|
|
412
|
-
let t2 = space[padding], t3 = Math.max(0, space[padding] - gutter), t4 = space[padding], t5;
|
|
413
|
-
$[2] !== t2 || $[3] !== t3 || $[4] !== t4 ? (t5 = {
|
|
414
|
-
left: t2,
|
|
415
|
-
right: t3,
|
|
416
|
-
bottom: t4
|
|
417
|
-
}, $[2] = t2, $[3] = t3, $[4] = t4, $[5] = t5) : t5 = $[5];
|
|
418
|
-
let inset = t5, t6;
|
|
419
|
-
$[6] === inset.right ? t6 = $[7] : (t6 = { paddingRight: inset.right }, $[6] = inset.right, $[7] = t6);
|
|
420
|
-
let t7;
|
|
421
|
-
$[8] !== children || $[9] !== inset ? (t7 = /* @__PURE__ */ jsx(ScrollAreaInsetContext.Provider, {
|
|
422
|
-
value: inset,
|
|
423
|
-
children
|
|
424
|
-
}), $[8] = children, $[9] = inset, $[10] = t7) : t7 = $[10];
|
|
425
|
-
let t8;
|
|
426
|
-
return $[11] !== padding || $[12] !== t6 || $[13] !== t7 ? (t8 = /* @__PURE__ */ jsx(Box, {
|
|
427
|
-
className: root$2,
|
|
428
|
-
flex: 1,
|
|
429
|
-
overflow: "auto",
|
|
430
|
-
padding,
|
|
431
|
-
ref,
|
|
432
|
-
style: t6,
|
|
433
|
-
children: t7
|
|
434
|
-
}), $[11] = padding, $[12] = t6, $[13] = t7, $[14] = t8) : t8 = $[14], t8;
|
|
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;
|
|
435
294
|
}
|
|
436
|
-
function
|
|
437
|
-
|
|
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;
|
|
438
298
|
}
|
|
439
299
|
/**
|
|
440
|
-
*
|
|
441
|
-
*
|
|
442
|
-
*
|
|
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.
|
|
443
303
|
*
|
|
444
304
|
* @internal
|
|
445
305
|
*/
|
|
446
|
-
function
|
|
447
|
-
let
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
let t4;
|
|
454
|
-
return $[4] !== props.children || $[5] !== t3 ? (t4 = /* @__PURE__ */ jsx("div", {
|
|
455
|
-
style: t3,
|
|
456
|
-
children: props.children
|
|
457
|
-
}), $[4] = props.children, $[5] = t3, $[6] = t4) : t4 = $[6], t4;
|
|
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;
|
|
458
313
|
}
|
|
459
|
-
var avatar = "_1aa9huuh", body = "_1aa9huui", button = "_1aa9huuq", colors = {
|
|
460
|
-
bg: "var(--_1aa9huu0)",
|
|
461
|
-
fg: "var(--_1aa9huu1)",
|
|
462
|
-
mutedFg: "var(--_1aa9huu2)",
|
|
463
|
-
border: "var(--_1aa9huu3)",
|
|
464
|
-
accent: "var(--_1aa9huu4)",
|
|
465
|
-
avatar: "var(--_1aa9huu5)",
|
|
466
|
-
selectedBg: "var(--_1aa9huu6)",
|
|
467
|
-
selectedFg: "var(--_1aa9huu7)",
|
|
468
|
-
inputBg: "var(--_1aa9huu8)",
|
|
469
|
-
inputBorder: "var(--_1aa9huu9)"
|
|
470
|
-
}, field = "_1aa9huun", footer = "_1aa9huup", form = "_1aa9huum", input = "_1aa9huuo", line = {
|
|
471
|
-
fg: "_1aa9huus _1aa9huur",
|
|
472
|
-
muted: "_1aa9huut _1aa9huur",
|
|
473
|
-
selected: "_1aa9huuu _1aa9huur"
|
|
474
|
-
}, list = "_1aa9huuj", listRow = "_1aa9huuk", logo = "_1aa9huuf", navbar = "_1aa9huue", pane = "_1aa9huul", root$1 = "_1aa9huua", spacer = "_1aa9huug", studio = {
|
|
475
|
-
light: "_1aa9huub",
|
|
476
|
-
dark: "_1aa9huud _1aa9huub"
|
|
477
|
-
};
|
|
478
|
-
/** The document rows in the list pane — the widths of their title lines */
|
|
479
|
-
const LIST_ROWS = [
|
|
480
|
-
"62%",
|
|
481
|
-
"46%",
|
|
482
|
-
"70%",
|
|
483
|
-
"52%"
|
|
484
|
-
], FIELDS = ["32%", "24%"];
|
|
485
314
|
/**
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
* the right — in both color schemes at once: the light scheme on the left and
|
|
489
|
-
* the dark scheme on the right, split diagonally. It only paints colors the
|
|
490
|
-
* theme options change (backgrounds, text, borders, the accent) so that a
|
|
491
|
-
* glance shows what the theme does to the Studio — with few and bold enough
|
|
492
|
-
* elements to still read when scaled down to a thumbnail.
|
|
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.
|
|
493
317
|
*
|
|
494
318
|
* @internal
|
|
495
319
|
*/
|
|
496
|
-
function
|
|
497
|
-
let
|
|
498
|
-
|
|
499
|
-
let theme = t0, t1;
|
|
500
|
-
$[2] === Symbol.for("react.memo_cache_sentinel") ? (t1 = /* @__PURE__ */ jsx(MockStudio, { scheme: "light" }), $[2] = t1) : t1 = $[2];
|
|
501
|
-
let t2;
|
|
502
|
-
$[3] === theme ? t2 = $[4] : (t2 = /* @__PURE__ */ jsx(ThemeProvider, {
|
|
503
|
-
scheme: "light",
|
|
504
|
-
theme,
|
|
505
|
-
tone: "default",
|
|
506
|
-
children: t1
|
|
507
|
-
}), $[3] = theme, $[4] = t2);
|
|
508
|
-
let t3;
|
|
509
|
-
$[5] === Symbol.for("react.memo_cache_sentinel") ? (t3 = /* @__PURE__ */ jsx(MockStudio, { scheme: "dark" }), $[5] = t3) : t3 = $[5];
|
|
510
|
-
let t4;
|
|
511
|
-
$[6] === theme ? t4 = $[7] : (t4 = /* @__PURE__ */ jsx(ThemeProvider, {
|
|
512
|
-
scheme: "dark",
|
|
513
|
-
theme,
|
|
514
|
-
tone: "default",
|
|
515
|
-
children: t3
|
|
516
|
-
}), $[6] = theme, $[7] = t4);
|
|
517
|
-
let t5;
|
|
518
|
-
return $[8] !== t2 || $[9] !== t4 ? (t5 = /* @__PURE__ */ jsxs("div", {
|
|
519
|
-
"aria-hidden": !0,
|
|
520
|
-
className: root$1,
|
|
521
|
-
children: [t2, t4]
|
|
522
|
-
}), $[8] = t2, $[9] = t4, $[10] = t5) : t5 = $[10], t5;
|
|
523
|
-
}
|
|
524
|
-
function MockStudio(props) {
|
|
525
|
-
let $ = c(19), { scheme } = props, { color } = useTheme_v2(), selected = color.selectable.default.selected, inputColor = color.input.default.enabled, t0 = studio[scheme], t1;
|
|
526
|
-
$[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({
|
|
527
|
-
[colors.bg]: color.bg,
|
|
528
|
-
[colors.fg]: color.fg,
|
|
529
|
-
[colors.mutedFg]: color.muted.fg,
|
|
530
|
-
[colors.border]: color.border,
|
|
531
|
-
[colors.accent]: color.button.default.primary.enabled.bg,
|
|
532
|
-
[colors.avatar]: color.avatar.blue.bg,
|
|
533
|
-
[colors.selectedBg]: selected.bg,
|
|
534
|
-
[colors.selectedFg]: selected.fg,
|
|
535
|
-
[colors.inputBg]: inputColor.bg,
|
|
536
|
-
[colors.inputBorder]: inputColor.border
|
|
537
|
-
}), $[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];
|
|
538
|
-
let t2;
|
|
539
|
-
$[11] === Symbol.for("react.memo_cache_sentinel") ? (t2 = /* @__PURE__ */ jsx("div", { className: logo }), $[11] = t2) : t2 = $[11];
|
|
540
|
-
let t3;
|
|
541
|
-
$[12] === Symbol.for("react.memo_cache_sentinel") ? (t3 = /* @__PURE__ */ jsxs("div", {
|
|
542
|
-
className: navbar,
|
|
543
|
-
children: [
|
|
544
|
-
t2,
|
|
545
|
-
/* @__PURE__ */ jsx("div", {
|
|
546
|
-
className: line.fg,
|
|
547
|
-
style: { width: "22%" }
|
|
548
|
-
}),
|
|
549
|
-
/* @__PURE__ */ jsx("div", { className: spacer }),
|
|
550
|
-
/* @__PURE__ */ jsx("div", { className: avatar })
|
|
551
|
-
]
|
|
552
|
-
}), $[12] = t3) : t3 = $[12];
|
|
553
|
-
let t4;
|
|
554
|
-
$[13] === Symbol.for("react.memo_cache_sentinel") ? (t4 = /* @__PURE__ */ jsx("div", {
|
|
555
|
-
className: list,
|
|
556
|
-
children: LIST_ROWS.map(_temp$6)
|
|
557
|
-
}), $[13] = t4) : t4 = $[13];
|
|
558
|
-
let t5;
|
|
559
|
-
$[14] === Symbol.for("react.memo_cache_sentinel") ? (t5 = /* @__PURE__ */ jsxs("div", {
|
|
560
|
-
className: form,
|
|
561
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
562
|
-
className: line.fg,
|
|
563
|
-
style: { width: "45%" }
|
|
564
|
-
}), FIELDS.map(_temp2)]
|
|
565
|
-
}), $[14] = t5) : t5 = $[14];
|
|
566
|
-
let t6;
|
|
567
|
-
$[15] === Symbol.for("react.memo_cache_sentinel") ? (t6 = /* @__PURE__ */ jsxs("div", {
|
|
568
|
-
className: body,
|
|
569
|
-
children: [t4, /* @__PURE__ */ jsxs("div", {
|
|
570
|
-
className: pane,
|
|
571
|
-
children: [t5, /* @__PURE__ */ jsx("div", {
|
|
572
|
-
className: footer,
|
|
573
|
-
children: /* @__PURE__ */ jsx("div", { className: button })
|
|
574
|
-
})]
|
|
575
|
-
})]
|
|
576
|
-
}), $[15] = t6) : t6 = $[15];
|
|
577
|
-
let t7;
|
|
578
|
-
return $[16] !== t0 || $[17] !== t1 ? (t7 = /* @__PURE__ */ jsxs("div", {
|
|
579
|
-
className: t0,
|
|
580
|
-
style: t1,
|
|
581
|
-
children: [t3, t6]
|
|
582
|
-
}), $[16] = t0, $[17] = t1, $[18] = t7) : t7 = $[18], t7;
|
|
583
|
-
}
|
|
584
|
-
function _temp2(width_0) {
|
|
585
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
586
|
-
className: field,
|
|
587
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
588
|
-
className: line.muted,
|
|
589
|
-
style: { width: width_0 }
|
|
590
|
-
}), /* @__PURE__ */ jsx("div", { className: input })]
|
|
591
|
-
}, width_0);
|
|
592
|
-
}
|
|
593
|
-
function _temp$6(width, index) {
|
|
594
|
-
let isSelected = index === 1;
|
|
595
|
-
return /* @__PURE__ */ jsx("div", {
|
|
596
|
-
className: listRow,
|
|
597
|
-
"data-selected": isSelected,
|
|
598
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
599
|
-
className: isSelected ? line.selected : line.muted,
|
|
600
|
-
style: { width }
|
|
601
|
-
})
|
|
602
|
-
}, width);
|
|
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;
|
|
603
323
|
}
|
|
604
324
|
/**
|
|
605
|
-
*
|
|
606
|
-
*
|
|
607
|
-
*
|
|
608
|
-
* @internal
|
|
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.
|
|
609
327
|
*/
|
|
610
|
-
function
|
|
611
|
-
let
|
|
612
|
-
return
|
|
613
|
-
overflow: "hidden",
|
|
614
|
-
radius: 3,
|
|
615
|
-
shadow: 1,
|
|
616
|
-
children: /* @__PURE__ */ jsx(ThemePreview, { options: props.options })
|
|
617
|
-
}), $[0] = props.options, $[1] = t0), t0;
|
|
328
|
+
function interest(hex) {
|
|
329
|
+
let { s, l } = hexToHsl(hex);
|
|
330
|
+
return .1 + .55 * s + .35 * (1 - Math.abs(l - .5) * 2);
|
|
618
331
|
}
|
|
619
|
-
var removedThumbnail = "_1vdkp0v0";
|
|
620
332
|
/**
|
|
621
|
-
*
|
|
622
|
-
*
|
|
623
|
-
*
|
|
624
|
-
* the last one is gone.
|
|
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.
|
|
625
336
|
*
|
|
626
337
|
* @internal
|
|
627
338
|
*/
|
|
628
|
-
function
|
|
629
|
-
let
|
|
630
|
-
if (
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
}),
|
|
639
|
-
/* @__PURE__ */ jsx(Text, {
|
|
640
|
-
align: "center",
|
|
641
|
-
size: 1,
|
|
642
|
-
textOverflow: "ellipsis",
|
|
643
|
-
children: displayTitle(theme.title)
|
|
644
|
-
}),
|
|
645
|
-
/* @__PURE__ */ jsxs(Flex, {
|
|
646
|
-
gap: 2,
|
|
647
|
-
justify: "center",
|
|
648
|
-
children: [/* @__PURE__ */ jsx(Button, {
|
|
649
|
-
fontSize: 1,
|
|
650
|
-
mode: "ghost",
|
|
651
|
-
onClick: () => send({
|
|
652
|
-
type: "theme.restore",
|
|
653
|
-
slug: theme.slug
|
|
654
|
-
}),
|
|
655
|
-
padding: 2,
|
|
656
|
-
text: "Restore"
|
|
657
|
-
}), theme.source === "custom" && /* @__PURE__ */ jsx(Button, {
|
|
658
|
-
fontSize: 1,
|
|
659
|
-
mode: "ghost",
|
|
660
|
-
onClick: () => send({
|
|
661
|
-
type: "theme.delete",
|
|
662
|
-
slug: theme.slug
|
|
663
|
-
}),
|
|
664
|
-
padding: 2,
|
|
665
|
-
text: "Delete",
|
|
666
|
-
tone: "critical"
|
|
667
|
-
})]
|
|
668
|
-
})
|
|
669
|
-
]
|
|
670
|
-
}, theme.slug), $[3] = send, $[4] = t1), t0 = removed.map(t1), $[0] = removed, $[1] = send, $[2] = t0;
|
|
671
|
-
} else t0 = $[2];
|
|
672
|
-
let t1;
|
|
673
|
-
return $[5] === t0 ? t1 = $[6] : (t1 = /* @__PURE__ */ jsx(ScrollArea, {
|
|
674
|
-
padding: 3,
|
|
675
|
-
children: /* @__PURE__ */ jsx(Stack, {
|
|
676
|
-
gap: 5,
|
|
677
|
-
children: t0
|
|
678
|
-
})
|
|
679
|
-
}), $[5] = t0, $[6] = t1), t1;
|
|
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";
|
|
680
349
|
}
|
|
681
|
-
/** @internal */
|
|
682
|
-
const IMAGE_PALETTE_KEYS = [
|
|
683
|
-
"vibrant",
|
|
684
|
-
"lightVibrant",
|
|
685
|
-
"darkVibrant",
|
|
686
|
-
"muted",
|
|
687
|
-
"lightMuted",
|
|
688
|
-
"darkMuted",
|
|
689
|
-
"dominant"
|
|
690
|
-
], IMAGE_PALETTE_VARIANTS = [
|
|
691
|
-
"muted",
|
|
692
|
-
"vibrant",
|
|
693
|
-
"lightMuted",
|
|
694
|
-
"lightVibrant",
|
|
695
|
-
"darkMuted",
|
|
696
|
-
"darkVibrant"
|
|
697
|
-
], IMAGE_PALETTE_TITLES = {
|
|
698
|
-
dominant: "Dominant",
|
|
699
|
-
vibrant: "Vibrant",
|
|
700
|
-
lightVibrant: "Light vibrant",
|
|
701
|
-
darkVibrant: "Dark vibrant",
|
|
702
|
-
muted: "Muted",
|
|
703
|
-
lightMuted: "Light muted",
|
|
704
|
-
darkMuted: "Dark muted"
|
|
705
|
-
}, TARGETS = [
|
|
706
|
-
["vibrant", {
|
|
707
|
-
lightness: [
|
|
708
|
-
.5,
|
|
709
|
-
.3,
|
|
710
|
-
.7
|
|
711
|
-
],
|
|
712
|
-
saturation: [
|
|
713
|
-
1,
|
|
714
|
-
.35,
|
|
715
|
-
1
|
|
716
|
-
]
|
|
717
|
-
}],
|
|
718
|
-
["lightVibrant", {
|
|
719
|
-
lightness: [
|
|
720
|
-
.74,
|
|
721
|
-
.55,
|
|
722
|
-
1
|
|
723
|
-
],
|
|
724
|
-
saturation: [
|
|
725
|
-
1,
|
|
726
|
-
.35,
|
|
727
|
-
1
|
|
728
|
-
]
|
|
729
|
-
}],
|
|
730
|
-
["darkVibrant", {
|
|
731
|
-
lightness: [
|
|
732
|
-
.26,
|
|
733
|
-
0,
|
|
734
|
-
.45
|
|
735
|
-
],
|
|
736
|
-
saturation: [
|
|
737
|
-
1,
|
|
738
|
-
.35,
|
|
739
|
-
1
|
|
740
|
-
]
|
|
741
|
-
}],
|
|
742
|
-
["muted", {
|
|
743
|
-
lightness: [
|
|
744
|
-
.5,
|
|
745
|
-
.3,
|
|
746
|
-
.7
|
|
747
|
-
],
|
|
748
|
-
saturation: [
|
|
749
|
-
.3,
|
|
750
|
-
0,
|
|
751
|
-
.4
|
|
752
|
-
]
|
|
753
|
-
}],
|
|
754
|
-
["lightMuted", {
|
|
755
|
-
lightness: [
|
|
756
|
-
.74,
|
|
757
|
-
.55,
|
|
758
|
-
1
|
|
759
|
-
],
|
|
760
|
-
saturation: [
|
|
761
|
-
.3,
|
|
762
|
-
0,
|
|
763
|
-
.4
|
|
764
|
-
]
|
|
765
|
-
}],
|
|
766
|
-
["darkMuted", {
|
|
767
|
-
lightness: [
|
|
768
|
-
.26,
|
|
769
|
-
0,
|
|
770
|
-
.45
|
|
771
|
-
],
|
|
772
|
-
saturation: [
|
|
773
|
-
.3,
|
|
774
|
-
0,
|
|
775
|
-
.4
|
|
776
|
-
]
|
|
777
|
-
}]
|
|
778
|
-
];
|
|
779
350
|
/**
|
|
780
|
-
*
|
|
781
|
-
*
|
|
782
|
-
*
|
|
783
|
-
*
|
|
784
|
-
*
|
|
785
|
-
*
|
|
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.
|
|
786
357
|
*
|
|
787
358
|
* @internal
|
|
788
359
|
*/
|
|
789
|
-
function
|
|
790
|
-
let
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
darkVibrant: null,
|
|
795
|
-
muted: null,
|
|
796
|
-
lightMuted: null,
|
|
797
|
-
darkMuted: null
|
|
798
|
-
};
|
|
799
|
-
if (candidates.length === 0) return palette;
|
|
800
|
-
let maximumPopulation = Math.max(...candidates.map((candidate) => candidate.population)), claimed = /* @__PURE__ */ new Set();
|
|
801
|
-
for (let [key, target] of TARGETS) {
|
|
802
|
-
let best = null, bestScore = -Infinity;
|
|
803
|
-
for (let candidate of candidates) {
|
|
804
|
-
if (claimed.has(candidate.hex) || !matchesTarget(candidate, target)) continue;
|
|
805
|
-
let score = scoreCandidate(candidate, target, maximumPopulation);
|
|
806
|
-
score > bestScore && (best = candidate, bestScore = score);
|
|
807
|
-
}
|
|
808
|
-
best && (palette[key] = best.hex, claimed.add(best.hex));
|
|
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);
|
|
809
365
|
}
|
|
810
|
-
return
|
|
366
|
+
return Object.keys(minimized).length === 0 ? null : minimized;
|
|
811
367
|
}
|
|
812
368
|
/**
|
|
813
|
-
*
|
|
814
|
-
*
|
|
815
|
-
*
|
|
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
|
|
816
374
|
*/
|
|
817
|
-
function
|
|
818
|
-
let
|
|
819
|
-
|
|
820
|
-
let r = data[offset], g = data[offset + 1], b = data[offset + 2];
|
|
821
|
-
if (data[offset + 3] < 125) continue;
|
|
822
|
-
let lightness = (Math.max(r, g, b) + Math.min(r, g, b)) / 510;
|
|
823
|
-
if (lightness < .05 || lightness > .95) continue;
|
|
824
|
-
let key = r >> 3 << 10 | g >> 3 << 5 | b >> 3, bucket = buckets.get(key);
|
|
825
|
-
bucket ? (bucket.count++, bucket.r += r, bucket.g += g, bucket.b += b) : buckets.set(key, {
|
|
826
|
-
count: 1,
|
|
827
|
-
r,
|
|
828
|
-
g,
|
|
829
|
-
b
|
|
830
|
-
});
|
|
831
|
-
}
|
|
832
|
-
let candidates = [];
|
|
833
|
-
for (let bucket of buckets.values()) {
|
|
834
|
-
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);
|
|
835
|
-
candidates.push({
|
|
836
|
-
hex: rgbToHex(r, g, b),
|
|
837
|
-
population: bucket.count,
|
|
838
|
-
saturation,
|
|
839
|
-
lightness
|
|
840
|
-
});
|
|
841
|
-
}
|
|
842
|
-
return candidates;
|
|
843
|
-
}
|
|
844
|
-
function matchesTarget(candidate, target) {
|
|
845
|
-
return candidate.lightness >= target.lightness[1] && candidate.lightness <= target.lightness[2] && candidate.saturation >= target.saturation[1] && candidate.saturation <= target.saturation[2];
|
|
375
|
+
function sameOptions(a, b) {
|
|
376
|
+
let left = resolveThemeOptions(a), right = resolveThemeOptions(b);
|
|
377
|
+
return SCHEMES.every((scheme) => sameScheme(left[scheme], right[scheme]));
|
|
846
378
|
}
|
|
847
|
-
function
|
|
848
|
-
return
|
|
379
|
+
function sameScheme(a, b) {
|
|
380
|
+
return a.accent === b.accent && a.text === b.text && a.background === b.background && a.contrast === b.contrast;
|
|
849
381
|
}
|
|
850
|
-
|
|
851
|
-
|
|
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";
|
|
455
|
+
function sanitizeColor(value) {
|
|
456
|
+
return typeof value == "string" && isColor(value) ? value.toLowerCase() : null;
|
|
457
|
+
}
|
|
458
|
+
function sanitizeContrast(value) {
|
|
459
|
+
return typeof value == "number" && Number.isFinite(value) ? Math.min(100, Math.max(15, value)) : null;
|
|
460
|
+
}
|
|
461
|
+
function sanitizeSchemeOptions(value) {
|
|
462
|
+
let options = {};
|
|
463
|
+
if (!value || typeof value != "object") return options;
|
|
464
|
+
let accent = sanitizeColor(Reflect.get(value, "accent")), text = sanitizeColor(Reflect.get(value, "text")), background = sanitizeColor(Reflect.get(value, "background")), contrast = sanitizeContrast(Reflect.get(value, "contrast"));
|
|
465
|
+
return accent && (options.accent = accent), text && (options.text = text), background && (options.background = background), contrast !== null && (options.contrast = contrast), options;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Options from before they were grouped by scheme: one accent, text color and
|
|
469
|
+
* contrast for both schemes, and a background per scheme. Both schemes get
|
|
470
|
+
* the shared colors, so the theme keeps looking the same.
|
|
471
|
+
*/
|
|
472
|
+
function sanitizeFlatOptions(value) {
|
|
473
|
+
let accent = sanitizeColor(Reflect.get(value, "accent"));
|
|
474
|
+
if (!accent) return null;
|
|
475
|
+
let shared = { accent }, text = sanitizeColor(Reflect.get(value, "text")), contrast = sanitizeContrast(Reflect.get(value, "contrast"));
|
|
476
|
+
text && (shared.text = text), contrast !== null && (shared.contrast = contrast);
|
|
477
|
+
let backgrounds = Reflect.get(value, "background"), options = {};
|
|
478
|
+
for (let scheme of SCHEMES) {
|
|
479
|
+
let background = backgrounds && typeof backgrounds == "object" ? sanitizeColor(Reflect.get(backgrounds, scheme)) : null;
|
|
480
|
+
options[scheme] = background ? {
|
|
481
|
+
...shared,
|
|
482
|
+
background
|
|
483
|
+
} : { ...shared };
|
|
484
|
+
}
|
|
485
|
+
return options;
|
|
486
|
+
}
|
|
487
|
+
function sanitizeOptions(value) {
|
|
488
|
+
if (!value || typeof value != "object") return null;
|
|
489
|
+
if ("accent" in value) return sanitizeFlatOptions(value);
|
|
490
|
+
let options = {};
|
|
491
|
+
for (let scheme of SCHEMES) {
|
|
492
|
+
let schemeOptions = sanitizeSchemeOptions(Reflect.get(value, scheme));
|
|
493
|
+
Object.keys(schemeOptions).length > 0 && (options[scheme] = schemeOptions);
|
|
494
|
+
}
|
|
495
|
+
return options;
|
|
496
|
+
}
|
|
497
|
+
function isReservedSlug(slug) {
|
|
498
|
+
return slug === "config" || presets.some((preset) => preset.slug === slug);
|
|
499
|
+
}
|
|
500
|
+
function sanitizeCustomTheme(value) {
|
|
501
|
+
if (!value || typeof value != "object") return null;
|
|
502
|
+
let slug = Reflect.get(value, "slug");
|
|
503
|
+
if (typeof slug != "string" || !slug || isReservedSlug(slug)) return null;
|
|
504
|
+
let options = sanitizeOptions(Reflect.get(value, "options"));
|
|
505
|
+
if (!options) return null;
|
|
506
|
+
let title = Reflect.get(value, "title"), palette = sanitizePalette(Reflect.get(value, "palette"));
|
|
507
|
+
return {
|
|
508
|
+
slug,
|
|
509
|
+
title: displayTitle(typeof title == "string" ? title : ""),
|
|
510
|
+
options,
|
|
511
|
+
...palette ? { palette } : {}
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
function sanitizePalette(value) {
|
|
515
|
+
if (!value || typeof value != "object") return null;
|
|
516
|
+
let palette = {}, swatches = 0;
|
|
517
|
+
for (let key of IMAGE_PALETTE_KEYS) {
|
|
518
|
+
let swatch = sanitizeColor(Reflect.get(value, key));
|
|
519
|
+
palette[key] = swatch, swatch && swatches++;
|
|
520
|
+
}
|
|
521
|
+
return swatches > 0 ? palette : null;
|
|
522
|
+
}
|
|
523
|
+
function sanitizeState(value) {
|
|
524
|
+
if (!value || typeof value != "object") return initialThemerState;
|
|
525
|
+
let custom = [], rawCustom = Reflect.get(value, "custom");
|
|
526
|
+
if (Array.isArray(rawCustom)) for (let item of rawCustom) {
|
|
527
|
+
let theme = sanitizeCustomTheme(item);
|
|
528
|
+
theme && !custom.some((existing) => existing.slug === theme.slug) && custom.push(theme);
|
|
529
|
+
}
|
|
530
|
+
let removed = [], rawRemoved = Reflect.get(value, "removed");
|
|
531
|
+
if (Array.isArray(rawRemoved)) for (let slug of rawRemoved) typeof slug == "string" && slug !== "config" && (presets.some((preset) => preset.slug === slug) || custom.some((theme) => theme.slug === slug)) && !removed.includes(slug) && removed.push(slug);
|
|
532
|
+
let order = [], rawOrder = Reflect.get(value, "order");
|
|
533
|
+
if (Array.isArray(rawOrder)) for (let slug of rawOrder) typeof slug == "string" && (slug === "config" || presets.some((preset) => preset.slug === slug) || custom.some((theme) => theme.slug === slug)) && !order.includes(slug) && order.push(slug);
|
|
534
|
+
let rawActive = Reflect.get(value, "active");
|
|
535
|
+
return {
|
|
536
|
+
active: typeof rawActive == "string" ? rawActive : null,
|
|
537
|
+
custom,
|
|
538
|
+
removed,
|
|
539
|
+
order
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
function readLegacyState() {
|
|
543
|
+
let raw = localStorage.getItem(LEGACY_STORAGE_KEY);
|
|
544
|
+
if (!raw) return null;
|
|
545
|
+
let options = sanitizeOptions(JSON.parse(raw));
|
|
546
|
+
if (!options) return null;
|
|
547
|
+
let theme = createCustomTheme("Draft theme", options);
|
|
548
|
+
return {
|
|
549
|
+
active: theme.slug,
|
|
550
|
+
custom: [theme],
|
|
551
|
+
removed: [],
|
|
552
|
+
order: []
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Restores the themer state from localStorage, so the user's themes survive
|
|
557
|
+
* studio reloads. Falls back to the initial state when nothing usable is
|
|
558
|
+
* stored.
|
|
559
|
+
*
|
|
560
|
+
* @internal
|
|
561
|
+
*/
|
|
562
|
+
function readStoredState() {
|
|
563
|
+
try {
|
|
564
|
+
if (typeof localStorage > "u") return initialThemerState;
|
|
565
|
+
let raw = localStorage.getItem(STORAGE_KEY);
|
|
566
|
+
return raw ? sanitizeState(JSON.parse(raw)) : readLegacyState() ?? initialThemerState;
|
|
567
|
+
} catch {
|
|
568
|
+
return initialThemerState;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/** @internal */
|
|
572
|
+
function writeStoredState(state) {
|
|
573
|
+
try {
|
|
574
|
+
if (typeof localStorage > "u") return;
|
|
575
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(state)), localStorage.removeItem(LEGACY_STORAGE_KEY);
|
|
576
|
+
} catch {}
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* How long the layout's motions — the panel's and the split preview's, and
|
|
580
|
+
* the cross-fade to another theme — are taken to last when the layout never
|
|
581
|
+
* says: it reports every view transition it starts, so this only ends a
|
|
582
|
+
* motion that has none (reduced motion, a browser without view transitions),
|
|
583
|
+
* where the `moving` and `switching` tags have nothing to affect. Generous,
|
|
584
|
+
* so that it never runs out before a slow deferred commit starts the real one.
|
|
585
|
+
*
|
|
586
|
+
* @internal
|
|
587
|
+
*/
|
|
588
|
+
const MOTION_DURATION = 2e3;
|
|
589
|
+
function themesOf(context) {
|
|
590
|
+
return resolveThemes(context, context.baseOptions);
|
|
591
|
+
}
|
|
592
|
+
function revokeObjectUrl(url) {
|
|
593
|
+
typeof URL < "u" && typeof URL.revokeObjectURL == "function" && URL.revokeObjectURL(url);
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* The state of the themer tool: three parallel regions, whether the
|
|
597
|
+
* `sidebar` is open, which `flow` it is in — picking a theme from the `list`,
|
|
598
|
+
* `edit`ing one of the user's own themes, or restoring `removed` ones — and
|
|
599
|
+
* whether the `preview` shows the Studio once (`single`) or in light and dark
|
|
600
|
+
* side by side (`split`). The context carries the persisted state (the
|
|
601
|
+
* applied theme, the user's themes and what was removed) alongside what the
|
|
602
|
+
* flows need.
|
|
603
|
+
*
|
|
604
|
+
* The sidebar and the preview pass through `opening`/`closing` and
|
|
605
|
+
* `splitting`/`unsplitting` on their way, states tagged for the UI: `panel`
|
|
606
|
+
* and `split` say what shows (the panel from `opening` on, the split copy from
|
|
607
|
+
* `splitting` on), `moving` that the Studio is about to give way or take room
|
|
608
|
+
* — which is when, and only when, the layout lets a view transition animate
|
|
609
|
+
* it. A fourth region, `theme`, is `switching` (its tag) right after an event
|
|
610
|
+
* that applies another theme — picking one, adding, duplicating, importing or
|
|
611
|
+
* editing one, removing or deleting the applied one — when the layout
|
|
612
|
+
* cross-fades to it; edits to the applied theme's colors are not switches,
|
|
613
|
+
* they follow the pointer. These states last until the layout reports its
|
|
614
|
+
* transition under way — every boundary reports, the panel's and the copy's
|
|
615
|
+
* entering and leaving as much as the Studio's morphing — or for
|
|
616
|
+
* `MOTION_DURATION` when nothing animates.
|
|
617
|
+
*
|
|
618
|
+
* Theme operations are handled in every flow, and the flows leave on their
|
|
619
|
+
* own when they lose their subject: the editor when its theme is removed or
|
|
620
|
+
* deleted, and the removed view when its last theme is restored or deleted.
|
|
621
|
+
*
|
|
622
|
+
* @internal
|
|
623
|
+
*/
|
|
624
|
+
const themerMachine = setup({
|
|
625
|
+
types: {
|
|
626
|
+
context: {},
|
|
627
|
+
events: {},
|
|
628
|
+
input: {}
|
|
629
|
+
},
|
|
630
|
+
guards: {
|
|
631
|
+
isCustomTheme: ({ context }, params) => themesOf(context).themes.some((theme) => theme.slug === params.slug && theme.source === "custom"),
|
|
632
|
+
isEditingListedTheme: ({ context }) => context.editing !== null && themesOf(context).themes.some((theme) => theme.slug === context.editing?.slug && theme.source === "custom"),
|
|
633
|
+
hasRemovedThemes: ({ context }) => themesOf(context).removed.length > 0,
|
|
634
|
+
sidebarShown: or([stateIn({ sidebar: "opening" }), stateIn({ sidebar: "open" })]),
|
|
635
|
+
isAnotherTheme: ({ context }, params) => params.slug !== (context.active ?? "config"),
|
|
636
|
+
isAppliedTheme: ({ context }, params) => params.slug === context.active,
|
|
637
|
+
hasOptions: (_, params) => params.options !== void 0
|
|
638
|
+
},
|
|
639
|
+
actions: {
|
|
640
|
+
persist: ({ context }) => {
|
|
641
|
+
let { active, custom, removed, order } = context;
|
|
642
|
+
writeStoredState({
|
|
643
|
+
active,
|
|
644
|
+
custom,
|
|
645
|
+
removed,
|
|
646
|
+
order
|
|
647
|
+
});
|
|
648
|
+
},
|
|
649
|
+
revokeImage: (_, params) => {
|
|
650
|
+
params.url && revokeObjectUrl(params.url);
|
|
651
|
+
},
|
|
652
|
+
pick: assign((_, params) => ({ active: params.slug === "config" ? null : params.slug })),
|
|
653
|
+
add: assign(({ context }, params) => {
|
|
654
|
+
let theme = createCustomTheme(params.title ?? "Untitled theme", params.options ?? themesOf(context).active.options, params.palette);
|
|
655
|
+
return {
|
|
656
|
+
active: theme.slug,
|
|
657
|
+
custom: [...context.custom, theme],
|
|
658
|
+
editing: {
|
|
659
|
+
slug: theme.slug,
|
|
660
|
+
focusTitle: params.title === void 0
|
|
661
|
+
},
|
|
662
|
+
images: params.imageUrl ? {
|
|
663
|
+
...context.images,
|
|
664
|
+
[theme.slug]: params.imageUrl
|
|
665
|
+
} : context.images
|
|
666
|
+
};
|
|
667
|
+
}),
|
|
668
|
+
import: assign(({ context }, params) => {
|
|
669
|
+
let theme = createCustomTheme(params.title, params.options);
|
|
670
|
+
return {
|
|
671
|
+
active: theme.slug,
|
|
672
|
+
custom: [...context.custom, theme]
|
|
673
|
+
};
|
|
674
|
+
}),
|
|
675
|
+
duplicate: assign(({ context }, params) => {
|
|
676
|
+
let { themes, removed } = themesOf(context), source = [...themes, ...removed].find((theme) => theme.slug === params.slug);
|
|
677
|
+
if (!source) return {};
|
|
678
|
+
let theme = createCustomTheme(duplicateTitle(source.title), source.options, source.palette);
|
|
679
|
+
return {
|
|
680
|
+
active: theme.slug,
|
|
681
|
+
custom: [...context.custom, theme],
|
|
682
|
+
editing: {
|
|
683
|
+
slug: theme.slug,
|
|
684
|
+
focusTitle: !0
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
}),
|
|
688
|
+
startEditing: assign((_, params) => ({
|
|
689
|
+
active: params.slug,
|
|
690
|
+
editing: {
|
|
691
|
+
slug: params.slug,
|
|
692
|
+
focusTitle: !1
|
|
693
|
+
}
|
|
694
|
+
})),
|
|
695
|
+
stopEditing: assign({ editing: null }),
|
|
696
|
+
update: assign(({ context }, params) => ({
|
|
697
|
+
custom: context.custom.map((theme) => theme.slug === params.slug ? {
|
|
698
|
+
...theme,
|
|
699
|
+
title: params.title ?? theme.title,
|
|
700
|
+
options: params.options ?? theme.options,
|
|
701
|
+
...params.palette ? { palette: params.palette } : {}
|
|
702
|
+
} : theme),
|
|
703
|
+
images: params.imageUrl && context.custom.some((theme) => theme.slug === params.slug) ? {
|
|
704
|
+
...context.images,
|
|
705
|
+
[params.slug]: params.imageUrl
|
|
706
|
+
} : context.images
|
|
707
|
+
})),
|
|
708
|
+
remove: assign(({ context }, params) => params.slug === "config" || context.removed.includes(params.slug) ? {} : {
|
|
709
|
+
active: context.active === params.slug ? null : context.active,
|
|
710
|
+
removed: [...context.removed, params.slug]
|
|
711
|
+
}),
|
|
712
|
+
restore: assign(({ context }, params) => ({ removed: context.removed.filter((slug) => slug !== params.slug) })),
|
|
713
|
+
reorder: assign(({ context }, params) => {
|
|
714
|
+
let listed = new Set(themesOf(context).themes.map((theme) => theme.slug)), order = params.order.filter((slug) => listed.has(slug));
|
|
715
|
+
return { order: [...order, ...context.order.filter((slug) => !order.includes(slug))] };
|
|
716
|
+
}),
|
|
717
|
+
delete: assign(({ context }, params) => {
|
|
718
|
+
if (!context.custom.some((theme) => theme.slug === params.slug)) return {};
|
|
719
|
+
let { [params.slug]: _deleted, ...images } = context.images;
|
|
720
|
+
return {
|
|
721
|
+
active: context.active === params.slug ? null : context.active,
|
|
722
|
+
custom: context.custom.filter((theme) => theme.slug !== params.slug),
|
|
723
|
+
removed: context.removed.filter((slug) => slug !== params.slug),
|
|
724
|
+
images
|
|
725
|
+
};
|
|
726
|
+
})
|
|
727
|
+
}
|
|
728
|
+
}).createMachine({
|
|
729
|
+
id: "themer",
|
|
730
|
+
context: ({ input }) => ({
|
|
731
|
+
...input.stored,
|
|
732
|
+
baseOptions: input.baseOptions,
|
|
733
|
+
editing: null,
|
|
734
|
+
images: {}
|
|
735
|
+
}),
|
|
736
|
+
entry: "persist",
|
|
737
|
+
type: "parallel",
|
|
738
|
+
states: {
|
|
739
|
+
sidebar: {
|
|
740
|
+
initial: "closed",
|
|
741
|
+
states: {
|
|
742
|
+
closed: { on: { "sidebar.toggle": "opening" } },
|
|
743
|
+
opening: {
|
|
744
|
+
tags: ["panel", "moving"],
|
|
745
|
+
after: { [MOTION_DURATION]: "open" },
|
|
746
|
+
on: {
|
|
747
|
+
"layout.transitioned": "open",
|
|
748
|
+
"sidebar.toggle": "closing",
|
|
749
|
+
"sidebar.close": "closing"
|
|
750
|
+
}
|
|
751
|
+
},
|
|
752
|
+
open: {
|
|
753
|
+
tags: ["panel"],
|
|
754
|
+
on: {
|
|
755
|
+
"sidebar.toggle": "closing",
|
|
756
|
+
"sidebar.close": "closing"
|
|
757
|
+
}
|
|
758
|
+
},
|
|
759
|
+
closing: {
|
|
760
|
+
tags: ["moving"],
|
|
761
|
+
after: { [MOTION_DURATION]: "closed" },
|
|
762
|
+
on: {
|
|
763
|
+
"layout.transitioned": "closed",
|
|
764
|
+
"sidebar.toggle": "opening"
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
},
|
|
769
|
+
preview: {
|
|
770
|
+
initial: "single",
|
|
771
|
+
states: {
|
|
772
|
+
single: { on: { "preview.toggle": "splitting" } },
|
|
773
|
+
splitting: {
|
|
774
|
+
tags: ["split", "moving"],
|
|
775
|
+
after: { [MOTION_DURATION]: "split" },
|
|
776
|
+
on: {
|
|
777
|
+
"layout.transitioned": "split",
|
|
778
|
+
"preview.toggle": "unsplitting",
|
|
779
|
+
"sidebar.close": "unsplitting",
|
|
780
|
+
"sidebar.toggle": {
|
|
781
|
+
guard: "sidebarShown",
|
|
782
|
+
target: "unsplitting"
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
},
|
|
786
|
+
split: {
|
|
787
|
+
tags: ["split"],
|
|
788
|
+
on: {
|
|
789
|
+
"preview.toggle": "unsplitting",
|
|
790
|
+
"sidebar.close": "unsplitting",
|
|
791
|
+
"sidebar.toggle": {
|
|
792
|
+
guard: "sidebarShown",
|
|
793
|
+
target: "unsplitting"
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
},
|
|
797
|
+
unsplitting: {
|
|
798
|
+
tags: ["moving"],
|
|
799
|
+
after: { [MOTION_DURATION]: "single" },
|
|
800
|
+
on: {
|
|
801
|
+
"layout.transitioned": "single",
|
|
802
|
+
"preview.toggle": "splitting"
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
},
|
|
807
|
+
theme: {
|
|
808
|
+
initial: "applied",
|
|
809
|
+
states: {
|
|
810
|
+
applied: { on: {
|
|
811
|
+
"theme.pick": {
|
|
812
|
+
guard: {
|
|
813
|
+
type: "isAnotherTheme",
|
|
814
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
815
|
+
},
|
|
816
|
+
target: "switching"
|
|
817
|
+
},
|
|
818
|
+
"theme.edit": {
|
|
819
|
+
guard: {
|
|
820
|
+
type: "isAnotherTheme",
|
|
821
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
822
|
+
},
|
|
823
|
+
target: "switching"
|
|
824
|
+
},
|
|
825
|
+
"theme.duplicate": {
|
|
826
|
+
guard: {
|
|
827
|
+
type: "isAnotherTheme",
|
|
828
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
829
|
+
},
|
|
830
|
+
target: "switching"
|
|
831
|
+
},
|
|
832
|
+
"theme.remove": {
|
|
833
|
+
guard: {
|
|
834
|
+
type: "isAppliedTheme",
|
|
835
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
836
|
+
},
|
|
837
|
+
target: "switching"
|
|
838
|
+
},
|
|
839
|
+
"theme.delete": {
|
|
840
|
+
guard: {
|
|
841
|
+
type: "isAppliedTheme",
|
|
842
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
843
|
+
},
|
|
844
|
+
target: "switching"
|
|
845
|
+
},
|
|
846
|
+
"theme.add": {
|
|
847
|
+
guard: {
|
|
848
|
+
type: "hasOptions",
|
|
849
|
+
params: ({ event }) => ({ options: event.options })
|
|
850
|
+
},
|
|
851
|
+
target: "switching"
|
|
852
|
+
},
|
|
853
|
+
"theme.import": "switching"
|
|
854
|
+
} },
|
|
855
|
+
switching: {
|
|
856
|
+
tags: ["switching"],
|
|
857
|
+
after: { [MOTION_DURATION]: "applied" },
|
|
858
|
+
on: {
|
|
859
|
+
"layout.transitioned": "applied",
|
|
860
|
+
"theme.pick": {
|
|
861
|
+
guard: {
|
|
862
|
+
type: "isAnotherTheme",
|
|
863
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
864
|
+
},
|
|
865
|
+
target: "switching",
|
|
866
|
+
reenter: !0
|
|
867
|
+
},
|
|
868
|
+
"theme.edit": {
|
|
869
|
+
guard: {
|
|
870
|
+
type: "isAnotherTheme",
|
|
871
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
872
|
+
},
|
|
873
|
+
target: "switching",
|
|
874
|
+
reenter: !0
|
|
875
|
+
},
|
|
876
|
+
"theme.duplicate": {
|
|
877
|
+
guard: {
|
|
878
|
+
type: "isAnotherTheme",
|
|
879
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
880
|
+
},
|
|
881
|
+
target: "switching",
|
|
882
|
+
reenter: !0
|
|
883
|
+
},
|
|
884
|
+
"theme.remove": {
|
|
885
|
+
guard: {
|
|
886
|
+
type: "isAppliedTheme",
|
|
887
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
888
|
+
},
|
|
889
|
+
target: "switching",
|
|
890
|
+
reenter: !0
|
|
891
|
+
},
|
|
892
|
+
"theme.delete": {
|
|
893
|
+
guard: {
|
|
894
|
+
type: "isAppliedTheme",
|
|
895
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
896
|
+
},
|
|
897
|
+
target: "switching",
|
|
898
|
+
reenter: !0
|
|
899
|
+
},
|
|
900
|
+
"theme.add": {
|
|
901
|
+
guard: {
|
|
902
|
+
type: "hasOptions",
|
|
903
|
+
params: ({ event }) => ({ options: event.options })
|
|
904
|
+
},
|
|
905
|
+
target: "switching",
|
|
906
|
+
reenter: !0
|
|
907
|
+
},
|
|
908
|
+
"theme.import": {
|
|
909
|
+
target: "switching",
|
|
910
|
+
reenter: !0
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
},
|
|
916
|
+
flow: {
|
|
917
|
+
initial: "list",
|
|
918
|
+
on: {
|
|
919
|
+
"theme.pick": { actions: [{
|
|
920
|
+
type: "pick",
|
|
921
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
922
|
+
}, "persist"] },
|
|
923
|
+
"theme.add": {
|
|
924
|
+
target: ".edit",
|
|
925
|
+
actions: [{
|
|
926
|
+
type: "add",
|
|
927
|
+
params: ({ event }) => ({
|
|
928
|
+
title: event.title,
|
|
929
|
+
options: event.options,
|
|
930
|
+
palette: event.palette,
|
|
931
|
+
imageUrl: event.imageUrl
|
|
932
|
+
})
|
|
933
|
+
}, "persist"]
|
|
934
|
+
},
|
|
935
|
+
"theme.duplicate": {
|
|
936
|
+
target: ".edit",
|
|
937
|
+
actions: [{
|
|
938
|
+
type: "duplicate",
|
|
939
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
940
|
+
}, "persist"]
|
|
941
|
+
},
|
|
942
|
+
"theme.edit": {
|
|
943
|
+
target: ".edit",
|
|
944
|
+
guard: {
|
|
945
|
+
type: "isCustomTheme",
|
|
946
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
947
|
+
},
|
|
948
|
+
actions: [{
|
|
949
|
+
type: "startEditing",
|
|
950
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
951
|
+
}, "persist"]
|
|
952
|
+
},
|
|
953
|
+
"theme.update": { actions: [
|
|
954
|
+
{
|
|
955
|
+
type: "revokeImage",
|
|
956
|
+
params: ({ context, event }) => ({ url: event.imageUrl ? context.images[event.slug] : void 0 })
|
|
957
|
+
},
|
|
958
|
+
{
|
|
959
|
+
type: "update",
|
|
960
|
+
params: ({ event }) => ({
|
|
961
|
+
slug: event.slug,
|
|
962
|
+
title: event.title,
|
|
963
|
+
options: event.options,
|
|
964
|
+
palette: event.palette,
|
|
965
|
+
imageUrl: event.imageUrl
|
|
966
|
+
})
|
|
967
|
+
},
|
|
968
|
+
"persist"
|
|
969
|
+
] },
|
|
970
|
+
"theme.remove": { actions: [{
|
|
971
|
+
type: "remove",
|
|
972
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
973
|
+
}, "persist"] },
|
|
974
|
+
"theme.restore": { actions: [{
|
|
975
|
+
type: "restore",
|
|
976
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
977
|
+
}, "persist"] },
|
|
978
|
+
"theme.delete": { actions: [
|
|
979
|
+
{
|
|
980
|
+
type: "revokeImage",
|
|
981
|
+
params: ({ context, event }) => ({ url: context.images[event.slug] })
|
|
982
|
+
},
|
|
983
|
+
{
|
|
984
|
+
type: "delete",
|
|
985
|
+
params: ({ event }) => ({ slug: event.slug })
|
|
986
|
+
},
|
|
987
|
+
"persist"
|
|
988
|
+
] },
|
|
989
|
+
"theme.reorder": { actions: [{
|
|
990
|
+
type: "reorder",
|
|
991
|
+
params: ({ event }) => ({ order: event.order })
|
|
992
|
+
}, "persist"] },
|
|
993
|
+
"theme.import": { actions: [{
|
|
994
|
+
type: "import",
|
|
995
|
+
params: ({ event }) => ({
|
|
996
|
+
title: event.title,
|
|
997
|
+
options: event.options
|
|
998
|
+
})
|
|
999
|
+
}, "persist"] },
|
|
1000
|
+
"flow.list": ".list",
|
|
1001
|
+
"flow.removed": {
|
|
1002
|
+
target: ".removed",
|
|
1003
|
+
guard: "hasRemovedThemes"
|
|
1004
|
+
}
|
|
1005
|
+
},
|
|
1006
|
+
states: {
|
|
1007
|
+
list: {},
|
|
1008
|
+
edit: {
|
|
1009
|
+
exit: "stopEditing",
|
|
1010
|
+
always: {
|
|
1011
|
+
guard: not("isEditingListedTheme"),
|
|
1012
|
+
target: "list"
|
|
1013
|
+
}
|
|
1014
|
+
},
|
|
1015
|
+
removed: { always: {
|
|
1016
|
+
guard: not("hasRemovedThemes"),
|
|
1017
|
+
target: "list"
|
|
1018
|
+
} }
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
});
|
|
1023
|
+
/** The part of the machine context that is persisted between sessions @internal */
|
|
1024
|
+
function selectStoredState(snapshot) {
|
|
1025
|
+
let { active, custom, removed, order } = snapshot.context;
|
|
852
1026
|
return {
|
|
853
|
-
|
|
854
|
-
|
|
1027
|
+
active,
|
|
1028
|
+
custom,
|
|
1029
|
+
removed,
|
|
1030
|
+
order
|
|
855
1031
|
};
|
|
856
1032
|
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
1033
|
+
var root$2 = "_1o50s7t0";
|
|
1034
|
+
const ScrollAreaInsetContext = createContext({
|
|
1035
|
+
left: 0,
|
|
1036
|
+
right: 0,
|
|
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;
|
|
877
1091
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
light: .08,
|
|
881
|
-
dark: .3
|
|
882
|
-
}, BUTTON_LABEL = {
|
|
883
|
-
light: "#ffffff",
|
|
884
|
-
dark: "#000000"
|
|
885
|
-
}, ACCENT_LIGHTNESS_STEP = .01;
|
|
886
|
-
function contrastRatio(a, b) {
|
|
887
|
-
let [lighter, darker] = [relativeLuminance(a), relativeLuminance(b)].sort((x, y) => y - x);
|
|
888
|
-
return (lighter + .05) / (darker + .05);
|
|
1092
|
+
function _temp$8() {
|
|
1093
|
+
return lastGutter;
|
|
889
1094
|
}
|
|
890
1095
|
/**
|
|
891
|
-
*
|
|
892
|
-
*
|
|
893
|
-
*
|
|
894
|
-
* hue and saturation allow. Accents that already pass come back untouched.
|
|
1096
|
+
* Content that runs edge to edge in a scroll area — up to the scrollbar —
|
|
1097
|
+
* instead of sitting inside its padding. It goes last: it runs down to the
|
|
1098
|
+
* bottom edge too.
|
|
895
1099
|
*
|
|
896
1100
|
* @internal
|
|
897
1101
|
*/
|
|
898
|
-
function
|
|
899
|
-
let
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
}
|
|
911
|
-
/** The accent a variant gives a scheme, made readable on a button */
|
|
912
|
-
function variantAccent(palette, variant, scheme) {
|
|
913
|
-
let swatch = (variant ? palette[variant] : null) ?? palette.vibrant ?? (scheme === "light" ? palette.darkVibrant : palette.lightVibrant) ?? palette.dominant;
|
|
914
|
-
return swatch === null ? null : readableAccent(swatch, scheme);
|
|
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;
|
|
915
1114
|
}
|
|
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%"];
|
|
916
1141
|
/**
|
|
917
|
-
*
|
|
918
|
-
*
|
|
919
|
-
*
|
|
920
|
-
*
|
|
921
|
-
*
|
|
922
|
-
*
|
|
923
|
-
*
|
|
1142
|
+
* A tiny, lo-fi mockup of a Studio in the given theme — the navbar over the
|
|
1143
|
+
* structure tool with a list of documents on the left and a document form on
|
|
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.
|
|
924
1149
|
*
|
|
925
1150
|
* @internal
|
|
926
1151
|
*/
|
|
927
|
-
function
|
|
928
|
-
let
|
|
929
|
-
|
|
1152
|
+
function ThemePreview(props) {
|
|
1153
|
+
let $ = c(11), { options } = props, t0;
|
|
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;
|
|
930
1239
|
}
|
|
931
|
-
function
|
|
932
|
-
|
|
933
|
-
|
|
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);
|
|
934
1248
|
}
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
...options[scheme],
|
|
946
|
-
...derived[scheme]
|
|
947
|
-
});
|
|
948
|
-
return next;
|
|
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);
|
|
949
1259
|
}
|
|
950
1260
|
/**
|
|
951
|
-
* The
|
|
952
|
-
*
|
|
1261
|
+
* The floating preview card of a theme: the lo-fi studio mockup on a raised,
|
|
1262
|
+
* rounded card, like the appearance previews in macOS System Settings.
|
|
953
1263
|
*
|
|
954
1264
|
* @internal
|
|
955
1265
|
*/
|
|
956
|
-
function
|
|
957
|
-
let
|
|
958
|
-
return
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
function interest(hex) {
|
|
965
|
-
let { s, l } = hexToHsl(hex);
|
|
966
|
-
return .1 + .55 * s + .35 * (1 - Math.abs(l - .5) * 2);
|
|
1266
|
+
function ThemeThumbnail(props) {
|
|
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;
|
|
967
1274
|
}
|
|
1275
|
+
var removedThumbnail = "_1vdkp0v0";
|
|
968
1276
|
/**
|
|
969
|
-
*
|
|
970
|
-
*
|
|
971
|
-
*
|
|
1277
|
+
* The flow for restoring removed themes: the presets and custom themes the
|
|
1278
|
+
* user took out of the list, each with a button to put it back — and, for
|
|
1279
|
+
* custom themes, one to delete it for good. The machine leaves the flow once
|
|
1280
|
+
* the last one is gone.
|
|
972
1281
|
*
|
|
973
1282
|
* @internal
|
|
974
1283
|
*/
|
|
975
|
-
function
|
|
976
|
-
let
|
|
977
|
-
if (
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1284
|
+
function RemovedThemes() {
|
|
1285
|
+
let $ = c(7), { removed, send } = useThemer(), t0;
|
|
1286
|
+
if ($[0] !== removed || $[1] !== send) {
|
|
1287
|
+
let t1;
|
|
1288
|
+
$[3] === send ? t1 = $[4] : (t1 = (theme) => /* @__PURE__ */ jsxs(Stack, {
|
|
1289
|
+
gap: 3,
|
|
1290
|
+
children: [
|
|
1291
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1292
|
+
className: removedThumbnail,
|
|
1293
|
+
children: /* @__PURE__ */ jsx(ThemeThumbnail, { options: theme.options })
|
|
1294
|
+
}),
|
|
1295
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1296
|
+
align: "center",
|
|
1297
|
+
size: 1,
|
|
1298
|
+
textOverflow: "ellipsis",
|
|
1299
|
+
children: displayTitle(theme.title)
|
|
1300
|
+
}),
|
|
1301
|
+
/* @__PURE__ */ jsxs(Flex, {
|
|
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;
|
|
985
1334
|
}
|
|
986
1335
|
/**
|
|
987
|
-
* A button with an animated tooltip instead of a `title` attribute
|
|
988
|
-
* buttons
|
|
1336
|
+
* A button with an animated tooltip instead of a `title` attribute, sized like
|
|
1337
|
+
* the Studio's own buttons (`padding` and `gap` of 2). Icon-only buttons take
|
|
1338
|
+
* the tooltip as their accessible name.
|
|
989
1339
|
*
|
|
990
1340
|
* @internal
|
|
991
1341
|
*/
|
|
@@ -1000,6 +1350,8 @@ function TooltipButton(props) {
|
|
|
1000
1350
|
let t1 = buttonProps.text === void 0 ? tooltip : void 0, t2;
|
|
1001
1351
|
$[5] !== buttonProps || $[6] !== t1 ? (t2 = /* @__PURE__ */ jsx(Button, {
|
|
1002
1352
|
"aria-label": t1,
|
|
1353
|
+
gap: 2,
|
|
1354
|
+
padding: 2,
|
|
1003
1355
|
...buttonProps
|
|
1004
1356
|
}), $[5] = buttonProps, $[6] = t1, $[7] = t2) : t2 = $[7];
|
|
1005
1357
|
let t3;
|
|
@@ -1099,7 +1451,7 @@ function useImagePalette(onPalette) {
|
|
|
1099
1451
|
setBusy(!0);
|
|
1100
1452
|
let isCurrent = () => mountedRef.current && latestPickRef.current === pick, extract = async () => {
|
|
1101
1453
|
let palette = extractImagePalette(await readImagePixels(file));
|
|
1102
|
-
isCurrent() && (Object.values(palette).every(_temp$
|
|
1454
|
+
isCurrent() && (Object.values(palette).every(_temp$6) ? toast.push({
|
|
1103
1455
|
status: "warning",
|
|
1104
1456
|
title: "Found no colors in that image"
|
|
1105
1457
|
}) : onPaletteRef.current(palette, file));
|
|
@@ -1119,7 +1471,7 @@ function useImagePalette(onPalette) {
|
|
|
1119
1471
|
pickImage
|
|
1120
1472
|
}, $[7] = busy, $[8] = pickImage, $[9] = t5) : t5 = $[9], t5;
|
|
1121
1473
|
}
|
|
1122
|
-
function _temp$
|
|
1474
|
+
function _temp$6(swatch) {
|
|
1123
1475
|
return swatch === null;
|
|
1124
1476
|
}
|
|
1125
1477
|
var paletteGrid = "_17s80v42", tile = "_17s80v44", variantButton = "_17s80v46", variantFrame = "_17s80v47", variantRow = "_17s80v45", variantScale = "var(--_17s80v41)", variantScaleBox = "_17s80v48", variantWidth = "var(--_17s80v40)";
|
|
@@ -1310,7 +1662,7 @@ function ImagePaletteSection(props) {
|
|
|
1310
1662
|
border: !0,
|
|
1311
1663
|
onDragEnter: t4,
|
|
1312
1664
|
onDragLeave: t5,
|
|
1313
|
-
onDragOver: _temp$
|
|
1665
|
+
onDragOver: _temp$5,
|
|
1314
1666
|
onDrop: t6,
|
|
1315
1667
|
padding: 3,
|
|
1316
1668
|
radius: 3,
|
|
@@ -1318,7 +1670,7 @@ function ImagePaletteSection(props) {
|
|
|
1318
1670
|
children: t12
|
|
1319
1671
|
}), $[34] = t12, $[35] = t6, $[36] = t7, $[37] = t13) : t13 = $[37], t13;
|
|
1320
1672
|
}
|
|
1321
|
-
function _temp$
|
|
1673
|
+
function _temp$5(event_0) {
|
|
1322
1674
|
event_0.preventDefault();
|
|
1323
1675
|
}
|
|
1324
1676
|
var colorRowText = "os3rj2", range = "os3rj1", swatch = "os3rj0";
|
|
@@ -1442,7 +1794,7 @@ function ThemeEditorForm(props) {
|
|
|
1442
1794
|
}), $[9] = t4) : t4 = $[9];
|
|
1443
1795
|
let t5;
|
|
1444
1796
|
$[10] === onTitleChange ? t5 = $[11] : (t5 = (event) => onTitleChange(event.currentTarget.value), $[10] = onTitleChange, $[11] = t5);
|
|
1445
|
-
let t6 = focusTitle ? _temp$
|
|
1797
|
+
let t6 = focusTitle ? _temp$4 : void 0, t7;
|
|
1446
1798
|
$[12] !== focusTitle || $[13] !== t5 || $[14] !== t6 || $[15] !== title ? (t7 = /* @__PURE__ */ jsxs(Stack, {
|
|
1447
1799
|
gap: 2,
|
|
1448
1800
|
children: [t4, /* @__PURE__ */ jsx(TextInput, {
|
|
@@ -1505,6 +1857,7 @@ function ThemeEditorForm(props) {
|
|
|
1505
1857
|
$[42] === onDone ? t14 = $[43] : (t14 = /* @__PURE__ */ jsx(Button, {
|
|
1506
1858
|
mode: "ghost",
|
|
1507
1859
|
onClick: onDone,
|
|
1860
|
+
padding: 2,
|
|
1508
1861
|
text: "Done"
|
|
1509
1862
|
}), $[42] = onDone, $[43] = t14);
|
|
1510
1863
|
let t15;
|
|
@@ -1523,7 +1876,7 @@ function ThemeEditorForm(props) {
|
|
|
1523
1876
|
let t16;
|
|
1524
1877
|
return $[47] !== t11 || $[48] !== t15 ? (t16 = /* @__PURE__ */ jsxs(Fragment, { children: [t11, t15] }), $[47] = t11, $[48] = t15, $[49] = t16) : t16 = $[49], t16;
|
|
1525
1878
|
}
|
|
1526
|
-
function _temp$
|
|
1879
|
+
function _temp$4(event_0) {
|
|
1527
1880
|
return event_0.currentTarget.select();
|
|
1528
1881
|
}
|
|
1529
1882
|
/**
|
|
@@ -1675,59 +2028,350 @@ function SchemeCard(props) {
|
|
|
1675
2028
|
})
|
|
1676
2029
|
}), $[60] = t10, $[61] = t15, $[62] = t21, $[63] = t31, $[64] = t5, $[65] = t32) : t32 = $[65], t32;
|
|
1677
2030
|
}
|
|
1678
|
-
/** One color of a scheme: a swatch with its value and an optional reset button */
|
|
1679
|
-
function ColorRow(props) {
|
|
1680
|
-
let $ = c(23), { adjusted, auto, autoLabel: t0, label, onChange, onClear, title, value } = props, autoLabel = t0 === void 0 ? "auto" : t0, t1;
|
|
1681
|
-
$[0] === onChange ? t1 = $[1] : (t1 = (event) => onChange(event.currentTarget.value), $[0] = onChange, $[1] = t1);
|
|
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);
|
|
1682
2294
|
let t2;
|
|
1683
|
-
$[
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
value
|
|
1689
|
-
}), $[2] = label, $[3] = t1, $[4] = value, $[5] = t2) : t2 = $[5];
|
|
1690
|
-
let t3;
|
|
1691
|
-
$[6] === title ? t3 = $[7] : (t3 = /* @__PURE__ */ jsx(Text, {
|
|
1692
|
-
size: 1,
|
|
1693
|
-
children: title
|
|
1694
|
-
}), $[6] = title, $[7] = t3);
|
|
1695
|
-
let t4 = adjusted !== void 0 && adjusted !== value ? ` → ${adjusted}` : "", t5 = auto ? ` · ${autoLabel}` : "", t6;
|
|
1696
|
-
$[8] !== t4 || $[9] !== t5 || $[10] !== value ? (t6 = /* @__PURE__ */ jsxs(Text, {
|
|
1697
|
-
muted: !0,
|
|
1698
|
-
size: 0,
|
|
1699
|
-
textOverflow: "ellipsis",
|
|
1700
|
-
children: [
|
|
1701
|
-
value,
|
|
1702
|
-
t4,
|
|
1703
|
-
t5
|
|
1704
|
-
]
|
|
1705
|
-
}), $[8] = t4, $[9] = t5, $[10] = value, $[11] = t6) : t6 = $[11];
|
|
1706
|
-
let t7;
|
|
1707
|
-
$[12] !== t3 || $[13] !== t6 ? (t7 = /* @__PURE__ */ jsxs(Stack, {
|
|
1708
|
-
className: colorRowText,
|
|
1709
|
-
flex: 1,
|
|
1710
|
-
gap: 2,
|
|
1711
|
-
children: [t3, t6]
|
|
1712
|
-
}), $[12] = t3, $[13] = t6, $[14] = t7) : t7 = $[14];
|
|
1713
|
-
let t8;
|
|
1714
|
-
$[15] !== auto || $[16] !== autoLabel || $[17] !== onClear ? (t8 = onClear && !auto && /* @__PURE__ */ jsx(TooltipButton, {
|
|
1715
|
-
icon: ResetIcon,
|
|
1716
|
-
mode: "bleed",
|
|
1717
|
-
onClick: onClear,
|
|
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,
|
|
1718
2300
|
padding: 2,
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
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, {
|
|
1724
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,
|
|
1725
2353
|
children: [
|
|
1726
|
-
t2,
|
|
1727
2354
|
t7,
|
|
1728
|
-
|
|
2355
|
+
t11,
|
|
2356
|
+
t12
|
|
1729
2357
|
]
|
|
1730
|
-
}), $[
|
|
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;
|
|
1731
2375
|
}
|
|
1732
2376
|
var frame$1 = "_1wsn7882", menuCard = "_1wsn7884", menuSlot = "_1wsn7883", pickButton = "_1wsn7881", root = "_1wsn7880";
|
|
1733
2377
|
/**
|
|
@@ -1751,13 +2395,13 @@ function scrollRefIntoViewIfNeeded(node) {
|
|
|
1751
2395
|
* @internal
|
|
1752
2396
|
*/
|
|
1753
2397
|
function ThemeCard(props) {
|
|
1754
|
-
let $ = c(
|
|
2398
|
+
let $ = c(85), { active, theme } = props, { themes, send } = useThemer(), { copyTheme } = useThemeCodes(), { scheme } = useRootTheme(), [menuOpen, setMenuOpen] = useState(!1), dragControls = useDragControls(), dragged = useRef(!1), t0;
|
|
1755
2399
|
$[0] === theme.title ? t0 = $[1] : (t0 = displayTitle(theme.title), $[0] = theme.title, $[1] = t0);
|
|
1756
2400
|
let title = t0, t1;
|
|
1757
2401
|
$[2] === theme.slug ? t1 = $[3] : (t1 = (candidate) => candidate.slug === theme.slug, $[2] = theme.slug, $[3] = t1);
|
|
1758
2402
|
let index = themes.findIndex(t1), t2;
|
|
1759
2403
|
$[4] !== index || $[5] !== send || $[6] !== themes ? (t2 = (to) => {
|
|
1760
|
-
let order = themes.map(_temp$
|
|
2404
|
+
let order = themes.map(_temp$3);
|
|
1761
2405
|
order.splice(to, 0, ...order.splice(index, 1)), send({
|
|
1762
2406
|
type: "theme.reorder",
|
|
1763
2407
|
order
|
|
@@ -1868,7 +2512,13 @@ function ThemeCard(props) {
|
|
|
1868
2512
|
text: t26
|
|
1869
2513
|
}), $[50] = t25, $[51] = t26, $[52] = t27) : t27 = $[52];
|
|
1870
2514
|
let t28;
|
|
1871
|
-
$[53] !==
|
|
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, {
|
|
1872
2522
|
icon: TrashIcon,
|
|
1873
2523
|
onClick: () => send({
|
|
1874
2524
|
type: "theme.remove",
|
|
@@ -1876,27 +2526,28 @@ function ThemeCard(props) {
|
|
|
1876
2526
|
}),
|
|
1877
2527
|
text: "Remove",
|
|
1878
2528
|
tone: "critical"
|
|
1879
|
-
})] }), $[
|
|
1880
|
-
let
|
|
1881
|
-
$[
|
|
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: [
|
|
1882
2532
|
t19,
|
|
1883
2533
|
t22,
|
|
1884
2534
|
t23,
|
|
1885
2535
|
t24,
|
|
1886
2536
|
t27,
|
|
1887
|
-
t28
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
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 = {
|
|
1893
2544
|
animate: !0,
|
|
1894
2545
|
placement: "bottom-end",
|
|
1895
2546
|
portal: !0,
|
|
1896
2547
|
scheme
|
|
1897
|
-
}, $[
|
|
1898
|
-
let
|
|
1899
|
-
$[
|
|
2548
|
+
}, $[69] = scheme, $[70] = t33);
|
|
2549
|
+
let t34;
|
|
2550
|
+
$[71] !== t15 || $[72] !== t16 || $[73] !== t30 || $[74] !== t33 ? (t34 = /* @__PURE__ */ jsx(Tooltip, {
|
|
1900
2551
|
animate: !0,
|
|
1901
2552
|
content: t13,
|
|
1902
2553
|
placement: "bottom",
|
|
@@ -1908,21 +2559,21 @@ function ThemeCard(props) {
|
|
|
1908
2559
|
children: /* @__PURE__ */ jsx(MenuButton, {
|
|
1909
2560
|
button: t15,
|
|
1910
2561
|
id: t16,
|
|
1911
|
-
menu:
|
|
1912
|
-
onClose:
|
|
1913
|
-
onOpen:
|
|
1914
|
-
popover:
|
|
2562
|
+
menu: t30,
|
|
2563
|
+
onClose: t31,
|
|
2564
|
+
onOpen: t32,
|
|
2565
|
+
popover: t33
|
|
1915
2566
|
})
|
|
1916
2567
|
})
|
|
1917
|
-
}), $[
|
|
1918
|
-
let
|
|
1919
|
-
$[
|
|
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", {
|
|
1920
2571
|
className: menuSlot,
|
|
1921
2572
|
"data-visible": menuOpen,
|
|
1922
|
-
children:
|
|
1923
|
-
}), $[
|
|
1924
|
-
let
|
|
1925
|
-
return $[
|
|
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, {
|
|
1926
2577
|
as: "div",
|
|
1927
2578
|
className: root,
|
|
1928
2579
|
dragControls,
|
|
@@ -1932,10 +2583,10 @@ function ThemeCard(props) {
|
|
|
1932
2583
|
ref: t5,
|
|
1933
2584
|
value: theme.slug,
|
|
1934
2585
|
whileDrag: t6,
|
|
1935
|
-
children: [t12,
|
|
1936
|
-
}), $[
|
|
2586
|
+
children: [t12, t35]
|
|
2587
|
+
}), $[79] = dragControls, $[80] = t12, $[81] = t35, $[82] = t5, $[83] = theme.slug, $[84] = t36) : t36 = $[84], t36;
|
|
1937
2588
|
}
|
|
1938
|
-
function _temp$
|
|
2589
|
+
function _temp$3(candidate_0) {
|
|
1939
2590
|
return candidate_0.slug;
|
|
1940
2591
|
}
|
|
1941
2592
|
var cardGrid = "twjqgh0";
|
|
@@ -1946,12 +2597,15 @@ var cardGrid = "twjqgh0";
|
|
|
1946
2597
|
/**
|
|
1947
2598
|
* The flow for picking a theme: a grid of theme cards — the configured theme,
|
|
1948
2599
|
* the presets and the user's own themes — that drag into the order the user
|
|
1949
|
-
* wants, with the entry points to the add and restore flows below.
|
|
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.
|
|
1950
2602
|
*
|
|
1951
2603
|
* @internal
|
|
1952
2604
|
*/
|
|
1953
2605
|
function ThemeList() {
|
|
1954
|
-
let $ = c(
|
|
2606
|
+
let $ = c(38), { themes, removed, active, send } = useThemer(), { addThemeFromClipboard } = useThemeCodes(), [pasting, setPasting] = useState(!1);
|
|
2607
|
+
usePasteThemeCodes();
|
|
2608
|
+
let t0;
|
|
1955
2609
|
$[0] === send ? t0 = $[1] : (t0 = (palette, file) => send({
|
|
1956
2610
|
type: "theme.add",
|
|
1957
2611
|
title: titleFromFileName(file.name),
|
|
@@ -1965,7 +2619,7 @@ function ThemeList() {
|
|
|
1965
2619
|
order
|
|
1966
2620
|
}), $[2] = send, $[3] = t1);
|
|
1967
2621
|
let t2;
|
|
1968
|
-
$[4] === themes ? t2 = $[5] : (t2 = themes.map(_temp$
|
|
2622
|
+
$[4] === themes ? t2 = $[5] : (t2 = themes.map(_temp$2), $[4] = themes, $[5] = t2);
|
|
1969
2623
|
let t3;
|
|
1970
2624
|
if ($[6] !== active || $[7] !== themes) {
|
|
1971
2625
|
let t4;
|
|
@@ -1987,9 +2641,11 @@ function ThemeList() {
|
|
|
1987
2641
|
}), $[11] = t1, $[12] = t2, $[13] = t3, $[14] = t4) : t4 = $[14];
|
|
1988
2642
|
let t5;
|
|
1989
2643
|
$[15] !== removed.length || $[16] !== send ? (t5 = removed.length > 0 && /* @__PURE__ */ jsx(Button, {
|
|
2644
|
+
gap: 2,
|
|
1990
2645
|
icon: RestoreIcon,
|
|
1991
2646
|
mode: "bleed",
|
|
1992
2647
|
onClick: () => send({ type: "flow.removed" }),
|
|
2648
|
+
padding: 2,
|
|
1993
2649
|
text: `Show removed (${removed.length})`,
|
|
1994
2650
|
width: "fill"
|
|
1995
2651
|
}), $[15] = removed.length, $[16] = send, $[17] = t5) : t5 = $[17];
|
|
@@ -2021,18 +2677,37 @@ function ThemeList() {
|
|
|
2021
2677
|
tooltip: "Add a theme from the colors of an image"
|
|
2022
2678
|
}), $[23] = busy, $[24] = pickImage, $[25] = t8) : t8 = $[25];
|
|
2023
2679
|
let t9;
|
|
2024
|
-
$[26]
|
|
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, {
|
|
2025
2690
|
borderTop: !0,
|
|
2026
2691
|
padding: 3,
|
|
2027
2692
|
children: /* @__PURE__ */ jsxs(Flex, {
|
|
2028
2693
|
gap: 2,
|
|
2029
|
-
children: [
|
|
2694
|
+
children: [
|
|
2695
|
+
t7,
|
|
2696
|
+
t8,
|
|
2697
|
+
t9
|
|
2698
|
+
]
|
|
2030
2699
|
})
|
|
2031
|
-
}), $[
|
|
2032
|
-
let
|
|
2033
|
-
|
|
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;
|
|
2034
2709
|
}
|
|
2035
|
-
function _temp$
|
|
2710
|
+
function _temp$2(theme) {
|
|
2036
2711
|
return theme.slug;
|
|
2037
2712
|
}
|
|
2038
2713
|
/**
|
|
@@ -2092,9 +2767,11 @@ function ThemeSnippetDialog(props) {
|
|
|
2092
2767
|
justify: "flex-end",
|
|
2093
2768
|
padding: 3,
|
|
2094
2769
|
children: /* @__PURE__ */ jsx(Button, {
|
|
2770
|
+
gap: 2,
|
|
2095
2771
|
icon: ClipboardIcon,
|
|
2096
2772
|
mode: "ghost",
|
|
2097
2773
|
onClick: () => void handleCopy(),
|
|
2774
|
+
padding: 2,
|
|
2098
2775
|
text: "Copy"
|
|
2099
2776
|
})
|
|
2100
2777
|
}), $[5] = handleCopy, $[6] = t2);
|
|
@@ -2367,129 +3044,25 @@ function ResizableSidebar(props) {
|
|
|
2367
3044
|
children: t10
|
|
2368
3045
|
}), $[20] = overlay, $[21] = t10, $[22] = t6, $[23] = t11) : t11 = $[23], t11;
|
|
2369
3046
|
}
|
|
2370
|
-
const
|
|
2371
|
-
function
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
function sanitizeContrast(value) {
|
|
2375
|
-
return typeof value == "number" && Number.isFinite(value) ? Math.min(100, Math.max(15, value)) : null;
|
|
2376
|
-
}
|
|
2377
|
-
function sanitizeSchemeOptions(value) {
|
|
2378
|
-
let options = {};
|
|
2379
|
-
if (!value || typeof value != "object") return options;
|
|
2380
|
-
let accent = sanitizeColor(Reflect.get(value, "accent")), text = sanitizeColor(Reflect.get(value, "text")), background = sanitizeColor(Reflect.get(value, "background")), contrast = sanitizeContrast(Reflect.get(value, "contrast"));
|
|
2381
|
-
return accent && (options.accent = accent), text && (options.text = text), background && (options.background = background), contrast !== null && (options.contrast = contrast), options;
|
|
2382
|
-
}
|
|
2383
|
-
/**
|
|
2384
|
-
* Options from before they were grouped by scheme: one accent, text color and
|
|
2385
|
-
* contrast for both schemes, and a background per scheme. Both schemes get
|
|
2386
|
-
* the shared colors, so the theme keeps looking the same.
|
|
2387
|
-
*/
|
|
2388
|
-
function sanitizeFlatOptions(value) {
|
|
2389
|
-
let accent = sanitizeColor(Reflect.get(value, "accent"));
|
|
2390
|
-
if (!accent) return null;
|
|
2391
|
-
let shared = { accent }, text = sanitizeColor(Reflect.get(value, "text")), contrast = sanitizeContrast(Reflect.get(value, "contrast"));
|
|
2392
|
-
text && (shared.text = text), contrast !== null && (shared.contrast = contrast);
|
|
2393
|
-
let backgrounds = Reflect.get(value, "background"), options = {};
|
|
2394
|
-
for (let scheme of SCHEMES) {
|
|
2395
|
-
let background = backgrounds && typeof backgrounds == "object" ? sanitizeColor(Reflect.get(backgrounds, scheme)) : null;
|
|
2396
|
-
options[scheme] = background ? {
|
|
2397
|
-
...shared,
|
|
2398
|
-
background
|
|
2399
|
-
} : { ...shared };
|
|
2400
|
-
}
|
|
2401
|
-
return options;
|
|
2402
|
-
}
|
|
2403
|
-
function sanitizeOptions(value) {
|
|
2404
|
-
if (!value || typeof value != "object") return null;
|
|
2405
|
-
if ("accent" in value) return sanitizeFlatOptions(value);
|
|
2406
|
-
let options = {};
|
|
2407
|
-
for (let scheme of SCHEMES) {
|
|
2408
|
-
let schemeOptions = sanitizeSchemeOptions(Reflect.get(value, scheme));
|
|
2409
|
-
Object.keys(schemeOptions).length > 0 && (options[scheme] = schemeOptions);
|
|
2410
|
-
}
|
|
2411
|
-
return options;
|
|
2412
|
-
}
|
|
2413
|
-
function isReservedSlug(slug) {
|
|
2414
|
-
return slug === "config" || presets.some((preset) => preset.slug === slug);
|
|
2415
|
-
}
|
|
2416
|
-
function sanitizeCustomTheme(value) {
|
|
2417
|
-
if (!value || typeof value != "object") return null;
|
|
2418
|
-
let slug = Reflect.get(value, "slug");
|
|
2419
|
-
if (typeof slug != "string" || !slug || isReservedSlug(slug)) return null;
|
|
2420
|
-
let options = sanitizeOptions(Reflect.get(value, "options"));
|
|
2421
|
-
if (!options) return null;
|
|
2422
|
-
let title = Reflect.get(value, "title"), palette = sanitizePalette(Reflect.get(value, "palette"));
|
|
2423
|
-
return {
|
|
2424
|
-
slug,
|
|
2425
|
-
title: displayTitle(typeof title == "string" ? title : ""),
|
|
2426
|
-
options,
|
|
2427
|
-
...palette ? { palette } : {}
|
|
2428
|
-
};
|
|
2429
|
-
}
|
|
2430
|
-
function sanitizePalette(value) {
|
|
2431
|
-
if (!value || typeof value != "object") return null;
|
|
2432
|
-
let palette = {}, swatches = 0;
|
|
2433
|
-
for (let key of IMAGE_PALETTE_KEYS) {
|
|
2434
|
-
let swatch = sanitizeColor(Reflect.get(value, key));
|
|
2435
|
-
palette[key] = swatch, swatch && swatches++;
|
|
2436
|
-
}
|
|
2437
|
-
return swatches > 0 ? palette : null;
|
|
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);
|
|
2438
3051
|
}
|
|
2439
|
-
function
|
|
2440
|
-
|
|
2441
|
-
let custom = [], rawCustom = Reflect.get(value, "custom");
|
|
2442
|
-
if (Array.isArray(rawCustom)) for (let item of rawCustom) {
|
|
2443
|
-
let theme = sanitizeCustomTheme(item);
|
|
2444
|
-
theme && !custom.some((existing) => existing.slug === theme.slug) && custom.push(theme);
|
|
2445
|
-
}
|
|
2446
|
-
let removed = [], rawRemoved = Reflect.get(value, "removed");
|
|
2447
|
-
if (Array.isArray(rawRemoved)) for (let slug of rawRemoved) typeof slug == "string" && slug !== "config" && (presets.some((preset) => preset.slug === slug) || custom.some((theme) => theme.slug === slug)) && !removed.includes(slug) && removed.push(slug);
|
|
2448
|
-
let order = [], rawOrder = Reflect.get(value, "order");
|
|
2449
|
-
if (Array.isArray(rawOrder)) for (let slug of rawOrder) typeof slug == "string" && (slug === "config" || presets.some((preset) => preset.slug === slug) || custom.some((theme) => theme.slug === slug)) && !order.includes(slug) && order.push(slug);
|
|
2450
|
-
let rawActive = Reflect.get(value, "active");
|
|
2451
|
-
return {
|
|
2452
|
-
active: typeof rawActive == "string" ? rawActive : null,
|
|
2453
|
-
custom,
|
|
2454
|
-
removed,
|
|
2455
|
-
order
|
|
2456
|
-
};
|
|
3052
|
+
function getSnapshot() {
|
|
3053
|
+
return matchMedia(QUERY).matches;
|
|
2457
3054
|
}
|
|
2458
|
-
function
|
|
2459
|
-
|
|
2460
|
-
if (!raw) return null;
|
|
2461
|
-
let options = sanitizeOptions(JSON.parse(raw));
|
|
2462
|
-
if (!options) return null;
|
|
2463
|
-
let theme = createCustomTheme("Draft theme", options);
|
|
2464
|
-
return {
|
|
2465
|
-
active: theme.slug,
|
|
2466
|
-
custom: [theme],
|
|
2467
|
-
removed: [],
|
|
2468
|
-
order: []
|
|
2469
|
-
};
|
|
3055
|
+
function getServerSnapshot() {
|
|
3056
|
+
return !1;
|
|
2470
3057
|
}
|
|
2471
3058
|
/**
|
|
2472
|
-
*
|
|
2473
|
-
*
|
|
2474
|
-
* stored.
|
|
3059
|
+
* Whether the user prefers reduced motion — live, so that turning the
|
|
3060
|
+
* setting on while the Studio is open takes effect right away.
|
|
2475
3061
|
*
|
|
2476
3062
|
* @internal
|
|
2477
3063
|
*/
|
|
2478
|
-
function
|
|
2479
|
-
|
|
2480
|
-
if (typeof localStorage > "u") return initialThemerState;
|
|
2481
|
-
let raw = localStorage.getItem(STORAGE_KEY);
|
|
2482
|
-
return raw ? sanitizeState(JSON.parse(raw)) : readLegacyState() ?? initialThemerState;
|
|
2483
|
-
} catch {
|
|
2484
|
-
return initialThemerState;
|
|
2485
|
-
}
|
|
2486
|
-
}
|
|
2487
|
-
/** @internal */
|
|
2488
|
-
function writeStoredState(state) {
|
|
2489
|
-
try {
|
|
2490
|
-
if (typeof localStorage > "u") return;
|
|
2491
|
-
localStorage.setItem(STORAGE_KEY, JSON.stringify(state)), localStorage.removeItem(LEGACY_STORAGE_KEY);
|
|
2492
|
-
} catch {}
|
|
3064
|
+
function usePrefersReducedMotion() {
|
|
3065
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
2493
3066
|
}
|
|
2494
3067
|
/**
|
|
2495
3068
|
* The height of the Studio navbar inside the given element, in pixels — kept
|
|
@@ -2519,18 +3092,54 @@ function useStudioNavbarHeight(ref) {
|
|
|
2519
3092
|
};
|
|
2520
3093
|
}, t1 = [ref], $[0] = ref, $[1] = t0, $[2] = t1), useEffect(t0, t1), height;
|
|
2521
3094
|
}
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
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"
|
|
2525
3133
|
}, splitTransitionClasses = {
|
|
2526
|
-
resize: "
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
3134
|
+
resize: "_1yq4ar70",
|
|
3135
|
+
crossfade: "_1yq4ar71",
|
|
3136
|
+
slideIn: "_1yq4ar72",
|
|
3137
|
+
slideOut: "_1yq4ar73",
|
|
3138
|
+
dropIn: "_1yq4ar74",
|
|
3139
|
+
dropOut: "_1yq4ar75"
|
|
2531
3140
|
}, studioScheme = {
|
|
2532
|
-
light: "
|
|
2533
|
-
dark: "
|
|
3141
|
+
light: "_1yq4ar79",
|
|
3142
|
+
dark: "_1yq4ar7a"
|
|
2534
3143
|
};
|
|
2535
3144
|
function sameStoredState(a, b) {
|
|
2536
3145
|
return a.active === b.active && a.custom === b.custom && a.removed === b.removed && a.order === b.order;
|
|
@@ -2561,10 +3170,21 @@ function sameView(a, b) {
|
|
|
2561
3170
|
* or on top, on small screens — through React's view transitions (React
|
|
2562
3171
|
* 19.3), styled in `ThemerLayout.css.ts`.
|
|
2563
3172
|
*
|
|
3173
|
+
* The machine says what shows and when the layout is in motion (its `panel`,
|
|
3174
|
+
* `split` and `moving` tags); the layout defers what shows, which puts the
|
|
3175
|
+
* panel's and the copy's mounts in a transition — what lets React animate
|
|
3176
|
+
* them — and picks the view transition classes from the tags.
|
|
3177
|
+
*
|
|
3178
|
+
* Whatever else on the page has a `view-transition-name` — an avatar the
|
|
3179
|
+
* Studio names so it moves as one piece — comes along in step: every
|
|
3180
|
+
* transition the layout starts carries `layoutTransitionType`, which the
|
|
3181
|
+
* stylesheet keys on to move every group in the layout's time (see
|
|
3182
|
+
* `ThemerLayout.css.ts`).
|
|
3183
|
+
*
|
|
2564
3184
|
* @internal
|
|
2565
3185
|
*/
|
|
2566
3186
|
function ThemerLayout(props) {
|
|
2567
|
-
let $ = c(
|
|
3187
|
+
let $ = c(85), { baseOptions, ...layoutProps } = props, t0;
|
|
2568
3188
|
$[0] === baseOptions ? t0 = $[1] : (t0 = () => ({
|
|
2569
3189
|
baseOptions,
|
|
2570
3190
|
stored: readStoredState()
|
|
@@ -2572,68 +3192,34 @@ function ThemerLayout(props) {
|
|
|
2572
3192
|
let [input] = useState(t0), t1;
|
|
2573
3193
|
$[2] === input ? t1 = $[3] : (t1 = { input }, $[2] = input, $[3] = t1);
|
|
2574
3194
|
let [snapshot, send, actorRef] = useActor(themerMachine, t1), stored = useSelector(actorRef, selectStoredState, sameStoredState), view = useSelector(actorRef, selectView, sameView), t2;
|
|
2575
|
-
$[4] === snapshot ? t2 = $[5] : (t2 = snapshot.
|
|
3195
|
+
$[4] === snapshot ? t2 = $[5] : (t2 = snapshot.hasTag("panel"), $[4] = snapshot, $[5] = t2);
|
|
2576
3196
|
let open = t2, t3;
|
|
2577
|
-
$[6] === snapshot ? t3 = $[7] : (t3 = snapshot.
|
|
2578
|
-
let split = t3,
|
|
2579
|
-
$[8]
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
},
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
open,
|
|
2590
|
-
split
|
|
2591
|
-
}), setResizing(!0);
|
|
2592
|
-
});
|
|
2593
|
-
}, $[12] = open, $[13] = shown.open, $[14] = shown.split, $[15] = split, $[16] = t6) : t6 = $[16];
|
|
2594
|
-
let t7;
|
|
2595
|
-
$[17] !== open || $[18] !== shown || $[19] !== split ? (t7 = [
|
|
2596
|
-
open,
|
|
2597
|
-
shown,
|
|
2598
|
-
split
|
|
2599
|
-
], $[17] = open, $[18] = shown, $[19] = split, $[20] = t7) : t7 = $[20], useEffect(t6, t7);
|
|
2600
|
-
let t8;
|
|
2601
|
-
$[21] === resizing ? t8 = $[22] : (t8 = () => {
|
|
2602
|
-
if (!resizing) return;
|
|
2603
|
-
let timer = setTimeout(() => setResizing(!1), 500);
|
|
2604
|
-
return () => clearTimeout(timer);
|
|
2605
|
-
}, $[21] = resizing, $[22] = t8);
|
|
2606
|
-
let t9;
|
|
2607
|
-
$[23] !== resizing || $[24] !== shown ? (t9 = [resizing, shown], $[23] = resizing, $[24] = shown, $[25] = t9) : t9 = $[25], useEffect(t8, t9);
|
|
2608
|
-
let t10, t11;
|
|
2609
|
-
$[26] === stored ? (t10 = $[27], t11 = $[28]) : (t10 = () => writeStoredState(stored), t11 = [stored], $[26] = stored, $[27] = t10, $[28] = t11), useEffect(t10, t11);
|
|
2610
|
-
let previousImages = useRef(images), t12, t13;
|
|
2611
|
-
$[29] === images ? (t12 = $[30], t13 = $[31]) : (t12 = () => {
|
|
2612
|
-
let current = new Set(Object.values(images));
|
|
2613
|
-
for (let url of Object.values(previousImages.current)) current.has(url) || URL.revokeObjectURL(url);
|
|
2614
|
-
previousImages.current = images;
|
|
2615
|
-
}, t13 = [images], $[29] = images, $[30] = t12, $[31] = t13), useEffect(t12, t13);
|
|
2616
|
-
let t14, t15;
|
|
2617
|
-
$[32] === Symbol.for("react.memo_cache_sentinel") ? (t14 = () => () => {
|
|
2618
|
-
for (let url_0 of Object.values(previousImages.current)) URL.revokeObjectURL(url_0);
|
|
2619
|
-
}, t15 = [], $[32] = t14, $[33] = t15) : (t14 = $[32], t15 = $[33]), useEffect(t14, t15);
|
|
2620
|
-
let t16;
|
|
2621
|
-
$[34] !== baseOptions || $[35] !== stored ? (t16 = resolveThemes(stored, baseOptions), $[34] = baseOptions, $[35] = stored, $[36] = t16) : t16 = $[36];
|
|
2622
|
-
let { themes, removed, active } = t16, activeOptions = active.source === "config" ? null : active.options, t17;
|
|
2623
|
-
$[37] === activeOptions ? t17 = $[38] : (t17 = activeOptions === null ? null : buildTheme(activeOptions), $[37] = activeOptions, $[38] = t17);
|
|
2624
|
-
let theme = t17, scheme = useColorSchemeValue(), oppositeScheme = scheme === "dark" ? "light" : "dark", t18;
|
|
2625
|
-
$[39] !== scheme || $[40] !== theme?.v2?.color ? (t18 = () => {
|
|
2626
|
-
let background = theme?.v2?.color[scheme].default.bg;
|
|
3197
|
+
$[6] === snapshot ? t3 = $[7] : (t3 = snapshot.hasTag("split"), $[6] = snapshot, $[7] = t3);
|
|
3198
|
+
let split = t3, t4;
|
|
3199
|
+
$[8] === snapshot ? t4 = $[9] : (t4 = snapshot.hasTag("moving"), $[8] = snapshot, $[9] = t4);
|
|
3200
|
+
let moving = t4, t5;
|
|
3201
|
+
$[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, t6;
|
|
3203
|
+
$[12] !== baseOptions || $[13] !== stored ? (t6 = resolveThemes(stored, baseOptions), $[12] = baseOptions, $[13] = stored, $[14] = t6) : t6 = $[14];
|
|
3204
|
+
let { themes, removed, active } = t6, activeOptions = active.source === "config" ? null : active.options, t7;
|
|
3205
|
+
$[15] === activeOptions ? t7 = $[16] : (t7 = activeOptions === null ? null : buildTheme(activeOptions), $[15] = activeOptions, $[16] = t7);
|
|
3206
|
+
let theme = t7, deferredTheme = useTypedDeferredValue(reduceMotion ? void 0 : theme, layoutTransitionType), shownTheme = deferredTheme === void 0 ? theme : deferredTheme, scheme = useColorSchemeValue(), oppositeScheme = scheme === "dark" ? "light" : "dark", t8;
|
|
3207
|
+
$[17] !== scheme || $[18] !== shownTheme?.v2?.color ? (t8 = () => {
|
|
3208
|
+
let background = shownTheme?.v2?.color[scheme].default.bg;
|
|
2627
3209
|
if (background === void 0) return;
|
|
2628
3210
|
let { style } = document.body, previous = style.backgroundColor;
|
|
2629
3211
|
return style.backgroundColor = background, () => {
|
|
2630
3212
|
style.backgroundColor = previous;
|
|
2631
3213
|
};
|
|
2632
|
-
}, $[
|
|
2633
|
-
let
|
|
2634
|
-
$[
|
|
2635
|
-
let
|
|
2636
|
-
$[
|
|
3214
|
+
}, $[17] = scheme, $[18] = shownTheme?.v2?.color, $[19] = t8) : t8 = $[19];
|
|
3215
|
+
let t9;
|
|
3216
|
+
$[20] !== scheme || $[21] !== shownTheme ? (t9 = [scheme, shownTheme], $[20] = scheme, $[21] = shownTheme, $[22] = t9) : t9 = $[22], useEffect(t8, t9);
|
|
3217
|
+
let t10, t11;
|
|
3218
|
+
$[23] === actorRef ? (t10 = $[24], t11 = $[25]) : (t10 = () => () => {
|
|
3219
|
+
for (let url of Object.values(actorRef.getSnapshot().context.images)) URL.revokeObjectURL(url);
|
|
3220
|
+
}, t11 = [actorRef], $[23] = actorRef, $[24] = t10, $[25] = t11), useEffect(t10, t11);
|
|
3221
|
+
let t12;
|
|
3222
|
+
$[26] !== active || $[27] !== baseOptions || $[28] !== images || $[29] !== mobile || $[30] !== navbarHeight || $[31] !== open || $[32] !== removed || $[33] !== send || $[34] !== split || $[35] !== themes || $[36] !== view ? (t12 = {
|
|
2637
3223
|
baseOptions,
|
|
2638
3224
|
themes,
|
|
2639
3225
|
removed,
|
|
@@ -2645,69 +3231,95 @@ function ThemerLayout(props) {
|
|
|
2645
3231
|
mobile,
|
|
2646
3232
|
navbarHeight,
|
|
2647
3233
|
send
|
|
2648
|
-
}, $[
|
|
2649
|
-
let context =
|
|
2650
|
-
$[
|
|
2651
|
-
|
|
3234
|
+
}, $[26] = active, $[27] = baseOptions, $[28] = images, $[29] = mobile, $[30] = navbarHeight, $[31] = open, $[32] = removed, $[33] = send, $[34] = split, $[35] = themes, $[36] = view, $[37] = t12) : t12 = $[37];
|
|
3235
|
+
let context = t12, studio = layoutProps.renderDefault(layoutProps), t13;
|
|
3236
|
+
$[38] === send ? t13 = $[39] : (t13 = () => send({ type: "layout.transitioned" }), $[38] = send, $[39] = t13);
|
|
3237
|
+
let transitioned = t13, t14;
|
|
3238
|
+
$[40] !== mobile || $[41] !== moving || $[42] !== reduceMotion || $[43] !== switching ? (t14 = reduceMotion ? {
|
|
3239
|
+
copyEnter: "none",
|
|
3240
|
+
copyExit: "none",
|
|
3241
|
+
panelEnter: "none",
|
|
3242
|
+
panelExit: "none",
|
|
3243
|
+
update: "none",
|
|
3244
|
+
studioUpdate: "none"
|
|
3245
|
+
} : {
|
|
3246
|
+
copyEnter: mobile ? splitTransitionClasses.dropIn : splitTransitionClasses.slideIn,
|
|
3247
|
+
copyExit: mobile ? splitTransitionClasses.dropOut : splitTransitionClasses.slideOut,
|
|
3248
|
+
panelEnter: panelTransitionClasses.slideIn,
|
|
3249
|
+
panelExit: panelTransitionClasses.slideOut,
|
|
3250
|
+
update: switching ? splitTransitionClasses.crossfade : "none",
|
|
3251
|
+
studioUpdate: moving ? splitTransitionClasses.resize : switching ? splitTransitionClasses.crossfade : "none"
|
|
3252
|
+
}, $[40] = mobile, $[41] = moving, $[42] = reduceMotion, $[43] = switching, $[44] = t14) : t14 = $[44];
|
|
3253
|
+
let classes = t14, t15 = mobile ? "column" : "row", t16;
|
|
3254
|
+
$[45] !== classes.copyEnter || $[46] !== classes.copyExit || $[47] !== classes.update || $[48] !== mobile || $[49] !== oppositeScheme || $[50] !== shownOpen || $[51] !== shownSplit || $[52] !== shownTheme || $[53] !== studio || $[54] !== transitioned ? (t16 = shownOpen && /* @__PURE__ */ jsx(Activity, {
|
|
3255
|
+
mode: shownSplit ? "visible" : "hidden",
|
|
2652
3256
|
children: /* @__PURE__ */ jsx(ViewTransition, {
|
|
2653
|
-
enter:
|
|
2654
|
-
exit:
|
|
2655
|
-
|
|
3257
|
+
enter: classes.copyEnter,
|
|
3258
|
+
exit: classes.copyExit,
|
|
3259
|
+
onEnter: transitioned,
|
|
3260
|
+
onExit: transitioned,
|
|
3261
|
+
onUpdate: transitioned,
|
|
3262
|
+
update: classes.update,
|
|
2656
3263
|
children: /* @__PURE__ */ jsx(StudioPreview, {
|
|
2657
3264
|
borderBottom: mobile,
|
|
2658
3265
|
borderRight: !mobile,
|
|
2659
3266
|
scheme: oppositeScheme,
|
|
2660
|
-
theme,
|
|
3267
|
+
theme: shownTheme,
|
|
2661
3268
|
children: studio
|
|
2662
3269
|
})
|
|
2663
3270
|
}, "opposite")
|
|
2664
|
-
}), $[
|
|
2665
|
-
let
|
|
2666
|
-
$[
|
|
3271
|
+
}), $[45] = classes.copyEnter, $[46] = classes.copyExit, $[47] = classes.update, $[48] = mobile, $[49] = oppositeScheme, $[50] = shownOpen, $[51] = shownSplit, $[52] = shownTheme, $[53] = studio, $[54] = transitioned, $[55] = t16) : t16 = $[55];
|
|
3272
|
+
let t17;
|
|
3273
|
+
$[56] !== shownTheme || $[57] !== studio ? (t17 = /* @__PURE__ */ jsx(StudioPreview, {
|
|
2667
3274
|
ref: studioRef,
|
|
2668
|
-
theme,
|
|
3275
|
+
theme: shownTheme,
|
|
2669
3276
|
children: studio
|
|
2670
|
-
}), $[
|
|
3277
|
+
}), $[56] = shownTheme, $[57] = studio, $[58] = t17) : t17 = $[58];
|
|
3278
|
+
let t18;
|
|
3279
|
+
$[59] !== classes.studioUpdate || $[60] !== t17 || $[61] !== transitioned ? (t18 = /* @__PURE__ */ jsx(ViewTransition, {
|
|
3280
|
+
onUpdate: transitioned,
|
|
3281
|
+
update: classes.studioUpdate,
|
|
3282
|
+
children: t17
|
|
3283
|
+
}, "primary"), $[59] = classes.studioUpdate, $[60] = t17, $[61] = transitioned, $[62] = t18) : t18 = $[62];
|
|
3284
|
+
let t19 = shownOpen ? "visible" : "hidden", t20 = shownTheme ?? void 0, t21;
|
|
3285
|
+
$[63] === mobile ? t21 = $[64] : (t21 = /* @__PURE__ */ jsx(ResizableSidebar, { overlay: mobile }), $[63] = mobile, $[64] = t21);
|
|
3286
|
+
let t22;
|
|
3287
|
+
$[65] !== t20 || $[66] !== t21 ? (t22 = /* @__PURE__ */ jsx(ThemeProvider, {
|
|
3288
|
+
theme: t20,
|
|
3289
|
+
children: t21
|
|
3290
|
+
}), $[65] = t20, $[66] = t21, $[67] = t22) : t22 = $[67];
|
|
3291
|
+
let t23;
|
|
3292
|
+
$[68] !== classes.panelEnter || $[69] !== classes.panelExit || $[70] !== classes.update || $[71] !== t22 || $[72] !== transitioned ? (t23 = /* @__PURE__ */ jsx(ViewTransition, {
|
|
3293
|
+
enter: classes.panelEnter,
|
|
3294
|
+
exit: classes.panelExit,
|
|
3295
|
+
onEnter: transitioned,
|
|
3296
|
+
onExit: transitioned,
|
|
3297
|
+
onUpdate: transitioned,
|
|
3298
|
+
update: classes.update,
|
|
3299
|
+
children: t22
|
|
3300
|
+
}, "panel"), $[68] = classes.panelEnter, $[69] = classes.panelExit, $[70] = classes.update, $[71] = t22, $[72] = transitioned, $[73] = t23) : t23 = $[73];
|
|
3301
|
+
let t24;
|
|
3302
|
+
$[74] !== t19 || $[75] !== t23 ? (t24 = /* @__PURE__ */ jsx(Activity, {
|
|
3303
|
+
mode: t19,
|
|
3304
|
+
children: t23
|
|
3305
|
+
}), $[74] = t19, $[75] = t23, $[76] = t24) : t24 = $[76];
|
|
2671
3306
|
let t25;
|
|
2672
|
-
$[
|
|
2673
|
-
onUpdate: stopResizing,
|
|
2674
|
-
update: t23,
|
|
2675
|
-
children: t24
|
|
2676
|
-
}, "primary"), $[67] = stopResizing, $[68] = t23, $[69] = t24, $[70] = t25) : t25 = $[70];
|
|
2677
|
-
let t26 = shown.open ? "visible" : "hidden", t27 = theme ?? void 0, t28;
|
|
2678
|
-
$[71] === mobile ? t28 = $[72] : (t28 = /* @__PURE__ */ jsx(ResizableSidebar, { overlay: mobile }), $[71] = mobile, $[72] = t28);
|
|
2679
|
-
let t29;
|
|
2680
|
-
$[73] !== t27 || $[74] !== t28 ? (t29 = /* @__PURE__ */ jsx(ViewTransition, {
|
|
2681
|
-
enter: panelTransitionClasses.slideIn,
|
|
2682
|
-
exit: panelTransitionClasses.slideOut,
|
|
2683
|
-
update: "none",
|
|
2684
|
-
children: /* @__PURE__ */ jsx(ThemeProvider, {
|
|
2685
|
-
theme: t27,
|
|
2686
|
-
children: t28
|
|
2687
|
-
})
|
|
2688
|
-
}, "panel"), $[73] = t27, $[74] = t28, $[75] = t29) : t29 = $[75];
|
|
2689
|
-
let t30;
|
|
2690
|
-
$[76] !== t26 || $[77] !== t29 ? (t30 = /* @__PURE__ */ jsx(Activity, {
|
|
2691
|
-
mode: t26,
|
|
2692
|
-
children: t29
|
|
2693
|
-
}), $[76] = t26, $[77] = t29, $[78] = t30) : t30 = $[78];
|
|
2694
|
-
let t31;
|
|
2695
|
-
$[79] !== t21 || $[80] !== t22 || $[81] !== t25 || $[82] !== t30 ? (t31 = /* @__PURE__ */ jsxs(Flex, {
|
|
3307
|
+
$[77] !== t15 || $[78] !== t16 || $[79] !== t18 || $[80] !== t24 ? (t25 = /* @__PURE__ */ jsxs(Flex, {
|
|
2696
3308
|
className: layout,
|
|
2697
|
-
direction:
|
|
3309
|
+
direction: t15,
|
|
2698
3310
|
height: "fill",
|
|
2699
3311
|
sizing: "border",
|
|
2700
3312
|
children: [
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
3313
|
+
t16,
|
|
3314
|
+
t18,
|
|
3315
|
+
t24
|
|
2704
3316
|
]
|
|
2705
|
-
}), $[
|
|
2706
|
-
let
|
|
2707
|
-
return $[
|
|
3317
|
+
}), $[77] = t15, $[78] = t16, $[79] = t18, $[80] = t24, $[81] = t25) : t25 = $[81];
|
|
3318
|
+
let t26;
|
|
3319
|
+
return $[82] !== context || $[83] !== t25 ? (t26 = /* @__PURE__ */ jsx(ThemerContext.Provider, {
|
|
2708
3320
|
value: context,
|
|
2709
|
-
children:
|
|
2710
|
-
}), $[
|
|
3321
|
+
children: t25
|
|
3322
|
+
}), $[82] = context, $[83] = t25, $[84] = t26) : t26 = $[84], t26;
|
|
2711
3323
|
}
|
|
2712
3324
|
/**
|
|
2713
3325
|
* One copy of the Studio, in the given color scheme — or, without one, in the
|