@ggui-ai/project-config 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/theme.d.ts +187 -0
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +165 -24
- package/package.json +3 -3
package/dist/theme.d.ts
CHANGED
|
@@ -326,6 +326,135 @@ export declare const ThemeDocumentV2: z.ZodObject<{
|
|
|
326
326
|
}, z.core.$strict>>;
|
|
327
327
|
}, z.core.$strict>>;
|
|
328
328
|
}, z.core.$strict>;
|
|
329
|
+
typeScale: z.ZodOptional<z.ZodObject<{
|
|
330
|
+
display: z.ZodOptional<z.ZodObject<{
|
|
331
|
+
size: z.ZodObject<{
|
|
332
|
+
$type: z.ZodLiteral<"dimension">;
|
|
333
|
+
$value: z.ZodString;
|
|
334
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
335
|
+
}, z.core.$strict>;
|
|
336
|
+
weight: z.ZodOptional<z.ZodObject<{
|
|
337
|
+
$type: z.ZodLiteral<"fontWeight">;
|
|
338
|
+
$value: z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>;
|
|
339
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
340
|
+
}, z.core.$strict>>;
|
|
341
|
+
tracking: z.ZodOptional<z.ZodObject<{
|
|
342
|
+
$type: z.ZodLiteral<"dimension">;
|
|
343
|
+
$value: z.ZodString;
|
|
344
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
345
|
+
}, z.core.$strict>>;
|
|
346
|
+
leading: z.ZodOptional<z.ZodObject<{
|
|
347
|
+
$type: z.ZodLiteral<"number">;
|
|
348
|
+
$value: z.ZodNumber;
|
|
349
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
350
|
+
}, z.core.$strict>>;
|
|
351
|
+
}, z.core.$strict>>;
|
|
352
|
+
h1: z.ZodOptional<z.ZodObject<{
|
|
353
|
+
size: z.ZodObject<{
|
|
354
|
+
$type: z.ZodLiteral<"dimension">;
|
|
355
|
+
$value: z.ZodString;
|
|
356
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
357
|
+
}, z.core.$strict>;
|
|
358
|
+
weight: z.ZodOptional<z.ZodObject<{
|
|
359
|
+
$type: z.ZodLiteral<"fontWeight">;
|
|
360
|
+
$value: z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>;
|
|
361
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
362
|
+
}, z.core.$strict>>;
|
|
363
|
+
tracking: z.ZodOptional<z.ZodObject<{
|
|
364
|
+
$type: z.ZodLiteral<"dimension">;
|
|
365
|
+
$value: z.ZodString;
|
|
366
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
367
|
+
}, z.core.$strict>>;
|
|
368
|
+
leading: z.ZodOptional<z.ZodObject<{
|
|
369
|
+
$type: z.ZodLiteral<"number">;
|
|
370
|
+
$value: z.ZodNumber;
|
|
371
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
372
|
+
}, z.core.$strict>>;
|
|
373
|
+
}, z.core.$strict>>;
|
|
374
|
+
h2: z.ZodOptional<z.ZodObject<{
|
|
375
|
+
size: z.ZodObject<{
|
|
376
|
+
$type: z.ZodLiteral<"dimension">;
|
|
377
|
+
$value: z.ZodString;
|
|
378
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
379
|
+
}, z.core.$strict>;
|
|
380
|
+
weight: z.ZodOptional<z.ZodObject<{
|
|
381
|
+
$type: z.ZodLiteral<"fontWeight">;
|
|
382
|
+
$value: z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>;
|
|
383
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
384
|
+
}, z.core.$strict>>;
|
|
385
|
+
tracking: z.ZodOptional<z.ZodObject<{
|
|
386
|
+
$type: z.ZodLiteral<"dimension">;
|
|
387
|
+
$value: z.ZodString;
|
|
388
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
389
|
+
}, z.core.$strict>>;
|
|
390
|
+
leading: z.ZodOptional<z.ZodObject<{
|
|
391
|
+
$type: z.ZodLiteral<"number">;
|
|
392
|
+
$value: z.ZodNumber;
|
|
393
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
394
|
+
}, z.core.$strict>>;
|
|
395
|
+
}, z.core.$strict>>;
|
|
396
|
+
body: z.ZodOptional<z.ZodObject<{
|
|
397
|
+
size: z.ZodObject<{
|
|
398
|
+
$type: z.ZodLiteral<"dimension">;
|
|
399
|
+
$value: z.ZodString;
|
|
400
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
401
|
+
}, z.core.$strict>;
|
|
402
|
+
weight: z.ZodOptional<z.ZodObject<{
|
|
403
|
+
$type: z.ZodLiteral<"fontWeight">;
|
|
404
|
+
$value: z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>;
|
|
405
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
406
|
+
}, z.core.$strict>>;
|
|
407
|
+
tracking: z.ZodOptional<z.ZodObject<{
|
|
408
|
+
$type: z.ZodLiteral<"dimension">;
|
|
409
|
+
$value: z.ZodString;
|
|
410
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
411
|
+
}, z.core.$strict>>;
|
|
412
|
+
leading: z.ZodOptional<z.ZodObject<{
|
|
413
|
+
$type: z.ZodLiteral<"number">;
|
|
414
|
+
$value: z.ZodNumber;
|
|
415
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
416
|
+
}, z.core.$strict>>;
|
|
417
|
+
}, z.core.$strict>>;
|
|
418
|
+
label: z.ZodOptional<z.ZodObject<{
|
|
419
|
+
size: z.ZodObject<{
|
|
420
|
+
$type: z.ZodLiteral<"dimension">;
|
|
421
|
+
$value: z.ZodString;
|
|
422
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
423
|
+
}, z.core.$strict>;
|
|
424
|
+
weight: z.ZodOptional<z.ZodObject<{
|
|
425
|
+
$type: z.ZodLiteral<"fontWeight">;
|
|
426
|
+
$value: z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>;
|
|
427
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
428
|
+
}, z.core.$strict>>;
|
|
429
|
+
tracking: z.ZodOptional<z.ZodObject<{
|
|
430
|
+
$type: z.ZodLiteral<"dimension">;
|
|
431
|
+
$value: z.ZodString;
|
|
432
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
433
|
+
}, z.core.$strict>>;
|
|
434
|
+
leading: z.ZodOptional<z.ZodObject<{
|
|
435
|
+
$type: z.ZodLiteral<"number">;
|
|
436
|
+
$value: z.ZodNumber;
|
|
437
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
438
|
+
}, z.core.$strict>>;
|
|
439
|
+
}, z.core.$strict>>;
|
|
440
|
+
}, z.core.$strict>>;
|
|
441
|
+
rhythm: z.ZodOptional<z.ZodObject<{
|
|
442
|
+
base: z.ZodObject<{
|
|
443
|
+
$type: z.ZodLiteral<"dimension">;
|
|
444
|
+
$value: z.ZodString;
|
|
445
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
446
|
+
}, z.core.$strict>;
|
|
447
|
+
section: z.ZodOptional<z.ZodObject<{
|
|
448
|
+
$type: z.ZodLiteral<"dimension">;
|
|
449
|
+
$value: z.ZodString;
|
|
450
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
451
|
+
}, z.core.$strict>>;
|
|
452
|
+
inset: z.ZodOptional<z.ZodObject<{
|
|
453
|
+
$type: z.ZodLiteral<"dimension">;
|
|
454
|
+
$value: z.ZodString;
|
|
455
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
456
|
+
}, z.core.$strict>>;
|
|
457
|
+
}, z.core.$strict>>;
|
|
329
458
|
motion: z.ZodOptional<z.ZodObject<{
|
|
330
459
|
transition: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
331
460
|
$type: z.ZodLiteral<"transition">;
|
|
@@ -341,6 +470,64 @@ export declare const ThemeDocumentV2: z.ZodObject<{
|
|
|
341
470
|
$value: z.ZodString;
|
|
342
471
|
$description: z.ZodOptional<z.ZodString>;
|
|
343
472
|
}, z.core.$strict>>>;
|
|
473
|
+
duration: z.ZodOptional<z.ZodObject<{
|
|
474
|
+
fast: z.ZodOptional<z.ZodObject<{
|
|
475
|
+
$type: z.ZodLiteral<"duration">;
|
|
476
|
+
$value: z.ZodString;
|
|
477
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
478
|
+
}, z.core.$strict>>;
|
|
479
|
+
base: z.ZodOptional<z.ZodObject<{
|
|
480
|
+
$type: z.ZodLiteral<"duration">;
|
|
481
|
+
$value: z.ZodString;
|
|
482
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
483
|
+
}, z.core.$strict>>;
|
|
484
|
+
slow: z.ZodOptional<z.ZodObject<{
|
|
485
|
+
$type: z.ZodLiteral<"duration">;
|
|
486
|
+
$value: z.ZodString;
|
|
487
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
488
|
+
}, z.core.$strict>>;
|
|
489
|
+
}, z.core.$strict>>;
|
|
490
|
+
easing: z.ZodOptional<z.ZodObject<{
|
|
491
|
+
standard: z.ZodOptional<z.ZodObject<{
|
|
492
|
+
$type: z.ZodString;
|
|
493
|
+
$value: z.ZodString;
|
|
494
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
495
|
+
}, z.core.$strict>>;
|
|
496
|
+
emphasized: z.ZodOptional<z.ZodObject<{
|
|
497
|
+
$type: z.ZodString;
|
|
498
|
+
$value: z.ZodString;
|
|
499
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
500
|
+
}, z.core.$strict>>;
|
|
501
|
+
exit: z.ZodOptional<z.ZodObject<{
|
|
502
|
+
$type: z.ZodString;
|
|
503
|
+
$value: z.ZodString;
|
|
504
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
505
|
+
}, z.core.$strict>>;
|
|
506
|
+
}, z.core.$strict>>;
|
|
507
|
+
reduce: z.ZodOptional<z.ZodEnum<{
|
|
508
|
+
ignore: "ignore";
|
|
509
|
+
respect: "respect";
|
|
510
|
+
}>>;
|
|
511
|
+
}, z.core.$strict>>;
|
|
512
|
+
scrim: z.ZodOptional<z.ZodObject<{
|
|
513
|
+
tone: z.ZodUnion<readonly [z.ZodEnum<{
|
|
514
|
+
light: "light";
|
|
515
|
+
dark: "dark";
|
|
516
|
+
}>, z.ZodObject<{
|
|
517
|
+
$type: z.ZodLiteral<"color">;
|
|
518
|
+
$value: z.ZodString;
|
|
519
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
520
|
+
}, z.core.$strict>]>;
|
|
521
|
+
opacity: z.ZodObject<{
|
|
522
|
+
$type: z.ZodLiteral<"number">;
|
|
523
|
+
$value: z.ZodNumber;
|
|
524
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
525
|
+
}, z.core.$strict>;
|
|
526
|
+
blur: z.ZodObject<{
|
|
527
|
+
$type: z.ZodLiteral<"dimension">;
|
|
528
|
+
$value: z.ZodString;
|
|
529
|
+
$description: z.ZodOptional<z.ZodString>;
|
|
530
|
+
}, z.core.$strict>;
|
|
344
531
|
}, z.core.$strict>>;
|
|
345
532
|
accessibility: z.ZodOptional<z.ZodObject<{
|
|
346
533
|
focusRing: z.ZodOptional<z.ZodObject<{
|
package/dist/theme.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,wBAAwB,CAAC;AAqYnE;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA8D1B,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
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
* keys fail parse, the same discipline as `ggui.json`.
|
|
39
39
|
*/
|
|
40
40
|
import { z } from 'zod';
|
|
41
|
+
import { fontFaceDeclarationSchema } from '@ggui-ai/protocol';
|
|
41
42
|
import { lightTheme } from '@ggui-ai/design/themes';
|
|
42
43
|
// ─── Token leaves ────────────────────────────────────────────────────
|
|
43
44
|
/** A CSS color string. `$value` is not structurally validated (hex,
|
|
@@ -115,27 +116,6 @@ const StringToken = z.strictObject({
|
|
|
115
116
|
$value: z.string().min(1),
|
|
116
117
|
$description: z.string().optional(),
|
|
117
118
|
});
|
|
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
|
-
});
|
|
139
119
|
// ─── Groups ──────────────────────────────────────────────────────────
|
|
140
120
|
/**
|
|
141
121
|
* A "palette" group — a record of color tokens keyed by scale step
|
|
@@ -205,7 +185,8 @@ const FontGroup = z.strictObject({
|
|
|
205
185
|
});
|
|
206
186
|
/** Declared font faces (ggui#987 §5). */
|
|
207
187
|
const TypographyGroup = z.strictObject({
|
|
208
|
-
|
|
188
|
+
/** The ONE face grammar — `@ggui-ai/protocol`'s `fontFaceDeclarationSchema` (ggui#1093): the document door and the AppTheme wire's `fonts` validate identically. */
|
|
189
|
+
faces: z.array(fontFaceDeclarationSchema).optional(),
|
|
209
190
|
});
|
|
210
191
|
/**
|
|
211
192
|
* Shape group — `radius` + `shadow`. Moved here from the previous
|
|
@@ -222,9 +203,155 @@ const ShapeGroup = z.strictObject({
|
|
|
222
203
|
* (optional). Durations and easings are not ladders any more: they ride
|
|
223
204
|
* each transition's own value.
|
|
224
205
|
*/
|
|
206
|
+
/**
|
|
207
|
+
* The HOST DESIGN LANGUAGE's document members (ggui#1093 P1b, #1075 Track C (a);
|
|
208
|
+
* grammar co-signed with rnd 2026-09-15). These are DOCUMENT tokens, never wire
|
|
209
|
+
* members: they reach the card through `deriveThemeVariables` → the overlay,
|
|
210
|
+
* which stays the ONE projection (`docs/principles/mcp-apps-compliance.md`).
|
|
211
|
+
* Every group is OPTIONAL and absent ⇒ the document parses exactly as before
|
|
212
|
+
* (INVARIANT 1: no declaration, byte-identical output).
|
|
213
|
+
*/
|
|
214
|
+
/**
|
|
215
|
+
* One type role. `size` is required; `leading` is a RATIO (`NumberToken`) and
|
|
216
|
+
* never a length — the projection writes `line-height: <ratio>`, which survives
|
|
217
|
+
* a size change, while a length leading breaks the moment the role's size moves.
|
|
218
|
+
*/
|
|
219
|
+
const TypeScaleRole = z.strictObject({
|
|
220
|
+
size: DimensionToken,
|
|
221
|
+
weight: FontWeightToken.optional(),
|
|
222
|
+
tracking: DimensionToken.optional(),
|
|
223
|
+
leading: NumberToken.optional(),
|
|
224
|
+
});
|
|
225
|
+
/**
|
|
226
|
+
* PRECEDENCE, and the ONE sentence that governs every member in this group
|
|
227
|
+
* (ggui#1093 R1, ruled with rnd 2026-09-16): **a host-design-language member
|
|
228
|
+
* overrides the base document's corresponding ladder.** A stated `typeScale`
|
|
229
|
+
* role beats the `font.ramp` stop that would have generated it; a stated
|
|
230
|
+
* `rhythm.base` beats the `spacing` stops it re-derives. Those two read as
|
|
231
|
+
* opposite directions (role over scale, scale over rungs) and are not: in
|
|
232
|
+
* both, the member the HOST measured wins over the ladder ggui supplied.
|
|
233
|
+
*
|
|
234
|
+
* The asymmetry that makes the sentence necessary: a `typeScale` role IS a
|
|
235
|
+
* rung, so it wins its own stop and leaves the others to the ramp — a harvest
|
|
236
|
+
* that measured only `h1` and `body` must not blank the rest. A `rhythm.base`
|
|
237
|
+
* is a statement about the SCALE, and every real document already carries
|
|
238
|
+
* spacing stops (the base theme supplies them), so "stated stop wins" would
|
|
239
|
+
* make the member inert on every document a harvest can produce.
|
|
240
|
+
*
|
|
241
|
+
* Nothing is lost by it: a document needing one bespoke step states no
|
|
242
|
+
* rhythm, and an app needing one pinned value has `AppTheme.cssVariables`,
|
|
243
|
+
* which overrides the whole projection at the overlay layer.
|
|
244
|
+
*
|
|
245
|
+
* CONTRACTED AHEAD OF THEIR CONSUMERS — read this before assuming a stated
|
|
246
|
+
* member changes a pixel. A member may be contracted before its consumer
|
|
247
|
+
* exists; a VARIABLE may not enter the consumed-token manifest before its
|
|
248
|
+
* consumer exists (a name nothing reads is coverage debt, not a feature).
|
|
249
|
+
* Today that makes three of these members STATED, STORED and NOT YET
|
|
250
|
+
* VISIBLE: the role-named type families and `scrim` arrive with their
|
|
251
|
+
* consumers (ggui#1075 Track C (b), ggui#1083), and `motion` with
|
|
252
|
+
* **ggui#1106** — the card's motion is build-time constants in the
|
|
253
|
+
* primitives, so no `--ggui-motion-*` variable exists to project onto yet
|
|
254
|
+
* (the manifest carries 122 tokens and none of them is motion). A writer
|
|
255
|
+
* may declare all of them now, which is the point of contracting first;
|
|
256
|
+
* nobody should read the contract as shipping the behaviour.
|
|
257
|
+
*/
|
|
258
|
+
/** The five type roles; `display` is the poster-scale role a host page leads with. */
|
|
259
|
+
const TypeScaleGroup = z.strictObject({
|
|
260
|
+
display: TypeScaleRole.optional(),
|
|
261
|
+
h1: TypeScaleRole.optional(),
|
|
262
|
+
h2: TypeScaleRole.optional(),
|
|
263
|
+
body: TypeScaleRole.optional(),
|
|
264
|
+
label: TypeScaleRole.optional(),
|
|
265
|
+
});
|
|
266
|
+
/**
|
|
267
|
+
* The spacing base a rhythm re-derives its scale from: a positive length in
|
|
268
|
+
* `px` (2–16) or `rem` / `em` (0.125–1, the same range at a 16px root). Other
|
|
269
|
+
* units and `calc()` are refused at the door — a base the reader cannot
|
|
270
|
+
* multiply is not a base.
|
|
271
|
+
*/
|
|
272
|
+
const RHYTHM_BASE_RE = /^(\d+(?:\.\d+)?)(px|rem|em)$/;
|
|
273
|
+
function isRhythmBase(value) {
|
|
274
|
+
const m = RHYTHM_BASE_RE.exec(value);
|
|
275
|
+
if (m === null)
|
|
276
|
+
return false;
|
|
277
|
+
const n = Number(m[1]);
|
|
278
|
+
return m[2] === 'px' ? n >= 2 && n <= 16 : n >= 0.125 && n <= 1;
|
|
279
|
+
}
|
|
280
|
+
const RhythmBaseToken = DimensionToken.refine((t) => isRhythmBase(t.$value), 'rhythm.base must be a length in px (2–16) or rem/em (0.125–1)');
|
|
281
|
+
/** `base` re-derives the spacing scale; `section` and `inset` are named steps. */
|
|
282
|
+
const RhythmGroup = z.strictObject({
|
|
283
|
+
base: RhythmBaseToken,
|
|
284
|
+
section: DimensionToken.optional(),
|
|
285
|
+
inset: DimensionToken.optional(),
|
|
286
|
+
});
|
|
287
|
+
/** The CSS easing keywords a document may name. */
|
|
288
|
+
const CSS_EASING_KEYWORDS = new Set(['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out', 'step-start', 'step-end']);
|
|
289
|
+
const CUBIC_BEZIER_RE = /^cubic-bezier\(\s*-?\d*\.?\d+\s*(?:,\s*-?\d*\.?\d+\s*){3}\)$/;
|
|
290
|
+
const STEPS_RE = /^steps\(\s*\d+\s*(?:,\s*(?:jump-start|jump-end|jump-none|jump-both|start|end)\s*)?\)$/;
|
|
291
|
+
/**
|
|
292
|
+
* An easing a projection can emit verbatim: a CSS keyword, a well-formed
|
|
293
|
+
* `cubic-bezier(a,b,c,d)`, or `steps(n[, position])`. Validated HERE so no
|
|
294
|
+
* reader has to clamp a string it cannot trust.
|
|
295
|
+
*/
|
|
296
|
+
function isCssEasing(value) {
|
|
297
|
+
return CSS_EASING_KEYWORDS.has(value) || CUBIC_BEZIER_RE.test(value) || STEPS_RE.test(value);
|
|
298
|
+
}
|
|
299
|
+
const EasingToken = z.strictObject({
|
|
300
|
+
$type: z.string().min(1),
|
|
301
|
+
$value: z.string().min(1).refine(isCssEasing, 'easing must be a CSS keyword, cubic-bezier(a,b,c,d) or steps(n[, position])'),
|
|
302
|
+
$description: z.string().optional(),
|
|
303
|
+
});
|
|
304
|
+
/**
|
|
305
|
+
* The per-app motion TEMPO OVERRIDE (ggui#1093 P1c; reverses the layer-1
|
|
306
|
+
* retirement of the theming spec §2.3, co-signed with rnd 2026-09-16).
|
|
307
|
+
*
|
|
308
|
+
* Layer-1 keeps the motion SCALE — the durations and easings the design
|
|
309
|
+
* system ships, which every card gets by default and no app may redefine
|
|
310
|
+
* wholesale. What a document may state is a BOUNDED tempo override, for a
|
|
311
|
+
* card that has to sit inside someone else's site: exactly the standing
|
|
312
|
+
* `palette` has (per-app forever, without that making colour "not
|
|
313
|
+
* layer-1"). The retired FREE-MAP ladders stay refused — three duration
|
|
314
|
+
* steps and three easing roles are admitted, and nothing else.
|
|
315
|
+
*/
|
|
316
|
+
const MotionDurationGroup = z.strictObject({
|
|
317
|
+
fast: DurationToken.optional(),
|
|
318
|
+
base: DurationToken.optional(),
|
|
319
|
+
slow: DurationToken.optional(),
|
|
320
|
+
});
|
|
321
|
+
const MotionEasingGroup = z.strictObject({
|
|
322
|
+
standard: EasingToken.optional(),
|
|
323
|
+
emphasized: EasingToken.optional(),
|
|
324
|
+
exit: EasingToken.optional(),
|
|
325
|
+
});
|
|
326
|
+
/**
|
|
327
|
+
* The scrim a host page's ground shows through (ggui#1083): `tone` is a word
|
|
328
|
+
* (`light` / `dark`) or a stated colour token, `opacity` a 0..1 ratio, `blur` a
|
|
329
|
+
* length. Absent ⇒ the completion derives it from the ground pair, so a
|
|
330
|
+
* builder who declares nothing still gets a scrim that matches their page.
|
|
331
|
+
*/
|
|
332
|
+
const ScrimGroup = z.strictObject({
|
|
333
|
+
tone: z.union([z.enum(['light', 'dark']), ColorToken]),
|
|
334
|
+
opacity: NumberToken.refine((t) => t.$value >= 0 && t.$value <= 1, 'scrim.opacity must be a ratio between 0 and 1'),
|
|
335
|
+
blur: DimensionToken,
|
|
336
|
+
});
|
|
225
337
|
const MotionGroup = z.strictObject({
|
|
226
338
|
transition: z.record(z.string(), TransitionToken),
|
|
227
339
|
keyframes: z.record(z.string(), StringToken).optional(),
|
|
340
|
+
/** Per-app tempo override — three steps, bounded (ggui#1093 P1c). */
|
|
341
|
+
duration: MotionDurationGroup.optional(),
|
|
342
|
+
/** Per-app easing override — three roles, each value validated at the door (ggui#1093 P1c). */
|
|
343
|
+
easing: MotionEasingGroup.optional(),
|
|
344
|
+
/**
|
|
345
|
+
* What the composer does under `prefers-reduced-motion: reduce`.
|
|
346
|
+
*
|
|
347
|
+
* Two defaults that must never be read as disagreeing (rnd, ggui#1093
|
|
348
|
+
* P1c): the DOOR does not stamp a value — absent stays absent, so the
|
|
349
|
+
* document never carries something its author did not write — and the
|
|
350
|
+
* COMPOSER's default is `respect`: it emits the reduced-motion rule
|
|
351
|
+
* unless a document explicitly says `ignore`. Absence therefore means
|
|
352
|
+
* "respect" downstream, without the wire inventing it.
|
|
353
|
+
*/
|
|
354
|
+
reduce: z.enum(['respect', 'ignore']).optional(),
|
|
228
355
|
});
|
|
229
356
|
const AccessibilityGroup = z.strictObject({
|
|
230
357
|
focusRing: z
|
|
@@ -305,9 +432,23 @@ export const ThemeDocumentV2 = z.strictObject({
|
|
|
305
432
|
typography: TypographyGroup.optional(),
|
|
306
433
|
/** Shape tokens — `radius` + `shadow`. Required. */
|
|
307
434
|
shape: ShapeGroup,
|
|
308
|
-
/**
|
|
309
|
-
* `
|
|
435
|
+
/** The five type ROLES of the host's design language (ggui#1093 P1b):
|
|
436
|
+
* display / h1 / h2 / body / label, each `size` + optional weight,
|
|
437
|
+
* tracking and a RATIO leading. Role-based, beside `font.ramp`'s
|
|
438
|
+
* size-stop ladder — the projection reads a role when it is stated and
|
|
439
|
+
* falls to the ramp when it is not. Optional. */
|
|
440
|
+
typeScale: TypeScaleGroup.optional(),
|
|
441
|
+
/** The host's spacing rhythm (ggui#1093 P1b): `base` re-derives the scale,
|
|
442
|
+
* `section` / `inset` are named steps. Optional. */
|
|
443
|
+
rhythm: RhythmGroup.optional(),
|
|
444
|
+
/** Motion tokens — `transition` (required sub-record), `keyframes`, and
|
|
445
|
+
* the bounded per-app TEMPO OVERRIDE (ggui#1093 P1c): `duration`
|
|
446
|
+
* (fast/base/slow), `easing` (standard/emphasized/exit) and `reduce`.
|
|
447
|
+
* The motion SCALE stays layer-1; the free-map ladders stay refused. */
|
|
310
448
|
motion: MotionGroup.optional(),
|
|
449
|
+
/** The scrim between the host page's ground and the card (ggui#1093 P1b,
|
|
450
|
+
* ggui#1083). Absent ⇒ derived from the ground pair. Optional. */
|
|
451
|
+
scrim: ScrimGroup.optional(),
|
|
311
452
|
/** Accessibility tokens (focus ring, reduced motion, high contrast).
|
|
312
453
|
* Optional. */
|
|
313
454
|
accessibility: AccessibilityGroup.optional(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ggui-ai/project-config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.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.17.0",
|
|
37
|
+
"@ggui-ai/protocol": "0.17.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^24.0.0",
|