@timmo001/groundwork-tokens 0.9.40 → 0.10.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/generated/Themes.qml +1 -1
- package/generated/opentui.ts +96 -4
- package/generated/themes.css +261 -196
- package/generated/themes.json +97 -82
- package/generated/themes.ts +97 -82
- package/package.json +1 -1
- package/src/choices.ts +33 -16
- package/src/generate.ts +8 -8
- package/src/page.ts +165 -159
- package/src/schema.ts +3 -2
- package/themes/tokyo-night-moon.json +14 -0
- package/themes/tokyo-night-storm.json +14 -0
- package/themes/tokyo-night.json +14 -0
- package/themes/tokyo-night-day.json +0 -21
package/src/page.ts
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
omarchyStorageKeys,
|
|
16
16
|
omarchyThemeId,
|
|
17
17
|
omarchyThemeStyleId,
|
|
18
|
-
|
|
18
|
+
themeAllows,
|
|
19
19
|
themeStorageKeys,
|
|
20
20
|
type ThemeBootOptions,
|
|
21
21
|
type ThemeChoice,
|
|
@@ -36,8 +36,10 @@ export {
|
|
|
36
36
|
omarchyThemeStyleId,
|
|
37
37
|
optionalThemeChoices,
|
|
38
38
|
optionalThemeDocuments,
|
|
39
|
+
themeAllows,
|
|
39
40
|
themeBootScript,
|
|
40
41
|
themeCovers,
|
|
42
|
+
themeOptions,
|
|
41
43
|
type ThemeBootOptions,
|
|
42
44
|
type ThemeChoice,
|
|
43
45
|
type ThemeMode,
|
|
@@ -50,8 +52,14 @@ export interface PageThemeState {
|
|
|
50
52
|
readonly mode: ThemeMode;
|
|
51
53
|
/** The scheme in use, with `auto` resolved from the system. */
|
|
52
54
|
readonly scheme: ThemeScheme;
|
|
53
|
-
/** The theme id in use: a built-in one, or `custom`. */
|
|
55
|
+
/** The theme id in use: a built-in one, `omarchy` or `custom`. */
|
|
54
56
|
readonly theme: string;
|
|
57
|
+
/**
|
|
58
|
+
* The one scheme the theme in use covers, such as `light` for Catppuccin
|
|
59
|
+
* Latte, or `null` when it has both. Pass it to the picker's `scheme`, so
|
|
60
|
+
* the other modes can't be picked.
|
|
61
|
+
*/
|
|
62
|
+
readonly only: ThemeScheme | null;
|
|
55
63
|
/** The visitor's own theme's name, when they've imported one. */
|
|
56
64
|
readonly custom: string | null;
|
|
57
65
|
/** The desktop's Omarchy theme as a choice, when the app passed one. */
|
|
@@ -94,7 +102,9 @@ export interface PageThemeOptions extends ThemeBootOptions {
|
|
|
94
102
|
|
|
95
103
|
export interface PageTheme {
|
|
96
104
|
readonly state: () => PageThemeState;
|
|
105
|
+
/** Sets the mode, unless the theme in use doesn't come in it. */
|
|
97
106
|
readonly setMode: (mode: ThemeMode) => void;
|
|
107
|
+
/** Picks a theme, moving to its scheme when it has only one. */
|
|
98
108
|
readonly setTheme: (id: string) => void;
|
|
99
109
|
/** Solves a theme document's JSON or an Omarchy `colors.toml` and applies it. */
|
|
100
110
|
readonly importTheme: (text: string) => Promise<ImportResult>;
|
|
@@ -102,9 +112,9 @@ export interface PageTheme {
|
|
|
102
112
|
readonly reset: () => void;
|
|
103
113
|
/**
|
|
104
114
|
* Offers the Omarchy theme the app runs under, or drops it with `null`.
|
|
105
|
-
* It's
|
|
106
|
-
* switch on the desktop follows through
|
|
107
|
-
*
|
|
115
|
+
* It's only ever a choice, never the default; while it's picked, a theme
|
|
116
|
+
* switch on the desktop follows through. Apps that can read it, such as
|
|
117
|
+
* desktop apps, pass it on start and on every switch.
|
|
108
118
|
*/
|
|
109
119
|
readonly setOmarchyTheme: (
|
|
110
120
|
theme: OmarchyCurrentTheme | null,
|
|
@@ -143,9 +153,17 @@ const readTheme = async (text: string) => {
|
|
|
143
153
|
return omarchyTheme(text);
|
|
144
154
|
};
|
|
145
155
|
|
|
156
|
+
const schemeValue = (value: string | null): ThemeScheme | undefined =>
|
|
157
|
+
value === "light" || value === "dark" ? value : undefined;
|
|
158
|
+
|
|
146
159
|
/**
|
|
147
160
|
* Sets up the page's theme from what's stored, falling back to the defaults,
|
|
148
161
|
* and registers the WebMCP theme tools where the browser offers them.
|
|
162
|
+
*
|
|
163
|
+
* There's one pick. A theme with one scheme keeps the page in it, so its
|
|
164
|
+
* other modes can't be set. An imported or Omarchy theme is offered once
|
|
165
|
+
* there is one, but is only used when it's picked: the default is always
|
|
166
|
+
* `defaultTheme`.
|
|
149
167
|
*/
|
|
150
168
|
export const pageTheme = (options: PageThemeOptions): PageTheme => {
|
|
151
169
|
const stored = themeStorageKeys(options.key);
|
|
@@ -156,10 +174,6 @@ export const pageTheme = (options: PageThemeOptions): PageTheme => {
|
|
|
156
174
|
|
|
157
175
|
const known = (id: string) => options.themes.some((theme) => theme.id === id);
|
|
158
176
|
|
|
159
|
-
const storedMode = read(stored.mode);
|
|
160
|
-
const customName = read(stored.name);
|
|
161
|
-
const customCss = read(stored.css);
|
|
162
|
-
|
|
163
177
|
const setStyle = (id: string, css: string | null) => {
|
|
164
178
|
let style = document.getElementById(id);
|
|
165
179
|
|
|
@@ -184,13 +198,9 @@ export const pageTheme = (options: PageThemeOptions): PageTheme => {
|
|
|
184
198
|
{
|
|
185
199
|
const css = read(storedOmarchy.css);
|
|
186
200
|
const name = read(storedOmarchy.name);
|
|
187
|
-
const mode = read(storedOmarchy.mode);
|
|
201
|
+
const mode = schemeValue(read(storedOmarchy.mode));
|
|
188
202
|
|
|
189
|
-
if (
|
|
190
|
-
css !== null &&
|
|
191
|
-
name !== null &&
|
|
192
|
-
(mode === "light" || mode === "dark")
|
|
193
|
-
) {
|
|
203
|
+
if (css !== null && name !== null && mode !== undefined) {
|
|
194
204
|
omarchy = { id: omarchyThemeId, name, mode };
|
|
195
205
|
|
|
196
206
|
// The boot script may have put it on already
|
|
@@ -200,8 +210,24 @@ export const pageTheme = (options: PageThemeOptions): PageTheme => {
|
|
|
200
210
|
}
|
|
201
211
|
}
|
|
202
212
|
|
|
203
|
-
|
|
204
|
-
|
|
213
|
+
/** The visitor's own theme as a choice, when there's one. */
|
|
214
|
+
let custom: ThemeChoice | null =
|
|
215
|
+
read(stored.css) === null
|
|
216
|
+
? null
|
|
217
|
+
: {
|
|
218
|
+
id: customThemeId,
|
|
219
|
+
name: read(stored.name) ?? "Custom",
|
|
220
|
+
mode: schemeValue(read(stored.scheme)),
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const choiceOf = (id: string | null) =>
|
|
224
|
+
id === null
|
|
225
|
+
? undefined
|
|
226
|
+
: [
|
|
227
|
+
...options.themes,
|
|
228
|
+
...(omarchy === null ? [] : [omarchy]),
|
|
229
|
+
...(custom === null ? [] : [custom]),
|
|
230
|
+
].find((theme) => theme.id === id);
|
|
205
231
|
|
|
206
232
|
const system = matchMedia("(prefers-color-scheme: dark)");
|
|
207
233
|
|
|
@@ -212,59 +238,46 @@ export const pageTheme = (options: PageThemeOptions): PageTheme => {
|
|
|
212
238
|
? "light"
|
|
213
239
|
: "dark";
|
|
214
240
|
|
|
215
|
-
/**
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
:
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
? options.defaultTheme
|
|
234
|
-
: (options.themes.find((theme) => themeCovers(theme, scheme))?.id ??
|
|
235
|
-
options.defaultTheme);
|
|
236
|
-
|
|
237
|
-
const storedLight = read(stored.theme);
|
|
238
|
-
const storedDark = read(stored.dark) ?? storedLight;
|
|
239
|
-
|
|
240
|
-
/** The theme kept for each scheme. */
|
|
241
|
-
let picks: Record<ThemeScheme, string> = {
|
|
242
|
-
light: fits(storedLight, "light", customCss !== null)
|
|
243
|
-
? (storedLight ?? options.defaultTheme)
|
|
244
|
-
: fallback("light"),
|
|
245
|
-
dark: fits(storedDark, "dark", customCss !== null)
|
|
246
|
-
? (storedDark ?? options.defaultTheme)
|
|
247
|
-
: fallback("dark"),
|
|
241
|
+
/** `mode`, or the theme's own scheme when it doesn't come in `mode`. */
|
|
242
|
+
const fit = (choice: ThemeChoice | undefined, mode: ThemeMode): ThemeMode =>
|
|
243
|
+
choice === undefined || themeAllows(choice, mode)
|
|
244
|
+
? mode
|
|
245
|
+
: (choice.mode ?? mode);
|
|
246
|
+
|
|
247
|
+
const resolve = (theme: string, mode: ThemeMode): PageThemeState => {
|
|
248
|
+
const choice = choiceOf(theme);
|
|
249
|
+
const fitted = fit(choice, mode);
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
mode: fitted,
|
|
253
|
+
scheme: schemeOf(fitted),
|
|
254
|
+
theme,
|
|
255
|
+
only: choice?.mode ?? null,
|
|
256
|
+
custom: custom?.name ?? null,
|
|
257
|
+
omarchy,
|
|
258
|
+
};
|
|
248
259
|
};
|
|
249
260
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
write(stored.dark, picks.dark);
|
|
253
|
-
};
|
|
261
|
+
/** The default, as `reset` and a vanished pick go back to. */
|
|
262
|
+
const fallback = () => resolve(options.defaultTheme, defaultMode);
|
|
254
263
|
|
|
255
|
-
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
return { mode, scheme, theme: picks[scheme], custom, omarchy };
|
|
259
|
-
};
|
|
264
|
+
let state: PageThemeState = (() => {
|
|
265
|
+
const theme = read(stored.theme);
|
|
266
|
+
const mode = modes.find((entry) => entry === read(stored.mode));
|
|
260
267
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
);
|
|
268
|
+
return theme !== null && choiceOf(theme) !== undefined
|
|
269
|
+
? resolve(theme, mode ?? defaultMode)
|
|
270
|
+
: resolve(options.defaultTheme, mode ?? defaultMode);
|
|
271
|
+
})();
|
|
265
272
|
|
|
266
|
-
const commit = (next: PageThemeState) => {
|
|
273
|
+
const commit = (next: PageThemeState, save = true) => {
|
|
267
274
|
state = next;
|
|
275
|
+
|
|
276
|
+
if (save) {
|
|
277
|
+
write(stored.theme, state.theme);
|
|
278
|
+
write(stored.mode, state.mode);
|
|
279
|
+
}
|
|
280
|
+
|
|
268
281
|
root.setAttribute("data-gw-theme", state.theme);
|
|
269
282
|
|
|
270
283
|
if (state.mode === "auto") root.removeAttribute("data-gw-mode");
|
|
@@ -273,8 +286,15 @@ export const pageTheme = (options: PageThemeOptions): PageTheme => {
|
|
|
273
286
|
for (const listener of listeners) listener(state);
|
|
274
287
|
};
|
|
275
288
|
|
|
276
|
-
/**
|
|
277
|
-
|
|
289
|
+
/**
|
|
290
|
+
* Solves a document under the custom id and keeps it. `pick` uses it,
|
|
291
|
+
* as an import does; a theme from the store is only offered.
|
|
292
|
+
*/
|
|
293
|
+
const applyCustom = async (
|
|
294
|
+
document: Theme,
|
|
295
|
+
text: string,
|
|
296
|
+
{ pick, keep }: { pick: boolean; keep: boolean },
|
|
297
|
+
) => {
|
|
278
298
|
const { appTonesCss, runtimeTheme } = await import("./runtime.ts");
|
|
279
299
|
const runtime = runtimeTheme(document, customThemeId);
|
|
280
300
|
|
|
@@ -283,13 +303,26 @@ export const pageTheme = (options: PageThemeOptions): PageTheme => {
|
|
|
283
303
|
? runtime.css
|
|
284
304
|
: `${runtime.css}\n\n${appTonesCss(options.tones, runtime.theme)}`;
|
|
285
305
|
|
|
306
|
+
const scheme =
|
|
307
|
+
runtime.theme.modes.light === undefined
|
|
308
|
+
? "dark"
|
|
309
|
+
: runtime.theme.modes.dark === undefined
|
|
310
|
+
? "light"
|
|
311
|
+
: undefined;
|
|
312
|
+
|
|
286
313
|
setStyle(customThemeStyleId, css);
|
|
287
314
|
write(stored.document, text);
|
|
288
315
|
write(stored.css, css);
|
|
289
316
|
write(stored.name, document.name);
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
317
|
+
write(stored.scheme, scheme ?? null);
|
|
318
|
+
custom = { id: customThemeId, name: document.name, mode: scheme };
|
|
319
|
+
|
|
320
|
+
commit(
|
|
321
|
+
resolve(
|
|
322
|
+
pick || state.theme === customThemeId ? customThemeId : state.theme,
|
|
323
|
+
state.mode,
|
|
324
|
+
),
|
|
325
|
+
);
|
|
293
326
|
|
|
294
327
|
if (keep) await options.store?.save(text).catch(() => undefined);
|
|
295
328
|
|
|
@@ -301,45 +334,41 @@ export const pageTheme = (options: PageThemeOptions): PageTheme => {
|
|
|
301
334
|
);
|
|
302
335
|
};
|
|
303
336
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
? { light: id, dark: id }
|
|
317
|
-
: { ...picks, [choice.mode]: id };
|
|
337
|
+
/** Drops the visitor's own theme, leaving it if it was in use. */
|
|
338
|
+
const dropCustom = () => {
|
|
339
|
+
setStyle(customThemeStyleId, null);
|
|
340
|
+
|
|
341
|
+
for (const key of [
|
|
342
|
+
stored.document,
|
|
343
|
+
stored.css,
|
|
344
|
+
stored.name,
|
|
345
|
+
stored.scheme,
|
|
346
|
+
]) {
|
|
347
|
+
write(key, null);
|
|
348
|
+
}
|
|
318
349
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
savePicks();
|
|
322
|
-
write(stored.mode, choice.mode);
|
|
323
|
-
commit(resolve(choice.mode, state.custom));
|
|
350
|
+
custom = null;
|
|
351
|
+
};
|
|
324
352
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
353
|
+
const setTheme = (id: string) => {
|
|
354
|
+
if (choiceOf(id) === undefined) return;
|
|
328
355
|
|
|
329
|
-
|
|
330
|
-
commit(resolve(state.mode, state.custom));
|
|
356
|
+
commit(resolve(id, state.mode));
|
|
331
357
|
};
|
|
332
358
|
|
|
333
|
-
// A system change moves `auto` between
|
|
359
|
+
// A system change moves `auto` between light and dark
|
|
334
360
|
system.addEventListener("change", () => {
|
|
335
|
-
if (state.mode === "auto") commit(resolve(state.
|
|
361
|
+
if (state.mode === "auto") commit(resolve(state.theme, state.mode), false);
|
|
336
362
|
});
|
|
337
363
|
|
|
338
364
|
const page: PageTheme = {
|
|
339
365
|
state: () => state,
|
|
340
366
|
setMode: (mode) => {
|
|
341
|
-
|
|
342
|
-
|
|
367
|
+
const choice = choiceOf(state.theme);
|
|
368
|
+
|
|
369
|
+
if (choice !== undefined && !themeAllows(choice, mode)) return;
|
|
370
|
+
|
|
371
|
+
commit(resolve(state.theme, mode));
|
|
343
372
|
},
|
|
344
373
|
setTheme,
|
|
345
374
|
importTheme: async (text) => {
|
|
@@ -353,32 +382,22 @@ export const pageTheme = (options: PageThemeOptions): PageTheme => {
|
|
|
353
382
|
};
|
|
354
383
|
}
|
|
355
384
|
|
|
356
|
-
const misses = await applyCustom(parsed.success, text
|
|
385
|
+
const misses = await applyCustom(parsed.success, text, {
|
|
386
|
+
pick: true,
|
|
387
|
+
keep: true,
|
|
388
|
+
});
|
|
357
389
|
|
|
358
390
|
return { ok: true, name: parsed.success.name, misses };
|
|
359
391
|
},
|
|
360
392
|
reset: () => {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
picks = { light: fallback("light"), dark: fallback("dark") };
|
|
366
|
-
commit(resolve(defaultMode, null));
|
|
393
|
+
dropCustom();
|
|
394
|
+
write(stored.theme, null);
|
|
395
|
+
write(stored.mode, null);
|
|
396
|
+
commit(fallback(), false);
|
|
367
397
|
void options.store?.clear().catch(() => undefined);
|
|
368
398
|
},
|
|
369
399
|
setOmarchyTheme: async (theme) => {
|
|
370
|
-
|
|
371
|
-
const followed = [
|
|
372
|
-
picks.light,
|
|
373
|
-
picks.dark,
|
|
374
|
-
read(stored.theme),
|
|
375
|
-
read(stored.dark),
|
|
376
|
-
].includes(omarchyThemeId);
|
|
377
|
-
|
|
378
|
-
const unpicked =
|
|
379
|
-
options.followOmarchy !== false && read(stored.theme) === null;
|
|
380
|
-
|
|
381
|
-
let mode: ThemeScheme | undefined;
|
|
400
|
+
let next: ThemeChoice | null = null;
|
|
382
401
|
|
|
383
402
|
if (theme !== null) {
|
|
384
403
|
const { Result } = await import("effect");
|
|
@@ -397,42 +416,32 @@ export const pageTheme = (options: PageThemeOptions): PageTheme => {
|
|
|
397
416
|
? runtime.css
|
|
398
417
|
: `${runtime.css}\n\n${appTonesCss(options.tones, runtime.theme)}`;
|
|
399
418
|
|
|
400
|
-
mode = parsed.success.mode;
|
|
401
419
|
setStyle(omarchyThemeStyleId, css);
|
|
402
420
|
write(storedOmarchy.css, css);
|
|
403
421
|
write(storedOmarchy.name, name);
|
|
404
|
-
write(storedOmarchy.mode, mode);
|
|
405
|
-
|
|
422
|
+
write(storedOmarchy.mode, parsed.success.mode);
|
|
423
|
+
next = { id: omarchyThemeId, name, mode: parsed.success.mode };
|
|
406
424
|
}
|
|
407
425
|
}
|
|
408
426
|
|
|
409
|
-
if (
|
|
410
|
-
// Gone, or a colors.toml that doesn't map
|
|
411
|
-
// pick so it comes back with the theme
|
|
427
|
+
if (next === null) {
|
|
428
|
+
// Gone, or a colors.toml that doesn't map
|
|
412
429
|
setStyle(omarchyThemeStyleId, null);
|
|
413
430
|
|
|
414
431
|
for (const key of Object.values(storedOmarchy)) write(key, null);
|
|
415
|
-
|
|
416
|
-
omarchy = null;
|
|
417
|
-
picks = {
|
|
418
|
-
light:
|
|
419
|
-
picks.light === omarchyThemeId ? fallback("light") : picks.light,
|
|
420
|
-
dark: picks.dark === omarchyThemeId ? fallback("dark") : picks.dark,
|
|
421
|
-
};
|
|
422
|
-
} else if (followed || unpicked) {
|
|
423
|
-
// A switch between a light and a dark Omarchy theme moves it across
|
|
424
|
-
const keep = (scheme: ThemeScheme) =>
|
|
425
|
-
picks[scheme] === omarchyThemeId ? fallback(scheme) : picks[scheme];
|
|
426
|
-
|
|
427
|
-
picks =
|
|
428
|
-
mode === "dark"
|
|
429
|
-
? { light: keep("light"), dark: omarchyThemeId }
|
|
430
|
-
: { light: omarchyThemeId, dark: keep("dark") };
|
|
431
|
-
|
|
432
|
-
if (!unpicked) savePicks();
|
|
433
432
|
}
|
|
434
433
|
|
|
435
|
-
|
|
434
|
+
omarchy = next;
|
|
435
|
+
|
|
436
|
+
// While it's picked, a switch between a light and a dark Omarchy
|
|
437
|
+
// theme moves the page across; once it's gone, the default takes over
|
|
438
|
+
commit(
|
|
439
|
+
state.theme !== omarchyThemeId
|
|
440
|
+
? resolve(state.theme, state.mode)
|
|
441
|
+
: next === null
|
|
442
|
+
? fallback()
|
|
443
|
+
: resolve(omarchyThemeId, state.mode),
|
|
444
|
+
);
|
|
436
445
|
},
|
|
437
446
|
subscribe: (listener) => {
|
|
438
447
|
listeners.add(listener);
|
|
@@ -443,10 +452,14 @@ export const pageTheme = (options: PageThemeOptions): PageTheme => {
|
|
|
443
452
|
},
|
|
444
453
|
};
|
|
445
454
|
|
|
446
|
-
|
|
455
|
+
// A stored pick is saved back, in case its mode had to move to fit the
|
|
456
|
+
// theme; with nothing stored, the defaults stay unsaved so a new default
|
|
457
|
+
// reaches the visitor
|
|
458
|
+
commit(state, read(stored.theme) !== null);
|
|
447
459
|
|
|
448
|
-
// The store's theme wins over this browser's
|
|
449
|
-
// changed or dropped it
|
|
460
|
+
// The store's theme wins over this browser's, since another client may
|
|
461
|
+
// have changed or dropped it. It's offered, and used only when this
|
|
462
|
+
// browser had the visitor's own theme picked
|
|
450
463
|
if (options.store !== undefined) {
|
|
451
464
|
void options.store
|
|
452
465
|
.load()
|
|
@@ -454,19 +467,12 @@ export const pageTheme = (options: PageThemeOptions): PageTheme => {
|
|
|
454
467
|
if (text === read(stored.document)) return;
|
|
455
468
|
|
|
456
469
|
if (text === null) {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
picks = {
|
|
464
|
-
light:
|
|
465
|
-
picks.light === customThemeId ? fallback("light") : picks.light,
|
|
466
|
-
dark: picks.dark === customThemeId ? fallback("dark") : picks.dark,
|
|
467
|
-
};
|
|
468
|
-
savePicks();
|
|
469
|
-
commit(resolve(state.mode, null));
|
|
470
|
+
dropCustom();
|
|
471
|
+
commit(
|
|
472
|
+
state.theme === customThemeId
|
|
473
|
+
? fallback()
|
|
474
|
+
: resolve(state.theme, state.mode),
|
|
475
|
+
);
|
|
470
476
|
|
|
471
477
|
return;
|
|
472
478
|
}
|
|
@@ -475,7 +481,7 @@ export const pageTheme = (options: PageThemeOptions): PageTheme => {
|
|
|
475
481
|
const parsed = await readTheme(text);
|
|
476
482
|
|
|
477
483
|
if (Result.isSuccess(parsed)) {
|
|
478
|
-
await applyCustom(parsed.success, text, false);
|
|
484
|
+
await applyCustom(parsed.success, text, { pick: false, keep: false });
|
|
479
485
|
}
|
|
480
486
|
})
|
|
481
487
|
.catch(() => undefined);
|
|
@@ -502,7 +508,7 @@ export const pageTheme = (options: PageThemeOptions): PageTheme => {
|
|
|
502
508
|
},
|
|
503
509
|
apply: ({ document: theme, text }) => {
|
|
504
510
|
if (documents.includes(theme)) setTheme(theme.id);
|
|
505
|
-
else void applyCustom(theme, text);
|
|
511
|
+
else void applyCustom(theme, text, { pick: true, keep: true });
|
|
506
512
|
},
|
|
507
513
|
});
|
|
508
514
|
},
|
package/src/schema.ts
CHANGED
|
@@ -108,8 +108,9 @@ export interface Palette extends Schema.Schema.Type<typeof Palette> {}
|
|
|
108
108
|
|
|
109
109
|
/**
|
|
110
110
|
* An optional theme mapped from an existing palette, such as Catppuccin, so it
|
|
111
|
-
* stays faithful. It takes Base's style.
|
|
112
|
-
* comes in both, such as GitHub's
|
|
111
|
+
* stays faithful. It takes Base's style. Some flavors are one mode, such as
|
|
112
|
+
* Catppuccin's; one that comes in both, such as GitHub's or Tokyo Night's,
|
|
113
|
+
* also gives the other mode's palette.
|
|
113
114
|
*/
|
|
114
115
|
export const MappedTheme = Schema.Struct({
|
|
115
116
|
kind: Schema.tag("mapped"),
|
|
@@ -17,5 +17,19 @@
|
|
|
17
17
|
"important": "#c099ff",
|
|
18
18
|
"warning": "#ffc777",
|
|
19
19
|
"danger": "#ff757f"
|
|
20
|
+
},
|
|
21
|
+
"otherPalette": {
|
|
22
|
+
"lower": "#d0d5e3",
|
|
23
|
+
"background": "#e1e2e7",
|
|
24
|
+
"raised": "#e9e9ed",
|
|
25
|
+
"border": "#c4c8da",
|
|
26
|
+
"text": "#3760bf",
|
|
27
|
+
"muted": "#6172b0",
|
|
28
|
+
"accent": "#2e7de9",
|
|
29
|
+
"info": "#2e7de9",
|
|
30
|
+
"success": "#587539",
|
|
31
|
+
"important": "#9854f1",
|
|
32
|
+
"warning": "#8c6c3e",
|
|
33
|
+
"danger": "#f52a65"
|
|
20
34
|
}
|
|
21
35
|
}
|
|
@@ -17,5 +17,19 @@
|
|
|
17
17
|
"important": "#bb9af7",
|
|
18
18
|
"warning": "#e0af68",
|
|
19
19
|
"danger": "#f7768e"
|
|
20
|
+
},
|
|
21
|
+
"otherPalette": {
|
|
22
|
+
"lower": "#d0d5e3",
|
|
23
|
+
"background": "#e1e2e7",
|
|
24
|
+
"raised": "#e9e9ed",
|
|
25
|
+
"border": "#c4c8da",
|
|
26
|
+
"text": "#3760bf",
|
|
27
|
+
"muted": "#6172b0",
|
|
28
|
+
"accent": "#2e7de9",
|
|
29
|
+
"info": "#2e7de9",
|
|
30
|
+
"success": "#587539",
|
|
31
|
+
"important": "#9854f1",
|
|
32
|
+
"warning": "#8c6c3e",
|
|
33
|
+
"danger": "#f52a65"
|
|
20
34
|
}
|
|
21
35
|
}
|
package/themes/tokyo-night.json
CHANGED
|
@@ -17,5 +17,19 @@
|
|
|
17
17
|
"important": "#bb9af7",
|
|
18
18
|
"warning": "#e0af68",
|
|
19
19
|
"danger": "#f7768e"
|
|
20
|
+
},
|
|
21
|
+
"otherPalette": {
|
|
22
|
+
"lower": "#d0d5e3",
|
|
23
|
+
"background": "#e1e2e7",
|
|
24
|
+
"raised": "#e9e9ed",
|
|
25
|
+
"border": "#c4c8da",
|
|
26
|
+
"text": "#3760bf",
|
|
27
|
+
"muted": "#6172b0",
|
|
28
|
+
"accent": "#2e7de9",
|
|
29
|
+
"info": "#2e7de9",
|
|
30
|
+
"success": "#587539",
|
|
31
|
+
"important": "#9854f1",
|
|
32
|
+
"warning": "#8c6c3e",
|
|
33
|
+
"danger": "#f52a65"
|
|
20
34
|
}
|
|
21
35
|
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"kind": "mapped",
|
|
3
|
-
"id": "tokyo-night-day",
|
|
4
|
-
"name": "Tokyo Night Day",
|
|
5
|
-
"family": "Tokyo Night",
|
|
6
|
-
"mode": "light",
|
|
7
|
-
"palette": {
|
|
8
|
-
"lower": "#d0d5e3",
|
|
9
|
-
"background": "#e1e2e7",
|
|
10
|
-
"raised": "#e9e9ed",
|
|
11
|
-
"border": "#c4c8da",
|
|
12
|
-
"text": "#3760bf",
|
|
13
|
-
"muted": "#6172b0",
|
|
14
|
-
"accent": "#2e7de9",
|
|
15
|
-
"info": "#2e7de9",
|
|
16
|
-
"success": "#587539",
|
|
17
|
-
"important": "#9854f1",
|
|
18
|
-
"warning": "#8c6c3e",
|
|
19
|
-
"danger": "#f52a65"
|
|
20
|
-
}
|
|
21
|
-
}
|