@fayz-ai/storefront 0.6.0 → 0.7.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 (94) hide show
  1. package/dist/auth.d.ts +12 -2
  2. package/dist/auth.d.ts.map +1 -1
  3. package/dist/blocks.d.ts +3 -3
  4. package/dist/blocks.d.ts.map +1 -1
  5. package/dist/component-contracts.d.ts +124 -0
  6. package/dist/component-contracts.d.ts.map +1 -0
  7. package/dist/component-selectors.d.ts +61 -0
  8. package/dist/component-selectors.d.ts.map +1 -0
  9. package/dist/components/CartDrawer.d.ts.map +1 -1
  10. package/dist/components/ProductCard.d.ts +2 -5
  11. package/dist/components/ProductCard.d.ts.map +1 -1
  12. package/dist/components/ProductEnquiryForm.d.ts +8 -0
  13. package/dist/components/ProductEnquiryForm.d.ts.map +1 -0
  14. package/dist/components/ProductGrid.d.ts.map +1 -1
  15. package/dist/components/RelatedProducts.d.ts +1 -1
  16. package/dist/components/RelatedProducts.d.ts.map +1 -1
  17. package/dist/components/SmoothImage.d.ts +7 -0
  18. package/dist/components/SmoothImage.d.ts.map +1 -0
  19. package/dist/components/StorefrontFooter.d.ts.map +1 -1
  20. package/dist/components/StorefrontHeader.d.ts.map +1 -1
  21. package/dist/components/sections/CategoryShowcase.d.ts.map +1 -1
  22. package/dist/components/sections/HeroSection.d.ts.map +1 -1
  23. package/dist/components/sections/MediaCarousel.d.ts +11 -0
  24. package/dist/components/sections/MediaCarousel.d.ts.map +1 -0
  25. package/dist/components/sections/MiscSections.d.ts.map +1 -1
  26. package/dist/components/sections/ProductRail.d.ts.map +1 -1
  27. package/dist/components/sections/ProductSlider.d.ts +15 -0
  28. package/dist/components/sections/ProductSlider.d.ts.map +1 -0
  29. package/dist/config.d.ts +38 -21
  30. package/dist/config.d.ts.map +1 -1
  31. package/dist/createStorefrontApp.d.ts +12 -0
  32. package/dist/createStorefrontApp.d.ts.map +1 -1
  33. package/dist/define.d.ts +17 -0
  34. package/dist/define.d.ts.map +1 -0
  35. package/dist/hooks/index.d.ts +1 -0
  36. package/dist/hooks/index.d.ts.map +1 -1
  37. package/dist/hooks/useStorefront.d.ts +35 -0
  38. package/dist/hooks/useStorefront.d.ts.map +1 -0
  39. package/dist/index.cjs +904 -185
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.d.ts +20 -10
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +797 -95
  44. package/dist/index.js.map +1 -1
  45. package/dist/pages/CheckoutPage.d.ts.map +1 -1
  46. package/dist/pages/HomePage.d.ts +5 -2
  47. package/dist/pages/HomePage.d.ts.map +1 -1
  48. package/dist/pages/MyPurchasesPage.d.ts.map +1 -1
  49. package/dist/pages/OrderConfirmationPage.d.ts.map +1 -1
  50. package/dist/pages/ProductDetailPage.d.ts.map +1 -1
  51. package/dist/sections.d.ts +45 -2
  52. package/dist/sections.d.ts.map +1 -1
  53. package/dist/testids.d.ts +20 -0
  54. package/dist/testids.d.ts.map +1 -1
  55. package/dist/theme.d.ts +4 -0
  56. package/dist/theme.d.ts.map +1 -1
  57. package/package.json +7 -9
  58. package/src/auth.ts +47 -10
  59. package/src/blocks.tsx +7 -3
  60. package/src/component-contracts.ts +140 -0
  61. package/src/component-selectors.ts +31 -0
  62. package/src/components/CartDrawer.tsx +2 -1
  63. package/src/components/ProductCard.tsx +39 -23
  64. package/src/components/ProductEnquiryForm.tsx +175 -0
  65. package/src/components/ProductGrid.tsx +12 -3
  66. package/src/components/RelatedProducts.tsx +13 -4
  67. package/src/components/SmoothImage.tsx +59 -0
  68. package/src/components/StorefrontFooter.tsx +24 -14
  69. package/src/components/StorefrontHeader.tsx +43 -38
  70. package/src/components/sections/CategoryShowcase.tsx +3 -2
  71. package/src/components/sections/HeroSection.tsx +18 -2
  72. package/src/components/sections/MediaCarousel.tsx +141 -0
  73. package/src/components/sections/MiscSections.tsx +2 -1
  74. package/src/components/sections/ProductRail.tsx +11 -3
  75. package/src/components/sections/ProductSlider.tsx +119 -0
  76. package/src/config.ts +65 -24
  77. package/src/createStorefrontApp.tsx +62 -9
  78. package/src/define.ts +34 -0
  79. package/src/hooks/index.ts +1 -0
  80. package/src/hooks/useStorefront.ts +58 -0
  81. package/src/index.ts +61 -11
  82. package/src/pages/CheckoutPage.tsx +2 -1
  83. package/src/pages/HomePage.tsx +6 -2
  84. package/src/pages/MyPurchasesPage.tsx +2 -1
  85. package/src/pages/OrderConfirmationPage.tsx +2 -1
  86. package/src/pages/ProductDetailPage.tsx +138 -62
  87. package/src/scaffold.tsx +1 -1
  88. package/src/sections.ts +49 -2
  89. package/src/testids.ts +20 -0
  90. package/src/theme.ts +4 -0
  91. package/src/workflows/checkout.ts +1 -1
  92. package/dist/slot-contracts.d.ts +0 -34
  93. package/dist/slot-contracts.d.ts.map +0 -1
  94. package/src/slot-contracts.ts +0 -20
package/dist/index.cjs CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  var runtime = require('@fayz-ai/shop/runtime');
4
4
  var mock = require('@fayz-ai/shop/mock');
5
- var auth = require('@fayz-ai/auth');
5
+ var pluginAuth = require('@fayz-ai/plugin-auth');
6
6
  var zustand = require('zustand');
7
7
  var middleware = require('zustand/middleware');
8
- var React18 = require('react');
8
+ var React6 = require('react');
9
9
  var core = require('@fayz-ai/core');
10
10
  var LucideIcons = require('lucide-react');
11
11
  var jsxRuntime = require('react/jsx-runtime');
@@ -31,7 +31,7 @@ function _interopNamespace(e) {
31
31
  return Object.freeze(n);
32
32
  }
33
33
 
34
- var React18__default = /*#__PURE__*/_interopDefault(React18);
34
+ var React6__default = /*#__PURE__*/_interopDefault(React6);
35
35
  var LucideIcons__namespace = /*#__PURE__*/_interopNamespace(LucideIcons);
36
36
 
37
37
  // src/createStorefrontApp.tsx
@@ -50,14 +50,31 @@ var useSessionStore = zustand.create()(
50
50
 
51
51
  // src/auth.ts
52
52
  var _adapter = null;
53
- function resolveAuthAdapter(configured) {
54
- if (configured && configured !== "mock" && configured !== "supabase") return configured;
55
- if (configured === "supabase") {
56
- console.warn(
57
- '@fayz-ai/shop: auth.adapter="supabase" is legacy. Pass an explicit AuthAdapter or use the Fayz SDK broker path.'
58
- );
59
- }
60
- return auth.createMockAuthAdapter();
53
+ function isAuthAdapter(value) {
54
+ return Boolean(
55
+ value && typeof value === "object" && "getSession" in value && "signIn" in value && "signOut" in value
56
+ );
57
+ }
58
+ function normalizeAuthConfig(configured) {
59
+ if (!configured) return {};
60
+ if (typeof configured === "string" || isAuthAdapter(configured)) return { adapter: configured };
61
+ return configured;
62
+ }
63
+ function resolveAuthAdapter(configured, fallback) {
64
+ const config = normalizeAuthConfig(configured);
65
+ return pluginAuth.createAuthRuntime({
66
+ provider: config.provider ?? config.adapter ?? (fallback?.supabaseUrl ? "supabase" : "mock"),
67
+ adapter: config.adapter,
68
+ routes: config.routes,
69
+ requireAuth: false,
70
+ supabase: {
71
+ url: config.supabase?.url ?? fallback?.supabaseUrl,
72
+ anonKey: config.supabase?.anonKey ?? fallback?.supabaseAnonKey,
73
+ client: config.supabase?.client,
74
+ callbackUrl: config.supabase?.callbackUrl,
75
+ resetPasswordUrl: config.supabase?.resetPasswordUrl
76
+ }
77
+ }).adapter;
61
78
  }
62
79
  function initCustomerAuth(adapter2) {
63
80
  _adapter = adapter2;
@@ -74,7 +91,7 @@ function initCustomerAuth(adapter2) {
74
91
  });
75
92
  }
76
93
  function getCustomerAuthAdapter() {
77
- if (!_adapter) _adapter = auth.createMockAuthAdapter();
94
+ if (!_adapter) _adapter = pluginAuth.createMockAuthAdapter();
78
95
  return _adapter;
79
96
  }
80
97
  async function resolveShopCustomer(email, name) {
@@ -128,18 +145,37 @@ async function signOutCustomer() {
128
145
  }
129
146
  function resolveConfig(config) {
130
147
  const catalogPath = config.home ? "/catalog" : "/";
148
+ const commerceMode = config.commerceMode ?? "checkout";
149
+ const checkoutEnabled = commerceMode === "checkout";
131
150
  return {
132
151
  ...config,
133
152
  currency: config.currency ?? "BRL",
134
153
  locale: config.locale ?? "pt-BR",
135
154
  shipping: { flatRate: config.shipping?.flatRate ?? 0, freeAbove: config.shipping?.freeAbove },
136
155
  payments: { mode: config.payments?.mode ?? "mock" },
156
+ commerceMode,
157
+ enquiry: {
158
+ label: config.enquiry?.label ?? "Contact me",
159
+ successMessage: config.enquiry?.successMessage ?? "Thanks. We received your enquiry.",
160
+ subjectPrefix: config.enquiry?.subjectPrefix ?? "Product enquiry",
161
+ email: config.enquiry?.email,
162
+ whatsappUrl: config.enquiry?.whatsappUrl,
163
+ requirePhone: config.enquiry?.requirePhone
164
+ },
137
165
  features: {
138
166
  // Auto-enable the coupon UI when the store actually ships discount codes
139
167
  // (via config.discounts or a seeded catalog), unless explicitly overridden —
140
168
  // otherwise a store can advertise a coupon with no field to enter it.
141
169
  discounts: config.features?.discounts ?? Boolean(config.discounts?.length || config.catalog?.discounts?.length),
142
- accounts: config.features?.accounts ?? true
170
+ accounts: config.features?.accounts ?? checkoutEnabled,
171
+ cart: config.features?.cart ?? checkoutEnabled,
172
+ checkout: config.features?.checkout ?? checkoutEnabled
173
+ },
174
+ imageLoading: {
175
+ mode: config.imageLoading?.mode ?? "fade",
176
+ durationMs: config.imageLoading?.durationMs ?? 420,
177
+ easing: config.imageLoading?.easing ?? "cubic-bezier(0.22, 1, 0.36, 1)",
178
+ blur: config.imageLoading?.blur ?? true
143
179
  },
144
180
  catalogPath,
145
181
  nav: config.nav ?? (config.home ? [
@@ -148,20 +184,26 @@ function resolveConfig(config) {
148
184
  ] : [{ label: "Loja", to: "/" }])
149
185
  };
150
186
  }
151
- var StorefrontConfigContext = React18.createContext(null);
187
+ var StorefrontConfigContext = React6.createContext(null);
152
188
  var StorefrontConfigProvider = StorefrontConfigContext.Provider;
153
189
  function useStorefrontConfig() {
154
- const ctx = React18.useContext(StorefrontConfigContext);
190
+ const ctx = React6.useContext(StorefrontConfigContext);
155
191
  if (!ctx) throw new Error("useStorefrontConfig must be used inside createStorefrontApp");
156
192
  return ctx;
157
193
  }
194
+ function useStorefrontConfigOptional() {
195
+ return React6.useContext(StorefrontConfigContext);
196
+ }
197
+ function getStorefrontComponents(config) {
198
+ return config.components ?? {};
199
+ }
158
200
  var adapter = core.hashRouterAdapter();
159
201
  function navigateTo(to) {
160
202
  adapter.navigate(to);
161
203
  }
162
204
  function useHashPath() {
163
- const [path, setPath] = React18.useState(() => adapter.getCurrentPath() || "/");
164
- React18.useEffect(() => {
205
+ const [path, setPath] = React6.useState(() => adapter.getCurrentPath() || "/");
206
+ React6.useEffect(() => {
165
207
  const unsub = adapter.onPathChange((p) => {
166
208
  setPath(p || "/");
167
209
  window.scrollTo(0, 0);
@@ -185,7 +227,7 @@ function matchPath(pattern, path) {
185
227
  return params;
186
228
  }
187
229
  function Link({ to, children, ...rest }) {
188
- return React18__default.default.createElement("a", { href: `#${to}`, ...rest }, children);
230
+ return React6__default.default.createElement("a", { href: `#${to}`, ...rest }, children);
189
231
  }
190
232
  var RADIUS_MAP = {
191
233
  none: { button: "0px", card: "0px", input: "0px" },
@@ -259,7 +301,7 @@ function googleFontsHref(theme) {
259
301
  }
260
302
  function StorefrontThemeStyle({ theme }) {
261
303
  const fontsHref = googleFontsHref(theme);
262
- React18.useEffect(() => {
304
+ React6.useEffect(() => {
263
305
  if (!fontsHref) return;
264
306
  const id = "sf-theme-fonts";
265
307
  let link = document.getElementById(id);
@@ -271,7 +313,7 @@ function StorefrontThemeStyle({ theme }) {
271
313
  }
272
314
  link.href = fontsHref;
273
315
  }, [fontsHref]);
274
- return React18__default.default.createElement("style", { "data-sf-theme": theme.name }, themeToCss(theme));
316
+ return React6__default.default.createElement("style", { "data-sf-theme": theme.name }, themeToCss(theme));
275
317
  }
276
318
 
277
319
  // src/format.ts
@@ -418,9 +460,9 @@ var useCatalogStore = zustand.create()((set) => ({
418
460
  reset: () => set(initial)
419
461
  }));
420
462
  function useCategories() {
421
- const [categories, setCategories] = React18.useState([]);
422
- const [loading, setLoading] = React18.useState(true);
423
- React18.useEffect(() => {
463
+ const [categories, setCategories] = React6.useState([]);
464
+ const [loading, setLoading] = React6.useState(true);
465
+ React6.useEffect(() => {
424
466
  let cancelled = false;
425
467
  runtime.getShopProvider().listCategories().then((data) => {
426
468
  if (!cancelled) setCategories(data);
@@ -439,9 +481,9 @@ function prefersReducedMotion() {
439
481
  return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
440
482
  }
441
483
  function useInView(margin = "0px 0px -8% 0px") {
442
- const ref = React18.useRef(null);
443
- const [inView, setInView] = React18.useState(() => prefersReducedMotion());
444
- React18.useEffect(() => {
484
+ const ref = React6.useRef(null);
485
+ const [inView, setInView] = React6.useState(() => prefersReducedMotion());
486
+ React6.useEffect(() => {
445
487
  if (inView) return;
446
488
  const el = ref.current;
447
489
  if (!el || typeof IntersectionObserver === "undefined") {
@@ -476,8 +518,8 @@ function Reveal({ delay = 0, children, className, style, ...rest }) {
476
518
  );
477
519
  }
478
520
  function useScrolled(threshold = 8) {
479
- const [scrolled, setScrolled] = React18.useState(false);
480
- React18.useEffect(() => {
521
+ const [scrolled, setScrolled] = React6.useState(false);
522
+ React6.useEffect(() => {
481
523
  const onScroll = () => setScrolled(window.scrollY > threshold);
482
524
  onScroll();
483
525
  window.addEventListener("scroll", onScroll, { passive: true });
@@ -486,9 +528,9 @@ function useScrolled(threshold = 8) {
486
528
  return scrolled;
487
529
  }
488
530
  function usePopOnChange(value) {
489
- const [pop, setPop] = React18.useState(false);
490
- const prev = React18.useRef(value);
491
- React18.useEffect(() => {
531
+ const [pop, setPop] = React6.useState(false);
532
+ const prev = React6.useRef(value);
533
+ React6.useEffect(() => {
492
534
  if (prev.current !== value && value > 0) {
493
535
  setPop(true);
494
536
  const t = setTimeout(() => setPop(false), 400);
@@ -497,7 +539,7 @@ function usePopOnChange(value) {
497
539
  prev.current = value;
498
540
  return void 0;
499
541
  }, [value]);
500
- React18.useEffect(() => {
542
+ React6.useEffect(() => {
501
543
  prev.current = value;
502
544
  });
503
545
  return pop;
@@ -519,6 +561,13 @@ var TID = {
519
561
  hero: "home-hero",
520
562
  heroPrev: "hero-prev",
521
563
  heroNext: "hero-next",
564
+ mediaCarousel: "media-carousel",
565
+ mediaCarouselPrev: "media-carousel-prev",
566
+ mediaCarouselNext: "media-carousel-next",
567
+ productSlider: "product-slider",
568
+ productSliderViewport: "product-slider-viewport",
569
+ productSliderPrev: "product-slider-prev",
570
+ productSliderNext: "product-slider-next",
522
571
  categoryShowcase: "category-showcase",
523
572
  productRail: "product-rail",
524
573
  benefits: "benefits-row",
@@ -553,6 +602,19 @@ var TID = {
553
602
  productOptionValue: (id, value) => `product-option-${id.toLowerCase().replace(/\s+/g, "-")}-${value.toLowerCase().replace(/\s+/g, "-")}`,
554
603
  pdpQtyInput: "pdp-qty-input",
555
604
  pdpAddToCart: "pdp-add-to-cart",
605
+ pdpRoot: "pdp-root",
606
+ pdpGallery: "pdp-gallery",
607
+ pdpActions: "pdp-actions",
608
+ pdpEnquiryButton: "pdp-enquiry-button",
609
+ enquiryForm: "enquiry-form",
610
+ enquiryName: "enquiry-name",
611
+ enquiryEmail: "enquiry-email",
612
+ enquiryPhone: "enquiry-phone",
613
+ enquirySubject: "enquiry-subject",
614
+ enquiryMessage: "enquiry-message",
615
+ enquirySubmit: "enquiry-submit",
616
+ enquirySuccess: "enquiry-success",
617
+ enquiryError: "enquiry-error",
556
618
  // cart
557
619
  cartDrawer: "cart-drawer",
558
620
  cartLine: "cart-line",
@@ -616,7 +678,7 @@ var headerDivider = {
616
678
  var ANNOUNCEMENT_KEY = "fayz.storefront.announcement.dismissed";
617
679
  function AnnouncementBar() {
618
680
  const config = useStorefrontConfig();
619
- const [dismissed, setDismissed] = React18.useState(() => {
681
+ const [dismissed, setDismissed] = React6.useState(() => {
620
682
  try {
621
683
  return localStorage.getItem(ANNOUNCEMENT_KEY) === config.announcement;
622
684
  } catch {
@@ -707,7 +769,7 @@ function HeaderActions() {
707
769
  const count = useCartStore(selectCount);
708
770
  const openDrawer = useCartStore((s) => s.openDrawer);
709
771
  const session = useSessionStore();
710
- const [accountOpen, setAccountOpen] = React18.useState(false);
772
+ const [accountOpen, setAccountOpen] = React6.useState(false);
711
773
  const pop = usePopOnChange(count);
712
774
  const firstName = session.name?.split(/\s+/)[0];
713
775
  const initial2 = (firstName ?? session.email ?? "C").slice(0, 1).toUpperCase();
@@ -780,7 +842,7 @@ function HeaderActions() {
780
842
  }
781
843
  )
782
844
  ] }),
783
- /* @__PURE__ */ jsxRuntime.jsxs(
845
+ config.features.cart && /* @__PURE__ */ jsxRuntime.jsxs(
784
846
  "button",
785
847
  {
786
848
  type: "button",
@@ -807,12 +869,13 @@ function NavLinks({ className }) {
807
869
  const config = useStorefrontConfig();
808
870
  const { categories } = useCategories();
809
871
  const setCategoryId = useCatalogStore((s) => s.setCategoryId);
872
+ const showCategories = config.theme?.header?.showCategories !== false;
810
873
  const goCategory = (categoryId) => {
811
874
  useCatalogStore.getState().reset();
812
875
  setCategoryId(categoryId);
813
876
  navigateTo(config.catalogPath);
814
877
  };
815
- return /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: `flex items-center gap-6 ${className ?? ""}`, children: [
878
+ return /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: `flex min-w-max items-center gap-6 ${className ?? ""}`, children: [
816
879
  config.nav.map((link) => /* @__PURE__ */ jsxRuntime.jsx(
817
880
  Link,
818
881
  {
@@ -823,7 +886,7 @@ function NavLinks({ className }) {
823
886
  },
824
887
  link.to
825
888
  )),
826
- categories.slice(0, 6).map((c) => /* @__PURE__ */ jsxRuntime.jsxs(
889
+ showCategories && categories.slice(0, 6).map((c) => /* @__PURE__ */ jsxRuntime.jsxs(
827
890
  "button",
828
891
  {
829
892
  type: "button",
@@ -843,6 +906,7 @@ function StorefrontHeader() {
843
906
  const config = useStorefrontConfig();
844
907
  const variant = config.theme?.header?.variant ?? "classic";
845
908
  const scrolled = useScrolled();
909
+ const showSearch = config.theme?.header?.showSearch !== false;
846
910
  const logo = /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/", className: "sf-heading shrink-0 text-xl font-bold tracking-tight", children: config.logo ?? config.name });
847
911
  return /* @__PURE__ */ jsxRuntime.jsxs(
848
912
  "header",
@@ -856,21 +920,21 @@ function StorefrontHeader() {
856
920
  // Rio/Flex pattern: centered logo row, nav row below
857
921
  /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
858
922
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto grid h-16 max-w-7xl grid-cols-[1fr_auto_1fr] items-center px-4 sm:px-6", children: [
859
- /* @__PURE__ */ jsxRuntime.jsx(SearchInput, { className: "hidden w-full max-w-xs sm:block" }),
923
+ showSearch ? /* @__PURE__ */ jsxRuntime.jsx(SearchInput, { className: "hidden w-full max-w-xs sm:block" }) : /* @__PURE__ */ jsxRuntime.jsx("div", {}),
860
924
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center", children: logo }),
861
925
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "justify-self-end", children: /* @__PURE__ */ jsxRuntime.jsx(HeaderActions, {}) })
862
926
  ] }),
863
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden justify-center border-t py-2.5 sm:flex", style: headerDivider, children: /* @__PURE__ */ jsxRuntime.jsx(NavLinks, {}) })
927
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex overflow-x-auto border-t py-2.5 sm:justify-center", style: headerDivider, children: /* @__PURE__ */ jsxRuntime.jsx(NavLinks, {}) })
864
928
  ] })
865
929
  ) : variant === "search" ? (
866
930
  // Brasília pattern: prominent search left, logo center, actions right; nav row below
867
931
  /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
868
932
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto grid h-16 max-w-7xl grid-cols-[1fr_auto_1fr] items-center gap-6 px-4 sm:px-6", children: [
869
- /* @__PURE__ */ jsxRuntime.jsx(SearchInput, { className: "hidden w-full max-w-sm sm:block" }),
933
+ showSearch ? /* @__PURE__ */ jsxRuntime.jsx(SearchInput, { className: "hidden w-full max-w-sm sm:block" }) : /* @__PURE__ */ jsxRuntime.jsx("div", {}),
870
934
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center", children: logo }),
871
935
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "justify-self-end", children: /* @__PURE__ */ jsxRuntime.jsx(HeaderActions, {}) })
872
936
  ] }),
873
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden border-t sm:block", style: headerDivider, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto max-w-7xl px-4 py-2.5 sm:px-6", children: /* @__PURE__ */ jsxRuntime.jsx(NavLinks, {}) }) })
937
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto border-t", style: headerDivider, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto max-w-7xl px-4 py-2.5 sm:px-6", children: /* @__PURE__ */ jsxRuntime.jsx(NavLinks, {}) }) })
874
938
  ] })
875
939
  ) : variant === "minimal" ? (
876
940
  // Uyuni pattern: nav left, centered logo, actions right — single compact row
@@ -878,7 +942,7 @@ function StorefrontHeader() {
878
942
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-5", children: /* @__PURE__ */ jsxRuntime.jsx(NavLinks, { className: "hidden sm:flex" }) }),
879
943
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-center", children: logo }),
880
944
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-3", children: [
881
- /* @__PURE__ */ jsxRuntime.jsx(SearchInput, { className: "hidden w-44 lg:block" }),
945
+ showSearch && /* @__PURE__ */ jsxRuntime.jsx(SearchInput, { className: "hidden w-44 lg:block" }),
882
946
  /* @__PURE__ */ jsxRuntime.jsx(HeaderActions, {})
883
947
  ] })
884
948
  ] })
@@ -887,7 +951,7 @@ function StorefrontHeader() {
887
951
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto flex h-16 max-w-7xl items-center gap-6 px-4 sm:px-6", children: [
888
952
  logo,
889
953
  /* @__PURE__ */ jsxRuntime.jsx(NavLinks, { className: "hidden md:flex" }),
890
- /* @__PURE__ */ jsxRuntime.jsx(SearchInput, { className: "ml-auto hidden w-full max-w-sm sm:block" }),
954
+ showSearch && /* @__PURE__ */ jsxRuntime.jsx(SearchInput, { className: "ml-auto hidden w-full max-w-sm sm:block" }),
891
955
  /* @__PURE__ */ jsxRuntime.jsx(HeaderActions, {})
892
956
  ] })
893
957
  )
@@ -900,6 +964,17 @@ function StorefrontFooter() {
900
964
  const { categories } = useCategories();
901
965
  const visibleCategories = categories.slice(0, 8);
902
966
  const hiddenCategoryCount = Math.max(categories.length - visibleCategories.length, 0);
967
+ const labels = {
968
+ categories: "Departamentos",
969
+ navigation: "Navega\xE7\xE3o",
970
+ contact: "Contato",
971
+ viewAll: "Ver todos",
972
+ purchases: "Minhas compras",
973
+ privacy: "Privacidade",
974
+ terms: "Termos",
975
+ returns: "Trocas e devolu\xE7\xF5es",
976
+ ...config.footer?.labels
977
+ };
903
978
  const goCategory = (categoryId) => {
904
979
  useCatalogStore.getState().reset();
905
980
  useCatalogStore.getState().setCategoryId(categoryId);
@@ -912,7 +987,7 @@ function StorefrontFooter() {
912
987
  config.footer?.about && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-3 text-sm leading-relaxed text-muted-foreground", children: config.footer.about })
913
988
  ] }),
914
989
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
915
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "Departamentos" }),
990
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: labels.categories }),
916
991
  /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "space-y-2 text-sm", children: [
917
992
  visibleCategories.map((c) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
918
993
  "button",
@@ -924,21 +999,22 @@ function StorefrontFooter() {
924
999
  }
925
1000
  ) }, c.id)),
926
1001
  hiddenCategoryCount > 0 && /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(Link, { to: config.catalogPath, className: "font-medium underline-offset-2 transition-opacity hover:underline", children: [
927
- "Ver todos (",
1002
+ labels.viewAll,
1003
+ " (",
928
1004
  categories.length,
929
1005
  ")"
930
1006
  ] }) })
931
1007
  ] })
932
1008
  ] }),
933
1009
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
934
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "Navega\xE7\xE3o" }),
1010
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: labels.navigation }),
935
1011
  /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "space-y-2 text-sm", children: [
936
1012
  config.nav.map((link) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(Link, { to: link.to, className: "transition-opacity hover:opacity-70", children: link.label }) }, link.to)),
937
- /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/account", className: "transition-opacity hover:opacity-70", children: "Minhas compras" }) })
1013
+ config.features.accounts && /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/account", className: "transition-opacity hover:opacity-70", children: labels.purchases }) })
938
1014
  ] })
939
1015
  ] }),
940
1016
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
941
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "Contato" }),
1017
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: labels.contact }),
942
1018
  /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "space-y-2 text-sm text-muted-foreground", children: [
943
1019
  config.footer?.contact?.phone && /* @__PURE__ */ jsxRuntime.jsx("li", { children: config.footer.contact.phone }),
944
1020
  config.footer?.contact?.email && /* @__PURE__ */ jsxRuntime.jsx("li", { children: config.footer.contact.email }),
@@ -949,17 +1025,15 @@ function StorefrontFooter() {
949
1025
  ] }),
950
1026
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto flex max-w-7xl flex-col items-center gap-3 px-4 py-5 text-xs text-muted-foreground sm:flex-row sm:justify-between sm:px-6", children: [
951
1027
  /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "flex flex-wrap items-center justify-center gap-x-5 gap-y-2", children: [
952
- /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/privacy", className: "transition-colors hover:text-foreground", children: "Privacidade" }),
953
- /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/terms", className: "transition-colors hover:text-foreground", children: "Termos" }),
954
- /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/refunds", className: "transition-colors hover:text-foreground", children: "Trocas e devolu\xE7\xF5es" })
1028
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/privacy", className: "transition-colors hover:text-foreground", children: labels.privacy }),
1029
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/terms", className: "transition-colors hover:text-foreground", children: labels.terms }),
1030
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/refunds", className: "transition-colors hover:text-foreground", children: labels.returns })
955
1031
  ] }),
956
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-center", children: [
1032
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-center", children: config.footer?.credit ?? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
957
1033
  "\xA9 ",
958
1034
  config.name,
959
- " \u2014 powered by Fayz \xB7 Fotos:",
960
- " ",
961
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: "https://unsplash.com", target: "_blank", rel: "noreferrer", className: "underline-offset-2 hover:underline", children: "Unsplash" })
962
- ] })
1035
+ " \u2014 powered by Fayz"
1036
+ ] }) })
963
1037
  ] }) })
964
1038
  ] });
965
1039
  }
@@ -971,7 +1045,7 @@ var REASON_MESSAGE = {
971
1045
  };
972
1046
  function useDiscountValidator() {
973
1047
  const config = useStorefrontConfig();
974
- return React18.useCallback(async (code) => {
1048
+ return React6.useCallback(async (code) => {
975
1049
  const normalized = code.trim().toUpperCase();
976
1050
  if (!normalized) return { valid: false, percent: 0, message: "Informe um cupom." };
977
1051
  const configDiscount = config.discounts?.find((d) => d.code.trim().toUpperCase() === normalized);
@@ -1052,14 +1126,61 @@ function QuantityInput({ value, onChange, min = 1, max, testId, incTestId, decTe
1052
1126
  )
1053
1127
  ] });
1054
1128
  }
1129
+ function SmoothImage({
1130
+ imageLoading,
1131
+ loading = "lazy",
1132
+ decoding = "async",
1133
+ onLoad,
1134
+ style,
1135
+ ...props
1136
+ }) {
1137
+ const config = useStorefrontConfigOptional();
1138
+ const resolved = {
1139
+ ...config?.imageLoading,
1140
+ ...imageLoading
1141
+ };
1142
+ const mode = resolved.mode ?? "fade";
1143
+ const durationMs = resolved.durationMs ?? 420;
1144
+ const easing = resolved.easing ?? "cubic-bezier(0.22, 1, 0.36, 1)";
1145
+ const blur = resolved.blur ?? true;
1146
+ const ref = React6__default.default.useRef(null);
1147
+ const [loaded, setLoaded] = React6__default.default.useState(mode === "none");
1148
+ React6__default.default.useEffect(() => {
1149
+ if (mode === "none") {
1150
+ setLoaded(true);
1151
+ return;
1152
+ }
1153
+ if (ref.current?.complete && ref.current.naturalWidth > 0) setLoaded(true);
1154
+ }, [mode, props.src]);
1155
+ const revealStyle = mode === "none" ? {} : {
1156
+ opacity: loaded ? 1 : 0,
1157
+ filter: blur && !loaded ? "blur(10px)" : "blur(0px)",
1158
+ transition: `opacity ${durationMs}ms ${easing}, filter ${durationMs}ms ${easing}, transform ${durationMs}ms ${easing}`
1159
+ };
1160
+ return /* @__PURE__ */ jsxRuntime.jsx(
1161
+ "img",
1162
+ {
1163
+ ...props,
1164
+ ref,
1165
+ loading,
1166
+ decoding,
1167
+ "data-sf-image-loaded": loaded ? "true" : "false",
1168
+ style: { ...style, ...revealStyle },
1169
+ onLoad: (event) => {
1170
+ setLoaded(true);
1171
+ onLoad?.(event);
1172
+ }
1173
+ }
1174
+ );
1175
+ }
1055
1176
  function CartDrawer() {
1056
1177
  const config = useStorefrontConfig();
1057
1178
  const cart = useCartStore();
1058
1179
  const validate = useDiscountValidator();
1059
- const [code, setCode] = React18.useState("");
1060
- const [discountError, setDiscountError] = React18.useState(null);
1061
- const [applying, setApplying] = React18.useState(false);
1062
- React18.useEffect(() => {
1180
+ const [code, setCode] = React6.useState("");
1181
+ const [discountError, setDiscountError] = React6.useState(null);
1182
+ const [applying, setApplying] = React6.useState(false);
1183
+ React6.useEffect(() => {
1063
1184
  if (!cart.isOpen) return;
1064
1185
  const onKey = (e) => {
1065
1186
  if (e.key === "Escape") cart.closeDrawer();
@@ -1151,7 +1272,7 @@ function CartDrawer() {
1151
1272
  "data-line-id": line.lineId ?? line.productId,
1152
1273
  className: `flex animate-fade-up gap-3 rounded-lg p-1.5 transition-all hover:bg-muted/40 ${(line.lineId ?? line.productId) === cart.justAddedLineId ? "bg-primary/5 ring-1 ring-primary/30" : ""}`,
1153
1274
  children: [
1154
- line.imageUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: line.imageUrl, alt: line.name, className: "h-20 w-20 shrink-0 rounded-lg border object-cover" }),
1275
+ line.imageUrl && /* @__PURE__ */ jsxRuntime.jsx(SmoothImage, { src: line.imageUrl, alt: line.name, className: "h-20 w-20 shrink-0 rounded-lg border object-cover" }),
1155
1276
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col", children: [
1156
1277
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
1157
1278
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium leading-snug", children: line.name }),
@@ -1282,12 +1403,12 @@ function CartDrawer() {
1282
1403
  ] });
1283
1404
  }
1284
1405
  function useProducts(opts) {
1285
- const [products, setProducts] = React18.useState([]);
1286
- const [loading, setLoading] = React18.useState(true);
1287
- const [error, setError] = React18.useState(null);
1288
- const [nonce, setNonce] = React18.useState(0);
1406
+ const [products, setProducts] = React6.useState([]);
1407
+ const [loading, setLoading] = React6.useState(true);
1408
+ const [error, setError] = React6.useState(null);
1409
+ const [nonce, setNonce] = React6.useState(0);
1289
1410
  const key = JSON.stringify(opts);
1290
- React18.useEffect(() => {
1411
+ React6.useEffect(() => {
1291
1412
  let cancelled = false;
1292
1413
  setLoading(true);
1293
1414
  runtime.getShopProvider().listProducts(JSON.parse(key)).then((data) => {
@@ -1321,36 +1442,91 @@ function Price({ value, compareAt, testId, compareTestId, className }) {
1321
1442
  ] });
1322
1443
  }
1323
1444
 
1324
- // src/slot-contracts.ts
1325
- var productCardSlotContract = {
1445
+ // src/component-selectors.ts
1446
+ var productCardComponentContract = {
1326
1447
  root: { "data-testid": TID.productCard },
1327
1448
  name: { "data-testid": TID.productCardName },
1328
1449
  priceTestId: TID.productCardPrice,
1329
1450
  addButton: { "data-testid": TID.productCardAdd }
1330
1451
  };
1331
- var storefrontSlotContracts = {
1332
- productCard: productCardSlotContract
1452
+ var storefrontComponentContracts = {
1453
+ productCard: productCardComponentContract,
1454
+ productDetail: {
1455
+ root: { "data-testid": TID.pdpRoot },
1456
+ gallery: { "data-testid": TID.pdpGallery },
1457
+ name: { "data-testid": TID.pdpName },
1458
+ description: { "data-testid": TID.pdpDescription },
1459
+ actions: { "data-testid": TID.pdpActions },
1460
+ addButton: { "data-testid": TID.pdpAddToCart },
1461
+ enquiryButton: { "data-testid": TID.pdpEnquiryButton },
1462
+ enquiryForm: { "data-testid": TID.enquiryForm }
1463
+ }
1333
1464
  };
1334
- function ProductCard({ product }) {
1465
+ function useStorefront() {
1466
+ return useStorefrontConfig();
1467
+ }
1468
+ function useCatalog() {
1335
1469
  const config = useStorefrontConfig();
1336
- const addItem = useCartStore((s) => s.addItem);
1470
+ const catalog = useCatalogStore();
1471
+ return {
1472
+ ...catalog,
1473
+ catalogPath: config.catalogPath,
1474
+ config
1475
+ };
1476
+ }
1477
+ function useCart() {
1478
+ return useCartStore();
1479
+ }
1480
+ function useEnquiry() {
1481
+ const config = useStorefrontConfig();
1482
+ const createProductEnquiry = async (input) => {
1483
+ const provider = runtime.getShopProvider();
1484
+ if (!provider.createProductEnquiry) {
1485
+ throw new Error("The active shop provider does not support product enquiries.");
1486
+ }
1487
+ return provider.createProductEnquiry(input);
1488
+ };
1489
+ return {
1490
+ enquiry: config.enquiry,
1491
+ createProductEnquiry
1492
+ };
1493
+ }
1494
+ function useStorefrontActions() {
1495
+ const addItem = useCartStore((state) => state.addItem);
1496
+ const openDrawer = useCartStore((state) => state.openDrawer);
1497
+ const closeDrawer = useCartStore((state) => state.closeDrawer);
1498
+ const { createProductEnquiry } = useEnquiry();
1499
+ return {
1500
+ navigateTo,
1501
+ addToCart: (product, quantity, options) => addItem(product, quantity, options),
1502
+ openCart: openDrawer,
1503
+ closeCart: closeDrawer,
1504
+ createProductEnquiry
1505
+ };
1506
+ }
1507
+ function ProductCard({ product, config: providedConfig, actions: providedActions }) {
1508
+ const runtimeConfig = useStorefrontConfig();
1509
+ const runtimeActions = useStorefrontActions();
1510
+ const config = providedConfig ?? runtimeConfig;
1511
+ const actions = providedActions ?? runtimeActions;
1337
1512
  const hasOptions = getProductOptionGroups(product).length > 0;
1338
1513
  const soldOut = product.inventoryCount <= 0;
1339
1514
  const onSale = product.compareAtPrice != null && product.compareAtPrice > product.price;
1340
1515
  const image = product.images.find((i) => i.isPrimary) ?? product.images[0];
1341
1516
  const cardStyle = config.theme?.productCard?.style ?? "card";
1342
1517
  const aspect = config.theme?.productCard?.imageAspect === "portrait" ? "aspect-[3/4]" : "aspect-square";
1518
+ const purchaseMode = config.commerceMode === "checkout";
1343
1519
  return /* @__PURE__ */ jsxRuntime.jsxs(
1344
1520
  "div",
1345
1521
  {
1346
- ...productCardSlotContract.root,
1522
+ ...productCardComponentContract.root,
1347
1523
  "data-slug": product.slug,
1348
1524
  className: `group flex flex-col overflow-hidden transition-all duration-300 ${cardStyle === "editorial" ? "hover:-translate-y-1" : "border bg-card hover:-translate-y-1 hover:shadow-lg"}`,
1349
1525
  style: { borderRadius: "var(--sf-radius-card)" },
1350
1526
  children: [
1351
1527
  /* @__PURE__ */ jsxRuntime.jsxs(Link, { to: `/product/${product.slug}`, className: `relative block ${aspect} overflow-hidden bg-muted`, style: { borderRadius: cardStyle === "editorial" ? "var(--sf-radius-card)" : void 0 }, children: [
1352
1528
  image && /* @__PURE__ */ jsxRuntime.jsx(
1353
- "img",
1529
+ SmoothImage,
1354
1530
  {
1355
1531
  src: image.url,
1356
1532
  alt: image.altText ?? product.name,
@@ -1383,33 +1559,37 @@ function ProductCard({ product }) {
1383
1559
  Link,
1384
1560
  {
1385
1561
  to: `/product/${product.slug}`,
1386
- ...productCardSlotContract.name,
1562
+ ...productCardComponentContract.name,
1387
1563
  className: "font-medium leading-snug hover:underline",
1388
1564
  children: product.name
1389
1565
  }
1390
1566
  ),
1391
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-auto flex items-center justify-between pt-2", children: [
1392
- /* @__PURE__ */ jsxRuntime.jsx(
1567
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-auto flex items-center justify-between gap-3 pt-2", children: [
1568
+ purchaseMode ? /* @__PURE__ */ jsxRuntime.jsx(
1393
1569
  Price,
1394
1570
  {
1395
1571
  value: product.price,
1396
1572
  compareAt: product.compareAtPrice,
1397
- testId: productCardSlotContract.priceTestId
1573
+ testId: productCardComponentContract.priceTestId
1398
1574
  }
1399
- ),
1575
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { "data-testid": productCardComponentContract.priceTestId, className: "text-sm text-muted-foreground", children: config.commerceMode === "enquiry" ? config.enquiry.label : "Ver detalhes" }),
1400
1576
  /* @__PURE__ */ jsxRuntime.jsx(
1401
1577
  "button",
1402
1578
  {
1403
1579
  type: "button",
1404
- ...productCardSlotContract.addButton,
1405
- disabled: soldOut,
1406
- "aria-label": hasOptions ? `Escolher op\xE7\xF5es de ${product.name}` : `Adicionar ${product.name} ao carrinho`,
1580
+ ...productCardComponentContract.addButton,
1581
+ disabled: purchaseMode && soldOut,
1582
+ "aria-label": purchaseMode ? hasOptions ? `Escolher op\xE7\xF5es de ${product.name}` : `Adicionar ${product.name} ao carrinho` : `${config.enquiry.label} ${product.name}`,
1407
1583
  onClick: () => {
1584
+ if (!purchaseMode) {
1585
+ navigateTo(`/product/${product.slug}`);
1586
+ return;
1587
+ }
1408
1588
  if (hasOptions) {
1409
1589
  navigateTo(`/product/${product.slug}`);
1410
1590
  return;
1411
1591
  }
1412
- addItem(product);
1592
+ actions.addToCart(product);
1413
1593
  },
1414
1594
  className: "rounded-full border bg-background/90 p-2.5 text-foreground shadow-sm transition-all duration-200 hover:scale-110 hover:bg-primary hover:text-primary-foreground hover:shadow disabled:cursor-not-allowed disabled:opacity-40 lg:translate-y-1 lg:opacity-0 lg:group-hover:translate-y-0 lg:group-hover:opacity-100 lg:disabled:opacity-0 lg:group-hover:disabled:opacity-40",
1415
1595
  children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.ShoppingBag, { className: "h-4 w-4" })
@@ -1423,14 +1603,25 @@ function ProductCard({ product }) {
1423
1603
  }
1424
1604
  function ProductGrid({ products, loading }) {
1425
1605
  const config = useStorefrontConfig();
1426
- const ProductCardComponent = config.slots?.ProductCard ?? ProductCard;
1606
+ const actions = useStorefrontActions();
1607
+ const components = getStorefrontComponents(config);
1608
+ const ProductCardComponent = components.ProductCard ?? ProductCard;
1427
1609
  if (loading) {
1428
1610
  return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-testid": TID.productGrid, className: "grid grid-cols-2 gap-5 md:grid-cols-3 xl:grid-cols-4", children: Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[3/4] animate-pulse rounded-xl bg-muted" }, i)) });
1429
1611
  }
1430
1612
  if (products.length === 0) {
1431
1613
  return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-testid": TID.productGrid, className: "py-20 text-center text-muted-foreground", children: "Nenhum produto encontrado." });
1432
1614
  }
1433
- return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-testid": TID.productGrid, className: "grid grid-cols-2 gap-5 md:grid-cols-3 xl:grid-cols-4", children: products.map((p) => /* @__PURE__ */ jsxRuntime.jsx(ProductCardComponent, { product: p }, p.id)) });
1615
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-testid": TID.productGrid, className: "grid grid-cols-2 gap-5 md:grid-cols-3 xl:grid-cols-4", children: products.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
1616
+ ProductCardComponent,
1617
+ {
1618
+ product: p,
1619
+ config,
1620
+ commerceMode: config.commerceMode,
1621
+ actions
1622
+ },
1623
+ p.id
1624
+ )) });
1434
1625
  }
1435
1626
  function FiltersPanel() {
1436
1627
  const { categories } = useCategories();
@@ -1535,10 +1726,10 @@ function FiltersPanel() {
1535
1726
  }
1536
1727
  function useStorefrontHead(input) {
1537
1728
  const { title, description } = input;
1538
- React18.useEffect(() => {
1729
+ React6.useEffect(() => {
1539
1730
  if (title) document.title = title;
1540
1731
  }, [title]);
1541
- React18.useEffect(() => {
1732
+ React6.useEffect(() => {
1542
1733
  if (!description) return;
1543
1734
  let tag = document.head.querySelector('meta[name="description"]');
1544
1735
  if (!tag) {
@@ -1557,9 +1748,9 @@ var SORT_MAP = {
1557
1748
  };
1558
1749
  var SORTS = ["newest", "price-asc", "price-desc", "name"];
1559
1750
  function useCatalogUrlSync() {
1560
- const hydrated = React18.useRef(false);
1751
+ const hydrated = React6.useRef(false);
1561
1752
  const catalog = useCatalogStore();
1562
- React18.useEffect(() => {
1753
+ React6.useEffect(() => {
1563
1754
  if (hydrated.current) return;
1564
1755
  hydrated.current = true;
1565
1756
  const q = window.location.hash.split("?")[1];
@@ -1574,7 +1765,7 @@ function useCatalogUrlSync() {
1574
1765
  if (p.has("min")) s.setPriceMin(Number(p.get("min")) || null);
1575
1766
  if (p.has("max")) s.setPriceMax(Number(p.get("max")) || null);
1576
1767
  }, []);
1577
- React18.useEffect(() => {
1768
+ React6.useEffect(() => {
1578
1769
  const p = new URLSearchParams();
1579
1770
  if (catalog.search) p.set("search", catalog.search);
1580
1771
  if (catalog.categoryId) p.set("category", catalog.categoryId);
@@ -1602,7 +1793,7 @@ function CatalogPage() {
1602
1793
  ...SORT_MAP[catalog.sort]
1603
1794
  };
1604
1795
  const { products, loading, error, reload } = useProducts(providerOpts);
1605
- const filtered = React18.useMemo(
1796
+ const filtered = React6.useMemo(
1606
1797
  () => products.filter((p) => {
1607
1798
  if (catalog.priceMin != null && p.price < catalog.priceMin) return false;
1608
1799
  if (catalog.priceMax != null && p.price > catalog.priceMax) return false;
@@ -1667,10 +1858,17 @@ function HeroSection({
1667
1858
  slides,
1668
1859
  height = "tall"
1669
1860
  }) {
1670
- const [index, setIndex] = React18.useState(0);
1671
- const interacted = React18.useRef(false);
1861
+ const [index, setIndex] = React6.useState(0);
1862
+ const [loadedSlides, setLoadedSlides] = React6.useState({});
1863
+ const config = useStorefrontConfig();
1864
+ const interacted = React6.useRef(false);
1672
1865
  const hClass = height === "tall" ? "h-[72vh] min-h-[440px]" : "h-[48vh] min-h-[340px]";
1673
- React18.useEffect(() => {
1866
+ const revealImages = config.imageLoading.mode !== "none";
1867
+ const revealStyle = (loaded) => revealImages ? {
1868
+ filter: config.imageLoading.blur && !loaded ? "blur(10px)" : "blur(0px)",
1869
+ transition: `opacity 700ms ease, filter ${config.imageLoading.durationMs}ms ${config.imageLoading.easing}`
1870
+ } : void 0;
1871
+ React6.useEffect(() => {
1674
1872
  if (variant !== "slider" || slides.length < 2 || prefersReducedMotion()) return;
1675
1873
  const timer = setInterval(() => {
1676
1874
  if (!interacted.current) setIndex((i) => (i + 1) % slides.length);
@@ -1690,7 +1888,7 @@ function HeroSection({
1690
1888
  className: "group relative block h-[42vh] min-h-[320px] overflow-hidden",
1691
1889
  children: [
1692
1890
  /* @__PURE__ */ jsxRuntime.jsx(
1693
- "img",
1891
+ SmoothImage,
1694
1892
  {
1695
1893
  src: slideImage(slide, 900, 700),
1696
1894
  alt: slide.title,
@@ -1722,7 +1920,11 @@ function HeroSection({
1722
1920
  src: slideImage(slide),
1723
1921
  alt: slide.title,
1724
1922
  "aria-hidden": i !== index,
1725
- className: `absolute inset-0 h-full w-full object-cover transition-opacity duration-700 ${i === index ? "animate-kenburns opacity-100" : "opacity-0"}`
1923
+ loading: i === 0 ? "eager" : "lazy",
1924
+ decoding: "async",
1925
+ onLoad: () => setLoadedSlides((current) => ({ ...current, [i]: true })),
1926
+ style: revealStyle(Boolean(loadedSlides[i])),
1927
+ className: `absolute inset-0 h-full w-full object-cover transition-opacity duration-700 ${i === index && (!revealImages || loadedSlides[i]) ? "animate-kenburns opacity-100" : "opacity-0"}`
1726
1928
  },
1727
1929
  i
1728
1930
  )),
@@ -1784,7 +1986,7 @@ function CategoryShowcase({ style, title }) {
1784
1986
  title && /* @__PURE__ */ jsxRuntime.jsx(Reveal, { children: /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading mb-8 text-center text-3xl font-bold", children: title }) }),
1785
1987
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-start justify-center gap-8", children: categories.map((c, i) => /* @__PURE__ */ jsxRuntime.jsx(Reveal, { delay: i * 80, children: /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", onClick: () => go(c.id), className: "group flex w-28 flex-col items-center gap-3", children: [
1786
1988
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block h-24 w-24 overflow-hidden rounded-full border-2 border-transparent shadow-sm transition-all duration-300 group-hover:scale-105 group-hover:border-primary group-hover:shadow-md", children: /* @__PURE__ */ jsxRuntime.jsx(
1787
- "img",
1989
+ SmoothImage,
1788
1990
  {
1789
1991
  src: c.imageUrl ?? bannerPlaceholder(c.name, 30 + i * 70, 70 + i * 70, 200, 200),
1790
1992
  alt: c.name,
@@ -1799,7 +2001,7 @@ function CategoryShowcase({ style, title }) {
1799
2001
  title && /* @__PURE__ */ jsxRuntime.jsx(Reveal, { children: /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading mb-8 text-center text-3xl font-bold", children: title }) }),
1800
2002
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-4 lg:grid-cols-4", children: categories.map((c, i) => /* @__PURE__ */ jsxRuntime.jsx(Reveal, { delay: i * 90, children: /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", onClick: () => go(c.id), className: "group relative block aspect-[4/5] w-full overflow-hidden", style: { borderRadius: "var(--sf-radius-card)" }, children: [
1801
2003
  /* @__PURE__ */ jsxRuntime.jsx(
1802
- "img",
2004
+ SmoothImage,
1803
2005
  {
1804
2006
  src: c.imageUrl ?? bannerPlaceholder(c.name, 30 + i * 70, 70 + i * 70, 480, 600),
1805
2007
  alt: c.name,
@@ -1821,11 +2023,13 @@ function ProductRail({
1821
2023
  limit = 4
1822
2024
  }) {
1823
2025
  const config = useStorefrontConfig();
1824
- const ProductCardComponent = config.slots?.ProductCard ?? ProductCard;
2026
+ const actions = useStorefrontActions();
2027
+ const components = getStorefrontComponents(config);
2028
+ const ProductCardComponent = components.ProductCard ?? ProductCard;
1825
2029
  const { products, loading } = useProducts(
1826
2030
  filter === "new" ? { status: "active", orderBy: "created_at", order: "desc" } : { status: "active" }
1827
2031
  );
1828
- const visible = React18.useMemo(() => {
2032
+ const visible = React6.useMemo(() => {
1829
2033
  let list = products;
1830
2034
  if (filter === "sale") list = list.filter((p) => p.compareAtPrice != null && p.compareAtPrice > p.price);
1831
2035
  return list.slice(0, limit);
@@ -1837,7 +2041,15 @@ function ProductRail({
1837
2041
  /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading text-3xl font-bold tracking-tight", children: title }),
1838
2042
  subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-muted-foreground", children: subtitle })
1839
2043
  ] }),
1840
- loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-5 md:grid-cols-4", children: Array.from({ length: limit }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[3/4] animate-pulse bg-muted", style: { borderRadius: "var(--sf-radius-card)" } }, i)) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-5 md:grid-cols-4", children: visible.map((p, i) => /* @__PURE__ */ jsxRuntime.jsx(Reveal, { delay: i * 90, children: /* @__PURE__ */ jsxRuntime.jsx(ProductCardComponent, { product: p }) }, p.id)) }),
2044
+ loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-5 md:grid-cols-4", children: Array.from({ length: limit }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-[3/4] animate-pulse bg-muted", style: { borderRadius: "var(--sf-radius-card)" } }, i)) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-5 md:grid-cols-4", children: visible.map((p, i) => /* @__PURE__ */ jsxRuntime.jsx(Reveal, { delay: i * 90, children: /* @__PURE__ */ jsxRuntime.jsx(
2045
+ ProductCardComponent,
2046
+ {
2047
+ product: p,
2048
+ config,
2049
+ commerceMode: config.commerceMode,
2050
+ actions
2051
+ }
2052
+ ) }, p.id)) }),
1841
2053
  /* @__PURE__ */ jsxRuntime.jsx(Reveal, { className: "mt-10 text-center", delay: 200, children: /* @__PURE__ */ jsxRuntime.jsx(
1842
2054
  Link,
1843
2055
  {
@@ -1849,6 +2061,201 @@ function ProductRail({
1849
2061
  ) })
1850
2062
  ] });
1851
2063
  }
2064
+ var heightClass = {
2065
+ medium: "h-[58vh] min-h-[420px]",
2066
+ tall: "h-[74vh] min-h-[520px]",
2067
+ screen: "min-h-[calc(100vh-64px)]"
2068
+ };
2069
+ var alignClass = {
2070
+ start: "items-start text-left",
2071
+ center: "items-center text-center",
2072
+ end: "items-end text-right"
2073
+ };
2074
+ function MediaCarousel({
2075
+ items,
2076
+ height = "tall",
2077
+ autoplayMs = 7e3,
2078
+ overlay = "dark",
2079
+ className = ""
2080
+ }) {
2081
+ const [index, setIndex] = React6.useState(0);
2082
+ const [loadedItems, setLoadedItems] = React6.useState({});
2083
+ const config = useStorefrontConfig();
2084
+ const interacted = React6.useRef(false);
2085
+ const revealImages = config.imageLoading.mode !== "none";
2086
+ const revealStyle = (loaded) => revealImages ? {
2087
+ filter: config.imageLoading.blur && !loaded ? "blur(10px)" : "blur(0px)",
2088
+ transition: `opacity 700ms ease, filter ${config.imageLoading.durationMs}ms ${config.imageLoading.easing}`
2089
+ } : void 0;
2090
+ React6.useEffect(() => {
2091
+ if (items.length < 2 || autoplayMs <= 0 || prefersReducedMotion()) return;
2092
+ const timer = setInterval(() => {
2093
+ if (!interacted.current) setIndex((i) => (i + 1) % items.length);
2094
+ }, autoplayMs);
2095
+ return () => clearInterval(timer);
2096
+ }, [autoplayMs, items.length]);
2097
+ if (items.length === 0) return null;
2098
+ const active = items[Math.min(index, items.length - 1)];
2099
+ const goTo = (next) => {
2100
+ interacted.current = true;
2101
+ setIndex((next + items.length) % items.length);
2102
+ };
2103
+ const overlayClass = overlay === "dark" ? "bg-gradient-to-r from-black/70 via-black/25 to-black/25" : overlay === "soft" ? "bg-gradient-to-t from-black/45 via-black/10 to-transparent" : "";
2104
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { "data-testid": TID.mediaCarousel, className: `relative isolate overflow-hidden ${heightClass[height]} ${className}`, children: [
2105
+ items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(
2106
+ "img",
2107
+ {
2108
+ src: item.image,
2109
+ alt: item.imageAlt ?? item.title,
2110
+ "aria-hidden": i !== index,
2111
+ loading: i === 0 ? "eager" : "lazy",
2112
+ decoding: "async",
2113
+ onLoad: () => setLoadedItems((current) => ({ ...current, [i]: true })),
2114
+ style: revealStyle(Boolean(loadedItems[i])),
2115
+ className: `absolute inset-0 -z-10 h-full w-full object-cover transition-opacity duration-700 ${i === index && (!revealImages || loadedItems[i]) ? "opacity-100" : "opacity-0"}`
2116
+ },
2117
+ `${item.title}-${i}`
2118
+ )),
2119
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute inset-0 -z-10 ${overlayClass}` }),
2120
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex h-full px-5 py-16 sm:px-10 lg:px-16 ${alignClass[active.align ?? "start"]}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "my-auto max-w-2xl text-white drop-shadow-md", children: [
2121
+ active.eyebrow && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-4 text-xs font-semibold uppercase tracking-[0.24em] text-white/80", children: active.eyebrow }),
2122
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "sf-heading text-5xl font-normal leading-[0.95] sm:text-7xl lg:text-8xl", children: active.title }),
2123
+ active.subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-5 max-w-xl text-base leading-8 text-white/88 sm:text-lg", children: active.subtitle }),
2124
+ active.cta && /* @__PURE__ */ jsxRuntime.jsx(
2125
+ Link,
2126
+ {
2127
+ to: active.href ?? "/catalog",
2128
+ className: "mt-8 inline-flex border border-white/75 px-6 py-3 text-xs font-semibold uppercase tracking-[0.18em] text-white transition hover:bg-white hover:text-black",
2129
+ children: active.cta
2130
+ }
2131
+ )
2132
+ ] }) }),
2133
+ items.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2134
+ /* @__PURE__ */ jsxRuntime.jsx(
2135
+ "button",
2136
+ {
2137
+ type: "button",
2138
+ "aria-label": "Previous slide",
2139
+ "data-testid": TID.mediaCarouselPrev,
2140
+ onClick: () => goTo(index - 1),
2141
+ className: "absolute left-4 top-1/2 inline-flex h-10 w-10 -translate-y-1/2 items-center justify-center border border-white/60 bg-black/20 text-white backdrop-blur transition hover:bg-white hover:text-black",
2142
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.ChevronLeft, { className: "h-5 w-5" })
2143
+ }
2144
+ ),
2145
+ /* @__PURE__ */ jsxRuntime.jsx(
2146
+ "button",
2147
+ {
2148
+ type: "button",
2149
+ "aria-label": "Next slide",
2150
+ "data-testid": TID.mediaCarouselNext,
2151
+ onClick: () => goTo(index + 1),
2152
+ className: "absolute right-4 top-1/2 inline-flex h-10 w-10 -translate-y-1/2 items-center justify-center border border-white/60 bg-black/20 text-white backdrop-blur transition hover:bg-white hover:text-black",
2153
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.ChevronRight, { className: "h-5 w-5" })
2154
+ }
2155
+ ),
2156
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-5 left-1/2 flex -translate-x-1/2 gap-2", children: items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(
2157
+ "button",
2158
+ {
2159
+ type: "button",
2160
+ "aria-label": `Go to slide ${i + 1}`,
2161
+ onClick: () => goTo(i),
2162
+ className: `h-2 rounded-full transition-all ${i === index ? "w-8 bg-white" : "w-2 bg-white/55 hover:bg-white"}`
2163
+ },
2164
+ `${item.title}-dot-${i}`
2165
+ )) })
2166
+ ] })
2167
+ ] });
2168
+ }
2169
+ function ProductSlider({
2170
+ title,
2171
+ subtitle,
2172
+ eyebrow,
2173
+ filter = "all",
2174
+ categoryName,
2175
+ collection,
2176
+ limit = 10,
2177
+ cta = "View all",
2178
+ href,
2179
+ className = ""
2180
+ }) {
2181
+ const config = useStorefrontConfig();
2182
+ const actions = useStorefrontActions();
2183
+ const viewportRef = React6.useRef(null);
2184
+ const components = getStorefrontComponents(config);
2185
+ const ProductCardComponent = components.ProductCard ?? ProductCard;
2186
+ const { products, loading } = useProducts(
2187
+ filter === "new" ? { status: "active", orderBy: "created_at", order: "desc" } : { status: "active" }
2188
+ );
2189
+ const visible = React6.useMemo(() => {
2190
+ let list = products;
2191
+ if (filter === "sale") list = list.filter((p) => p.compareAtPrice != null && p.compareAtPrice > p.price);
2192
+ if (categoryName) list = list.filter((p) => p.categoryName === categoryName);
2193
+ if (collection) list = list.filter((p) => p.metadata?.collection === collection);
2194
+ return list.slice(0, limit);
2195
+ }, [categoryName, collection, filter, limit, products]);
2196
+ const scroll = (direction) => {
2197
+ viewportRef.current?.scrollBy({ left: direction * Math.max(320, viewportRef.current.clientWidth * 0.75), behavior: "smooth" });
2198
+ };
2199
+ if (!loading && visible.length === 0) return null;
2200
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { "data-testid": TID.productSlider, className: `mx-auto max-w-7xl px-4 py-14 sm:px-6 ${className}`, children: [
2201
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-8 flex flex-col gap-5 sm:flex-row sm:items-end sm:justify-between", children: [
2202
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-2xl", children: [
2203
+ eyebrow && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-[0.2em] text-primary", children: eyebrow }),
2204
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading text-3xl font-normal leading-tight tracking-normal sm:text-5xl", children: title }),
2205
+ subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-3 leading-7 text-muted-foreground", children: subtitle })
2206
+ ] }),
2207
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
2208
+ /* @__PURE__ */ jsxRuntime.jsx(
2209
+ "button",
2210
+ {
2211
+ type: "button",
2212
+ "aria-label": "Previous products",
2213
+ "data-testid": TID.productSliderPrev,
2214
+ onClick: () => scroll(-1),
2215
+ className: "inline-flex h-10 w-10 items-center justify-center border border-border bg-background transition hover:bg-foreground hover:text-background",
2216
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.ChevronLeft, { className: "h-5 w-5" })
2217
+ }
2218
+ ),
2219
+ /* @__PURE__ */ jsxRuntime.jsx(
2220
+ "button",
2221
+ {
2222
+ type: "button",
2223
+ "aria-label": "Next products",
2224
+ "data-testid": TID.productSliderNext,
2225
+ onClick: () => scroll(1),
2226
+ className: "inline-flex h-10 w-10 items-center justify-center border border-border bg-background transition hover:bg-foreground hover:text-background",
2227
+ children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.ChevronRight, { className: "h-5 w-5" })
2228
+ }
2229
+ )
2230
+ ] })
2231
+ ] }),
2232
+ /* @__PURE__ */ jsxRuntime.jsx(
2233
+ "div",
2234
+ {
2235
+ ref: viewportRef,
2236
+ "data-testid": TID.productSliderViewport,
2237
+ className: "flex snap-x gap-5 overflow-x-auto pb-5 [scrollbar-width:thin]",
2238
+ children: loading ? Array.from({ length: Math.min(limit, 6) }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[360px] min-w-[260px] snap-start animate-pulse bg-muted" }, i)) : visible.map((product) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-[260px] max-w-[300px] flex-[0_0_72vw] snap-start sm:flex-[0_0_300px]", children: /* @__PURE__ */ jsxRuntime.jsx(
2239
+ ProductCardComponent,
2240
+ {
2241
+ product,
2242
+ config,
2243
+ commerceMode: config.commerceMode,
2244
+ actions
2245
+ }
2246
+ ) }, product.id))
2247
+ }
2248
+ ),
2249
+ cta && /* @__PURE__ */ jsxRuntime.jsx(
2250
+ Link,
2251
+ {
2252
+ to: href ?? config.catalogPath,
2253
+ className: "mt-4 inline-flex border border-foreground/30 px-6 py-3 text-xs font-semibold uppercase tracking-[0.16em] transition hover:bg-foreground hover:text-background",
2254
+ children: cta
2255
+ }
2256
+ )
2257
+ ] });
2258
+ }
1852
2259
  function Icon({ name, className }) {
1853
2260
  const C = LucideIcons__namespace[name];
1854
2261
  return C ? /* @__PURE__ */ jsxRuntime.jsx(C, { className }) : null;
@@ -1873,7 +2280,7 @@ function PromoBanner({
1873
2280
  }) {
1874
2281
  return /* @__PURE__ */ jsxRuntime.jsxs("section", { "data-testid": TID.promoBanner, className: "group relative my-12 overflow-hidden", children: [
1875
2282
  /* @__PURE__ */ jsxRuntime.jsx(
1876
- "img",
2283
+ SmoothImage,
1877
2284
  {
1878
2285
  src: image ?? bannerPlaceholder(title, hue, hue + 50, 1600, 420),
1879
2286
  alt: title,
@@ -1927,8 +2334,8 @@ function Testimonials({ title, items }) {
1927
2334
  ] }) });
1928
2335
  }
1929
2336
  function NewsletterBand({ title, subtitle }) {
1930
- const [email, setEmail] = React18.useState("");
1931
- const [done, setDone] = React18.useState(false);
2337
+ const [email, setEmail] = React6.useState("");
2338
+ const [done, setDone] = React6.useState(false);
1932
2339
  return /* @__PURE__ */ jsxRuntime.jsx("section", { "data-testid": TID.newsletter, className: "bg-primary py-16 text-primary-foreground", children: /* @__PURE__ */ jsxRuntime.jsxs(Reveal, { className: "mx-auto max-w-xl px-4 text-center sm:px-6", children: [
1933
2340
  /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading text-3xl font-bold", children: title ?? "Receba nossas novidades" }),
1934
2341
  subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 opacity-90", children: subtitle }),
@@ -1976,8 +2383,10 @@ function registerStorefrontBlocks() {
1976
2383
  if (registered) return;
1977
2384
  registered = true;
1978
2385
  core.registerBlock("hero", HeroSection, { source: "sdk", label: "Hero" });
2386
+ core.registerBlock("mediaCarousel", MediaCarousel, { source: "sdk", label: "Media carousel" });
1979
2387
  core.registerBlock("categories", CategoryShowcase, { source: "sdk", label: "Categories" });
1980
2388
  core.registerBlock("products", ProductRail, { source: "sdk", label: "Product rail" });
2389
+ core.registerBlock("productSlider", ProductSlider, { source: "sdk", label: "Product slider" });
1981
2390
  core.registerBlock("benefits", BenefitsRow, { source: "sdk", label: "Benefits row" });
1982
2391
  core.registerBlock("banner", PromoBanner, { source: "sdk", label: "Promo banner" });
1983
2392
  core.registerBlock("manifesto", ManifestoBlock, { source: "sdk", label: "Manifesto" });
@@ -1991,14 +2400,17 @@ function sectionsToBlocks(sections) {
1991
2400
  return { type, id: `${type}-${i}`, props };
1992
2401
  });
1993
2402
  }
1994
- function HomePage({ sections }) {
2403
+ function StorefrontSections({ sections }) {
1995
2404
  return /* @__PURE__ */ jsxRuntime.jsx("main", { children: core.renderBlocks(sectionsToBlocks(sections)) });
1996
2405
  }
2406
+ function HomePage({ sections }) {
2407
+ return /* @__PURE__ */ jsxRuntime.jsx(StorefrontSections, { sections });
2408
+ }
1997
2409
  function useProduct(slug2) {
1998
- const [product, setProduct] = React18.useState(null);
1999
- const [loading, setLoading] = React18.useState(true);
2000
- const [error, setError] = React18.useState(null);
2001
- React18.useEffect(() => {
2410
+ const [product, setProduct] = React6.useState(null);
2411
+ const [loading, setLoading] = React6.useState(true);
2412
+ const [error, setError] = React6.useState(null);
2413
+ React6.useEffect(() => {
2002
2414
  let cancelled = false;
2003
2415
  setLoading(true);
2004
2416
  runtime.getShopProvider().listProducts({ slug: slug2, status: "active", limit: 1 }).then((data) => {
@@ -2105,28 +2517,218 @@ function ProductSpecs({ product }) {
2105
2517
  }
2106
2518
  function RelatedProducts({ product }) {
2107
2519
  const config = useStorefrontConfig();
2108
- const Card = config.slots?.ProductCard ?? ProductCard;
2520
+ const actions = useStorefrontActions();
2521
+ const components = getStorefrontComponents(config);
2522
+ const Card = components.ProductCard ?? ProductCard;
2109
2523
  const opts = product.categoryId ? { categoryId: product.categoryId, status: "active", limit: 5 } : { status: "active", limit: 5 };
2110
2524
  const { products, loading } = useProducts(opts);
2111
2525
  const related = products.filter((p) => p.id !== product.id).slice(0, 4);
2112
2526
  if (loading || related.length === 0) return null;
2113
2527
  return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "mt-16", children: [
2114
2528
  /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading text-xl font-semibold tracking-tight", children: "Voc\xEA tamb\xE9m pode gostar" }),
2115
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-6 grid grid-cols-2 gap-5 md:grid-cols-4", children: related.map((p) => /* @__PURE__ */ jsxRuntime.jsx(Card, { product: p }, p.id)) })
2529
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-6 grid grid-cols-2 gap-5 md:grid-cols-4", children: related.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
2530
+ Card,
2531
+ {
2532
+ product: p,
2533
+ config,
2534
+ commerceMode: config.commerceMode,
2535
+ actions
2536
+ },
2537
+ p.id
2538
+ )) })
2116
2539
  ] });
2117
2540
  }
2541
+ function defaultSubject(prefix, product) {
2542
+ return `${prefix}: ${product.name}`;
2543
+ }
2544
+ function buildWhatsAppHref(url, product) {
2545
+ const text = encodeURIComponent(`Hello, I would like more information about ${product.name}.`);
2546
+ const sep = url.includes("?") ? "&" : "?";
2547
+ return `${url}${sep}text=${text}`;
2548
+ }
2549
+ function ProductEnquiryForm({ product, onSuccess }) {
2550
+ const config = useStorefrontConfig();
2551
+ const [customerName, setCustomerName] = React6.useState("");
2552
+ const [customerEmail, setCustomerEmail] = React6.useState("");
2553
+ const [customerPhone, setCustomerPhone] = React6.useState("");
2554
+ const [subject, setSubject] = React6.useState(defaultSubject(config.enquiry.subjectPrefix, product));
2555
+ const [message, setMessage] = React6.useState(`I would like more information about ${product.name}.`);
2556
+ const [status, setStatus] = React6.useState("idle");
2557
+ const [error, setError] = React6.useState(null);
2558
+ const submit = async (event) => {
2559
+ event.preventDefault();
2560
+ setStatus("submitting");
2561
+ setError(null);
2562
+ try {
2563
+ const provider = runtime.getShopProvider();
2564
+ if (provider.createProductEnquiry) {
2565
+ await provider.createProductEnquiry({
2566
+ productId: product.id,
2567
+ productName: product.name,
2568
+ productSlug: product.slug,
2569
+ customerName,
2570
+ customerEmail,
2571
+ customerPhone: customerPhone || void 0,
2572
+ subject,
2573
+ message,
2574
+ sourceUrl: typeof window === "undefined" ? void 0 : window.location.href,
2575
+ metadata: { sku: product.sku, categoryName: product.categoryName }
2576
+ });
2577
+ } else if (config.enquiry.email) {
2578
+ window.location.href = `mailto:${config.enquiry.email}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(message)}`;
2579
+ }
2580
+ setStatus("success");
2581
+ onSuccess?.();
2582
+ } catch (e) {
2583
+ setStatus("error");
2584
+ setError(e instanceof Error ? e.message : String(e));
2585
+ }
2586
+ };
2587
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2588
+ "form",
2589
+ {
2590
+ ...storefrontComponentContracts.productDetail.enquiryForm,
2591
+ onSubmit: submit,
2592
+ className: "space-y-4",
2593
+ children: [
2594
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [
2595
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "space-y-1.5 text-sm font-medium", children: [
2596
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Your name" }),
2597
+ /* @__PURE__ */ jsxRuntime.jsx(
2598
+ "input",
2599
+ {
2600
+ "data-testid": TID.enquiryName,
2601
+ required: true,
2602
+ value: customerName,
2603
+ onChange: (e) => setCustomerName(e.target.value),
2604
+ className: "w-full border bg-background px-3 py-2 outline-none focus:border-primary",
2605
+ style: { borderRadius: "var(--sf-radius-input)" }
2606
+ }
2607
+ )
2608
+ ] }),
2609
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "space-y-1.5 text-sm font-medium", children: [
2610
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Your email" }),
2611
+ /* @__PURE__ */ jsxRuntime.jsx(
2612
+ "input",
2613
+ {
2614
+ "data-testid": TID.enquiryEmail,
2615
+ required: true,
2616
+ type: "email",
2617
+ value: customerEmail,
2618
+ onChange: (e) => setCustomerEmail(e.target.value),
2619
+ className: "w-full border bg-background px-3 py-2 outline-none focus:border-primary",
2620
+ style: { borderRadius: "var(--sf-radius-input)" }
2621
+ }
2622
+ )
2623
+ ] })
2624
+ ] }),
2625
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block space-y-1.5 text-sm font-medium", children: [
2626
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2627
+ "Phone",
2628
+ config.enquiry.requirePhone ? "" : " (optional)"
2629
+ ] }),
2630
+ /* @__PURE__ */ jsxRuntime.jsx(
2631
+ "input",
2632
+ {
2633
+ "data-testid": TID.enquiryPhone,
2634
+ required: config.enquiry.requirePhone,
2635
+ value: customerPhone,
2636
+ onChange: (e) => setCustomerPhone(e.target.value),
2637
+ className: "w-full border bg-background px-3 py-2 outline-none focus:border-primary",
2638
+ style: { borderRadius: "var(--sf-radius-input)" }
2639
+ }
2640
+ )
2641
+ ] }),
2642
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block space-y-1.5 text-sm font-medium", children: [
2643
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Subject" }),
2644
+ /* @__PURE__ */ jsxRuntime.jsx(
2645
+ "input",
2646
+ {
2647
+ "data-testid": TID.enquirySubject,
2648
+ required: true,
2649
+ value: subject,
2650
+ onChange: (e) => setSubject(e.target.value),
2651
+ className: "w-full border bg-background px-3 py-2 outline-none focus:border-primary",
2652
+ style: { borderRadius: "var(--sf-radius-input)" }
2653
+ }
2654
+ )
2655
+ ] }),
2656
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block space-y-1.5 text-sm font-medium", children: [
2657
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Message" }),
2658
+ /* @__PURE__ */ jsxRuntime.jsx(
2659
+ "textarea",
2660
+ {
2661
+ "data-testid": TID.enquiryMessage,
2662
+ required: true,
2663
+ rows: 5,
2664
+ value: message,
2665
+ onChange: (e) => setMessage(e.target.value),
2666
+ className: "w-full resize-none border bg-background px-3 py-2 outline-none focus:border-primary",
2667
+ style: { borderRadius: "var(--sf-radius-input)" }
2668
+ }
2669
+ )
2670
+ ] }),
2671
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
2672
+ /* @__PURE__ */ jsxRuntime.jsxs(
2673
+ "button",
2674
+ {
2675
+ "data-testid": TID.enquirySubmit,
2676
+ type: "submit",
2677
+ disabled: status === "submitting",
2678
+ className: "sf-cta inline-flex items-center gap-2 bg-primary px-5 py-3 font-semibold text-primary-foreground disabled:cursor-wait disabled:opacity-60",
2679
+ style: { borderRadius: "var(--sf-radius-button)" },
2680
+ children: [
2681
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Send, { className: "h-4 w-4" }),
2682
+ status === "submitting" ? "Sending..." : "Send enquiry"
2683
+ ]
2684
+ }
2685
+ ),
2686
+ config.enquiry.whatsappUrl && /* @__PURE__ */ jsxRuntime.jsxs(
2687
+ "a",
2688
+ {
2689
+ href: buildWhatsAppHref(config.enquiry.whatsappUrl, product),
2690
+ target: "_blank",
2691
+ rel: "noreferrer",
2692
+ className: "inline-flex items-center gap-2 border px-5 py-3 text-sm font-semibold transition-colors hover:bg-muted",
2693
+ style: { borderRadius: "var(--sf-radius-button)" },
2694
+ children: [
2695
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.MessageCircle, { className: "h-4 w-4" }),
2696
+ "WhatsApp"
2697
+ ]
2698
+ }
2699
+ ),
2700
+ config.enquiry.email && /* @__PURE__ */ jsxRuntime.jsxs(
2701
+ "a",
2702
+ {
2703
+ href: `mailto:${config.enquiry.email}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(message)}`,
2704
+ className: "inline-flex items-center gap-2 border px-5 py-3 text-sm font-semibold transition-colors hover:bg-muted",
2705
+ style: { borderRadius: "var(--sf-radius-button)" },
2706
+ children: [
2707
+ /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Mail, { className: "h-4 w-4" }),
2708
+ "Email"
2709
+ ]
2710
+ }
2711
+ )
2712
+ ] }),
2713
+ status === "success" && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-testid": TID.enquirySuccess, className: "text-sm font-medium text-emerald-600", children: config.enquiry.successMessage }),
2714
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-testid": TID.enquiryError, className: "text-sm font-medium text-red-600", children: error })
2715
+ ]
2716
+ }
2717
+ );
2718
+ }
2118
2719
  function ProductDetailPage({ slug: slug2 }) {
2119
2720
  const config = useStorefrontConfig();
2721
+ const actions = useStorefrontActions();
2120
2722
  const { product, loading } = useProduct(slug2);
2121
2723
  const addItem = useCartStore((s) => s.addItem);
2122
- const [qty, setQty] = React18.useState(1);
2123
- const [selectedOptions, setSelectedOptions] = React18.useState({});
2724
+ const [qty, setQty] = React6.useState(1);
2725
+ const [selectedOptions, setSelectedOptions] = React6.useState({});
2124
2726
  const optionGroups = product ? getProductOptionGroups(product) : [];
2125
2727
  useStorefrontHead({
2126
2728
  title: product ? `${product.name} \u2014 ${config.name}` : config.name,
2127
2729
  description: product?.description
2128
2730
  });
2129
- React18.useEffect(() => {
2731
+ React6.useEffect(() => {
2130
2732
  if (!product) return;
2131
2733
  const defaults = Object.fromEntries(
2132
2734
  getProductOptionGroups(product).map((group) => [group.label, group.values[0] ?? ""])
@@ -2152,21 +2754,56 @@ function ProductDetailPage({ slug: slug2 }) {
2152
2754
  const soldOut = product.inventoryCount <= 0;
2153
2755
  const lowStock = !soldOut && product.inventoryCount <= 5;
2154
2756
  const image = product.images.find((i) => i.isPrimary) ?? product.images[0];
2757
+ const components = getStorefrontComponents(config);
2758
+ const ProductDetailComponent = components.ProductDetail;
2759
+ const addToCart = () => addItem(product, qty, selectedOptions);
2760
+ const openEnquiry = () => {
2761
+ document.getElementById("storefront-product-enquiry")?.scrollIntoView({ behavior: "smooth", block: "start" });
2762
+ };
2155
2763
  const discountPct = product.compareAtPrice && product.compareAtPrice > product.price ? Math.round((1 - product.price / product.compareAtPrice) * 100) : 0;
2156
- return /* @__PURE__ */ jsxRuntime.jsxs("main", { className: "mx-auto max-w-5xl px-4 py-8 sm:px-6", children: [
2764
+ if (ProductDetailComponent) {
2765
+ return /* @__PURE__ */ jsxRuntime.jsx(
2766
+ ProductDetailComponent,
2767
+ {
2768
+ product,
2769
+ config,
2770
+ commerceMode: config.commerceMode,
2771
+ primaryImage: image,
2772
+ optionGroups,
2773
+ selectedOptions,
2774
+ setSelectedOptions,
2775
+ quantity: qty,
2776
+ setQuantity: setQty,
2777
+ soldOut,
2778
+ lowStock,
2779
+ actions,
2780
+ addToCart,
2781
+ openEnquiry
2782
+ }
2783
+ );
2784
+ }
2785
+ return /* @__PURE__ */ jsxRuntime.jsxs("main", { ...storefrontComponentContracts.productDetail.root, className: "mx-auto max-w-5xl px-4 py-8 sm:px-6", children: [
2157
2786
  /* @__PURE__ */ jsxRuntime.jsxs(Link, { to: "/", className: "mb-6 inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground", children: [
2158
2787
  /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.ChevronLeft, { className: "h-4 w-4" }),
2159
2788
  " Continuar comprando"
2160
2789
  ] }),
2161
2790
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid animate-fade-up gap-10 md:grid-cols-2", children: [
2162
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "group overflow-hidden border bg-muted", style: { borderRadius: "var(--sf-radius-card)" }, children: image && /* @__PURE__ */ jsxRuntime.jsx(
2163
- "img",
2791
+ /* @__PURE__ */ jsxRuntime.jsx(
2792
+ "div",
2164
2793
  {
2165
- src: image.url,
2166
- alt: image.altText ?? product.name,
2167
- className: "aspect-square w-full object-cover transition-transform duration-700 ease-out group-hover:scale-105"
2794
+ ...storefrontComponentContracts.productDetail.gallery,
2795
+ className: "group overflow-hidden border bg-muted",
2796
+ style: { borderRadius: "var(--sf-radius-card)" },
2797
+ children: image && /* @__PURE__ */ jsxRuntime.jsx(
2798
+ SmoothImage,
2799
+ {
2800
+ src: image.url,
2801
+ alt: image.altText ?? product.name,
2802
+ className: "aspect-square w-full object-cover transition-transform duration-700 ease-out group-hover:scale-105"
2803
+ }
2804
+ )
2168
2805
  }
2169
- ) }),
2806
+ ),
2170
2807
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col py-2 lg:sticky lg:top-24 lg:self-start", children: [
2171
2808
  /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "mb-3 flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground", children: [
2172
2809
  /* @__PURE__ */ jsxRuntime.jsx(Link, { to: "/", className: "transition-colors hover:text-foreground", children: "In\xEDcio" }),
@@ -2178,7 +2815,7 @@ function ProductDetailPage({ slug: slug2 }) {
2178
2815
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground", children: product.name })
2179
2816
  ] }),
2180
2817
  /* @__PURE__ */ jsxRuntime.jsx("h1", { "data-testid": TID.pdpName, className: "sf-heading text-3xl font-bold tracking-tight", children: product.name }),
2181
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 flex items-center gap-3", children: [
2818
+ config.commerceMode === "checkout" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 flex items-center gap-3", children: [
2182
2819
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xl", children: /* @__PURE__ */ jsxRuntime.jsx(
2183
2820
  Price,
2184
2821
  {
@@ -2195,7 +2832,7 @@ function ProductDetailPage({ slug: slug2 }) {
2195
2832
  ] })
2196
2833
  ] }),
2197
2834
  /* @__PURE__ */ jsxRuntime.jsx("p", { "data-testid": TID.pdpDescription, className: "mt-5 leading-relaxed text-muted-foreground", children: product.description }),
2198
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 flex flex-wrap items-center gap-x-3 gap-y-1 text-sm", children: [
2835
+ config.commerceMode === "checkout" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 flex flex-wrap items-center gap-x-3 gap-y-1 text-sm", children: [
2199
2836
  soldOut ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-2 font-medium text-muted-foreground", children: [
2200
2837
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-2 w-2 rounded-full bg-muted-foreground" }),
2201
2838
  " Sem estoque"
@@ -2221,7 +2858,7 @@ function ProductDetailPage({ slug: slug2 }) {
2221
2858
  onChange: setSelectedOptions
2222
2859
  }
2223
2860
  ),
2224
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 flex items-center gap-4", children: [
2861
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ...storefrontComponentContracts.productDetail.actions, className: "mt-8", children: config.commerceMode === "checkout" ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
2225
2862
  /* @__PURE__ */ jsxRuntime.jsx(
2226
2863
  QuantityInput,
2227
2864
  {
@@ -2237,14 +2874,32 @@ function ProductDetailPage({ slug: slug2 }) {
2237
2874
  type: "button",
2238
2875
  "data-testid": TID.pdpAddToCart,
2239
2876
  disabled: soldOut,
2240
- onClick: () => addItem(product, qty, selectedOptions),
2877
+ onClick: addToCart,
2241
2878
  className: "sf-cta flex-1 bg-primary py-3.5 font-semibold text-primary-foreground shadow-md transition-all hover:-translate-y-0.5 hover:shadow-lg disabled:cursor-not-allowed disabled:opacity-40 disabled:shadow-none disabled:hover:translate-y-0",
2242
2879
  style: { borderRadius: "var(--sf-radius-button)" },
2243
2880
  children: soldOut ? "Esgotado" : "Adicionar ao carrinho"
2244
2881
  }
2245
2882
  )
2246
- ] }),
2247
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 grid grid-cols-3 gap-3 border-t pt-6 text-center", children: [
2883
+ ] }) : config.commerceMode === "enquiry" ? /* @__PURE__ */ jsxRuntime.jsx(
2884
+ "button",
2885
+ {
2886
+ type: "button",
2887
+ ...storefrontComponentContracts.productDetail.enquiryButton,
2888
+ onClick: openEnquiry,
2889
+ className: "sf-cta w-full bg-primary py-3.5 font-semibold text-primary-foreground shadow-md transition-all hover:-translate-y-0.5 hover:shadow-lg",
2890
+ style: { borderRadius: "var(--sf-radius-button)" },
2891
+ children: config.enquiry.label
2892
+ }
2893
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
2894
+ Link,
2895
+ {
2896
+ to: config.catalogPath,
2897
+ className: "inline-flex border px-5 py-3 text-sm font-semibold transition-colors hover:bg-muted",
2898
+ style: { borderRadius: "var(--sf-radius-button)" },
2899
+ children: "Voltar ao cat\xE1logo"
2900
+ }
2901
+ ) }),
2902
+ config.commerceMode === "checkout" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 grid grid-cols-3 gap-3 border-t pt-6 text-center", children: [
2248
2903
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-1.5 text-[11px] font-medium text-muted-foreground", children: [
2249
2904
  /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Truck, { className: "h-5 w-5 text-primary" }),
2250
2905
  " Envio r\xE1pido"
@@ -2260,6 +2915,11 @@ function ProductDetailPage({ slug: slug2 }) {
2260
2915
  ] })
2261
2916
  ] })
2262
2917
  ] }),
2918
+ config.commerceMode === "enquiry" && /* @__PURE__ */ jsxRuntime.jsx("section", { id: "storefront-product-enquiry", className: "mt-12 border-t pt-10", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto max-w-3xl", children: [
2919
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sf-heading text-2xl font-semibold", children: "Need more information?" }),
2920
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-muted-foreground", children: "Send an enquiry about this product." }),
2921
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-6", children: /* @__PURE__ */ jsxRuntime.jsx(ProductEnquiryForm, { product }) })
2922
+ ] }) }),
2263
2923
  /* @__PURE__ */ jsxRuntime.jsx(ProductSpecs, { product }),
2264
2924
  /* @__PURE__ */ jsxRuntime.jsx(RelatedProducts, { product })
2265
2925
  ] });
@@ -2305,11 +2965,11 @@ async function placeStorefrontOrder({
2305
2965
  return { order, customerId: customerId ?? order.customerId ?? "" };
2306
2966
  }
2307
2967
  function SignInModal({ defaultEmail = "", onClose, onSignedIn }) {
2308
- const [email, setEmail] = React18.useState(defaultEmail);
2309
- const [password, setPassword] = React18.useState("");
2310
- const [busy, setBusy] = React18.useState(false);
2311
- const [error, setError] = React18.useState(null);
2312
- React18.useEffect(() => {
2968
+ const [email, setEmail] = React6.useState(defaultEmail);
2969
+ const [password, setPassword] = React6.useState("");
2970
+ const [busy, setBusy] = React6.useState(false);
2971
+ const [error, setError] = React6.useState(null);
2972
+ React6.useEffect(() => {
2313
2973
  const onKey = (event) => {
2314
2974
  if (event.key === "Escape") onClose();
2315
2975
  };
@@ -2471,13 +3131,13 @@ function CheckoutPage() {
2471
3131
  const session = useSessionStore();
2472
3132
  const validateDiscount2 = useDiscountValidator();
2473
3133
  useStorefrontHead({ title: `Checkout \u2014 ${config.name}` });
2474
- const [selectedAddressId, setSelectedAddressId] = React18.useState(DEFAULT_ADDRESS.id);
2475
- const [selectedPaymentId, setSelectedPaymentId] = React18.useState(DEFAULT_PAYMENT.id);
2476
- const [savedAddresses, setSavedAddresses] = React18.useState(SAVED_ADDRESSES);
2477
- const [savedPaymentMethods, setSavedPaymentMethods] = React18.useState(SAVED_PAYMENT_METHODS);
2478
- const [addressMode, setAddressMode] = React18.useState("saved");
2479
- const [paymentMode, setPaymentMode] = React18.useState("saved");
2480
- const [form, setForm] = React18.useState({
3134
+ const [selectedAddressId, setSelectedAddressId] = React6.useState(DEFAULT_ADDRESS.id);
3135
+ const [selectedPaymentId, setSelectedPaymentId] = React6.useState(DEFAULT_PAYMENT.id);
3136
+ const [savedAddresses, setSavedAddresses] = React6.useState(SAVED_ADDRESSES);
3137
+ const [savedPaymentMethods, setSavedPaymentMethods] = React6.useState(SAVED_PAYMENT_METHODS);
3138
+ const [addressMode, setAddressMode] = React6.useState("saved");
3139
+ const [paymentMode, setPaymentMode] = React6.useState("saved");
3140
+ const [form, setForm] = React6.useState({
2481
3141
  email: session.email ?? "",
2482
3142
  name: session.name ?? "",
2483
3143
  street: DEFAULT_ADDRESS.street,
@@ -2487,20 +3147,20 @@ function CheckoutPage() {
2487
3147
  expiry: DEFAULT_PAYMENT.expiry,
2488
3148
  cvc: DEFAULT_PAYMENT.cvc
2489
3149
  });
2490
- const [error, setError] = React18.useState(null);
2491
- const [discountCode, setDiscountCode] = React18.useState("");
2492
- const [discountError, setDiscountError] = React18.useState(null);
2493
- const [discountSuccess, setDiscountSuccess] = React18.useState(null);
2494
- const [applyingDiscount, setApplyingDiscount] = React18.useState(false);
2495
- const [placing, setPlacing] = React18.useState(false);
2496
- const [processingStep, setProcessingStep] = React18.useState(0);
2497
- const [showSignin, setShowSignin] = React18.useState(false);
3150
+ const [error, setError] = React6.useState(null);
3151
+ const [discountCode, setDiscountCode] = React6.useState("");
3152
+ const [discountError, setDiscountError] = React6.useState(null);
3153
+ const [discountSuccess, setDiscountSuccess] = React6.useState(null);
3154
+ const [applyingDiscount, setApplyingDiscount] = React6.useState(false);
3155
+ const [placing, setPlacing] = React6.useState(false);
3156
+ const [processingStep, setProcessingStep] = React6.useState(0);
3157
+ const [showSignin, setShowSignin] = React6.useState(false);
2498
3158
  const subtotal = selectSubtotal(cart);
2499
3159
  const discountTotal = selectDiscountTotal(cart);
2500
3160
  const shipping = selectShipping(cart, config);
2501
3161
  const total = selectTotal(cart, config);
2502
3162
  const money = (value) => formatMoney(value, config.currency, config.locale);
2503
- React18.useEffect(() => {
3163
+ React6.useEffect(() => {
2504
3164
  if (cart.lines.length === 0 && !placing) navigateTo(config.catalogPath);
2505
3165
  }, [cart.lines.length, config.catalogPath, placing]);
2506
3166
  const set = (key) => (value) => setForm((current) => ({ ...current, [key]: value }));
@@ -2815,7 +3475,7 @@ function CheckoutPage() {
2815
3475
  /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "sr-only", children: "Resumo do pedido" }),
2816
3476
  /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-4", children: cart.lines.map((line) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex gap-3 text-sm", children: [
2817
3477
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative h-16 w-16 flex-none overflow-hidden rounded-lg border bg-background", children: [
2818
- line.imageUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: line.imageUrl, alt: line.name, className: "h-full w-full object-cover" }),
3478
+ line.imageUrl && /* @__PURE__ */ jsxRuntime.jsx(SmoothImage, { src: line.imageUrl, alt: line.name, className: "h-full w-full object-cover" }),
2819
3479
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -right-1 -top-1 flex h-5 min-w-5 items-center justify-center rounded-full bg-muted-foreground px-1.5 text-[10px] font-bold text-background", children: line.quantity })
2820
3480
  ] }),
2821
3481
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
@@ -3155,11 +3815,11 @@ function SuccessMark() {
3155
3815
  }
3156
3816
  function OrderConfirmationPage({ orderId }) {
3157
3817
  const config = useStorefrontConfig();
3158
- const [order, setOrder] = React18.useState(null);
3159
- const [loading, setLoading] = React18.useState(true);
3818
+ const [order, setOrder] = React6.useState(null);
3819
+ const [loading, setLoading] = React6.useState(true);
3160
3820
  const money = (v) => formatMoney(v, config.currency, config.locale);
3161
3821
  useStorefrontHead({ title: order ? `Pedido #${order.orderNumber} \u2014 ${config.name}` : config.name });
3162
- React18.useEffect(() => {
3822
+ React6.useEffect(() => {
3163
3823
  let cancelled = false;
3164
3824
  runtime.getShopProvider().getOrder(orderId).then((o) => {
3165
3825
  if (!cancelled) setOrder(o);
@@ -3237,7 +3897,7 @@ function OrderConfirmationPage({ orderId }) {
3237
3897
  /* @__PURE__ */ jsxRuntime.jsx(OrderTrackingTimeline, { order })
3238
3898
  ] }),
3239
3899
  /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-8 space-y-3 text-left", children: order.items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-center gap-3 text-sm", children: [
3240
- item.imageUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: item.imageUrl, alt: item.name, className: "h-12 w-12 rounded-lg border object-cover" }),
3900
+ item.imageUrl && /* @__PURE__ */ jsxRuntime.jsx(SmoothImage, { src: item.imageUrl, alt: item.name, className: "h-12 w-12 rounded-lg border object-cover" }),
3241
3901
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex-1", children: [
3242
3902
  item.name,
3243
3903
  " ",
@@ -3291,11 +3951,11 @@ function OrderConfirmationPage({ orderId }) {
3291
3951
  function useMyOrders() {
3292
3952
  const customerId = useSessionStore((s) => s.customerId);
3293
3953
  const email = useSessionStore((s) => s.email);
3294
- const [orders, setOrders] = React18.useState([]);
3295
- const [loading, setLoading] = React18.useState(true);
3296
- const [tick, setTick] = React18.useState(0);
3297
- const refresh = React18.useCallback(() => setTick((t) => t + 1), []);
3298
- React18.useEffect(() => {
3954
+ const [orders, setOrders] = React6.useState([]);
3955
+ const [loading, setLoading] = React6.useState(true);
3956
+ const [tick, setTick] = React6.useState(0);
3957
+ const refresh = React6.useCallback(() => setTick((t) => t + 1), []);
3958
+ React6.useEffect(() => {
3299
3959
  let cancelled = false;
3300
3960
  if (!customerId && !email) {
3301
3961
  setOrders([]);
@@ -3317,12 +3977,12 @@ function useMyOrders() {
3317
3977
  return { orders, loading, refresh };
3318
3978
  }
3319
3979
  function AuthForm() {
3320
- const [mode, setMode] = React18.useState("signin");
3321
- const [email, setEmail] = React18.useState("");
3322
- const [password, setPassword] = React18.useState("");
3323
- const [name, setName] = React18.useState("");
3324
- const [busy, setBusy] = React18.useState(false);
3325
- const [error, setError] = React18.useState(null);
3980
+ const [mode, setMode] = React6.useState("signin");
3981
+ const [email, setEmail] = React6.useState("");
3982
+ const [password, setPassword] = React6.useState("");
3983
+ const [name, setName] = React6.useState("");
3984
+ const [busy, setBusy] = React6.useState(false);
3985
+ const [error, setError] = React6.useState(null);
3326
3986
  async function submit(e) {
3327
3987
  e.preventDefault();
3328
3988
  setError(null);
@@ -3445,7 +4105,7 @@ function OrdersPanel() {
3445
4105
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: new Date(order.createdAt).toLocaleDateString(config.locale) }),
3446
4106
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsxRuntime.jsx(OrderTrackingTimeline, { order, compact: true }) }),
3447
4107
  /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-3 flex flex-wrap gap-2", children: order.items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-center gap-2 rounded-lg border px-2 py-1 text-xs", children: [
3448
- item.imageUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: item.imageUrl, alt: item.name, className: "h-6 w-6 rounded object-cover" }),
4108
+ item.imageUrl && /* @__PURE__ */ jsxRuntime.jsx(SmoothImage, { src: item.imageUrl, alt: item.name, className: "h-6 w-6 rounded object-cover" }),
3449
4109
  item.name,
3450
4110
  " \xD7 ",
3451
4111
  item.quantity
@@ -3464,9 +4124,9 @@ function OrdersPanel() {
3464
4124
  }
3465
4125
  function ProfilePanel() {
3466
4126
  const session = useSessionStore();
3467
- const [name, setName] = React18.useState(session.name ?? "");
3468
- const [saving, setSaving] = React18.useState(false);
3469
- const [saved, setSaved] = React18.useState(false);
4127
+ const [name, setName] = React6.useState(session.name ?? "");
4128
+ const [saving, setSaving] = React6.useState(false);
4129
+ const [saved, setSaved] = React6.useState(false);
3470
4130
  async function save(e) {
3471
4131
  e.preventDefault();
3472
4132
  const trimmed = name.trim();
@@ -3562,7 +4222,7 @@ var SECTION_SUBTITLE = {
3562
4222
  function MyPurchasesPage() {
3563
4223
  const config = useStorefrontConfig();
3564
4224
  const session = useSessionStore();
3565
- const [section, setSection] = React18.useState("orders");
4225
+ const [section, setSection] = React6.useState("orders");
3566
4226
  useStorefrontHead({ title: `Minha conta \u2014 ${config.name}` });
3567
4227
  if (!session.email) {
3568
4228
  return /* @__PURE__ */ jsxRuntime.jsx("main", { className: "mx-auto max-w-3xl px-4 py-12 sm:px-6", children: /* @__PURE__ */ jsxRuntime.jsx(AuthForm, {}) });
@@ -3625,7 +4285,7 @@ function PolicyPage({ kind }) {
3625
4285
  ] }) })
3626
4286
  ] });
3627
4287
  }
3628
- var StorefrontErrorBoundary = class extends React18__default.default.Component {
4288
+ var StorefrontErrorBoundary = class extends React6__default.default.Component {
3629
4289
  constructor() {
3630
4290
  super(...arguments);
3631
4291
  this.state = { error: null };
@@ -3669,7 +4329,7 @@ var ACCENT = {
3669
4329
  function ToastCard({ toast: toast2 }) {
3670
4330
  const dismiss = useToastStore((s) => s.dismiss);
3671
4331
  const Icon2 = ICON[toast2.variant];
3672
- React18.useEffect(() => {
4332
+ React6.useEffect(() => {
3673
4333
  const timer = window.setTimeout(() => dismiss(toast2.id), toast2.durationMs);
3674
4334
  return () => window.clearTimeout(timer);
3675
4335
  }, [toast2.id, toast2.durationMs, dismiss]);
@@ -3726,8 +4386,8 @@ function RouteSwitch({ path }) {
3726
4386
  if (product?.slug) return /* @__PURE__ */ jsxRuntime.jsx(ProductDetailPage, { slug: product.slug });
3727
4387
  const order = matchPath("/order/:id", path);
3728
4388
  if (order?.id) return /* @__PURE__ */ jsxRuntime.jsx(OrderConfirmationPage, { orderId: order.id });
3729
- if (matchPath("/checkout", path)) return /* @__PURE__ */ jsxRuntime.jsx(CheckoutPage, {});
3730
- if (matchPath("/account", path)) return /* @__PURE__ */ jsxRuntime.jsx(MyPurchasesPage, {});
4389
+ if (matchPath("/checkout", path) && config.features.checkout) return /* @__PURE__ */ jsxRuntime.jsx(CheckoutPage, {});
4390
+ if (matchPath("/account", path) && config.features.accounts) return /* @__PURE__ */ jsxRuntime.jsx(MyPurchasesPage, {});
3731
4391
  if (matchPath("/catalog", path)) return /* @__PURE__ */ jsxRuntime.jsx(CatalogPage, {});
3732
4392
  if (matchPath("/privacy", path)) return /* @__PURE__ */ jsxRuntime.jsx(PolicyPage, { kind: "privacy" });
3733
4393
  if (matchPath("/terms", path)) return /* @__PURE__ */ jsxRuntime.jsx(PolicyPage, { kind: "terms" });
@@ -3769,7 +4429,12 @@ function buildMockSeed(config) {
3769
4429
  };
3770
4430
  }
3771
4431
  function initStorefrontRuntime(config) {
3772
- initCustomerAuth(resolveAuthAdapter(config.auth?.adapter));
4432
+ if (resolveConfig(config).features.accounts) {
4433
+ initCustomerAuth(resolveAuthAdapter(config.auth, {
4434
+ supabaseUrl: config.supabaseUrl,
4435
+ supabaseAnonKey: config.supabaseAnonKey
4436
+ }));
4437
+ }
3773
4438
  if (config.provider) {
3774
4439
  runtime.setShopProvider(config.provider);
3775
4440
  } else {
@@ -3785,14 +4450,20 @@ function StorefrontShell() {
3785
4450
  const config = useStorefrontConfig();
3786
4451
  const path = useHashPath();
3787
4452
  const focused = isFocusedRoute(config, path);
3788
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-screen bg-background text-foreground", children: [
4453
+ const components = getStorefrontComponents(config);
4454
+ const chromeProps = { config, commerceMode: config.commerceMode };
4455
+ const body = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3789
4456
  config.theme && /* @__PURE__ */ jsxRuntime.jsx(StorefrontThemeStyle, { theme: config.theme }),
3790
- !focused && /* @__PURE__ */ jsxRuntime.jsx(StorefrontHeader, {}),
4457
+ !focused && (components.Header ? /* @__PURE__ */ jsxRuntime.jsx(components.Header, { ...chromeProps }) : /* @__PURE__ */ jsxRuntime.jsx(StorefrontHeader, {})),
3791
4458
  /* @__PURE__ */ jsxRuntime.jsx(StorefrontErrorBoundary, { children: /* @__PURE__ */ jsxRuntime.jsx(RouteSwitch, { path }) }, path),
3792
- !focused && /* @__PURE__ */ jsxRuntime.jsx(CartDrawer, {}),
3793
- !focused && /* @__PURE__ */ jsxRuntime.jsx(StorefrontFooter, {}),
4459
+ !focused && config.features.cart && /* @__PURE__ */ jsxRuntime.jsx(CartDrawer, {}),
4460
+ !focused && (components.Footer ? /* @__PURE__ */ jsxRuntime.jsx(components.Footer, { ...chromeProps }) : /* @__PURE__ */ jsxRuntime.jsx(StorefrontFooter, {})),
3794
4461
  /* @__PURE__ */ jsxRuntime.jsx(Toaster, {})
3795
4462
  ] });
4463
+ if (components.Shell) {
4464
+ return /* @__PURE__ */ jsxRuntime.jsx(components.Shell, { ...chromeProps, children: body });
4465
+ }
4466
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-screen bg-background text-foreground", children: body });
3796
4467
  }
3797
4468
  function createStorefrontApp(config) {
3798
4469
  const resolved = resolveConfig(config);
@@ -3803,6 +4474,37 @@ function createStorefrontApp(config) {
3803
4474
  StorefrontApp.displayName = "StorefrontApp";
3804
4475
  return StorefrontApp;
3805
4476
  }
4477
+ function createStorefront(options) {
4478
+ const routes = [...options.pages ?? [], ...options.routes ?? []];
4479
+ const config = {
4480
+ ...options.config,
4481
+ provider: options.provider ?? options.config.provider,
4482
+ components: {
4483
+ ...options.config.components,
4484
+ ...options.components
4485
+ },
4486
+ routes: routes.length > 0 ? routes : options.config.routes,
4487
+ home: options.sections ? { ...options.config.home ?? { sections: [] }, sections: options.sections } : options.config.home
4488
+ };
4489
+ return createStorefrontApp(config);
4490
+ }
4491
+
4492
+ // src/define.ts
4493
+ function defineStorefrontConfig(config) {
4494
+ return config;
4495
+ }
4496
+ function defineStorefrontComponents(components) {
4497
+ return components;
4498
+ }
4499
+ function defineStorefrontRoutes(routes) {
4500
+ return routes;
4501
+ }
4502
+ function defineStorefrontSections(sections) {
4503
+ return sections;
4504
+ }
4505
+ function defineStorefrontApp(options) {
4506
+ return options;
4507
+ }
3806
4508
  var runtimeConfigRegistry = /* @__PURE__ */ new Map();
3807
4509
  function slug(name) {
3808
4510
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "app";
@@ -3861,7 +4563,7 @@ function manifestToStorefrontConfig(manifest, surfaceName) {
3861
4563
  provider: manifest.backend?.provider,
3862
4564
  url: manifest.backend?.url
3863
4565
  },
3864
- slots: runtimeConfig?.slots,
4566
+ components: runtimeConfig?.components,
3865
4567
  routes: runtimeConfig?.routes,
3866
4568
  provider: runtimeConfig?.provider,
3867
4569
  auth: runtimeConfig?.auth,
@@ -3869,9 +4571,9 @@ function manifestToStorefrontConfig(manifest, surfaceName) {
3869
4571
  };
3870
4572
  }
3871
4573
  function StorefrontScaffold({ manifest, surface }) {
3872
- const config = React18__default.default.useMemo(() => manifestToStorefrontConfig(manifest, surface), [manifest, surface]);
3873
- const resolved = React18__default.default.useMemo(() => resolveConfig(config), [config]);
3874
- const inited = React18__default.default.useRef(false);
4574
+ const config = React6__default.default.useMemo(() => manifestToStorefrontConfig(manifest, surface), [manifest, surface]);
4575
+ const resolved = React6__default.default.useMemo(() => resolveConfig(config), [config]);
4576
+ const inited = React6__default.default.useRef(false);
3875
4577
  if (!inited.current) {
3876
4578
  initStorefrontRuntime(config);
3877
4579
  inited.current = true;
@@ -4122,6 +4824,7 @@ exports.HeroSection = HeroSection;
4122
4824
  exports.HomePage = HomePage;
4123
4825
  exports.Link = Link;
4124
4826
  exports.ManifestoBlock = ManifestoBlock;
4827
+ exports.MediaCarousel = MediaCarousel;
4125
4828
  exports.MyPurchasesPage = MyPurchasesPage;
4126
4829
  exports.NewsletterBand = NewsletterBand;
4127
4830
  exports.OrderConfirmationPage = OrderConfirmationPage;
@@ -4129,23 +4832,33 @@ exports.OrderTrackingTimeline = OrderTrackingTimeline;
4129
4832
  exports.Price = Price;
4130
4833
  exports.ProductCard = ProductCard;
4131
4834
  exports.ProductDetailPage = ProductDetailPage;
4835
+ exports.ProductEnquiryForm = ProductEnquiryForm;
4132
4836
  exports.ProductGrid = ProductGrid;
4133
4837
  exports.ProductOptionSelector = ProductOptionSelector;
4134
4838
  exports.ProductRail = ProductRail;
4839
+ exports.ProductSlider = ProductSlider;
4135
4840
  exports.PromoBanner = PromoBanner;
4136
4841
  exports.QuantityInput = QuantityInput;
4137
4842
  exports.Reveal = Reveal;
4843
+ exports.SmoothImage = SmoothImage;
4138
4844
  exports.StorefrontFooter = StorefrontFooter;
4139
4845
  exports.StorefrontHeader = StorefrontHeader;
4140
4846
  exports.StorefrontScaffold = StorefrontScaffold;
4847
+ exports.StorefrontSections = StorefrontSections;
4141
4848
  exports.StorefrontShell = StorefrontShell;
4142
4849
  exports.StorefrontThemeStyle = StorefrontThemeStyle;
4143
4850
  exports.TID = TID;
4144
4851
  exports.Testimonials = Testimonials;
4145
4852
  exports.atelierTemplate = atelierTemplate;
4146
4853
  exports.bannerPlaceholder = bannerPlaceholder;
4854
+ exports.createStorefront = createStorefront;
4147
4855
  exports.createStorefrontApp = createStorefrontApp;
4148
4856
  exports.defineStorefront = defineStorefront;
4857
+ exports.defineStorefrontApp = defineStorefrontApp;
4858
+ exports.defineStorefrontComponents = defineStorefrontComponents;
4859
+ exports.defineStorefrontConfig = defineStorefrontConfig;
4860
+ exports.defineStorefrontRoutes = defineStorefrontRoutes;
4861
+ exports.defineStorefrontSections = defineStorefrontSections;
4149
4862
  exports.establishCustomerSession = establishCustomerSession;
4150
4863
  exports.formatMoney = formatMoney;
4151
4864
  exports.formatProductOptionSelection = formatProductOptionSelection;
@@ -4158,7 +4871,7 @@ exports.navigateTo = navigateTo;
4158
4871
  exports.normalizeProductOptionSelection = normalizeProductOptionSelection;
4159
4872
  exports.placeStorefrontOrder = placeStorefrontOrder;
4160
4873
  exports.prefersReducedMotion = prefersReducedMotion;
4161
- exports.productCardSlotContract = productCardSlotContract;
4874
+ exports.productCardComponentContract = productCardComponentContract;
4162
4875
  exports.productOptionSelectionKey = productOptionSelectionKey;
4163
4876
  exports.registerStorefrontBlocks = registerStorefrontBlocks;
4164
4877
  exports.resolveAuthAdapter = resolveAuthAdapter;
@@ -4173,13 +4886,16 @@ exports.sertaoTemplate = sertaoTemplate;
4173
4886
  exports.signInByEmail = signInByEmail;
4174
4887
  exports.signOutCustomer = signOutCustomer;
4175
4888
  exports.signUpCustomer = signUpCustomer;
4176
- exports.storefrontSlotContracts = storefrontSlotContracts;
4889
+ exports.storefrontComponentContracts = storefrontComponentContracts;
4177
4890
  exports.storefrontTemplates = storefrontTemplates;
4178
4891
  exports.themeToCss = themeToCss;
4892
+ exports.useCart = useCart;
4179
4893
  exports.useCartStore = useCartStore;
4894
+ exports.useCatalog = useCatalog;
4180
4895
  exports.useCatalogStore = useCatalogStore;
4181
4896
  exports.useCategories = useCategories;
4182
4897
  exports.useDiscountValidator = useDiscountValidator;
4898
+ exports.useEnquiry = useEnquiry;
4183
4899
  exports.useHashPath = useHashPath;
4184
4900
  exports.useInView = useInView;
4185
4901
  exports.useMyOrders = useMyOrders;
@@ -4188,7 +4904,10 @@ exports.useProduct = useProduct;
4188
4904
  exports.useProducts = useProducts;
4189
4905
  exports.useScrolled = useScrolled;
4190
4906
  exports.useSessionStore = useSessionStore;
4907
+ exports.useStorefront = useStorefront;
4908
+ exports.useStorefrontActions = useStorefrontActions;
4191
4909
  exports.useStorefrontConfig = useStorefrontConfig;
4910
+ exports.useStorefrontConfigOptional = useStorefrontConfigOptional;
4192
4911
  exports.voltTemplate = voltTemplate;
4193
4912
  //# sourceMappingURL=index.cjs.map
4194
4913
  //# sourceMappingURL=index.cjs.map