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.
- package/BUN-LICENSE.md +91 -0
- package/LICENSE +202 -0
- package/NOTICE +9 -0
- package/README.md +288 -2
- package/THIRD-PARTY-NOTICES.md +467 -0
- package/assets/elsewhere-alps-9fqhv029.jpg +0 -0
- package/assets/elsewhere-bali-vp60g115.jpg +0 -0
- package/assets/elsewhere-forest-xgn23zrj.jpg +0 -0
- package/assets/elsewhere-japan-qs7qkbr2.jpg +0 -0
- package/assets/elsewhere-kyoto-map-n2bvkrya.svg +1 -0
- package/assets/elsewhere-kyoto-qemtn6nk.jpg +0 -0
- package/assets/elsewhere-room-g1gaw7cs.jpg +0 -0
- package/canvas/apple-touch-icon.png +0 -0
- package/canvas/assets/index-BCvDZiBo.css +2 -0
- package/canvas/assets/index-ClaBqcaS.js +28 -0
- package/canvas/assets/lucide-all-iIF-pmzN.js +1 -0
- package/canvas/assets/radix-C3HaOB-d.js +41 -0
- package/canvas/assets/react-D0Ec-185.js +9 -0
- package/canvas/assets/rolldown-runtime-hePW80VL.js +1 -0
- package/canvas/assets/vendor-CKmQ-Zst.js +53 -0
- package/canvas/favicon.ico +0 -0
- package/canvas/favicon.svg +12 -0
- package/canvas/fonts/OFL-1.1.txt +93 -0
- package/canvas/fonts/poppins-700.woff2 +0 -0
- package/canvas/icon-192.png +0 -0
- package/canvas/icon-512.png +0 -0
- package/canvas/index.html +22 -0
- package/canvas/site.webmanifest +11 -0
- package/chunk-08sv3k41.js +4 -0
- package/chunk-0fe83yzt.js +2 -0
- package/chunk-0t68szgr.js +3 -0
- package/chunk-1h55s5he.js +2 -0
- package/chunk-2c7jbx46.js +2 -0
- package/chunk-2smsg2ct.js +7 -0
- package/chunk-308ynsxd.js +2 -0
- package/chunk-3e8vez4d.js +1229 -0
- package/chunk-40xgcth8.js +63 -0
- package/chunk-4dn75m6t.js +3 -0
- package/chunk-4fj5tahv.js +3 -0
- package/chunk-6nqghjda.js +3 -0
- package/chunk-6tx68d90.js +3 -0
- package/chunk-6w6ch4ce.js +2 -0
- package/chunk-735ezz23.js +86 -0
- package/chunk-7eq7nx37.js +7 -0
- package/chunk-7eyb8yrf.js +6 -0
- package/chunk-80fjfyha.js +86 -0
- package/chunk-80r93t2e.js +4 -0
- package/chunk-94vx1yb0.js +3 -0
- package/chunk-9vg3yafz.js +3 -0
- package/chunk-a84n72m2.js +4 -0
- package/chunk-ampmdzz4.js +10 -0
- package/chunk-avns4dbf.js +2 -0
- package/chunk-ayrb3qnm.js +3 -0
- package/chunk-bwnvcncx.js +3 -0
- package/chunk-ccszg02q.js +5 -0
- package/chunk-cvn689d5.js +3 -0
- package/chunk-cxe6jjwj.js +3 -0
- package/chunk-dg6z4vdd.js +3 -0
- package/chunk-dgwjpbp9.js +3 -0
- package/chunk-dy41n60j.js +52 -0
- package/chunk-ek71z2fv.js +226 -0
- package/chunk-er1fxb7d.js +2 -0
- package/chunk-eyr8hrzf.js +2 -0
- package/chunk-fjbzk4sw.js +3 -0
- package/chunk-fsbsksga.js +3 -0
- package/chunk-h6gchare.js +3 -0
- package/chunk-hcb68zt7.js +85 -0
- package/chunk-hggap8fq.js +3 -0
- package/chunk-j11fyeqh.js +2 -0
- package/chunk-jb21jkp1.js +8 -0
- package/chunk-jfx9xjmr.js +18 -0
- package/chunk-jr0ss0mm.js +303 -0
- package/chunk-jrsknzjt.js +3 -0
- package/chunk-jv0xggh0.js +2 -0
- package/chunk-k17gfwq3.js +2 -0
- package/chunk-mh2pr7cq.js +5 -0
- package/chunk-mnapdp1y.js +2 -0
- package/chunk-mzhdrawd.js +3 -0
- package/chunk-n9gs6yx9.js +2 -0
- package/chunk-ngnymg2q.js +3 -0
- package/chunk-nwx5700b.js +3 -0
- package/chunk-pjtg3wnm.js +4 -0
- package/chunk-pp86v6m6.js +3 -0
- package/chunk-r22epxz2.js +3 -0
- package/chunk-rgasy862.js +5 -0
- package/chunk-rpwpeph5.js +3 -0
- package/chunk-s23gtejf.js +3 -0
- package/chunk-sjgqnykd.js +6 -0
- package/chunk-sn3x2m0m.js +6 -0
- package/chunk-t448rrfr.js +2 -0
- package/chunk-tdsk5wxa.js +451 -0
- package/chunk-v40fn08f.js +442 -0
- package/chunk-vbk1qhdz.js +3 -0
- package/chunk-vdkrbzh1.js +2 -0
- package/chunk-vgmr98cv.js +40 -0
- package/chunk-vsv8z729.js +6 -0
- package/chunk-x4f4k9pp.js +26 -0
- package/chunk-xt5ayg7g.js +4 -0
- package/chunk-ydhwv29b.js +244 -0
- package/chunk-zb6hc1y0.js +2 -0
- package/cli.js +3 -0
- package/launcher.cjs +97 -0
- package/package.json +58 -5
- package/pkgs/helpers/src/box.tsx +21 -0
- package/pkgs/helpers/src/cn.ts +32 -0
- package/pkgs/helpers/src/descriptors.ts +270 -0
- package/pkgs/helpers/src/divider.tsx +123 -0
- package/pkgs/helpers/src/gradient.tsx +90 -0
- package/pkgs/helpers/src/heading.tsx +20 -0
- package/pkgs/helpers/src/icon-data.ts +8009 -0
- package/pkgs/helpers/src/icon.tsx +53 -0
- package/pkgs/helpers/src/image.tsx +111 -0
- package/pkgs/helpers/src/index.ts +29 -0
- package/pkgs/helpers/src/layer.tsx +61 -0
- package/pkgs/helpers/src/lowering.ts +53 -0
- package/pkgs/helpers/src/paths.ts +47 -0
- package/pkgs/helpers/src/placeholder.tsx +88 -0
- package/pkgs/helpers/src/prose.tsx +42 -0
- package/pkgs/helpers/src/registry.ts +41 -0
- package/pkgs/helpers/src/svg.tsx +62 -0
- package/pkgs/helpers/src/text.tsx +16 -0
- package/pkgs/provider-antd/src/index.ts +78 -0
- package/pkgs/provider-antd/src/intro.ts +14 -0
- package/pkgs/provider-antd/src/manifest.ts +439 -0
- package/pkgs/provider-antd/src/overlays.ts +176 -0
- package/pkgs/provider-antd/src/registry.ts +104 -0
- package/pkgs/provider-antd/src/render-pass.ts +43 -0
- package/pkgs/provider-antd/src/tailwind-entry.css +21 -0
- package/pkgs/provider-antd/src/theme.ts +132 -0
- package/pkgs/provider-antd/src/version.ts +8 -0
- package/pkgs/provider-chakra/src/index.ts +80 -0
- package/pkgs/provider-chakra/src/intro.ts +14 -0
- package/pkgs/provider-chakra/src/manifest.ts +421 -0
- package/pkgs/provider-chakra/src/overlays.ts +246 -0
- package/pkgs/provider-chakra/src/registry.ts +227 -0
- package/pkgs/provider-chakra/src/render-pass.ts +55 -0
- package/pkgs/provider-chakra/src/tailwind-entry.css +21 -0
- package/pkgs/provider-chakra/src/theme.ts +180 -0
- package/pkgs/provider-chakra/src/version.ts +8 -0
- package/pkgs/provider-mui/src/index.ts +117 -0
- package/pkgs/provider-mui/src/intro.ts +12 -0
- package/pkgs/provider-mui/src/manifest.ts +263 -0
- package/pkgs/provider-mui/src/overlays.ts +124 -0
- package/pkgs/provider-mui/src/registry.ts +120 -0
- package/pkgs/provider-mui/src/render-pass.ts +36 -0
- package/pkgs/provider-mui/src/tailwind-entry.css +21 -0
- package/pkgs/provider-mui/src/theme.ts +129 -0
- package/pkgs/provider-mui/src/version.ts +8 -0
- package/pkgs/provider-none/src/components-inline.tsx +204 -0
- package/pkgs/provider-none/src/components.tsx +155 -0
- package/pkgs/provider-none/src/index.ts +57 -0
- package/pkgs/provider-none/src/intro.ts +18 -0
- package/pkgs/provider-none/src/manifest.ts +163 -0
- package/pkgs/provider-none/src/registry-inline.ts +36 -0
- package/pkgs/provider-none/src/registry.ts +39 -0
- package/pkgs/provider-none/src/tailwind-entry.css +57 -0
- package/pkgs/provider-none/src/version.ts +10 -0
- package/pkgs/schema/src/annotation.ts +120 -0
- package/pkgs/schema/src/asset.ts +66 -0
- package/pkgs/schema/src/board.ts +71 -0
- package/pkgs/schema/src/comment.ts +107 -0
- package/pkgs/schema/src/config.ts +185 -0
- package/pkgs/schema/src/css-sanitize.ts +56 -0
- package/pkgs/schema/src/extension.ts +105 -0
- package/pkgs/schema/src/fonts.ts +690 -0
- package/pkgs/schema/src/frame.ts +40 -0
- package/pkgs/schema/src/icon-name.ts +21 -0
- package/pkgs/schema/src/ids.ts +14 -0
- package/pkgs/schema/src/index.ts +169 -0
- package/pkgs/schema/src/migrate.ts +297 -0
- package/pkgs/schema/src/node.ts +229 -0
- package/pkgs/schema/src/paths.ts +28 -0
- package/pkgs/schema/src/repo.ts +134 -0
- package/pkgs/schema/src/screen.ts +30 -0
- package/pkgs/schema/src/snippet-resolve.ts +193 -0
- package/pkgs/schema/src/snippet.ts +104 -0
- package/pkgs/schema/src/svg-sanitize.ts +487 -0
- package/pkgs/schema/src/theme.ts +218 -0
- package/pkgs/schema/src/typeset.ts +693 -0
- package/pkgs/schema/src/validate-ids.ts +46 -0
- package/pkgs/schema/src/viewport.ts +8 -0
- package/pkgs/shadcn-snapshot/dist/manifest.json +8109 -0
- package/pkgs/shadcn-snapshot/src/components/canvas-portal.tsx +55 -0
- package/pkgs/shadcn-snapshot/src/components/ui/accordion.tsx +122 -0
- package/pkgs/shadcn-snapshot/src/components/ui/alert-dialog.tsx +164 -0
- package/pkgs/shadcn-snapshot/src/components/ui/alert.tsx +74 -0
- package/pkgs/shadcn-snapshot/src/components/ui/aspect-ratio.tsx +13 -0
- package/pkgs/shadcn-snapshot/src/components/ui/attachment.tsx +197 -0
- package/pkgs/shadcn-snapshot/src/components/ui/avatar.tsx +100 -0
- package/pkgs/shadcn-snapshot/src/components/ui/badge.tsx +50 -0
- package/pkgs/shadcn-snapshot/src/components/ui/breadcrumb.tsx +109 -0
- package/pkgs/shadcn-snapshot/src/components/ui/bubble.tsx +130 -0
- package/pkgs/shadcn-snapshot/src/components/ui/button-group.tsx +82 -0
- package/pkgs/shadcn-snapshot/src/components/ui/button.tsx +72 -0
- package/pkgs/shadcn-snapshot/src/components/ui/calendar.tsx +229 -0
- package/pkgs/shadcn-snapshot/src/components/ui/card.tsx +92 -0
- package/pkgs/shadcn-snapshot/src/components/ui/carousel.tsx +235 -0
- package/pkgs/shadcn-snapshot/src/components/ui/chart-option.ts +250 -0
- package/pkgs/shadcn-snapshot/src/components/ui/chart.tsx +177 -0
- package/pkgs/shadcn-snapshot/src/components/ui/checkbox.tsx +41 -0
- package/pkgs/shadcn-snapshot/src/components/ui/collapsible.tsx +32 -0
- package/pkgs/shadcn-snapshot/src/components/ui/combobox.tsx +196 -0
- package/pkgs/shadcn-snapshot/src/components/ui/context-menu.tsx +227 -0
- package/pkgs/shadcn-snapshot/src/components/ui/dialog.tsx +131 -0
- package/pkgs/shadcn-snapshot/src/components/ui/direction.tsx +24 -0
- package/pkgs/shadcn-snapshot/src/components/ui/drawer.tsx +112 -0
- package/pkgs/shadcn-snapshot/src/components/ui/dropdown-menu.tsx +205 -0
- package/pkgs/shadcn-snapshot/src/components/ui/empty.tsx +98 -0
- package/pkgs/shadcn-snapshot/src/components/ui/field.tsx +228 -0
- package/pkgs/shadcn-snapshot/src/components/ui/hover-card.tsx +51 -0
- package/pkgs/shadcn-snapshot/src/components/ui/input-group.tsx +148 -0
- package/pkgs/shadcn-snapshot/src/components/ui/input.tsx +43 -0
- package/pkgs/shadcn-snapshot/src/components/ui/item.tsx +187 -0
- package/pkgs/shadcn-snapshot/src/components/ui/kbd.tsx +30 -0
- package/pkgs/shadcn-snapshot/src/components/ui/label.tsx +25 -0
- package/pkgs/shadcn-snapshot/src/components/ui/marker.tsx +71 -0
- package/pkgs/shadcn-snapshot/src/components/ui/menubar.tsx +243 -0
- package/pkgs/shadcn-snapshot/src/components/ui/message.tsx +89 -0
- package/pkgs/shadcn-snapshot/src/components/ui/native-select.tsx +60 -0
- package/pkgs/shadcn-snapshot/src/components/ui/navigation-menu.tsx +165 -0
- package/pkgs/shadcn-snapshot/src/components/ui/pagination.tsx +117 -0
- package/pkgs/shadcn-snapshot/src/components/ui/popover.tsx +66 -0
- package/pkgs/shadcn-snapshot/src/components/ui/progress.tsx +35 -0
- package/pkgs/shadcn-snapshot/src/components/ui/radio-group.tsx +48 -0
- package/pkgs/shadcn-snapshot/src/components/ui/scroll-area.tsx +59 -0
- package/pkgs/shadcn-snapshot/src/components/ui/select.tsx +180 -0
- package/pkgs/shadcn-snapshot/src/components/ui/separator.tsx +32 -0
- package/pkgs/shadcn-snapshot/src/components/ui/sheet.tsx +107 -0
- package/pkgs/shadcn-snapshot/src/components/ui/skeleton.tsx +17 -0
- package/pkgs/shadcn-snapshot/src/components/ui/slider.tsx +68 -0
- package/pkgs/shadcn-snapshot/src/components/ui/sonner.tsx +91 -0
- package/pkgs/shadcn-snapshot/src/components/ui/spinner.tsx +21 -0
- package/pkgs/shadcn-snapshot/src/components/ui/switch.tsx +41 -0
- package/pkgs/shadcn-snapshot/src/components/ui/table.tsx +93 -0
- package/pkgs/shadcn-snapshot/src/components/ui/tabs.tsx +84 -0
- package/pkgs/shadcn-snapshot/src/components/ui/textarea.tsx +24 -0
- package/pkgs/shadcn-snapshot/src/components/ui/toggle-group.tsx +94 -0
- package/pkgs/shadcn-snapshot/src/components/ui/toggle.tsx +50 -0
- package/pkgs/shadcn-snapshot/src/components/ui/tooltip.tsx +47 -0
- package/pkgs/shadcn-snapshot/src/examples.ts +36 -0
- package/pkgs/shadcn-snapshot/src/groups.ts +87 -0
- package/pkgs/shadcn-snapshot/src/index.ts +43 -0
- package/pkgs/shadcn-snapshot/src/lib/utils.ts +4 -0
- package/pkgs/shadcn-snapshot/src/manifest.ts +12 -0
- package/pkgs/shadcn-snapshot/src/notes.ts +97 -0
- package/pkgs/shadcn-snapshot/src/paths.ts +47 -0
- package/pkgs/shadcn-snapshot/src/registry-files.ts +293 -0
- package/pkgs/shadcn-snapshot/src/registry.ts +631 -0
- package/pkgs/shadcn-snapshot/src/shadcn-tailwind.css +632 -0
- package/pkgs/shadcn-snapshot/src/tailwind-entry.css +159 -0
- package/pkgs/shadcn-snapshot/src/version.ts +8 -0
- package/plugins/claude/velloo/.claude-plugin/plugin.json +6 -0
- package/plugins/claude/velloo/agents/velloo-design-reviewer.md +33 -0
- package/plugins/claude/velloo/agents/velloo-designer.md +39 -0
- package/plugins/claude/velloo/commands/design.md +11 -0
- package/plugins/claude/velloo/commands/implement.md +10 -0
- package/plugins/claude/velloo/commands/review.md +8 -0
- package/plugins/gemini/GEMINI.md +31 -0
- package/plugins/gemini/commands/velloo/design.toml +10 -0
- package/plugins/gemini/commands/velloo/implement.toml +9 -0
- package/plugins/gemini/gemini-extension.json +9 -0
- package/skills/velloo-brand/SKILL.md +124 -0
- package/skills/velloo-design/SKILL.md +144 -0
- package/skills/velloo-design-system/SKILL.md +90 -0
- package/skills/velloo-implement/SKILL.md +101 -0
- package/skills/velloo-logo/SKILL.md +93 -0
- 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>;
|