@numueg/theme-sdk 0.2.1 → 0.2.3

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
@@ -1,9 +1,9 @@
1
- import { S as Store, e as Product, b as Collection, C as Cart, c as Customer, P as Page, g as ProductVariant } from './entities-iiuRSPpk.mjs';
2
- export { A as Address, a as CartItem, O as Order, d as OrderItem, f as ProductImage, h as ProductOption } from './entities-iiuRSPpk.mjs';
1
+ import { S as Store, e as Product, b as Collection, C as Cart, c as Customer, P as Page, g as ProductVariant } from './entities-C8B2U-V0.mjs';
2
+ export { A as Address, a as CartItem, O as Order, d as OrderItem, f as ProductImage, h as ProductOption } from './entities-C8B2U-V0.mjs';
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-D0QybTQS.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-D0QybTQS.mjs';
5
5
  import * as react from 'react';
6
- import { ReactNode, ElementType, ImgHTMLAttributes, AnchorHTMLAttributes, ButtonHTMLAttributes, HTMLAttributes, FormHTMLAttributes, ComponentType } from 'react';
6
+ import { ReactNode, ElementType, CSSProperties, ImgHTMLAttributes, AnchorHTMLAttributes, ButtonHTMLAttributes, HTMLAttributes, FormHTMLAttributes, ComponentType } from 'react';
7
7
  export { resolveThemeSettings } from './normalize.mjs';
8
8
 
9
9
  /**
@@ -1263,6 +1263,78 @@ interface MoneyProps {
1263
1263
  */
1264
1264
  declare function Money({ amount, currency, compareAt, className, as, }: MoneyProps): react.ReactElement<any, string | react.JSXElementConstructor<any>>;
1265
1265
 
1266
+ /**
1267
+ * Non-destructive image framing transform (focal / zoom / rotation).
1268
+ *
1269
+ * An image setting value may carry optional `transform` metadata
1270
+ * (`{ url, alt, transform }`). The original asset is never modified — the
1271
+ * storefront reproduces the framing purely from these numbers via CSS, so the
1272
+ * SAME uploaded image can be framed differently per placement (hero vs card).
1273
+ *
1274
+ * Hoisted into the SDK (was duplicated in every theme's `_shared.ts` and in
1275
+ * the merchant-hub editor's `imageTransform.ts`). The editor copy MUST stay
1276
+ * equivalent so its preview matches the storefront render exactly.
1277
+ */
1278
+ interface ImageTransform {
1279
+ v: 1;
1280
+ focal?: {
1281
+ x: number;
1282
+ y: number;
1283
+ };
1284
+ zoom?: number;
1285
+ rotation?: number;
1286
+ fit?: "cover" | "contain";
1287
+ }
1288
+ /** Read the transform off an image setting value (string | {url,alt,transform}). */
1289
+ declare function asImageTransform(v: unknown): ImageTransform | undefined;
1290
+ /**
1291
+ * CSS reproducing the transform on an `<img>` that fills a fixed-aspect,
1292
+ * overflow-hidden frame. Default fit is `cover` (Shopify-style: the image
1293
+ * fills its frame, cropping to the focal point) — pass `"contain"` only for
1294
+ * placements that must show the whole image (e.g. a logo). Empty object when
1295
+ * there is no transform AND the caller wants the section's own className to
1296
+ * decide; pass an explicit `fit` to force a default even without a transform.
1297
+ */
1298
+ declare function applyImageTransform(t: ImageTransform | undefined | null, fit?: "cover" | "contain"): CSSProperties;
1299
+ /**
1300
+ * focalSrc — build a URL for a server-side, focal-point-aware image transform.
1301
+ *
1302
+ * OPT-IN helper. A theme that wants a bandwidth-efficient SMART CROP for a big
1303
+ * image (typically a hero) calls this to point the <img src> at the storefront's
1304
+ * `/api/image-transform` endpoint with focal/aspect params. The endpoint honors
1305
+ * them only when Cloudflare Image Resizing is enabled on the zone
1306
+ * (NUMU_CF_IMAGE_RESIZING=1); otherwise it gracefully ignores them and serves a
1307
+ * plain resized image — so the theme's CSS `applyImageTransform` framing remains
1308
+ * the correctness baseline either way. This is purely a perf optimization.
1309
+ *
1310
+ * Returns a RELATIVE path (`/api/image-transform?...`) — the SDK runs
1311
+ * same-origin inside the storefront, matching how <Form>/useShop build URLs.
1312
+ *
1313
+ * @example
1314
+ * <img
1315
+ * src={focalSrc(hero.url, { width: 1600, focal: t?.focal, aspect: "16/9" })}
1316
+ * style={applyImageTransform(t, "cover")} // CSS still frames as fallback
1317
+ * />
1318
+ */
1319
+ interface FocalSrcOptions {
1320
+ /** Target width in px (e.g. 1600 for a desktop hero). Required for a crop. */
1321
+ width?: number;
1322
+ /** Focal point, normalized 0..1 (0.5,0.5 = center). */
1323
+ focal?: {
1324
+ x?: number;
1325
+ y?: number;
1326
+ };
1327
+ /** Target aspect ratio "W/H" (e.g. "16/9"); drives the crop box height. */
1328
+ aspect?: string;
1329
+ /** Crop mode. Default "cover". */
1330
+ fit?: "cover" | "contain";
1331
+ /** Quality 1..100. */
1332
+ quality?: number;
1333
+ /** Output format. */
1334
+ format?: "webp" | "avif" | "jpeg" | "jpg" | "png";
1335
+ }
1336
+ declare function focalSrc(url: string | null | undefined, options?: FocalSrcOptions): string;
1337
+
1266
1338
  interface ImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "src" | "srcSet"> {
1267
1339
  src: string | undefined | null;
1268
1340
  alt: string;
@@ -1283,6 +1355,29 @@ interface ImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "src" | "
1283
1355
  * "eager" for above-the-fold imagery.
1284
1356
  */
1285
1357
  loading?: "eager" | "lazy";
1358
+ /**
1359
+ * Shopify-style frame. When set (e.g. "3/4", "16/9", "1/1") the image is
1360
+ * wrapped in a fixed-aspect, overflow-hidden box and the `<img>` fills it.
1361
+ * Combined with `objectFit="cover"` (the default) this guarantees the image
1362
+ * ALWAYS fits its frame — no letterboxing, no overflow — cropping to the
1363
+ * focal point. Omit to render a bare `<img>` (legacy behavior).
1364
+ */
1365
+ aspectRatio?: string;
1366
+ /**
1367
+ * How the image fills its box. Default "cover" (fill + crop) — the merchant
1368
+ * never sees a letterboxed/squashed image. Use "contain" for logos/badges
1369
+ * that must show in full.
1370
+ */
1371
+ objectFit?: "cover" | "contain" | "fill" | "scale-down" | "none";
1372
+ /** CSS object-position (e.g. "50% 50%"). Ignored if `transform` is set. */
1373
+ objectPosition?: string;
1374
+ /**
1375
+ * Non-destructive focal/zoom/rotation metadata from the image setting value
1376
+ * (`asImageTransform(setting)`). When present it drives object-fit +
1377
+ * object-position + scale/rotate so the editor preview and the live render
1378
+ * match exactly. Overrides `objectFit`/`objectPosition`.
1379
+ */
1380
+ transform?: ImageTransform | null;
1286
1381
  }
1287
1382
  /**
1288
1383
  * <Image> — drop-in replacement for `<img>` with srcSet + lazy loading
@@ -1293,7 +1388,7 @@ interface ImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "src" | "
1293
1388
  * If `src` is empty/null, renders a placeholder div so the layout
1294
1389
  * doesn't shift while a merchant configures images in the customizer.
1295
1390
  */
1296
- declare function Image({ src, alt, sizes, responsive, loading, className, style, ...rest }: ImageProps): react.JSX.Element;
1391
+ declare function Image({ src, alt, sizes, responsive, loading, aspectRatio, objectFit, objectPosition, transform, className, style, ...rest }: ImageProps): react.JSX.Element;
1297
1392
 
1298
1393
  interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"> {
1299
1394
  /**
@@ -2097,4 +2192,4 @@ declare function pickTranslations(bundle: LocaleBundle, locale: string): LocaleM
2097
2192
  */
2098
2193
  declare function buildLocaleBundle<T extends Record<string, unknown>>(modules: T): LocaleBundle;
2099
2194
 
2100
- export { AddToCartButton, type AddressInput, type AnalyticsApi, type AnalyticsPayload, type AppManifestBlock, type AppPayload, type AppState, Block, BlockInstance, BlockProps$1 as BlockProps, BlockSchema, Cart, CartContext, type CheckoutAddress, type CheckoutApi, type CheckoutSessionState, type CheckoutStep, Collection, CollectionCard, type CollectionCardProps, type CollectionCardSlots, CollectionContext, CollectionProvider, type CurrencyConfig, type CurrencyState, CurrencySwitcher, type CurrencySwitcherProps, Customer, type CustomerAddress, type CustomerAddressesState, CustomerContext, type DefineBlockInput, type DefineSectionInput, type DefinedBlock, type DefinedSection, type DynamicResolveContext, type DynamicSourceRef, EditableImage, type EditableImageProps, EditableText, type EditableTextProps, Form, type GiftCardBalance, ICON_NAMES, Icon, IconMap, type IconProps, Image, Link, type LocaleBundle, type LocaleMessages, LocaleSwitcher, type LocaleSwitcherProps, LocalizationContext, type MenuItemData, Money, MountResult, NavigationContext, type NavigationItem, type NavigationState, NuMuProvider, type OrderDetail, type OrderListEntry, type OrderListState, type OrderState, Page, PageContext, type PlaceOrderResult, Product, ProductCard, type ProductCardProps, type ProductCardSlots, ProductContext, ProductProvider, ProductVariant, type RelatedProductsState, type ReorderResult, type ReorderSkipReason, type ReorderSkippedItem, RichText, type RichTextProps, type SearchResults, type SearchState, Section, SectionContext, SectionInstance, SectionProps$1 as SectionProps, SectionSchema, type ShippingRateOption, ShopContext, type ShopWithHelpers, Store, type ThemeMountContext, type ThemeMountPage, type ThemeRenderArgs, ThemeSettingsContext, ThemeSettingsV3, type UseGiftCardBalance, type UseReorder, type UseSearchOptions, type UseShippingRatesOptions, type UseShippingRatesState, type UseVariantSelection, type WishlistItem, type WishlistState, applyGlobalStyleTokens, assetUrl, availableValues, buildLocaleBundle, clearSdkSingleton, collectBlocks, collectSections, defaultVariant, defineBlock, defineSection, dynamicSource, findVariantByOptions, flattenMessages, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, mountTheme, pickTranslations, registerReactSingleton, registerSdkSingleton, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSourcePath, sanitizeHtml, useAnalytics, useApp, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCurrentTemplate, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useLocale, useLocalization, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };
2195
+ export { AddToCartButton, type AddressInput, type AnalyticsApi, type AnalyticsPayload, type AppManifestBlock, type AppPayload, type AppState, Block, BlockInstance, BlockProps$1 as BlockProps, BlockSchema, Cart, CartContext, type CheckoutAddress, type CheckoutApi, type CheckoutSessionState, type CheckoutStep, Collection, CollectionCard, type CollectionCardProps, type CollectionCardSlots, CollectionContext, CollectionProvider, type CurrencyConfig, type CurrencyState, CurrencySwitcher, type CurrencySwitcherProps, Customer, type CustomerAddress, type CustomerAddressesState, CustomerContext, type DefineBlockInput, type DefineSectionInput, type DefinedBlock, type DefinedSection, type DynamicResolveContext, type DynamicSourceRef, EditableImage, type EditableImageProps, EditableText, type EditableTextProps, type FocalSrcOptions, Form, type GiftCardBalance, ICON_NAMES, Icon, IconMap, type IconProps, Image, type ImageTransform, Link, type LocaleBundle, type LocaleMessages, LocaleSwitcher, type LocaleSwitcherProps, LocalizationContext, type MenuItemData, Money, MountResult, NavigationContext, type NavigationItem, type NavigationState, NuMuProvider, type OrderDetail, type OrderListEntry, type OrderListState, type OrderState, Page, PageContext, type PlaceOrderResult, Product, ProductCard, type ProductCardProps, type ProductCardSlots, ProductContext, ProductProvider, ProductVariant, type RelatedProductsState, type ReorderResult, type ReorderSkipReason, type ReorderSkippedItem, RichText, type RichTextProps, type SearchResults, type SearchState, Section, SectionContext, SectionInstance, SectionProps$1 as SectionProps, SectionSchema, type ShippingRateOption, ShopContext, type ShopWithHelpers, Store, type ThemeMountContext, type ThemeMountPage, type ThemeRenderArgs, ThemeSettingsContext, ThemeSettingsV3, 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, defaultVariant, defineBlock, defineSection, dynamicSource, findVariantByOptions, flattenMessages, focalSrc, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, mountTheme, pickTranslations, registerReactSingleton, registerSdkSingleton, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSourcePath, sanitizeHtml, useAnalytics, useApp, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCurrentTemplate, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useLocale, useLocalization, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { S as Store, e as Product, b as Collection, C as Cart, c as Customer, P as Page, g as ProductVariant } from './entities-iiuRSPpk.js';
2
- export { A as Address, a as CartItem, O as Order, d as OrderItem, f as ProductImage, h as ProductOption } from './entities-iiuRSPpk.js';
1
+ import { S as Store, e as Product, b as Collection, C as Cart, c as Customer, P as Page, g as ProductVariant } from './entities-C8B2U-V0.js';
2
+ export { A as Address, a as CartItem, O as Order, d as OrderItem, f as ProductImage, h as ProductOption } from './entities-C8B2U-V0.js';
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-D0QybTQS.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-D0QybTQS.js';
5
5
  import * as react from 'react';
6
- import { ReactNode, ElementType, ImgHTMLAttributes, AnchorHTMLAttributes, ButtonHTMLAttributes, HTMLAttributes, FormHTMLAttributes, ComponentType } from 'react';
6
+ import { ReactNode, ElementType, CSSProperties, ImgHTMLAttributes, AnchorHTMLAttributes, ButtonHTMLAttributes, HTMLAttributes, FormHTMLAttributes, ComponentType } from 'react';
7
7
  export { resolveThemeSettings } from './normalize.js';
8
8
 
9
9
  /**
@@ -1263,6 +1263,78 @@ interface MoneyProps {
1263
1263
  */
1264
1264
  declare function Money({ amount, currency, compareAt, className, as, }: MoneyProps): react.ReactElement<any, string | react.JSXElementConstructor<any>>;
1265
1265
 
1266
+ /**
1267
+ * Non-destructive image framing transform (focal / zoom / rotation).
1268
+ *
1269
+ * An image setting value may carry optional `transform` metadata
1270
+ * (`{ url, alt, transform }`). The original asset is never modified — the
1271
+ * storefront reproduces the framing purely from these numbers via CSS, so the
1272
+ * SAME uploaded image can be framed differently per placement (hero vs card).
1273
+ *
1274
+ * Hoisted into the SDK (was duplicated in every theme's `_shared.ts` and in
1275
+ * the merchant-hub editor's `imageTransform.ts`). The editor copy MUST stay
1276
+ * equivalent so its preview matches the storefront render exactly.
1277
+ */
1278
+ interface ImageTransform {
1279
+ v: 1;
1280
+ focal?: {
1281
+ x: number;
1282
+ y: number;
1283
+ };
1284
+ zoom?: number;
1285
+ rotation?: number;
1286
+ fit?: "cover" | "contain";
1287
+ }
1288
+ /** Read the transform off an image setting value (string | {url,alt,transform}). */
1289
+ declare function asImageTransform(v: unknown): ImageTransform | undefined;
1290
+ /**
1291
+ * CSS reproducing the transform on an `<img>` that fills a fixed-aspect,
1292
+ * overflow-hidden frame. Default fit is `cover` (Shopify-style: the image
1293
+ * fills its frame, cropping to the focal point) — pass `"contain"` only for
1294
+ * placements that must show the whole image (e.g. a logo). Empty object when
1295
+ * there is no transform AND the caller wants the section's own className to
1296
+ * decide; pass an explicit `fit` to force a default even without a transform.
1297
+ */
1298
+ declare function applyImageTransform(t: ImageTransform | undefined | null, fit?: "cover" | "contain"): CSSProperties;
1299
+ /**
1300
+ * focalSrc — build a URL for a server-side, focal-point-aware image transform.
1301
+ *
1302
+ * OPT-IN helper. A theme that wants a bandwidth-efficient SMART CROP for a big
1303
+ * image (typically a hero) calls this to point the <img src> at the storefront's
1304
+ * `/api/image-transform` endpoint with focal/aspect params. The endpoint honors
1305
+ * them only when Cloudflare Image Resizing is enabled on the zone
1306
+ * (NUMU_CF_IMAGE_RESIZING=1); otherwise it gracefully ignores them and serves a
1307
+ * plain resized image — so the theme's CSS `applyImageTransform` framing remains
1308
+ * the correctness baseline either way. This is purely a perf optimization.
1309
+ *
1310
+ * Returns a RELATIVE path (`/api/image-transform?...`) — the SDK runs
1311
+ * same-origin inside the storefront, matching how <Form>/useShop build URLs.
1312
+ *
1313
+ * @example
1314
+ * <img
1315
+ * src={focalSrc(hero.url, { width: 1600, focal: t?.focal, aspect: "16/9" })}
1316
+ * style={applyImageTransform(t, "cover")} // CSS still frames as fallback
1317
+ * />
1318
+ */
1319
+ interface FocalSrcOptions {
1320
+ /** Target width in px (e.g. 1600 for a desktop hero). Required for a crop. */
1321
+ width?: number;
1322
+ /** Focal point, normalized 0..1 (0.5,0.5 = center). */
1323
+ focal?: {
1324
+ x?: number;
1325
+ y?: number;
1326
+ };
1327
+ /** Target aspect ratio "W/H" (e.g. "16/9"); drives the crop box height. */
1328
+ aspect?: string;
1329
+ /** Crop mode. Default "cover". */
1330
+ fit?: "cover" | "contain";
1331
+ /** Quality 1..100. */
1332
+ quality?: number;
1333
+ /** Output format. */
1334
+ format?: "webp" | "avif" | "jpeg" | "jpg" | "png";
1335
+ }
1336
+ declare function focalSrc(url: string | null | undefined, options?: FocalSrcOptions): string;
1337
+
1266
1338
  interface ImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "src" | "srcSet"> {
1267
1339
  src: string | undefined | null;
1268
1340
  alt: string;
@@ -1283,6 +1355,29 @@ interface ImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "src" | "
1283
1355
  * "eager" for above-the-fold imagery.
1284
1356
  */
1285
1357
  loading?: "eager" | "lazy";
1358
+ /**
1359
+ * Shopify-style frame. When set (e.g. "3/4", "16/9", "1/1") the image is
1360
+ * wrapped in a fixed-aspect, overflow-hidden box and the `<img>` fills it.
1361
+ * Combined with `objectFit="cover"` (the default) this guarantees the image
1362
+ * ALWAYS fits its frame — no letterboxing, no overflow — cropping to the
1363
+ * focal point. Omit to render a bare `<img>` (legacy behavior).
1364
+ */
1365
+ aspectRatio?: string;
1366
+ /**
1367
+ * How the image fills its box. Default "cover" (fill + crop) — the merchant
1368
+ * never sees a letterboxed/squashed image. Use "contain" for logos/badges
1369
+ * that must show in full.
1370
+ */
1371
+ objectFit?: "cover" | "contain" | "fill" | "scale-down" | "none";
1372
+ /** CSS object-position (e.g. "50% 50%"). Ignored if `transform` is set. */
1373
+ objectPosition?: string;
1374
+ /**
1375
+ * Non-destructive focal/zoom/rotation metadata from the image setting value
1376
+ * (`asImageTransform(setting)`). When present it drives object-fit +
1377
+ * object-position + scale/rotate so the editor preview and the live render
1378
+ * match exactly. Overrides `objectFit`/`objectPosition`.
1379
+ */
1380
+ transform?: ImageTransform | null;
1286
1381
  }
1287
1382
  /**
1288
1383
  * <Image> — drop-in replacement for `<img>` with srcSet + lazy loading
@@ -1293,7 +1388,7 @@ interface ImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "src" | "
1293
1388
  * If `src` is empty/null, renders a placeholder div so the layout
1294
1389
  * doesn't shift while a merchant configures images in the customizer.
1295
1390
  */
1296
- declare function Image({ src, alt, sizes, responsive, loading, className, style, ...rest }: ImageProps): react.JSX.Element;
1391
+ declare function Image({ src, alt, sizes, responsive, loading, aspectRatio, objectFit, objectPosition, transform, className, style, ...rest }: ImageProps): react.JSX.Element;
1297
1392
 
1298
1393
  interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"> {
1299
1394
  /**
@@ -2097,4 +2192,4 @@ declare function pickTranslations(bundle: LocaleBundle, locale: string): LocaleM
2097
2192
  */
2098
2193
  declare function buildLocaleBundle<T extends Record<string, unknown>>(modules: T): LocaleBundle;
2099
2194
 
2100
- export { AddToCartButton, type AddressInput, type AnalyticsApi, type AnalyticsPayload, type AppManifestBlock, type AppPayload, type AppState, Block, BlockInstance, BlockProps$1 as BlockProps, BlockSchema, Cart, CartContext, type CheckoutAddress, type CheckoutApi, type CheckoutSessionState, type CheckoutStep, Collection, CollectionCard, type CollectionCardProps, type CollectionCardSlots, CollectionContext, CollectionProvider, type CurrencyConfig, type CurrencyState, CurrencySwitcher, type CurrencySwitcherProps, Customer, type CustomerAddress, type CustomerAddressesState, CustomerContext, type DefineBlockInput, type DefineSectionInput, type DefinedBlock, type DefinedSection, type DynamicResolveContext, type DynamicSourceRef, EditableImage, type EditableImageProps, EditableText, type EditableTextProps, Form, type GiftCardBalance, ICON_NAMES, Icon, IconMap, type IconProps, Image, Link, type LocaleBundle, type LocaleMessages, LocaleSwitcher, type LocaleSwitcherProps, LocalizationContext, type MenuItemData, Money, MountResult, NavigationContext, type NavigationItem, type NavigationState, NuMuProvider, type OrderDetail, type OrderListEntry, type OrderListState, type OrderState, Page, PageContext, type PlaceOrderResult, Product, ProductCard, type ProductCardProps, type ProductCardSlots, ProductContext, ProductProvider, ProductVariant, type RelatedProductsState, type ReorderResult, type ReorderSkipReason, type ReorderSkippedItem, RichText, type RichTextProps, type SearchResults, type SearchState, Section, SectionContext, SectionInstance, SectionProps$1 as SectionProps, SectionSchema, type ShippingRateOption, ShopContext, type ShopWithHelpers, Store, type ThemeMountContext, type ThemeMountPage, type ThemeRenderArgs, ThemeSettingsContext, ThemeSettingsV3, type UseGiftCardBalance, type UseReorder, type UseSearchOptions, type UseShippingRatesOptions, type UseShippingRatesState, type UseVariantSelection, type WishlistItem, type WishlistState, applyGlobalStyleTokens, assetUrl, availableValues, buildLocaleBundle, clearSdkSingleton, collectBlocks, collectSections, defaultVariant, defineBlock, defineSection, dynamicSource, findVariantByOptions, flattenMessages, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, mountTheme, pickTranslations, registerReactSingleton, registerSdkSingleton, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSourcePath, sanitizeHtml, useAnalytics, useApp, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCurrentTemplate, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useLocale, useLocalization, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };
2195
+ export { AddToCartButton, type AddressInput, type AnalyticsApi, type AnalyticsPayload, type AppManifestBlock, type AppPayload, type AppState, Block, BlockInstance, BlockProps$1 as BlockProps, BlockSchema, Cart, CartContext, type CheckoutAddress, type CheckoutApi, type CheckoutSessionState, type CheckoutStep, Collection, CollectionCard, type CollectionCardProps, type CollectionCardSlots, CollectionContext, CollectionProvider, type CurrencyConfig, type CurrencyState, CurrencySwitcher, type CurrencySwitcherProps, Customer, type CustomerAddress, type CustomerAddressesState, CustomerContext, type DefineBlockInput, type DefineSectionInput, type DefinedBlock, type DefinedSection, type DynamicResolveContext, type DynamicSourceRef, EditableImage, type EditableImageProps, EditableText, type EditableTextProps, type FocalSrcOptions, Form, type GiftCardBalance, ICON_NAMES, Icon, IconMap, type IconProps, Image, type ImageTransform, Link, type LocaleBundle, type LocaleMessages, LocaleSwitcher, type LocaleSwitcherProps, LocalizationContext, type MenuItemData, Money, MountResult, NavigationContext, type NavigationItem, type NavigationState, NuMuProvider, type OrderDetail, type OrderListEntry, type OrderListState, type OrderState, Page, PageContext, type PlaceOrderResult, Product, ProductCard, type ProductCardProps, type ProductCardSlots, ProductContext, ProductProvider, ProductVariant, type RelatedProductsState, type ReorderResult, type ReorderSkipReason, type ReorderSkippedItem, RichText, type RichTextProps, type SearchResults, type SearchState, Section, SectionContext, SectionInstance, SectionProps$1 as SectionProps, SectionSchema, type ShippingRateOption, ShopContext, type ShopWithHelpers, Store, type ThemeMountContext, type ThemeMountPage, type ThemeRenderArgs, ThemeSettingsContext, ThemeSettingsV3, 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, defaultVariant, defineBlock, defineSection, dynamicSource, findVariantByOptions, flattenMessages, focalSrc, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, mountTheme, pickTranslations, registerReactSingleton, registerSdkSingleton, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSourcePath, sanitizeHtml, useAnalytics, useApp, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCurrentTemplate, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useLocale, useLocalization, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };
package/dist/index.mjs CHANGED
@@ -1455,6 +1455,12 @@ function normalizeCartFromServer(cart) {
1455
1455
  items: Array.isArray(cart.items) ? cart.items.map((it) => ({ ...it, price: toMajor(it.price) })) : []
1456
1456
  };
1457
1457
  }
1458
+ function unwrapCart(json) {
1459
+ if (json && typeof json === "object" && "data" in json && json.data && typeof json.data === "object") {
1460
+ return json.data;
1461
+ }
1462
+ return json;
1463
+ }
1458
1464
  function readCsrfCookie() {
1459
1465
  if (typeof document === "undefined") return null;
1460
1466
  const match = document.cookie.match(/(?:^|;\s*)numu_csrf=([^;]+)/);
@@ -1474,8 +1480,8 @@ async function postCartMutation(endpoint, body, applyCart, reserveToken) {
1474
1480
  body: body === void 0 ? void 0 : JSON.stringify(body)
1475
1481
  });
1476
1482
  if (!res.ok) return;
1477
- const data = await res.json();
1478
- applyCart(data);
1483
+ const json = await res.json();
1484
+ applyCart(unwrapCart(json));
1479
1485
  }
1480
1486
  function NuMuProvider({
1481
1487
  store,
@@ -1655,7 +1661,8 @@ function NuMuProvider({
1655
1661
  cache: "no-store"
1656
1662
  });
1657
1663
  if (!res.ok || cancelled) return;
1658
- const data = await res.json();
1664
+ const json = await res.json();
1665
+ const data = unwrapCart(json);
1659
1666
  if (data && typeof data === "object") {
1660
1667
  setCart(normalizeCartFromServer(data));
1661
1668
  }
@@ -1702,11 +1709,32 @@ function NuMuProvider({
1702
1709
  );
1703
1710
  const addItem = useCallback(
1704
1711
  async (productId, variantId, quantity) => {
1712
+ const eventId = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}`;
1713
+ const qty = quantity || 1;
1705
1714
  await mutate("/api/cart/add", {
1706
1715
  product_id: productId,
1707
1716
  variant_id: variantId,
1708
- quantity: quantity || 1
1717
+ quantity: qty,
1718
+ _event_id: eventId
1709
1719
  });
1720
+ try {
1721
+ if (typeof window !== "undefined") {
1722
+ window.dispatchEvent(
1723
+ new CustomEvent("numu:analytics:event", {
1724
+ detail: {
1725
+ event: "add_to_cart",
1726
+ payload: {
1727
+ content_ids: [productId],
1728
+ content_type: "product",
1729
+ num_items: qty
1730
+ },
1731
+ event_id: eventId
1732
+ }
1733
+ })
1734
+ );
1735
+ }
1736
+ } catch {
1737
+ }
1710
1738
  },
1711
1739
  [mutate]
1712
1740
  );
@@ -2131,6 +2159,46 @@ function Money({
2131
2159
  children
2132
2160
  );
2133
2161
  }
2162
+
2163
+ // src/utils/imageTransform.ts
2164
+ var _clampT = (n, lo, hi) => Math.min(hi, Math.max(lo, Number.isFinite(n) ? n : lo));
2165
+ function asImageTransform(v) {
2166
+ if (v && typeof v === "object" && "transform" in v) {
2167
+ const t = v.transform;
2168
+ if (t && typeof t === "object") return t;
2169
+ }
2170
+ return void 0;
2171
+ }
2172
+ function applyImageTransform(t, fit = "cover") {
2173
+ if (!t) return { objectFit: fit };
2174
+ const fx = Math.round(_clampT(t.focal?.x ?? 0.5, 0, 1) * 1e4) / 100;
2175
+ const fy = Math.round(_clampT(t.focal?.y ?? 0.5, 0, 1) * 1e4) / 100;
2176
+ const zoom = _clampT(t.zoom ?? 1, 1, 4);
2177
+ const rot = ((t.rotation ?? 0) % 360 + 360) % 360;
2178
+ const effFit = t.fit ?? fit;
2179
+ const style = {
2180
+ transform: `scale(${zoom}) rotate(${rot}deg)`,
2181
+ transformOrigin: `${fx}% ${fy}%`,
2182
+ objectFit: effFit
2183
+ };
2184
+ if (effFit === "cover") style.objectPosition = `${fx}% ${fy}%`;
2185
+ return style;
2186
+ }
2187
+ var clamp01 = (n) => Math.min(1, Math.max(0, n));
2188
+ function focalSrc(url, options = {}) {
2189
+ if (!url) return "";
2190
+ if (url.startsWith("data:") || /[?&](fp-x|fp-y)=/.test(url)) return url;
2191
+ const p = new URLSearchParams();
2192
+ p.set("url", url);
2193
+ if (options.width) p.set("w", String(Math.round(options.width)));
2194
+ if (options.focal?.x != null) p.set("fp-x", String(clamp01(options.focal.x)));
2195
+ if (options.focal?.y != null) p.set("fp-y", String(clamp01(options.focal.y)));
2196
+ if (options.aspect) p.set("ar", options.aspect);
2197
+ if (options.fit) p.set("fit", options.fit);
2198
+ if (options.quality) p.set("q", String(Math.min(100, Math.max(1, Math.round(options.quality)))));
2199
+ if (options.format) p.set("f", options.format.toLowerCase());
2200
+ return `/api/image-transform?${p.toString()}`;
2201
+ }
2134
2202
  var DEFAULT_WIDTHS2 = [320, 480, 640, 768, 1024, 1280, 1600, 1920];
2135
2203
  function buildSrcSet(src, widths = DEFAULT_WIDTHS2) {
2136
2204
  if (/[?&]w=\d+/.test(src)) return "";
@@ -2143,27 +2211,48 @@ function Image({
2143
2211
  sizes = "(min-width: 1024px) 25vw, (min-width: 640px) 50vw, 100vw",
2144
2212
  responsive = true,
2145
2213
  loading = "lazy",
2214
+ aspectRatio,
2215
+ objectFit,
2216
+ objectPosition,
2217
+ transform,
2146
2218
  className,
2147
2219
  style,
2148
2220
  ...rest
2149
2221
  }) {
2222
+ const framed = Boolean(aspectRatio);
2150
2223
  if (!src) {
2151
- return /* @__PURE__ */ jsx(
2224
+ const placeholder = /* @__PURE__ */ jsx(
2152
2225
  "div",
2153
2226
  {
2154
- className,
2227
+ className: framed ? void 0 : className,
2155
2228
  role: "img",
2156
2229
  "aria-label": alt,
2157
2230
  style: {
2158
2231
  backgroundColor: "rgba(0,0,0,0.05)",
2159
2232
  display: "block",
2160
- ...style
2233
+ width: "100%",
2234
+ height: framed ? "100%" : void 0,
2235
+ ...framed ? {} : style
2161
2236
  }
2162
2237
  }
2163
2238
  );
2239
+ if (!framed) return placeholder;
2240
+ return /* @__PURE__ */ jsx(
2241
+ "span",
2242
+ {
2243
+ className,
2244
+ style: { display: "block", aspectRatio, overflow: "hidden", ...style },
2245
+ children: placeholder
2246
+ }
2247
+ );
2164
2248
  }
2165
2249
  const srcSet = responsive ? buildSrcSet(src) : void 0;
2166
- return /* @__PURE__ */ jsx(
2250
+ const effFit = objectFit ?? (framed ? "cover" : void 0);
2251
+ const fitStyle = transform ? applyImageTransform(transform, effFit === "contain" ? "contain" : "cover") : {
2252
+ ...effFit ? { objectFit: effFit } : {},
2253
+ ...objectPosition ? { objectPosition } : {}
2254
+ };
2255
+ const img = /* @__PURE__ */ jsx(
2167
2256
  "img",
2168
2257
  {
2169
2258
  src,
@@ -2172,11 +2261,27 @@ function Image({
2172
2261
  sizes: srcSet ? sizes : void 0,
2173
2262
  loading,
2174
2263
  decoding: "async",
2175
- className,
2176
- style,
2264
+ className: framed ? void 0 : className,
2265
+ style: framed ? { width: "100%", height: "100%", display: "block", ...fitStyle } : { ...fitStyle, ...style },
2177
2266
  ...rest
2178
2267
  }
2179
2268
  );
2269
+ if (!framed) return img;
2270
+ return /* @__PURE__ */ jsx(
2271
+ "span",
2272
+ {
2273
+ className,
2274
+ style: {
2275
+ display: "block",
2276
+ position: "relative",
2277
+ width: "100%",
2278
+ aspectRatio,
2279
+ overflow: "hidden",
2280
+ ...style
2281
+ },
2282
+ children: img
2283
+ }
2284
+ );
2180
2285
  }
2181
2286
  var ABSOLUTE_URL = /^[a-z]+:|^\/\//i;
2182
2287
  function Link({ to, children, ...rest }) {
@@ -3376,6 +3481,6 @@ function buildLocaleBundle(modules) {
3376
3481
  return bundle;
3377
3482
  }
3378
3483
 
3379
- export { AddToCartButton, Block, CartContext, CollectionCard, CollectionContext, CollectionProvider, CurrencySwitcher, CustomerContext, EditableImage, EditableText, Form, ICON_NAMES, Icon, IconMap, Image, Link, LocaleSwitcher, LocalizationContext, MAX_BLOCK_DEPTH, Money, NavigationContext, NuMuProvider, PageContext, ProductCard, ProductContext, ProductProvider, RichText, Section, SectionContext, ShopContext, ThemeSettingsContext, applyGlobalStyleTokens, assetUrl, availableValues, buildLocaleBundle, clearSdkSingleton, collectBlocks, collectSections, defaultVariant, defineBlock, defineSection, dynamicSource, findVariantByOptions, flattenMessages, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, mountTheme, pickTranslations, registerReactSingleton, registerSdkSingleton, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSourcePath, resolveThemeSettings, sanitizeHtml, useAnalytics, useApp, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCurrentTemplate, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useLocale, useLocalization, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };
3484
+ export { AddToCartButton, Block, CartContext, CollectionCard, CollectionContext, CollectionProvider, CurrencySwitcher, CustomerContext, EditableImage, EditableText, Form, ICON_NAMES, Icon, IconMap, Image, Link, LocaleSwitcher, LocalizationContext, MAX_BLOCK_DEPTH, Money, NavigationContext, NuMuProvider, PageContext, ProductCard, ProductContext, ProductProvider, RichText, Section, SectionContext, ShopContext, ThemeSettingsContext, applyGlobalStyleTokens, applyImageTransform, asImageTransform, assetUrl, availableValues, buildLocaleBundle, clearSdkSingleton, collectBlocks, collectSections, defaultVariant, defineBlock, defineSection, dynamicSource, findVariantByOptions, flattenMessages, focalSrc, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, mountTheme, pickTranslations, registerReactSingleton, registerSdkSingleton, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSourcePath, resolveThemeSettings, sanitizeHtml, useAnalytics, useApp, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCurrentTemplate, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useLocale, useLocalization, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };
3380
3485
  //# sourceMappingURL=index.mjs.map
3381
3486
  //# sourceMappingURL=index.mjs.map