@particle-academy/fancy-cms-ui 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/editor.cjs CHANGED
@@ -47,51 +47,7 @@ function setOwn(map, key, value) {
47
47
  Object.defineProperty(map, key, { value, writable: true, enumerable: true, configurable: true });
48
48
  }
49
49
 
50
- // src/document/migrate.ts
51
- function normalizeDoc(input3) {
52
- if (hasNodeMap(input3)) return input3;
53
- const id = typeof input3 === "object" && input3 !== null && !Array.isArray(input3) && typeof input3.id === "string" ? input3.id : "malformed";
54
- return emptyDoc(id);
55
- }
56
- function migrateDocVerbose(input3) {
57
- const normalized = normalizeDoc(input3);
58
- const legacy = normalized;
59
- const sections = legacy.sections;
60
- if (!Array.isArray(sections)) {
61
- return { doc: normalized, migrated: false, danglingIds: [], unlistedRootIds: [] };
62
- }
63
- const danglingIds = [];
64
- const ordered = [];
65
- const seen = /* @__PURE__ */ new Set();
66
- for (const id of sections) {
67
- if (!own(normalized.nodes, id)) {
68
- danglingIds.push(id);
69
- continue;
70
- }
71
- if (seen.has(id)) continue;
72
- seen.add(id);
73
- ordered.push(id);
74
- }
75
- const unlistedRootIds = Object.values(normalized.nodes).filter((n) => n.parent === null && !seen.has(n.id)).sort((a, b) => a.order < b.order ? -1 : a.order > b.order ? 1 : 0).map((n) => n.id);
76
- const finalOrder = [...ordered, ...unlistedRootIds];
77
- const nodes = { ...normalized.nodes };
78
- let prev = null;
79
- for (const id of finalOrder) {
80
- const key = fancyDocCommons.fractionalKey(prev, null);
81
- setOwn(nodes, id, { ...own(nodes, id), order: key });
82
- prev = key;
83
- }
84
- const { sections: _dropped, ...rest } = legacy;
85
- return {
86
- doc: { ...rest, nodes },
87
- migrated: true,
88
- danglingIds,
89
- unlistedRootIds
90
- };
91
- }
92
- function migrateDoc(input3) {
93
- return migrateDocVerbose(input3).doc;
94
- }
50
+ // src/document/reduce.ts
95
51
  function childrenOf(doc, parent) {
96
52
  return fancyDocCommons.childrenOf(doc, parent);
97
53
  }
@@ -231,6 +187,50 @@ function reduce(doc, op, opts = {}) {
231
187
  return invalid(`unknown op "${op.t}"`);
232
188
  }
233
189
  }
190
+ function normalizeDoc(input3) {
191
+ if (hasNodeMap(input3)) return input3;
192
+ const id = typeof input3 === "object" && input3 !== null && !Array.isArray(input3) && typeof input3.id === "string" ? input3.id : "malformed";
193
+ return emptyDoc(id);
194
+ }
195
+ function migrateDocVerbose(input3) {
196
+ const normalized = normalizeDoc(input3);
197
+ const legacy = normalized;
198
+ const sections = legacy.sections;
199
+ if (!Array.isArray(sections)) {
200
+ return { doc: normalized, migrated: false, danglingIds: [], unlistedRootIds: [] };
201
+ }
202
+ const danglingIds = [];
203
+ const ordered = [];
204
+ const seen = /* @__PURE__ */ new Set();
205
+ for (const id of sections) {
206
+ if (!own(normalized.nodes, id)) {
207
+ danglingIds.push(id);
208
+ continue;
209
+ }
210
+ if (seen.has(id)) continue;
211
+ seen.add(id);
212
+ ordered.push(id);
213
+ }
214
+ const unlistedRootIds = Object.values(normalized.nodes).filter((n) => n.parent === null && !seen.has(n.id)).sort((a, b) => a.order < b.order ? -1 : a.order > b.order ? 1 : 0).map((n) => n.id);
215
+ const finalOrder = [...ordered, ...unlistedRootIds];
216
+ const nodes = { ...normalized.nodes };
217
+ let prev = null;
218
+ for (const id of finalOrder) {
219
+ const key = fancyDocCommons.fractionalKey(prev, null);
220
+ setOwn(nodes, id, { ...own(nodes, id), order: key });
221
+ prev = key;
222
+ }
223
+ const { sections: _dropped, ...rest } = legacy;
224
+ return {
225
+ doc: { ...rest, nodes },
226
+ migrated: true,
227
+ danglingIds,
228
+ unlistedRootIds
229
+ };
230
+ }
231
+ function migrateDoc(input3) {
232
+ return migrateDocVerbose(input3).doc;
233
+ }
234
234
  function initEditor(doc) {
235
235
  return { doc: migrateDoc(doc), past: [], future: [], selection: null };
236
236
  }
@@ -900,16 +900,42 @@ var input = {
900
900
  font: "inherit",
901
901
  fontSize: 13
902
902
  };
903
- function Inspector({ doc, selection, apply }) {
903
+ function Inspector({ doc, selection, apply, drafts }) {
904
904
  const uid = react.useId();
905
905
  const fieldId = (key) => `${uid}-${key}`;
906
906
  const node = selection ? doc.nodes[selection] : null;
907
907
  if (!node) {
908
908
  return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: 16, color: "var(--fcms-muted)", fontFamily: "system-ui, sans-serif", fontSize: 13 }, children: "Select an element to edit." });
909
909
  }
910
- const s = node.style.base;
911
- const setStyle = (patch) => apply({ t: "set_style", id: node.id, breakpoint: "base", patch });
912
- const setProp = (key, value) => apply({ t: "set_node_props", id: node.id, patch: { [key]: value } });
910
+ const nodeId = node.id;
911
+ const bind = (name, read, op, numeric = false) => {
912
+ const key = JSON.stringify([nodeId, name]);
913
+ const field2 = {
914
+ key,
915
+ label: `${nodeId} / ${name}`,
916
+ op,
917
+ read: (next) => {
918
+ const item = next.nodes[nodeId];
919
+ return item ? read(item) : void 0;
920
+ },
921
+ canonical: numeric ? (raw) => String(Number(raw) || 0) : (raw) => raw
922
+ };
923
+ const accepted = read(node);
924
+ return {
925
+ value: drafts ? drafts.value(key, accepted) : accepted,
926
+ onChange: (event) => {
927
+ if (drafts) drafts.change(field2, event.target.value);
928
+ else apply(op(event.target.value));
929
+ },
930
+ onFocus: () => drafts?.focus(key),
931
+ onBlur: () => drafts?.blur(key),
932
+ onCompositionStart: () => drafts?.composition(key, true),
933
+ onCompositionEnd: () => drafts?.composition(key, false)
934
+ };
935
+ };
936
+ const prop = (key) => bind(key, (item) => typeof item.props[key] === "string" ? item.props[key] : "", (raw) => ({ t: "set_node_props", id: nodeId, patch: { [key]: raw } }));
937
+ const style = (key) => bind(key, (item) => item.style.base[key] ?? "", (raw) => ({ t: "set_style", id: nodeId, breakpoint: "base", patch: { [key]: raw } }));
938
+ const length = (key) => bind(key, (item) => item.style.base[key]?.value?.toString() ?? "", (raw) => ({ t: "set_style", id: nodeId, breakpoint: "base", patch: { [key]: { value: Number(raw) || 0, unit: "px" } } }), true);
913
939
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { overflow: "auto", padding: 16, fontFamily: "system-ui, sans-serif", height: "100%", boxSizing: "border-box" }, children: [
914
940
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 13, fontWeight: 600, color: "var(--fcms-fg)" }, children: node.type }),
915
941
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, color: "var(--fcms-muted)", marginBottom: 8 }, children: node.id }),
@@ -921,8 +947,7 @@ function Inspector({ doc, selection, apply }) {
921
947
  id: fieldId("content"),
922
948
  "data-cms-field": "content",
923
949
  style: { ...input, minHeight: 64, resize: "vertical" },
924
- value: typeof node.props.content === "string" ? node.props.content : "",
925
- onChange: (e) => setProp("content", e.target.value)
950
+ ...prop("content")
926
951
  }
927
952
  )
928
953
  ] }) : null,
@@ -934,8 +959,7 @@ function Inspector({ doc, selection, apply }) {
934
959
  id: fieldId("src"),
935
960
  "data-cms-field": "src",
936
961
  style: input,
937
- value: typeof node.props.src === "string" ? node.props.src : "",
938
- onChange: (e) => setProp("src", e.target.value)
962
+ ...prop("src")
939
963
  }
940
964
  )
941
965
  ] }) : null,
@@ -947,8 +971,7 @@ function Inspector({ doc, selection, apply }) {
947
971
  "data-cms-field": "background",
948
972
  style: input,
949
973
  placeholder: "#ffffff | gradient | url(\u2026)",
950
- value: s.background ?? "",
951
- onChange: (e) => setStyle({ background: e.target.value })
974
+ ...style("background")
952
975
  }
953
976
  ),
954
977
  /* @__PURE__ */ jsxRuntime.jsx("label", { style: label, htmlFor: fieldId("color"), children: "Text color" }),
@@ -959,8 +982,7 @@ function Inspector({ doc, selection, apply }) {
959
982
  "data-cms-field": "color",
960
983
  style: input,
961
984
  placeholder: "#0f172a",
962
- value: s.color ?? "",
963
- onChange: (e) => setStyle({ color: e.target.value })
985
+ ...style("color")
964
986
  }
965
987
  ),
966
988
  /* @__PURE__ */ jsxRuntime.jsx("label", { style: label, htmlFor: fieldId("fontSize"), children: "Font size (px)" }),
@@ -971,8 +993,7 @@ function Inspector({ doc, selection, apply }) {
971
993
  "data-cms-field": "fontSize",
972
994
  type: "number",
973
995
  style: input,
974
- value: s.fontSize?.value ?? "",
975
- onChange: (e) => setStyle({ fontSize: { value: Number(e.target.value) || 0, unit: "px" } })
996
+ ...length("fontSize")
976
997
  }
977
998
  ),
978
999
  /* @__PURE__ */ jsxRuntime.jsx("label", { style: label, htmlFor: fieldId("padding"), children: "Padding (px)" }),
@@ -983,8 +1004,7 @@ function Inspector({ doc, selection, apply }) {
983
1004
  "data-cms-field": "padding",
984
1005
  type: "number",
985
1006
  style: input,
986
- value: s.padding?.value ?? "",
987
- onChange: (e) => setStyle({ padding: { value: Number(e.target.value) || 0, unit: "px" } })
1007
+ ...length("padding")
988
1008
  }
989
1009
  ),
990
1010
  /* @__PURE__ */ jsxRuntime.jsx("label", { style: label, htmlFor: fieldId("opacity"), children: "Opacity" }),
@@ -998,12 +1018,171 @@ function Inspector({ doc, selection, apply }) {
998
1018
  min: "0",
999
1019
  max: "1",
1000
1020
  style: input,
1001
- value: s.opacity ?? "",
1002
- onChange: (e) => setStyle({ opacity: Number(e.target.value) })
1021
+ ...bind("opacity", (item) => item.style.base.opacity?.toString() ?? "", (raw) => ({ t: "set_style", id: nodeId, breakpoint: "base", patch: { opacity: Number(raw) } }), true)
1003
1022
  }
1004
1023
  )
1005
1024
  ] });
1006
1025
  }
1026
+
1027
+ // src/editor/InspectorDrafts.ts
1028
+ var InspectorDrafts = class {
1029
+ constructor(doc) {
1030
+ this.doc = doc;
1031
+ this.drafts = /* @__PURE__ */ new Map();
1032
+ this.composing = /* @__PURE__ */ new Set();
1033
+ this.focused = null;
1034
+ this.flight = null;
1035
+ this.listeners = /* @__PURE__ */ new Set();
1036
+ this.version = 0;
1037
+ this.disabled = false;
1038
+ this.emit = () => false;
1039
+ this.subscribe = (listener) => {
1040
+ this.listeners.add(listener);
1041
+ return () => {
1042
+ this.listeners.delete(listener);
1043
+ };
1044
+ };
1045
+ this.snapshot = () => this.version;
1046
+ }
1047
+ get busy() {
1048
+ return this.flight !== null || [...this.drafts.values()].some((draft) => draft.dirty || draft.conflict !== null);
1049
+ }
1050
+ get conflicts() {
1051
+ return [...this.drafts.values()].filter((draft) => draft.conflict !== null).map((draft) => ({
1052
+ key: draft.field.key,
1053
+ label: draft.field.label,
1054
+ message: draft.conflict,
1055
+ missing: draft.field.read(this.doc) === void 0
1056
+ }));
1057
+ }
1058
+ value(key, accepted) {
1059
+ return this.drafts.get(key)?.raw ?? accepted;
1060
+ }
1061
+ configure(doc, disabled, emit) {
1062
+ const settled = this.disabled && !disabled;
1063
+ this.doc = doc;
1064
+ this.disabled = disabled;
1065
+ this.emit = emit;
1066
+ let changed = false;
1067
+ for (const [key, draft] of this.drafts) {
1068
+ const accepted = draft.field.read(doc);
1069
+ if (this.flight?.draft === draft && accepted === this.flight.expected) {
1070
+ this.clearFlight();
1071
+ draft.base = accepted;
1072
+ draft.dirty = draft.field.canonical(draft.raw) !== accepted;
1073
+ if (!draft.dirty && !draft.focused) this.drafts.delete(key);
1074
+ changed = true;
1075
+ } else if (accepted !== draft.base) {
1076
+ this.conflict(draft, accepted === void 0 ? "The edited element was removed. Your draft is retained." : "The server changed this field. Your draft is retained.");
1077
+ draft.base = accepted;
1078
+ changed = true;
1079
+ } else if (settled && this.flight?.draft === draft) {
1080
+ this.conflict(draft, "The save was not accepted. Your draft is retained.");
1081
+ changed = true;
1082
+ }
1083
+ }
1084
+ if (changed) this.notify();
1085
+ this.arm();
1086
+ }
1087
+ focus(key) {
1088
+ this.focused = key;
1089
+ const draft = this.drafts.get(key);
1090
+ if (draft) draft.focused = true;
1091
+ }
1092
+ change(field2, raw) {
1093
+ let draft = this.drafts.get(field2.key);
1094
+ if (!draft) {
1095
+ draft = { field: field2, raw, base: field2.read(this.doc), focused: this.focused === field2.key, dirty: true, conflict: null, readyAt: Date.now() + 250 };
1096
+ this.drafts.set(field2.key, draft);
1097
+ } else {
1098
+ draft.field = field2;
1099
+ draft.raw = raw;
1100
+ draft.readyAt = Date.now() + 250;
1101
+ }
1102
+ draft.dirty = field2.canonical(raw) !== draft.base;
1103
+ this.notify();
1104
+ this.arm();
1105
+ }
1106
+ blur(key) {
1107
+ if (this.focused === key) this.focused = null;
1108
+ const draft = this.drafts.get(key);
1109
+ if (draft) {
1110
+ draft.focused = false;
1111
+ draft.readyAt = Date.now();
1112
+ if (!draft.dirty && !draft.conflict && this.flight?.draft !== draft) this.drafts.delete(key);
1113
+ this.notify();
1114
+ this.pump();
1115
+ }
1116
+ }
1117
+ composition(key, active) {
1118
+ if (active) this.composing.add(key);
1119
+ else this.composing.delete(key);
1120
+ const draft = this.drafts.get(key);
1121
+ if (draft) draft.readyAt = Date.now() + 250;
1122
+ this.arm();
1123
+ }
1124
+ resolve(key, keep) {
1125
+ if (this.disabled) return;
1126
+ const draft = this.drafts.get(key);
1127
+ if (!draft) return;
1128
+ if (this.flight?.draft === draft) this.clearFlight();
1129
+ if (!keep) this.drafts.delete(key);
1130
+ else {
1131
+ const accepted = draft.field.read(this.doc);
1132
+ if (accepted === void 0) return;
1133
+ draft.base = accepted;
1134
+ draft.conflict = null;
1135
+ draft.dirty = draft.field.canonical(draft.raw) !== accepted;
1136
+ draft.readyAt = Date.now();
1137
+ if (!draft.dirty && !draft.focused) this.drafts.delete(key);
1138
+ }
1139
+ this.notify();
1140
+ this.pump();
1141
+ }
1142
+ dispose() {
1143
+ clearTimeout(this.timer);
1144
+ clearTimeout(this.ackTimer);
1145
+ }
1146
+ notify() {
1147
+ this.version++;
1148
+ for (const listener of this.listeners) listener();
1149
+ }
1150
+ clearFlight() {
1151
+ this.flight = null;
1152
+ clearTimeout(this.ackTimer);
1153
+ this.ackTimer = void 0;
1154
+ }
1155
+ conflict(draft, message) {
1156
+ draft.conflict = message;
1157
+ if (this.flight?.draft === draft) this.clearFlight();
1158
+ }
1159
+ arm() {
1160
+ clearTimeout(this.timer);
1161
+ this.timer = void 0;
1162
+ if (this.disabled || this.flight || this.conflicts.length > 0) return;
1163
+ const ready = [...this.drafts.values()].filter((draft) => draft.dirty && !this.composing.has(draft.field.key));
1164
+ if (ready.length) this.timer = setTimeout(() => this.pump(), Math.max(0, Math.min(...ready.map((draft) => draft.readyAt)) - Date.now()));
1165
+ }
1166
+ pump() {
1167
+ if (this.disabled || this.flight || this.conflicts.length > 0) return;
1168
+ const draft = [...this.drafts.values()].find((item) => item.dirty && item.readyAt <= Date.now() && !this.composing.has(item.field.key));
1169
+ if (!draft) {
1170
+ this.arm();
1171
+ return;
1172
+ }
1173
+ this.flight = { draft, expected: draft.field.canonical(draft.raw) };
1174
+ this.ackTimer = setTimeout(() => {
1175
+ this.conflict(draft, "The save has not been acknowledged. Your draft is retained.");
1176
+ this.notify();
1177
+ }, 1e4);
1178
+ try {
1179
+ if (!this.emit(draft.field.op(draft.raw))) this.conflict(draft, "This edit could not be submitted. Your draft is retained.");
1180
+ } catch {
1181
+ this.conflict(draft, "This edit could not be submitted. Your draft is retained.");
1182
+ }
1183
+ this.notify();
1184
+ }
1185
+ };
1007
1186
  var CONTAINER_TYPES = /* @__PURE__ */ new Set([
1008
1187
  "section",
1009
1188
  "frame",
@@ -1077,18 +1256,59 @@ function buildInsertOp(doc, kind, selectedId, targetId) {
1077
1256
  }
1078
1257
  };
1079
1258
  }
1080
- function Editor({ defaultValue, onChange, registry, data }) {
1259
+ function Editor({ value, defaultValue, onChange, onOp, disabled = false, registry, data }) {
1081
1260
  const initial = react.useMemo(() => defaultValue ?? emptyDoc("untitled"), [defaultValue]);
1082
- const ed = useEditor(initial);
1083
- const first = react.useRef(true);
1261
+ const local = useEditor(initial);
1262
+ const controlled = value !== void 0;
1263
+ const localDoc = useLatestRef(local.state.doc);
1264
+ const lastNotified = react.useRef(local.state.doc);
1265
+ const onOpRef = useLatestRef(onOp);
1266
+ const [drafts] = react.useState(() => new InspectorDrafts(value ?? initial));
1267
+ react.useSyncExternalStore(drafts.subscribe, drafts.snapshot, drafts.snapshot);
1084
1268
  const onChangeRef = useLatestRef(onChange);
1085
1269
  react.useEffect(() => {
1086
- if (first.current) {
1087
- first.current = false;
1088
- return;
1270
+ if (controlled || lastNotified.current === local.state.doc) return;
1271
+ lastNotified.current = local.state.doc;
1272
+ onChangeRef.current?.(local.state.doc);
1273
+ }, [controlled, local.state.doc, onChangeRef]);
1274
+ const apply = (op) => {
1275
+ if (disabled) return false;
1276
+ const source = value ?? localDoc.current;
1277
+ const next = reduce(source, op);
1278
+ if (next === source) return false;
1279
+ if (!controlled) {
1280
+ localDoc.current = next;
1281
+ local.apply(op);
1089
1282
  }
1090
- onChangeRef.current?.(ed.state.doc);
1091
- }, [ed.state.doc, onChangeRef]);
1283
+ onOpRef.current?.(op);
1284
+ if (controlled) onChangeRef.current?.(next);
1285
+ return true;
1286
+ };
1287
+ react.useEffect(() => {
1288
+ if (controlled) drafts.configure(value, disabled, apply);
1289
+ });
1290
+ react.useEffect(() => () => drafts.dispose(), [drafts]);
1291
+ const structuralDisabled = disabled || controlled && drafts.busy;
1292
+ const ed = {
1293
+ ...local,
1294
+ state: { ...local.state, doc: value ?? local.state.doc },
1295
+ apply: (op) => {
1296
+ if (!structuralDisabled) apply(op);
1297
+ },
1298
+ // Snapshot history belongs to the local document. Replaying it into a
1299
+ // server-owned value could overwrite another writer's accepted work.
1300
+ undo: () => {
1301
+ if (!controlled && !disabled) local.undo();
1302
+ },
1303
+ redo: () => {
1304
+ if (!controlled && !disabled) local.redo();
1305
+ },
1306
+ replace: (next) => {
1307
+ if (!controlled && !disabled) local.replace(next);
1308
+ },
1309
+ canUndo: !controlled && !disabled && local.canUndo,
1310
+ canRedo: !controlled && !disabled && local.canRedo
1311
+ };
1092
1312
  const shell = {
1093
1313
  display: "grid",
1094
1314
  gridTemplateColumns: "220px 1fr 300px",
@@ -1100,9 +1320,9 @@ function Editor({ defaultValue, onChange, registry, data }) {
1100
1320
  borderRadius: 12,
1101
1321
  overflow: "hidden"
1102
1322
  };
1103
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fancy-cms-editor", style: shell, children: [
1323
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fancy-cms-editor", style: shell, "aria-busy": structuralDisabled || void 0, children: [
1104
1324
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: CHROME_CSS }),
1105
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { borderRight: "1px solid var(--fcms-border)", display: "flex", flexDirection: "column", minHeight: 0 }, children: [
1325
+ /* @__PURE__ */ jsxRuntime.jsx("fieldset", { disabled: structuralDisabled, "aria-label": "Page structure controls", style: { display: "contents", border: 0, padding: 0, margin: 0 }, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { borderRight: "1px solid var(--fcms-border)", display: "flex", flexDirection: "column", minHeight: 0 }, children: [
1106
1326
  /* @__PURE__ */ jsxRuntime.jsx(Toolbar, { ed }),
1107
1327
  /* @__PURE__ */ jsxRuntime.jsx(
1108
1328
  LayersPanel,
@@ -1113,9 +1333,22 @@ function Editor({ defaultValue, onChange, registry, data }) {
1113
1333
  onMove: (id, parent, order) => ed.apply({ t: "move_node", id, parent, order })
1114
1334
  }
1115
1335
  )
1116
- ] }),
1336
+ ] }) }),
1117
1337
  /* @__PURE__ */ jsxRuntime.jsx(Canvas, { doc: ed.state.doc, selection: ed.state.selection, onSelect: ed.select, apply: ed.apply, registry, data }),
1118
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { borderLeft: "1px solid var(--fcms-border)", minHeight: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(Inspector, { doc: ed.state.doc, selection: ed.state.selection, apply: ed.apply }) })
1338
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { borderLeft: "1px solid var(--fcms-border)", minHeight: 0, overflow: "auto" }, children: [
1339
+ controlled && drafts.conflicts.map((conflict) => /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alert", "data-cms-draft-conflict": conflict.key, style: { padding: 12, fontFamily: "system-ui,sans-serif", fontSize: 13 }, children: [
1340
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
1341
+ conflict.label,
1342
+ ": ",
1343
+ conflict.message
1344
+ ] }),
1345
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { "data-cms-retained-draft": "", style: { whiteSpace: "pre-wrap", overflowWrap: "anywhere" }, children: drafts.value(conflict.key, "") }),
1346
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", disabled, onClick: () => drafts.resolve(conflict.key, false), children: "Use server value" }),
1347
+ " ",
1348
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", disabled: disabled || conflict.missing, onClick: () => drafts.resolve(conflict.key, true), children: "Keep draft" })
1349
+ ] }, conflict.key)),
1350
+ /* @__PURE__ */ jsxRuntime.jsx("fieldset", { disabled: !controlled && disabled, "aria-label": "Page property controls", style: { border: 0, padding: 0, margin: 0, minWidth: 0, height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(Inspector, { doc: ed.state.doc, selection: ed.state.selection, apply: ed.apply, drafts: controlled ? drafts : void 0 }) })
1351
+ ] })
1119
1352
  ] });
1120
1353
  }
1121
1354
  function Toolbar({ ed }) {