velloo 0.4.1 → 0.5.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/README.md +29 -7
- package/canvas/assets/index-BDBBHdHH.css +2 -0
- package/canvas/assets/index-QdsLJNY9.js +29 -0
- package/canvas/assets/{lucide-all-Bcm80WFX.js → lucide-all-R8DUyY-B.js} +1 -1
- package/canvas/index.html +2 -2
- package/chunk-0rdf6trc.js +2 -0
- package/{chunk-npsg7btt.js → chunk-0wafc9aw.js} +1 -1
- package/chunk-0wqxb8r9.js +3 -0
- package/{chunk-e6nhc3ay.js → chunk-10hap8kc.js} +1 -1
- package/chunk-18eqh3s1.js +2 -0
- package/chunk-19xyzep8.js +2 -0
- package/{chunk-7gtvstne.js → chunk-1h3gqy7w.js} +1 -1
- package/{chunk-vjrn10xr.js → chunk-33t0csxs.js} +2 -2
- package/{chunk-7xffbj5h.js → chunk-3ffs39e8.js} +1 -1
- package/chunk-42bj6r30.js +3 -0
- package/chunk-49w26e94.js +5 -0
- package/{chunk-7a45tn73.js → chunk-4pkw6d9n.js} +2 -2
- package/{chunk-ph94nrc8.js → chunk-4sjvdakg.js} +1 -1
- package/chunk-5f0xv80x.js +5 -0
- package/{chunk-2dm4x17z.js → chunk-5ma8zbty.js} +1 -1
- package/{chunk-t9ygewjx.js → chunk-5y9hem1j.js} +1 -1
- package/chunk-66qgm91k.js +708 -0
- package/{chunk-dj1pp4e3.js → chunk-675tr675.js} +1 -1
- package/{chunk-jwzhhhzn.js → chunk-6cgvzwec.js} +1 -1
- package/chunk-6k05kqnc.js +3 -0
- package/{chunk-py6sjyzk.js → chunk-6ytv1v2r.js} +1 -1
- package/{chunk-2h24va0j.js → chunk-75sc3adm.js} +1 -1
- package/chunk-7cexzg71.js +2 -0
- package/chunk-98rann8t.js +2 -0
- package/{chunk-b58pnj20.js → chunk-a16mz9zf.js} +1 -1
- package/{chunk-m1wp8cpt.js → chunk-afe25dpr.js} +1 -1
- package/{chunk-k3dpwjx4.js → chunk-ahm4zd56.js} +1 -1
- package/chunk-asvdqkdp.js +6 -0
- package/{chunk-rfrbnw8r.js → chunk-b3x80jc1.js} +1 -1
- package/{chunk-tr49kraa.js → chunk-cdqah1hy.js} +1 -1
- package/{chunk-r03aywsb.js → chunk-cqt1dpxd.js} +1 -1
- package/{chunk-xbhxzdat.js → chunk-d4r0myxr.js} +1 -1
- package/{chunk-km3z2czq.js → chunk-d9kx7v12.js} +1 -1
- package/{chunk-2y9nnzpr.js → chunk-df557bn4.js} +1 -1
- package/{chunk-6zr596r2.js → chunk-edgwx026.js} +1 -1
- package/chunk-emv1j50t.js +2 -0
- package/{chunk-jep31ffe.js → chunk-ewgdcgrk.js} +1 -1
- package/chunk-f45p6kcb.js +2 -0
- package/{chunk-b4jy4hcd.js → chunk-gn9wcc1b.js} +1 -1
- package/{chunk-cejmmx3r.js → chunk-hr3gbak5.js} +2 -2
- package/{chunk-91pajfnm.js → chunk-j7tzbt3d.js} +1 -1
- package/chunk-mm8wv3bg.js +3 -0
- package/chunk-nbbnb5e9.js +3 -0
- package/{chunk-a9mgvspm.js → chunk-ngbqd7kb.js} +32 -12
- package/chunk-nkqtnwzz.js +2 -0
- package/{chunk-2jbhv7vh.js → chunk-q3kf8243.js} +1 -1
- package/{chunk-8t5phg0s.js → chunk-qpv28z8d.js} +1 -1
- package/{chunk-hsrg23gd.js → chunk-rd65xm6r.js} +1 -1
- package/{chunk-aa3str7g.js → chunk-ryga3ny1.js} +1 -1
- package/{chunk-cvn689d5.js → chunk-sb5h27w9.js} +2 -2
- package/chunk-tw4kw4ps.js +22 -0
- package/chunk-v6c3h9ft.js +3 -0
- package/chunk-vgx47anc.js +3 -0
- package/chunk-vrvkvb0v.js +244 -0
- package/{chunk-db7mf114.js → chunk-vryadew3.js} +9 -7
- package/chunk-w6f40y2x.js +11 -0
- package/{chunk-0446t32z.js → chunk-we8hvy6e.js} +1 -1
- package/{chunk-ykdx12na.js → chunk-wkqrb2tz.js} +1 -1
- package/{chunk-r9ekd92e.js → chunk-xzkczmgg.js} +1 -1
- package/{chunk-yfttm20w.js → chunk-ycrddh47.js} +1 -1
- package/{chunk-j5axqyan.js → chunk-z78c1kf8.js} +1 -1
- package/cli.js +1 -1
- package/package.json +3 -1
- package/pkgs/provider-html/src/index.ts +68 -0
- package/pkgs/provider-html/src/registry.ts +121 -0
- package/pkgs/provider-html/src/version.ts +1 -0
- package/pkgs/provider-none/src/components-inline.tsx +12 -3
- package/pkgs/provider-none/src/components.tsx +11 -3
- package/pkgs/provider-none/src/manifest.ts +1 -0
- package/pkgs/schema/src/config.ts +33 -1
- package/pkgs/schema/src/css-declarations.ts +41 -0
- package/pkgs/schema/src/index.ts +3 -0
- package/pkgs/schema/src/theme.ts +40 -0
- package/plugins/claude/velloo/agents/velloo-design-reviewer.md +56 -13
- package/plugins/claude/velloo/agents/velloo-designer.md +4 -1
- package/skills/velloo-setup/SKILL.md +19 -55
- package/canvas/assets/index-B1OD0Yz5.css +0 -2
- package/canvas/assets/index-BWYFx2Rr.js +0 -29
- package/chunk-00t0bz7x.js +0 -244
- package/chunk-0zen7bzb.js +0 -3
- package/chunk-5ywkk7d6.js +0 -2
- package/chunk-b7famda5.js +0 -3
- package/chunk-f5g45xed.js +0 -2
- package/chunk-fjbzk4sw.js +0 -3
- package/chunk-hv52a6hf.js +0 -2
- package/chunk-mcymwefw.js +0 -5
- package/chunk-n0kx20s2.js +0 -2
- package/chunk-nwf2kn41.js +0 -26
- package/chunk-phz993p3.js +0 -2
- package/chunk-qan5dnjt.js +0 -688
- package/chunk-r5d0jsnd.js +0 -5
- package/chunk-shac6bat.js +0 -6
- package/chunk-t4zvcece.js +0 -10
- package/chunk-tarws372.js +0 -2
- package/chunk-yghypth2.js +0 -2
- package/chunk-zqtz23xk.js +0 -3
|
@@ -28,7 +28,7 @@ export const LibrarySchema = z.object({
|
|
|
28
28
|
* Component provider id. The server's provider loader maps ids to
|
|
29
29
|
* factories; `"shadcn-upstream"` is the default for new folders.
|
|
30
30
|
*/
|
|
31
|
-
id: z.enum(["shadcn-upstream", "none", "mui", "antd", "chakra"]),
|
|
31
|
+
id: z.enum(["shadcn-upstream", "none", "html", "mui", "antd", "chakra"]),
|
|
32
32
|
version: z.string().min(1),
|
|
33
33
|
source: z.enum(["binary", "cache", "in-repo"]),
|
|
34
34
|
/** Where the components live, relative to the design folder root. */
|
|
@@ -50,6 +50,19 @@ const CodegenConfigSchema = z.object({
|
|
|
50
50
|
|
|
51
51
|
export type CodegenConfig = z.infer<typeof CodegenConfigSchema>;
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* A path inside the host app, relative to its root. The config is committed
|
|
55
|
+
* with the repo and the agents are told to open what it names, so a cloned
|
|
56
|
+
* repo must not be able to aim it anywhere else: absolute paths and `..`
|
|
57
|
+
* segments make the file invalid rather than being resolved.
|
|
58
|
+
*/
|
|
59
|
+
const AppRelativePathSchema = z
|
|
60
|
+
.string()
|
|
61
|
+
.min(1)
|
|
62
|
+
.refine((path) => !/^(?:[\\/~]|[A-Za-z]:)/.test(path) && !path.split(/[\\/]/).includes(".."), {
|
|
63
|
+
message: "must be relative to the app root, with no absolute path or `..` segment",
|
|
64
|
+
});
|
|
65
|
+
|
|
53
66
|
/**
|
|
54
67
|
* Where the host app lives, used by the live-island bundler to resolve
|
|
55
68
|
* a `render:"live"` extension's `importPath` (and its dependencies, e.g.
|
|
@@ -73,6 +86,11 @@ export const HostAppSchema = z.object({
|
|
|
73
86
|
* built-in framework recipe, then no wrapper.
|
|
74
87
|
*/
|
|
75
88
|
preview: z.string().min(1).optional(),
|
|
89
|
+
/**
|
|
90
|
+
* The app's stylesheets an HTML design is styled by, in cascade order. The
|
|
91
|
+
* design keeps copies of them under `assets/host/` (`store_host_files`).
|
|
92
|
+
*/
|
|
93
|
+
stylesheets: z.array(z.string().regex(/^(?:\/(?!\/)|https:\/\/)/)).optional(),
|
|
76
94
|
/**
|
|
77
95
|
* Bounds repository-component discovery beyond what the app's entries and
|
|
78
96
|
* routes import: `include` adds component roots (files or directories,
|
|
@@ -171,6 +189,20 @@ export const ConfigSchema = z
|
|
|
171
189
|
styling: z.object({ framework: z.enum(["tailwind", "none"]) }).optional(),
|
|
172
190
|
/** Host app location for the live-island bundler. See `HostAppSchema`. */
|
|
173
191
|
hostApp: HostAppSchema.optional(),
|
|
192
|
+
/**
|
|
193
|
+
* A design-system document this folder follows — normally the repo's
|
|
194
|
+
* `DESIGN.md`.
|
|
195
|
+
*
|
|
196
|
+
* A path, never a copy. The file belongs to the repo and goes on being
|
|
197
|
+
* edited there; a copy inside the design folder would drift away from the
|
|
198
|
+
* rules it was supposed to state, which is the one thing a design system
|
|
199
|
+
* must not do. Relative to the host app root, so the usual value is
|
|
200
|
+
* `DESIGN.md`.
|
|
201
|
+
*
|
|
202
|
+
* Optional because the folder also finds one by convention — this records
|
|
203
|
+
* a file somewhere unconventional, or pins the choice when several exist.
|
|
204
|
+
*/
|
|
205
|
+
designSystem: z.object({ path: AppRelativePathSchema }).optional(),
|
|
174
206
|
/**
|
|
175
207
|
* Named host apps for monorepos — the multi-app twin of `hostApp`, keyed
|
|
176
208
|
* by a short app name (`"web"`, `"admin"`; `init`'s multi-app scan uses
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const CSS_PROPERTY = /^(--[\w-]+|-?[a-z][a-z-]*)$/;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CSS declaration text as a React style object (`background-size` ⇒
|
|
5
|
+
* `backgroundSize`, custom properties kept), or null when the text is not a
|
|
6
|
+
* declaration list — a class string, say. Semicolons inside `url(…)` or quotes
|
|
7
|
+
* do not split.
|
|
8
|
+
*/
|
|
9
|
+
export function styleObjectFromCss(text: string): Record<string, string> | null {
|
|
10
|
+
const declarations: string[] = [];
|
|
11
|
+
let depth = 0;
|
|
12
|
+
let quote: string | null = null;
|
|
13
|
+
let start = 0;
|
|
14
|
+
for (let i = 0; i < text.length; i++) {
|
|
15
|
+
const char = text[i];
|
|
16
|
+
if (quote) {
|
|
17
|
+
if (char === quote) quote = null;
|
|
18
|
+
} else if (char === '"' || char === "'") quote = char;
|
|
19
|
+
else if (char === "(") depth++;
|
|
20
|
+
else if (char === ")") depth = Math.max(0, depth - 1);
|
|
21
|
+
else if (char === ";" && depth === 0) {
|
|
22
|
+
declarations.push(text.slice(start, i));
|
|
23
|
+
start = i + 1;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
declarations.push(text.slice(start));
|
|
27
|
+
const style: Record<string, string> = {};
|
|
28
|
+
for (const declaration of declarations) {
|
|
29
|
+
if (declaration.trim() === "") continue;
|
|
30
|
+
const colon = declaration.indexOf(":");
|
|
31
|
+
if (colon < 0) return null;
|
|
32
|
+
const property = declaration.slice(0, colon).trim().toLowerCase();
|
|
33
|
+
const value = declaration.slice(colon + 1).trim();
|
|
34
|
+
if (!CSS_PROPERTY.test(property) || value === "") return null;
|
|
35
|
+
const key = property.startsWith("--")
|
|
36
|
+
? property
|
|
37
|
+
: property.replace(/^-(ms)-/, "$1-").replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());
|
|
38
|
+
style[key] = value;
|
|
39
|
+
}
|
|
40
|
+
return Object.keys(style).length > 0 ? style : null;
|
|
41
|
+
}
|
package/pkgs/schema/src/index.ts
CHANGED
|
@@ -56,6 +56,7 @@ export {
|
|
|
56
56
|
type ViewportPreset,
|
|
57
57
|
ViewportPresetSchema,
|
|
58
58
|
} from "./config.ts";
|
|
59
|
+
export { styleObjectFromCss } from "./css-declarations.ts";
|
|
59
60
|
export {
|
|
60
61
|
isCssIdent,
|
|
61
62
|
neutralizeCssText,
|
|
@@ -139,6 +140,8 @@ export {
|
|
|
139
140
|
ColorsSchema,
|
|
140
141
|
type LooseTokenGroup,
|
|
141
142
|
resolveColors,
|
|
143
|
+
spacingCssTokens,
|
|
144
|
+
TAILWIND_SIZE_NAMES,
|
|
142
145
|
type Theme,
|
|
143
146
|
ThemeSchema,
|
|
144
147
|
TypographySchema,
|
package/pkgs/schema/src/theme.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { isCssIdent } from "./css-sanitize.ts";
|
|
2
3
|
|
|
3
4
|
const NumOrCssLen = z.union([z.number(), z.string().min(1)]);
|
|
4
5
|
|
|
@@ -56,6 +57,45 @@ export type Colors = z.infer<typeof ColorsSchema>;
|
|
|
56
57
|
*/
|
|
57
58
|
export type ColorsOverride = { [K in keyof Colors]?: Colors[K] | undefined };
|
|
58
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Spacing names Tailwind v4 already reads as a size scale. Its `max-w-*`,
|
|
62
|
+
* `w-*` and `min-w-*` utilities resolve a `--spacing-<name>` ahead of
|
|
63
|
+
* `--container-<name>`, so a design system's t-shirt spacing (`xl: 1.5rem`)
|
|
64
|
+
* emitted under one of these names turns every `max-w-xl` into 24px and a
|
|
65
|
+
* paragraph into one word per line.
|
|
66
|
+
*/
|
|
67
|
+
export const TAILWIND_SIZE_NAMES: ReadonlySet<string> = new Set([
|
|
68
|
+
"3xs",
|
|
69
|
+
"2xs",
|
|
70
|
+
"xs",
|
|
71
|
+
"sm",
|
|
72
|
+
"md",
|
|
73
|
+
"lg",
|
|
74
|
+
"xl",
|
|
75
|
+
"2xl",
|
|
76
|
+
"3xl",
|
|
77
|
+
"4xl",
|
|
78
|
+
"5xl",
|
|
79
|
+
"6xl",
|
|
80
|
+
"7xl",
|
|
81
|
+
"prose",
|
|
82
|
+
]);
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The named spacing tokens that become `--spacing-<name>` in CSS. Numeric
|
|
86
|
+
* steps are Tailwind's built-in scale and names Tailwind reads as sizes stay
|
|
87
|
+
* in the theme only; the canvas stylesheet and the emitted globals.css both
|
|
88
|
+
* come from this list, so they cannot disagree.
|
|
89
|
+
*/
|
|
90
|
+
export function spacingCssTokens(spacing: Theme["spacing"]): Array<[string, string | number]> {
|
|
91
|
+
const out: Array<[string, string | number]> = [];
|
|
92
|
+
for (const [name, value] of Object.entries(spacing ?? {})) {
|
|
93
|
+
if (!Number.isNaN(Number(name)) || !isCssIdent(name) || TAILWIND_SIZE_NAMES.has(name)) continue;
|
|
94
|
+
if (typeof value === "string" || typeof value === "number") out.push([name, value]);
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
|
|
59
99
|
/**
|
|
60
100
|
* The effective colors for a mode: `colors`, with `colorsDark` layered on when
|
|
61
101
|
* rendering dark.
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: velloo-design-reviewer
|
|
3
3
|
description: >-
|
|
4
|
-
Reviews Velloo screens without modifying them —
|
|
5
|
-
token discipline, contrast, layout at mobile
|
|
6
|
-
live app page. Use after a design pass for an
|
|
4
|
+
Reviews Velloo screens without modifying them — the folder's own stated design
|
|
5
|
+
rules, dark-mode adaptation, theme token discipline, contrast, layout at mobile
|
|
6
|
+
width, and fidelity against a live app page. Use after a design pass for an
|
|
7
|
+
independent findings list.
|
|
7
8
|
---
|
|
8
9
|
|
|
9
10
|
You are a design reviewer for a Velloo design folder. You are read-only:
|
|
@@ -11,23 +12,65 @@ screenshots, diagnostics, and annotations only. Do NOT mutate screens, snippets,
|
|
|
11
12
|
or the theme — the one exception is `add_annotation`, to pin a finding to the
|
|
12
13
|
node it concerns.
|
|
13
14
|
|
|
15
|
+
Start with `get_theme`. Besides the tokens it may return `designSystem.path` —
|
|
16
|
+
the design system document this folder follows, normally the repo's
|
|
17
|
+
`DESIGN.md`. **Open that file and read it before looking at any screen.** Velloo
|
|
18
|
+
points at it rather than copying it, so the file you read is the current one.
|
|
19
|
+
Its "Do's and Don'ts" section is the house rules, and they outrank your taste.
|
|
20
|
+
|
|
14
21
|
For each target screen (default: every screen on the default board):
|
|
15
22
|
|
|
16
|
-
1.
|
|
23
|
+
1. **The folder's stated rules** — check the screens against the Do's and
|
|
24
|
+
Don'ts in the design system document. See below; this dimension comes first
|
|
25
|
+
because it is the only one that is not generic.
|
|
26
|
+
2. `screenshot mode: "compare"` — does the design actually adapt to dark
|
|
17
27
|
mode, or do raw palette colors freeze it?
|
|
18
|
-
|
|
28
|
+
3. The screenshot's `diagnostics` — read the `theme/raw-color` entries;
|
|
19
29
|
distinguish real token violations from intentional accents (`data-accent`
|
|
20
30
|
nodes are exempt).
|
|
21
|
-
|
|
31
|
+
4. `score_theme_contrast` — flag failing pairs; dark is where contrast
|
|
22
32
|
usually breaks.
|
|
23
|
-
|
|
33
|
+
5. `screenshot` at a mobile viewport (390 wide) — does the layout hold, or do
|
|
24
34
|
grids overflow and type sizes collapse?
|
|
25
|
-
|
|
35
|
+
6. If given a live URL, `compare_to_url` at the same viewport — report the
|
|
26
36
|
similarity score and what the per-region refs point at. An `unverified`
|
|
27
37
|
result is itself a finding: say so instead of guessing.
|
|
28
|
-
|
|
38
|
+
7. `list_annotations` — surface unresolved designer/reviewer notes.
|
|
39
|
+
|
|
40
|
+
## Reviewing against the folder's rules
|
|
41
|
+
|
|
42
|
+
Work rule by rule, not screen by screen — a rule like "one accent per screen"
|
|
43
|
+
is about the whole screen, and you will miss it if you are looking at nodes.
|
|
44
|
+
|
|
45
|
+
**Quote the rule verbatim in every finding it produces**, copied from the file.
|
|
46
|
+
The user wrote these; a finding that paraphrases them reads as your opinion, and
|
|
47
|
+
the point of this dimension is that it is not.
|
|
48
|
+
|
|
49
|
+
**Check only what you can actually see.** A rule is checkable when a screenshot
|
|
50
|
+
or a diagnostic settles it — how many accent colors a screen uses, whether a
|
|
51
|
+
flat surface carries a shadow, whether CTAs are pill-shaped, whether a heading
|
|
52
|
+
uses the display face. A rule is not checkable here when it is about behavior
|
|
53
|
+
(motion, transitions, hover, focus order), about content or tone, or about
|
|
54
|
+
things outside the screens you were given.
|
|
55
|
+
|
|
56
|
+
For an unverifiable rule, say so once in a short list at the end — "not checked:
|
|
57
|
+
rules 4, 7 (both about hover states)" — and move on. Do not guess, do not
|
|
58
|
+
soften it into a maybe, and do not pad the review by restating rules you did not
|
|
59
|
+
test. An honest "not checked" is worth more than a speculative finding.
|
|
60
|
+
|
|
61
|
+
When a rule and one of the generic dimensions disagree, report both and say
|
|
62
|
+
which is which. If the folder's rule is the reason something looks wrong to
|
|
63
|
+
you, the rule wins and there is no finding.
|
|
64
|
+
|
|
65
|
+
If `get_theme` returns no `designSystem`, or the document has no Do's and Don'ts
|
|
66
|
+
section, the folder states no rules. Say that once — it is not the same as the
|
|
67
|
+
screens passing — and review the other dimensions normally. Do not invent house
|
|
68
|
+
rules to fill the gap. If the path is there but you cannot open the file, say
|
|
69
|
+
that too rather than reviewing as if it were empty.
|
|
70
|
+
|
|
71
|
+
## Reporting
|
|
29
72
|
|
|
30
|
-
Report findings ranked by severity. Each finding: screen id, node ref
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
thing you'd fix first.
|
|
73
|
+
Report findings ranked by severity. Each finding: screen id, node ref (`"@id"`
|
|
74
|
+
or path), what is wrong, and a concrete fix. A rule-based finding also carries
|
|
75
|
+
the quoted rule. Pin the top findings with `add_annotation` so
|
|
76
|
+
they're addressable on the canvas. End with the one thing you'd fix first.
|
|
@@ -16,7 +16,10 @@ Workflow:
|
|
|
16
16
|
|
|
17
17
|
1. **Discover before composing.** `list_components` (`mode: "summary"` first),
|
|
18
18
|
`get_theme`, `list_components` (`kind: "snippet"`), `list_boards`. Reuse existing snippets before
|
|
19
|
-
defining new ones.
|
|
19
|
+
defining new ones. When `get_theme` returns a `designSystem.path`, open that
|
|
20
|
+
file and read it: it is the folder's own design system in prose — house rules
|
|
21
|
+
that outrank the defaults below, and what the reviewer will check this work
|
|
22
|
+
against. Velloo points at the file rather than copying it, so it is current.
|
|
20
23
|
2. **Build in big strokes.** `compose` accepts a full subtree as restricted
|
|
21
24
|
JSX — a whole section per call, not node-by-node; `batch` groups mutations
|
|
22
25
|
atomically. Repeated structure (cards, rows, nav items) becomes a snippet
|
|
@@ -6,7 +6,8 @@ description: >-
|
|
|
6
6
|
verify the canvas against the running app. Use once per repo before the first
|
|
7
7
|
design task, and again when the app's design language changes. Triggers:
|
|
8
8
|
"set up velloo for this repo", "make the preview match my app", "why doesn't
|
|
9
|
-
the canvas look like my app", or
|
|
9
|
+
the canvas look like my app", or before recreating an app's page from an
|
|
10
|
+
init handoff prompt.
|
|
10
11
|
---
|
|
11
12
|
|
|
12
13
|
# Calibrating Velloo to an existing app
|
|
@@ -44,60 +45,23 @@ halfway through a design.
|
|
|
44
45
|
tool re-probes and answers with the new state; iterate until `valid`.
|
|
45
46
|
|
|
46
47
|
A snippet can't do this job: it is node data, so it can't import CSS, build a
|
|
47
|
-
router or wrap the whole screen. Snippets are for step
|
|
48
|
-
|
|
49
|
-
## 1. Theme
|
|
50
|
-
|
|
51
|
-
`import_theme { cssPath, apply: true }` against the app's stylesheet. This is
|
|
52
|
-
not just colors: it ingests `--radius`, `--font-*` roles, the non-semantic
|
|
53
|
-
palette (`--primary-600`, `--ink`), and — when it finds a `tailwind.config`
|
|
54
|
-
nearby — that config's `theme.extend` spacing, shadows, fonts, keyframes, and
|
|
55
|
-
`container` settings. Run it dry first (the default) and read the reported
|
|
56
|
-
changes; `apply: true` when they look like the app.
|
|
57
|
-
|
|
58
|
-
Init records the stylesheet it found in the folder config, so check there
|
|
59
|
-
before hunting. If there's no stylesheet (a `none`-CSS folder, an MUI app),
|
|
60
|
-
`import_theme` still takes raw `css` text, and an MUI app's `createTheme` call
|
|
61
|
-
is the equivalent source.
|
|
62
|
-
|
|
63
|
-
**Fonts are the highest-leverage single token.** A design in the wrong typeface
|
|
64
|
-
reads as wrong no matter how correct the layout is. If `import_theme` didn't
|
|
65
|
-
resolve real families, set them with `set_theme { fonts }` — read the app's font loading
|
|
66
|
-
(next/font, a `@font-face`, a Google Fonts link) to get the actual names.
|
|
67
|
-
|
|
68
|
-
Then `score_theme_contrast` to confirm the imported palette holds up in both
|
|
69
|
-
modes; an app that only ever ships light mode often imports into a dark palette
|
|
70
|
-
that fails.
|
|
71
|
-
|
|
72
|
-
## 2. Verify against the real thing, early
|
|
73
|
-
|
|
74
|
-
Do not wait until a design is finished to discover the baseline was wrong.
|
|
75
|
-
Recreate one representative screen — or use whatever init scaffolded — and run
|
|
76
|
-
`compare_to_url` against the running app.
|
|
77
|
-
|
|
78
|
-
- Get the app running first. Start its dev server yourself if the package
|
|
79
|
-
scripts make it obvious; otherwise ask the user for the command, a running
|
|
80
|
-
URL, or a deployed preview. Ask once, plainly, rather than guessing ports.
|
|
81
|
-
- **Read `unverified` on every result.** `redirected` / `authWall` means you
|
|
82
|
-
captured a login page, so the similarity number is meaningless.
|
|
83
|
-
- **For a login wall, use a capture session.** `start_capture_session { url }`
|
|
84
|
-
opens a real browser the *user* drives. It returns immediately — it does not
|
|
85
|
-
wait — so say plainly what you need ("log in, then hit **Capture page** in the
|
|
86
|
-
velloo toolbar on each page, then **Done**") and poll `list_captures` until
|
|
87
|
-
their captures land. Then verify with `compare_to_url { captureId }` instead
|
|
88
|
-
of `url`: the capture is already past the login and frozen, so it can't bounce
|
|
89
|
-
to a login page or drift between runs. `storageStatePath` / `cookies` /
|
|
90
|
-
`localStorage` stay available for when you already hold a session.
|
|
91
|
-
- If you cannot get a real capture, **leave it unverified and say so.** Tuning a
|
|
92
|
-
design toward a page you never saw is worse than admitting the gap.
|
|
93
|
-
- For a dashboard or feed whose content shifts between loads, pass
|
|
94
|
-
`cacheUrl: true` so you diff against one frozen capture instead of drifting
|
|
95
|
-
content.
|
|
48
|
+
router or wrap the whole screen. Snippets are for step 3 below.
|
|
49
|
+
|
|
50
|
+
## 1. Theme, fonts and the compare loop
|
|
96
51
|
|
|
97
|
-
|
|
98
|
-
|
|
52
|
+
The MCP instructions and `velloo://guide/porting` own this part: import the
|
|
53
|
+
app's stylesheet with `import_theme` (dry run, then `apply: true`), set its real
|
|
54
|
+
fonts with `set_theme { fonts }` — read how the app loads them; a wrong typeface
|
|
55
|
+
makes a correct layout read as wrong — then recreate one representative screen
|
|
56
|
+
and iterate `compare_to_url` against the running app, reading `unverified`
|
|
57
|
+
before trusting any number. Two additions that live only here:
|
|
58
|
+
|
|
59
|
+
- Run `score_theme_contrast` after the import. An app that only ever ships
|
|
60
|
+
light mode often imports into a dark palette that fails.
|
|
61
|
+
- For a dashboard or feed whose content shifts between loads, pass
|
|
62
|
+
`cacheUrl: true` so you diff against one frozen capture.
|
|
99
63
|
|
|
100
|
-
##
|
|
64
|
+
## 2. Establish component fidelity before adapting anything
|
|
101
65
|
|
|
102
66
|
Only after theme parity, call `component_status { screen: "<id>" }` for each
|
|
103
67
|
screen you're about to design or verify (or `{ ids: [...] }` before a screen
|
|
@@ -128,7 +92,7 @@ Host-source edits invalidate the canvas bundle automatically. After changing a
|
|
|
128
92
|
component, wait for the frame to reload and call `component_status` again; do
|
|
129
93
|
not restart the daemon merely to pick up a normal source edit.
|
|
130
94
|
|
|
131
|
-
##
|
|
95
|
+
## 3. Close only the important remaining gaps
|
|
132
96
|
|
|
133
97
|
If an on-screen component is not `exact` and the difference is load-bearing,
|
|
134
98
|
choose the smallest honest adaptation:
|
|
@@ -153,7 +117,7 @@ the adaptation is what keeps dialogs, menus, popovers, and similar components
|
|
|
153
117
|
visible and selectable on a static canvas. Adapt only when the visual contract
|
|
154
118
|
the user cares about is materially different.
|
|
155
119
|
|
|
156
|
-
##
|
|
120
|
+
## 4. Leave a record
|
|
157
121
|
|
|
158
122
|
Write what you found as a note on the main board (`add_note`) so the next
|
|
159
123
|
session — and the user — knows where things stand. Three headings, honest:
|