meno-core 1.0.48 → 1.0.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/build-astro.ts +6 -2
  2. package/dist/build-static.js +7 -7
  3. package/dist/chunks/{chunk-D5E3OKSL.js → chunk-56EUSC6D.js} +5 -5
  4. package/dist/chunks/{chunk-3FHJUHAS.js → chunk-7NIC4I3V.js} +300 -43
  5. package/dist/chunks/chunk-7NIC4I3V.js.map +7 -0
  6. package/dist/chunks/{chunk-B2RTLDXY.js → chunk-AZQYF6KE.js} +132 -1
  7. package/dist/chunks/chunk-AZQYF6KE.js.map +7 -0
  8. package/dist/chunks/{chunk-TPQ7APVQ.js → chunk-CVLFID6V.js} +473 -73
  9. package/dist/chunks/chunk-CVLFID6V.js.map +7 -0
  10. package/dist/chunks/{chunk-NP76N4HQ.js → chunk-EDQSMAMP.js} +13 -2
  11. package/dist/chunks/{chunk-NP76N4HQ.js.map → chunk-EDQSMAMP.js.map} +2 -2
  12. package/dist/chunks/{chunk-RQSTH2BS.js → chunk-H4JSCDNW.js} +2 -2
  13. package/dist/chunks/{chunk-EK4KESLU.js → chunk-J23ZX5AP.js} +8 -2
  14. package/dist/chunks/{chunk-EK4KESLU.js.map → chunk-J23ZX5AP.js.map} +2 -2
  15. package/dist/chunks/{chunk-UUA5LEWF.js → chunk-LPVETICS.js} +156 -8
  16. package/dist/chunks/chunk-LPVETICS.js.map +7 -0
  17. package/dist/chunks/{chunk-BJRKEPMP.js → chunk-PQ2HRXDR.js} +5 -2
  18. package/dist/chunks/chunk-PQ2HRXDR.js.map +7 -0
  19. package/dist/chunks/{chunk-NKUV77SR.js → chunk-YWJJD5D6.js} +133 -37
  20. package/dist/chunks/chunk-YWJJD5D6.js.map +7 -0
  21. package/dist/chunks/{configService-IGJEC3MC.js → configService-VOY2MY2K.js} +3 -3
  22. package/dist/entries/server-router.js +9 -9
  23. package/dist/entries/server-router.js.map +2 -2
  24. package/dist/lib/client/index.js +92 -32
  25. package/dist/lib/client/index.js.map +3 -3
  26. package/dist/lib/server/index.js +14 -12
  27. package/dist/lib/server/index.js.map +2 -2
  28. package/dist/lib/shared/index.js +46 -10
  29. package/dist/lib/shared/index.js.map +3 -3
  30. package/entries/server-router.tsx +6 -2
  31. package/lib/client/core/ComponentBuilder.test.ts +34 -0
  32. package/lib/client/core/ComponentBuilder.ts +33 -4
  33. package/lib/client/core/builders/embedBuilder.ts +28 -7
  34. package/lib/client/core/builders/linkNodeBuilder.ts +28 -7
  35. package/lib/client/core/builders/localeListBuilder.ts +30 -11
  36. package/lib/client/styles/StyleInjector.ts +3 -2
  37. package/lib/client/templateEngine.ts +24 -0
  38. package/lib/client/theme.ts +4 -4
  39. package/lib/server/cssGenerator.test.ts +64 -1
  40. package/lib/server/cssGenerator.ts +48 -9
  41. package/lib/server/fileWatcher.test.ts +134 -0
  42. package/lib/server/fileWatcher.ts +100 -32
  43. package/lib/server/jsonLoader.ts +1 -0
  44. package/lib/server/providers/fileSystemCMSProvider.test.ts +163 -0
  45. package/lib/server/providers/fileSystemCMSProvider.ts +240 -19
  46. package/lib/server/routes/index.ts +1 -1
  47. package/lib/server/routes/pages.ts +23 -1
  48. package/lib/server/services/cmsService.test.ts +246 -0
  49. package/lib/server/services/cmsService.ts +122 -5
  50. package/lib/server/services/configService.ts +6 -0
  51. package/lib/server/services/fileWatcherService.ts +17 -0
  52. package/lib/server/ssr/attributeBuilder.ts +41 -0
  53. package/lib/server/ssr/htmlGenerator.test.ts +113 -0
  54. package/lib/server/ssr/htmlGenerator.ts +62 -7
  55. package/lib/server/ssr/liveReloadIntegration.test.ts +209 -0
  56. package/lib/server/ssr/ssrRenderer.test.ts +564 -0
  57. package/lib/server/ssr/ssrRenderer.ts +228 -49
  58. package/lib/server/webflow/buildWebflow.ts +1 -1
  59. package/lib/server/websocketManager.test.ts +61 -6
  60. package/lib/server/websocketManager.ts +25 -1
  61. package/lib/shared/cssGeneration.test.ts +267 -1
  62. package/lib/shared/cssGeneration.ts +240 -18
  63. package/lib/shared/cssProperties.test.ts +275 -1
  64. package/lib/shared/cssProperties.ts +223 -7
  65. package/lib/shared/interfaces/contentProvider.ts +39 -6
  66. package/lib/shared/pathSecurity.ts +16 -0
  67. package/lib/shared/responsiveScaling.test.ts +143 -0
  68. package/lib/shared/responsiveScaling.ts +253 -2
  69. package/lib/shared/themeDefaults.test.ts +3 -3
  70. package/lib/shared/themeDefaults.ts +3 -3
  71. package/lib/shared/types/api.ts +10 -1
  72. package/lib/shared/types/cms.ts +46 -12
  73. package/lib/shared/types/index.ts +1 -0
  74. package/lib/shared/utilityClassConfig.ts +3 -0
  75. package/lib/shared/utilityClassMapper.test.ts +123 -0
  76. package/lib/shared/utilityClassMapper.ts +179 -8
  77. package/lib/shared/validation/schemas.test.ts +93 -0
  78. package/lib/shared/validation/schemas.ts +71 -16
  79. package/lib/shared/validation/validators.ts +26 -1
  80. package/package.json +1 -1
  81. package/dist/chunks/chunk-3FHJUHAS.js.map +0 -7
  82. package/dist/chunks/chunk-B2RTLDXY.js.map +0 -7
  83. package/dist/chunks/chunk-BJRKEPMP.js.map +0 -7
  84. package/dist/chunks/chunk-NKUV77SR.js.map +0 -7
  85. package/dist/chunks/chunk-TPQ7APVQ.js.map +0 -7
  86. package/dist/chunks/chunk-UUA5LEWF.js.map +0 -7
  87. /package/dist/chunks/{chunk-D5E3OKSL.js.map → chunk-56EUSC6D.js.map} +0 -0
  88. /package/dist/chunks/{chunk-RQSTH2BS.js.map → chunk-H4JSCDNW.js.map} +0 -0
  89. /package/dist/chunks/{configService-IGJEC3MC.js.map → configService-VOY2MY2K.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  configService
3
- } from "./chunk-BJRKEPMP.js";
3
+ } from "./chunk-PQ2HRXDR.js";
4
4
  import {
5
5
  projectPaths,
6
6
  resolveProjectPath,
@@ -13,10 +13,12 @@ import {
13
13
  writeFile
14
14
  } from "./chunk-WQFG7PAH.js";
15
15
  import {
16
+ CMS_DRAFT_SUFFIX,
17
+ isReservedDraftFilename,
16
18
  isSafePathSegment,
17
19
  isValidIdentifier,
18
20
  resolvePaletteColor
19
- } from "./chunk-EK4KESLU.js";
21
+ } from "./chunk-J23ZX5AP.js";
20
22
  import {
21
23
  extractAttributesFromNode,
22
24
  isHtmlMapping,
@@ -24,7 +26,7 @@ import {
24
26
  processStructure,
25
27
  resolveHtmlMapping,
26
28
  skipEmptyTemplateAttributes
27
- } from "./chunk-NP76N4HQ.js";
29
+ } from "./chunk-EDQSMAMP.js";
28
30
  import {
29
31
  DEFAULT_PREFETCH_CONFIG,
30
32
  SSRRegistry,
@@ -61,20 +63,26 @@ import {
61
63
  resolveTemplateRawValue,
62
64
  responsiveStylesToClasses,
63
65
  singularize,
66
+ validateCMSDraftItem,
64
67
  validateCMSItem,
65
68
  validateComponentDefinition
66
- } from "./chunk-3FHJUHAS.js";
69
+ } from "./chunk-7NIC4I3V.js";
67
70
  import {
68
71
  DEFAULT_BREAKPOINTS,
72
+ DEFAULT_FLUID_RANGE,
69
73
  DEFAULT_I18N_CONFIG,
74
+ DEFAULT_SITE_MARGIN,
75
+ buildFluidPropertyValue,
70
76
  buildLocalizedPath,
77
+ buildSiteMarginClamp,
71
78
  extractLocaleFromPath,
79
+ getSmallestBreakpointName,
72
80
  isI18nValue,
73
81
  migrateI18nConfig,
74
82
  normalizeBreakpointConfig,
75
83
  resolveI18nValue,
76
84
  scalePropertyValue
77
- } from "./chunk-B2RTLDXY.js";
85
+ } from "./chunk-AZQYF6KE.js";
78
86
  import {
79
87
  isTiptapDocument,
80
88
  tiptapToHtml
@@ -717,6 +725,7 @@ var CMSService = class {
717
725
  schemaCache = /* @__PURE__ */ new Map();
718
726
  routePatterns = [];
719
727
  provider;
728
+ previewMode;
720
729
  /** Item cache with TTL-based expiration */
721
730
  itemsCache = /* @__PURE__ */ new Map();
722
731
  /** Cache TTL in milliseconds (5 seconds) */
@@ -724,9 +733,11 @@ var CMSService = class {
724
733
  /**
725
734
  * Creates a new CMSService instance
726
735
  * @param provider - Optional CMSProvider for loading data (enables DI for testing)
736
+ * @param options - Service-level flags (preview mode for dev server)
727
737
  */
728
- constructor(provider) {
738
+ constructor(provider, options = {}) {
729
739
  this.provider = provider;
740
+ this.previewMode = options.previewMode === true;
730
741
  }
731
742
  /**
732
743
  * Set the CMS provider
@@ -737,9 +748,14 @@ var CMSService = class {
737
748
  this.provider = provider;
738
749
  }
739
750
  /**
740
- * Get items with caching
741
- * Returns cached items if available and not expired, otherwise fetches fresh data
742
- * Rich-text fields are automatically preprocessed to HTML for template interpolation
751
+ * Get items with caching, used by SSR-facing read methods.
752
+ *
753
+ * In preview mode (dev server) drafts are merged over published — drafts
754
+ * win on a per-`_filename` basis and draft-only items are included — so
755
+ * the editor preview reflects unpublished edits. Returns published-only
756
+ * otherwise. Rich-text fields are preprocessed to HTML for template
757
+ * interpolation either way.
758
+ *
743
759
  * @param collection - Collection ID to fetch items for
744
760
  * @returns Array of CMSItems with rich-text fields converted to HTML markers
745
761
  */
@@ -749,7 +765,20 @@ var CMSService = class {
749
765
  if (cached && now - cached.timestamp < this.ITEMS_CACHE_TTL) {
750
766
  return cached.items;
751
767
  }
752
- const rawItems = await this.provider.getItems(collection);
768
+ let rawItems = await this.provider.getItems(collection);
769
+ if (this.previewMode) {
770
+ const drafts = await this.provider.getAllDrafts(collection);
771
+ if (drafts.length > 0) {
772
+ const byFilename = /* @__PURE__ */ new Map();
773
+ for (const item of rawItems) {
774
+ if (item._filename) byFilename.set(item._filename, item);
775
+ }
776
+ for (const draft of drafts) {
777
+ if (draft._filename) byFilename.set(draft._filename, draft);
778
+ }
779
+ rawItems = Array.from(byFilename.values());
780
+ }
781
+ }
753
782
  const items = this.preprocessRichTextFields(collection, rawItems);
754
783
  this.itemsCache.set(collection, { items, timestamp: now });
755
784
  return items;
@@ -987,6 +1016,71 @@ var CMSService = class {
987
1016
  * Only clears items cache and provider cache before re-initializing.
988
1017
  * Schema/route caches are swapped atomically inside initialize().
989
1018
  */
1019
+ // ----------------------------------------------------------------------
1020
+ // Draft-version methods (Studio-only — never used by SSR / static export)
1021
+ // ----------------------------------------------------------------------
1022
+ /**
1023
+ * Load both published and draft versions of an item, used by the editor.
1024
+ * Returns `{}` when neither version exists.
1025
+ */
1026
+ async getItemVersions(collection, filename) {
1027
+ if (!this.provider) return {};
1028
+ const [published, draft] = await Promise.all([
1029
+ this.provider.getItemByFilename(collection, filename),
1030
+ this.provider.getDraft(collection, filename)
1031
+ ]);
1032
+ const result = {};
1033
+ if (published) result.published = published;
1034
+ if (draft) result.draft = draft;
1035
+ return result;
1036
+ }
1037
+ /**
1038
+ * List all items in a collection for the Studio item list:
1039
+ * - Published items annotated with `_hasDraft: true` when a draft sibling exists.
1040
+ * - Draft-only items (no published file yet) returned with `_isDraft: true`.
1041
+ */
1042
+ async listItemsWithDraftFlag(collection) {
1043
+ if (!this.provider) return [];
1044
+ const [published, drafts] = await Promise.all([
1045
+ this.provider.getItems(collection),
1046
+ this.provider.getAllDrafts(collection)
1047
+ ]);
1048
+ const draftFilenames = new Set(drafts.map((d) => d._filename).filter(Boolean));
1049
+ const annotatedPublished = published.map((item) => {
1050
+ if (item._filename && draftFilenames.has(item._filename)) {
1051
+ return { ...item, _hasDraft: true };
1052
+ }
1053
+ return item;
1054
+ });
1055
+ const publishedFilenames = new Set(published.map((i) => i._filename).filter(Boolean));
1056
+ const draftOnly = drafts.filter((d) => d._filename && !publishedFilenames.has(d._filename));
1057
+ return [...annotatedPublished, ...draftOnly];
1058
+ }
1059
+ /** Pass-through to provider; no caching (drafts are read fresh in the editor). */
1060
+ async getDraft(collection, filename) {
1061
+ if (!this.provider) return null;
1062
+ return this.provider.getDraft(collection, filename);
1063
+ }
1064
+ async hasDraft(collection, filename) {
1065
+ if (!this.provider) return false;
1066
+ return this.provider.hasDraft(collection, filename);
1067
+ }
1068
+ async saveDraft(collection, item) {
1069
+ if (!this.provider) throw new Error("CMS provider not configured");
1070
+ await this.provider.saveDraft(collection, item);
1071
+ this.itemsCache.delete(collection);
1072
+ }
1073
+ async discardDraft(collection, filename) {
1074
+ if (!this.provider) throw new Error("CMS provider not configured");
1075
+ await this.provider.discardDraft(collection, filename);
1076
+ this.itemsCache.delete(collection);
1077
+ }
1078
+ async publishDraft(collection, filename) {
1079
+ if (!this.provider) throw new Error("CMS provider not configured");
1080
+ const item = await this.provider.publishDraft(collection, filename);
1081
+ this.itemsCache.delete(collection);
1082
+ return item;
1083
+ }
990
1084
  async refreshSchemas() {
991
1085
  if (!this.provider) {
992
1086
  return;
@@ -1295,20 +1389,45 @@ ${cssVars.join("\n")}
1295
1389
  return cssBlocks.join("\n\n");
1296
1390
  }
1297
1391
  function generateVariablesCSS(config, breakpoints, responsiveScales) {
1298
- if (!config.variables || config.variables.length === 0) {
1392
+ const cssBlocks = [];
1393
+ const fluidActive = responsiveScales?.enabled === true && responsiveScales?.mode === "fluid";
1394
+ const fluidRange = responsiveScales?.fluidRange ?? DEFAULT_FLUID_RANGE;
1395
+ const fluidBaseRef = responsiveScales?.baseReference || 16;
1396
+ const smallestBp = fluidActive ? getSmallestBreakpointName(breakpoints) : null;
1397
+ const baseVars = (config.variables ?? []).map((v) => {
1398
+ if (fluidActive && smallestBp && v.type !== "none") {
1399
+ const categoryScales = responsiveScales?.[v.type];
1400
+ const scale = categoryScales?.[smallestBp];
1401
+ if (scale != null && scale !== 1) {
1402
+ const fluid = buildFluidPropertyValue(
1403
+ v.value,
1404
+ scale,
1405
+ fluidRange.min,
1406
+ fluidRange.max,
1407
+ fluidBaseRef
1408
+ );
1409
+ if (fluid) return ` ${v.cssVar}: ${fluid};`;
1410
+ }
1411
+ }
1412
+ return ` ${v.cssVar}: ${v.value};`;
1413
+ });
1414
+ if (fluidActive) {
1415
+ const siteMargin = responsiveScales?.siteMargin ?? DEFAULT_SITE_MARGIN;
1416
+ baseVars.push(` --site-margin: ${buildSiteMarginClamp(siteMargin, fluidRange)};`);
1417
+ }
1418
+ if (baseVars.length === 0) {
1299
1419
  return "";
1300
1420
  }
1301
- const cssBlocks = [];
1302
- const baseVars = config.variables.map((v) => ` ${v.cssVar}: ${v.value};`);
1303
1421
  cssBlocks.push(`:root {
1304
1422
  ${baseVars.join("\n")}
1305
1423
  }`);
1306
- if (breakpoints && responsiveScales?.enabled) {
1424
+ const userVariables = config.variables ?? [];
1425
+ if (breakpoints && responsiveScales?.enabled && !fluidActive && userVariables.length > 0) {
1307
1426
  const baseRef = responsiveScales.baseReference || 16;
1308
1427
  const sortedBreakpoints = Object.entries(breakpoints).sort((a, b) => b[1].breakpoint - a[1].breakpoint);
1309
1428
  for (const [bpName, bpEntry] of sortedBreakpoints) {
1310
1429
  const scaledVars = [];
1311
- for (const variable of config.variables) {
1430
+ for (const variable of userVariables) {
1312
1431
  if (variable.scales && variable.scales[bpName]) {
1313
1432
  const overrideValue = variable.scales[bpName];
1314
1433
  if (overrideValue !== variable.value) {
@@ -1389,6 +1508,27 @@ function buildAttributes(props, exclude = []) {
1389
1508
  }
1390
1509
  return attrs.length > 0 ? " " + attrs.join(" ") : "";
1391
1510
  }
1511
+ function buildEditorAttrs(opts) {
1512
+ const { elementPath } = opts;
1513
+ if (!elementPath) return "";
1514
+ const parts = [`data-element-path="${escapeHtml(elementPath.join(","))}"`];
1515
+ if (opts.cmsItemIndexPath && opts.cmsItemIndexPath.length > 0) {
1516
+ parts.push(`data-cms-item-index="${escapeHtml(opts.cmsItemIndexPath.join("."))}"`);
1517
+ if (opts.cmsListPaths && opts.cmsListPaths.length === opts.cmsItemIndexPath.length) {
1518
+ const ctx = JSON.stringify({ itemIndexPath: opts.cmsItemIndexPath, listPaths: opts.cmsListPaths });
1519
+ parts.push(`data-cms-context="${escapeHtml(ctx)}"`);
1520
+ }
1521
+ }
1522
+ if (opts.isCMSListContainer) parts.push(`data-cms-list="true"`);
1523
+ if (opts.isComponentRoot) parts.push(`data-component-root="true"`);
1524
+ if (opts.parentComponentName) {
1525
+ parts.push(`data-parent-component="${escapeHtml(opts.parentComponentName)}"`);
1526
+ }
1527
+ if (opts.componentContext && !opts.isSlotContent) {
1528
+ parts.push(`data-component-context="${escapeHtml(opts.componentContext)}"`);
1529
+ }
1530
+ return " " + parts.join(" ");
1531
+ }
1392
1532
  function styleToString(style) {
1393
1533
  if (!style || Object.keys(style).length === 0) return "";
1394
1534
  const declarations = [];
@@ -1898,9 +2038,27 @@ function getDOMPurify() {
1898
2038
  }
1899
2039
  return _DOMPurify;
1900
2040
  }
2041
+ function resolveI18nAttrs(attrs, locale, i18nConfig) {
2042
+ let mutated = null;
2043
+ const config = i18nConfig ?? DEFAULT_I18N_CONFIG;
2044
+ const effectiveLocale = locale || config.defaultLocale;
2045
+ for (const [key, value] of Object.entries(attrs)) {
2046
+ if (isI18nValue(value)) {
2047
+ mutated = mutated ?? { ...attrs };
2048
+ mutated[key] = resolveI18nValue(value, effectiveLocale, config);
2049
+ }
2050
+ }
2051
+ return mutated ?? attrs;
2052
+ }
1901
2053
  function getTemplateContext(ctx) {
1902
2054
  return ctx.templateContext || null;
1903
2055
  }
2056
+ function buildListResolutionScope(ctx) {
2057
+ const tplCtx = ctx.templateContext;
2058
+ const props = ctx.componentResolvedProps;
2059
+ if (!tplCtx && !props) return void 0;
2060
+ return { ...props ?? {}, ...tplCtx ?? {} };
2061
+ }
1904
2062
  function getI18nResolver(ctx) {
1905
2063
  return createI18nResolver(ctx.locale, ctx.i18nConfig);
1906
2064
  }
@@ -1939,7 +2097,11 @@ function processStyleToClasses(style, ctx) {
1939
2097
  getI18nResolver(ctx)
1940
2098
  );
1941
2099
  }
1942
- return responsiveStylesToClasses(processedStyle);
2100
+ const fluidActive = ctx.responsiveScales?.enabled === true && ctx.responsiveScales?.mode === "fluid";
2101
+ return responsiveStylesToClasses(
2102
+ processedStyle,
2103
+ { fluidActive, responsiveScales: ctx.responsiveScales }
2104
+ );
1943
2105
  }
1944
2106
  function evaluateIfCondition(node, ctx) {
1945
2107
  const ifValue = hasIf(node) ? node.if : void 0;
@@ -1971,31 +2133,31 @@ function evaluateIfCondition(node, ctx) {
1971
2133
  }
1972
2134
  return true;
1973
2135
  }
1974
- function resolveFilterValue(value, templateContext) {
1975
- if (!templateContext || typeof value !== "string" || !value.startsWith("{{") || !value.endsWith("}}")) {
2136
+ function resolveFilterValue(value, scope) {
2137
+ if (!scope || typeof value !== "string" || !value.startsWith("{{") || !value.endsWith("}}")) {
1976
2138
  return value;
1977
2139
  }
1978
2140
  const path2 = value.slice(2, -2).trim();
1979
- const resolved = getNestedValue(templateContext, path2);
2141
+ const resolved = getNestedValue(scope, path2);
1980
2142
  return resolved !== void 0 ? resolved : value;
1981
2143
  }
1982
- function resolveFilterTemplates(filter, templateContext) {
1983
- if (!filter || !templateContext) return filter;
2144
+ function resolveFilterTemplates(filter, scope) {
2145
+ if (!filter || !scope) return filter;
1984
2146
  if (Array.isArray(filter)) {
1985
2147
  return filter.map((cond) => ({
1986
2148
  ...cond,
1987
- value: resolveFilterValue(cond.value, templateContext)
2149
+ value: resolveFilterValue(cond.value, scope)
1988
2150
  }));
1989
2151
  }
1990
2152
  if ("field" in filter && "value" in filter) {
1991
2153
  return {
1992
2154
  ...filter,
1993
- value: resolveFilterValue(filter.value, templateContext)
2155
+ value: resolveFilterValue(filter.value, scope)
1994
2156
  };
1995
2157
  }
1996
2158
  const resolved = {};
1997
2159
  for (const [key, value] of Object.entries(filter)) {
1998
- resolved[key] = resolveFilterValue(value, templateContext);
2160
+ resolved[key] = resolveFilterValue(value, scope);
1999
2161
  }
2000
2162
  return resolved;
2001
2163
  }
@@ -2032,7 +2194,7 @@ async function expandRichTextComponents(html, ctx) {
2032
2194
  return resolved.join("");
2033
2195
  }
2034
2196
  var ssrComponentRegistry = new SSRRegistry();
2035
- async function buildComponentHTML(node, globalComponents = {}, pageComponents = {}, locale, i18nConfig, slugMappings, pagePath, cmsContext, cmsService, isProductionBuild) {
2197
+ async function buildComponentHTML(node, globalComponents = {}, pageComponents = {}, locale, i18nConfig, slugMappings, pagePath, cmsContext, cmsService, isProductionBuild, injectEditorAttrs) {
2036
2198
  const interactiveStylesMap = /* @__PURE__ */ new Map();
2037
2199
  const preloadImages = [];
2038
2200
  const neededCollections = /* @__PURE__ */ new Set();
@@ -2067,7 +2229,9 @@ async function buildComponentHTML(node, globalComponents = {}, pageComponents =
2067
2229
  // Collect SSR fallback HTML for complex nodes
2068
2230
  processedRawHtmlCollector,
2069
2231
  // Collect raw→processed HTML for Astro exporter
2070
- imageFormat: configService.getImageFormat()
2232
+ imageFormat: configService.getImageFormat(),
2233
+ injectEditorAttrs,
2234
+ responsiveScales: configService.getResponsiveScales()
2071
2235
  };
2072
2236
  const html = await renderNode(node, ctx);
2073
2237
  return { html, interactiveStylesMap, preloadImages, neededCollections, ssrFallbackCollector, processedRawHtmlCollector };
@@ -2098,7 +2262,7 @@ async function renderNestedListPlaceholder(node, ctx) {
2098
2262
  };
2099
2263
  const templateContent = node.children ? await renderChildrenAsync(node.children, childTemplateCtx) : "";
2100
2264
  const configJson = escapeHtml(JSON.stringify(config));
2101
- return `<div data-cms-list-nested="true" data-collection="${escapeHtml(sourceStr)}" data-cms-config="${configJson}"><template data-nested-template>${templateContent}</template></div>`;
2265
+ return `<div data-cms-list-nested="true" data-collection="${escapeHtml(sourceStr)}" data-cms-config="${configJson}"${editorAttrs(ctx, { isCMSListContainer: true })}><template data-nested-template>${templateContent}</template></div>`;
2102
2266
  }
2103
2267
  async function processList(node, ctx) {
2104
2268
  const nodeType = node.type;
@@ -2157,7 +2321,9 @@ async function processList(node, ctx) {
2157
2321
  );
2158
2322
  const itemCtx = {
2159
2323
  ...ctx,
2160
- templateContext
2324
+ templateContext,
2325
+ cmsItemIndexPath: [...ctx.cmsItemIndexPath ?? [], i],
2326
+ cmsListPaths: [...ctx.cmsListPaths ?? [], ctx.elementPath ?? []]
2161
2327
  };
2162
2328
  const childrenHtml = await renderChildrenAsync(node.children || [], itemCtx);
2163
2329
  renderedItems.push(childrenHtml);
@@ -2201,7 +2367,8 @@ async function getCollectionItems(node, source, ctx) {
2201
2367
  resolvedIds = Array.isArray(value) ? value.map((v) => String(v)) : String(value);
2202
2368
  }
2203
2369
  } else {
2204
- const parentContext = ctx.templateContext || { _type: "template" };
2370
+ const mergedScope = buildListResolutionScope(ctx) ?? {};
2371
+ const parentContext = { _type: "template", ...mergedScope };
2205
2372
  resolvedIds = resolveItemsTemplate(node.items, parentContext);
2206
2373
  }
2207
2374
  if (!resolvedIds) {
@@ -2219,7 +2386,7 @@ async function getCollectionItems(node, source, ctx) {
2219
2386
  } else {
2220
2387
  const query = {
2221
2388
  collection: source,
2222
- filter: resolveFilterTemplates(node.filter, ctx.templateContext),
2389
+ filter: resolveFilterTemplates(node.filter, buildListResolutionScope(ctx)),
2223
2390
  sort: node.sort,
2224
2391
  limit: node.limit,
2225
2392
  offset: node.offset,
@@ -2274,11 +2441,13 @@ function buildNodeElementClass(ctx, label, isSlotContent2) {
2274
2441
  const useComponentContext = !isSlotContent2 && Boolean(ctx.componentContext);
2275
2442
  const effectiveFileType = useComponentContext ? "component" : "page";
2276
2443
  const effectiveFileName = useComponentContext ? ctx.componentContext : pagePath ? pagePath.replace(/^\//, "").replace(/\//g, "_") || "index" : "page";
2444
+ const rawPath = ctx.elementPath || [];
2445
+ const path2 = useComponentContext && ctx.componentRootPath ? rawPath.slice(ctx.componentRootPath.length) : rawPath;
2277
2446
  const elementClassCtx = {
2278
2447
  fileType: effectiveFileType,
2279
2448
  fileName: effectiveFileName || "page",
2280
2449
  label,
2281
- path: ctx.elementPath || []
2450
+ path: path2
2282
2451
  };
2283
2452
  return generateElementClassName(elementClassCtx);
2284
2453
  }
@@ -2298,6 +2467,26 @@ function buildCssVariableStyleAttr(cssVariables) {
2298
2467
  const styleString = Object.entries(cssVariables).map(([k, v]) => `${k}: ${v}`).join("; ");
2299
2468
  return ` style="${escapeHtml(styleString)}"`;
2300
2469
  }
2470
+ function arraysEqual(a, b) {
2471
+ if (!a || !b || a.length !== b.length) return false;
2472
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
2473
+ return true;
2474
+ }
2475
+ function editorAttrs(ctx, opts = {}) {
2476
+ if (!ctx.injectEditorAttrs) return "";
2477
+ const isComponentRoot = !!ctx.componentContext && !opts.isSlotContent && arraysEqual(ctx.elementPath, ctx.componentRootPath);
2478
+ const effectiveParent = opts.isSlotContent ? ctx.parentComponentName : isComponentRoot ? ctx.parentComponentName : ctx.componentContext ?? ctx.parentComponentName;
2479
+ return buildEditorAttrs({
2480
+ elementPath: ctx.elementPath,
2481
+ cmsItemIndexPath: ctx.cmsItemIndexPath,
2482
+ cmsListPaths: ctx.cmsListPaths,
2483
+ componentContext: ctx.componentContext,
2484
+ parentComponentName: effectiveParent,
2485
+ isComponentRoot,
2486
+ isSlotContent: opts.isSlotContent,
2487
+ isCMSListContainer: opts.isCMSListContainer
2488
+ });
2489
+ }
2301
2490
  async function renderNode(node, ctx) {
2302
2491
  const { breakpoints, viewportWidth, locale, i18nConfig, slugMappings, pagePath } = ctx;
2303
2492
  if (node === null || node === void 0) return "";
@@ -2343,6 +2532,15 @@ async function renderNode(node, ctx) {
2343
2532
  }))).join("");
2344
2533
  }
2345
2534
  if (typeof node !== "object") return "";
2535
+ if (isI18nValue(node)) {
2536
+ const i18nResolveConfig = i18nConfig ?? DEFAULT_I18N_CONFIG;
2537
+ const i18nEffectiveLocale = locale || i18nResolveConfig.defaultLocale;
2538
+ const resolved = resolveI18nValue(node, i18nEffectiveLocale, i18nResolveConfig);
2539
+ return renderNode(
2540
+ resolved,
2541
+ ctx
2542
+ );
2543
+ }
2346
2544
  if (!evaluateIfCondition(node, ctx)) {
2347
2545
  return "";
2348
2546
  }
@@ -2382,7 +2580,7 @@ async function renderNode(node, ctx) {
2382
2580
  KEEP_CONTENT: true
2383
2581
  }) : htmlContent;
2384
2582
  const optimizedHtml = ctx.imageMetadataMap ? rewriteRichTextImages(sanitizedHtml, ctx.imageMetadataMap, ctx.imageFormat) : sanitizedHtml;
2385
- const nodeAttributes2 = extractAttributesFromNode(node);
2583
+ const nodeAttributes2 = resolveI18nAttrs(extractAttributesFromNode(node), locale, i18nConfig);
2386
2584
  const classNames = ["oem"];
2387
2585
  if (nodeStyle) {
2388
2586
  const utilityClasses2 = processStyleToClasses(nodeStyle, ctx);
@@ -2406,7 +2604,7 @@ async function renderNode(node, ctx) {
2406
2604
  delete nodeAttributes2.class;
2407
2605
  const attrs2 = buildAttributes(nodeAttributes2);
2408
2606
  const classAttr2 = classNames.length > 0 ? ` class="${escapeHtml(classNames.filter(Boolean).join(" "))}"` : "";
2409
- return `<span${classAttr2}${embedStyleAttr}${attrs2}>${optimizedHtml}</span>`;
2607
+ return `<span${classAttr2}${embedStyleAttr}${attrs2}${editorAttrs(ctx, { isSlotContent: isSlotContent(node) })}>${optimizedHtml}</span>`;
2410
2608
  }
2411
2609
  const attrClassName2 = nodeAttributes2.className || nodeAttributes2.class || "";
2412
2610
  if (attrClassName2) {
@@ -2416,7 +2614,7 @@ async function renderNode(node, ctx) {
2416
2614
  delete nodeAttributes2.class;
2417
2615
  const attrs = buildAttributes(nodeAttributes2);
2418
2616
  const classAttr = classNames.length > 0 ? ` class="${escapeHtml(classNames.filter(Boolean).join(" "))}"` : "";
2419
- return `<span${classAttr}${attrs}>${optimizedHtml}</span>`;
2617
+ return `<span${classAttr}${attrs}${editorAttrs(ctx, { isSlotContent: isSlotContent(node) })}>${optimizedHtml}</span>`;
2420
2618
  }
2421
2619
  if (isLinkNode(node)) {
2422
2620
  let href = typeof node.href === "string" ? node.href : "#";
@@ -2441,7 +2639,7 @@ async function renderNode(node, ctx) {
2441
2639
  href = processCMSTemplate(href, ctx.cmsContext.cms, locale, i18nConfig);
2442
2640
  }
2443
2641
  href = localizeHref(href, ctx);
2444
- const nodeAttributes2 = extractAttributesFromNode(node);
2642
+ const nodeAttributes2 = resolveI18nAttrs(extractAttributesFromNode(node), locale, i18nConfig);
2445
2643
  const classNames = ["olink"];
2446
2644
  if (nodeStyle) {
2447
2645
  const utilityClasses2 = processStyleToClasses(nodeStyle, ctx);
@@ -2477,7 +2675,7 @@ async function renderNode(node, ctx) {
2477
2675
  const childPath = ctx.elementPath ? [...ctx.elementPath, index] : [index];
2478
2676
  return renderNode(child, { ...ctx, elementPath: childPath });
2479
2677
  }))).join("") : await renderNode(children, { ...ctx, elementPath: ctx.elementPath ? [...ctx.elementPath, 0] : [0] });
2480
- return `<a href="${escapeHtml(String(href))}"${classAttr}${olinkStyleAttr}${attrs}>${childrenHTML}</a>`;
2678
+ return `<a href="${escapeHtml(String(href))}"${classAttr}${olinkStyleAttr}${attrs}${editorAttrs(ctx, { isSlotContent: isSlotContent(node) })}>${childrenHTML}</a>`;
2481
2679
  }
2482
2680
  if (isLocaleListNode(node)) {
2483
2681
  return renderLocaleList(node, ctx);
@@ -2493,7 +2691,7 @@ async function renderNode(node, ctx) {
2493
2691
  if (!ctx.templateMode && templateCtx && Object.keys(nodeProps).length > 0) {
2494
2692
  nodeProps = processItemPropsTemplate(nodeProps, templateCtx, i18nResolver);
2495
2693
  }
2496
- let nodeAttributes = extractAttributesFromNode(node);
2694
+ let nodeAttributes = resolveI18nAttrs(extractAttributesFromNode(node), locale, i18nConfig);
2497
2695
  const originalAttributes = { ...nodeAttributes };
2498
2696
  if (ctx.cmsContext?.cms && Object.keys(nodeAttributes).length > 0) {
2499
2697
  nodeAttributes = processCMSPropsTemplate(nodeAttributes, ctx.cmsContext.cms, locale, i18nConfig);
@@ -2651,8 +2849,10 @@ async function renderComponent(componentName, propsWithStyleAndAttrs, children,
2651
2849
  }
2652
2850
  return await renderNode(processedStructure, {
2653
2851
  ...ctx,
2852
+ // The previously-active component (if any) becomes the parent for editor attrs
2853
+ parentComponentName: ctx.componentContext,
2654
2854
  componentContext: componentName,
2655
- elementPath: [0],
2855
+ componentRootPath: ctx.elementPath,
2656
2856
  componentResolvedProps: resolvedProps
2657
2857
  });
2658
2858
  } catch (error) {
@@ -2672,7 +2872,7 @@ async function renderLinkNode(propsWithStyleAndAttrs, children, ctx) {
2672
2872
  const childPath = ctx.elementPath ? [...ctx.elementPath, index] : [index];
2673
2873
  return renderNode(child, { ...ctx, elementPath: childPath });
2674
2874
  }))).join("") : await renderNode(children, { ...ctx, elementPath: ctx.elementPath ? [...ctx.elementPath, 0] : [0] });
2675
- return `<a href="${escapeHtml(String(href))}"${linkClassAttr}${attrs}>${childrenHTML}</a>`;
2875
+ return `<a href="${escapeHtml(String(href))}"${linkClassAttr}${attrs}${editorAttrs(ctx)}>${childrenHTML}</a>`;
2676
2876
  }
2677
2877
  async function renderHtmlElement(tag, propsWithStyleAndAttrs, children, ctx) {
2678
2878
  let classValue = propsWithStyleAndAttrs.className ? String(propsWithStyleAndAttrs.className) : "";
@@ -2694,18 +2894,32 @@ async function renderHtmlElement(tag, propsWithStyleAndAttrs, children, ctx) {
2694
2894
  const imageProps = ["src", "alt", "loading", "width", "height", "sizes", "srcset", "fetchpriority"];
2695
2895
  const excludeProps = tag.toLowerCase() === "img" ? ["style", "className", ...imageProps] : ["style", "className"];
2696
2896
  const attrs = buildAttributes(propsWithStyleAndAttrs, excludeProps);
2697
- const childrenHTML = Array.isArray(children) ? (await Promise.all(children.map((child, index) => {
2698
- const childPath = ctx.elementPath ? [...ctx.elementPath, index] : [index];
2699
- return renderNode(child, { ...ctx, elementPath: childPath });
2700
- }))).join("") : await renderNode(children, { ...ctx, elementPath: ctx.elementPath ? [...ctx.elementPath, 0] : [0] });
2897
+ const isRawTextElement = tag.toLowerCase() === "style" || tag.toLowerCase() === "script";
2898
+ let childrenHTML;
2899
+ if (isRawTextElement) {
2900
+ const flatten = (node) => {
2901
+ if (node == null) return "";
2902
+ if (typeof node === "string") return node;
2903
+ if (typeof node === "number") return String(node);
2904
+ if (Array.isArray(node)) return node.map(flatten).join("");
2905
+ return "";
2906
+ };
2907
+ childrenHTML = flatten(children);
2908
+ } else {
2909
+ childrenHTML = Array.isArray(children) ? (await Promise.all(children.map((child, index) => {
2910
+ const childPath = ctx.elementPath ? [...ctx.elementPath, index] : [index];
2911
+ return renderNode(child, { ...ctx, elementPath: childPath });
2912
+ }))).join("") : await renderNode(children, { ...ctx, elementPath: ctx.elementPath ? [...ctx.elementPath, 0] : [0] });
2913
+ }
2914
+ const ea = editorAttrs(ctx);
2701
2915
  const voidElements = ["img", "input", "br", "hr", "meta", "link", "area", "base", "col", "embed", "source", "track", "wbr"];
2702
2916
  if (voidElements.includes(tag.toLowerCase())) {
2703
2917
  if (tag.toLowerCase() === "img") {
2704
- return renderImageElement(propsWithStyleAndAttrs, classAttr, styleAttr, attrs, ctx);
2918
+ return renderImageElement(propsWithStyleAndAttrs, classAttr, styleAttr, attrs + ea, ctx);
2705
2919
  }
2706
- return `<${tag}${classAttr}${styleAttr}${attrs} />`;
2920
+ return `<${tag}${classAttr}${styleAttr}${attrs}${ea} />`;
2707
2921
  }
2708
- return `<${tag}${classAttr}${styleAttr}${attrs}>${childrenHTML}</${tag}>`;
2922
+ return `<${tag}${classAttr}${styleAttr}${attrs}${ea}>${childrenHTML}</${tag}>`;
2709
2923
  }
2710
2924
  function renderImageElement(propsWithStyleAndAttrs, classAttr, styleAttr, attrs, ctx) {
2711
2925
  const imgProps = propsWithStyleAndAttrs;
@@ -2793,9 +3007,13 @@ function renderLocaleList(node, ctx) {
2793
3007
  localeIconMap.set(localeConfig.code, localeConfig.icon);
2794
3008
  }
2795
3009
  }
3010
+ const localeStyleOpts = {
3011
+ fluidActive: ctx.responsiveScales?.enabled === true && ctx.responsiveScales?.mode === "fluid",
3012
+ responsiveScales: ctx.responsiveScales
3013
+ };
2796
3014
  let containerClasses = [];
2797
3015
  if (nodeStyle) {
2798
- containerClasses = responsiveStylesToClasses(nodeStyle);
3016
+ containerClasses = responsiveStylesToClasses(nodeStyle, localeStyleOpts);
2799
3017
  }
2800
3018
  const localeListInteractiveStyles = node.interactiveStyles;
2801
3019
  const localeListGenerateElementClass = node.generateElementClass;
@@ -2812,21 +3030,21 @@ function renderLocaleList(node, ctx) {
2812
3030
  const containerClassAttr = containerClasses.length > 0 ? ` class="${escapeHtml(containerClasses.join(" "))}"` : "";
2813
3031
  let itemClasses = [];
2814
3032
  if (node.itemStyle) {
2815
- itemClasses = responsiveStylesToClasses(node.itemStyle);
3033
+ itemClasses = responsiveStylesToClasses(node.itemStyle, localeStyleOpts);
2816
3034
  }
2817
3035
  const itemClassAttr = itemClasses.length > 0 ? ` class="${escapeHtml(itemClasses.join(" "))}"` : "";
2818
3036
  let activeItemClasses = [];
2819
3037
  if (node.activeItemStyle) {
2820
- activeItemClasses = responsiveStylesToClasses(node.activeItemStyle);
3038
+ activeItemClasses = responsiveStylesToClasses(node.activeItemStyle, localeStyleOpts);
2821
3039
  }
2822
3040
  let separatorClasses = [];
2823
3041
  if (node.separatorStyle) {
2824
- separatorClasses = responsiveStylesToClasses(node.separatorStyle);
3042
+ separatorClasses = responsiveStylesToClasses(node.separatorStyle, localeStyleOpts);
2825
3043
  }
2826
3044
  const separatorClassAttr = separatorClasses.length > 0 ? ` class="${escapeHtml(separatorClasses.join(" "))}"` : "";
2827
3045
  let flagClasses = [];
2828
3046
  if (node.flagStyle) {
2829
- flagClasses = responsiveStylesToClasses(node.flagStyle);
3047
+ flagClasses = responsiveStylesToClasses(node.flagStyle, localeStyleOpts);
2830
3048
  }
2831
3049
  const flagClassAttr = flagClasses.length > 0 ? ` class="${escapeHtml(flagClasses.join(" "))}"` : "";
2832
3050
  const currentItemClasses = [...itemClasses, ...activeItemClasses];
@@ -2859,9 +3077,9 @@ function renderLocaleList(node, ctx) {
2859
3077
  links.push(`<a href="${escapeHtml(link.path)}"${hreflangAttr}${currentAttr} data-locale="${escapeHtml(link.locale)}"${classAttrForLink}>${linkContent}</a>`);
2860
3078
  }
2861
3079
  const linksHTML = showSeparator ? links.join(`<span${separatorClassAttr}></span>`) : links.join("");
2862
- const nodeAttributes = extractAttributesFromNode(node);
3080
+ const nodeAttributes = resolveI18nAttrs(extractAttributesFromNode(node), locale, i18nConfig);
2863
3081
  const attrsStr = buildAttributes(nodeAttributes);
2864
- const localeListResult = `<div data-locale-list="true"${containerClassAttr}${localeListStyleAttr}${attrsStr}>${linksHTML}</div>`;
3082
+ const localeListResult = `<div data-locale-list="true"${containerClassAttr}${localeListStyleAttr}${attrsStr}${editorAttrs(ctx)}>${linksHTML}</div>`;
2865
3083
  if (ctx.ssrFallbackCollector && ctx.elementPath) {
2866
3084
  ctx.ssrFallbackCollector.set(ctx.elementPath.join("."), localeListResult);
2867
3085
  }
@@ -2869,7 +3087,7 @@ function renderLocaleList(node, ctx) {
2869
3087
  }
2870
3088
  return '<div data-locale-list="true"></div>';
2871
3089
  }
2872
- async function renderPageSSR(pageData, globalComponents = {}, pagePath = "/", baseUrl = "", locale, i18nConfig, slugMappings, cmsContext, cmsService, isProductionBuild) {
3090
+ async function renderPageSSR(pageData, globalComponents = {}, pagePath = "/", baseUrl = "", locale, i18nConfig, slugMappings, cmsContext, cmsService, isProductionBuild, injectEditorAttrs) {
2873
3091
  const rootNode = pageData?.root || void 0;
2874
3092
  if (!rootNode) {
2875
3093
  throw new Error("Page data must have a root node");
@@ -2893,7 +3111,7 @@ async function renderPageSSR(pageData, globalComponents = {}, pagePath = "/", ba
2893
3111
  }
2894
3112
  }
2895
3113
  const pageComponents = pageData?.components || {};
2896
- const { html: contentHTML, interactiveStylesMap, preloadImages, neededCollections, ssrFallbackCollector, processedRawHtmlCollector } = rootNode ? await buildComponentHTML(rootNode, globalComponents, pageComponents, effectiveLocale, config, slugMappings, pagePath, cmsContext, cmsService, isProductionBuild) : { html: "", interactiveStylesMap: /* @__PURE__ */ new Map(), preloadImages: [], neededCollections: /* @__PURE__ */ new Set(), ssrFallbackCollector: /* @__PURE__ */ new Map(), processedRawHtmlCollector: /* @__PURE__ */ new Map() };
3114
+ const { html: contentHTML, interactiveStylesMap, preloadImages, neededCollections, ssrFallbackCollector, processedRawHtmlCollector } = rootNode ? await buildComponentHTML(rootNode, globalComponents, pageComponents, effectiveLocale, config, slugMappings, pagePath, cmsContext, cmsService, isProductionBuild, injectEditorAttrs) : { html: "", interactiveStylesMap: /* @__PURE__ */ new Map(), preloadImages: [], neededCollections: /* @__PURE__ */ new Set(), ssrFallbackCollector: /* @__PURE__ */ new Map(), processedRawHtmlCollector: /* @__PURE__ */ new Map() };
2897
3115
  const javascript = await collectComponentJavaScript(globalComponents, pageComponents);
2898
3116
  const componentCSS = collectComponentCSS(globalComponents, pageComponents);
2899
3117
  const fullUrl = baseUrl ? `${baseUrl}${pagePath}` : pagePath;
@@ -3217,7 +3435,7 @@ function generateImagePreloadTags(preloadImages) {
3217
3435
  }
3218
3436
  function minifyCSS(code) {
3219
3437
  if (!code.trim()) return code;
3220
- return code.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\s*([{};:,>~+])\s*/g, "$1").replace(/\s+/g, " ").replace(/\{\s+/g, "{").replace(/\s+\}/g, "}").replace(/;}/g, "}").trim();
3438
+ return code.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\s*([{};:,>~])\s*/g, "$1").replace(/\s+/g, " ").replace(/\{\s+/g, "{").replace(/\s+\}/g, "}").replace(/;}/g, "}").trim();
3221
3439
  }
3222
3440
  async function generateSSRHTML(pageDataOrOptions, globalComponents = {}, pagePath = "/", baseUrl = "", useBuiltBundle = false, locale, slugMappings, cmsContext, cmsService, externalScriptPath) {
3223
3441
  let options;
@@ -3254,11 +3472,12 @@ async function generateSSRHTML(pageDataOrOptions, globalComponents = {}, pagePat
3254
3472
  pageCustomCode,
3255
3473
  clientDataCollections,
3256
3474
  injectLiveReload = false,
3475
+ injectEditorAttrs = false,
3257
3476
  isEditor = false,
3258
3477
  isProductionBuild = false,
3259
3478
  serverPort
3260
3479
  } = options;
3261
- const rendered = await renderPageSSR(pageData, components, path2, base, loc, void 0, slugs, cms, cmsServ, isProductionBuild);
3480
+ const rendered = await renderPageSSR(pageData, components, path2, base, loc, void 0, slugs, cms, cmsServ, isProductionBuild, injectEditorAttrs);
3262
3481
  let finalClientDataCollections = clientDataCollections;
3263
3482
  if (rendered.neededCollections.size > 0 && cmsServ) {
3264
3483
  finalClientDataCollections = clientDataCollections ? new Map(clientDataCollections) : /* @__PURE__ */ new Map();
@@ -3434,13 +3653,15 @@ picture {
3434
3653
  const cmsInlineScript = cmsTemplatePath && cms && (!useBundled || injectLiveReload) ? `<script>window.__MENO_CMS__=${JSON.stringify({ item: cms.cms, templatePath: cmsTemplatePath })}</script>
3435
3654
  ` : "";
3436
3655
  const clientDataScripts = finalClientDataCollections && finalClientDataCollections.size > 0 ? generateAllInlineDataScripts(finalClientDataCollections) + "\n " : "";
3437
- const faviconTag = iconsConfig.favicon ? `<link rel="icon" href="${escapeHtml(iconsConfig.favicon)}" />` : "";
3656
+ const hasDarkFavicon = !!(iconsConfig.favicon && iconsConfig.faviconDark);
3657
+ const faviconTag = iconsConfig.favicon ? `<link rel="icon" href="${escapeHtml(iconsConfig.favicon)}"${hasDarkFavicon ? ' media="(prefers-color-scheme: light)"' : ""} />` : "";
3658
+ const faviconDarkTag = iconsConfig.faviconDark ? `<link rel="icon" href="${escapeHtml(iconsConfig.faviconDark)}" media="(prefers-color-scheme: dark)" />` : "";
3438
3659
  const appleTouchIconTag = iconsConfig.appleTouchIcon ? `<link rel="apple-touch-icon" href="${escapeHtml(iconsConfig.appleTouchIcon)}" />` : "";
3439
- const iconTags = [faviconTag, appleTouchIconTag].filter(Boolean).join("\n ");
3660
+ const iconTags = [faviconTag, faviconDarkTag, appleTouchIconTag].filter(Boolean).join("\n ");
3440
3661
  const scriptPreloadTag = extScriptPath ? `<link rel="preload" href="${extScriptPath}" as="script">` : "";
3441
3662
  const imagePreloadTags = generateImagePreloadTags(rendered.preloadImages);
3442
3663
  const wsUrl = serverPort ? `'ws://localhost:${serverPort}/hmr'` : `location.origin.replace('http','ws')+'/hmr'`;
3443
- const liveReloadScript = injectLiveReload ? `<script>(function(){var ws,timer,gen=0;function connect(){ws=new WebSocket(${wsUrl});ws.onmessage=function(e){var d=JSON.parse(e.data);if(d.type==='hmr:libraries-update'){location.reload()}else if(d.type==='hmr:update'||d.type==='hmr:cms-update'||d.type==='hmr:colors-update'||d.type==='hmr:variables-update')hotReload()};ws.onclose=function(){clearTimeout(timer);timer=setTimeout(connect,1000)}}function hotReload(){var g=++gen;var sx=window.scrollX,sy=window.scrollY;fetch(location.href,{cache:'no-store'}).then(function(r){return r.text()}).then(function(html){if(g!==gen)return;var p=new DOMParser();var d=p.parseFromString(html,'text/html');var or=document.getElementById('root');var nr=d.getElementById('root');if(or&&nr)or.innerHTML=nr.innerHTML;var os=document.getElementById('meno-styles');var ns=d.getElementById('meno-styles');if(os&&ns){os.parentNode.replaceChild(ns.cloneNode(true),os)}var nh=d.documentElement;if(nh){document.documentElement.setAttribute('lang',nh.getAttribute('lang')||'en');document.documentElement.setAttribute('theme',nh.getAttribute('theme')||'light')}document.querySelectorAll('script[id^="meno-cms-"]').forEach(function(s){s.remove()});d.querySelectorAll('script[id^="meno-cms-"]').forEach(function(s){var c=document.createElement('script');c.type=s.type;c.id=s.id;c.textContent=s.textContent;document.head.appendChild(c)});window.__menoHotReload=true;document.querySelectorAll('body > script[src^="/libraries/"]').forEach(function(o){o.remove()});d.querySelectorAll('body > script[src^="/libraries/"]').forEach(function(n){var ls=document.createElement('script');ls.src=n.getAttribute('src')+(n.getAttribute('src').indexOf('?')>-1?'&':'?')+'_r='+Date.now();document.body.appendChild(ls)});var oscr=document.querySelector('script[src^="/_scripts/"]');var nscr=d.querySelector('script[src^="/_scripts/"]');if(nscr){var src=nscr.getAttribute('src');if(oscr)oscr.remove();var s=document.createElement('script');s.src=src+(src.indexOf('?')>-1?'&':'?')+'_r='+Date.now();s.onload=function(){document.dispatchEvent(new Event('DOMContentLoaded'));window.scrollTo(sx,sy)};s.onerror=function(){window.scrollTo(sx,sy)};document.body.appendChild(s)}else{if(oscr)oscr.remove();document.dispatchEvent(new Event('DOMContentLoaded'));window.scrollTo(sx,sy)}}).catch(function(){location.reload()})}connect()})()</script>` : "";
3664
+ const liveReloadScript = injectLiveReload ? `<script>(function(){var ws,timer,gen=0,lastSrvRoot=null;function strip(s){return s?s.replace(/[?&]_r=\\d+/,''):''}function classList(el){return (el.getAttribute('class')||'').split(/\\s+/).filter(Boolean)}function syncEl(cur,srv,old){var cc=classList(cur),sc=classList(srv),oc=old?new Set(classList(old)):new Set();var rt=cc.filter(function(c){return !oc.has(c)});var seen=new Set(),fin=[];sc.concat(rt).forEach(function(c){if(!seen.has(c)){seen.add(c);fin.push(c)}});var fs=fin.join(' ');if((cur.getAttribute('class')||'')!==fs){if(fs)cur.setAttribute('class',fs);else cur.removeAttribute('class')}for(var i=0;i<srv.attributes.length;i++){var a=srv.attributes[i];if(a.name==='class')continue;if(cur.getAttribute(a.name)!==a.value)cur.setAttribute(a.name,a.value)}if(old){for(var i=0;i<old.attributes.length;i++){var a=old.attributes[i];if(a.name==='class')continue;if(!srv.hasAttribute(a.name)&&cur.hasAttribute(a.name))cur.removeAttribute(a.name)}}}function syncText(cur,srv){var cc=cur.childNodes,sc=srv.childNodes;for(var i=0;i<sc.length;i++){var s=sc[i],c=cc[i];if(s.nodeType===3&&c&&c.nodeType===3){if(c.textContent!==s.textContent)c.textContent=s.textContent}}}function smartUpdate(curR,srvR,oldR){var ce=curR.querySelectorAll('[data-element-path]'),se=srvR.querySelectorAll('[data-element-path]');if(ce.length!==se.length){if(curR.innerHTML!==srvR.innerHTML)curR.innerHTML=srvR.innerHTML;return}var sbp={};for(var i=0;i<se.length;i++)sbp[se[i].getAttribute('data-element-path')]=se[i];var obp={};if(oldR){var oe=oldR.querySelectorAll('[data-element-path]');for(var i=0;i<oe.length;i++)obp[oe[i].getAttribute('data-element-path')]=oe[i]}for(var i=0;i<ce.length;i++){var c=ce[i],p=c.getAttribute('data-element-path'),s=sbp[p];if(!s){if(curR.innerHTML!==srvR.innerHTML)curR.innerHTML=srvR.innerHTML;return}syncEl(c,s,obp[p]);syncText(c,s)}syncText(curR,srvR)}function connect(){ws=new WebSocket(${wsUrl});ws.onmessage=function(e){var d=JSON.parse(e.data);if(d.type==='hmr:libraries-update'){location.reload()}else if(d.type==='hmr:update'||d.type==='hmr:cms-update'||d.type==='hmr:colors-update'||d.type==='hmr:variables-update')hotReload()};ws.onclose=function(){clearTimeout(timer);timer=setTimeout(connect,1000)}}function hotReload(){var g=++gen;var sx=window.scrollX,sy=window.scrollY;fetch(location.href,{cache:'no-store'}).then(function(r){return r.text()}).then(function(html){if(g!==gen)return;var p=new DOMParser();var d=p.parseFromString(html,'text/html');var or=document.getElementById('root'),nr=d.getElementById('root');if(or&&nr)smartUpdate(or,nr,lastSrvRoot);if(nr)lastSrvRoot=nr.cloneNode(true);var os=document.getElementById('meno-styles'),ns=d.getElementById('meno-styles');if(os&&ns&&os.textContent!==ns.textContent)os.parentNode.replaceChild(ns.cloneNode(true),os);var nh=d.documentElement;if(nh){var nl=nh.getAttribute('lang')||'en',nt=nh.getAttribute('theme')||'light';if(document.documentElement.getAttribute('lang')!==nl)document.documentElement.setAttribute('lang',nl);if(document.documentElement.getAttribute('theme')!==nt)document.documentElement.setAttribute('theme',nt)}var ocms=document.querySelectorAll('script[id^="meno-cms-"]'),ncms=d.querySelectorAll('script[id^="meno-cms-"]');var ock=JSON.stringify(Array.prototype.map.call(ocms,function(s){return [s.id,s.textContent]}));var nck=JSON.stringify(Array.prototype.map.call(ncms,function(s){return [s.id,s.textContent]}));if(ock!==nck){ocms.forEach(function(s){s.remove()});ncms.forEach(function(s){var c=document.createElement('script');c.type=s.type;c.id=s.id;c.textContent=s.textContent;document.head.appendChild(c)})}window.__menoHotReload=true;var olib=document.querySelectorAll('body > script[src^="/libraries/"]'),nlib=d.querySelectorAll('body > script[src^="/libraries/"]');var olk=JSON.stringify(Array.prototype.map.call(olib,function(s){return strip(s.getAttribute('src'))}).sort());var nlk=JSON.stringify(Array.prototype.map.call(nlib,function(s){return strip(s.getAttribute('src'))}).sort());if(olk!==nlk){olib.forEach(function(o){o.remove()});nlib.forEach(function(n){var src=n.getAttribute('src');var ls=document.createElement('script');ls.src=src+(src.indexOf('?')>-1?'&':'?')+'_r='+Date.now();document.body.appendChild(ls)})}var oscr=document.querySelector('script[src^="/_scripts/"]'),nscr=d.querySelector('script[src^="/_scripts/"]');var oss=oscr?strip(oscr.getAttribute('src')):'',nss=nscr?strip(nscr.getAttribute('src')):'';if(oss===nss){window.scrollTo(sx,sy)}else{if(oscr)oscr.remove();if(nscr){var src=nscr.getAttribute('src');var s=document.createElement('script');s.src=src+(src.indexOf('?')>-1?'&':'?')+'_r='+Date.now();s.onload=function(){document.dispatchEvent(new Event('DOMContentLoaded'));window.scrollTo(sx,sy)};s.onerror=function(){window.scrollTo(sx,sy)};document.body.appendChild(s)}else{document.dispatchEvent(new Event('DOMContentLoaded'));window.scrollTo(sx,sy)}}}).catch(function(){location.reload()})}var iR=document.getElementById('root');if(iR)lastSrvRoot=iR.cloneNode(true);connect()})()</script>` : "";
3444
3665
  const scrollHandlerScript = injectLiveReload ? `<script>(function(){window.addEventListener('message',function(e){if(e.data.type==='GET_SCROLL_POSITION'){window.parent.postMessage({type:'SCROLL_POSITION_RESPONSE',scrollX:window.scrollX,scrollY:window.scrollY},'*')}else if(e.data.type==='SET_SCROLL_POSITION'){window.scrollTo(e.data.scrollX,e.data.scrollY)}})})()</script>` : "";
3445
3666
  const styleContent = useBundled ? finalCSS : `
3446
3667
  ${combinedCSS.split("\n").join("\n ")}
@@ -5622,6 +5843,7 @@ autoInit();
5622
5843
  // lib/server/providers/fileSystemCMSProvider.ts
5623
5844
  import { existsSync as existsSync2, readdirSync as readdirSync2, mkdirSync } from "fs";
5624
5845
  import { join as join2 } from "path";
5846
+ var DRAFT_FILE_SUFFIX = `${CMS_DRAFT_SUFFIX}.json`;
5625
5847
  async function loadJSONFile2(filePath) {
5626
5848
  try {
5627
5849
  if (await fileExists(filePath)) {
@@ -5633,12 +5855,24 @@ async function loadJSONFile2(filePath) {
5633
5855
  return null;
5634
5856
  }
5635
5857
  }
5636
- function normalizeItem(content, filename) {
5637
- return {
5638
- ...content,
5858
+ function normalizeItem(content, filename, isDraft = false) {
5859
+ const raw = content;
5860
+ const base = {
5861
+ ...raw,
5862
+ _id: raw._id || filename,
5639
5863
  _slug: filename,
5640
5864
  _filename: filename
5641
5865
  };
5866
+ if (isDraft) base._isDraft = true;
5867
+ return base;
5868
+ }
5869
+ function stripTransient(item) {
5870
+ const { _slug, _isDraft, _hasDraft, _url, _filename, ...rest } = item;
5871
+ const out = rest;
5872
+ if (typeof _filename === "string" && _filename !== out._id) {
5873
+ out._filename = _filename;
5874
+ }
5875
+ return out;
5642
5876
  }
5643
5877
  var FileSystemCMSProvider = class {
5644
5878
  constructor(templatesDir, cmsDir) {
@@ -5656,13 +5890,16 @@ var FileSystemCMSProvider = class {
5656
5890
  }
5657
5891
  }
5658
5892
  /**
5659
- * Validate filename to prevent path traversal attacks
5893
+ * Validate filename to prevent path traversal attacks and reserved-suffix collisions.
5660
5894
  * @throws Error if filename is invalid
5661
5895
  */
5662
5896
  validateFilename(filename) {
5663
5897
  if (!isSafePathSegment(filename)) {
5664
5898
  throw new Error(`Invalid filename: "${filename}". Filenames cannot contain path separators or traversal sequences.`);
5665
5899
  }
5900
+ if (isReservedDraftFilename(filename)) {
5901
+ throw new Error(`Invalid filename: "${filename}". The "${CMS_DRAFT_SUFFIX}" suffix is reserved for draft files.`);
5902
+ }
5666
5903
  }
5667
5904
  /**
5668
5905
  * Load all CMS schemas from page files with source: 'cms' in templates/
@@ -5709,7 +5946,7 @@ var FileSystemCMSProvider = class {
5709
5946
  return [];
5710
5947
  }
5711
5948
  const files = readdirSync2(collectionDir);
5712
- const jsonFiles = files.filter((f) => f.endsWith(".json"));
5949
+ const jsonFiles = files.filter((f) => f.endsWith(".json") && !f.endsWith(DRAFT_FILE_SUFFIX));
5713
5950
  const results = await Promise.all(
5714
5951
  jsonFiles.map(async (file) => {
5715
5952
  const filePath = join2(collectionDir, file);
@@ -5766,8 +6003,10 @@ var FileSystemCMSProvider = class {
5766
6003
  return items.find((item) => item._id === id) || null;
5767
6004
  }
5768
6005
  /**
5769
- * Save item to file system
5770
- * Uses _filename for file path (stable, doesn't change when slug changes)
6006
+ * Save item to file system.
6007
+ * The on-disk filename is derived from `_filename` (legacy alias) when set,
6008
+ * otherwise from `_id` (canonical identifier; equals the filename for new
6009
+ * items). Falls back to the slugField value as a last resort.
5771
6010
  */
5772
6011
  async saveItem(collection, item) {
5773
6012
  this.validateCollection(collection);
@@ -5783,32 +6022,193 @@ var FileSystemCMSProvider = class {
5783
6022
  } else {
5784
6023
  const slugField = schemaInfo.schema.slugField;
5785
6024
  const slugValue = item[slugField];
5786
- filename = typeof slugValue === "string" ? slugValue : String(slugValue);
6025
+ if (typeof slugValue === "string" && slugValue) {
6026
+ filename = slugValue;
6027
+ } else if (typeof item._id === "string" && item._id) {
6028
+ filename = item._id;
6029
+ } else {
6030
+ filename = String(slugValue);
6031
+ }
5787
6032
  }
5788
6033
  if (!filename || filename === "[object Object]") {
5789
- throw new Error("Missing _filename field. Items must have _filename set on creation.");
6034
+ throw new Error("Cannot derive filename: item is missing _id, _filename, and a usable slug-field value.");
5790
6035
  }
5791
6036
  this.validateFilename(filename);
5792
6037
  const collectionDir = join2(this.cmsDir, collection);
5793
6038
  if (!existsSync2(collectionDir)) {
5794
6039
  mkdirSync(collectionDir, { recursive: true });
5795
6040
  }
5796
- const { _slug, ...itemData } = item;
6041
+ const itemData = stripTransient(item);
5797
6042
  const filePath = join2(collectionDir, `${filename}.json`);
5798
6043
  await writeFile2(filePath, JSON.stringify(itemData, null, 2), "utf-8");
5799
6044
  }
5800
6045
  /**
5801
- * Delete item by filename (or slug for backward compat)
6046
+ * Delete item by filename. Removes the published file AND any draft sibling.
5802
6047
  */
5803
6048
  async deleteItem(collection, filename) {
5804
6049
  this.validateCollection(collection);
5805
6050
  this.validateFilename(filename);
5806
6051
  const { unlink } = await import("fs/promises");
5807
- const filePath = join2(this.cmsDir, collection, `${filename}.json`);
6052
+ const publishedPath = join2(this.cmsDir, collection, `${filename}.json`);
6053
+ if (existsSync2(publishedPath)) {
6054
+ await unlink(publishedPath);
6055
+ }
6056
+ const draftPath = this.draftPath(collection, filename);
6057
+ if (existsSync2(draftPath)) {
6058
+ await unlink(draftPath);
6059
+ }
6060
+ }
6061
+ // ---- Draft helpers ----------------------------------------------------
6062
+ draftPath(collection, filename) {
6063
+ return join2(this.cmsDir, collection, `${filename}${DRAFT_FILE_SUFFIX}`);
6064
+ }
6065
+ /**
6066
+ * Get the draft version of an item, or null if no draft file exists.
6067
+ * Drafts skip strict validation — they may be partial / WIP.
6068
+ */
6069
+ async getDraft(collection, filename) {
6070
+ this.validateCollection(collection);
6071
+ this.validateFilename(filename);
6072
+ const filePath = this.draftPath(collection, filename);
6073
+ const content = await loadJSONFile2(filePath);
6074
+ if (!content || typeof content !== "object") {
6075
+ return null;
6076
+ }
6077
+ return normalizeItem(
6078
+ content,
6079
+ filename,
6080
+ /*isDraft*/
6081
+ true
6082
+ );
6083
+ }
6084
+ /**
6085
+ * List all drafts in a collection. Used by the Studio item list to mark
6086
+ * items that have an outstanding draft sibling (or are draft-only).
6087
+ */
6088
+ async getAllDrafts(collection) {
6089
+ this.validateCollection(collection);
6090
+ const collectionDir = join2(this.cmsDir, collection);
6091
+ if (!existsSync2(collectionDir)) return [];
6092
+ const files = readdirSync2(collectionDir).filter((f) => f.endsWith(DRAFT_FILE_SUFFIX));
6093
+ const results = await Promise.all(
6094
+ files.map(async (file) => {
6095
+ const filePath = join2(collectionDir, file);
6096
+ const content = await loadJSONFile2(filePath);
6097
+ return { file, content };
6098
+ })
6099
+ );
6100
+ const drafts = [];
6101
+ for (const { file, content } of results) {
6102
+ if (content && typeof content === "object") {
6103
+ const filename = file.slice(0, -DRAFT_FILE_SUFFIX.length);
6104
+ drafts.push(normalizeItem(
6105
+ content,
6106
+ filename,
6107
+ /*isDraft*/
6108
+ true
6109
+ ));
6110
+ }
6111
+ }
6112
+ return drafts;
6113
+ }
6114
+ async hasDraft(collection, filename) {
6115
+ this.validateCollection(collection);
6116
+ this.validateFilename(filename);
6117
+ return existsSync2(this.draftPath(collection, filename));
6118
+ }
6119
+ /**
6120
+ * Save the draft version of an item. Loose validation — drafts may have
6121
+ * missing required fields or partial data. Strict validation only runs at
6122
+ * publish time. The on-disk filename is derived the same way as for
6123
+ * `saveItem` (prefer `_filename`, fall back to `_id`).
6124
+ */
6125
+ async saveDraft(collection, item) {
6126
+ this.validateCollection(collection);
6127
+ const { writeFile: writeFile2 } = await import("fs/promises");
6128
+ const schemas = await this.getAllSchemas();
6129
+ const schemaInfo = schemas.get(collection);
6130
+ if (!schemaInfo) {
6131
+ throw new Error(`Unknown collection: ${collection}`);
6132
+ }
6133
+ let filename;
6134
+ if (item._filename) {
6135
+ filename = item._filename;
6136
+ } else {
6137
+ const slugField = schemaInfo.schema.slugField;
6138
+ const slugValue = item[slugField];
6139
+ if (typeof slugValue === "string" && slugValue) {
6140
+ filename = slugValue;
6141
+ } else if (typeof item._id === "string" && item._id) {
6142
+ filename = item._id;
6143
+ } else {
6144
+ filename = String(slugValue);
6145
+ }
6146
+ }
6147
+ if (!filename || filename === "[object Object]") {
6148
+ throw new Error("Cannot derive draft filename: item is missing _id, _filename, and a usable slug-field value.");
6149
+ }
6150
+ this.validateFilename(filename);
6151
+ const collectionDir = join2(this.cmsDir, collection);
6152
+ if (!existsSync2(collectionDir)) {
6153
+ mkdirSync(collectionDir, { recursive: true });
6154
+ }
6155
+ const itemData = stripTransient(item);
6156
+ const validation = validateCMSDraftItem(itemData);
6157
+ if (!validation.valid) {
6158
+ const messages = validation.errors.map((e) => `${e.path}: ${e.message}`).join(", ");
6159
+ throw new Error(`Invalid draft: ${messages}`);
6160
+ }
6161
+ const filePath = this.draftPath(collection, filename);
6162
+ await writeFile2(filePath, JSON.stringify(itemData, null, 2), "utf-8");
6163
+ }
6164
+ /**
6165
+ * Discard the draft version of an item. No-op if no draft exists.
6166
+ */
6167
+ async discardDraft(collection, filename) {
6168
+ this.validateCollection(collection);
6169
+ this.validateFilename(filename);
6170
+ const { unlink } = await import("fs/promises");
6171
+ const filePath = this.draftPath(collection, filename);
5808
6172
  if (existsSync2(filePath)) {
5809
6173
  await unlink(filePath);
5810
6174
  }
5811
6175
  }
6176
+ /**
6177
+ * Promote a draft to published. Reads `{filename}.draft.json`, writes the
6178
+ * content to `{filename}.json`, then unlinks the draft. The published write
6179
+ * happens first so a crash mid-operation leaves a valid published file plus
6180
+ * an orphan draft (recoverable via the editor's Discard button) — never a
6181
+ * gap with no published content.
6182
+ *
6183
+ * Throws if no draft exists.
6184
+ */
6185
+ async publishDraft(collection, filename) {
6186
+ this.validateCollection(collection);
6187
+ this.validateFilename(filename);
6188
+ const { writeFile: writeFile2, unlink } = await import("fs/promises");
6189
+ const draftFilePath = this.draftPath(collection, filename);
6190
+ const content = await loadJSONFile2(draftFilePath);
6191
+ if (!content || typeof content !== "object") {
6192
+ throw new Error(`No draft to publish for ${collection}/${filename}`);
6193
+ }
6194
+ const item = normalizeItem(content, filename);
6195
+ const validation = validateCMSItem(item);
6196
+ if (!validation.valid) {
6197
+ const messages = validation.errors.map((e) => `${e.path}: ${e.message}`).join(", ");
6198
+ throw new Error(`Cannot publish invalid draft: ${messages}`);
6199
+ }
6200
+ const collectionDir = join2(this.cmsDir, collection);
6201
+ if (!existsSync2(collectionDir)) {
6202
+ mkdirSync(collectionDir, { recursive: true });
6203
+ }
6204
+ const itemData = stripTransient(validation.data);
6205
+ const publishedPath = join2(collectionDir, `${filename}.json`);
6206
+ await writeFile2(publishedPath, JSON.stringify(itemData, null, 2), "utf-8");
6207
+ if (existsSync2(draftFilePath)) {
6208
+ await unlink(draftFilePath);
6209
+ }
6210
+ return normalizeItem(itemData, filename);
6211
+ }
5812
6212
  /**
5813
6213
  * Clear schema cache (useful when pages are modified)
5814
6214
  */
@@ -5932,4 +6332,4 @@ export {
5932
6332
  FileSystemCMSProvider,
5933
6333
  migrateTemplatesDirectory
5934
6334
  };
5935
- //# sourceMappingURL=chunk-TPQ7APVQ.js.map
6335
+ //# sourceMappingURL=chunk-CVLFID6V.js.map