@velumo/core 0.1.0-beta.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.
Files changed (53) hide show
  1. package/dist/background-validation.d.ts +3 -0
  2. package/dist/background-validation.d.ts.map +1 -0
  3. package/dist/background-validation.js +177 -0
  4. package/dist/background-validation.js.map +1 -0
  5. package/dist/builders.d.ts +134 -0
  6. package/dist/builders.d.ts.map +1 -0
  7. package/dist/builders.js +332 -0
  8. package/dist/builders.js.map +1 -0
  9. package/dist/capabilities.d.ts +7 -0
  10. package/dist/capabilities.d.ts.map +1 -0
  11. package/dist/capabilities.js +59 -0
  12. package/dist/capabilities.js.map +1 -0
  13. package/dist/index.d.ts +9 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +7 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/layout-validation.d.ts +4 -0
  18. package/dist/layout-validation.d.ts.map +1 -0
  19. package/dist/layout-validation.js +745 -0
  20. package/dist/layout-validation.js.map +1 -0
  21. package/dist/manifest-schema.d.ts +1328 -0
  22. package/dist/manifest-schema.d.ts.map +1 -0
  23. package/dist/manifest-schema.js +774 -0
  24. package/dist/manifest-schema.js.map +1 -0
  25. package/dist/theme-validation.d.ts +3 -0
  26. package/dist/theme-validation.d.ts.map +1 -0
  27. package/dist/theme-validation.js +239 -0
  28. package/dist/theme-validation.js.map +1 -0
  29. package/dist/tsconfig.tsbuildinfo +1 -0
  30. package/dist/types.d.ts +398 -0
  31. package/dist/types.d.ts.map +1 -0
  32. package/dist/types.js +2 -0
  33. package/dist/types.js.map +1 -0
  34. package/dist/validation-issue.d.ts +18 -0
  35. package/dist/validation-issue.d.ts.map +1 -0
  36. package/dist/validation-issue.js +30 -0
  37. package/dist/validation-issue.js.map +1 -0
  38. package/dist/validation.d.ts +4 -0
  39. package/dist/validation.d.ts.map +1 -0
  40. package/dist/validation.js +274 -0
  41. package/dist/validation.js.map +1 -0
  42. package/package.json +16 -0
  43. package/src/background-validation.ts +315 -0
  44. package/src/builders.ts +626 -0
  45. package/src/capabilities.ts +99 -0
  46. package/src/index.ts +147 -0
  47. package/src/layout-validation.ts +1385 -0
  48. package/src/manifest-schema.ts +823 -0
  49. package/src/theme-validation.ts +410 -0
  50. package/src/types.ts +560 -0
  51. package/src/validation-issue.ts +43 -0
  52. package/src/validation.ts +468 -0
  53. package/tsconfig.json +9 -0
@@ -0,0 +1,823 @@
1
+ /**
2
+ * JSON Schema (draft 2020-12) derived by hand from the `Manifest` TypeScript
3
+ * type in `types.ts`. This is the published, machine-readable form of the
4
+ * manifest contract — the artifact an agent or MCP server validates against.
5
+ *
6
+ * It is intentionally a richer, stricter mirror of the type than the
7
+ * lightweight runtime `validateManifest()`: it requires the `kind`
8
+ * discriminator on every slide and layout node, enforces every enum, and
9
+ * forbids unknown fields. The two are kept in sync by a drift test that runs a
10
+ * canonical manifest through both and asserts they agree.
11
+ *
12
+ * No schema library lives in `@velumo/core`: this is a plain, serializable
13
+ * object. JSON Schema execution (ajv) is confined to the test suite.
14
+ */
15
+
16
+ const jsonValue = {
17
+ $comment: "Any JSON value (string, number, boolean, null, object, array).",
18
+ };
19
+
20
+ const cameraVector = {
21
+ type: "object",
22
+ properties: {
23
+ x: { type: "number" },
24
+ y: { type: "number" },
25
+ z: { type: "number" },
26
+ },
27
+ additionalProperties: false,
28
+ };
29
+
30
+ const layoutMeta = {
31
+ type: "object",
32
+ properties: {
33
+ safeArea: { enum: ["default", "full-bleed", "title-safe"] },
34
+ depth: { type: "number" },
35
+ cameraHint: { enum: ["static", "push", "pan", "orbit"] },
36
+ },
37
+ additionalProperties: false,
38
+ };
39
+
40
+ const stringOrNumber = { type: ["string", "number"] };
41
+
42
+ const layoutChildren = {
43
+ type: "array",
44
+ items: { $ref: "#/$defs/layoutNode" },
45
+ };
46
+
47
+ export const manifestJsonSchema = {
48
+ $schema: "https://json-schema.org/draft/2020-12/schema",
49
+ $id: "https://velumo.dev/schemas/manifest.json",
50
+ title: "Velumo Manifest",
51
+ type: "object",
52
+ required: ["deck", "slides"],
53
+ additionalProperties: false,
54
+ properties: {
55
+ deck: { $ref: "#/$defs/deckMetadata" },
56
+ slides: { type: "array", items: { $ref: "#/$defs/slideEntry" } },
57
+ assets: { type: "array", items: { $ref: "#/$defs/asset" } },
58
+ plugins: { type: "array", items: { $ref: "#/$defs/plugin" } },
59
+ theme: { $ref: "#/$defs/theme" },
60
+ export: { $ref: "#/$defs/exportOptions" },
61
+ },
62
+ $defs: {
63
+ deckMetadata: {
64
+ type: "object",
65
+ required: ["title"],
66
+ properties: {
67
+ title: { type: "string", minLength: 1 },
68
+ description: { type: "string" },
69
+ author: { type: "string" },
70
+ watermark: { type: "string" },
71
+ },
72
+ additionalProperties: jsonValue,
73
+ },
74
+
75
+ slideEntry: {
76
+ oneOf: [{ $ref: "#/$defs/slide" }, { $ref: "#/$defs/scene" }],
77
+ },
78
+
79
+ slide: {
80
+ type: "object",
81
+ required: ["id", "kind"],
82
+ additionalProperties: false,
83
+ properties: {
84
+ id: { type: "string", minLength: 1 },
85
+ kind: { const: "slide" },
86
+ content: jsonValue,
87
+ layout: { $ref: "#/$defs/layoutNode" },
88
+ layers: { type: "array", items: { $ref: "#/$defs/layer" } },
89
+ fragments: { type: "array", items: { $ref: "#/$defs/fragment" } },
90
+ cues: { type: "array", items: { $ref: "#/$defs/cue" } },
91
+ notes: { type: "string" },
92
+ mood: { type: "string" },
93
+ transition: { type: "string" },
94
+ chrome: { type: "boolean" },
95
+ background: { $ref: "#/$defs/background" },
96
+ },
97
+ },
98
+
99
+ scene: {
100
+ type: "object",
101
+ required: ["id", "kind"],
102
+ additionalProperties: false,
103
+ properties: {
104
+ id: { type: "string", minLength: 1 },
105
+ kind: { const: "scene" },
106
+ duration: { type: "number" },
107
+ camera: { $ref: "#/$defs/camera" },
108
+ cameraPath: {
109
+ type: "array",
110
+ minItems: 1,
111
+ items: { $ref: "#/$defs/cameraStop" },
112
+ },
113
+ layout: { $ref: "#/$defs/layoutNode" },
114
+ layers: { type: "array", items: { $ref: "#/$defs/layer" } },
115
+ fragments: { type: "array", items: { $ref: "#/$defs/fragment" } },
116
+ cues: { type: "array", items: { $ref: "#/$defs/cue" } },
117
+ notes: { type: "string" },
118
+ mood: { type: "string" },
119
+ transition: { type: "string" },
120
+ chrome: { type: "boolean" },
121
+ background: { $ref: "#/$defs/background" },
122
+ },
123
+ },
124
+
125
+ camera: {
126
+ type: "object",
127
+ properties: {
128
+ from: cameraVector,
129
+ to: cameraVector,
130
+ zoom: { type: "number" },
131
+ },
132
+ additionalProperties: true,
133
+ },
134
+
135
+ cameraView: {
136
+ type: "object",
137
+ required: ["x", "y"],
138
+ additionalProperties: false,
139
+ properties: {
140
+ x: { type: "number", minimum: 0, maximum: 100 },
141
+ y: { type: "number", minimum: 0, maximum: 100 },
142
+ zoom: { type: "number", exclusiveMinimum: 0 },
143
+ },
144
+ },
145
+ cameraTransition: {
146
+ type: "object",
147
+ additionalProperties: false,
148
+ properties: {
149
+ duration: { type: "number", minimum: 0 },
150
+ easing: {
151
+ enum: ["linear", "ease", "ease-in", "ease-out", "ease-in-out"],
152
+ },
153
+ },
154
+ },
155
+ cameraStop: {
156
+ type: "object",
157
+ required: ["camera"],
158
+ additionalProperties: false,
159
+ properties: {
160
+ camera: { $ref: "#/$defs/cameraView" },
161
+ transition: { $ref: "#/$defs/cameraTransition" },
162
+ cues: { type: "array", items: { $ref: "#/$defs/cue" } },
163
+ hold: { type: "number", minimum: 0 },
164
+ },
165
+ },
166
+
167
+ layer: {
168
+ type: "object",
169
+ required: ["type"],
170
+ additionalProperties: false,
171
+ properties: {
172
+ id: { type: "string" },
173
+ type: { type: "string", minLength: 1 },
174
+ content: jsonValue,
175
+ props: { type: "object", additionalProperties: jsonValue },
176
+ timing: { type: "object", additionalProperties: jsonValue },
177
+ visible: { type: "boolean" },
178
+ },
179
+ },
180
+
181
+ fragment: {
182
+ type: "object",
183
+ required: ["id"],
184
+ additionalProperties: false,
185
+ properties: {
186
+ id: { type: "string", minLength: 1 },
187
+ layerId: { type: "string" },
188
+ transition: { type: "string" },
189
+ cues: { type: "array", items: { type: "string" } },
190
+ },
191
+ },
192
+
193
+ cue: {
194
+ type: "object",
195
+ required: ["time", "action"],
196
+ additionalProperties: false,
197
+ properties: {
198
+ time: { type: "number", minimum: 0 },
199
+ action: { $ref: "#/$defs/cueAction" },
200
+ },
201
+ },
202
+
203
+ cueAction: {
204
+ type: "object",
205
+ required: ["type"],
206
+ additionalProperties: false,
207
+ properties: {
208
+ type: { type: "string", minLength: 1 },
209
+ target: { type: "string" },
210
+ payload: jsonValue,
211
+ },
212
+ },
213
+
214
+ layoutNode: {
215
+ oneOf: [
216
+ { $ref: "#/$defs/stackLayout" },
217
+ { $ref: "#/$defs/gridLayout" },
218
+ { $ref: "#/$defs/splitLayout" },
219
+ { $ref: "#/$defs/heroLayout" },
220
+ { $ref: "#/$defs/frameLayout" },
221
+ { $ref: "#/$defs/canvasLayout" },
222
+ { $ref: "#/$defs/animateLayout" },
223
+ { $ref: "#/$defs/headingElement" },
224
+ { $ref: "#/$defs/textElement" },
225
+ { $ref: "#/$defs/mediaElement" },
226
+ { $ref: "#/$defs/calloutElement" },
227
+ { $ref: "#/$defs/codeElement" },
228
+ { $ref: "#/$defs/iconElement" },
229
+ { $ref: "#/$defs/tableLayout" },
230
+ { $ref: "#/$defs/connectorElement" },
231
+ { $ref: "#/$defs/badgeElement" },
232
+ { $ref: "#/$defs/ruleElement" },
233
+ ],
234
+ },
235
+
236
+ stackLayout: {
237
+ type: "object",
238
+ required: ["kind", "children"],
239
+ additionalProperties: false,
240
+ properties: {
241
+ kind: { const: "stack" },
242
+ children: layoutChildren,
243
+ direction: { enum: ["vertical", "horizontal"] },
244
+ align: { enum: ["start", "center", "end", "stretch"] },
245
+ gap: { type: "string" },
246
+ justify: { enum: ["start", "center", "between", "end"] },
247
+ meta: layoutMeta,
248
+ },
249
+ },
250
+
251
+ gridLayout: {
252
+ type: "object",
253
+ required: ["kind", "children"],
254
+ additionalProperties: false,
255
+ properties: {
256
+ kind: { const: "grid" },
257
+ children: layoutChildren,
258
+ columns: { type: "integer", minimum: 1 },
259
+ rows: { type: "integer", minimum: 1 },
260
+ meta: layoutMeta,
261
+ },
262
+ },
263
+
264
+ splitLayout: {
265
+ type: "object",
266
+ required: ["kind", "children"],
267
+ additionalProperties: false,
268
+ properties: {
269
+ kind: { const: "split" },
270
+ children: {
271
+ type: "array",
272
+ minItems: 2,
273
+ items: { $ref: "#/$defs/layoutNode" },
274
+ },
275
+ weights: {
276
+ type: "array",
277
+ items: { type: "number", exclusiveMinimum: 0 },
278
+ },
279
+ meta: layoutMeta,
280
+ },
281
+ },
282
+
283
+ heroLayout: {
284
+ type: "object",
285
+ required: ["kind", "children"],
286
+ additionalProperties: false,
287
+ properties: {
288
+ kind: { const: "hero" },
289
+ children: {
290
+ type: "array",
291
+ minItems: 1,
292
+ items: { $ref: "#/$defs/layoutNode" },
293
+ },
294
+ meta: layoutMeta,
295
+ },
296
+ },
297
+
298
+ frameLayout: {
299
+ type: "object",
300
+ required: ["kind", "children"],
301
+ additionalProperties: false,
302
+ properties: {
303
+ kind: { const: "frame" },
304
+ children: layoutChildren,
305
+ meta: layoutMeta,
306
+ tone: {
307
+ enum: [
308
+ "default",
309
+ "muted",
310
+ "accent",
311
+ "danger",
312
+ "magenta",
313
+ "cyan",
314
+ "purple",
315
+ "green",
316
+ ],
317
+ },
318
+ },
319
+ },
320
+
321
+ canvasLayout: {
322
+ type: "object",
323
+ required: ["kind", "items"],
324
+ additionalProperties: false,
325
+ properties: {
326
+ kind: { const: "canvas" },
327
+ items: { type: "array", items: { $ref: "#/$defs/canvasItem" } },
328
+ meta: layoutMeta,
329
+ },
330
+ },
331
+
332
+ canvasItem: {
333
+ type: "object",
334
+ required: ["x", "y", "child"],
335
+ additionalProperties: false,
336
+ properties: {
337
+ x: { type: "number", minimum: 0, maximum: 100 },
338
+ y: { type: "number", minimum: 0, maximum: 100 },
339
+ width: { type: "number", minimum: 0, maximum: 100 },
340
+ height: { type: "number", minimum: 0, maximum: 100 },
341
+ child: { $ref: "#/$defs/layoutNode" },
342
+ },
343
+ },
344
+
345
+ animateLayout: {
346
+ type: "object",
347
+ required: ["kind", "name", "child"],
348
+ additionalProperties: false,
349
+ properties: {
350
+ kind: { const: "animate" },
351
+ name: {
352
+ enum: [
353
+ "rise",
354
+ "fade",
355
+ "zoom",
356
+ "draw",
357
+ "in-left",
358
+ "in-right",
359
+ "glow",
360
+ "blink",
361
+ "pulse",
362
+ "sweep",
363
+ ],
364
+ },
365
+ delay: { type: "number", minimum: 0 },
366
+ duration: { type: "number", exclusiveMinimum: 0 },
367
+ easing: { type: "string", minLength: 1 },
368
+ child: { $ref: "#/$defs/layoutNode" },
369
+ },
370
+ },
371
+
372
+ headingElement: {
373
+ type: "object",
374
+ required: ["kind", "text"],
375
+ additionalProperties: false,
376
+ properties: {
377
+ kind: { const: "heading" },
378
+ text: { type: "string", minLength: 1 },
379
+ size: { enum: ["default", "display", "hero"] },
380
+ level: { type: "integer", minimum: 1, maximum: 6 },
381
+ transform: { enum: ["uppercase", "lowercase", "capitalize"] },
382
+ decoration: { enum: ["underline", "line-through"] },
383
+ glow: { type: "boolean" },
384
+ tone: {
385
+ enum: [
386
+ "default",
387
+ "muted",
388
+ "accent",
389
+ "danger",
390
+ "magenta",
391
+ "cyan",
392
+ "purple",
393
+ "green",
394
+ ],
395
+ },
396
+ },
397
+ },
398
+
399
+ textElement: {
400
+ type: "object",
401
+ required: ["kind", "text"],
402
+ additionalProperties: false,
403
+ properties: {
404
+ kind: { const: "text" },
405
+ text: { type: "string" },
406
+ variant: { enum: ["body", "lead", "caption"] },
407
+ transform: { enum: ["uppercase", "lowercase", "capitalize"] },
408
+ decoration: { enum: ["underline", "line-through"] },
409
+ glow: { type: "boolean" },
410
+ tone: {
411
+ enum: [
412
+ "default",
413
+ "muted",
414
+ "accent",
415
+ "danger",
416
+ "magenta",
417
+ "cyan",
418
+ "purple",
419
+ "green",
420
+ ],
421
+ },
422
+ },
423
+ },
424
+
425
+ mediaElement: {
426
+ type: "object",
427
+ required: ["kind", "src", "alt"],
428
+ additionalProperties: false,
429
+ properties: {
430
+ kind: { const: "media" },
431
+ src: { type: "string", minLength: 1 },
432
+ alt: { type: "string", minLength: 1 },
433
+ fit: { enum: ["contain", "cover", "fill"] },
434
+ },
435
+ },
436
+
437
+ calloutElement: {
438
+ type: "object",
439
+ required: ["kind", "children"],
440
+ additionalProperties: false,
441
+ properties: {
442
+ kind: { const: "callout" },
443
+ tone: { enum: ["info", "warning", "success", "danger", "quote"] },
444
+ children: layoutChildren,
445
+ },
446
+ },
447
+
448
+ codeElement: {
449
+ type: "object",
450
+ required: ["kind", "lines"],
451
+ additionalProperties: false,
452
+ properties: {
453
+ kind: { const: "code" },
454
+ lines: {
455
+ type: "array",
456
+ items: { type: "array", items: { $ref: "#/$defs/codeToken" } },
457
+ },
458
+ },
459
+ },
460
+
461
+ iconElement: {
462
+ type: "object",
463
+ required: ["kind", "name"],
464
+ additionalProperties: false,
465
+ properties: {
466
+ kind: { const: "icon" },
467
+ name: { enum: ["check", "cross", "dash", "ring", "dot", "arrow"] },
468
+ tone: {
469
+ enum: [
470
+ "default",
471
+ "muted",
472
+ "accent",
473
+ "danger",
474
+ "magenta",
475
+ "cyan",
476
+ "purple",
477
+ "green",
478
+ ],
479
+ },
480
+ label: { type: "string" },
481
+ },
482
+ },
483
+
484
+ badgeElement: {
485
+ type: "object",
486
+ required: ["kind", "text"],
487
+ additionalProperties: false,
488
+ properties: {
489
+ kind: { const: "badge" },
490
+ text: { type: "string" },
491
+ tone: {
492
+ enum: [
493
+ "default",
494
+ "muted",
495
+ "accent",
496
+ "danger",
497
+ "magenta",
498
+ "cyan",
499
+ "purple",
500
+ "green",
501
+ ],
502
+ },
503
+ variant: { enum: ["outline", "soft", "solid"] },
504
+ dot: {
505
+ enum: [
506
+ "default",
507
+ "muted",
508
+ "accent",
509
+ "danger",
510
+ "magenta",
511
+ "cyan",
512
+ "purple",
513
+ "green",
514
+ ],
515
+ },
516
+ mono: { type: "boolean" },
517
+ },
518
+ },
519
+
520
+ ruleElement: {
521
+ type: "object",
522
+ required: ["kind"],
523
+ additionalProperties: false,
524
+ properties: {
525
+ kind: { const: "rule" },
526
+ length: { type: ["string", "number"] },
527
+ thickness: { type: "number" },
528
+ tone: {
529
+ enum: [
530
+ "default",
531
+ "muted",
532
+ "accent",
533
+ "danger",
534
+ "magenta",
535
+ "cyan",
536
+ "purple",
537
+ "green",
538
+ ],
539
+ },
540
+ gradient: {
541
+ type: "array",
542
+ minItems: 2,
543
+ maxItems: 2,
544
+ items: {
545
+ enum: [
546
+ "default",
547
+ "muted",
548
+ "accent",
549
+ "danger",
550
+ "magenta",
551
+ "cyan",
552
+ "purple",
553
+ "green",
554
+ ],
555
+ },
556
+ },
557
+ radius: { type: "number" },
558
+ },
559
+ },
560
+
561
+ tableColumn: {
562
+ type: "object",
563
+ additionalProperties: false,
564
+ properties: {
565
+ header: { $ref: "#/$defs/layoutNode" },
566
+ weight: { type: "number", exclusiveMinimum: 0 },
567
+ align: { enum: ["start", "center", "end"] },
568
+ emphasis: { type: "boolean" },
569
+ },
570
+ },
571
+
572
+ tableLayout: {
573
+ type: "object",
574
+ required: ["kind", "columns", "rows"],
575
+ additionalProperties: false,
576
+ properties: {
577
+ kind: { const: "table" },
578
+ columns: {
579
+ type: "array",
580
+ minItems: 1,
581
+ items: { $ref: "#/$defs/tableColumn" },
582
+ },
583
+ rows: {
584
+ type: "array",
585
+ items: { type: "array", items: { $ref: "#/$defs/layoutNode" } },
586
+ },
587
+ meta: layoutMeta,
588
+ },
589
+ },
590
+
591
+ connectorPoint: {
592
+ type: "object",
593
+ required: ["x", "y"],
594
+ additionalProperties: false,
595
+ properties: {
596
+ x: { type: "number", minimum: 0, maximum: 100 },
597
+ y: { type: "number", minimum: 0, maximum: 100 },
598
+ },
599
+ },
600
+
601
+ connectorElement: {
602
+ type: "object",
603
+ required: ["kind", "from", "to"],
604
+ additionalProperties: false,
605
+ properties: {
606
+ kind: { const: "connector" },
607
+ from: { $ref: "#/$defs/connectorPoint" },
608
+ to: { $ref: "#/$defs/connectorPoint" },
609
+ control: { $ref: "#/$defs/connectorPoint" },
610
+ arrow: { enum: ["none", "end", "start", "both"] },
611
+ dashed: { type: "boolean" },
612
+ tone: {
613
+ enum: [
614
+ "default",
615
+ "muted",
616
+ "accent",
617
+ "danger",
618
+ "magenta",
619
+ "cyan",
620
+ "purple",
621
+ "green",
622
+ ],
623
+ },
624
+ },
625
+ },
626
+
627
+ codeToken: {
628
+ type: "object",
629
+ required: ["text"],
630
+ additionalProperties: false,
631
+ properties: {
632
+ text: { type: "string" },
633
+ kind: {
634
+ enum: [
635
+ "keyword",
636
+ "function",
637
+ "string",
638
+ "number",
639
+ "property",
640
+ "comment",
641
+ "punctuation",
642
+ "plain",
643
+ ],
644
+ },
645
+ },
646
+ },
647
+
648
+ asset: {
649
+ type: "object",
650
+ required: ["id", "type", "src"],
651
+ additionalProperties: false,
652
+ properties: {
653
+ id: { type: "string", minLength: 1 },
654
+ type: { type: "string", minLength: 1 },
655
+ src: { type: "string", minLength: 1 },
656
+ metadata: { type: "object", additionalProperties: jsonValue },
657
+ },
658
+ },
659
+
660
+ plugin: {
661
+ type: "object",
662
+ required: ["id"],
663
+ additionalProperties: false,
664
+ properties: {
665
+ id: { type: "string", minLength: 1 },
666
+ packageName: { type: "string" },
667
+ options: { type: "object", additionalProperties: jsonValue },
668
+ },
669
+ },
670
+
671
+ exportOptions: {
672
+ type: "object",
673
+ additionalProperties: false,
674
+ properties: {
675
+ formats: { type: "array", items: { type: "string" } },
676
+ metadata: { type: "object", additionalProperties: jsonValue },
677
+ },
678
+ },
679
+
680
+ theme: {
681
+ type: "object",
682
+ required: ["id"],
683
+ additionalProperties: false,
684
+ properties: {
685
+ id: { type: "string", minLength: 1 },
686
+ tokens: { $ref: "#/$defs/themeTokens" },
687
+ css: { type: "string" },
688
+ fonts: {
689
+ type: "array",
690
+ items: { $ref: "#/$defs/themeFontFace" },
691
+ },
692
+ },
693
+ },
694
+
695
+ themeFontFace: {
696
+ type: "object",
697
+ required: ["family", "source"],
698
+ additionalProperties: false,
699
+ properties: {
700
+ family: { type: "string", minLength: 1 },
701
+ source: { type: "string", minLength: 1 },
702
+ format: { type: "string" },
703
+ weight: stringOrNumber,
704
+ style: { type: "string" },
705
+ display: { type: "string" },
706
+ unicodeRange: { type: "string" },
707
+ stretch: { type: "string" },
708
+ },
709
+ },
710
+
711
+ themeTokens: {
712
+ type: "object",
713
+ additionalProperties: false,
714
+ properties: {
715
+ colors: { type: "object", additionalProperties: { type: "string" } },
716
+ typography: {
717
+ type: "object",
718
+ additionalProperties: { $ref: "#/$defs/typographyToken" },
719
+ },
720
+ spacing: { type: "object", additionalProperties: stringOrNumber },
721
+ radii: { type: "object", additionalProperties: stringOrNumber },
722
+ shadows: { type: "object", additionalProperties: { type: "string" } },
723
+ motion: {
724
+ type: "object",
725
+ additionalProperties: { $ref: "#/$defs/motionToken" },
726
+ },
727
+ surfaces: { type: "object", additionalProperties: { type: "string" } },
728
+ text: { type: "object", additionalProperties: { type: "string" } },
729
+ },
730
+ },
731
+
732
+ typographyToken: {
733
+ type: "object",
734
+ additionalProperties: false,
735
+ properties: {
736
+ fontFamily: { type: "string" },
737
+ fontSize: stringOrNumber,
738
+ lineHeight: stringOrNumber,
739
+ fontWeight: stringOrNumber,
740
+ letterSpacing: stringOrNumber,
741
+ },
742
+ },
743
+
744
+ motionToken: {
745
+ type: "object",
746
+ additionalProperties: false,
747
+ properties: {
748
+ duration: stringOrNumber,
749
+ easing: { type: "string" },
750
+ delay: stringOrNumber,
751
+ },
752
+ },
753
+
754
+ background: {
755
+ type: "object",
756
+ additionalProperties: false,
757
+ properties: {
758
+ color: { type: "string", minLength: 1 },
759
+ layers: { type: "array", items: { $ref: "#/$defs/backgroundLayer" } },
760
+ },
761
+ },
762
+
763
+ backgroundLayer: {
764
+ oneOf: [
765
+ { $ref: "#/$defs/linearGradientLayer" },
766
+ { $ref: "#/$defs/radialGradientLayer" },
767
+ ],
768
+ },
769
+
770
+ linearGradientLayer: {
771
+ type: "object",
772
+ required: ["type", "stops"],
773
+ additionalProperties: false,
774
+ properties: {
775
+ type: { const: "linear" },
776
+ stops: {
777
+ type: "array",
778
+ minItems: 1,
779
+ items: { $ref: "#/$defs/gradientStop" },
780
+ },
781
+ angle: { type: "number" },
782
+ blend: { enum: ["normal", "screen", "multiply", "overlay"] },
783
+ opacity: { type: "number", minimum: 0, maximum: 1 },
784
+ },
785
+ },
786
+
787
+ radialGradientLayer: {
788
+ type: "object",
789
+ required: ["type", "stops"],
790
+ additionalProperties: false,
791
+ properties: {
792
+ type: { const: "radial" },
793
+ stops: {
794
+ type: "array",
795
+ minItems: 1,
796
+ items: { $ref: "#/$defs/gradientStop" },
797
+ },
798
+ shape: { enum: ["circle", "ellipse"] },
799
+ position: {
800
+ type: "object",
801
+ required: ["x", "y"],
802
+ additionalProperties: false,
803
+ properties: {
804
+ x: { type: "number", minimum: 0, maximum: 100 },
805
+ y: { type: "number", minimum: 0, maximum: 100 },
806
+ },
807
+ },
808
+ blend: { enum: ["normal", "screen", "multiply", "overlay"] },
809
+ opacity: { type: "number", minimum: 0, maximum: 1 },
810
+ },
811
+ },
812
+
813
+ gradientStop: {
814
+ type: "object",
815
+ required: ["color"],
816
+ additionalProperties: false,
817
+ properties: {
818
+ color: { type: "string", minLength: 1 },
819
+ at: { type: "number", minimum: 0, maximum: 100 },
820
+ },
821
+ },
822
+ },
823
+ } as const;