@numueg/theme-sdk 0.1.2 → 0.2.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
@@ -1,10 +1,9 @@
1
- import { S as Store, P as Product, C as Collection, a as Cart, b as Customer, c as Page, d as ProductVariant } from './index-zSb0FIyh.mjs';
2
- export { A as Address, e as CartItem, O as Order, f as OrderItem, g as ProductImage, h as ProductOption } from './index-zSb0FIyh.mjs';
3
- import { T as ThemeSettingsV3, S as SectionInstance, B as BlockSchema, a as BlockProps$1, b as SectionSchema, c as SectionProps$1 } from './theme-NWJAU9jd.mjs';
4
- export { d as BlockInstance, E as ExternalThemeMetadata, P as PageTemplate, e as SectionGroup, f as SectionPreset, g as SettingDefinition } from './theme-NWJAU9jd.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-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';
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
+ 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
6
  import { ReactNode, ElementType, ImgHTMLAttributes, AnchorHTMLAttributes, ButtonHTMLAttributes, HTMLAttributes, FormHTMLAttributes, ComponentType } from 'react';
7
- import * as react_jsx_runtime from 'react/jsx-runtime';
8
7
  export { resolveThemeSettings } from './normalize.mjs';
9
8
 
10
9
  /**
@@ -57,6 +56,32 @@ declare function useCustomer(): Customer | null;
57
56
 
58
57
  declare function useThemeSettings(): ThemeSettingsV3;
59
58
 
59
+ /**
60
+ * useCurrentTemplate — read the active page template's id.
61
+ *
62
+ * Values are the same strings used as keys in
63
+ * `themeSettings.templates.<id>`:
64
+ *
65
+ * "home" | "product" | "collection" | "cart" | "checkout"
66
+ * "order-confirmation" | "profile" | "page" | "404" | "password"
67
+ *
68
+ * Themes use this to render the matching section list:
69
+ *
70
+ * const template = useCurrentTemplate();
71
+ * const settings = useThemeSettings();
72
+ * const sections = settings.templates?.[template]?.sections ?? {};
73
+ *
74
+ * Default: "home" — when the host hasn't supplied a CurrentTemplate
75
+ * provider, themes still render their home template instead of
76
+ * crashing or showing a blank screen.
77
+ *
78
+ * The hook is read-only; the active template changes via the host
79
+ * navigating between Next.js routes (each route's `page.tsx` wraps
80
+ * its content with the correct currentTemplate at the
81
+ * `<NuMuProvider currentTemplate="…">` prop).
82
+ */
83
+ declare function useCurrentTemplate(): string;
84
+
60
85
  interface LocalizationState {
61
86
  locale: string;
62
87
  direction: "ltr" | "rtl";
@@ -105,6 +130,35 @@ declare const CustomerContext: react.Context<Customer | null>;
105
130
  declare const ThemeSettingsContext: react.Context<ThemeSettingsV3 | null>;
106
131
  declare const LocalizationContext: react.Context<LocalizationState | null>;
107
132
  declare const PageContext: react.Context<Page | null>;
133
+ /**
134
+ * A merchant-managed navigation menu item, exactly as the storefront
135
+ * menus resolver returns it (`GET /storefront/store/{id}/menus`):
136
+ * bilingual `label`, a pre-resolved `url`, and nested `children`.
137
+ *
138
+ * This is the RAW shape the host injects via `NuMuProvider`'s
139
+ * `navigation` prop. `useNavigation(handle)` localizes it to the
140
+ * display-ready `NavigationItem` (a single `title` string for the
141
+ * active locale).
142
+ */
143
+ interface MenuItemData {
144
+ id: string;
145
+ label: Record<string, string>;
146
+ url: string;
147
+ type?: string | null;
148
+ resource_id?: string | null;
149
+ children?: MenuItemData[];
150
+ }
151
+ /**
152
+ * Phase 2.4 — navigation menus keyed by handle (`main-menu`, `footer`,
153
+ * plus custom), injected by the host from the storefront resolver so a
154
+ * theme's `useNavigation(handle)` resolves without a client round-trip.
155
+ *
156
+ * Defaults to `{}` — an empty map signals "host provided no menus", at
157
+ * which point `useNavigation` falls back to its own fetch / a theme's
158
+ * `DEFAULT_NAV`. A present-but-handle-absent map means the menu simply
159
+ * doesn't exist (render nothing / fallback), no fetch attempted.
160
+ */
161
+ declare const NavigationContext: react.Context<Record<string, MenuItemData[]>>;
108
162
 
109
163
  declare function useLocalization(): LocalizationState;
110
164
  declare function useDirection(): "ltr" | "rtl";
@@ -443,19 +497,17 @@ interface NavigationState {
443
497
  /**
444
498
  * Fetch a merchant-managed nav menu by handle.
445
499
  *
446
- * Backend contract: GET /api/storefront/navigation/{handle} →
447
- * { items: NavigationItem[] }
448
- * On 404 / network error / non-OK response the hook returns an empty
449
- * list the calling theme decides whether to render nothing or show
450
- * a fallback. We deliberately don't throw because a missing menu is
451
- * a soft failure (theme should still render).
452
- *
453
- * Why no SSR pre-fetch:
454
- * The storefront's [domain]/layout doesn't currently inject menus
455
- * into `page.data.navigation`. Once it does, themes can pass an
456
- * `initialItems` prop (added below) and skip the round-trip; until
457
- * then we fetch on mount with a process-local cache so the same
458
- * handle doesn't fetch twice per session.
500
+ * Resolution order (Phase 2.4):
501
+ * 1. Host-injected menus — the storefront resolves menus server-side
502
+ * (`GET /storefront/store/{id}/menus`) and injects them via
503
+ * `NuMuProvider`'s `navigation` prop `NavigationContext`. When the
504
+ * handle is present we localize + return it synchronously, no fetch.
505
+ * A non-empty map that lacks the handle means "no such menu" → [].
506
+ * 2. `options.initialItems` — a theme that pre-fetched its own list.
507
+ * 3. Client fetch of `GET /api/storefront/navigation/{handle}` — the
508
+ * legacy fallback for hosts that inject nothing. On 404 / network
509
+ * error the hook resolves to [] (a missing menu is a soft failure;
510
+ * the theme falls back to its own DEFAULT_NAV).
459
511
  */
460
512
  declare function useNavigation(handle: string, options?: {
461
513
  initialItems?: NavigationItem[];
@@ -511,11 +563,11 @@ declare function useSearch(query: string, options?: UseSearchOptions): SearchSta
511
563
  *
512
564
  * Two delivery channels, fired in parallel:
513
565
  * 1. Server-side track: POST /api/storefront/track with the event
514
- * payload. The backend fans out to merchant-configured pixels
515
- * (GA4 Measurement Protocol, Meta CAPI, TikTok Events API). The
516
- * server-side fanout lands in Phase 4 — until then the endpoint
517
- * either no-ops or returns 404, which is fine; we don't await
518
- * the response because failed pixels must never block the UI.
566
+ * payload. The host storefront proxies to FastAPI's funnel-event
567
+ * endpoint (/storefront/store/{id}/track) and fans out to
568
+ * merchant-configured pixels (GA4 Measurement Protocol, Meta CAPI,
569
+ * TikTok Events API). Failed pixels never block the UI we
570
+ * don't await the response.
519
571
  * 2. Window CustomEvent (`numu:analytics:event`): so theme devs can
520
572
  * wire their own GTM container, third-party SDK, or in-house
521
573
  * pixel without going through the server. Detail shape mirrors
@@ -527,6 +579,17 @@ declare function useSearch(query: string, options?: UseSearchOptions): SearchSta
527
579
  * add_shipping_info, purchase, refund, sign_up, login,
528
580
  * add_to_wishlist, share. Custom names are also fine — the dispatcher
529
581
  * does not validate, so themes can ship store-specific events.
582
+ *
583
+ * Funnel-event mapping
584
+ * --------------------
585
+ * Standard event names that map to the backend's funnel steps are
586
+ * dispatched with the full funnel-event payload shape (path,
587
+ * session_fingerprint, attribution envelope from
588
+ * window.__numu_attribution). This is what powers the merchant
589
+ * funnel / journey / multi-touch attribution dashboards. Events with
590
+ * no funnel mapping still POST with the loose `{event, payload}`
591
+ * shape — the proxy forwards them so the pixel-fanout layer still
592
+ * fires.
530
593
  */
531
594
  interface AnalyticsPayload {
532
595
  /** Standard or custom event name. Lowercase + underscores recommended. */
@@ -539,6 +602,32 @@ interface AnalyticsApi {
539
602
  */
540
603
  track: (eventName: string, payload?: AnalyticsPayload) => void;
541
604
  }
605
+ interface AttributionEnvelope {
606
+ v?: number;
607
+ first_touch?: unknown;
608
+ last_touch?: unknown;
609
+ session_id?: string | null;
610
+ }
611
+ interface NumuAttributionBridge {
612
+ get(): AttributionEnvelope | null;
613
+ }
614
+ interface NumuCustomerBridge {
615
+ /** Authenticated customer's UUID, or null when the visitor is a guest. */
616
+ getId(): string | null;
617
+ }
618
+ declare global {
619
+ interface Window {
620
+ __numu_attribution?: NumuAttributionBridge;
621
+ /**
622
+ * Optional bridge installed by the host storefront's customer
623
+ * provider. When set, the SDK includes ``customer_id`` on
624
+ * funnel-step events so the backend can record touches against
625
+ * the authenticated customer immediately instead of waiting for
626
+ * the next checkout to backfill.
627
+ */
628
+ __numu_customer?: NumuCustomerBridge;
629
+ }
630
+ }
542
631
  declare function useAnalytics(): AnalyticsApi;
543
632
 
544
633
  /**
@@ -983,6 +1072,115 @@ interface UseShippingRatesState {
983
1072
  }
984
1073
  declare function useShippingRates({ address, location_id, enabled, }?: UseShippingRatesOptions): UseShippingRatesState;
985
1074
 
1075
+ /**
1076
+ * `mountTheme(el, ctx, renderApp)` — the canonical V3 bundle entry helper.
1077
+ *
1078
+ * ## Why this exists
1079
+ *
1080
+ * Every theme bundle exports `mount(el, ctx): MountResult`. Historically each
1081
+ * theme hand-wrote that function, and the wiring it needs is non-trivial and
1082
+ * easy to get wrong:
1083
+ *
1084
+ * 1. **Forward the real catalog.** The host ships the page's products /
1085
+ * collections in `ctx.page.data` (home + listing routes) and the product
1086
+ * in `ctx.page.data.product` (PDP). A theme that doesn't pass these into
1087
+ * `NuMuProvider` (+ wrap the PDP in `<ProductProvider>`) sees
1088
+ * `useProducts()` / `useProductOptional()` return empty on a REAL store —
1089
+ * so product sections render "No products yet" or fall back to demo data
1090
+ * on a stocked merchant. This was the single most common BYOT bug:
1091
+ * only bon-younes wired it; the other 13 themes dropped the catalog.
1092
+ *
1093
+ * 2. **Apply global style tokens.** Merchant-chosen colors/fonts live in
1094
+ * `themeSettings.global_settings`; they only paint if the bundle calls
1095
+ * `applyGlobalStyleTokens` on its mount root (and resolves font tokens
1096
+ * to real stacks + injects the webfont link). Themes that skipped this
1097
+ * ignored every color/font picker.
1098
+ *
1099
+ * 3. **Forward navigation.** `useNavigation(handle)` only resolves the
1100
+ * header/footer menus the host pre-resolved if the bundle passes
1101
+ * `ctx.navigation` into `NuMuProvider`.
1102
+ *
1103
+ * 4. **Live-preview + lifecycle.** The customizer streams draft settings via
1104
+ * the host's `applyDraft`; the bundle must hold them in state and re-paint
1105
+ * the style tokens on every draft. And it must return a `MountResult`
1106
+ * (`cleanup` + `applyDraft`) the host's `ByotThemeBoundary` understands.
1107
+ *
1108
+ * `mountTheme` does all of that once, so a theme's `main.tsx` collapses to:
1109
+ *
1110
+ * ```tsx
1111
+ * import { mountTheme } from "@numueg/theme-sdk";
1112
+ * export function mount(el, ctx) {
1113
+ * return mountTheme(el, ctx, ({ currentTemplate }) =>
1114
+ * <ThemeApp currentTemplate={currentTemplate} />,
1115
+ * );
1116
+ * }
1117
+ * ```
1118
+ *
1119
+ * The theme owns only its section list (`ThemeApp`). Everything in the list
1120
+ * above is handled here — fix it once, every theme benefits.
1121
+ *
1122
+ * ## Both ctx shapes
1123
+ *
1124
+ * The host (numu-storefront `ByotThemeBoundary`) passes
1125
+ * `{ themeSettings, storeData, page, locale, demo, navigation }`. Older / dev
1126
+ * contexts used `{ store, currentTemplate }`. We normalise both so a bundle
1127
+ * built against this helper works regardless of which host calls it.
1128
+ */
1129
+
1130
+ /** Minimal page descriptor the host forwards in the mount context. */
1131
+ interface ThemeMountPage {
1132
+ type?: string;
1133
+ handle?: string;
1134
+ title?: string;
1135
+ data?: Record<string, unknown>;
1136
+ }
1137
+ /**
1138
+ * The mount context a host (or dev harness) passes to a bundle's `mount`.
1139
+ * Accepts both the current storefront shape (`storeData`/`page`) and the
1140
+ * legacy/dev shape (`store`/`currentTemplate`); `mountTheme` normalises them.
1141
+ */
1142
+ interface ThemeMountContext {
1143
+ storeData?: Store;
1144
+ page?: ThemeMountPage;
1145
+ store?: Store;
1146
+ currentTemplate?: string;
1147
+ themeSettings: ThemeSettingsV3;
1148
+ initialCart?: Cart;
1149
+ customer?: Customer | null;
1150
+ locale?: string;
1151
+ translations?: Record<string, string>;
1152
+ /** AUTHORITATIVE marketplace-preview flag from the host (true only for the
1153
+ * catalog "Try theme" preview). Themes with demo-image fallbacks gate on
1154
+ * it so a real installed store never shows demo imagery. */
1155
+ demo?: boolean;
1156
+ /** Store navigation menus keyed by handle, resolved server-side. */
1157
+ navigation?: Record<string, MenuItemData[]>;
1158
+ [extra: string]: unknown;
1159
+ }
1160
+ /** Arguments handed to a theme's render callback on every (re)render. */
1161
+ interface ThemeRenderArgs {
1162
+ /** Active template key — "home" | "product" | "collection" | "cart" | … */
1163
+ currentTemplate: string;
1164
+ /** Marketplace-preview flag (see ThemeMountContext.demo). */
1165
+ demo: boolean;
1166
+ /** The raw host page descriptor (type/handle/data), or null. */
1167
+ page: ThemeMountPage | null;
1168
+ /** Normalised store record (never undefined). */
1169
+ store: Store;
1170
+ /** Live theme settings (reflects customizer drafts via applyDraft). */
1171
+ themeSettings: ThemeSettingsV3;
1172
+ }
1173
+ /**
1174
+ * Mount a V3 theme. Owns the React root, the provider stack (catalog + nav +
1175
+ * style tokens), and the live-preview draft cycle. Returns the host-contract
1176
+ * `MountResult` (`cleanup` + `applyDraft`).
1177
+ *
1178
+ * @param el the host-supplied container element
1179
+ * @param ctx the mount context (either host or legacy/dev shape)
1180
+ * @param renderApp returns the theme's section tree for the current args
1181
+ */
1182
+ declare function mountTheme(el: HTMLElement, ctx: ThemeMountContext, renderApp: (args: ThemeRenderArgs) => ReactNode): MountResult;
1183
+
986
1184
  interface NuMuProviderProps {
987
1185
  store: Store;
988
1186
  themeSettings: ThemeSettingsV3;
@@ -990,21 +1188,52 @@ interface NuMuProviderProps {
990
1188
  customer?: Customer | null;
991
1189
  locale?: string;
992
1190
  translations?: Record<string, string>;
1191
+ /**
1192
+ * Wave 5 — id of the active page template. Hosts pass this from the
1193
+ * route (e.g. "product" inside app/(store)/[subdomain]/product/[id]).
1194
+ * Themes read it via `useCurrentTemplate()` to render the matching
1195
+ * section list. Defaults to "home" when omitted, so existing themes
1196
+ * built before the prop existed keep rendering their home template.
1197
+ */
1198
+ currentTemplate?: string;
1199
+ /**
1200
+ * Pre-fetched product list for the current page. Themes that call
1201
+ * `useProducts()` will read these from PageContext without needing
1202
+ * `fetchIfMissing: true`. Hosts typically populate this from their
1203
+ * route loader / SSR pass; omitting it leaves `useProducts()`
1204
+ * returning an empty array unless the theme opts into client fetch.
1205
+ */
1206
+ initialProducts?: Product[];
1207
+ /**
1208
+ * Pre-fetched collection list — same pattern as initialProducts for
1209
+ * `useCollections()`. Categories/collections shown on the home page
1210
+ * read this slot.
1211
+ */
1212
+ initialCollections?: Collection[];
1213
+ /**
1214
+ * Phase 2.4 — store navigation menus keyed by handle (`main-menu`,
1215
+ * `footer`, …), resolved server-side by the host and injected so a
1216
+ * theme's `useNavigation(handle)` resolves synchronously without a
1217
+ * client round-trip. Omit for hosts/themes that don't wire menus —
1218
+ * `useNavigation` then falls back to its own fetch or the theme's
1219
+ * `DEFAULT_NAV`.
1220
+ */
1221
+ navigation?: Record<string, MenuItemData[]>;
993
1222
  children: ReactNode;
994
1223
  }
995
- declare function NuMuProvider({ store, themeSettings, initialCart, customer, locale: initialLocale, translations: initialTranslations, children, }: NuMuProviderProps): react_jsx_runtime.JSX.Element;
1224
+ declare function NuMuProvider({ store, themeSettings, initialCart, customer, locale: initialLocale, translations: initialTranslations, currentTemplate, initialProducts, initialCollections, navigation, children, }: NuMuProviderProps): react.JSX.Element;
996
1225
 
997
1226
  interface ProductProviderProps {
998
1227
  product: Product;
999
1228
  children: ReactNode;
1000
1229
  }
1001
- declare function ProductProvider({ product, children }: ProductProviderProps): react_jsx_runtime.JSX.Element;
1230
+ declare function ProductProvider({ product, children }: ProductProviderProps): react.JSX.Element;
1002
1231
 
1003
1232
  interface CollectionProviderProps {
1004
1233
  collection: Collection;
1005
1234
  children: ReactNode;
1006
1235
  }
1007
- declare function CollectionProvider({ collection, children }: CollectionProviderProps): react_jsx_runtime.JSX.Element;
1236
+ declare function CollectionProvider({ collection, children }: CollectionProviderProps): react.JSX.Element;
1008
1237
 
1009
1238
  interface MoneyProps {
1010
1239
  /** Amount in major units (e.g. dollars, not cents). */
@@ -1064,7 +1293,7 @@ interface ImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "src" | "
1064
1293
  * If `src` is empty/null, renders a placeholder div so the layout
1065
1294
  * doesn't shift while a merchant configures images in the customizer.
1066
1295
  */
1067
- declare function Image({ src, alt, sizes, responsive, loading, className, style, ...rest }: ImageProps): react_jsx_runtime.JSX.Element;
1296
+ declare function Image({ src, alt, sizes, responsive, loading, className, style, ...rest }: ImageProps): react.JSX.Element;
1068
1297
 
1069
1298
  interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"> {
1070
1299
  /**
@@ -1090,7 +1319,7 @@ interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"
1090
1319
  * unchanged so social-media links, CDN paths, etc. work without
1091
1320
  * special casing.
1092
1321
  */
1093
- declare function Link({ to, children, ...rest }: LinkProps): react_jsx_runtime.JSX.Element;
1322
+ declare function Link({ to, children, ...rest }: LinkProps): react.JSX.Element;
1094
1323
 
1095
1324
  interface AddToCartButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, "onClick" | "disabled"> {
1096
1325
  product: Product;
@@ -1118,7 +1347,7 @@ interface AddToCartButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonEleme
1118
1347
  * Doesn't trap navigation — for "buy now" flows that should redirect
1119
1348
  * to checkout, themes wrap this in their own `<a>` after onAdded.
1120
1349
  */
1121
- declare function AddToCartButton({ product, variant, quantity, label, loadingLabel, soldOutLabel, errorLabel, onAdded, ...rest }: AddToCartButtonProps): react_jsx_runtime.JSX.Element;
1350
+ declare function AddToCartButton({ product, variant, quantity, label, loadingLabel, soldOutLabel, errorLabel, onAdded, ...rest }: AddToCartButtonProps): react.JSX.Element;
1122
1351
 
1123
1352
  interface SectionProps extends HTMLAttributes<HTMLElement> {
1124
1353
  /** Section instance id (the order key in templates). Required for the
@@ -1159,7 +1388,7 @@ interface SectionProps extends HTMLAttributes<HTMLElement> {
1159
1388
  * so one bad section doesn't unmount its siblings. Customize the
1160
1389
  * fallback via `errorFallback`.
1161
1390
  */
1162
- declare function Section({ id, type, groupId, errorFallback, children, ...rest }: SectionProps): react_jsx_runtime.JSX.Element;
1391
+ declare function Section({ id, type, groupId, errorFallback, children, ...rest }: SectionProps): react.JSX.Element;
1163
1392
  interface BlockProps extends HTMLAttributes<HTMLDivElement> {
1164
1393
  /** Block instance id within its parent section. */
1165
1394
  id: string;
@@ -1176,7 +1405,7 @@ interface BlockProps extends HTMLAttributes<HTMLDivElement> {
1176
1405
  * Wraps `children` in the same per-instance ErrorBoundary as <Section>
1177
1406
  * so a bad block within a section isolates its failure.
1178
1407
  */
1179
- declare function Block({ id, type, errorFallback, children, ...rest }: BlockProps): react_jsx_runtime.JSX.Element;
1408
+ declare function Block({ id, type, errorFallback, children, ...rest }: BlockProps): react.JSX.Element;
1180
1409
 
1181
1410
  interface FormProps extends Omit<FormHTMLAttributes<HTMLFormElement>, "onSubmit" | "method" | "action" | "children" | "onError"> {
1182
1411
  /**
@@ -1233,7 +1462,7 @@ interface FormProps extends Omit<FormHTMLAttributes<HTMLFormElement>, "onSubmit"
1233
1462
  * itself or an `/api/*` proxy) so a misconfigured theme can't leak
1234
1463
  * customer data to a third party origin.
1235
1464
  */
1236
- declare function Form({ action, method, onSuccess, onError, children, ...rest }: FormProps): react_jsx_runtime.JSX.Element;
1465
+ declare function Form({ action, method, onSuccess, onError, children, ...rest }: FormProps): react.JSX.Element;
1237
1466
 
1238
1467
  /**
1239
1468
  * Opinionated product tile.
@@ -1287,7 +1516,7 @@ interface ProductCardProps {
1287
1516
  */
1288
1517
  imageSizes?: string;
1289
1518
  }
1290
- declare function ProductCard({ product, href, className, slots, imageSizes, }: ProductCardProps): react_jsx_runtime.JSX.Element;
1519
+ declare function ProductCard({ product, href, className, slots, imageSizes, }: ProductCardProps): react.JSX.Element;
1291
1520
 
1292
1521
  /**
1293
1522
  * Opinionated collection tile.
@@ -1317,7 +1546,7 @@ interface CollectionCardProps {
1317
1546
  slots?: CollectionCardSlots;
1318
1547
  imageSizes?: string;
1319
1548
  }
1320
- declare function CollectionCard({ collection, href, className, slots, imageSizes, }: CollectionCardProps): react_jsx_runtime.JSX.Element;
1549
+ declare function CollectionCard({ collection, href, className, slots, imageSizes, }: CollectionCardProps): react.JSX.Element;
1321
1550
 
1322
1551
  /**
1323
1552
  * <RichText html=... /> — sanitized HTML renderer.
@@ -1371,7 +1600,7 @@ interface RichTextProps {
1371
1600
  * `bypassSanitize` (escape hatch below).
1372
1601
  */
1373
1602
  declare function sanitizeHtml(input: string): string;
1374
- declare function RichText({ html, className, as }: RichTextProps): react_jsx_runtime.JSX.Element | null;
1603
+ declare function RichText({ html, className, as }: RichTextProps): react.JSX.Element | null;
1375
1604
 
1376
1605
  /**
1377
1606
  * <CurrencySwitcher /> — wired in Phase 6.
@@ -1397,7 +1626,7 @@ interface CurrencySwitcherProps {
1397
1626
  onChange: (next: string) => void;
1398
1627
  }) => React.ReactNode;
1399
1628
  }
1400
- declare function CurrencySwitcher({ className, onSelect, render, }: CurrencySwitcherProps): react_jsx_runtime.JSX.Element | null;
1629
+ declare function CurrencySwitcher({ className, onSelect, render, }: CurrencySwitcherProps): react.JSX.Element | null;
1401
1630
 
1402
1631
  interface LocaleSwitcherProps {
1403
1632
  className?: string;
@@ -1410,7 +1639,62 @@ interface LocaleSwitcherProps {
1410
1639
  onChange: (next: string) => void;
1411
1640
  }) => React.ReactNode;
1412
1641
  }
1413
- declare function LocaleSwitcher({ className, onSelect, render, }: LocaleSwitcherProps): react_jsx_runtime.JSX.Element | null;
1642
+ declare function LocaleSwitcher({ className, onSelect, render, }: LocaleSwitcherProps): react.JSX.Element | null;
1643
+
1644
+ interface EditableTextProps extends Omit<HTMLAttributes<HTMLElement>, "children"> {
1645
+ /** The section instance id (key inside templates.<page>.sections). */
1646
+ sectionId: string;
1647
+ /** Optional block id when the field lives inside a section block. */
1648
+ blockId?: string | null;
1649
+ /** The setting key from the section's schema (e.g. "headline"). */
1650
+ settingId: string;
1651
+ /** The current text value (may be HTML for inline_richtext settings). */
1652
+ value: string | undefined | null;
1653
+ /**
1654
+ * Element to render. Defaults to `<span>` so the component is
1655
+ * inline-compatible. Pass "h1" / "h2" / "p" / "div" for block-level
1656
+ * usage.
1657
+ */
1658
+ as?: ElementType;
1659
+ /**
1660
+ * Set to true to render the value as HTML (for inline_richtext
1661
+ * settings whose stored form is `<b>Welcome</b>`). Defaults to false
1662
+ * — plain-text fields are rendered as a text node.
1663
+ */
1664
+ html?: boolean;
1665
+ /** Empty-state placeholder shown when value is "" / null. */
1666
+ placeholder?: ReactNode;
1667
+ }
1668
+ declare const EditableText: react.ForwardRefExoticComponent<EditableTextProps & react.RefAttributes<HTMLElement>>;
1669
+ interface EditableImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, "src"> {
1670
+ sectionId: string;
1671
+ blockId?: string | null;
1672
+ settingId: string;
1673
+ /** Image src — may be empty/undefined if the merchant hasn't picked one yet. */
1674
+ src: string | undefined | null;
1675
+ /** Placeholder src shown when `src` is empty. Defaults to a 1x1 transparent gif. */
1676
+ emptyPlaceholderSrc?: string;
1677
+ }
1678
+ declare const EditableImage: react.ForwardRefExoticComponent<EditableImageProps & react.RefAttributes<HTMLImageElement>>;
1679
+
1680
+ declare const ICON_NAMES: string[];
1681
+ /** Public alias for the icon name → inner-SVG map. */
1682
+ declare const IconMap: Record<string, string>;
1683
+ interface IconProps {
1684
+ /** Icon name from {@link ICON_NAMES}. Unknown names render a neutral glyph. */
1685
+ name: string;
1686
+ /** Pixel size (width === height). Defaults to 24. */
1687
+ size?: number | string;
1688
+ strokeWidth?: number;
1689
+ className?: string;
1690
+ /** Accessible label. When omitted the icon is treated as decorative. */
1691
+ title?: string;
1692
+ }
1693
+ /**
1694
+ * Render an icon by name from the shared {@link ICON_DEFS} set. Inherits color
1695
+ * from `currentColor`, so set the parent's text color to recolor it.
1696
+ */
1697
+ declare function Icon({ name, size, strokeWidth, className, title, }: IconProps): react.JSX.Element;
1414
1698
 
1415
1699
  /**
1416
1700
  * Module Federation singleton sharing for @numueg/theme-sdk.
@@ -1457,10 +1741,135 @@ interface ReactSingleton {
1457
1741
  }
1458
1742
  declare function registerSdkSingleton(sdk: SdkSingleton): void;
1459
1743
  declare function getSdkSingleton(): SdkSingleton | null;
1744
+ declare function clearSdkSingleton(): void;
1460
1745
  declare function registerReactSingleton(react: unknown, reactDom: unknown): void;
1461
1746
  declare function getReactSingleton(): ReactSingleton | null;
1462
1747
  declare function isSdkAvailable(): boolean;
1463
1748
 
1749
+ /**
1750
+ * Dynamic sources — Shopify-parity feature that lets merchants bind a
1751
+ * section setting to a value that's pulled from store data at render
1752
+ * time instead of being a literal authored value.
1753
+ *
1754
+ * Stored shape:
1755
+ * { __numu_source: "<path>" }
1756
+ *
1757
+ * Paths use dot notation against the resolution context:
1758
+ * - `product.title` / `product.description` / `product.price` /
1759
+ * `product.image` / `product.first_image_url`
1760
+ * - `collection.title` / `collection.description` / `collection.image`
1761
+ * - `store.name` / `store.description`
1762
+ *
1763
+ * Why a reserved discriminant key (`__numu_source`)?
1764
+ *
1765
+ * Setting values today are plain literals (`"Hero headline"`,
1766
+ * `12345`, `true`). Themes that read settings as strings/numbers
1767
+ * would break if we suddenly stored an object. The reserved key
1768
+ * tags object-shape values so the resolver only intercepts the ones
1769
+ * it knows about. Anything else (e.g. the `{ url, alt }` shape from
1770
+ * the image picker, or a future structured setting) passes through.
1771
+ *
1772
+ * Bindable-by-type matrix (enforced in the customizer's source picker):
1773
+ *
1774
+ * text | product.title, product.description (snippet),
1775
+ * | collection.title, collection.description (snippet),
1776
+ * | store.name, store.description
1777
+ * textarea | same as text + full descriptions
1778
+ * richtext | full descriptions only
1779
+ * url | not bindable yet — product/collection URLs require
1780
+ * | the host's route resolver; deferred to a follow-up.
1781
+ * image_picker | product.image, collection.image, store.logo
1782
+ * number/range | product.price (raw cents) — risky because format
1783
+ * | varies. Deferred.
1784
+ * color/checkbox/select/radio/font | not bindable.
1785
+ *
1786
+ * Themes consume dynamic sources via `useResolvedSettings(instance)`
1787
+ * which returns the section's settings map with every dynamic ref
1788
+ * pre-resolved against the active product/collection/store context.
1789
+ * Literal values pass through unchanged.
1790
+ */
1791
+
1792
+ /** Stored shape for a dynamic source reference. */
1793
+ interface DynamicSourceRef {
1794
+ __numu_source: string;
1795
+ }
1796
+ /**
1797
+ * Type guard. True when `value` is a dynamic source reference object.
1798
+ * Order matters: we check the discriminant key BEFORE assuming the
1799
+ * value is anything else, so an `{ __numu_source: "..." }` from the
1800
+ * draft doesn't get treated as a literal record.
1801
+ */
1802
+ declare function isDynamicSource(value: unknown): value is DynamicSourceRef;
1803
+ /**
1804
+ * Build a fresh dynamic source ref. Pure helper used by the customizer
1805
+ * when the merchant clicks a source in the picker.
1806
+ */
1807
+ declare function dynamicSource(path: string): DynamicSourceRef;
1808
+ /** Resolution context for `resolveDynamicValue`. Each field is
1809
+ * independently optional so callers can pass only what's relevant
1810
+ * (e.g. cart template skips product/collection). */
1811
+ interface DynamicResolveContext {
1812
+ product?: Product | null;
1813
+ collection?: Collection | null;
1814
+ store?: Pick<Store, "name" | "description" | "logo_url"> | null;
1815
+ }
1816
+ /**
1817
+ * Look up the path inside the resolution context. Returns `null` when
1818
+ * the source is unknown OR the context doesn't have the resource
1819
+ * needed to resolve it (e.g. `product.title` when no product is in
1820
+ * context). The customizer's source picker already hides incompatible
1821
+ * sources, so this fallback only fires during preview navigation
1822
+ * (merchant flips to a product template before the picker re-renders).
1823
+ */
1824
+ declare function resolveSourcePath(path: string, ctx: DynamicResolveContext): unknown;
1825
+ /**
1826
+ * Resolve a single setting value. Literal values pass through
1827
+ * unchanged; dynamic references resolve against the context. Returns
1828
+ * `null` (not the original ref) when resolution fails, so themes can
1829
+ * branch on the null and either render a placeholder or hide the
1830
+ * element entirely.
1831
+ */
1832
+ declare function resolveDynamicValue<T = unknown>(value: unknown, ctx: DynamicResolveContext): T | null;
1833
+ /**
1834
+ * Walk every key of a settings map and resolve every dynamic
1835
+ * reference. Pure helper backing `useResolvedSettings`.
1836
+ *
1837
+ * The generic is unconstrained on purpose. Theme authors typically
1838
+ * type their settings as `interface HeroSettings { headline?: string }`,
1839
+ * and TypeScript interfaces don't implicitly satisfy
1840
+ * `Record<string, unknown>` — there's no index signature on an
1841
+ * interface. Leaving `T` open lets us pass through whatever shape the
1842
+ * caller declared and still return the resolved-but-same shape.
1843
+ */
1844
+ declare function resolveSettingsMap<T = Record<string, unknown>>(settings: T, ctx: DynamicResolveContext): T;
1845
+
1846
+ /**
1847
+ * Resolve every dynamic source in a section's settings against the
1848
+ * active product / collection / store contexts.
1849
+ *
1850
+ * Section components that want to support merchant-bound fields call
1851
+ * this in place of reading `instance.settings` directly:
1852
+ *
1853
+ * const settings = useResolvedSettings(instance);
1854
+ *
1855
+ * Literal values pass through; `{ __numu_source: "product.title" }`
1856
+ * becomes the current product's title when the section is rendered
1857
+ * inside a `<ProductProvider>`. The returned object is memoized on
1858
+ * the upstream contexts so themes can pass it straight into
1859
+ * `useMemo` deps without retriggering renders.
1860
+ *
1861
+ * When the bound resource isn't in context (e.g. `product.*` on a
1862
+ * non-product template), the resolved value is `null`. Themes can
1863
+ * detect this and render a placeholder or hide the affected element.
1864
+ *
1865
+ * Note: the SectionContext provider already supplies the `instance`
1866
+ * to children of `<Section>` — but we take it explicitly so section
1867
+ * components can call this hook before they've descended into
1868
+ * SectionContext (i.e. at the top of their render function, alongside
1869
+ * destructuring `settings` from props).
1870
+ */
1871
+ declare function useResolvedSettings<T = Record<string, unknown>>(instance: SectionInstance | BlockInstance | null | undefined): T;
1872
+
1464
1873
  /**
1465
1874
  * Section + block authoring helpers.
1466
1875
  *
@@ -1584,6 +1993,52 @@ declare function collectBlocks<T extends Record<string, unknown>>(modules: T): R
1584
1993
  */
1585
1994
  declare function assetUrl(name: string): string;
1586
1995
 
1996
+ /**
1997
+ * `applyGlobalStyleTokens(globalSettings, el)` — Phase 3.5.
1998
+ *
1999
+ * Bridges a store's **global theme settings** (colors, fonts, layout) to
2000
+ * **CSS custom properties** on the bundle's mount root, so editing a color
2001
+ * or font in Theme Settings actually re-paints the storefront. Before this,
2002
+ * themes shipped static CSS tokens that nothing wrote to → the pickers were
2003
+ * a silent no-op.
2004
+ *
2005
+ * ## Token contract (canonical names every theme can rely on)
2006
+ *
2007
+ * For each global setting `<id>` we set `--theme-<id>` verbatim (colors as
2008
+ * their value, fonts resolved to a CSS font stack, other scalars as-is).
2009
+ * Themes consume these with the current static value as a fallback:
2010
+ *
2011
+ * --by-cream: var(--theme-background_color, #f7f1e8);
2012
+ * --by-espresso: var(--theme-primary_color, #3a2418);
2013
+ * --by-font-serif: var(--theme-heading_font, "Cormorant Garamond", serif);
2014
+ *
2015
+ * In addition, well-known ids are aliased to **role** tokens so themes that
2016
+ * prefer semantic names share one vocabulary:
2017
+ *
2018
+ * colors → --theme-color-{background,text,primary,secondary,accent,border,button}
2019
+ * fonts → --theme-font-{heading,body}
2020
+ *
2021
+ * `color_scheme` / `color_scheme_group` settings (object values keyed by role)
2022
+ * emit `--scheme-<id>-<role>`.
2023
+ *
2024
+ * The helper is **idempotent** and safe to call on every `applyDraft` so the
2025
+ * customizer's live preview re-paints as the merchant drags a color.
2026
+ */
2027
+ type GlobalSettings = Record<string, unknown> | null | undefined;
2028
+ /**
2029
+ * Resolve a font setting value to a CSS stack. Known tokens map through the
2030
+ * registry (and load the webfont); anything else is treated as a literal
2031
+ * family/stack the theme author supplied.
2032
+ */
2033
+ declare function resolveFontStack(value: string): string;
2034
+ /**
2035
+ * Map a store's global settings onto CSS custom properties on `el`. Call on
2036
+ * mount and on every `applyDraft` (live preview). No-op when `el` or the
2037
+ * settings are missing. Reserved `__`-prefixed keys (e.g. `__translations`)
2038
+ * are skipped.
2039
+ */
2040
+ declare function applyGlobalStyleTokens(globalSettings: GlobalSettings, el: HTMLElement | null | undefined): void;
2041
+
1587
2042
  /**
1588
2043
  * Theme-bundled locale files: load + merge.
1589
2044
  *
@@ -1642,4 +2097,4 @@ declare function pickTranslations(bundle: LocaleBundle, locale: string): LocaleM
1642
2097
  */
1643
2098
  declare function buildLocaleBundle<T extends Record<string, unknown>>(modules: T): LocaleBundle;
1644
2099
 
1645
- export { AddToCartButton, type AddressInput, type AnalyticsApi, type AnalyticsPayload, type AppManifestBlock, type AppPayload, type AppState, Block, 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, Form, type GiftCardBalance, Image, Link, type LocaleBundle, type LocaleMessages, LocaleSwitcher, type LocaleSwitcherProps, LocalizationContext, Money, 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, ThemeSettingsContext, ThemeSettingsV3, type UseGiftCardBalance, type UseReorder, type UseSearchOptions, type UseShippingRatesOptions, type UseShippingRatesState, type UseVariantSelection, type WishlistItem, type WishlistState, assetUrl, availableValues, buildLocaleBundle, collectBlocks, collectSections, defaultVariant, defineBlock, defineSection, findVariantByOptions, flattenMessages, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isSdkAvailable, pickTranslations, registerReactSingleton, registerSdkSingleton, sanitizeHtml, useAnalytics, useApp, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useLocale, useLocalization, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProducts, useRelatedProducts, useReorder, useSearch, useSection, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };
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 };