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,40 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ResourceIdSchema } from "./ids.ts";
|
|
3
|
+
|
|
4
|
+
export const FrameSchemeSchema = z.enum(["light", "dark"]);
|
|
5
|
+
export type FrameScheme = z.infer<typeof FrameSchemeSchema>;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A Frame is a placement of a Screen on the Board: position, size, optional
|
|
9
|
+
* label, optional group. Multiple frames can reference the same screen at
|
|
10
|
+
* different sizes — edits to that screen's tree update every frame that
|
|
11
|
+
* shows it. The canvas renders visual linkage so siblings are obvious.
|
|
12
|
+
*
|
|
13
|
+
* Frames are freely resizable on the canvas. Snap-to-viewport-preset is a
|
|
14
|
+
* UI affordance; the underlying w/h is just a number.
|
|
15
|
+
*/
|
|
16
|
+
export const FrameSchema = z.object({
|
|
17
|
+
id: ResourceIdSchema,
|
|
18
|
+
/** The Screen id this frame renders. */
|
|
19
|
+
screen: z.string().min(1),
|
|
20
|
+
x: z.number(),
|
|
21
|
+
y: z.number(),
|
|
22
|
+
w: z.number().int().positive(),
|
|
23
|
+
h: z.number().int().positive(),
|
|
24
|
+
/** Optional human label shown above the frame on the canvas. */
|
|
25
|
+
label: z.string().optional(),
|
|
26
|
+
/** Optional BoardGroup id. */
|
|
27
|
+
group: z.string().optional(),
|
|
28
|
+
/** Pinned render scheme; absent means follow the canvas default. */
|
|
29
|
+
scheme: FrameSchemeSchema.optional(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export type Frame = z.infer<typeof FrameSchema>;
|
|
33
|
+
|
|
34
|
+
/** Resolve a frame pin against the current canvas-level default. */
|
|
35
|
+
export function resolveFrameScheme(
|
|
36
|
+
frame: Pick<Frame, "scheme">,
|
|
37
|
+
canvasDefault: FrameScheme,
|
|
38
|
+
): FrameScheme {
|
|
39
|
+
return frame.scheme ?? canvasDefault;
|
|
40
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lucide icon names are PascalCase exports ("SquarePen"), but lucide's
|
|
3
|
+
* own site and most docs present kebab-case ids ("square-pen") — agents
|
|
4
|
+
* pass both. Shared normalizer so the runtime Icon helper, codegen's
|
|
5
|
+
* lucide import resolver, and the server's advisory prop warnings all
|
|
6
|
+
* agree on what resolves.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** "square-pen" / "square_pen" / "square pen" → "SquarePen". PascalCase input passes through. */
|
|
10
|
+
export function pascalizeIconName(name: string): string {
|
|
11
|
+
const trimmed = name.trim();
|
|
12
|
+
if (trimmed === "") return trimmed;
|
|
13
|
+
if (!/[-_\s]/.test(trimmed)) {
|
|
14
|
+
return trimmed.charAt(0).toUpperCase() + trimmed.slice(1);
|
|
15
|
+
}
|
|
16
|
+
return trimmed
|
|
17
|
+
.split(/[-_\s]+/)
|
|
18
|
+
.filter((part) => part !== "")
|
|
19
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
20
|
+
.join("");
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The one id format for on-disk resources (screens, boards, groups, frames,
|
|
5
|
+
* snippets, annotations, notes): URL- and filename-safe, no dots (filename
|
|
6
|
+
* stems double as ids and the loader treats dotted stems as sidecars), max 64.
|
|
7
|
+
* Node `$id`s are stricter (letter-led — see `NodeIdSchema`) because they
|
|
8
|
+
* double as `@id` locator references.
|
|
9
|
+
*/
|
|
10
|
+
export const ResourceIdSchema = z
|
|
11
|
+
.string()
|
|
12
|
+
.regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/, "ids are [A-Za-z0-9][A-Za-z0-9_-]{0,63}");
|
|
13
|
+
|
|
14
|
+
export type ResourceId = z.infer<typeof ResourceIdSchema>;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
export {
|
|
2
|
+
type Annotation,
|
|
3
|
+
AnnotationSchema,
|
|
4
|
+
type AnnotationTarget,
|
|
5
|
+
AnnotationTargetSchema,
|
|
6
|
+
type CanvasNote,
|
|
7
|
+
type CanvasNoteAttachment,
|
|
8
|
+
CanvasNoteAttachmentSchema,
|
|
9
|
+
CanvasNoteSchema,
|
|
10
|
+
} from "./annotation.ts";
|
|
11
|
+
export {
|
|
12
|
+
type AssetsFile,
|
|
13
|
+
AssetsFileSchema,
|
|
14
|
+
assetPathFromSrc,
|
|
15
|
+
EMPTY_ASSETS_FILE,
|
|
16
|
+
type GeneratedAsset,
|
|
17
|
+
GeneratedAssetSchema,
|
|
18
|
+
} from "./asset.ts";
|
|
19
|
+
export {
|
|
20
|
+
type Board,
|
|
21
|
+
type BoardGroup,
|
|
22
|
+
BoardGroupSchema,
|
|
23
|
+
BoardSchema,
|
|
24
|
+
isArchived,
|
|
25
|
+
MAX_BOARD_NAME_LENGTH,
|
|
26
|
+
} from "./board.ts";
|
|
27
|
+
export {
|
|
28
|
+
type CommentAnchor,
|
|
29
|
+
CommentAnchorSchema,
|
|
30
|
+
type CommentAnchorState,
|
|
31
|
+
type CommentAuthor,
|
|
32
|
+
CommentAuthorSchema,
|
|
33
|
+
type CommentBounds,
|
|
34
|
+
CommentBoundsSchema,
|
|
35
|
+
type CommentLocator,
|
|
36
|
+
CommentLocatorSchema,
|
|
37
|
+
type CommentMessage,
|
|
38
|
+
CommentMessageSchema,
|
|
39
|
+
type CommentNodeFingerprint,
|
|
40
|
+
CommentNodeFingerprintSchema,
|
|
41
|
+
type CommentOrigin,
|
|
42
|
+
CommentOriginSchema,
|
|
43
|
+
type CommentThread,
|
|
44
|
+
CommentThreadSchema,
|
|
45
|
+
type CommentThreadView,
|
|
46
|
+
} from "./comment.ts";
|
|
47
|
+
export {
|
|
48
|
+
type CodegenConfig,
|
|
49
|
+
type Config,
|
|
50
|
+
ConfigSchema,
|
|
51
|
+
type HostApp,
|
|
52
|
+
HostAppSchema,
|
|
53
|
+
type Library,
|
|
54
|
+
LibrarySchema,
|
|
55
|
+
type ViewportPreset,
|
|
56
|
+
ViewportPresetSchema,
|
|
57
|
+
} from "./config.ts";
|
|
58
|
+
export {
|
|
59
|
+
isCssIdent,
|
|
60
|
+
neutralizeCssText,
|
|
61
|
+
sanitizeCssTokenValue,
|
|
62
|
+
sanitizeGoogleFontSpec,
|
|
63
|
+
} from "./css-sanitize.ts";
|
|
64
|
+
export {
|
|
65
|
+
type Extension,
|
|
66
|
+
type ExtensionPropDescriptor,
|
|
67
|
+
ExtensionPropDescriptorSchema,
|
|
68
|
+
ExtensionSchema,
|
|
69
|
+
} from "./extension.ts";
|
|
70
|
+
export {
|
|
71
|
+
type Frame,
|
|
72
|
+
FrameSchema,
|
|
73
|
+
type FrameScheme,
|
|
74
|
+
FrameSchemeSchema,
|
|
75
|
+
resolveFrameScheme,
|
|
76
|
+
} from "./frame.ts";
|
|
77
|
+
export { pascalizeIconName } from "./icon-name.ts";
|
|
78
|
+
export { type ResourceId, ResourceIdSchema } from "./ids.ts";
|
|
79
|
+
export {
|
|
80
|
+
APP_PATH_PREFIX,
|
|
81
|
+
CURRENT_SCHEMA_VERSION,
|
|
82
|
+
FOLDER_MIGRATIONS,
|
|
83
|
+
type FolderMigration,
|
|
84
|
+
type MigrationContext,
|
|
85
|
+
type MigrationRun,
|
|
86
|
+
planMigration,
|
|
87
|
+
schemaVersionOf,
|
|
88
|
+
} from "./migrate.ts";
|
|
89
|
+
export {
|
|
90
|
+
type ComponentNode,
|
|
91
|
+
isComponentNode,
|
|
92
|
+
isParamRef,
|
|
93
|
+
isSnippetInstance,
|
|
94
|
+
type Node,
|
|
95
|
+
NodeIdSchema,
|
|
96
|
+
NodeSchema,
|
|
97
|
+
nodeId,
|
|
98
|
+
type ParamRef,
|
|
99
|
+
type SnippetInstance,
|
|
100
|
+
} from "./node.ts";
|
|
101
|
+
export {
|
|
102
|
+
DesignNameSchema,
|
|
103
|
+
designNameIssue,
|
|
104
|
+
type FeedbackPrefs,
|
|
105
|
+
FeedbackPrefsSchema,
|
|
106
|
+
isDesignName,
|
|
107
|
+
type NormalizedRepoManifest,
|
|
108
|
+
normalizeRepoManifest,
|
|
109
|
+
REPO_MANIFEST_FILE,
|
|
110
|
+
type RepoManifest,
|
|
111
|
+
RepoManifestSchema,
|
|
112
|
+
toDesignName,
|
|
113
|
+
} from "./repo.ts";
|
|
114
|
+
export { type Screen, ScreenSchema } from "./screen.ts";
|
|
115
|
+
export { type Snippet, type SnippetParam, SnippetParamSchema, SnippetSchema } from "./snippet.ts";
|
|
116
|
+
export {
|
|
117
|
+
applySnippetExtraClassName,
|
|
118
|
+
applySnippetOverrides,
|
|
119
|
+
type InvalidParamPlacement,
|
|
120
|
+
resolveSnippetArgs,
|
|
121
|
+
substituteSnippetParams,
|
|
122
|
+
} from "./snippet-resolve.ts";
|
|
123
|
+
export { sanitizeSvgMarkup, svgLooksActive } from "./svg-sanitize.ts";
|
|
124
|
+
export {
|
|
125
|
+
type ColorPair,
|
|
126
|
+
type Colors,
|
|
127
|
+
type ColorsOverride,
|
|
128
|
+
ColorsSchema,
|
|
129
|
+
type LooseTokenGroup,
|
|
130
|
+
resolveColors,
|
|
131
|
+
type Theme,
|
|
132
|
+
ThemeSchema,
|
|
133
|
+
TypographySchema,
|
|
134
|
+
} from "./theme.ts";
|
|
135
|
+
export {
|
|
136
|
+
DEFAULT_TYPESET_NAME,
|
|
137
|
+
HEADING_ROLE_BY_LEVEL,
|
|
138
|
+
headingClasses,
|
|
139
|
+
headingInlineStyle,
|
|
140
|
+
isTypesetName,
|
|
141
|
+
type ResolvedTypesetRole,
|
|
142
|
+
resolveHeadingLevel,
|
|
143
|
+
resolveTypeset,
|
|
144
|
+
TEXT_ROLE_BY_VARIANT,
|
|
145
|
+
TEXT_TONE_BY_VARIANT,
|
|
146
|
+
type TextVariant,
|
|
147
|
+
TYPESET_CLASSES,
|
|
148
|
+
TYPESET_DEFAULT,
|
|
149
|
+
TYPESET_INLINE_STYLE,
|
|
150
|
+
TYPESET_RATIOS,
|
|
151
|
+
TYPESET_SCALE_NAMES,
|
|
152
|
+
type Typeset,
|
|
153
|
+
type TypesetRatio,
|
|
154
|
+
type TypesetRole,
|
|
155
|
+
type TypesetScale,
|
|
156
|
+
textClasses,
|
|
157
|
+
textInlineStyle,
|
|
158
|
+
typesetBaseVars,
|
|
159
|
+
typesetCss,
|
|
160
|
+
typesetSafelist,
|
|
161
|
+
typesetScale,
|
|
162
|
+
typesetSizePx,
|
|
163
|
+
typesetThemeTokens,
|
|
164
|
+
typesetUtilityClasses,
|
|
165
|
+
typesetV3FontSize,
|
|
166
|
+
typesetVars,
|
|
167
|
+
} from "./typeset.ts";
|
|
168
|
+
export { type DuplicateId, findDuplicateIds } from "./validate-ids.ts";
|
|
169
|
+
export { type Viewport, ViewportSchema } from "./viewport.ts";
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* On-disk format versioning + migrations.
|
|
3
|
+
*
|
|
4
|
+
* A design folder's format version lives in `.design/config.json` →
|
|
5
|
+
* `schemaVersion` (one version for the whole folder; individual files carry
|
|
6
|
+
* none). The loader refuses folders whose version doesn't match
|
|
7
|
+
* {@link CURRENT_SCHEMA_VERSION} — older folders are migrated on disk by
|
|
8
|
+
* `velloo upgrade`, newer ones need a newer binary.
|
|
9
|
+
*
|
|
10
|
+
* Migrations here are PURE raw-JSON → raw-JSON transforms (this package does
|
|
11
|
+
* no I/O). The CLI's `upgrade` command owns reading/writing files and applies
|
|
12
|
+
* these in order; each step migrates exactly one version.
|
|
13
|
+
*
|
|
14
|
+
* History:
|
|
15
|
+
* 1 — the original format: legacy single-`library` config still parsed,
|
|
16
|
+
* `source` was free-form ("embedded:shadcn"/"registry:shadcn" aliases),
|
|
17
|
+
* the vendored `shadcn-react` provider was a valid library id, and
|
|
18
|
+
* `Annotation.author` was optional (absent = "user").
|
|
19
|
+
* 2 — multi-library only (`libraries` + `defaultLibrary`), `source` is the
|
|
20
|
+
* enum "binary" | "cache" | "in-repo", `shadcn-react` retired in favor
|
|
21
|
+
* of `shadcn-upstream` (same canvas runtime, real install deferred to
|
|
22
|
+
* the host app), `author` required.
|
|
23
|
+
* 3 — typography is typesets. The `fontSize` / `fontWeight` / `lineHeight` /
|
|
24
|
+
* `letterSpacing` records are gone and `typography` parses strictly, so
|
|
25
|
+
* a v2 theme file is now a hard validation failure rather than dead
|
|
26
|
+
* weight — hence the version gate.
|
|
27
|
+
* 4 — designs carry their own `name` (formerly the `velloo.json` project
|
|
28
|
+
* key or a local record's `projectName`), and app-relative paths use the
|
|
29
|
+
* `app:` prefix instead of `project:`.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { isDesignName, toDesignName } from "./repo.ts";
|
|
33
|
+
|
|
34
|
+
export const CURRENT_SCHEMA_VERSION = 4;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Read the format version off a raw (unvalidated) config object. Historical
|
|
38
|
+
* folders always wrote `schemaVersion: 1`, so anything absent or malformed is
|
|
39
|
+
* treated as 1 rather than an error — `velloo upgrade` then normalizes it.
|
|
40
|
+
*/
|
|
41
|
+
export function schemaVersionOf(rawConfig: unknown): number {
|
|
42
|
+
if (typeof rawConfig !== "object" || rawConfig === null) return 1;
|
|
43
|
+
const v = (rawConfig as Record<string, unknown>).schemaVersion;
|
|
44
|
+
return typeof v === "number" && Number.isInteger(v) && v >= 1 ? v : 1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
type RawObject = Record<string, unknown>;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Facts a pure migration can't read for itself. The CLI's upgrade supplies
|
|
51
|
+
* them from the files around the folder.
|
|
52
|
+
*/
|
|
53
|
+
export interface MigrationContext {
|
|
54
|
+
/** The design's name as its registration recorded it. */
|
|
55
|
+
name?: string | undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface FolderMigration {
|
|
59
|
+
/** Migrates from exactly this version to `from + 1`. */
|
|
60
|
+
from: number;
|
|
61
|
+
/** One-line, user-facing description (printed by `velloo upgrade`). */
|
|
62
|
+
summary: string;
|
|
63
|
+
/** Transform the raw config.json object (already shallow-cloned). */
|
|
64
|
+
config?(raw: RawObject, context: MigrationContext): RawObject;
|
|
65
|
+
/** Transform one annotation entry from a `screens/<id>.annotations.json` sidecar. */
|
|
66
|
+
annotation?(raw: RawObject): RawObject;
|
|
67
|
+
/** Transform one `theme/<name>.json` document. */
|
|
68
|
+
theme?(raw: RawObject): RawObject;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Prefix of a path resolved from the design's application root. */
|
|
72
|
+
export const APP_PATH_PREFIX = "app:";
|
|
73
|
+
const LEGACY_APP_PREFIX = "project:";
|
|
74
|
+
|
|
75
|
+
/** Library id assigned to a legacy single-library config when it is promoted. */
|
|
76
|
+
const LEGACY_LIBRARY_KEY = "default";
|
|
77
|
+
|
|
78
|
+
function migrateLibraryEntry(raw: unknown): unknown {
|
|
79
|
+
if (typeof raw !== "object" || raw === null) return raw;
|
|
80
|
+
const lib = { ...(raw as RawObject) };
|
|
81
|
+
// Pre-v2 source vocabulary: both aliases meant "the snapshot inside the
|
|
82
|
+
// velloo binary".
|
|
83
|
+
if (lib.source === "embedded:shadcn" || lib.source === "registry:shadcn") {
|
|
84
|
+
lib.source = "binary";
|
|
85
|
+
lib.componentsPath = "binary";
|
|
86
|
+
}
|
|
87
|
+
// The vendored snapshot is no longer a user-facing provider. shadcn-upstream
|
|
88
|
+
// keeps the same canvas runtime; a real install into the host app is offered
|
|
89
|
+
// at agent handoff, so `componentsPath: "binary"` remains valid.
|
|
90
|
+
if (lib.id === "shadcn-react") {
|
|
91
|
+
lib.id = "shadcn-upstream";
|
|
92
|
+
}
|
|
93
|
+
return lib;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The typography records the typeset replaced. All four were declared, written
|
|
98
|
+
* by every preset, and read by nothing that painted — the sizes that actually
|
|
99
|
+
* rendered were literal utility classes. `TypographySchema` is strict now, so
|
|
100
|
+
* they have to go rather than linger as ignored keys.
|
|
101
|
+
*
|
|
102
|
+
* `fontWeight` and `letterSpacing` have no destination: both are per-role
|
|
103
|
+
* constants in the ratio table, deliberately not among the three controls.
|
|
104
|
+
*/
|
|
105
|
+
const RETIRED_TYPOGRAPHY_KEYS = ["fontSize", "fontWeight", "lineHeight", "letterSpacing"] as const;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Font *role* names to bind each typeset face to, in preference order. A theme
|
|
109
|
+
* names its families freely, so rather than guess we look for the keys velloo's
|
|
110
|
+
* own scaffolds, wizard, and CSS import have always written.
|
|
111
|
+
*/
|
|
112
|
+
const FACE_ROLE_CANDIDATES: Record<"fontBody" | "fontHeading" | "fontMono", string[]> = {
|
|
113
|
+
fontBody: ["sans", "body", "base"],
|
|
114
|
+
fontHeading: ["display", "heading", "serif"],
|
|
115
|
+
fontMono: ["mono", "code"],
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/** The base size the ratio table already assumes, in px. */
|
|
119
|
+
const IMPLIED_BASE_PX = 16;
|
|
120
|
+
|
|
121
|
+
function asObject(value: unknown): RawObject | undefined {
|
|
122
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
123
|
+
? (value as RawObject)
|
|
124
|
+
: undefined;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function positiveNumber(value: unknown): number | undefined {
|
|
128
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Read a v2 theme's typography intent into a typeset. Only `fontSize.base` and
|
|
133
|
+
* `lineHeight.normal` carry over — the rest of both records described a ladder
|
|
134
|
+
* the typeset now derives, so restating any of it would freeze proportions that
|
|
135
|
+
* are supposed to move with the three controls.
|
|
136
|
+
*/
|
|
137
|
+
function deriveTypeset(typography: RawObject): RawObject {
|
|
138
|
+
const derived: RawObject = {};
|
|
139
|
+
|
|
140
|
+
// 16px is what the ladder scales from anyway, so a folder that never tuned
|
|
141
|
+
// the base keeps the container-relative `1em` default instead of being pinned
|
|
142
|
+
// to an absolute size it never asked for.
|
|
143
|
+
const base = positiveNumber(asObject(typography.fontSize)?.base);
|
|
144
|
+
if (base !== undefined && base !== IMPLIED_BASE_PX) derived.size = base;
|
|
145
|
+
|
|
146
|
+
const leading = positiveNumber(asObject(typography.lineHeight)?.normal);
|
|
147
|
+
if (leading !== undefined) derived.leading = leading;
|
|
148
|
+
|
|
149
|
+
const families = asObject(typography.fontFamily);
|
|
150
|
+
if (families) {
|
|
151
|
+
for (const [face, candidates] of Object.entries(FACE_ROLE_CANDIDATES)) {
|
|
152
|
+
const role = candidates.find((name) => typeof families[name] === "string");
|
|
153
|
+
if (role) derived[face] = role;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return derived;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export const FOLDER_MIGRATIONS: FolderMigration[] = [
|
|
161
|
+
{
|
|
162
|
+
from: 1,
|
|
163
|
+
summary:
|
|
164
|
+
"multi-library config shape, source enum, shadcn-react → shadcn-upstream, required annotation author",
|
|
165
|
+
config(raw) {
|
|
166
|
+
const out = { ...raw };
|
|
167
|
+
if (out.library !== undefined && out.libraries === undefined) {
|
|
168
|
+
out.libraries = { [LEGACY_LIBRARY_KEY]: out.library };
|
|
169
|
+
out.defaultLibrary = LEGACY_LIBRARY_KEY;
|
|
170
|
+
}
|
|
171
|
+
delete out.library;
|
|
172
|
+
if (typeof out.libraries === "object" && out.libraries !== null) {
|
|
173
|
+
out.libraries = Object.fromEntries(
|
|
174
|
+
Object.entries(out.libraries as RawObject).map(([id, lib]) => [
|
|
175
|
+
id,
|
|
176
|
+
migrateLibraryEntry(lib),
|
|
177
|
+
]),
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
out.schemaVersion = 2;
|
|
181
|
+
return out;
|
|
182
|
+
},
|
|
183
|
+
annotation(raw) {
|
|
184
|
+
if (raw.author === undefined) return { ...raw, author: "user" };
|
|
185
|
+
return raw;
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
from: 2,
|
|
190
|
+
summary:
|
|
191
|
+
"typography scales → typesets (font roles bound, dead size/weight/tracking records dropped)",
|
|
192
|
+
config(raw) {
|
|
193
|
+
return { ...raw, schemaVersion: 3 };
|
|
194
|
+
},
|
|
195
|
+
theme(raw) {
|
|
196
|
+
const typography = asObject(raw.typography);
|
|
197
|
+
if (!typography) return raw;
|
|
198
|
+
const next = { ...typography };
|
|
199
|
+
|
|
200
|
+
// Binding the font roles is the load-bearing half of this: without
|
|
201
|
+
// `fontBody` a migrated folder's typeset regions resolve to `inherit` and
|
|
202
|
+
// quietly stop using the theme's own faces.
|
|
203
|
+
const typesets = asObject(next.typesets);
|
|
204
|
+
if (typesets?.default === undefined) {
|
|
205
|
+
const derived = deriveTypeset(next);
|
|
206
|
+
if (Object.keys(derived).length > 0) {
|
|
207
|
+
next.typesets = { ...typesets, default: derived };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
for (const key of RETIRED_TYPOGRAPHY_KEYS) delete next[key];
|
|
212
|
+
return { ...raw, typography: next };
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
from: 3,
|
|
217
|
+
summary: "the design's name moves into its config; `project:` paths become `app:`",
|
|
218
|
+
config(raw, context) {
|
|
219
|
+
const out: RawObject = { ...raw, schemaVersion: 4 };
|
|
220
|
+
if (typeof out.name !== "string" || !isDesignName(out.name)) {
|
|
221
|
+
out.name = (context.name && toDesignName(context.name)) ?? "design";
|
|
222
|
+
}
|
|
223
|
+
const toApp = (path: unknown) =>
|
|
224
|
+
typeof path === "string" && path.startsWith(LEGACY_APP_PREFIX)
|
|
225
|
+
? `${APP_PATH_PREFIX}${path.slice(LEGACY_APP_PREFIX.length)}`
|
|
226
|
+
: path;
|
|
227
|
+
const hostApp = asObject(out.hostApp);
|
|
228
|
+
if (hostApp) out.hostApp = { ...hostApp, root: toApp(hostApp.root) };
|
|
229
|
+
const hostApps = asObject(out.hostApps);
|
|
230
|
+
if (hostApps) {
|
|
231
|
+
out.hostApps = Object.fromEntries(
|
|
232
|
+
Object.entries(hostApps).map(([key, app]) => {
|
|
233
|
+
const entry = asObject(app);
|
|
234
|
+
return [key, entry ? { ...entry, root: toApp(entry.root) } : app];
|
|
235
|
+
}),
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
const libraries = asObject(out.libraries);
|
|
239
|
+
if (libraries) {
|
|
240
|
+
out.libraries = Object.fromEntries(
|
|
241
|
+
Object.entries(libraries).map(([key, lib]) => {
|
|
242
|
+
const entry = asObject(lib);
|
|
243
|
+
return [key, entry ? { ...entry, componentsPath: toApp(entry.componentsPath) } : lib];
|
|
244
|
+
}),
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
return out;
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
];
|
|
251
|
+
|
|
252
|
+
export interface MigrationRun {
|
|
253
|
+
/** The migrated raw config, ready for `ConfigSchema.parse`. */
|
|
254
|
+
config: RawObject;
|
|
255
|
+
/** Summaries of the steps that ran, in order. Empty ⇒ already current. */
|
|
256
|
+
applied: string[];
|
|
257
|
+
/**
|
|
258
|
+
* Per-annotation transform composed from the same steps — the CLI maps it
|
|
259
|
+
* over every entry of every `*.annotations.json` sidecar it finds.
|
|
260
|
+
*/
|
|
261
|
+
annotation: (raw: RawObject) => RawObject;
|
|
262
|
+
/** Per-theme transform, applied to every `theme/<name>.json` in the folder. */
|
|
263
|
+
theme: (raw: RawObject) => RawObject;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Compose the migration chain from a folder's current version up to
|
|
268
|
+
* {@link CURRENT_SCHEMA_VERSION}. Throws when the folder is FROM THE FUTURE
|
|
269
|
+
* (version > current) — the caller should tell the user to upgrade velloo —
|
|
270
|
+
* or when the chain has a gap (a bug in this module).
|
|
271
|
+
*/
|
|
272
|
+
export function planMigration(rawConfig: unknown, context: MigrationContext = {}): MigrationRun {
|
|
273
|
+
const from = schemaVersionOf(rawConfig);
|
|
274
|
+
if (from > CURRENT_SCHEMA_VERSION) {
|
|
275
|
+
throw new Error(
|
|
276
|
+
`Design folder uses schema version ${from}, newer than this velloo (v${CURRENT_SCHEMA_VERSION}). Upgrade velloo.`,
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
const steps: FolderMigration[] = [];
|
|
280
|
+
for (let v = from; v < CURRENT_SCHEMA_VERSION; v++) {
|
|
281
|
+
const step = FOLDER_MIGRATIONS.find((m) => m.from === v);
|
|
282
|
+
if (!step) throw new Error(`velloo: no migration step from schema version ${v} — bug.`);
|
|
283
|
+
steps.push(step);
|
|
284
|
+
}
|
|
285
|
+
let config = { ...((rawConfig ?? {}) as RawObject) };
|
|
286
|
+
for (const step of steps) {
|
|
287
|
+
if (step.config) config = step.config(config, context);
|
|
288
|
+
}
|
|
289
|
+
// Even a no-step run normalizes the version field so `upgrade` is idempotent.
|
|
290
|
+
config.schemaVersion = CURRENT_SCHEMA_VERSION;
|
|
291
|
+
return {
|
|
292
|
+
config,
|
|
293
|
+
applied: steps.map((s) => s.summary),
|
|
294
|
+
annotation: (raw) => steps.reduce((acc, step) => step.annotation?.(acc) ?? acc, raw),
|
|
295
|
+
theme: (raw) => steps.reduce((acc, step) => step.theme?.(acc) ?? acc, raw),
|
|
296
|
+
};
|
|
297
|
+
}
|