@ggui-ai/project-config 0.15.0 → 0.17.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/node.d.ts CHANGED
@@ -121,6 +121,6 @@ export { discoverLocalUis, discoverFromGguiJsonPath, type DiscoveredUi, type Dis
121
121
  export { discoverPrimitives, discoverPrimitivesFromGguiJsonPath, type DiscoveredPrimitiveCatalog, type PrimitiveDiscoveryIssue, type PrimitiveDiscoveryResult, type DiscoverPrimitivesOptions, } from './primitive-discovery.js';
122
122
  export { discoverMcpMounts, GGUI_MCP_MOUNT_FACTORY_EXPORT, type DiscoveredMcpMount, type McpMountDiscoveryIssue, type McpMountDiscoveryResult, type DiscoverMcpMountsOptions, } from './mcp-mount-discovery.js';
123
123
  export { GGUI_PRIMITIVES_JSON_FILENAME, PrimitivesManifestV1, parsePrimitivesManifest, safeParsePrimitivesManifest, type PrimitivesManifest, } from './primitives-manifest.js';
124
- export { ThemeDocumentV1, parseThemeDocument, safeParseThemeDocument, type ThemeDocument, } from './theme.js';
124
+ export { ThemeDocumentV2, normalizeThemeDocument, parseThemeDocument, safeParseThemeDocument, type ThemeDocument, } from './theme.js';
125
125
  export { loadTheme, loadThemeFromGguiJsonPath, type LoadedTheme, type LoadThemeOptions, type LoadThemeResult, type ThemeLoadIssue, } from './theme-loader.js';
126
126
  //# sourceMappingURL=node.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"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"}
package/dist/node.js CHANGED
@@ -15,7 +15,7 @@
15
15
  * upward from `process.cwd()` to find the project-root file.
16
16
  * - Any hosting integration that reads the open manifest to
17
17
  * reconstitute an agent. Hosting layers treat `ggui.json` as
18
- * input-only; per-tenant overlay config lives outside this package.
18
+ * input-only; per-app overlay config lives outside this package.
19
19
  */
20
20
  import { existsSync, readFileSync, writeFileSync } from 'node:fs';
21
21
  import { dirname, join, resolve } from 'node:path';
@@ -215,9 +215,9 @@ export { discoverMcpMounts, GGUI_MCP_MOUNT_FACTORY_EXPORT, } from './mcp-mount-d
215
215
  export { GGUI_PRIMITIVES_JSON_FILENAME, PrimitivesManifestV1, parsePrimitivesManifest, safeParsePrimitivesManifest, } from './primitives-manifest.js';
216
216
  // Browser-safe theme document schema + parsers — re-exported on the
217
217
  // Node subpath so `ggui serve` / `ggui dev` get the full theme
218
- // surface from one import path. See `./theme.js` for the v1
218
+ // surface from one import path. See `./theme.js` for the v2
219
219
  // plain-DTCG contract.
220
- export { ThemeDocumentV1, parseThemeDocument, safeParseThemeDocument, } from './theme.js';
220
+ export { ThemeDocumentV2, normalizeThemeDocument, parseThemeDocument, safeParseThemeDocument, } from './theme.js';
221
221
  // Filesystem-backed theme loader — consumes `ggui.json#theme`,
222
222
  // resolves it against the project root, parses + validates, and
223
223
  // returns a `LoadedTheme` ready for the OSS `ggui serve` server.
package/dist/schema.d.ts CHANGED
@@ -156,7 +156,7 @@ declare const GenerationSchema: z.ZodObject<{
156
156
  model: "claude-fable-5-1" | "claude-fable-5" | "claude-opus-5" | "claude-sonnet-5" | "claude-haiku-4-5-20251001" | "claude-opus-4-8" | "claude-sonnet-4-6" | "claude-opus-4-7" | "claude-opus-4-6";
157
157
  } | {
158
158
  provider: "openai";
159
- model: "gpt-5.6" | "gpt-5.6-sol" | "gpt-5.6-terra" | "gpt-5.6-luna" | "gpt-5.5" | "gpt-5.5-2026-04-23" | "gpt-5.5-pro" | "gpt-5.5-pro-2026-04-23" | "gpt-5.4" | "gpt-5.4-2026-03-05" | "gpt-5.4-mini" | "gpt-5.4-mini-2026-03-17" | "gpt-5.4-nano" | "gpt-5.4-nano-2026-03-17" | "gpt-5.3-codex";
159
+ model: "gpt-6-astra" | "gpt-5.6" | "gpt-5.6-sol" | "gpt-5.6-terra" | "gpt-5.6-luna" | "gpt-5.5" | "gpt-5.5-2026-04-23" | "gpt-5.5-pro" | "gpt-5.5-pro-2026-04-23" | "gpt-5.4" | "gpt-5.4-2026-03-05" | "gpt-5.4-mini" | "gpt-5.4-mini-2026-03-17" | "gpt-5.4-nano" | "gpt-5.4-nano-2026-03-17" | "gpt-5.3-codex";
160
160
  } | {
161
161
  provider: "google";
162
162
  model: "gemini-3.7-flash" | "gemini-3.6-flash" | "gemini-3.5-flash" | "gemini-3.5-flash-lite" | "gemini-flash-latest" | "gemini-3.1-flash-lite" | "gemini-3.1-pro-preview" | "gemini-3.1-flash-lite-preview" | "gemini-3-flash-preview";
@@ -333,7 +333,7 @@ export declare const GguiJsonV1: z.ZodObject<{
333
333
  model: "claude-fable-5-1" | "claude-fable-5" | "claude-opus-5" | "claude-sonnet-5" | "claude-haiku-4-5-20251001" | "claude-opus-4-8" | "claude-sonnet-4-6" | "claude-opus-4-7" | "claude-opus-4-6";
334
334
  } | {
335
335
  provider: "openai";
336
- model: "gpt-5.6" | "gpt-5.6-sol" | "gpt-5.6-terra" | "gpt-5.6-luna" | "gpt-5.5" | "gpt-5.5-2026-04-23" | "gpt-5.5-pro" | "gpt-5.5-pro-2026-04-23" | "gpt-5.4" | "gpt-5.4-2026-03-05" | "gpt-5.4-mini" | "gpt-5.4-mini-2026-03-17" | "gpt-5.4-nano" | "gpt-5.4-nano-2026-03-17" | "gpt-5.3-codex";
336
+ model: "gpt-6-astra" | "gpt-5.6" | "gpt-5.6-sol" | "gpt-5.6-terra" | "gpt-5.6-luna" | "gpt-5.5" | "gpt-5.5-2026-04-23" | "gpt-5.5-pro" | "gpt-5.5-pro-2026-04-23" | "gpt-5.4" | "gpt-5.4-2026-03-05" | "gpt-5.4-mini" | "gpt-5.4-mini-2026-03-17" | "gpt-5.4-nano" | "gpt-5.4-nano-2026-03-17" | "gpt-5.3-codex";
337
337
  } | {
338
338
  provider: "google";
339
339
  model: "gemini-3.7-flash" | "gemini-3.6-flash" | "gemini-3.5-flash" | "gemini-3.5-flash-lite" | "gemini-flash-latest" | "gemini-3.1-flash-lite" | "gemini-3.1-pro-preview" | "gemini-3.1-flash-lite-preview" | "gemini-3-flash-preview";
@@ -1,33 +1,34 @@
1
- import type { DtcgTheme, ThemeMode } from '@ggui-ai/design/themes';
1
+ import type { DtcgTheme, ThemeMode, ThemeVariableMap } from '@ggui-ai/design/themes';
2
2
  import type { GguiJsonV1, ThemeConfig } from './schema.js';
3
- import { type ThemeDocument } from './theme.js';
3
+ /**
4
+ * Both modes' projections of a loaded theme — the `overlays` an app
5
+ * theme carries on the wire (ggui#987 §3), produced by the ONE producer.
6
+ */
7
+ export interface ThemeOverlays {
8
+ readonly light: ThemeVariableMap;
9
+ readonly dark: ThemeVariableMap;
10
+ }
4
11
  /**
5
12
  * One loaded theme — discriminated on `source`. Every variant carries
6
- * the resolved `mode`, the parsed token document, and the pre-rendered
7
- * `--ggui-*` CSS variable block.
13
+ * the resolved `mode` (the default the card paints when no host
14
+ * announces one), the producer-ready document, and both modes'
15
+ * projections (`overlays`).
8
16
  *
9
- * **`document` is shape-discriminated by `source`** (no casts, two
10
- * structurally-similar but separately-validated underlying types):
17
+ * **`document` is the producer's `DtcgTheme` on every variant**: the
18
+ * registry's own document for `default` / `preset`, and for `file` the
19
+ * strict-Zod `ThemeDocument` after `normalizeThemeDocument` (the open
20
+ * `ggui.json#theme` format keeps `motion`/`accessibility`/`zIndex`
21
+ * optional so external tools that emit only a subset still parse; the
22
+ * normaliser fills them from the shipped default).
11
23
  *
12
- * - `default` / `preset` → `DtcgTheme` (the canonical theme shape
13
- * consumed by every registry-side theme — produces full
14
- * `--ggui-*` CSS through `parseTheme`; every group required).
15
- * - `file` → `ThemeDocument` (the strict-Zod schema for the open
16
- * `ggui.json#theme` file format — same `color`/`font`/`spacing`/
17
- * `shape`/`motion`/`accessibility`/`zIndex` vocabulary
18
- * as `DtcgTheme`, but `motion`/`accessibility`/`zIndex`
19
- * are optional so external tools that emit only a subset still
20
- * parse).
21
- *
22
- * Consumers narrow on `source` if they ever need the shape. CSS-variable
23
- * emission happens upstream into `cssVariables`, so most downstream code
24
- * only reads that string.
24
+ * Consumers narrow on `source` only for provenance. The projection
25
+ * happens here, once, into `overlays`; downstream code reads the maps.
25
26
  */
26
27
  export type LoadedTheme = {
27
28
  readonly source: 'default';
28
29
  readonly mode: ThemeMode;
29
30
  readonly document: DtcgTheme;
30
- readonly cssVariables: string;
31
+ readonly overlays: ThemeOverlays;
31
32
  } | {
32
33
  readonly source: 'preset';
33
34
  /** Registry id (`'claudic'`, `'ggui'`, …). */
@@ -36,14 +37,15 @@ export type LoadedTheme = {
36
37
  /** Flat dot-path token overrides applied on top of the preset. */
37
38
  readonly overrides?: Record<string, string>;
38
39
  readonly document: DtcgTheme;
39
- readonly cssVariables: string;
40
+ readonly overlays: ThemeOverlays;
40
41
  } | {
41
42
  readonly source: 'file';
42
43
  /** Absolute filesystem path of the loaded theme file. */
43
44
  readonly path: string;
44
45
  readonly mode: ThemeMode;
45
- readonly document: ThemeDocument;
46
- readonly cssVariables: string;
46
+ /** The parsed file, normalised to the producer's document (see `normalizeThemeDocument`). */
47
+ readonly document: DtcgTheme;
48
+ readonly overlays: ThemeOverlays;
47
49
  };
48
50
  /**
49
51
  * An error surfaced during theme load. Shape matches the issue
@@ -1 +1 @@
1
- {"version":3,"file":"theme-loader.d.ts","sourceRoot":"","sources":["../src/theme-loader.ts"],"names":[],"mappings":"AA4CA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,WAAW,GACnB;IACE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B,GACD;IACE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,kEAAkE;IAClE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B,GACD;IACE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEN;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,mEAAmE;AACnE,MAAM,MAAM,eAAe,GACvB;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;CAC7B,GACD;IACE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;CAChC,CAAC;AAEN;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,QAAQ,EAAE,UAAU,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,eAAe,CAwCpE;AAyMD;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,UAAU,GACnB,eAAe,CAGjB;AAED;;;;;GAKG;AACH,YAAY,EAAE,WAAW,EAAE,CAAC"}
1
+ {"version":3,"file":"theme-loader.d.ts","sourceRoot":"","sources":["../src/theme-loader.ts"],"names":[],"mappings":"AA2CA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AACrF,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG3D;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;CACjC;AAUD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,WAAW,GACnB;IACE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;CAClC,GACD;IACE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,kEAAkE;IAClE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;CAClC,GACD;IACE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,6FAA6F;IAC7F,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;CAClC,CAAC;AAEN;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,mEAAmE;AACnE,MAAM,MAAM,eAAe,GACvB;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;CAC7B,GACD;IACE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;CAChC,CAAC;AAEN;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,QAAQ,EAAE,UAAU,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,eAAe,CAwCpE;AA0MD;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,UAAU,GACnB,eAAe,CAGjB;AAED;;;;;GAKG;AACH,YAAY,EAAE,WAAW,EAAE,CAAC"}
@@ -14,16 +14,15 @@
14
14
  * exception. `ggui serve` escalates issues to a fatal exit before
15
15
  * binding a port.
16
16
  *
17
- * ## cssVariables pre-rendering
17
+ * ## Both modes, projected
18
18
  *
19
- * The loader pre-renders the `:root { --ggui-*: value; }` CSS block
20
- * via `@ggui-ai/design/themes`: `parseTheme(id, DtcgTheme)` for the
21
- * preset / default paths (full DTCG with motion tokens) and
22
- * `generateCssVariables(ThemeDocument)` for the file path (the
23
- * duck-typed walker accepts the plain DTCG document without requiring
24
- * the strict {@link DtcgTheme} shape). Downstream consumers (console,
25
- * render endpoint, MCP apps iframe) inject the pre-rendered string
26
- * without re-walking the token tree.
19
+ * Every `LoadedTheme` carries `overlays` — the `--ggui-*` variable map
20
+ * for BOTH modes, produced by `@ggui-ai/design/themes`'
21
+ * `deriveThemeVariables(document, mode)` (the one producer, ggui#987):
22
+ * a preset's light document and its dark twin, or a file's single
23
+ * document projected for each mode. Downstream consumers (the CLI's
24
+ * deploy PATCH, the console picker) ship the projection as the app's
25
+ * theme; nothing re-walks the token tree.
27
26
  *
28
27
  * ## Mode handling
29
28
  *
@@ -35,8 +34,15 @@
35
34
  */
36
35
  import { readFileSync, existsSync } from 'node:fs';
37
36
  import { dirname, isAbsolute, resolve } from 'node:path';
38
- import { generateCssVariables, getRawTheme, getTheme, lightTheme, parseTheme, } from '@ggui-ai/design/themes';
39
- import { parseThemeDocument } from './theme.js';
37
+ import { ThemeDocumentInvalidError, darkTheme, deriveThemeVariables, getRawTheme, lightTheme, } from '@ggui-ai/design/themes';
38
+ import { normalizeThemeDocument, parseThemeDocument } from './theme.js';
39
+ /** Project a light document (and its dark twin when it has one) for both modes. */
40
+ function deriveOverlays(light, dark) {
41
+ return {
42
+ light: deriveThemeVariables(light, 'light'),
43
+ dark: deriveThemeVariables(dark ?? light, 'dark'),
44
+ };
45
+ }
40
46
  /**
41
47
  * Load the theme declared in `ggui.json#theme`, or fall back to the
42
48
  * shipped default when the field is absent.
@@ -59,7 +65,7 @@ export function loadTheme(options) {
59
65
  source: 'default',
60
66
  mode: 'light',
61
67
  document: lightTheme,
62
- cssVariables: parseTheme('default', lightTheme).cssVariables,
68
+ overlays: deriveOverlays(lightTheme, darkTheme),
63
69
  },
64
70
  };
65
71
  }
@@ -86,8 +92,8 @@ export function loadTheme(options) {
86
92
  * manifest parse.
87
93
  */
88
94
  function loadPreset(preset, mode, overrides) {
89
- // Resolve the raw token tree first so we can layer overrides
90
- // before parseTheme walks the leaves.
95
+ // Resolve the raw token tree first (also the registration check)
96
+ // before layering overrides and projecting both modes.
91
97
  const raw = getRawTheme(preset, mode);
92
98
  if (!raw) {
93
99
  return {
@@ -100,45 +106,25 @@ function loadPreset(preset, mode, overrides) {
100
106
  },
101
107
  };
102
108
  }
103
- // No overrides: hand off to the registry's parser directly so we
104
- // share its cache.
105
- if (!overrides || Object.keys(overrides).length === 0) {
106
- const parsed = getTheme(preset, mode);
107
- if (!parsed) {
108
- // Should not happen — getRawTheme succeeded above. Defensive.
109
- return {
110
- ok: false,
111
- issue: {
112
- path: preset,
113
- message: `Theme preset "${preset}" parsed unexpectedly empty.`,
114
- },
115
- };
116
- }
117
- return {
118
- ok: true,
119
- theme: {
120
- source: 'preset',
121
- preset,
122
- mode,
123
- document: raw,
124
- cssVariables: parsed.cssVariables,
125
- },
126
- };
127
- }
128
- // With overrides: clone the raw tree, deep-set each leaf's $value,
129
- // then re-parse. We bypass the registry's cache because the
130
- // override set is per-call and would pollute it.
131
- const mutated = applyOverrides(raw, overrides);
132
- const parsed = parseTheme(preset, mutated);
109
+ // Both modes ride the loaded theme (ggui#987 §3): the preset's light
110
+ // document and its dark twin (a single-mode preset projects the one
111
+ // document for both), overrides applied to each before projection.
112
+ // Overrides clone the raw trees so the registry's cache stays clean.
113
+ const rawLight = getRawTheme(preset, 'light') ?? raw;
114
+ const rawDark = getRawTheme(preset, 'dark');
115
+ const hasOverrides = overrides !== undefined && Object.keys(overrides).length > 0;
116
+ const light = hasOverrides ? applyOverrides(rawLight, overrides) : rawLight;
117
+ const dark = rawDark === undefined ? undefined : hasOverrides ? applyOverrides(rawDark, overrides) : rawDark;
118
+ const document = mode === 'dark' ? (dark ?? light) : light;
133
119
  return {
134
120
  ok: true,
135
121
  theme: {
136
122
  source: 'preset',
137
123
  preset,
138
124
  mode,
139
- overrides,
140
- document: mutated,
141
- cssVariables: parsed.cssVariables,
125
+ ...(hasOverrides ? { overrides } : {}),
126
+ document,
127
+ overlays: deriveOverlays(light, dark),
142
128
  },
143
129
  };
144
130
  }
@@ -203,14 +189,35 @@ function loadFile(filePath, mode, projectRoot) {
203
189
  },
204
190
  };
205
191
  }
192
+ // The producer is the validator of what the schema cannot see (a
193
+ // family with no `500` anchor, an unparseable colour): its refusal is
194
+ // the load issue, named by path.
195
+ const normalized = normalizeThemeDocument(document);
196
+ let overlays;
197
+ try {
198
+ overlays = deriveOverlays(normalized, undefined);
199
+ }
200
+ catch (cause) {
201
+ if (cause instanceof ThemeDocumentInvalidError) {
202
+ return {
203
+ ok: false,
204
+ issue: {
205
+ path: absolutePath,
206
+ message: `Theme file cannot be projected: ${cause.message}`,
207
+ cause,
208
+ },
209
+ };
210
+ }
211
+ throw cause;
212
+ }
206
213
  return {
207
214
  ok: true,
208
215
  theme: {
209
216
  source: 'file',
210
217
  path: absolutePath,
211
218
  mode,
212
- document,
213
- cssVariables: generateCssVariables(document),
219
+ document: normalized,
220
+ overlays,
214
221
  },
215
222
  };
216
223
  }