create-cartwright 2.0.16 → 2.0.17
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/dist/generate/generate.test.d.ts +1 -0
- package/dist/generate/generate.test.js +92 -0
- package/dist/generate/generate.test.js.map +1 -0
- package/dist/generate/index.d.ts +37 -0
- package/dist/generate/index.js +157 -15
- package/dist/generate/index.js.map +1 -1
- package/dist/generate/index.test.js +7 -5
- package/dist/generate/index.test.js.map +1 -1
- package/dist/inject.d.ts +9 -19
- package/dist/inject.js +53 -31
- package/dist/inject.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { adjustHex, derivePalette, slugify, patchThemeCssPalette, generateThemeCss, generateSeedData, } from "./index";
|
|
3
|
+
const BRIEF = {
|
|
4
|
+
storeName: "Nordlys Keramik",
|
|
5
|
+
slug: "nordlys-keramik",
|
|
6
|
+
tagline: "Håndlavet stentøj fra nord",
|
|
7
|
+
sells: "Handmade ceramics",
|
|
8
|
+
audience: "Design lovers",
|
|
9
|
+
tone: "Warm, minimal",
|
|
10
|
+
country: "DK",
|
|
11
|
+
currency: "DKK",
|
|
12
|
+
palette: { primary: "#2e7d6b", background: "#f7f3ec" },
|
|
13
|
+
categories: [
|
|
14
|
+
{ name: "Krus", slug: "krus" },
|
|
15
|
+
{ name: "Skåle", slug: "skaale" },
|
|
16
|
+
],
|
|
17
|
+
products: [
|
|
18
|
+
{ name: "Morgenkrus", categorySlug: "krus", priceMinor: 24900, blurb: "Et solidt morgenkrus." },
|
|
19
|
+
{ name: "Morgenkrus", categorySlug: "krus", priceMinor: 24900, blurb: "Dup-navn for slug-test." },
|
|
20
|
+
{ name: "Stor Skål", categorySlug: "skaale", priceMinor: 39900, blurb: "Til salat og frugt." },
|
|
21
|
+
{ name: "Lille Skål", categorySlug: "skaale", priceMinor: 14900, blurb: "Til dip." },
|
|
22
|
+
],
|
|
23
|
+
};
|
|
24
|
+
describe("colour helpers", () => {
|
|
25
|
+
it("adjustHex darkens and lightens", () => {
|
|
26
|
+
expect(adjustHex("#808080", -50)).toBe("#404040");
|
|
27
|
+
expect(adjustHex("#808080", 100)).toBe("#ffffff");
|
|
28
|
+
});
|
|
29
|
+
it("derivePalette maps brief colours to sol shades", () => {
|
|
30
|
+
const p = derivePalette(BRIEF.palette);
|
|
31
|
+
expect(p.accent).toBe("#2e7d6b");
|
|
32
|
+
expect(p.cream).toBe("#f7f3ec");
|
|
33
|
+
expect(p.accentDeep).not.toBe(p.accent); // darker variant
|
|
34
|
+
expect(p.sand).not.toBe(p.cream); // slightly darker panel bg
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe("slugify", () => {
|
|
38
|
+
it("kebab-cases names", () => {
|
|
39
|
+
expect(slugify("Morgenkrus")).toBe("morgenkrus");
|
|
40
|
+
expect(slugify("Stor Skål")).toBe("stor-skal"); // NFKD folds å→a
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe("patchThemeCssPalette", () => {
|
|
44
|
+
const CSS = [
|
|
45
|
+
"@theme {",
|
|
46
|
+
" --color-sol-accent: #1e3f5a;",
|
|
47
|
+
" --color-sol-accent-deep: #0f2438;",
|
|
48
|
+
" --color-sol-cream: #f4efe6;",
|
|
49
|
+
" --color-sol-sand: #e8e1d3;",
|
|
50
|
+
"}",
|
|
51
|
+
":root.dark {",
|
|
52
|
+
" --color-sol-accent-deep: #000000;",
|
|
53
|
+
"}",
|
|
54
|
+
].join("\n");
|
|
55
|
+
it("recolours the @theme block but leaves dark-mode overrides intact", () => {
|
|
56
|
+
const out = patchThemeCssPalette(CSS, derivePalette(BRIEF.palette));
|
|
57
|
+
expect(out).toContain("--color-sol-accent: #2e7d6b");
|
|
58
|
+
expect(out).toContain("--color-sol-cream: #f7f3ec");
|
|
59
|
+
// dark-mode accent-deep (#000000) must be preserved (first-occurrence replace)
|
|
60
|
+
expect(out).toContain("--color-sol-accent-deep: #000000");
|
|
61
|
+
// ...and the @theme accent-deep must have changed away from the original
|
|
62
|
+
expect(out).not.toContain("--color-sol-accent-deep: #0f2438");
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe("generateThemeCss", () => {
|
|
66
|
+
it("emits the brief primary as --color-sol-accent", () => {
|
|
67
|
+
const css = generateThemeCss(BRIEF);
|
|
68
|
+
expect(css).toContain("--color-sol-accent: #2e7d6b");
|
|
69
|
+
expect(css).toContain("--color-sol-cream: #f7f3ec");
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe("generateSeedData", () => {
|
|
73
|
+
const out = generateSeedData(BRIEF);
|
|
74
|
+
it("exports an engine-shaped genericTemplate", () => {
|
|
75
|
+
expect(out).toContain(`import type { IndustryTemplate } from "../types"`);
|
|
76
|
+
expect(out).toContain("export const genericTemplate: IndustryTemplate");
|
|
77
|
+
expect(out).toContain(`label: "Nordlys Keramik"`);
|
|
78
|
+
});
|
|
79
|
+
it("maps priceMinor → priceDkk (øre, no division) and derives slugs", () => {
|
|
80
|
+
expect(out).toContain(`"priceDkk": 24900`);
|
|
81
|
+
expect(out).toContain(`"slug": "morgenkrus"`);
|
|
82
|
+
// duplicate product name gets a unique slug
|
|
83
|
+
expect(out).toContain(`"morgenkrus-2"`);
|
|
84
|
+
});
|
|
85
|
+
it("marks the first three products featured and gives every product an image", () => {
|
|
86
|
+
const parsed = out.match(/"featured": true/g) || [];
|
|
87
|
+
expect(parsed.length).toBe(3);
|
|
88
|
+
expect(out).toContain("images.unsplash.com");
|
|
89
|
+
expect(out).toContain(`"categorySlug": "krus"`);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=generate.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.test.js","sourceRoot":"","sources":["../../src/generate/generate.test.ts"],"names":[],"mappings":"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"}
|
package/dist/generate/index.d.ts
CHANGED
|
@@ -1,4 +1,41 @@
|
|
|
1
1
|
import { type ShopBrief } from "../brief.js";
|
|
2
|
+
export declare function hexToRgb(hex: string): [number, number, number];
|
|
3
|
+
/** Adjust lightness: negative pct = darken toward black, positive = lighten toward white. */
|
|
4
|
+
export declare function adjustHex(hex: string, pct: number): string;
|
|
5
|
+
export type DerivedPalette = {
|
|
6
|
+
accent: string;
|
|
7
|
+
accentDeep: string;
|
|
8
|
+
accentDark: string;
|
|
9
|
+
accentLight: string;
|
|
10
|
+
cream: string;
|
|
11
|
+
sand: string;
|
|
12
|
+
};
|
|
13
|
+
/** Map the brief's {primary, background} into the storefront's core sol-* shades. */
|
|
14
|
+
export declare function derivePalette(palette: ShopBrief["palette"]): DerivedPalette;
|
|
15
|
+
/**
|
|
16
|
+
* Recolour the engine's active theme CSS (themes/generic.css) in place by
|
|
17
|
+
* swapping ONLY the core sol-* token values — CLAUDE.md-compliant ("only change
|
|
18
|
+
* values in themes/<slug>.css"), preserving the dark-mode block, glass/shadow
|
|
19
|
+
* scale, utilities, and neutral ink/muted/sun. Each replace targets the FIRST
|
|
20
|
+
* occurrence (the @theme block precedes the `:root.dark` overrides), so the
|
|
21
|
+
* dark-mode values (e.g. accent-deep #000000) stay intact.
|
|
22
|
+
*/
|
|
23
|
+
export declare function patchThemeCssPalette(css: string, p: DerivedPalette): string;
|
|
24
|
+
export declare function slugify(name: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Generate a reference themes/<slug>.css with the brief palette in the CORRECT
|
|
27
|
+
* sol-* token names. Activation happens via patchThemeCssPalette on the active
|
|
28
|
+
* theme file; this file documents the palette and is reusable.
|
|
29
|
+
*/
|
|
2
30
|
export declare function generateThemeCss(brief: ShopBrief): string;
|
|
3
31
|
export declare function generatePromptModule(brief: ShopBrief): string;
|
|
32
|
+
/**
|
|
33
|
+
* Generate the engine-shaped IndustryTemplate from the brief, exported as
|
|
34
|
+
* `genericTemplate` so it can OVERWRITE industry-templates/generic/seed-data.ts
|
|
35
|
+
* (the registered slot prisma/seed.ts reads for brand.industryTemplate "generic").
|
|
36
|
+
* This is what makes the AI catalog actually seed.
|
|
37
|
+
*
|
|
38
|
+
* NB: priceDkk is stored in minor units (øre), and brief.priceMinor is already
|
|
39
|
+
* minor units, so priceDkk = priceMinor (no division).
|
|
40
|
+
*/
|
|
4
41
|
export declare function generateSeedData(brief: ShopBrief): string;
|
package/dist/generate/index.js
CHANGED
|
@@ -1,15 +1,122 @@
|
|
|
1
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
2
|
+
// Colour helpers — derive the engine's 6-token ThemePalette (+ accent variants)
|
|
3
|
+
// from the brief's 2 colours, and adjust lightness for deep/dark/light shades.
|
|
4
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
5
|
+
function clamp(n) {
|
|
6
|
+
return Math.max(0, Math.min(255, Math.round(n)));
|
|
7
|
+
}
|
|
8
|
+
export function hexToRgb(hex) {
|
|
9
|
+
const h = hex.replace("#", "");
|
|
10
|
+
const f = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
|
|
11
|
+
return [
|
|
12
|
+
parseInt(f.slice(0, 2), 16),
|
|
13
|
+
parseInt(f.slice(2, 4), 16),
|
|
14
|
+
parseInt(f.slice(4, 6), 16),
|
|
15
|
+
];
|
|
16
|
+
}
|
|
17
|
+
function rgbToHex(rgb) {
|
|
18
|
+
return "#" + rgb.map((x) => clamp(x).toString(16).padStart(2, "0")).join("");
|
|
19
|
+
}
|
|
20
|
+
/** Adjust lightness: negative pct = darken toward black, positive = lighten toward white. */
|
|
21
|
+
export function adjustHex(hex, pct) {
|
|
22
|
+
const [r, g, b] = hexToRgb(hex);
|
|
23
|
+
const f = pct / 100;
|
|
24
|
+
const a = (c) => (pct < 0 ? c * (1 + f) : c + (255 - c) * f);
|
|
25
|
+
return rgbToHex([a(r), a(g), a(b)]);
|
|
26
|
+
}
|
|
27
|
+
/** Map the brief's {primary, background} into the storefront's core sol-* shades. */
|
|
28
|
+
export function derivePalette(palette) {
|
|
29
|
+
return {
|
|
30
|
+
accent: palette.primary,
|
|
31
|
+
accentDeep: adjustHex(palette.primary, -32),
|
|
32
|
+
accentDark: adjustHex(palette.primary, -14),
|
|
33
|
+
accentLight: adjustHex(palette.primary, 16),
|
|
34
|
+
cream: palette.background,
|
|
35
|
+
sand: adjustHex(palette.background, -6),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Recolour the engine's active theme CSS (themes/generic.css) in place by
|
|
40
|
+
* swapping ONLY the core sol-* token values — CLAUDE.md-compliant ("only change
|
|
41
|
+
* values in themes/<slug>.css"), preserving the dark-mode block, glass/shadow
|
|
42
|
+
* scale, utilities, and neutral ink/muted/sun. Each replace targets the FIRST
|
|
43
|
+
* occurrence (the @theme block precedes the `:root.dark` overrides), so the
|
|
44
|
+
* dark-mode values (e.g. accent-deep #000000) stay intact.
|
|
45
|
+
*/
|
|
46
|
+
export function patchThemeCssPalette(css, p) {
|
|
47
|
+
const set = (src, token, value) => src.replace(new RegExp(`(--${token}:\\s*)#[0-9a-fA-F]{3,6}`), `$1${value}`);
|
|
48
|
+
let out = css;
|
|
49
|
+
out = set(out, "color-sol-accent", p.accent);
|
|
50
|
+
out = set(out, "color-sol-accent-deep", p.accentDeep);
|
|
51
|
+
out = set(out, "color-sol-accent-dark", p.accentDark);
|
|
52
|
+
out = set(out, "color-sol-accent-light", p.accentLight);
|
|
53
|
+
out = set(out, "color-sol-cream", p.cream);
|
|
54
|
+
out = set(out, "color-sol-sand", p.sand);
|
|
55
|
+
out = set(out, "shadow-color-sol-accent", p.accent);
|
|
56
|
+
out = set(out, "shadow-color-sol-accent-deep", p.accentDeep);
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
export function slugify(name) {
|
|
60
|
+
return name
|
|
61
|
+
.toLowerCase()
|
|
62
|
+
.normalize("NFKD")
|
|
63
|
+
.replace(/[̀-ͯ]/g, "")
|
|
64
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
65
|
+
.replace(/^-+|-+$/g, "");
|
|
66
|
+
}
|
|
67
|
+
// Stable Unsplash placeholders (same set the generic template ships) so brief
|
|
68
|
+
// products render with images until the customer uploads their own.
|
|
69
|
+
const PLACEHOLDER_IMAGES = [
|
|
70
|
+
"https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=800",
|
|
71
|
+
"https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=800",
|
|
72
|
+
"https://images.unsplash.com/photo-1556761175-5973dc0f32e7?w=800",
|
|
73
|
+
"https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=800",
|
|
74
|
+
"https://images.unsplash.com/photo-1572635196237-14b3f281503f?w=800",
|
|
75
|
+
"https://images.unsplash.com/photo-1511499767150-a48a237f0083?w=800",
|
|
76
|
+
];
|
|
77
|
+
// Standard pages kept from the generic template (the brief has no pages).
|
|
78
|
+
const STANDARD_PAGES = [
|
|
79
|
+
{
|
|
80
|
+
slug: "about",
|
|
81
|
+
title: "About",
|
|
82
|
+
body: "## Welcome\n\nThis is a demo page. Edit the content in /admin/sider to tell your own story.",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
slug: "contact",
|
|
86
|
+
title: "Contact",
|
|
87
|
+
body: "## Get in touch\n\nContact us at the email address you have set in brand.config.ts.",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
slug: "faq",
|
|
91
|
+
title: "FAQ",
|
|
92
|
+
body: "## How long do I have to return an item?\n\nThe default return window is 30 days. Adjust it in brand.config.policies.returnDays.",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
slug: "terms",
|
|
96
|
+
title: "Terms",
|
|
97
|
+
body: "## Company information\n\nTODO: Add your company registration number, address, and contact information.",
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
/**
|
|
101
|
+
* Generate a reference themes/<slug>.css with the brief palette in the CORRECT
|
|
102
|
+
* sol-* token names. Activation happens via patchThemeCssPalette on the active
|
|
103
|
+
* theme file; this file documents the palette and is reusable.
|
|
104
|
+
*/
|
|
1
105
|
export function generateThemeCss(brief) {
|
|
106
|
+
const p = derivePalette(brief.palette);
|
|
2
107
|
return `/**
|
|
3
108
|
* Theme: ${brief.storeName}
|
|
4
|
-
* Generated by create-cartwright
|
|
109
|
+
* Generated by create-cartwright from the AI brief palette.
|
|
5
110
|
*/
|
|
6
|
-
|
|
7
|
-
--color-accent: ${
|
|
8
|
-
--color-
|
|
9
|
-
--color-
|
|
10
|
-
--color-
|
|
11
|
-
--color-
|
|
12
|
-
--color-
|
|
111
|
+
@theme {
|
|
112
|
+
--color-sol-accent: ${p.accent};
|
|
113
|
+
--color-sol-accent-deep: ${p.accentDeep};
|
|
114
|
+
--color-sol-accent-dark: ${p.accentDark};
|
|
115
|
+
--color-sol-accent-light: ${p.accentLight};
|
|
116
|
+
--color-sol-cream: ${p.cream};
|
|
117
|
+
--color-sol-sand: ${p.sand};
|
|
118
|
+
--color-sol-ink: #1a1a1a;
|
|
119
|
+
--color-sol-muted: #726d62;
|
|
13
120
|
}
|
|
14
121
|
`;
|
|
15
122
|
}
|
|
@@ -27,15 +134,50 @@ Tone of voice: ${brief.tone}
|
|
|
27
134
|
\`;
|
|
28
135
|
`;
|
|
29
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Generate the engine-shaped IndustryTemplate from the brief, exported as
|
|
139
|
+
* `genericTemplate` so it can OVERWRITE industry-templates/generic/seed-data.ts
|
|
140
|
+
* (the registered slot prisma/seed.ts reads for brand.industryTemplate "generic").
|
|
141
|
+
* This is what makes the AI catalog actually seed.
|
|
142
|
+
*
|
|
143
|
+
* NB: priceDkk is stored in minor units (øre), and brief.priceMinor is already
|
|
144
|
+
* minor units, so priceDkk = priceMinor (no division).
|
|
145
|
+
*/
|
|
30
146
|
export function generateSeedData(brief) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
147
|
+
const categories = brief.categories.map((c) => ({
|
|
148
|
+
name: c.name,
|
|
149
|
+
slug: c.slug,
|
|
150
|
+
description: c.name,
|
|
151
|
+
}));
|
|
152
|
+
const seen = new Set();
|
|
153
|
+
const products = brief.products.map((p, i) => {
|
|
154
|
+
let slug = slugify(p.name) || `product-${i + 1}`;
|
|
155
|
+
if (seen.has(slug))
|
|
156
|
+
slug = `${slug}-${i + 1}`;
|
|
157
|
+
seen.add(slug);
|
|
158
|
+
return {
|
|
159
|
+
name: p.name,
|
|
160
|
+
slug,
|
|
161
|
+
description: p.blurb,
|
|
162
|
+
priceDkk: p.priceMinor,
|
|
163
|
+
images: [PLACEHOLDER_IMAGES[i % PLACEHOLDER_IMAGES.length]],
|
|
164
|
+
stock: 25,
|
|
165
|
+
categorySlug: p.categorySlug,
|
|
166
|
+
featured: i < 3,
|
|
167
|
+
};
|
|
168
|
+
});
|
|
169
|
+
return `import type { IndustryTemplate } from "../types";
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Generated by create-cartwright from the AI brief for "${brief.storeName}".
|
|
173
|
+
* Replace or extend via /admin once the shop is running.
|
|
34
174
|
*/
|
|
35
|
-
export const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
175
|
+
export const genericTemplate: IndustryTemplate = {
|
|
176
|
+
label: ${JSON.stringify(brief.storeName)},
|
|
177
|
+
description: ${JSON.stringify(brief.tagline)},
|
|
178
|
+
categories: ${JSON.stringify(categories, null, 2)},
|
|
179
|
+
pages: ${JSON.stringify(STANDARD_PAGES, null, 2)},
|
|
180
|
+
products: ${JSON.stringify(products, null, 2)},
|
|
39
181
|
};
|
|
40
182
|
`;
|
|
41
183
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/generate/index.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/generate/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,9 +16,9 @@ const mockBrief = {
|
|
|
16
16
|
describe("generateThemeCss", () => {
|
|
17
17
|
it("laver css fil med palette", () => {
|
|
18
18
|
const css = generateThemeCss(mockBrief);
|
|
19
|
-
|
|
20
|
-
expect(css).toContain("--color-
|
|
21
|
-
expect(css).toContain("--color-
|
|
19
|
+
// Must emit the real storefront token names (--color-sol-*), not --color-*.
|
|
20
|
+
expect(css).toContain("--color-sol-accent: #112233;");
|
|
21
|
+
expect(css).toContain("--color-sol-cream: #ffeedd;");
|
|
22
22
|
});
|
|
23
23
|
});
|
|
24
24
|
describe("generatePromptModule", () => {
|
|
@@ -38,10 +38,12 @@ describe("generateSeedData", () => {
|
|
|
38
38
|
products: [{ name: "Bønne", categorySlug: "kaffe", priceMinor: 1000, blurb: "God" }]
|
|
39
39
|
};
|
|
40
40
|
const ts = generateSeedData(brief);
|
|
41
|
-
|
|
41
|
+
// Engine-shaped IndustryTemplate (registered as genericTemplate), not the
|
|
42
|
+
// old inert `seedData` object.
|
|
43
|
+
expect(ts).toContain("export const genericTemplate: IndustryTemplate");
|
|
42
44
|
expect(ts).toContain("Kaffe");
|
|
43
45
|
expect(ts).toContain("Bønne");
|
|
44
|
-
expect(ts).toContain("1000
|
|
46
|
+
expect(ts).toContain(`"priceDkk": 1000`);
|
|
45
47
|
});
|
|
46
48
|
});
|
|
47
49
|
//# sourceMappingURL=index.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/generate/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAGjE,MAAM,SAAS,GAAc;IAC3B,SAAS,EAAE,MAAM;IACjB,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,UAAU;IACnB,KAAK,EAAE,MAAM;IACb,QAAQ,EAAE,MAAM;IAChB,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,IAAI;IACb,QAAQ,EAAE,KAAK;IACf,OAAO,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE;IACtD,UAAU,EAAE,EAAE;IACd,QAAQ,EAAE,EAAE;CACb,CAAC;AAEF,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,GAAG,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;QACxC,
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/generate/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAGjE,MAAM,SAAS,GAAc;IAC3B,SAAS,EAAE,MAAM;IACjB,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,UAAU;IACnB,KAAK,EAAE,MAAM;IACb,QAAQ,EAAE,MAAM;IAChB,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,IAAI;IACb,QAAQ,EAAE,KAAK;IACf,OAAO,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE;IACtD,UAAU,EAAE,EAAE;IACd,QAAQ,EAAE,EAAE;CACb,CAAC;AAEF,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,GAAG,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;QACxC,4EAA4E;QAC5E,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAC;QACtD,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,6BAA6B,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,EAAE,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;QAC3C,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,2BAA2B,CAAC,CAAC;QAClD,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACjC,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,KAAK,GAAc;YACvB,GAAG,SAAS;YACZ,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;YAC9C,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;SACrF,CAAC;QACF,MAAM,EAAE,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACnC,0EAA0E;QAC1E,+BAA+B;QAC/B,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,gDAAgD,CAAC,CAAC;QACvE,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC9B,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC9B,MAAM,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/inject.d.ts
CHANGED
|
@@ -1,23 +1,13 @@
|
|
|
1
1
|
import { type ShopBrief } from "./brief.js";
|
|
2
|
-
export declare function injectBriefFiles(targetDir: string, brief: ShopBrief): void;
|
|
3
2
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* 2. The customer's own marketing — every entry here is something the
|
|
13
|
-
* customer can claim about their site, lifted straight into a
|
|
14
|
-
* product page, About section, or developer blurb.
|
|
15
|
-
*
|
|
16
|
-
* Unconditional injection: every scaffold gets the file. Feature flags
|
|
17
|
-
* are listed alongside each entry so the customer can see what is on by
|
|
18
|
-
* default vs opt-in for their template (`brand.features.*`).
|
|
19
|
-
*
|
|
20
|
-
* Idempotent: callers can safely re-run; the file is always overwritten
|
|
21
|
-
* with the current snapshot of features the CLI knows about.
|
|
3
|
+
* Apply the AI brief to the scaffold so the shop actually renders in its own
|
|
4
|
+
* design + catalog. Three activations (all on the customer's copy):
|
|
5
|
+
* 1. Catalog → overwrite the REGISTERED generic template seed-data.ts, so
|
|
6
|
+
* `brand.industryTemplate: "generic"` seeds the brief's categories/products
|
|
7
|
+
* (no industry-templates/index.ts patch needed).
|
|
8
|
+
* 2. Theme → recolour the active theme file (themes/generic.css) sol-* token
|
|
9
|
+
* VALUES with the brief palette, so the storefront renders in those colours.
|
|
10
|
+
* 3. Reference files → themes/<slug>.css + lib/ai/prompts/<slug>.ts.
|
|
22
11
|
*/
|
|
12
|
+
export declare function injectBriefFiles(targetDir: string, brief: ShopBrief): void;
|
|
23
13
|
export declare function injectModernWebDoc(targetDir: string): void;
|
package/dist/inject.js
CHANGED
|
@@ -1,43 +1,65 @@
|
|
|
1
|
-
import { writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { generateThemeCss, generatePromptModule, generateSeedData } from "./generate/index.js";
|
|
3
|
+
import { generateThemeCss, generatePromptModule, generateSeedData, derivePalette, patchThemeCssPalette, } from "./generate/index.js";
|
|
4
|
+
/**
|
|
5
|
+
* Apply the AI brief to the scaffold so the shop actually renders in its own
|
|
6
|
+
* design + catalog. Three activations (all on the customer's copy):
|
|
7
|
+
* 1. Catalog → overwrite the REGISTERED generic template seed-data.ts, so
|
|
8
|
+
* `brand.industryTemplate: "generic"` seeds the brief's categories/products
|
|
9
|
+
* (no industry-templates/index.ts patch needed).
|
|
10
|
+
* 2. Theme → recolour the active theme file (themes/generic.css) sol-* token
|
|
11
|
+
* VALUES with the brief palette, so the storefront renders in those colours.
|
|
12
|
+
* 3. Reference files → themes/<slug>.css + lib/ai/prompts/<slug>.ts.
|
|
13
|
+
*/
|
|
4
14
|
export function injectBriefFiles(targetDir, brief) {
|
|
5
|
-
// Opret mapper
|
|
6
15
|
const cssDir = join(targetDir, "themes");
|
|
7
16
|
const promptsDir = join(targetDir, "lib", "ai", "prompts");
|
|
8
|
-
const
|
|
9
|
-
[cssDir, promptsDir,
|
|
10
|
-
if (!existsSync(dir))
|
|
17
|
+
const genericSeedDir = join(targetDir, "industry-templates", "generic");
|
|
18
|
+
[cssDir, promptsDir, genericSeedDir].forEach((dir) => {
|
|
19
|
+
if (!existsSync(dir))
|
|
11
20
|
mkdirSync(dir, { recursive: true });
|
|
12
|
-
}
|
|
13
21
|
});
|
|
14
|
-
//
|
|
22
|
+
// Reference theme file (correct sol-* token names) + AI prompt module.
|
|
15
23
|
writeFileSync(join(cssDir, `${brief.slug}.css`), generateThemeCss(brief));
|
|
16
24
|
writeFileSync(join(promptsDir, `${brief.slug}.ts`), generatePromptModule(brief));
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
|
|
25
|
+
// 1. Catalog: overwrite the registered generic template with the brief's
|
|
26
|
+
// categories + products in the engine's IndustryTemplate shape.
|
|
27
|
+
writeFileSync(join(genericSeedDir, "seed-data.ts"), generateSeedData(brief));
|
|
28
|
+
// 2. Theme activation (compile-time base): recolour the active theme CSS in
|
|
29
|
+
// place. On newer engines whose design layer skips sol-* tokens, this is
|
|
30
|
+
// what renders; on the current template the design-pack injects sol-*
|
|
31
|
+
// inline, so step 3 (themeJson) is what actually wins. Keeping both covers
|
|
32
|
+
// every engine version.
|
|
33
|
+
const palette = derivePalette(brief.palette);
|
|
34
|
+
const genericThemePath = join(cssDir, "generic.css");
|
|
35
|
+
if (existsSync(genericThemePath)) {
|
|
36
|
+
const css = readFileSync(genericThemePath, "utf8");
|
|
37
|
+
const patched = patchThemeCssPalette(css, palette);
|
|
38
|
+
if (patched !== css)
|
|
39
|
+
writeFileSync(genericThemePath, patched);
|
|
40
|
+
}
|
|
41
|
+
// 3. Theme activation (runtime override — the one that wins): seed
|
|
42
|
+
// BrandingSettings.themeJson with the 6-token palette. getActiveTheme()
|
|
43
|
+
// reads it and app/layout.tsx injects it as the LAST inline <style>, so it
|
|
44
|
+
// overrides the design-pack's default sol-* palette. Patch the scaffold's
|
|
45
|
+
// prisma/seed.ts so the customer's first `db seed` sets it.
|
|
46
|
+
const seedPath = join(targetDir, "prisma", "seed.ts");
|
|
47
|
+
if (existsSync(seedPath)) {
|
|
48
|
+
const themeJson = JSON.stringify({
|
|
49
|
+
accent: palette.accent,
|
|
50
|
+
accentDeep: palette.accentDeep,
|
|
51
|
+
cream: palette.cream,
|
|
52
|
+
sand: palette.sand,
|
|
53
|
+
ink: "#1a1a1a",
|
|
54
|
+
muted: "#726d62",
|
|
55
|
+
});
|
|
56
|
+
const seed = readFileSync(seedPath, "utf8");
|
|
57
|
+
// Inject into the BrandingSettings.upsert create block (after storeName).
|
|
58
|
+
const patched = seed.replace(/(storeName: brand\.storeName,)/, `$1\n themeJson: ${JSON.stringify(themeJson)},`);
|
|
59
|
+
if (patched !== seed)
|
|
60
|
+
writeFileSync(seedPath, patched);
|
|
61
|
+
}
|
|
20
62
|
}
|
|
21
|
-
/**
|
|
22
|
-
* Phase D4 — write `MODERN_WEB.md` into the scaffolded project listing
|
|
23
|
-
* every modern web platform feature Cartwright wires up out of the box.
|
|
24
|
-
*
|
|
25
|
-
* The doc is meant for two audiences:
|
|
26
|
-
*
|
|
27
|
-
* 1. The customer's AI coding agent (Claude Code / Cursor / Copilot) —
|
|
28
|
-
* so when the agent reads the project, it has a clear inventory of
|
|
29
|
-
* the platform features available without having to grep the source.
|
|
30
|
-
* 2. The customer's own marketing — every entry here is something the
|
|
31
|
-
* customer can claim about their site, lifted straight into a
|
|
32
|
-
* product page, About section, or developer blurb.
|
|
33
|
-
*
|
|
34
|
-
* Unconditional injection: every scaffold gets the file. Feature flags
|
|
35
|
-
* are listed alongside each entry so the customer can see what is on by
|
|
36
|
-
* default vs opt-in for their template (`brand.features.*`).
|
|
37
|
-
*
|
|
38
|
-
* Idempotent: callers can safely re-run; the file is always overwritten
|
|
39
|
-
* with the current snapshot of features the CLI knows about.
|
|
40
|
-
*/
|
|
41
63
|
export function injectModernWebDoc(targetDir) {
|
|
42
64
|
writeFileSync(join(targetDir, "MODERN_WEB.md"), MODERN_WEB_MD);
|
|
43
65
|
}
|
package/dist/inject.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inject.js","sourceRoot":"","sources":["../src/inject.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"inject.js","sourceRoot":"","sources":["../src/inject.ts"],"names":[],"mappings":"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"}
|