@ggui-ai/project-config 0.14.0 → 0.16.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 +1 -1
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +3 -3
- package/dist/schema.d.ts +5 -5
- package/dist/theme-loader.d.ts +25 -23
- package/dist/theme-loader.d.ts.map +1 -1
- package/dist/theme-loader.js +56 -49
- package/dist/theme.d.ts +268 -104
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +180 -133
- package/package.json +3 -3
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 {
|
|
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
|
package/dist/node.d.ts.map
CHANGED
|
@@ -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-
|
|
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
|
|
218
|
+
// surface from one import path. See `./theme.js` for the v2
|
|
219
219
|
// plain-DTCG contract.
|
|
220
|
-
export {
|
|
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";
|
|
@@ -265,12 +265,12 @@ export declare const GguiJsonV1: z.ZodObject<{
|
|
|
265
265
|
bundleHost: z.ZodOptional<z.ZodString>;
|
|
266
266
|
bundleSri: z.ZodOptional<z.ZodString>;
|
|
267
267
|
styleUrl: z.ZodOptional<z.ZodURL>;
|
|
268
|
-
connect: z.ZodOptional<z.
|
|
269
|
-
requires: z.ZodOptional<z.
|
|
268
|
+
connect: z.ZodOptional<z.ZodArray<z.ZodURL>>;
|
|
269
|
+
requires: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
270
270
|
typesUrl: z.ZodOptional<z.ZodURL>;
|
|
271
271
|
typesSri: z.ZodOptional<z.ZodString>;
|
|
272
272
|
}, z.core.$strict>>>;
|
|
273
|
-
publicEnv: z.ZodOptional<z.
|
|
273
|
+
publicEnv: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
274
274
|
defaultDisplayMode: z.ZodOptional<z.ZodEnum<{
|
|
275
275
|
inline: "inline";
|
|
276
276
|
fullscreen: "fullscreen";
|
|
@@ -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";
|
package/dist/theme-loader.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
7
|
-
*
|
|
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
|
|
10
|
-
*
|
|
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
|
-
*
|
|
13
|
-
*
|
|
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
|
|
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
|
|
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
|
-
|
|
46
|
-
readonly
|
|
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":"
|
|
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"}
|
package/dist/theme-loader.js
CHANGED
|
@@ -14,16 +14,15 @@
|
|
|
14
14
|
* exception. `ggui serve` escalates issues to a fatal exit before
|
|
15
15
|
* binding a port.
|
|
16
16
|
*
|
|
17
|
-
* ##
|
|
17
|
+
* ## Both modes, projected
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
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 {
|
|
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
|
-
|
|
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
|
|
90
|
-
// before
|
|
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
|
-
//
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
|
141
|
-
|
|
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
|
-
|
|
219
|
+
document: normalized,
|
|
220
|
+
overlays,
|
|
214
221
|
},
|
|
215
222
|
};
|
|
216
223
|
}
|
package/dist/theme.d.ts
CHANGED
|
@@ -1,82 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `ggui.json#theme` → plain DTCG JSON document.
|
|
2
|
+
* `ggui.json#theme` → plain DTCG JSON document, v2 (ggui#987).
|
|
3
3
|
*
|
|
4
4
|
* The theme file is a plain JSON document matching the Design Tokens
|
|
5
|
-
* Community Group (DTCG) spec
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
5
|
+
* Community Group (DTCG) spec, in the same vocabulary as
|
|
6
|
+
* `@ggui-ai/design`'s canonical {@link DtcgTheme}: the curated registry
|
|
7
|
+
* themes and an authored `theme.json` are the same shape.
|
|
8
8
|
*
|
|
9
|
-
* **
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* `
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* the
|
|
9
|
+
* **What is authored, what is derived.** An author states the six
|
|
10
|
+
* layering roles (`ground`/`onGround`, `container`/`onContainer`,
|
|
11
|
+
* `sunken`/`onSunken`), one `500` anchor per family (`primary`,
|
|
12
|
+
* `success`, `warning`, `error`, `info`), the font families and
|
|
13
|
+
* weights, spacing, radii, shadows. Everything else — the ten-stop
|
|
14
|
+
* ramps, every `on*` ink and container, the neutral ladder, outlines,
|
|
15
|
+
* `elevated`, the type scale from the one `font.ramp` — is DERIVED by
|
|
16
|
+
* the one producer (`deriveThemeVariables`), so the retired ladders
|
|
17
|
+
* (`font.size`, `font.lineHeight`, `motion.duration`, `motion.easing`)
|
|
18
|
+
* and `$metadata.fontUrl` are refused, not ignored. Fonts are declared
|
|
19
|
+
* as `typography.faces` (https `src` only); the card installs them.
|
|
17
20
|
*
|
|
18
|
-
* **External-tool
|
|
19
|
-
*
|
|
20
|
-
* the
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* (`$name`, `$description`, `$metadata`) are all OPTIONAL so tools
|
|
24
|
-
* that only emit colors + dimensions still parse cleanly.
|
|
25
|
-
*
|
|
26
|
-
* **BREAKING for old `theme.json` files.** Authors who hand-wrote a
|
|
27
|
-
* `theme.json` against the pre-rc schema must rename:
|
|
28
|
-
*
|
|
29
|
-
* - `typography.fontFamily` → `font.family`
|
|
30
|
-
* - `typography.fontSize` → `font.size`
|
|
31
|
-
* - `typography.fontWeight` → `font.weight`
|
|
32
|
-
* - `typography.lineHeight` → `font.lineHeight`
|
|
33
|
-
* - top-level `radius` → `shape.radius`
|
|
34
|
-
* - top-level `shadow` → `shape.shadow`
|
|
35
|
-
* - top-level `duration` → `motion.duration`
|
|
36
|
-
* - top-level `transition` → `motion.transition`
|
|
37
|
-
*
|
|
38
|
-
* Plus: under `font.family`, `sans` is the only required sub-token
|
|
39
|
-
* (was previously freeform); other family slots (`mono`, …) are
|
|
40
|
-
* optional. The font-family record itself stays open so additional
|
|
41
|
-
* named families parse without a schema change.
|
|
21
|
+
* **External-tool leniency.** `motion`, `accessibility`, `zIndex` and
|
|
22
|
+
* the DTCG metadata fields stay OPTIONAL; {@link normalizeThemeDocument}
|
|
23
|
+
* fills them from the shipped default and flattens the DTCG object
|
|
24
|
+
* forms (structured shadows, transitions, array font stacks) to the CSS
|
|
25
|
+
* strings the producer reads.
|
|
42
26
|
*
|
|
43
27
|
* **Ownership boundary:**
|
|
44
28
|
*
|
|
45
29
|
* - *Where* the theme lives → `ggui.json#theme` (pointer string).
|
|
46
30
|
* - *What* the theme file looks like → this module's schema.
|
|
47
|
-
* - *How*
|
|
48
|
-
* `
|
|
49
|
-
* emit). The duck-typed walker accepts any DTCG-shaped tree, so
|
|
50
|
-
* the new nested groups (`font.size`, `shape.radius`, …) walk
|
|
51
|
-
* into `--ggui-font-size-md`/`--ggui-shape-radius-md` CSS vars
|
|
52
|
-
* without code changes.
|
|
31
|
+
* - *How* the document becomes CSS variables → `@ggui-ai/design`'s
|
|
32
|
+
* `deriveThemeVariables` (the loader calls it for both modes).
|
|
53
33
|
* - *Built-in default* when `theme` is absent → `@ggui-ai/design`'s
|
|
54
|
-
* shipped `lightTheme`.
|
|
55
|
-
*
|
|
56
|
-
* **Extending rules:**
|
|
57
|
-
*
|
|
58
|
-
* 1. Additive only within `schema: '1'`. New optional fields must
|
|
59
|
-
* default to no-op behaviour so older tooling ignores them.
|
|
60
|
-
* 2. Framework-neutral + host-neutral. No vendor names in enum
|
|
61
|
-
* values, no build-pipeline fields.
|
|
62
|
-
* 3. Root and per-group objects are strict — unknown keys fail
|
|
63
|
-
* parse. Same discipline as `ggui.json`, `ggui.ui.json`, and
|
|
64
|
-
* `ggui.primitives.json`.
|
|
34
|
+
* shipped `lightTheme` / `darkTheme`.
|
|
65
35
|
*
|
|
66
|
-
* **
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* union over `$type` (color / dimension / fontFamily / fontWeight /
|
|
70
|
-
* shadow / duration / cubicBezier / transition / number). Each
|
|
71
|
-
* variant validates its `$value` shape. `$description` is accepted
|
|
72
|
-
* everywhere as a free-form docstring. Token leaves under newer
|
|
73
|
-
* groups (`accessibility`, `zIndex`) accept a slightly
|
|
74
|
-
* wider token vocabulary because external tools sometimes encode
|
|
75
|
-
* scalar/array values with permissive `$type` strings (`string`,
|
|
76
|
-
* `array`, …); the schema accepts those without enumerating every
|
|
77
|
-
* possible spelling.
|
|
36
|
+
* **Extending rules:** additive only within `schema: '1'`; framework-
|
|
37
|
+
* and host-neutral; root and per-group objects are strict — unknown
|
|
38
|
+
* keys fail parse, the same discipline as `ggui.json`.
|
|
78
39
|
*/
|
|
79
40
|
import { z } from 'zod';
|
|
41
|
+
import type { DtcgTheme } from '@ggui-ai/design/themes';
|
|
80
42
|
/**
|
|
81
43
|
* Plain DTCG theme document v1. Required groups (`color`, `font`,
|
|
82
44
|
* `spacing`, `shape`) map 1:1 to `@ggui-ai/design`'s
|
|
@@ -96,25 +58,188 @@ import { z } from 'zod';
|
|
|
96
58
|
* allow-listed (DTCG metadata); anything else is a typo. Additive
|
|
97
59
|
* slices add new fields here under `schema: '1'`.
|
|
98
60
|
*/
|
|
99
|
-
export declare const
|
|
61
|
+
export declare const ThemeDocumentV2: z.ZodObject<{
|
|
100
62
|
$schema: z.ZodOptional<z.ZodString>;
|
|
101
63
|
$version: z.ZodOptional<z.ZodString>;
|
|
102
64
|
$name: z.ZodOptional<z.ZodString>;
|
|
103
65
|
$description: z.ZodOptional<z.ZodString>;
|
|
104
66
|
$metadata: z.ZodOptional<z.ZodObject<{
|
|
105
67
|
font: z.ZodOptional<z.ZodString>;
|
|
106
|
-
fontUrl: z.ZodOptional<z.ZodString>;
|
|
107
68
|
philosophy: z.ZodOptional<z.ZodString>;
|
|
69
|
+
frameless: z.ZodOptional<z.ZodBoolean>;
|
|
108
70
|
}, z.core.$strict>>;
|
|
109
|
-
color: z.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
71
|
+
color: z.ZodObject<{
|
|
72
|
+
primary: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
73
|
+
$type: z.ZodLiteral<"color">;
|
|
74
|
+
$value: z.ZodString;
|
|
75
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
76
|
+
}, z.core.$strict>>;
|
|
77
|
+
neutral: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
78
|
+
$type: z.ZodLiteral<"color">;
|
|
79
|
+
$value: z.ZodString;
|
|
80
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
81
|
+
}, z.core.$strict>>>;
|
|
82
|
+
success: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
83
|
+
$type: z.ZodLiteral<"color">;
|
|
84
|
+
$value: z.ZodString;
|
|
85
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
86
|
+
}, z.core.$strict>>;
|
|
87
|
+
warning: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
88
|
+
$type: z.ZodLiteral<"color">;
|
|
89
|
+
$value: z.ZodString;
|
|
90
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
91
|
+
}, z.core.$strict>>;
|
|
92
|
+
error: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
93
|
+
$type: z.ZodLiteral<"color">;
|
|
94
|
+
$value: z.ZodString;
|
|
95
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
96
|
+
}, z.core.$strict>>;
|
|
97
|
+
info: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
98
|
+
$type: z.ZodLiteral<"color">;
|
|
99
|
+
$value: z.ZodString;
|
|
100
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
101
|
+
}, z.core.$strict>>;
|
|
102
|
+
ground: z.ZodObject<{
|
|
103
|
+
$type: z.ZodLiteral<"color">;
|
|
104
|
+
$value: z.ZodString;
|
|
105
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
106
|
+
}, z.core.$strict>;
|
|
107
|
+
onGround: z.ZodObject<{
|
|
108
|
+
$type: z.ZodLiteral<"color">;
|
|
109
|
+
$value: z.ZodString;
|
|
110
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
111
|
+
}, z.core.$strict>;
|
|
112
|
+
container: z.ZodObject<{
|
|
113
|
+
$type: z.ZodLiteral<"color">;
|
|
114
|
+
$value: z.ZodString;
|
|
115
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
116
|
+
}, z.core.$strict>;
|
|
117
|
+
onContainer: z.ZodObject<{
|
|
118
|
+
$type: z.ZodLiteral<"color">;
|
|
119
|
+
$value: z.ZodString;
|
|
120
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
121
|
+
}, z.core.$strict>;
|
|
122
|
+
sunken: z.ZodObject<{
|
|
123
|
+
$type: z.ZodLiteral<"color">;
|
|
124
|
+
$value: z.ZodString;
|
|
125
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
126
|
+
}, z.core.$strict>;
|
|
127
|
+
onSunken: z.ZodObject<{
|
|
128
|
+
$type: z.ZodLiteral<"color">;
|
|
129
|
+
$value: z.ZodString;
|
|
130
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
131
|
+
}, z.core.$strict>;
|
|
132
|
+
link: z.ZodOptional<z.ZodObject<{
|
|
133
|
+
$type: z.ZodLiteral<"color">;
|
|
134
|
+
$value: z.ZodString;
|
|
135
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
136
|
+
}, z.core.$strict>>;
|
|
137
|
+
outline: z.ZodOptional<z.ZodObject<{
|
|
138
|
+
$type: z.ZodLiteral<"color">;
|
|
139
|
+
$value: z.ZodString;
|
|
140
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
141
|
+
}, z.core.$strict>>;
|
|
142
|
+
outlineVariant: z.ZodOptional<z.ZodObject<{
|
|
143
|
+
$type: z.ZodLiteral<"color">;
|
|
144
|
+
$value: z.ZodString;
|
|
145
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
146
|
+
}, z.core.$strict>>;
|
|
147
|
+
onPrimary: z.ZodOptional<z.ZodObject<{
|
|
148
|
+
$type: z.ZodLiteral<"color">;
|
|
149
|
+
$value: z.ZodString;
|
|
150
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
151
|
+
}, z.core.$strict>>;
|
|
152
|
+
primaryContainer: z.ZodOptional<z.ZodObject<{
|
|
153
|
+
$type: z.ZodLiteral<"color">;
|
|
154
|
+
$value: z.ZodString;
|
|
155
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
156
|
+
}, z.core.$strict>>;
|
|
157
|
+
onPrimaryContainer: z.ZodOptional<z.ZodObject<{
|
|
158
|
+
$type: z.ZodLiteral<"color">;
|
|
159
|
+
$value: z.ZodString;
|
|
160
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
161
|
+
}, z.core.$strict>>;
|
|
162
|
+
onSuccess: z.ZodOptional<z.ZodObject<{
|
|
163
|
+
$type: z.ZodLiteral<"color">;
|
|
164
|
+
$value: z.ZodString;
|
|
165
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
166
|
+
}, z.core.$strict>>;
|
|
167
|
+
successContainer: z.ZodOptional<z.ZodObject<{
|
|
168
|
+
$type: z.ZodLiteral<"color">;
|
|
169
|
+
$value: z.ZodString;
|
|
170
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
171
|
+
}, z.core.$strict>>;
|
|
172
|
+
onSuccessContainer: z.ZodOptional<z.ZodObject<{
|
|
173
|
+
$type: z.ZodLiteral<"color">;
|
|
174
|
+
$value: z.ZodString;
|
|
175
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
176
|
+
}, z.core.$strict>>;
|
|
177
|
+
onWarning: z.ZodOptional<z.ZodObject<{
|
|
178
|
+
$type: z.ZodLiteral<"color">;
|
|
179
|
+
$value: z.ZodString;
|
|
180
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
181
|
+
}, z.core.$strict>>;
|
|
182
|
+
warningContainer: z.ZodOptional<z.ZodObject<{
|
|
183
|
+
$type: z.ZodLiteral<"color">;
|
|
184
|
+
$value: z.ZodString;
|
|
185
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
186
|
+
}, z.core.$strict>>;
|
|
187
|
+
onWarningContainer: z.ZodOptional<z.ZodObject<{
|
|
188
|
+
$type: z.ZodLiteral<"color">;
|
|
189
|
+
$value: z.ZodString;
|
|
190
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
191
|
+
}, z.core.$strict>>;
|
|
192
|
+
onError: z.ZodOptional<z.ZodObject<{
|
|
193
|
+
$type: z.ZodLiteral<"color">;
|
|
194
|
+
$value: z.ZodString;
|
|
195
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
196
|
+
}, z.core.$strict>>;
|
|
197
|
+
errorContainer: z.ZodOptional<z.ZodObject<{
|
|
198
|
+
$type: z.ZodLiteral<"color">;
|
|
199
|
+
$value: z.ZodString;
|
|
200
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
201
|
+
}, z.core.$strict>>;
|
|
202
|
+
onErrorContainer: z.ZodOptional<z.ZodObject<{
|
|
203
|
+
$type: z.ZodLiteral<"color">;
|
|
204
|
+
$value: z.ZodString;
|
|
205
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
206
|
+
}, z.core.$strict>>;
|
|
207
|
+
onInfo: z.ZodOptional<z.ZodObject<{
|
|
208
|
+
$type: z.ZodLiteral<"color">;
|
|
209
|
+
$value: z.ZodString;
|
|
210
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
211
|
+
}, z.core.$strict>>;
|
|
212
|
+
infoContainer: z.ZodOptional<z.ZodObject<{
|
|
213
|
+
$type: z.ZodLiteral<"color">;
|
|
214
|
+
$value: z.ZodString;
|
|
215
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
216
|
+
}, z.core.$strict>>;
|
|
217
|
+
onInfoContainer: z.ZodOptional<z.ZodObject<{
|
|
218
|
+
$type: z.ZodLiteral<"color">;
|
|
219
|
+
$value: z.ZodString;
|
|
220
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
221
|
+
}, z.core.$strict>>;
|
|
222
|
+
tertiary: z.ZodOptional<z.ZodObject<{
|
|
223
|
+
$type: z.ZodLiteral<"color">;
|
|
224
|
+
$value: z.ZodString;
|
|
225
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
226
|
+
}, z.core.$strict>>;
|
|
227
|
+
onTertiary: z.ZodOptional<z.ZodObject<{
|
|
228
|
+
$type: z.ZodLiteral<"color">;
|
|
229
|
+
$value: z.ZodString;
|
|
230
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
231
|
+
}, z.core.$strict>>;
|
|
232
|
+
tertiaryContainer: z.ZodOptional<z.ZodObject<{
|
|
233
|
+
$type: z.ZodLiteral<"color">;
|
|
234
|
+
$value: z.ZodString;
|
|
235
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
236
|
+
}, z.core.$strict>>;
|
|
237
|
+
onTertiaryContainer: z.ZodOptional<z.ZodObject<{
|
|
238
|
+
$type: z.ZodLiteral<"color">;
|
|
239
|
+
$value: z.ZodString;
|
|
240
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
241
|
+
}, z.core.$strict>>;
|
|
242
|
+
}, z.core.$strict>;
|
|
118
243
|
spacing: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
119
244
|
$type: z.ZodLiteral<"dimension">;
|
|
120
245
|
$value: z.ZodString;
|
|
@@ -132,26 +257,45 @@ export declare const ThemeDocumentV1: z.ZodObject<{
|
|
|
132
257
|
$description: z.ZodOptional<z.ZodString>;
|
|
133
258
|
}, z.core.$strict>;
|
|
134
259
|
}, z.core.$strip>>;
|
|
135
|
-
size: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
136
|
-
$type: z.ZodLiteral<"dimension">;
|
|
137
|
-
$value: z.ZodString;
|
|
138
|
-
$description: z.ZodOptional<z.ZodString>;
|
|
139
|
-
}, z.core.$strict>>;
|
|
140
260
|
weight: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
141
261
|
$type: z.ZodLiteral<"fontWeight">;
|
|
142
262
|
$value: z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>;
|
|
143
263
|
$description: z.ZodOptional<z.ZodString>;
|
|
144
264
|
}, z.core.$strict>>;
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
265
|
+
letterSpacing: z.ZodOptional<z.ZodObject<{
|
|
266
|
+
body: z.ZodOptional<z.ZodObject<{
|
|
267
|
+
$type: z.ZodLiteral<"dimension">;
|
|
268
|
+
$value: z.ZodString;
|
|
269
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
270
|
+
}, z.core.$strict>>;
|
|
271
|
+
heading: z.ZodOptional<z.ZodObject<{
|
|
272
|
+
$type: z.ZodLiteral<"dimension">;
|
|
273
|
+
$value: z.ZodString;
|
|
274
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
275
|
+
}, z.core.$strict>>;
|
|
276
|
+
}, z.core.$strict>>;
|
|
277
|
+
ramp: z.ZodOptional<z.ZodObject<{
|
|
278
|
+
base: z.ZodObject<{
|
|
279
|
+
$type: z.ZodLiteral<"dimension">;
|
|
280
|
+
$value: z.ZodString;
|
|
281
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
282
|
+
}, z.core.$strict>;
|
|
283
|
+
ratio: z.ZodObject<{
|
|
284
|
+
$type: z.ZodLiteral<"number">;
|
|
285
|
+
$value: z.ZodNumber;
|
|
286
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
287
|
+
}, z.core.$strict>;
|
|
288
|
+
}, z.core.$strict>>;
|
|
154
289
|
}, z.core.$strict>;
|
|
290
|
+
typography: z.ZodOptional<z.ZodObject<{
|
|
291
|
+
faces: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
292
|
+
family: z.ZodString;
|
|
293
|
+
src: z.ZodString;
|
|
294
|
+
weight: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
|
|
295
|
+
style: z.ZodOptional<z.ZodString>;
|
|
296
|
+
display: z.ZodOptional<z.ZodString>;
|
|
297
|
+
}, z.core.$strict>>>;
|
|
298
|
+
}, z.core.$strict>>;
|
|
155
299
|
shape: z.ZodObject<{
|
|
156
300
|
radius: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
157
301
|
$type: z.ZodLiteral<"dimension">;
|
|
@@ -169,18 +313,20 @@ export declare const ThemeDocumentV1: z.ZodObject<{
|
|
|
169
313
|
}, z.core.$strict>]>;
|
|
170
314
|
$description: z.ZodOptional<z.ZodString>;
|
|
171
315
|
}, z.core.$strict>>;
|
|
316
|
+
border: z.ZodOptional<z.ZodObject<{
|
|
317
|
+
width: z.ZodOptional<z.ZodObject<{
|
|
318
|
+
$type: z.ZodLiteral<"dimension">;
|
|
319
|
+
$value: z.ZodString;
|
|
320
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
321
|
+
}, z.core.$strict>>;
|
|
322
|
+
style: z.ZodOptional<z.ZodObject<{
|
|
323
|
+
$type: z.ZodString;
|
|
324
|
+
$value: z.ZodString;
|
|
325
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
326
|
+
}, z.core.$strict>>;
|
|
327
|
+
}, z.core.$strict>>;
|
|
172
328
|
}, z.core.$strict>;
|
|
173
329
|
motion: z.ZodOptional<z.ZodObject<{
|
|
174
|
-
duration: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
175
|
-
$type: z.ZodLiteral<"duration">;
|
|
176
|
-
$value: z.ZodString;
|
|
177
|
-
$description: z.ZodOptional<z.ZodString>;
|
|
178
|
-
}, z.core.$strict>>;
|
|
179
|
-
easing: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
180
|
-
$type: z.ZodLiteral<"cubicBezier">;
|
|
181
|
-
$value: z.ZodString;
|
|
182
|
-
$description: z.ZodOptional<z.ZodString>;
|
|
183
|
-
}, z.core.$strict>>>;
|
|
184
330
|
transition: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
185
331
|
$type: z.ZodLiteral<"transition">;
|
|
186
332
|
$value: z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
@@ -192,7 +338,7 @@ export declare const ThemeDocumentV1: z.ZodObject<{
|
|
|
192
338
|
}, z.core.$strict>>;
|
|
193
339
|
keyframes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
194
340
|
$type: z.ZodString;
|
|
195
|
-
$value: z.
|
|
341
|
+
$value: z.ZodString;
|
|
196
342
|
$description: z.ZodOptional<z.ZodString>;
|
|
197
343
|
}, z.core.$strict>>>;
|
|
198
344
|
}, z.core.$strict>>;
|
|
@@ -251,9 +397,9 @@ export declare const ThemeDocumentV1: z.ZodObject<{
|
|
|
251
397
|
}, z.core.$strict>>>;
|
|
252
398
|
}, z.core.$strict>;
|
|
253
399
|
/** Static TypeScript type derived from the v1 schema. */
|
|
254
|
-
export type
|
|
400
|
+
export type ThemeDocumentV2 = z.infer<typeof ThemeDocumentV2>;
|
|
255
401
|
/** Canonical type alias used everywhere else. */
|
|
256
|
-
export type ThemeDocument =
|
|
402
|
+
export type ThemeDocument = ThemeDocumentV2;
|
|
257
403
|
/**
|
|
258
404
|
* Parse a raw JSON value into a validated {@link ThemeDocument}.
|
|
259
405
|
* Throws a `ZodError` with human-readable issues on invalid input.
|
|
@@ -264,5 +410,23 @@ export declare function parseThemeDocument(raw: unknown): ThemeDocument;
|
|
|
264
410
|
* Prefer this inside CLI tooling where you want to render the issue
|
|
265
411
|
* list without try/catch.
|
|
266
412
|
*/
|
|
267
|
-
export declare function safeParseThemeDocument(raw: unknown): ReturnType<typeof
|
|
413
|
+
export declare function safeParseThemeDocument(raw: unknown): ReturnType<typeof ThemeDocumentV2.safeParse>;
|
|
414
|
+
/**
|
|
415
|
+
* Turn a parsed document into the EXACT {@link DtcgTheme} the one
|
|
416
|
+
* producer (`deriveThemeVariables`) consumes:
|
|
417
|
+
*
|
|
418
|
+
* - names the document (`$name` ⇒ `'custom'`, `$description` ⇒ `''`);
|
|
419
|
+
* - drops the DTCG envelope fields the producer does not read
|
|
420
|
+
* (`$schema`, `$version`);
|
|
421
|
+
* - flattens the DTCG object forms to the CSS strings the producer
|
|
422
|
+
* reads: array font stacks → `'Inter, system-ui'`, numeric weights →
|
|
423
|
+
* `'400'`, structured shadows → `'0 1px 2px 0 …'`, structured
|
|
424
|
+
* transitions → `'<property ?? all> <duration> <timingFunction>'`;
|
|
425
|
+
* - fills the optional groups (`motion`, `accessibility`, `zIndex` and
|
|
426
|
+
* their sub-records) from the shipped default, so an external tool's
|
|
427
|
+
* colours-and-dimensions export still derives every consumed token.
|
|
428
|
+
*
|
|
429
|
+
* The authored roles, anchors and faces pass through verbatim.
|
|
430
|
+
*/
|
|
431
|
+
export declare function normalizeThemeDocument(doc: ThemeDocument): DtcgTheme;
|
|
268
432
|
//# sourceMappingURL=theme.d.ts.map
|
package/dist/theme.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,wBAAwB,CAAC;AA0PnE;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA6C1B,CAAC;AAEH,yDAAyD;AACzD,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,iDAAiD;AACjD,MAAM,MAAM,aAAa,GAAG,eAAe,CAAC;AAE5C;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,CAE9D;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,OAAO,GACX,UAAU,CAAC,OAAO,eAAe,CAAC,SAAS,CAAC,CAE9C;AAYD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,aAAa,GAAG,SAAS,CAmDpE"}
|
package/dist/theme.js
CHANGED
|
@@ -1,82 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `ggui.json#theme` → plain DTCG JSON document.
|
|
2
|
+
* `ggui.json#theme` → plain DTCG JSON document, v2 (ggui#987).
|
|
3
3
|
*
|
|
4
4
|
* The theme file is a plain JSON document matching the Design Tokens
|
|
5
|
-
* Community Group (DTCG) spec
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
5
|
+
* Community Group (DTCG) spec, in the same vocabulary as
|
|
6
|
+
* `@ggui-ai/design`'s canonical {@link DtcgTheme}: the curated registry
|
|
7
|
+
* themes and an authored `theme.json` are the same shape.
|
|
8
8
|
*
|
|
9
|
-
* **
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* `
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* the
|
|
9
|
+
* **What is authored, what is derived.** An author states the six
|
|
10
|
+
* layering roles (`ground`/`onGround`, `container`/`onContainer`,
|
|
11
|
+
* `sunken`/`onSunken`), one `500` anchor per family (`primary`,
|
|
12
|
+
* `success`, `warning`, `error`, `info`), the font families and
|
|
13
|
+
* weights, spacing, radii, shadows. Everything else — the ten-stop
|
|
14
|
+
* ramps, every `on*` ink and container, the neutral ladder, outlines,
|
|
15
|
+
* `elevated`, the type scale from the one `font.ramp` — is DERIVED by
|
|
16
|
+
* the one producer (`deriveThemeVariables`), so the retired ladders
|
|
17
|
+
* (`font.size`, `font.lineHeight`, `motion.duration`, `motion.easing`)
|
|
18
|
+
* and `$metadata.fontUrl` are refused, not ignored. Fonts are declared
|
|
19
|
+
* as `typography.faces` (https `src` only); the card installs them.
|
|
17
20
|
*
|
|
18
|
-
* **External-tool
|
|
19
|
-
*
|
|
20
|
-
* the
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* (`$name`, `$description`, `$metadata`) are all OPTIONAL so tools
|
|
24
|
-
* that only emit colors + dimensions still parse cleanly.
|
|
25
|
-
*
|
|
26
|
-
* **BREAKING for old `theme.json` files.** Authors who hand-wrote a
|
|
27
|
-
* `theme.json` against the pre-rc schema must rename:
|
|
28
|
-
*
|
|
29
|
-
* - `typography.fontFamily` → `font.family`
|
|
30
|
-
* - `typography.fontSize` → `font.size`
|
|
31
|
-
* - `typography.fontWeight` → `font.weight`
|
|
32
|
-
* - `typography.lineHeight` → `font.lineHeight`
|
|
33
|
-
* - top-level `radius` → `shape.radius`
|
|
34
|
-
* - top-level `shadow` → `shape.shadow`
|
|
35
|
-
* - top-level `duration` → `motion.duration`
|
|
36
|
-
* - top-level `transition` → `motion.transition`
|
|
37
|
-
*
|
|
38
|
-
* Plus: under `font.family`, `sans` is the only required sub-token
|
|
39
|
-
* (was previously freeform); other family slots (`mono`, …) are
|
|
40
|
-
* optional. The font-family record itself stays open so additional
|
|
41
|
-
* named families parse without a schema change.
|
|
21
|
+
* **External-tool leniency.** `motion`, `accessibility`, `zIndex` and
|
|
22
|
+
* the DTCG metadata fields stay OPTIONAL; {@link normalizeThemeDocument}
|
|
23
|
+
* fills them from the shipped default and flattens the DTCG object
|
|
24
|
+
* forms (structured shadows, transitions, array font stacks) to the CSS
|
|
25
|
+
* strings the producer reads.
|
|
42
26
|
*
|
|
43
27
|
* **Ownership boundary:**
|
|
44
28
|
*
|
|
45
29
|
* - *Where* the theme lives → `ggui.json#theme` (pointer string).
|
|
46
30
|
* - *What* the theme file looks like → this module's schema.
|
|
47
|
-
* - *How*
|
|
48
|
-
* `
|
|
49
|
-
* emit). The duck-typed walker accepts any DTCG-shaped tree, so
|
|
50
|
-
* the new nested groups (`font.size`, `shape.radius`, …) walk
|
|
51
|
-
* into `--ggui-font-size-md`/`--ggui-shape-radius-md` CSS vars
|
|
52
|
-
* without code changes.
|
|
31
|
+
* - *How* the document becomes CSS variables → `@ggui-ai/design`'s
|
|
32
|
+
* `deriveThemeVariables` (the loader calls it for both modes).
|
|
53
33
|
* - *Built-in default* when `theme` is absent → `@ggui-ai/design`'s
|
|
54
|
-
* shipped `lightTheme`.
|
|
55
|
-
*
|
|
56
|
-
* **Extending rules:**
|
|
34
|
+
* shipped `lightTheme` / `darkTheme`.
|
|
57
35
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* values, no build-pipeline fields.
|
|
62
|
-
* 3. Root and per-group objects are strict — unknown keys fail
|
|
63
|
-
* parse. Same discipline as `ggui.json`, `ggui.ui.json`, and
|
|
64
|
-
* `ggui.primitives.json`.
|
|
65
|
-
*
|
|
66
|
-
* **Strictness tradeoff:**
|
|
67
|
-
*
|
|
68
|
-
* Individual DTCG token leaves are validated with a discriminated
|
|
69
|
-
* union over `$type` (color / dimension / fontFamily / fontWeight /
|
|
70
|
-
* shadow / duration / cubicBezier / transition / number). Each
|
|
71
|
-
* variant validates its `$value` shape. `$description` is accepted
|
|
72
|
-
* everywhere as a free-form docstring. Token leaves under newer
|
|
73
|
-
* groups (`accessibility`, `zIndex`) accept a slightly
|
|
74
|
-
* wider token vocabulary because external tools sometimes encode
|
|
75
|
-
* scalar/array values with permissive `$type` strings (`string`,
|
|
76
|
-
* `array`, …); the schema accepts those without enumerating every
|
|
77
|
-
* possible spelling.
|
|
36
|
+
* **Extending rules:** additive only within `schema: '1'`; framework-
|
|
37
|
+
* and host-neutral; root and per-group objects are strict — unknown
|
|
38
|
+
* keys fail parse, the same discipline as `ggui.json`.
|
|
78
39
|
*/
|
|
79
40
|
import { z } from 'zod';
|
|
41
|
+
import { lightTheme } from '@ggui-ai/design/themes';
|
|
80
42
|
// ─── Token leaves ────────────────────────────────────────────────────
|
|
81
43
|
/** A CSS color string. `$value` is not structurally validated (hex,
|
|
82
44
|
* rgba, color-mix, named, etc. are all legal DTCG colors). */
|
|
@@ -112,14 +74,6 @@ const DurationToken = z.strictObject({
|
|
|
112
74
|
$value: z.string().min(1),
|
|
113
75
|
$description: z.string().optional(),
|
|
114
76
|
});
|
|
115
|
-
/** DTCG cubic-bezier easing token. `$value` is a CSS timing function
|
|
116
|
-
* string (`"cubic-bezier(0.4, 0, 0.2, 1)"`, keyword aliases like
|
|
117
|
-
* `"ease-out"`, etc.). */
|
|
118
|
-
const CubicBezierToken = z.strictObject({
|
|
119
|
-
$type: z.literal('cubicBezier'),
|
|
120
|
-
$value: z.string().min(1),
|
|
121
|
-
$description: z.string().optional(),
|
|
122
|
-
});
|
|
123
77
|
/** Structured shadow value — DTCG spec composite shape. */
|
|
124
78
|
const ShadowValue = z.strictObject({
|
|
125
79
|
offsetX: z.string().min(1),
|
|
@@ -154,32 +108,34 @@ const NumberToken = z.strictObject({
|
|
|
154
108
|
$description: z.string().optional(),
|
|
155
109
|
});
|
|
156
110
|
/** Line-height token — DTCG allows unit-less number or dimension. */
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
$value: z.number(),
|
|
161
|
-
$description: z.string().optional(),
|
|
162
|
-
}),
|
|
163
|
-
z.strictObject({
|
|
164
|
-
$type: z.literal('dimension'),
|
|
165
|
-
$value: z.string().min(1),
|
|
166
|
-
$description: z.string().optional(),
|
|
167
|
-
}),
|
|
168
|
-
]);
|
|
169
|
-
/**
|
|
170
|
-
* Permissive token leaf for the newer optional groups
|
|
171
|
-
* (`motion.keyframes`, `zIndex`). DTCG hasn't standardised every
|
|
172
|
-
* `$type` value the curated registry themes carry — for example
|
|
173
|
-
* easing uses `$type: 'cubicBezier'`. Rather than
|
|
174
|
-
* enumerate every spelling, this leaf accepts any `$type` string and
|
|
175
|
-
* any JSON-serializable `$value`. Strict per-leaf shape stays in
|
|
176
|
-
* force for the original token types via the leaves above.
|
|
177
|
-
*/
|
|
178
|
-
const PermissiveToken = z.strictObject({
|
|
111
|
+
/** A token whose `$value` is a CSS string under any `$type` spelling
|
|
112
|
+
* (keyframes text, border style, …). */
|
|
113
|
+
const StringToken = z.strictObject({
|
|
179
114
|
$type: z.string().min(1),
|
|
180
|
-
$value: z.
|
|
115
|
+
$value: z.string().min(1),
|
|
181
116
|
$description: z.string().optional(),
|
|
182
117
|
});
|
|
118
|
+
/** A declared font face (ggui#987 §5): `src` MUST be `https:` with a
|
|
119
|
+
* well-formed host — the same rule the design package asserts. */
|
|
120
|
+
const FontFaceDeclarationSchema = z.strictObject({
|
|
121
|
+
family: z.string().min(1).refine((f) => !/[\r\n]/.test(f), 'family must be one line'),
|
|
122
|
+
src: z
|
|
123
|
+
.string()
|
|
124
|
+
.min(1)
|
|
125
|
+
.refine((src) => {
|
|
126
|
+
let url;
|
|
127
|
+
try {
|
|
128
|
+
url = new URL(src);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
return url.protocol === 'https:' && /^[a-z0-9.-]+$/i.test(url.hostname) && url.hostname.includes('.');
|
|
134
|
+
}, 'src must be an https: URL with a well-formed host'),
|
|
135
|
+
weight: z.union([z.string().min(1), z.number().int().min(1).max(1000)]).optional(),
|
|
136
|
+
style: z.string().min(1).optional(),
|
|
137
|
+
display: z.string().min(1).optional(),
|
|
138
|
+
});
|
|
183
139
|
// ─── Groups ──────────────────────────────────────────────────────────
|
|
184
140
|
/**
|
|
185
141
|
* A "palette" group — a record of color tokens keyed by scale step
|
|
@@ -188,42 +144,68 @@ const PermissiveToken = z.strictObject({
|
|
|
188
144
|
*/
|
|
189
145
|
const ColorPalette = z.record(z.string(), ColorToken);
|
|
190
146
|
/**
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
* Kept open (`z.record` not `z.strictObject`) so authored themes
|
|
196
|
-
* can add brand-specific palettes (`accent`, `brand-green`, …) and
|
|
197
|
-
* semantic roles without a schema change. `color` itself is
|
|
198
|
-
* required but its internal shape is author-extensible.
|
|
199
|
-
*
|
|
200
|
-
* Each entry can be EITHER a singleton `ColorToken` (e.g. a Material
|
|
201
|
-
* role like `surface`) OR a `ColorPalette` (a scale like `primary`'s
|
|
202
|
-
* 50-900 stops). Tools that emit only singleton colors (no scales)
|
|
203
|
-
* stay parseable.
|
|
147
|
+
* The colour block: five family anchors (`500` stated; the other stops
|
|
148
|
+
* derived unless stated) + the six layering roles, all REQUIRED; the
|
|
149
|
+
* derived roles (`neutral`, outlines, `on*` inks, containers,
|
|
150
|
+
* `tertiary`, `link`) are optional and win over derivation when stated.
|
|
204
151
|
*/
|
|
205
|
-
const ColorGroup = z.
|
|
152
|
+
const ColorGroup = z.strictObject({
|
|
153
|
+
primary: ColorPalette,
|
|
154
|
+
neutral: ColorPalette.optional(),
|
|
155
|
+
success: ColorPalette,
|
|
156
|
+
warning: ColorPalette,
|
|
157
|
+
error: ColorPalette,
|
|
158
|
+
info: ColorPalette,
|
|
159
|
+
ground: ColorToken,
|
|
160
|
+
onGround: ColorToken,
|
|
161
|
+
container: ColorToken,
|
|
162
|
+
onContainer: ColorToken,
|
|
163
|
+
sunken: ColorToken,
|
|
164
|
+
onSunken: ColorToken,
|
|
165
|
+
link: ColorToken.optional(),
|
|
166
|
+
outline: ColorToken.optional(),
|
|
167
|
+
outlineVariant: ColorToken.optional(),
|
|
168
|
+
onPrimary: ColorToken.optional(),
|
|
169
|
+
primaryContainer: ColorToken.optional(),
|
|
170
|
+
onPrimaryContainer: ColorToken.optional(),
|
|
171
|
+
onSuccess: ColorToken.optional(),
|
|
172
|
+
successContainer: ColorToken.optional(),
|
|
173
|
+
onSuccessContainer: ColorToken.optional(),
|
|
174
|
+
onWarning: ColorToken.optional(),
|
|
175
|
+
warningContainer: ColorToken.optional(),
|
|
176
|
+
onWarningContainer: ColorToken.optional(),
|
|
177
|
+
onError: ColorToken.optional(),
|
|
178
|
+
errorContainer: ColorToken.optional(),
|
|
179
|
+
onErrorContainer: ColorToken.optional(),
|
|
180
|
+
onInfo: ColorToken.optional(),
|
|
181
|
+
infoContainer: ColorToken.optional(),
|
|
182
|
+
onInfoContainer: ColorToken.optional(),
|
|
183
|
+
tertiary: ColorToken.optional(),
|
|
184
|
+
onTertiary: ColorToken.optional(),
|
|
185
|
+
tertiaryContainer: ColorToken.optional(),
|
|
186
|
+
onTertiaryContainer: ColorToken.optional(),
|
|
187
|
+
});
|
|
206
188
|
const SpacingGroup = z.record(z.string(), DimensionToken);
|
|
207
189
|
/**
|
|
208
|
-
* Font group — `family`
|
|
209
|
-
*
|
|
210
|
-
* `
|
|
211
|
-
* shape's `font.family` / `font.size` / `font.weight` /
|
|
212
|
-
* `font.lineHeight`.
|
|
213
|
-
*
|
|
214
|
-
* Inside `family`, `sans` is the only required slot — the canonical
|
|
215
|
-
* theme always carries a sans-serif default. `mono` and any other
|
|
216
|
-
* family slot (e.g. `serif`, `display`) are optional. The record
|
|
217
|
-
* stays open so authors can declare additional named families.
|
|
190
|
+
* Font group — `family` (the `sans` slot required; `mono`, `heading`
|
|
191
|
+
* and any other named family optional) and `weight`; optional
|
|
192
|
+
* `letterSpacing` and the ONE `ramp` the type scale is derived from.
|
|
218
193
|
*/
|
|
194
|
+
/** Font families: `sans` required; `mono`, `heading` and any other named family optional. */
|
|
219
195
|
const FontFamilyGroup = z.record(z.string(), FontFamilyToken).and(z.object({
|
|
220
196
|
sans: FontFamilyToken,
|
|
221
197
|
}));
|
|
222
198
|
const FontGroup = z.strictObject({
|
|
223
199
|
family: FontFamilyGroup,
|
|
224
|
-
size: z.record(z.string(), DimensionToken),
|
|
225
200
|
weight: z.record(z.string(), FontWeightToken),
|
|
226
|
-
|
|
201
|
+
letterSpacing: z
|
|
202
|
+
.strictObject({ body: DimensionToken.optional(), heading: DimensionToken.optional() })
|
|
203
|
+
.optional(),
|
|
204
|
+
ramp: z.strictObject({ base: DimensionToken, ratio: NumberToken }).optional(),
|
|
205
|
+
});
|
|
206
|
+
/** Declared font faces (ggui#987 §5). */
|
|
207
|
+
const TypographyGroup = z.strictObject({
|
|
208
|
+
faces: z.array(FontFaceDeclarationSchema).optional(),
|
|
227
209
|
});
|
|
228
210
|
/**
|
|
229
211
|
* Shape group — `radius` + `shadow`. Moved here from the previous
|
|
@@ -233,18 +215,16 @@ const FontGroup = z.strictObject({
|
|
|
233
215
|
const ShapeGroup = z.strictObject({
|
|
234
216
|
radius: z.record(z.string(), DimensionToken),
|
|
235
217
|
shadow: z.record(z.string(), ShadowToken),
|
|
218
|
+
border: z.strictObject({ width: DimensionToken.optional(), style: StringToken.optional() }).optional(),
|
|
236
219
|
});
|
|
237
220
|
/**
|
|
238
|
-
* Motion group — `
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
* tools (Figma Tokens, Style Dictionary) don't always emit them.
|
|
221
|
+
* Motion group — `transition` (required sub-record) + `keyframes`
|
|
222
|
+
* (optional). Durations and easings are not ladders any more: they ride
|
|
223
|
+
* each transition's own value.
|
|
242
224
|
*/
|
|
243
225
|
const MotionGroup = z.strictObject({
|
|
244
|
-
duration: z.record(z.string(), DurationToken),
|
|
245
|
-
easing: z.record(z.string(), CubicBezierToken).optional(),
|
|
246
226
|
transition: z.record(z.string(), TransitionToken),
|
|
247
|
-
keyframes: z.record(z.string(),
|
|
227
|
+
keyframes: z.record(z.string(), StringToken).optional(),
|
|
248
228
|
});
|
|
249
229
|
const AccessibilityGroup = z.strictObject({
|
|
250
230
|
focusRing: z
|
|
@@ -276,8 +256,9 @@ const ZIndexGroup = z.record(z.string(), NumberToken);
|
|
|
276
256
|
*/
|
|
277
257
|
const MetadataGroup = z.strictObject({
|
|
278
258
|
font: z.string().min(1).optional(),
|
|
279
|
-
fontUrl: z.string().min(1).optional(),
|
|
280
259
|
philosophy: z.string().min(1).optional(),
|
|
260
|
+
/** The embedding host draws the card silhouette; the renderer suppresses root-children strokes. */
|
|
261
|
+
frameless: z.boolean().optional(),
|
|
281
262
|
});
|
|
282
263
|
// ─── Root document ───────────────────────────────────────────────────
|
|
283
264
|
/**
|
|
@@ -299,7 +280,7 @@ const MetadataGroup = z.strictObject({
|
|
|
299
280
|
* allow-listed (DTCG metadata); anything else is a typo. Additive
|
|
300
281
|
* slices add new fields here under `schema: '1'`.
|
|
301
282
|
*/
|
|
302
|
-
export const
|
|
283
|
+
export const ThemeDocumentV2 = z.strictObject({
|
|
303
284
|
/** Optional DTCG spec URL. Not validated — the spec hasn't frozen
|
|
304
285
|
* a canonical URL and hand-authored themes often omit it. */
|
|
305
286
|
$schema: z.string().min(1).optional(),
|
|
@@ -320,6 +301,8 @@ export const ThemeDocumentV1 = z.strictObject({
|
|
|
320
301
|
spacing: SpacingGroup,
|
|
321
302
|
/** Font tokens — `family` / `size` / `weight` / `lineHeight`. Required. */
|
|
322
303
|
font: FontGroup,
|
|
304
|
+
/** Declared font faces (ggui#987 §5) — https `src` only; the card installs them. */
|
|
305
|
+
typography: TypographyGroup.optional(),
|
|
323
306
|
/** Shape tokens — `radius` + `shadow`. Required. */
|
|
324
307
|
shape: ShapeGroup,
|
|
325
308
|
/** Motion tokens — `duration` + `transition` (required sub-records),
|
|
@@ -336,7 +319,7 @@ export const ThemeDocumentV1 = z.strictObject({
|
|
|
336
319
|
* Throws a `ZodError` with human-readable issues on invalid input.
|
|
337
320
|
*/
|
|
338
321
|
export function parseThemeDocument(raw) {
|
|
339
|
-
return
|
|
322
|
+
return ThemeDocumentV2.parse(raw);
|
|
340
323
|
}
|
|
341
324
|
/**
|
|
342
325
|
* Safe-parse variant — returns a discriminated `z.safeParse` result.
|
|
@@ -344,5 +327,69 @@ export function parseThemeDocument(raw) {
|
|
|
344
327
|
* list without try/catch.
|
|
345
328
|
*/
|
|
346
329
|
export function safeParseThemeDocument(raw) {
|
|
347
|
-
return
|
|
330
|
+
return ThemeDocumentV2.safeParse(raw);
|
|
331
|
+
}
|
|
332
|
+
// ─── Normalisation → the producer's document ─────────────────────────
|
|
333
|
+
function stringToken(token, value) {
|
|
334
|
+
return { $type: token.$type, $value: value, ...(token.$description !== undefined ? { $description: token.$description } : {}) };
|
|
335
|
+
}
|
|
336
|
+
function mapRecord(record, f) {
|
|
337
|
+
return Object.fromEntries(Object.entries(record).map(([k, v]) => [k, f(v)]));
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Turn a parsed document into the EXACT {@link DtcgTheme} the one
|
|
341
|
+
* producer (`deriveThemeVariables`) consumes:
|
|
342
|
+
*
|
|
343
|
+
* - names the document (`$name` ⇒ `'custom'`, `$description` ⇒ `''`);
|
|
344
|
+
* - drops the DTCG envelope fields the producer does not read
|
|
345
|
+
* (`$schema`, `$version`);
|
|
346
|
+
* - flattens the DTCG object forms to the CSS strings the producer
|
|
347
|
+
* reads: array font stacks → `'Inter, system-ui'`, numeric weights →
|
|
348
|
+
* `'400'`, structured shadows → `'0 1px 2px 0 …'`, structured
|
|
349
|
+
* transitions → `'<property ?? all> <duration> <timingFunction>'`;
|
|
350
|
+
* - fills the optional groups (`motion`, `accessibility`, `zIndex` and
|
|
351
|
+
* their sub-records) from the shipped default, so an external tool's
|
|
352
|
+
* colours-and-dimensions export still derives every consumed token.
|
|
353
|
+
*
|
|
354
|
+
* The authored roles, anchors and faces pass through verbatim.
|
|
355
|
+
*/
|
|
356
|
+
export function normalizeThemeDocument(doc) {
|
|
357
|
+
const family = doc.font.family;
|
|
358
|
+
const fontFamily = (t) => stringToken(t, Array.isArray(t.$value) ? t.$value.join(', ') : t.$value);
|
|
359
|
+
const accessibility = doc.accessibility;
|
|
360
|
+
return {
|
|
361
|
+
$name: doc.$name ?? 'custom',
|
|
362
|
+
$description: doc.$description ?? '',
|
|
363
|
+
...(doc.$metadata !== undefined ? { $metadata: doc.$metadata } : {}),
|
|
364
|
+
color: doc.color,
|
|
365
|
+
font: {
|
|
366
|
+
family: { ...mapRecord(family, fontFamily), sans: fontFamily(family.sans) },
|
|
367
|
+
weight: mapRecord(doc.font.weight, (t) => stringToken(t, String(t.$value))),
|
|
368
|
+
...(doc.font.letterSpacing !== undefined ? { letterSpacing: doc.font.letterSpacing } : {}),
|
|
369
|
+
...(doc.font.ramp !== undefined ? { ramp: doc.font.ramp } : {}),
|
|
370
|
+
},
|
|
371
|
+
...(doc.typography !== undefined ? { typography: doc.typography } : {}),
|
|
372
|
+
spacing: doc.spacing,
|
|
373
|
+
shape: {
|
|
374
|
+
radius: doc.shape.radius,
|
|
375
|
+
shadow: mapRecord(doc.shape.shadow, (t) => stringToken(t, typeof t.$value === 'string'
|
|
376
|
+
? t.$value
|
|
377
|
+
: `${t.$value.offsetX} ${t.$value.offsetY} ${t.$value.blur} ${t.$value.spread} ${t.$value.color}`)),
|
|
378
|
+
...(doc.shape.border !== undefined ? { border: doc.shape.border } : {}),
|
|
379
|
+
},
|
|
380
|
+
motion: doc.motion === undefined
|
|
381
|
+
? lightTheme.motion
|
|
382
|
+
: {
|
|
383
|
+
transition: mapRecord(doc.motion.transition, (t) => stringToken(t, typeof t.$value === 'string'
|
|
384
|
+
? t.$value
|
|
385
|
+
: `${t.$value.property ?? 'all'} ${t.$value.duration} ${t.$value.timingFunction}`)),
|
|
386
|
+
keyframes: doc.motion.keyframes ?? lightTheme.motion.keyframes,
|
|
387
|
+
},
|
|
388
|
+
accessibility: {
|
|
389
|
+
focusRing: accessibility?.focusRing ?? lightTheme.accessibility.focusRing,
|
|
390
|
+
reducedMotion: accessibility?.reducedMotion ?? lightTheme.accessibility.reducedMotion,
|
|
391
|
+
highContrast: accessibility?.highContrast ?? lightTheme.accessibility.highContrast,
|
|
392
|
+
},
|
|
393
|
+
zIndex: doc.zIndex ?? lightTheme.zIndex,
|
|
394
|
+
};
|
|
348
395
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ggui-ai/project-config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Schema and loader for `ggui.json` — the portable app/agent manifest for the ggui protocol. Vendor-neutral. Consumed by the `ggui` CLI and any host that reconstitutes an agent from the manifest.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ggui",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"tinyglobby": "^0.2.16",
|
|
35
35
|
"zod": "^4.3.6",
|
|
36
|
-
"@ggui-ai/design": "0.
|
|
37
|
-
"@ggui-ai/protocol": "0.
|
|
36
|
+
"@ggui-ai/design": "0.16.0",
|
|
37
|
+
"@ggui-ai/protocol": "0.16.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^24.0.0",
|