velloo 0.0.0 → 0.2.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 (267) hide show
  1. package/BUN-LICENSE.md +91 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +9 -0
  4. package/README.md +288 -2
  5. package/THIRD-PARTY-NOTICES.md +467 -0
  6. package/assets/elsewhere-alps-9fqhv029.jpg +0 -0
  7. package/assets/elsewhere-bali-vp60g115.jpg +0 -0
  8. package/assets/elsewhere-forest-xgn23zrj.jpg +0 -0
  9. package/assets/elsewhere-japan-qs7qkbr2.jpg +0 -0
  10. package/assets/elsewhere-kyoto-map-n2bvkrya.svg +1 -0
  11. package/assets/elsewhere-kyoto-qemtn6nk.jpg +0 -0
  12. package/assets/elsewhere-room-g1gaw7cs.jpg +0 -0
  13. package/canvas/apple-touch-icon.png +0 -0
  14. package/canvas/assets/index-BCvDZiBo.css +2 -0
  15. package/canvas/assets/index-ClaBqcaS.js +28 -0
  16. package/canvas/assets/lucide-all-iIF-pmzN.js +1 -0
  17. package/canvas/assets/radix-C3HaOB-d.js +41 -0
  18. package/canvas/assets/react-D0Ec-185.js +9 -0
  19. package/canvas/assets/rolldown-runtime-hePW80VL.js +1 -0
  20. package/canvas/assets/vendor-CKmQ-Zst.js +53 -0
  21. package/canvas/favicon.ico +0 -0
  22. package/canvas/favicon.svg +12 -0
  23. package/canvas/fonts/OFL-1.1.txt +93 -0
  24. package/canvas/fonts/poppins-700.woff2 +0 -0
  25. package/canvas/icon-192.png +0 -0
  26. package/canvas/icon-512.png +0 -0
  27. package/canvas/index.html +22 -0
  28. package/canvas/site.webmanifest +11 -0
  29. package/chunk-08sv3k41.js +4 -0
  30. package/chunk-0fe83yzt.js +2 -0
  31. package/chunk-0t68szgr.js +3 -0
  32. package/chunk-1h55s5he.js +2 -0
  33. package/chunk-2c7jbx46.js +2 -0
  34. package/chunk-2smsg2ct.js +7 -0
  35. package/chunk-308ynsxd.js +2 -0
  36. package/chunk-3e8vez4d.js +1229 -0
  37. package/chunk-40xgcth8.js +63 -0
  38. package/chunk-4dn75m6t.js +3 -0
  39. package/chunk-4fj5tahv.js +3 -0
  40. package/chunk-6nqghjda.js +3 -0
  41. package/chunk-6tx68d90.js +3 -0
  42. package/chunk-6w6ch4ce.js +2 -0
  43. package/chunk-735ezz23.js +86 -0
  44. package/chunk-7eq7nx37.js +7 -0
  45. package/chunk-7eyb8yrf.js +6 -0
  46. package/chunk-80fjfyha.js +86 -0
  47. package/chunk-80r93t2e.js +4 -0
  48. package/chunk-94vx1yb0.js +3 -0
  49. package/chunk-9vg3yafz.js +3 -0
  50. package/chunk-a84n72m2.js +4 -0
  51. package/chunk-ampmdzz4.js +10 -0
  52. package/chunk-avns4dbf.js +2 -0
  53. package/chunk-ayrb3qnm.js +3 -0
  54. package/chunk-bwnvcncx.js +3 -0
  55. package/chunk-ccszg02q.js +5 -0
  56. package/chunk-cvn689d5.js +3 -0
  57. package/chunk-cxe6jjwj.js +3 -0
  58. package/chunk-dg6z4vdd.js +3 -0
  59. package/chunk-dgwjpbp9.js +3 -0
  60. package/chunk-dy41n60j.js +52 -0
  61. package/chunk-ek71z2fv.js +226 -0
  62. package/chunk-er1fxb7d.js +2 -0
  63. package/chunk-eyr8hrzf.js +2 -0
  64. package/chunk-fjbzk4sw.js +3 -0
  65. package/chunk-fsbsksga.js +3 -0
  66. package/chunk-h6gchare.js +3 -0
  67. package/chunk-hcb68zt7.js +85 -0
  68. package/chunk-hggap8fq.js +3 -0
  69. package/chunk-j11fyeqh.js +2 -0
  70. package/chunk-jb21jkp1.js +8 -0
  71. package/chunk-jfx9xjmr.js +18 -0
  72. package/chunk-jr0ss0mm.js +303 -0
  73. package/chunk-jrsknzjt.js +3 -0
  74. package/chunk-jv0xggh0.js +2 -0
  75. package/chunk-k17gfwq3.js +2 -0
  76. package/chunk-mh2pr7cq.js +5 -0
  77. package/chunk-mnapdp1y.js +2 -0
  78. package/chunk-mzhdrawd.js +3 -0
  79. package/chunk-n9gs6yx9.js +2 -0
  80. package/chunk-ngnymg2q.js +3 -0
  81. package/chunk-nwx5700b.js +3 -0
  82. package/chunk-pjtg3wnm.js +4 -0
  83. package/chunk-pp86v6m6.js +3 -0
  84. package/chunk-r22epxz2.js +3 -0
  85. package/chunk-rgasy862.js +5 -0
  86. package/chunk-rpwpeph5.js +3 -0
  87. package/chunk-s23gtejf.js +3 -0
  88. package/chunk-sjgqnykd.js +6 -0
  89. package/chunk-sn3x2m0m.js +6 -0
  90. package/chunk-t448rrfr.js +2 -0
  91. package/chunk-tdsk5wxa.js +451 -0
  92. package/chunk-v40fn08f.js +442 -0
  93. package/chunk-vbk1qhdz.js +3 -0
  94. package/chunk-vdkrbzh1.js +2 -0
  95. package/chunk-vgmr98cv.js +40 -0
  96. package/chunk-vsv8z729.js +6 -0
  97. package/chunk-x4f4k9pp.js +26 -0
  98. package/chunk-xt5ayg7g.js +4 -0
  99. package/chunk-ydhwv29b.js +244 -0
  100. package/chunk-zb6hc1y0.js +2 -0
  101. package/cli.js +3 -0
  102. package/launcher.cjs +97 -0
  103. package/package.json +58 -5
  104. package/pkgs/helpers/src/box.tsx +21 -0
  105. package/pkgs/helpers/src/cn.ts +32 -0
  106. package/pkgs/helpers/src/descriptors.ts +270 -0
  107. package/pkgs/helpers/src/divider.tsx +123 -0
  108. package/pkgs/helpers/src/gradient.tsx +90 -0
  109. package/pkgs/helpers/src/heading.tsx +20 -0
  110. package/pkgs/helpers/src/icon-data.ts +8009 -0
  111. package/pkgs/helpers/src/icon.tsx +53 -0
  112. package/pkgs/helpers/src/image.tsx +111 -0
  113. package/pkgs/helpers/src/index.ts +29 -0
  114. package/pkgs/helpers/src/layer.tsx +61 -0
  115. package/pkgs/helpers/src/lowering.ts +53 -0
  116. package/pkgs/helpers/src/paths.ts +47 -0
  117. package/pkgs/helpers/src/placeholder.tsx +88 -0
  118. package/pkgs/helpers/src/prose.tsx +42 -0
  119. package/pkgs/helpers/src/registry.ts +41 -0
  120. package/pkgs/helpers/src/svg.tsx +62 -0
  121. package/pkgs/helpers/src/text.tsx +16 -0
  122. package/pkgs/provider-antd/src/index.ts +78 -0
  123. package/pkgs/provider-antd/src/intro.ts +14 -0
  124. package/pkgs/provider-antd/src/manifest.ts +439 -0
  125. package/pkgs/provider-antd/src/overlays.ts +176 -0
  126. package/pkgs/provider-antd/src/registry.ts +104 -0
  127. package/pkgs/provider-antd/src/render-pass.ts +43 -0
  128. package/pkgs/provider-antd/src/tailwind-entry.css +21 -0
  129. package/pkgs/provider-antd/src/theme.ts +132 -0
  130. package/pkgs/provider-antd/src/version.ts +8 -0
  131. package/pkgs/provider-chakra/src/index.ts +80 -0
  132. package/pkgs/provider-chakra/src/intro.ts +14 -0
  133. package/pkgs/provider-chakra/src/manifest.ts +421 -0
  134. package/pkgs/provider-chakra/src/overlays.ts +246 -0
  135. package/pkgs/provider-chakra/src/registry.ts +227 -0
  136. package/pkgs/provider-chakra/src/render-pass.ts +55 -0
  137. package/pkgs/provider-chakra/src/tailwind-entry.css +21 -0
  138. package/pkgs/provider-chakra/src/theme.ts +180 -0
  139. package/pkgs/provider-chakra/src/version.ts +8 -0
  140. package/pkgs/provider-mui/src/index.ts +117 -0
  141. package/pkgs/provider-mui/src/intro.ts +12 -0
  142. package/pkgs/provider-mui/src/manifest.ts +263 -0
  143. package/pkgs/provider-mui/src/overlays.ts +124 -0
  144. package/pkgs/provider-mui/src/registry.ts +120 -0
  145. package/pkgs/provider-mui/src/render-pass.ts +36 -0
  146. package/pkgs/provider-mui/src/tailwind-entry.css +21 -0
  147. package/pkgs/provider-mui/src/theme.ts +129 -0
  148. package/pkgs/provider-mui/src/version.ts +8 -0
  149. package/pkgs/provider-none/src/components-inline.tsx +204 -0
  150. package/pkgs/provider-none/src/components.tsx +155 -0
  151. package/pkgs/provider-none/src/index.ts +57 -0
  152. package/pkgs/provider-none/src/intro.ts +18 -0
  153. package/pkgs/provider-none/src/manifest.ts +163 -0
  154. package/pkgs/provider-none/src/registry-inline.ts +36 -0
  155. package/pkgs/provider-none/src/registry.ts +39 -0
  156. package/pkgs/provider-none/src/tailwind-entry.css +57 -0
  157. package/pkgs/provider-none/src/version.ts +10 -0
  158. package/pkgs/schema/src/annotation.ts +120 -0
  159. package/pkgs/schema/src/asset.ts +66 -0
  160. package/pkgs/schema/src/board.ts +71 -0
  161. package/pkgs/schema/src/comment.ts +107 -0
  162. package/pkgs/schema/src/config.ts +185 -0
  163. package/pkgs/schema/src/css-sanitize.ts +56 -0
  164. package/pkgs/schema/src/extension.ts +105 -0
  165. package/pkgs/schema/src/fonts.ts +690 -0
  166. package/pkgs/schema/src/frame.ts +40 -0
  167. package/pkgs/schema/src/icon-name.ts +21 -0
  168. package/pkgs/schema/src/ids.ts +14 -0
  169. package/pkgs/schema/src/index.ts +169 -0
  170. package/pkgs/schema/src/migrate.ts +297 -0
  171. package/pkgs/schema/src/node.ts +229 -0
  172. package/pkgs/schema/src/paths.ts +28 -0
  173. package/pkgs/schema/src/repo.ts +134 -0
  174. package/pkgs/schema/src/screen.ts +30 -0
  175. package/pkgs/schema/src/snippet-resolve.ts +193 -0
  176. package/pkgs/schema/src/snippet.ts +104 -0
  177. package/pkgs/schema/src/svg-sanitize.ts +487 -0
  178. package/pkgs/schema/src/theme.ts +218 -0
  179. package/pkgs/schema/src/typeset.ts +693 -0
  180. package/pkgs/schema/src/validate-ids.ts +46 -0
  181. package/pkgs/schema/src/viewport.ts +8 -0
  182. package/pkgs/shadcn-snapshot/dist/manifest.json +8109 -0
  183. package/pkgs/shadcn-snapshot/src/components/canvas-portal.tsx +55 -0
  184. package/pkgs/shadcn-snapshot/src/components/ui/accordion.tsx +122 -0
  185. package/pkgs/shadcn-snapshot/src/components/ui/alert-dialog.tsx +164 -0
  186. package/pkgs/shadcn-snapshot/src/components/ui/alert.tsx +74 -0
  187. package/pkgs/shadcn-snapshot/src/components/ui/aspect-ratio.tsx +13 -0
  188. package/pkgs/shadcn-snapshot/src/components/ui/attachment.tsx +197 -0
  189. package/pkgs/shadcn-snapshot/src/components/ui/avatar.tsx +100 -0
  190. package/pkgs/shadcn-snapshot/src/components/ui/badge.tsx +50 -0
  191. package/pkgs/shadcn-snapshot/src/components/ui/breadcrumb.tsx +109 -0
  192. package/pkgs/shadcn-snapshot/src/components/ui/bubble.tsx +130 -0
  193. package/pkgs/shadcn-snapshot/src/components/ui/button-group.tsx +82 -0
  194. package/pkgs/shadcn-snapshot/src/components/ui/button.tsx +72 -0
  195. package/pkgs/shadcn-snapshot/src/components/ui/calendar.tsx +229 -0
  196. package/pkgs/shadcn-snapshot/src/components/ui/card.tsx +92 -0
  197. package/pkgs/shadcn-snapshot/src/components/ui/carousel.tsx +235 -0
  198. package/pkgs/shadcn-snapshot/src/components/ui/chart-option.ts +250 -0
  199. package/pkgs/shadcn-snapshot/src/components/ui/chart.tsx +177 -0
  200. package/pkgs/shadcn-snapshot/src/components/ui/checkbox.tsx +41 -0
  201. package/pkgs/shadcn-snapshot/src/components/ui/collapsible.tsx +32 -0
  202. package/pkgs/shadcn-snapshot/src/components/ui/combobox.tsx +196 -0
  203. package/pkgs/shadcn-snapshot/src/components/ui/context-menu.tsx +227 -0
  204. package/pkgs/shadcn-snapshot/src/components/ui/dialog.tsx +131 -0
  205. package/pkgs/shadcn-snapshot/src/components/ui/direction.tsx +24 -0
  206. package/pkgs/shadcn-snapshot/src/components/ui/drawer.tsx +112 -0
  207. package/pkgs/shadcn-snapshot/src/components/ui/dropdown-menu.tsx +205 -0
  208. package/pkgs/shadcn-snapshot/src/components/ui/empty.tsx +98 -0
  209. package/pkgs/shadcn-snapshot/src/components/ui/field.tsx +228 -0
  210. package/pkgs/shadcn-snapshot/src/components/ui/hover-card.tsx +51 -0
  211. package/pkgs/shadcn-snapshot/src/components/ui/input-group.tsx +148 -0
  212. package/pkgs/shadcn-snapshot/src/components/ui/input.tsx +43 -0
  213. package/pkgs/shadcn-snapshot/src/components/ui/item.tsx +187 -0
  214. package/pkgs/shadcn-snapshot/src/components/ui/kbd.tsx +30 -0
  215. package/pkgs/shadcn-snapshot/src/components/ui/label.tsx +25 -0
  216. package/pkgs/shadcn-snapshot/src/components/ui/marker.tsx +71 -0
  217. package/pkgs/shadcn-snapshot/src/components/ui/menubar.tsx +243 -0
  218. package/pkgs/shadcn-snapshot/src/components/ui/message.tsx +89 -0
  219. package/pkgs/shadcn-snapshot/src/components/ui/native-select.tsx +60 -0
  220. package/pkgs/shadcn-snapshot/src/components/ui/navigation-menu.tsx +165 -0
  221. package/pkgs/shadcn-snapshot/src/components/ui/pagination.tsx +117 -0
  222. package/pkgs/shadcn-snapshot/src/components/ui/popover.tsx +66 -0
  223. package/pkgs/shadcn-snapshot/src/components/ui/progress.tsx +35 -0
  224. package/pkgs/shadcn-snapshot/src/components/ui/radio-group.tsx +48 -0
  225. package/pkgs/shadcn-snapshot/src/components/ui/scroll-area.tsx +59 -0
  226. package/pkgs/shadcn-snapshot/src/components/ui/select.tsx +180 -0
  227. package/pkgs/shadcn-snapshot/src/components/ui/separator.tsx +32 -0
  228. package/pkgs/shadcn-snapshot/src/components/ui/sheet.tsx +107 -0
  229. package/pkgs/shadcn-snapshot/src/components/ui/skeleton.tsx +17 -0
  230. package/pkgs/shadcn-snapshot/src/components/ui/slider.tsx +68 -0
  231. package/pkgs/shadcn-snapshot/src/components/ui/sonner.tsx +91 -0
  232. package/pkgs/shadcn-snapshot/src/components/ui/spinner.tsx +21 -0
  233. package/pkgs/shadcn-snapshot/src/components/ui/switch.tsx +41 -0
  234. package/pkgs/shadcn-snapshot/src/components/ui/table.tsx +93 -0
  235. package/pkgs/shadcn-snapshot/src/components/ui/tabs.tsx +84 -0
  236. package/pkgs/shadcn-snapshot/src/components/ui/textarea.tsx +24 -0
  237. package/pkgs/shadcn-snapshot/src/components/ui/toggle-group.tsx +94 -0
  238. package/pkgs/shadcn-snapshot/src/components/ui/toggle.tsx +50 -0
  239. package/pkgs/shadcn-snapshot/src/components/ui/tooltip.tsx +47 -0
  240. package/pkgs/shadcn-snapshot/src/examples.ts +36 -0
  241. package/pkgs/shadcn-snapshot/src/groups.ts +87 -0
  242. package/pkgs/shadcn-snapshot/src/index.ts +43 -0
  243. package/pkgs/shadcn-snapshot/src/lib/utils.ts +4 -0
  244. package/pkgs/shadcn-snapshot/src/manifest.ts +12 -0
  245. package/pkgs/shadcn-snapshot/src/notes.ts +97 -0
  246. package/pkgs/shadcn-snapshot/src/paths.ts +47 -0
  247. package/pkgs/shadcn-snapshot/src/registry-files.ts +293 -0
  248. package/pkgs/shadcn-snapshot/src/registry.ts +631 -0
  249. package/pkgs/shadcn-snapshot/src/shadcn-tailwind.css +632 -0
  250. package/pkgs/shadcn-snapshot/src/tailwind-entry.css +159 -0
  251. package/pkgs/shadcn-snapshot/src/version.ts +8 -0
  252. package/plugins/claude/velloo/.claude-plugin/plugin.json +6 -0
  253. package/plugins/claude/velloo/agents/velloo-design-reviewer.md +33 -0
  254. package/plugins/claude/velloo/agents/velloo-designer.md +39 -0
  255. package/plugins/claude/velloo/commands/design.md +11 -0
  256. package/plugins/claude/velloo/commands/implement.md +10 -0
  257. package/plugins/claude/velloo/commands/review.md +8 -0
  258. package/plugins/gemini/GEMINI.md +31 -0
  259. package/plugins/gemini/commands/velloo/design.toml +10 -0
  260. package/plugins/gemini/commands/velloo/implement.toml +9 -0
  261. package/plugins/gemini/gemini-extension.json +9 -0
  262. package/skills/velloo-brand/SKILL.md +124 -0
  263. package/skills/velloo-design/SKILL.md +144 -0
  264. package/skills/velloo-design-system/SKILL.md +90 -0
  265. package/skills/velloo-implement/SKILL.md +101 -0
  266. package/skills/velloo-logo/SKILL.md +93 -0
  267. package/skills/velloo-setup/SKILL.md +156 -0
@@ -0,0 +1,218 @@
1
+ import { z } from "zod";
2
+
3
+ const NumOrCssLen = z.union([z.number(), z.string().min(1)]);
4
+
5
+ /** Loose schema for user-extension token namespaces (`shadows`, future fields). */
6
+ const LooseTokenGroupSchema: z.ZodType<LooseTokenGroup> = z.lazy(() =>
7
+ z.record(z.string(), z.union([z.string(), z.number(), LooseTokenGroupSchema])),
8
+ );
9
+ /**
10
+ * Exported only so declaration emit can name it: a downstream schema built on
11
+ * `ThemeSchema` (`@velloo/protocol/publish`) inlines this type into its own
12
+ * `.d.ts`, which `tsc` refuses to do for a module-private name.
13
+ */
14
+ export type LooseTokenGroup = { [key: string]: string | number | LooseTokenGroup };
15
+
16
+ /**
17
+ * A color slot is either a single CSS color or a pair { DEFAULT, foreground }.
18
+ * Matches shadcn's CSS-variable convention (e.g. --primary, --primary-foreground).
19
+ */
20
+ const ColorPairSchema = z.union([
21
+ z.string().min(1),
22
+ z.object({
23
+ DEFAULT: z.string().min(1),
24
+ foreground: z.string().min(1).optional(),
25
+ }),
26
+ ]);
27
+
28
+ export type ColorPair = z.infer<typeof ColorPairSchema>;
29
+
30
+ /**
31
+ * Typed color slots. `background`, `foreground`, and `primary` are required —
32
+ * everything else is optional so existing themes don't regress.
33
+ */
34
+ export const ColorsSchema = z.object({
35
+ background: z.string().min(1),
36
+ foreground: z.string().min(1),
37
+ primary: ColorPairSchema,
38
+ secondary: ColorPairSchema.optional(),
39
+ muted: ColorPairSchema.optional(),
40
+ accent: ColorPairSchema.optional(),
41
+ destructive: ColorPairSchema.optional(),
42
+ card: ColorPairSchema.optional(),
43
+ popover: ColorPairSchema.optional(),
44
+ border: z.string().min(1).optional(),
45
+ input: z.string().min(1).optional(),
46
+ ring: z.string().min(1).optional(),
47
+ });
48
+
49
+ export type Colors = z.infer<typeof ColorsSchema>;
50
+
51
+ /**
52
+ * A sparse color override: every slot optional and explicitly undefined-able,
53
+ * matching what zod's `.partial()` produces for `colorsDark`. `Partial<Colors>`
54
+ * is NOT the same thing under `exactOptionalPropertyTypes` — it adds `?` but
55
+ * still rejects an explicit `undefined`.
56
+ */
57
+ export type ColorsOverride = { [K in keyof Colors]?: Colors[K] | undefined };
58
+
59
+ /**
60
+ * The effective colors for a mode: `colors`, with `colorsDark` layered on when
61
+ * rendering dark.
62
+ *
63
+ * Every caller used to spread this inline, and the spread does not type as a
64
+ * complete `Colors`: `colorsDark` is a zod `.partial()`, so each of its fields
65
+ * is `T | undefined`, and spreading that over the full set widens even the
66
+ * required `background`/`foreground` to possibly-undefined. Callers coped by
67
+ * casting (codegen's dtcg) or by not noticing. Skipping undefined values keeps
68
+ * the result a real `Colors` — and matches the intent: an override that is not
69
+ * set must not blank the base color.
70
+ */
71
+ export function resolveColors(theme: Theme, dark = false): Colors {
72
+ if (!dark || !theme.colorsDark) return theme.colors;
73
+ const merged: Colors = { ...theme.colors };
74
+ for (const [key, value] of Object.entries(theme.colorsDark)) {
75
+ if (value !== undefined) (merged as Record<string, unknown>)[key] = value;
76
+ }
77
+ return merged;
78
+ }
79
+
80
+ /**
81
+ * Numeric color scales + extra semantic roles captured verbatim from a host
82
+ * app — `primary-600`, `success-500`, `danger`, `border-primary-300`. Keyed by
83
+ * the Tailwind color name (no `--color-` prefix), values are CSS colors.
84
+ * Emitted as `--color-<name>` so `bg-<name>` / `text-<name>` / `border-<name>`
85
+ * resolve literally during a code-to-design port, instead of silently falling
86
+ * back to the default palette.
87
+ *
88
+ * Distinct from `colors`: those are the semantic single-token slots that
89
+ * theme-flip and drive the component snapshot; `palette` is a raw passthrough
90
+ * for an app's own scale so verbatim classes render. Keys are constrained to a
91
+ * CSS-safe ident so they can't inject into the emitted stylesheet.
92
+ */
93
+ const PaletteSchema = z.record(
94
+ z.string().regex(/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/),
95
+ z.string().min(1),
96
+ );
97
+
98
+ /**
99
+ * A typeset: three rhythm controls plus font roles. Everything visible —
100
+ * the h1..h6 ladder, body/lead/small copy, the space under a heading — derives
101
+ * from these through `TYPESET_RATIOS` (see `./typeset.ts`), which is why there
102
+ * is no size/weight/leading/tracking scale here to keep in sync.
103
+ */
104
+ const TypesetSchema = z.object({
105
+ /** Base text size. `1em` (the default) follows the surrounding container. */
106
+ size: NumOrCssLen.optional(),
107
+ /** Body line-height, unitless. Heading leading derives from it. */
108
+ leading: z.number().positive().optional(),
109
+ /** Vertical space between blocks. Heading margins and rule spacing derive from it. */
110
+ flow: NumOrCssLen.optional(),
111
+ /** A `fontFamily` role name for body copy, e.g. "sans". */
112
+ fontBody: z.string().min(1).optional(),
113
+ /** A `fontFamily` role name for headings, e.g. "display". */
114
+ fontHeading: z.string().min(1).optional(),
115
+ /** A `fontFamily` role name for code. */
116
+ fontMono: z.string().min(1).optional(),
117
+ });
118
+
119
+ /**
120
+ * Typography: font roles, webfont loading, and the folder's typesets.
121
+ *
122
+ * `.strict()` on purpose. This shape once carried `fontSize` / `fontWeight` /
123
+ * `lineHeight` / `letterSpacing` records that no renderer, importer, or codegen
124
+ * path ever read — tokens you could set and that silently did nothing. Typesets
125
+ * replaced them, and strict validation means an attempt to write the old scale
126
+ * fails loudly instead of being quietly dropped.
127
+ */
128
+ export const TypographySchema = z
129
+ .object({
130
+ /**
131
+ * Font stacks keyed by role. `sans` / `mono` / `serif` are the
132
+ * conventional roles, but any key works — `display: '"Unbounded",
133
+ * sans-serif'` becomes `--font-display`, which Tailwind v4 turns into
134
+ * a `font-display` utility. Roles are the lever for typographic
135
+ * personality: declare one per voice, not one per screen.
136
+ */
137
+ fontFamily: z.record(z.string(), z.string().min(1)).optional(),
138
+ /**
139
+ * Google Fonts css2 family specs to load in design mode and emit as
140
+ * an @import in generated globals.css. Full spec syntax, e.g.
141
+ * "Unbounded:wght@400..900" or "Fraunces:ital,wght@0,300..900".
142
+ * Set via the `set_fonts` MCP tool rather than by hand.
143
+ */
144
+ googleFonts: z.array(z.string().min(1)).optional(),
145
+ /**
146
+ * Named typesets. `default` is the folder baseline and projects onto
147
+ * `:root`, so it styles every screen; every other name becomes a
148
+ * `.typeset-<name>` preset class a Prose region can opt into. Keys are
149
+ * constrained to a CSS-safe ident because they are emitted as selectors.
150
+ * Set via the `set_typeset` MCP tool.
151
+ */
152
+ typesets: z.record(z.string().regex(/^[A-Za-z0-9_-]+$/), TypesetSchema).optional(),
153
+ })
154
+ .strict();
155
+
156
+ /**
157
+ * Structured radius slots. `md` is what emit_theme picks up as `--radius`.
158
+ * Additional named sizes go into the record without losing the named ones.
159
+ */
160
+ const RadiusSchema = z
161
+ .object({
162
+ none: NumOrCssLen.optional(),
163
+ sm: NumOrCssLen.optional(),
164
+ md: NumOrCssLen.optional(),
165
+ lg: NumOrCssLen.optional(),
166
+ xl: NumOrCssLen.optional(),
167
+ "2xl": NumOrCssLen.optional(),
168
+ "3xl": NumOrCssLen.optional(),
169
+ full: NumOrCssLen.optional(),
170
+ })
171
+ .partial();
172
+
173
+ /**
174
+ * The host app's Tailwind `container` settings, captured so `class="container"`
175
+ * centers / pads / caps the same way it does in the app. Tailwind's stock
176
+ * `.container` does none of this (v4 dropped the `center`/`padding` options), so
177
+ * a code-to-design port's gutters drift until these are honored. Emitted as a
178
+ * `.container` override (render) / `@utility container` (codegen).
179
+ */
180
+ const ContainerSchema = z.object({
181
+ center: z.boolean().optional(),
182
+ /** Horizontal padding, e.g. "1.5rem". */
183
+ padding: z.string().min(1).optional(),
184
+ /** Largest max-width cap, e.g. "1320px". */
185
+ maxWidth: z.string().min(1).optional(),
186
+ });
187
+
188
+ export const ThemeSchema = z.object({
189
+ name: z.string().min(1),
190
+ colors: ColorsSchema,
191
+ /** Dark-mode overrides — only the color slots that differ from `colors`.
192
+ * When present, emit_theme writes a `.dark { ... }` block and the canvas
193
+ * can preview both modes. Tokens missing here fall back to `colors`. */
194
+ colorsDark: ColorsSchema.partial().optional(),
195
+ /** Numeric scales + extra roles captured from a host app (see PaletteSchema).
196
+ * Light values; dark overrides go in `paletteDark`. Optional — themes
197
+ * without an imported app palette omit it. */
198
+ palette: PaletteSchema.optional(),
199
+ paletteDark: PaletteSchema.optional(),
200
+ typography: TypographySchema,
201
+ spacing: LooseTokenGroupSchema,
202
+ radius: RadiusSchema,
203
+ shadows: LooseTokenGroupSchema.optional(),
204
+ /** Host app's Tailwind container config — see ContainerSchema. */
205
+ container: ContainerSchema.optional(),
206
+ /**
207
+ * `@keyframes` captured from a host app's `tailwind.config`:
208
+ * name → selector ("0%" / "from") → CSS declarations. Paired with
209
+ * `animation` so the app's `animate-<name>` utilities render on the canvas.
210
+ */
211
+ keyframes: z
212
+ .record(z.string(), z.record(z.string(), z.record(z.string(), z.string())))
213
+ .optional(),
214
+ /** Animation shorthands keyed by utility name → `--animate-<name>` (e.g. `"fade-in": "fadeIn .3s ease-out"`). */
215
+ animation: z.record(z.string(), z.string()).optional(),
216
+ });
217
+
218
+ export type Theme = z.infer<typeof ThemeSchema>;