@pixldocs/canvas-renderer 0.4.9 → 0.5.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.cjs CHANGED
@@ -2203,6 +2203,18 @@ const useEditorStore = zustand.create((set, get) => ({
2203
2203
  const committed = commitFromState(state, nextCanvas);
2204
2204
  return { canvas: nextCanvas, ...committed };
2205
2205
  }),
2206
+ setPageBoundRepeatablePage: (pageId, repeatablePageId) => set((state) => {
2207
+ const updatedPages = state.canvas.pages.map((p) => {
2208
+ if (p.id !== pageId) return p;
2209
+ const next = { ...p };
2210
+ if (repeatablePageId) next.boundRepeatablePageId = repeatablePageId;
2211
+ else delete next.boundRepeatablePageId;
2212
+ return next;
2213
+ });
2214
+ const nextCanvas = { ...state.canvas, pages: updatedPages };
2215
+ const committed = commitFromState(state, nextCanvas);
2216
+ return { canvas: nextCanvas, ...committed };
2217
+ }),
2206
2218
  moveElementsToPage: (elementIds, targetPageId) => set((state) => {
2207
2219
  const currentPage = getCurrentPageFromCanvas(state.canvas);
2208
2220
  const nodesToMove = [];
@@ -2347,7 +2359,8 @@ const useEditorStore = zustand.create((set, get) => ({
2347
2359
  id: p.id,
2348
2360
  name: p.name,
2349
2361
  children,
2350
- settings: { backgroundColor: "#ffffff", ...p.settings }
2362
+ settings: { backgroundColor: "#ffffff", ...p.settings },
2363
+ ...p.boundRepeatablePageId ? { boundRepeatablePageId: p.boundRepeatablePageId } : {}
2351
2364
  };
2352
2365
  });
2353
2366
  pagesWithPositions = pagesWithPositions.map((p) => {
@@ -8785,13 +8798,14 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
8785
8798
  const labelPrefix = rawPrefix.startsWith("field_") ? rawPrefix : `field_${rawPrefix}`;
8786
8799
  const treeNodeId = repeatableNodeMap == null ? void 0 : repeatableNodeMap.get(def.label.trim().toLowerCase());
8787
8800
  const prefix = treeNodeId ? `field_${treeNodeId}` : labelPrefix;
8801
+ const minEntries = def.minEntries != null ? Math.max(0, def.minEntries) : 1;
8788
8802
  const section = {
8789
8803
  id: def.id,
8790
8804
  label: def.label,
8791
8805
  order: def.order ?? 0,
8792
8806
  type: "repeatable",
8793
8807
  templateKeyPrefix: prefix,
8794
- minEntries: def.minEntries ?? 1,
8808
+ minEntries,
8795
8809
  maxEntries: def.maxEntries,
8796
8810
  entryFields: def.fields.map((f, i) => ({
8797
8811
  key: f.key,
@@ -8801,7 +8815,8 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
8801
8815
  templateKey: f.key,
8802
8816
  placeholder: f.placeholder
8803
8817
  })),
8804
- initialEntryCount: def.minEntries ?? 1,
8818
+ // Honor minEntries: 0 — start with no entries, user adds via "+ Add" button.
8819
+ initialEntryCount: minEntries,
8805
8820
  parentId
8806
8821
  };
8807
8822
  if (treeNodeId) section.treeNodeId = treeNodeId;
@@ -9260,7 +9275,7 @@ function applyTextCase(text, textCase) {
9260
9275
  return text;
9261
9276
  }
9262
9277
  }
9263
- function setInTree$1(nodes, elementId, targetProperty, value) {
9278
+ function setInTree(nodes, elementId, targetProperty, value) {
9264
9279
  for (const node of nodes) {
9265
9280
  if (node.id === elementId) {
9266
9281
  if (targetProperty === "link") {
@@ -9287,7 +9302,7 @@ function setInTree$1(nodes, elementId, targetProperty, value) {
9287
9302
  return true;
9288
9303
  }
9289
9304
  if (node.children && Array.isArray(node.children)) {
9290
- if (setInTree$1(node.children, elementId, targetProperty, value)) return true;
9305
+ if (setInTree(node.children, elementId, targetProperty, value)) return true;
9291
9306
  }
9292
9307
  }
9293
9308
  return false;
@@ -9357,11 +9372,11 @@ function idPrefix(node) {
9357
9372
  if (t === "shape") return "shape";
9358
9373
  return "el";
9359
9374
  }
9360
- function cloneNodeWithNewIds$1(node) {
9375
+ function cloneNodeWithNewIds$1(node, cloneSuffix) {
9361
9376
  const oldToNew = /* @__PURE__ */ new Map();
9362
9377
  function clone(n) {
9363
9378
  var _a;
9364
- const newId = generateId(idPrefix(n));
9379
+ const newId = cloneSuffix ? `${baseId(n.id)}__c${cloneSuffix}` : generateId(idPrefix(n));
9365
9380
  oldToNew.set(n.id, newId);
9366
9381
  const base = baseId(n.id);
9367
9382
  const withMeta = { ...n, id: newId, __baseNodeId: base, __sourceId: n.id };
@@ -9527,12 +9542,55 @@ function getNestedRepeatableEntryCount(parentId, parentIndex, childId, formValue
9527
9542
  }
9528
9543
  return Math.max(1, maxIndex);
9529
9544
  }
9530
- function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsFromSchema, repeatableEntryCounts, repeatableNestedEntryCounts, displayFormatMap) {
9531
- var _a, _b, _c, _d, _e;
9545
+ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsFromSchema, repeatableEntryCounts, repeatableNestedEntryCounts, displayFormatMap, repeatablePagesFromSchema) {
9546
+ var _a, _b, _c, _d, _e, _f;
9532
9547
  const cloned = JSON.parse(JSON.stringify(config));
9533
9548
  if (!cloned.pages) return cloned;
9534
9549
  const dynamicFields = cloned.dynamicFields;
9535
- const pages = cloned.pages;
9550
+ let pages = cloned.pages;
9551
+ if (repeatablePagesFromSchema == null ? void 0 : repeatablePagesFromSchema.length) {
9552
+ const expanded = [];
9553
+ for (const page of pages) {
9554
+ const boundId = page.boundRepeatablePageId;
9555
+ const schemaEntry = boundId ? repeatablePagesFromSchema.find((p) => p.pageId === boundId) : void 0;
9556
+ if (!schemaEntry) {
9557
+ expanded.push(page);
9558
+ continue;
9559
+ }
9560
+ const prefix = schemaEntry.templateKeyPrefix;
9561
+ let inferredN = 1;
9562
+ const keyPrefix = `field_${prefix}_`;
9563
+ for (const k of Object.keys(formValues)) {
9564
+ if (!k.startsWith(keyPrefix)) continue;
9565
+ const rest = k.slice(keyPrefix.length);
9566
+ const m = /^(\d+)_/.exec(rest);
9567
+ if (m) inferredN = Math.max(inferredN, parseInt(m[1], 10));
9568
+ }
9569
+ const N = Math.max(0, schemaEntry.entryCount ?? inferredN);
9570
+ if (N === 0) {
9571
+ continue;
9572
+ }
9573
+ const basePageId = baseId(page.id);
9574
+ for (let i = 1; i <= N; i++) {
9575
+ const clonedPage = JSON.parse(JSON.stringify(page));
9576
+ clonedPage.id = N === 1 && i === 1 ? page.id : `${basePageId}_${i}`;
9577
+ clonedPage.__repeatablePagePrefix = prefix;
9578
+ clonedPage.__repeatablePageEntryIndex = i;
9579
+ clonedPage.__repeatablePageBaseId = basePageId;
9580
+ if ((_a = clonedPage.children) == null ? void 0 : _a.length) {
9581
+ const suffix = `${basePageId}_p${i}`;
9582
+ const reidChildren = (nodes) => nodes.map((n) => {
9583
+ const [reclone] = cloneNodeWithNewIds$1(n, suffix);
9584
+ return reclone;
9585
+ });
9586
+ if (i > 1) clonedPage.children = reidChildren(clonedPage.children);
9587
+ }
9588
+ expanded.push(clonedPage);
9589
+ }
9590
+ }
9591
+ pages = expanded;
9592
+ cloned.pages = pages;
9593
+ }
9536
9594
  const repeatableList = (repeatableSectionsFromSchema == null ? void 0 : repeatableSectionsFromSchema.length) ? repeatableSectionsFromSchema : getRepeatableFromConfig(pages);
9537
9595
  const nodeIds = repeatableList.map((r) => r.nodeId);
9538
9596
  const entryCountFromList = (baseNodeId) => {
@@ -9544,7 +9602,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9544
9602
  const elementIdToFieldId = /* @__PURE__ */ new Map();
9545
9603
  if (dynamicFields == null ? void 0 : dynamicFields.length) {
9546
9604
  for (const f of dynamicFields) {
9547
- const elId = (_b = (_a = f.mappings) == null ? void 0 : _a[0]) == null ? void 0 : _b.elementId;
9605
+ const elId = (_c = (_b = f.mappings) == null ? void 0 : _b[0]) == null ? void 0 : _c.elementId;
9548
9606
  if (elId) elementIdToFieldId.set(elId, f.id);
9549
9607
  }
9550
9608
  }
@@ -9575,7 +9633,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9575
9633
  const N = Math.max(1, countFromState ?? getRepeatableEntryCount(baseNodeId, formValues));
9576
9634
  const clones = [];
9577
9635
  for (let i = 1; i <= N; i++) {
9578
- const [clone, oldToNew] = cloneNodeWithNewIds$1(node);
9636
+ const [clone, oldToNew] = cloneNodeWithNewIds$1(node, `${baseNodeId}_e${i}`);
9579
9637
  delete clone.repeatableSection;
9580
9638
  clones.push(clone);
9581
9639
  const newToOriginal = /* @__PURE__ */ new Map();
@@ -9611,7 +9669,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9611
9669
  const phase1Map = phase1NewToOriginal.get(`${parentBaseNodeId}_${parentIndex1Based}`);
9612
9670
  const clones = [];
9613
9671
  for (let i = 1; i <= N; i++) {
9614
- const [clone, oldToNew] = cloneNodeWithNewIds$1(node);
9672
+ const [clone, oldToNew] = cloneNodeWithNewIds$1(node, `${parentBaseNodeId}_p${parentIndex1Based}_${baseNodeId}_e${i}`);
9615
9673
  delete clone.repeatableSection;
9616
9674
  clones.push(clone);
9617
9675
  for (const [oldId, newId] of oldToNew) {
@@ -9758,7 +9816,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9758
9816
  }
9759
9817
  }
9760
9818
  for (const page of pages) {
9761
- if (page.children && setInTree$1(page.children, elementId, targetProperty, effectiveValue)) return true;
9819
+ if (page.children && setInTree(page.children, elementId, targetProperty, effectiveValue)) return true;
9762
9820
  }
9763
9821
  return false;
9764
9822
  };
@@ -9789,7 +9847,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9789
9847
  const [, parentId, parentIndexStr, childId, childIndexStr, fieldId] = nestedMatch;
9790
9848
  const value = formValues[key];
9791
9849
  const field = getFieldForRepeatableKey(fieldId, key);
9792
- const mapping = (_c = field == null ? void 0 : field.mappings) == null ? void 0 : _c[0];
9850
+ const mapping = (_d = field == null ? void 0 : field.mappings) == null ? void 0 : _d[0];
9793
9851
  if (!mapping) continue;
9794
9852
  const oldElementId = mapping.elementId;
9795
9853
  const mapKeyByElement = `${baseId(parentId)}_${parentIndexStr}_${baseId(childId)}_${childIndexStr}_${oldElementId}`;
@@ -9825,15 +9883,26 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9825
9883
  }
9826
9884
  }
9827
9885
  }
9886
+ const repeatablePagePrefixSet = new Set(
9887
+ (repeatablePagesFromSchema ?? []).map((p) => `field_${p.templateKeyPrefix}_`)
9888
+ );
9828
9889
  if (dynamicFields == null ? void 0 : dynamicFields.length) {
9829
9890
  for (const key of Object.keys(formValues)) {
9830
9891
  if (NESTED_REPEATABLE_KEY_REGEX.test(key)) continue;
9892
+ let isPageKey = false;
9893
+ for (const pfx of repeatablePagePrefixSet) {
9894
+ if (key.startsWith(pfx)) {
9895
+ isPageKey = true;
9896
+ break;
9897
+ }
9898
+ }
9899
+ if (isPageKey) continue;
9831
9900
  const match = key.match(REPEATABLE_KEY_REGEX);
9832
9901
  if (!match) continue;
9833
9902
  const [, nodeId, indexStr, fieldId] = match;
9834
9903
  const value = formValues[key];
9835
9904
  const field = getFieldForRepeatableKey(fieldId);
9836
- const mapping = (_d = field == null ? void 0 : field.mappings) == null ? void 0 : _d[0];
9905
+ const mapping = (_e = field == null ? void 0 : field.mappings) == null ? void 0 : _e[0];
9837
9906
  if (!mapping) continue;
9838
9907
  const oldElementId = mapping.elementId;
9839
9908
  const mapKeyByElement = `${baseId(nodeId)}_${indexStr}_${oldElementId}`;
@@ -9855,8 +9924,49 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9855
9924
  const value = formValues[m.field_key];
9856
9925
  applyValue(m.element_id, m.target_property, value, m.field_key);
9857
9926
  }
9927
+ if ((repeatablePagesFromSchema == null ? void 0 : repeatablePagesFromSchema.length) && (dynamicFields == null ? void 0 : dynamicFields.length)) {
9928
+ const findInPage = (page, originalId) => {
9929
+ var _a2;
9930
+ if (!((_a2 = page.children) == null ? void 0 : _a2.length)) return void 0;
9931
+ const findIn = (nodes) => {
9932
+ var _a3;
9933
+ for (const n of nodes) {
9934
+ const src = n.__sourceId;
9935
+ if (n.id === originalId || src === originalId || baseId(n.id) === baseId(originalId)) return n.id;
9936
+ if (isGroup(n) && ((_a3 = n.children) == null ? void 0 : _a3.length)) {
9937
+ const f = findIn(n.children);
9938
+ if (f) return f;
9939
+ }
9940
+ }
9941
+ return void 0;
9942
+ };
9943
+ return findIn(page.children);
9944
+ };
9945
+ for (const page of pages) {
9946
+ const meta = page;
9947
+ const prefix = meta.__repeatablePagePrefix;
9948
+ const entryIndex = meta.__repeatablePageEntryIndex;
9949
+ if (!prefix || !entryIndex) continue;
9950
+ const fieldIdPrefix = `field_${prefix}_N_`;
9951
+ for (const field of dynamicFields) {
9952
+ if (!field.id.startsWith(fieldIdPrefix)) continue;
9953
+ const fieldKeySuffix = field.id.slice(fieldIdPrefix.length);
9954
+ const formKey = `field_${prefix}_${entryIndex}_${fieldKeySuffix}`;
9955
+ if (!(formKey in formValues)) continue;
9956
+ const value = formValues[formKey];
9957
+ for (const m of field.mappings ?? []) {
9958
+ const originalId = m.elementId;
9959
+ const targetProperty = m.targetProperty || "text";
9960
+ if (!originalId) continue;
9961
+ const resolvedId = findInPage(page, originalId);
9962
+ if (!resolvedId) continue;
9963
+ if (page.children) setInTree(page.children, resolvedId, targetProperty, value);
9964
+ }
9965
+ }
9966
+ }
9967
+ }
9858
9968
  for (const page of pages) {
9859
- if ((_e = page.children) == null ? void 0 : _e.length) {
9969
+ if ((_f = page.children) == null ? void 0 : _f.length) {
9860
9970
  page.children = applyStackReflowToPageTree(page.children);
9861
9971
  }
9862
9972
  }
@@ -10221,52 +10331,122 @@ async function fetchDefaultForm(supabaseUrl, anonKey, formSchemaId) {
10221
10331
  const rows = await res.json();
10222
10332
  return rows.length ? rows[0] : null;
10223
10333
  }
10224
- function applyFormDataSimple(config, formData) {
10225
- var _a;
10226
- const cloned = JSON.parse(JSON.stringify(config));
10227
- if (!cloned.pages || !((_a = cloned.dynamicFields) == null ? void 0 : _a.length)) return cloned;
10228
- const dynamicFields = cloned.dynamicFields;
10229
- for (const field of dynamicFields) {
10230
- const value = formData[field.id];
10231
- if (value === void 0) continue;
10232
- for (const mapping of field.mappings) {
10233
- setInTree(cloned.pages.flatMap((p) => p.children), mapping.elementId, mapping.targetProperty, value);
10334
+ function deriveRepeatablePagesFromTemplate(config, inlineFormSchema) {
10335
+ if (!Array.isArray(config == null ? void 0 : config.pages) || config.pages.length === 0) return [];
10336
+ const schemaPages = inlineFormSchema == null ? void 0 : inlineFormSchema.repeatablePages;
10337
+ const collectIds = (nodes, out2) => {
10338
+ for (const n of nodes ?? []) {
10339
+ if (n == null ? void 0 : n.id) out2.add(n.id);
10340
+ if (Array.isArray(n == null ? void 0 : n.children)) collectIds(n.children, out2);
10234
10341
  }
10235
- }
10236
- return cloned;
10237
- }
10238
- function setInTree(nodes, elementId, targetProperty, value) {
10239
- for (const node of nodes) {
10240
- if (node.id === elementId) {
10241
- if (targetProperty === "text" && node.type === "text" && typeof value === "string") {
10242
- node[targetProperty] = value === "" ? " " : value;
10243
- } else if (targetProperty === "link") {
10244
- node.linkConfig = { ...node.linkConfig || {}, url: String(value ?? "") };
10245
- } else {
10246
- node[targetProperty] = value;
10342
+ };
10343
+ const dynamicFields = config.dynamicFields;
10344
+ const out = [];
10345
+ for (const page of config.pages) {
10346
+ const boundId = page == null ? void 0 : page.boundRepeatablePageId;
10347
+ if (!boundId) continue;
10348
+ let templateKeyPrefix;
10349
+ if (Array.isArray(schemaPages)) {
10350
+ const found = schemaPages.find((rp) => (rp == null ? void 0 : rp.id) === boundId);
10351
+ if (found == null ? void 0 : found.templateKeyPrefix) templateKeyPrefix = found.templateKeyPrefix;
10352
+ }
10353
+ if (!templateKeyPrefix && Array.isArray(dynamicFields) && dynamicFields.length > 0) {
10354
+ const idsOnThisPage = /* @__PURE__ */ new Set();
10355
+ collectIds(page.children || [], idsOnThisPage);
10356
+ const prefixCounts = /* @__PURE__ */ new Map();
10357
+ for (const f of dynamicFields) {
10358
+ const m = /^field_(.+)_N_/.exec(f.id);
10359
+ if (!m) continue;
10360
+ const prefix = m[1];
10361
+ for (const map of f.mappings || []) {
10362
+ if (idsOnThisPage.has(map.elementId)) {
10363
+ prefixCounts.set(prefix, (prefixCounts.get(prefix) || 0) + 1);
10364
+ break;
10365
+ }
10366
+ }
10247
10367
  }
10248
- if (targetProperty === "text" && node.type === "text") {
10249
- const overflowPolicy = String(node.overflowPolicy ?? "grow-and-push");
10250
- if (overflowPolicy !== "auto-shrink") {
10251
- delete node.height;
10368
+ let best;
10369
+ let bestCount = 0;
10370
+ for (const [prefix, count] of prefixCounts) {
10371
+ if (count > bestCount) {
10372
+ best = prefix;
10373
+ bestCount = count;
10252
10374
  }
10253
10375
  }
10254
- return true;
10376
+ if (best) templateKeyPrefix = best;
10255
10377
  }
10256
- if (node.children && Array.isArray(node.children)) {
10257
- if (setInTree(node.children, elementId, targetProperty, value)) return true;
10378
+ if (templateKeyPrefix) {
10379
+ out.push({ pageId: boundId, templateKeyPrefix });
10258
10380
  }
10259
10381
  }
10260
- return false;
10382
+ return out;
10261
10383
  }
10262
10384
  async function resolveTemplateData(options) {
10263
10385
  const { templateId, formData, supabaseUrl, supabaseAnonKey } = options;
10264
10386
  const template = await fetchRow(supabaseUrl, supabaseAnonKey, "templates", templateId);
10265
10387
  let config = template.config;
10388
+ const inlineFormSchema = template.form_schema;
10266
10389
  const defaultData = template.default_data;
10267
- if (defaultData) config = applyFormDataSimple(config, defaultData);
10268
- if (formData && Object.keys(formData).length > 0) config = applyFormDataSimple(config, formData);
10269
- return { config, templateName: template.name || "Untitled", templateId, price: template.price ?? 0 };
10390
+ if (inlineFormSchema && typeof inlineFormSchema === "object") {
10391
+ if (!Array.isArray(config.dynamicFields) && Array.isArray(inlineFormSchema.dynamicFields)) {
10392
+ config.dynamicFields = inlineFormSchema.dynamicFields;
10393
+ }
10394
+ if (!Array.isArray(config.fieldGroups) && Array.isArray(inlineFormSchema.fieldGroups)) {
10395
+ config.fieldGroups = inlineFormSchema.fieldGroups;
10396
+ }
10397
+ }
10398
+ normalizeLayoutModes(config);
10399
+ const repeatableSectionsInput = [];
10400
+ if (Array.isArray(inlineFormSchema == null ? void 0 : inlineFormSchema.repeatableSections)) {
10401
+ for (const r of inlineFormSchema.repeatableSections) {
10402
+ if ((r == null ? void 0 : r.nodeId) && (r == null ? void 0 : r.label)) {
10403
+ repeatableSectionsInput.push({
10404
+ nodeId: r.nodeId,
10405
+ label: r.label,
10406
+ minEntries: r.minEntries,
10407
+ maxEntries: r.maxEntries
10408
+ });
10409
+ }
10410
+ }
10411
+ if (repeatableSectionsInput.length > 0) {
10412
+ paintRepeatableSections(config, repeatableSectionsInput);
10413
+ }
10414
+ }
10415
+ const mergedFormData = {
10416
+ ...defaultData && typeof defaultData === "object" && !Array.isArray(defaultData) ? defaultData : {},
10417
+ ...formData ?? {}
10418
+ };
10419
+ const dynamicFields = config.dynamicFields;
10420
+ if (!Array.isArray(dynamicFields) || dynamicFields.length === 0) {
10421
+ return { config, templateName: template.name || "Untitled", templateId, price: template.price ?? 0 };
10422
+ }
10423
+ const mappings = [];
10424
+ for (const f of dynamicFields) {
10425
+ for (const m of f.mappings || []) {
10426
+ mappings.push({
10427
+ field_key: f.id,
10428
+ element_id: m.elementId,
10429
+ target_property: m.targetProperty || "text"
10430
+ });
10431
+ }
10432
+ }
10433
+ const repeatablePagesInput = deriveRepeatablePagesFromTemplate(config, inlineFormSchema);
10434
+ const resolvedConfig = applyFormDataToConfig(
10435
+ config,
10436
+ mappings,
10437
+ mergedFormData,
10438
+ repeatableSectionsInput,
10439
+ void 0,
10440
+ void 0,
10441
+ void 0,
10442
+ repeatablePagesInput.length > 0 ? repeatablePagesInput : void 0
10443
+ );
10444
+ return {
10445
+ config: resolvedConfig,
10446
+ templateName: template.name || "Untitled",
10447
+ templateId,
10448
+ price: template.price ?? 0
10449
+ };
10270
10450
  }
10271
10451
  async function resolveFromForm(options) {
10272
10452
  var _a, _b, _c, _d;
@@ -10734,6 +10914,7 @@ function PixldocsPreview(props) {
10734
10914
  const [resolvedConfig, setResolvedConfig] = react.useState(null);
10735
10915
  const [isLoading, setIsLoading] = react.useState(false);
10736
10916
  const [fontsReady, setFontsReady] = react.useState(false);
10917
+ const [fontsReadyVersion, setFontsReadyVersion] = react.useState(0);
10737
10918
  const isResolveMode = !("config" in props && props.config);
10738
10919
  react.useEffect(() => {
10739
10920
  if (!isResolveMode) {
@@ -10744,6 +10925,7 @@ function PixldocsPreview(props) {
10744
10925
  if (!p.templateId || !p.formSchemaId || !p.supabaseUrl || !p.supabaseAnonKey) return;
10745
10926
  let cancelled = false;
10746
10927
  setIsLoading(true);
10928
+ setFontsReady(false);
10747
10929
  resolveFromForm({
10748
10930
  templateId: p.templateId,
10749
10931
  formSchemaId: p.formSchemaId,
@@ -10785,7 +10967,28 @@ function PixldocsPreview(props) {
10785
10967
  ]);
10786
10968
  const config = isResolveMode ? resolvedConfig : props.config;
10787
10969
  react.useEffect(() => {
10788
- if (isResolveMode || !config) return;
10970
+ var _a, _b;
10971
+ if (!config) return;
10972
+ let cancelled = false;
10973
+ const bump = () => {
10974
+ if (cancelled) return;
10975
+ clearMeasurementCache();
10976
+ setFontsReadyVersion((v) => v + 1);
10977
+ };
10978
+ (_b = (_a = document.fonts) == null ? void 0 : _a.ready) == null ? void 0 : _b.then(bump);
10979
+ const timeoutId = window.setTimeout(bump, 350);
10980
+ return () => {
10981
+ cancelled = true;
10982
+ window.clearTimeout(timeoutId);
10983
+ };
10984
+ }, [config]);
10985
+ const previewKey = react.useMemo(() => `${pageIndex}-${fontsReadyVersion}`, [pageIndex, fontsReadyVersion]);
10986
+ react.useEffect(() => {
10987
+ if (isResolveMode) return;
10988
+ if (!config) {
10989
+ setFontsReady(false);
10990
+ return;
10991
+ }
10789
10992
  setFontsReady(false);
10790
10993
  ensureFontsForResolvedConfig(config).then(() => setFontsReady(true)).catch(() => setFontsReady(true));
10791
10994
  }, [isResolveMode, config]);
@@ -10793,6 +10996,9 @@ function PixldocsPreview(props) {
10793
10996
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { ...style, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) });
10794
10997
  }
10795
10998
  if (!config) return null;
10999
+ if (!fontsReady) {
11000
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { ...style, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) });
11001
+ }
10796
11002
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style, children: /* @__PURE__ */ jsxRuntime.jsx(
10797
11003
  PreviewCanvas,
10798
11004
  {
@@ -10802,7 +11008,8 @@ function PixldocsPreview(props) {
10802
11008
  absoluteZoom,
10803
11009
  onDynamicFieldClick,
10804
11010
  onReady
10805
- }
11011
+ },
11012
+ previewKey
10806
11013
  ) });
10807
11014
  }
10808
11015
  class PixldocsRenderer {