@numueg/theme-sdk 0.9.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -3,8 +3,8 @@ export { A as Address, a as CartItem, O as Order, d as OrderItem, f as ProductIm
3
3
  import { T as ThemeSettingsV3, c as SectionInstance, M as MountResult, B as BlockInstance, b as BlockSchema, a as BlockProps$1, f as SectionSchema, e as SectionProps$1 } from './theme-CNTB4KnU.mjs';
4
4
  export { E as ExternalThemeMetadata, h as MAX_BLOCK_DEPTH, P as PageTemplate, i as PresetBlock, S as SectionGroup, d as SectionPreset, g as SettingDefinition } from './theme-CNTB4KnU.mjs';
5
5
  export { KNOWN_SETTING_TYPES, KNOWN_TEMPLATES, REQUIRED_TEMPLATES, SDK_VERSION, THEME_CONTRACT_VERSION, ValidationIssue, ValidationResult, mergeResults, validateBuiltManifest, validateManifest, validateSectionSchema, validateSettingsAgainstSchema } from './validation.mjs';
6
- import { C as CartContextValue, L as LocalizationState, a as CurrencyState, T as ThemeMountContext, b as ThemeRenderArgs, M as MenuItemData } from './mount-NLjFNyBv.mjs';
7
- export { c as CartContext, d as CartMutationResult, e as CollectionContext, f as CurrencyConfig, g as CurrencyContext, h as CustomerContext, i as LocalizationContext, N as NavigationContext, P as PageContext, j as ProductContext, S as ShopContext, k as ThemeMountPage, l as ThemeSettingsContext, m as buildThemeElement, n as mountTheme } from './mount-NLjFNyBv.mjs';
6
+ import { C as CartContextValue, L as LocalizationState, a as CurrencyState, T as ThemeMountContext, b as ThemeRenderArgs, M as MenuItemData } from './mount-BDo4a42I.mjs';
7
+ export { c as CartContext, d as CartMutationResult, e as CollectionContext, f as CurrencyConfig, g as CurrencyContext, h as CustomerContext, i as LocalizationContext, N as NavigationContext, P as PageContext, j as ProductContext, S as ShopContext, k as ThemeMountPage, l as ThemeSettingsContext, m as buildThemeElement, n as mountTheme } from './mount-BDo4a42I.mjs';
8
8
  import * as react from 'react';
9
9
  import { ReactElement, ReactNode, ElementType, CSSProperties, ImgHTMLAttributes, AnchorHTMLAttributes, ButtonHTMLAttributes, HTMLAttributes, FormHTMLAttributes, ComponentType } from 'react';
10
10
  export { resolveThemeSettings } from './normalize.mjs';
@@ -928,10 +928,30 @@ interface UseVariantSelection {
928
928
  * Shopify's behavior. Themes that prefer "Choose your size" empty
929
929
  * state can pass `autoSelect: false`.
930
930
  */
931
- declare function useVariantSelection(product: Pick<Product, "options" | "variants">, opts?: {
931
+ declare function useVariantSelection(product: Pick<Product, "options" | "variants"> & {
932
+ id?: string;
933
+ }, opts?: {
932
934
  autoSelect?: boolean;
933
935
  }): UseVariantSelection;
934
936
 
937
+ /**
938
+ * Live variant-picker selection registry.
939
+ *
940
+ * `useVariantSelection` publishes its current axis→value map here keyed by
941
+ * product id; `AddToCartButton` reads it back at click time. This is what
942
+ * lets the selection reach the cart even when no real variant row matches —
943
+ * legacy products keep their axes in attributes JSON with a single
944
+ * placeholder variant whose option_values is {}, so `findVariantByOptions`
945
+ * returns null and there'd otherwise be nothing to send.
946
+ *
947
+ * Module-scoped (not React context) on purpose: the hook instance lives in
948
+ * the theme's PDP section while the button may be a sibling — they share no
949
+ * provider of ours. Bounded: one entry per product id, last write wins.
950
+ */
951
+ declare function publishVariantSelection(productId: string | undefined | null, selection: Record<string, string>): void;
952
+ /** Latest non-empty selection for a product, or null. */
953
+ declare function readVariantSelection(productId: string | undefined | null): Record<string, string> | null;
954
+
935
955
  /**
936
956
  * Gift card balance check — Phase 8.3.
937
957
  *
@@ -1516,27 +1536,66 @@ interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"
1516
1536
  to: string;
1517
1537
  children: ReactNode;
1518
1538
  }
1539
+ /**
1540
+ * Event dispatched on `window` when a Link is clicked and eligible for
1541
+ * client-side (soft) navigation. `detail.href` is the target path.
1542
+ *
1543
+ * Contract: the event is CANCELABLE. A host that can perform soft
1544
+ * navigation (e.g. the Next.js storefront routing through its client
1545
+ * router) listens for it and calls `event.preventDefault()` to claim
1546
+ * the navigation. If no listener claims it, the Link falls back to
1547
+ * default anchor behavior — a normal full-page navigation — so themes
1548
+ * running under hosts without the bridge (older storefronts, the CLI
1549
+ * dev server, static previews) keep working unchanged.
1550
+ */
1551
+ declare const NAVIGATE_EVENT = "numu:navigate";
1552
+ interface NavigateEventDetail {
1553
+ href: string;
1554
+ }
1555
+ /**
1556
+ * Ask the host to soft-navigate to `href`. Returns true when a host
1557
+ * listener claimed the navigation (the caller should suppress its own
1558
+ * default behavior), false when no handler is present (the caller
1559
+ * should fall back to a full navigation). Exposed for themes that
1560
+ * navigate programmatically (e.g. after a search submit).
1561
+ */
1562
+ declare function requestNavigate(href: string): boolean;
1519
1563
  /**
1520
1564
  * Route-aware <Link>. Themes write paths as `/products/<slug>` (matches
1521
1565
  * the production subdomain root). The storefront proxy rewrites those
1522
1566
  * under `/<subdomain>/...` in dev path-segment routing; in production
1523
1567
  * the subdomain hostname does the same job at the edge.
1524
1568
  *
1525
- * For plain anchor behavior — server-rendered HTML, full page nav — we
1526
- * just emit a regular `<a>`. Themes that want client-side transitions
1527
- * can wrap this in their own router-aware component; in practice
1528
- * storefront pages are SSR'd so a full nav is fine and predictable.
1529
- *
1530
- * External URLs (have a protocol or start with `//`) pass through
1531
- * unchanged so social-media links, CDN paths, etc. work without
1532
- * special casing.
1533
- */
1534
- declare function Link({ to, children, ...rest }: LinkProps): react.JSX.Element;
1569
+ * Navigation is a two-tier contract:
1570
+ * 1. Soft (preferred): on an eligible plain left-click we dispatch a
1571
+ * cancelable NAVIGATE_EVENT. A router-aware host claims it with
1572
+ * preventDefault() and performs a client-side transition React,
1573
+ * the SDK runtime, and the evaluated theme bundle all stay warm,
1574
+ * so page-to-page moves skip the full document reload + remount.
1575
+ * 2. Hard (fallback): no listener claims the event default anchor
1576
+ * behavior, a normal full-page navigation. Identical to the
1577
+ * pre-0.10 behavior, so themes never break on hosts without the
1578
+ * bridge.
1579
+ *
1580
+ * Soft navigation is only attempted for storefront-internal paths and
1581
+ * unmodified left-clicks: external URLs (protocol or `//`), hash-only
1582
+ * anchors, modified clicks (ctrl/cmd/shift/alt — "open in new tab"),
1583
+ * non-left buttons, `target` other than `_self`, and `download` links
1584
+ * all keep default browser behavior.
1585
+ */
1586
+ declare function Link({ to, children, onClick, ...rest }: LinkProps): react.JSX.Element;
1535
1587
 
1536
1588
  interface AddToCartButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "onClick" | "disabled"> {
1537
1589
  product: Product;
1538
1590
  variant?: ProductVariant;
1539
1591
  quantity?: number;
1592
+ /**
1593
+ * Picker axes to persist on the cart line ({Color: "Black"}). Optional —
1594
+ * when omitted, the button reads the live useVariantSelection state for
1595
+ * this product from the SDK registry, so existing themes get the variant
1596
+ * label end-to-end (cart → checkout → order → email) with no changes.
1597
+ */
1598
+ selectedOptions?: Record<string, string>;
1540
1599
  /** Custom labels — fallbacks are English defaults. */
1541
1600
  label?: ReactNode;
1542
1601
  loadingLabel?: ReactNode;
@@ -1559,7 +1618,7 @@ interface AddToCartButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonEleme
1559
1618
  * Doesn't trap navigation — for "buy now" flows that should redirect
1560
1619
  * to checkout, themes wrap this in their own `<a>` after onAdded.
1561
1620
  */
1562
- declare function AddToCartButton({ product, variant, quantity, label, loadingLabel, soldOutLabel, errorLabel, onAdded, ...rest }: AddToCartButtonProps): react.JSX.Element;
1621
+ declare function AddToCartButton({ product, variant, quantity, selectedOptions, label, loadingLabel, soldOutLabel, errorLabel, onAdded, ...rest }: AddToCartButtonProps): react.JSX.Element;
1563
1622
 
1564
1623
  interface SectionProps extends HTMLAttributes<HTMLElement> {
1565
1624
  /** Section instance id (the order key in templates). Required for the
@@ -2400,4 +2459,4 @@ declare function pickTranslations(bundle: LocaleBundle, locale: string): LocaleM
2400
2459
  */
2401
2460
  declare function buildLocaleBundle<T extends Record<string, unknown>>(modules: T): LocaleBundle;
2402
2461
 
2403
- export { AddToCartButton, type AddressInput, type AnalyticsApi, type AnalyticsPayload, type AppManifestBlock, type AppPayload, type AppState, Block, BlockInstance, BlockProps$1 as BlockProps, BlockSchema, type CacheFetcher, type CacheMutator, type CachedResource, type CachedResourceState, Cart, CartContextValue, type CheckoutAddress, type CheckoutApi, type CheckoutSessionState, type CheckoutStep, Collection, CollectionCard, type CollectionCardProps, type CollectionCardSlots, CollectionProvider, type ComputedStyleTokens, CurrencyState, CurrencySwitcher, type CurrencySwitcherProps, Customer, type CustomerAddress, type CustomerAddressesState, type DefineBlockInput, type DefineSectionInput, type DefinedBlock, type DefinedSection, type DynamicResolveContext, type DynamicSourceRef, EditableImage, type EditableImageProps, EditableText, type EditableTextProps, type FocalSrcOptions, Form, type GiftCardBalance, HeroMedia, type HeroMediaProps, ICON_NAMES, Icon, IconMap, type IconProps, Image, type ImageTransform, LOGO_SHAPE_OPTIONS, LOGO_SIZE_OPTIONS, Link, type LocaleBundle, type LocaleMessages, LocaleSwitcher, type LocaleSwitcherProps, Logo, type LogoProps, type LogoShape, type LogoSize, MenuItemData, Money, MountResult, type MutateOptions, type NavigationItem, type NavigationState, NuMuProvider, type OrderDetail, type OrderListEntry, type OrderListState, type OrderState, Page, type PlaceOrderResult, Product, ProductCard, type ProductCardProps, type ProductCardSlots, ProductProvider, ProductVariant, type RelatedProductsState, type ReorderResult, type ReorderSkipReason, type ReorderSkippedItem, RichText, type RichTextProps, type SearchResults, type SearchState, Section, SectionContext, type SectionGroupInstance, SectionInstance, SectionProps$1 as SectionProps, SectionSchema, type ShippingRateOption, type ShopWithHelpers, SizeChart, Store, type ThemeEntry, ThemeMountContext, ThemeRenderArgs, ThemeSettingsV3, type UseCachedResourceOptions, type UseGiftCardBalance, type UseReorder, type UseSearchOptions, type UseShippingRatesOptions, type UseShippingRatesState, type UseVariantSelection, type WishlistItem, type WishlistState, applyGlobalStyleTokens, applyImageTransform, asImageTransform, assetUrl, availableValues, buildLocaleBundle, clearSdkSingleton, collectBlocks, collectSections, computeGlobalStyleTokens, defaultVariant, defineBlock, defineSection, defineThemeEntry, dynamicSource, findVariantByOptions, flattenMessages, focalSrc, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, logoImgStyle, logoStyleTokens, pickTranslations, registerReactSingleton, registerSdkSingleton, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSizeChart, resolveSourcePath, sanitizeHtml, useAnalytics, useApp, useCachedResource, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCurrentTemplate, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useLocale, useLocalization, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProductSizeChart, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionGroup, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };
2462
+ export { AddToCartButton, type AddressInput, type AnalyticsApi, type AnalyticsPayload, type AppManifestBlock, type AppPayload, type AppState, Block, BlockInstance, BlockProps$1 as BlockProps, BlockSchema, type CacheFetcher, type CacheMutator, type CachedResource, type CachedResourceState, Cart, CartContextValue, type CheckoutAddress, type CheckoutApi, type CheckoutSessionState, type CheckoutStep, Collection, CollectionCard, type CollectionCardProps, type CollectionCardSlots, CollectionProvider, type ComputedStyleTokens, CurrencyState, CurrencySwitcher, type CurrencySwitcherProps, Customer, type CustomerAddress, type CustomerAddressesState, type DefineBlockInput, type DefineSectionInput, type DefinedBlock, type DefinedSection, type DynamicResolveContext, type DynamicSourceRef, EditableImage, type EditableImageProps, EditableText, type EditableTextProps, type FocalSrcOptions, Form, type GiftCardBalance, HeroMedia, type HeroMediaProps, ICON_NAMES, Icon, IconMap, type IconProps, Image, type ImageTransform, LOGO_SHAPE_OPTIONS, LOGO_SIZE_OPTIONS, Link, type LocaleBundle, type LocaleMessages, LocaleSwitcher, type LocaleSwitcherProps, Logo, type LogoProps, type LogoShape, type LogoSize, MenuItemData, Money, MountResult, type MutateOptions, NAVIGATE_EVENT, type NavigateEventDetail, type NavigationItem, type NavigationState, NuMuProvider, type OrderDetail, type OrderListEntry, type OrderListState, type OrderState, Page, type PlaceOrderResult, Product, ProductCard, type ProductCardProps, type ProductCardSlots, ProductProvider, ProductVariant, type RelatedProductsState, type ReorderResult, type ReorderSkipReason, type ReorderSkippedItem, RichText, type RichTextProps, type SearchResults, type SearchState, Section, SectionContext, type SectionGroupInstance, SectionInstance, SectionProps$1 as SectionProps, SectionSchema, type ShippingRateOption, type ShopWithHelpers, SizeChart, Store, type ThemeEntry, ThemeMountContext, ThemeRenderArgs, ThemeSettingsV3, type UseCachedResourceOptions, type UseGiftCardBalance, type UseReorder, type UseSearchOptions, type UseShippingRatesOptions, type UseShippingRatesState, type UseVariantSelection, type WishlistItem, type WishlistState, applyGlobalStyleTokens, applyImageTransform, asImageTransform, assetUrl, availableValues, buildLocaleBundle, clearSdkSingleton, collectBlocks, collectSections, computeGlobalStyleTokens, defaultVariant, defineBlock, defineSection, defineThemeEntry, dynamicSource, findVariantByOptions, flattenMessages, focalSrc, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, logoImgStyle, logoStyleTokens, pickTranslations, publishVariantSelection, readVariantSelection, registerReactSingleton, registerSdkSingleton, requestNavigate, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSizeChart, resolveSourcePath, sanitizeHtml, useAnalytics, useApp, useCachedResource, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCurrentTemplate, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useLocale, useLocalization, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProductSizeChart, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionGroup, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };
package/dist/index.d.ts CHANGED
@@ -3,8 +3,8 @@ export { A as Address, a as CartItem, O as Order, d as OrderItem, f as ProductIm
3
3
  import { T as ThemeSettingsV3, c as SectionInstance, M as MountResult, B as BlockInstance, b as BlockSchema, a as BlockProps$1, f as SectionSchema, e as SectionProps$1 } from './theme-CNTB4KnU.js';
4
4
  export { E as ExternalThemeMetadata, h as MAX_BLOCK_DEPTH, P as PageTemplate, i as PresetBlock, S as SectionGroup, d as SectionPreset, g as SettingDefinition } from './theme-CNTB4KnU.js';
5
5
  export { KNOWN_SETTING_TYPES, KNOWN_TEMPLATES, REQUIRED_TEMPLATES, SDK_VERSION, THEME_CONTRACT_VERSION, ValidationIssue, ValidationResult, mergeResults, validateBuiltManifest, validateManifest, validateSectionSchema, validateSettingsAgainstSchema } from './validation.js';
6
- import { C as CartContextValue, L as LocalizationState, a as CurrencyState, T as ThemeMountContext, b as ThemeRenderArgs, M as MenuItemData } from './mount-CM4kGg9w.js';
7
- export { c as CartContext, d as CartMutationResult, e as CollectionContext, f as CurrencyConfig, g as CurrencyContext, h as CustomerContext, i as LocalizationContext, N as NavigationContext, P as PageContext, j as ProductContext, S as ShopContext, k as ThemeMountPage, l as ThemeSettingsContext, m as buildThemeElement, n as mountTheme } from './mount-CM4kGg9w.js';
6
+ import { C as CartContextValue, L as LocalizationState, a as CurrencyState, T as ThemeMountContext, b as ThemeRenderArgs, M as MenuItemData } from './mount-DQZu8aBB.js';
7
+ export { c as CartContext, d as CartMutationResult, e as CollectionContext, f as CurrencyConfig, g as CurrencyContext, h as CustomerContext, i as LocalizationContext, N as NavigationContext, P as PageContext, j as ProductContext, S as ShopContext, k as ThemeMountPage, l as ThemeSettingsContext, m as buildThemeElement, n as mountTheme } from './mount-DQZu8aBB.js';
8
8
  import * as react from 'react';
9
9
  import { ReactElement, ReactNode, ElementType, CSSProperties, ImgHTMLAttributes, AnchorHTMLAttributes, ButtonHTMLAttributes, HTMLAttributes, FormHTMLAttributes, ComponentType } from 'react';
10
10
  export { resolveThemeSettings } from './normalize.js';
@@ -928,10 +928,30 @@ interface UseVariantSelection {
928
928
  * Shopify's behavior. Themes that prefer "Choose your size" empty
929
929
  * state can pass `autoSelect: false`.
930
930
  */
931
- declare function useVariantSelection(product: Pick<Product, "options" | "variants">, opts?: {
931
+ declare function useVariantSelection(product: Pick<Product, "options" | "variants"> & {
932
+ id?: string;
933
+ }, opts?: {
932
934
  autoSelect?: boolean;
933
935
  }): UseVariantSelection;
934
936
 
937
+ /**
938
+ * Live variant-picker selection registry.
939
+ *
940
+ * `useVariantSelection` publishes its current axis→value map here keyed by
941
+ * product id; `AddToCartButton` reads it back at click time. This is what
942
+ * lets the selection reach the cart even when no real variant row matches —
943
+ * legacy products keep their axes in attributes JSON with a single
944
+ * placeholder variant whose option_values is {}, so `findVariantByOptions`
945
+ * returns null and there'd otherwise be nothing to send.
946
+ *
947
+ * Module-scoped (not React context) on purpose: the hook instance lives in
948
+ * the theme's PDP section while the button may be a sibling — they share no
949
+ * provider of ours. Bounded: one entry per product id, last write wins.
950
+ */
951
+ declare function publishVariantSelection(productId: string | undefined | null, selection: Record<string, string>): void;
952
+ /** Latest non-empty selection for a product, or null. */
953
+ declare function readVariantSelection(productId: string | undefined | null): Record<string, string> | null;
954
+
935
955
  /**
936
956
  * Gift card balance check — Phase 8.3.
937
957
  *
@@ -1516,27 +1536,66 @@ interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"
1516
1536
  to: string;
1517
1537
  children: ReactNode;
1518
1538
  }
1539
+ /**
1540
+ * Event dispatched on `window` when a Link is clicked and eligible for
1541
+ * client-side (soft) navigation. `detail.href` is the target path.
1542
+ *
1543
+ * Contract: the event is CANCELABLE. A host that can perform soft
1544
+ * navigation (e.g. the Next.js storefront routing through its client
1545
+ * router) listens for it and calls `event.preventDefault()` to claim
1546
+ * the navigation. If no listener claims it, the Link falls back to
1547
+ * default anchor behavior — a normal full-page navigation — so themes
1548
+ * running under hosts without the bridge (older storefronts, the CLI
1549
+ * dev server, static previews) keep working unchanged.
1550
+ */
1551
+ declare const NAVIGATE_EVENT = "numu:navigate";
1552
+ interface NavigateEventDetail {
1553
+ href: string;
1554
+ }
1555
+ /**
1556
+ * Ask the host to soft-navigate to `href`. Returns true when a host
1557
+ * listener claimed the navigation (the caller should suppress its own
1558
+ * default behavior), false when no handler is present (the caller
1559
+ * should fall back to a full navigation). Exposed for themes that
1560
+ * navigate programmatically (e.g. after a search submit).
1561
+ */
1562
+ declare function requestNavigate(href: string): boolean;
1519
1563
  /**
1520
1564
  * Route-aware <Link>. Themes write paths as `/products/<slug>` (matches
1521
1565
  * the production subdomain root). The storefront proxy rewrites those
1522
1566
  * under `/<subdomain>/...` in dev path-segment routing; in production
1523
1567
  * the subdomain hostname does the same job at the edge.
1524
1568
  *
1525
- * For plain anchor behavior — server-rendered HTML, full page nav — we
1526
- * just emit a regular `<a>`. Themes that want client-side transitions
1527
- * can wrap this in their own router-aware component; in practice
1528
- * storefront pages are SSR'd so a full nav is fine and predictable.
1529
- *
1530
- * External URLs (have a protocol or start with `//`) pass through
1531
- * unchanged so social-media links, CDN paths, etc. work without
1532
- * special casing.
1533
- */
1534
- declare function Link({ to, children, ...rest }: LinkProps): react.JSX.Element;
1569
+ * Navigation is a two-tier contract:
1570
+ * 1. Soft (preferred): on an eligible plain left-click we dispatch a
1571
+ * cancelable NAVIGATE_EVENT. A router-aware host claims it with
1572
+ * preventDefault() and performs a client-side transition React,
1573
+ * the SDK runtime, and the evaluated theme bundle all stay warm,
1574
+ * so page-to-page moves skip the full document reload + remount.
1575
+ * 2. Hard (fallback): no listener claims the event default anchor
1576
+ * behavior, a normal full-page navigation. Identical to the
1577
+ * pre-0.10 behavior, so themes never break on hosts without the
1578
+ * bridge.
1579
+ *
1580
+ * Soft navigation is only attempted for storefront-internal paths and
1581
+ * unmodified left-clicks: external URLs (protocol or `//`), hash-only
1582
+ * anchors, modified clicks (ctrl/cmd/shift/alt — "open in new tab"),
1583
+ * non-left buttons, `target` other than `_self`, and `download` links
1584
+ * all keep default browser behavior.
1585
+ */
1586
+ declare function Link({ to, children, onClick, ...rest }: LinkProps): react.JSX.Element;
1535
1587
 
1536
1588
  interface AddToCartButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "onClick" | "disabled"> {
1537
1589
  product: Product;
1538
1590
  variant?: ProductVariant;
1539
1591
  quantity?: number;
1592
+ /**
1593
+ * Picker axes to persist on the cart line ({Color: "Black"}). Optional —
1594
+ * when omitted, the button reads the live useVariantSelection state for
1595
+ * this product from the SDK registry, so existing themes get the variant
1596
+ * label end-to-end (cart → checkout → order → email) with no changes.
1597
+ */
1598
+ selectedOptions?: Record<string, string>;
1540
1599
  /** Custom labels — fallbacks are English defaults. */
1541
1600
  label?: ReactNode;
1542
1601
  loadingLabel?: ReactNode;
@@ -1559,7 +1618,7 @@ interface AddToCartButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonEleme
1559
1618
  * Doesn't trap navigation — for "buy now" flows that should redirect
1560
1619
  * to checkout, themes wrap this in their own `<a>` after onAdded.
1561
1620
  */
1562
- declare function AddToCartButton({ product, variant, quantity, label, loadingLabel, soldOutLabel, errorLabel, onAdded, ...rest }: AddToCartButtonProps): react.JSX.Element;
1621
+ declare function AddToCartButton({ product, variant, quantity, selectedOptions, label, loadingLabel, soldOutLabel, errorLabel, onAdded, ...rest }: AddToCartButtonProps): react.JSX.Element;
1563
1622
 
1564
1623
  interface SectionProps extends HTMLAttributes<HTMLElement> {
1565
1624
  /** Section instance id (the order key in templates). Required for the
@@ -2400,4 +2459,4 @@ declare function pickTranslations(bundle: LocaleBundle, locale: string): LocaleM
2400
2459
  */
2401
2460
  declare function buildLocaleBundle<T extends Record<string, unknown>>(modules: T): LocaleBundle;
2402
2461
 
2403
- export { AddToCartButton, type AddressInput, type AnalyticsApi, type AnalyticsPayload, type AppManifestBlock, type AppPayload, type AppState, Block, BlockInstance, BlockProps$1 as BlockProps, BlockSchema, type CacheFetcher, type CacheMutator, type CachedResource, type CachedResourceState, Cart, CartContextValue, type CheckoutAddress, type CheckoutApi, type CheckoutSessionState, type CheckoutStep, Collection, CollectionCard, type CollectionCardProps, type CollectionCardSlots, CollectionProvider, type ComputedStyleTokens, CurrencyState, CurrencySwitcher, type CurrencySwitcherProps, Customer, type CustomerAddress, type CustomerAddressesState, type DefineBlockInput, type DefineSectionInput, type DefinedBlock, type DefinedSection, type DynamicResolveContext, type DynamicSourceRef, EditableImage, type EditableImageProps, EditableText, type EditableTextProps, type FocalSrcOptions, Form, type GiftCardBalance, HeroMedia, type HeroMediaProps, ICON_NAMES, Icon, IconMap, type IconProps, Image, type ImageTransform, LOGO_SHAPE_OPTIONS, LOGO_SIZE_OPTIONS, Link, type LocaleBundle, type LocaleMessages, LocaleSwitcher, type LocaleSwitcherProps, Logo, type LogoProps, type LogoShape, type LogoSize, MenuItemData, Money, MountResult, type MutateOptions, type NavigationItem, type NavigationState, NuMuProvider, type OrderDetail, type OrderListEntry, type OrderListState, type OrderState, Page, type PlaceOrderResult, Product, ProductCard, type ProductCardProps, type ProductCardSlots, ProductProvider, ProductVariant, type RelatedProductsState, type ReorderResult, type ReorderSkipReason, type ReorderSkippedItem, RichText, type RichTextProps, type SearchResults, type SearchState, Section, SectionContext, type SectionGroupInstance, SectionInstance, SectionProps$1 as SectionProps, SectionSchema, type ShippingRateOption, type ShopWithHelpers, SizeChart, Store, type ThemeEntry, ThemeMountContext, ThemeRenderArgs, ThemeSettingsV3, type UseCachedResourceOptions, type UseGiftCardBalance, type UseReorder, type UseSearchOptions, type UseShippingRatesOptions, type UseShippingRatesState, type UseVariantSelection, type WishlistItem, type WishlistState, applyGlobalStyleTokens, applyImageTransform, asImageTransform, assetUrl, availableValues, buildLocaleBundle, clearSdkSingleton, collectBlocks, collectSections, computeGlobalStyleTokens, defaultVariant, defineBlock, defineSection, defineThemeEntry, dynamicSource, findVariantByOptions, flattenMessages, focalSrc, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, logoImgStyle, logoStyleTokens, pickTranslations, registerReactSingleton, registerSdkSingleton, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSizeChart, resolveSourcePath, sanitizeHtml, useAnalytics, useApp, useCachedResource, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCurrentTemplate, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useLocale, useLocalization, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProductSizeChart, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionGroup, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };
2462
+ export { AddToCartButton, type AddressInput, type AnalyticsApi, type AnalyticsPayload, type AppManifestBlock, type AppPayload, type AppState, Block, BlockInstance, BlockProps$1 as BlockProps, BlockSchema, type CacheFetcher, type CacheMutator, type CachedResource, type CachedResourceState, Cart, CartContextValue, type CheckoutAddress, type CheckoutApi, type CheckoutSessionState, type CheckoutStep, Collection, CollectionCard, type CollectionCardProps, type CollectionCardSlots, CollectionProvider, type ComputedStyleTokens, CurrencyState, CurrencySwitcher, type CurrencySwitcherProps, Customer, type CustomerAddress, type CustomerAddressesState, type DefineBlockInput, type DefineSectionInput, type DefinedBlock, type DefinedSection, type DynamicResolveContext, type DynamicSourceRef, EditableImage, type EditableImageProps, EditableText, type EditableTextProps, type FocalSrcOptions, Form, type GiftCardBalance, HeroMedia, type HeroMediaProps, ICON_NAMES, Icon, IconMap, type IconProps, Image, type ImageTransform, LOGO_SHAPE_OPTIONS, LOGO_SIZE_OPTIONS, Link, type LocaleBundle, type LocaleMessages, LocaleSwitcher, type LocaleSwitcherProps, Logo, type LogoProps, type LogoShape, type LogoSize, MenuItemData, Money, MountResult, type MutateOptions, NAVIGATE_EVENT, type NavigateEventDetail, type NavigationItem, type NavigationState, NuMuProvider, type OrderDetail, type OrderListEntry, type OrderListState, type OrderState, Page, type PlaceOrderResult, Product, ProductCard, type ProductCardProps, type ProductCardSlots, ProductProvider, ProductVariant, type RelatedProductsState, type ReorderResult, type ReorderSkipReason, type ReorderSkippedItem, RichText, type RichTextProps, type SearchResults, type SearchState, Section, SectionContext, type SectionGroupInstance, SectionInstance, SectionProps$1 as SectionProps, SectionSchema, type ShippingRateOption, type ShopWithHelpers, SizeChart, Store, type ThemeEntry, ThemeMountContext, ThemeRenderArgs, ThemeSettingsV3, type UseCachedResourceOptions, type UseGiftCardBalance, type UseReorder, type UseSearchOptions, type UseShippingRatesOptions, type UseShippingRatesState, type UseVariantSelection, type WishlistItem, type WishlistState, applyGlobalStyleTokens, applyImageTransform, asImageTransform, assetUrl, availableValues, buildLocaleBundle, clearSdkSingleton, collectBlocks, collectSections, computeGlobalStyleTokens, defaultVariant, defineBlock, defineSection, defineThemeEntry, dynamicSource, findVariantByOptions, flattenMessages, focalSrc, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, logoImgStyle, logoStyleTokens, pickTranslations, publishVariantSelection, readVariantSelection, registerReactSingleton, registerSdkSingleton, requestNavigate, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSizeChart, resolveSourcePath, sanitizeHtml, useAnalytics, useApp, useCachedResource, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCurrentTemplate, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useLocale, useLocalization, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProductSizeChart, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionGroup, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  export { resolveThemeSettings } from './chunk-XF2FGIVS.mjs';
2
- export { KNOWN_SETTING_TYPES, KNOWN_TEMPLATES, REQUIRED_TEMPLATES, SDK_VERSION, THEME_CONTRACT_VERSION, mergeResults, validateBuiltManifest, validateManifest, validateSectionSchema, validateSettingsAgainstSchema } from './chunk-JIAA7TGR.mjs';
3
- import { ShopContext, ThemeSettingsContext, CurrentTemplateContext, PageContext, LocalizationContext, CurrencyContext, CartContext, CustomerContext, NavigationContext, CollectionContext, ProductContext, useShop, useLocalization, useCustomer, useThemeSettings } from './chunk-PUR3FRGQ.mjs';
4
- export { CartContext, CollectionContext, CurrencyContext, CustomerContext, LocalizationContext, NavigationContext, PageContext, ProductContext, ShopContext, ThemeSettingsContext, useCollections, useCustomer, useDirection, useFieldTranslation, useLocale, useLocalization, useNumberFormat, usePage, useProducts, useShop, useThemeSettings, useTranslation } from './chunk-PUR3FRGQ.mjs';
2
+ export { KNOWN_SETTING_TYPES, KNOWN_TEMPLATES, REQUIRED_TEMPLATES, SDK_VERSION, THEME_CONTRACT_VERSION, mergeResults, validateBuiltManifest, validateManifest, validateSectionSchema, validateSettingsAgainstSchema } from './chunk-QFFTHIFZ.mjs';
3
+ import { ShopContext, ThemeSettingsContext, CurrentTemplateContext, PageContext, LocalizationContext, CurrencyContext, CartContext, CustomerContext, NavigationContext, CollectionContext, ProductContext, useShop, useLocalization, useCustomer, useThemeSettings } from './chunk-ZYZZG4JR.mjs';
4
+ export { CartContext, CollectionContext, CurrencyContext, CustomerContext, LocalizationContext, NavigationContext, PageContext, ProductContext, ShopContext, ThemeSettingsContext, useCollections, useCustomer, useDirection, useFieldTranslation, useLocale, useLocalization, useNumberFormat, usePage, useProducts, useShop, useThemeSettings, useTranslation } from './chunk-ZYZZG4JR.mjs';
5
5
  import { createContext, forwardRef, useState, useImperativeHandle, useEffect, useCallback, useMemo, useRef, useContext, useSyncExternalStore, StrictMode, createElement, Component } from 'react';
6
6
  import { hydrateRoot, createRoot } from 'react-dom/client';
7
7
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
@@ -588,10 +588,11 @@ function getFingerprint() {
588
588
  }
589
589
  function dispatchAnalyticsEvent(eventName, payload = {}) {
590
590
  if (typeof window === "undefined") return;
591
+ const eventId = crypto.randomUUID();
591
592
  try {
592
593
  window.dispatchEvent(
593
594
  new CustomEvent("numu:analytics:event", {
594
- detail: { event: eventName, payload, ts: Date.now() }
595
+ detail: { event: eventName, payload, event_id: eventId, ts: Date.now() }
595
596
  })
596
597
  );
597
598
  } catch {
@@ -599,7 +600,7 @@ function dispatchAnalyticsEvent(eventName, payload = {}) {
599
600
  const step = EVENT_TO_FUNNEL_STEP[eventName] ?? null;
600
601
  const customerId = readCustomerId();
601
602
  const body = step ? {
602
- event_id: crypto.randomUUID(),
603
+ event_id: eventId,
603
604
  path: window.location.pathname,
604
605
  fingerprint: getFingerprint(),
605
606
  step,
@@ -610,6 +611,7 @@ function dispatchAnalyticsEvent(eventName, payload = {}) {
610
611
  } : {
611
612
  event: eventName,
612
613
  payload,
614
+ event_id: eventId,
613
615
  ts: Date.now(),
614
616
  attribution: readAttribution() ?? void 0,
615
617
  customer_id: customerId ?? void 0
@@ -1020,6 +1022,25 @@ function availableValues(product, selection) {
1020
1022
  }
1021
1023
  return out;
1022
1024
  }
1025
+
1026
+ // src/utils/selectionRegistry.ts
1027
+ var selections = /* @__PURE__ */ new Map();
1028
+ function publishVariantSelection(productId, selection) {
1029
+ if (!productId) return;
1030
+ selections.set(productId, selection);
1031
+ }
1032
+ function readVariantSelection(productId) {
1033
+ if (!productId) return null;
1034
+ const sel = selections.get(productId);
1035
+ if (!sel) return null;
1036
+ const filtered = {};
1037
+ for (const [k, v] of Object.entries(sel)) {
1038
+ if (v) filtered[k] = v;
1039
+ }
1040
+ return Object.keys(filtered).length > 0 ? filtered : null;
1041
+ }
1042
+
1043
+ // src/hooks/useVariantSelection.ts
1023
1044
  function useVariantSelection(product, opts = {}) {
1024
1045
  const autoSelect = opts.autoSelect ?? true;
1025
1046
  const initial = useMemo(() => {
@@ -1038,6 +1059,9 @@ function useVariantSelection(product, opts = {}) {
1038
1059
  () => findVariantByOptions(product, selection),
1039
1060
  [product, selection]
1040
1061
  );
1062
+ useEffect(() => {
1063
+ publishVariantSelection(product.id, selection);
1064
+ }, [product.id, selection]);
1041
1065
  const availability = useMemo(
1042
1066
  () => availableValues(product, selection),
1043
1067
  [product, selection]
@@ -1470,8 +1494,9 @@ async function postCartMutation(endpoint, body, applyCart, reserveToken) {
1470
1494
  return { ok: false, status: res.status, message };
1471
1495
  }
1472
1496
  const json = await res.json();
1473
- applyCart(unwrapCart(json));
1474
- return { ok: true, status: res.status };
1497
+ const applied = unwrapCart(json);
1498
+ applyCart(applied);
1499
+ return { ok: true, status: res.status, cart: normalizeCartFromServer(applied) };
1475
1500
  }
1476
1501
  function NuMuProvider({
1477
1502
  store,
@@ -1705,18 +1730,24 @@ function NuMuProvider({
1705
1730
  [reserveToken, buildApplyCart]
1706
1731
  );
1707
1732
  const addItem = useCallback(
1708
- async (productId, variantId, quantity) => {
1733
+ async (productId, variantId, quantity, selectedOptions) => {
1709
1734
  const eventId = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}`;
1710
1735
  const qty = quantity || 1;
1736
+ const axes = selectedOptions && Object.keys(selectedOptions).length > 0 ? selectedOptions : readVariantSelection(productId) ?? void 0;
1711
1737
  const result = await mutate("/api/cart/add", {
1712
1738
  product_id: productId,
1713
1739
  variant_id: variantId,
1714
1740
  quantity: qty,
1741
+ selected_options: axes,
1715
1742
  _event_id: eventId
1716
1743
  });
1717
1744
  if (!result.ok) return result;
1718
1745
  try {
1719
1746
  if (typeof window !== "undefined") {
1747
+ const line = result.cart?.items.find(
1748
+ (it) => variantId ? it.variant_id === variantId : it.product_id === productId
1749
+ );
1750
+ const value = line && typeof line.price === "number" && line.price > 0 ? Math.round(line.price * qty * 100) / 100 : void 0;
1720
1751
  window.dispatchEvent(
1721
1752
  new CustomEvent("numu:analytics:event", {
1722
1753
  detail: {
@@ -1724,7 +1755,8 @@ function NuMuProvider({
1724
1755
  payload: {
1725
1756
  content_ids: [productId],
1726
1757
  content_type: "product",
1727
- num_items: qty
1758
+ num_items: qty,
1759
+ ...value !== void 0 ? { value, currency: result.cart?.currency } : {}
1728
1760
  },
1729
1761
  event_id: eventId
1730
1762
  }
@@ -2608,19 +2640,40 @@ function Logo({
2608
2640
  );
2609
2641
  }
2610
2642
  var ABSOLUTE_URL = /^[a-z]+:|^\/\//i;
2611
- function Link({ to, children, ...rest }) {
2643
+ var NAVIGATE_EVENT = "numu:navigate";
2644
+ function requestNavigate(href) {
2645
+ if (typeof window === "undefined") return false;
2646
+ const event = new CustomEvent(NAVIGATE_EVENT, {
2647
+ detail: { href },
2648
+ cancelable: true
2649
+ });
2650
+ return !window.dispatchEvent(event);
2651
+ }
2652
+ function Link({ to, children, onClick, ...rest }) {
2612
2653
  const shop = useShop();
2613
2654
  const isAbsolute = ABSOLUTE_URL.test(to);
2614
2655
  let href = to;
2615
2656
  if (!isAbsolute && shop && !to.startsWith("/")) {
2616
2657
  href = `/${to}`;
2617
2658
  }
2618
- return /* @__PURE__ */ jsx("a", { href, ...rest, children });
2659
+ const handleClick = (e) => {
2660
+ onClick?.(e);
2661
+ if (e.defaultPrevented) return;
2662
+ if (e.button !== 0) return;
2663
+ if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
2664
+ if (rest.target && rest.target !== "_self") return;
2665
+ if (rest.download !== void 0) return;
2666
+ if (isAbsolute) return;
2667
+ if (to.startsWith("#") || href.startsWith("#")) return;
2668
+ if (requestNavigate(href)) e.preventDefault();
2669
+ };
2670
+ return /* @__PURE__ */ jsx("a", { href, onClick: handleClick, ...rest, children });
2619
2671
  }
2620
2672
  function AddToCartButton({
2621
2673
  product,
2622
2674
  variant,
2623
2675
  quantity = 1,
2676
+ selectedOptions,
2624
2677
  label = "Add to cart",
2625
2678
  loadingLabel = "Adding\u2026",
2626
2679
  soldOutLabel = "Sold out",
@@ -2647,7 +2700,7 @@ function AddToCartButton({
2647
2700
  if (state === "adding") return;
2648
2701
  setState("adding");
2649
2702
  try {
2650
- await addItem(product.id, variant?.id, quantity);
2703
+ await addItem(product.id, variant?.id, quantity, selectedOptions);
2651
2704
  setState("idle");
2652
2705
  onAdded?.(product, variant);
2653
2706
  } catch {
@@ -3744,6 +3797,6 @@ function buildLocaleBundle(modules) {
3744
3797
  return bundle;
3745
3798
  }
3746
3799
 
3747
- export { AddToCartButton, Block, CollectionCard, CollectionProvider, CurrencySwitcher, EditableImage, EditableText, Form, HeroMedia, ICON_NAMES, Icon, IconMap, Image, LOGO_SHAPE_OPTIONS, LOGO_SIZE_OPTIONS, Link, LocaleSwitcher, Logo, MAX_BLOCK_DEPTH, Money, NuMuProvider, ProductCard, ProductProvider, RichText, Section, SectionContext, applyGlobalStyleTokens, applyImageTransform, asImageTransform, assetUrl, availableValues, buildLocaleBundle, buildThemeElement, clearSdkSingleton, collectBlocks, collectSections, computeGlobalStyleTokens, defaultVariant, defineBlock, defineSection, defineThemeEntry, dynamicSource, findVariantByOptions, flattenMessages, focalSrc, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, logoImgStyle, logoStyleTokens, mountTheme, pickTranslations, registerReactSingleton, registerSdkSingleton, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSizeChart, resolveSourcePath, sanitizeHtml, useAnalytics, useApp, useCachedResource, useCart, useCheckout, useCollection, useCollectionOptional, useCurrency, useCurrentTemplate, useCustomerActions, useCustomerAddresses, useGiftCardBalance, useImage, useMoney, useNavigation, useOrder, useOrders, useProduct, useProductOptional, useProductSizeChart, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionGroup, useSectionOptional, useShippingRates, useVariantSelection, useWishlist };
3800
+ export { AddToCartButton, Block, CollectionCard, CollectionProvider, CurrencySwitcher, EditableImage, EditableText, Form, HeroMedia, ICON_NAMES, Icon, IconMap, Image, LOGO_SHAPE_OPTIONS, LOGO_SIZE_OPTIONS, Link, LocaleSwitcher, Logo, MAX_BLOCK_DEPTH, Money, NAVIGATE_EVENT, NuMuProvider, ProductCard, ProductProvider, RichText, Section, SectionContext, applyGlobalStyleTokens, applyImageTransform, asImageTransform, assetUrl, availableValues, buildLocaleBundle, buildThemeElement, clearSdkSingleton, collectBlocks, collectSections, computeGlobalStyleTokens, defaultVariant, defineBlock, defineSection, defineThemeEntry, dynamicSource, findVariantByOptions, flattenMessages, focalSrc, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, logoImgStyle, logoStyleTokens, mountTheme, pickTranslations, publishVariantSelection, readVariantSelection, registerReactSingleton, registerSdkSingleton, requestNavigate, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSizeChart, resolveSourcePath, sanitizeHtml, useAnalytics, useApp, useCachedResource, useCart, useCheckout, useCollection, useCollectionOptional, useCurrency, useCurrentTemplate, useCustomerActions, useCustomerAddresses, useGiftCardBalance, useImage, useMoney, useNavigation, useOrder, useOrders, useProduct, useProductOptional, useProductSizeChart, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionGroup, useSectionOptional, useShippingRates, useVariantSelection, useWishlist };
3748
3801
  //# sourceMappingURL=index.mjs.map
3749
3802
  //# sourceMappingURL=index.mjs.map