@numueg/theme-sdk 0.3.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,9 +1,403 @@
1
1
  import { createContext, forwardRef, useState, useImperativeHandle, useEffect, useCallback, useMemo, useRef, useContext, StrictMode, createElement, Component } from 'react';
2
2
  import { hydrateRoot, createRoot } from 'react-dom/client';
3
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
4
4
 
5
5
  // src/types/theme.ts
6
6
  var MAX_BLOCK_DEPTH = 5;
7
+
8
+ // src/validation/index.ts
9
+ var THEME_CONTRACT_VERSION = 1;
10
+ var SDK_VERSION = "0.5.0" ;
11
+ var REQUIRED_TEMPLATES = [
12
+ "home",
13
+ "product",
14
+ "collection",
15
+ "cart",
16
+ "page",
17
+ "search",
18
+ "404"
19
+ ];
20
+ var KNOWN_TEMPLATES = /* @__PURE__ */ new Set([
21
+ ...REQUIRED_TEMPLATES,
22
+ "blog",
23
+ "article",
24
+ "policies",
25
+ "password",
26
+ "account",
27
+ "checkout"
28
+ ]);
29
+ var KNOWN_SETTING_TYPES = /* @__PURE__ */ new Set([
30
+ "text",
31
+ "textarea",
32
+ "richtext",
33
+ "number",
34
+ "range",
35
+ "color",
36
+ "checkbox",
37
+ "select",
38
+ "radio",
39
+ "font",
40
+ "image_picker",
41
+ "url",
42
+ "product",
43
+ "product_list",
44
+ "collection",
45
+ "collection_list",
46
+ "header",
47
+ "paragraph",
48
+ "html",
49
+ "date",
50
+ "time",
51
+ "video_picker",
52
+ "color_scheme",
53
+ "page_picker",
54
+ "blog_picker",
55
+ "link_list_picker",
56
+ "variant_picker",
57
+ "file_upload",
58
+ "icon_picker",
59
+ "icon"
60
+ ]);
61
+ var PRESENTATIONAL_SETTING_TYPES = /* @__PURE__ */ new Set([
62
+ "header",
63
+ "paragraph",
64
+ "html"
65
+ ]);
66
+ var SECTION_TYPE_RE = /^[a-z][a-z0-9_-]*$/;
67
+ var THEME_ID_RE = /^[a-z0-9][a-z0-9_-]*[a-z0-9]$/;
68
+ var SEMVER_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?(?:\+[0-9A-Za-z-.]+)?$/;
69
+ function isObject(v) {
70
+ return typeof v === "object" && v !== null && !Array.isArray(v);
71
+ }
72
+ function isNonEmptyString(v) {
73
+ return typeof v === "string" && v.trim().length > 0;
74
+ }
75
+ var IssueBag = class {
76
+ constructor() {
77
+ this.issues = [];
78
+ }
79
+ err(code, message, path) {
80
+ this.issues.push({ level: "error", code, message, path });
81
+ }
82
+ warn(code, message, path) {
83
+ this.issues.push({ level: "warning", code, message, path });
84
+ }
85
+ result() {
86
+ return {
87
+ valid: !this.issues.some((i) => i.level === "error"),
88
+ issues: this.issues
89
+ };
90
+ }
91
+ };
92
+ function collectPresetSectionTypes(manifest) {
93
+ const types = /* @__PURE__ */ new Set();
94
+ const presets = manifest.presets;
95
+ if (!isObject(presets)) return types;
96
+ const buckets = [presets.templates, presets.section_groups];
97
+ for (const bucket of buckets) {
98
+ if (!isObject(bucket)) continue;
99
+ for (const entry of Object.values(bucket)) {
100
+ if (!isObject(entry)) continue;
101
+ const sections = entry.sections;
102
+ const instances = Array.isArray(sections) ? sections : isObject(sections) ? Object.values(sections) : [];
103
+ for (const inst of instances) {
104
+ if (isObject(inst) && isNonEmptyString(inst.type)) types.add(inst.type);
105
+ }
106
+ }
107
+ }
108
+ return types;
109
+ }
110
+ function validateSettingInto(bag, setting, pathPrefix, seenIds) {
111
+ if (!isObject(setting)) {
112
+ bag.err("schema.setting.invalid", "Setting must be an object.", pathPrefix);
113
+ return;
114
+ }
115
+ const type = setting.type;
116
+ if (!isNonEmptyString(type)) {
117
+ bag.err("schema.setting.type.missing", "Setting is missing a `type`.", pathPrefix);
118
+ } else if (!KNOWN_SETTING_TYPES.has(type)) {
119
+ bag.warn(
120
+ "schema.setting.type.unknown",
121
+ `Unknown setting type "${type}" \u2014 the customizer will fall back to a text input.`,
122
+ pathPrefix
123
+ );
124
+ }
125
+ const presentational = isNonEmptyString(type) && PRESENTATIONAL_SETTING_TYPES.has(type);
126
+ if (!presentational) {
127
+ if (!isNonEmptyString(setting.id)) {
128
+ bag.err("schema.setting.id.missing", "Setting is missing an `id`.", pathPrefix);
129
+ } else {
130
+ if (seenIds.has(setting.id)) {
131
+ bag.err(
132
+ "schema.setting.id.duplicate",
133
+ `Duplicate setting id "${setting.id}".`,
134
+ pathPrefix
135
+ );
136
+ }
137
+ seenIds.add(setting.id);
138
+ }
139
+ if (!isNonEmptyString(setting.label)) {
140
+ bag.warn("schema.setting.label.missing", "Setting has no `label`.", pathPrefix);
141
+ }
142
+ }
143
+ if (type === "select" || type === "radio") {
144
+ const options = setting.options;
145
+ if (!Array.isArray(options) || options.length === 0) {
146
+ bag.err(
147
+ "schema.setting.options.missing",
148
+ `"${type}" setting "${String(setting.id)}" must declare a non-empty \`options\` array.`,
149
+ pathPrefix
150
+ );
151
+ }
152
+ }
153
+ if (type === "range") {
154
+ if (typeof setting.min !== "number" || typeof setting.max !== "number") {
155
+ bag.err(
156
+ "schema.setting.range.bounds",
157
+ `"range" setting "${String(setting.id)}" must declare numeric \`min\` and \`max\`.`,
158
+ pathPrefix
159
+ );
160
+ } else if (setting.min >= setting.max) {
161
+ bag.err(
162
+ "schema.setting.range.order",
163
+ `"range" setting "${String(setting.id)}" has min >= max.`,
164
+ pathPrefix
165
+ );
166
+ }
167
+ }
168
+ }
169
+ function validateSectionSchema(schema, opts = {}) {
170
+ const bag = new IssueBag();
171
+ const where = opts.filenameType ? `${opts.filenameType}.json` : "section schema";
172
+ if (!isObject(schema)) {
173
+ bag.err("schema.invalid", "Section schema must be a JSON object.", where);
174
+ return bag.result();
175
+ }
176
+ const type = schema.type;
177
+ if (!isNonEmptyString(type)) {
178
+ bag.err("schema.type.missing", "Section schema is missing a `type`.", where);
179
+ } else {
180
+ if (!SECTION_TYPE_RE.test(type)) {
181
+ bag.err(
182
+ "schema.type.format",
183
+ `Section type "${type}" must match ${SECTION_TYPE_RE} (lowercase, start with a letter).`,
184
+ where
185
+ );
186
+ }
187
+ if (opts.filenameType && type !== opts.filenameType) {
188
+ bag.err(
189
+ "schema.type.filename_mismatch",
190
+ `Schema type "${type}" must equal its filename "${opts.filenameType}" (component-filename = schema-type convention).`,
191
+ where
192
+ );
193
+ }
194
+ }
195
+ if (!isNonEmptyString(schema.name)) {
196
+ bag.err("schema.name.missing", "Section schema is missing a `name`.", where);
197
+ }
198
+ if (schema.settings === void 0) {
199
+ bag.warn("schema.settings.missing", "Section schema has no `settings`.", where);
200
+ } else if (!Array.isArray(schema.settings)) {
201
+ bag.err("schema.settings.invalid", "`settings` must be an array.", where);
202
+ } else {
203
+ const seen = /* @__PURE__ */ new Set();
204
+ schema.settings.forEach(
205
+ (s, i) => validateSettingInto(bag, s, `${where}.settings[${i}]`, seen)
206
+ );
207
+ }
208
+ if (schema.blocks !== void 0) {
209
+ if (!Array.isArray(schema.blocks)) {
210
+ bag.err("schema.blocks.invalid", "`blocks` must be an array.", where);
211
+ } else {
212
+ schema.blocks.forEach((b, i) => {
213
+ if (!isObject(b) || !isNonEmptyString(b.type)) {
214
+ bag.err("schema.block.type.missing", `Block[${i}] is missing a \`type\`.`, where);
215
+ return;
216
+ }
217
+ if (!SECTION_TYPE_RE.test(b.type)) {
218
+ bag.err(
219
+ "schema.block.type.format",
220
+ `Block type "${b.type}" must match ${SECTION_TYPE_RE}.`,
221
+ where
222
+ );
223
+ }
224
+ if (Array.isArray(b.settings)) {
225
+ const seen = /* @__PURE__ */ new Set();
226
+ b.settings.forEach(
227
+ (s, j) => validateSettingInto(bag, s, `${where}.blocks[${i}].settings[${j}]`, seen)
228
+ );
229
+ }
230
+ });
231
+ }
232
+ }
233
+ return bag.result();
234
+ }
235
+ function validateSettingsAgainstSchema(settings, schema, pathPrefix = "settings") {
236
+ const bag = new IssueBag();
237
+ if (!isObject(settings)) {
238
+ bag.err("instance.settings.invalid", "Section settings must be an object.", pathPrefix);
239
+ return bag.result();
240
+ }
241
+ const defs = Array.isArray(schema.settings) ? schema.settings : [];
242
+ const byId = /* @__PURE__ */ new Map();
243
+ for (const d of defs) if (isNonEmptyString(d?.id)) byId.set(d.id, d);
244
+ for (const [key, value] of Object.entries(settings)) {
245
+ const def = byId.get(key);
246
+ if (!def) {
247
+ bag.warn(
248
+ "instance.setting.unknown",
249
+ `Setting "${key}" is not declared in the "${String(schema.type)}" schema.`,
250
+ `${pathPrefix}.${key}`
251
+ );
252
+ continue;
253
+ }
254
+ if (value === null || value === void 0) continue;
255
+ const p = `${pathPrefix}.${key}`;
256
+ if ((def.type === "select" || def.type === "radio") && Array.isArray(def.options)) {
257
+ const allowed = def.options.map((o) => o.value);
258
+ if (typeof value === "string" && !allowed.includes(value)) {
259
+ bag.err(
260
+ "instance.setting.option.invalid",
261
+ `"${key}" = "${value}" is not one of: ${allowed.join(", ")}.`,
262
+ p
263
+ );
264
+ }
265
+ }
266
+ if (def.type === "range" && typeof value === "number") {
267
+ if (typeof def.min === "number" && value < def.min) {
268
+ bag.err("instance.setting.range.under", `"${key}" = ${value} is below min ${def.min}.`, p);
269
+ }
270
+ if (typeof def.max === "number" && value > def.max) {
271
+ bag.err("instance.setting.range.over", `"${key}" = ${value} is above max ${def.max}.`, p);
272
+ }
273
+ }
274
+ if (def.type === "checkbox" && typeof value !== "boolean") {
275
+ bag.warn("instance.setting.type.mismatch", `"${key}" should be a boolean.`, p);
276
+ }
277
+ if ((def.type === "number" || def.type === "range") && typeof value !== "number") {
278
+ bag.warn("instance.setting.type.mismatch", `"${key}" should be a number.`, p);
279
+ }
280
+ }
281
+ return bag.result();
282
+ }
283
+ function validateManifestCore(bag, m) {
284
+ if (!isNonEmptyString(m.id)) {
285
+ bag.err("manifest.id.missing", "theme.json is missing `id`.", "id");
286
+ } else if (!THEME_ID_RE.test(m.id)) {
287
+ bag.err(
288
+ "manifest.id.format",
289
+ `Theme id "${m.id}" must be lowercase alphanumeric with dashes/underscores (no leading/trailing separator).`,
290
+ "id"
291
+ );
292
+ }
293
+ const name = m.name;
294
+ const nameOk = isNonEmptyString(name) || isObject(name) && Object.values(name).some((v) => isNonEmptyString(v));
295
+ if (!nameOk) {
296
+ bag.err("manifest.name.missing", "theme.json is missing a non-empty `name`.", "name");
297
+ }
298
+ if (!isNonEmptyString(m.version)) {
299
+ bag.err("manifest.version.missing", "theme.json is missing `version`.", "version");
300
+ } else if (!SEMVER_RE.test(m.version)) {
301
+ bag.err("manifest.version.invalid", `Version "${m.version}" is not valid semver.`, "version");
302
+ }
303
+ if (!isNonEmptyString(m.author)) {
304
+ bag.err("manifest.author.missing", "theme.json is missing `author`.", "author");
305
+ }
306
+ if (m.min_sdk_version !== void 0 && !SEMVER_RE.test(String(m.min_sdk_version))) {
307
+ bag.warn("manifest.min_sdk_version.invalid", "`min_sdk_version` is not valid semver.", "min_sdk_version");
308
+ }
309
+ }
310
+ function validateManifest(manifest, ctx = {}) {
311
+ const bag = new IssueBag();
312
+ if (!isObject(manifest)) {
313
+ bag.err("manifest.invalid", "theme.json must be a JSON object.", "theme.json");
314
+ return bag.result();
315
+ }
316
+ validateManifestCore(bag, manifest);
317
+ const presets = manifest.presets;
318
+ if (!isObject(presets) || Object.keys(presets).length === 0) {
319
+ bag.warn("manifest.presets.empty", "theme.json has no presets \u2014 merchants start with an empty page.", "presets");
320
+ } else {
321
+ if (ctx.sectionTypes) {
322
+ for (const type of collectPresetSectionTypes(manifest)) {
323
+ if (!ctx.sectionTypes.has(type)) {
324
+ bag.err(
325
+ "manifest.preset.unknown_section",
326
+ `Preset references section type "${type}" with no schemas/sections/${type}.json.`,
327
+ "presets"
328
+ );
329
+ }
330
+ }
331
+ }
332
+ const templates = isObject(presets.templates) ? presets.templates : {};
333
+ for (const t of REQUIRED_TEMPLATES) {
334
+ if (!(t in templates)) {
335
+ bag.warn(
336
+ "manifest.template.missing",
337
+ `No preset for required template "${t}" \u2014 the storefront will fall back to its built-in.`,
338
+ `presets.templates.${t}`
339
+ );
340
+ }
341
+ }
342
+ }
343
+ return bag.result();
344
+ }
345
+ function validateBuiltManifest(builtManifest, importMap, opts = {}) {
346
+ const bag = new IssueBag();
347
+ if (!isObject(builtManifest)) {
348
+ bag.err("built.invalid", "manifest.json must be a JSON object.", "manifest.json");
349
+ return bag.result();
350
+ }
351
+ validateManifestCore(bag, builtManifest);
352
+ const schemas = builtManifest.section_schemas;
353
+ const shipped = new Set(isObject(schemas) ? Object.keys(schemas) : []);
354
+ if (!isObject(schemas)) {
355
+ bag.warn("built.section_schemas.missing", "manifest.json has no `section_schemas`.", "section_schemas");
356
+ }
357
+ for (const type of collectPresetSectionTypes(builtManifest)) {
358
+ if (!shipped.has(type)) {
359
+ bag.err(
360
+ "built.preset.unknown_section",
361
+ `Preset references section type "${type}" not present in section_schemas.`,
362
+ "presets"
363
+ );
364
+ }
365
+ }
366
+ if (isObject(schemas)) {
367
+ for (const [type, schema] of Object.entries(schemas)) {
368
+ const r = validateSectionSchema(schema, { filenameType: type });
369
+ for (const issue of r.issues) bag.issues.push(issue);
370
+ }
371
+ }
372
+ if (!isNonEmptyString(builtManifest.plugin_version)) {
373
+ bag.warn("built.plugin_version.missing", "manifest.json has no `plugin_version`.", "plugin_version");
374
+ }
375
+ if (importMap !== void 0) {
376
+ if (!isObject(importMap)) {
377
+ bag.err("importmap.invalid", "import-map.json must be a JSON object.", "import-map.json");
378
+ } else {
379
+ const cv = importMap.contract_version;
380
+ if (typeof cv !== "number") {
381
+ bag.warn(
382
+ "importmap.contract_version.missing",
383
+ "import-map.json has no numeric `contract_version` \u2014 built by an older plugin.",
384
+ "contract_version"
385
+ );
386
+ } else if (typeof opts.hostContractVersion === "number" && cv > opts.hostContractVersion) {
387
+ bag.err(
388
+ "importmap.contract_version.incompatible",
389
+ `Theme built for contract v${cv}; this platform supports up to v${opts.hostContractVersion}.`,
390
+ "contract_version"
391
+ );
392
+ }
393
+ }
394
+ }
395
+ return bag.result();
396
+ }
397
+ function mergeResults(...results) {
398
+ const issues = results.flatMap((r) => r.issues);
399
+ return { valid: !issues.some((i) => i.level === "error"), issues };
400
+ }
7
401
  var ShopContext = createContext(null);
8
402
  var ProductContext = createContext(null);
9
403
  var CollectionContext = createContext(null);
@@ -1934,6 +2328,64 @@ function NuMuProvider({
1934
2328
  function ProductProvider({ product, children }) {
1935
2329
  return /* @__PURE__ */ jsx(ProductContext.Provider, { value: product, children });
1936
2330
  }
2331
+ function CollectionProvider({ collection, children }) {
2332
+ return /* @__PURE__ */ jsx(CollectionContext.Provider, { value: collection, children });
2333
+ }
2334
+
2335
+ // src/utils/logoStyle.ts
2336
+ var LOGO_SHAPE_OPTIONS = [
2337
+ { value: "none", label: "Original", label_ar: "\u0627\u0644\u0623\u0635\u0644\u064A" },
2338
+ { value: "square", label: "Square", label_ar: "\u0645\u0631\u0628\u0639" },
2339
+ { value: "rounded", label: "Rounded square", label_ar: "\u0645\u0631\u0628\u0639 \u0628\u0632\u0648\u0627\u064A\u0627 \u062F\u0627\u0626\u0631\u064A\u0629" },
2340
+ { value: "circle", label: "Circle", label_ar: "\u062F\u0627\u0626\u0631\u0629" },
2341
+ { value: "triangle", label: "Triangle", label_ar: "\u0645\u062B\u0644\u062B" }
2342
+ ];
2343
+ var LOGO_SIZE_OPTIONS = [
2344
+ { value: "small", label: "Small", label_ar: "\u0635\u063A\u064A\u0631" },
2345
+ { value: "medium", label: "Medium", label_ar: "\u0645\u062A\u0648\u0633\u0637" },
2346
+ { value: "large", label: "Large", label_ar: "\u0643\u0628\u064A\u0631" }
2347
+ ];
2348
+ var SHAPED_PX = {
2349
+ small: { plain: 32, rounded: 48 },
2350
+ medium: { plain: 40, rounded: 64 },
2351
+ large: { plain: 56, rounded: 80 }
2352
+ };
2353
+ function normalizeSize(size) {
2354
+ return size === "large" || size === "medium" ? size : "small";
2355
+ }
2356
+ function logoImgStyle(shape, size) {
2357
+ if (!shape || shape === "none") return void 0;
2358
+ const s = normalizeSize(size);
2359
+ const isRound = shape === "circle" || shape === "rounded";
2360
+ const px = isRound ? SHAPED_PX[s].rounded : SHAPED_PX[s].plain;
2361
+ const base = {
2362
+ height: px,
2363
+ width: px,
2364
+ objectFit: "cover",
2365
+ flex: "0 0 auto"
2366
+ };
2367
+ switch (shape) {
2368
+ case "circle":
2369
+ return { ...base, borderRadius: "9999px" };
2370
+ case "rounded":
2371
+ return { ...base, borderRadius: "0.5rem" };
2372
+ case "triangle":
2373
+ return { ...base, clipPath: "polygon(50% 0%, 100% 100%, 0% 100%)" };
2374
+ case "square":
2375
+ default:
2376
+ return base;
2377
+ }
2378
+ }
2379
+ function logoStyleTokens(shape, size) {
2380
+ const style = logoImgStyle(shape, size);
2381
+ if (!style) return {};
2382
+ const box = `${style.height}px`;
2383
+ return {
2384
+ "--theme-logo-box": box,
2385
+ "--theme-logo-radius": typeof style.borderRadius === "string" ? style.borderRadius : "0",
2386
+ "--theme-logo-clip": typeof style.clipPath === "string" ? style.clipPath : "none"
2387
+ };
2388
+ }
1937
2389
 
1938
2390
  // src/utils/styleTokens.ts
1939
2391
  var COLOR_ROLE_ALIASES = {
@@ -2074,6 +2526,12 @@ function computeGlobalStyleTokens(globalSettings) {
2074
2526
  pushHref(FONT_REGISTRY[value]?.href);
2075
2527
  }
2076
2528
  }
2529
+ const gs = globalSettings;
2530
+ const logoTokens = logoStyleTokens(
2531
+ typeof gs.logo_shape === "string" ? gs.logo_shape : void 0,
2532
+ typeof gs.logo_size === "string" ? gs.logo_size : void 0
2533
+ );
2534
+ for (const [k, v] of Object.entries(logoTokens)) cssVars[k] = v;
2077
2535
  return { cssVars, fontHrefs };
2078
2536
  }
2079
2537
  function applyGlobalStyleTokens(globalSettings, el) {
@@ -2116,6 +2574,16 @@ function pickDemo(ctx, themeSettings) {
2116
2574
  const t = themeSettings.templates;
2117
2575
  return !t || Object.keys(t).length === 0;
2118
2576
  }
2577
+ function wrapEntityProviders(app, pageData) {
2578
+ let inner = app;
2579
+ if (pageData.collection) {
2580
+ inner = /* @__PURE__ */ jsx(CollectionProvider, { collection: pageData.collection, children: inner });
2581
+ }
2582
+ if (pageData.product) {
2583
+ inner = /* @__PURE__ */ jsx(ProductProvider, { product: pageData.product, children: inner });
2584
+ }
2585
+ return inner;
2586
+ }
2119
2587
  var ThemeMountBridge = forwardRef(function ThemeMountBridge2({ ctx, mountEl, renderApp }, ref) {
2120
2588
  const [themeSettings, setThemeSettings] = useState(
2121
2589
  ctx.themeSettings
@@ -2156,7 +2624,7 @@ var ThemeMountBridge = forwardRef(function ThemeMountBridge2({ ctx, mountEl, ren
2156
2624
  initialProducts: pageData.products,
2157
2625
  initialCollections: pageData.collections,
2158
2626
  currentTemplate: template,
2159
- children: pageData.product ? /* @__PURE__ */ jsx(ProductProvider, { product: pageData.product, children: app }) : app
2627
+ children: wrapEntityProviders(app, pageData)
2160
2628
  }
2161
2629
  );
2162
2630
  });
@@ -2192,9 +2660,6 @@ function defineThemeEntry(renderApp) {
2192
2660
  createApp: (ctx) => buildThemeElement(ctx, null, renderApp)
2193
2661
  };
2194
2662
  }
2195
- function CollectionProvider({ collection, children }) {
2196
- return /* @__PURE__ */ jsx(CollectionContext.Provider, { value: collection, children });
2197
- }
2198
2663
  function Money({
2199
2664
  amount,
2200
2665
  currency,
@@ -2345,6 +2810,43 @@ function Image({
2345
2810
  }
2346
2811
  );
2347
2812
  }
2813
+ var str = (v) => typeof v === "string" ? v : "";
2814
+ var NONE_HEIGHT = { small: 28, medium: 36, large: 48 };
2815
+ function Logo({
2816
+ src,
2817
+ alt,
2818
+ shape,
2819
+ size,
2820
+ className,
2821
+ style,
2822
+ fallback
2823
+ }) {
2824
+ const settings = useThemeSettings();
2825
+ const shop = useShop();
2826
+ const g = settings?.global_settings ?? {};
2827
+ const url = str(src) || str(g.logo_url) || shop?.logo_url || "";
2828
+ const resolvedShape = shape || str(g.logo_shape) || "none";
2829
+ const resolvedSize = size || str(g.logo_size) || "small";
2830
+ const altText = alt || str(g.brand_name) || shop?.name || "";
2831
+ if (!url) return /* @__PURE__ */ jsx(Fragment, { children: fallback ?? null });
2832
+ const shaped = resolvedShape !== "none";
2833
+ const imgStyle = shaped ? { ...logoImgStyle(resolvedShape, resolvedSize), ...style } : {
2834
+ height: NONE_HEIGHT[resolvedSize] ?? NONE_HEIGHT.small,
2835
+ width: "auto",
2836
+ objectFit: "contain",
2837
+ ...style
2838
+ };
2839
+ return /* @__PURE__ */ jsx(
2840
+ "img",
2841
+ {
2842
+ src: url,
2843
+ alt: altText,
2844
+ className,
2845
+ style: imgStyle,
2846
+ loading: "eager"
2847
+ }
2848
+ );
2849
+ }
2348
2850
  var ABSOLUTE_URL = /^[a-z]+:|^\/\//i;
2349
2851
  function Link({ to, children, ...rest }) {
2350
2852
  const shop = useShop();
@@ -3555,6 +4057,6 @@ function buildLocaleBundle(modules) {
3555
4057
  return bundle;
3556
4058
  }
3557
4059
 
3558
- 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, buildThemeElement, clearSdkSingleton, collectBlocks, collectSections, computeGlobalStyleTokens, defaultVariant, defineBlock, defineSection, defineThemeEntry, dynamicSource, findVariantByOptions, flattenMessages, focalSrc, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, mountTheme, pickTranslations, registerReactSingleton, registerSdkSingleton, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSizeChart, 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, useProductSizeChart, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };
4060
+ export { AddToCartButton, Block, CartContext, CollectionCard, CollectionContext, CollectionProvider, CurrencySwitcher, CustomerContext, EditableImage, EditableText, Form, ICON_NAMES, Icon, IconMap, Image, KNOWN_SETTING_TYPES, KNOWN_TEMPLATES, LOGO_SHAPE_OPTIONS, LOGO_SIZE_OPTIONS, Link, LocaleSwitcher, LocalizationContext, Logo, MAX_BLOCK_DEPTH, Money, NavigationContext, NuMuProvider, PageContext, ProductCard, ProductContext, ProductProvider, REQUIRED_TEMPLATES, RichText, SDK_VERSION, Section, SectionContext, ShopContext, THEME_CONTRACT_VERSION, ThemeSettingsContext, 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, mergeResults, mountTheme, pickTranslations, registerReactSingleton, registerSdkSingleton, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSizeChart, 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, useProductSizeChart, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist, validateBuiltManifest, validateManifest, validateSectionSchema, validateSettingsAgainstSchema };
3559
4061
  //# sourceMappingURL=index.mjs.map
3560
4062
  //# sourceMappingURL=index.mjs.map