create-brainerce-store 1.50.5 → 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 (230) hide show
  1. package/README.md +72 -0
  2. package/dist/index.js +146 -100
  3. package/messages/en.json +36 -1
  4. package/messages/he.json +36 -1
  5. package/package.json +1 -1
  6. package/templates/nextjs/base/.claude/commands/design.md +60 -0
  7. package/templates/nextjs/base/.env.local.ejs +5 -0
  8. package/templates/nextjs/base/.eslintrc.json +57 -3
  9. package/templates/nextjs/base/AGENTS.md +41 -0
  10. package/templates/nextjs/base/AI-GUIDE.md +143 -0
  11. package/templates/nextjs/base/CLAUDE.md +52 -0
  12. package/templates/nextjs/base/TRANSLATIONS.md +1 -1
  13. package/templates/nextjs/base/next.config.ts +2 -2
  14. package/templates/nextjs/base/package.json.ejs +52 -39
  15. package/templates/nextjs/base/src/app/account/page.tsx +5 -5
  16. package/templates/nextjs/base/src/app/api/auth/logout/route.ts +1 -1
  17. package/templates/nextjs/base/src/app/api/auth/reset-password/route.ts +2 -2
  18. package/templates/nextjs/base/src/app/api/store/[...path]/route.ts +1 -1
  19. package/templates/nextjs/base/src/app/auth/callback/page.tsx +4 -4
  20. package/templates/nextjs/base/src/app/blog/[slug]/page.tsx.ejs +4 -4
  21. package/templates/nextjs/base/src/app/blog/page.tsx.ejs +2 -2
  22. package/templates/nextjs/base/src/app/cart/page.tsx +2 -173
  23. package/templates/nextjs/base/src/app/checkout/page.tsx +12 -11
  24. package/templates/nextjs/base/src/app/contact/page.tsx +3 -3
  25. package/templates/nextjs/base/src/app/faq/page.tsx.ejs +2 -2
  26. package/templates/nextjs/base/src/app/forgot-password/page.tsx +4 -4
  27. package/templates/nextjs/base/src/app/globals.css +36 -30
  28. package/templates/nextjs/base/src/app/layout.tsx.ejs +12 -12
  29. package/templates/nextjs/base/src/app/login/page.tsx +4 -4
  30. package/templates/nextjs/base/src/app/order-confirmation/page.tsx +19 -12
  31. package/templates/nextjs/base/src/app/page.tsx +4 -4
  32. package/templates/nextjs/base/src/app/pages/[slug]/page.tsx.ejs +3 -3
  33. package/templates/nextjs/base/src/app/payment-complete/page.tsx +1 -1
  34. package/templates/nextjs/base/src/app/products/[slug]/page.tsx +6 -6
  35. package/templates/nextjs/base/src/app/products/page.tsx +4 -623
  36. package/templates/nextjs/base/src/app/register/page.tsx +4 -4
  37. package/templates/nextjs/base/src/app/reset-password/page.tsx +5 -5
  38. package/templates/nextjs/base/src/app/sitemap.ts +1 -1
  39. package/templates/nextjs/base/src/app/verify-email/page.tsx +5 -5
  40. package/templates/nextjs/base/src/components/account/address-book.tsx +3 -3
  41. package/templates/nextjs/base/src/components/account/order-customizations.tsx +3 -3
  42. package/templates/nextjs/base/src/components/account/order-history.tsx +13 -5
  43. package/templates/nextjs/base/src/components/account/order-payment-block.tsx +2 -2
  44. package/templates/nextjs/base/src/components/account/order-shipping-block.tsx +2 -2
  45. package/templates/nextjs/base/src/components/account/order-status-timeline.tsx +2 -2
  46. package/templates/nextjs/base/src/components/account/profile-section.tsx +3 -3
  47. package/templates/nextjs/base/src/components/auth/login-form.tsx +4 -4
  48. package/templates/nextjs/base/src/components/auth/oauth-buttons.tsx +4 -4
  49. package/templates/nextjs/base/src/components/auth/register-form.tsx +4 -4
  50. package/templates/nextjs/base/src/components/brainerce-bot.tsx +2 -2
  51. package/templates/nextjs/base/src/components/checkout/checkout-form.tsx +24 -3
  52. package/templates/nextjs/base/src/components/checkout/custom-fields-step.tsx +2 -2
  53. package/templates/nextjs/base/src/components/checkout/delivery-method-step.tsx +2 -2
  54. package/templates/nextjs/base/src/components/checkout/order-bump-card.tsx +4 -4
  55. package/templates/nextjs/base/src/components/checkout/payment-step.tsx +20 -16
  56. package/templates/nextjs/base/src/components/checkout/pickup-step.tsx +3 -3
  57. package/templates/nextjs/base/src/components/checkout/shipping-step.tsx +3 -3
  58. package/templates/nextjs/base/src/components/checkout/tax-display.tsx +3 -3
  59. package/templates/nextjs/base/src/components/seo/organization-json-ld.tsx +2 -2
  60. package/templates/nextjs/base/src/components/seo/product-json-ld.tsx +3 -3
  61. package/templates/nextjs/base/src/components/ui/accordion.tsx +53 -0
  62. package/templates/nextjs/base/src/components/ui/badge.tsx +32 -0
  63. package/templates/nextjs/base/src/components/ui/button.tsx +49 -0
  64. package/templates/nextjs/base/src/components/ui/card.tsx +55 -0
  65. package/templates/nextjs/base/src/components/ui/checkbox.tsx +28 -0
  66. package/templates/nextjs/base/src/components/ui/dialog.tsx +107 -0
  67. package/templates/nextjs/base/src/components/ui/input.tsx +22 -0
  68. package/templates/nextjs/base/src/components/ui/label.tsx +21 -0
  69. package/templates/nextjs/base/src/components/ui/radio-group.tsx +38 -0
  70. package/templates/nextjs/base/src/components/ui/select.tsx +153 -0
  71. package/templates/nextjs/base/src/components/ui/separator.tsx +26 -0
  72. package/templates/nextjs/base/src/components/ui/sheet.tsx +125 -0
  73. package/templates/nextjs/base/src/components/ui/skeleton.tsx +7 -0
  74. package/templates/nextjs/base/src/components/ui/tabs.tsx +55 -0
  75. package/templates/nextjs/base/src/components/ui/textarea.tsx +21 -0
  76. package/templates/nextjs/base/src/components/ui/tooltip.tsx +32 -0
  77. package/templates/nextjs/base/src/core/hooks/use-cart-page.ts +58 -0
  78. package/templates/nextjs/base/src/core/hooks/use-home-data.ts +51 -0
  79. package/templates/nextjs/base/src/core/hooks/use-product-listing.ts +295 -0
  80. package/templates/nextjs/base/src/core/hooks/use-product-page.ts +278 -0
  81. package/templates/nextjs/base/src/{hooks → core/hooks}/use-search.ts +1 -1
  82. package/templates/nextjs/base/src/{lib → core/lib}/brainerce.ts.ejs +1 -1
  83. package/templates/nextjs/base/src/core/lib/product-options.ts +121 -0
  84. package/templates/nextjs/base/src/{lib → core/lib}/resolve-currency.ts +1 -1
  85. package/templates/nextjs/base/src/{lib → core/lib}/safe-redirect.ts +1 -1
  86. package/templates/nextjs/base/src/{lib → core/lib}/sanitize.ts.ejs +1 -1
  87. package/templates/nextjs/base/src/{lib → core/lib}/use-currency.ts +1 -1
  88. package/templates/nextjs/base/src/{providers → core/providers}/store-provider.tsx.ejs +4 -4
  89. package/templates/nextjs/base/src/{components → ui}/cart/cart-bundle-offer.tsx +22 -16
  90. package/templates/nextjs/base/src/{components → ui}/cart/cart-item.tsx +8 -14
  91. package/templates/nextjs/base/src/{components → ui}/cart/cart-nudges.tsx +28 -39
  92. package/templates/nextjs/base/src/{components → ui}/cart/cart-summary.tsx +7 -5
  93. package/templates/nextjs/base/src/{components → ui}/cart/cart-upgrade-banner.tsx +13 -28
  94. package/templates/nextjs/base/src/ui/cart/cart-view.tsx +140 -0
  95. package/templates/nextjs/base/src/{components → ui}/cart/coupon-input.tsx +14 -25
  96. package/templates/nextjs/base/src/{components → ui}/cart/free-shipping-bar.tsx +6 -7
  97. package/templates/nextjs/base/src/{components → ui}/cart/reservation-countdown.tsx +95 -103
  98. package/templates/nextjs/base/src/ui/home/discount-banner-strip.tsx +20 -0
  99. package/templates/nextjs/base/src/ui/home/featured-products-section.tsx +24 -0
  100. package/templates/nextjs/base/src/ui/home/hero-section.tsx +27 -0
  101. package/templates/nextjs/base/src/ui/home/home-client.tsx +29 -0
  102. package/templates/nextjs/base/src/{components/content → ui/layout}/announcement-bar.tsx.ejs +116 -125
  103. package/templates/nextjs/base/src/ui/layout/faq-section.tsx.ejs +90 -0
  104. package/templates/nextjs/base/src/{components/content → ui/layout}/header-account.tsx +5 -20
  105. package/templates/nextjs/base/src/ui/layout/language-switcher.tsx.ejs +80 -0
  106. package/templates/nextjs/base/src/{components/content → ui/layout}/rich-text-block.tsx.ejs +1 -1
  107. package/templates/nextjs/base/src/{components/content → ui/layout}/site-footer.tsx.ejs +144 -164
  108. package/templates/nextjs/base/src/{components/content → ui/layout}/site-header.tsx.ejs +152 -182
  109. package/templates/nextjs/base/src/{components/products → ui/product}/customization-fields.tsx +457 -478
  110. package/templates/nextjs/base/src/{components/products → ui/product}/discount-badge.tsx +18 -22
  111. package/templates/nextjs/base/src/{components/products → ui/product}/frequently-bought-together.tsx +21 -33
  112. package/templates/nextjs/base/src/ui/product/modifier-group-selector.tsx +206 -0
  113. package/templates/nextjs/base/src/{components/shared → ui/product}/price-display.tsx +5 -4
  114. package/templates/nextjs/base/src/{components/products → ui/product}/product-card.tsx +238 -271
  115. package/templates/nextjs/base/src/{app/products/[slug] → ui/product}/product-client-section.tsx +455 -631
  116. package/templates/nextjs/base/src/{components/products → ui/product}/product-grid.tsx +3 -3
  117. package/templates/nextjs/base/src/ui/product/product-listing.tsx +446 -0
  118. package/templates/nextjs/base/src/{components/products → ui/product}/recommendation-section.tsx +102 -107
  119. package/templates/nextjs/base/src/{components/reviews → ui/product}/review-form.tsx +46 -45
  120. package/templates/nextjs/base/src/{components/reviews/reviews-section.tsx → ui/product/reviews-section.tsx.ejs} +38 -15
  121. package/templates/nextjs/base/src/{components/products → ui/product}/stock-badge.tsx +61 -63
  122. package/templates/nextjs/base/src/{components/products → ui/product}/variant-selector.tsx +3 -3
  123. package/templates/nextjs/base/src/{components → ui}/shared/cdn-image.tsx +1 -1
  124. package/templates/nextjs/base/src/{components → ui}/shared/loading-spinner.tsx +2 -2
  125. package/templates/nextjs/base/tailwind.config.ts +69 -30
  126. package/templates/nextjs/designs/atelier/app-overlay/layout.tsx.ejs +251 -0
  127. package/templates/nextjs/designs/atelier/globals.css +396 -0
  128. package/templates/nextjs/designs/atelier/messages-patch/en.json +99 -0
  129. package/templates/nextjs/designs/atelier/messages-patch/he.json +99 -0
  130. package/templates/nextjs/designs/atelier/ui/cart/cart-bundle-offer.tsx +110 -0
  131. package/templates/nextjs/designs/atelier/ui/cart/cart-drawer.tsx +163 -0
  132. package/templates/nextjs/designs/atelier/ui/cart/cart-item.tsx +140 -0
  133. package/templates/nextjs/designs/atelier/ui/cart/cart-nudges.tsx +34 -0
  134. package/templates/nextjs/designs/atelier/ui/cart/cart-summary.tsx +107 -0
  135. package/templates/nextjs/designs/atelier/ui/cart/cart-upgrade-banner.tsx +123 -0
  136. package/templates/nextjs/designs/atelier/ui/cart/cart-view.tsx +147 -0
  137. package/templates/nextjs/designs/atelier/ui/cart/coupon-input.tsx +142 -0
  138. package/templates/nextjs/designs/atelier/ui/cart/free-shipping-bar.tsx +77 -0
  139. package/templates/nextjs/designs/atelier/ui/cart/reservation-countdown.tsx +89 -0
  140. package/templates/nextjs/designs/atelier/ui/home/benefits-band.tsx +34 -0
  141. package/templates/nextjs/designs/atelier/ui/home/category-tiles.tsx +148 -0
  142. package/templates/nextjs/designs/atelier/ui/home/discount-banner-strip.tsx +27 -0
  143. package/templates/nextjs/designs/atelier/ui/home/editorial-band.tsx +73 -0
  144. package/templates/nextjs/designs/atelier/ui/home/featured-products-section.tsx +36 -0
  145. package/templates/nextjs/designs/atelier/ui/home/hero-section.tsx +148 -0
  146. package/templates/nextjs/designs/atelier/ui/home/home-client.tsx +54 -0
  147. package/templates/nextjs/designs/atelier/ui/home/newsletter-section.tsx +78 -0
  148. package/templates/nextjs/designs/atelier/ui/home/testimonials-band.tsx +46 -0
  149. package/templates/nextjs/designs/atelier/ui/layout/announcement-bar.tsx +118 -0
  150. package/templates/nextjs/designs/atelier/ui/layout/faq-section.tsx +96 -0
  151. package/templates/nextjs/designs/atelier/ui/layout/header-account.tsx +36 -0
  152. package/templates/nextjs/designs/atelier/ui/layout/header-cart.tsx +36 -0
  153. package/templates/nextjs/designs/atelier/ui/layout/language-switcher.tsx.ejs +137 -0
  154. package/templates/nextjs/designs/atelier/ui/layout/nav-url.ts +14 -0
  155. package/templates/nextjs/designs/atelier/ui/layout/rich-text-block.tsx +34 -0
  156. package/templates/nextjs/designs/atelier/ui/layout/site-footer.tsx.ejs +141 -0
  157. package/templates/nextjs/designs/atelier/ui/layout/site-header.tsx.ejs +138 -0
  158. package/templates/nextjs/designs/atelier/ui/product/customization-fields.tsx +466 -0
  159. package/templates/nextjs/designs/atelier/ui/product/discount-badge.tsx +17 -0
  160. package/templates/nextjs/designs/atelier/ui/product/frequently-bought-together.tsx +196 -0
  161. package/templates/nextjs/designs/atelier/ui/product/modifier-group-selector.tsx +193 -0
  162. package/templates/nextjs/designs/atelier/ui/product/price-display.tsx +69 -0
  163. package/templates/nextjs/designs/atelier/ui/product/product-card.tsx +260 -0
  164. package/templates/nextjs/designs/atelier/ui/product/product-client-section.tsx +471 -0
  165. package/templates/nextjs/designs/atelier/ui/product/product-grid.tsx +47 -0
  166. package/templates/nextjs/designs/atelier/ui/product/product-listing.tsx +260 -0
  167. package/templates/nextjs/designs/atelier/ui/product/recommendation-section.tsx +95 -0
  168. package/templates/nextjs/designs/atelier/ui/product/review-form.tsx +267 -0
  169. package/templates/nextjs/designs/atelier/ui/product/reviews-section.tsx.ejs +148 -0
  170. package/templates/nextjs/designs/atelier/ui/product/stock-badge.tsx +66 -0
  171. package/templates/nextjs/designs/atelier/ui/product/variant-selector.tsx +206 -0
  172. package/templates/nextjs/designs/atelier/ui/shared/cdn-image.tsx +29 -0
  173. package/templates/nextjs/designs/atelier/ui/shared/fly-to-cart.ts +86 -0
  174. package/templates/nextjs/designs/atelier/ui/shared/icons.tsx +269 -0
  175. package/templates/nextjs/designs/atelier/ui/shared/loading-spinner.tsx +64 -0
  176. package/templates/nextjs/designs/atelier/ui/shared/reveal.tsx +71 -0
  177. package/templates/nextjs/designs/atelier/ui/shared/select-menu.tsx +125 -0
  178. package/templates/nextjs/themes/luxury/globals.css +405 -399
  179. package/templates/nextjs/themes/minimal/globals.css +36 -30
  180. package/templates/nextjs/themes/playful/globals.css +406 -400
  181. package/templates/nextjs/ui-canvas/cart/cart-bundle-offer.tsx +89 -0
  182. package/templates/nextjs/ui-canvas/cart/cart-item.tsx +123 -0
  183. package/templates/nextjs/ui-canvas/cart/cart-nudges.tsx +27 -0
  184. package/templates/nextjs/ui-canvas/cart/cart-summary.tsx +99 -0
  185. package/templates/nextjs/ui-canvas/cart/cart-upgrade-banner.tsx +105 -0
  186. package/templates/nextjs/ui-canvas/cart/cart-view.tsx +106 -0
  187. package/templates/nextjs/ui-canvas/cart/coupon-input.tsx +119 -0
  188. package/templates/nextjs/ui-canvas/cart/free-shipping-bar.tsx +54 -0
  189. package/templates/nextjs/ui-canvas/cart/reservation-countdown.tsx +81 -0
  190. package/templates/nextjs/ui-canvas/home/discount-banner-strip.tsx +19 -0
  191. package/templates/nextjs/ui-canvas/home/featured-products-section.tsx +23 -0
  192. package/templates/nextjs/ui-canvas/home/hero-section.tsx +21 -0
  193. package/templates/nextjs/ui-canvas/home/home-client.tsx +26 -0
  194. package/templates/nextjs/ui-canvas/layout/announcement-bar.tsx.ejs +101 -0
  195. package/templates/nextjs/ui-canvas/layout/faq-section.tsx.ejs +71 -0
  196. package/templates/nextjs/ui-canvas/layout/header-account.tsx +31 -0
  197. package/templates/nextjs/{base/src/components → ui-canvas}/layout/language-switcher.tsx.ejs +76 -70
  198. package/templates/nextjs/ui-canvas/layout/rich-text-block.tsx.ejs +36 -0
  199. package/templates/nextjs/ui-canvas/layout/site-footer.tsx.ejs +82 -0
  200. package/templates/nextjs/ui-canvas/layout/site-header.tsx.ejs +119 -0
  201. package/templates/nextjs/ui-canvas/product/customization-fields.tsx +445 -0
  202. package/templates/nextjs/ui-canvas/product/discount-badge.tsx +19 -0
  203. package/templates/nextjs/ui-canvas/product/frequently-bought-together.tsx +168 -0
  204. package/templates/nextjs/ui-canvas/product/modifier-group-selector.tsx +165 -0
  205. package/templates/nextjs/ui-canvas/product/price-display.tsx +52 -0
  206. package/templates/nextjs/ui-canvas/product/product-card.tsx +197 -0
  207. package/templates/nextjs/ui-canvas/product/product-client-section.tsx +352 -0
  208. package/templates/nextjs/ui-canvas/product/product-grid.tsx +27 -0
  209. package/templates/nextjs/ui-canvas/product/product-listing.tsx +224 -0
  210. package/templates/nextjs/ui-canvas/product/recommendation-section.tsx +99 -0
  211. package/templates/nextjs/ui-canvas/product/review-form.tsx +266 -0
  212. package/templates/nextjs/ui-canvas/product/reviews-section.tsx.ejs +96 -0
  213. package/templates/nextjs/ui-canvas/product/stock-badge.tsx +45 -0
  214. package/templates/nextjs/ui-canvas/product/variant-selector.tsx +212 -0
  215. package/templates/nextjs/ui-canvas/shared/cdn-image.tsx +31 -0
  216. package/templates/nextjs/ui-canvas/shared/loading-spinner.tsx +27 -0
  217. package/templates/nextjs/base/src/app/home-client.tsx +0 -98
  218. package/templates/nextjs/base/src/components/content/faq-section.tsx.ejs +0 -103
  219. package/templates/nextjs/base/src/components/products/modifier-group-selector.tsx +0 -238
  220. /package/templates/nextjs/base/src/{lib → core/lib}/auth.ts +0 -0
  221. /package/templates/nextjs/base/src/{lib → core/lib}/csrf.ts +0 -0
  222. /package/templates/nextjs/base/src/{lib → core/lib}/image-hosts.ts +0 -0
  223. /package/templates/nextjs/base/src/{lib → core/lib}/navigation.tsx.ejs +0 -0
  224. /package/templates/nextjs/base/src/{lib → core/lib}/nonce.ts +0 -0
  225. /package/templates/nextjs/base/src/{lib → core/lib}/sanitize-html.ts +0 -0
  226. /package/templates/nextjs/base/src/{lib → core/lib}/seo.ts +0 -0
  227. /package/templates/nextjs/base/src/{lib → core/lib}/store-info.ts +0 -0
  228. /package/templates/nextjs/base/src/{lib → core/lib}/translations.ts.ejs +0 -0
  229. /package/templates/nextjs/base/src/{lib → core/lib}/utils.ts +0 -0
  230. /package/templates/nextjs/base/src/{lib → core/lib}/validation.ts +0 -0
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.50.5",
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"
@@ -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,30 @@ 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
- {
302
- type: "select",
303
- name: "theme",
304
- message: "Theme:",
305
- choices: [
306
- {
307
- title: "Minimal",
308
- description: "Clean, neutral design with Inter font",
309
- value: "minimal"
310
- },
311
- {
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"
320
- }
321
- ],
322
- 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
- );
291
+ questions.push({
292
+ type: "select",
293
+ name: "pkgManager",
294
+ message: "Package manager:",
295
+ choices: [
296
+ {
297
+ title: `pnpm${detectInvokingPackageManager() === "pnpm" ? " (detected)" : ""}`,
298
+ value: "pnpm"
299
+ },
300
+ {
301
+ title: `npm${detectInvokingPackageManager() === "npm" ? " (detected)" : ""}`,
302
+ value: "npm"
303
+ },
304
+ {
305
+ title: `yarn${detectInvokingPackageManager() === "yarn" ? " (detected)" : ""}`,
306
+ value: "yarn"
307
+ },
308
+ {
309
+ title: `bun${detectInvokingPackageManager() === "bun" ? " (detected)" : ""}`,
310
+ value: "bun"
311
+ }
312
+ ],
313
+ initial: ["pnpm", "npm", "yarn", "bun"].indexOf(detectInvokingPackageManager())
314
+ });
349
315
  const response = await (0, import_prompts.default)(questions, {
350
316
  onCancel: () => {
351
317
  throw new Error("PROMPT_CANCELLED");
@@ -355,8 +321,9 @@ async function runInteractive(defaults) {
355
321
  projectName: defaults.projectName || response.projectName,
356
322
  connectionId: defaults.connectionId || response.connectionId,
357
323
  language: response.language || defaults.language || "en",
358
- framework: response.framework || defaults.framework || "nextjs",
324
+ framework: defaults.framework || "nextjs",
359
325
  theme: response.theme || defaults.theme || "minimal",
326
+ design: response.design || defaults.design || "atelier",
360
327
  pkgManager: response.pkgManager || defaults.pkgManager || detectInvokingPackageManager()
361
328
  };
362
329
  }
@@ -383,22 +350,8 @@ function toEnvLiteral(value) {
383
350
  function isValidLocale(locale) {
384
351
  return typeof locale === "string" && /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})?$/.test(locale);
385
352
  }
386
- function getFontConfig(language, theme) {
353
+ function getFontConfig(language) {
387
354
  const isHebrew = language === "he";
388
- if (theme === "luxury") {
389
- const subsets = isHebrew ? "['latin']" : "['latin']";
390
- return {
391
- fontImport: "import { Cormorant_Garamond } from 'next/font/google';",
392
- fontVariable: `const font = Cormorant_Garamond({ weight: ['400', '500', '600', '700'], subsets: ${subsets} });`
393
- };
394
- }
395
- if (theme === "playful") {
396
- const subsets = isHebrew ? "['latin']" : "['latin']";
397
- return {
398
- fontImport: "import { Nunito } from 'next/font/google';",
399
- fontVariable: `const font = Nunito({ subsets: ${subsets} });`
400
- };
401
- }
402
355
  if (isHebrew) {
403
356
  return {
404
357
  fontImport: "import { Rubik } from 'next/font/google';",
@@ -411,7 +364,7 @@ function getFontConfig(language, theme) {
411
364
  };
412
365
  }
413
366
  async function scaffold(options) {
414
- const { projectName, framework, theme, scaffoldInPlace } = options;
367
+ const { projectName, framework, scaffoldInPlace } = options;
415
368
  const targetDir = scaffoldInPlace ? process.cwd() : import_path.default.resolve(process.cwd(), projectName);
416
369
  if (!scaffoldInPlace && await import_fs_extra.default.pathExists(targetDir)) {
417
370
  const contents = await import_fs_extra.default.readdir(targetDir);
@@ -421,12 +374,11 @@ async function scaffold(options) {
421
374
  }
422
375
  const templatesRoot = findTemplatesDir(__dirname);
423
376
  const baseDir = import_path.default.join(templatesRoot, framework, "base");
424
- const themeDir = import_path.default.join(templatesRoot, framework, "themes", theme);
425
377
  if (!await import_fs_extra.default.pathExists(baseDir)) {
426
378
  throw new Error(`Template "${framework}" not found at ${baseDir}`);
427
379
  }
428
380
  const direction = getDirection(options.language);
429
- const fontConfig = getFontConfig(options.language, theme);
381
+ const fontConfig = getFontConfig(options.language);
430
382
  const ogLocale = options.language === "he" ? "he_IL" : "en_US";
431
383
  const rawSupportedLocales = options.i18n?.supportedLocales || [options.language];
432
384
  const supportedLocales = rawSupportedLocales.filter(isValidLocale);
@@ -459,6 +411,37 @@ async function scaffold(options) {
459
411
  isomorphicDompurifyVersion: BRAINERCE_RUNTIME_DEPS["isomorphic-dompurify"]
460
412
  };
461
413
  await copyWithEjs(baseDir, targetDir, templateVars);
414
+ if (options.canvas) {
415
+ const canvasDir = import_path.default.join(templatesRoot, framework, "ui-canvas");
416
+ if (!await import_fs_extra.default.pathExists(canvasDir)) {
417
+ throw new Error(`Canvas template not found at ${canvasDir}`);
418
+ }
419
+ const uiTarget = import_path.default.join(targetDir, "src", "ui");
420
+ await import_fs_extra.default.remove(uiTarget);
421
+ await copyWithEjs(canvasDir, uiTarget, templateVars);
422
+ }
423
+ const designName = options.design && options.design !== "minimal" ? options.design : null;
424
+ if (designName && !/^[a-z0-9-]+$/.test(designName)) {
425
+ throw new Error(`Invalid design name "${designName}"`);
426
+ }
427
+ const designDir = designName ? import_path.default.join(templatesRoot, framework, "designs", designName) : null;
428
+ if (designName && !options.canvas) {
429
+ if (!designDir || !await import_fs_extra.default.pathExists(designDir)) {
430
+ throw new Error(`Design "${designName}" not found at ${designDir}`);
431
+ }
432
+ const designUi = import_path.default.join(designDir, "ui");
433
+ if (await import_fs_extra.default.pathExists(designUi)) {
434
+ await copyWithEjs(designUi, import_path.default.join(targetDir, "src", "ui"), templateVars);
435
+ }
436
+ const designCss = import_path.default.join(designDir, "globals.css");
437
+ if (await import_fs_extra.default.pathExists(designCss)) {
438
+ await import_fs_extra.default.copy(designCss, import_path.default.join(targetDir, "src", "app", "globals.css"));
439
+ }
440
+ const designApp = import_path.default.join(designDir, "app-overlay");
441
+ if (await import_fs_extra.default.pathExists(designApp)) {
442
+ await copyWithEjs(designApp, import_path.default.join(targetDir, "src", "app"), templateVars);
443
+ }
444
+ }
462
445
  const messagesRoot = findMessagesDir(__dirname);
463
446
  const targetMessages = import_path.default.join(targetDir, "messages");
464
447
  await import_fs_extra.default.ensureDir(targetMessages);
@@ -479,6 +462,24 @@ async function scaffold(options) {
479
462
  import_path.default.join(targetMessages, `${options.language}.json`)
480
463
  );
481
464
  }
465
+ if (designName && designDir && !options.canvas) {
466
+ const patchDir = import_path.default.join(designDir, "messages-patch");
467
+ if (await import_fs_extra.default.pathExists(patchDir)) {
468
+ const localeFiles = (await import_fs_extra.default.readdir(targetMessages)).filter((f) => f.endsWith(".json"));
469
+ for (const file of localeFiles) {
470
+ const locale = file.replace(/\.json$/, "");
471
+ let patchFile = import_path.default.join(patchDir, `${locale}.json`);
472
+ if (!await import_fs_extra.default.pathExists(patchFile)) {
473
+ patchFile = import_path.default.join(patchDir, "en.json");
474
+ }
475
+ if (!await import_fs_extra.default.pathExists(patchFile)) continue;
476
+ const targetFile = import_path.default.join(targetMessages, file);
477
+ const base = await import_fs_extra.default.readJson(targetFile);
478
+ const patch = await import_fs_extra.default.readJson(patchFile);
479
+ await import_fs_extra.default.writeJson(targetFile, deepMerge(base, patch), { spaces: 2 });
480
+ }
481
+ }
482
+ }
482
483
  if (isMultiLocale) {
483
484
  const appDir = import_path.default.join(targetDir, "src", "app");
484
485
  const localeDir = import_path.default.join(appDir, "[locale]");
@@ -490,16 +491,18 @@ async function scaffold(options) {
490
491
  await import_fs_extra.default.move(import_path.default.join(appDir, entry.name), import_path.default.join(localeDir, entry.name));
491
492
  }
492
493
  }
493
- if (await import_fs_extra.default.pathExists(themeDir)) {
494
- const themeCss = import_path.default.join(themeDir, "globals.css");
495
- if (await import_fs_extra.default.pathExists(themeCss)) {
496
- await import_fs_extra.default.copy(themeCss, import_path.default.join(targetDir, "src", "app", "globals.css"));
497
- }
498
- const themeJson = import_path.default.join(themeDir, "theme.json");
499
- if (await import_fs_extra.default.pathExists(themeJson)) {
500
- await import_fs_extra.default.copy(themeJson, import_path.default.join(targetDir, "theme.json"));
494
+ }
495
+ function deepMerge(base, patch) {
496
+ const out = { ...base };
497
+ for (const [key, value] of Object.entries(patch)) {
498
+ const existing = out[key];
499
+ if (value && typeof value === "object" && !Array.isArray(value) && existing && typeof existing === "object" && !Array.isArray(existing)) {
500
+ out[key] = deepMerge(existing, value);
501
+ } else {
502
+ out[key] = value;
501
503
  }
502
504
  }
505
+ return out;
503
506
  }
504
507
  function findTemplatesDir(fromDir) {
505
508
  let dir = fromDir;
@@ -693,7 +696,11 @@ async function checkForUpdate(name, current) {
693
696
  res.on("end", () => {
694
697
  try {
695
698
  const latest = JSON.parse(data).version;
696
- resolve(latest && latest !== current ? latest : null);
699
+ const newer = latest && latest.split(".").map(Number).reduce((acc, seg, i) => {
700
+ if (acc !== 0) return acc;
701
+ return seg - (Number(current.split(".")[i]) || 0);
702
+ }, 0) > 0;
703
+ resolve(newer ? latest : null);
697
704
  } catch {
698
705
  resolve(null);
699
706
  }
@@ -714,7 +721,10 @@ var program = new import_commander.Command();
714
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(
715
722
  "--api-url <url>",
716
723
  "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) => {
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(
725
+ "--canvas",
726
+ "Scaffold src/ui as bare unstyled skeletons \u2014 a blank canvas for AI-driven design (commerce stays fully wired)"
727
+ ).option("--no-git", "Skip git initialization").option("--no-install", "Skip dependency installation").action(async (projectNameArg, options) => {
718
728
  try {
719
729
  logger.banner(pkg.version);
720
730
  const nodeError = validateNodeVersion(process.versions.node);
@@ -746,6 +756,7 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
746
756
  const VALID_PKG_MANAGERS = ["npm", "pnpm", "yarn", "bun"];
747
757
  const VALID_FRAMEWORKS = ["nextjs"];
748
758
  const VALID_THEMES = ["minimal", "luxury", "playful"];
759
+ const VALID_DESIGNS = ["minimal", "atelier"];
749
760
  let language = options.language;
750
761
  if (language && !VALID_LANGUAGES.includes(language)) {
751
762
  logger.error(
@@ -765,6 +776,11 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
765
776
  logger.error(`Invalid --theme "${theme}". Expected one of: ${VALID_THEMES.join(", ")}`);
766
777
  process.exit(1);
767
778
  }
779
+ let design = options.design;
780
+ if (design && !VALID_DESIGNS.includes(design)) {
781
+ logger.error(`Invalid --design "${design}". Expected one of: ${VALID_DESIGNS.join(", ")}`);
782
+ process.exit(1);
783
+ }
768
784
  let pkgManager = options.pkgManager;
769
785
  if (pkgManager && !VALID_PKG_MANAGERS.includes(pkgManager)) {
770
786
  logger.error(
@@ -774,6 +790,7 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
774
790
  }
775
791
  const skipGit = options.git === false;
776
792
  const skipInstall = options.install === false;
793
+ const canvas = options.canvas === true;
777
794
  let storeInfo = null;
778
795
  let resolvedApiUrl = candidateApiUrls[0];
779
796
  if (connectionId) {
@@ -813,13 +830,16 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
813
830
  language: languageDefault,
814
831
  framework,
815
832
  theme,
816
- pkgManager
833
+ design,
834
+ pkgManager,
835
+ canvas
817
836
  });
818
837
  projectName = answers.projectName;
819
838
  connectionId = answers.connectionId;
820
839
  language = answers.language;
821
840
  framework = answers.framework;
822
841
  theme = answers.theme;
842
+ design = answers.design;
823
843
  pkgManager = answers.pkgManager;
824
844
  }
825
845
  const nameError = validateProjectName(projectName);
@@ -871,7 +891,9 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
871
891
  currency: storeInfo.currency,
872
892
  language: language || storeInfo.language,
873
893
  i18n: storeInfo.i18n,
874
- scaffoldInPlace
894
+ scaffoldInPlace,
895
+ canvas,
896
+ design: canvas ? void 0 : design || "atelier"
875
897
  });
876
898
  scaffoldSpinner.succeed(
877
899
  scaffoldInPlace ? "Scaffolded into current directory" : `Scaffolded into ./${projectName}`
@@ -911,14 +933,38 @@ program.name("create-brainerce-store").description("Scaffold a production-ready
911
933
  );
912
934
  }
913
935
  }
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(`
936
+ if (canvas) {
937
+ logger.success("\nYour Brainerce store is ready \u2014 as a blank canvas!\n");
938
+ logger.info(
939
+ "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:"
940
+ );
941
+ logger.step(`/design <your art-direction brief>`);
942
+ logger.info("\nThen:");
943
+ if (!scaffoldInPlace) {
944
+ logger.step(`cd ${projectName}`);
945
+ }
946
+ logger.step(`${pkgManager}${pkgManager === "npm" ? " run" : ""} dev`);
947
+ logger.info(
948
+ `
949
+ Your store will be running at http://localhost:3000
950
+ (hook contracts + rules: AI-GUIDE.md)`
951
+ );
952
+ } else {
953
+ logger.success("\nYour Brainerce store is ready!\n");
954
+ logger.info("Next steps:");
955
+ if (!scaffoldInPlace) {
956
+ logger.step(`cd ${projectName}`);
957
+ }
958
+ logger.step(`${pkgManager}${pkgManager === "npm" ? " run" : ""} dev`);
959
+ logger.info(`
921
960
  Your store will be running at http://localhost:3000`);
961
+ logger.info(
962
+ `
963
+ Want a unique design? Open this folder in Claude Code and run:
964
+ /design <your art-direction brief>
965
+ (commerce stays intact \u2014 see AI-GUIDE.md)`
966
+ );
967
+ }
922
968
  const latestVersion = await updateCheck;
923
969
  if (latestVersion) logger.updateAvailable(latestVersion, pkg.version);
924
970
  } catch (err) {
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",
@@ -247,7 +279,9 @@
247
279
  "privacyPolicyLink": "Privacy Policy",
248
280
  "privacyRequired": "You must accept the privacy policy to continue",
249
281
  "acceptsMarketing": "Send me news, promotions, and updates by email",
250
- "saveDetailsForNextTime": "Save my details for faster checkout next time"
282
+ "saveDetailsForNextTime": "Save my details for faster checkout next time",
283
+ "orderNotes": "Order notes (optional)",
284
+ "orderNotesPlaceholder": "Anything we should know about your order? e.g. delivery instructions"
251
285
  },
252
286
  "auth": {
253
287
  "loginPageTitle": "Sign In",
@@ -331,6 +365,7 @@
331
365
  "orderHistory": "Order History",
332
366
  "noOrders": "No orders yet",
333
367
  "noOrdersDesc": "Your order history will appear here after your first purchase.",
368
+ "orderNotes": "Your order note",
334
369
  "verified": "Verified",
335
370
  "unverified": "Unverified",
336
371
  "memberSince": "Member since",
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": "בחר אפשרות",
@@ -247,7 +279,9 @@
247
279
  "privacyPolicyLink": "מדיניות הפרטיות",
248
280
  "privacyRequired": "יש לאשר את מדיניות הפרטיות כדי להמשיך",
249
281
  "acceptsMarketing": "שלחו לי חדשות, מבצעים ועדכונים במייל",
250
- "saveDetailsForNextTime": "שמרו את הפרטים שלי לתשלום מהיר בפעם הבאה"
282
+ "saveDetailsForNextTime": "שמרו את הפרטים שלי לתשלום מהיר בפעם הבאה",
283
+ "orderNotes": "הערות להזמנה (אופציונלי)",
284
+ "orderNotesPlaceholder": "משהו שכדאי שנדע על ההזמנה? למשל הנחיות למשלוח"
251
285
  },
252
286
  "auth": {
253
287
  "loginPageTitle": "התחברות",
@@ -331,6 +365,7 @@
331
365
  "orderHistory": "היסטוריית הזמנות",
332
366
  "noOrders": "אין הזמנות עדיין",
333
367
  "noOrdersDesc": "היסטוריית ההזמנות שלך תופיע כאן לאחר הרכישה הראשונה.",
368
+ "orderNotes": "ההערה שלך להזמנה",
334
369
  "verified": "מאומת",
335
370
  "unverified": "לא מאומת",
336
371
  "memberSince": "חבר מאז",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-brainerce-store",
3
- "version": "1.50.5",
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"
@@ -0,0 +1,60 @@
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
+ ## 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
+
44
+ 1. **Commit to ONE art direction** — write it down in 4 lines (typography
45
+ pairing incl. a script-compatible webfont, 6-10 color tokens, layout
46
+ concept, motion language) at the top of `DESIGN.md` in the project root.
47
+ Bold and specific beats safe and generic; do not hedge toward the middle.
48
+ 2. **Tokens first**: rewrite `globals.css` custom properties + font loading
49
+ in the locale layout.
50
+ 3. **Surfaces in order**: header/footer → homepage sections → product card +
51
+ grid → product page → cart. Keep each step compiling.
52
+ 4. **Micro-details**: hover states, loading states, one signature
53
+ interaction. Wrap all motion in `prefers-reduced-motion` guards.
54
+ 5. **Verify** (mandatory): `pnpm exec tsc --noEmit` = 0, then `pnpm dev` and
55
+ drive home → product → add-to-cart → cart in a real browser; screenshot
56
+ desktop (1440) AND mobile (390). For RTL locales check anchoring and
57
+ arrow directions. Fix what looks off before reporting.
58
+
59
+ Report with: the direction chosen, what changed, screenshots, and anything
60
+ 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=