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,185 @@
1
+ import { z } from "zod";
2
+ import { BoardGroupSchema } from "./board.ts";
3
+ import { ExtensionSchema } from "./extension.ts";
4
+ import { CURRENT_SCHEMA_VERSION } from "./migrate.ts";
5
+ import { DesignNameSchema, FeedbackPrefsSchema } from "./repo.ts";
6
+
7
+ export const ViewportPresetSchema = z.object({
8
+ name: z.string().min(1),
9
+ w: z.number().int().positive(),
10
+ h: z.number().int().positive(),
11
+ });
12
+
13
+ export type ViewportPreset = z.infer<typeof ViewportPresetSchema>;
14
+
15
+ /**
16
+ * Library declaration. The `id` selects which component provider the
17
+ * design folder is built against (see `@velloo/provider`). `source`
18
+ * tells the loader where the components live for that provider:
19
+ *
20
+ * "binary" — components ship with the velloo binary.
21
+ * "cache" — components live under `~/.velloo/…`.
22
+ * "in-repo" — components live inside the user's app folder.
23
+ * `componentsPath` is the resolved location; the shadcn
24
+ * canvas imports supported files from here directly.
25
+ */
26
+ export const LibrarySchema = z.object({
27
+ /**
28
+ * Component provider id. The server's provider loader maps ids to
29
+ * factories; `"shadcn-upstream"` is the default for new folders.
30
+ */
31
+ id: z.enum(["shadcn-upstream", "none", "mui", "antd", "chakra"]),
32
+ version: z.string().min(1),
33
+ source: z.enum(["binary", "cache", "in-repo"]),
34
+ /** Where the components live, relative to the design folder root. */
35
+ componentsPath: z.string().min(1),
36
+ });
37
+
38
+ export type Library = z.infer<typeof LibrarySchema>;
39
+
40
+ const CodegenConfigSchema = z.object({
41
+ /** Import prefix for emitted shadcn imports. Defaults to "@/components/ui". */
42
+ componentsAlias: z.string().min(1).optional(),
43
+ /**
44
+ * Where the app's UI components live, relative to the app root — what
45
+ * `init` asked (or detected). Recorded so a *second* design folder in the
46
+ * same repo inherits the answer instead of asking again.
47
+ */
48
+ componentsDir: z.string().min(1).optional(),
49
+ });
50
+
51
+ export type CodegenConfig = z.infer<typeof CodegenConfigSchema>;
52
+
53
+ /**
54
+ * Where the host app lives, used by the live-island bundler to resolve
55
+ * a `render:"live"` extension's `importPath` (and its dependencies, e.g.
56
+ * the app's own recharts) into a browser bundle. `root` is absolute or
57
+ * resolved from the design folder root; `project:` paths resolve from the
58
+ * locally bound application root for managed designs. Absent ⇒ the bundler defaults to
59
+ * the design folder's parent (the `<appRoot>/velloo` layout `init`
60
+ * produces). `aliases` mirrors the host tsconfig path map (e.g.
61
+ * `{ "@/*": "src/*" }`); absent ⇒ `{ "@/*": "*" }`. Reading the host
62
+ * tsconfig per build is fragile (JSON5, `extends` chains), so it's
63
+ * persisted once at registration time.
64
+ */
65
+ export const HostAppSchema = z.object({
66
+ root: z.string().min(1),
67
+ aliases: z.record(z.string().min(1), z.string().min(1)).optional(),
68
+ });
69
+
70
+ export type HostApp = z.infer<typeof HostAppSchema>;
71
+
72
+ /**
73
+ * Folder config. Multi-library: a folder registers N libraries by id
74
+ * and pins one as the default; each screen optionally declares which
75
+ * library it uses (`screen.library`), falling back to `defaultLibrary`.
76
+ *
77
+ * `schemaVersion` gates loading: the server refuses folders on any
78
+ * other version (older ⇒ `velloo upgrade` migrates them on disk — see
79
+ * `migrate.ts`; newer ⇒ the binary is too old).
80
+ */
81
+ export const ConfigSchema = z
82
+ .object({
83
+ schemaVersion: z.literal(CURRENT_SCHEMA_VERSION),
84
+ toolVersion: z.string().min(1),
85
+ /**
86
+ * The design's name — what commands, `velloo.json`'s `defaultDesign`, the
87
+ * canvas title and agents call it. Lives here, not in the manifest, so a
88
+ * design carries its identity wherever it is stored. Unique among the
89
+ * designs of one checkout.
90
+ */
91
+ name: DesignNameSchema,
92
+ /**
93
+ * Stable folder identity for velloo-cloud (a UUID). Published share links
94
+ * carry it server-side, so every clone of the folder finds its links (and
95
+ * their comments) by id — no committed link records. `init` generates it;
96
+ * pre-existing folders get one lazily on their first `velloo publish`.
97
+ * Optional because it's meaningless until the folder touches the cloud.
98
+ */
99
+ folderId: z
100
+ .string()
101
+ .regex(/^[A-Za-z0-9_-]{8,64}$/)
102
+ .optional(),
103
+ /**
104
+ * Map of `libraryId → Library`. Library ids are free-form strings
105
+ * the user picks (e.g. "shadcn", "marketing", "internal"); the
106
+ * `Library.id` field inside each entry still names the provider
107
+ * implementation.
108
+ */
109
+ libraries: z.record(z.string().min(1), LibrarySchema),
110
+ /** Library id used when a screen doesn't declare one. */
111
+ defaultLibrary: z.string().min(1),
112
+ /**
113
+ * User-declared custom components. The agent adds these via the
114
+ * `add_extension` MCP tool when a screen needs a component the
115
+ * active library doesn't have. Keyed by component id; ids
116
+ * shadow library components of the same name.
117
+ */
118
+ extensions: z.record(z.string().min(1), ExtensionSchema).optional(),
119
+ viewportPresets: z.array(ViewportPresetSchema).min(1),
120
+ /** Screen id the canvas should focus on first load. Falls back to the first screen. */
121
+ defaultScreen: z.string().min(1).optional(),
122
+ /** Board id the canvas should open on first load. Falls back to the first board. */
123
+ defaultBoard: z.string().min(1).optional(),
124
+ /**
125
+ * Left-sidebar display order of boards, as board ids. The canvas
126
+ * writes this when the user drags boards to reorder them. Boards not
127
+ * listed fall to the end in filename (alphabetical) order; ids that
128
+ * no longer resolve are ignored. Absent ⇒ filename order — the
129
+ * historical default, so existing folders are unaffected.
130
+ */
131
+ boardOrder: z.array(z.string().min(1)).optional(),
132
+ /**
133
+ * Sidebar groups of boards, in display order — an area of work ("Side
134
+ * pane", "Account page") holding a set of boards. A board points at one
135
+ * via `Board.group`; boards pointing at nothing render under Ungrouped.
136
+ * `boardOrder` still orders boards *within* a group. Absent ⇒ today's
137
+ * flat list, so existing folders are unaffected.
138
+ */
139
+ boardGroups: z.array(BoardGroupSchema).optional(),
140
+ codegen: CodegenConfigSchema.optional(),
141
+ /**
142
+ * The folder's CSS framework — the styling axis, independent of the
143
+ * component library (`library`). `init` detects it from the host app.
144
+ * `"tailwind"` ⇒ Tailwind utility classes on `className`; `"none"` ⇒ inline
145
+ * `style` objects (themed via the CSS vars `themeToCss` injects, no build
146
+ * step). Absent ⇒ the active library's default channel (Tailwind for
147
+ * shadcn/none, `sx` for MUI). Honored per screen only when that screen's
148
+ * library supports it — a shadcn screen stays Tailwind even here. The
149
+ * server rejects a folder whose CSS framework no registered library
150
+ * supports (e.g. shadcn + none).
151
+ */
152
+ styling: z.object({ framework: z.enum(["tailwind", "none"]) }).optional(),
153
+ /** Host app location for the live-island bundler. See `HostAppSchema`. */
154
+ hostApp: HostAppSchema.optional(),
155
+ /**
156
+ * Named host apps for monorepos — the multi-app twin of `hostApp`, keyed
157
+ * by a short app name (`"web"`, `"admin"`; `init`'s multi-app scan uses
158
+ * the same prefixes as the screen ids it generates). An extension can
159
+ * pick its app via `extension.app`, so its live island bundles against
160
+ * that app's root/aliases/node_modules (and its React copy). `hostApp`
161
+ * stays the default for extensions that don't name one. Absent ⇒
162
+ * single-app folder, everything resolves through `hostApp`.
163
+ */
164
+ hostApps: z.record(z.string().min(1), HostAppSchema).optional(),
165
+ /**
166
+ * Opt-in product feedback. Set during interactive `init` (after the user
167
+ * signs in). When `enabled`, the server exposes the `send_feedback` MCP
168
+ * tool, which posts free-text feedback about Velloo to velloo-cloud.
169
+ * `contactOk` records consent to be contacted about that feedback. Absent
170
+ * ⇒ disabled (the default; the local tool stays account-free + offline).
171
+ */
172
+ /**
173
+ * @deprecated Feedback consent is a repo-level preference now, stored in
174
+ * `velloo.json` — it's about the person, not the design folder, and a
175
+ * repo with several folders shouldn't ask (or answer) it twice. Still
176
+ * read as the fallback for folders written before the move; never
177
+ * written.
178
+ */
179
+ feedback: FeedbackPrefsSchema.optional(),
180
+ })
181
+ .refine((c) => c.defaultLibrary in c.libraries, {
182
+ message: "`defaultLibrary` must name an entry in `libraries`.",
183
+ });
184
+
185
+ export type Config = z.infer<typeof ConfigSchema>;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * CSS-injection hardening for untrusted design-folder strings that reach an
3
+ * inline `<style>` (the renderer's canvas + Playwright document) or an emitted
4
+ * stylesheet (codegen's globals.css). Theme values, palette entries, custom.css,
5
+ * and Google-font specs are authored by whoever wrote the design JSON, so a
6
+ * value like `#fff</style><script>…</script>` must not break out of the style
7
+ * element or the declaration. This is the single home for that logic — the
8
+ * renderer's `theme-to-css.ts`/`document.ts` and codegen's `emit-theme` both
9
+ * route through it (mirrors how `svg-sanitize.ts` centralizes SVG safety).
10
+ */
11
+
12
+ /** A CSS custom-property / token name: letters, digits, dash, underscore only. */
13
+ const CSS_IDENT_RE = /^[A-Za-z0-9_-]+$/;
14
+
15
+ /** True when `name` is safe to interpolate as a `--<name>` token identifier. */
16
+ export function isCssIdent(name: string): boolean {
17
+ return CSS_IDENT_RE.test(name);
18
+ }
19
+
20
+ /**
21
+ * Make an untrusted theme *token value* safe to interpolate into a CSS
22
+ * declaration. Theme values (colors, spacing, shadows, radius, font stacks,
23
+ * animation shorthands) never legitimately contain declaration/element/comment
24
+ * delimiters, so strip the characters that could break out — `<> {} ;`,
25
+ * backslash, the comment-close `*​/`, and newlines — while preserving everything
26
+ * a real value needs (parens, commas, quotes, %, #, dots, spaces).
27
+ */
28
+ export function sanitizeCssTokenValue(value: string): string {
29
+ return value
30
+ .replace(/\*\//g, "")
31
+ .replace(/[<>{};\\]/g, "")
32
+ .replace(/[\r\n]+/g, " ")
33
+ .trim();
34
+ }
35
+
36
+ /**
37
+ * Neutralize an untrusted *raw CSS block* (folder custom.css, adapter emotion
38
+ * CSS) for inlining into an HTML `<style>` element. The only breakout from a
39
+ * raw-text element is the end-tag sequence `</`, so escape it: `<\/` is inert to
40
+ * the HTML tokenizer and reads as `/` to the CSS parser (a valid escape,
41
+ * preserved even inside `url()`/string values). Structural CSS (`;{}`) is left
42
+ * intact because the block IS structural CSS.
43
+ */
44
+ export function neutralizeCssText(css: string): string {
45
+ return css.replace(/<\//g, "<\\/");
46
+ }
47
+
48
+ /**
49
+ * Restrict a Google Fonts css2 `family=` spec to the characters the syntax
50
+ * actually uses (family name + `ital,wght@…;…` axis tuples), folding spaces to
51
+ * `+`. Drops anything that could break out of the `<link href>` attribute or the
52
+ * `@import url("…")` string (quotes, angle brackets, parens, backslash).
53
+ */
54
+ export function sanitizeGoogleFontSpec(spec: string): string {
55
+ return spec.replace(/[^A-Za-z0-9 +:,.;@_-]/g, "").replace(/ /g, "+");
56
+ }
@@ -0,0 +1,105 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * A user-declared component the agent can place in a screen tree but
5
+ * which isn't part of any library's registry. The canvas renders an
6
+ * extension node as a labelled placeholder (Tier 1); codegen emits a
7
+ * real import to `importPath` so the user's app mounts the actual
8
+ * component — one tier of the three-tier customization story.
9
+ *
10
+ * The prop schema mirrors `@velloo/provider`'s `PropDescriptor` — kept
11
+ * here as a Zod schema so `.design/config.json` validation works
12
+ * without `@velloo/schema` depending on `@velloo/provider` (which
13
+ * would invert the package graph). The type produced is structurally
14
+ * compatible: extension props feed into the same inspector affordances
15
+ * the manifest-derived ones use.
16
+ */
17
+ const PropControlSchema = z.enum(["boolean", "number", "string", "color", "enum", "icon"]);
18
+
19
+ export const ExtensionPropDescriptorSchema = z
20
+ .object({
21
+ name: z.string().min(1),
22
+ /** Raw TS type string (e.g. "boolean | undefined"). Free-form; just used for display. */
23
+ type: z.string().min(1),
24
+ optional: z.boolean(),
25
+ defaultValue: z.string().optional(),
26
+ control: PropControlSchema,
27
+ /** Allowed values when `control === "enum"`. */
28
+ enumValues: z.array(z.union([z.string(), z.number()])).optional(),
29
+ })
30
+ .superRefine((prop, ctx) => {
31
+ // An enum control with no values would render an empty select in the
32
+ // inspector — reject it at parse time instead.
33
+ if (prop.control === "enum" && (!prop.enumValues || prop.enumValues.length === 0)) {
34
+ ctx.addIssue({
35
+ code: "custom",
36
+ path: ["enumValues"],
37
+ message: `prop "${prop.name}": control "enum" requires a non-empty \`enumValues\` array`,
38
+ });
39
+ }
40
+ });
41
+
42
+ export type ExtensionPropDescriptor = z.infer<typeof ExtensionPropDescriptorSchema>;
43
+
44
+ export const ExtensionSchema = z.object({
45
+ /**
46
+ * Bare import specifier emitted by codegen. Examples:
47
+ * "@/components/data-table" — alias-based relative path
48
+ * "../components/PriceChart" — relative path
49
+ * "@acme/charts" — npm package
50
+ */
51
+ importPath: z.string().min(1),
52
+ /**
53
+ * UI vs typography. Drives the Library tab grouping; defaults to
54
+ * "ui" when omitted.
55
+ */
56
+ category: z.enum(["ui", "typography"]).optional(),
57
+ /** Free-form description shown in the Library detail page. */
58
+ description: z.string().optional(),
59
+ /** Schema of props the canvas inspector + agent both consume. */
60
+ props: z.array(ExtensionPropDescriptorSchema),
61
+ /**
62
+ * Marks how the agent registered this extension. "agent" is the
63
+ * common case (via `add_extension` MCP tool); "manual" indicates the
64
+ * user edited config.json directly. Today purely informational —
65
+ * affects nothing in code.
66
+ */
67
+ origin: z.enum(["agent", "manual"]).optional(),
68
+ /**
69
+ * How the canvas previews this extension node.
70
+ * "static" (default) — labelled placeholder card (Tier 1).
71
+ * "live" — the real component is bundled from the host
72
+ * app and client-mounted into a SSR marker
73
+ * ("live island"). Used for charts and other
74
+ * components whose visual fidelity needs the
75
+ * actual implementation. Falls back to the
76
+ * placeholder if the bundle/render fails.
77
+ * Optional, absent ⇒ "static". Never written when static so existing
78
+ * `config.json` files round-trip unchanged.
79
+ */
80
+ render: z.enum(["static", "live"]).optional(),
81
+ /**
82
+ * Which host app this component lives in — a key into `config.hostApps`
83
+ * (monorepos only). A `render:"live"` island bundles its `importPath`
84
+ * against that app's root/aliases and mounts with that app's React copy.
85
+ * Absent ⇒ the folder default (`config.hostApp`), which is every
86
+ * single-app folder.
87
+ */
88
+ app: z.string().min(1).optional(),
89
+ /**
90
+ * How a `render:"live"` island is sized on the canvas.
91
+ * "aspect-video" (default) — wrapper locks a 16:9 box (`aspect-video
92
+ * w-full`); right for charts that fill their
93
+ * container via a responsive container.
94
+ * "content" — wrapper imposes no ratio (`w-full` only) and
95
+ * grows to the component's intrinsic height. Use
96
+ * for a fixed-height chart (a recharts
97
+ * `ResponsiveContainer height={300}`) or an
98
+ * `absolute inset-0` overlay that the 16:9 box
99
+ * would misalign.
100
+ * Optional, absent ⇒ "aspect-video". Only meaningful when render:"live".
101
+ */
102
+ fit: z.enum(["aspect-video", "content"]).optional(),
103
+ });
104
+
105
+ export type Extension = z.infer<typeof ExtensionSchema>;