@waypointjs/builder 0.1.2 → 0.1.5

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
@@ -5,6 +5,7 @@ var react = require('react');
5
5
  var zustand = require('zustand');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var react$1 = require('@waypointjs/react');
8
+ var devtools = require('@waypointjs/devtools');
8
9
 
9
10
  // src/components/WaypointBuilder.tsx
10
11
 
@@ -196,6 +197,44 @@ var useBuilderStore = zustand.create((set, _get) => ({
196
197
  selectedFieldId: s.selectedStepId === stepId ? null : s.selectedFieldId,
197
198
  isDirty: true
198
199
  })),
200
+ duplicateStep: (stepId) => {
201
+ const state = _get();
202
+ const step = state.schema.steps.find((s) => s.id === stepId);
203
+ if (!step) return;
204
+ const newStepId = generateId("step");
205
+ const fieldIdMap = {};
206
+ for (const f of step.fields) {
207
+ fieldIdMap[f.id] = generateId("field");
208
+ }
209
+ const newFields = step.fields.map((f) => ({
210
+ ...f,
211
+ id: fieldIdMap[f.id],
212
+ dependsOn: f.dependsOn?.map((dep) => {
213
+ if (dep.startsWith(`${stepId}.`)) {
214
+ const oldFieldId = dep.slice(stepId.length + 1);
215
+ const newFieldId = fieldIdMap[oldFieldId];
216
+ return newFieldId ? `${newStepId}.${newFieldId}` : dep;
217
+ }
218
+ return dep;
219
+ })
220
+ }));
221
+ const newStep = {
222
+ ...step,
223
+ id: newStepId,
224
+ title: `${step.title} (copy)`,
225
+ url: `${step.url}-copy`,
226
+ fields: newFields
227
+ };
228
+ const stepIndex = state.schema.steps.findIndex((s) => s.id === stepId);
229
+ const newSteps = [...state.schema.steps];
230
+ newSteps.splice(stepIndex + 1, 0, newStep);
231
+ set({
232
+ schema: { ...state.schema, steps: newSteps },
233
+ selectedStepId: newStepId,
234
+ selectedFieldId: null,
235
+ isDirty: true
236
+ });
237
+ },
199
238
  reorderSteps: (fromIndex, toIndex) => set((s) => {
200
239
  const steps = [...s.schema.steps];
201
240
  const [moved] = steps.splice(fromIndex, 1);
@@ -248,6 +287,32 @@ var useBuilderStore = zustand.create((set, _get) => ({
248
287
  selectedFieldId: s.selectedFieldId === fieldId ? null : s.selectedFieldId,
249
288
  isDirty: true
250
289
  })),
290
+ duplicateField: (stepId, fieldId) => {
291
+ const state = _get();
292
+ const step = state.schema.steps.find((s) => s.id === stepId);
293
+ if (!step) return;
294
+ const field = step.fields.find((f) => f.id === fieldId);
295
+ if (!field) return;
296
+ const newFieldId = generateId("field");
297
+ const newField = {
298
+ ...field,
299
+ id: newFieldId,
300
+ label: `${field.label} (copy)`
301
+ };
302
+ const fieldIndex = step.fields.findIndex((f) => f.id === fieldId);
303
+ const newFields = [...step.fields];
304
+ newFields.splice(fieldIndex + 1, 0, newField);
305
+ set((s) => ({
306
+ schema: {
307
+ ...s.schema,
308
+ steps: s.schema.steps.map(
309
+ (st) => st.id === stepId ? { ...st, fields: newFields } : st
310
+ )
311
+ },
312
+ selectedFieldId: newFieldId,
313
+ isDirty: true
314
+ }));
315
+ },
251
316
  reorderFields: (stepId, fromIndex, toIndex) => set((s) => ({
252
317
  schema: {
253
318
  ...s.schema,
@@ -346,9 +411,32 @@ var useBuilderStore = zustand.create((set, _get) => ({
346
411
  isDirty: true
347
412
  }))
348
413
  }));
349
- var BLANK_FORM = { id: "", label: "", type: "string", blocking: false };
414
+ var BuilderReadOnlyContext = react.createContext(false);
415
+ function useBuilderReadOnly() {
416
+ return react.useContext(BuilderReadOnlyContext);
417
+ }
418
+ var BuilderCustomTypesContext = react.createContext([]);
419
+ function useBuilderCustomTypes() {
420
+ return react.useContext(BuilderCustomTypesContext);
421
+ }
422
+ var BuilderExternalEnumsContext = react.createContext([]);
423
+ function useBuilderExternalEnums() {
424
+ return react.useContext(BuilderExternalEnumsContext);
425
+ }
426
+ var BLANK_FORM = {
427
+ id: "",
428
+ label: "",
429
+ type: "string",
430
+ blocking: false
431
+ };
350
432
  function ExternalVariablePanel() {
351
- const { schema, addExternalVariable, updateExternalVariable, removeExternalVariable } = useBuilderStore();
433
+ const {
434
+ schema,
435
+ addExternalVariable,
436
+ updateExternalVariable,
437
+ removeExternalVariable
438
+ } = useBuilderStore();
439
+ const readOnly = useBuilderReadOnly();
352
440
  const variables = schema.externalVariables ?? [];
353
441
  const [isAdding, setIsAdding] = react.useState(false);
354
442
  const [editingId, setEditingId] = react.useState(null);
@@ -374,9 +462,11 @@ function ExternalVariablePanel() {
374
462
  }
375
463
  function validateForm() {
376
464
  if (!form.id.trim()) return "ID is required";
377
- if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(form.id.trim())) return "ID must be alphanumeric (no spaces)";
465
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(form.id.trim()))
466
+ return "ID must be alphanumeric (no spaces)";
378
467
  if (!form.label.trim()) return "Label is required";
379
- if (isAdding && variables.some((v) => v.id === form.id.trim())) return `ID "${form.id}" already exists`;
468
+ if (isAdding && variables.some((v) => v.id === form.id.trim()))
469
+ return `ID "${form.id}" already exists`;
380
470
  return null;
381
471
  }
382
472
  function submitAdd() {
@@ -385,7 +475,12 @@ function ExternalVariablePanel() {
385
475
  setError(err);
386
476
  return;
387
477
  }
388
- addExternalVariable({ id: form.id.trim(), label: form.label.trim(), type: form.type, blocking: form.blocking });
478
+ addExternalVariable({
479
+ id: form.id.trim(),
480
+ label: form.label.trim(),
481
+ type: form.type,
482
+ blocking: form.blocking
483
+ });
389
484
  setIsAdding(false);
390
485
  setForm(BLANK_FORM);
391
486
  setError(null);
@@ -397,7 +492,11 @@ function ExternalVariablePanel() {
397
492
  return;
398
493
  }
399
494
  if (!editingId) return;
400
- updateExternalVariable(editingId, { label: form.label.trim(), type: form.type, blocking: form.blocking });
495
+ updateExternalVariable(editingId, {
496
+ label: form.label.trim(),
497
+ type: form.type,
498
+ blocking: form.blocking
499
+ });
401
500
  setEditingId(null);
402
501
  setError(null);
403
502
  }
@@ -411,7 +510,15 @@ function ExternalVariablePanel() {
411
510
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: panelStyle, children: [
412
511
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: headerStyle, children: [
413
512
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: titleStyle, children: "External Variables" }),
414
- !isAdding && /* @__PURE__ */ jsxRuntime.jsx("button", { style: addBtnStyle, onClick: startAdd, title: "Add external variable", children: "+ Add" })
513
+ !readOnly && !isAdding && /* @__PURE__ */ jsxRuntime.jsx(
514
+ "button",
515
+ {
516
+ style: addBtnStyle,
517
+ onClick: startAdd,
518
+ title: "Add external variable",
519
+ children: "+ Add"
520
+ }
521
+ )
415
522
  ] }),
416
523
  variables.length === 0 && !isAdding && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: emptyStyle, children: [
417
524
  "No external variables declared.",
@@ -425,37 +532,55 @@ function ExternalVariablePanel() {
425
532
  variables.map((v) => {
426
533
  const refs = usageMap.get(v.id) ?? [];
427
534
  const isBeingEdited = editingId === v.id;
428
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...varRowStyle, ...isBeingEdited ? varRowActiveStyle : {} }, children: isBeingEdited ? /* @__PURE__ */ jsxRuntime.jsx(
429
- VarForm,
535
+ return /* @__PURE__ */ jsxRuntime.jsx(
536
+ "div",
430
537
  {
431
- form,
432
- onChange: setForm,
433
- error,
434
- onSubmit: submitEdit,
435
- onCancel: cancelForm,
436
- submitLabel: "Save",
437
- idReadOnly: true
438
- }
439
- ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
440
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: varMainStyle, children: [
441
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: varTopRowStyle, children: [
442
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: varIdStyle, children: [
443
- "$",
444
- `ext.${v.id}`
538
+ style: {
539
+ ...varRowStyle,
540
+ ...isBeingEdited ? varRowActiveStyle : {}
541
+ },
542
+ children: isBeingEdited ? /* @__PURE__ */ jsxRuntime.jsx(
543
+ VarForm,
544
+ {
545
+ form,
546
+ onChange: setForm,
547
+ error,
548
+ onSubmit: submitEdit,
549
+ onCancel: cancelForm,
550
+ submitLabel: "Save",
551
+ idReadOnly: true
552
+ }
553
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
554
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: varMainStyle, children: [
555
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: varTopRowStyle, children: [
556
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: varIdStyle, children: [
557
+ "$",
558
+ `ext.${v.id}`
559
+ ] }),
560
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: badgeRowStyle, children: [
561
+ /* @__PURE__ */ jsxRuntime.jsx(TypeBadge, { type: v.type }),
562
+ v.blocking && /* @__PURE__ */ jsxRuntime.jsx("span", { style: blockingBadgeStyle, children: "blocking" })
563
+ ] })
564
+ ] }),
565
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: varLabelStyle, children: v.label }),
566
+ refs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: refsStyle, children: refs.map((ref, i) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: refChipStyle, children: ref }, i)) })
445
567
  ] }),
446
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: badgeRowStyle, children: [
447
- /* @__PURE__ */ jsxRuntime.jsx(TypeBadge, { type: v.type }),
448
- v.blocking && /* @__PURE__ */ jsxRuntime.jsx("span", { style: blockingBadgeStyle, children: "blocking" })
568
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: varActionsStyle, children: [
569
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: actionBtnStyle, onClick: () => startEdit(v), children: "Edit" }),
570
+ /* @__PURE__ */ jsxRuntime.jsx(
571
+ "button",
572
+ {
573
+ style: { ...actionBtnStyle, color: "#ef4444" },
574
+ title: "Remove variable",
575
+ onClick: () => remove(v.id),
576
+ children: "\u2715"
577
+ }
578
+ )
449
579
  ] })
450
- ] }),
451
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: varLabelStyle, children: v.label }),
452
- refs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: refsStyle, children: refs.map((ref, i) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: refChipStyle, children: ref }, i)) })
453
- ] }),
454
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: varActionsStyle, children: [
455
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: actionBtnStyle, onClick: () => startEdit(v), children: "Edit" }),
456
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: { ...actionBtnStyle, color: "#ef4444" }, title: "Remove variable", onClick: () => remove(v.id), children: "\u2715" })
457
- ] })
458
- ] }) }, v.id);
580
+ ] })
581
+ },
582
+ v.id
583
+ );
459
584
  }),
460
585
  isAdding && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...varRowStyle, ...varRowActiveStyle }, children: /* @__PURE__ */ jsxRuntime.jsx(
461
586
  VarForm,
@@ -470,7 +595,15 @@ function ExternalVariablePanel() {
470
595
  ) })
471
596
  ] });
472
597
  }
473
- function VarForm({ form, onChange, error, onSubmit, onCancel, submitLabel, idReadOnly }) {
598
+ function VarForm({
599
+ form,
600
+ onChange,
601
+ error,
602
+ onSubmit,
603
+ onCancel,
604
+ submitLabel,
605
+ idReadOnly
606
+ }) {
474
607
  function set(key, value) {
475
608
  onChange({ ...form, [key]: value });
476
609
  }
@@ -479,7 +612,10 @@ function VarForm({ form, onChange, error, onSubmit, onCancel, submitLabel, idRea
479
612
  /* @__PURE__ */ jsxRuntime.jsx(
480
613
  "input",
481
614
  {
482
- style: { ...inputStyle, ...idReadOnly ? { background: "#f9fafb", color: "#6b7280" } : {} },
615
+ style: {
616
+ ...inputStyle,
617
+ ...idReadOnly ? { background: "#f9fafb", color: "#6b7280" } : {}
618
+ },
483
619
  value: form.id,
484
620
  onChange: (e) => set("id", e.target.value),
485
621
  placeholder: "e.g. userId",
@@ -497,12 +633,20 @@ function VarForm({ form, onChange, error, onSubmit, onCancel, submitLabel, idRea
497
633
  }
498
634
  ),
499
635
  /* @__PURE__ */ jsxRuntime.jsx("label", { style: formLabelStyle, children: "Type" }),
500
- /* @__PURE__ */ jsxRuntime.jsxs("select", { style: selectStyle, value: form.type, onChange: (e) => set("type", e.target.value), children: [
501
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "string", children: "string" }),
502
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "number", children: "number" }),
503
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "boolean", children: "boolean" }),
504
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "object", children: "object" })
505
- ] }),
636
+ /* @__PURE__ */ jsxRuntime.jsxs(
637
+ "select",
638
+ {
639
+ style: selectStyle,
640
+ value: form.type,
641
+ onChange: (e) => set("type", e.target.value),
642
+ children: [
643
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "string", children: "string" }),
644
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "number", children: "number" }),
645
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "boolean", children: "boolean" }),
646
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "object", children: "object" })
647
+ ]
648
+ }
649
+ ),
506
650
  /* @__PURE__ */ jsxRuntime.jsxs("label", { style: checkboxRowStyle, children: [
507
651
  /* @__PURE__ */ jsxRuntime.jsx(
508
652
  "input",
@@ -596,7 +740,8 @@ var emptyStyle = {
596
740
  };
597
741
  var codeStyle = {
598
742
  fontFamily: "monospace",
599
- background: "#f3f4f6",
743
+ background: "#f97316",
744
+ color: "#070714",
600
745
  borderRadius: 3,
601
746
  padding: "1px 3px"
602
747
  };
@@ -747,15 +892,23 @@ var submitBtnStyle = {
747
892
  // src/hooks/useAllFieldPaths.ts
748
893
  function useAllFieldPaths(excludeStepId, excludeFieldId) {
749
894
  const { schema } = useBuilderStore();
895
+ const externalEnums = useBuilderExternalEnums();
750
896
  const paths = [];
751
897
  for (const step of schema.steps) {
752
898
  for (const field of step.fields) {
753
899
  if (step.id === excludeStepId && field.id === excludeFieldId) continue;
900
+ let options;
901
+ if (field.externalEnumId) {
902
+ options = externalEnums.find((e) => e.id === field.externalEnumId)?.values;
903
+ } else if (field.options?.length) {
904
+ options = field.options;
905
+ }
754
906
  paths.push({
755
907
  path: `${step.id}.${field.id}`,
756
908
  label: `${step.title} \u2192 ${field.label}`,
757
909
  stepId: step.id,
758
- fieldId: field.id
910
+ fieldId: field.id,
911
+ options
759
912
  });
760
913
  }
761
914
  }
@@ -781,7 +934,9 @@ var OPERATORS = [
781
934
  { value: "notIn", label: "not in (comma list)", hasValue: true },
782
935
  { value: "matches", label: "matches regex", hasValue: true },
783
936
  { value: "exists", label: "exists", hasValue: false },
784
- { value: "notExists", label: "not exists", hasValue: false }
937
+ { value: "notExists", label: "not exists", hasValue: false },
938
+ { value: "inEnum", label: "is in enum", hasValue: true, isEnum: true },
939
+ { value: "notInEnum", label: "not in enum", hasValue: true, isEnum: true }
785
940
  ];
786
941
  function ConditionBuilder({
787
942
  value,
@@ -790,6 +945,7 @@ function ConditionBuilder({
790
945
  excludeFieldId
791
946
  }) {
792
947
  const allPaths = useAllFieldPaths(excludeStepId, excludeFieldId);
948
+ const externalEnums = useBuilderExternalEnums();
793
949
  const group = value ?? { combinator: "and", rules: [] };
794
950
  const updateRule = (index, updates) => {
795
951
  const rules = group.rules.map((r, i) => i === index ? { ...r, ...updates } : r);
@@ -852,15 +1008,48 @@ function ConditionBuilder({
852
1008
  children: OPERATORS.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.value, children: o.label }, o.value))
853
1009
  }
854
1010
  ),
855
- opDef?.hasValue && /* @__PURE__ */ jsxRuntime.jsx(
856
- "input",
1011
+ opDef?.hasValue && (opDef.isEnum ? /* @__PURE__ */ jsxRuntime.jsxs(
1012
+ "select",
857
1013
  {
858
- style: styles.valueInput,
859
- placeholder: "value",
1014
+ style: { ...styles.select, width: 140 },
860
1015
  value: rule.value != null ? String(rule.value) : "",
861
- onChange: (e) => updateRule(index, { value: e.target.value })
1016
+ onChange: (e) => updateRule(index, { value: e.target.value }),
1017
+ children: [
1018
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u2014 pick enum \u2014" }),
1019
+ externalEnums.map((en) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: en.id, children: en.label }, en.id))
1020
+ ]
862
1021
  }
863
- ),
1022
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.valueGroup, children: [
1023
+ /* @__PURE__ */ jsxRuntime.jsx(
1024
+ "input",
1025
+ {
1026
+ style: styles.valueInput,
1027
+ placeholder: "value",
1028
+ value: rule.value != null ? String(rule.value) : "",
1029
+ onChange: (e) => updateRule(index, { value: e.target.value })
1030
+ }
1031
+ ),
1032
+ externalEnums.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
1033
+ "select",
1034
+ {
1035
+ style: styles.enumPicker,
1036
+ title: "Pick a value from an enum",
1037
+ value: "",
1038
+ onChange: (e) => {
1039
+ if (e.target.value) updateRule(index, { value: e.target.value });
1040
+ },
1041
+ children: [
1042
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u229E" }),
1043
+ externalEnums.map((en) => /* @__PURE__ */ jsxRuntime.jsx("optgroup", { label: en.label, children: en.values.map((v) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: String(v.value), children: [
1044
+ v.label,
1045
+ " (",
1046
+ v.value,
1047
+ ")"
1048
+ ] }, String(v.value))) }, en.id))
1049
+ ]
1050
+ }
1051
+ )
1052
+ ] })),
864
1053
  /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles.removeBtn, onClick: () => removeRule(index), children: "\u2715" })
865
1054
  ] }, index);
866
1055
  }),
@@ -938,6 +1127,17 @@ var styles = {
938
1127
  alignSelf: "flex-start",
939
1128
  color: "var(--wp-text-secondary)"
940
1129
  },
1130
+ valueGroup: { display: "flex", alignItems: "center", gap: 4 },
1131
+ enumPicker: {
1132
+ fontSize: 11,
1133
+ padding: "4px 4px",
1134
+ border: "1px solid var(--wp-border-muted)",
1135
+ borderRadius: "var(--wp-radius)",
1136
+ background: "var(--wp-canvas)",
1137
+ color: "var(--wp-primary)",
1138
+ cursor: "pointer",
1139
+ flexShrink: 0
1140
+ },
941
1141
  preview: { marginTop: 4 },
942
1142
  previewLabel: {
943
1143
  fontSize: 10,
@@ -959,6 +1159,190 @@ var styles = {
959
1159
  color: "var(--wp-text-mono)"
960
1160
  }
961
1161
  };
1162
+ var VALIDATION_RULES = [
1163
+ { type: "required", label: "Required", hasValue: false },
1164
+ { type: "min", label: "Min value", hasValue: true },
1165
+ { type: "max", label: "Max value", hasValue: true },
1166
+ { type: "minLength", label: "Min length", hasValue: true },
1167
+ { type: "maxLength", label: "Max length", hasValue: true },
1168
+ { type: "email", label: "Email format", hasValue: false },
1169
+ { type: "url", label: "URL format", hasValue: false },
1170
+ { type: "regex", label: "Matches regex", hasValue: true },
1171
+ { type: "equals", label: "equals", hasValue: true },
1172
+ { type: "notEquals", label: "not equals", hasValue: true },
1173
+ { type: "greaterThan", label: ">", hasValue: true },
1174
+ { type: "greaterThanOrEqual", label: ">=", hasValue: true },
1175
+ { type: "lessThan", label: "<", hasValue: true },
1176
+ { type: "lessThanOrEqual", label: "<=", hasValue: true },
1177
+ { type: "contains", label: "contains", hasValue: true },
1178
+ { type: "notContains", label: "not contains", hasValue: true },
1179
+ { type: "matches", label: "matches regex", hasValue: true },
1180
+ { type: "inEnum", label: "is in enum", hasValue: true, isEnum: true },
1181
+ { type: "notInEnum", label: "not in enum", hasValue: true, isEnum: true },
1182
+ { type: "custom", label: "Custom validator", hasValue: false }
1183
+ ];
1184
+ function ValidationBuilder({ value, onChange }) {
1185
+ const externalEnums = useBuilderExternalEnums();
1186
+ const updateRule = (index, updates) => {
1187
+ onChange(value.map((r, i) => i === index ? { ...r, ...updates } : r));
1188
+ };
1189
+ const addRule = () => {
1190
+ onChange([...value, { type: "required", message: "This field is required" }]);
1191
+ };
1192
+ const removeRule = (index) => {
1193
+ onChange(value.filter((_, i) => i !== index));
1194
+ };
1195
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles2.container, children: [
1196
+ value.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles2.empty, children: "No rules \u2014 field is optional by default." }),
1197
+ value.map((rule, index) => {
1198
+ const def = VALIDATION_RULES.find((r) => r.type === rule.type);
1199
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles2.rule, children: [
1200
+ /* @__PURE__ */ jsxRuntime.jsx(
1201
+ "select",
1202
+ {
1203
+ style: styles2.typeSelect,
1204
+ value: rule.type,
1205
+ onChange: (e) => updateRule(index, { type: e.target.value }),
1206
+ children: VALIDATION_RULES.map((r) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: r.type, children: r.label }, r.type))
1207
+ }
1208
+ ),
1209
+ def?.hasValue && (def.isEnum ? /* @__PURE__ */ jsxRuntime.jsxs(
1210
+ "select",
1211
+ {
1212
+ style: { ...styles2.typeSelect, flex: "0 0 140px" },
1213
+ value: rule.value != null ? String(rule.value) : "",
1214
+ onChange: (e) => updateRule(index, { value: e.target.value }),
1215
+ children: [
1216
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u2014 pick enum \u2014" }),
1217
+ externalEnums.map((en) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: en.id, children: en.label }, en.id))
1218
+ ]
1219
+ }
1220
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles2.valueGroup, children: [
1221
+ /* @__PURE__ */ jsxRuntime.jsx(
1222
+ "input",
1223
+ {
1224
+ style: styles2.valueInput,
1225
+ placeholder: "value",
1226
+ value: rule.value != null ? String(rule.value) : "",
1227
+ onChange: (e) => updateRule(index, { value: e.target.value })
1228
+ }
1229
+ ),
1230
+ externalEnums.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
1231
+ "select",
1232
+ {
1233
+ style: styles2.enumPicker,
1234
+ title: "Pick a value from an enum",
1235
+ value: "",
1236
+ onChange: (e) => {
1237
+ if (e.target.value) updateRule(index, { value: e.target.value });
1238
+ },
1239
+ children: [
1240
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u229E" }),
1241
+ externalEnums.map((en) => /* @__PURE__ */ jsxRuntime.jsx("optgroup", { label: en.label, children: en.values.map((v) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: String(v.value), children: [
1242
+ v.label,
1243
+ " (",
1244
+ v.value,
1245
+ ")"
1246
+ ] }, String(v.value))) }, en.id))
1247
+ ]
1248
+ }
1249
+ )
1250
+ ] })),
1251
+ rule.type === "custom" && /* @__PURE__ */ jsxRuntime.jsx(
1252
+ "input",
1253
+ {
1254
+ style: styles2.valueInput,
1255
+ placeholder: "validatorId",
1256
+ value: rule.customValidatorId ?? "",
1257
+ onChange: (e) => updateRule(index, { customValidatorId: e.target.value })
1258
+ }
1259
+ ),
1260
+ /* @__PURE__ */ jsxRuntime.jsx(
1261
+ "input",
1262
+ {
1263
+ style: styles2.messageInput,
1264
+ placeholder: "error message",
1265
+ value: rule.message,
1266
+ onChange: (e) => updateRule(index, { message: e.target.value })
1267
+ }
1268
+ ),
1269
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles2.removeBtn, onClick: () => removeRule(index), children: "\u2715" })
1270
+ ] }, index);
1271
+ }),
1272
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles2.addBtn, onClick: addRule, children: "+ Add rule" })
1273
+ ] });
1274
+ }
1275
+ var styles2 = {
1276
+ container: { display: "flex", flexDirection: "column", gap: 10 },
1277
+ empty: { fontSize: 13, color: "var(--wp-text-subtle)", textAlign: "center", padding: "12px 0" },
1278
+ rule: {
1279
+ display: "flex",
1280
+ alignItems: "center",
1281
+ gap: 8,
1282
+ background: "var(--wp-surface)",
1283
+ border: "1px solid var(--wp-border)",
1284
+ borderRadius: "var(--wp-radius-lg)",
1285
+ padding: "8px 10px"
1286
+ },
1287
+ typeSelect: {
1288
+ flex: "0 0 150px",
1289
+ fontSize: 12,
1290
+ padding: "5px 6px",
1291
+ border: "1px solid var(--wp-border-muted)",
1292
+ borderRadius: "var(--wp-radius)",
1293
+ background: "var(--wp-canvas)",
1294
+ color: "var(--wp-text)"
1295
+ },
1296
+ valueGroup: { display: "flex", alignItems: "center", gap: 4 },
1297
+ valueInput: {
1298
+ width: 90,
1299
+ fontSize: 12,
1300
+ padding: "5px 6px",
1301
+ border: "1px solid var(--wp-border-muted)",
1302
+ borderRadius: "var(--wp-radius)",
1303
+ background: "var(--wp-canvas)",
1304
+ color: "var(--wp-text)"
1305
+ },
1306
+ enumPicker: {
1307
+ fontSize: 11,
1308
+ padding: "4px 4px",
1309
+ border: "1px solid var(--wp-border-muted)",
1310
+ borderRadius: "var(--wp-radius)",
1311
+ background: "var(--wp-canvas)",
1312
+ color: "var(--wp-primary)",
1313
+ cursor: "pointer",
1314
+ flexShrink: 0
1315
+ },
1316
+ messageInput: {
1317
+ flex: 1,
1318
+ fontSize: 12,
1319
+ padding: "5px 6px",
1320
+ border: "1px solid var(--wp-border-muted)",
1321
+ borderRadius: "var(--wp-radius)",
1322
+ background: "var(--wp-canvas)",
1323
+ color: "var(--wp-text)",
1324
+ minWidth: 0
1325
+ },
1326
+ removeBtn: {
1327
+ border: "none",
1328
+ background: "transparent",
1329
+ color: "var(--wp-danger)",
1330
+ cursor: "pointer",
1331
+ fontSize: 13,
1332
+ flexShrink: 0
1333
+ },
1334
+ addBtn: {
1335
+ fontSize: 12,
1336
+ padding: "6px 12px",
1337
+ background: "var(--wp-surface-muted)",
1338
+ border: "1px solid var(--wp-border-muted)",
1339
+ borderRadius: "var(--wp-radius)",
1340
+ cursor: "pointer",
1341
+ fontWeight: 500,
1342
+ alignSelf: "flex-start",
1343
+ color: "var(--wp-text-secondary)"
1344
+ }
1345
+ };
962
1346
  function DependsOnInput({
963
1347
  value,
964
1348
  onChange,
@@ -991,17 +1375,17 @@ function DependsOnInput({
991
1375
  }, []);
992
1376
  const getLabel = (path) => allPaths.find((p) => p.path === path)?.label ?? path;
993
1377
  const isExternal = (path) => path.startsWith("$ext.");
994
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, style: styles2.container, children: [
995
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles2.tags, children: [
996
- value.map((path) => /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { ...styles2.tag, ...isExternal(path) ? styles2.tagExt : {} }, children: [
1378
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, style: styles3.container, children: [
1379
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles3.tags, children: [
1380
+ value.map((path) => /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { ...styles3.tag, ...isExternal(path) ? styles3.tagExt : {} }, children: [
997
1381
  getLabel(path),
998
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles2.tagRemove, onClick: () => remove(path), children: "\u2715" })
1382
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles3.tagRemove, onClick: () => remove(path), children: "\u2715" })
999
1383
  ] }, path)),
1000
1384
  /* @__PURE__ */ jsxRuntime.jsx(
1001
1385
  "input",
1002
1386
  {
1003
1387
  ref: inputRef,
1004
- style: styles2.input,
1388
+ style: styles3.input,
1005
1389
  placeholder: value.length === 0 ? "Search fields or $ext vars\u2026" : "Add more\u2026",
1006
1390
  value: query,
1007
1391
  onChange: (e) => {
@@ -1012,28 +1396,28 @@ function DependsOnInput({
1012
1396
  }
1013
1397
  )
1014
1398
  ] }),
1015
- open && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles2.dropdown, children: [
1016
- available.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles2.noResults, children: allPaths.length === 0 ? "No fields available in the tree yet." : "No matching fields." }),
1399
+ open && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles3.dropdown, children: [
1400
+ available.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles3.noResults, children: allPaths.length === 0 ? "No fields available in the tree yet." : "No matching fields." }),
1017
1401
  available.map((p) => /* @__PURE__ */ jsxRuntime.jsxs(
1018
1402
  "button",
1019
1403
  {
1020
- style: styles2.option,
1404
+ style: styles3.option,
1021
1405
  onMouseDown: (e) => {
1022
1406
  e.preventDefault();
1023
1407
  add(p.path);
1024
1408
  },
1025
1409
  children: [
1026
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles2.optionLabel, children: p.label }),
1027
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles2.optionPath, children: p.path })
1410
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles3.optionLabel, children: p.label }),
1411
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles3.optionPath, children: p.path })
1028
1412
  ]
1029
1413
  },
1030
1414
  p.path
1031
1415
  ))
1032
1416
  ] }),
1033
- value.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles2.hint, children: "This field will be blocked until all dependencies have a value." })
1417
+ value.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles3.hint, children: "This field will be blocked until all dependencies have a value." })
1034
1418
  ] });
1035
1419
  }
1036
- var styles2 = {
1420
+ var styles3 = {
1037
1421
  container: { position: "relative" },
1038
1422
  tags: {
1039
1423
  display: "flex",
@@ -1115,15 +1499,15 @@ function Modal({ title, onClose, children, width = 560 }) {
1115
1499
  document.addEventListener("keydown", handler);
1116
1500
  return () => document.removeEventListener("keydown", handler);
1117
1501
  }, [onClose]);
1118
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles3.overlay, onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...styles3.panel, width }, onClick: (e) => e.stopPropagation(), children: [
1119
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles3.header, children: [
1120
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles3.title, children: title }),
1121
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles3.closeBtn, onClick: onClose, children: "\u2715" })
1502
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles4.overlay, onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...styles4.panel, width }, onClick: (e) => e.stopPropagation(), children: [
1503
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.header, children: [
1504
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles4.title, children: title }),
1505
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles4.closeBtn, onClick: onClose, children: "\u2715" })
1122
1506
  ] }),
1123
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles3.body, children })
1507
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles4.body, children })
1124
1508
  ] }) });
1125
1509
  }
1126
- var styles3 = {
1510
+ var styles4 = {
1127
1511
  overlay: {
1128
1512
  position: "fixed",
1129
1513
  inset: 0,
@@ -1160,16 +1544,7 @@ var styles3 = {
1160
1544
  },
1161
1545
  body: { overflowY: "auto", padding: 20, color: "var(--wp-text)" }
1162
1546
  };
1163
- var VALIDATION_TYPES = [
1164
- { type: "required", label: "Required", hasValue: false },
1165
- { type: "min", label: "Min value", hasValue: true },
1166
- { type: "max", label: "Max value", hasValue: true },
1167
- { type: "minLength", label: "Min length", hasValue: true },
1168
- { type: "maxLength", label: "Max length", hasValue: true },
1169
- { type: "email", label: "Email format", hasValue: false },
1170
- { type: "url", label: "URL format", hasValue: false },
1171
- { type: "regex", label: "Regex pattern", hasValue: true }
1172
- ];
1547
+ var ENUM_FIELD_TYPES = ["select", "multiselect", "radio"];
1173
1548
  function FieldEditor() {
1174
1549
  const {
1175
1550
  schema,
@@ -1178,85 +1553,117 @@ function FieldEditor() {
1178
1553
  updateField,
1179
1554
  setFieldCondition
1180
1555
  } = useBuilderStore();
1181
- const [newValidationType, setNewValidationType] = react.useState("required");
1556
+ const readOnly = useBuilderReadOnly();
1557
+ const externalEnums = useBuilderExternalEnums();
1182
1558
  const [conditionModalOpen, setConditionModalOpen] = react.useState(false);
1559
+ const [validationModalOpen, setValidationModalOpen] = react.useState(false);
1183
1560
  const step = schema.steps.find((s) => s.id === selectedStepId);
1184
1561
  const field = step?.fields.find((f) => f.id === selectedFieldId);
1185
1562
  if (!field || !step) {
1186
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles4.empty, children: "Select a field in the middle panel to edit its properties." });
1563
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.empty, children: "Select a field in the middle panel to edit its properties." });
1187
1564
  }
1188
1565
  const validation = field.validation ?? [];
1189
1566
  const isRequired = validation.some((v) => v.type === "required");
1190
1567
  const hasCondition = !!field.visibleWhen;
1191
1568
  const ruleCount = field.visibleWhen?.rules.length ?? 0;
1192
- const updateValidationRule = (index, updates) => {
1193
- const updated = validation.map((v, i) => i === index ? { ...v, ...updates } : v);
1194
- updateField(step.id, field.id, { validation: updated });
1195
- };
1196
- const removeValidationRule = (index) => {
1197
- const updated = validation.filter((_, i) => i !== index);
1198
- updateField(step.id, field.id, { validation: updated.length ? updated : void 0 });
1199
- };
1200
- const addValidationRule = () => {
1201
- const newRule = {
1202
- type: newValidationType,
1203
- message: `${newValidationType} error`
1204
- };
1205
- updateField(step.id, field.id, { validation: [...validation, newRule] });
1569
+ const handleValidationChange = (rules) => {
1570
+ updateField(step.id, field.id, { validation: rules.length ? rules : void 0 });
1206
1571
  };
1207
1572
  const handleDependsOnChange = (paths) => {
1208
1573
  updateField(step.id, field.id, { dependsOn: paths.length ? paths : void 0 });
1209
1574
  };
1210
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.container, children: [
1211
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.header, children: [
1212
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.headerLeft, children: [
1213
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles4.headerTitle, children: "Field Editor" }),
1214
- !isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles4.optionalBadge, children: "optional" }),
1215
- isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles4.requiredBadge, children: "required" })
1575
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.container, children: [
1576
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.header, children: [
1577
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.headerLeft, children: [
1578
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.headerTitle, children: "Field Editor" }),
1579
+ !isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.optionalBadge, children: "optional" }),
1580
+ isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.requiredBadge, children: "required" })
1216
1581
  ] }),
1217
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.fieldId, children: [
1582
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.fieldId, children: [
1218
1583
  "id: ",
1219
1584
  field.id
1220
1585
  ] })
1221
1586
  ] }),
1222
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.body, children: [
1223
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.group, children: [
1224
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles4.label, children: "Label" }),
1587
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.body, children: [
1588
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.group, children: [
1589
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles5.label, children: "Label" }),
1225
1590
  /* @__PURE__ */ jsxRuntime.jsx(
1226
1591
  "input",
1227
1592
  {
1228
- style: styles4.input,
1593
+ style: styles5.input,
1229
1594
  value: field.label,
1230
1595
  onChange: (e) => updateField(step.id, field.id, { label: e.target.value })
1231
1596
  }
1232
1597
  )
1233
1598
  ] }),
1234
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.group, children: [
1235
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles4.label, children: "Placeholder" }),
1599
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.group, children: [
1600
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles5.label, children: "Placeholder" }),
1236
1601
  /* @__PURE__ */ jsxRuntime.jsx(
1237
1602
  "input",
1238
1603
  {
1239
- style: styles4.input,
1604
+ style: styles5.input,
1240
1605
  value: field.placeholder ?? "",
1241
1606
  placeholder: "Optional",
1242
1607
  onChange: (e) => updateField(step.id, field.id, { placeholder: e.target.value || void 0 })
1243
1608
  }
1244
1609
  )
1245
1610
  ] }),
1246
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.group, children: [
1247
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles4.label, children: "Default value" }),
1611
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.group, children: [
1612
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles5.label, children: "Default value" }),
1248
1613
  /* @__PURE__ */ jsxRuntime.jsx(
1249
1614
  "input",
1250
1615
  {
1251
- style: styles4.input,
1616
+ style: styles5.input,
1252
1617
  value: field.defaultValue != null ? String(field.defaultValue) : "",
1253
1618
  placeholder: "Optional",
1254
1619
  onChange: (e) => updateField(step.id, field.id, { defaultValue: e.target.value || void 0 })
1255
1620
  }
1256
1621
  )
1257
1622
  ] }),
1258
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.group, children: [
1259
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles4.label, children: "Depends on" }),
1623
+ ENUM_FIELD_TYPES.includes(field.type) && externalEnums.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.group, children: [
1624
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles5.label, children: "Options source" }),
1625
+ /* @__PURE__ */ jsxRuntime.jsxs(
1626
+ "select",
1627
+ {
1628
+ style: styles5.input,
1629
+ disabled: readOnly,
1630
+ value: field.externalEnumId ?? "",
1631
+ onChange: (e) => {
1632
+ const enumId = e.target.value || void 0;
1633
+ updateField(step.id, field.id, {
1634
+ externalEnumId: enumId,
1635
+ // Clear hardcoded options when switching to an enum
1636
+ options: enumId ? void 0 : field.options
1637
+ });
1638
+ },
1639
+ children: [
1640
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u2014 Hardcoded options \u2014" }),
1641
+ externalEnums.map((en) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: en.id, children: [
1642
+ en.label,
1643
+ " (",
1644
+ en.values.length,
1645
+ " items)"
1646
+ ] }, en.id))
1647
+ ]
1648
+ }
1649
+ ),
1650
+ field.externalEnumId && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.enumInfo, children: (() => {
1651
+ const en = externalEnums.find((e) => e.id === field.externalEnumId);
1652
+ return en ? /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles5.enumBadge, children: [
1653
+ "\u229E ",
1654
+ en.label,
1655
+ " \xB7 ",
1656
+ en.values.length,
1657
+ " options"
1658
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles5.enumMissing, children: [
1659
+ '\u26A0 Enum "',
1660
+ field.externalEnumId,
1661
+ '" not found'
1662
+ ] });
1663
+ })() })
1664
+ ] }),
1665
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.group, children: [
1666
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles5.label, children: "Depends on" }),
1260
1667
  /* @__PURE__ */ jsxRuntime.jsx(
1261
1668
  DependsOnInput,
1262
1669
  {
@@ -1267,92 +1674,79 @@ function FieldEditor() {
1267
1674
  }
1268
1675
  )
1269
1676
  ] }),
1270
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles4.divider }),
1271
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.conditionRow, children: [
1272
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.conditionInfo, children: [
1273
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles4.label, children: "Visibility condition" }),
1274
- hasCondition ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.conditionSummary, children: [
1275
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles4.conditionBadge, children: [
1677
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.divider }),
1678
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionRow, children: [
1679
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionInfo, children: [
1680
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.label, children: "Visibility condition" }),
1681
+ hasCondition ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionSummary, children: [
1682
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles5.conditionBadge, children: [
1276
1683
  ruleCount,
1277
1684
  " rule",
1278
1685
  ruleCount !== 1 ? "s" : "",
1279
1686
  " \xB7 ",
1280
1687
  field.visibleWhen.combinator.toUpperCase()
1281
1688
  ] }),
1282
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles4.conditionDesc, children: "Field is conditional" })
1283
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles4.conditionNone, children: "Always visible" })
1689
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.conditionDesc, children: "Field is conditional" })
1690
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.conditionNone, children: "Always visible" })
1284
1691
  ] }),
1285
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.conditionActions, children: [
1286
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles4.editConditionBtn, onClick: () => setConditionModalOpen(true), children: hasCondition ? "Edit" : "Add" }),
1692
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionActions, children: [
1693
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles5.editConditionBtn, onClick: () => setConditionModalOpen(true), children: hasCondition ? "Edit" : "Add" }),
1287
1694
  hasCondition && /* @__PURE__ */ jsxRuntime.jsx(
1288
1695
  "button",
1289
1696
  {
1290
- style: styles4.clearConditionBtn,
1697
+ style: styles5.clearConditionBtn,
1291
1698
  onClick: () => setFieldCondition(step.id, field.id, void 0),
1292
1699
  children: "Clear"
1293
1700
  }
1294
1701
  )
1295
1702
  ] })
1296
1703
  ] }),
1297
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles4.divider }),
1298
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.sectionTitle, children: [
1299
- "Validation",
1300
- !isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles4.optionalHint, children: '\u2014 no "required" rule \u2192 field is optional' })
1301
- ] }),
1302
- validation.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles4.noRules, children: "No rules \xB7 field is optional by default." }),
1303
- validation.map((rule, index) => {
1304
- const def = VALIDATION_TYPES.find((vt) => vt.type === rule.type);
1305
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.ruleCard, children: [
1306
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.ruleHeader, children: [
1307
- /* @__PURE__ */ jsxRuntime.jsx(
1308
- "span",
1309
- {
1310
- style: {
1311
- ...styles4.ruleBadge,
1312
- ...rule.type === "required" ? styles4.requiredRuleBadge : {}
1313
- },
1314
- children: rule.type
1315
- }
1316
- ),
1317
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles4.removeRuleBtn, onClick: () => removeValidationRule(index), children: "\u2715" })
1318
- ] }),
1319
- def?.hasValue && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.ruleRow, children: [
1320
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles4.ruleLabel, children: "Value" }),
1321
- /* @__PURE__ */ jsxRuntime.jsx(
1322
- "input",
1323
- {
1324
- style: styles4.ruleInput,
1325
- value: rule.value != null ? String(rule.value) : "",
1326
- onChange: (e) => updateValidationRule(index, { value: e.target.value })
1327
- }
1328
- )
1329
- ] }),
1330
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.ruleRow, children: [
1331
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles4.ruleLabel, children: "Error message" }),
1332
- /* @__PURE__ */ jsxRuntime.jsx(
1333
- "input",
1334
- {
1335
- style: styles4.ruleInput,
1336
- value: rule.message,
1337
- onChange: (e) => updateValidationRule(index, { message: e.target.value })
1338
- }
1339
- )
1340
- ] })
1341
- ] }, index);
1342
- }),
1343
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles4.addRule, children: [
1344
- /* @__PURE__ */ jsxRuntime.jsx(
1345
- "select",
1346
- {
1347
- style: styles4.ruleSelect,
1348
- value: newValidationType,
1349
- onChange: (e) => setNewValidationType(e.target.value),
1350
- children: VALIDATION_TYPES.map((vt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: vt.type, children: vt.label }, vt.type))
1351
- }
1352
- ),
1353
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles4.addRuleBtn, onClick: addValidationRule, children: "+ Add rule" })
1704
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.divider }),
1705
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionRow, children: [
1706
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionInfo, children: [
1707
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.label, children: "Validation" }),
1708
+ validation.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionSummary, children: [
1709
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles5.validationBadge, children: [
1710
+ validation.length,
1711
+ " rule",
1712
+ validation.length !== 1 ? "s" : "",
1713
+ isRequired ? " \xB7 required" : ""
1714
+ ] }),
1715
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.conditionDesc, children: validation.map((r) => r.type).join(", ") })
1716
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.conditionNone, children: "No rules \xB7 field is optional" })
1717
+ ] }),
1718
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.conditionActions, children: [
1719
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles5.editConditionBtn, onClick: () => setValidationModalOpen(true), children: validation.length > 0 ? "Edit" : "Add" }),
1720
+ validation.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
1721
+ "button",
1722
+ {
1723
+ style: styles5.clearConditionBtn,
1724
+ onClick: () => updateField(step.id, field.id, { validation: void 0 }),
1725
+ children: "Clear"
1726
+ }
1727
+ )
1728
+ ] })
1354
1729
  ] })
1355
1730
  ] }),
1731
+ validationModalOpen && /* @__PURE__ */ jsxRuntime.jsxs(
1732
+ Modal,
1733
+ {
1734
+ title: `Validation \u2014 "${field.label}"`,
1735
+ onClose: () => setValidationModalOpen(false),
1736
+ width: 680,
1737
+ children: [
1738
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles5.modalHint, children: "Define validation rules for this field. All rules must pass for the field to be valid." }),
1739
+ /* @__PURE__ */ jsxRuntime.jsx(
1740
+ ValidationBuilder,
1741
+ {
1742
+ value: validation,
1743
+ onChange: handleValidationChange
1744
+ }
1745
+ ),
1746
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.modalFooter, children: /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles5.modalCloseBtn, onClick: () => setValidationModalOpen(false), children: "Done" }) })
1747
+ ]
1748
+ }
1749
+ ),
1356
1750
  conditionModalOpen && /* @__PURE__ */ jsxRuntime.jsxs(
1357
1751
  Modal,
1358
1752
  {
@@ -1360,7 +1754,7 @@ function FieldEditor() {
1360
1754
  onClose: () => setConditionModalOpen(false),
1361
1755
  width: 620,
1362
1756
  children: [
1363
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles4.modalHint, children: "Define when this field is visible within its step." }),
1757
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles5.modalHint, children: "Define when this field is visible within its step." }),
1364
1758
  /* @__PURE__ */ jsxRuntime.jsx(
1365
1759
  ConditionBuilder,
1366
1760
  {
@@ -1370,13 +1764,13 @@ function FieldEditor() {
1370
1764
  excludeFieldId: field.id
1371
1765
  }
1372
1766
  ),
1373
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles4.modalFooter, children: /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles4.modalCloseBtn, onClick: () => setConditionModalOpen(false), children: "Done" }) })
1767
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.modalFooter, children: /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles5.modalCloseBtn, onClick: () => setConditionModalOpen(false), children: "Done" }) })
1374
1768
  ]
1375
1769
  }
1376
1770
  )
1377
1771
  ] });
1378
1772
  }
1379
- var styles4 = {
1773
+ var styles5 = {
1380
1774
  container: { display: "flex", flexDirection: "column", height: "100%", overflow: "hidden" },
1381
1775
  empty: {
1382
1776
  display: "flex",
@@ -1460,60 +1854,30 @@ var styles4 = {
1460
1854
  borderRadius: "var(--wp-radius)",
1461
1855
  cursor: "pointer"
1462
1856
  },
1463
- sectionTitle: { fontSize: 12, fontWeight: 700, color: "var(--wp-text-secondary)", textTransform: "uppercase", letterSpacing: "0.05em" },
1464
- optionalHint: { fontSize: 10, color: "var(--wp-text-subtle)", fontWeight: 400, textTransform: "none", letterSpacing: 0 },
1465
- noRules: { fontSize: 12, color: "var(--wp-text-subtle)" },
1466
- ruleCard: {
1467
- background: "var(--wp-surface)",
1468
- border: "1px solid var(--wp-border)",
1469
- borderRadius: "var(--wp-radius-lg)",
1470
- padding: 10,
1471
- display: "flex",
1472
- flexDirection: "column",
1473
- gap: 8
1474
- },
1475
- ruleHeader: { display: "flex", justifyContent: "space-between", alignItems: "center" },
1476
- ruleBadge: {
1857
+ validationBadge: {
1477
1858
  fontSize: 11,
1478
1859
  fontWeight: 700,
1479
- color: "var(--wp-primary-dark)",
1480
1860
  background: "var(--wp-primary-bg)",
1861
+ color: "var(--wp-primary-dark)",
1481
1862
  padding: "2px 8px",
1482
1863
  borderRadius: 4
1483
1864
  },
1484
- requiredRuleBadge: { background: "var(--wp-danger-bg-strong)", color: "var(--wp-danger)" },
1485
- removeRuleBtn: { border: "none", background: "transparent", color: "var(--wp-danger)", cursor: "pointer", fontSize: 12 },
1486
- ruleRow: { display: "flex", flexDirection: "column", gap: 3 },
1487
- ruleLabel: { fontSize: 10, fontWeight: 600, color: "var(--wp-text-subtle)", textTransform: "uppercase" },
1488
- ruleInput: {
1489
- fontSize: 12,
1490
- padding: "4px 6px",
1491
- border: "1px solid var(--wp-border-muted)",
1492
- borderRadius: 4,
1493
- outline: "none",
1494
- background: "var(--wp-canvas)",
1495
- color: "var(--wp-text)"
1496
- },
1497
- addRule: { display: "flex", gap: 8, alignItems: "center" },
1498
- ruleSelect: {
1499
- flex: 1,
1500
- fontSize: 12,
1501
- padding: "5px 6px",
1502
- border: "1px solid var(--wp-border-muted)",
1503
- borderRadius: "var(--wp-radius)",
1504
- background: "var(--wp-canvas)",
1505
- color: "var(--wp-text)"
1865
+ enumInfo: { marginTop: 4 },
1866
+ enumBadge: {
1867
+ fontSize: 11,
1868
+ fontWeight: 600,
1869
+ padding: "2px 8px",
1870
+ background: "var(--wp-info-bg)",
1871
+ color: "var(--wp-info-text)",
1872
+ borderRadius: 4
1506
1873
  },
1507
- addRuleBtn: {
1508
- fontSize: 12,
1509
- padding: "5px 10px",
1510
- background: "var(--wp-surface-muted)",
1511
- border: "1px solid var(--wp-border-muted)",
1512
- borderRadius: "var(--wp-radius)",
1513
- cursor: "pointer",
1514
- fontWeight: 500,
1515
- whiteSpace: "nowrap",
1516
- color: "var(--wp-text-secondary)"
1874
+ enumMissing: {
1875
+ fontSize: 11,
1876
+ fontWeight: 600,
1877
+ padding: "2px 8px",
1878
+ background: "var(--wp-warning-bg)",
1879
+ color: "var(--wp-warning)",
1880
+ borderRadius: 4
1517
1881
  },
1518
1882
  modalHint: { fontSize: 13, color: "var(--wp-text-muted)", marginBottom: 16, marginTop: 0 },
1519
1883
  modalFooter: { marginTop: 20, display: "flex", justifyContent: "flex-end" },
@@ -1647,10 +2011,14 @@ function FieldList() {
1647
2011
  selectedFieldId,
1648
2012
  addField,
1649
2013
  removeField,
2014
+ duplicateField,
1650
2015
  updateField,
1651
2016
  selectField,
1652
2017
  reorderFields
1653
2018
  } = useBuilderStore();
2019
+ const readOnly = useBuilderReadOnly();
2020
+ const appCustomTypes = useBuilderCustomTypes();
2021
+ const externalEnums = useBuilderExternalEnums();
1654
2022
  const [moveError, setMoveError] = react.useState(null);
1655
2023
  const step = schema.steps.find((s) => s.id === selectedStepId);
1656
2024
  const allDependencyTargets = /* @__PURE__ */ new Set();
@@ -1662,7 +2030,7 @@ function FieldList() {
1662
2030
  }
1663
2031
  }
1664
2032
  if (!step) {
1665
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.empty, children: "Select a step on the left to manage its fields." });
2033
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.empty, children: "Select a step on the left to manage its fields." });
1666
2034
  }
1667
2035
  const tryMove = (fromIndex, toIndex) => {
1668
2036
  const check = isFieldMoveValid(step.fields, step.id, fromIndex, toIndex);
@@ -1674,30 +2042,31 @@ function FieldList() {
1674
2042
  setMoveError(null);
1675
2043
  reorderFields(step.id, fromIndex, toIndex);
1676
2044
  };
1677
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.container, children: [
1678
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.header, children: [
2045
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.container, children: [
2046
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.header, children: [
1679
2047
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1680
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.stepTitle, children: step.title }),
1681
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.stepSub, children: [
2048
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.stepTitle, children: step.title }),
2049
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.stepSub, children: [
1682
2050
  step.fields.length,
1683
2051
  " field",
1684
2052
  step.fields.length !== 1 ? "s" : ""
1685
2053
  ] })
1686
2054
  ] }),
1687
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles5.addBtn, onClick: () => addField(step.id), children: "+ Add field" })
2055
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles6.addBtn, onClick: () => addField(step.id), children: "+ Add field" })
1688
2056
  ] }),
1689
- moveError && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.errorBanner, children: [
2057
+ moveError && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.errorBanner, children: [
1690
2058
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u26A0" }),
1691
2059
  " ",
1692
2060
  moveError
1693
2061
  ] }),
1694
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.list, children: [
1695
- step.fields.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.emptyFields, children: 'No fields yet. Click "Add field" to start.' }),
2062
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.list, children: [
2063
+ step.fields.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.emptyFields, children: 'No fields yet. Click "Add field" to start.' }),
1696
2064
  step.fields.map((field, index) => {
1697
2065
  const isSelected = field.id === selectedFieldId;
1698
2066
  const isRequired = field.validation?.some((v) => v.type === "required") ?? false;
1699
2067
  const hasCondition = !!field.visibleWhen;
1700
2068
  const hasDeps = (field.dependsOn?.length ?? 0) > 0;
2069
+ const enumDef = field.externalEnumId ? externalEnums.find((e) => e.id === field.externalEnumId) : void 0;
1701
2070
  const isUsedAsDep = allDependencyTargets.has(`${step.id}.${field.id}`);
1702
2071
  const canMoveUp = index > 0 && isFieldMoveValid(step.fields, step.id, index, index - 1).valid;
1703
2072
  const canMoveDown = index < step.fields.length - 1 && isFieldMoveValid(step.fields, step.id, index, index + 1).valid;
@@ -1711,29 +2080,45 @@ function FieldList() {
1711
2080
  return /* @__PURE__ */ jsxRuntime.jsxs(
1712
2081
  "div",
1713
2082
  {
1714
- style: { ...styles5.card, ...isSelected ? styles5.cardSelected : {} },
2083
+ style: { ...styles6.card, ...isSelected ? styles6.cardSelected : {} },
1715
2084
  onClick: () => selectField(field.id),
1716
2085
  children: [
1717
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.cardTop, children: [
1718
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.cardLeft, children: [
1719
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.typeBadge, children: field.type }),
1720
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.fieldLabel, children: field.label })
2086
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.cardTop, children: [
2087
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.cardLeft, children: [
2088
+ (() => {
2089
+ const ct = appCustomTypes.find((c) => c.id === field.type);
2090
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { style: { ...styles6.typeBadge, ...ct ? styles6.typeBadgeCustom : {} }, children: ct ? `${ct.icon ? ct.icon + " " : ""}${ct.label}` : field.type });
2091
+ })(),
2092
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.fieldLabel, children: field.label })
1721
2093
  ] }),
1722
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.cardRight, children: [
1723
- /* @__PURE__ */ jsxRuntime.jsx(
2094
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.cardRight, children: [
2095
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsxs(
1724
2096
  "select",
1725
2097
  {
1726
- style: styles5.typeSelect,
2098
+ style: styles6.typeSelect,
1727
2099
  value: field.type,
1728
2100
  onClick: (e) => e.stopPropagation(),
1729
- onChange: (e) => updateField(step.id, field.id, { type: e.target.value }),
1730
- children: FIELD_TYPES.map((t) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: t, children: t }, t))
2101
+ onChange: (e) => {
2102
+ const newType = e.target.value;
2103
+ const customType = appCustomTypes.find((ct) => ct.id === newType);
2104
+ updateField(step.id, field.id, {
2105
+ type: newType,
2106
+ ...customType?.defaultValidation ? { validation: customType.defaultValidation } : {}
2107
+ });
2108
+ },
2109
+ children: [
2110
+ FIELD_TYPES.map((t) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: t, children: t }, t)),
2111
+ appCustomTypes.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("optgroup", { label: "Custom", children: appCustomTypes.map((ct) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: ct.id, children: [
2112
+ ct.icon ? `${ct.icon} ` : "",
2113
+ ct.label
2114
+ ] }, ct.id)) })
2115
+ ]
1731
2116
  }
1732
2117
  ),
1733
- index > 0 && /* @__PURE__ */ jsxRuntime.jsx(
2118
+ !readOnly && index > 0 && /* @__PURE__ */ jsxRuntime.jsx(
1734
2119
  "button",
1735
2120
  {
1736
- style: { ...styles5.iconBtn, ...canMoveUp ? {} : styles5.iconBtnBlocked },
2121
+ style: { ...styles6.iconBtn, ...canMoveUp ? {} : styles6.iconBtnBlocked },
1737
2122
  title: canMoveUp ? "Move up" : "Can't move \u2014 dependency order required",
1738
2123
  onClick: (e) => {
1739
2124
  e.stopPropagation();
@@ -1742,10 +2127,10 @@ function FieldList() {
1742
2127
  children: "\u2191"
1743
2128
  }
1744
2129
  ),
1745
- index < step.fields.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(
2130
+ !readOnly && index < step.fields.length - 1 && /* @__PURE__ */ jsxRuntime.jsx(
1746
2131
  "button",
1747
2132
  {
1748
- style: { ...styles5.iconBtn, ...canMoveDown ? {} : styles5.iconBtnBlocked },
2133
+ style: { ...styles6.iconBtn, ...canMoveDown ? {} : styles6.iconBtnBlocked },
1749
2134
  title: canMoveDown ? "Move down" : "Can't move \u2014 dependency order required",
1750
2135
  onClick: (e) => {
1751
2136
  e.stopPropagation();
@@ -1754,10 +2139,22 @@ function FieldList() {
1754
2139
  children: "\u2193"
1755
2140
  }
1756
2141
  ),
1757
- /* @__PURE__ */ jsxRuntime.jsx(
2142
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
1758
2143
  "button",
1759
2144
  {
1760
- style: { ...styles5.iconBtn, color: "var(--wp-danger)" },
2145
+ style: styles6.iconBtn,
2146
+ title: "Duplicate field",
2147
+ onClick: (e) => {
2148
+ e.stopPropagation();
2149
+ duplicateField(step.id, field.id);
2150
+ },
2151
+ children: "\u29C9"
2152
+ }
2153
+ ),
2154
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
2155
+ "button",
2156
+ {
2157
+ style: { ...styles6.iconBtn, color: "var(--wp-danger)" },
1761
2158
  title: "Remove field",
1762
2159
  onClick: (e) => {
1763
2160
  e.stopPropagation();
@@ -1768,23 +2165,27 @@ function FieldList() {
1768
2165
  )
1769
2166
  ] })
1770
2167
  ] }),
1771
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.badges, children: [
1772
- !isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.badgeOptional, children: "optional" }),
1773
- isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.badgeRequired, children: "required" }),
1774
- hasCondition && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.badgeCondition, children: "conditional" }),
1775
- hasDeps && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles5.badgeDep, children: [
2168
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.badges, children: [
2169
+ !isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.badgeOptional, children: "optional" }),
2170
+ isRequired && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.badgeRequired, children: "required" }),
2171
+ hasCondition && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.badgeCondition, children: "conditional" }),
2172
+ hasDeps && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles6.badgeDep, children: [
1776
2173
  "depends on ",
1777
2174
  field.dependsOn.length
1778
2175
  ] }),
1779
- isUsedAsDep && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.badgeUsed, children: "\u2190 dependency" })
2176
+ isUsedAsDep && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.badgeUsed, children: "\u2190 dependency" }),
2177
+ field.externalEnumId && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles6.badgeEnum, children: [
2178
+ "\u229E ",
2179
+ enumDef ? enumDef.label : field.externalEnumId
2180
+ ] })
1780
2181
  ] }),
1781
- intraStepDeps.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.depRow, children: [
1782
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.depLabel, children: "needs:" }),
1783
- intraStepDeps.map((label) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.depBadge, children: label }, label))
2182
+ intraStepDeps.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.depRow, children: [
2183
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.depLabel, children: "needs:" }),
2184
+ intraStepDeps.map((label) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.depBadge, children: label }, label))
1784
2185
  ] }),
1785
- intraStepDependents.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles5.depRow, children: [
1786
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.depLabelUsed, children: "used by:" }),
1787
- intraStepDependents.map((f) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles5.depBadgeUsed, children: f.label }, f.id))
2186
+ intraStepDependents.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.depRow, children: [
2187
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.depLabelUsed, children: "used by:" }),
2188
+ intraStepDependents.map((f) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.depBadgeUsed, children: f.label }, f.id))
1788
2189
  ] })
1789
2190
  ]
1790
2191
  },
@@ -1794,7 +2195,7 @@ function FieldList() {
1794
2195
  ] })
1795
2196
  ] });
1796
2197
  }
1797
- var styles5 = {
2198
+ var styles6 = {
1798
2199
  container: { display: "flex", flexDirection: "column", height: "100%" },
1799
2200
  empty: {
1800
2201
  display: "flex",
@@ -1863,6 +2264,10 @@ var styles5 = {
1863
2264
  fontWeight: 600,
1864
2265
  flexShrink: 0
1865
2266
  },
2267
+ typeBadgeCustom: {
2268
+ background: "var(--wp-success-bg)",
2269
+ color: "var(--wp-success)"
2270
+ },
1866
2271
  fieldLabel: {
1867
2272
  fontSize: 13,
1868
2273
  fontWeight: 600,
@@ -1937,6 +2342,14 @@ var styles5 = {
1937
2342
  borderRadius: 3,
1938
2343
  textTransform: "uppercase"
1939
2344
  },
2345
+ badgeEnum: {
2346
+ fontSize: 9,
2347
+ fontWeight: 600,
2348
+ padding: "1px 6px",
2349
+ background: "var(--wp-info-bg)",
2350
+ color: "var(--wp-info-text)",
2351
+ borderRadius: 3
2352
+ },
1940
2353
  depRow: { display: "flex", alignItems: "center", flexWrap: "wrap", gap: 4 },
1941
2354
  depLabel: { fontSize: 10, fontWeight: 600, color: "var(--wp-text-muted)", textTransform: "uppercase" },
1942
2355
  depLabelUsed: { fontSize: 10, fontWeight: 600, color: "var(--wp-success)", textTransform: "uppercase" },
@@ -1957,12 +2370,28 @@ var styles5 = {
1957
2370
  borderRadius: 4
1958
2371
  }
1959
2372
  };
1960
- function PreviewPanel({ store, schema }) {
2373
+ function PreviewPanel({ store, schema, externalEnums }) {
1961
2374
  const [done, setDone] = react.useState(false);
1962
- const { tree, currentStep, progress } = react$1.useWaypoint(store);
2375
+ const { tree, currentStep, progress } = react$1.useWaypoint(store, externalEnums);
1963
2376
  const stepId = currentStep?.definition.id ?? "";
1964
2377
  const { fields, stepData, setFieldValue } = react$1.useWaypointStep(store, stepId);
1965
2378
  const [errors, setErrors] = react.useState({});
2379
+ const extVarDefs = schema.externalVariables ?? [];
2380
+ const [mockVars, setMockVars] = react.useState(() => {
2381
+ const initial = {};
2382
+ for (const v of extVarDefs) {
2383
+ initial[v.id] = v.type === "boolean" ? false : v.type === "number" ? 0 : "";
2384
+ }
2385
+ return initial;
2386
+ });
2387
+ react.useEffect(() => {
2388
+ for (const [varId, value] of Object.entries(mockVars)) {
2389
+ store.getState().setExternalVar(varId, value);
2390
+ }
2391
+ }, [mockVars]);
2392
+ function handleMockVarChange(varId, value) {
2393
+ setMockVars((prev) => ({ ...prev, [varId]: value }));
2394
+ }
1966
2395
  function handleNext() {
1967
2396
  const newErrors = {};
1968
2397
  for (const field of fields) {
@@ -1983,7 +2412,7 @@ function PreviewPanel({ store, schema }) {
1983
2412
  const oldIds = tree.steps.map((s) => s.definition.id).join(",");
1984
2413
  store.getState().setStepData(stepId, stepData);
1985
2414
  const newData = store.getState().data;
1986
- const newTree = core.resolveTree(schema, newData, {});
2415
+ const newTree = core.resolveTree(schema, newData, store.getState().externalVars, externalEnums);
1987
2416
  const newIds = newTree.steps.map((s) => s.definition.id).join(",");
1988
2417
  if (oldIds !== newIds) {
1989
2418
  store.getState().truncateHistoryAt(stepId);
@@ -2001,6 +2430,9 @@ function PreviewPanel({ store, schema }) {
2001
2430
  }
2002
2431
  function handleRestart() {
2003
2432
  store.getState().init(schema);
2433
+ for (const [varId, value] of Object.entries(mockVars)) {
2434
+ store.getState().setExternalVar(varId, value);
2435
+ }
2004
2436
  setDone(false);
2005
2437
  setErrors({});
2006
2438
  }
@@ -2008,38 +2440,59 @@ function PreviewPanel({ store, schema }) {
2008
2440
  (s) => s.definition.id === currentStep?.definition.id
2009
2441
  );
2010
2442
  if (done) {
2011
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.panel, children: [
2012
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.leftCol, children: /* @__PURE__ */ jsxRuntime.jsx(
2443
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.panel, children: [
2444
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.leftCol, children: [
2445
+ /* @__PURE__ */ jsxRuntime.jsx(
2446
+ StepList,
2447
+ {
2448
+ tree,
2449
+ currentIdx,
2450
+ onSelect: (id) => store.getState().setCurrentStep(id)
2451
+ }
2452
+ ),
2453
+ extVarDefs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
2454
+ MockVarPanel,
2455
+ {
2456
+ extVarDefs,
2457
+ mockVars,
2458
+ onChange: handleMockVarChange
2459
+ }
2460
+ )
2461
+ ] }),
2462
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.divider }),
2463
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.rightCol, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.doneScreen, children: [
2464
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.doneIcon, children: "\u2713" }),
2465
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.doneTitle, children: "Parcours termin\xE9 !" }),
2466
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles7.doneText, children: "Toutes les \xE9tapes ont \xE9t\xE9 compl\xE9t\xE9es avec succ\xE8s." }),
2467
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles7.primaryBtn, onClick: handleRestart, children: "Recommencer" })
2468
+ ] }) })
2469
+ ] });
2470
+ }
2471
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.panel, children: [
2472
+ /* @__PURE__ */ jsxRuntime.jsx(devtools.DevPanel, { store }),
2473
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.leftCol, children: [
2474
+ /* @__PURE__ */ jsxRuntime.jsx(
2013
2475
  StepList,
2014
2476
  {
2015
2477
  tree,
2016
2478
  currentIdx,
2017
2479
  onSelect: (id) => store.getState().setCurrentStep(id)
2018
2480
  }
2019
- ) }),
2020
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.divider }),
2021
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.rightCol, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.doneScreen, children: [
2022
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.doneIcon, children: "\u2713" }),
2023
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.doneTitle, children: "Parcours termin\xE9 !" }),
2024
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles6.doneText, children: "Toutes les \xE9tapes ont \xE9t\xE9 compl\xE9t\xE9es avec succ\xE8s." }),
2025
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles6.primaryBtn, onClick: handleRestart, children: "Recommencer" })
2026
- ] }) })
2027
- ] });
2028
- }
2029
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.panel, children: [
2030
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.leftCol, children: /* @__PURE__ */ jsxRuntime.jsx(
2031
- StepList,
2032
- {
2033
- tree,
2034
- currentIdx,
2035
- onSelect: (id) => store.getState().setCurrentStep(id)
2036
- }
2037
- ) }),
2038
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.divider }),
2039
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.rightCol, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.stepRenderer, children: [
2040
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.progressTrack, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...styles6.progressFill, width: `${progress}%` } }) }),
2041
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: styles6.stepTitle, children: currentStep?.definition.title ?? "" }),
2042
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.fieldsContainer, children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(
2481
+ ),
2482
+ extVarDefs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
2483
+ MockVarPanel,
2484
+ {
2485
+ extVarDefs,
2486
+ mockVars,
2487
+ onChange: handleMockVarChange
2488
+ }
2489
+ )
2490
+ ] }),
2491
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.divider }),
2492
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.rightCol, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.stepRenderer, children: [
2493
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.progressTrack, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...styles7.progressFill, width: `${progress}%` } }) }),
2494
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: styles7.stepTitle, children: currentStep?.definition.title ?? "" }),
2495
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.fieldsContainer, children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(
2043
2496
  FieldRenderer,
2044
2497
  {
2045
2498
  field,
@@ -2058,9 +2511,9 @@ function PreviewPanel({ store, schema }) {
2058
2511
  },
2059
2512
  field.definition.id
2060
2513
  )) }),
2061
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.navRow, children: [
2062
- currentIdx > 0 && /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles6.secondaryBtn, onClick: handlePrev, children: "\u2190 Pr\xE9c\xE9dent" }),
2063
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: { ...styles6.primaryBtn, marginLeft: "auto" }, onClick: handleNext, children: core.getNextStep(tree.steps, stepId) ? "Continuer \u2192" : "Terminer \u2713" })
2514
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.navRow, children: [
2515
+ currentIdx > 0 && /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles7.secondaryBtn, onClick: handlePrev, children: "\u2190 Pr\xE9c\xE9dent" }),
2516
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: { ...styles7.primaryBtn, marginLeft: "auto" }, onClick: handleNext, children: core.getNextStep(tree.steps, stepId) ? "Continuer \u2192" : "Terminer \u2713" })
2064
2517
  ] })
2065
2518
  ] }) })
2066
2519
  ] });
@@ -2070,8 +2523,8 @@ function StepList({ tree, currentIdx, onSelect }) {
2070
2523
  ...tree.steps.map((s) => ({ ...s, hidden: false })),
2071
2524
  ...tree.hiddenSteps.map((s) => ({ ...s, hidden: true }))
2072
2525
  ];
2073
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.stepList, children: [
2074
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.stepListTitle, children: "\xC9tapes" }),
2526
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.stepList, children: [
2527
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.stepListTitle, children: "\xC9tapes" }),
2075
2528
  allSteps.map((step) => {
2076
2529
  const isVisible = !step.hidden;
2077
2530
  const visIdx = tree.steps.findIndex((s) => s.definition.id === step.definition.id);
@@ -2085,23 +2538,23 @@ function StepList({ tree, currentIdx, onSelect }) {
2085
2538
  "div",
2086
2539
  {
2087
2540
  style: {
2088
- ...styles6.stepItem,
2089
- ...status === "current" ? styles6.stepItemCurrent : {},
2090
- ...status === "hidden" ? styles6.stepItemHidden : {},
2541
+ ...styles7.stepItem,
2542
+ ...status === "current" ? styles7.stepItemCurrent : {},
2543
+ ...status === "hidden" ? styles7.stepItemHidden : {},
2091
2544
  cursor: status === "done" ? "pointer" : "default"
2092
2545
  },
2093
2546
  onClick: () => {
2094
2547
  if (status === "done") onSelect(step.definition.id);
2095
2548
  },
2096
2549
  children: [
2097
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles6.stepStatus, children: [
2550
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles7.stepStatus, children: [
2098
2551
  status === "done" && "\u2713",
2099
2552
  status === "current" && "\u2192",
2100
2553
  status === "upcoming" && "\u25CB",
2101
2554
  status === "hidden" && "\u2013"
2102
2555
  ] }),
2103
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.stepName, children: step.definition.title }),
2104
- status === "hidden" && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.hiddenBadge, children: "hidden" })
2556
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles7.stepName, children: step.definition.title }),
2557
+ status === "hidden" && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles7.hiddenBadge, children: "hidden" })
2105
2558
  ]
2106
2559
  },
2107
2560
  step.definition.id
@@ -2109,13 +2562,51 @@ function StepList({ tree, currentIdx, onSelect }) {
2109
2562
  })
2110
2563
  ] });
2111
2564
  }
2565
+ function MockVarPanel({ extVarDefs, mockVars, onChange }) {
2566
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.mockPanel, children: [
2567
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.mockPanelTitle, children: [
2568
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u26A1 External Variables" }),
2569
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles7.mockPanelHint, children: "mock values" })
2570
+ ] }),
2571
+ extVarDefs.map((v) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.mockVarRow, children: [
2572
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.mockVarLabel, children: [
2573
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles7.mockVarId, children: [
2574
+ "$",
2575
+ `ext.${v.id}`
2576
+ ] }),
2577
+ v.blocking && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles7.mockBlockingBadge, children: "!" })
2578
+ ] }),
2579
+ v.type === "boolean" ? /* @__PURE__ */ jsxRuntime.jsxs("label", { style: styles7.mockCheckRow, children: [
2580
+ /* @__PURE__ */ jsxRuntime.jsx(
2581
+ "input",
2582
+ {
2583
+ type: "checkbox",
2584
+ checked: Boolean(mockVars[v.id]),
2585
+ onChange: (e) => onChange(v.id, e.target.checked)
2586
+ }
2587
+ ),
2588
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 11, color: "var(--wp-text-muted)" }, children: String(mockVars[v.id]) })
2589
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
2590
+ "input",
2591
+ {
2592
+ style: styles7.mockInput,
2593
+ type: v.type === "number" ? "number" : "text",
2594
+ value: String(mockVars[v.id] ?? ""),
2595
+ placeholder: v.label,
2596
+ onChange: (e) => onChange(v.id, v.type === "number" ? Number(e.target.value) : e.target.value)
2597
+ }
2598
+ )
2599
+ ] }, v.id))
2600
+ ] });
2601
+ }
2112
2602
  function FieldRenderer({ field, value, error, onChange }) {
2113
2603
  const { definition } = field;
2114
- const inputStyle2 = { ...styles6.input, ...error ? styles6.inputError : {} };
2115
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles6.fieldGroup, children: [
2116
- /* @__PURE__ */ jsxRuntime.jsxs("label", { style: styles6.label, children: [
2604
+ const options = field.resolvedOptions ?? definition.options ?? [];
2605
+ const inputStyle2 = { ...styles7.input, ...error ? styles7.inputError : {} };
2606
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.fieldGroup, children: [
2607
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { style: styles7.label, children: [
2117
2608
  definition.label,
2118
- definition.validation?.some((r) => r.type === "required") && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles6.required, children: " *" })
2609
+ definition.validation?.some((r) => r.type === "required") && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles7.required, children: " *" })
2119
2610
  ] }),
2120
2611
  (definition.type === "text" || definition.type === "email" || definition.type === "tel" || definition.type === "password" || definition.type === "url" || definition.type === "number" || definition.type === "date") && /* @__PURE__ */ jsxRuntime.jsx(
2121
2612
  "input",
@@ -2144,7 +2635,7 @@ function FieldRenderer({ field, value, error, onChange }) {
2144
2635
  onChange: (e) => onChange(e.target.value),
2145
2636
  children: [
2146
2637
  /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "\u2014 Choisir \u2014" }),
2147
- definition.options?.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: String(opt.value), children: opt.label }, String(opt.value)))
2638
+ options.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: String(opt.value), children: opt.label }, String(opt.value)))
2148
2639
  ]
2149
2640
  }
2150
2641
  ),
@@ -2158,10 +2649,10 @@ function FieldRenderer({ field, value, error, onChange }) {
2158
2649
  const selected = Array.from(e.target.selectedOptions).map((o) => o.value);
2159
2650
  onChange(selected);
2160
2651
  },
2161
- children: definition.options?.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: String(opt.value), children: opt.label }, String(opt.value)))
2652
+ children: options.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: String(opt.value), children: opt.label }, String(opt.value)))
2162
2653
  }
2163
2654
  ),
2164
- definition.type === "radio" && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: definition.options?.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: 8, cursor: "pointer", fontSize: 13 }, children: [
2655
+ definition.type === "radio" && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: options.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", alignItems: "center", gap: 8, cursor: "pointer", fontSize: 13 }, children: [
2165
2656
  /* @__PURE__ */ jsxRuntime.jsx(
2166
2657
  "input",
2167
2658
  {
@@ -2185,10 +2676,10 @@ function FieldRenderer({ field, value, error, onChange }) {
2185
2676
  ),
2186
2677
  definition.placeholder ?? definition.label
2187
2678
  ] }),
2188
- error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles6.errorMsg, children: error })
2679
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.errorMsg, children: error })
2189
2680
  ] });
2190
2681
  }
2191
- var styles6 = {
2682
+ var styles7 = {
2192
2683
  panel: {
2193
2684
  display: "flex",
2194
2685
  flex: 1,
@@ -2357,6 +2848,80 @@ var styles6 = {
2357
2848
  letterSpacing: "0.3px",
2358
2849
  flexShrink: 0
2359
2850
  },
2851
+ // Mock var panel
2852
+ mockPanel: {
2853
+ borderTop: "1px solid var(--wp-border)",
2854
+ padding: "12px",
2855
+ display: "flex",
2856
+ flexDirection: "column",
2857
+ gap: 8
2858
+ },
2859
+ mockPanelTitle: {
2860
+ display: "flex",
2861
+ alignItems: "center",
2862
+ justifyContent: "space-between",
2863
+ fontSize: 11,
2864
+ fontWeight: 700,
2865
+ color: "var(--wp-text-muted)",
2866
+ textTransform: "uppercase",
2867
+ letterSpacing: "0.5px",
2868
+ marginBottom: 4
2869
+ },
2870
+ mockPanelHint: {
2871
+ fontSize: 9,
2872
+ fontWeight: 500,
2873
+ color: "var(--wp-warning)",
2874
+ textTransform: "none",
2875
+ background: "var(--wp-warning-bg)",
2876
+ padding: "1px 5px",
2877
+ borderRadius: 4
2878
+ },
2879
+ mockVarRow: {
2880
+ display: "flex",
2881
+ flexDirection: "column",
2882
+ gap: 3
2883
+ },
2884
+ mockVarLabel: {
2885
+ display: "flex",
2886
+ alignItems: "center",
2887
+ gap: 4
2888
+ },
2889
+ mockVarId: {
2890
+ fontSize: 10,
2891
+ fontFamily: "monospace",
2892
+ fontWeight: 600,
2893
+ color: "var(--wp-text-secondary)"
2894
+ },
2895
+ mockBlockingBadge: {
2896
+ fontSize: 9,
2897
+ fontWeight: 700,
2898
+ width: 13,
2899
+ height: 13,
2900
+ borderRadius: "50%",
2901
+ background: "var(--wp-danger-bg-strong)",
2902
+ color: "var(--wp-danger)",
2903
+ display: "flex",
2904
+ alignItems: "center",
2905
+ justifyContent: "center",
2906
+ flexShrink: 0
2907
+ },
2908
+ mockInput: {
2909
+ fontSize: 11,
2910
+ padding: "4px 6px",
2911
+ border: "1px solid var(--wp-border-muted)",
2912
+ borderRadius: "var(--wp-radius)",
2913
+ background: "var(--wp-canvas)",
2914
+ color: "var(--wp-text)",
2915
+ outline: "none",
2916
+ width: "100%",
2917
+ boxSizing: "border-box"
2918
+ },
2919
+ mockCheckRow: {
2920
+ display: "flex",
2921
+ alignItems: "center",
2922
+ gap: 6,
2923
+ cursor: "pointer"
2924
+ },
2360
2925
  // Done screen
2361
2926
  doneScreen: {
2362
2927
  display: "flex",
@@ -2393,83 +2958,87 @@ var styles6 = {
2393
2958
  };
2394
2959
  function StepEditor() {
2395
2960
  const { schema, selectedStepId, updateStep, setStepCondition } = useBuilderStore();
2961
+ const readOnly = useBuilderReadOnly();
2396
2962
  const [conditionModalOpen, setConditionModalOpen] = react.useState(false);
2397
2963
  const step = schema.steps.find((s) => s.id === selectedStepId);
2398
2964
  if (!step) {
2399
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.empty, children: "Select a step to configure its properties." });
2965
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.empty, children: "Select a step to configure its properties." });
2400
2966
  }
2401
2967
  const hasCondition = !!step.visibleWhen;
2402
2968
  const ruleCount = step.visibleWhen?.rules.length ?? 0;
2403
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.container, children: [
2404
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.header, children: [
2405
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.headerTitle, children: "Step Config" }),
2406
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.stepId, children: [
2969
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.container, children: [
2970
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.header, children: [
2971
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.headerTitle, children: "Step Config" }),
2972
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.stepId, children: [
2407
2973
  "id: ",
2408
2974
  step.id
2409
2975
  ] })
2410
2976
  ] }),
2411
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.body, children: [
2412
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.group, children: [
2413
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles7.label, children: "Title" }),
2977
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.body, children: [
2978
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.group, children: [
2979
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles8.label, children: "Title" }),
2414
2980
  /* @__PURE__ */ jsxRuntime.jsx(
2415
2981
  "input",
2416
2982
  {
2417
- style: styles7.input,
2983
+ style: styles8.input,
2418
2984
  value: step.title,
2419
2985
  placeholder: "Step title",
2420
- onChange: (e) => updateStep(step.id, { title: e.target.value })
2986
+ readOnly,
2987
+ onChange: readOnly ? void 0 : (e) => updateStep(step.id, { title: e.target.value })
2421
2988
  }
2422
2989
  )
2423
2990
  ] }),
2424
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.group, children: [
2425
- /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles7.label, children: "URL" }),
2991
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.group, children: [
2992
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: styles8.label, children: "URL" }),
2426
2993
  /* @__PURE__ */ jsxRuntime.jsx(
2427
2994
  "input",
2428
2995
  {
2429
- style: styles7.input,
2996
+ style: styles8.input,
2430
2997
  value: step.url,
2431
2998
  placeholder: "/onboarding/step-name",
2432
- onChange: (e) => updateStep(step.id, { url: e.target.value })
2999
+ readOnly,
3000
+ onChange: readOnly ? void 0 : (e) => updateStep(step.id, { url: e.target.value })
2433
3001
  }
2434
3002
  ),
2435
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.hint, children: [
3003
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.hint, children: [
2436
3004
  "Supports ",
2437
3005
  "{{PARAM}}",
2438
3006
  " placeholders"
2439
3007
  ] })
2440
3008
  ] }),
2441
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.checkRow, children: [
3009
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.checkRow, children: [
2442
3010
  /* @__PURE__ */ jsxRuntime.jsx(
2443
3011
  "input",
2444
3012
  {
2445
3013
  type: "checkbox",
2446
3014
  id: `resume-${step.id}`,
2447
3015
  checked: !!step.enableResumeFromHere,
2448
- onChange: (e) => updateStep(step.id, { enableResumeFromHere: e.target.checked || void 0 })
3016
+ disabled: readOnly,
3017
+ onChange: readOnly ? void 0 : (e) => updateStep(step.id, { enableResumeFromHere: e.target.checked || void 0 })
2449
3018
  }
2450
3019
  ),
2451
- /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: `resume-${step.id}`, style: styles7.checkLabel, children: "Resume from this step" })
3020
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: `resume-${step.id}`, style: styles8.checkLabel, children: "Resume from this step" })
2452
3021
  ] }),
2453
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.divider }),
2454
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.conditionRow, children: [
2455
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.conditionInfo, children: [
2456
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.label, children: "Visibility condition" }),
2457
- hasCondition ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.conditionSummary, children: [
2458
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles7.conditionBadge, children: [
3022
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.divider }),
3023
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.conditionRow, children: [
3024
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.conditionInfo, children: [
3025
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.label, children: "Visibility condition" }),
3026
+ hasCondition ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.conditionSummary, children: [
3027
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles8.conditionBadge, children: [
2459
3028
  ruleCount,
2460
3029
  " rule",
2461
3030
  ruleCount !== 1 ? "s" : "",
2462
3031
  " \xB7 ",
2463
3032
  step.visibleWhen.combinator.toUpperCase()
2464
3033
  ] }),
2465
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles7.conditionDesc, children: "Step is conditional" })
2466
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.conditionNone, children: "Always visible" })
3034
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles8.conditionDesc, children: "Step is conditional" })
3035
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.conditionNone, children: "Always visible" })
2467
3036
  ] }),
2468
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles7.conditionActions, children: [
3037
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.conditionActions, children: [
2469
3038
  /* @__PURE__ */ jsxRuntime.jsx(
2470
3039
  "button",
2471
3040
  {
2472
- style: styles7.editConditionBtn,
3041
+ style: styles8.editConditionBtn,
2473
3042
  onClick: () => setConditionModalOpen(true),
2474
3043
  children: hasCondition ? "Edit" : "Add condition"
2475
3044
  }
@@ -2477,7 +3046,7 @@ function StepEditor() {
2477
3046
  hasCondition && /* @__PURE__ */ jsxRuntime.jsx(
2478
3047
  "button",
2479
3048
  {
2480
- style: styles7.clearConditionBtn,
3049
+ style: styles8.clearConditionBtn,
2481
3050
  onClick: () => setStepCondition(step.id, void 0),
2482
3051
  children: "Clear"
2483
3052
  }
@@ -2492,7 +3061,7 @@ function StepEditor() {
2492
3061
  onClose: () => setConditionModalOpen(false),
2493
3062
  width: 620,
2494
3063
  children: [
2495
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles7.modalHint, children: "Define when this step is visible. Leave empty to always show it." }),
3064
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: styles8.modalHint, children: "Define when this step is visible. Leave empty to always show it." }),
2496
3065
  /* @__PURE__ */ jsxRuntime.jsx(
2497
3066
  ConditionBuilder,
2498
3067
  {
@@ -2500,10 +3069,10 @@ function StepEditor() {
2500
3069
  onChange: (c) => setStepCondition(step.id, c)
2501
3070
  }
2502
3071
  ),
2503
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles7.modalFooter, children: /* @__PURE__ */ jsxRuntime.jsx(
3072
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.modalFooter, children: /* @__PURE__ */ jsxRuntime.jsx(
2504
3073
  "button",
2505
3074
  {
2506
- style: styles7.modalCloseBtn,
3075
+ style: styles8.modalCloseBtn,
2507
3076
  onClick: () => setConditionModalOpen(false),
2508
3077
  children: "Done"
2509
3078
  }
@@ -2513,7 +3082,7 @@ function StepEditor() {
2513
3082
  )
2514
3083
  ] });
2515
3084
  }
2516
- var styles7 = {
3085
+ var styles8 = {
2517
3086
  container: { display: "flex", flexDirection: "column", height: "100%", overflow: "hidden" },
2518
3087
  empty: {
2519
3088
  display: "flex",
@@ -2602,7 +3171,8 @@ var styles7 = {
2602
3171
  }
2603
3172
  };
2604
3173
  function StepList2() {
2605
- const { schema, selectedStepId, addStep, removeStep, selectStep, reorderSteps } = useBuilderStore();
3174
+ const { schema, selectedStepId, addStep, removeStep, duplicateStep, selectStep, reorderSteps } = useBuilderStore();
3175
+ const readOnly = useBuilderReadOnly();
2606
3176
  const [moveError, setMoveError] = react.useState(null);
2607
3177
  const steps = schema.steps;
2608
3178
  const deps = computeStepDependencies(schema);
@@ -2616,21 +3186,21 @@ function StepList2() {
2616
3186
  setMoveError(null);
2617
3187
  reorderSteps(fromIndex, toIndex);
2618
3188
  };
2619
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.container, children: [
2620
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.header, children: [
2621
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles8.title, children: [
3189
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.container, children: [
3190
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.header, children: [
3191
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: styles9.title, children: [
2622
3192
  "Steps (",
2623
3193
  steps.length,
2624
3194
  ")"
2625
3195
  ] }),
2626
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles8.addBtn, onClick: () => addStep(), children: "+ Add step" })
3196
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles9.addBtn, onClick: () => addStep(), children: "+ Add step" })
2627
3197
  ] }),
2628
- moveError && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.errorBanner, children: [
2629
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles8.errorIcon, children: "\u26A0" }),
3198
+ moveError && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.errorBanner, children: [
3199
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.errorIcon, children: "\u26A0" }),
2630
3200
  moveError
2631
3201
  ] }),
2632
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.list, children: [
2633
- steps.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.empty, children: 'No steps yet. Click "Add step" to start.' }),
3202
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.list, children: [
3203
+ steps.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.empty, children: 'No steps yet. Click "Add step" to start.' }),
2634
3204
  steps.map((step, index) => {
2635
3205
  const isSelected = step.id === selectedStepId;
2636
3206
  const hasCondition = !!step.visibleWhen;
@@ -2644,31 +3214,31 @@ function StepList2() {
2644
3214
  "div",
2645
3215
  {
2646
3216
  style: {
2647
- ...styles8.card,
2648
- ...isSelected ? styles8.cardSelected : {}
3217
+ ...styles9.card,
3218
+ ...isSelected ? styles9.cardSelected : {}
2649
3219
  },
2650
3220
  onClick: () => selectStep(step.id),
2651
3221
  children: [
2652
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.cardMain, children: [
2653
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.cardLeft, children: [
2654
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.cardIndex, children: index + 1 }),
3222
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.cardMain, children: [
3223
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.cardLeft, children: [
3224
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.cardIndex, children: index + 1 }),
2655
3225
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { minWidth: 0 }, children: [
2656
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles8.cardTitle, children: step.title }),
2657
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.cardMeta, children: [
3226
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.cardTitle, children: step.title }),
3227
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.cardMeta, children: [
2658
3228
  step.fields.length,
2659
3229
  " field",
2660
3230
  step.fields.length !== 1 ? "s" : "",
2661
- hasCondition && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles8.conditionBadge, children: "conditional" })
3231
+ hasCondition && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.conditionBadge, children: "conditional" })
2662
3232
  ] })
2663
3233
  ] })
2664
3234
  ] }),
2665
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.cardActions, children: [
3235
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.cardActions, children: [
2666
3236
  index > 0 && /* @__PURE__ */ jsxRuntime.jsx(
2667
3237
  "button",
2668
3238
  {
2669
3239
  style: {
2670
- ...styles8.iconBtn,
2671
- ...canMoveUp ? {} : styles8.iconBtnBlocked
3240
+ ...styles9.iconBtn,
3241
+ ...canMoveUp ? {} : styles9.iconBtnBlocked
2672
3242
  },
2673
3243
  title: canMoveUp ? "Move up" : `Can't move up \u2014 dependency order required`,
2674
3244
  onClick: (e) => {
@@ -2682,8 +3252,8 @@ function StepList2() {
2682
3252
  "button",
2683
3253
  {
2684
3254
  style: {
2685
- ...styles8.iconBtn,
2686
- ...canMoveDown ? {} : styles8.iconBtnBlocked
3255
+ ...styles9.iconBtn,
3256
+ ...canMoveDown ? {} : styles9.iconBtnBlocked
2687
3257
  },
2688
3258
  title: canMoveDown ? "Move down" : `Can't move down \u2014 dependency order required`,
2689
3259
  onClick: (e) => {
@@ -2696,7 +3266,19 @@ function StepList2() {
2696
3266
  /* @__PURE__ */ jsxRuntime.jsx(
2697
3267
  "button",
2698
3268
  {
2699
- style: { ...styles8.iconBtn, ...styles8.deleteBtn },
3269
+ style: styles9.iconBtn,
3270
+ title: "Duplicate step",
3271
+ onClick: (e) => {
3272
+ e.stopPropagation();
3273
+ duplicateStep(step.id);
3274
+ },
3275
+ children: "\u29C9"
3276
+ }
3277
+ ),
3278
+ /* @__PURE__ */ jsxRuntime.jsx(
3279
+ "button",
3280
+ {
3281
+ style: { ...styles9.iconBtn, ...styles9.deleteBtn },
2700
3282
  title: "Remove step",
2701
3283
  onClick: (e) => {
2702
3284
  e.stopPropagation();
@@ -2707,13 +3289,13 @@ function StepList2() {
2707
3289
  )
2708
3290
  ] })
2709
3291
  ] }),
2710
- depLabels.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.depRow, children: [
2711
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles8.depLabel, children: "needs:" }),
2712
- depLabels.map((label) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles8.depBadge, children: label }, label))
3292
+ depLabels.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.depRow, children: [
3293
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.depLabel, children: "needs:" }),
3294
+ depLabels.map((label) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.depBadge, children: label }, label))
2713
3295
  ] }),
2714
- dependents.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles8.depRow, children: [
2715
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles8.depLabelUsed, children: "used by:" }),
2716
- dependents.map((s) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles8.depBadgeUsed, children: s.title }, s.id))
3296
+ dependents.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.depRow, children: [
3297
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.depLabelUsed, children: "used by:" }),
3298
+ dependents.map((s) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.depBadgeUsed, children: s.title }, s.id))
2717
3299
  ] })
2718
3300
  ]
2719
3301
  },
@@ -2723,7 +3305,7 @@ function StepList2() {
2723
3305
  ] })
2724
3306
  ] });
2725
3307
  }
2726
- var styles8 = {
3308
+ var styles9 = {
2727
3309
  container: { display: "flex", flexDirection: "column", height: "100%" },
2728
3310
  header: {
2729
3311
  display: "flex",
@@ -2843,7 +3425,7 @@ var styles8 = {
2843
3425
  borderRadius: 4
2844
3426
  }
2845
3427
  };
2846
- function Toolbar({ onSave, previewMode, onTest }) {
3428
+ function Toolbar({ onSave, previewMode, onTest, isMobile, readOnly }) {
2847
3429
  const { schema, isDirty, resetSchema } = useBuilderStore();
2848
3430
  const handleExport = () => {
2849
3431
  const json = JSON.stringify(schema, null, 2);
@@ -2883,30 +3465,33 @@ ${result.errors.map((e2) => `\u2022 ${e2}`).join("\n")}`);
2883
3465
  input.click();
2884
3466
  };
2885
3467
  if (previewMode) {
2886
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.toolbar, children: [
2887
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.left, children: [
2888
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.logo, children: "\u25C8 waypoint" }),
2889
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.separator, children: "/" }),
3468
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles10.toolbar, children: [
3469
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles10.left, children: [
3470
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles10.logo, children: "\u25C8 waypoint" }),
3471
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles10.separator, children: "/" }),
2890
3472
  /* @__PURE__ */ jsxRuntime.jsx(
2891
3473
  "button",
2892
3474
  {
2893
- style: { ...styles9.btn, ...styles9.editBtn },
3475
+ style: { ...styles10.btn, ...styles10.editBtn },
2894
3476
  onClick: onTest,
2895
3477
  children: "\u2190 \xC9diter"
2896
3478
  }
2897
3479
  )
2898
3480
  ] }),
2899
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles9.right, children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "var(--wp-text-muted)", fontStyle: "italic" }, children: "Mode aper\xE7u" }) })
3481
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles10.right, children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: "var(--wp-text-muted)", fontStyle: "italic" }, children: "Mode aper\xE7u" }) })
2900
3482
  ] });
2901
3483
  }
2902
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.toolbar, children: [
2903
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.left, children: [
2904
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.logo, children: "\u25C8 waypoint" }),
2905
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.separator, children: "/" }),
3484
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles10.toolbar, children: [
3485
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles10.left, children: [
3486
+ !isMobile && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles10.logo, children: "\u25C8 waypoint" }),
3487
+ !isMobile && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles10.separator, children: "/" }),
2906
3488
  /* @__PURE__ */ jsxRuntime.jsx(
2907
3489
  "input",
2908
3490
  {
2909
- style: styles9.journeyName,
3491
+ style: {
3492
+ ...styles10.journeyName,
3493
+ ...isMobile ? { maxWidth: 120, fontSize: 12 } : {}
3494
+ },
2910
3495
  value: schema.name,
2911
3496
  placeholder: "Journey name",
2912
3497
  onChange: (e) => useBuilderStore.setState((s) => ({
@@ -2915,34 +3500,37 @@ ${result.errors.map((e2) => `\u2022 ${e2}`).join("\n")}`);
2915
3500
  }))
2916
3501
  }
2917
3502
  ),
2918
- isDirty && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles9.dirtyDot, title: "Unsaved changes" })
3503
+ isDirty && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles10.dirtyDot, title: "Unsaved changes" })
2919
3504
  ] }),
2920
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles9.right, children: [
2921
- onTest && /* @__PURE__ */ jsxRuntime.jsx("button", { style: { ...styles9.btn, ...styles9.testBtn }, onClick: onTest, children: "\u25B6 Tester" }),
2922
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles9.btn, onClick: handleImport, children: "Import" }),
2923
- /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles9.btn, onClick: handleExport, children: "Export JSON" }),
2924
- onSave && /* @__PURE__ */ jsxRuntime.jsx(
3505
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles10.right, children: [
3506
+ readOnly && /* @__PURE__ */ jsxRuntime.jsx("span", { style: styles10.readOnlyBadge, children: "View only" }),
3507
+ !readOnly && onTest && /* @__PURE__ */ jsxRuntime.jsx("button", { style: { ...styles10.btn, ...styles10.testBtn }, onClick: onTest, title: "Tester", children: isMobile ? "\u25B6" : "\u25B6 Tester" }),
3508
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles10.btn, onClick: handleImport, title: "Import", children: isMobile ? "\u2193" : "Import" }),
3509
+ /* @__PURE__ */ jsxRuntime.jsx("button", { style: styles10.btn, onClick: handleExport, title: "Export JSON", children: isMobile ? "\u2191" : "Export JSON" }),
3510
+ !readOnly && onSave && /* @__PURE__ */ jsxRuntime.jsx(
2925
3511
  "button",
2926
3512
  {
2927
- style: { ...styles9.btn, ...styles9.saveBtn },
3513
+ style: { ...styles10.btn, ...styles10.saveBtn },
2928
3514
  onClick: () => onSave(schema),
2929
- children: "Save"
3515
+ title: "Save",
3516
+ children: isMobile ? "\u2713" : "Save"
2930
3517
  }
2931
3518
  ),
2932
- /* @__PURE__ */ jsxRuntime.jsx(
3519
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
2933
3520
  "button",
2934
3521
  {
2935
- style: { ...styles9.btn, color: "var(--wp-danger)" },
3522
+ style: { ...styles10.btn, color: "var(--wp-danger)" },
3523
+ title: "Reset",
2936
3524
  onClick: () => {
2937
3525
  if (confirm("Reset the journey? All changes will be lost.")) resetSchema();
2938
3526
  },
2939
- children: "Reset"
3527
+ children: isMobile ? "\u27F3" : "Reset"
2940
3528
  }
2941
3529
  )
2942
3530
  ] })
2943
3531
  ] });
2944
3532
  }
2945
- var styles9 = {
3533
+ var styles10 = {
2946
3534
  toolbar: {
2947
3535
  display: "flex",
2948
3536
  alignItems: "center",
@@ -2984,6 +3572,15 @@ var styles9 = {
2984
3572
  fontWeight: 500
2985
3573
  },
2986
3574
  saveBtn: { background: "var(--wp-primary)", color: "var(--wp-canvas)", border: "1px solid var(--wp-primary)" },
3575
+ readOnlyBadge: {
3576
+ fontSize: 11,
3577
+ fontWeight: 600,
3578
+ color: "var(--wp-text-subtle)",
3579
+ background: "var(--wp-surface)",
3580
+ border: "1px solid var(--wp-border)",
3581
+ borderRadius: "var(--wp-radius)",
3582
+ padding: "3px 8px"
3583
+ },
2987
3584
  testBtn: { background: "var(--wp-success, #22c55e)", color: "#fff", border: "1px solid var(--wp-success, #22c55e)", fontWeight: 600 },
2988
3585
  editBtn: { background: "transparent", color: "var(--wp-toolbar-text)", border: "none", fontWeight: 600, cursor: "pointer", fontSize: 13, padding: "5px 0" }
2989
3586
  };
@@ -2993,32 +3590,51 @@ function WaypointBuilder({
2993
3590
  onSave,
2994
3591
  theme,
2995
3592
  className,
2996
- style
3593
+ style,
3594
+ readOnly = false,
3595
+ appCustomTypes = [],
3596
+ externalEnums = []
2997
3597
  }) {
2998
- const { loadSchema, schema } = useBuilderStore();
3598
+ const { loadSchema, schema, selectedStepId, selectedFieldId } = useBuilderStore();
2999
3599
  const [previewMode, setPreviewMode] = react.useState(false);
3600
+ const [isMobile, setIsMobile] = react.useState(false);
3601
+ const [activeTab, setActiveTab] = react.useState("steps");
3000
3602
  const previewStoreRef = react.useRef(null);
3001
3603
  if (previewStoreRef.current === null) {
3002
3604
  previewStoreRef.current = core.createRuntimeStore();
3003
3605
  }
3606
+ react.useEffect(() => {
3607
+ const check = () => setIsMobile(window.innerWidth < 640);
3608
+ check();
3609
+ window.addEventListener("resize", check);
3610
+ return () => window.removeEventListener("resize", check);
3611
+ }, []);
3004
3612
  react.useEffect(() => {
3005
3613
  if (defaultValue) loadSchema(defaultValue);
3006
3614
  }, []);
3007
3615
  react.useEffect(() => {
3008
3616
  onChange?.(schema);
3009
3617
  }, [schema, onChange]);
3618
+ react.useEffect(() => {
3619
+ if (isMobile && selectedStepId) setActiveTab("fields");
3620
+ }, [selectedStepId]);
3621
+ react.useEffect(() => {
3622
+ if (isMobile && selectedFieldId) setActiveTab("config");
3623
+ }, [selectedFieldId]);
3010
3624
  function handleTest() {
3011
3625
  previewStoreRef.current.getState().init(schema);
3012
3626
  setPreviewMode(true);
3013
3627
  }
3014
3628
  const themeVars = buildThemeVars(theme);
3015
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: { ...rootStyle, ...themeVars, ...style }, children: [
3629
+ return /* @__PURE__ */ jsxRuntime.jsx(BuilderReadOnlyContext.Provider, { value: readOnly, children: /* @__PURE__ */ jsxRuntime.jsx(BuilderCustomTypesContext.Provider, { value: appCustomTypes, children: /* @__PURE__ */ jsxRuntime.jsx(BuilderExternalEnumsContext.Provider, { value: externalEnums, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: { ...rootStyle, ...themeVars, ...style }, children: [
3016
3630
  /* @__PURE__ */ jsxRuntime.jsx(
3017
3631
  Toolbar,
3018
3632
  {
3019
- onSave: !previewMode && onSave ? () => onSave(schema) : void 0,
3633
+ isMobile,
3634
+ readOnly,
3635
+ onSave: !previewMode && !readOnly && onSave ? () => onSave(schema) : void 0,
3020
3636
  previewMode,
3021
- onTest: previewMode ? () => setPreviewMode(false) : handleTest
3637
+ onTest: !readOnly ? previewMode ? () => setPreviewMode(false) : handleTest : void 0
3022
3638
  }
3023
3639
  ),
3024
3640
  previewMode ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -3026,9 +3642,32 @@ function WaypointBuilder({
3026
3642
  {
3027
3643
  store: previewStoreRef.current,
3028
3644
  schema,
3645
+ externalEnums,
3029
3646
  onEdit: () => setPreviewMode(false)
3030
3647
  }
3031
- ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { style: layoutStyle, children: [
3648
+ ) : isMobile ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3649
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...colStyle, display: activeTab === "steps" ? "flex" : "none" }, children: [
3650
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, overflow: "hidden", display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsxRuntime.jsx(StepList2, {}) }),
3651
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { borderTop: "1px solid var(--wp-border)", flexShrink: 0, overflow: "auto", maxHeight: 200 }, children: /* @__PURE__ */ jsxRuntime.jsx(ExternalVariablePanel, {}) })
3652
+ ] }),
3653
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...colStyle, display: activeTab === "fields" ? "flex" : "none" }, children: /* @__PURE__ */ jsxRuntime.jsx(FieldList, {}) }),
3654
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...colStyle, display: activeTab === "config" ? "flex" : "none" }, children: [
3655
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, borderBottom: "1px solid var(--wp-border)", overflow: "hidden" }, children: /* @__PURE__ */ jsxRuntime.jsx(StepEditor, {}) }),
3656
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, overflow: "hidden" }, children: /* @__PURE__ */ jsxRuntime.jsx(FieldEditor, {}) })
3657
+ ] }),
3658
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: tabBarStyle, children: ["steps", "fields", "config"].map((tab) => /* @__PURE__ */ jsxRuntime.jsxs(
3659
+ "button",
3660
+ {
3661
+ style: { ...tabBtnStyle, ...activeTab === tab ? tabBtnActiveStyle : {} },
3662
+ onClick: () => setActiveTab(tab),
3663
+ children: [
3664
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 14 }, children: tab === "steps" ? "\u26A1" : tab === "fields" ? "\u229E" : "\u2699" }),
3665
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 11, fontWeight: 600, textTransform: "capitalize" }, children: tab })
3666
+ ]
3667
+ },
3668
+ tab
3669
+ )) })
3670
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { style: layoutStyle, children: [
3032
3671
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: colStyle, children: [
3033
3672
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, overflow: "hidden", display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsxRuntime.jsx(StepList2, {}) }),
3034
3673
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { borderTop: "1px solid var(--wp-border)", flexShrink: 0, overflow: "auto", maxHeight: 280 }, children: /* @__PURE__ */ jsxRuntime.jsx(ExternalVariablePanel, {}) })
@@ -3041,7 +3680,7 @@ function WaypointBuilder({
3041
3680
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, overflow: "hidden" }, children: /* @__PURE__ */ jsxRuntime.jsx(FieldEditor, {}) })
3042
3681
  ] })
3043
3682
  ] })
3044
- ] });
3683
+ ] }) }) }) });
3045
3684
  }
3046
3685
  var rootStyle = {
3047
3686
  display: "flex",
@@ -3070,6 +3709,29 @@ var dividerStyle = {
3070
3709
  background: "var(--wp-border)",
3071
3710
  flexShrink: 0
3072
3711
  };
3712
+ var tabBarStyle = {
3713
+ display: "flex",
3714
+ borderTop: "1px solid var(--wp-border)",
3715
+ background: "var(--wp-toolbar-bg)",
3716
+ flexShrink: 0
3717
+ };
3718
+ var tabBtnStyle = {
3719
+ flex: 1,
3720
+ display: "flex",
3721
+ flexDirection: "column",
3722
+ alignItems: "center",
3723
+ justifyContent: "center",
3724
+ gap: 3,
3725
+ padding: "8px 4px",
3726
+ border: "none",
3727
+ background: "transparent",
3728
+ color: "var(--wp-text-subtle)",
3729
+ cursor: "pointer"
3730
+ };
3731
+ var tabBtnActiveStyle = {
3732
+ color: "var(--wp-primary)",
3733
+ borderTop: "2px solid var(--wp-primary)"
3734
+ };
3073
3735
 
3074
3736
  exports.DARK_THEME = DARK_THEME;
3075
3737
  exports.DEFAULT_THEME = DEFAULT_THEME;