create-brainerce-store 1.50.1 → 1.51.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 (189) hide show
  1. package/dist/index.js +171 -66
  2. package/messages/en.json +4 -1
  3. package/messages/he.json +4 -1
  4. package/package.json +1 -1
  5. package/templates/nextjs/base/.claude/commands/design.md +51 -0
  6. package/templates/nextjs/base/.env.local.ejs +5 -0
  7. package/templates/nextjs/base/.eslintrc.json +57 -3
  8. package/templates/nextjs/base/AGENTS.md +41 -0
  9. package/templates/nextjs/base/AI-GUIDE.md +94 -0
  10. package/templates/nextjs/base/CLAUDE.md +42 -0
  11. package/templates/nextjs/base/TRANSLATIONS.md +1 -1
  12. package/templates/nextjs/base/next.config.ts +18 -10
  13. package/templates/nextjs/base/package.json.ejs +5 -1
  14. package/templates/nextjs/base/src/app/account/page.tsx +5 -5
  15. package/templates/nextjs/base/src/app/api/auth/logout/route.ts +1 -1
  16. package/templates/nextjs/base/src/app/api/auth/reset-password/route.ts +2 -2
  17. package/templates/nextjs/base/src/app/api/store/[...path]/route.ts +1 -1
  18. package/templates/nextjs/base/src/app/auth/callback/page.tsx +4 -4
  19. package/templates/nextjs/base/src/app/blog/[slug]/page.tsx.ejs +4 -4
  20. package/templates/nextjs/base/src/app/blog/page.tsx.ejs +2 -2
  21. package/templates/nextjs/base/src/app/cart/page.tsx +2 -173
  22. package/templates/nextjs/base/src/app/checkout/page.tsx +12 -11
  23. package/templates/nextjs/base/src/app/contact/page.tsx +3 -3
  24. package/templates/nextjs/base/src/app/faq/page.tsx.ejs +2 -2
  25. package/templates/nextjs/base/src/app/forgot-password/page.tsx +4 -4
  26. package/templates/nextjs/base/src/app/layout.tsx.ejs +16 -14
  27. package/templates/nextjs/base/src/app/login/page.tsx +4 -4
  28. package/templates/nextjs/base/src/app/order-confirmation/page.tsx +19 -12
  29. package/templates/nextjs/base/src/app/page.tsx +4 -4
  30. package/templates/nextjs/base/src/app/pages/[slug]/page.tsx.ejs +3 -3
  31. package/templates/nextjs/base/src/app/payment-complete/page.tsx +1 -1
  32. package/templates/nextjs/base/src/app/products/[slug]/page.tsx +6 -6
  33. package/templates/nextjs/base/src/app/products/page.tsx +4 -623
  34. package/templates/nextjs/base/src/app/register/page.tsx +4 -4
  35. package/templates/nextjs/base/src/app/reset-password/page.tsx +5 -5
  36. package/templates/nextjs/base/src/app/sitemap.ts +1 -1
  37. package/templates/nextjs/base/src/app/verify-email/page.tsx +5 -5
  38. package/templates/nextjs/base/src/components/account/address-book.tsx +3 -3
  39. package/templates/nextjs/base/src/components/account/order-customizations.tsx +3 -3
  40. package/templates/nextjs/base/src/components/account/order-history.tsx +13 -5
  41. package/templates/nextjs/base/src/components/account/order-payment-block.tsx +2 -2
  42. package/templates/nextjs/base/src/components/account/order-shipping-block.tsx +2 -2
  43. package/templates/nextjs/base/src/components/account/order-status-timeline.tsx +2 -2
  44. package/templates/nextjs/base/src/components/account/profile-section.tsx +3 -3
  45. package/templates/nextjs/base/src/components/auth/login-form.tsx +4 -4
  46. package/templates/nextjs/base/src/components/auth/oauth-buttons.tsx +4 -4
  47. package/templates/nextjs/base/src/components/auth/register-form.tsx +4 -4
  48. package/templates/nextjs/base/src/components/brainerce-bot.tsx +2 -2
  49. package/templates/nextjs/base/src/components/checkout/checkout-form.tsx +24 -3
  50. package/templates/nextjs/base/src/components/checkout/custom-fields-step.tsx +2 -2
  51. package/templates/nextjs/base/src/components/checkout/delivery-method-step.tsx +2 -2
  52. package/templates/nextjs/base/src/components/checkout/order-bump-card.tsx +4 -4
  53. package/templates/nextjs/base/src/components/checkout/payment-step.tsx +6 -6
  54. package/templates/nextjs/base/src/components/checkout/pickup-step.tsx +3 -3
  55. package/templates/nextjs/base/src/components/checkout/shipping-step.tsx +3 -3
  56. package/templates/nextjs/base/src/components/checkout/tax-display.tsx +3 -3
  57. package/templates/nextjs/base/src/components/seo/organization-json-ld.tsx +2 -2
  58. package/templates/nextjs/base/src/components/seo/product-json-ld.tsx +3 -3
  59. package/templates/nextjs/base/src/core/hooks/use-cart-page.ts +58 -0
  60. package/templates/nextjs/base/src/core/hooks/use-home-data.ts +51 -0
  61. package/templates/nextjs/base/src/core/hooks/use-product-listing.ts +295 -0
  62. package/templates/nextjs/base/src/core/hooks/use-product-page.ts +278 -0
  63. package/templates/nextjs/base/src/{hooks → core/hooks}/use-search.ts +1 -1
  64. package/templates/nextjs/base/src/{lib → core/lib}/brainerce.ts.ejs +1 -1
  65. package/templates/nextjs/base/src/core/lib/image-hosts.ts +7 -0
  66. package/templates/nextjs/base/src/core/lib/product-options.ts +121 -0
  67. package/templates/nextjs/base/src/{lib → core/lib}/resolve-currency.ts +1 -1
  68. package/templates/nextjs/base/src/{lib → core/lib}/safe-redirect.ts +1 -1
  69. package/templates/nextjs/base/src/{lib → core/lib}/sanitize-html.ts +12 -0
  70. package/templates/nextjs/base/src/{lib → core/lib}/sanitize.ts.ejs +1 -1
  71. package/templates/nextjs/base/src/{lib → core/lib}/use-currency.ts +1 -1
  72. package/templates/nextjs/base/src/{providers → core/providers}/store-provider.tsx.ejs +4 -4
  73. package/templates/nextjs/base/src/middleware.ts.ejs +2 -2
  74. package/templates/nextjs/base/src/{components → ui}/cart/cart-bundle-offer.tsx +5 -5
  75. package/templates/nextjs/base/src/{components → ui}/cart/cart-item.tsx +6 -6
  76. package/templates/nextjs/base/src/{components → ui}/cart/cart-nudges.tsx +1 -1
  77. package/templates/nextjs/base/src/{components → ui}/cart/cart-summary.tsx +4 -4
  78. package/templates/nextjs/base/src/{components → ui}/cart/cart-upgrade-banner.tsx +5 -5
  79. package/templates/nextjs/base/src/ui/cart/cart-view.tsx +149 -0
  80. package/templates/nextjs/base/src/{components → ui}/cart/coupon-input.tsx +4 -4
  81. package/templates/nextjs/base/src/{components → ui}/cart/free-shipping-bar.tsx +4 -4
  82. package/templates/nextjs/base/src/{components → ui}/cart/reservation-countdown.tsx +2 -2
  83. package/templates/nextjs/base/src/ui/home/discount-banner-strip.tsx +20 -0
  84. package/templates/nextjs/base/src/ui/home/featured-products-section.tsx +24 -0
  85. package/templates/nextjs/base/src/ui/home/hero-section.tsx +29 -0
  86. package/templates/nextjs/base/src/ui/home/home-client.tsx +29 -0
  87. package/templates/nextjs/base/src/{components/content → ui/layout}/faq-section.tsx.ejs +2 -2
  88. package/templates/nextjs/base/src/{components/content → ui/layout}/header-account.tsx +3 -3
  89. package/templates/nextjs/base/src/{components/content → ui/layout}/rich-text-block.tsx.ejs +1 -1
  90. package/templates/nextjs/base/src/{components/content → ui/layout}/site-header.tsx.ejs +1 -1
  91. package/templates/nextjs/base/src/{components/products → ui/product}/customization-fields.tsx +18 -51
  92. package/templates/nextjs/base/src/{components/products → ui/product}/discount-badge.tsx +1 -1
  93. package/templates/nextjs/base/src/{components/products → ui/product}/frequently-bought-together.tsx +6 -6
  94. package/templates/nextjs/base/src/{components/products → ui/product}/modifier-group-selector.tsx +10 -77
  95. package/templates/nextjs/base/src/{components/shared → ui/product}/price-display.tsx +2 -2
  96. package/templates/nextjs/base/src/{components/products → ui/product}/product-card.tsx +10 -10
  97. package/templates/nextjs/base/src/{app/products/[slug] → ui/product}/product-client-section.tsx +45 -206
  98. package/templates/nextjs/base/src/{components/products → ui/product}/product-grid.tsx +3 -3
  99. package/templates/nextjs/base/src/ui/product/product-listing.tsx +429 -0
  100. package/templates/nextjs/base/src/{components/products → ui/product}/recommendation-section.tsx +4 -4
  101. package/templates/nextjs/base/src/{components/reviews → ui/product}/review-form.tsx +2 -2
  102. package/templates/nextjs/base/src/{components/reviews → ui/product}/reviews-section.tsx +1 -1
  103. package/templates/nextjs/base/src/{components/products → ui/product}/stock-badge.tsx +2 -2
  104. package/templates/nextjs/base/src/{components/products → ui/product}/variant-selector.tsx +3 -3
  105. package/templates/nextjs/base/src/ui/shared/cdn-image.tsx +26 -0
  106. package/templates/nextjs/base/src/{components → ui}/shared/loading-spinner.tsx +2 -2
  107. package/templates/nextjs/base/tailwind.config.ts +12 -0
  108. package/templates/nextjs/designs/passepartout/app-overlay/layout.tsx.ejs +253 -0
  109. package/templates/nextjs/designs/passepartout/globals.css +412 -0
  110. package/templates/nextjs/designs/passepartout/messages-patch/en.json +87 -0
  111. package/templates/nextjs/designs/passepartout/messages-patch/he.json +87 -0
  112. package/templates/nextjs/designs/passepartout/ui/cart/cart-bundle-offer.tsx +111 -0
  113. package/templates/nextjs/designs/passepartout/ui/cart/cart-nudges.tsx +39 -0
  114. package/templates/nextjs/designs/passepartout/ui/cart/cart-upgrade-banner.tsx +141 -0
  115. package/templates/nextjs/designs/passepartout/ui/cart/cart-view.tsx +149 -0
  116. package/templates/nextjs/designs/passepartout/ui/cart/free-shipping-bar.tsx +62 -0
  117. package/templates/nextjs/designs/passepartout/ui/cart/reservation-countdown.tsx +103 -0
  118. package/templates/nextjs/designs/passepartout/ui/home/home-client.tsx +401 -0
  119. package/templates/nextjs/designs/passepartout/ui/layout/announcement-bar.tsx +139 -0
  120. package/templates/nextjs/designs/passepartout/ui/layout/cart-link.tsx +45 -0
  121. package/templates/nextjs/designs/passepartout/ui/layout/faq-section.tsx +103 -0
  122. package/templates/nextjs/designs/passepartout/ui/layout/site-footer.tsx +159 -0
  123. package/templates/nextjs/designs/passepartout/ui/layout/site-header.tsx.ejs +155 -0
  124. package/templates/nextjs/designs/passepartout/ui/layout/trust-icons.tsx +60 -0
  125. package/templates/nextjs/designs/passepartout/ui/layout/whatsapp-button.tsx +34 -0
  126. package/templates/nextjs/designs/passepartout/ui/product/discount-badge.tsx +22 -0
  127. package/templates/nextjs/designs/passepartout/ui/product/frequently-bought-together.tsx +205 -0
  128. package/templates/nextjs/designs/passepartout/ui/product/price-display.tsx +111 -0
  129. package/templates/nextjs/designs/passepartout/ui/product/product-card.tsx +265 -0
  130. package/templates/nextjs/designs/passepartout/ui/product/product-client-section.tsx +667 -0
  131. package/templates/nextjs/designs/passepartout/ui/product/product-grid.tsx +35 -0
  132. package/templates/nextjs/designs/passepartout/ui/product/product-listing.tsx +427 -0
  133. package/templates/nextjs/designs/passepartout/ui/product/recommendation-section.tsx +113 -0
  134. package/templates/nextjs/designs/passepartout/ui/product/review-form.tsx +294 -0
  135. package/templates/nextjs/designs/passepartout/ui/product/reviews-section.tsx.ejs +96 -0
  136. package/templates/nextjs/designs/passepartout/ui/product/stock-badge.tsx +52 -0
  137. package/templates/nextjs/designs/passepartout/ui/product/variant-selector.tsx +307 -0
  138. package/templates/nextjs/designs/passepartout/ui/shared/fly-to-cart.ts +85 -0
  139. package/templates/nextjs/designs/passepartout/ui/shared/loading-spinner.tsx +33 -0
  140. package/templates/nextjs/ui-canvas/cart/cart-bundle-offer.tsx +87 -0
  141. package/templates/nextjs/ui-canvas/cart/cart-item.tsx +121 -0
  142. package/templates/nextjs/ui-canvas/cart/cart-nudges.tsx +25 -0
  143. package/templates/nextjs/ui-canvas/cart/cart-summary.tsx +97 -0
  144. package/templates/nextjs/ui-canvas/cart/cart-upgrade-banner.tsx +103 -0
  145. package/templates/nextjs/ui-canvas/cart/cart-view.tsx +106 -0
  146. package/templates/nextjs/ui-canvas/cart/coupon-input.tsx +117 -0
  147. package/templates/nextjs/ui-canvas/cart/free-shipping-bar.tsx +52 -0
  148. package/templates/nextjs/ui-canvas/cart/reservation-countdown.tsx +79 -0
  149. package/templates/nextjs/ui-canvas/home/discount-banner-strip.tsx +19 -0
  150. package/templates/nextjs/ui-canvas/home/featured-products-section.tsx +23 -0
  151. package/templates/nextjs/ui-canvas/home/hero-section.tsx +21 -0
  152. package/templates/nextjs/ui-canvas/home/home-client.tsx +26 -0
  153. package/templates/nextjs/ui-canvas/layout/announcement-bar.tsx.ejs +101 -0
  154. package/templates/nextjs/ui-canvas/layout/faq-section.tsx.ejs +71 -0
  155. package/templates/nextjs/ui-canvas/layout/header-account.tsx +31 -0
  156. package/templates/nextjs/ui-canvas/layout/language-switcher.tsx.ejs +74 -0
  157. package/templates/nextjs/ui-canvas/layout/rich-text-block.tsx.ejs +34 -0
  158. package/templates/nextjs/ui-canvas/layout/site-footer.tsx.ejs +82 -0
  159. package/templates/nextjs/ui-canvas/layout/site-header.tsx.ejs +119 -0
  160. package/templates/nextjs/ui-canvas/product/customization-fields.tsx +443 -0
  161. package/templates/nextjs/ui-canvas/product/discount-badge.tsx +17 -0
  162. package/templates/nextjs/ui-canvas/product/frequently-bought-together.tsx +166 -0
  163. package/templates/nextjs/ui-canvas/product/modifier-group-selector.tsx +163 -0
  164. package/templates/nextjs/ui-canvas/product/price-display.tsx +50 -0
  165. package/templates/nextjs/ui-canvas/product/product-card.tsx +197 -0
  166. package/templates/nextjs/ui-canvas/product/product-client-section.tsx +352 -0
  167. package/templates/nextjs/ui-canvas/product/product-grid.tsx +27 -0
  168. package/templates/nextjs/ui-canvas/product/product-listing.tsx +224 -0
  169. package/templates/nextjs/ui-canvas/product/recommendation-section.tsx +95 -0
  170. package/templates/nextjs/ui-canvas/product/review-form.tsx +269 -0
  171. package/templates/nextjs/ui-canvas/product/reviews-section.tsx +78 -0
  172. package/templates/nextjs/ui-canvas/product/stock-badge.tsx +43 -0
  173. package/templates/nextjs/ui-canvas/product/variant-selector.tsx +210 -0
  174. package/templates/nextjs/ui-canvas/shared/cdn-image.tsx +29 -0
  175. package/templates/nextjs/ui-canvas/shared/loading-spinner.tsx +25 -0
  176. package/templates/nextjs/base/src/app/home-client.tsx +0 -98
  177. package/templates/nextjs/base/src/components/products/allergen-chips.tsx +0 -78
  178. /package/templates/nextjs/base/src/{lib → core/lib}/auth.ts +0 -0
  179. /package/templates/nextjs/base/src/{lib → core/lib}/csrf.ts +0 -0
  180. /package/templates/nextjs/base/src/{lib → core/lib}/navigation.tsx.ejs +0 -0
  181. /package/templates/nextjs/base/src/{lib → core/lib}/nonce.ts +0 -0
  182. /package/templates/nextjs/base/src/{lib → core/lib}/seo.ts +0 -0
  183. /package/templates/nextjs/base/src/{lib → core/lib}/store-info.ts +0 -0
  184. /package/templates/nextjs/base/src/{lib → core/lib}/translations.ts.ejs +0 -0
  185. /package/templates/nextjs/base/src/{lib → core/lib}/utils.ts +0 -0
  186. /package/templates/nextjs/base/src/{lib → core/lib}/validation.ts +0 -0
  187. /package/templates/nextjs/base/src/{components/content → ui/layout}/announcement-bar.tsx.ejs +0 -0
  188. /package/templates/nextjs/base/src/{components → ui}/layout/language-switcher.tsx.ejs +0 -0
  189. /package/templates/nextjs/base/src/{components/content → ui/layout}/site-footer.tsx.ejs +0 -0
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.50.1",
34
+ version: "1.51.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"
@@ -178,7 +178,9 @@ var BRAINERCE_RUNTIME_DEPS = Object.freeze({
178
178
  // (Product.displayPrice / displayCurrency / displaySalePrice attached
179
179
  // additively when getProducts is called with a regionId). Scaffolded stores
180
180
  // must pin >=1.30 to get the auto-region + tax-estimate methods.
181
- brainerce: "^1.32.0",
181
+ // 1.44 adds the order-level `notes` field to SetShippingAddressDto /
182
+ // SetCheckoutCustomerDto — the scaffolded checkout form types depend on it.
183
+ brainerce: "^1.44.0",
182
184
  "isomorphic-dompurify": "^3.8.0"
183
185
  });
184
186
 
@@ -286,66 +288,61 @@ async function runInteractive(defaults) {
286
288
  initial: 0
287
289
  });
288
290
  }
289
- questions.push(
290
- {
291
- type: "select",
292
- name: "framework",
293
- message: "Framework:",
294
- choices: [
295
- { title: "Next.js 15 (recommended)", value: "nextjs" },
296
- { title: "Vite + React (coming soon)", value: "vite", disabled: true },
297
- { title: "Remix (coming soon)", value: "remix", disabled: true }
298
- ],
299
- initial: 0
300
- },
301
- {
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({
302
304
  type: "select",
303
- name: "theme",
304
- message: "Theme:",
305
+ name: "design",
306
+ message: "Design:",
305
307
  choices: [
306
308
  {
307
- title: "Minimal",
308
- description: "Clean, neutral design with Inter font",
309
- value: "minimal"
309
+ title: "Passe-Partout \u2014 warm gallery minimal (recommended)",
310
+ description: "Framed product cards, placard PDP, print-serif titles",
311
+ value: "passepartout"
310
312
  },
311
313
  {
312
- title: "Luxury",
313
- description: "Dark, sophisticated design with Cormorant Garamond serif font",
314
- value: "luxury"
315
- },
316
- {
317
- title: "Playful",
318
- description: "Warm, vibrant design with Nunito rounded font",
319
- value: "playful"
314
+ title: "Minimal \u2014 bare clean base",
315
+ description: "Neutral tokens, unopinionated layout",
316
+ value: "minimal"
320
317
  }
321
318
  ],
322
319
  initial: 0
323
- },
324
- {
325
- type: "select",
326
- name: "pkgManager",
327
- message: "Package manager:",
328
- choices: [
329
- {
330
- title: `pnpm${detectInvokingPackageManager() === "pnpm" ? " (detected)" : ""}`,
331
- value: "pnpm"
332
- },
333
- {
334
- title: `npm${detectInvokingPackageManager() === "npm" ? " (detected)" : ""}`,
335
- value: "npm"
336
- },
337
- {
338
- title: `yarn${detectInvokingPackageManager() === "yarn" ? " (detected)" : ""}`,
339
- value: "yarn"
340
- },
341
- {
342
- title: `bun${detectInvokingPackageManager() === "bun" ? " (detected)" : ""}`,
343
- value: "bun"
344
- }
345
- ],
346
- initial: ["pnpm", "npm", "yarn", "bun"].indexOf(detectInvokingPackageManager())
347
- }
348
- );
320
+ });
321
+ }
322
+ questions.push({
323
+ type: "select",
324
+ name: "pkgManager",
325
+ message: "Package manager:",
326
+ choices: [
327
+ {
328
+ title: `pnpm${detectInvokingPackageManager() === "pnpm" ? " (detected)" : ""}`,
329
+ value: "pnpm"
330
+ },
331
+ {
332
+ title: `npm${detectInvokingPackageManager() === "npm" ? " (detected)" : ""}`,
333
+ value: "npm"
334
+ },
335
+ {
336
+ title: `yarn${detectInvokingPackageManager() === "yarn" ? " (detected)" : ""}`,
337
+ value: "yarn"
338
+ },
339
+ {
340
+ title: `bun${detectInvokingPackageManager() === "bun" ? " (detected)" : ""}`,
341
+ value: "bun"
342
+ }
343
+ ],
344
+ initial: ["pnpm", "npm", "yarn", "bun"].indexOf(detectInvokingPackageManager())
345
+ });
349
346
  const response = await (0, import_prompts.default)(questions, {
350
347
  onCancel: () => {
351
348
  throw new Error("PROMPT_CANCELLED");
@@ -357,6 +354,7 @@ async function runInteractive(defaults) {
357
354
  language: response.language || defaults.language || "en",
358
355
  framework: response.framework || defaults.framework || "nextjs",
359
356
  theme: response.theme || defaults.theme || "minimal",
357
+ design: response.design || defaults.design || "minimal",
360
358
  pkgManager: response.pkgManager || defaults.pkgManager || detectInvokingPackageManager()
361
359
  };
362
360
  }
@@ -459,6 +457,37 @@ async function scaffold(options) {
459
457
  isomorphicDompurifyVersion: BRAINERCE_RUNTIME_DEPS["isomorphic-dompurify"]
460
458
  };
461
459
  await copyWithEjs(baseDir, targetDir, templateVars);
460
+ if (options.canvas) {
461
+ const canvasDir = import_path.default.join(templatesRoot, framework, "ui-canvas");
462
+ if (!await import_fs_extra.default.pathExists(canvasDir)) {
463
+ throw new Error(`Canvas template not found at ${canvasDir}`);
464
+ }
465
+ const uiTarget = import_path.default.join(targetDir, "src", "ui");
466
+ await import_fs_extra.default.remove(uiTarget);
467
+ await copyWithEjs(canvasDir, uiTarget, templateVars);
468
+ }
469
+ const designName = options.design && options.design !== "minimal" ? options.design : null;
470
+ if (designName && !/^[a-z0-9-]+$/.test(designName)) {
471
+ throw new Error(`Invalid design name "${designName}"`);
472
+ }
473
+ const designDir = designName ? import_path.default.join(templatesRoot, framework, "designs", designName) : null;
474
+ if (designName && !options.canvas) {
475
+ if (!designDir || !await import_fs_extra.default.pathExists(designDir)) {
476
+ throw new Error(`Design "${designName}" not found at ${designDir}`);
477
+ }
478
+ const designUi = import_path.default.join(designDir, "ui");
479
+ if (await import_fs_extra.default.pathExists(designUi)) {
480
+ await copyWithEjs(designUi, import_path.default.join(targetDir, "src", "ui"), templateVars);
481
+ }
482
+ const designCss = import_path.default.join(designDir, "globals.css");
483
+ if (await import_fs_extra.default.pathExists(designCss)) {
484
+ await import_fs_extra.default.copy(designCss, import_path.default.join(targetDir, "src", "app", "globals.css"));
485
+ }
486
+ const designApp = import_path.default.join(designDir, "app-overlay");
487
+ if (await import_fs_extra.default.pathExists(designApp)) {
488
+ await copyWithEjs(designApp, import_path.default.join(targetDir, "src", "app"), templateVars);
489
+ }
490
+ }
462
491
  const messagesRoot = findMessagesDir(__dirname);
463
492
  const targetMessages = import_path.default.join(targetDir, "messages");
464
493
  await import_fs_extra.default.ensureDir(targetMessages);
@@ -479,6 +508,24 @@ async function scaffold(options) {
479
508
  import_path.default.join(targetMessages, `${options.language}.json`)
480
509
  );
481
510
  }
511
+ if (designName && designDir && !options.canvas) {
512
+ const patchDir = import_path.default.join(designDir, "messages-patch");
513
+ if (await import_fs_extra.default.pathExists(patchDir)) {
514
+ const localeFiles = (await import_fs_extra.default.readdir(targetMessages)).filter((f) => f.endsWith(".json"));
515
+ for (const file of localeFiles) {
516
+ const locale = file.replace(/\.json$/, "");
517
+ let patchFile = import_path.default.join(patchDir, `${locale}.json`);
518
+ if (!await import_fs_extra.default.pathExists(patchFile)) {
519
+ patchFile = import_path.default.join(patchDir, "en.json");
520
+ }
521
+ if (!await import_fs_extra.default.pathExists(patchFile)) continue;
522
+ const targetFile = import_path.default.join(targetMessages, file);
523
+ const base = await import_fs_extra.default.readJson(targetFile);
524
+ const patch = await import_fs_extra.default.readJson(patchFile);
525
+ await import_fs_extra.default.writeJson(targetFile, deepMerge(base, patch), { spaces: 2 });
526
+ }
527
+ }
528
+ }
482
529
  if (isMultiLocale) {
483
530
  const appDir = import_path.default.join(targetDir, "src", "app");
484
531
  const localeDir = import_path.default.join(appDir, "[locale]");
@@ -490,7 +537,7 @@ async function scaffold(options) {
490
537
  await import_fs_extra.default.move(import_path.default.join(appDir, entry.name), import_path.default.join(localeDir, entry.name));
491
538
  }
492
539
  }
493
- if (await import_fs_extra.default.pathExists(themeDir)) {
540
+ if (!options.canvas && !designName && await import_fs_extra.default.pathExists(themeDir)) {
494
541
  const themeCss = import_path.default.join(themeDir, "globals.css");
495
542
  if (await import_fs_extra.default.pathExists(themeCss)) {
496
543
  await import_fs_extra.default.copy(themeCss, import_path.default.join(targetDir, "src", "app", "globals.css"));
@@ -501,6 +548,18 @@ async function scaffold(options) {
501
548
  }
502
549
  }
503
550
  }
551
+ function deepMerge(base, patch) {
552
+ const out = { ...base };
553
+ for (const [key, value] of Object.entries(patch)) {
554
+ const existing = out[key];
555
+ if (value && typeof value === "object" && !Array.isArray(value) && existing && typeof existing === "object" && !Array.isArray(existing)) {
556
+ out[key] = deepMerge(existing, value);
557
+ } else {
558
+ out[key] = value;
559
+ }
560
+ }
561
+ return out;
562
+ }
504
563
  function findTemplatesDir(fromDir) {
505
564
  let dir = fromDir;
506
565
  for (let i = 0; i < 5; i++) {
@@ -693,7 +752,11 @@ async function checkForUpdate(name, current) {
693
752
  res.on("end", () => {
694
753
  try {
695
754
  const latest = JSON.parse(data).version;
696
- resolve(latest && latest !== current ? latest : null);
755
+ const newer = latest && latest.split(".").map(Number).reduce((acc, seg, i) => {
756
+ if (acc !== 0) return acc;
757
+ return seg - (Number(current.split(".")[i]) || 0);
758
+ }, 0) > 0;
759
+ resolve(newer ? latest : null);
697
760
  } catch {
698
761
  resolve(null);
699
762
  }
@@ -714,7 +777,13 @@ var program = new import_commander.Command();
714
777
  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(
715
778
  "--api-url <url>",
716
779
  "Brainerce API base URL (overrides BRAINERCE_API_URL env, defaults to https://api.brainerce.com)"
717
- ).option("--language <lang>", "Store language (en, he)").option("--framework <framework>", "Framework to use", "nextjs").option("--theme <theme>", "Theme to apply", "minimal").option("--pkg-manager <manager>", "Package manager (npm, pnpm, yarn, bun)").option("--no-git", "Skip git initialization").option("--no-install", "Skip dependency installation").action(async (projectNameArg, options) => {
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(
784
+ "--canvas",
785
+ "Scaffold src/ui as bare unstyled skeletons \u2014 a blank canvas for AI-driven design (commerce stays fully wired)"
786
+ ).option("--no-git", "Skip git initialization").option("--no-install", "Skip dependency installation").action(async (projectNameArg, options) => {
718
787
  try {
719
788
  logger.banner(pkg.version);
720
789
  const nodeError = validateNodeVersion(process.versions.node);
@@ -746,6 +815,7 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
746
815
  const VALID_PKG_MANAGERS = ["npm", "pnpm", "yarn", "bun"];
747
816
  const VALID_FRAMEWORKS = ["nextjs"];
748
817
  const VALID_THEMES = ["minimal", "luxury", "playful"];
818
+ const VALID_DESIGNS = ["minimal", "passepartout"];
749
819
  let language = options.language;
750
820
  if (language && !VALID_LANGUAGES.includes(language)) {
751
821
  logger.error(
@@ -765,6 +835,11 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
765
835
  logger.error(`Invalid --theme "${theme}". Expected one of: ${VALID_THEMES.join(", ")}`);
766
836
  process.exit(1);
767
837
  }
838
+ let design = options.design;
839
+ if (design && !VALID_DESIGNS.includes(design)) {
840
+ logger.error(`Invalid --design "${design}". Expected one of: ${VALID_DESIGNS.join(", ")}`);
841
+ process.exit(1);
842
+ }
768
843
  let pkgManager = options.pkgManager;
769
844
  if (pkgManager && !VALID_PKG_MANAGERS.includes(pkgManager)) {
770
845
  logger.error(
@@ -774,6 +849,7 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
774
849
  }
775
850
  const skipGit = options.git === false;
776
851
  const skipInstall = options.install === false;
852
+ const canvas = options.canvas === true;
777
853
  let storeInfo = null;
778
854
  let resolvedApiUrl = candidateApiUrls[0];
779
855
  if (connectionId) {
@@ -813,13 +889,16 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
813
889
  language: languageDefault,
814
890
  framework,
815
891
  theme,
816
- pkgManager
892
+ design,
893
+ pkgManager,
894
+ canvas
817
895
  });
818
896
  projectName = answers.projectName;
819
897
  connectionId = answers.connectionId;
820
898
  language = answers.language;
821
899
  framework = answers.framework;
822
900
  theme = answers.theme;
901
+ design = answers.design;
823
902
  pkgManager = answers.pkgManager;
824
903
  }
825
904
  const nameError = validateProjectName(projectName);
@@ -871,7 +950,9 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
871
950
  currency: storeInfo.currency,
872
951
  language: language || storeInfo.language,
873
952
  i18n: storeInfo.i18n,
874
- scaffoldInPlace
953
+ scaffoldInPlace,
954
+ canvas,
955
+ design: canvas ? void 0 : design || "minimal"
875
956
  });
876
957
  scaffoldSpinner.succeed(
877
958
  scaffoldInPlace ? "Scaffolded into current directory" : `Scaffolded into ./${projectName}`
@@ -911,14 +992,38 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
911
992
  );
912
993
  }
913
994
  }
914
- logger.success("\nYour Brainerce store is ready!\n");
915
- logger.info("Next steps:");
916
- if (!scaffoldInPlace) {
917
- logger.step(`cd ${projectName}`);
918
- }
919
- logger.step(`${pkgManager}${pkgManager === "npm" ? " run" : ""} dev`);
920
- logger.info(`
995
+ if (canvas) {
996
+ logger.success("\nYour Brainerce store is ready \u2014 as a blank canvas!\n");
997
+ logger.info(
998
+ "Commerce is fully wired (cart, checkout, variants, coupons), but src/ui\nis bare, unstyled skeletons with zero aesthetic opinion. THE next step\nis to design it \u2014 open this folder in Claude Code and run:"
999
+ );
1000
+ logger.step(`/design <your art-direction brief>`);
1001
+ logger.info("\nThen:");
1002
+ if (!scaffoldInPlace) {
1003
+ logger.step(`cd ${projectName}`);
1004
+ }
1005
+ logger.step(`${pkgManager}${pkgManager === "npm" ? " run" : ""} dev`);
1006
+ logger.info(
1007
+ `
1008
+ Your store will be running at http://localhost:3000
1009
+ (hook contracts + rules: AI-GUIDE.md)`
1010
+ );
1011
+ } else {
1012
+ logger.success("\nYour Brainerce store is ready!\n");
1013
+ logger.info("Next steps:");
1014
+ if (!scaffoldInPlace) {
1015
+ logger.step(`cd ${projectName}`);
1016
+ }
1017
+ logger.step(`${pkgManager}${pkgManager === "npm" ? " run" : ""} dev`);
1018
+ logger.info(`
921
1019
  Your store will be running at http://localhost:3000`);
1020
+ logger.info(
1021
+ `
1022
+ Want a unique design? Open this folder in Claude Code and run:
1023
+ /design <your art-direction brief>
1024
+ (commerce stays intact \u2014 see AI-GUIDE.md)`
1025
+ );
1026
+ }
922
1027
  const latestVersion = await updateCheck;
923
1028
  if (latestVersion) logger.updateAvailable(latestVersion, pkg.version);
924
1029
  } catch (err) {
package/messages/en.json CHANGED
@@ -247,7 +247,9 @@
247
247
  "privacyPolicyLink": "Privacy Policy",
248
248
  "privacyRequired": "You must accept the privacy policy to continue",
249
249
  "acceptsMarketing": "Send me news, promotions, and updates by email",
250
- "saveDetailsForNextTime": "Save my details for faster checkout next time"
250
+ "saveDetailsForNextTime": "Save my details for faster checkout next time",
251
+ "orderNotes": "Order notes (optional)",
252
+ "orderNotesPlaceholder": "Anything we should know about your order? e.g. delivery instructions"
251
253
  },
252
254
  "auth": {
253
255
  "loginPageTitle": "Sign In",
@@ -331,6 +333,7 @@
331
333
  "orderHistory": "Order History",
332
334
  "noOrders": "No orders yet",
333
335
  "noOrdersDesc": "Your order history will appear here after your first purchase.",
336
+ "orderNotes": "Your order note",
334
337
  "verified": "Verified",
335
338
  "unverified": "Unverified",
336
339
  "memberSince": "Member since",
package/messages/he.json CHANGED
@@ -247,7 +247,9 @@
247
247
  "privacyPolicyLink": "מדיניות הפרטיות",
248
248
  "privacyRequired": "יש לאשר את מדיניות הפרטיות כדי להמשיך",
249
249
  "acceptsMarketing": "שלחו לי חדשות, מבצעים ועדכונים במייל",
250
- "saveDetailsForNextTime": "שמרו את הפרטים שלי לתשלום מהיר בפעם הבאה"
250
+ "saveDetailsForNextTime": "שמרו את הפרטים שלי לתשלום מהיר בפעם הבאה",
251
+ "orderNotes": "הערות להזמנה (אופציונלי)",
252
+ "orderNotesPlaceholder": "משהו שכדאי שנדע על ההזמנה? למשל הנחיות למשלוח"
251
253
  },
252
254
  "auth": {
253
255
  "loginPageTitle": "התחברות",
@@ -331,6 +333,7 @@
331
333
  "orderHistory": "היסטוריית הזמנות",
332
334
  "noOrders": "אין הזמנות עדיין",
333
335
  "noOrdersDesc": "היסטוריית ההזמנות שלך תופיע כאן לאחר הרכישה הראשונה.",
336
+ "orderNotes": "ההערה שלך להזמנה",
334
337
  "verified": "מאומת",
335
338
  "unverified": "לא מאומת",
336
339
  "memberSince": "חבר מאז",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-brainerce-store",
3
- "version": "1.50.1",
3
+ "version": "1.51.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"
@@ -0,0 +1,51 @@
1
+ ---
2
+ description: Redesign this storefront's presentation layer to a distinctive, production-grade look
3
+ ---
4
+
5
+ You are the design lead for this Brainerce storefront. Design brief from the
6
+ merchant: **$ARGUMENTS** (if empty, ask for a one-line direction first — e.g.
7
+ "dark cinematic jewelry gallery", "brutalist streetwear", "warm Mediterranean
8
+ boutique").
9
+
10
+ ## Rebuild, don't restyle
11
+
12
+ The shipped `src/ui/` is a WORKING REFERENCE, not an aesthetic to preserve.
13
+ (In a `--canvas` scaffold it's already a bare unstyled skeleton — each
14
+ component carries a `DESIGN ME` marker telling you what the surface is and
15
+ which hook feeds it; skip straight to designing.)
16
+ You are expected to design from scratch: deleting entire files under
17
+ `src/ui/` and rebuilding them around the same hooks is encouraged — that is
18
+ what the folder exists for. Do not inherit the current look's layout,
19
+ spacing, or component structure unless it genuinely serves your direction.
20
+ Build the store YOU would build; the reference only shows which hooks feed
21
+ which surface.
22
+
23
+ ## Ground rules (non-negotiable)
24
+
25
+ 1. Read `AI-GUIDE.md` and `src/ui/`'s current structure BEFORE writing code.
26
+ 2. You may rewrite anything in `src/ui/**` and `src/app/globals.css`, and
27
+ wire new ui components in the thin `src/app/**` routes. You may NOT touch
28
+ `src/core/**`, `src/app/api/**`, or checkout/auth/account components.
29
+ 3. All data via `@/core/hooks/*` and the store provider — no hardcoded
30
+ products, prices, or currency symbols.
31
+ 4. All copy via `useTranslations()`, keys added to every file in `messages/`.
32
+
33
+ ## The process
34
+
35
+ 1. **Commit to ONE art direction** — write it down in 4 lines (typography
36
+ pairing incl. a script-compatible webfont, 6-10 color tokens, layout
37
+ concept, motion language) at the top of `DESIGN.md` in the project root.
38
+ Bold and specific beats safe and generic; do not hedge toward the middle.
39
+ 2. **Tokens first**: rewrite `globals.css` custom properties + font loading
40
+ in the locale layout.
41
+ 3. **Surfaces in order**: header/footer → homepage sections → product card +
42
+ grid → product page → cart. Keep each step compiling.
43
+ 4. **Micro-details**: hover states, loading states, one signature
44
+ interaction. Wrap all motion in `prefers-reduced-motion` guards.
45
+ 5. **Verify** (mandatory): `pnpm exec tsc --noEmit` = 0, then `pnpm dev` and
46
+ drive home → product → add-to-cart → cart in a real browser; screenshot
47
+ desktop (1440) AND mobile (390). For RTL locales check anchoring and
48
+ arrow directions. Fix what looks off before reporting.
49
+
50
+ Report with: the direction chosen, what changed, screenshots, and anything
51
+ that needs merchant input (e.g. brand name, logo, photography).
@@ -17,3 +17,8 @@ NEXT_PUBLIC_BRAINERCE_API_URL=<%- apiBaseUrlEnv %>
17
17
 
18
18
  # Public site URL — used for sitemap, robots.txt, and SEO metadata
19
19
  NEXT_PUBLIC_SITE_URL=http://localhost:3000
20
+
21
+ # Optional: floating WhatsApp contact button (used by some designs, e.g.
22
+ # passepartout). Digits only, international format without "+"
23
+ # (e.g. 14155550123). Leave unset to hide the button.
24
+ # NEXT_PUBLIC_WHATSAPP_PHONE=
@@ -1,3 +1,57 @@
1
- {
2
- "extends": ["next/core-web-vitals"]
3
- }
1
+ {
2
+ "extends": [
3
+ "next/core-web-vitals"
4
+ ],
5
+ "overrides": [
6
+ {
7
+ "files": [
8
+ "src/ui/**/*.{ts,tsx}"
9
+ ],
10
+ "rules": {
11
+ "no-restricted-imports": [
12
+ "error",
13
+ {
14
+ "patterns": [
15
+ {
16
+ "group": [
17
+ "@/core/lib/*",
18
+ "!@/core/lib/auth",
19
+ "!@/core/lib/brainerce",
20
+ "!@/core/lib/image-hosts",
21
+ "!@/core/lib/navigation",
22
+ "!@/core/lib/product-options",
23
+ "!@/core/lib/sanitize",
24
+ "!@/core/lib/sanitize-html",
25
+ "!@/core/lib/translations",
26
+ "!@/core/lib/use-currency",
27
+ "!@/core/lib/utils"
28
+ ],
29
+ "message": "UI consumes data via @/core/hooks/* and @/core/providers/* — not core internals. (Approved presentation helpers are exempt.)"
30
+ }
31
+ ]
32
+ }
33
+ ]
34
+ }
35
+ },
36
+ {
37
+ "files": [
38
+ "src/core/**/*.{ts,tsx}"
39
+ ],
40
+ "rules": {
41
+ "no-restricted-imports": [
42
+ "error",
43
+ {
44
+ "patterns": [
45
+ {
46
+ "group": [
47
+ "@/ui/*"
48
+ ],
49
+ "message": "core/ must never depend on presentation (ui/)."
50
+ }
51
+ ]
52
+ }
53
+ ]
54
+ }
55
+ }
56
+ ]
57
+ }
@@ -0,0 +1,41 @@
1
+ # AGENTS.md — this is a Brainerce storefront
2
+
3
+ This store's commerce engine (cart, checkout, variants, auth, i18n) is
4
+ already wired and working. Your job here is almost always **design**.
5
+
6
+ ## The one rule
7
+
8
+ **`src/core/` is the platform's. `src/ui/` is yours.**
9
+
10
+ The shipped `src/ui/` is a working reference, not a design to preserve —
11
+ full delete-and-rebuild of ui files is encouraged and expected.
12
+ Rewrite anything under `src/ui/` and `src/app/globals.css` as boldly as you
13
+ like — the store keeps working. Never modify `src/core/`, `src/app/api/`, or
14
+ the checkout/auth/account components. Data and behavior come exclusively from
15
+ `@/core/hooks/*` and `@/core/providers/store-provider` — hooks return state
16
+ and handlers, never JSX. Never hardcode catalog content.
17
+
18
+ **Read `AI-GUIDE.md` before any redesign** — it has the full file map, hook
19
+ contracts, and hard-won RTL/i18n gotchas that will save you real debugging
20
+ time.
21
+
22
+ ## Redesigning?
23
+
24
+ Follow the process documented in AI-GUIDE.md: commit to one art direction,
25
+ rewrite tokens first, then surfaces in order (header, home, card, product
26
+ page, cart), then verify.
27
+
28
+ ## Verify before declaring done
29
+
30
+ 1. `pnpm exec tsc --noEmit` → 0 errors
31
+ 2. `pnpm dev` → drive the changed flow in a real browser (home → product →
32
+ add to cart → cart)
33
+ 3. Screenshot desktop (1440px) and mobile (390px)
34
+ 4. RTL stores: check anchoring and arrow directions
35
+
36
+ ## i18n
37
+
38
+ Every user-facing string goes through `useTranslations()` with keys in **all**
39
+ files under `messages/`. Hebrew: no uppercase transforms, no wide
40
+ letter-spacing on headings, logical CSS properties only (`ms-/me-`, `ps-/pe-`,
41
+ `start-/end-`).