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