create-brainerce-store 1.51.0 → 1.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/README.md +72 -0
  2. package/dist/index.js +9 -68
  3. package/messages/en.json +32 -0
  4. package/messages/he.json +32 -0
  5. package/package.json +1 -1
  6. package/templates/nextjs/base/.claude/commands/design.md +9 -0
  7. package/templates/nextjs/base/AI-GUIDE.md +52 -3
  8. package/templates/nextjs/base/CLAUDE.md +10 -0
  9. package/templates/nextjs/base/package.json.ejs +52 -39
  10. package/templates/nextjs/base/src/app/globals.css +36 -30
  11. package/templates/nextjs/base/src/components/checkout/payment-step.tsx +14 -10
  12. package/templates/nextjs/base/src/components/ui/accordion.tsx +53 -0
  13. package/templates/nextjs/base/src/components/ui/badge.tsx +32 -0
  14. package/templates/nextjs/base/src/components/ui/button.tsx +49 -0
  15. package/templates/nextjs/base/src/components/ui/card.tsx +55 -0
  16. package/templates/nextjs/base/src/components/ui/checkbox.tsx +28 -0
  17. package/templates/nextjs/base/src/components/ui/dialog.tsx +107 -0
  18. package/templates/nextjs/base/src/components/ui/input.tsx +22 -0
  19. package/templates/nextjs/base/src/components/ui/label.tsx +21 -0
  20. package/templates/nextjs/base/src/components/ui/radio-group.tsx +38 -0
  21. package/templates/nextjs/base/src/components/ui/select.tsx +153 -0
  22. package/templates/nextjs/base/src/components/ui/separator.tsx +26 -0
  23. package/templates/nextjs/base/src/components/ui/sheet.tsx +125 -0
  24. package/templates/nextjs/base/src/components/ui/skeleton.tsx +7 -0
  25. package/templates/nextjs/base/src/components/ui/tabs.tsx +55 -0
  26. package/templates/nextjs/base/src/components/ui/textarea.tsx +21 -0
  27. package/templates/nextjs/base/src/components/ui/tooltip.tsx +32 -0
  28. package/templates/nextjs/base/src/ui/cart/cart-bundle-offer.tsx +17 -11
  29. package/templates/nextjs/base/src/ui/cart/cart-item.tsx +2 -8
  30. package/templates/nextjs/base/src/ui/cart/cart-nudges.tsx +28 -39
  31. package/templates/nextjs/base/src/ui/cart/cart-summary.tsx +3 -1
  32. package/templates/nextjs/base/src/ui/cart/cart-upgrade-banner.tsx +8 -23
  33. package/templates/nextjs/base/src/ui/cart/cart-view.tsx +18 -27
  34. package/templates/nextjs/base/src/ui/cart/coupon-input.tsx +10 -21
  35. package/templates/nextjs/base/src/ui/cart/free-shipping-bar.tsx +2 -3
  36. package/templates/nextjs/base/src/ui/cart/reservation-countdown.tsx +95 -103
  37. package/templates/nextjs/base/src/ui/home/hero-section.tsx +4 -6
  38. package/templates/nextjs/base/src/ui/layout/announcement-bar.tsx.ejs +116 -125
  39. package/templates/nextjs/base/src/ui/layout/faq-section.tsx.ejs +90 -103
  40. package/templates/nextjs/base/src/ui/layout/header-account.tsx +2 -17
  41. package/templates/nextjs/base/src/ui/layout/language-switcher.tsx.ejs +80 -70
  42. package/templates/nextjs/base/src/ui/layout/site-footer.tsx.ejs +144 -164
  43. package/templates/nextjs/base/src/ui/layout/site-header.tsx.ejs +152 -182
  44. package/templates/nextjs/base/src/ui/product/customization-fields.tsx +456 -444
  45. package/templates/nextjs/base/src/ui/product/discount-badge.tsx +18 -22
  46. package/templates/nextjs/base/src/ui/product/frequently-bought-together.tsx +15 -27
  47. package/templates/nextjs/base/src/ui/product/modifier-group-selector.tsx +206 -175
  48. package/templates/nextjs/base/src/ui/product/price-display.tsx +3 -2
  49. package/templates/nextjs/base/src/ui/product/product-card.tsx +238 -271
  50. package/templates/nextjs/base/src/ui/product/product-client-section.tsx +455 -470
  51. package/templates/nextjs/base/src/ui/product/product-listing.tsx +96 -79
  52. package/templates/nextjs/base/src/ui/product/recommendation-section.tsx +102 -107
  53. package/templates/nextjs/base/src/ui/product/review-form.tsx +44 -43
  54. package/templates/nextjs/{designs/passepartout → base/src}/ui/product/reviews-section.tsx.ejs +108 -96
  55. package/templates/nextjs/base/src/ui/product/stock-badge.tsx +61 -63
  56. package/templates/nextjs/base/tailwind.config.ts +69 -42
  57. package/templates/nextjs/designs/{passepartout → atelier}/app-overlay/layout.tsx.ejs +23 -25
  58. package/templates/nextjs/designs/atelier/globals.css +396 -0
  59. package/templates/nextjs/designs/atelier/messages-patch/en.json +99 -0
  60. package/templates/nextjs/designs/atelier/messages-patch/he.json +99 -0
  61. package/templates/nextjs/designs/{passepartout → atelier}/ui/cart/cart-bundle-offer.tsx +110 -111
  62. package/templates/nextjs/designs/atelier/ui/cart/cart-drawer.tsx +163 -0
  63. package/templates/nextjs/designs/atelier/ui/cart/cart-item.tsx +140 -0
  64. package/templates/nextjs/designs/atelier/ui/cart/cart-nudges.tsx +34 -0
  65. package/templates/nextjs/designs/atelier/ui/cart/cart-summary.tsx +107 -0
  66. package/templates/nextjs/designs/{passepartout → atelier}/ui/cart/cart-upgrade-banner.tsx +123 -141
  67. package/templates/nextjs/designs/atelier/ui/cart/cart-view.tsx +147 -0
  68. package/templates/nextjs/designs/atelier/ui/cart/coupon-input.tsx +142 -0
  69. package/templates/nextjs/designs/{passepartout → atelier}/ui/cart/free-shipping-bar.tsx +77 -62
  70. package/templates/nextjs/designs/{passepartout → atelier}/ui/cart/reservation-countdown.tsx +89 -103
  71. package/templates/nextjs/designs/atelier/ui/home/benefits-band.tsx +34 -0
  72. package/templates/nextjs/designs/atelier/ui/home/category-tiles.tsx +148 -0
  73. package/templates/nextjs/designs/atelier/ui/home/discount-banner-strip.tsx +27 -0
  74. package/templates/nextjs/designs/atelier/ui/home/editorial-band.tsx +73 -0
  75. package/templates/nextjs/designs/atelier/ui/home/featured-products-section.tsx +36 -0
  76. package/templates/nextjs/designs/atelier/ui/home/hero-section.tsx +148 -0
  77. package/templates/nextjs/designs/atelier/ui/home/home-client.tsx +54 -0
  78. package/templates/nextjs/designs/atelier/ui/home/newsletter-section.tsx +78 -0
  79. package/templates/nextjs/designs/atelier/ui/home/testimonials-band.tsx +46 -0
  80. package/templates/nextjs/designs/atelier/ui/layout/announcement-bar.tsx +118 -0
  81. package/templates/nextjs/designs/atelier/ui/layout/faq-section.tsx +96 -0
  82. package/templates/nextjs/designs/atelier/ui/layout/header-account.tsx +36 -0
  83. package/templates/nextjs/designs/atelier/ui/layout/header-cart.tsx +36 -0
  84. package/templates/nextjs/designs/atelier/ui/layout/language-switcher.tsx.ejs +137 -0
  85. package/templates/nextjs/designs/atelier/ui/layout/nav-url.ts +14 -0
  86. package/templates/nextjs/designs/atelier/ui/layout/rich-text-block.tsx +34 -0
  87. package/templates/nextjs/designs/atelier/ui/layout/site-footer.tsx.ejs +141 -0
  88. package/templates/nextjs/designs/atelier/ui/layout/site-header.tsx.ejs +138 -0
  89. package/templates/nextjs/designs/atelier/ui/product/customization-fields.tsx +466 -0
  90. package/templates/nextjs/designs/{passepartout → atelier}/ui/product/discount-badge.tsx +17 -22
  91. package/templates/nextjs/designs/{passepartout → atelier}/ui/product/frequently-bought-together.tsx +196 -205
  92. package/templates/nextjs/designs/atelier/ui/product/modifier-group-selector.tsx +193 -0
  93. package/templates/nextjs/designs/atelier/ui/product/price-display.tsx +69 -0
  94. package/templates/nextjs/designs/atelier/ui/product/product-card.tsx +260 -0
  95. package/templates/nextjs/designs/atelier/ui/product/product-client-section.tsx +471 -0
  96. package/templates/nextjs/designs/atelier/ui/product/product-grid.tsx +47 -0
  97. package/templates/nextjs/designs/atelier/ui/product/product-listing.tsx +260 -0
  98. package/templates/nextjs/designs/atelier/ui/product/recommendation-section.tsx +95 -0
  99. package/templates/nextjs/designs/{passepartout → atelier}/ui/product/review-form.tsx +267 -294
  100. package/templates/nextjs/designs/atelier/ui/product/reviews-section.tsx.ejs +148 -0
  101. package/templates/nextjs/designs/atelier/ui/product/stock-badge.tsx +66 -0
  102. package/templates/nextjs/designs/atelier/ui/product/variant-selector.tsx +206 -0
  103. package/templates/nextjs/designs/atelier/ui/shared/cdn-image.tsx +29 -0
  104. package/templates/nextjs/designs/{passepartout → atelier}/ui/shared/fly-to-cart.ts +86 -85
  105. package/templates/nextjs/designs/atelier/ui/shared/icons.tsx +269 -0
  106. package/templates/nextjs/designs/atelier/ui/shared/loading-spinner.tsx +64 -0
  107. package/templates/nextjs/designs/atelier/ui/shared/reveal.tsx +71 -0
  108. package/templates/nextjs/designs/atelier/ui/shared/select-menu.tsx +125 -0
  109. package/templates/nextjs/themes/luxury/globals.css +405 -399
  110. package/templates/nextjs/themes/minimal/globals.css +36 -30
  111. package/templates/nextjs/themes/playful/globals.css +406 -400
  112. package/templates/nextjs/ui-canvas/cart/cart-bundle-offer.tsx +2 -0
  113. package/templates/nextjs/ui-canvas/cart/cart-item.tsx +2 -0
  114. package/templates/nextjs/ui-canvas/cart/cart-nudges.tsx +2 -0
  115. package/templates/nextjs/ui-canvas/cart/cart-summary.tsx +2 -0
  116. package/templates/nextjs/ui-canvas/cart/cart-upgrade-banner.tsx +2 -0
  117. package/templates/nextjs/ui-canvas/cart/cart-view.tsx +2 -2
  118. package/templates/nextjs/ui-canvas/cart/coupon-input.tsx +2 -0
  119. package/templates/nextjs/ui-canvas/cart/free-shipping-bar.tsx +2 -0
  120. package/templates/nextjs/ui-canvas/cart/reservation-countdown.tsx +2 -0
  121. package/templates/nextjs/ui-canvas/home/discount-banner-strip.tsx +1 -1
  122. package/templates/nextjs/ui-canvas/home/featured-products-section.tsx +1 -1
  123. package/templates/nextjs/ui-canvas/home/hero-section.tsx +1 -1
  124. package/templates/nextjs/ui-canvas/home/home-client.tsx +1 -1
  125. package/templates/nextjs/ui-canvas/layout/announcement-bar.tsx.ejs +1 -1
  126. package/templates/nextjs/ui-canvas/layout/faq-section.tsx.ejs +2 -2
  127. package/templates/nextjs/ui-canvas/layout/header-account.tsx +1 -1
  128. package/templates/nextjs/ui-canvas/layout/language-switcher.tsx.ejs +2 -0
  129. package/templates/nextjs/ui-canvas/layout/rich-text-block.tsx.ejs +2 -0
  130. package/templates/nextjs/ui-canvas/layout/site-footer.tsx.ejs +2 -2
  131. package/templates/nextjs/ui-canvas/layout/site-header.tsx.ejs +2 -2
  132. package/templates/nextjs/ui-canvas/product/customization-fields.tsx +2 -0
  133. package/templates/nextjs/ui-canvas/product/discount-badge.tsx +2 -0
  134. package/templates/nextjs/ui-canvas/product/frequently-bought-together.tsx +2 -0
  135. package/templates/nextjs/ui-canvas/product/modifier-group-selector.tsx +2 -0
  136. package/templates/nextjs/ui-canvas/product/price-display.tsx +2 -0
  137. package/templates/nextjs/ui-canvas/product/product-card.tsx +1 -1
  138. package/templates/nextjs/ui-canvas/product/product-client-section.tsx +1 -1
  139. package/templates/nextjs/ui-canvas/product/product-grid.tsx +1 -1
  140. package/templates/nextjs/ui-canvas/product/product-listing.tsx +1 -1
  141. package/templates/nextjs/ui-canvas/product/recommendation-section.tsx +4 -0
  142. package/templates/nextjs/ui-canvas/product/review-form.tsx +29 -32
  143. package/templates/nextjs/ui-canvas/product/{reviews-section.tsx → reviews-section.tsx.ejs} +26 -8
  144. package/templates/nextjs/ui-canvas/product/stock-badge.tsx +2 -0
  145. package/templates/nextjs/ui-canvas/product/variant-selector.tsx +2 -0
  146. package/templates/nextjs/ui-canvas/shared/cdn-image.tsx +2 -0
  147. package/templates/nextjs/ui-canvas/shared/loading-spinner.tsx +2 -0
  148. package/templates/nextjs/base/src/ui/product/reviews-section.tsx +0 -85
  149. package/templates/nextjs/designs/passepartout/globals.css +0 -412
  150. package/templates/nextjs/designs/passepartout/messages-patch/en.json +0 -87
  151. package/templates/nextjs/designs/passepartout/messages-patch/he.json +0 -87
  152. package/templates/nextjs/designs/passepartout/ui/cart/cart-nudges.tsx +0 -39
  153. package/templates/nextjs/designs/passepartout/ui/cart/cart-view.tsx +0 -149
  154. package/templates/nextjs/designs/passepartout/ui/home/home-client.tsx +0 -401
  155. package/templates/nextjs/designs/passepartout/ui/layout/announcement-bar.tsx +0 -139
  156. package/templates/nextjs/designs/passepartout/ui/layout/cart-link.tsx +0 -45
  157. package/templates/nextjs/designs/passepartout/ui/layout/faq-section.tsx +0 -103
  158. package/templates/nextjs/designs/passepartout/ui/layout/site-footer.tsx +0 -159
  159. package/templates/nextjs/designs/passepartout/ui/layout/site-header.tsx.ejs +0 -155
  160. package/templates/nextjs/designs/passepartout/ui/layout/trust-icons.tsx +0 -60
  161. package/templates/nextjs/designs/passepartout/ui/layout/whatsapp-button.tsx +0 -34
  162. package/templates/nextjs/designs/passepartout/ui/product/price-display.tsx +0 -111
  163. package/templates/nextjs/designs/passepartout/ui/product/product-card.tsx +0 -265
  164. package/templates/nextjs/designs/passepartout/ui/product/product-client-section.tsx +0 -667
  165. package/templates/nextjs/designs/passepartout/ui/product/product-grid.tsx +0 -35
  166. package/templates/nextjs/designs/passepartout/ui/product/product-listing.tsx +0 -427
  167. package/templates/nextjs/designs/passepartout/ui/product/recommendation-section.tsx +0 -113
  168. package/templates/nextjs/designs/passepartout/ui/product/stock-badge.tsx +0 -52
  169. package/templates/nextjs/designs/passepartout/ui/product/variant-selector.tsx +0 -307
  170. package/templates/nextjs/designs/passepartout/ui/shared/loading-spinner.tsx +0 -33
package/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # create-brainerce-store
2
+
3
+ Scaffold a production-ready e-commerce storefront connected to [Brainerce](https://brainerce.com) — cart, checkout, variants, coupons, auth, SEO, and i18n wired out of the box.
4
+
5
+ ```bash
6
+ npm create brainerce-store@latest my-store -- --connection-id vc_xxxxxxxx
7
+ # or
8
+ pnpm create brainerce-store my-store --connection-id vc_xxxxxxxx
9
+ ```
10
+
11
+ Run it with no flags for a fully interactive setup. The CLI fetches your store's name, currency, language, and locales from the connection ID (`vc_*`, from your Brainerce dashboard), so the generated store matches the real channel from the first `dev` run.
12
+
13
+ ## The design
14
+
15
+ Every scaffold ships **Atelier** — a complete, AI-built storefront look (not just a color theme): warm editorial gallery with soft washes + glass surfaces, a floating hero collage, near-full-bleed product grid, styled popover dropdowns, a mini-cart drawer that opens on add-to-cart, and an RTL-safe fly-to-cart micro-interaction. Fully de-branded — driven by your store's name, catalog and dashboard content.
16
+
17
+ It's a starting point, not a lock-in: the entire look lives in `src/ui/` + `globals.css` and is designed to be rewritten (see below).
18
+
19
+ ## Blank-canvas mode
20
+
21
+ ```bash
22
+ npm create brainerce-store@latest my-store -- --connection-id vc_xxx --canvas
23
+ ```
24
+
25
+ `--canvas` ships `src/ui/` as bare, semantic, essentially-unstyled skeletons (`DESIGN ME` markers included) with zero aesthetic opinion — commerce stays fully wired. It's the starting point for designing a storefront from scratch with an AI session, and the authoring substrate for new gallery designs. Canvas mode skips design packs and theme overlays entirely.
26
+
27
+ ## AI-first by default
28
+
29
+ Every scaffolded store is ready to be redesigned by an AI coding session:
30
+
31
+ - **`CLAUDE.md`** — auto-loaded by Claude Code; teaches the session the one rule (`core/` is the platform's, `ui/` is yours) before it touches anything.
32
+ - **`AGENTS.md`** — the same context for other agentic tools.
33
+ - **`/design <your art-direction brief>`** — a ready-made slash command that walks the full redesign: concept → implementation in `ui/` → verification (tsc, real browser, screenshots, RTL checks).
34
+ - **`AI-GUIDE.md`** — the design contract: file map, per-page hook contracts, verification loop, and hard-won RTL/i18n gotchas from real agency builds.
35
+
36
+ ## Architecture (core/ui)
37
+
38
+ - `src/core/` — platform-owned commerce: client, auth, providers, and data/behavior hooks that return state + handlers, never JSX. Don't modify.
39
+ - `src/ui/` — 100% replaceable presentation. Delete-and-rebuild is encouraged; the store keeps working.
40
+ - `src/app/` — thin routes composing the two (~10 lines each).
41
+ - An ESLint boundary rule enforces the split in both directions.
42
+
43
+ Full details: [TEMPLATE-ARCHITECTURE.md](./TEMPLATE-ARCHITECTURE.md).
44
+
45
+ ## Flags
46
+
47
+ | Flag | Description | Default |
48
+ | --- | --- | --- |
49
+ | `--connection-id <id>` | Brainerce vibe-coded connection ID (`vc_*`) | prompted |
50
+ | `--canvas` | Bare unstyled `src/ui/` skeletons for AI-driven design | off |
51
+ | `--language <lang>` | Store language (`en`, `he`) | fetched from store |
52
+ | `--pkg-manager <pm>` | `npm`, `pnpm`, `yarn`, `bun` | auto-detected |
53
+ | `--framework <fw>` | `nextjs` (Vite/Remix coming) | `nextjs` |
54
+ | `--theme <name>` | Token theme (`minimal`, `luxury`, `playful`) — kept for backward compat | `minimal` |
55
+ | `--api-url <url>` | Brainerce API base URL (or `BRAINERCE_API_URL` env) | auto-discovered |
56
+ | `--no-git` | Skip git initialization | git on |
57
+ | `--no-install` | Skip dependency installation | install on |
58
+
59
+ Pass `.` as the project name to scaffold into the current directory.
60
+
61
+ ## Requirements
62
+
63
+ Node `^20.19.0 || ^22.13.0 || >=24.0.0`.
64
+
65
+ ## After scaffolding
66
+
67
+ ```bash
68
+ cd my-store
69
+ pnpm dev # store at http://localhost:3000
70
+ ```
71
+
72
+ Want a unique look? Open the folder in Claude Code and run `/design <your art-direction brief>`.
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ var require_package = __commonJS({
31
31
  "package.json"(exports2, module2) {
32
32
  module2.exports = {
33
33
  name: "create-brainerce-store",
34
- version: "1.51.0",
34
+ version: "1.52.0",
35
35
  description: "Scaffold a production-ready e-commerce storefront connected to Brainerce",
36
36
  bin: {
37
37
  "create-brainerce-store": "dist/index.js"
@@ -288,37 +288,6 @@ async function runInteractive(defaults) {
288
288
  initial: 0
289
289
  });
290
290
  }
291
- questions.push({
292
- type: "select",
293
- name: "framework",
294
- message: "Framework:",
295
- choices: [
296
- { title: "Next.js 15 (recommended)", value: "nextjs" },
297
- { title: "Vite + React (coming soon)", value: "vite", disabled: true },
298
- { title: "Remix (coming soon)", value: "remix", disabled: true }
299
- ],
300
- initial: 0
301
- });
302
- if (!defaults.canvas && !defaults.design) {
303
- questions.push({
304
- type: "select",
305
- name: "design",
306
- message: "Design:",
307
- choices: [
308
- {
309
- title: "Passe-Partout \u2014 warm gallery minimal (recommended)",
310
- description: "Framed product cards, placard PDP, print-serif titles",
311
- value: "passepartout"
312
- },
313
- {
314
- title: "Minimal \u2014 bare clean base",
315
- description: "Neutral tokens, unopinionated layout",
316
- value: "minimal"
317
- }
318
- ],
319
- initial: 0
320
- });
321
- }
322
291
  questions.push({
323
292
  type: "select",
324
293
  name: "pkgManager",
@@ -352,9 +321,9 @@ async function runInteractive(defaults) {
352
321
  projectName: defaults.projectName || response.projectName,
353
322
  connectionId: defaults.connectionId || response.connectionId,
354
323
  language: response.language || defaults.language || "en",
355
- framework: response.framework || defaults.framework || "nextjs",
324
+ framework: defaults.framework || "nextjs",
356
325
  theme: response.theme || defaults.theme || "minimal",
357
- design: response.design || defaults.design || "minimal",
326
+ design: response.design || defaults.design || "atelier",
358
327
  pkgManager: response.pkgManager || defaults.pkgManager || detectInvokingPackageManager()
359
328
  };
360
329
  }
@@ -381,22 +350,8 @@ function toEnvLiteral(value) {
381
350
  function isValidLocale(locale) {
382
351
  return typeof locale === "string" && /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})?$/.test(locale);
383
352
  }
384
- function getFontConfig(language, theme) {
353
+ function getFontConfig(language) {
385
354
  const isHebrew = language === "he";
386
- if (theme === "luxury") {
387
- const subsets = isHebrew ? "['latin']" : "['latin']";
388
- return {
389
- fontImport: "import { Cormorant_Garamond } from 'next/font/google';",
390
- fontVariable: `const font = Cormorant_Garamond({ weight: ['400', '500', '600', '700'], subsets: ${subsets} });`
391
- };
392
- }
393
- if (theme === "playful") {
394
- const subsets = isHebrew ? "['latin']" : "['latin']";
395
- return {
396
- fontImport: "import { Nunito } from 'next/font/google';",
397
- fontVariable: `const font = Nunito({ subsets: ${subsets} });`
398
- };
399
- }
400
355
  if (isHebrew) {
401
356
  return {
402
357
  fontImport: "import { Rubik } from 'next/font/google';",
@@ -409,7 +364,7 @@ function getFontConfig(language, theme) {
409
364
  };
410
365
  }
411
366
  async function scaffold(options) {
412
- const { projectName, framework, theme, scaffoldInPlace } = options;
367
+ const { projectName, framework, scaffoldInPlace } = options;
413
368
  const targetDir = scaffoldInPlace ? process.cwd() : import_path.default.resolve(process.cwd(), projectName);
414
369
  if (!scaffoldInPlace && await import_fs_extra.default.pathExists(targetDir)) {
415
370
  const contents = await import_fs_extra.default.readdir(targetDir);
@@ -419,12 +374,11 @@ async function scaffold(options) {
419
374
  }
420
375
  const templatesRoot = findTemplatesDir(__dirname);
421
376
  const baseDir = import_path.default.join(templatesRoot, framework, "base");
422
- const themeDir = import_path.default.join(templatesRoot, framework, "themes", theme);
423
377
  if (!await import_fs_extra.default.pathExists(baseDir)) {
424
378
  throw new Error(`Template "${framework}" not found at ${baseDir}`);
425
379
  }
426
380
  const direction = getDirection(options.language);
427
- const fontConfig = getFontConfig(options.language, theme);
381
+ const fontConfig = getFontConfig(options.language);
428
382
  const ogLocale = options.language === "he" ? "he_IL" : "en_US";
429
383
  const rawSupportedLocales = options.i18n?.supportedLocales || [options.language];
430
384
  const supportedLocales = rawSupportedLocales.filter(isValidLocale);
@@ -537,16 +491,6 @@ async function scaffold(options) {
537
491
  await import_fs_extra.default.move(import_path.default.join(appDir, entry.name), import_path.default.join(localeDir, entry.name));
538
492
  }
539
493
  }
540
- if (!options.canvas && !designName && await import_fs_extra.default.pathExists(themeDir)) {
541
- const themeCss = import_path.default.join(themeDir, "globals.css");
542
- if (await import_fs_extra.default.pathExists(themeCss)) {
543
- await import_fs_extra.default.copy(themeCss, import_path.default.join(targetDir, "src", "app", "globals.css"));
544
- }
545
- const themeJson = import_path.default.join(themeDir, "theme.json");
546
- if (await import_fs_extra.default.pathExists(themeJson)) {
547
- await import_fs_extra.default.copy(themeJson, import_path.default.join(targetDir, "theme.json"));
548
- }
549
- }
550
494
  }
551
495
  function deepMerge(base, patch) {
552
496
  const out = { ...base };
@@ -777,10 +721,7 @@ var program = new import_commander.Command();
777
721
  program.name("create-brainerce-store").description("Scaffold a production-ready e-commerce storefront connected to Brainerce").version(pkg.version).argument("[project-name]", "Name for the project directory").option("--connection-id <id>", "Brainerce vibe-coded connection ID (vc_*)").option(
778
722
  "--api-url <url>",
779
723
  "Brainerce API base URL (overrides BRAINERCE_API_URL env, defaults to https://api.brainerce.com)"
780
- ).option("--language <lang>", "Store language (en, he)").option("--framework <framework>", "Framework to use", "nextjs").option("--theme <theme>", "Token theme to apply (kept for backward compat)", "minimal").option(
781
- "--design <design>",
782
- "Storefront design pack: passepartout (warm gallery minimal) or minimal (bare base)"
783
- ).option("--pkg-manager <manager>", "Package manager (npm, pnpm, yarn, bun)").option(
724
+ ).option("--language <lang>", "Store language (en, he)").addOption(new import_commander.Option("--framework <framework>", "Framework to use").default("nextjs").hideHelp()).option("--theme <theme>", "Token theme to apply (kept for backward compat)", "minimal").addOption(new import_commander.Option("--design <design>", "Storefront design pack").hideHelp()).option("--pkg-manager <manager>", "Package manager (npm, pnpm, yarn, bun)").option(
784
725
  "--canvas",
785
726
  "Scaffold src/ui as bare unstyled skeletons \u2014 a blank canvas for AI-driven design (commerce stays fully wired)"
786
727
  ).option("--no-git", "Skip git initialization").option("--no-install", "Skip dependency installation").action(async (projectNameArg, options) => {
@@ -815,7 +756,7 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
815
756
  const VALID_PKG_MANAGERS = ["npm", "pnpm", "yarn", "bun"];
816
757
  const VALID_FRAMEWORKS = ["nextjs"];
817
758
  const VALID_THEMES = ["minimal", "luxury", "playful"];
818
- const VALID_DESIGNS = ["minimal", "passepartout"];
759
+ const VALID_DESIGNS = ["minimal", "atelier"];
819
760
  let language = options.language;
820
761
  if (language && !VALID_LANGUAGES.includes(language)) {
821
762
  logger.error(
@@ -952,7 +893,7 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
952
893
  i18n: storeInfo.i18n,
953
894
  scaffoldInPlace,
954
895
  canvas,
955
- design: canvas ? void 0 : design || "minimal"
896
+ design: canvas ? void 0 : design || "atelier"
956
897
  });
957
898
  scaffoldSpinner.succeed(
958
899
  scaffoldInPlace ? "Scaffolded into current directory" : `Scaffolded into ./${projectName}`
package/messages/en.json CHANGED
@@ -100,6 +100,38 @@
100
100
  "addingAll": "Adding...",
101
101
  "by": "By"
102
102
  },
103
+ "reviews": {
104
+ "title": "Reviews",
105
+ "noReviews": "No reviews yet — be the first to share your experience.",
106
+ "verifiedPurchase": "Verified purchase",
107
+ "loading": "Loading…",
108
+ "signIn": "Sign in",
109
+ "signInSuffix": "to leave a review for this product.",
110
+ "onlyPurchasers": "Only customers who purchased this product can leave a review.",
111
+ "reviewsDisabled": "Reviews are not enabled for this store.",
112
+ "cannotReview": "You cannot review this product right now.",
113
+ "confirmDelete": "Delete your review for this product?",
114
+ "deleteFailed": "Could not delete your review. Please try again.",
115
+ "startOver": "Want to start over? You can delete your review.",
116
+ "deleting": "Deleting…",
117
+ "deleteReview": "Delete review",
118
+ "alreadySubmitted": "You've already submitted a review for this product.",
119
+ "tooMany": "Too many submissions. Please try again in a few minutes.",
120
+ "updateFailed": "Could not update your review. Please try again.",
121
+ "submitFailed": "Could not submit your review. Please try again.",
122
+ "thanksUpdated": "Thanks! Your review has been updated.",
123
+ "thanksSubmitted": "Thanks! Your review has been submitted.",
124
+ "editYourReview": "Edit your review",
125
+ "writeReview": "Write a review",
126
+ "yourRating": "Your rating",
127
+ "starRating": "Star rating",
128
+ "yourReview": "Your review",
129
+ "optionalField": "optional",
130
+ "saving": "Saving…",
131
+ "submittingBtn": "Submitting…",
132
+ "saveChanges": "Save changes",
133
+ "submitReview": "Submit review"
134
+ },
103
135
  "customization": {
104
136
  "title": "Personalize this product",
105
137
  "selectPlaceholder": "Choose an option",
package/messages/he.json CHANGED
@@ -100,6 +100,38 @@
100
100
  "addingAll": "מוסיף...",
101
101
  "by": "מאת"
102
102
  },
103
+ "reviews": {
104
+ "title": "ביקורות",
105
+ "noReviews": "אין עדיין ביקורות — היו הראשונים לשתף.",
106
+ "verifiedPurchase": "רכישה מאומתת",
107
+ "loading": "טוען…",
108
+ "signIn": "התחברו",
109
+ "signInSuffix": "כדי לכתוב ביקורת על המוצר.",
110
+ "onlyPurchasers": "רק לקוחות שרכשו את המוצר יכולים לכתוב ביקורת.",
111
+ "reviewsDisabled": "ביקורות אינן פעילות בחנות זו.",
112
+ "cannotReview": "לא ניתן לכתוב ביקורת על המוצר כרגע.",
113
+ "confirmDelete": "למחוק את הביקורת שלך על המוצר?",
114
+ "deleteFailed": "לא ניתן היה למחוק את הביקורת. נסו שוב.",
115
+ "startOver": "רוצים להתחיל מחדש? אפשר למחוק את הביקורת.",
116
+ "deleting": "מוחק…",
117
+ "deleteReview": "מחיקת ביקורת",
118
+ "alreadySubmitted": "כבר שלחתם ביקורת על המוצר הזה.",
119
+ "tooMany": "יותר מדי נסיונות. נסו שוב בעוד כמה דקות.",
120
+ "updateFailed": "לא ניתן היה לעדכן את הביקורת. נסו שוב.",
121
+ "submitFailed": "לא ניתן היה לשלוח את הביקורת. נסו שוב.",
122
+ "thanksUpdated": "תודה! הביקורת עודכנה.",
123
+ "thanksSubmitted": "תודה! הביקורת נשלחה.",
124
+ "editYourReview": "עריכת הביקורת",
125
+ "writeReview": "כתיבת ביקורת",
126
+ "yourRating": "הדירוג שלכם",
127
+ "starRating": "דירוג כוכבים",
128
+ "yourReview": "הביקורת שלכם",
129
+ "optionalField": "לא חובה",
130
+ "saving": "שומר…",
131
+ "submittingBtn": "שולח…",
132
+ "saveChanges": "שמירת שינויים",
133
+ "submitReview": "שליחת ביקורת"
134
+ },
103
135
  "customization": {
104
136
  "title": "התאמה אישית של המוצר",
105
137
  "selectPlaceholder": "בחר אפשרות",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-brainerce-store",
3
- "version": "1.51.0",
3
+ "version": "1.52.0",
4
4
  "description": "Scaffold a production-ready e-commerce storefront connected to Brainerce",
5
5
  "bin": {
6
6
  "create-brainerce-store": "dist/index.js"
@@ -32,6 +32,15 @@ which surface.
32
32
 
33
33
  ## The process
34
34
 
35
+ ## The quality bar (non-negotiable — from the AI-GUIDE)
36
+
37
+ Exactly 3-5 colors (no purple unless asked, no gradients by default), max 2
38
+ font families, tokens-only colors, Tailwind spacing scale, real imagery
39
+ (never abstract filler), content-rich sections, mobile-first. After EACH
40
+ major surface: screenshot → self-critique ("would a Lovable user accept
41
+ this?") → fix → continue. See AI-GUIDE.md "The quality bar".
42
+
43
+
35
44
  1. **Commit to ONE art direction** — write it down in 4 lines (typography
36
45
  pairing incl. a script-compatible webfont, 6-10 color tokens, layout
37
46
  concept, motion language) at the top of `DESIGN.md` in the project root.
@@ -26,10 +26,31 @@ core change, stop and say so instead.
26
26
  | `src/ui/cart/` | you | cart page pieces |
27
27
  | `src/ui/shared/` | you | loader, image wrapper |
28
28
  | `src/app/globals.css` | you | tokens + global styles |
29
+ | `src/components/ui/` | shared | shadcn/ui primitives — compose them, extend via variants |
29
30
  | `src/core/**` | platform | hooks, lib, providers — read, never edit |
30
31
  | `src/components/{checkout,auth,account,seo}` | platform | core-styled in v1; they inherit your tokens |
31
32
  | `src/app/**` | platform | thin routes; edit only to wire new ui components |
32
33
 
34
+ ## The dialect (shadcn/ui + lucide + cn)
35
+
36
+ This store speaks the same component dialect as Lovable / v0 / Bolt output:
37
+
38
+ - **`src/components/ui/` holds official shadcn/ui primitives** (button, card,
39
+ badge, input, label, textarea, select, checkbox, radio-group, separator,
40
+ accordion, dialog, sheet, skeleton, tabs, tooltip). Compose them in `src/ui/`
41
+ instead of hand-rolling controls. Need a new look? **Extend via CVA
42
+ variants** (add a variant to `buttonVariants`/`badgeVariants`), never by
43
+ inline-overriding the same styles in twenty call sites.
44
+ - **Icons: lucide-react only.** No inline `<svg>` paths, no emoji-as-icons,
45
+ no second icon set. Consistent 16/20/24px sizes.
46
+ - **Class merging: `cn()`** from `@/core/lib/utils` (clsx + tailwind-merge) —
47
+ it's the same helper shadcn expects, already wired into every primitive.
48
+ - **Tokens drive everything**: primitives read `--primary`, `--card`,
49
+ `--input`, `--ring`, `--radius`, etc. from `globals.css`. Restyle the store
50
+ by changing tokens (and variants), not by scattering literal colors.
51
+ - `rounded`, `rounded-md`, `rounded-lg` all derive from `--radius`
52
+ (shadcn convention), so corner radius follows the theme automatically.
53
+
33
54
  ## The hook contract (your data supply)
34
55
 
35
56
  UI components get *everything* from `@/core/hooks/*` and
@@ -54,6 +75,34 @@ images, stock) comes from them — **never hardcode products**.
54
75
  4. If the store is RTL: check nothing is left-anchored that should be
55
76
  right-anchored, and directional arrows point with the reading direction.
56
77
 
78
+ ## The quality bar (distilled from Lovable / v0 / Bolt system prompts)
79
+
80
+ Beautiful is the top priority — the first render must wow. Hard rules:
81
+
82
+ 1. **Design system first**: define all HSL semantic tokens in globals.css
83
+ BEFORE styling any component. Never direct colors (`text-white`,
84
+ `bg-black`) in classNames — tokens only.
85
+ 2. **Color budget: exactly 3-5 colors.** 1 brand color, 2-3 neutrals, 1-2
86
+ accents. Never purple/violet unless the merchant asked. No gradients by
87
+ default — solid colors; gradients only as subtle accents if requested.
88
+ 3. **Max 2 font families** (1 display + 1 body, both with the store's
89
+ script). Body line-height 1.4-1.6. Nothing below 14px.
90
+ 4. **Mobile-first**; flexbox by default, grid for true 2D; no absolute
91
+ positioning unless necessary.
92
+ 5. **Tailwind discipline**: spacing scale only (`p-4`, never `p-[16px]`),
93
+ `gap-*` over margins between siblings, `text-balance` on headlines.
94
+ 6. **Real imagery everywhere** — product photos from the catalog do the
95
+ heavy lifting; free stock hotlinks (Pexels/Unsplash) for editorial
96
+ sections. NEVER abstract filler shapes (gradient circles, blobs).
97
+ NEVER emojis as icons — one icon set, consistent 16/20/24px sizes.
98
+ 7. **Content-rich**: no section may render empty or thin. Empty states are
99
+ designed states with copy and structure, not blank space.
100
+ 8. Component variants in the design system, not inline overrides.
101
+ 9. Semantic HTML + ARIA + alt text + `sr-only` labels — always.
102
+ 10. **Verify with your eyes**: screenshot after each major surface, critique
103
+ it ("would a Lovable user accept this?"), fix, and only then move on.
104
+
105
+
57
106
  ## Hard-won gotchas (each one cost a real debugging session)
58
107
 
59
108
  - **RTL + cloned images**: `next/image` renders its `<img>` with `inset: 0`.
@@ -62,9 +111,9 @@ images, stock) comes from them — **never hardcode products**.
62
111
  Set `inset: 'auto'` before positioning.
63
112
  - **Never name a custom class `overline`** — it is a Tailwind utility
64
113
  (`text-decoration-line: overline`) and will draw a line above your text.
65
- - **`rounded-md` / `rounded-lg` are fixed radii** — they ignore your
66
- `--radius` token. For token-driven corners use `rounded` (DEFAULT) or set
67
- radii explicitly.
114
+ - **`rounded` / `rounded-md` / `rounded-lg` all follow `--radius`** (shadcn
115
+ convention, wired in tailwind.config.ts) change the token, not the
116
+ classes, when you want sharper or softer corners.
68
117
  - **Hebrew has no uppercase** — remove `text-transform: uppercase` and wide
69
118
  `letter-spacing` from heading styles; they are Latin idioms that distort
70
119
  Hebrew (tracking is acceptable only at caption sizes).
@@ -21,6 +21,16 @@ and handlers, never JSX. Never hardcode catalog content.
21
21
  contracts, and hard-won RTL/i18n gotchas that will save you real debugging
22
22
  time.
23
23
 
24
+ ## The dialect
25
+
26
+ - `src/components/ui/` = official **shadcn/ui primitives** (Button, Card,
27
+ Badge, Input, Select, Accordion, Dialog, Sheet, Skeleton, …). Compose them;
28
+ extend looks via CVA **variants**, never repeated inline overrides.
29
+ - Icons: **lucide-react only** — no inline `<svg>`, no emoji-as-icons.
30
+ - Class merging: **`cn()`** from `@/core/lib/utils`.
31
+ - Colors/radius come from the semantic HSL tokens in `globals.css`
32
+ (`--primary`, `--card`, `--ring`, `--radius`, …) — restyle via tokens.
33
+
24
34
  ## Redesigning? Use the ready-made flow
25
35
 
26
36
  Run `/design <your art-direction brief>` — it walks the whole process:
@@ -1,39 +1,52 @@
1
- {
2
- "name": "<%= projectName %>",
3
- "version": "0.1.0",
4
- "private": true,
5
- "scripts": {
6
- "dev": "next dev",
7
- "build": "next build",
8
- "start": "next start",
9
- "lint": "next lint",
10
- "setup": "node scripts/fetch-store-info.mjs"
11
- },
12
- "dependencies": {
13
- "brainerce": "<%= brainerceVersion %>",
14
- "next": "^15.3.4",
15
- "react": "^19.0.0",
16
- "react-dom": "^19.0.0",
17
- "clsx": "^2.1.0",
18
- "tailwind-merge": "^2.2.0",
19
- "isomorphic-dompurify": "<%= isomorphicDompurifyVersion %>",
20
- "sharp": "^0.35.3"
21
- },
22
- "devDependencies": {
23
- "@types/node": "^20.0.0",
24
- "@types/react": "^19.0.0",
25
- "@types/react-dom": "^19.0.0",
26
- "autoprefixer": "^10.4.0",
27
- "eslint": "^9.0.0",
28
- "eslint-config-next": "^15.3.4",
29
- "postcss": "^8.4.49",
30
- "tailwindcss": "^3.4.0",
31
- "typescript": "^5.4.0"
32
- },
33
- "engines": {
34
- "node": "^20.19.0 || ^22.13.0 || >=24.0.0"
35
- },
36
- "pnpm": {
37
- "onlyBuiltDependencies": ["sharp"]
38
- }
39
- }
1
+ {
2
+ "name": "<%= projectName %>",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "next lint",
10
+ "setup": "node scripts/fetch-store-info.mjs"
11
+ },
12
+ "dependencies": {
13
+ "brainerce": "<%= brainerceVersion %>",
14
+ "next": "^15.3.4",
15
+ "react": "^19.0.0",
16
+ "react-dom": "^19.0.0",
17
+ "@radix-ui/react-accordion": "^1.2.0",
18
+ "@radix-ui/react-checkbox": "^1.1.1",
19
+ "@radix-ui/react-dialog": "^1.1.2",
20
+ "@radix-ui/react-label": "^2.1.0",
21
+ "@radix-ui/react-radio-group": "^1.2.0",
22
+ "@radix-ui/react-select": "^2.1.1",
23
+ "@radix-ui/react-separator": "^1.1.0",
24
+ "@radix-ui/react-slot": "^1.1.0",
25
+ "@radix-ui/react-tabs": "^1.1.0",
26
+ "@radix-ui/react-tooltip": "^1.1.4",
27
+ "class-variance-authority": "^0.7.1",
28
+ "clsx": "^2.1.1",
29
+ "lucide-react": "^0.462.0",
30
+ "tailwind-merge": "^2.5.2",
31
+ "tailwindcss-animate": "^1.0.7",
32
+ "isomorphic-dompurify": "<%= isomorphicDompurifyVersion %>",
33
+ "sharp": "^0.35.3"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^20.0.0",
37
+ "@types/react": "^19.0.0",
38
+ "@types/react-dom": "^19.0.0",
39
+ "autoprefixer": "^10.4.0",
40
+ "eslint": "^9.0.0",
41
+ "eslint-config-next": "^15.3.4",
42
+ "postcss": "^8.4.49",
43
+ "tailwindcss": "^3.4.0",
44
+ "typescript": "^5.4.0"
45
+ },
46
+ "engines": {
47
+ "node": "^20.19.0 || ^22.13.0 || >=24.0.0"
48
+ },
49
+ "pnpm": {
50
+ "onlyBuiltDependencies": ["sharp"]
51
+ }
52
+ }
@@ -1,30 +1,36 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
4
-
5
- @layer base {
6
- :root {
7
- --background: 0 0% 100%;
8
- --foreground: 0 0% 9%;
9
- --primary: 0 0% 9%;
10
- --primary-foreground: 0 0% 98%;
11
- --secondary: 0 0% 96%;
12
- --secondary-foreground: 0 0% 9%;
13
- --muted: 0 0% 96%;
14
- --muted-foreground: 0 0% 45%;
15
- --accent: 0 0% 96%;
16
- --accent-foreground: 0 0% 9%;
17
- --destructive: 0 84% 60%;
18
- --destructive-foreground: 0 0% 98%;
19
- --border: 0 0% 90%;
20
- --radius: 0.5rem;
21
- }
22
-
23
- * {
24
- @apply border-border;
25
- }
26
-
27
- body {
28
- @apply bg-background text-foreground antialiased;
29
- }
30
- }
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ @layer base {
6
+ :root {
7
+ --background: 0 0% 100%;
8
+ --foreground: 0 0% 9%;
9
+ --primary: 0 0% 9%;
10
+ --primary-foreground: 0 0% 98%;
11
+ --secondary: 0 0% 96%;
12
+ --secondary-foreground: 0 0% 9%;
13
+ --muted: 0 0% 96%;
14
+ --muted-foreground: 0 0% 45%;
15
+ --accent: 0 0% 96%;
16
+ --accent-foreground: 0 0% 9%;
17
+ --destructive: 0 84% 60%;
18
+ --destructive-foreground: 0 0% 98%;
19
+ --border: 0 0% 90%;
20
+ --input: 0 0% 90%;
21
+ --ring: 0 0% 9%;
22
+ --card: 0 0% 100%;
23
+ --card-foreground: 0 0% 9%;
24
+ --popover: 0 0% 100%;
25
+ --popover-foreground: 0 0% 9%;
26
+ --radius: 0.5rem;
27
+ }
28
+
29
+ * {
30
+ @apply border-border;
31
+ }
32
+
33
+ body {
34
+ @apply bg-background text-foreground antialiased;
35
+ }
36
+ }
@@ -76,16 +76,6 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
76
76
  const t = useTranslations('checkout');
77
77
  const { storeInfo } = useStoreInfo();
78
78
 
79
- // Defense in depth: the parent already validates checkoutId from URL params,
80
- // but we re-check here so the component is safe to render in any context.
81
- if (!isValidCheckoutId(checkoutId)) {
82
- return (
83
- <div className={cn('border-destructive/50 rounded-md border p-4', className)}>
84
- <p className="text-destructive text-sm">{t('paymentError')}</p>
85
- </div>
86
- );
87
- }
88
-
89
79
  const [paymentIntent, setPaymentIntent] = useState<PaymentIntent | null>(null);
90
80
  const [preloadedSdk, setPreloadedSdk] = useState<PaymentClientSdk | null>(null);
91
81
  const [loading, setLoading] = useState(true);
@@ -174,6 +164,10 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
174
164
  // "call createPaymentProcess right before you need to render the wallet"
175
165
  // =========================================================================
176
166
  useEffect(() => {
167
+ // Defense in depth: the parent already validates checkoutId from URL
168
+ // params, but we re-check here so the component is safe to render in any
169
+ // context. Invalid id → no SDK loading, no API calls (error UI below).
170
+ if (!isValidCheckoutId(checkoutId)) return;
177
171
  if (initialized.current) return;
178
172
  initialized.current = true;
179
173
 
@@ -488,6 +482,16 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
488
482
 
489
483
  // --- UI ---
490
484
 
485
+ // Invalid checkout id — render the error box instead of ever spinning.
486
+ // (Checked after the hooks above so hook order is stable across renders.)
487
+ if (!isValidCheckoutId(checkoutId)) {
488
+ return (
489
+ <div className={cn('border-destructive/50 rounded-md border p-4', className)}>
490
+ <p className="text-destructive text-sm">{t('paymentError')}</p>
491
+ </div>
492
+ );
493
+ }
494
+
491
495
  if (loading) {
492
496
  return (
493
497
  <div className={cn('flex flex-col items-center justify-center py-12', className)}>