@questpie/admin 3.2.0 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/client/blocks/block-renderer.d.mts +12 -2
  2. package/dist/client/blocks/block-renderer.mjs +357 -49
  3. package/dist/client/components/blocks/block-editor-context.mjs +11 -1
  4. package/dist/client/components/blocks/block-editor-provider.mjs +68 -26
  5. package/dist/client/components/blocks/block-item.mjs +181 -170
  6. package/dist/client/components/blocks/utils/tree-utils.mjs +13 -1
  7. package/dist/client/components/fields/array-field.mjs +177 -118
  8. package/dist/client/components/filter-builder/filter-builder-sheet.mjs +305 -310
  9. package/dist/client/components/filter-builder/filters-tab.mjs +1 -1
  10. package/dist/client/components/history-sidebar.mjs +121 -114
  11. package/dist/client/components/preview/live-preview-mode.mjs +140 -114
  12. package/dist/client/components/preview/preview-pane.mjs +288 -333
  13. package/dist/client/components/primitives/option-label.mjs +44 -0
  14. package/dist/client/components/primitives/select-multi.mjs +408 -383
  15. package/dist/client/components/primitives/select-single.mjs +387 -357
  16. package/dist/client/components/widgets/chart-widget.mjs +168 -143
  17. package/dist/client/contexts/focus-context.d.mts +11 -0
  18. package/dist/client/contexts/focus-context.mjs +51 -34
  19. package/dist/client/hooks/use-brand.mjs +2 -1
  20. package/dist/client/preview/block-scope-context.d.mts +2 -2
  21. package/dist/client/preview/block-scope-context.mjs +10 -20
  22. package/dist/client/preview/index.d.mts +1 -1
  23. package/dist/client/preview/patch.mjs +100 -0
  24. package/dist/client/preview/preview-banner.d.mts +2 -2
  25. package/dist/client/preview/preview-field.d.mts +34 -5
  26. package/dist/client/preview/preview-field.mjs +385 -118
  27. package/dist/client/preview/types.d.mts +82 -3
  28. package/dist/client/preview/types.mjs +85 -6
  29. package/dist/client/preview/use-collection-preview.d.mts +19 -1
  30. package/dist/client/preview/use-collection-preview.mjs +182 -58
  31. package/dist/client/runtime/index.d.mts +2 -2
  32. package/dist/client/runtime/index.mjs +2 -2
  33. package/dist/client/runtime/provider.d.mts +5 -5
  34. package/dist/client/utils/build-field-definitions-from-schema.mjs +8 -3
  35. package/dist/client/views/auth/reset-password-form.d.mts +2 -2
  36. package/dist/client/views/auth/setup-form.d.mts +2 -2
  37. package/dist/client/views/collection/bulk-action-toolbar.mjs +23 -25
  38. package/dist/client/views/collection/cells/primitive-cells.mjs +63 -13
  39. package/dist/client/views/collection/columns/build-columns.mjs +1 -0
  40. package/dist/client/views/collection/form-view.mjs +262 -33
  41. package/dist/client/views/collection/table-view.mjs +16 -11
  42. package/dist/client/views/layout/admin-layout-provider.d.mts +5 -5
  43. package/dist/client/views/layout/admin-layout-provider.mjs +107 -16
  44. package/dist/client.d.mts +2 -2
  45. package/dist/client.mjs +1 -1
  46. package/dist/components/rich-text/rich-text-renderer.d.mts +2 -2
  47. package/dist/index.d.mts +2 -2
  48. package/dist/index.mjs +1 -1
  49. package/dist/server/augmentation/common.d.mts +8 -4
  50. package/dist/server/augmentation/form-layout.d.mts +1 -1
  51. package/dist/server/i18n/messages/cs.mjs +11 -0
  52. package/dist/server/i18n/messages/de.mjs +11 -0
  53. package/dist/server/i18n/messages/en.mjs +11 -0
  54. package/dist/server/i18n/messages/es.mjs +11 -0
  55. package/dist/server/i18n/messages/fr.mjs +11 -0
  56. package/dist/server/i18n/messages/pl.mjs +11 -0
  57. package/dist/server/i18n/messages/pt.mjs +11 -0
  58. package/dist/server/i18n/messages/sk.mjs +11 -0
  59. package/dist/server/modules/admin/block/block-builder.d.mts +7 -10
  60. package/dist/server/modules/admin/block/block-builder.mjs +7 -10
  61. package/dist/server/modules/admin/collections/account.d.mts +50 -50
  62. package/dist/server/modules/admin/collections/admin-locks.d.mts +54 -54
  63. package/dist/server/modules/admin/collections/admin-preferences.d.mts +39 -39
  64. package/dist/server/modules/admin/collections/admin-saved-views.d.mts +47 -47
  65. package/dist/server/modules/admin/collections/apikey.d.mts +68 -68
  66. package/dist/server/modules/admin/collections/assets.d.mts +57 -20
  67. package/dist/server/modules/admin/collections/session.d.mts +42 -42
  68. package/dist/server/modules/admin/collections/user.d.mts +100 -34
  69. package/dist/server/modules/admin/collections/user.mjs +4 -4
  70. package/dist/server/modules/admin/collections/verification.d.mts +32 -32
  71. package/dist/server/modules/admin/index.d.mts +3 -3
  72. package/dist/server/modules/admin/routes/admin-config.d.mts +2 -2
  73. package/dist/server/modules/admin/routes/admin-config.mjs +9 -12
  74. package/dist/server/modules/admin/routes/execute-action.d.mts +9 -9
  75. package/dist/server/modules/admin/routes/locales.d.mts +2 -2
  76. package/dist/server/modules/admin/routes/preview.d.mts +11 -11
  77. package/dist/server/modules/admin/routes/reactive.d.mts +9 -9
  78. package/dist/server/modules/admin/routes/setup.d.mts +7 -7
  79. package/dist/server/modules/admin/routes/translations.d.mts +4 -4
  80. package/dist/server/modules/admin/routes/translations.mjs +1 -1
  81. package/dist/server/modules/admin/routes/widget-data.d.mts +5 -5
  82. package/dist/server/modules/admin-preferences/collections/admin-preferences.mjs +4 -6
  83. package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +31 -33
  84. package/dist/server/modules/admin-preferences/collections/saved-views.mjs +4 -6
  85. package/package.json +3 -3
@@ -22,6 +22,13 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
22
22
  *
23
23
  * Combines both functionalities into a single non-jumping UI element.
24
24
  */
25
+ function getSelectedIds(items) {
26
+ return items.flatMap((item) => {
27
+ if (typeof item !== "object" || item === null || !("id" in item)) return [];
28
+ const id = item.id;
29
+ return id == null ? [] : [String(id)];
30
+ });
31
+ }
25
32
  /**
26
33
  * BulkActionToolbar - Shows selection count and bulk actions
27
34
  *
@@ -142,12 +149,12 @@ function BulkActionToolbar(t0) {
142
149
  const { handler } = action_0;
143
150
  if (action_0.id === "deleteMany" && onBulkDelete) {
144
151
  setIsLoading(true);
145
- const ids = selectedItems.map(_temp2).filter(Boolean);
152
+ const ids = getSelectedIds(selectedItems);
146
153
  try {
147
154
  await onBulkDelete(ids);
148
155
  table.resetRowSelection();
149
156
  setIsLoading(false);
150
- } catch (t10$1) {
157
+ } catch {
151
158
  helpers.toast.error(t("collection.bulkDeleteError"));
152
159
  setIsLoading(false);
153
160
  }
@@ -155,32 +162,32 @@ function BulkActionToolbar(t0) {
155
162
  }
156
163
  if (action_0.id === "restoreMany" && onBulkRestore) {
157
164
  setIsLoading(true);
158
- const ids_0 = selectedItems.map(_temp3).filter(Boolean);
165
+ const ids_0 = getSelectedIds(selectedItems);
159
166
  try {
160
167
  await onBulkRestore(ids_0);
161
168
  table.resetRowSelection();
162
169
  setIsLoading(false);
163
- } catch (t11$1) {
170
+ } catch {
164
171
  helpers.toast.error(t("collection.bulkRestoreError"));
165
172
  setIsLoading(false);
166
173
  }
167
174
  return;
168
175
  }
169
- bb96: switch (handler.type) {
176
+ bb86: switch (handler.type) {
170
177
  case "api": {
171
178
  setIsLoading(true);
172
- const ids_1 = selectedItems.map(_temp4).filter(Boolean);
179
+ const ids_1 = getSelectedIds(selectedItems);
173
180
  const method = handler.method ? handler.method : "POST";
174
181
  try {
175
182
  helpers.toast.info(`Bulk API call: ${method} ${handler.endpoint} (${ids_1.length} items)`);
176
183
  helpers.refresh();
177
184
  table.resetRowSelection();
178
185
  setIsLoading(false);
179
- } catch (t12$1) {
186
+ } catch {
180
187
  helpers.toast.error(t("collection.bulkActionFailed"));
181
188
  setIsLoading(false);
182
189
  }
183
- break bb96;
190
+ break bb86;
184
191
  }
185
192
  case "custom":
186
193
  setIsLoading(true);
@@ -188,18 +195,18 @@ function BulkActionToolbar(t0) {
188
195
  await handler.fn(ctx);
189
196
  table.resetRowSelection();
190
197
  setIsLoading(false);
191
- } catch (t13$1) {
198
+ } catch {
192
199
  helpers.toast.error(t("collection.bulkActionFailed"));
193
200
  setIsLoading(false);
194
201
  }
195
- break bb96;
202
+ break bb86;
196
203
  case "dialog":
197
204
  case "form":
198
205
  onOpenDialog?.(action_0, selectedItems);
199
- break bb96;
206
+ break bb86;
200
207
  case "navigate":
201
208
  helpers.toast.error("Navigate action not supported for bulk operations");
202
- break bb96;
209
+ break bb86;
203
210
  case "server": onOpenDialog?.(action_0, selectedItems);
204
211
  }
205
212
  };
@@ -272,8 +279,8 @@ function BulkActionToolbar(t0) {
272
279
  let t17;
273
280
  let t18;
274
281
  if ($[38] !== filterCount || $[39] !== handleActionClick || $[40] !== handleSelectAllMatching || $[41] !== hasFilters || $[42] !== hasSelection || $[43] !== isDisabled || $[44] !== isLoading || $[45] !== isSelectingAll || $[46] !== isVisible || $[47] !== onClearFilters || $[48] !== onOpenFilters || $[49] !== onSelectAllMatching || $[50] !== pageCount || $[51] !== resolveText || $[52] !== selectedCount || $[53] !== t || $[54] !== table || $[55] !== totalCount || $[56] !== visibleActions) {
275
- const regularActions = visibleActions.filter(_temp5);
276
- const destructiveActions = visibleActions.filter(_temp6);
282
+ const regularActions = visibleActions.filter(_temp2);
283
+ const destructiveActions = visibleActions.filter(_temp3);
277
284
  t17 = isVisible ? "open" : "closed";
278
285
  t18 = "qa-bulk-toolbar fixed bottom-6 left-1/2 z-50 max-w-[calc(100%-2rem)] -translate-x-1/2 transition-[opacity,translate,scale] duration-[var(--motion-duration-slow)] ease-[var(--motion-ease-enter)] data-[state=closed]:pointer-events-none data-[state=closed]:translate-y-2 data-[state=closed]:scale-[0.98] data-[state=closed]:opacity-0 data-[state=open]:translate-y-0 data-[state=open]:scale-100 data-[state=open]:opacity-100 motion-reduce:transition-none sm:max-w-none";
279
286
  t14 = "qa-bulk-toolbar__bar bg-background border-border flex items-center gap-2 overflow-x-auto rounded-full border px-3 py-2 shadow-lg transition-[gap,padding] duration-[var(--motion-duration-base)] ease-[var(--motion-ease-standard)] sm:gap-3 sm:px-4 sm:py-2.5";
@@ -492,21 +499,12 @@ function BulkActionToolbar(t0) {
492
499
  } else t22 = $[85];
493
500
  return t22;
494
501
  }
495
- function _temp6(a_0) {
502
+ function _temp3(a_0) {
496
503
  return a_0.variant === "destructive";
497
504
  }
498
- function _temp5(a) {
505
+ function _temp2(a) {
499
506
  return a.variant !== "destructive";
500
507
  }
501
- function _temp4(item_1) {
502
- if (item_1 != null) return item_1.id;
503
- }
504
- function _temp3(item_0) {
505
- if (item_0 != null) return item_0.id;
506
- }
507
- function _temp2(item) {
508
- if (item != null) return item.id;
509
- }
510
508
  function _temp(row) {
511
509
  return row.original;
512
510
  }
@@ -1,4 +1,5 @@
1
- import { useTranslation } from "../../../i18n/hooks.mjs";
1
+ import { useResolveText, useTranslation } from "../../../i18n/hooks.mjs";
2
+ import { resolveOptionLabelForValue } from "../../../components/primitives/option-label.mjs";
2
3
  import { Badge } from "../../../components/ui/badge.mjs";
3
4
  import { c } from "react/compiler-runtime";
4
5
  import "react";
@@ -364,10 +365,25 @@ function EmailCell(t0) {
364
365
  /**
365
366
  * Select/Status cell - badge display
366
367
  */
368
+ function getSelectOptions(fieldDef) {
369
+ const options = fieldDef?.["~options"]?.options;
370
+ return Array.isArray(options) ? options : void 0;
371
+ }
372
+ function resolveSelectCellLabel({ value, fieldDef, resolveText, t, locale }) {
373
+ return resolveOptionLabelForValue({
374
+ value,
375
+ options: getSelectOptions(fieldDef),
376
+ resolveText,
377
+ t,
378
+ locale
379
+ });
380
+ }
367
381
  function SelectCell(t0) {
368
- const $ = c(3);
369
- const { value } = t0;
370
- if (value === null || value === void 0 || value === "") {
382
+ const $ = c(16);
383
+ const { value, fieldDef } = t0;
384
+ const { t, locale } = useTranslation();
385
+ const resolveText = useResolveText();
386
+ if (value === null || value === void 0 || value === "" || Array.isArray(value) && value.length === 0) {
371
387
  let t1$1;
372
388
  if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
373
389
  t1$1 = /* @__PURE__ */ jsx("span", {
@@ -378,17 +394,51 @@ function SelectCell(t0) {
378
394
  } else t1$1 = $[0];
379
395
  return t1$1;
380
396
  }
381
- const t1 = String(value);
397
+ let t1;
398
+ if ($[1] !== value) {
399
+ t1 = Array.isArray(value) ? value : [value];
400
+ $[1] = value;
401
+ $[2] = t1;
402
+ } else t1 = $[2];
403
+ const values = t1;
382
404
  let t2;
383
- if ($[1] !== t1) {
384
- t2 = /* @__PURE__ */ jsx(Badge, {
385
- variant: "outline",
386
- children: t1
405
+ if ($[3] !== fieldDef || $[4] !== locale || $[5] !== resolveText || $[6] !== t || $[7] !== values) {
406
+ let t3$1;
407
+ if ($[9] !== fieldDef || $[10] !== locale || $[11] !== resolveText || $[12] !== t) {
408
+ t3$1 = (item, index) => /* @__PURE__ */ jsx(Badge, {
409
+ variant: "outline",
410
+ children: resolveSelectCellLabel({
411
+ value: item,
412
+ fieldDef,
413
+ resolveText,
414
+ t,
415
+ locale
416
+ })
417
+ }, `${String(item)}-${index}`);
418
+ $[9] = fieldDef;
419
+ $[10] = locale;
420
+ $[11] = resolveText;
421
+ $[12] = t;
422
+ $[13] = t3$1;
423
+ } else t3$1 = $[13];
424
+ t2 = values.map(t3$1);
425
+ $[3] = fieldDef;
426
+ $[4] = locale;
427
+ $[5] = resolveText;
428
+ $[6] = t;
429
+ $[7] = values;
430
+ $[8] = t2;
431
+ } else t2 = $[8];
432
+ let t3;
433
+ if ($[14] !== t2) {
434
+ t3 = /* @__PURE__ */ jsx("span", {
435
+ className: "inline-flex max-w-[300px] flex-wrap gap-1",
436
+ children: t2
387
437
  });
388
- $[1] = t1;
389
- $[2] = t2;
390
- } else t2 = $[2];
391
- return t2;
438
+ $[14] = t2;
439
+ $[15] = t3;
440
+ } else t3 = $[15];
441
+ return t3;
392
442
  }
393
443
 
394
444
  //#endregion
@@ -75,6 +75,7 @@ function ColumnHeader(t0) {
75
75
  const FIELD_TYPES_NEEDING_FIELD_DEF = new Set([
76
76
  "object",
77
77
  "array",
78
+ "select",
78
79
  "relation",
79
80
  "reverseRelation",
80
81
  "upload",
@@ -10,6 +10,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSepara
10
10
  import { LocaleSwitcher } from "../../components/locale-switcher.mjs";
11
11
  import { Label } from "../../components/ui/label.mjs";
12
12
  import { RenderProfiler } from "../../lib/render-profiler.mjs";
13
+ import { scrollFieldIntoView } from "../../contexts/focus-context.mjs";
13
14
  import { Checkbox } from "../../components/ui/checkbox.mjs";
14
15
  import { DateTimeInput } from "../../components/primitives/date-input.mjs";
15
16
  import { getDefaultFormActions } from "../../builder/types/action-registry.mjs";
@@ -63,6 +64,139 @@ function extractReactiveConfigs(schema) {
63
64
  for (const [fieldName, fieldDef] of Object.entries(schema.fields)) if (fieldDef.reactive) configs[fieldName] = fieldDef.reactive;
64
65
  return configs;
65
66
  }
67
+ function isPlainObject(value) {
68
+ if (!value || typeof value !== "object") return false;
69
+ const prototype = Object.getPrototypeOf(value);
70
+ return prototype === Object.prototype || prototype === null;
71
+ }
72
+ function clonePreviewSnapshot(value) {
73
+ if (typeof structuredClone === "function") try {
74
+ return structuredClone(value);
75
+ } catch {}
76
+ const json = JSON.stringify(value);
77
+ return json === void 0 ? value : JSON.parse(json);
78
+ }
79
+ function arePreviewValuesEqual(a, b) {
80
+ if (Object.is(a, b)) return true;
81
+ if (Array.isArray(a) || Array.isArray(b)) {
82
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
83
+ return a.every((value, index) => arePreviewValuesEqual(value, b[index]));
84
+ }
85
+ if (isPlainObject(a) || isPlainObject(b)) {
86
+ if (!isPlainObject(a) || !isPlainObject(b)) return false;
87
+ const aKeys = Object.keys(a);
88
+ const bKeys = Object.keys(b);
89
+ if (aKeys.length !== bKeys.length) return false;
90
+ return aKeys.every((key) => Object.prototype.hasOwnProperty.call(b, key) && arePreviewValuesEqual(a[key], b[key]));
91
+ }
92
+ return false;
93
+ }
94
+ function joinPreviewPath(basePath, key) {
95
+ return basePath ? `${basePath}.${key}` : key;
96
+ }
97
+ function diffPreviewSnapshots(previous, current, basePath = "") {
98
+ if (arePreviewValuesEqual(previous, current)) return [];
99
+ if (isPlainObject(previous) && isPlainObject(current)) {
100
+ const ops = [];
101
+ const keys = new Set([...Object.keys(previous), ...Object.keys(current)]);
102
+ for (const key of keys) {
103
+ const path = joinPreviewPath(basePath, key);
104
+ const previousHasKey = Object.prototype.hasOwnProperty.call(previous, key);
105
+ if (!Object.prototype.hasOwnProperty.call(current, key)) {
106
+ ops.push({
107
+ op: "remove",
108
+ path
109
+ });
110
+ continue;
111
+ }
112
+ if (!previousHasKey) {
113
+ ops.push({
114
+ op: "set",
115
+ path,
116
+ value: current[key]
117
+ });
118
+ continue;
119
+ }
120
+ ops.push(...diffPreviewSnapshots(previous[key], current[key], path));
121
+ }
122
+ return ops;
123
+ }
124
+ if (!basePath) return [{
125
+ op: "set",
126
+ path: "",
127
+ value: current
128
+ }];
129
+ return [{
130
+ op: "set",
131
+ path: basePath,
132
+ value: current
133
+ }];
134
+ }
135
+ function isSafePreviewEditPath(path) {
136
+ if (!path || path.length > 512) return false;
137
+ return path.split(".").every((segment) => segment.length > 0 && segment !== "__proto__" && segment !== "prototype" && segment !== "constructor");
138
+ }
139
+ function hasValueAtPath(value, path) {
140
+ let current = value;
141
+ for (const segment of path.split(".")) {
142
+ if (!current || typeof current !== "object") return false;
143
+ if (!Object.prototype.hasOwnProperty.call(current, segment)) return false;
144
+ current = current[segment];
145
+ }
146
+ return true;
147
+ }
148
+ function getTopLevelFieldSchema(schema, path) {
149
+ const topLevelField = path.split(".")[0];
150
+ if (!topLevelField) return void 0;
151
+ return schema?.fields?.[topLevelField];
152
+ }
153
+ function fieldSupportsInlineEdit(field, path, inputKind) {
154
+ const fieldType = field.metadata?.type;
155
+ const isNestedPath = path.includes(".");
156
+ if (fieldType === "object" || fieldType === "json") return isNestedPath;
157
+ switch (inputKind) {
158
+ case "text": return fieldType === "text" || fieldType === "email" || fieldType === "url" || fieldType === "select";
159
+ case "textarea": return fieldType === "textarea" || fieldType === "richText" || fieldType === "text";
160
+ case "number": return fieldType === "number";
161
+ case "boolean": return fieldType === "boolean";
162
+ }
163
+ }
164
+ function isAllowedPreviewEditPath({ message, schema, values }) {
165
+ if (!schema || !isSafePreviewEditPath(message.path)) return false;
166
+ if (message.fieldType === "relation") return false;
167
+ const blockMatch = message.path.match(/^([^.]+)\._values\.([^.]+)\.(.+)$/);
168
+ if (blockMatch) {
169
+ const [, blocksFieldName, blockId] = blockMatch;
170
+ if ((schema.fields?.[blocksFieldName])?.metadata?.type !== "blocks") return false;
171
+ if (message.blockId && message.blockId !== blockId) return false;
172
+ return hasValueAtPath(values, message.path);
173
+ }
174
+ const field = getTopLevelFieldSchema(schema, message.path);
175
+ if (!field) return false;
176
+ if (field.metadata?.readOnly || field.access?.update?.allowed === false) return false;
177
+ if (message.path.includes(".") && !hasValueAtPath(values, message.path)) return false;
178
+ return fieldSupportsInlineEdit(field, message.path, message.inputKind);
179
+ }
180
+ function normalizeInlineEditValue(message) {
181
+ switch (message.inputKind) {
182
+ case "text":
183
+ case "textarea": return typeof message.value === "string" ? {
184
+ ok: true,
185
+ value: message.value
186
+ } : { ok: false };
187
+ case "number": {
188
+ const value = typeof message.value === "number" ? message.value : typeof message.value === "string" ? Number(message.value) : NaN;
189
+ return Number.isFinite(value) ? {
190
+ ok: true,
191
+ value
192
+ } : { ok: false };
193
+ }
194
+ case "boolean": return typeof message.value === "boolean" ? {
195
+ ok: true,
196
+ value: message.value
197
+ } : { ok: false };
198
+ }
199
+ }
66
200
  /**
67
201
  * Component that manages reactive field states.
68
202
  * Must be rendered inside FormProvider to access form context.
@@ -174,13 +308,64 @@ const FormStateRefBridge = React.memo(function FormStateRefBridge$1(t0) {
174
308
  React.useEffect(t4, t5);
175
309
  return null;
176
310
  });
311
+ const PreviewPatchBridge = React.memo(function PreviewPatchBridge$1(t0) {
312
+ const $ = c(5);
313
+ const { form, previewRef, enabled } = t0;
314
+ const previousSnapshotRef = React.useRef(null);
315
+ const snapshotVersionRef = React.useRef(void 0);
316
+ let t1;
317
+ let t2;
318
+ if ($[0] !== enabled || $[1] !== form || $[2] !== previewRef) {
319
+ t1 = () => {
320
+ if (!enabled) {
321
+ previousSnapshotRef.current = null;
322
+ snapshotVersionRef.current = void 0;
323
+ return;
324
+ }
325
+ const initialSnapshot = clonePreviewSnapshot(form.getValues());
326
+ previousSnapshotRef.current = initialSnapshot;
327
+ snapshotVersionRef.current = previewRef.current?.sendInitSnapshot(initialSnapshot);
328
+ const subscription = form.watch((values) => {
329
+ const previousSnapshot = previousSnapshotRef.current;
330
+ const nextSnapshot = clonePreviewSnapshot(values);
331
+ if (!previousSnapshot) {
332
+ previousSnapshotRef.current = nextSnapshot;
333
+ snapshotVersionRef.current = previewRef.current?.sendInitSnapshot(nextSnapshot);
334
+ return;
335
+ }
336
+ const ops = diffPreviewSnapshots(previousSnapshot, nextSnapshot).filter(_temp);
337
+ previousSnapshotRef.current = nextSnapshot;
338
+ if (ops.length === 0) return;
339
+ previewRef.current?.sendPatchBatch(ops, snapshotVersionRef.current);
340
+ });
341
+ return () => {
342
+ subscription.unsubscribe();
343
+ };
344
+ };
345
+ t2 = [
346
+ enabled,
347
+ form,
348
+ previewRef
349
+ ];
350
+ $[0] = enabled;
351
+ $[1] = form;
352
+ $[2] = previewRef;
353
+ $[3] = t1;
354
+ $[4] = t2;
355
+ } else {
356
+ t1 = $[3];
357
+ t2 = $[4];
358
+ }
359
+ React.useEffect(t1, t2);
360
+ return null;
361
+ });
177
362
  const AutosaveManager = React.memo(function AutosaveManager$1(t0) {
178
- const $ = c(18);
179
- const { form, formElementRef, isEditMode, id, enabled, debounce, isDirtyRef, isSubmittingRef, updateMutation, onPreviewRefresh, onSavingChange, onSaved } = t0;
363
+ const $ = c(19);
364
+ const { form, formElementRef, isEditMode, id, enabled, debounce, isDirtyRef, isSubmittingRef, updateMutation, onPreviewRefresh, onPreviewCommit, onSavingChange, onSaved } = t0;
180
365
  const { t } = useTranslation();
181
366
  const timerRef = React.useRef(null);
182
367
  let t1;
183
- if ($[0] !== form || $[1] !== id || $[2] !== isDirtyRef || $[3] !== isSubmittingRef || $[4] !== onPreviewRefresh || $[5] !== onSaved || $[6] !== onSavingChange || $[7] !== t || $[8] !== updateMutation) {
368
+ if ($[0] !== form || $[1] !== id || $[2] !== isDirtyRef || $[3] !== isSubmittingRef || $[4] !== onPreviewCommit || $[5] !== onPreviewRefresh || $[6] !== onSaved || $[7] !== onSavingChange || $[8] !== t || $[9] !== updateMutation) {
184
369
  t1 = async () => {
185
370
  if (!id || !isDirtyRef.current || isSubmittingRef.current) return;
186
371
  try {
@@ -191,6 +376,7 @@ const AutosaveManager = React.memo(function AutosaveManager$1(t0) {
191
376
  data
192
377
  });
193
378
  form.reset(result, { keepTouched: true });
379
+ onPreviewCommit?.(result);
194
380
  onPreviewRefresh?.();
195
381
  onSaved(/* @__PURE__ */ new Date());
196
382
  onSavingChange(false);
@@ -208,17 +394,18 @@ const AutosaveManager = React.memo(function AutosaveManager$1(t0) {
208
394
  $[1] = id;
209
395
  $[2] = isDirtyRef;
210
396
  $[3] = isSubmittingRef;
211
- $[4] = onPreviewRefresh;
212
- $[5] = onSaved;
213
- $[6] = onSavingChange;
214
- $[7] = t;
215
- $[8] = updateMutation;
216
- $[9] = t1;
217
- } else t1 = $[9];
397
+ $[4] = onPreviewCommit;
398
+ $[5] = onPreviewRefresh;
399
+ $[6] = onSaved;
400
+ $[7] = onSavingChange;
401
+ $[8] = t;
402
+ $[9] = updateMutation;
403
+ $[10] = t1;
404
+ } else t1 = $[10];
218
405
  const runAutosave = t1;
219
406
  let t2;
220
407
  let t3;
221
- if ($[10] !== debounce || $[11] !== enabled || $[12] !== formElementRef || $[13] !== id || $[14] !== isEditMode || $[15] !== runAutosave) {
408
+ if ($[11] !== debounce || $[12] !== enabled || $[13] !== formElementRef || $[14] !== id || $[15] !== isEditMode || $[16] !== runAutosave) {
222
409
  t2 = () => {
223
410
  if (timerRef.current) clearTimeout(timerRef.current);
224
411
  if (!enabled || !isEditMode || !id) return;
@@ -246,17 +433,17 @@ const AutosaveManager = React.memo(function AutosaveManager$1(t0) {
246
433
  isEditMode,
247
434
  runAutosave
248
435
  ];
249
- $[10] = debounce;
250
- $[11] = enabled;
251
- $[12] = formElementRef;
252
- $[13] = id;
253
- $[14] = isEditMode;
254
- $[15] = runAutosave;
255
- $[16] = t2;
256
- $[17] = t3;
436
+ $[11] = debounce;
437
+ $[12] = enabled;
438
+ $[13] = formElementRef;
439
+ $[14] = id;
440
+ $[15] = isEditMode;
441
+ $[16] = runAutosave;
442
+ $[17] = t2;
443
+ $[18] = t3;
257
444
  } else {
258
- t2 = $[16];
259
- t3 = $[17];
445
+ t2 = $[17];
446
+ t3 = $[18];
260
447
  }
261
448
  React.useEffect(t2, t3);
262
449
  return null;
@@ -271,7 +458,7 @@ const AutosaveIndicator = React.memo(function AutosaveIndicator$1(t0) {
271
458
  $[1] = t1;
272
459
  } else t1 = $[1];
273
460
  const { isDirty } = useFormState(t1);
274
- const [, forceUpdate] = React.useReducer(_temp, 0);
461
+ const [, forceUpdate] = React.useReducer(_temp2, 0);
275
462
  let t2;
276
463
  if ($[2] !== forceUpdate || $[3] !== lastSaved) {
277
464
  t2 = () => {
@@ -571,6 +758,33 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
571
758
  resolver,
572
759
  mode: "onBlur"
573
760
  });
761
+ const commitPreviewSnapshot = React.useCallback((data) => {
762
+ if (!isLivePreviewOpen) return;
763
+ previewRef.current?.sendCommit(clonePreviewSnapshot(data));
764
+ }, [isLivePreviewOpen]);
765
+ const triggerPreviewFullResync = React.useCallback((reason) => {
766
+ if (!isLivePreviewOpen) return;
767
+ previewRef.current?.sendFullResync(reason);
768
+ previewRef.current?.triggerRefresh();
769
+ }, [isLivePreviewOpen]);
770
+ const handlePreviewFieldValueEdited = React.useCallback((message) => {
771
+ if (!isAllowedPreviewEditPath({
772
+ message,
773
+ schema,
774
+ values: form.getValues()
775
+ })) return;
776
+ const normalized = normalizeInlineEditValue(message);
777
+ if (!normalized.ok) return;
778
+ form.setValue(message.path, normalized.value, {
779
+ shouldDirty: true,
780
+ shouldTouch: true,
781
+ shouldValidate: true
782
+ });
783
+ setTimeout(() => scrollFieldIntoView(message.path), 0);
784
+ }, [form, schema]);
785
+ const handlePreviewResyncRequest = React.useCallback((reason_0) => {
786
+ triggerPreviewFullResync(reason_0 ?? "preview-request");
787
+ }, [triggerPreviewFullResync]);
574
788
  /**
575
789
  * Execute the confirmed workflow transition (immediate or scheduled).
576
790
  */
@@ -595,6 +809,7 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
595
809
  if ("id" in result_0) form.reset(result_0);
596
810
  }
597
811
  }
812
+ triggerPreviewFullResync("workflow-transition");
598
813
  if (transitionSchedule) if (transitionScheduledAt) toast.success(t("workflow.scheduledSuccess", {
599
814
  stage: stageLabel,
600
815
  date: transitionScheduledAt.toLocaleString()
@@ -664,7 +879,7 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
664
879
  }
665
880
  }, [
666
881
  contentLocale,
667
- form.getValues,
882
+ form,
668
883
  setContentLocale,
669
884
  localeChangeDialog.open,
670
885
  isEditMode
@@ -688,6 +903,7 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
688
903
  prevLocaleRef.current = localeChangeDialog.pendingLocale;
689
904
  setContentLocale(localeChangeDialog.pendingLocale);
690
905
  localeQueryClient.invalidateQueries({ queryKey: ["collections", collection] });
906
+ triggerPreviewFullResync("locale-change");
691
907
  }
692
908
  setLocaleChangeDialog({
693
909
  open: false,
@@ -697,7 +913,8 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
697
913
  localeChangeDialog.pendingLocale,
698
914
  setContentLocale,
699
915
  localeQueryClient,
700
- collection
916
+ collection,
917
+ triggerPreviewFullResync
701
918
  ]);
702
919
  const handleLocaleChangeCancel = React.useCallback(() => {
703
920
  skipItemResetRef.current = false;
@@ -716,13 +933,13 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
716
933
  const handleLocaleDialogOpenChange = React.useCallback((open) => {
717
934
  if (!open) handleLocaleChangeCancel();
718
935
  }, [handleLocaleChangeCancel]);
719
- const onSubmit = React.useEffectEvent(async (data) => {
936
+ const onSubmit = React.useEffectEvent(async (data_0) => {
720
937
  const savePromise = async () => {
721
938
  if (isEditMode && id) return await updateMutation.mutateAsync({
722
939
  id,
723
- data
940
+ data: data_0
724
941
  });
725
- else return await createMutation.mutateAsync(data);
942
+ else return await createMutation.mutateAsync(data_0);
726
943
  };
727
944
  toast.promise(savePromise(), {
728
945
  loading: isEditMode ? t("toast.saving") : t("toast.creating"),
@@ -730,6 +947,7 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
730
947
  if (onSuccess) onSuccess(result_1);
731
948
  else if (isEditMode) {
732
949
  form.reset(result_1);
950
+ commitPreviewSnapshot(result_1);
733
951
  triggerPreviewRefresh();
734
952
  } else if (result_1?.id) navigate(`${basePath}/collections/${collection}/${result_1.id}`);
735
953
  else navigate(`${basePath}/collections/${collection}`);
@@ -743,9 +961,9 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
743
961
  });
744
962
  return t("toast.validationFailed");
745
963
  }
746
- const message = error instanceof Error ? error.message : t("error.unknown");
747
- onError?.(error instanceof Error ? error : new Error(message));
748
- return `${t("toast.saveFailed")}: ${message}`;
964
+ const message_0 = error instanceof Error ? error.message : t("error.unknown");
965
+ onError?.(error instanceof Error ? error : new Error(message_0));
966
+ return `${t("toast.saveFailed")}: ${message_0}`;
749
967
  }
750
968
  });
751
969
  });
@@ -988,7 +1206,7 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
988
1206
  let errorBody = {};
989
1207
  try {
990
1208
  errorBody = await response_0.json();
991
- } catch (_parseErr) {}
1209
+ } catch {}
992
1210
  let errorMessage;
993
1211
  if (errorBody.message) if (typeof errorBody.message === "string") errorMessage = errorBody.message;
994
1212
  else errorMessage = t("toast.actionFailed");
@@ -1062,7 +1280,7 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
1062
1280
  else if (typeof pendingRevertVersion.versionNumber === "number") payload.version = pendingRevertVersion.versionNumber;
1063
1281
  const result_3 = await revertVersionMutation.mutateAsync(payload);
1064
1282
  form.reset(result_3);
1065
- triggerPreviewRefresh();
1283
+ triggerPreviewFullResync("version-revert");
1066
1284
  toast.success(t("version.revertSuccess"));
1067
1285
  setPendingRevertVersion(null);
1068
1286
  };
@@ -1252,10 +1470,16 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
1252
1470
  isDirtyRef: formIsDirtyRef,
1253
1471
  isSubmittingRef: formIsSubmittingRef,
1254
1472
  updateMutation,
1473
+ onPreviewCommit: commitPreviewSnapshot,
1255
1474
  onPreviewRefresh: triggerPreviewRefresh,
1256
1475
  onSavingChange: setIsSaving,
1257
1476
  onSaved: setLastSaved
1258
1477
  }),
1478
+ /* @__PURE__ */ jsx(PreviewPatchBridge, {
1479
+ form,
1480
+ previewRef,
1481
+ enabled: isLivePreviewOpen && !!previewUrl && !isBlocked
1482
+ }),
1259
1483
  /* @__PURE__ */ jsx(ReactiveFieldsManager, {
1260
1484
  collection,
1261
1485
  reactiveConfigs,
@@ -1591,12 +1815,17 @@ function FormView({ collection, id, config, viewConfig, navigate, basePath = "/a
1591
1815
  onClose: () => setIsLivePreviewOpen(false),
1592
1816
  previewUrl,
1593
1817
  previewRef,
1818
+ onFieldValueEdited: handlePreviewFieldValueEdited,
1819
+ onResyncRequest: handlePreviewResyncRequest,
1594
1820
  defaultSize: schemaPreview?.defaultSize,
1595
1821
  minSize: schemaPreview?.minSize,
1596
1822
  children: formShell
1597
1823
  });
1598
1824
  }
1599
- function _temp(x) {
1825
+ function _temp(op) {
1826
+ return op.path;
1827
+ }
1828
+ function _temp2(x) {
1600
1829
  return x + 1;
1601
1830
  }
1602
1831