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.
- package/README.md +72 -0
- package/dist/index.js +146 -100
- package/messages/en.json +36 -1
- package/messages/he.json +36 -1
- package/package.json +1 -1
- package/templates/nextjs/base/.claude/commands/design.md +60 -0
- package/templates/nextjs/base/.env.local.ejs +5 -0
- package/templates/nextjs/base/.eslintrc.json +57 -3
- package/templates/nextjs/base/AGENTS.md +41 -0
- package/templates/nextjs/base/AI-GUIDE.md +143 -0
- package/templates/nextjs/base/CLAUDE.md +52 -0
- package/templates/nextjs/base/TRANSLATIONS.md +1 -1
- package/templates/nextjs/base/next.config.ts +2 -2
- package/templates/nextjs/base/package.json.ejs +52 -39
- package/templates/nextjs/base/src/app/account/page.tsx +5 -5
- package/templates/nextjs/base/src/app/api/auth/logout/route.ts +1 -1
- package/templates/nextjs/base/src/app/api/auth/reset-password/route.ts +2 -2
- package/templates/nextjs/base/src/app/api/store/[...path]/route.ts +1 -1
- package/templates/nextjs/base/src/app/auth/callback/page.tsx +4 -4
- package/templates/nextjs/base/src/app/blog/[slug]/page.tsx.ejs +4 -4
- package/templates/nextjs/base/src/app/blog/page.tsx.ejs +2 -2
- package/templates/nextjs/base/src/app/cart/page.tsx +2 -173
- package/templates/nextjs/base/src/app/checkout/page.tsx +12 -11
- package/templates/nextjs/base/src/app/contact/page.tsx +3 -3
- package/templates/nextjs/base/src/app/faq/page.tsx.ejs +2 -2
- package/templates/nextjs/base/src/app/forgot-password/page.tsx +4 -4
- package/templates/nextjs/base/src/app/globals.css +36 -30
- package/templates/nextjs/base/src/app/layout.tsx.ejs +12 -12
- package/templates/nextjs/base/src/app/login/page.tsx +4 -4
- package/templates/nextjs/base/src/app/order-confirmation/page.tsx +19 -12
- package/templates/nextjs/base/src/app/page.tsx +4 -4
- package/templates/nextjs/base/src/app/pages/[slug]/page.tsx.ejs +3 -3
- package/templates/nextjs/base/src/app/payment-complete/page.tsx +1 -1
- package/templates/nextjs/base/src/app/products/[slug]/page.tsx +6 -6
- package/templates/nextjs/base/src/app/products/page.tsx +4 -623
- package/templates/nextjs/base/src/app/register/page.tsx +4 -4
- package/templates/nextjs/base/src/app/reset-password/page.tsx +5 -5
- package/templates/nextjs/base/src/app/sitemap.ts +1 -1
- package/templates/nextjs/base/src/app/verify-email/page.tsx +5 -5
- package/templates/nextjs/base/src/components/account/address-book.tsx +3 -3
- package/templates/nextjs/base/src/components/account/order-customizations.tsx +3 -3
- package/templates/nextjs/base/src/components/account/order-history.tsx +13 -5
- package/templates/nextjs/base/src/components/account/order-payment-block.tsx +2 -2
- package/templates/nextjs/base/src/components/account/order-shipping-block.tsx +2 -2
- package/templates/nextjs/base/src/components/account/order-status-timeline.tsx +2 -2
- package/templates/nextjs/base/src/components/account/profile-section.tsx +3 -3
- package/templates/nextjs/base/src/components/auth/login-form.tsx +4 -4
- package/templates/nextjs/base/src/components/auth/oauth-buttons.tsx +4 -4
- package/templates/nextjs/base/src/components/auth/register-form.tsx +4 -4
- package/templates/nextjs/base/src/components/brainerce-bot.tsx +2 -2
- package/templates/nextjs/base/src/components/checkout/checkout-form.tsx +24 -3
- package/templates/nextjs/base/src/components/checkout/custom-fields-step.tsx +2 -2
- package/templates/nextjs/base/src/components/checkout/delivery-method-step.tsx +2 -2
- package/templates/nextjs/base/src/components/checkout/order-bump-card.tsx +4 -4
- package/templates/nextjs/base/src/components/checkout/payment-step.tsx +20 -16
- package/templates/nextjs/base/src/components/checkout/pickup-step.tsx +3 -3
- package/templates/nextjs/base/src/components/checkout/shipping-step.tsx +3 -3
- package/templates/nextjs/base/src/components/checkout/tax-display.tsx +3 -3
- package/templates/nextjs/base/src/components/seo/organization-json-ld.tsx +2 -2
- package/templates/nextjs/base/src/components/seo/product-json-ld.tsx +3 -3
- package/templates/nextjs/base/src/components/ui/accordion.tsx +53 -0
- package/templates/nextjs/base/src/components/ui/badge.tsx +32 -0
- package/templates/nextjs/base/src/components/ui/button.tsx +49 -0
- package/templates/nextjs/base/src/components/ui/card.tsx +55 -0
- package/templates/nextjs/base/src/components/ui/checkbox.tsx +28 -0
- package/templates/nextjs/base/src/components/ui/dialog.tsx +107 -0
- package/templates/nextjs/base/src/components/ui/input.tsx +22 -0
- package/templates/nextjs/base/src/components/ui/label.tsx +21 -0
- package/templates/nextjs/base/src/components/ui/radio-group.tsx +38 -0
- package/templates/nextjs/base/src/components/ui/select.tsx +153 -0
- package/templates/nextjs/base/src/components/ui/separator.tsx +26 -0
- package/templates/nextjs/base/src/components/ui/sheet.tsx +125 -0
- package/templates/nextjs/base/src/components/ui/skeleton.tsx +7 -0
- package/templates/nextjs/base/src/components/ui/tabs.tsx +55 -0
- package/templates/nextjs/base/src/components/ui/textarea.tsx +21 -0
- package/templates/nextjs/base/src/components/ui/tooltip.tsx +32 -0
- package/templates/nextjs/base/src/core/hooks/use-cart-page.ts +58 -0
- package/templates/nextjs/base/src/core/hooks/use-home-data.ts +51 -0
- package/templates/nextjs/base/src/core/hooks/use-product-listing.ts +295 -0
- package/templates/nextjs/base/src/core/hooks/use-product-page.ts +278 -0
- package/templates/nextjs/base/src/{hooks → core/hooks}/use-search.ts +1 -1
- package/templates/nextjs/base/src/{lib → core/lib}/brainerce.ts.ejs +1 -1
- package/templates/nextjs/base/src/core/lib/product-options.ts +121 -0
- package/templates/nextjs/base/src/{lib → core/lib}/resolve-currency.ts +1 -1
- package/templates/nextjs/base/src/{lib → core/lib}/safe-redirect.ts +1 -1
- package/templates/nextjs/base/src/{lib → core/lib}/sanitize.ts.ejs +1 -1
- package/templates/nextjs/base/src/{lib → core/lib}/use-currency.ts +1 -1
- package/templates/nextjs/base/src/{providers → core/providers}/store-provider.tsx.ejs +4 -4
- package/templates/nextjs/base/src/{components → ui}/cart/cart-bundle-offer.tsx +22 -16
- package/templates/nextjs/base/src/{components → ui}/cart/cart-item.tsx +8 -14
- package/templates/nextjs/base/src/{components → ui}/cart/cart-nudges.tsx +28 -39
- package/templates/nextjs/base/src/{components → ui}/cart/cart-summary.tsx +7 -5
- package/templates/nextjs/base/src/{components → ui}/cart/cart-upgrade-banner.tsx +13 -28
- package/templates/nextjs/base/src/ui/cart/cart-view.tsx +140 -0
- package/templates/nextjs/base/src/{components → ui}/cart/coupon-input.tsx +14 -25
- package/templates/nextjs/base/src/{components → ui}/cart/free-shipping-bar.tsx +6 -7
- package/templates/nextjs/base/src/{components → ui}/cart/reservation-countdown.tsx +95 -103
- package/templates/nextjs/base/src/ui/home/discount-banner-strip.tsx +20 -0
- package/templates/nextjs/base/src/ui/home/featured-products-section.tsx +24 -0
- package/templates/nextjs/base/src/ui/home/hero-section.tsx +27 -0
- package/templates/nextjs/base/src/ui/home/home-client.tsx +29 -0
- package/templates/nextjs/base/src/{components/content → ui/layout}/announcement-bar.tsx.ejs +116 -125
- package/templates/nextjs/base/src/ui/layout/faq-section.tsx.ejs +90 -0
- package/templates/nextjs/base/src/{components/content → ui/layout}/header-account.tsx +5 -20
- package/templates/nextjs/base/src/ui/layout/language-switcher.tsx.ejs +80 -0
- package/templates/nextjs/base/src/{components/content → ui/layout}/rich-text-block.tsx.ejs +1 -1
- package/templates/nextjs/base/src/{components/content → ui/layout}/site-footer.tsx.ejs +144 -164
- package/templates/nextjs/base/src/{components/content → ui/layout}/site-header.tsx.ejs +152 -182
- package/templates/nextjs/base/src/{components/products → ui/product}/customization-fields.tsx +457 -478
- package/templates/nextjs/base/src/{components/products → ui/product}/discount-badge.tsx +18 -22
- package/templates/nextjs/base/src/{components/products → ui/product}/frequently-bought-together.tsx +21 -33
- package/templates/nextjs/base/src/ui/product/modifier-group-selector.tsx +206 -0
- package/templates/nextjs/base/src/{components/shared → ui/product}/price-display.tsx +5 -4
- package/templates/nextjs/base/src/{components/products → ui/product}/product-card.tsx +238 -271
- package/templates/nextjs/base/src/{app/products/[slug] → ui/product}/product-client-section.tsx +455 -631
- package/templates/nextjs/base/src/{components/products → ui/product}/product-grid.tsx +3 -3
- package/templates/nextjs/base/src/ui/product/product-listing.tsx +446 -0
- package/templates/nextjs/base/src/{components/products → ui/product}/recommendation-section.tsx +102 -107
- package/templates/nextjs/base/src/{components/reviews → ui/product}/review-form.tsx +46 -45
- package/templates/nextjs/base/src/{components/reviews/reviews-section.tsx → ui/product/reviews-section.tsx.ejs} +38 -15
- package/templates/nextjs/base/src/{components/products → ui/product}/stock-badge.tsx +61 -63
- package/templates/nextjs/base/src/{components/products → ui/product}/variant-selector.tsx +3 -3
- package/templates/nextjs/base/src/{components → ui}/shared/cdn-image.tsx +1 -1
- package/templates/nextjs/base/src/{components → ui}/shared/loading-spinner.tsx +2 -2
- package/templates/nextjs/base/tailwind.config.ts +69 -30
- package/templates/nextjs/designs/atelier/app-overlay/layout.tsx.ejs +251 -0
- package/templates/nextjs/designs/atelier/globals.css +396 -0
- package/templates/nextjs/designs/atelier/messages-patch/en.json +99 -0
- package/templates/nextjs/designs/atelier/messages-patch/he.json +99 -0
- package/templates/nextjs/designs/atelier/ui/cart/cart-bundle-offer.tsx +110 -0
- package/templates/nextjs/designs/atelier/ui/cart/cart-drawer.tsx +163 -0
- package/templates/nextjs/designs/atelier/ui/cart/cart-item.tsx +140 -0
- package/templates/nextjs/designs/atelier/ui/cart/cart-nudges.tsx +34 -0
- package/templates/nextjs/designs/atelier/ui/cart/cart-summary.tsx +107 -0
- package/templates/nextjs/designs/atelier/ui/cart/cart-upgrade-banner.tsx +123 -0
- package/templates/nextjs/designs/atelier/ui/cart/cart-view.tsx +147 -0
- package/templates/nextjs/designs/atelier/ui/cart/coupon-input.tsx +142 -0
- package/templates/nextjs/designs/atelier/ui/cart/free-shipping-bar.tsx +77 -0
- package/templates/nextjs/designs/atelier/ui/cart/reservation-countdown.tsx +89 -0
- package/templates/nextjs/designs/atelier/ui/home/benefits-band.tsx +34 -0
- package/templates/nextjs/designs/atelier/ui/home/category-tiles.tsx +148 -0
- package/templates/nextjs/designs/atelier/ui/home/discount-banner-strip.tsx +27 -0
- package/templates/nextjs/designs/atelier/ui/home/editorial-band.tsx +73 -0
- package/templates/nextjs/designs/atelier/ui/home/featured-products-section.tsx +36 -0
- package/templates/nextjs/designs/atelier/ui/home/hero-section.tsx +148 -0
- package/templates/nextjs/designs/atelier/ui/home/home-client.tsx +54 -0
- package/templates/nextjs/designs/atelier/ui/home/newsletter-section.tsx +78 -0
- package/templates/nextjs/designs/atelier/ui/home/testimonials-band.tsx +46 -0
- package/templates/nextjs/designs/atelier/ui/layout/announcement-bar.tsx +118 -0
- package/templates/nextjs/designs/atelier/ui/layout/faq-section.tsx +96 -0
- package/templates/nextjs/designs/atelier/ui/layout/header-account.tsx +36 -0
- package/templates/nextjs/designs/atelier/ui/layout/header-cart.tsx +36 -0
- package/templates/nextjs/designs/atelier/ui/layout/language-switcher.tsx.ejs +137 -0
- package/templates/nextjs/designs/atelier/ui/layout/nav-url.ts +14 -0
- package/templates/nextjs/designs/atelier/ui/layout/rich-text-block.tsx +34 -0
- package/templates/nextjs/designs/atelier/ui/layout/site-footer.tsx.ejs +141 -0
- package/templates/nextjs/designs/atelier/ui/layout/site-header.tsx.ejs +138 -0
- package/templates/nextjs/designs/atelier/ui/product/customization-fields.tsx +466 -0
- package/templates/nextjs/designs/atelier/ui/product/discount-badge.tsx +17 -0
- package/templates/nextjs/designs/atelier/ui/product/frequently-bought-together.tsx +196 -0
- package/templates/nextjs/designs/atelier/ui/product/modifier-group-selector.tsx +193 -0
- package/templates/nextjs/designs/atelier/ui/product/price-display.tsx +69 -0
- package/templates/nextjs/designs/atelier/ui/product/product-card.tsx +260 -0
- package/templates/nextjs/designs/atelier/ui/product/product-client-section.tsx +471 -0
- package/templates/nextjs/designs/atelier/ui/product/product-grid.tsx +47 -0
- package/templates/nextjs/designs/atelier/ui/product/product-listing.tsx +260 -0
- package/templates/nextjs/designs/atelier/ui/product/recommendation-section.tsx +95 -0
- package/templates/nextjs/designs/atelier/ui/product/review-form.tsx +267 -0
- package/templates/nextjs/designs/atelier/ui/product/reviews-section.tsx.ejs +148 -0
- package/templates/nextjs/designs/atelier/ui/product/stock-badge.tsx +66 -0
- package/templates/nextjs/designs/atelier/ui/product/variant-selector.tsx +206 -0
- package/templates/nextjs/designs/atelier/ui/shared/cdn-image.tsx +29 -0
- package/templates/nextjs/designs/atelier/ui/shared/fly-to-cart.ts +86 -0
- package/templates/nextjs/designs/atelier/ui/shared/icons.tsx +269 -0
- package/templates/nextjs/designs/atelier/ui/shared/loading-spinner.tsx +64 -0
- package/templates/nextjs/designs/atelier/ui/shared/reveal.tsx +71 -0
- package/templates/nextjs/designs/atelier/ui/shared/select-menu.tsx +125 -0
- package/templates/nextjs/themes/luxury/globals.css +405 -399
- package/templates/nextjs/themes/minimal/globals.css +36 -30
- package/templates/nextjs/themes/playful/globals.css +406 -400
- package/templates/nextjs/ui-canvas/cart/cart-bundle-offer.tsx +89 -0
- package/templates/nextjs/ui-canvas/cart/cart-item.tsx +123 -0
- package/templates/nextjs/ui-canvas/cart/cart-nudges.tsx +27 -0
- package/templates/nextjs/ui-canvas/cart/cart-summary.tsx +99 -0
- package/templates/nextjs/ui-canvas/cart/cart-upgrade-banner.tsx +105 -0
- package/templates/nextjs/ui-canvas/cart/cart-view.tsx +106 -0
- package/templates/nextjs/ui-canvas/cart/coupon-input.tsx +119 -0
- package/templates/nextjs/ui-canvas/cart/free-shipping-bar.tsx +54 -0
- package/templates/nextjs/ui-canvas/cart/reservation-countdown.tsx +81 -0
- package/templates/nextjs/ui-canvas/home/discount-banner-strip.tsx +19 -0
- package/templates/nextjs/ui-canvas/home/featured-products-section.tsx +23 -0
- package/templates/nextjs/ui-canvas/home/hero-section.tsx +21 -0
- package/templates/nextjs/ui-canvas/home/home-client.tsx +26 -0
- package/templates/nextjs/ui-canvas/layout/announcement-bar.tsx.ejs +101 -0
- package/templates/nextjs/ui-canvas/layout/faq-section.tsx.ejs +71 -0
- package/templates/nextjs/ui-canvas/layout/header-account.tsx +31 -0
- package/templates/nextjs/{base/src/components → ui-canvas}/layout/language-switcher.tsx.ejs +76 -70
- package/templates/nextjs/ui-canvas/layout/rich-text-block.tsx.ejs +36 -0
- package/templates/nextjs/ui-canvas/layout/site-footer.tsx.ejs +82 -0
- package/templates/nextjs/ui-canvas/layout/site-header.tsx.ejs +119 -0
- package/templates/nextjs/ui-canvas/product/customization-fields.tsx +445 -0
- package/templates/nextjs/ui-canvas/product/discount-badge.tsx +19 -0
- package/templates/nextjs/ui-canvas/product/frequently-bought-together.tsx +168 -0
- package/templates/nextjs/ui-canvas/product/modifier-group-selector.tsx +165 -0
- package/templates/nextjs/ui-canvas/product/price-display.tsx +52 -0
- package/templates/nextjs/ui-canvas/product/product-card.tsx +197 -0
- package/templates/nextjs/ui-canvas/product/product-client-section.tsx +352 -0
- package/templates/nextjs/ui-canvas/product/product-grid.tsx +27 -0
- package/templates/nextjs/ui-canvas/product/product-listing.tsx +224 -0
- package/templates/nextjs/ui-canvas/product/recommendation-section.tsx +99 -0
- package/templates/nextjs/ui-canvas/product/review-form.tsx +266 -0
- package/templates/nextjs/ui-canvas/product/reviews-section.tsx.ejs +96 -0
- package/templates/nextjs/ui-canvas/product/stock-badge.tsx +45 -0
- package/templates/nextjs/ui-canvas/product/variant-selector.tsx +212 -0
- package/templates/nextjs/ui-canvas/shared/cdn-image.tsx +31 -0
- package/templates/nextjs/ui-canvas/shared/loading-spinner.tsx +27 -0
- package/templates/nextjs/base/src/app/home-client.tsx +0 -98
- package/templates/nextjs/base/src/components/content/faq-section.tsx.ejs +0 -103
- package/templates/nextjs/base/src/components/products/modifier-group-selector.tsx +0 -238
- /package/templates/nextjs/base/src/{lib → core/lib}/auth.ts +0 -0
- /package/templates/nextjs/base/src/{lib → core/lib}/csrf.ts +0 -0
- /package/templates/nextjs/base/src/{lib → core/lib}/image-hosts.ts +0 -0
- /package/templates/nextjs/base/src/{lib → core/lib}/navigation.tsx.ejs +0 -0
- /package/templates/nextjs/base/src/{lib → core/lib}/nonce.ts +0 -0
- /package/templates/nextjs/base/src/{lib → core/lib}/sanitize-html.ts +0 -0
- /package/templates/nextjs/base/src/{lib → core/lib}/seo.ts +0 -0
- /package/templates/nextjs/base/src/{lib → core/lib}/store-info.ts +0 -0
- /package/templates/nextjs/base/src/{lib → core/lib}/translations.ts.ejs +0 -0
- /package/templates/nextjs/base/src/{lib → core/lib}/utils.ts +0 -0
- /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.
|
|
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
|
-
|
|
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
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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:
|
|
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
|
|
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,
|
|
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
|
|
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
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
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
|
-
|
|
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)").
|
|
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
|
-
|
|
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
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
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
|
@@ -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=
|