@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.js CHANGED
@@ -2184,6 +2184,18 @@ const useEditorStore = create((set, get) => ({
2184
2184
  const committed = commitFromState(state, nextCanvas);
2185
2185
  return { canvas: nextCanvas, ...committed };
2186
2186
  }),
2187
+ setPageBoundRepeatablePage: (pageId, repeatablePageId) => set((state) => {
2188
+ const updatedPages = state.canvas.pages.map((p) => {
2189
+ if (p.id !== pageId) return p;
2190
+ const next = { ...p };
2191
+ if (repeatablePageId) next.boundRepeatablePageId = repeatablePageId;
2192
+ else delete next.boundRepeatablePageId;
2193
+ return next;
2194
+ });
2195
+ const nextCanvas = { ...state.canvas, pages: updatedPages };
2196
+ const committed = commitFromState(state, nextCanvas);
2197
+ return { canvas: nextCanvas, ...committed };
2198
+ }),
2187
2199
  moveElementsToPage: (elementIds, targetPageId) => set((state) => {
2188
2200
  const currentPage = getCurrentPageFromCanvas(state.canvas);
2189
2201
  const nodesToMove = [];
@@ -2328,7 +2340,8 @@ const useEditorStore = create((set, get) => ({
2328
2340
  id: p.id,
2329
2341
  name: p.name,
2330
2342
  children,
2331
- settings: { backgroundColor: "#ffffff", ...p.settings }
2343
+ settings: { backgroundColor: "#ffffff", ...p.settings },
2344
+ ...p.boundRepeatablePageId ? { boundRepeatablePageId: p.boundRepeatablePageId } : {}
2332
2345
  };
2333
2346
  });
2334
2347
  pagesWithPositions = pagesWithPositions.map((p) => {
@@ -8766,13 +8779,14 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
8766
8779
  const labelPrefix = rawPrefix.startsWith("field_") ? rawPrefix : `field_${rawPrefix}`;
8767
8780
  const treeNodeId = repeatableNodeMap == null ? void 0 : repeatableNodeMap.get(def.label.trim().toLowerCase());
8768
8781
  const prefix = treeNodeId ? `field_${treeNodeId}` : labelPrefix;
8782
+ const minEntries = def.minEntries != null ? Math.max(0, def.minEntries) : 1;
8769
8783
  const section = {
8770
8784
  id: def.id,
8771
8785
  label: def.label,
8772
8786
  order: def.order ?? 0,
8773
8787
  type: "repeatable",
8774
8788
  templateKeyPrefix: prefix,
8775
- minEntries: def.minEntries ?? 1,
8789
+ minEntries,
8776
8790
  maxEntries: def.maxEntries,
8777
8791
  entryFields: def.fields.map((f, i) => ({
8778
8792
  key: f.key,
@@ -8782,7 +8796,8 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
8782
8796
  templateKey: f.key,
8783
8797
  placeholder: f.placeholder
8784
8798
  })),
8785
- initialEntryCount: def.minEntries ?? 1,
8799
+ // Honor minEntries: 0 — start with no entries, user adds via "+ Add" button.
8800
+ initialEntryCount: minEntries,
8786
8801
  parentId
8787
8802
  };
8788
8803
  if (treeNodeId) section.treeNodeId = treeNodeId;
@@ -9241,7 +9256,7 @@ function applyTextCase(text, textCase) {
9241
9256
  return text;
9242
9257
  }
9243
9258
  }
9244
- function setInTree$1(nodes, elementId, targetProperty, value) {
9259
+ function setInTree(nodes, elementId, targetProperty, value) {
9245
9260
  for (const node of nodes) {
9246
9261
  if (node.id === elementId) {
9247
9262
  if (targetProperty === "link") {
@@ -9268,7 +9283,7 @@ function setInTree$1(nodes, elementId, targetProperty, value) {
9268
9283
  return true;
9269
9284
  }
9270
9285
  if (node.children && Array.isArray(node.children)) {
9271
- if (setInTree$1(node.children, elementId, targetProperty, value)) return true;
9286
+ if (setInTree(node.children, elementId, targetProperty, value)) return true;
9272
9287
  }
9273
9288
  }
9274
9289
  return false;
@@ -9338,11 +9353,11 @@ function idPrefix(node) {
9338
9353
  if (t === "shape") return "shape";
9339
9354
  return "el";
9340
9355
  }
9341
- function cloneNodeWithNewIds$1(node) {
9356
+ function cloneNodeWithNewIds$1(node, cloneSuffix) {
9342
9357
  const oldToNew = /* @__PURE__ */ new Map();
9343
9358
  function clone(n) {
9344
9359
  var _a;
9345
- const newId = generateId(idPrefix(n));
9360
+ const newId = cloneSuffix ? `${baseId(n.id)}__c${cloneSuffix}` : generateId(idPrefix(n));
9346
9361
  oldToNew.set(n.id, newId);
9347
9362
  const base = baseId(n.id);
9348
9363
  const withMeta = { ...n, id: newId, __baseNodeId: base, __sourceId: n.id };
@@ -9508,12 +9523,55 @@ function getNestedRepeatableEntryCount(parentId, parentIndex, childId, formValue
9508
9523
  }
9509
9524
  return Math.max(1, maxIndex);
9510
9525
  }
9511
- function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsFromSchema, repeatableEntryCounts, repeatableNestedEntryCounts, displayFormatMap) {
9512
- var _a, _b, _c, _d, _e;
9526
+ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsFromSchema, repeatableEntryCounts, repeatableNestedEntryCounts, displayFormatMap, repeatablePagesFromSchema) {
9527
+ var _a, _b, _c, _d, _e, _f;
9513
9528
  const cloned = JSON.parse(JSON.stringify(config));
9514
9529
  if (!cloned.pages) return cloned;
9515
9530
  const dynamicFields = cloned.dynamicFields;
9516
- const pages = cloned.pages;
9531
+ let pages = cloned.pages;
9532
+ if (repeatablePagesFromSchema == null ? void 0 : repeatablePagesFromSchema.length) {
9533
+ const expanded = [];
9534
+ for (const page of pages) {
9535
+ const boundId = page.boundRepeatablePageId;
9536
+ const schemaEntry = boundId ? repeatablePagesFromSchema.find((p) => p.pageId === boundId) : void 0;
9537
+ if (!schemaEntry) {
9538
+ expanded.push(page);
9539
+ continue;
9540
+ }
9541
+ const prefix = schemaEntry.templateKeyPrefix;
9542
+ let inferredN = 1;
9543
+ const keyPrefix = `field_${prefix}_`;
9544
+ for (const k of Object.keys(formValues)) {
9545
+ if (!k.startsWith(keyPrefix)) continue;
9546
+ const rest = k.slice(keyPrefix.length);
9547
+ const m = /^(\d+)_/.exec(rest);
9548
+ if (m) inferredN = Math.max(inferredN, parseInt(m[1], 10));
9549
+ }
9550
+ const N = Math.max(0, schemaEntry.entryCount ?? inferredN);
9551
+ if (N === 0) {
9552
+ continue;
9553
+ }
9554
+ const basePageId = baseId(page.id);
9555
+ for (let i = 1; i <= N; i++) {
9556
+ const clonedPage = JSON.parse(JSON.stringify(page));
9557
+ clonedPage.id = N === 1 && i === 1 ? page.id : `${basePageId}_${i}`;
9558
+ clonedPage.__repeatablePagePrefix = prefix;
9559
+ clonedPage.__repeatablePageEntryIndex = i;
9560
+ clonedPage.__repeatablePageBaseId = basePageId;
9561
+ if ((_a = clonedPage.children) == null ? void 0 : _a.length) {
9562
+ const suffix = `${basePageId}_p${i}`;
9563
+ const reidChildren = (nodes) => nodes.map((n) => {
9564
+ const [reclone] = cloneNodeWithNewIds$1(n, suffix);
9565
+ return reclone;
9566
+ });
9567
+ if (i > 1) clonedPage.children = reidChildren(clonedPage.children);
9568
+ }
9569
+ expanded.push(clonedPage);
9570
+ }
9571
+ }
9572
+ pages = expanded;
9573
+ cloned.pages = pages;
9574
+ }
9517
9575
  const repeatableList = (repeatableSectionsFromSchema == null ? void 0 : repeatableSectionsFromSchema.length) ? repeatableSectionsFromSchema : getRepeatableFromConfig(pages);
9518
9576
  const nodeIds = repeatableList.map((r) => r.nodeId);
9519
9577
  const entryCountFromList = (baseNodeId) => {
@@ -9525,7 +9583,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9525
9583
  const elementIdToFieldId = /* @__PURE__ */ new Map();
9526
9584
  if (dynamicFields == null ? void 0 : dynamicFields.length) {
9527
9585
  for (const f of dynamicFields) {
9528
- const elId = (_b = (_a = f.mappings) == null ? void 0 : _a[0]) == null ? void 0 : _b.elementId;
9586
+ const elId = (_c = (_b = f.mappings) == null ? void 0 : _b[0]) == null ? void 0 : _c.elementId;
9529
9587
  if (elId) elementIdToFieldId.set(elId, f.id);
9530
9588
  }
9531
9589
  }
@@ -9556,7 +9614,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9556
9614
  const N = Math.max(1, countFromState ?? getRepeatableEntryCount(baseNodeId, formValues));
9557
9615
  const clones = [];
9558
9616
  for (let i = 1; i <= N; i++) {
9559
- const [clone, oldToNew] = cloneNodeWithNewIds$1(node);
9617
+ const [clone, oldToNew] = cloneNodeWithNewIds$1(node, `${baseNodeId}_e${i}`);
9560
9618
  delete clone.repeatableSection;
9561
9619
  clones.push(clone);
9562
9620
  const newToOriginal = /* @__PURE__ */ new Map();
@@ -9592,7 +9650,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9592
9650
  const phase1Map = phase1NewToOriginal.get(`${parentBaseNodeId}_${parentIndex1Based}`);
9593
9651
  const clones = [];
9594
9652
  for (let i = 1; i <= N; i++) {
9595
- const [clone, oldToNew] = cloneNodeWithNewIds$1(node);
9653
+ const [clone, oldToNew] = cloneNodeWithNewIds$1(node, `${parentBaseNodeId}_p${parentIndex1Based}_${baseNodeId}_e${i}`);
9596
9654
  delete clone.repeatableSection;
9597
9655
  clones.push(clone);
9598
9656
  for (const [oldId, newId] of oldToNew) {
@@ -9739,7 +9797,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9739
9797
  }
9740
9798
  }
9741
9799
  for (const page of pages) {
9742
- if (page.children && setInTree$1(page.children, elementId, targetProperty, effectiveValue)) return true;
9800
+ if (page.children && setInTree(page.children, elementId, targetProperty, effectiveValue)) return true;
9743
9801
  }
9744
9802
  return false;
9745
9803
  };
@@ -9770,7 +9828,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9770
9828
  const [, parentId, parentIndexStr, childId, childIndexStr, fieldId] = nestedMatch;
9771
9829
  const value = formValues[key];
9772
9830
  const field = getFieldForRepeatableKey(fieldId, key);
9773
- const mapping = (_c = field == null ? void 0 : field.mappings) == null ? void 0 : _c[0];
9831
+ const mapping = (_d = field == null ? void 0 : field.mappings) == null ? void 0 : _d[0];
9774
9832
  if (!mapping) continue;
9775
9833
  const oldElementId = mapping.elementId;
9776
9834
  const mapKeyByElement = `${baseId(parentId)}_${parentIndexStr}_${baseId(childId)}_${childIndexStr}_${oldElementId}`;
@@ -9806,15 +9864,26 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9806
9864
  }
9807
9865
  }
9808
9866
  }
9867
+ const repeatablePagePrefixSet = new Set(
9868
+ (repeatablePagesFromSchema ?? []).map((p) => `field_${p.templateKeyPrefix}_`)
9869
+ );
9809
9870
  if (dynamicFields == null ? void 0 : dynamicFields.length) {
9810
9871
  for (const key of Object.keys(formValues)) {
9811
9872
  if (NESTED_REPEATABLE_KEY_REGEX.test(key)) continue;
9873
+ let isPageKey = false;
9874
+ for (const pfx of repeatablePagePrefixSet) {
9875
+ if (key.startsWith(pfx)) {
9876
+ isPageKey = true;
9877
+ break;
9878
+ }
9879
+ }
9880
+ if (isPageKey) continue;
9812
9881
  const match = key.match(REPEATABLE_KEY_REGEX);
9813
9882
  if (!match) continue;
9814
9883
  const [, nodeId, indexStr, fieldId] = match;
9815
9884
  const value = formValues[key];
9816
9885
  const field = getFieldForRepeatableKey(fieldId);
9817
- const mapping = (_d = field == null ? void 0 : field.mappings) == null ? void 0 : _d[0];
9886
+ const mapping = (_e = field == null ? void 0 : field.mappings) == null ? void 0 : _e[0];
9818
9887
  if (!mapping) continue;
9819
9888
  const oldElementId = mapping.elementId;
9820
9889
  const mapKeyByElement = `${baseId(nodeId)}_${indexStr}_${oldElementId}`;
@@ -9836,8 +9905,49 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9836
9905
  const value = formValues[m.field_key];
9837
9906
  applyValue(m.element_id, m.target_property, value, m.field_key);
9838
9907
  }
9908
+ if ((repeatablePagesFromSchema == null ? void 0 : repeatablePagesFromSchema.length) && (dynamicFields == null ? void 0 : dynamicFields.length)) {
9909
+ const findInPage = (page, originalId) => {
9910
+ var _a2;
9911
+ if (!((_a2 = page.children) == null ? void 0 : _a2.length)) return void 0;
9912
+ const findIn = (nodes) => {
9913
+ var _a3;
9914
+ for (const n of nodes) {
9915
+ const src = n.__sourceId;
9916
+ if (n.id === originalId || src === originalId || baseId(n.id) === baseId(originalId)) return n.id;
9917
+ if (isGroup(n) && ((_a3 = n.children) == null ? void 0 : _a3.length)) {
9918
+ const f = findIn(n.children);
9919
+ if (f) return f;
9920
+ }
9921
+ }
9922
+ return void 0;
9923
+ };
9924
+ return findIn(page.children);
9925
+ };
9926
+ for (const page of pages) {
9927
+ const meta = page;
9928
+ const prefix = meta.__repeatablePagePrefix;
9929
+ const entryIndex = meta.__repeatablePageEntryIndex;
9930
+ if (!prefix || !entryIndex) continue;
9931
+ const fieldIdPrefix = `field_${prefix}_N_`;
9932
+ for (const field of dynamicFields) {
9933
+ if (!field.id.startsWith(fieldIdPrefix)) continue;
9934
+ const fieldKeySuffix = field.id.slice(fieldIdPrefix.length);
9935
+ const formKey = `field_${prefix}_${entryIndex}_${fieldKeySuffix}`;
9936
+ if (!(formKey in formValues)) continue;
9937
+ const value = formValues[formKey];
9938
+ for (const m of field.mappings ?? []) {
9939
+ const originalId = m.elementId;
9940
+ const targetProperty = m.targetProperty || "text";
9941
+ if (!originalId) continue;
9942
+ const resolvedId = findInPage(page, originalId);
9943
+ if (!resolvedId) continue;
9944
+ if (page.children) setInTree(page.children, resolvedId, targetProperty, value);
9945
+ }
9946
+ }
9947
+ }
9948
+ }
9839
9949
  for (const page of pages) {
9840
- if ((_e = page.children) == null ? void 0 : _e.length) {
9950
+ if ((_f = page.children) == null ? void 0 : _f.length) {
9841
9951
  page.children = applyStackReflowToPageTree(page.children);
9842
9952
  }
9843
9953
  }
@@ -10202,52 +10312,122 @@ async function fetchDefaultForm(supabaseUrl, anonKey, formSchemaId) {
10202
10312
  const rows = await res.json();
10203
10313
  return rows.length ? rows[0] : null;
10204
10314
  }
10205
- function applyFormDataSimple(config, formData) {
10206
- var _a;
10207
- const cloned = JSON.parse(JSON.stringify(config));
10208
- if (!cloned.pages || !((_a = cloned.dynamicFields) == null ? void 0 : _a.length)) return cloned;
10209
- const dynamicFields = cloned.dynamicFields;
10210
- for (const field of dynamicFields) {
10211
- const value = formData[field.id];
10212
- if (value === void 0) continue;
10213
- for (const mapping of field.mappings) {
10214
- setInTree(cloned.pages.flatMap((p) => p.children), mapping.elementId, mapping.targetProperty, value);
10315
+ function deriveRepeatablePagesFromTemplate(config, inlineFormSchema) {
10316
+ if (!Array.isArray(config == null ? void 0 : config.pages) || config.pages.length === 0) return [];
10317
+ const schemaPages = inlineFormSchema == null ? void 0 : inlineFormSchema.repeatablePages;
10318
+ const collectIds = (nodes, out2) => {
10319
+ for (const n of nodes ?? []) {
10320
+ if (n == null ? void 0 : n.id) out2.add(n.id);
10321
+ if (Array.isArray(n == null ? void 0 : n.children)) collectIds(n.children, out2);
10215
10322
  }
10216
- }
10217
- return cloned;
10218
- }
10219
- function setInTree(nodes, elementId, targetProperty, value) {
10220
- for (const node of nodes) {
10221
- if (node.id === elementId) {
10222
- if (targetProperty === "text" && node.type === "text" && typeof value === "string") {
10223
- node[targetProperty] = value === "" ? " " : value;
10224
- } else if (targetProperty === "link") {
10225
- node.linkConfig = { ...node.linkConfig || {}, url: String(value ?? "") };
10226
- } else {
10227
- node[targetProperty] = value;
10323
+ };
10324
+ const dynamicFields = config.dynamicFields;
10325
+ const out = [];
10326
+ for (const page of config.pages) {
10327
+ const boundId = page == null ? void 0 : page.boundRepeatablePageId;
10328
+ if (!boundId) continue;
10329
+ let templateKeyPrefix;
10330
+ if (Array.isArray(schemaPages)) {
10331
+ const found = schemaPages.find((rp) => (rp == null ? void 0 : rp.id) === boundId);
10332
+ if (found == null ? void 0 : found.templateKeyPrefix) templateKeyPrefix = found.templateKeyPrefix;
10333
+ }
10334
+ if (!templateKeyPrefix && Array.isArray(dynamicFields) && dynamicFields.length > 0) {
10335
+ const idsOnThisPage = /* @__PURE__ */ new Set();
10336
+ collectIds(page.children || [], idsOnThisPage);
10337
+ const prefixCounts = /* @__PURE__ */ new Map();
10338
+ for (const f of dynamicFields) {
10339
+ const m = /^field_(.+)_N_/.exec(f.id);
10340
+ if (!m) continue;
10341
+ const prefix = m[1];
10342
+ for (const map of f.mappings || []) {
10343
+ if (idsOnThisPage.has(map.elementId)) {
10344
+ prefixCounts.set(prefix, (prefixCounts.get(prefix) || 0) + 1);
10345
+ break;
10346
+ }
10347
+ }
10228
10348
  }
10229
- if (targetProperty === "text" && node.type === "text") {
10230
- const overflowPolicy = String(node.overflowPolicy ?? "grow-and-push");
10231
- if (overflowPolicy !== "auto-shrink") {
10232
- delete node.height;
10349
+ let best;
10350
+ let bestCount = 0;
10351
+ for (const [prefix, count] of prefixCounts) {
10352
+ if (count > bestCount) {
10353
+ best = prefix;
10354
+ bestCount = count;
10233
10355
  }
10234
10356
  }
10235
- return true;
10357
+ if (best) templateKeyPrefix = best;
10236
10358
  }
10237
- if (node.children && Array.isArray(node.children)) {
10238
- if (setInTree(node.children, elementId, targetProperty, value)) return true;
10359
+ if (templateKeyPrefix) {
10360
+ out.push({ pageId: boundId, templateKeyPrefix });
10239
10361
  }
10240
10362
  }
10241
- return false;
10363
+ return out;
10242
10364
  }
10243
10365
  async function resolveTemplateData(options) {
10244
10366
  const { templateId, formData, supabaseUrl, supabaseAnonKey } = options;
10245
10367
  const template = await fetchRow(supabaseUrl, supabaseAnonKey, "templates", templateId);
10246
10368
  let config = template.config;
10369
+ const inlineFormSchema = template.form_schema;
10247
10370
  const defaultData = template.default_data;
10248
- if (defaultData) config = applyFormDataSimple(config, defaultData);
10249
- if (formData && Object.keys(formData).length > 0) config = applyFormDataSimple(config, formData);
10250
- return { config, templateName: template.name || "Untitled", templateId, price: template.price ?? 0 };
10371
+ if (inlineFormSchema && typeof inlineFormSchema === "object") {
10372
+ if (!Array.isArray(config.dynamicFields) && Array.isArray(inlineFormSchema.dynamicFields)) {
10373
+ config.dynamicFields = inlineFormSchema.dynamicFields;
10374
+ }
10375
+ if (!Array.isArray(config.fieldGroups) && Array.isArray(inlineFormSchema.fieldGroups)) {
10376
+ config.fieldGroups = inlineFormSchema.fieldGroups;
10377
+ }
10378
+ }
10379
+ normalizeLayoutModes(config);
10380
+ const repeatableSectionsInput = [];
10381
+ if (Array.isArray(inlineFormSchema == null ? void 0 : inlineFormSchema.repeatableSections)) {
10382
+ for (const r of inlineFormSchema.repeatableSections) {
10383
+ if ((r == null ? void 0 : r.nodeId) && (r == null ? void 0 : r.label)) {
10384
+ repeatableSectionsInput.push({
10385
+ nodeId: r.nodeId,
10386
+ label: r.label,
10387
+ minEntries: r.minEntries,
10388
+ maxEntries: r.maxEntries
10389
+ });
10390
+ }
10391
+ }
10392
+ if (repeatableSectionsInput.length > 0) {
10393
+ paintRepeatableSections(config, repeatableSectionsInput);
10394
+ }
10395
+ }
10396
+ const mergedFormData = {
10397
+ ...defaultData && typeof defaultData === "object" && !Array.isArray(defaultData) ? defaultData : {},
10398
+ ...formData ?? {}
10399
+ };
10400
+ const dynamicFields = config.dynamicFields;
10401
+ if (!Array.isArray(dynamicFields) || dynamicFields.length === 0) {
10402
+ return { config, templateName: template.name || "Untitled", templateId, price: template.price ?? 0 };
10403
+ }
10404
+ const mappings = [];
10405
+ for (const f of dynamicFields) {
10406
+ for (const m of f.mappings || []) {
10407
+ mappings.push({
10408
+ field_key: f.id,
10409
+ element_id: m.elementId,
10410
+ target_property: m.targetProperty || "text"
10411
+ });
10412
+ }
10413
+ }
10414
+ const repeatablePagesInput = deriveRepeatablePagesFromTemplate(config, inlineFormSchema);
10415
+ const resolvedConfig = applyFormDataToConfig(
10416
+ config,
10417
+ mappings,
10418
+ mergedFormData,
10419
+ repeatableSectionsInput,
10420
+ void 0,
10421
+ void 0,
10422
+ void 0,
10423
+ repeatablePagesInput.length > 0 ? repeatablePagesInput : void 0
10424
+ );
10425
+ return {
10426
+ config: resolvedConfig,
10427
+ templateName: template.name || "Untitled",
10428
+ templateId,
10429
+ price: template.price ?? 0
10430
+ };
10251
10431
  }
10252
10432
  async function resolveFromForm(options) {
10253
10433
  var _a, _b, _c, _d;
@@ -10715,6 +10895,7 @@ function PixldocsPreview(props) {
10715
10895
  const [resolvedConfig, setResolvedConfig] = useState(null);
10716
10896
  const [isLoading, setIsLoading] = useState(false);
10717
10897
  const [fontsReady, setFontsReady] = useState(false);
10898
+ const [fontsReadyVersion, setFontsReadyVersion] = useState(0);
10718
10899
  const isResolveMode = !("config" in props && props.config);
10719
10900
  useEffect(() => {
10720
10901
  if (!isResolveMode) {
@@ -10725,6 +10906,7 @@ function PixldocsPreview(props) {
10725
10906
  if (!p.templateId || !p.formSchemaId || !p.supabaseUrl || !p.supabaseAnonKey) return;
10726
10907
  let cancelled = false;
10727
10908
  setIsLoading(true);
10909
+ setFontsReady(false);
10728
10910
  resolveFromForm({
10729
10911
  templateId: p.templateId,
10730
10912
  formSchemaId: p.formSchemaId,
@@ -10766,7 +10948,28 @@ function PixldocsPreview(props) {
10766
10948
  ]);
10767
10949
  const config = isResolveMode ? resolvedConfig : props.config;
10768
10950
  useEffect(() => {
10769
- if (isResolveMode || !config) return;
10951
+ var _a, _b;
10952
+ if (!config) return;
10953
+ let cancelled = false;
10954
+ const bump = () => {
10955
+ if (cancelled) return;
10956
+ clearMeasurementCache();
10957
+ setFontsReadyVersion((v) => v + 1);
10958
+ };
10959
+ (_b = (_a = document.fonts) == null ? void 0 : _a.ready) == null ? void 0 : _b.then(bump);
10960
+ const timeoutId = window.setTimeout(bump, 350);
10961
+ return () => {
10962
+ cancelled = true;
10963
+ window.clearTimeout(timeoutId);
10964
+ };
10965
+ }, [config]);
10966
+ const previewKey = useMemo(() => `${pageIndex}-${fontsReadyVersion}`, [pageIndex, fontsReadyVersion]);
10967
+ useEffect(() => {
10968
+ if (isResolveMode) return;
10969
+ if (!config) {
10970
+ setFontsReady(false);
10971
+ return;
10972
+ }
10770
10973
  setFontsReady(false);
10771
10974
  ensureFontsForResolvedConfig(config).then(() => setFontsReady(true)).catch(() => setFontsReady(true));
10772
10975
  }, [isResolveMode, config]);
@@ -10774,6 +10977,9 @@ function PixldocsPreview(props) {
10774
10977
  return /* @__PURE__ */ jsx("div", { className, style: { ...style, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) });
10775
10978
  }
10776
10979
  if (!config) return null;
10980
+ if (!fontsReady) {
10981
+ return /* @__PURE__ */ jsx("div", { className, style: { ...style, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) });
10982
+ }
10777
10983
  return /* @__PURE__ */ jsx("div", { className, style, children: /* @__PURE__ */ jsx(
10778
10984
  PreviewCanvas,
10779
10985
  {
@@ -10783,7 +10989,8 @@ function PixldocsPreview(props) {
10783
10989
  absoluteZoom,
10784
10990
  onDynamicFieldClick,
10785
10991
  onReady
10786
- }
10992
+ },
10993
+ previewKey
10787
10994
  ) });
10788
10995
  }
10789
10996
  class PixldocsRenderer {