@squaredr/fieldcraft-pro 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,13 @@
1
1
  import { cn } from './chunk-QQ4JZGTD.mjs';
2
2
  import { requireLicense } from './chunk-VECQKSWS.mjs';
3
- import { forwardRef, createContext, Component, useRef, useState, useEffect, useCallback, useMemo, useContext } from 'react';
3
+ import { forwardRef, lazy, memo, createContext, Component, useState, useRef, useEffect, useCallback, useMemo, Suspense, useContext } from 'react';
4
4
  import { DndContext, DragOverlay, useSensors, useSensor, MouseSensor, TouchSensor, useDraggable, useDroppable } from '@dnd-kit/core';
5
- import { ChevronDown, Undo2, Redo2, Upload, Download, Save, X, PanelLeft, PanelRight, Search, ChevronRight, Plus, Settings, Copy, Trash2, HelpCircle, MoveVertical, Minus, Video, Image, FileText, PartyPopper, Hand, ShieldCheck, SeparatorHorizontal, Info, Heading, Trophy, EyeOff, Calculator, CreditCard, MapPin, Repeat, Grid3X3, Camera, PenTool, Paperclip, CalendarCheck, CalendarRange, Clock, Calendar, ArrowUpDown, Globe, ToggleLeft, CheckSquare, CircleDot, ListOrdered, TrendingUp, BarChart3, Star, SlidersHorizontal, Hash, UserCheck, Link, PhoneCall, Phone, Mail, AlignLeft, Type, GripVertical } from 'lucide-react';
6
- import { Button, Separator, Input, Badge, Textarea, Label, Switch } from '@squaredr/fieldcraft-react';
5
+ import { ChevronDown, Undo2, Redo2, PencilRuler, Eye, Code, LayoutTemplate, GitBranch, Upload, Download, Save, X, PanelLeft, PanelRight, Search, ChevronRight, Plus, Settings, FileText, Layers, Copy, Trash2, HelpCircle, MoveVertical, Minus, Video, Image, PartyPopper, Hand, ShieldCheck, SeparatorHorizontal, Info, Heading, Trophy, EyeOff, Calculator, CreditCard, MapPin, Repeat, Grid3X3, Camera, PenTool, Paperclip, CalendarCheck, CalendarRange, Clock, Calendar, ArrowUpDown, Globe, ToggleLeft, CheckSquare, CircleDot, ListOrdered, TrendingUp, BarChart3, Star, SlidersHorizontal, Hash, UserCheck, Link, PhoneCall, Phone, Mail, AlignLeft, Type, GripVertical } from 'lucide-react';
6
+ import { Button, Separator, FormEngineRenderer, Input, Badge, Textarea, Label, Switch } from '@squaredr/fieldcraft-react';
7
7
  import { validateSchema, FormEngineSchemaError } from '@squaredr/fieldcraft-core';
8
8
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
9
+ import { MarkerType, Handle, Position, getBezierPath, BaseEdge, EdgeLabelRenderer, useNodesState, useEdgesState, ReactFlow, Background, Controls, MiniMap } from '@xyflow/react';
10
+ import '@xyflow/react/dist/style.css';
9
11
 
10
12
  var MAX_HISTORY = 50;
11
13
  function useUndoRedo(currentSchema, setSchema) {
@@ -451,6 +453,14 @@ var QUESTION_TYPE_INFO = {
451
453
  icon: "Phone",
452
454
  description: "US phone number input"
453
455
  },
456
+ phone_international: {
457
+ type: "phone_international",
458
+ label: "Intl Phone",
459
+ category: "text",
460
+ icon: "Globe2",
461
+ description: "Phone with country code",
462
+ defaultConfig: { type: "phone_international", defaultCountry: "US" }
463
+ },
454
464
  url: {
455
465
  type: "url",
456
466
  label: "URL",
@@ -544,6 +554,14 @@ var QUESTION_TYPE_INFO = {
544
554
  requiresOptions: true,
545
555
  defaultConfig: { type: "ranking", items: [] }
546
556
  },
557
+ country_select: {
558
+ type: "country_select",
559
+ label: "Country",
560
+ category: "selection",
561
+ icon: "Globe",
562
+ description: "Country dropdown with search",
563
+ defaultConfig: { type: "country_select" }
564
+ },
547
565
  // ── Date/Time ──
548
566
  date: {
549
567
  type: "date",
@@ -570,7 +588,97 @@ var QUESTION_TYPE_INFO = {
570
588
  description: "Upload files",
571
589
  defaultConfig: { type: "file_upload", maxFiles: 1, maxSizeMb: 10 }
572
590
  },
591
+ // ── Compound ──
592
+ legal_name: {
593
+ type: "legal_name",
594
+ label: "Legal Name",
595
+ category: "text",
596
+ icon: "UserCheck",
597
+ description: "First, middle, and last name",
598
+ defaultConfig: { type: "legal_name" }
599
+ },
600
+ address: {
601
+ type: "address",
602
+ label: "Address",
603
+ category: "text",
604
+ icon: "MapPin",
605
+ description: "Street, city, state, ZIP",
606
+ defaultConfig: { type: "address", includeCountry: false }
607
+ },
608
+ signature: {
609
+ type: "signature",
610
+ label: "Signature",
611
+ category: "media",
612
+ icon: "PenTool",
613
+ description: "Canvas signature pad",
614
+ defaultConfig: { type: "signature" }
615
+ },
616
+ image_capture: {
617
+ type: "image_capture",
618
+ label: "Camera",
619
+ category: "media",
620
+ icon: "Camera",
621
+ description: "Capture photo from camera",
622
+ defaultConfig: { type: "image_capture", allowGallery: true }
623
+ },
624
+ date_range: {
625
+ type: "date_range",
626
+ label: "Date Range",
627
+ category: "datetime",
628
+ icon: "CalendarRange",
629
+ description: "Start and end date picker",
630
+ defaultConfig: { type: "date_range" }
631
+ },
632
+ appointment: {
633
+ type: "appointment",
634
+ label: "Appointment",
635
+ category: "datetime",
636
+ icon: "CalendarCheck",
637
+ description: "Date & time slot booking",
638
+ defaultConfig: { type: "appointment", duration: 30 }
639
+ },
573
640
  // ── Advanced ──
641
+ repeater: {
642
+ type: "repeater",
643
+ label: "Repeater",
644
+ category: "advanced",
645
+ icon: "ListPlus",
646
+ description: "Repeatable group of fields",
647
+ defaultConfig: { type: "repeater", fields: [], minItems: 1, maxItems: 10 }
648
+ },
649
+ likert: {
650
+ type: "likert",
651
+ label: "Likert Scale",
652
+ category: "advanced",
653
+ icon: "AlignHorizontalSpaceAround",
654
+ description: "Agreement scale (e.g. Strongly Disagree to Strongly Agree)",
655
+ requiresOptions: true,
656
+ defaultConfig: {
657
+ type: "likert",
658
+ scale: [
659
+ { label: "Strongly Disagree", value: "1" },
660
+ { label: "Disagree", value: "2" },
661
+ { label: "Neutral", value: "3" },
662
+ { label: "Agree", value: "4" },
663
+ { label: "Strongly Agree", value: "5" }
664
+ ]
665
+ }
666
+ },
667
+ scoring: {
668
+ type: "scoring",
669
+ label: "Scoring",
670
+ category: "advanced",
671
+ icon: "Award",
672
+ description: "Scored question with point values per option",
673
+ requiresOptions: true,
674
+ defaultConfig: {
675
+ type: "scoring",
676
+ options: [
677
+ { label: "Option 1", value: "1", score: 1 },
678
+ { label: "Option 2", value: "2", score: 2 }
679
+ ]
680
+ }
681
+ },
574
682
  matrix: {
575
683
  type: "matrix",
576
684
  label: "Matrix",
@@ -600,6 +708,14 @@ var QUESTION_TYPE_INFO = {
600
708
  description: "Hidden value from URL or static",
601
709
  defaultConfig: { type: "hidden", source: "static" }
602
710
  },
711
+ payment: {
712
+ type: "payment",
713
+ label: "Payment",
714
+ category: "advanced",
715
+ icon: "CreditCard",
716
+ description: "Collect payment via Stripe",
717
+ defaultConfig: { type: "payment", provider: "stripe", publicKey: "", currency: "USD" }
718
+ },
603
719
  // ── Structural ──
604
720
  section_header: {
605
721
  type: "section_header",
@@ -704,7 +820,7 @@ var DEFAULT_PALETTE = [
704
820
  {
705
821
  category: "text",
706
822
  label: "Text Input",
707
- types: ["short_text", "long_text", "email", "phone", "url"]
823
+ types: ["short_text", "long_text", "email", "phone", "phone_international", "url", "legal_name", "address"]
708
824
  },
709
825
  {
710
826
  category: "numeric",
@@ -714,17 +830,17 @@ var DEFAULT_PALETTE = [
714
830
  {
715
831
  category: "selection",
716
832
  label: "Selection",
717
- types: ["single_select", "multi_select", "dropdown", "boolean", "ranking"]
833
+ types: ["single_select", "multi_select", "dropdown", "boolean", "ranking", "country_select"]
718
834
  },
719
835
  {
720
836
  category: "datetime",
721
837
  label: "Date & Time",
722
- types: ["date", "time"]
838
+ types: ["date", "time", "date_range", "appointment"]
723
839
  },
724
840
  {
725
841
  category: "media",
726
842
  label: "Media",
727
- types: ["file_upload"]
843
+ types: ["file_upload", "signature", "image_capture"]
728
844
  },
729
845
  {
730
846
  category: "content",
@@ -739,7 +855,7 @@ var DEFAULT_PALETTE = [
739
855
  {
740
856
  category: "advanced",
741
857
  label: "Advanced",
742
- types: ["matrix", "calculated", "hidden"]
858
+ types: ["repeater", "likert", "scoring", "matrix", "calculated", "hidden", "payment"]
743
859
  }
744
860
  ];
745
861
 
@@ -2253,7 +2369,8 @@ function getFieldOptions(schema, excludeId) {
2253
2369
  return fields;
2254
2370
  }
2255
2371
  function ConditionEditor({ question, schema, onUpdate }) {
2256
- const showIf = question.showIf;
2372
+ const rawShowIf = question.showIf;
2373
+ const showIf = rawShowIf && rawShowIf.field && !rawShowIf.conditions ? { combine: "AND", conditions: [rawShowIf] } : rawShowIf;
2257
2374
  const fieldOptions = getFieldOptions(schema, question.id);
2258
2375
  const updateShowIf = (next) => {
2259
2376
  onUpdate({ showIf: next });
@@ -2375,6 +2492,24 @@ function ConditionEditor({ question, schema, onUpdate }) {
2375
2492
  }
2376
2493
  function FormSettingsPanel({ schema, onUpdate }) {
2377
2494
  const settings = schema.settings ?? {};
2495
+ const hasConditions = schema.sections.some(
2496
+ (s) => s.showIf || s.questions.some((q) => q.showIf)
2497
+ );
2498
+ const displayModeOptions = hasConditions ? [
2499
+ { label: "Stepped", value: "stepped" },
2500
+ { label: "Conversational", value: "conversational" }
2501
+ ] : [
2502
+ { label: "Stepped", value: "stepped" },
2503
+ { label: "Classic", value: "classic" },
2504
+ { label: "Conversational", value: "conversational" }
2505
+ ];
2506
+ const displayMode = settings.displayMode ?? "stepped";
2507
+ const effectiveDisplayMode = hasConditions && displayMode === "classic" ? "stepped" : displayMode;
2508
+ useEffect(() => {
2509
+ if (effectiveDisplayMode !== displayMode) {
2510
+ onUpdate({ ...schema, settings: { ...settings, displayMode: effectiveDisplayMode } });
2511
+ }
2512
+ }, [effectiveDisplayMode, displayMode]);
2378
2513
  const updateSettings = (updates) => {
2379
2514
  onUpdate({ ...schema, settings: { ...settings, ...updates } });
2380
2515
  };
@@ -2402,12 +2537,8 @@ function FormSettingsPanel({ schema, onUpdate }) {
2402
2537
  SettingsSelect,
2403
2538
  {
2404
2539
  label: "Mode",
2405
- value: settings.displayMode ?? "classic",
2406
- options: [
2407
- { label: "Classic", value: "classic" },
2408
- { label: "Stepped", value: "stepped" },
2409
- { label: "Conversational", value: "conversational" }
2410
- ],
2540
+ value: effectiveDisplayMode,
2541
+ options: displayModeOptions,
2411
2542
  onChange: (v) => updateSettings({ displayMode: v })
2412
2543
  }
2413
2544
  ),
@@ -2785,6 +2916,873 @@ function FieldGroup({ label, children }) {
2785
2916
  children
2786
2917
  ] });
2787
2918
  }
2919
+ var PreviewErrorBoundary = class extends Component {
2920
+ state = { error: null };
2921
+ static getDerivedStateFromError(error) {
2922
+ return { error };
2923
+ }
2924
+ componentDidCatch(error, info) {
2925
+ console.error("[FormBuilder Preview]", error, info);
2926
+ }
2927
+ componentDidUpdate(prevProps) {
2928
+ if (prevProps.resetKey !== this.props.resetKey && this.state.error) {
2929
+ this.setState({ error: null });
2930
+ }
2931
+ }
2932
+ render() {
2933
+ if (this.state.error) {
2934
+ return /* @__PURE__ */ jsxs("div", { className: "p-4 text-destructive text-sm font-mono", children: [
2935
+ /* @__PURE__ */ jsx("strong", { children: "Render Error:" }),
2936
+ " ",
2937
+ this.state.error.message
2938
+ ] });
2939
+ }
2940
+ return this.props.children;
2941
+ }
2942
+ };
2943
+ function FormPreviewPanel({ schema, preview }) {
2944
+ const hasSections = schema.sections && schema.sections.length > 0;
2945
+ const hasFields = hasSections && schema.sections.some((s) => s.questions.length > 0);
2946
+ if (!hasFields) {
2947
+ return /* @__PURE__ */ jsx("div", { className: "flex-1 flex items-center justify-center text-muted-foreground text-sm", children: /* @__PURE__ */ jsxs("div", { className: "text-center space-y-2", children: [
2948
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: "No fields to preview" }),
2949
+ /* @__PURE__ */ jsx("p", { className: "text-xs", children: "Add fields in the Design view to see a live preview here." })
2950
+ ] }) });
2951
+ }
2952
+ return /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto", children: /* @__PURE__ */ jsx("div", { className: "max-w-2xl mx-auto py-8 px-4", children: /* @__PURE__ */ jsx(PreviewErrorBoundary, { resetKey: schema.id + (schema.version ?? ""), children: /* @__PURE__ */ jsx(
2953
+ FormEngineRenderer,
2954
+ {
2955
+ schema,
2956
+ theme: preview?.theme,
2957
+ components: preview?.components,
2958
+ onReady: preview?.onReady,
2959
+ onFieldChange: preview?.onFieldChange,
2960
+ onSubmit: preview?.onSubmit ?? (() => {
2961
+ })
2962
+ }
2963
+ ) }) }) });
2964
+ }
2965
+
2966
+ // src/form-builder/utils/validation-markers.ts
2967
+ var MarkerSeverity = {
2968
+ Error: 8};
2969
+ function errorsToMarkers(errors) {
2970
+ return errors.map((error) => ({
2971
+ severity: MarkerSeverity.Error,
2972
+ message: error.message,
2973
+ startLineNumber: error.line ?? 1,
2974
+ startColumn: error.column ?? 1,
2975
+ endLineNumber: error.endLine ?? error.line ?? 1,
2976
+ endColumn: error.endColumn ?? error.column ?? 1,
2977
+ source: "FieldCraft Schema Validator"
2978
+ }));
2979
+ }
2980
+ var Editor = lazy(() => import('@monaco-editor/react').then((m) => ({ default: m.default })));
2981
+ function MonacoWrapper({ value, onChange, errors }) {
2982
+ const editorRef = useRef(null);
2983
+ const monacoRef = useRef(null);
2984
+ const handleMount = (editor, monaco) => {
2985
+ editorRef.current = editor;
2986
+ monacoRef.current = monaco;
2987
+ };
2988
+ useEffect(() => {
2989
+ const editor = editorRef.current;
2990
+ const monaco = monacoRef.current;
2991
+ if (!editor || !monaco) return;
2992
+ const model = editor.getModel();
2993
+ if (!model) return;
2994
+ const markers = errorsToMarkers(errors);
2995
+ monaco.editor.setModelMarkers(model, "fieldcraft-schema", markers);
2996
+ }, [errors]);
2997
+ return /* @__PURE__ */ jsx(
2998
+ Editor,
2999
+ {
3000
+ height: "100%",
3001
+ language: "json",
3002
+ theme: "vs-dark",
3003
+ value,
3004
+ onChange: (val) => onChange(val ?? ""),
3005
+ onMount: handleMount,
3006
+ options: {
3007
+ minimap: { enabled: false },
3008
+ fontSize: 13,
3009
+ wordWrap: "on",
3010
+ formatOnPaste: true,
3011
+ automaticLayout: true,
3012
+ tabSize: 2,
3013
+ scrollBeyondLastLine: false,
3014
+ padding: { top: 12 }
3015
+ }
3016
+ }
3017
+ );
3018
+ }
3019
+ function JsonEditorPanel({ value, onChange, errors, onValidate }) {
3020
+ const handleChange = useCallback(
3021
+ (newValue) => {
3022
+ onChange(newValue);
3023
+ onValidate(newValue);
3024
+ },
3025
+ [onChange, onValidate]
3026
+ );
3027
+ const handleFormat = useCallback(() => {
3028
+ try {
3029
+ const formatted = JSON.stringify(JSON.parse(value), null, 2);
3030
+ onChange(formatted);
3031
+ } catch {
3032
+ }
3033
+ }, [value, onChange]);
3034
+ return /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col overflow-hidden", children: [
3035
+ errors.length > 0 && /* @__PURE__ */ jsxs("div", { className: "shrink-0 px-3 py-1.5 bg-destructive/10 border-b border-destructive/20 text-destructive text-xs font-mono overflow-x-auto", children: [
3036
+ errors.slice(0, 3).map((e, i) => /* @__PURE__ */ jsxs("div", { children: [
3037
+ e.line ? `Line ${e.line}: ` : "",
3038
+ e.path ? `${e.path}: ` : "",
3039
+ e.message
3040
+ ] }, i)),
3041
+ errors.length > 3 && /* @__PURE__ */ jsxs("div", { className: "text-muted-foreground", children: [
3042
+ "...and ",
3043
+ errors.length - 3,
3044
+ " more"
3045
+ ] })
3046
+ ] }),
3047
+ /* @__PURE__ */ jsxs("div", { className: "shrink-0 flex items-center justify-between px-3 py-1.5 bg-card border-b border-border text-xs", children: [
3048
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: errors.length === 0 ? "Valid schema" : `${errors.length} error${errors.length !== 1 ? "s" : ""}` }),
3049
+ /* @__PURE__ */ jsx(
3050
+ "button",
3051
+ {
3052
+ type: "button",
3053
+ onClick: handleFormat,
3054
+ className: "text-primary hover:underline",
3055
+ children: "Format JSON"
3056
+ }
3057
+ )
3058
+ ] }),
3059
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsx(
3060
+ Suspense,
3061
+ {
3062
+ fallback: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full text-sm text-muted-foreground", children: "Loading editor..." }),
3063
+ children: /* @__PURE__ */ jsx(MonacoWrapper, { value, onChange: handleChange, errors })
3064
+ }
3065
+ ) })
3066
+ ] });
3067
+ }
3068
+
3069
+ // src/form-builder/utils/logic-graph.ts
3070
+ function extractFieldRefs(expr) {
3071
+ const refs = [];
3072
+ if (expr.field) {
3073
+ refs.push({ field: expr.field, operator: expr.operator, value: expr.value });
3074
+ }
3075
+ if (expr.conditions) {
3076
+ for (const child of expr.conditions) {
3077
+ refs.push(...extractFieldRefs(child));
3078
+ }
3079
+ }
3080
+ return refs;
3081
+ }
3082
+ function buildLogicGraph(schema) {
3083
+ const nodes = [];
3084
+ const edges = [];
3085
+ const nodeIds = /* @__PURE__ */ new Set();
3086
+ for (const section of schema.sections) {
3087
+ nodes.push({
3088
+ id: section.id,
3089
+ label: section.title || "Untitled Section",
3090
+ type: "section",
3091
+ sectionId: section.id,
3092
+ sectionTitle: section.title || "Untitled Section",
3093
+ hasCondition: !!section.showIf
3094
+ });
3095
+ nodeIds.add(section.id);
3096
+ if (section.showIf) {
3097
+ const refs = extractFieldRefs(section.showIf);
3098
+ for (const ref of refs) {
3099
+ edges.push({ from: ref.field, to: section.id, edgeType: "showIf", operator: ref.operator, value: ref.value });
3100
+ }
3101
+ }
3102
+ if (section.onExit) {
3103
+ for (const rule of section.onExit.rules) {
3104
+ if (rule.jumpTo) {
3105
+ edges.push({ from: section.id, to: rule.jumpTo, edgeType: "onExit", jumpTo: rule.jumpTo });
3106
+ }
3107
+ }
3108
+ if (section.onExit.default) {
3109
+ edges.push({ from: section.id, to: section.onExit.default, edgeType: "onExit", jumpTo: section.onExit.default });
3110
+ }
3111
+ }
3112
+ for (const question of section.questions) {
3113
+ nodes.push({
3114
+ id: question.id,
3115
+ label: question.label || question.id,
3116
+ type: "field",
3117
+ fieldType: question.type,
3118
+ sectionId: section.id,
3119
+ sectionTitle: section.title || "Untitled Section",
3120
+ hasCondition: !!question.showIf
3121
+ });
3122
+ nodeIds.add(question.id);
3123
+ if (question.showIf) {
3124
+ const refs = extractFieldRefs(question.showIf);
3125
+ for (const ref of refs) {
3126
+ edges.push({ from: ref.field, to: question.id, edgeType: "showIf", operator: ref.operator, value: ref.value });
3127
+ }
3128
+ }
3129
+ }
3130
+ }
3131
+ const validEdges = edges.filter((e) => nodeIds.has(e.from) && nodeIds.has(e.to));
3132
+ return { nodes, edges: validEdges };
3133
+ }
3134
+ var SECTION_COLORS = [
3135
+ "#3b82f6",
3136
+ // blue
3137
+ "#8b5cf6",
3138
+ // violet
3139
+ "#06b6d4",
3140
+ // cyan
3141
+ "#f59e0b",
3142
+ // amber
3143
+ "#10b981",
3144
+ // emerald
3145
+ "#ef4444",
3146
+ // red
3147
+ "#ec4899",
3148
+ // pink
3149
+ "#6366f1"
3150
+ // indigo
3151
+ ];
3152
+ var FIELD_NODE_WIDTH = 220;
3153
+ var FIELD_NODE_HEIGHT = 52;
3154
+ var SECTION_HEADER_HEIGHT = 40;
3155
+ var SECTION_PADDING_X = 16;
3156
+ var SECTION_PADDING_TOP = 12;
3157
+ var SECTION_PADDING_BOTTOM = 16;
3158
+ var FIELD_GAP = 10;
3159
+ var SECTION_GAP = 80;
3160
+ function formatEdgeLabel(edge) {
3161
+ if (edge.edgeType === "onExit") return "jump";
3162
+ if (!edge.operator) return "";
3163
+ const op = edge.operator;
3164
+ const val = edge.value;
3165
+ if (op === "exists") return "has value";
3166
+ if (op === "notExists") return "is empty";
3167
+ if (val === void 0 || val === null || val === "") return op;
3168
+ const short = String(val).length > 12 ? String(val).slice(0, 12) + "\u2026" : String(val);
3169
+ return `${op} ${short}`;
3170
+ }
3171
+ function buildReactFlowGraph(schema) {
3172
+ const graph = buildLogicGraph(schema);
3173
+ const nodes = [];
3174
+ const edges = [];
3175
+ let sectionX = 0;
3176
+ let maxSectionHeight = 0;
3177
+ for (const section of schema.sections) {
3178
+ const fieldCount = section.questions.length;
3179
+ const contentHeight = SECTION_HEADER_HEIGHT + SECTION_PADDING_TOP + fieldCount * FIELD_NODE_HEIGHT + Math.max(0, fieldCount - 1) * FIELD_GAP + SECTION_PADDING_BOTTOM;
3180
+ if (contentHeight > maxSectionHeight) maxSectionHeight = contentHeight;
3181
+ }
3182
+ if (maxSectionHeight < SECTION_HEADER_HEIGHT + SECTION_PADDING_TOP + SECTION_PADDING_BOTTOM + FIELD_NODE_HEIGHT) {
3183
+ maxSectionHeight = SECTION_HEADER_HEIGHT + SECTION_PADDING_TOP + SECTION_PADDING_BOTTOM + FIELD_NODE_HEIGHT;
3184
+ }
3185
+ const sectionWidth = FIELD_NODE_WIDTH + SECTION_PADDING_X * 2;
3186
+ for (let sIdx = 0; sIdx < schema.sections.length; sIdx++) {
3187
+ const section = schema.sections[sIdx];
3188
+ const colorIndex = sIdx;
3189
+ nodes.push({
3190
+ id: section.id,
3191
+ type: "sectionNode",
3192
+ position: { x: sectionX, y: 0 },
3193
+ data: {
3194
+ label: section.title || "Untitled Section",
3195
+ sectionId: section.id,
3196
+ fieldCount: section.questions.length,
3197
+ hasCondition: !!section.showIf,
3198
+ hasExitLogic: !!section.onExit,
3199
+ colorIndex
3200
+ },
3201
+ style: { width: sectionWidth, height: maxSectionHeight }
3202
+ });
3203
+ for (let fIdx = 0; fIdx < section.questions.length; fIdx++) {
3204
+ const question = section.questions[fIdx];
3205
+ const fieldY = SECTION_HEADER_HEIGHT + SECTION_PADDING_TOP + fIdx * (FIELD_NODE_HEIGHT + FIELD_GAP);
3206
+ nodes.push({
3207
+ id: question.id,
3208
+ type: "fieldNode",
3209
+ position: { x: SECTION_PADDING_X, y: fieldY },
3210
+ parentId: section.id,
3211
+ extent: "parent",
3212
+ data: {
3213
+ label: question.label || question.id,
3214
+ fieldType: question.type,
3215
+ sectionId: section.id,
3216
+ hasCondition: !!question.showIf,
3217
+ colorIndex
3218
+ },
3219
+ style: { width: FIELD_NODE_WIDTH }
3220
+ });
3221
+ }
3222
+ if (sIdx < schema.sections.length - 1) {
3223
+ const nextSection = schema.sections[sIdx + 1];
3224
+ edges.push({
3225
+ id: `pipeline-${section.id}-${nextSection.id}`,
3226
+ source: section.id,
3227
+ target: nextSection.id,
3228
+ type: "pipelineEdge",
3229
+ data: { edgeType: "pipeline" }
3230
+ });
3231
+ }
3232
+ sectionX += sectionWidth + SECTION_GAP;
3233
+ }
3234
+ for (let i = 0; i < graph.edges.length; i++) {
3235
+ const edge = graph.edges[i];
3236
+ edges.push({
3237
+ id: `e-${edge.from}-${edge.to}-${i}`,
3238
+ source: edge.from,
3239
+ target: edge.to,
3240
+ type: "conditionEdge",
3241
+ animated: edge.edgeType === "showIf",
3242
+ style: edge.edgeType === "onExit" ? { strokeDasharray: "6 3", stroke: "#f59e0b" } : void 0,
3243
+ label: formatEdgeLabel(edge),
3244
+ data: {
3245
+ edgeType: edge.edgeType,
3246
+ operator: edge.operator,
3247
+ value: edge.value,
3248
+ sourceId: edge.from,
3249
+ targetId: edge.to
3250
+ }
3251
+ });
3252
+ }
3253
+ return { nodes, edges };
3254
+ }
3255
+ function SectionNodeInner({ data }) {
3256
+ const d = data;
3257
+ const color = SECTION_COLORS[d.colorIndex % SECTION_COLORS.length];
3258
+ return /* @__PURE__ */ jsxs("div", { className: "fc-flow-section-group", style: { borderColor: color }, children: [
3259
+ /* @__PURE__ */ jsx(Handle, { type: "target", position: Position.Left, className: "fc-flow-handle fc-flow-handle--section" }),
3260
+ /* @__PURE__ */ jsxs("div", { className: "fc-flow-section-group__header", style: { background: color }, children: [
3261
+ /* @__PURE__ */ jsx("span", { className: "fc-flow-section-group__label", children: d.label.length > 30 ? d.label.slice(0, 30) + "\u2026" : d.label }),
3262
+ /* @__PURE__ */ jsxs("span", { className: "fc-flow-section-group__meta", children: [
3263
+ d.fieldCount,
3264
+ " field",
3265
+ d.fieldCount !== 1 ? "s" : "",
3266
+ d.hasCondition && /* @__PURE__ */ jsx("span", { className: "fc-flow-section-group__badge", title: "Has showIf condition", children: "?" }),
3267
+ d.hasExitLogic && /* @__PURE__ */ jsx("span", { className: "fc-flow-section-group__badge fc-flow-section-group__badge--jump", title: "Has onExit jump", children: "\u2934" })
3268
+ ] })
3269
+ ] }),
3270
+ /* @__PURE__ */ jsx(Handle, { type: "source", position: Position.Right, className: "fc-flow-handle fc-flow-handle--section" })
3271
+ ] });
3272
+ }
3273
+ var SectionNode = memo(SectionNodeInner);
3274
+ function FieldNodeInner({ data }) {
3275
+ const d = data;
3276
+ const color = SECTION_COLORS[d.colorIndex % SECTION_COLORS.length];
3277
+ return /* @__PURE__ */ jsxs(
3278
+ "div",
3279
+ {
3280
+ className: "fc-flow-field",
3281
+ style: {
3282
+ borderColor: d.hasCondition ? color : void 0,
3283
+ borderWidth: d.hasCondition ? 2 : 1
3284
+ },
3285
+ children: [
3286
+ /* @__PURE__ */ jsx(Handle, { type: "target", position: Position.Left, className: "fc-flow-handle" }),
3287
+ /* @__PURE__ */ jsxs("div", { className: "fc-flow-field__content", children: [
3288
+ /* @__PURE__ */ jsx("span", { className: "fc-flow-field__label", children: d.label.length > 26 ? d.label.slice(0, 26) + "\u2026" : d.label }),
3289
+ /* @__PURE__ */ jsx("span", { className: "fc-flow-field__type", children: d.fieldType })
3290
+ ] }),
3291
+ d.hasCondition && /* @__PURE__ */ jsx("div", { className: "fc-flow-badge", style: { background: color }, title: "Has showIf condition", children: "?" }),
3292
+ /* @__PURE__ */ jsx(Handle, { type: "source", position: Position.Right, className: "fc-flow-handle" })
3293
+ ]
3294
+ }
3295
+ );
3296
+ }
3297
+ var FieldNode = memo(FieldNodeInner);
3298
+ var logicFlowNodeTypes = {
3299
+ sectionNode: SectionNode,
3300
+ fieldNode: FieldNode
3301
+ };
3302
+ function ConditionEdgeInner({
3303
+ id,
3304
+ sourceX,
3305
+ sourceY,
3306
+ targetX,
3307
+ targetY,
3308
+ sourcePosition,
3309
+ targetPosition,
3310
+ label,
3311
+ style,
3312
+ data,
3313
+ markerEnd
3314
+ }) {
3315
+ const [edgePath, labelX, labelY] = getBezierPath({
3316
+ sourceX,
3317
+ sourceY,
3318
+ sourcePosition,
3319
+ targetX,
3320
+ targetY,
3321
+ targetPosition
3322
+ });
3323
+ const isJump = data?.edgeType === "onExit";
3324
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3325
+ /* @__PURE__ */ jsx(
3326
+ BaseEdge,
3327
+ {
3328
+ id,
3329
+ path: edgePath,
3330
+ markerEnd,
3331
+ style: {
3332
+ stroke: isJump ? "#f59e0b" : "var(--primary, #3b82f6)",
3333
+ strokeWidth: 1.5,
3334
+ strokeOpacity: 0.7,
3335
+ ...style
3336
+ }
3337
+ }
3338
+ ),
3339
+ label && /* @__PURE__ */ jsx(EdgeLabelRenderer, { children: /* @__PURE__ */ jsx(
3340
+ "div",
3341
+ {
3342
+ className: "fc-flow-edge-label",
3343
+ style: {
3344
+ transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
3345
+ background: isJump ? "#f59e0b" : "var(--primary, #3b82f6)"
3346
+ },
3347
+ children: label
3348
+ }
3349
+ ) })
3350
+ ] });
3351
+ }
3352
+ var ConditionEdge = memo(ConditionEdgeInner);
3353
+ function PipelineEdgeInner({
3354
+ id,
3355
+ sourceX,
3356
+ sourceY,
3357
+ targetX,
3358
+ targetY,
3359
+ sourcePosition,
3360
+ targetPosition,
3361
+ markerEnd
3362
+ }) {
3363
+ const [edgePath] = getBezierPath({
3364
+ sourceX,
3365
+ sourceY,
3366
+ sourcePosition,
3367
+ targetX,
3368
+ targetY,
3369
+ targetPosition
3370
+ });
3371
+ return /* @__PURE__ */ jsx(
3372
+ BaseEdge,
3373
+ {
3374
+ id,
3375
+ path: edgePath,
3376
+ markerEnd,
3377
+ style: {
3378
+ stroke: "var(--muted-foreground, #71717a)",
3379
+ strokeWidth: 2,
3380
+ strokeOpacity: 0.4
3381
+ }
3382
+ }
3383
+ );
3384
+ }
3385
+ var PipelineEdge = memo(PipelineEdgeInner);
3386
+ var logicFlowEdgeTypes = {
3387
+ conditionEdge: ConditionEdge,
3388
+ pipelineEdge: PipelineEdge
3389
+ };
3390
+ function findQuestion2(schema, fieldId) {
3391
+ for (const section of schema.sections) {
3392
+ for (const q of section.questions) {
3393
+ if (q.id === fieldId) return q;
3394
+ }
3395
+ }
3396
+ return void 0;
3397
+ }
3398
+ function updateQuestionInSchema(schema, fieldId, updates) {
3399
+ return {
3400
+ ...schema,
3401
+ sections: schema.sections.map((section) => ({
3402
+ ...section,
3403
+ questions: section.questions.map(
3404
+ (q) => q.id === fieldId ? { ...q, ...updates } : q
3405
+ )
3406
+ }))
3407
+ };
3408
+ }
3409
+ function removeConditionForSource(showIf, sourceFieldId) {
3410
+ if (!showIf) return void 0;
3411
+ if (showIf.field === sourceFieldId) return void 0;
3412
+ if (showIf.field && showIf.field !== sourceFieldId) return showIf;
3413
+ if (showIf.conditions) {
3414
+ const filtered = showIf.conditions.map((c) => removeConditionForSource(c, sourceFieldId)).filter((c) => c !== void 0);
3415
+ if (filtered.length === 0) return void 0;
3416
+ if (filtered.length === 1) return filtered[0];
3417
+ return { ...showIf, conditions: filtered };
3418
+ }
3419
+ return showIf;
3420
+ }
3421
+ var defaultEdgeOptions = {
3422
+ markerEnd: {
3423
+ type: MarkerType.ArrowClosed,
3424
+ width: 16,
3425
+ height: 12
3426
+ }
3427
+ };
3428
+ function LogicFlowEditor({ schema, onChange, onClose }) {
3429
+ const noSections = schema.sections.length === 0;
3430
+ const flowGraph = useMemo(() => buildReactFlowGraph(schema), [schema]);
3431
+ const [nodes, setNodes, onNodesChange] = useNodesState(flowGraph.nodes);
3432
+ const [edges, setEdges, onEdgesChange] = useEdgesState(flowGraph.edges);
3433
+ const [selectedFieldId, setSelectedFieldId] = useState(null);
3434
+ const selectedQuestion = selectedFieldId ? findQuestion2(schema, selectedFieldId) : void 0;
3435
+ useEffect(() => {
3436
+ setNodes(flowGraph.nodes);
3437
+ setEdges(flowGraph.edges);
3438
+ }, [flowGraph, setNodes, setEdges]);
3439
+ const sectionIds = useMemo(() => {
3440
+ const ids = [];
3441
+ for (const section of schema.sections) {
3442
+ ids.push(section.id);
3443
+ }
3444
+ return ids;
3445
+ }, [schema]);
3446
+ const onNodeClick = useCallback(
3447
+ (_event, node) => {
3448
+ if (node.type === "fieldNode") {
3449
+ setSelectedFieldId(node.id);
3450
+ }
3451
+ },
3452
+ []
3453
+ );
3454
+ const onConnect = useCallback(
3455
+ (connection) => {
3456
+ if (!connection.source || !connection.target) return;
3457
+ const targetQuestion = findQuestion2(schema, connection.target);
3458
+ if (!targetQuestion) return;
3459
+ const newCondition = {
3460
+ field: connection.source,
3461
+ operator: "exists"
3462
+ };
3463
+ let updatedShowIf;
3464
+ const existing = targetQuestion.showIf;
3465
+ if (!existing) {
3466
+ updatedShowIf = { combine: "AND", conditions: [newCondition] };
3467
+ } else {
3468
+ updatedShowIf = {
3469
+ ...existing,
3470
+ conditions: [...existing.conditions ?? [], newCondition]
3471
+ };
3472
+ }
3473
+ const updatedSchema = updateQuestionInSchema(schema, connection.target, {
3474
+ showIf: updatedShowIf
3475
+ });
3476
+ onChange(updatedSchema);
3477
+ setSelectedFieldId(connection.target);
3478
+ },
3479
+ [schema, onChange]
3480
+ );
3481
+ const onEdgesDelete = useCallback(
3482
+ (deletedEdges) => {
3483
+ let updatedSchema = schema;
3484
+ for (const edge of deletedEdges) {
3485
+ const edgeData = edge.data;
3486
+ if (!edgeData) continue;
3487
+ const targetId = edge.target;
3488
+ const sourceId = edge.source;
3489
+ const edgeType = edgeData.edgeType;
3490
+ if (edgeType === "showIf") {
3491
+ const question = findQuestion2(updatedSchema, targetId);
3492
+ if (!question) continue;
3493
+ const updatedShowIf = removeConditionForSource(
3494
+ question.showIf,
3495
+ sourceId
3496
+ );
3497
+ updatedSchema = updateQuestionInSchema(updatedSchema, targetId, {
3498
+ showIf: updatedShowIf
3499
+ });
3500
+ }
3501
+ }
3502
+ onChange(updatedSchema);
3503
+ },
3504
+ [schema, onChange]
3505
+ );
3506
+ const handleConditionUpdate = useCallback(
3507
+ (updates) => {
3508
+ if (!selectedFieldId) return;
3509
+ const updatedSchema = updateQuestionInSchema(schema, selectedFieldId, updates);
3510
+ onChange(updatedSchema);
3511
+ },
3512
+ [schema, selectedFieldId, onChange]
3513
+ );
3514
+ const showIfCount = flowGraph.edges.filter(
3515
+ (e) => e.data?.edgeType === "showIf"
3516
+ ).length;
3517
+ const jumpCount = flowGraph.edges.filter(
3518
+ (e) => e.data?.edgeType === "onExit"
3519
+ ).length;
3520
+ return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex flex-col bg-background/95 backdrop-blur-sm", children: [
3521
+ /* @__PURE__ */ jsxs("div", { className: "h-12 shrink-0 flex items-center justify-between px-4 border-b border-border bg-card", children: [
3522
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3523
+ /* @__PURE__ */ jsx(GitBranch, { size: 16, className: "text-primary" }),
3524
+ /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold", children: "Logic Flow" }),
3525
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
3526
+ showIfCount,
3527
+ " condition",
3528
+ showIfCount !== 1 ? "s" : "",
3529
+ jumpCount > 0 && ` \xB7 ${jumpCount} jump${jumpCount !== 1 ? "s" : ""}`,
3530
+ " \xB7 ",
3531
+ sectionIds.length,
3532
+ " section",
3533
+ sectionIds.length !== 1 ? "s" : ""
3534
+ ] })
3535
+ ] }),
3536
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
3537
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground mr-2", children: "Drag between nodes to create conditions \xB7 Select edges and press Delete to remove" }),
3538
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", onClick: onClose, title: "Close logic flow", children: /* @__PURE__ */ jsx(X, { size: 16, strokeWidth: 1.75 }) })
3539
+ ] })
3540
+ ] }),
3541
+ noSections ? /* @__PURE__ */ jsx("div", { className: "flex-1 flex items-center justify-center text-muted-foreground text-sm", children: /* @__PURE__ */ jsxs("div", { className: "text-center space-y-2", children: [
3542
+ /* @__PURE__ */ jsx(GitBranch, { size: 32, className: "mx-auto opacity-40" }),
3543
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: "No sections found" }),
3544
+ /* @__PURE__ */ jsx("p", { className: "text-xs max-w-xs", children: "Add sections and fields to your form to see the logic flow pipeline." })
3545
+ ] }) }) : /* @__PURE__ */ jsxs("div", { className: "flex-1 flex", children: [
3546
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxs(
3547
+ ReactFlow,
3548
+ {
3549
+ nodes,
3550
+ edges,
3551
+ onNodesChange,
3552
+ onEdgesChange,
3553
+ onNodeClick,
3554
+ onConnect,
3555
+ onEdgesDelete,
3556
+ nodeTypes: logicFlowNodeTypes,
3557
+ edgeTypes: logicFlowEdgeTypes,
3558
+ defaultEdgeOptions,
3559
+ fitView: true,
3560
+ fitViewOptions: { padding: 0.2 },
3561
+ deleteKeyCode: ["Backspace", "Delete"],
3562
+ proOptions: { hideAttribution: true },
3563
+ children: [
3564
+ /* @__PURE__ */ jsx(Background, { gap: 20, size: 1 }),
3565
+ /* @__PURE__ */ jsx(Controls, { showInteractive: false }),
3566
+ /* @__PURE__ */ jsx(
3567
+ MiniMap,
3568
+ {
3569
+ nodeColor: (node) => {
3570
+ const d = node.data;
3571
+ const idx = d.colorIndex ?? 0;
3572
+ return SECTION_COLORS[idx % SECTION_COLORS.length];
3573
+ },
3574
+ maskColor: "rgba(0, 0, 0, 0.3)"
3575
+ }
3576
+ )
3577
+ ]
3578
+ }
3579
+ ) }),
3580
+ selectedFieldId && selectedQuestion && /* @__PURE__ */ jsxs("div", { className: "w-72 border-l border-border bg-card overflow-auto", children: [
3581
+ /* @__PURE__ */ jsxs("div", { className: "px-3 py-3 border-b border-border flex items-center justify-between", children: [
3582
+ /* @__PURE__ */ jsxs("div", { children: [
3583
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold truncate", children: selectedQuestion.label || selectedQuestion.id }),
3584
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground", children: selectedQuestion.type })
3585
+ ] }),
3586
+ /* @__PURE__ */ jsx(
3587
+ Button,
3588
+ {
3589
+ variant: "ghost",
3590
+ size: "icon-xs",
3591
+ onClick: () => setSelectedFieldId(null),
3592
+ title: "Close panel",
3593
+ children: /* @__PURE__ */ jsx(X, { size: 14, strokeWidth: 1.75 })
3594
+ }
3595
+ )
3596
+ ] }),
3597
+ /* @__PURE__ */ jsx("div", { className: "px-3 py-3", children: /* @__PURE__ */ jsx(
3598
+ ConditionEditor,
3599
+ {
3600
+ question: selectedQuestion,
3601
+ schema,
3602
+ onUpdate: handleConditionUpdate
3603
+ }
3604
+ ) })
3605
+ ] })
3606
+ ] }),
3607
+ /* @__PURE__ */ jsxs("div", { className: "shrink-0 px-4 py-2 border-t border-border bg-card flex items-center gap-4 text-xs text-muted-foreground flex-wrap", children: [
3608
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
3609
+ /* @__PURE__ */ jsx("div", { className: "w-5 h-0.5 rounded", style: { background: "var(--muted-foreground)", opacity: 0.4 } }),
3610
+ /* @__PURE__ */ jsx("span", { children: "flow" })
3611
+ ] }),
3612
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
3613
+ /* @__PURE__ */ jsx("div", { className: "w-5 h-0.5 bg-primary rounded" }),
3614
+ /* @__PURE__ */ jsx("span", { children: "showIf" })
3615
+ ] }),
3616
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
3617
+ /* @__PURE__ */ jsx("div", { className: "w-5 h-0.5 rounded", style: { background: "#f59e0b" } }),
3618
+ /* @__PURE__ */ jsx("span", { children: "jump" })
3619
+ ] }),
3620
+ sectionIds.map((id, i) => {
3621
+ const section = schema.sections.find((s) => s.id === id);
3622
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
3623
+ /* @__PURE__ */ jsx("div", { className: "w-3 h-3 rounded-sm", style: { background: SECTION_COLORS[i % SECTION_COLORS.length] } }),
3624
+ /* @__PURE__ */ jsx("span", { children: section?.title || id })
3625
+ ] }, id);
3626
+ })
3627
+ ] })
3628
+ ] });
3629
+ }
3630
+ function TemplateGallery({ templates, onSelect, onClose }) {
3631
+ const [search, setSearch] = useState("");
3632
+ const [activeCategory, setActiveCategory] = useState("all");
3633
+ const [confirmTemplate, setConfirmTemplate] = useState(null);
3634
+ const categories = useMemo(() => {
3635
+ const cats = /* @__PURE__ */ new Set();
3636
+ for (const t of templates) cats.add(t.meta.category);
3637
+ return ["all", ...Array.from(cats)];
3638
+ }, [templates]);
3639
+ const filtered = useMemo(() => {
3640
+ return templates.filter((t) => {
3641
+ const matchesCategory = activeCategory === "all" || t.meta.category === activeCategory;
3642
+ const matchesSearch = !search || t.meta.name.toLowerCase().includes(search.toLowerCase()) || t.meta.description.toLowerCase().includes(search.toLowerCase()) || t.meta.tags?.some((tag) => tag.toLowerCase().includes(search.toLowerCase()));
3643
+ return matchesCategory && matchesSearch;
3644
+ });
3645
+ }, [templates, activeCategory, search]);
3646
+ const handleConfirm = () => {
3647
+ if (confirmTemplate) {
3648
+ onSelect(confirmTemplate);
3649
+ setConfirmTemplate(null);
3650
+ onClose();
3651
+ }
3652
+ };
3653
+ return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-3xl max-h-[85vh] mx-4 bg-card border border-border rounded-lg shadow-xl flex flex-col overflow-hidden", children: [
3654
+ /* @__PURE__ */ jsxs("div", { className: "shrink-0 flex items-center justify-between px-4 py-3 border-b border-border", children: [
3655
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3656
+ /* @__PURE__ */ jsx(LayoutTemplate, { size: 18, className: "text-primary", strokeWidth: 1.75 }),
3657
+ /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold", children: "Template Gallery" }),
3658
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
3659
+ "(",
3660
+ templates.length,
3661
+ " templates)"
3662
+ ] })
3663
+ ] }),
3664
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", onClick: onClose, title: "Close", children: /* @__PURE__ */ jsx(X, { size: 16, strokeWidth: 1.75 }) })
3665
+ ] }),
3666
+ /* @__PURE__ */ jsxs("div", { className: "shrink-0 px-4 py-2 space-y-2 border-b border-border", children: [
3667
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
3668
+ /* @__PURE__ */ jsx(Search, { size: 14, className: "absolute left-2.5 top-1/2 -translate-y-1/2 text-muted-foreground", strokeWidth: 1.75 }),
3669
+ /* @__PURE__ */ jsx(
3670
+ "input",
3671
+ {
3672
+ type: "text",
3673
+ placeholder: "Search templates...",
3674
+ value: search,
3675
+ onChange: (e) => setSearch(e.target.value),
3676
+ className: "w-full pl-8 pr-3 py-1.5 text-sm bg-background border border-border rounded-md text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-primary"
3677
+ }
3678
+ )
3679
+ ] }),
3680
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 overflow-x-auto pb-0.5", children: categories.map((cat) => /* @__PURE__ */ jsx(
3681
+ "button",
3682
+ {
3683
+ type: "button",
3684
+ onClick: () => setActiveCategory(cat),
3685
+ className: `shrink-0 px-2.5 py-1 rounded-md text-xs font-medium transition-colors ${activeCategory === cat ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:text-foreground"}`,
3686
+ children: cat === "all" ? "All" : cat.charAt(0).toUpperCase() + cat.slice(1)
3687
+ },
3688
+ cat
3689
+ )) })
3690
+ ] }),
3691
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto p-4", children: filtered.length === 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center justify-center h-40 text-muted-foreground text-sm", children: /* @__PURE__ */ jsx("p", { children: "No templates match your search." }) }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3", children: filtered.map((template) => /* @__PURE__ */ jsxs(
3692
+ "button",
3693
+ {
3694
+ type: "button",
3695
+ onClick: () => setConfirmTemplate(template),
3696
+ className: "text-left p-3 rounded-lg border border-border bg-background hover:border-primary/50 hover:bg-primary/5 transition-colors group",
3697
+ children: [
3698
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2 mb-1.5", children: [
3699
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium text-foreground group-hover:text-primary transition-colors", children: template.meta.name }),
3700
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 px-1.5 py-0.5 rounded text-[10px] font-medium bg-muted text-muted-foreground", children: template.meta.category })
3701
+ ] }),
3702
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mb-2 line-clamp-2", children: template.meta.description }),
3703
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
3704
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
3705
+ /* @__PURE__ */ jsx(FileText, { size: 11, strokeWidth: 1.75 }),
3706
+ template.meta.fieldCount,
3707
+ " field",
3708
+ template.meta.fieldCount !== 1 ? "s" : ""
3709
+ ] }),
3710
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
3711
+ /* @__PURE__ */ jsx(Layers, { size: 11, strokeWidth: 1.75 }),
3712
+ template.meta.sectionCount,
3713
+ " section",
3714
+ template.meta.sectionCount !== 1 ? "s" : ""
3715
+ ] })
3716
+ ] })
3717
+ ]
3718
+ },
3719
+ template.meta.id
3720
+ )) }) }),
3721
+ confirmTemplate && /* @__PURE__ */ jsxs("div", { className: "shrink-0 px-4 py-3 border-t border-border bg-muted/50 flex items-center justify-between gap-3", children: [
3722
+ /* @__PURE__ */ jsxs("div", { className: "text-sm", children: [
3723
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Use " }),
3724
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: confirmTemplate.meta.name }),
3725
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "? This will replace the current form." })
3726
+ ] }),
3727
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
3728
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => setConfirmTemplate(null), children: "Cancel" }),
3729
+ /* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleConfirm, children: "Use Template" })
3730
+ ] })
3731
+ ] })
3732
+ ] }) });
3733
+ }
3734
+ function useDebouncedValidation(delayMs = 500) {
3735
+ const [errors, setErrors] = useState([]);
3736
+ const timerRef = useRef(null);
3737
+ const validate = useCallback(
3738
+ (text) => {
3739
+ if (timerRef.current) {
3740
+ clearTimeout(timerRef.current);
3741
+ }
3742
+ timerRef.current = setTimeout(() => {
3743
+ const result = [];
3744
+ let parsed;
3745
+ try {
3746
+ parsed = JSON.parse(text);
3747
+ } catch (err) {
3748
+ if (err instanceof SyntaxError) {
3749
+ const posMatch = err.message.match(/position\s+(\d+)/i);
3750
+ let line = 1;
3751
+ let column = 1;
3752
+ if (posMatch) {
3753
+ const pos = parseInt(posMatch[1], 10);
3754
+ const upToPos = text.slice(0, pos);
3755
+ line = (upToPos.match(/\n/g) || []).length + 1;
3756
+ column = pos - upToPos.lastIndexOf("\n");
3757
+ }
3758
+ result.push({ message: err.message, line, column, endLine: line, endColumn: column + 1 });
3759
+ } else {
3760
+ result.push({ message: "Invalid JSON" });
3761
+ }
3762
+ setErrors(result);
3763
+ return;
3764
+ }
3765
+ try {
3766
+ validateSchema(parsed);
3767
+ } catch (err) {
3768
+ if (err instanceof FormEngineSchemaError) {
3769
+ for (const issue of err.issues) {
3770
+ result.push({
3771
+ message: issue.message,
3772
+ path: issue.path?.join(".")
3773
+ });
3774
+ }
3775
+ } else if (err instanceof Error) {
3776
+ result.push({ message: err.message });
3777
+ }
3778
+ }
3779
+ setErrors(result);
3780
+ }, delayMs);
3781
+ },
3782
+ [delayMs]
3783
+ );
3784
+ return { errors, validate };
3785
+ }
2788
3786
  var ThemeCtx = createContext({});
2789
3787
  function useBuilderTheme() {
2790
3788
  return useContext(ThemeCtx);
@@ -2857,7 +3855,16 @@ var FormBuilderErrorBoundary = class extends Component {
2857
3855
  }
2858
3856
  };
2859
3857
  function FormBuilderCore(props) {
2860
- const { initialSchema = DEFAULT_SCHEMA, onChange, onSave, height = "100vh", theme, className, toolbarExtra, questionTypes, palette } = props;
3858
+ const { initialSchema = DEFAULT_SCHEMA, onChange, onSave, height = "100vh", theme, className, toolbarExtra, questionTypes, palette, preview, templates, schemaUrl } = props;
3859
+ const [viewMode, setViewMode] = useState("design");
3860
+ const [jsonText, setJsonText] = useState("");
3861
+ const [jsonSwitchError, setJsonSwitchError] = useState(null);
3862
+ const [showLogicMap, setShowLogicMap] = useState(false);
3863
+ const [showTemplateGallery, setShowTemplateGallery] = useState(false);
3864
+ const [saveValidationErrors, setSaveValidationErrors] = useState(null);
3865
+ const [schemaUrlLoading, setSchemaUrlLoading] = useState(!!schemaUrl);
3866
+ const [schemaUrlError, setSchemaUrlError] = useState(null);
3867
+ const { errors: jsonErrors, validate: validateJson } = useDebouncedValidation(400);
2861
3868
  const mergedQuestionTypes = questionTypes ? { ...QUESTION_TYPE_INFO, ...questionTypes } : QUESTION_TYPE_INFO;
2862
3869
  const builderState = useBuilderState(initialSchema);
2863
3870
  const dragDrop = useDragDrop(builderState);
@@ -2873,15 +3880,54 @@ function FormBuilderCore(props) {
2873
3880
  setMobilePanel("none");
2874
3881
  }
2875
3882
  }, [dragDrop.activeDragItem]);
3883
+ useEffect(() => {
3884
+ if (!schemaUrl) return;
3885
+ let cancelled = false;
3886
+ setSchemaUrlLoading(true);
3887
+ setSchemaUrlError(null);
3888
+ fetch(schemaUrl).then((res) => {
3889
+ if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
3890
+ return res.json();
3891
+ }).then((json) => {
3892
+ if (cancelled) return;
3893
+ try {
3894
+ const validated = validateSchema(json);
3895
+ builderState.resetSchema(validated);
3896
+ } catch (err) {
3897
+ if (err instanceof FormEngineSchemaError) {
3898
+ setSchemaUrlError(`Invalid schema: ${err.issues[0]?.message ?? "validation failed"}`);
3899
+ } else {
3900
+ setSchemaUrlError(err.message);
3901
+ }
3902
+ }
3903
+ setSchemaUrlLoading(false);
3904
+ }).catch((err) => {
3905
+ if (cancelled) return;
3906
+ setSchemaUrlError(err.message ?? "Failed to fetch schema");
3907
+ setSchemaUrlLoading(false);
3908
+ });
3909
+ return () => {
3910
+ cancelled = true;
3911
+ };
3912
+ }, [schemaUrl]);
2876
3913
  useEffect(() => {
2877
3914
  if (onChange && builderState.isDirty) {
2878
3915
  onChange(builderState.schema);
2879
3916
  }
2880
3917
  }, [builderState.schema, builderState.isDirty, onChange]);
2881
3918
  const handleSave = useCallback(() => {
2882
- if (onSave) {
3919
+ if (!onSave) return;
3920
+ try {
3921
+ validateSchema(builderState.schema);
3922
+ setSaveValidationErrors(null);
2883
3923
  onSave(builderState.schema);
2884
3924
  builderState.markClean();
3925
+ } catch (err) {
3926
+ if (err instanceof FormEngineSchemaError) {
3927
+ setSaveValidationErrors(err.issues.map((issue) => issue.message));
3928
+ } else {
3929
+ setSaveValidationErrors([err.message ?? "Unknown validation error"]);
3930
+ }
2885
3931
  }
2886
3932
  }, [onSave, builderState]);
2887
3933
  const handleExport = useCallback(() => {
@@ -2924,6 +3970,32 @@ ${details}`);
2924
3970
  },
2925
3971
  [builderState]
2926
3972
  );
3973
+ const handleViewModeChange = useCallback(
3974
+ (newMode) => {
3975
+ setJsonSwitchError(null);
3976
+ if (viewMode === "json" && newMode !== "json") {
3977
+ try {
3978
+ const parsed = JSON.parse(jsonText);
3979
+ const validated = validateSchema(parsed);
3980
+ builderState.resetSchema(validated);
3981
+ } catch (err) {
3982
+ if (err instanceof SyntaxError) {
3983
+ setJsonSwitchError("Cannot switch: JSON has syntax errors");
3984
+ } else if (err instanceof FormEngineSchemaError) {
3985
+ setJsonSwitchError(`Cannot switch: ${err.issues[0]?.message ?? "Invalid schema"}`);
3986
+ } else {
3987
+ setJsonSwitchError("Cannot switch: Invalid schema");
3988
+ }
3989
+ return;
3990
+ }
3991
+ }
3992
+ if (newMode === "json") {
3993
+ setJsonText(JSON.stringify(builderState.schema, null, 2));
3994
+ }
3995
+ setViewMode(newMode);
3996
+ },
3997
+ [viewMode, jsonText, builderState]
3998
+ );
2927
3999
  const handleKeyDown = useCallback(
2928
4000
  (e) => {
2929
4001
  const mod = e.metaKey || e.ctrlKey;
@@ -3025,8 +4097,74 @@ ${details}`);
3025
4097
  /* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "mx-2 h-5" }),
3026
4098
  builderState.isDirty && /* @__PURE__ */ jsx("span", { className: "text-xs text-primary", role: "status", children: "Unsaved changes" })
3027
4099
  ] }),
3028
- /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: toolbarExtra }),
4100
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2", children: [
4101
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 bg-muted rounded-md p-0.5", children: [
4102
+ /* @__PURE__ */ jsxs(
4103
+ Button,
4104
+ {
4105
+ variant: viewMode === "design" ? "secondary" : "ghost",
4106
+ size: "sm",
4107
+ onClick: () => handleViewModeChange("design"),
4108
+ className: "gap-1.5 h-7 px-2.5 text-xs",
4109
+ children: [
4110
+ /* @__PURE__ */ jsx(PencilRuler, { size: 13, strokeWidth: 1.75 }),
4111
+ "Design"
4112
+ ]
4113
+ }
4114
+ ),
4115
+ /* @__PURE__ */ jsxs(
4116
+ Button,
4117
+ {
4118
+ variant: viewMode === "preview" ? "secondary" : "ghost",
4119
+ size: "sm",
4120
+ onClick: () => handleViewModeChange("preview"),
4121
+ className: "gap-1.5 h-7 px-2.5 text-xs",
4122
+ children: [
4123
+ /* @__PURE__ */ jsx(Eye, { size: 13, strokeWidth: 1.75 }),
4124
+ "Preview"
4125
+ ]
4126
+ }
4127
+ ),
4128
+ /* @__PURE__ */ jsxs(
4129
+ Button,
4130
+ {
4131
+ variant: viewMode === "json" ? "secondary" : "ghost",
4132
+ size: "sm",
4133
+ onClick: () => handleViewModeChange("json"),
4134
+ className: "gap-1.5 h-7 px-2.5 text-xs",
4135
+ children: [
4136
+ /* @__PURE__ */ jsx(Code, { size: 13, strokeWidth: 1.75 }),
4137
+ "JSON"
4138
+ ]
4139
+ }
4140
+ )
4141
+ ] }),
4142
+ toolbarExtra
4143
+ ] }),
3029
4144
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-1.5", children: [
4145
+ templates && templates.length > 0 && /* @__PURE__ */ jsx(
4146
+ Button,
4147
+ {
4148
+ variant: "ghost",
4149
+ size: "icon-sm",
4150
+ onClick: () => setShowTemplateGallery(true),
4151
+ title: "Browse templates",
4152
+ "aria-label": "Template gallery",
4153
+ children: /* @__PURE__ */ jsx(LayoutTemplate, { size: 15, strokeWidth: 1.75 })
4154
+ }
4155
+ ),
4156
+ /* @__PURE__ */ jsx(
4157
+ Button,
4158
+ {
4159
+ variant: "ghost",
4160
+ size: "icon-sm",
4161
+ onClick: () => setShowLogicMap(true),
4162
+ title: "View branching logic map",
4163
+ "aria-label": "Logic map",
4164
+ children: /* @__PURE__ */ jsx(GitBranch, { size: 15, strokeWidth: 1.75 })
4165
+ }
4166
+ ),
4167
+ /* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "mx-1 h-5" }),
3030
4168
  /* @__PURE__ */ jsx(
3031
4169
  Button,
3032
4170
  {
@@ -3070,6 +4208,47 @@ ${details}`);
3070
4208
  }
3071
4209
  ) }),
3072
4210
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-1", children: [
4211
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 bg-muted rounded-md p-0.5", children: [
4212
+ /* @__PURE__ */ jsxs(
4213
+ Button,
4214
+ {
4215
+ variant: viewMode === "design" ? "secondary" : "ghost",
4216
+ size: "sm",
4217
+ onClick: () => handleViewModeChange("design"),
4218
+ className: "gap-1 h-7 px-2 text-xs",
4219
+ children: [
4220
+ /* @__PURE__ */ jsx(PencilRuler, { size: 12, strokeWidth: 1.75 }),
4221
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Design" })
4222
+ ]
4223
+ }
4224
+ ),
4225
+ /* @__PURE__ */ jsxs(
4226
+ Button,
4227
+ {
4228
+ variant: viewMode === "preview" ? "secondary" : "ghost",
4229
+ size: "sm",
4230
+ onClick: () => handleViewModeChange("preview"),
4231
+ className: "gap-1 h-7 px-2 text-xs",
4232
+ children: [
4233
+ /* @__PURE__ */ jsx(Eye, { size: 12, strokeWidth: 1.75 }),
4234
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Preview" })
4235
+ ]
4236
+ }
4237
+ ),
4238
+ /* @__PURE__ */ jsxs(
4239
+ Button,
4240
+ {
4241
+ variant: viewMode === "json" ? "secondary" : "ghost",
4242
+ size: "sm",
4243
+ onClick: () => handleViewModeChange("json"),
4244
+ className: "gap-1 h-7 px-2 text-xs",
4245
+ children: [
4246
+ /* @__PURE__ */ jsx(Code, { size: 12, strokeWidth: 1.75 }),
4247
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "JSON" })
4248
+ ]
4249
+ }
4250
+ )
4251
+ ] }),
3073
4252
  toolbarExtra,
3074
4253
  /* @__PURE__ */ jsxs(Button, { onClick: handleSave, size: "sm", className: "border-0 shadow-sm fcb-glow ml-1", "aria-label": "Save form", children: [
3075
4254
  /* @__PURE__ */ jsx(Save, { size: 14, strokeWidth: 2 }),
@@ -3158,7 +4337,50 @@ ${details}`);
3158
4337
  "aria-hidden": "true"
3159
4338
  }
3160
4339
  ),
3161
- /* @__PURE__ */ jsxs("div", { className: "flex-1 flex overflow-hidden relative", children: [
4340
+ jsonSwitchError && /* @__PURE__ */ jsxs("div", { className: "shrink-0 px-4 py-2 bg-destructive/10 border-b border-destructive/20 text-destructive text-xs flex items-center justify-between", children: [
4341
+ /* @__PURE__ */ jsx("span", { children: jsonSwitchError }),
4342
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setJsonSwitchError(null), className: "ml-2 hover:underline", children: "Dismiss" })
4343
+ ] }),
4344
+ saveValidationErrors && saveValidationErrors.length > 0 && /* @__PURE__ */ jsxs("div", { className: "shrink-0 px-4 py-2 bg-destructive/10 border-b border-destructive/20 text-xs", children: [
4345
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-1", children: [
4346
+ /* @__PURE__ */ jsxs("span", { className: "font-semibold text-destructive", children: [
4347
+ "Schema validation failed (",
4348
+ saveValidationErrors.length,
4349
+ " issue",
4350
+ saveValidationErrors.length !== 1 ? "s" : "",
4351
+ ")"
4352
+ ] }),
4353
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setSaveValidationErrors(null), className: "text-destructive hover:underline", children: "Dismiss" })
4354
+ ] }),
4355
+ /* @__PURE__ */ jsxs("ul", { className: "list-disc pl-4 text-destructive/80 space-y-0.5", children: [
4356
+ saveValidationErrors.slice(0, 10).map((msg, i) => /* @__PURE__ */ jsx("li", { children: msg }, i)),
4357
+ saveValidationErrors.length > 10 && /* @__PURE__ */ jsxs("li", { children: [
4358
+ "...and ",
4359
+ saveValidationErrors.length - 10,
4360
+ " more"
4361
+ ] })
4362
+ ] })
4363
+ ] }),
4364
+ schemaUrlLoading && /* @__PURE__ */ jsxs("div", { className: "shrink-0 px-4 py-2 border-b border-border bg-muted/50 text-xs text-muted-foreground flex items-center gap-2", children: [
4365
+ /* @__PURE__ */ jsx("span", { className: "inline-block w-3 h-3 border-2 border-primary border-t-transparent rounded-full animate-spin" }),
4366
+ "Loading schema from URL..."
4367
+ ] }),
4368
+ schemaUrlError && /* @__PURE__ */ jsxs("div", { className: "shrink-0 px-4 py-2 bg-destructive/10 border-b border-destructive/20 text-destructive text-xs flex items-center justify-between", children: [
4369
+ /* @__PURE__ */ jsxs("span", { children: [
4370
+ "Failed to load schema: ",
4371
+ schemaUrlError
4372
+ ] }),
4373
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setSchemaUrlError(null), className: "ml-2 hover:underline", children: "Dismiss" })
4374
+ ] }),
4375
+ /* @__PURE__ */ jsx("div", { className: "flex-1 flex overflow-hidden relative", children: viewMode === "json" ? /* @__PURE__ */ jsx(
4376
+ JsonEditorPanel,
4377
+ {
4378
+ value: jsonText,
4379
+ onChange: setJsonText,
4380
+ errors: jsonErrors,
4381
+ onValidate: validateJson
4382
+ }
4383
+ ) : viewMode === "preview" ? /* @__PURE__ */ jsx(FormPreviewPanel, { schema: builderState.schema, preview }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3162
4384
  /* @__PURE__ */ jsx("div", { className: "hidden md:flex", children: /* @__PURE__ */ jsx(QuestionPalette, { questionTypes: mergedQuestionTypes, palette }) }),
3163
4385
  /* @__PURE__ */ jsx(FormCanvas, { builderState }),
3164
4386
  /* @__PURE__ */ jsx("div", { className: "hidden md:flex", children: /* @__PURE__ */ jsx(PropertiesPanel, { builderState }) }),
@@ -3172,11 +4394,27 @@ ${details}`);
3172
4394
  "aria-hidden": "true"
3173
4395
  }
3174
4396
  )
3175
- ] })
4397
+ ] }) })
3176
4398
  ]
3177
4399
  }
3178
4400
  ),
3179
- /* @__PURE__ */ jsx(DragOverlay, { dropAnimation: null, children: dragDrop.activeDragItem && /* @__PURE__ */ jsx(DragOverlayContent, { item: dragDrop.activeDragItem, schema: builderState.schema, questionTypes: mergedQuestionTypes }) })
4401
+ /* @__PURE__ */ jsx(DragOverlay, { dropAnimation: null, children: dragDrop.activeDragItem && /* @__PURE__ */ jsx(DragOverlayContent, { item: dragDrop.activeDragItem, schema: builderState.schema, questionTypes: mergedQuestionTypes }) }),
4402
+ showLogicMap && /* @__PURE__ */ jsx(
4403
+ LogicFlowEditor,
4404
+ {
4405
+ schema: builderState.schema,
4406
+ onChange: (updated) => builderState.resetSchema(updated),
4407
+ onClose: () => setShowLogicMap(false)
4408
+ }
4409
+ ),
4410
+ showTemplateGallery && templates && /* @__PURE__ */ jsx(
4411
+ TemplateGallery,
4412
+ {
4413
+ templates,
4414
+ onSelect: (template) => builderState.resetSchema(template.schema),
4415
+ onClose: () => setShowTemplateGallery(false)
4416
+ }
4417
+ )
3180
4418
  ]
3181
4419
  }
3182
4420
  ) });
@@ -3231,76 +4469,54 @@ var FormBuilder = requireLicense(FormBuilderInner, "FormBuilder");
3231
4469
 
3232
4470
  // src/form-builder/theme/presets.ts
3233
4471
  var squaredrDarkPreset = {
3234
- background: "#0a0a0b",
3235
- // ink-950
3236
- foreground: "#e8e8ea",
3237
- // ink-100
3238
- card: "#111113",
3239
- // ink-900
3240
- primary: "oklch(0.82 0.14 210)",
3241
- // sr-accent-cyan
3242
- primaryForeground: "#0a0a0b",
3243
- // ink-950
3244
- secondary: "#17171a",
3245
- // ink-850
3246
- secondaryForeground: "#e8e8ea",
3247
- // ink-100
3248
- muted: "#111113",
3249
- // ink-900
3250
- mutedForeground: "#8a8a95",
3251
- // ink-400
3252
- accent: "#17171a",
3253
- // ink-850
3254
- accentForeground: "#e8e8ea",
3255
- // ink-100
3256
- destructive: "oklch(0.68 0.22 25)",
3257
- // sr-error
3258
- destructiveForeground: "#e8e8ea",
3259
- // ink-100
3260
- border: "#1c1c20",
3261
- // ink-800
3262
- input: "#1c1c20",
3263
- // ink-800
3264
- ring: "oklch(0.82 0.14 210)",
3265
- // sr-accent-cyan
3266
- radius: "6px",
3267
- surface: "#111113",
3268
- // ink-900
3269
- surfaceHover: "#17171a",
3270
- // ink-850
3271
- canvas: "#0a0a0b",
3272
- // ink-950
3273
- panel: "#111113",
3274
- // ink-900
3275
- borderStrong: "#26262c",
3276
- // ink-700
3277
- textDim: "#5a5a66"
3278
- // ink-500
4472
+ background: "#0F1A1F",
4473
+ foreground: "#E8EFF1",
4474
+ card: "#16242A",
4475
+ primary: "#63BDB4",
4476
+ primaryForeground: "#0F1A1F",
4477
+ secondary: "#182F31",
4478
+ secondaryForeground: "#E8EFF1",
4479
+ muted: "#182F31",
4480
+ mutedForeground: "#8CA1A9",
4481
+ accent: "#182F31",
4482
+ accentForeground: "#E8EFF1",
4483
+ destructive: "#E08072",
4484
+ destructiveForeground: "#0F1A1F",
4485
+ border: "#2A3B42",
4486
+ input: "#2A3B42",
4487
+ ring: "#63BDB4",
4488
+ radius: "0px",
4489
+ surface: "#16242A",
4490
+ surfaceHover: "#182F31",
4491
+ canvas: "#0F1A1F",
4492
+ panel: "#16242A",
4493
+ borderStrong: "#2F4F4C",
4494
+ textDim: "#5E7680"
3279
4495
  };
3280
4496
  var cleanPreset = {
3281
- background: "#ffffff",
3282
- foreground: "#111113",
3283
- card: "#f9fafb",
3284
- primary: "#0d9488",
3285
- primaryForeground: "#ffffff",
3286
- secondary: "#f3f4f6",
3287
- secondaryForeground: "#111113",
3288
- muted: "#f3f4f6",
3289
- mutedForeground: "#6b7280",
3290
- accent: "#f3f4f6",
3291
- accentForeground: "#111113",
3292
- destructive: "#ef4444",
3293
- destructiveForeground: "#ffffff",
3294
- border: "#e5e7eb",
3295
- input: "#e5e7eb",
3296
- ring: "#0d9488",
3297
- radius: "6px",
3298
- surface: "#f9fafb",
3299
- surfaceHover: "#f3f4f6",
3300
- canvas: "#ffffff",
3301
- panel: "#ffffff",
3302
- borderStrong: "#d1d5db",
3303
- textDim: "#9ca3af"
4497
+ background: "#F4F7F8",
4498
+ foreground: "#12222A",
4499
+ card: "#FFFFFF",
4500
+ primary: "#1F6B6E",
4501
+ primaryForeground: "#FFFFFF",
4502
+ secondary: "#EDF3F2",
4503
+ secondaryForeground: "#12222A",
4504
+ muted: "#EDF3F2",
4505
+ mutedForeground: "#6A7B85",
4506
+ accent: "#EDF3F2",
4507
+ accentForeground: "#12222A",
4508
+ destructive: "#B04A3C",
4509
+ destructiveForeground: "#FFFFFF",
4510
+ border: "#DCE4E8",
4511
+ input: "#DCE4E8",
4512
+ ring: "#1F6B6E",
4513
+ radius: "0px",
4514
+ surface: "#FAFCFC",
4515
+ surfaceHover: "#EDF3F2",
4516
+ canvas: "#F4F7F8",
4517
+ panel: "#FFFFFF",
4518
+ borderStrong: "#B9D1CF",
4519
+ textDim: "#6A7B85"
3304
4520
  };
3305
4521
 
3306
4522
  export { DEFAULT_PALETTE, DEFAULT_SCHEMA, FormBuilder, FormBuilderThemeProvider, QUESTION_TYPE_INFO, addOption, addQuestion, addSection, cleanPreset, duplicateQuestion, duplicateSection, findQuestion, findSection, generateId, generateOptionId, generateQuestionId, generateSectionId, moveOption, moveQuestion, moveSection, removeOption, removeQuestion, removeSection, squaredrDarkPreset, updateOption, updateQuestion, updateSection, useBuilderState, useBuilderTheme, useDragDrop, useUndoRedo };