@ggui-ai/project-config 0.1.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +66 -0
- package/dist/discovery.d.ts +74 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +167 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/mcp-mount-discovery.d.ts +82 -0
- package/dist/mcp-mount-discovery.d.ts.map +1 -0
- package/dist/mcp-mount-discovery.js +226 -0
- package/dist/node.d.ts +69 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +157 -0
- package/dist/primitive-discovery.d.ts +95 -0
- package/dist/primitive-discovery.d.ts.map +1 -0
- package/dist/primitive-discovery.js +281 -0
- package/dist/primitives-manifest.d.ts +106 -0
- package/dist/primitives-manifest.d.ts.map +1 -0
- package/dist/primitives-manifest.js +131 -0
- package/dist/schema.d.ts +520 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +556 -0
- package/dist/theme-loader.d.ts +104 -0
- package/dist/theme-loader.d.ts.map +1 -0
- package/dist/theme-loader.js +263 -0
- package/dist/theme.d.ts +311 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +366 -0
- package/dist/ui-manifest.d.ts +95 -0
- package/dist/ui-manifest.d.ts.map +1 -0
- package/dist/ui-manifest.js +114 -0
- package/package.json +67 -0
package/dist/theme.js
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ggui.json#theme` → plain DTCG JSON document.
|
|
3
|
+
*
|
|
4
|
+
* The theme file is a plain JSON document matching the Design Tokens
|
|
5
|
+
* Community Group (DTCG) spec. It is one of the open file-format
|
|
6
|
+
* surfaces in the ggui manifest model, alongside
|
|
7
|
+
* `ggui.primitives.json` and `ggui.ui.json`.
|
|
8
|
+
*
|
|
9
|
+
* **Shape parity with the internal `DtcgTheme`.** This v1 schema is
|
|
10
|
+
* deliberately the same shape as `@ggui-ai/design`'s canonical
|
|
11
|
+
* {@link DtcgTheme} — `color`/`font`/`spacing`/`shape`/`motion`/
|
|
12
|
+
* `canvas`/`accessibility`/`zIndex` — so an authored `theme.json`
|
|
13
|
+
* uses the same vocabulary the curated registry themes (light/dark/
|
|
14
|
+
* premium-*) use. Previously this schema was Tailwind-style flat
|
|
15
|
+
* (`typography`/`radius`/`shadow` at root); the v1 schema now mirrors
|
|
16
|
+
* the internal shape to remove the divergence.
|
|
17
|
+
*
|
|
18
|
+
* **External-tool compatibility.** External tooling (Figma Tokens,
|
|
19
|
+
* Style Dictionary, Tokens Studio) commonly emits only a subset of
|
|
20
|
+
* the canonical shape. Only the original required groups (`color`,
|
|
21
|
+
* `font`, `spacing`, `shape`) stay required; `motion`, `canvas`,
|
|
22
|
+
* `accessibility`, `zIndex`, and the new DTCG metadata fields
|
|
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.
|
|
42
|
+
*
|
|
43
|
+
* **Ownership boundary:**
|
|
44
|
+
*
|
|
45
|
+
* - *Where* the theme lives → `ggui.json#theme` (pointer string).
|
|
46
|
+
* - *What* the theme file looks like → this module's schema.
|
|
47
|
+
* - *How* DTCG tokens become CSS variables → `@ggui-ai/design`'s
|
|
48
|
+
* `generateCssVariables` (stays over there; this schema does not
|
|
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.
|
|
53
|
+
* - *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. (`canvas` IS hosting-
|
|
62
|
+
* vendor vocabulary; it is opt-in via the optional field.)
|
|
63
|
+
* 3. Root and per-group objects are strict — unknown keys fail
|
|
64
|
+
* parse. Same discipline as `ggui.json`, `ggui.ui.json`, and
|
|
65
|
+
* `ggui.primitives.json`.
|
|
66
|
+
*
|
|
67
|
+
* **Strictness tradeoff:**
|
|
68
|
+
*
|
|
69
|
+
* Individual DTCG token leaves are validated with a discriminated
|
|
70
|
+
* union over `$type` (color / dimension / fontFamily / fontWeight /
|
|
71
|
+
* shadow / duration / cubicBezier / transition / number). Each
|
|
72
|
+
* variant validates its `$value` shape. `$description` is accepted
|
|
73
|
+
* everywhere as a free-form docstring. Token leaves under newer
|
|
74
|
+
* groups (`canvas`, `accessibility`, `zIndex`) accept a slightly
|
|
75
|
+
* wider token vocabulary because external tools sometimes encode
|
|
76
|
+
* scalar/array values with permissive `$type` strings (`string`,
|
|
77
|
+
* `array`, …); the schema accepts those without enumerating every
|
|
78
|
+
* possible spelling.
|
|
79
|
+
*/
|
|
80
|
+
import { z } from 'zod';
|
|
81
|
+
// ─── Token leaves ────────────────────────────────────────────────────
|
|
82
|
+
/** A CSS color string. `$value` is not structurally validated (hex,
|
|
83
|
+
* rgba, color-mix, named, etc. are all legal DTCG colors). */
|
|
84
|
+
const ColorToken = z.strictObject({
|
|
85
|
+
$type: z.literal('color'),
|
|
86
|
+
$value: z.string().min(1),
|
|
87
|
+
$description: z.string().optional(),
|
|
88
|
+
});
|
|
89
|
+
/** A CSS dimension string (`"16px"`, `"1rem"`, `"0.5em"`, …). */
|
|
90
|
+
const DimensionToken = z.strictObject({
|
|
91
|
+
$type: z.literal('dimension'),
|
|
92
|
+
$value: z.string().min(1),
|
|
93
|
+
$description: z.string().optional(),
|
|
94
|
+
});
|
|
95
|
+
/** Font family stack. Array-form is canonical (matches how
|
|
96
|
+
* `generateCssVariables` emits `"Inter", "system-ui"`). String-form
|
|
97
|
+
* also accepted for single-family leaves. */
|
|
98
|
+
const FontFamilyToken = z.strictObject({
|
|
99
|
+
$type: z.literal('fontFamily'),
|
|
100
|
+
$value: z.union([z.string().min(1), z.array(z.string().min(1)).min(1)]),
|
|
101
|
+
$description: z.string().optional(),
|
|
102
|
+
});
|
|
103
|
+
/** Font weight — DTCG allows numeric weight (100..900) or keyword
|
|
104
|
+
* aliases; the design package's emitter handles both. */
|
|
105
|
+
const FontWeightToken = z.strictObject({
|
|
106
|
+
$type: z.literal('fontWeight'),
|
|
107
|
+
$value: z.union([z.number().int().min(1).max(1000), z.string().min(1)]),
|
|
108
|
+
$description: z.string().optional(),
|
|
109
|
+
});
|
|
110
|
+
/** A CSS duration (`"200ms"`, `"0.3s"`). */
|
|
111
|
+
const DurationToken = z.strictObject({
|
|
112
|
+
$type: z.literal('duration'),
|
|
113
|
+
$value: z.string().min(1),
|
|
114
|
+
$description: z.string().optional(),
|
|
115
|
+
});
|
|
116
|
+
/** DTCG cubic-bezier easing token. `$value` is a CSS timing function
|
|
117
|
+
* string (`"cubic-bezier(0.4, 0, 0.2, 1)"`, keyword aliases like
|
|
118
|
+
* `"ease-out"`, etc.). */
|
|
119
|
+
const CubicBezierToken = z.strictObject({
|
|
120
|
+
$type: z.literal('cubicBezier'),
|
|
121
|
+
$value: z.string().min(1),
|
|
122
|
+
$description: z.string().optional(),
|
|
123
|
+
});
|
|
124
|
+
/** Structured shadow value — DTCG spec composite shape. */
|
|
125
|
+
const ShadowValue = z.strictObject({
|
|
126
|
+
offsetX: z.string().min(1),
|
|
127
|
+
offsetY: z.string().min(1),
|
|
128
|
+
blur: z.string().min(1),
|
|
129
|
+
spread: z.string().min(1),
|
|
130
|
+
color: z.string().min(1),
|
|
131
|
+
});
|
|
132
|
+
/** Shadow token — accepts either the structured DTCG composite or a
|
|
133
|
+
* raw CSS box-shadow string (the curated registry themes ship the
|
|
134
|
+
* string form, e.g. `"0 1px 2px 0 rgba(0, 0, 0, 0.05)"`). */
|
|
135
|
+
const ShadowToken = z.strictObject({
|
|
136
|
+
$type: z.literal('shadow'),
|
|
137
|
+
$value: z.union([z.string().min(1), ShadowValue]),
|
|
138
|
+
$description: z.string().optional(),
|
|
139
|
+
});
|
|
140
|
+
/** Structured transition value. String-form also accepted because
|
|
141
|
+
* DTCG spec itself allows a single `property duration timing` line. */
|
|
142
|
+
const TransitionValue = z.strictObject({
|
|
143
|
+
duration: z.string().min(1),
|
|
144
|
+
timingFunction: z.string().min(1),
|
|
145
|
+
property: z.string().min(1).optional(),
|
|
146
|
+
});
|
|
147
|
+
const TransitionToken = z.strictObject({
|
|
148
|
+
$type: z.literal('transition'),
|
|
149
|
+
$value: z.union([z.string().min(1), TransitionValue]),
|
|
150
|
+
$description: z.string().optional(),
|
|
151
|
+
});
|
|
152
|
+
const NumberToken = z.strictObject({
|
|
153
|
+
$type: z.literal('number'),
|
|
154
|
+
$value: z.number(),
|
|
155
|
+
$description: z.string().optional(),
|
|
156
|
+
});
|
|
157
|
+
/** Line-height token — DTCG allows unit-less number or dimension. */
|
|
158
|
+
const LineHeightToken = z.union([
|
|
159
|
+
z.strictObject({
|
|
160
|
+
$type: z.literal('number'),
|
|
161
|
+
$value: z.number(),
|
|
162
|
+
$description: z.string().optional(),
|
|
163
|
+
}),
|
|
164
|
+
z.strictObject({
|
|
165
|
+
$type: z.literal('dimension'),
|
|
166
|
+
$value: z.string().min(1),
|
|
167
|
+
$description: z.string().optional(),
|
|
168
|
+
}),
|
|
169
|
+
]);
|
|
170
|
+
/**
|
|
171
|
+
* Permissive token leaf for the newer optional groups (`canvas`,
|
|
172
|
+
* `motion.keyframes`, `zIndex`). DTCG hasn't standardised every
|
|
173
|
+
* `$type` value the curated registry themes carry — for example
|
|
174
|
+
* canvas mode uses `$type: 'string'`, canvas colors uses
|
|
175
|
+
* `$type: 'array'`, easing uses `$type: 'cubicBezier'`. Rather than
|
|
176
|
+
* enumerate every spelling, this leaf accepts any `$type` string and
|
|
177
|
+
* any JSON-serializable `$value`. Strict per-leaf shape stays in
|
|
178
|
+
* force for the original token types via the leaves above.
|
|
179
|
+
*/
|
|
180
|
+
const PermissiveToken = z.strictObject({
|
|
181
|
+
$type: z.string().min(1),
|
|
182
|
+
$value: z.unknown(),
|
|
183
|
+
$description: z.string().optional(),
|
|
184
|
+
});
|
|
185
|
+
// ─── Groups ──────────────────────────────────────────────────────────
|
|
186
|
+
/**
|
|
187
|
+
* A "palette" group — a record of color tokens keyed by scale step
|
|
188
|
+
* (`50`, `100`, `200`, …). DTCG allows freeform keys so the record
|
|
189
|
+
* stays open; keys are not enforced at schema level.
|
|
190
|
+
*/
|
|
191
|
+
const ColorPalette = z.record(z.string(), ColorToken);
|
|
192
|
+
/**
|
|
193
|
+
* Color group — two-tier: `{palette}` records for scales (primary /
|
|
194
|
+
* neutral / semantic scales) plus single-token semantic roles
|
|
195
|
+
* (surface / onSurface / outline / …) at the top level.
|
|
196
|
+
*
|
|
197
|
+
* Kept open (`z.record` not `z.strictObject`) so authored themes
|
|
198
|
+
* can add brand-specific palettes (`accent`, `brand-green`, …) and
|
|
199
|
+
* semantic roles without a schema change. `color` itself is
|
|
200
|
+
* required but its internal shape is author-extensible.
|
|
201
|
+
*
|
|
202
|
+
* Each entry can be EITHER a singleton `ColorToken` (e.g. a Material
|
|
203
|
+
* role like `surface`) OR a `ColorPalette` (a scale like `primary`'s
|
|
204
|
+
* 50-900 stops). Tools that emit only singleton colors (no scales)
|
|
205
|
+
* stay parseable.
|
|
206
|
+
*/
|
|
207
|
+
const ColorGroup = z.record(z.string(), z.union([ColorToken, ColorPalette]));
|
|
208
|
+
const SpacingGroup = z.record(z.string(), DimensionToken);
|
|
209
|
+
/**
|
|
210
|
+
* Font group — `family` / `size` / `weight` / `lineHeight` records.
|
|
211
|
+
* Renamed from `typography` (previously: `typography.fontFamily`,
|
|
212
|
+
* `typography.fontSize`, …) to mirror the internal `DtcgTheme`
|
|
213
|
+
* shape's `font.family` / `font.size` / `font.weight` /
|
|
214
|
+
* `font.lineHeight`.
|
|
215
|
+
*
|
|
216
|
+
* Inside `family`, `sans` is the only required slot — the canonical
|
|
217
|
+
* theme always carries a sans-serif default. `mono` and any other
|
|
218
|
+
* family slot (e.g. `serif`, `display`) are optional. The record
|
|
219
|
+
* stays open so authors can declare additional named families.
|
|
220
|
+
*/
|
|
221
|
+
const FontFamilyGroup = z.record(z.string(), FontFamilyToken).and(z.object({
|
|
222
|
+
sans: FontFamilyToken,
|
|
223
|
+
}));
|
|
224
|
+
const FontGroup = z.strictObject({
|
|
225
|
+
family: FontFamilyGroup,
|
|
226
|
+
size: z.record(z.string(), DimensionToken),
|
|
227
|
+
weight: z.record(z.string(), FontWeightToken),
|
|
228
|
+
lineHeight: z.record(z.string(), LineHeightToken),
|
|
229
|
+
});
|
|
230
|
+
/**
|
|
231
|
+
* Shape group — `radius` + `shadow`. Moved here from the previous
|
|
232
|
+
* top-level `radius` and `shadow` fields to mirror the internal
|
|
233
|
+
* `DtcgTheme.shape` group.
|
|
234
|
+
*/
|
|
235
|
+
const ShapeGroup = z.strictObject({
|
|
236
|
+
radius: z.record(z.string(), DimensionToken),
|
|
237
|
+
shadow: z.record(z.string(), ShadowToken),
|
|
238
|
+
});
|
|
239
|
+
/**
|
|
240
|
+
* Motion group — `duration` / `easing` / `transition` / `keyframes`.
|
|
241
|
+
* Moved here from the previous top-level `duration` and `transition`
|
|
242
|
+
* fields. `easing` and `keyframes` are optional because external
|
|
243
|
+
* tools (Figma Tokens, Style Dictionary) don't always emit them.
|
|
244
|
+
*/
|
|
245
|
+
const MotionGroup = z.strictObject({
|
|
246
|
+
duration: z.record(z.string(), DurationToken),
|
|
247
|
+
easing: z.record(z.string(), CubicBezierToken).optional(),
|
|
248
|
+
transition: z.record(z.string(), TransitionToken),
|
|
249
|
+
keyframes: z.record(z.string(), PermissiveToken).optional(),
|
|
250
|
+
});
|
|
251
|
+
/**
|
|
252
|
+
* Canvas group — GenerativeCanvas background configuration.
|
|
253
|
+
* Optional at the document root (external tools won't emit this).
|
|
254
|
+
* Token leaves use `PermissiveToken` because the canonical shape
|
|
255
|
+
* mixes `$type: 'string'` (mode), `$type: 'number'` (speed),
|
|
256
|
+
* `$type: 'array'` (colors), `$type: 'color'` (background).
|
|
257
|
+
*/
|
|
258
|
+
const CanvasGroup = z.strictObject({
|
|
259
|
+
mode: PermissiveToken,
|
|
260
|
+
speed: PermissiveToken,
|
|
261
|
+
colors: PermissiveToken,
|
|
262
|
+
background: PermissiveToken,
|
|
263
|
+
});
|
|
264
|
+
const AccessibilityGroup = z.strictObject({
|
|
265
|
+
focusRing: z
|
|
266
|
+
.strictObject({
|
|
267
|
+
color: ColorToken,
|
|
268
|
+
width: DimensionToken,
|
|
269
|
+
offset: DimensionToken,
|
|
270
|
+
})
|
|
271
|
+
.optional(),
|
|
272
|
+
reducedMotion: z
|
|
273
|
+
.strictObject({
|
|
274
|
+
duration: DurationToken,
|
|
275
|
+
})
|
|
276
|
+
.optional(),
|
|
277
|
+
highContrast: z
|
|
278
|
+
.strictObject({
|
|
279
|
+
borderWidth: DimensionToken,
|
|
280
|
+
textColor: ColorToken,
|
|
281
|
+
backgroundColor: ColorToken,
|
|
282
|
+
linkColor: ColorToken,
|
|
283
|
+
})
|
|
284
|
+
.optional(),
|
|
285
|
+
});
|
|
286
|
+
const ZIndexGroup = z.record(z.string(), NumberToken);
|
|
287
|
+
/**
|
|
288
|
+
* Optional `$metadata` bag — mirrors the internal
|
|
289
|
+
* `DtcgTheme.$metadata` shape so registry themes can round-trip
|
|
290
|
+
* through the file format without information loss.
|
|
291
|
+
*/
|
|
292
|
+
const MetadataGroup = z.strictObject({
|
|
293
|
+
font: z.string().min(1).optional(),
|
|
294
|
+
fontUrl: z.string().min(1).optional(),
|
|
295
|
+
philosophy: z.string().min(1).optional(),
|
|
296
|
+
});
|
|
297
|
+
// ─── Root document ───────────────────────────────────────────────────
|
|
298
|
+
/**
|
|
299
|
+
* Plain DTCG theme document v1. Required groups (`color`, `font`,
|
|
300
|
+
* `spacing`, `shape`) map 1:1 to `@ggui-ai/design`'s
|
|
301
|
+
* `generateCssVariables` walker targets — anything less would mean
|
|
302
|
+
* a partial CSS output worse than falling back to the shipped
|
|
303
|
+
* default. Optional groups (`motion`, `canvas`, `accessibility`,
|
|
304
|
+
* `zIndex`) are the well-known DTCG additives; present → emitted,
|
|
305
|
+
* absent → the design system's built-in fallbacks apply.
|
|
306
|
+
*
|
|
307
|
+
* `$name`, `$description`, and `$metadata` are accepted as optional
|
|
308
|
+
* DTCG-standard metadata. They round-trip cleanly to/from the
|
|
309
|
+
* internal `DtcgTheme` shape but external tools don't always emit
|
|
310
|
+
* them.
|
|
311
|
+
*
|
|
312
|
+
* Strict root: unknown top-level keys fail parse. `$schema`,
|
|
313
|
+
* `$version`, `$name`, `$description`, `$metadata` are all
|
|
314
|
+
* allow-listed (DTCG metadata); anything else is a typo. Additive
|
|
315
|
+
* slices add new fields here under `schema: '1'`.
|
|
316
|
+
*/
|
|
317
|
+
export const ThemeDocumentV1 = z.strictObject({
|
|
318
|
+
/** Optional DTCG spec URL. Not validated — the spec hasn't frozen
|
|
319
|
+
* a canonical URL and hand-authored themes often omit it. */
|
|
320
|
+
$schema: z.string().min(1).optional(),
|
|
321
|
+
/** Optional author-declared version. Not validated — string-form. */
|
|
322
|
+
$version: z.string().min(1).optional(),
|
|
323
|
+
/** Optional human-readable theme name. DTCG metadata; mirrors
|
|
324
|
+
* `DtcgTheme.$name`. */
|
|
325
|
+
$name: z.string().min(1).optional(),
|
|
326
|
+
/** Optional human-readable theme description. DTCG metadata;
|
|
327
|
+
* mirrors `DtcgTheme.$description`. */
|
|
328
|
+
$description: z.string().min(1).optional(),
|
|
329
|
+
/** Optional metadata bag — font fallback, hosting-philosophy notes,
|
|
330
|
+
* etc. Mirrors `DtcgTheme.$metadata`. */
|
|
331
|
+
$metadata: MetadataGroup.optional(),
|
|
332
|
+
/** Color tokens — palettes + semantic roles. Required. */
|
|
333
|
+
color: ColorGroup,
|
|
334
|
+
/** Spacing scale. Required. */
|
|
335
|
+
spacing: SpacingGroup,
|
|
336
|
+
/** Font tokens — `family` / `size` / `weight` / `lineHeight`. Required. */
|
|
337
|
+
font: FontGroup,
|
|
338
|
+
/** Shape tokens — `radius` + `shadow`. Required. */
|
|
339
|
+
shape: ShapeGroup,
|
|
340
|
+
/** Motion tokens — `duration` + `transition` (required sub-records),
|
|
341
|
+
* `easing` + `keyframes` (optional sub-records). Optional. */
|
|
342
|
+
motion: MotionGroup.optional(),
|
|
343
|
+
/** Canvas configuration for the GenerativeCanvas background.
|
|
344
|
+
* Optional — external tools won't emit this. */
|
|
345
|
+
canvas: CanvasGroup.optional(),
|
|
346
|
+
/** Accessibility tokens (focus ring, reduced motion, high contrast).
|
|
347
|
+
* Optional. */
|
|
348
|
+
accessibility: AccessibilityGroup.optional(),
|
|
349
|
+
/** Z-index scale. Optional. */
|
|
350
|
+
zIndex: ZIndexGroup.optional(),
|
|
351
|
+
});
|
|
352
|
+
/**
|
|
353
|
+
* Parse a raw JSON value into a validated {@link ThemeDocument}.
|
|
354
|
+
* Throws a `ZodError` with human-readable issues on invalid input.
|
|
355
|
+
*/
|
|
356
|
+
export function parseThemeDocument(raw) {
|
|
357
|
+
return ThemeDocumentV1.parse(raw);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Safe-parse variant — returns a discriminated `z.safeParse` result.
|
|
361
|
+
* Prefer this inside CLI tooling where you want to render the issue
|
|
362
|
+
* list without try/catch.
|
|
363
|
+
*/
|
|
364
|
+
export function safeParseThemeDocument(raw) {
|
|
365
|
+
return ThemeDocumentV1.safeParse(raw);
|
|
366
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ggui.ui.json` v1 — per-UI manifest, colocated with source.
|
|
3
|
+
*
|
|
4
|
+
* One of three file-format surfaces in the ggui manifest model (see
|
|
5
|
+
* `schema.ts` header for the full context). Each authored UI ships
|
|
6
|
+
* its own `ggui.ui.json` next to its TSX source:
|
|
7
|
+
*
|
|
8
|
+
* ```
|
|
9
|
+
* ui/weather-card/
|
|
10
|
+
* ├── ggui.ui.json ← this file
|
|
11
|
+
* └── index.tsx
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* The root `ggui.json`'s `blueprints.include` globs index these
|
|
15
|
+
* files at OSS-server boot. The `id` field is the stable identifier
|
|
16
|
+
* that crosses source boundaries — it's how publish, pull, conflict
|
|
17
|
+
* detection, and agent references address a UI independent of path
|
|
18
|
+
* or display name.
|
|
19
|
+
*
|
|
20
|
+
* **Identity rules:**
|
|
21
|
+
*
|
|
22
|
+
* 1. `id` is **required**. Zero-config isn't allowed here — a UI
|
|
23
|
+
* without an id can't be published, pulled, or referenced.
|
|
24
|
+
* 2. `id` is **stable across content edits**. Do NOT derive it from
|
|
25
|
+
* `contentHash` (which changes on every edit) or the display
|
|
26
|
+
* name (which is user-editable prose).
|
|
27
|
+
* 3. `id` is **machine-oriented**. Lowercase-ish, URL-safe. Not a
|
|
28
|
+
* UUID requirement — any stable-ish identifier works — but it
|
|
29
|
+
* must be grep-friendly, not a human display name.
|
|
30
|
+
* 4. `id` is **not path-derived**. Renaming or moving a UI file
|
|
31
|
+
* MUST NOT invalidate references. The id follows the UI.
|
|
32
|
+
*
|
|
33
|
+
* `contentHash` stays as a separate, derived field. It versions the
|
|
34
|
+
* compiled artifact and drives conflict detection on publish. It is
|
|
35
|
+
* NOT the identity.
|
|
36
|
+
*/
|
|
37
|
+
import { z } from 'zod';
|
|
38
|
+
import type { DataContract } from '@ggui-ai/protocol';
|
|
39
|
+
/**
|
|
40
|
+
* Per-UI manifest schema. Strict-object for v1 — unknown fields
|
|
41
|
+
* fail parse. Additive fields land as coordinated schema changes.
|
|
42
|
+
*/
|
|
43
|
+
export declare const UiManifestV1: z.ZodObject<{
|
|
44
|
+
/** Stable identity. See identity rules in module docstring. */
|
|
45
|
+
id: z.ZodString;
|
|
46
|
+
/** Human-readable name (e.g., "Weather Card", "Kanban Board"). */
|
|
47
|
+
name: z.ZodString;
|
|
48
|
+
/** One-line description of what this UI does. */
|
|
49
|
+
description: z.ZodOptional<z.ZodString>;
|
|
50
|
+
/** Data contract: props, actions, streams, wired tools, client tools. */
|
|
51
|
+
contract: z.ZodCustom<DataContract, DataContract>;
|
|
52
|
+
/** Category for browsing/filtering (e.g., "dashboard", "form", "data-viz"). */
|
|
53
|
+
category: z.ZodOptional<z.ZodString>;
|
|
54
|
+
/** Phrases that help blueprint-first matching find this UI. */
|
|
55
|
+
matchPatterns: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
56
|
+
/** MCP tool names this UI is designed for. Used for discovery-layer matching. */
|
|
57
|
+
mcpTools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
58
|
+
/** MCP server name this UI is optimized for (e.g., "github", "stripe"). */
|
|
59
|
+
mcpServer: z.ZodOptional<z.ZodString>;
|
|
60
|
+
/** UI class: sandboxed (portable) or fullstack (requires client bundle). */
|
|
61
|
+
uiClass: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"sandboxed">, z.ZodLiteral<"fullstack">]>>;
|
|
62
|
+
/** Content hash of the compiled ESM output — versions the artifact.
|
|
63
|
+
* Derived, not authored. NOT the identity (see `id`). */
|
|
64
|
+
contentHash: z.ZodOptional<z.ZodString>;
|
|
65
|
+
/** Parent content hash (for exported → modified → re-registered lineage). */
|
|
66
|
+
parentHash: z.ZodOptional<z.ZodString>;
|
|
67
|
+
/** Source entry point relative to project root (e.g., "src/component.tsx"). */
|
|
68
|
+
entryPoint: z.ZodOptional<z.ZodString>;
|
|
69
|
+
}, z.core.$strict>;
|
|
70
|
+
/** Static TypeScript type derived from the v1 schema. */
|
|
71
|
+
export type UiManifestV1 = z.infer<typeof UiManifestV1>;
|
|
72
|
+
/**
|
|
73
|
+
* Canonical type alias — `UiManifest` is the term used in protocol
|
|
74
|
+
* docs, code, and plan docs. `UiManifestV1` is the version-pinned
|
|
75
|
+
* name. Both resolve to the same shape; consumers may import either.
|
|
76
|
+
*/
|
|
77
|
+
export type UiManifest = UiManifestV1;
|
|
78
|
+
/**
|
|
79
|
+
* Canonical filename for the per-UI manifest. Always this name,
|
|
80
|
+
* always colocated with the UI's source. Exported so tooling uses
|
|
81
|
+
* the constant instead of hard-coding the string.
|
|
82
|
+
*/
|
|
83
|
+
export declare const GGUI_UI_JSON_FILENAME = "ggui.ui.json";
|
|
84
|
+
/**
|
|
85
|
+
* Parse a raw JSON value into a validated {@link UiManifest}.
|
|
86
|
+
* Throws a `ZodError` with human-readable issues on invalid input.
|
|
87
|
+
*/
|
|
88
|
+
export declare function parseUiManifest(raw: unknown): UiManifest;
|
|
89
|
+
/**
|
|
90
|
+
* Safe-parse variant — returns a discriminated `z.safeParse` result.
|
|
91
|
+
* Prefer this inside CLI tooling where you want to render the issue
|
|
92
|
+
* list without try/catch.
|
|
93
|
+
*/
|
|
94
|
+
export declare function safeParseUiManifest(raw: unknown): ReturnType<typeof UiManifestV1.safeParse>;
|
|
95
|
+
//# sourceMappingURL=ui-manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-manifest.d.ts","sourceRoot":"","sources":["../src/ui-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AA4BtD;;;GAGG;AACH,eAAO,MAAM,YAAY;IACvB,+DAA+D;;IAG/D,kEAAkE;;IAGlE,iDAAiD;;IAGjD,yEAAyE;;IAGzE,+EAA+E;;IAG/E,+DAA+D;;IAK/D,iFAAiF;;IAGjF,2EAA2E;;IAK3E,4EAA4E;;IAG5E;6DACyD;;IAGzD,6EAA6E;;IAG7E,+EAA+E;;kBAE/E,CAAC;AAEH,yDAAyD;AACzD,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,YAAY,CAAC;AAEtC;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,iBAAiB,CAAC;AAEpD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CAExD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,OAAO,GACX,UAAU,CAAC,OAAO,YAAY,CAAC,SAAS,CAAC,CAE3C"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ggui.ui.json` v1 — per-UI manifest, colocated with source.
|
|
3
|
+
*
|
|
4
|
+
* One of three file-format surfaces in the ggui manifest model (see
|
|
5
|
+
* `schema.ts` header for the full context). Each authored UI ships
|
|
6
|
+
* its own `ggui.ui.json` next to its TSX source:
|
|
7
|
+
*
|
|
8
|
+
* ```
|
|
9
|
+
* ui/weather-card/
|
|
10
|
+
* ├── ggui.ui.json ← this file
|
|
11
|
+
* └── index.tsx
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* The root `ggui.json`'s `blueprints.include` globs index these
|
|
15
|
+
* files at OSS-server boot. The `id` field is the stable identifier
|
|
16
|
+
* that crosses source boundaries — it's how publish, pull, conflict
|
|
17
|
+
* detection, and agent references address a UI independent of path
|
|
18
|
+
* or display name.
|
|
19
|
+
*
|
|
20
|
+
* **Identity rules:**
|
|
21
|
+
*
|
|
22
|
+
* 1. `id` is **required**. Zero-config isn't allowed here — a UI
|
|
23
|
+
* without an id can't be published, pulled, or referenced.
|
|
24
|
+
* 2. `id` is **stable across content edits**. Do NOT derive it from
|
|
25
|
+
* `contentHash` (which changes on every edit) or the display
|
|
26
|
+
* name (which is user-editable prose).
|
|
27
|
+
* 3. `id` is **machine-oriented**. Lowercase-ish, URL-safe. Not a
|
|
28
|
+
* UUID requirement — any stable-ish identifier works — but it
|
|
29
|
+
* must be grep-friendly, not a human display name.
|
|
30
|
+
* 4. `id` is **not path-derived**. Renaming or moving a UI file
|
|
31
|
+
* MUST NOT invalidate references. The id follows the UI.
|
|
32
|
+
*
|
|
33
|
+
* `contentHash` stays as a separate, derived field. It versions the
|
|
34
|
+
* compiled artifact and drives conflict detection on publish. It is
|
|
35
|
+
* NOT the identity.
|
|
36
|
+
*/
|
|
37
|
+
import { z } from 'zod';
|
|
38
|
+
/**
|
|
39
|
+
* Stable UI identity — lowercase-ish, URL-safe, ≤128 chars. Accepts
|
|
40
|
+
* letters, digits, hyphens, underscores, dots, and colons (for
|
|
41
|
+
* scoping like `company:weather-card`). Deliberately permissive
|
|
42
|
+
* enough to admit common conventions (uuids, nanoids, slug-like ids)
|
|
43
|
+
* without forcing one shape.
|
|
44
|
+
*
|
|
45
|
+
* Rejects whitespace, slashes, and unprintables so file paths can
|
|
46
|
+
* safely embed the id without escaping.
|
|
47
|
+
*/
|
|
48
|
+
const UiIdSchema = z
|
|
49
|
+
.string()
|
|
50
|
+
.min(1)
|
|
51
|
+
.max(128)
|
|
52
|
+
.regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/, {
|
|
53
|
+
message: 'ggui.ui.json.id must be 1-128 chars of A-Za-z0-9 plus . _ : -, starting with an alphanumeric.',
|
|
54
|
+
});
|
|
55
|
+
/**
|
|
56
|
+
* UI class — matches `UiClass` from `@ggui-ai/protocol` (vocabulary
|
|
57
|
+
* owned there so the `classifyUi()` runtime classifier can ship
|
|
58
|
+
* without a reverse dep on this package).
|
|
59
|
+
*/
|
|
60
|
+
const UiClassSchema = z.union([z.literal('sandboxed'), z.literal('fullstack')]);
|
|
61
|
+
/**
|
|
62
|
+
* Per-UI manifest schema. Strict-object for v1 — unknown fields
|
|
63
|
+
* fail parse. Additive fields land as coordinated schema changes.
|
|
64
|
+
*/
|
|
65
|
+
export const UiManifestV1 = z.strictObject({
|
|
66
|
+
/** Stable identity. See identity rules in module docstring. */
|
|
67
|
+
id: UiIdSchema,
|
|
68
|
+
/** Human-readable name (e.g., "Weather Card", "Kanban Board"). */
|
|
69
|
+
name: z.string().min(1).max(120),
|
|
70
|
+
/** One-line description of what this UI does. */
|
|
71
|
+
description: z.string().max(500).optional(),
|
|
72
|
+
/** Data contract: props, actions, streams, wired tools, client tools. */
|
|
73
|
+
contract: z.custom((v) => typeof v === 'object' && v !== null),
|
|
74
|
+
/** Category for browsing/filtering (e.g., "dashboard", "form", "data-viz"). */
|
|
75
|
+
category: z.string().max(60).optional(),
|
|
76
|
+
/** Phrases that help blueprint-first matching find this UI. */
|
|
77
|
+
matchPatterns: z.array(z.string().min(1)).optional(),
|
|
78
|
+
// ── MCP Pairing (optional) ─────────────────────────────────────────
|
|
79
|
+
/** MCP tool names this UI is designed for. Used for discovery-layer matching. */
|
|
80
|
+
mcpTools: z.array(z.string().min(1)).optional(),
|
|
81
|
+
/** MCP server name this UI is optimized for (e.g., "github", "stripe"). */
|
|
82
|
+
mcpServer: z.string().min(1).optional(),
|
|
83
|
+
// ── Classification & Lineage ───────────────────────────────────────
|
|
84
|
+
/** UI class: sandboxed (portable) or fullstack (requires client bundle). */
|
|
85
|
+
uiClass: UiClassSchema.optional(),
|
|
86
|
+
/** Content hash of the compiled ESM output — versions the artifact.
|
|
87
|
+
* Derived, not authored. NOT the identity (see `id`). */
|
|
88
|
+
contentHash: z.string().min(1).optional(),
|
|
89
|
+
/** Parent content hash (for exported → modified → re-registered lineage). */
|
|
90
|
+
parentHash: z.string().min(1).optional(),
|
|
91
|
+
/** Source entry point relative to project root (e.g., "src/component.tsx"). */
|
|
92
|
+
entryPoint: z.string().min(1).optional(),
|
|
93
|
+
});
|
|
94
|
+
/**
|
|
95
|
+
* Canonical filename for the per-UI manifest. Always this name,
|
|
96
|
+
* always colocated with the UI's source. Exported so tooling uses
|
|
97
|
+
* the constant instead of hard-coding the string.
|
|
98
|
+
*/
|
|
99
|
+
export const GGUI_UI_JSON_FILENAME = 'ggui.ui.json';
|
|
100
|
+
/**
|
|
101
|
+
* Parse a raw JSON value into a validated {@link UiManifest}.
|
|
102
|
+
* Throws a `ZodError` with human-readable issues on invalid input.
|
|
103
|
+
*/
|
|
104
|
+
export function parseUiManifest(raw) {
|
|
105
|
+
return UiManifestV1.parse(raw);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Safe-parse variant — returns a discriminated `z.safeParse` result.
|
|
109
|
+
* Prefer this inside CLI tooling where you want to render the issue
|
|
110
|
+
* list without try/catch.
|
|
111
|
+
*/
|
|
112
|
+
export function safeParseUiManifest(raw) {
|
|
113
|
+
return UiManifestV1.safeParse(raw);
|
|
114
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ggui-ai/project-config",
|
|
3
|
+
"version": "0.1.0-rc.1",
|
|
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
|
+
"keywords": [
|
|
6
|
+
"ggui",
|
|
7
|
+
"manifest",
|
|
8
|
+
"schema",
|
|
9
|
+
"config",
|
|
10
|
+
"mcp",
|
|
11
|
+
"zod"
|
|
12
|
+
],
|
|
13
|
+
"license": "Apache-2.0",
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "dist/index.js",
|
|
16
|
+
"types": "dist/index.d.ts",
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js",
|
|
25
|
+
"default": "./dist/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./node": {
|
|
28
|
+
"types": "./dist/node.d.ts",
|
|
29
|
+
"import": "./dist/node.js",
|
|
30
|
+
"default": "./dist/node.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"tinyglobby": "^0.2.16",
|
|
35
|
+
"zod": "^4.3.6",
|
|
36
|
+
"@ggui-ai/protocol": "0.1.0-rc.1",
|
|
37
|
+
"@ggui-ai/design": "0.1.0-rc.1"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^24.0.0",
|
|
41
|
+
"typescript": "^5.0.0",
|
|
42
|
+
"vitest": "^3.0.0"
|
|
43
|
+
},
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "https://github.com/ggui-ai/ggui.git",
|
|
47
|
+
"directory": "packages/project-config"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/ggui-ai/ggui/tree/main/packages/project-config",
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/ggui-ai/ggui/issues"
|
|
52
|
+
},
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=20.0.0"
|
|
58
|
+
},
|
|
59
|
+
"author": "ggui contributors <hello@ggui.ai>",
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "tsc -p tsconfig.build.json",
|
|
62
|
+
"dev": "tsc --watch",
|
|
63
|
+
"typecheck": "tsc --noEmit",
|
|
64
|
+
"test": "vitest run",
|
|
65
|
+
"test:watch": "vitest"
|
|
66
|
+
}
|
|
67
|
+
}
|