@sanity/themer 0.0.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/LICENSE +21 -0
- package/README.md +138 -0
- package/dist/_chunks/mix.cjs +52 -0
- package/dist/_chunks/mix.cjs.map +1 -0
- package/dist/_chunks/mix.js +42 -0
- package/dist/_chunks/mix.js.map +1 -0
- package/dist/_chunks/presets.cjs +215 -0
- package/dist/_chunks/presets.cjs.map +1 -0
- package/dist/_chunks/presets.js +201 -0
- package/dist/_chunks/presets.js.map +1 -0
- package/dist/_chunks/types.d.cts +26 -0
- package/dist/_chunks/types.d.cts.map +1 -0
- package/dist/_chunks/types.d.ts +26 -0
- package/dist/_chunks/types.d.ts.map +1 -0
- package/dist/index.cjs +3 -0
- package/dist/index.d.cts +63 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/legacy.cjs +1199 -0
- package/dist/legacy.cjs.map +1 -0
- package/dist/legacy.d.cts +166 -0
- package/dist/legacy.d.cts.map +1 -0
- package/dist/legacy.d.ts +166 -0
- package/dist/legacy.d.ts.map +1 -0
- package/dist/legacy.js +1199 -0
- package/dist/legacy.js.map +1 -0
- package/dist/tool.cjs +407 -0
- package/dist/tool.cjs.map +1 -0
- package/dist/tool.d.cts +45 -0
- package/dist/tool.d.cts.map +1 -0
- package/dist/tool.d.ts +45 -0
- package/dist/tool.d.ts.map +1 -0
- package/dist/tool.js +415 -0
- package/dist/tool.js.map +1 -0
- package/package.json +94 -0
package/dist/tool.d.cts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { t as CreateThemeOptions } from "./_chunks/types.cjs";
|
|
2
|
+
/**
|
|
3
|
+
* Options for the {@link themerTool} plugin.
|
|
4
|
+
*
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
interface ThemerToolOptions {
|
|
8
|
+
/**
|
|
9
|
+
* The colors that the Studio's configured theme was generated from — the
|
|
10
|
+
* themer starts editing from these, so pass the same object that the
|
|
11
|
+
* `theme` in the Studio config uses:
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* const colors = {primary: '#2276fc'}
|
|
15
|
+
*
|
|
16
|
+
* export default defineConfig({
|
|
17
|
+
* theme: createTheme(colors),
|
|
18
|
+
* plugins: [themerTool({colors})],
|
|
19
|
+
* })
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
colors?: CreateThemeOptions;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A Studio plugin that adds a themer sidebar for generating Studio themes:
|
|
26
|
+
* a navbar toggle opens the sidebar next to the active tool, where presets
|
|
27
|
+
* and color pickers preview a `createTheme` theme live on the whole Studio
|
|
28
|
+
* while you browse around. Toggle between light and dark mode with the
|
|
29
|
+
* regular appearance menu — the preview follows it.
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* import {themerTool} from '@sanity/themer/tool'
|
|
33
|
+
* import {defineConfig} from 'sanity'
|
|
34
|
+
*
|
|
35
|
+
* export default defineConfig({
|
|
36
|
+
* plugins: [themerTool()],
|
|
37
|
+
* // ...rest of the config
|
|
38
|
+
* })
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
declare const themerTool: import("sanity").Plugin<void | ThemerToolOptions>;
|
|
44
|
+
export { type ThemerToolOptions, themerTool };
|
|
45
|
+
//# sourceMappingURL=tool.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.d.cts","names":[],"sources":["../src/tool/plugin.tsx"],"mappings":";;;;;;UAYiB;;;;;;;;;;;;;;;EAef,SAAS;;;;;;;;;;;;;;;;;;;;;cAsBE,6BAAU,cAAA"}
|
package/dist/tool.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { t as CreateThemeOptions } from "./_chunks/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Options for the {@link themerTool} plugin.
|
|
4
|
+
*
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
interface ThemerToolOptions {
|
|
8
|
+
/**
|
|
9
|
+
* The colors that the Studio's configured theme was generated from — the
|
|
10
|
+
* themer starts editing from these, so pass the same object that the
|
|
11
|
+
* `theme` in the Studio config uses:
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* const colors = {primary: '#2276fc'}
|
|
15
|
+
*
|
|
16
|
+
* export default defineConfig({
|
|
17
|
+
* theme: createTheme(colors),
|
|
18
|
+
* plugins: [themerTool({colors})],
|
|
19
|
+
* })
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
colors?: CreateThemeOptions;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A Studio plugin that adds a themer sidebar for generating Studio themes:
|
|
26
|
+
* a navbar toggle opens the sidebar next to the active tool, where presets
|
|
27
|
+
* and color pickers preview a `createTheme` theme live on the whole Studio
|
|
28
|
+
* while you browse around. Toggle between light and dark mode with the
|
|
29
|
+
* regular appearance menu — the preview follows it.
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* import {themerTool} from '@sanity/themer/tool'
|
|
33
|
+
* import {defineConfig} from 'sanity'
|
|
34
|
+
*
|
|
35
|
+
* export default defineConfig({
|
|
36
|
+
* plugins: [themerTool()],
|
|
37
|
+
* // ...rest of the config
|
|
38
|
+
* })
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
declare const themerTool: import("sanity").Plugin<void | ThemerToolOptions>;
|
|
44
|
+
export { type ThemerToolOptions, themerTool };
|
|
45
|
+
//# sourceMappingURL=tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.d.ts","names":[],"sources":["../src/tool/plugin.tsx"],"mappings":";;;;;;UAYiB;;;;;;;;;;;;;;;EAef,SAAS;;;;;;;;;;;;;;;;;;;;;cAsBE,6BAAU,cAAA"}
|
package/dist/tool.js
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import { t as isColor } from "./_chunks/mix.js";
|
|
2
|
+
import { n as createTheme, t as presets } from "./_chunks/presets.js";
|
|
3
|
+
import { color } from "@sanity/color";
|
|
4
|
+
import { definePlugin } from "sanity";
|
|
5
|
+
import { Box, Button, Card, Code, Flex, Layer, Select, Stack, Text, ThemeProvider, useToast } from "@sanity/ui";
|
|
6
|
+
import { createContext, useContext, useEffect, useMemo, useState } from "react";
|
|
7
|
+
import { ClipboardIcon } from "@sanity/icons/Clipboard";
|
|
8
|
+
import { CloseIcon } from "@sanity/icons/Close";
|
|
9
|
+
import { ResetIcon } from "@sanity/icons/Reset";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
import { ColorWheelIcon } from "@sanity/icons/ColorWheel";
|
|
12
|
+
/** @internal */
|
|
13
|
+
const ThemerContext = createContext(null);
|
|
14
|
+
/** @internal */
|
|
15
|
+
function useThemer() {
|
|
16
|
+
let context = useContext(ThemerContext);
|
|
17
|
+
if (!context) throw Error("useThemer must be used within the `themerTool` plugin");
|
|
18
|
+
return context;
|
|
19
|
+
}
|
|
20
|
+
/** @internal */
|
|
21
|
+
const THEMER_FIELDS = [
|
|
22
|
+
{
|
|
23
|
+
key: "primary",
|
|
24
|
+
title: "Primary",
|
|
25
|
+
description: "Buttons, focus rings and links",
|
|
26
|
+
defaultValue: color.blue[500].hex
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
key: "gray",
|
|
30
|
+
title: "Gray",
|
|
31
|
+
description: "Neutral surfaces, borders and text",
|
|
32
|
+
defaultValue: color.gray[500].hex
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
key: "positive",
|
|
36
|
+
title: "Positive",
|
|
37
|
+
description: "Success accents",
|
|
38
|
+
defaultValue: color.green[500].hex
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
key: "caution",
|
|
42
|
+
title: "Caution",
|
|
43
|
+
description: "Warning accents",
|
|
44
|
+
defaultValue: color.yellow[500].hex
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
key: "critical",
|
|
48
|
+
title: "Critical",
|
|
49
|
+
description: "Errors and destructive actions",
|
|
50
|
+
defaultValue: color.red[500].hex
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
key: "lightest",
|
|
54
|
+
title: "Lightest",
|
|
55
|
+
description: "Light mode background",
|
|
56
|
+
defaultValue: color.white.hex
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
key: "darkest",
|
|
60
|
+
title: "Darkest",
|
|
61
|
+
description: "Dark mode background",
|
|
62
|
+
defaultValue: color.black.hex
|
|
63
|
+
}
|
|
64
|
+
];
|
|
65
|
+
/**
|
|
66
|
+
* Serializes colors into the `createTheme` call to paste into
|
|
67
|
+
* `sanity.config.ts`.
|
|
68
|
+
*
|
|
69
|
+
* @internal
|
|
70
|
+
*/
|
|
71
|
+
function createThemeSnippet(colors) {
|
|
72
|
+
let entries = THEMER_FIELDS.filter(({ key }) => colors[key]).map(({ key }) => ` ${key}: '${colors[key]}',`);
|
|
73
|
+
return `import {createTheme} from '@sanity/themer'\n\nexport const theme = ${entries.length === 0 ? "createTheme()" : `createTheme({\n${entries.join("\n")}\n})`}\n`;
|
|
74
|
+
}
|
|
75
|
+
const swatchStyle = {
|
|
76
|
+
width: 33,
|
|
77
|
+
height: 33,
|
|
78
|
+
padding: 0,
|
|
79
|
+
border: "1px solid var(--card-border-color)",
|
|
80
|
+
borderRadius: 6,
|
|
81
|
+
background: "transparent",
|
|
82
|
+
cursor: "pointer",
|
|
83
|
+
flex: "none"
|
|
84
|
+
};
|
|
85
|
+
function sameColors(a, b) {
|
|
86
|
+
return THEMER_FIELDS.every(({ key }) => (a[key]?.toLowerCase() ?? void 0) === (b[key]?.toLowerCase() ?? void 0));
|
|
87
|
+
}
|
|
88
|
+
/** Expands `#abc` to `#aabbcc`, which is the only format `<input type="color">` accepts */
|
|
89
|
+
function expandHex(hex) {
|
|
90
|
+
return hex.length === 4 ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The themer sidebar: a preset picker and a handful of color pickers that
|
|
94
|
+
* generate the previewed theme, plus the `createTheme` snippet to make it
|
|
95
|
+
* permanent.
|
|
96
|
+
*
|
|
97
|
+
* @internal
|
|
98
|
+
*/
|
|
99
|
+
function ThemerSidebar() {
|
|
100
|
+
let { baseColors, colors, setColors, setOpen } = useThemer(), toast = useToast(), active = colors ?? baseColors, activePresetSlug = presets.find((preset) => sameColors(preset.colors, active))?.slug ?? "", snippet = createThemeSnippet(active), handleFieldChange = (key, value) => {
|
|
101
|
+
let next = { ...active };
|
|
102
|
+
value === void 0 ? delete next[key] : next[key] = value, setColors(next);
|
|
103
|
+
}, handlePresetChange = (slug) => {
|
|
104
|
+
let preset = presets.find((candidate) => candidate.slug === slug);
|
|
105
|
+
preset && setColors({ ...preset.colors });
|
|
106
|
+
}, handleCopy = async () => {
|
|
107
|
+
try {
|
|
108
|
+
await navigator.clipboard.writeText(snippet), toast.push({
|
|
109
|
+
status: "success",
|
|
110
|
+
title: "Copied theme to the clipboard"
|
|
111
|
+
});
|
|
112
|
+
} catch {
|
|
113
|
+
toast.push({
|
|
114
|
+
status: "error",
|
|
115
|
+
title: "Could not copy the theme"
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
return /* @__PURE__ */ jsx(Card, {
|
|
120
|
+
height: "fill",
|
|
121
|
+
children: /* @__PURE__ */ jsxs(Flex, {
|
|
122
|
+
direction: "column",
|
|
123
|
+
height: "fill",
|
|
124
|
+
children: [/* @__PURE__ */ jsx(Card, {
|
|
125
|
+
borderBottom: !0,
|
|
126
|
+
padding: 3,
|
|
127
|
+
children: /* @__PURE__ */ jsxs(Flex, {
|
|
128
|
+
align: "center",
|
|
129
|
+
gap: 2,
|
|
130
|
+
children: [/* @__PURE__ */ jsx(Box, {
|
|
131
|
+
flex: 1,
|
|
132
|
+
paddingLeft: 1,
|
|
133
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
134
|
+
size: 1,
|
|
135
|
+
weight: "semibold",
|
|
136
|
+
children: "Themer"
|
|
137
|
+
})
|
|
138
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
139
|
+
icon: CloseIcon,
|
|
140
|
+
mode: "bleed",
|
|
141
|
+
onClick: () => setOpen(!1),
|
|
142
|
+
padding: 2,
|
|
143
|
+
title: "Close themer"
|
|
144
|
+
})]
|
|
145
|
+
})
|
|
146
|
+
}), /* @__PURE__ */ jsx(Box, {
|
|
147
|
+
flex: 1,
|
|
148
|
+
overflow: "auto",
|
|
149
|
+
padding: 4,
|
|
150
|
+
children: /* @__PURE__ */ jsxs(Stack, {
|
|
151
|
+
gap: 5,
|
|
152
|
+
children: [
|
|
153
|
+
/* @__PURE__ */ jsxs(Stack, {
|
|
154
|
+
gap: 3,
|
|
155
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
156
|
+
size: 1,
|
|
157
|
+
weight: "medium",
|
|
158
|
+
children: "Preset"
|
|
159
|
+
}), /* @__PURE__ */ jsxs(Select, {
|
|
160
|
+
onChange: (event) => handlePresetChange(event.currentTarget.value),
|
|
161
|
+
value: activePresetSlug,
|
|
162
|
+
children: [activePresetSlug === "" && /* @__PURE__ */ jsx("option", {
|
|
163
|
+
value: "",
|
|
164
|
+
children: "Custom"
|
|
165
|
+
}), presets.map((preset) => /* @__PURE__ */ jsx("option", {
|
|
166
|
+
value: preset.slug,
|
|
167
|
+
children: preset.title
|
|
168
|
+
}, preset.slug))]
|
|
169
|
+
})]
|
|
170
|
+
}),
|
|
171
|
+
/* @__PURE__ */ jsx(Stack, {
|
|
172
|
+
gap: 4,
|
|
173
|
+
children: THEMER_FIELDS.map((field) => /* @__PURE__ */ jsx(ColorField, {
|
|
174
|
+
field,
|
|
175
|
+
onChange: handleFieldChange,
|
|
176
|
+
value: active[field.key]
|
|
177
|
+
}, field.key))
|
|
178
|
+
}),
|
|
179
|
+
/* @__PURE__ */ jsxs(Stack, {
|
|
180
|
+
gap: 3,
|
|
181
|
+
children: [
|
|
182
|
+
/* @__PURE__ */ jsx(Text, {
|
|
183
|
+
size: 1,
|
|
184
|
+
weight: "medium",
|
|
185
|
+
children: "Add to your config"
|
|
186
|
+
}),
|
|
187
|
+
/* @__PURE__ */ jsx(Card, {
|
|
188
|
+
border: !0,
|
|
189
|
+
overflow: "auto",
|
|
190
|
+
padding: 3,
|
|
191
|
+
radius: 2,
|
|
192
|
+
tone: "transparent",
|
|
193
|
+
children: /* @__PURE__ */ jsx(Code, {
|
|
194
|
+
language: "ts",
|
|
195
|
+
size: 0,
|
|
196
|
+
children: snippet
|
|
197
|
+
})
|
|
198
|
+
}),
|
|
199
|
+
/* @__PURE__ */ jsxs(Flex, {
|
|
200
|
+
gap: 2,
|
|
201
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
202
|
+
icon: ClipboardIcon,
|
|
203
|
+
mode: "ghost",
|
|
204
|
+
onClick: () => void handleCopy(),
|
|
205
|
+
text: "Copy"
|
|
206
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
207
|
+
disabled: colors === null,
|
|
208
|
+
icon: ResetIcon,
|
|
209
|
+
mode: "ghost",
|
|
210
|
+
onClick: () => setColors(null),
|
|
211
|
+
text: "Reset",
|
|
212
|
+
tone: "critical"
|
|
213
|
+
})]
|
|
214
|
+
})
|
|
215
|
+
]
|
|
216
|
+
})
|
|
217
|
+
]
|
|
218
|
+
})
|
|
219
|
+
})]
|
|
220
|
+
})
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
function ColorField(props) {
|
|
224
|
+
let { field, value, onChange } = props;
|
|
225
|
+
return /* @__PURE__ */ jsxs(Flex, {
|
|
226
|
+
align: "center",
|
|
227
|
+
gap: 3,
|
|
228
|
+
children: [
|
|
229
|
+
/* @__PURE__ */ jsxs(Stack, {
|
|
230
|
+
flex: 1,
|
|
231
|
+
gap: 2,
|
|
232
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
233
|
+
size: 1,
|
|
234
|
+
weight: "medium",
|
|
235
|
+
children: field.title
|
|
236
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
237
|
+
muted: !0,
|
|
238
|
+
size: 0,
|
|
239
|
+
children: value ?? field.description
|
|
240
|
+
})]
|
|
241
|
+
}),
|
|
242
|
+
value !== void 0 && /* @__PURE__ */ jsx(Button, {
|
|
243
|
+
icon: CloseIcon,
|
|
244
|
+
mode: "bleed",
|
|
245
|
+
onClick: () => onChange(field.key, void 0),
|
|
246
|
+
padding: 2,
|
|
247
|
+
title: `Reset ${field.title}`
|
|
248
|
+
}),
|
|
249
|
+
/* @__PURE__ */ jsx("input", {
|
|
250
|
+
"aria-label": `${field.title} color`,
|
|
251
|
+
onChange: (event) => onChange(field.key, event.currentTarget.value),
|
|
252
|
+
style: swatchStyle,
|
|
253
|
+
type: "color",
|
|
254
|
+
value: expandHex(value ?? field.defaultValue)
|
|
255
|
+
})
|
|
256
|
+
]
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
const sidebarStyle = {
|
|
260
|
+
width: 360,
|
|
261
|
+
flex: "none",
|
|
262
|
+
borderLeft: "1px solid var(--card-border-color)",
|
|
263
|
+
boxSizing: "border-box",
|
|
264
|
+
overflow: "hidden"
|
|
265
|
+
};
|
|
266
|
+
/**
|
|
267
|
+
* Renders the themer sidebar next to the active tool, so the user can browse
|
|
268
|
+
* around their own studio while tweaking the theme.
|
|
269
|
+
*
|
|
270
|
+
* @internal
|
|
271
|
+
*/
|
|
272
|
+
function ThemerActiveToolLayout(props) {
|
|
273
|
+
let { open } = useThemer();
|
|
274
|
+
return /* @__PURE__ */ jsxs(Flex, {
|
|
275
|
+
height: "fill",
|
|
276
|
+
sizing: "border",
|
|
277
|
+
children: [/* @__PURE__ */ jsx(Box, {
|
|
278
|
+
flex: 1,
|
|
279
|
+
height: "fill",
|
|
280
|
+
overflow: "auto",
|
|
281
|
+
children: props.renderDefault(props)
|
|
282
|
+
}), open && /* @__PURE__ */ jsx(Layer, {
|
|
283
|
+
height: "fill",
|
|
284
|
+
style: sidebarStyle,
|
|
285
|
+
zOffset: 100,
|
|
286
|
+
children: /* @__PURE__ */ jsx(ThemerSidebar, {})
|
|
287
|
+
})]
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
const STORAGE_KEY = "sanityStudio:themer:colors";
|
|
291
|
+
/**
|
|
292
|
+
* Restores draft colors from localStorage, so theme drafts survive studio
|
|
293
|
+
* reloads.
|
|
294
|
+
*
|
|
295
|
+
* @internal
|
|
296
|
+
*/
|
|
297
|
+
function readStoredColors() {
|
|
298
|
+
try {
|
|
299
|
+
if (typeof localStorage > "u") return null;
|
|
300
|
+
let raw = localStorage.getItem(STORAGE_KEY);
|
|
301
|
+
if (!raw) return null;
|
|
302
|
+
let parsed = JSON.parse(raw);
|
|
303
|
+
if (!parsed || typeof parsed != "object") return null;
|
|
304
|
+
let colors = {};
|
|
305
|
+
for (let { key } of THEMER_FIELDS) {
|
|
306
|
+
let value = Reflect.get(parsed, key);
|
|
307
|
+
typeof value == "string" && isColor(value) && (colors[key] = value);
|
|
308
|
+
}
|
|
309
|
+
return colors;
|
|
310
|
+
} catch {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/** @internal */
|
|
315
|
+
function writeStoredColors(colors) {
|
|
316
|
+
try {
|
|
317
|
+
if (typeof localStorage > "u") return;
|
|
318
|
+
colors === null ? localStorage.removeItem(STORAGE_KEY) : localStorage.setItem(STORAGE_KEY, JSON.stringify(colors));
|
|
319
|
+
} catch {}
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Wraps the whole Studio so that draft themes generated by the themer sidebar
|
|
323
|
+
* apply everywhere while the user browses around, and hosts the state that the
|
|
324
|
+
* navbar toggle and the sidebar share.
|
|
325
|
+
*
|
|
326
|
+
* The draft theme provider inherits the color scheme from the Studio, so the
|
|
327
|
+
* preview follows the appearance setting (light/dark/system) like any other
|
|
328
|
+
* theme.
|
|
329
|
+
*
|
|
330
|
+
* @internal
|
|
331
|
+
*/
|
|
332
|
+
function ThemerLayout(props) {
|
|
333
|
+
let { baseColors, ...layoutProps } = props, [open, setOpen] = useState(!1), [colors, setColors] = useState(readStoredColors);
|
|
334
|
+
useEffect(() => writeStoredColors(colors), [colors]);
|
|
335
|
+
let theme = useMemo(() => colors === null ? null : createTheme({
|
|
336
|
+
...baseColors,
|
|
337
|
+
...colors
|
|
338
|
+
}), [baseColors, colors]), context = useMemo(() => ({
|
|
339
|
+
baseColors,
|
|
340
|
+
colors,
|
|
341
|
+
setColors,
|
|
342
|
+
open,
|
|
343
|
+
setOpen
|
|
344
|
+
}), [
|
|
345
|
+
baseColors,
|
|
346
|
+
colors,
|
|
347
|
+
open
|
|
348
|
+
]);
|
|
349
|
+
return /* @__PURE__ */ jsx(ThemerContext.Provider, {
|
|
350
|
+
value: context,
|
|
351
|
+
children: theme === null ? layoutProps.renderDefault(layoutProps) : /* @__PURE__ */ jsx(ThemeProvider, {
|
|
352
|
+
theme,
|
|
353
|
+
children: layoutProps.renderDefault(layoutProps)
|
|
354
|
+
})
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Adds the toggle that opens and closes the themer sidebar to the Studio
|
|
359
|
+
* navbar.
|
|
360
|
+
*
|
|
361
|
+
* @internal
|
|
362
|
+
*/
|
|
363
|
+
function ThemerNavbar(props) {
|
|
364
|
+
let { open, setOpen } = useThemer();
|
|
365
|
+
return props.renderDefault({
|
|
366
|
+
...props,
|
|
367
|
+
__internal_actions: [...props.__internal_actions ?? [], {
|
|
368
|
+
icon: ColorWheelIcon,
|
|
369
|
+
location: "topbar",
|
|
370
|
+
name: "themer",
|
|
371
|
+
onAction: () => setOpen(!open),
|
|
372
|
+
selected: open,
|
|
373
|
+
title: "Themer"
|
|
374
|
+
}]
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* A Studio plugin that adds a themer sidebar for generating Studio themes:
|
|
379
|
+
* a navbar toggle opens the sidebar next to the active tool, where presets
|
|
380
|
+
* and color pickers preview a `createTheme` theme live on the whole Studio
|
|
381
|
+
* while you browse around. Toggle between light and dark mode with the
|
|
382
|
+
* regular appearance menu — the preview follows it.
|
|
383
|
+
*
|
|
384
|
+
* ```ts
|
|
385
|
+
* import {themerTool} from '@sanity/themer/tool'
|
|
386
|
+
* import {defineConfig} from 'sanity'
|
|
387
|
+
*
|
|
388
|
+
* export default defineConfig({
|
|
389
|
+
* plugins: [themerTool()],
|
|
390
|
+
* // ...rest of the config
|
|
391
|
+
* })
|
|
392
|
+
* ```
|
|
393
|
+
*
|
|
394
|
+
* @public
|
|
395
|
+
*/
|
|
396
|
+
const themerTool = definePlugin((options) => {
|
|
397
|
+
let baseColors = options?.colors ?? {};
|
|
398
|
+
function ThemerLayoutWithOptions(props) {
|
|
399
|
+
return /* @__PURE__ */ jsx(ThemerLayout, {
|
|
400
|
+
...props,
|
|
401
|
+
baseColors
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
return {
|
|
405
|
+
name: "@sanity/themer/tool",
|
|
406
|
+
studio: { components: {
|
|
407
|
+
layout: ThemerLayoutWithOptions,
|
|
408
|
+
navbar: ThemerNavbar,
|
|
409
|
+
activeToolLayout: ThemerActiveToolLayout
|
|
410
|
+
} }
|
|
411
|
+
};
|
|
412
|
+
});
|
|
413
|
+
export { themerTool };
|
|
414
|
+
|
|
415
|
+
//# sourceMappingURL=tool.js.map
|
package/dist/tool.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool.js","names":[],"sources":["../src/tool/context.ts","../src/tool/fields.ts","../src/tool/snippet.ts","../src/tool/ThemerSidebar.tsx","../src/tool/ThemerActiveToolLayout.tsx","../src/tool/storage.ts","../src/tool/ThemerLayout.tsx","../src/tool/ThemerNavbar.tsx","../src/tool/plugin.tsx"],"sourcesContent":["import {createContext, useContext} from 'react'\n\nimport {CreateThemeOptions} from '../types'\n\n/** @internal */\nexport interface ThemerContextValue {\n /** The colors the Studio's configured theme was generated from */\n baseColors: CreateThemeOptions\n /** The draft colors, or `null` when the configured theme is untouched */\n colors: CreateThemeOptions | null\n setColors: (colors: CreateThemeOptions | null) => void\n /** Whether the themer sidebar is open */\n open: boolean\n setOpen: (open: boolean) => void\n}\n\n/** @internal */\nexport const ThemerContext = createContext<ThemerContextValue | null>(null)\n\n/** @internal */\nexport function useThemer(): ThemerContextValue {\n const context = useContext(ThemerContext)\n\n if (!context) {\n throw new Error('useThemer must be used within the `themerTool` plugin')\n }\n\n return context\n}\n","import {color} from '@sanity/color'\n\nimport {CreateThemeOptions} from '../types'\n\n/** @internal */\nexport interface ThemerField {\n key: keyof CreateThemeOptions\n title: string\n description: string\n /** The color the picker shows while the field is unset (6-digit hex) */\n defaultValue: string\n}\n\n/** @internal */\nexport const THEMER_FIELDS: ThemerField[] = [\n {\n key: 'primary',\n title: 'Primary',\n description: 'Buttons, focus rings and links',\n defaultValue: color.blue[500].hex,\n },\n {\n key: 'gray',\n title: 'Gray',\n description: 'Neutral surfaces, borders and text',\n defaultValue: color.gray[500].hex,\n },\n {\n key: 'positive',\n title: 'Positive',\n description: 'Success accents',\n defaultValue: color.green[500].hex,\n },\n {\n key: 'caution',\n title: 'Caution',\n description: 'Warning accents',\n defaultValue: color.yellow[500].hex,\n },\n {\n key: 'critical',\n title: 'Critical',\n description: 'Errors and destructive actions',\n defaultValue: color.red[500].hex,\n },\n {\n key: 'lightest',\n title: 'Lightest',\n description: 'Light mode background',\n defaultValue: color.white.hex,\n },\n {\n key: 'darkest',\n title: 'Darkest',\n description: 'Dark mode background',\n defaultValue: color.black.hex,\n },\n]\n","import {CreateThemeOptions} from '../types'\nimport {THEMER_FIELDS} from './fields'\n\n/**\n * Serializes colors into the `createTheme` call to paste into\n * `sanity.config.ts`.\n *\n * @internal\n */\nexport function createThemeSnippet(colors: CreateThemeOptions): string {\n const entries = THEMER_FIELDS.filter(({key}) => colors[key]).map(\n ({key}) => ` ${key}: '${colors[key]}',`,\n )\n\n const call = entries.length === 0 ? 'createTheme()' : `createTheme({\\n${entries.join('\\n')}\\n})`\n\n return `import {createTheme} from '@sanity/themer'\\n\\nexport const theme = ${call}\\n`\n}\n","import {ClipboardIcon} from '@sanity/icons/Clipboard'\nimport {CloseIcon} from '@sanity/icons/Close'\nimport {ResetIcon} from '@sanity/icons/Reset'\nimport {Box, Button, Card, Code, Flex, Select, Stack, Text, useToast} from '@sanity/ui'\n\nimport {presets} from '../presets'\nimport {CreateThemeOptions} from '../types'\nimport {useThemer} from './context'\nimport {THEMER_FIELDS, ThemerField} from './fields'\nimport {createThemeSnippet} from './snippet'\n\nconst swatchStyle: React.CSSProperties = {\n width: 33,\n height: 33,\n padding: 0,\n border: '1px solid var(--card-border-color)',\n borderRadius: 6,\n background: 'transparent',\n cursor: 'pointer',\n flex: 'none',\n}\n\nfunction sameColors(a: CreateThemeOptions, b: CreateThemeOptions): boolean {\n return THEMER_FIELDS.every(\n ({key}) => (a[key]?.toLowerCase() ?? undefined) === (b[key]?.toLowerCase() ?? undefined),\n )\n}\n\n/** Expands `#abc` to `#aabbcc`, which is the only format `<input type=\"color\">` accepts */\nfunction expandHex(hex: string): string {\n if (hex.length === 4) {\n return `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}`\n }\n\n return hex\n}\n\n/**\n * The themer sidebar: a preset picker and a handful of color pickers that\n * generate the previewed theme, plus the `createTheme` snippet to make it\n * permanent.\n *\n * @internal\n */\nexport function ThemerSidebar() {\n const {baseColors, colors, setColors, setOpen} = useThemer()\n const toast = useToast()\n\n const active = colors ?? baseColors\n const activePresetSlug = presets.find((preset) => sameColors(preset.colors, active))?.slug ?? ''\n const snippet = createThemeSnippet(active)\n\n const handleFieldChange = (key: keyof CreateThemeOptions, value: string | undefined) => {\n const next = {...active}\n\n if (value === undefined) {\n delete next[key]\n } else {\n next[key] = value\n }\n\n setColors(next)\n }\n\n const handlePresetChange = (slug: string) => {\n const preset = presets.find((candidate) => candidate.slug === slug)\n\n if (preset) {\n setColors({...preset.colors})\n }\n }\n\n const handleCopy = async () => {\n try {\n await navigator.clipboard.writeText(snippet)\n toast.push({status: 'success', title: 'Copied theme to the clipboard'})\n } catch {\n toast.push({status: 'error', title: 'Could not copy the theme'})\n }\n }\n\n return (\n <Card height=\"fill\">\n <Flex direction=\"column\" height=\"fill\">\n <Card borderBottom padding={3}>\n <Flex align=\"center\" gap={2}>\n <Box flex={1} paddingLeft={1}>\n <Text size={1} weight=\"semibold\">\n Themer\n </Text>\n </Box>\n <Button\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() => setOpen(false)}\n padding={2}\n title=\"Close themer\"\n />\n </Flex>\n </Card>\n\n <Box flex={1} overflow=\"auto\" padding={4}>\n <Stack gap={5}>\n <Stack gap={3}>\n <Text size={1} weight=\"medium\">\n Preset\n </Text>\n <Select\n onChange={(event) => handlePresetChange(event.currentTarget.value)}\n value={activePresetSlug}\n >\n {activePresetSlug === '' && <option value=\"\">Custom</option>}\n {presets.map((preset) => (\n <option key={preset.slug} value={preset.slug}>\n {preset.title}\n </option>\n ))}\n </Select>\n </Stack>\n\n <Stack gap={4}>\n {THEMER_FIELDS.map((field) => (\n <ColorField\n field={field}\n key={field.key}\n onChange={handleFieldChange}\n value={active[field.key]}\n />\n ))}\n </Stack>\n\n <Stack gap={3}>\n <Text size={1} weight=\"medium\">\n Add to your config\n </Text>\n <Card border overflow=\"auto\" padding={3} radius={2} tone=\"transparent\">\n <Code language=\"ts\" size={0}>\n {snippet}\n </Code>\n </Card>\n <Flex gap={2}>\n <Button\n icon={ClipboardIcon}\n mode=\"ghost\"\n onClick={() => void handleCopy()}\n text=\"Copy\"\n />\n <Button\n disabled={colors === null}\n icon={ResetIcon}\n mode=\"ghost\"\n onClick={() => setColors(null)}\n text=\"Reset\"\n tone=\"critical\"\n />\n </Flex>\n </Stack>\n </Stack>\n </Box>\n </Flex>\n </Card>\n )\n}\n\nfunction ColorField(props: {\n field: ThemerField\n value: string | undefined\n onChange: (key: keyof CreateThemeOptions, value: string | undefined) => void\n}) {\n const {field, value, onChange} = props\n\n return (\n <Flex align=\"center\" gap={3}>\n <Stack flex={1} gap={2}>\n <Text size={1} weight=\"medium\">\n {field.title}\n </Text>\n <Text muted size={0}>\n {value ?? field.description}\n </Text>\n </Stack>\n {value !== undefined && (\n <Button\n icon={CloseIcon}\n mode=\"bleed\"\n onClick={() => onChange(field.key, undefined)}\n padding={2}\n title={`Reset ${field.title}`}\n />\n )}\n <input\n aria-label={`${field.title} color`}\n onChange={(event) => onChange(field.key, event.currentTarget.value)}\n style={swatchStyle}\n type=\"color\"\n value={expandHex(value ?? field.defaultValue)}\n />\n </Flex>\n )\n}\n","import {Box, Flex, Layer} from '@sanity/ui'\nimport {type ActiveToolLayoutProps} from 'sanity'\n\nimport {useThemer} from './context'\nimport {ThemerSidebar} from './ThemerSidebar'\n\nconst sidebarStyle: React.CSSProperties = {\n width: 360,\n flex: 'none',\n borderLeft: '1px solid var(--card-border-color)',\n boxSizing: 'border-box',\n overflow: 'hidden',\n}\n\n/**\n * Renders the themer sidebar next to the active tool, so the user can browse\n * around their own studio while tweaking the theme.\n *\n * @internal\n */\nexport function ThemerActiveToolLayout(props: ActiveToolLayoutProps) {\n const {open} = useThemer()\n\n return (\n <Flex height=\"fill\" sizing=\"border\">\n <Box flex={1} height=\"fill\" overflow=\"auto\">\n {props.renderDefault(props)}\n </Box>\n\n {open && (\n <Layer height=\"fill\" style={sidebarStyle} zOffset={100}>\n <ThemerSidebar />\n </Layer>\n )}\n </Flex>\n )\n}\n","import {isColor} from '../lib/mix'\nimport {CreateThemeOptions} from '../types'\nimport {THEMER_FIELDS} from './fields'\n\nconst STORAGE_KEY = 'sanityStudio:themer:colors'\n\n/**\n * Restores draft colors from localStorage, so theme drafts survive studio\n * reloads.\n *\n * @internal\n */\nexport function readStoredColors(): CreateThemeOptions | null {\n try {\n if (typeof localStorage === 'undefined') return null\n\n const raw = localStorage.getItem(STORAGE_KEY)\n\n if (!raw) return null\n\n const parsed: unknown = JSON.parse(raw)\n\n if (!parsed || typeof parsed !== 'object') return null\n\n const colors: CreateThemeOptions = {}\n\n for (const {key} of THEMER_FIELDS) {\n const value: unknown = Reflect.get(parsed, key)\n\n if (typeof value === 'string' && isColor(value)) {\n colors[key] = value\n }\n }\n\n return colors\n } catch {\n return null\n }\n}\n\n/** @internal */\nexport function writeStoredColors(colors: CreateThemeOptions | null): void {\n try {\n if (typeof localStorage === 'undefined') return\n\n if (colors === null) {\n localStorage.removeItem(STORAGE_KEY)\n } else {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(colors))\n }\n } catch {\n // Storage can be unavailable (e.g. private browsing) — drafts just won't persist\n }\n}\n","import {ThemeProvider} from '@sanity/ui'\nimport {useEffect, useMemo, useState} from 'react'\nimport {type LayoutProps} from 'sanity'\n\nimport {createTheme} from '../createTheme'\nimport {CreateThemeOptions} from '../types'\nimport {ThemerContext, ThemerContextValue} from './context'\nimport {readStoredColors, writeStoredColors} from './storage'\n\n/**\n * Wraps the whole Studio so that draft themes generated by the themer sidebar\n * apply everywhere while the user browses around, and hosts the state that the\n * navbar toggle and the sidebar share.\n *\n * The draft theme provider inherits the color scheme from the Studio, so the\n * preview follows the appearance setting (light/dark/system) like any other\n * theme.\n *\n * @internal\n */\nexport function ThemerLayout(props: LayoutProps & {baseColors: CreateThemeOptions}) {\n const {baseColors, ...layoutProps} = props\n const [open, setOpen] = useState(false)\n const [colors, setColors] = useState<CreateThemeOptions | null>(readStoredColors)\n\n useEffect(() => writeStoredColors(colors), [colors])\n\n // The theme identity must be stable between renders: it feeds the\n // styled-components theme context for the whole Studio, and rebuilding it\n // would re-render everything\n const theme = useMemo(\n () => (colors === null ? null : createTheme({...baseColors, ...colors})),\n [baseColors, colors],\n )\n\n const context = useMemo<ThemerContextValue>(\n () => ({baseColors, colors, setColors, open, setOpen}),\n [baseColors, colors, open],\n )\n\n return (\n <ThemerContext.Provider value={context}>\n {theme === null ? (\n layoutProps.renderDefault(layoutProps)\n ) : (\n <ThemeProvider theme={theme}>{layoutProps.renderDefault(layoutProps)}</ThemeProvider>\n )}\n </ThemerContext.Provider>\n )\n}\n","import {ColorWheelIcon} from '@sanity/icons/ColorWheel'\nimport {type NavbarProps} from 'sanity'\n\nimport {useThemer} from './context'\n\n/**\n * Adds the toggle that opens and closes the themer sidebar to the Studio\n * navbar.\n *\n * @internal\n */\nexport function ThemerNavbar(props: NavbarProps) {\n const {open, setOpen} = useThemer()\n\n return props.renderDefault({\n ...props,\n __internal_actions: [\n ...(props.__internal_actions ?? []),\n {\n icon: ColorWheelIcon,\n location: 'topbar',\n name: 'themer',\n onAction: () => setOpen(!open),\n selected: open,\n title: 'Themer',\n },\n ],\n })\n}\n","import {definePlugin, type LayoutProps} from 'sanity'\n\nimport {CreateThemeOptions} from '../types'\nimport {ThemerActiveToolLayout} from './ThemerActiveToolLayout'\nimport {ThemerLayout} from './ThemerLayout'\nimport {ThemerNavbar} from './ThemerNavbar'\n\n/**\n * Options for the {@link themerTool} plugin.\n *\n * @public\n */\nexport interface ThemerToolOptions {\n /**\n * The colors that the Studio's configured theme was generated from — the\n * themer starts editing from these, so pass the same object that the\n * `theme` in the Studio config uses:\n *\n * ```ts\n * const colors = {primary: '#2276fc'}\n *\n * export default defineConfig({\n * theme: createTheme(colors),\n * plugins: [themerTool({colors})],\n * })\n * ```\n */\n colors?: CreateThemeOptions\n}\n\n/**\n * A Studio plugin that adds a themer sidebar for generating Studio themes:\n * a navbar toggle opens the sidebar next to the active tool, where presets\n * and color pickers preview a `createTheme` theme live on the whole Studio\n * while you browse around. Toggle between light and dark mode with the\n * regular appearance menu — the preview follows it.\n *\n * ```ts\n * import {themerTool} from '@sanity/themer/tool'\n * import {defineConfig} from 'sanity'\n *\n * export default defineConfig({\n * plugins: [themerTool()],\n * // ...rest of the config\n * })\n * ```\n *\n * @public\n */\nexport const themerTool = definePlugin<ThemerToolOptions | void>((options) => {\n const baseColors: CreateThemeOptions = options?.colors ?? {}\n\n function ThemerLayoutWithOptions(props: LayoutProps) {\n return <ThemerLayout {...props} baseColors={baseColors} />\n }\n\n return {\n name: '@sanity/themer/tool',\n studio: {\n components: {\n layout: ThemerLayoutWithOptions,\n navbar: ThemerNavbar,\n activeToolLayout: ThemerActiveToolLayout,\n },\n },\n }\n})\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sanity/themer",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "Generate Sanity Studio themes, and preview them with a Studio tool.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"sanity",
|
|
7
|
+
"studio",
|
|
8
|
+
"theme",
|
|
9
|
+
"themer",
|
|
10
|
+
"ui"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://www.sanity.io/",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/sanity-io/ui/issues"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "Sanity.io <hello@sanity.io>",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/sanity-io/ui.git",
|
|
21
|
+
"directory": "packages/themer"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"main": "./dist/index.cjs",
|
|
29
|
+
"module": "./dist/index.js",
|
|
30
|
+
"types": "./dist/index.d.cts",
|
|
31
|
+
"typesVersions": {
|
|
32
|
+
"*": {
|
|
33
|
+
"legacy": [
|
|
34
|
+
"./dist/legacy.d.cts"
|
|
35
|
+
],
|
|
36
|
+
"tool": [
|
|
37
|
+
"./dist/tool.d.cts"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"exports": {
|
|
42
|
+
".": {
|
|
43
|
+
"import": "./dist/index.js",
|
|
44
|
+
"require": "./dist/index.cjs"
|
|
45
|
+
},
|
|
46
|
+
"./legacy": {
|
|
47
|
+
"import": "./dist/legacy.js",
|
|
48
|
+
"require": "./dist/legacy.cjs"
|
|
49
|
+
},
|
|
50
|
+
"./tool": {
|
|
51
|
+
"import": "./dist/tool.js",
|
|
52
|
+
"require": "./dist/tool.cjs"
|
|
53
|
+
},
|
|
54
|
+
"./package.json": "./package.json"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"@sanity/color": "^3.0.7",
|
|
61
|
+
"@sanity/ui": "^3.4.3",
|
|
62
|
+
"@sanity/icons": "^5.2.1"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@sanity/tsdown-config": "^0.19.6",
|
|
66
|
+
"@types/node": "^24.13.3",
|
|
67
|
+
"@types/react": "^19.2.17",
|
|
68
|
+
"react": "^19.2.7",
|
|
69
|
+
"rimraf": "^5.0.5",
|
|
70
|
+
"sanity": "^6.5.0",
|
|
71
|
+
"tsdown": "^0.22.12",
|
|
72
|
+
"typescript": "6.0.3",
|
|
73
|
+
"vitest": "^4.1.10"
|
|
74
|
+
},
|
|
75
|
+
"peerDependencies": {
|
|
76
|
+
"react": "^18 || >=19.0.0-0",
|
|
77
|
+
"sanity": "^5 || ^6"
|
|
78
|
+
},
|
|
79
|
+
"peerDependenciesMeta": {
|
|
80
|
+
"sanity": {
|
|
81
|
+
"optional": true
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"engines": {
|
|
85
|
+
"node": ">=20.19 <22 || >=22.12"
|
|
86
|
+
},
|
|
87
|
+
"scripts": {
|
|
88
|
+
"build": "tsdown",
|
|
89
|
+
"clean": "rimraf dist",
|
|
90
|
+
"test": "vitest run",
|
|
91
|
+
"test:watch": "vitest",
|
|
92
|
+
"watch": "tsdown --watch"
|
|
93
|
+
}
|
|
94
|
+
}
|