@zpress/core 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/index.d.ts CHANGED
@@ -69,12 +69,22 @@ export declare interface CardConfig {
69
69
  };
70
70
  }
71
71
 
72
+ /**
73
+ * All valid color modes — used for validation.
74
+ */
75
+ export declare const COLOR_MODES: readonly ColorMode[];
76
+
77
+ /**
78
+ * How dark/light mode is controlled.
79
+ */
80
+ export declare type ColorMode = 'dark' | 'light' | 'toggle';
81
+
72
82
  /**
73
83
  * Error produced during config validation in `defineConfig`.
74
84
  */
75
85
  export declare interface ConfigError {
76
86
  readonly _tag: 'ConfigError';
77
- readonly type: 'empty_sections' | 'missing_field' | 'duplicate_prefix' | 'invalid_icon' | 'invalid_entry';
87
+ readonly type: 'empty_sections' | 'missing_field' | 'duplicate_prefix' | 'invalid_icon' | 'invalid_entry' | 'invalid_theme';
78
88
  readonly message: string;
79
89
  }
80
90
 
@@ -513,6 +523,14 @@ export declare interface Paths {
513
523
  readonly cacheDir: string;
514
524
  }
515
525
 
526
+ /**
527
+ * Resolve the default color mode for a given theme name.
528
+ *
529
+ * @param theme - Built-in theme identifier
530
+ * @returns The theme's natural color mode
531
+ */
532
+ export declare function resolveDefaultColorMode(theme: ThemeName): ColorMode;
533
+
516
534
  /**
517
535
  * Internal resolved node — produced by the resolver, consumed by copy + sidebar/nav generators.
518
536
  */
@@ -750,6 +768,73 @@ export declare interface SyncResult {
750
768
  readonly elapsed: number;
751
769
  }
752
770
 
771
+ /**
772
+ * All valid theme names — used for validation.
773
+ */
774
+ export declare const THEME_NAMES: readonly ThemeName[];
775
+
776
+ /**
777
+ * Optional color overrides keyed to CSS custom properties.
778
+ *
779
+ * Each key maps to one or more `--zp-c-*` / `--rp-c-*` variables.
780
+ * Values must be valid CSS color strings (hex or rgba).
781
+ */
782
+ export declare interface ThemeColors {
783
+ readonly brand?: string;
784
+ readonly brandLight?: string;
785
+ readonly brandDark?: string;
786
+ readonly brandSoft?: string;
787
+ readonly bg?: string;
788
+ readonly bgAlt?: string;
789
+ readonly bgElv?: string;
790
+ readonly bgSoft?: string;
791
+ readonly text1?: string;
792
+ readonly text2?: string;
793
+ readonly text3?: string;
794
+ readonly divider?: string;
795
+ readonly border?: string;
796
+ readonly homeBg?: string;
797
+ }
798
+
799
+ /**
800
+ * Top-level theme configuration for `zpress.config.ts`.
801
+ */
802
+ export declare interface ThemeConfig {
803
+ /**
804
+ * Built-in theme to use.
805
+ * @default 'base'
806
+ */
807
+ readonly name?: ThemeName;
808
+ /**
809
+ * Color mode behavior. Defaults to the theme's natural mode.
810
+ */
811
+ readonly colorMode?: ColorMode;
812
+ /**
813
+ * Show the theme switcher dropdown in the nav bar.
814
+ * @default false
815
+ */
816
+ readonly switcher?: boolean;
817
+ /**
818
+ * Partial color overrides applied in light mode (or base mode).
819
+ */
820
+ readonly colors?: ThemeColors;
821
+ /**
822
+ * Partial color overrides applied only in dark mode.
823
+ */
824
+ readonly darkColors?: ThemeColors;
825
+ }
826
+
827
+ /**
828
+ * Theme types and default resolution.
829
+ *
830
+ * Defines the built-in theme palette system: theme names, color modes,
831
+ * per-theme color overrides, and the top-level ThemeConfig shape.
832
+ */
833
+ /**
834
+ * Built-in theme identifiers.
835
+ */
836
+ export declare type ThemeName = 'base' | 'midnight' | 'arcade';
837
+
753
838
  /**
754
839
  * URL path (e.g. `"/guides/add-api-route"`)
755
840
  */
@@ -917,6 +1002,11 @@ export declare interface ZpressConfig {
917
1002
  * Site meta description. Used as the hero headline on the home page.
918
1003
  */
919
1004
  readonly description?: string;
1005
+ /**
1006
+ * Theme configuration.
1007
+ * Controls the visual theme, color mode, and optional color overrides.
1008
+ */
1009
+ readonly theme?: ThemeConfig;
920
1010
  /**
921
1011
  * Path to a custom favicon file served from `.zpress/public/`.
922
1012
  * When omitted, defaults to the auto-generated `/icon.svg`.
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
+ import { P, match } from "ts-pattern";
1
2
  import { loadConfig } from "c12";
2
3
  import promises from "node:fs/promises";
3
4
  import node_path from "node:path";
4
5
  import { log } from "@clack/prompts";
5
- import { P, match } from "ts-pattern";
6
6
  import { capitalize, groupBy, isUndefined, omitBy, range, words } from "es-toolkit";
7
7
  import { createHash } from "node:crypto";
8
8
  import gray_matter from "gray-matter";
@@ -47,6 +47,19 @@ function collectResults(results) {
47
47
  []
48
48
  ]);
49
49
  }
50
+ const THEME_NAMES = [
51
+ 'base',
52
+ 'midnight',
53
+ 'arcade'
54
+ ];
55
+ const COLOR_MODES = [
56
+ 'dark',
57
+ 'light',
58
+ 'toggle'
59
+ ];
60
+ function resolveDefaultColorMode(theme) {
61
+ return match(theme).with('base', ()=>'toggle').with('midnight', ()=>'dark').with('arcade', ()=>'dark').exhaustive();
62
+ }
50
63
  function defineConfig(config) {
51
64
  return config;
52
65
  }
@@ -85,6 +98,11 @@ function validateConfig(config) {
85
98
  featErr,
86
99
  null
87
100
  ];
101
+ const [themeErr] = validateTheme(config.theme);
102
+ if (themeErr) return [
103
+ themeErr,
104
+ null
105
+ ];
88
106
  return [
89
107
  null,
90
108
  config
@@ -217,6 +235,71 @@ function validateFeature(feature) {
217
235
  if (feature.icon && !feature.icon.includes(':')) return configError('invalid_icon', `Feature "${feature.text}": icon must be an Iconify identifier (e.g. "pixelarticons:speed-fast")`);
218
236
  return null;
219
237
  }
238
+ function validateThemeColors(colors, label) {
239
+ const colorPattern = /^(?:#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})|rgba?\([^)]*\))$/;
240
+ const keys = [
241
+ 'brand',
242
+ 'brandLight',
243
+ 'brandDark',
244
+ 'brandSoft',
245
+ 'bg',
246
+ 'bgAlt',
247
+ 'bgElv',
248
+ 'bgSoft',
249
+ 'text1',
250
+ 'text2',
251
+ 'text3',
252
+ 'divider',
253
+ 'border',
254
+ 'homeBg'
255
+ ];
256
+ const firstError = keys.reduce((acc, key)=>{
257
+ if (acc) return acc;
258
+ const value = colors[key];
259
+ if (void 0 !== value && !colorPattern.test(value)) return configError('invalid_theme', `theme.${label}.${key}: "${value}" is not a valid color (use hex #xxx/#xxxxxx or rgba())`);
260
+ return null;
261
+ }, null);
262
+ if (firstError) return [
263
+ firstError,
264
+ null
265
+ ];
266
+ return [
267
+ null,
268
+ true
269
+ ];
270
+ }
271
+ function validateTheme(theme) {
272
+ if (void 0 === theme) return [
273
+ null,
274
+ true
275
+ ];
276
+ if (void 0 !== theme.name && !THEME_NAMES.includes(theme.name)) return [
277
+ configError('invalid_theme', `theme.name: "${theme.name}" is not a valid theme (use ${THEME_NAMES.map((n)=>`"${n}"`).join(', ')})`),
278
+ null
279
+ ];
280
+ if (void 0 !== theme.colorMode && !COLOR_MODES.includes(theme.colorMode)) return [
281
+ configError('invalid_theme', `theme.colorMode: "${theme.colorMode}" is not valid (use ${COLOR_MODES.map((m)=>`"${m}"`).join(', ')})`),
282
+ null
283
+ ];
284
+ if (theme.colors) {
285
+ const [colorsErr] = validateThemeColors(theme.colors, 'colors');
286
+ if (colorsErr) return [
287
+ colorsErr,
288
+ null
289
+ ];
290
+ }
291
+ if (theme.darkColors) {
292
+ const [darkErr] = validateThemeColors(theme.darkColors, 'darkColors');
293
+ if (darkErr) return [
294
+ darkErr,
295
+ null
296
+ ];
297
+ }
298
+ return [
299
+ null,
300
+ true
301
+ ];
302
+ }
220
303
  async function config_loadConfig(dir) {
221
304
  const { config } = await loadConfig({
222
305
  cwd: dir,
@@ -2831,4 +2914,4 @@ function createPaths(dir) {
2831
2914
  cacheDir: node_path.resolve(outputRoot, 'cache')
2832
2915
  };
2833
2916
  }
2834
- export { configError, config_loadConfig as loadConfig, createPaths, defineConfig, generateAssets, generateBannerSvg, generateIconSvg, generateLogoSvg, hasGlobChars, loadManifest, resolveEntries, sync, syncError, validateConfig };
2917
+ export { COLOR_MODES, THEME_NAMES, configError, config_loadConfig as loadConfig, createPaths, defineConfig, generateAssets, generateBannerSvg, generateIconSvg, generateLogoSvg, hasGlobChars, loadManifest, resolveDefaultColorMode, resolveEntries, sync, syncError, validateConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zpress/core",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Config loading, sync engine, and asset utilities for zpress",
5
5
  "keywords": [
6
6
  "config",