@sanity/themer 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tool.js CHANGED
@@ -1,12 +1,15 @@
1
1
  import { a as DEFAULT_BACKGROUND, c as resolveThemeOptions, d as hslToHex, f as relativeLuminance, i as DEFAULT_ACCENT, l as isColor, n as buildTheme, o as SCHEMES, r as buildPalette, s as deriveTextColor, t as presets, u as hexToHsl } from "./presets-CqBgS3A-.js";
2
2
  import { c } from "react-compiler-runtime";
3
3
  import { definePlugin, useColorSchemeValue } from "sanity";
4
- import { Badge, Box, Button, Card, Dialog, Flex, Layer, Stack, Text, TextInput, ThemeProvider, useTheme_v2 } from "@sanity/ui";
4
+ import { Badge, Box, Button, Card, Dialog, Flex, Layer, Stack, Text, TextInput, ThemeProvider, useRootTheme, useTheme_v2 } from "@sanity/ui";
5
+ import { useActor, useSelector } from "@xstate/react";
5
6
  import { createContext, useContext, useEffect, useRef, useState } from "react";
7
+ import { assign, not, setup } from "xstate";
6
8
  import { styled } from "styled-components";
7
9
  import { ArrowLeftIcon } from "@sanity/icons/ArrowLeft";
8
10
  import { CloseIcon } from "@sanity/icons/Close";
9
11
  import { CodeBlockIcon } from "@sanity/icons/CodeBlock";
12
+ import { SplitVerticalIcon } from "@sanity/icons/SplitVertical";
10
13
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
14
  import { ResetIcon } from "@sanity/icons/Reset";
12
15
  import { TrashIcon } from "@sanity/icons/Trash";
@@ -24,8 +27,6 @@ import { ClipboardIcon } from "@sanity/icons/Clipboard";
24
27
  import { Code } from "@sanity/ui/code";
25
28
  import { registerLanguage } from "react-refractor";
26
29
  import typescript from "refractor/typescript";
27
- import { useActor, useSelector } from "@xstate/react";
28
- import { assign, not, setup } from "xstate";
29
30
  import { ColorWheelIcon } from "@sanity/icons/ColorWheel";
30
31
  /** @internal */
31
32
  const ThemerContext = createContext(null);
@@ -35,41 +36,6 @@ function useThemer() {
35
36
  if (!context) throw Error("useThemer must be used within the `themerTool` plugin");
36
37
  return context;
37
38
  }
38
- const Root$2 = styled(Box).withConfig({
39
- displayName: "Root",
40
- componentId: "sc-6zqwg0-0"
41
- })`scrollbar-gutter:stable;`;
42
- /**
43
- * The scrolling body of a sidebar flow. It always reserves the scrollbar's
44
- * space, so the content does not shift when the list grows or shrinks past
45
- * the point where it needs to scroll — and it takes that space out of its
46
- * right padding, so the content stays centered rather than hugging the left.
47
- * With overlay scrollbars there is no gutter and the padding stays even.
48
- *
49
- * @internal
50
- */
51
- function ScrollArea(props) {
52
- let $ = c(8), { children, padding } = props, { space } = useTheme_v2(), ref = useRef(null), [gutter, setGutter] = useState(0), t0, t1;
53
- $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = () => {
54
- let element = ref.current;
55
- if (!element) return;
56
- let measure = () => setGutter(element.offsetWidth - element.clientWidth);
57
- measure();
58
- let observer = new ResizeObserver(measure);
59
- return observer.observe(element), () => observer.disconnect();
60
- }, t1 = [], $[0] = t0, $[1] = t1) : (t0 = $[0], t1 = $[1]), useEffect(t0, t1);
61
- let t2 = Math.max(0, space[padding] - gutter), t3;
62
- $[2] === t2 ? t3 = $[3] : (t3 = { paddingRight: t2 }, $[2] = t2, $[3] = t3);
63
- let t4;
64
- return $[4] !== children || $[5] !== padding || $[6] !== t3 ? (t4 = /* @__PURE__ */ jsx(Root$2, {
65
- flex: 1,
66
- overflow: "auto",
67
- padding,
68
- ref,
69
- style: t3,
70
- children
71
- }), $[4] = children, $[5] = padding, $[6] = t3, $[7] = t4) : t4 = $[7], t4;
72
- }
73
39
  /**
74
40
  * Reduces theme options to the minimal object that recreates them through
75
41
  * `buildTheme`: in each scheme the accent is dropped when it is the stock
@@ -169,6 +135,289 @@ function displayTitle(title) {
169
135
  function randomId() {
170
136
  return typeof crypto < "u" && typeof crypto.randomUUID == "function" ? crypto.randomUUID() : `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
171
137
  }
138
+ function themesOf(context) {
139
+ return resolveThemes(context, context.baseOptions);
140
+ }
141
+ /**
142
+ * The state of the themer tool: three parallel regions, whether the
143
+ * `sidebar` is open, which `flow` it is in — picking a theme from the `list`,
144
+ * `edit`ing one of the user's own themes, or restoring `removed` ones — and
145
+ * whether the `preview` shows the Studio once (`single`) or in light and dark
146
+ * side by side (`split`). The context carries the persisted state (the
147
+ * applied theme, the user's themes and what was removed) alongside what the
148
+ * flows need.
149
+ *
150
+ * Theme operations are handled in every flow, and the flows leave on their
151
+ * own when they lose their subject: the editor when its theme is removed or
152
+ * deleted, and the removed view when its last theme is restored or deleted.
153
+ *
154
+ * @internal
155
+ */
156
+ const themerMachine = setup({
157
+ types: {
158
+ context: {},
159
+ events: {},
160
+ input: {}
161
+ },
162
+ guards: {
163
+ isCustomTheme: ({ context }, params) => themesOf(context).themes.some((theme) => theme.slug === params.slug && theme.source === "custom"),
164
+ isEditingListedTheme: ({ context }) => context.editing !== null && themesOf(context).themes.some((theme) => theme.slug === context.editing?.slug && theme.source === "custom"),
165
+ hasRemovedThemes: ({ context }) => themesOf(context).removed.length > 0
166
+ },
167
+ actions: {
168
+ pick: assign((_, params) => ({ active: params.slug === "config" ? null : params.slug })),
169
+ add: assign(({ context }, params) => {
170
+ let theme = createCustomTheme(params.title ?? "Untitled theme", params.options ?? themesOf(context).active.options, params.palette);
171
+ return {
172
+ active: theme.slug,
173
+ custom: [...context.custom, theme],
174
+ editing: {
175
+ slug: theme.slug,
176
+ focusTitle: params.title === void 0
177
+ },
178
+ images: params.imageUrl ? {
179
+ ...context.images,
180
+ [theme.slug]: params.imageUrl
181
+ } : context.images
182
+ };
183
+ }),
184
+ duplicate: assign(({ context }, params) => {
185
+ let { themes, removed } = themesOf(context), source = [...themes, ...removed].find((theme) => theme.slug === params.slug);
186
+ if (!source) return {};
187
+ let theme = createCustomTheme(duplicateTitle(source.title), source.options, source.palette);
188
+ return {
189
+ active: theme.slug,
190
+ custom: [...context.custom, theme],
191
+ editing: {
192
+ slug: theme.slug,
193
+ focusTitle: !0
194
+ }
195
+ };
196
+ }),
197
+ startEditing: assign((_, params) => ({
198
+ active: params.slug,
199
+ editing: {
200
+ slug: params.slug,
201
+ focusTitle: !1
202
+ }
203
+ })),
204
+ stopEditing: assign({ editing: null }),
205
+ update: assign(({ context }, params) => ({
206
+ custom: context.custom.map((theme) => theme.slug === params.slug ? {
207
+ ...theme,
208
+ title: params.title ?? theme.title,
209
+ options: params.options ?? theme.options,
210
+ ...params.palette ? { palette: params.palette } : {}
211
+ } : theme),
212
+ images: params.imageUrl && context.custom.some((theme) => theme.slug === params.slug) ? {
213
+ ...context.images,
214
+ [params.slug]: params.imageUrl
215
+ } : context.images
216
+ })),
217
+ remove: assign(({ context }, params) => params.slug === "config" || context.removed.includes(params.slug) ? {} : {
218
+ active: context.active === params.slug ? null : context.active,
219
+ removed: [...context.removed, params.slug]
220
+ }),
221
+ restore: assign(({ context }, params) => ({ removed: context.removed.filter((slug) => slug !== params.slug) })),
222
+ delete: assign(({ context }, params) => {
223
+ if (!context.custom.some((theme) => theme.slug === params.slug)) return {};
224
+ let { [params.slug]: _deleted, ...images } = context.images;
225
+ return {
226
+ active: context.active === params.slug ? null : context.active,
227
+ custom: context.custom.filter((theme) => theme.slug !== params.slug),
228
+ removed: context.removed.filter((slug) => slug !== params.slug),
229
+ images
230
+ };
231
+ })
232
+ }
233
+ }).createMachine({
234
+ id: "themer",
235
+ context: ({ input }) => ({
236
+ ...input.stored,
237
+ baseOptions: input.baseOptions,
238
+ editing: null,
239
+ images: {}
240
+ }),
241
+ type: "parallel",
242
+ states: {
243
+ sidebar: {
244
+ initial: "closed",
245
+ states: {
246
+ closed: { on: { "sidebar.toggle": "open" } },
247
+ open: { on: {
248
+ "sidebar.toggle": "closed",
249
+ "sidebar.close": "closed"
250
+ } }
251
+ }
252
+ },
253
+ preview: {
254
+ initial: "single",
255
+ states: {
256
+ single: { on: { "preview.toggle": "split" } },
257
+ split: { on: { "preview.toggle": "single" } }
258
+ }
259
+ },
260
+ flow: {
261
+ initial: "list",
262
+ on: {
263
+ "theme.pick": { actions: {
264
+ type: "pick",
265
+ params: ({ event }) => ({ slug: event.slug })
266
+ } },
267
+ "theme.add": {
268
+ target: ".edit",
269
+ actions: {
270
+ type: "add",
271
+ params: ({ event }) => ({
272
+ title: event.title,
273
+ options: event.options,
274
+ palette: event.palette,
275
+ imageUrl: event.imageUrl
276
+ })
277
+ }
278
+ },
279
+ "theme.duplicate": {
280
+ target: ".edit",
281
+ actions: {
282
+ type: "duplicate",
283
+ params: ({ event }) => ({ slug: event.slug })
284
+ }
285
+ },
286
+ "theme.edit": {
287
+ target: ".edit",
288
+ guard: {
289
+ type: "isCustomTheme",
290
+ params: ({ event }) => ({ slug: event.slug })
291
+ },
292
+ actions: {
293
+ type: "startEditing",
294
+ params: ({ event }) => ({ slug: event.slug })
295
+ }
296
+ },
297
+ "theme.update": { actions: {
298
+ type: "update",
299
+ params: ({ event }) => ({
300
+ slug: event.slug,
301
+ title: event.title,
302
+ options: event.options,
303
+ palette: event.palette,
304
+ imageUrl: event.imageUrl
305
+ })
306
+ } },
307
+ "theme.remove": { actions: {
308
+ type: "remove",
309
+ params: ({ event }) => ({ slug: event.slug })
310
+ } },
311
+ "theme.restore": { actions: {
312
+ type: "restore",
313
+ params: ({ event }) => ({ slug: event.slug })
314
+ } },
315
+ "theme.delete": { actions: {
316
+ type: "delete",
317
+ params: ({ event }) => ({ slug: event.slug })
318
+ } },
319
+ "flow.list": ".list",
320
+ "flow.removed": {
321
+ target: ".removed",
322
+ guard: "hasRemovedThemes"
323
+ }
324
+ },
325
+ states: {
326
+ list: {},
327
+ edit: {
328
+ exit: "stopEditing",
329
+ always: {
330
+ guard: not("isEditingListedTheme"),
331
+ target: "list"
332
+ }
333
+ },
334
+ removed: { always: {
335
+ guard: not("hasRemovedThemes"),
336
+ target: "list"
337
+ } }
338
+ }
339
+ }
340
+ }
341
+ });
342
+ /** The part of the machine context that is persisted between sessions @internal */
343
+ function selectStoredState(snapshot) {
344
+ let { active, custom, removed } = snapshot.context;
345
+ return {
346
+ active,
347
+ custom,
348
+ removed
349
+ };
350
+ }
351
+ const Root$2 = styled(Box).withConfig({
352
+ displayName: "Root",
353
+ componentId: "sc-6lslk9-0"
354
+ })`scrollbar-gutter:stable;`, ScrollAreaInsetContext = createContext({
355
+ left: 0,
356
+ right: 0,
357
+ bottom: 0
358
+ });
359
+ /**
360
+ * The scrolling body of a sidebar flow. It always reserves the scrollbar's
361
+ * space, so the content does not shift when the list grows or shrinks past
362
+ * the point where it needs to scroll — and it takes that space out of its
363
+ * right padding, so the content stays centered rather than hugging the left.
364
+ * With overlay scrollbars there is no gutter and the padding stays even.
365
+ *
366
+ * @internal
367
+ */
368
+ function ScrollArea(props) {
369
+ let $ = c(15), { children, padding } = props, { space } = useTheme_v2(), ref = useRef(null), [gutter, setGutter] = useState(0), t0, t1;
370
+ $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = () => {
371
+ let element = ref.current;
372
+ if (!element) return;
373
+ let measure = () => setGutter(element.offsetWidth - element.clientWidth);
374
+ measure();
375
+ let observer = new ResizeObserver(measure);
376
+ return observer.observe(element), () => observer.disconnect();
377
+ }, t1 = [], $[0] = t0, $[1] = t1) : (t0 = $[0], t1 = $[1]), useEffect(t0, t1);
378
+ let t2 = space[padding], t3 = Math.max(0, space[padding] - gutter), t4 = space[padding], t5;
379
+ $[2] !== t2 || $[3] !== t3 || $[4] !== t4 ? (t5 = {
380
+ left: t2,
381
+ right: t3,
382
+ bottom: t4
383
+ }, $[2] = t2, $[3] = t3, $[4] = t4, $[5] = t5) : t5 = $[5];
384
+ let inset = t5, t6;
385
+ $[6] === inset.right ? t6 = $[7] : (t6 = { paddingRight: inset.right }, $[6] = inset.right, $[7] = t6);
386
+ let t7;
387
+ $[8] !== children || $[9] !== inset ? (t7 = /* @__PURE__ */ jsx(ScrollAreaInsetContext.Provider, {
388
+ value: inset,
389
+ children
390
+ }), $[8] = children, $[9] = inset, $[10] = t7) : t7 = $[10];
391
+ let t8;
392
+ return $[11] !== padding || $[12] !== t6 || $[13] !== t7 ? (t8 = /* @__PURE__ */ jsx(Root$2, {
393
+ flex: 1,
394
+ overflow: "auto",
395
+ padding,
396
+ ref,
397
+ style: t6,
398
+ children: t7
399
+ }), $[11] = padding, $[12] = t6, $[13] = t7, $[14] = t8) : t8 = $[14], t8;
400
+ }
401
+ /**
402
+ * Content that runs edge to edge in a scroll area — up to the scrollbar —
403
+ * instead of sitting inside its padding. It goes last: it runs down to the
404
+ * bottom edge too.
405
+ *
406
+ * @internal
407
+ */
408
+ function ScrollAreaBleed(props) {
409
+ let $ = c(7), { left, right, bottom } = useContext(ScrollAreaInsetContext), t0 = -left, t1 = -right, t2 = -bottom, t3;
410
+ $[0] !== t0 || $[1] !== t1 || $[2] !== t2 ? (t3 = {
411
+ marginLeft: t0,
412
+ marginRight: t1,
413
+ marginBottom: t2
414
+ }, $[0] = t0, $[1] = t1, $[2] = t2, $[3] = t3) : t3 = $[3];
415
+ let t4;
416
+ return $[4] !== props.children || $[5] !== t3 ? (t4 = /* @__PURE__ */ jsx("div", {
417
+ style: t3,
418
+ children: props.children
419
+ }), $[4] = props.children, $[5] = t3, $[6] = t4) : t4 = $[6], t4;
420
+ }
172
421
  /**
173
422
  * The dark scheme covers the right side of the preview behind a diagonal cut,
174
423
  * like the "Auto" appearance preview in macOS System Settings shows both
@@ -1116,6 +1365,9 @@ function _temp$2(event_0) {
1116
1365
  const SCHEME_TITLES = {
1117
1366
  dark: "Dark mode",
1118
1367
  light: "Light mode"
1368
+ }, SPLIT_HINTS = {
1369
+ dark: "Shown on the right",
1370
+ light: "Shown on the left"
1119
1371
  }, SCHEME_OPTION_KEYS = [
1120
1372
  "accent",
1121
1373
  "text",
@@ -1123,21 +1375,22 @@ const SCHEME_TITLES = {
1123
1375
  "contrast"
1124
1376
  ], Swatch = styled.input.withConfig({
1125
1377
  displayName: "Swatch",
1126
- componentId: "sc-vs5143-0"
1378
+ componentId: "sc-rahyxu-0"
1127
1379
  })`box-sizing:border-box;flex:none;width:33px;height:33px;padding:0;border:1px solid var(--card-border-color);border-radius:4px;background:none;cursor:pointer;&::-webkit-color-swatch-wrapper{padding:0;}&::-webkit-color-swatch{border:none;border-radius:3px;}&::-moz-color-swatch{border:none;border-radius:3px;}`, Range = styled.input.withConfig({
1128
1380
  displayName: "Range",
1129
- componentId: "sc-vs5143-1"
1381
+ componentId: "sc-rahyxu-1"
1130
1382
  })`display:block;width:100%;margin:0;accent-color:var(--card-focus-ring-color);`;
1131
1383
  /**
1132
1384
  * The flow for editing one of the user's own themes: its title, a live
1133
1385
  * preview, and a card per color scheme with the accent/text/background
1134
1386
  * pickers and the contrast slider of that scheme. Every change applies to the
1135
- * whole Studio right away — in the scheme the Studio is showing.
1387
+ * whole Studio right away — in the scheme the Studio is showing, or in both
1388
+ * side by side in the split preview.
1136
1389
  *
1137
1390
  * @internal
1138
1391
  */
1139
1392
  function ThemeEditor(props) {
1140
- let $ = c(40), { focusTitle, slug } = props, { themes, images, send } = useThemer(), t0;
1393
+ let $ = c(41), { focusTitle, slug } = props, { themes, images, split, send } = useThemer(), t0;
1141
1394
  if ($[0] !== slug || $[1] !== themes) {
1142
1395
  let t1;
1143
1396
  $[3] === slug ? t1 = $[4] : (t1 = (candidate) => candidate.slug === slug && candidate.source === "custom", $[3] = slug, $[4] = t1), t0 = themes.find(t1), $[0] = slug, $[1] = themes, $[2] = t0;
@@ -1182,7 +1435,7 @@ function ThemeEditor(props) {
1182
1435
  title
1183
1436
  }), $[23] = send, $[24] = slug, $[25] = t7, $[26] = t8) : (t7 = $[25], t8 = $[26]);
1184
1437
  let t9;
1185
- return $[27] !== applyVariant || $[28] !== focusTitle || $[29] !== t2 || $[30] !== t3 || $[31] !== t4 || $[32] !== t5 || $[33] !== t6 || $[34] !== t7 || $[35] !== t8 || $[36] !== theme.options || $[37] !== theme.palette || $[38] !== theme.title ? (t9 = /* @__PURE__ */ jsx(ThemeEditorForm, {
1438
+ return $[27] !== applyVariant || $[28] !== focusTitle || $[29] !== split || $[30] !== t2 || $[31] !== t3 || $[32] !== t4 || $[33] !== t5 || $[34] !== t6 || $[35] !== t7 || $[36] !== t8 || $[37] !== theme.options || $[38] !== theme.palette || $[39] !== theme.title ? (t9 = /* @__PURE__ */ jsx(ThemeEditorForm, {
1186
1439
  focusTitle,
1187
1440
  imageUrl: t2,
1188
1441
  onDone: t3,
@@ -1194,11 +1447,12 @@ function ThemeEditor(props) {
1194
1447
  onVariant: applyVariant,
1195
1448
  options: theme.options,
1196
1449
  palette: theme.palette,
1450
+ split,
1197
1451
  title: theme.title
1198
- }), $[27] = applyVariant, $[28] = focusTitle, $[29] = t2, $[30] = t3, $[31] = t4, $[32] = t5, $[33] = t6, $[34] = t7, $[35] = t8, $[36] = theme.options, $[37] = theme.palette, $[38] = theme.title, $[39] = t9) : t9 = $[39], t9;
1452
+ }), $[27] = applyVariant, $[28] = focusTitle, $[29] = split, $[30] = t2, $[31] = t3, $[32] = t4, $[33] = t5, $[34] = t6, $[35] = t7, $[36] = t8, $[37] = theme.options, $[38] = theme.palette, $[39] = theme.title, $[40] = t9) : t9 = $[40], t9;
1199
1453
  }
1200
1454
  function ThemeEditorForm(props) {
1201
- let $ = c(46), { focusTitle, imageUrl, onDone, onLucky, onOptionsChange, onPalette, onRemove, onTitleChange, onVariant, options, palette, title } = props, studioScheme = useColorSchemeValue(), t0;
1455
+ let $ = c(49), { focusTitle, imageUrl, onDone, onLucky, onOptionsChange, onPalette, onRemove, onTitleChange, onVariant, options, palette, split, title } = props, studioScheme = useColorSchemeValue(), t0;
1202
1456
  $[0] === options ? t0 = $[1] : (t0 = resolveThemeOptions(options), $[0] = options, $[1] = t0);
1203
1457
  let resolved = t0, t1;
1204
1458
  $[2] === options ? t1 = $[3] : (t1 = buildPalette(options), $[2] = options, $[3] = t1);
@@ -1245,16 +1499,19 @@ function ThemeEditorForm(props) {
1245
1499
  palette
1246
1500
  }), $[17] = imageUrl, $[18] = onLucky, $[19] = onPalette, $[20] = onVariant, $[21] = options, $[22] = palette, $[23] = t8) : t8 = $[23];
1247
1501
  let t9;
1248
- $[24] !== options || $[25] !== palettes || $[26] !== patchScheme || $[27] !== resolved || $[28] !== studioScheme ? (t9 = SCHEMES.map((scheme_0) => /* @__PURE__ */ jsx(SchemeCard, {
1249
- active: scheme_0 === studioScheme,
1502
+ $[24] !== options || $[25] !== palettes || $[26] !== patchScheme || $[27] !== resolved || $[28] !== split || $[29] !== studioScheme ? (t9 = SCHEMES.map((scheme_0) => /* @__PURE__ */ jsx(SchemeCard, {
1503
+ active: split || scheme_0 === studioScheme,
1250
1504
  onChange: (changes_0) => patchScheme(scheme_0, changes_0),
1251
1505
  options: options[scheme_0] ?? {},
1252
1506
  palette: palettes[scheme_0],
1253
1507
  resolved: resolved[scheme_0],
1254
- scheme: scheme_0
1255
- }, scheme_0)), $[24] = options, $[25] = palettes, $[26] = patchScheme, $[27] = resolved, $[28] = studioScheme, $[29] = t9) : t9 = $[29];
1508
+ scheme: scheme_0,
1509
+ split
1510
+ }, scheme_0)), $[24] = options, $[25] = palettes, $[26] = patchScheme, $[27] = resolved, $[28] = split, $[29] = studioScheme, $[30] = t9) : t9 = $[30];
1256
1511
  let t10;
1257
- $[30] !== t3 || $[31] !== t7 || $[32] !== t8 || $[33] !== t9 ? (t10 = /* @__PURE__ */ jsx(ScrollArea, {
1512
+ $[31] === t9 ? t10 = $[32] : (t10 = /* @__PURE__ */ jsx(ScrollAreaBleed, { children: t9 }), $[31] = t9, $[32] = t10);
1513
+ let t11;
1514
+ $[33] !== t10 || $[34] !== t3 || $[35] !== t7 || $[36] !== t8 ? (t11 = /* @__PURE__ */ jsx(ScrollArea, {
1258
1515
  padding: 3,
1259
1516
  children: /* @__PURE__ */ jsxs(Stack, {
1260
1517
  gap: 4,
@@ -1262,54 +1519,56 @@ function ThemeEditorForm(props) {
1262
1519
  t3,
1263
1520
  t7,
1264
1521
  t8,
1265
- t9
1522
+ t10
1266
1523
  ]
1267
1524
  })
1268
- }), $[30] = t3, $[31] = t7, $[32] = t8, $[33] = t9, $[34] = t10) : t10 = $[34];
1269
- let t11;
1270
- $[35] === onRemove ? t11 = $[36] : (t11 = /* @__PURE__ */ jsx(TooltipButton, {
1525
+ }), $[33] = t10, $[34] = t3, $[35] = t7, $[36] = t8, $[37] = t11) : t11 = $[37];
1526
+ let t12;
1527
+ $[38] === onRemove ? t12 = $[39] : (t12 = /* @__PURE__ */ jsx(TooltipButton, {
1271
1528
  icon: TrashIcon,
1272
1529
  mode: "ghost",
1273
1530
  onClick: onRemove,
1274
1531
  text: "Remove",
1275
1532
  tone: "critical",
1276
1533
  tooltip: "Remove the theme — it can be restored until it is deleted"
1277
- }), $[35] = onRemove, $[36] = t11);
1278
- let t12;
1279
- $[37] === Symbol.for("react.memo_cache_sentinel") ? (t12 = /* @__PURE__ */ jsx(Box, { flex: 1 }), $[37] = t12) : t12 = $[37];
1534
+ }), $[38] = onRemove, $[39] = t12);
1280
1535
  let t13;
1281
- $[38] === onDone ? t13 = $[39] : (t13 = /* @__PURE__ */ jsx(Button, {
1536
+ $[40] === Symbol.for("react.memo_cache_sentinel") ? (t13 = /* @__PURE__ */ jsx(Box, { flex: 1 }), $[40] = t13) : t13 = $[40];
1537
+ let t14;
1538
+ $[41] === onDone ? t14 = $[42] : (t14 = /* @__PURE__ */ jsx(Button, {
1282
1539
  mode: "ghost",
1283
1540
  onClick: onDone,
1284
1541
  text: "Done"
1285
- }), $[38] = onDone, $[39] = t13);
1286
- let t14;
1287
- $[40] !== t11 || $[41] !== t13 ? (t14 = /* @__PURE__ */ jsx(Card, {
1542
+ }), $[41] = onDone, $[42] = t14);
1543
+ let t15;
1544
+ $[43] !== t12 || $[44] !== t14 ? (t15 = /* @__PURE__ */ jsx(Card, {
1288
1545
  borderTop: !0,
1289
1546
  padding: 3,
1290
1547
  children: /* @__PURE__ */ jsxs(Flex, {
1291
1548
  gap: 2,
1292
1549
  children: [
1293
- t11,
1294
1550
  t12,
1295
- t13
1551
+ t13,
1552
+ t14
1296
1553
  ]
1297
1554
  })
1298
- }), $[40] = t11, $[41] = t13, $[42] = t14) : t14 = $[42];
1299
- let t15;
1300
- return $[43] !== t10 || $[44] !== t14 ? (t15 = /* @__PURE__ */ jsxs(Fragment, { children: [t10, t14] }), $[43] = t10, $[44] = t14, $[45] = t15) : t15 = $[45], t15;
1555
+ }), $[43] = t12, $[44] = t14, $[45] = t15) : t15 = $[45];
1556
+ let t16;
1557
+ return $[46] !== t11 || $[47] !== t15 ? (t16 = /* @__PURE__ */ jsxs(Fragment, { children: [t11, t15] }), $[46] = t11, $[47] = t15, $[48] = t16) : t16 = $[48], t16;
1301
1558
  }
1302
1559
  function _temp$1(event_0) {
1303
1560
  return event_0.currentTarget.select();
1304
1561
  }
1305
1562
  /**
1306
- * The colors of one scheme, on a card painted in that scheme so that each
1307
- * card shows the colors it edits. The scheme the Studio is showing is marked
1308
- * as active: its changes show up in the Studio right away, the other scheme's
1309
- * only once the appearance setting switches to it.
1563
+ * The colors of one scheme. The cards span the sidebar edge to edge, one
1564
+ * after the other with a border between them, in the sidebar's own scheme —
1565
+ * the swatches show the colors they edit. The scheme the Studio is showing is
1566
+ * marked as active: its changes show up in the Studio right away, the other
1567
+ * scheme's only once the appearance setting switches to it — or both are
1568
+ * active, in the split preview.
1310
1569
  */
1311
1570
  function SchemeCard(props) {
1312
- let $ = c(69), { active, onChange, options, palette, resolved, scheme } = props, name = SCHEME_TITLES[scheme], t0;
1571
+ let $ = c(66), { active, onChange, options, palette, resolved, scheme, split } = props, name = SCHEME_TITLES[scheme], t0;
1313
1572
  $[0] === name ? t0 = $[1] : (t0 = /* @__PURE__ */ jsx(Box, {
1314
1573
  flex: 1,
1315
1574
  children: /* @__PURE__ */ jsx(Text, {
@@ -1330,7 +1589,7 @@ function SchemeCard(props) {
1330
1589
  gap: 2,
1331
1590
  children: [t0, t1]
1332
1591
  }), $[4] = t0, $[5] = t1, $[6] = t2) : t2 = $[6];
1333
- let t3 = active ? "Shown in the Studio right now" : `Shown once the Studio appearance is ${scheme}`, t4;
1592
+ let t3 = split ? SPLIT_HINTS[scheme] : active ? "Shown in the Studio right now" : `Shown once the Studio appearance is ${scheme}`, t4;
1334
1593
  $[7] === t3 ? t4 = $[8] : (t4 = /* @__PURE__ */ jsx(Text, {
1335
1594
  muted: !0,
1336
1595
  size: 0,
@@ -1433,25 +1692,20 @@ function SchemeCard(props) {
1433
1692
  ]
1434
1693
  }), $[57] = t26, $[58] = t29, $[59] = t31) : t31 = $[59];
1435
1694
  let t32;
1436
- $[60] !== t10 || $[61] !== t15 || $[62] !== t21 || $[63] !== t31 || $[64] !== t5 ? (t32 = /* @__PURE__ */ jsxs(Stack, {
1437
- gap: 4,
1438
- children: [
1439
- t5,
1440
- t10,
1441
- t15,
1442
- t21,
1443
- t31
1444
- ]
1445
- }), $[60] = t10, $[61] = t15, $[62] = t21, $[63] = t31, $[64] = t5, $[65] = t32) : t32 = $[65];
1446
- let t33;
1447
- return $[66] !== scheme || $[67] !== t32 ? (t33 = /* @__PURE__ */ jsx(Card, {
1448
- border: !0,
1695
+ return $[60] !== t10 || $[61] !== t15 || $[62] !== t21 || $[63] !== t31 || $[64] !== t5 ? (t32 = /* @__PURE__ */ jsx(Card, {
1696
+ borderTop: !0,
1449
1697
  padding: 3,
1450
- radius: 3,
1451
- scheme,
1452
- tone: "default",
1453
- children: t32
1454
- }), $[66] = scheme, $[67] = t32, $[68] = t33) : t33 = $[68], t33;
1698
+ children: /* @__PURE__ */ jsxs(Stack, {
1699
+ gap: 4,
1700
+ children: [
1701
+ t5,
1702
+ t10,
1703
+ t15,
1704
+ t21,
1705
+ t31
1706
+ ]
1707
+ })
1708
+ }), $[60] = t10, $[61] = t15, $[62] = t21, $[63] = t31, $[64] = t5, $[65] = t32) : t32 = $[65], t32;
1455
1709
  }
1456
1710
  /** One color of a scheme: a swatch with its value and an optional reset button */
1457
1711
  function ColorRow(props) {
@@ -1510,16 +1764,16 @@ function ColorRow(props) {
1510
1764
  }
1511
1765
  const Root = styled.div.withConfig({
1512
1766
  displayName: "Root",
1513
- componentId: "sc-a1a6d4-0"
1767
+ componentId: "sc-krwbzw-0"
1514
1768
  })`position:relative;`, PickButton = styled.button.withConfig({
1515
1769
  displayName: "PickButton",
1516
- componentId: "sc-a1a6d4-1"
1517
- })`appearance:none;display:block;box-sizing:border-box;width:100%;margin:0;padding:0;border:0;background:none;color:inherit;font:inherit;text-align:center;cursor:pointer;&:focus{outline:none;}`, Frame = styled.span.withConfig({
1770
+ componentId: "sc-krwbzw-1"
1771
+ })`appearance:none;display:block;box-sizing:border-box;width:100%;margin:0;padding:0;border:0;background:none;color:inherit;font:inherit;text-align:center;cursor:pointer;&:focus{outline:none;}`, Frame$1 = styled.span.withConfig({
1518
1772
  displayName: "Frame",
1519
- componentId: "sc-a1a6d4-2"
1773
+ componentId: "sc-krwbzw-2"
1520
1774
  })`display:block;padding:4px;border-radius:10px;transition:box-shadow 100ms;${PickButton}:hover &{box-shadow:inset 0 0 0 2px var(--card-border-color);}${PickButton}[aria-pressed='true'] &{box-shadow:inset 0 0 0 2px var(--card-focus-ring-color);}${PickButton}:focus-visible &{outline:2px solid var(--card-focus-ring-color);outline-offset:-2px;}`, MenuSlot = styled.div.withConfig({
1521
1775
  displayName: "MenuSlot",
1522
- componentId: "sc-a1a6d4-3"
1776
+ componentId: "sc-krwbzw-3"
1523
1777
  })`position:absolute;top:10px;right:10px;opacity:0;transition:opacity 100ms;${Root}:hover &,${Root}:focus-within &,&[data-visible='true']{opacity:1;}`;
1524
1778
  /**
1525
1779
  * One theme in the list: the floating preview with the title below it, like
@@ -1530,7 +1784,7 @@ const Root = styled.div.withConfig({
1530
1784
  * @internal
1531
1785
  */
1532
1786
  function ThemeCard(props) {
1533
- let $ = c(50), { active, theme } = props, { send } = useThemer(), [menuOpen, setMenuOpen] = useState(!1), t0;
1787
+ let $ = c(52), { active, theme } = props, { send } = useThemer(), { scheme } = useRootTheme(), [menuOpen, setMenuOpen] = useState(!1), t0;
1534
1788
  $[0] === theme.title ? t0 = $[1] : (t0 = displayTitle(theme.title), $[0] = theme.title, $[1] = t0);
1535
1789
  let title = t0, t1;
1536
1790
  $[2] !== send || $[3] !== theme.slug ? (t1 = () => send({
@@ -1538,7 +1792,7 @@ function ThemeCard(props) {
1538
1792
  slug: theme.slug
1539
1793
  }), $[2] = send, $[3] = theme.slug, $[4] = t1) : t1 = $[4];
1540
1794
  let t2;
1541
- $[5] === theme.options ? t2 = $[6] : (t2 = /* @__PURE__ */ jsx(Frame, { children: /* @__PURE__ */ jsx(ThemeThumbnail, { options: theme.options }) }), $[5] = theme.options, $[6] = t2);
1795
+ $[5] === theme.options ? t2 = $[6] : (t2 = /* @__PURE__ */ jsx(Frame$1, { children: /* @__PURE__ */ jsx(ThemeThumbnail, { options: theme.options }) }), $[5] = theme.options, $[6] = t2);
1542
1796
  let t3 = active ? "medium" : "regular", t4;
1543
1797
  $[7] !== t3 || $[8] !== title ? (t4 = /* @__PURE__ */ jsx(Box, {
1544
1798
  as: "span",
@@ -1613,19 +1867,24 @@ function ThemeCard(props) {
1613
1867
  t14,
1614
1868
  t15
1615
1869
  ] }), $[33] = t11, $[34] = t14, $[35] = t15, $[36] = t16) : t16 = $[36];
1616
- let t17, t18, t19;
1617
- $[37] === Symbol.for("react.memo_cache_sentinel") ? (t17 = () => setMenuOpen(!1), t18 = () => setMenuOpen(!0), t19 = {
1870
+ let t17, t18;
1871
+ $[37] === Symbol.for("react.memo_cache_sentinel") ? (t17 = () => setMenuOpen(!1), t18 = () => setMenuOpen(!0), $[37] = t17, $[38] = t18) : (t17 = $[37], t18 = $[38]);
1872
+ let t19;
1873
+ $[39] === scheme ? t19 = $[40] : (t19 = {
1618
1874
  animate: !0,
1619
1875
  placement: "bottom-end",
1620
- portal: !0
1621
- }, $[37] = t17, $[38] = t18, $[39] = t19) : (t17 = $[37], t18 = $[38], t19 = $[39]);
1876
+ portal: !0,
1877
+ scheme
1878
+ }, $[39] = scheme, $[40] = t19);
1622
1879
  let t20;
1623
- $[40] !== t10 || $[41] !== t16 || $[42] !== t9 ? (t20 = /* @__PURE__ */ jsx(Tooltip, {
1880
+ $[41] !== t10 || $[42] !== t16 || $[43] !== t19 || $[44] !== t9 ? (t20 = /* @__PURE__ */ jsx(Tooltip, {
1624
1881
  animate: !0,
1625
1882
  content: t6,
1626
1883
  placement: "bottom",
1627
1884
  portal: !0,
1628
- children: /* @__PURE__ */ jsx("span", {
1885
+ children: /* @__PURE__ */ jsx(Card, {
1886
+ radius: 2,
1887
+ scheme: "dark",
1629
1888
  style: t7,
1630
1889
  children: /* @__PURE__ */ jsx(MenuButton, {
1631
1890
  button: t9,
@@ -1636,14 +1895,14 @@ function ThemeCard(props) {
1636
1895
  popover: t19
1637
1896
  })
1638
1897
  })
1639
- }), $[40] = t10, $[41] = t16, $[42] = t9, $[43] = t20) : t20 = $[43];
1898
+ }), $[41] = t10, $[42] = t16, $[43] = t19, $[44] = t9, $[45] = t20) : t20 = $[45];
1640
1899
  let t21;
1641
- $[44] !== menuOpen || $[45] !== t20 ? (t21 = /* @__PURE__ */ jsx(MenuSlot, {
1900
+ $[46] !== menuOpen || $[47] !== t20 ? (t21 = /* @__PURE__ */ jsx(MenuSlot, {
1642
1901
  "data-visible": menuOpen,
1643
1902
  children: t20
1644
- }), $[44] = menuOpen, $[45] = t20, $[46] = t21) : t21 = $[46];
1903
+ }), $[46] = menuOpen, $[47] = t20, $[48] = t21) : t21 = $[48];
1645
1904
  let t22;
1646
- return $[47] !== t21 || $[48] !== t5 ? (t22 = /* @__PURE__ */ jsxs(Root, { children: [t5, t21] }), $[47] = t21, $[48] = t5, $[49] = t22) : t22 = $[49], t22;
1905
+ return $[49] !== t21 || $[50] !== t5 ? (t22 = /* @__PURE__ */ jsxs(Root, { children: [t5, t21] }), $[49] = t21, $[50] = t5, $[51] = t22) : t22 = $[51], t22;
1647
1906
  }
1648
1907
  /**
1649
1908
  * The flow for picking a theme: one column of theme cards — the configured
@@ -1843,14 +2102,15 @@ const VIEW_TITLES = {
1843
2102
  removed: "Removed themes"
1844
2103
  };
1845
2104
  /**
1846
- * The themer sidebar: a header that navigates between the flows, the flow
1847
- * itself — picking a theme, editing one, or restoring removed ones — and the
1848
- * dialog with the `buildTheme` snippet of the applied theme.
2105
+ * The themer sidebar: a header that navigates between the flows and toggles
2106
+ * the split preview, the flow itself — picking a theme, editing one, or
2107
+ * restoring removed ones — and the dialog with the `buildTheme` snippet of
2108
+ * the applied theme.
1849
2109
  *
1850
2110
  * @internal
1851
2111
  */
1852
2112
  function ThemerSidebar() {
1853
- let $ = c(37), { active, view, send } = useThemer(), [snippetOpen, setSnippetOpen] = useState(!1), inList = view.name === "list", t0;
2113
+ let $ = c(41), { active, split, view, send } = useThemer(), [snippetOpen, setSnippetOpen] = useState(!1), inList = view.name === "list", t0;
1854
2114
  $[0] !== inList || $[1] !== send ? (t0 = !inList && /* @__PURE__ */ jsx(TooltipButton, {
1855
2115
  icon: ArrowLeftIcon,
1856
2116
  mode: "bleed",
@@ -1875,23 +2135,33 @@ function ThemerSidebar() {
1875
2135
  children: t4
1876
2136
  }), $[6] = t1, $[7] = t4, $[8] = t5) : t5 = $[8];
1877
2137
  let t6;
1878
- $[9] === view.name ? t6 = $[10] : (t6 = view.name !== "removed" && /* @__PURE__ */ jsx(TooltipButton, {
2138
+ $[9] !== send || $[10] !== split || $[11] !== view.name ? (t6 = view.name !== "removed" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(TooltipButton, {
2139
+ "aria-pressed": split,
2140
+ icon: SplitVerticalIcon,
2141
+ mode: "bleed",
2142
+ onClick: () => send({ type: "preview.toggle" }),
2143
+ padding: 2,
2144
+ selected: split,
2145
+ tooltip: "Show light and dark side by side"
2146
+ }), /* @__PURE__ */ jsx(TooltipButton, {
1879
2147
  icon: CodeBlockIcon,
1880
2148
  mode: "bleed",
1881
2149
  onClick: () => setSnippetOpen(!0),
1882
2150
  padding: 2,
1883
2151
  tooltip: "Show the code for the applied theme"
1884
- }), $[9] = view.name, $[10] = t6);
2152
+ })] }), $[9] = send, $[10] = split, $[11] = view.name, $[12] = t6) : t6 = $[12];
1885
2153
  let t7;
1886
- $[11] === send ? t7 = $[12] : (t7 = /* @__PURE__ */ jsx(TooltipButton, {
2154
+ $[13] === send ? t7 = $[14] : (t7 = /* @__PURE__ */ jsx(TooltipButton, {
1887
2155
  icon: CloseIcon,
1888
2156
  mode: "bleed",
1889
2157
  onClick: () => send({ type: "sidebar.close" }),
1890
2158
  padding: 2,
1891
2159
  tooltip: "Close themer"
1892
- }), $[11] = send, $[12] = t7);
2160
+ }), $[13] = send, $[14] = t7);
1893
2161
  let t8;
1894
- $[13] !== t0 || $[14] !== t5 || $[15] !== t6 || $[16] !== t7 ? (t8 = /* @__PURE__ */ jsx(Card, {
2162
+ $[15] !== t6 || $[16] !== t7 ? (t8 = /* @__PURE__ */ jsxs(Flex, { children: [t6, t7] }), $[15] = t6, $[16] = t7, $[17] = t8) : t8 = $[17];
2163
+ let t9;
2164
+ $[18] !== t0 || $[19] !== t5 || $[20] !== t8 ? (t9 = /* @__PURE__ */ jsx(Card, {
1895
2165
  padding: 2,
1896
2166
  children: /* @__PURE__ */ jsxs(Flex, {
1897
2167
  align: "center",
@@ -1899,41 +2169,40 @@ function ThemerSidebar() {
1899
2169
  children: [
1900
2170
  t0,
1901
2171
  t5,
1902
- t6,
1903
- t7
2172
+ t8
1904
2173
  ]
1905
2174
  })
1906
- }), $[13] = t0, $[14] = t5, $[15] = t6, $[16] = t7, $[17] = t8) : t8 = $[17];
1907
- let t9;
1908
- $[18] === view.name ? t9 = $[19] : (t9 = view.name === "list" && /* @__PURE__ */ jsx(ThemeList, {}), $[18] = view.name, $[19] = t9);
2175
+ }), $[18] = t0, $[19] = t5, $[20] = t8, $[21] = t9) : t9 = $[21];
1909
2176
  let t10;
1910
- $[20] !== view.focusTitle || $[21] !== view.name || $[22] !== view.slug ? (t10 = view.name === "edit" && /* @__PURE__ */ jsx(ThemeEditor, {
2177
+ $[22] === view.name ? t10 = $[23] : (t10 = view.name === "list" && /* @__PURE__ */ jsx(ThemeList, {}), $[22] = view.name, $[23] = t10);
2178
+ let t11;
2179
+ $[24] !== view.focusTitle || $[25] !== view.name || $[26] !== view.slug ? (t11 = view.name === "edit" && /* @__PURE__ */ jsx(ThemeEditor, {
1911
2180
  focusTitle: view.focusTitle,
1912
2181
  slug: view.slug
1913
- }), $[20] = view.focusTitle, $[21] = view.name, $[22] = view.slug, $[23] = t10) : t10 = $[23];
1914
- let t11;
1915
- $[24] === view.name ? t11 = $[25] : (t11 = view.name === "removed" && /* @__PURE__ */ jsx(RemovedThemes, {}), $[24] = view.name, $[25] = t11);
2182
+ }), $[24] = view.focusTitle, $[25] = view.name, $[26] = view.slug, $[27] = t11) : t11 = $[27];
1916
2183
  let t12;
1917
- $[26] !== t10 || $[27] !== t11 || $[28] !== t8 || $[29] !== t9 ? (t12 = /* @__PURE__ */ jsxs(Flex, {
2184
+ $[28] === view.name ? t12 = $[29] : (t12 = view.name === "removed" && /* @__PURE__ */ jsx(RemovedThemes, {}), $[28] = view.name, $[29] = t12);
2185
+ let t13;
2186
+ $[30] !== t10 || $[31] !== t11 || $[32] !== t12 || $[33] !== t9 ? (t13 = /* @__PURE__ */ jsxs(Flex, {
1918
2187
  direction: "column",
1919
2188
  height: "fill",
1920
2189
  children: [
1921
- t8,
1922
2190
  t9,
1923
2191
  t10,
1924
- t11
2192
+ t11,
2193
+ t12
1925
2194
  ]
1926
- }), $[26] = t10, $[27] = t11, $[28] = t8, $[29] = t9, $[30] = t12) : t12 = $[30];
1927
- let t13;
1928
- $[31] !== active || $[32] !== snippetOpen ? (t13 = snippetOpen && /* @__PURE__ */ jsx(ThemeSnippetDialog, {
2195
+ }), $[30] = t10, $[31] = t11, $[32] = t12, $[33] = t9, $[34] = t13) : t13 = $[34];
2196
+ let t14;
2197
+ $[35] !== active || $[36] !== snippetOpen ? (t14 = snippetOpen && /* @__PURE__ */ jsx(ThemeSnippetDialog, {
1929
2198
  onClose: () => setSnippetOpen(!1),
1930
2199
  theme: active
1931
- }), $[31] = active, $[32] = snippetOpen, $[33] = t13) : t13 = $[33];
1932
- let t14;
1933
- return $[34] !== t12 || $[35] !== t13 ? (t14 = /* @__PURE__ */ jsxs(Card, {
2200
+ }), $[35] = active, $[36] = snippetOpen, $[37] = t14) : t14 = $[37];
2201
+ let t15;
2202
+ return $[38] !== t13 || $[39] !== t14 ? (t15 = /* @__PURE__ */ jsxs(Card, {
1934
2203
  height: "fill",
1935
- children: [t12, t13]
1936
- }), $[34] = t12, $[35] = t13, $[36] = t14) : t14 = $[36], t14;
2204
+ children: [t13, t14]
2205
+ }), $[38] = t13, $[39] = t14, $[40] = t15) : t15 = $[40], t15;
1937
2206
  }
1938
2207
  /**
1939
2208
  * The narrowest the sidebar goes — and its default: narrow enough to leave the
@@ -1957,27 +2226,25 @@ function writeStoredWidth(width) {
1957
2226
  width === 200 ? localStorage.removeItem(WIDTH_STORAGE_KEY) : localStorage.setItem(WIDTH_STORAGE_KEY, String(width));
1958
2227
  } catch {}
1959
2228
  }
1960
- /**
1961
- * The sidebar itself does not clip, so the resize handle can straddle its
1962
- * border — the content is clipped one level down instead
1963
- */
1964
2229
  const Sidebar = styled(Layer).withConfig({
1965
2230
  displayName: "Sidebar",
1966
- componentId: "sc-xkgsr1-0"
1967
- })`position:relative;flex:none;box-sizing:border-box;border-left:1px solid var(--card-border-color);`, ResizeHandle = styled.div.withConfig({
2231
+ componentId: "sc-ryrt6y-0"
2232
+ })`flex:none;`, Frame = styled(Card).withConfig({
2233
+ displayName: "Frame",
2234
+ componentId: "sc-ryrt6y-1"
2235
+ })`position:relative;`, ResizeHandle = styled.div.withConfig({
1968
2236
  displayName: "ResizeHandle",
1969
- componentId: "sc-xkgsr1-1"
2237
+ componentId: "sc-ryrt6y-2"
1970
2238
  })`position:absolute;top:0;bottom:0;left:-3px;z-index:1;width:7px;cursor:col-resize;touch-action:none;&::after{content:'';position:absolute;top:0;bottom:0;left:2px;width:3px;background:var(--card-focus-ring-color);opacity:0;transition:opacity 100ms;}&:hover::after,&:focus-visible::after,&[data-dragging='true']::after{opacity:1;}&:focus{outline:none;}`;
1971
2239
  /**
1972
- * Renders the themer sidebar next to the active tool, so the user can browse
1973
- * around their own studio while tweaking the theme. The sidebar's left edge
1974
- * drags (or arrow-keys) it wider, up to twice its default width — the width
1975
- * sticks between sessions.
2240
+ * The themer sidebar, in a layer along the right edge of the Studio. Its left
2241
+ * edge drags (or arrow-keys) it wider, up to twice its default width — the
2242
+ * width sticks between sessions.
1976
2243
  *
1977
2244
  * @internal
1978
2245
  */
1979
- function ThemerActiveToolLayout(props) {
1980
- let $ = c(20), { open } = useThemer(), [width, setWidth] = useState(readStoredWidth), [dragging, setDragging] = useState(!1), drag = useRef(null), t0, t1;
2246
+ function ResizableSidebar() {
2247
+ let $ = c(21), [width, setWidth] = useState(readStoredWidth), [dragging, setDragging] = useState(!1), drag = useRef(null), t0, t1;
1981
2248
  $[0] === width ? (t0 = $[1], t1 = $[2]) : (t0 = () => writeStoredWidth(width), t1 = [width], $[0] = width, $[1] = t0, $[2] = t1), useEffect(t0, t1);
1982
2249
  let t2;
1983
2250
  $[3] === width ? t2 = $[4] : (t2 = (event) => {
@@ -2006,250 +2273,44 @@ function ThemerActiveToolLayout(props) {
2006
2273
  step !== void 0 && (event_2.preventDefault(), setWidth((current) => clampWidth(current + step)));
2007
2274
  }, $[7] = t5) : t5 = $[7];
2008
2275
  let handleKeyDown = t5, t6;
2009
- $[8] === props ? t6 = $[9] : (t6 = props.renderDefault(props), $[8] = props, $[9] = t6);
2276
+ $[8] === width ? t6 = $[9] : (t6 = { width }, $[8] = width, $[9] = t6);
2010
2277
  let t7;
2011
- $[10] === t6 ? t7 = $[11] : (t7 = /* @__PURE__ */ jsx(Box, {
2012
- flex: 1,
2013
- height: "fill",
2014
- overflow: "auto",
2015
- children: t6
2016
- }), $[10] = t6, $[11] = t7);
2278
+ $[10] === Symbol.for("react.memo_cache_sentinel") ? (t7 = () => setWidth(200), $[10] = t7) : t7 = $[10];
2017
2279
  let t8;
2018
- $[12] !== dragging || $[13] !== handlePointerDown || $[14] !== open || $[15] !== width ? (t8 = open && /* @__PURE__ */ jsxs(Sidebar, {
2019
- height: "fill",
2020
- style: { width },
2021
- zOffset: 100,
2022
- children: [/* @__PURE__ */ jsx(ResizeHandle, {
2023
- "aria-label": "Resize the themer",
2024
- "aria-orientation": "vertical",
2025
- "aria-valuemax": 400,
2026
- "aria-valuemin": 200,
2027
- "aria-valuenow": width,
2028
- "data-dragging": dragging,
2029
- onDoubleClick: () => setWidth(200),
2030
- onKeyDown: handleKeyDown,
2031
- onPointerCancel: handlePointerUp,
2032
- onPointerDown: handlePointerDown,
2033
- onPointerMove: handlePointerMove,
2034
- onPointerUp: handlePointerUp,
2035
- role: "separator",
2036
- tabIndex: 0
2037
- }), /* @__PURE__ */ jsx(Box, {
2038
- height: "fill",
2039
- overflow: "hidden",
2040
- children: /* @__PURE__ */ jsx(ThemerSidebar, {})
2041
- })]
2042
- }), $[12] = dragging, $[13] = handlePointerDown, $[14] = open, $[15] = width, $[16] = t8) : t8 = $[16];
2280
+ $[11] !== dragging || $[12] !== handlePointerDown || $[13] !== width ? (t8 = /* @__PURE__ */ jsx(ResizeHandle, {
2281
+ "aria-label": "Resize the themer",
2282
+ "aria-orientation": "vertical",
2283
+ "aria-valuemax": 400,
2284
+ "aria-valuemin": 200,
2285
+ "aria-valuenow": width,
2286
+ "data-dragging": dragging,
2287
+ onDoubleClick: t7,
2288
+ onKeyDown: handleKeyDown,
2289
+ onPointerCancel: handlePointerUp,
2290
+ onPointerDown: handlePointerDown,
2291
+ onPointerMove: handlePointerMove,
2292
+ onPointerUp: handlePointerUp,
2293
+ role: "separator",
2294
+ tabIndex: 0
2295
+ }), $[11] = dragging, $[12] = handlePointerDown, $[13] = width, $[14] = t8) : t8 = $[14];
2043
2296
  let t9;
2044
- return $[17] !== t7 || $[18] !== t8 ? (t9 = /* @__PURE__ */ jsxs(Flex, {
2297
+ $[15] === Symbol.for("react.memo_cache_sentinel") ? (t9 = /* @__PURE__ */ jsx(Box, {
2045
2298
  height: "fill",
2046
- sizing: "border",
2047
- children: [t7, t8]
2048
- }), $[17] = t7, $[18] = t8, $[19] = t9) : t9 = $[19], t9;
2049
- }
2050
- function themesOf(context) {
2051
- return resolveThemes(context, context.baseOptions);
2052
- }
2053
- /**
2054
- * The state of the themer tool: two parallel regions, whether the `sidebar`
2055
- * is open and which `flow` it is in — picking a theme from the `list`,
2056
- * `edit`ing one of the user's own themes, or restoring `removed` ones. The
2057
- * context carries the persisted state (the applied theme, the user's themes
2058
- * and what was removed) alongside what the flows need.
2059
- *
2060
- * Theme operations are handled in every flow, and the flows leave on their
2061
- * own when they lose their subject: the editor when its theme is removed or
2062
- * deleted, and the removed view when its last theme is restored or deleted.
2063
- *
2064
- * @internal
2065
- */
2066
- const themerMachine = setup({
2067
- types: {
2068
- context: {},
2069
- events: {},
2070
- input: {}
2071
- },
2072
- guards: {
2073
- isCustomTheme: ({ context }, params) => themesOf(context).themes.some((theme) => theme.slug === params.slug && theme.source === "custom"),
2074
- isEditingListedTheme: ({ context }) => context.editing !== null && themesOf(context).themes.some((theme) => theme.slug === context.editing?.slug && theme.source === "custom"),
2075
- hasRemovedThemes: ({ context }) => themesOf(context).removed.length > 0
2076
- },
2077
- actions: {
2078
- pick: assign((_, params) => ({ active: params.slug === "config" ? null : params.slug })),
2079
- add: assign(({ context }, params) => {
2080
- let theme = createCustomTheme(params.title ?? "Untitled theme", params.options ?? themesOf(context).active.options, params.palette);
2081
- return {
2082
- active: theme.slug,
2083
- custom: [...context.custom, theme],
2084
- editing: {
2085
- slug: theme.slug,
2086
- focusTitle: params.title === void 0
2087
- },
2088
- images: params.imageUrl ? {
2089
- ...context.images,
2090
- [theme.slug]: params.imageUrl
2091
- } : context.images
2092
- };
2093
- }),
2094
- duplicate: assign(({ context }, params) => {
2095
- let { themes, removed } = themesOf(context), source = [...themes, ...removed].find((theme) => theme.slug === params.slug);
2096
- if (!source) return {};
2097
- let theme = createCustomTheme(duplicateTitle(source.title), source.options, source.palette);
2098
- return {
2099
- active: theme.slug,
2100
- custom: [...context.custom, theme],
2101
- editing: {
2102
- slug: theme.slug,
2103
- focusTitle: !0
2104
- }
2105
- };
2106
- }),
2107
- startEditing: assign((_, params) => ({
2108
- active: params.slug,
2109
- editing: {
2110
- slug: params.slug,
2111
- focusTitle: !1
2112
- }
2113
- })),
2114
- stopEditing: assign({ editing: null }),
2115
- update: assign(({ context }, params) => ({
2116
- custom: context.custom.map((theme) => theme.slug === params.slug ? {
2117
- ...theme,
2118
- title: params.title ?? theme.title,
2119
- options: params.options ?? theme.options,
2120
- ...params.palette ? { palette: params.palette } : {}
2121
- } : theme),
2122
- images: params.imageUrl && context.custom.some((theme) => theme.slug === params.slug) ? {
2123
- ...context.images,
2124
- [params.slug]: params.imageUrl
2125
- } : context.images
2126
- })),
2127
- remove: assign(({ context }, params) => params.slug === "config" || context.removed.includes(params.slug) ? {} : {
2128
- active: context.active === params.slug ? null : context.active,
2129
- removed: [...context.removed, params.slug]
2130
- }),
2131
- restore: assign(({ context }, params) => ({ removed: context.removed.filter((slug) => slug !== params.slug) })),
2132
- delete: assign(({ context }, params) => {
2133
- if (!context.custom.some((theme) => theme.slug === params.slug)) return {};
2134
- let { [params.slug]: _deleted, ...images } = context.images;
2135
- return {
2136
- active: context.active === params.slug ? null : context.active,
2137
- custom: context.custom.filter((theme) => theme.slug !== params.slug),
2138
- removed: context.removed.filter((slug) => slug !== params.slug),
2139
- images
2140
- };
2141
- })
2142
- }
2143
- }).createMachine({
2144
- id: "themer",
2145
- context: ({ input }) => ({
2146
- ...input.stored,
2147
- baseOptions: input.baseOptions,
2148
- editing: null,
2149
- images: {}
2150
- }),
2151
- type: "parallel",
2152
- states: {
2153
- sidebar: {
2154
- initial: "closed",
2155
- states: {
2156
- closed: { on: { "sidebar.toggle": "open" } },
2157
- open: { on: {
2158
- "sidebar.toggle": "closed",
2159
- "sidebar.close": "closed"
2160
- } }
2161
- }
2162
- },
2163
- flow: {
2164
- initial: "list",
2165
- on: {
2166
- "theme.pick": { actions: {
2167
- type: "pick",
2168
- params: ({ event }) => ({ slug: event.slug })
2169
- } },
2170
- "theme.add": {
2171
- target: ".edit",
2172
- actions: {
2173
- type: "add",
2174
- params: ({ event }) => ({
2175
- title: event.title,
2176
- options: event.options,
2177
- palette: event.palette,
2178
- imageUrl: event.imageUrl
2179
- })
2180
- }
2181
- },
2182
- "theme.duplicate": {
2183
- target: ".edit",
2184
- actions: {
2185
- type: "duplicate",
2186
- params: ({ event }) => ({ slug: event.slug })
2187
- }
2188
- },
2189
- "theme.edit": {
2190
- target: ".edit",
2191
- guard: {
2192
- type: "isCustomTheme",
2193
- params: ({ event }) => ({ slug: event.slug })
2194
- },
2195
- actions: {
2196
- type: "startEditing",
2197
- params: ({ event }) => ({ slug: event.slug })
2198
- }
2199
- },
2200
- "theme.update": { actions: {
2201
- type: "update",
2202
- params: ({ event }) => ({
2203
- slug: event.slug,
2204
- title: event.title,
2205
- options: event.options,
2206
- palette: event.palette,
2207
- imageUrl: event.imageUrl
2208
- })
2209
- } },
2210
- "theme.remove": { actions: {
2211
- type: "remove",
2212
- params: ({ event }) => ({ slug: event.slug })
2213
- } },
2214
- "theme.restore": { actions: {
2215
- type: "restore",
2216
- params: ({ event }) => ({ slug: event.slug })
2217
- } },
2218
- "theme.delete": { actions: {
2219
- type: "delete",
2220
- params: ({ event }) => ({ slug: event.slug })
2221
- } },
2222
- "flow.list": ".list",
2223
- "flow.removed": {
2224
- target: ".removed",
2225
- guard: "hasRemovedThemes"
2226
- }
2227
- },
2228
- states: {
2229
- list: {},
2230
- edit: {
2231
- exit: "stopEditing",
2232
- always: {
2233
- guard: not("isEditingListedTheme"),
2234
- target: "list"
2235
- }
2236
- },
2237
- removed: { always: {
2238
- guard: not("hasRemovedThemes"),
2239
- target: "list"
2240
- } }
2241
- }
2242
- }
2243
- }
2244
- });
2245
- /** The part of the machine context that is persisted between sessions @internal */
2246
- function selectStoredState(snapshot) {
2247
- let { active, custom, removed } = snapshot.context;
2248
- return {
2249
- active,
2250
- custom,
2251
- removed
2252
- };
2299
+ overflow: "hidden",
2300
+ children: /* @__PURE__ */ jsx(ThemerSidebar, {})
2301
+ }), $[15] = t9) : t9 = $[15];
2302
+ let t10;
2303
+ $[16] === t8 ? t10 = $[17] : (t10 = /* @__PURE__ */ jsxs(Frame, {
2304
+ borderLeft: !0,
2305
+ height: "fill",
2306
+ children: [t8, t9]
2307
+ }), $[16] = t8, $[17] = t10);
2308
+ let t11;
2309
+ return $[18] !== t10 || $[19] !== t6 ? (t11 = /* @__PURE__ */ jsx(Sidebar, {
2310
+ style: t6,
2311
+ zOffset: 100,
2312
+ children: t10
2313
+ }), $[18] = t10, $[19] = t6, $[20] = t11) : t11 = $[20], t11;
2253
2314
  }
2254
2315
  const STORAGE_KEY = "sanityStudio:themer:state", LEGACY_STORAGE_KEY = "sanityStudio:themer:options";
2255
2316
  function sanitizeColor(value) {
@@ -2387,18 +2448,20 @@ function sameView(a, b) {
2387
2448
  }
2388
2449
  /**
2389
2450
  * Wraps the whole Studio so that the theme picked in the themer sidebar
2390
- * applies everywhere while the user browses around, and runs the themer
2391
- * machine that the navbar toggle and the sidebar share: the user's themes,
2392
- * which one is applied, and which flow the sidebar is in.
2451
+ * applies everywhere while the user browses around, runs the themer machine
2452
+ * that the navbar toggle and the sidebar share — the user's themes, which one
2453
+ * is applied, which flow the sidebar is in and how the Studio is previewed —
2454
+ * and renders the sidebar next to the Studio. The sidebar sits at the
2455
+ * `layout` level rather than in `activeToolLayout` because the split preview
2456
+ * renders the Studio twice, and the sidebar must not come along.
2393
2457
  *
2394
- * The theme provider inherits the color scheme from the Studio, so the
2395
- * preview follows the appearance setting (light/dark/system) like any other
2396
- * theme.
2458
+ * The single preview inherits the color scheme from the Studio, so it follows
2459
+ * the appearance setting (light/dark/system) like any other theme.
2397
2460
  *
2398
2461
  * @internal
2399
2462
  */
2400
2463
  function ThemerLayout(props) {
2401
- let $ = c(38), { baseOptions, ...layoutProps } = props, t0;
2464
+ let $ = c(55), { baseOptions, ...layoutProps } = props, t0;
2402
2465
  $[0] === baseOptions ? t0 = $[1] : (t0 = () => ({
2403
2466
  baseOptions,
2404
2467
  stored: readStoredState()
@@ -2407,35 +2470,37 @@ function ThemerLayout(props) {
2407
2470
  $[2] === input ? t1 = $[3] : (t1 = { input }, $[2] = input, $[3] = t1);
2408
2471
  let [snapshot, send, actorRef] = useActor(themerMachine, t1), stored = useSelector(actorRef, selectStoredState, sameStoredState), view = useSelector(actorRef, selectView, sameView), t2;
2409
2472
  $[4] === snapshot ? t2 = $[5] : (t2 = snapshot.matches({ sidebar: "open" }), $[4] = snapshot, $[5] = t2);
2410
- let open = t2, { images } = snapshot.context, t3, t4;
2411
- $[6] === stored ? (t3 = $[7], t4 = $[8]) : (t3 = () => writeStoredState(stored), t4 = [stored], $[6] = stored, $[7] = t3, $[8] = t4), useEffect(t3, t4);
2412
- let previousImages = useRef(images), t5, t6;
2413
- $[9] === images ? (t5 = $[10], t6 = $[11]) : (t5 = () => {
2473
+ let open = t2, t3;
2474
+ $[6] === snapshot ? t3 = $[7] : (t3 = snapshot.matches({ preview: "split" }), $[6] = snapshot, $[7] = t3);
2475
+ let split = t3, { images } = snapshot.context, t4, t5;
2476
+ $[8] === stored ? (t4 = $[9], t5 = $[10]) : (t4 = () => writeStoredState(stored), t5 = [stored], $[8] = stored, $[9] = t4, $[10] = t5), useEffect(t4, t5);
2477
+ let previousImages = useRef(images), t6, t7;
2478
+ $[11] === images ? (t6 = $[12], t7 = $[13]) : (t6 = () => {
2414
2479
  let current = new Set(Object.values(images));
2415
2480
  for (let url of Object.values(previousImages.current)) current.has(url) || URL.revokeObjectURL(url);
2416
2481
  previousImages.current = images;
2417
- }, t6 = [images], $[9] = images, $[10] = t5, $[11] = t6), useEffect(t5, t6);
2418
- let t7, t8;
2419
- $[12] === Symbol.for("react.memo_cache_sentinel") ? (t7 = () => () => {
2482
+ }, t7 = [images], $[11] = images, $[12] = t6, $[13] = t7), useEffect(t6, t7);
2483
+ let t8, t9;
2484
+ $[14] === Symbol.for("react.memo_cache_sentinel") ? (t8 = () => () => {
2420
2485
  for (let url_0 of Object.values(previousImages.current)) URL.revokeObjectURL(url_0);
2421
- }, t8 = [], $[12] = t7, $[13] = t8) : (t7 = $[12], t8 = $[13]), useEffect(t7, t8);
2422
- let t9;
2423
- $[14] !== baseOptions || $[15] !== stored ? (t9 = resolveThemes(stored, baseOptions), $[14] = baseOptions, $[15] = stored, $[16] = t9) : t9 = $[16];
2424
- let { themes, removed, active } = t9, activeOptions = active.source === "config" ? null : active.options, t10;
2425
- $[17] === activeOptions ? t10 = $[18] : (t10 = activeOptions === null ? null : buildTheme(activeOptions), $[17] = activeOptions, $[18] = t10);
2426
- let theme = t10, scheme = useColorSchemeValue(), t11;
2427
- $[19] !== scheme || $[20] !== theme?.v2?.color ? (t11 = () => {
2486
+ }, t9 = [], $[14] = t8, $[15] = t9) : (t8 = $[14], t9 = $[15]), useEffect(t8, t9);
2487
+ let t10;
2488
+ $[16] !== baseOptions || $[17] !== stored ? (t10 = resolveThemes(stored, baseOptions), $[16] = baseOptions, $[17] = stored, $[18] = t10) : t10 = $[18];
2489
+ let { themes, removed, active } = t10, activeOptions = active.source === "config" ? null : active.options, t11;
2490
+ $[19] === activeOptions ? t11 = $[20] : (t11 = activeOptions === null ? null : buildTheme(activeOptions), $[19] = activeOptions, $[20] = t11);
2491
+ let theme = t11, scheme = useColorSchemeValue(), t12;
2492
+ $[21] !== scheme || $[22] !== theme?.v2?.color ? (t12 = () => {
2428
2493
  let background = theme?.v2?.color[scheme].default.bg;
2429
2494
  if (background === void 0) return;
2430
2495
  let { style } = document.body, previous = style.backgroundColor;
2431
2496
  return style.backgroundColor = background, () => {
2432
2497
  style.backgroundColor = previous;
2433
2498
  };
2434
- }, $[19] = scheme, $[20] = theme?.v2?.color, $[21] = t11) : t11 = $[21];
2435
- let t12;
2436
- $[22] !== scheme || $[23] !== theme ? (t12 = [scheme, theme], $[22] = scheme, $[23] = theme, $[24] = t12) : t12 = $[24], useEffect(t11, t12);
2499
+ }, $[21] = scheme, $[22] = theme?.v2?.color, $[23] = t12) : t12 = $[23];
2437
2500
  let t13;
2438
- $[25] !== active || $[26] !== baseOptions || $[27] !== images || $[28] !== open || $[29] !== removed || $[30] !== send || $[31] !== themes || $[32] !== view ? (t13 = {
2501
+ $[24] !== scheme || $[25] !== theme ? (t13 = [scheme, theme], $[24] = scheme, $[25] = theme, $[26] = t13) : t13 = $[26], useEffect(t12, t13);
2502
+ let t14;
2503
+ $[27] !== active || $[28] !== baseOptions || $[29] !== images || $[30] !== open || $[31] !== removed || $[32] !== send || $[33] !== split || $[34] !== themes || $[35] !== view ? (t14 = {
2439
2504
  baseOptions,
2440
2505
  themes,
2441
2506
  removed,
@@ -2443,16 +2508,72 @@ function ThemerLayout(props) {
2443
2508
  images,
2444
2509
  view,
2445
2510
  open,
2511
+ split,
2446
2512
  send
2447
- }, $[25] = active, $[26] = baseOptions, $[27] = images, $[28] = open, $[29] = removed, $[30] = send, $[31] = themes, $[32] = view, $[33] = t13) : t13 = $[33];
2448
- let context = t13, t14 = ThemerContext, t15 = theme === null ? layoutProps.renderDefault(layoutProps) : /* @__PURE__ */ jsx(ThemeProvider, {
2513
+ }, $[27] = active, $[28] = baseOptions, $[29] = images, $[30] = open, $[31] = removed, $[32] = send, $[33] = split, $[34] = themes, $[35] = view, $[36] = t14) : t14 = $[36];
2514
+ let context = t14, studio = layoutProps.renderDefault(layoutProps), t15 = split ? "light" : void 0, t16;
2515
+ $[37] !== studio || $[38] !== t15 || $[39] !== theme ? (t16 = /* @__PURE__ */ jsx(StudioPreview, {
2516
+ scheme: t15,
2449
2517
  theme,
2450
- children: layoutProps.renderDefault(layoutProps)
2451
- }), t16;
2452
- return $[34] !== context || $[35] !== t14.Provider || $[36] !== t15 ? (t16 = /* @__PURE__ */ jsx(t14.Provider, {
2518
+ children: studio
2519
+ }), $[37] = studio, $[38] = t15, $[39] = theme, $[40] = t16) : t16 = $[40];
2520
+ let t17;
2521
+ $[41] !== split || $[42] !== studio || $[43] !== theme ? (t17 = split && /* @__PURE__ */ jsx(StudioPreview, {
2522
+ borderLeft: !0,
2523
+ scheme: "dark",
2524
+ theme,
2525
+ children: studio
2526
+ }), $[41] = split, $[42] = studio, $[43] = theme, $[44] = t17) : t17 = $[44];
2527
+ let t18;
2528
+ $[45] !== open || $[46] !== theme ? (t18 = open && /* @__PURE__ */ jsx(ThemeProvider, {
2529
+ theme: theme ?? void 0,
2530
+ children: /* @__PURE__ */ jsx(ResizableSidebar, {})
2531
+ }), $[45] = open, $[46] = theme, $[47] = t18) : t18 = $[47];
2532
+ let t19;
2533
+ $[48] !== t16 || $[49] !== t17 || $[50] !== t18 ? (t19 = /* @__PURE__ */ jsxs(Flex, {
2534
+ height: "fill",
2535
+ sizing: "border",
2536
+ children: [
2537
+ t16,
2538
+ t17,
2539
+ t18
2540
+ ]
2541
+ }), $[48] = t16, $[49] = t17, $[50] = t18, $[51] = t19) : t19 = $[51];
2542
+ let t20;
2543
+ return $[52] !== context || $[53] !== t19 ? (t20 = /* @__PURE__ */ jsx(ThemerContext.Provider, {
2453
2544
  value: context,
2454
- children: t15
2455
- }), $[34] = context, $[35] = t14.Provider, $[36] = t15, $[37] = t16) : t16 = $[37], t16;
2545
+ children: t19
2546
+ }), $[52] = context, $[53] = t19, $[54] = t20) : t20 = $[54], t20;
2547
+ }
2548
+ /**
2549
+ * One copy of the Studio, in the given color scheme — or, without one, in the
2550
+ * scheme the Studio is showing. The configured theme (`null`) goes through
2551
+ * the provider too, inheriting the Studio's, so picking a theme swaps it
2552
+ * instead of remounting the Studio under a new provider.
2553
+ *
2554
+ * The two copies of the split preview share the router, the document store
2555
+ * and every other provider above the layout — only the scheme differs — so
2556
+ * they stay in sync while navigating. The `color-scheme` of a forced scheme
2557
+ * keeps native form controls and scrollbars in step with it.
2558
+ */
2559
+ function StudioPreview(props) {
2560
+ let $ = c(10), { borderLeft, children, scheme, theme } = props, t0 = theme ?? void 0, t1;
2561
+ $[0] === scheme ? t1 = $[1] : (t1 = scheme ? { colorScheme: scheme } : void 0, $[0] = scheme, $[1] = t1);
2562
+ let t2;
2563
+ $[2] !== borderLeft || $[3] !== children || $[4] !== t1 ? (t2 = /* @__PURE__ */ jsx(Card, {
2564
+ borderLeft,
2565
+ flex: 1,
2566
+ height: "fill",
2567
+ overflow: "hidden",
2568
+ style: t1,
2569
+ children
2570
+ }), $[2] = borderLeft, $[3] = children, $[4] = t1, $[5] = t2) : t2 = $[5];
2571
+ let t3;
2572
+ return $[6] !== scheme || $[7] !== t0 || $[8] !== t2 ? (t3 = /* @__PURE__ */ jsx(ThemeProvider, {
2573
+ scheme,
2574
+ theme: t0,
2575
+ children: t2
2576
+ }), $[6] = scheme, $[7] = t0, $[8] = t2, $[9] = t3) : t3 = $[9], t3;
2456
2577
  }
2457
2578
  function ThemerNavbarButton() {
2458
2579
  let $ = c(6), { open, send } = useThemer(), t0;
@@ -2482,6 +2603,9 @@ function ThemerNavbarButton() {
2482
2603
  * navbar — an icon button with a tooltip in the top bar (like the Tasks
2483
2604
  * toggle), and a regular titled action in the narrow-screen sidebar menu.
2484
2605
  *
2606
+ * In the split preview every Studio copy renders its own navbar, so the
2607
+ * toggle shows up in both — they drive the same sidebar.
2608
+ *
2485
2609
  * @internal
2486
2610
  */
2487
2611
  function ThemerNavbar(props) {
@@ -2517,15 +2641,16 @@ function _temp() {
2517
2641
  }
2518
2642
  /**
2519
2643
  * A Studio plugin that adds a themer sidebar for `buildTheme` themes: a
2520
- * navbar toggle opens the sidebar next to the active tool, with a list of
2521
- * themes — the configured theme, the presets and your own — each previewed
2522
- * as a tiny Studio in both color schemes. Picking one applies it live to the
2523
- * whole Studio while you browse around; your own themes can be edited with
2644
+ * navbar toggle opens the sidebar next to the Studio, with a list of themes —
2645
+ * the configured theme, the presets and your own — each previewed as a tiny
2646
+ * Studio in both color schemes. Picking one applies it live to the whole
2647
+ * Studio while you browse around; your own themes can be edited with
2524
2648
  * accent/text/background pickers and a contrast slider per scheme, added,
2525
2649
  * duplicated, given the colors of an image (its palette is read on device),
2526
2650
  * removed and restored, and a dialog shows the `buildTheme` snippet that
2527
2651
  * makes the applied theme permanent. Toggle between light and dark mode with
2528
- * the regular appearance menu — the preview follows it.
2652
+ * the regular appearance menu — the preview follows it — or split the
2653
+ * preview to see the whole Studio in light and dark side by side.
2529
2654
  *
2530
2655
  * ```ts
2531
2656
  * import {themerTool} from '@sanity/themer/tool'
@@ -2555,8 +2680,7 @@ const themerTool = definePlugin((options) => {
2555
2680
  name: "@sanity/themer/tool",
2556
2681
  studio: { components: {
2557
2682
  layout: ThemerLayoutWithOptions,
2558
- navbar: ThemerNavbar,
2559
- activeToolLayout: ThemerActiveToolLayout
2683
+ navbar: ThemerNavbar
2560
2684
  } }
2561
2685
  };
2562
2686
  });