@squaredr/fieldcraft-pro 1.3.0 → 1.4.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) {
@@ -570,7 +572,81 @@ var QUESTION_TYPE_INFO = {
570
572
  description: "Upload files",
571
573
  defaultConfig: { type: "file_upload", maxFiles: 1, maxSizeMb: 10 }
572
574
  },
575
+ // ── Compound ──
576
+ legal_name: {
577
+ type: "legal_name",
578
+ label: "Legal Name",
579
+ category: "text",
580
+ icon: "UserCheck",
581
+ description: "First, middle, and last name",
582
+ defaultConfig: { type: "legal_name" }
583
+ },
584
+ address: {
585
+ type: "address",
586
+ label: "Address",
587
+ category: "text",
588
+ icon: "MapPin",
589
+ description: "Street, city, state, ZIP",
590
+ defaultConfig: { type: "address", includeCountry: false }
591
+ },
592
+ signature: {
593
+ type: "signature",
594
+ label: "Signature",
595
+ category: "media",
596
+ icon: "PenTool",
597
+ description: "Canvas signature pad",
598
+ defaultConfig: { type: "signature" }
599
+ },
600
+ date_range: {
601
+ type: "date_range",
602
+ label: "Date Range",
603
+ category: "datetime",
604
+ icon: "CalendarRange",
605
+ description: "Start and end date picker",
606
+ defaultConfig: { type: "date_range" }
607
+ },
573
608
  // ── Advanced ──
609
+ repeater: {
610
+ type: "repeater",
611
+ label: "Repeater",
612
+ category: "advanced",
613
+ icon: "ListPlus",
614
+ description: "Repeatable group of fields",
615
+ defaultConfig: { type: "repeater", fields: [], minItems: 1, maxItems: 10 }
616
+ },
617
+ likert: {
618
+ type: "likert",
619
+ label: "Likert Scale",
620
+ category: "advanced",
621
+ icon: "AlignHorizontalSpaceAround",
622
+ description: "Agreement scale (e.g. Strongly Disagree to Strongly Agree)",
623
+ requiresOptions: true,
624
+ defaultConfig: {
625
+ type: "likert",
626
+ scale: [
627
+ { label: "Strongly Disagree", value: "1" },
628
+ { label: "Disagree", value: "2" },
629
+ { label: "Neutral", value: "3" },
630
+ { label: "Agree", value: "4" },
631
+ { label: "Strongly Agree", value: "5" }
632
+ ]
633
+ }
634
+ },
635
+ scoring: {
636
+ type: "scoring",
637
+ label: "Scoring",
638
+ category: "advanced",
639
+ icon: "Award",
640
+ description: "Scored question with point values per option",
641
+ requiresOptions: true,
642
+ defaultConfig: {
643
+ type: "scoring",
644
+ options: [
645
+ { label: "Option 1", value: "1", score: 1 },
646
+ { label: "Option 2", value: "2", score: 2 }
647
+ ]
648
+ }
649
+ },
574
650
  matrix: {
575
651
  type: "matrix",
576
652
  label: "Matrix",
@@ -704,7 +780,7 @@ var DEFAULT_PALETTE = [
704
780
  {
705
781
  category: "text",
706
782
  label: "Text Input",
707
- types: ["short_text", "long_text", "email", "phone", "url"]
783
+ types: ["short_text", "long_text", "email", "phone", "url", "legal_name", "address"]
708
784
  },
709
785
  {
710
786
  category: "numeric",
@@ -719,12 +795,12 @@ var DEFAULT_PALETTE = [
719
795
  {
720
796
  category: "datetime",
721
797
  label: "Date & Time",
722
- types: ["date", "time"]
798
+ types: ["date", "time", "date_range"]
723
799
  },
724
800
  {
725
801
  category: "media",
726
802
  label: "Media",
727
- types: ["file_upload"]
803
+ types: ["file_upload", "signature"]
728
804
  },
729
805
  {
730
806
  category: "content",
@@ -739,7 +815,7 @@ var DEFAULT_PALETTE = [
739
815
  {
740
816
  category: "advanced",
741
817
  label: "Advanced",
742
- types: ["matrix", "calculated", "hidden"]
818
+ types: ["repeater", "likert", "scoring", "matrix", "calculated", "hidden"]
743
819
  }
744
820
  ];
745
821
 
@@ -2253,7 +2329,8 @@ function getFieldOptions(schema, excludeId) {
2253
2329
  return fields;
2254
2330
  }
2255
2331
  function ConditionEditor({ question, schema, onUpdate }) {
2256
- const showIf = question.showIf;
2332
+ const rawShowIf = question.showIf;
2333
+ const showIf = rawShowIf && rawShowIf.field && !rawShowIf.conditions ? { combine: "AND", conditions: [rawShowIf] } : rawShowIf;
2257
2334
  const fieldOptions = getFieldOptions(schema, question.id);
2258
2335
  const updateShowIf = (next) => {
2259
2336
  onUpdate({ showIf: next });
@@ -2375,6 +2452,24 @@ function ConditionEditor({ question, schema, onUpdate }) {
2375
2452
  }
2376
2453
  function FormSettingsPanel({ schema, onUpdate }) {
2377
2454
  const settings = schema.settings ?? {};
2455
+ const hasConditions = schema.sections.some(
2456
+ (s) => s.showIf || s.questions.some((q) => q.showIf)
2457
+ );
2458
+ const displayModeOptions = hasConditions ? [
2459
+ { label: "Stepped", value: "stepped" },
2460
+ { label: "Conversational", value: "conversational" }
2461
+ ] : [
2462
+ { label: "Stepped", value: "stepped" },
2463
+ { label: "Classic", value: "classic" },
2464
+ { label: "Conversational", value: "conversational" }
2465
+ ];
2466
+ const displayMode = settings.displayMode ?? "stepped";
2467
+ const effectiveDisplayMode = hasConditions && displayMode === "classic" ? "stepped" : displayMode;
2468
+ useEffect(() => {
2469
+ if (effectiveDisplayMode !== displayMode) {
2470
+ onUpdate({ ...schema, settings: { ...settings, displayMode: effectiveDisplayMode } });
2471
+ }
2472
+ }, [effectiveDisplayMode, displayMode]);
2378
2473
  const updateSettings = (updates) => {
2379
2474
  onUpdate({ ...schema, settings: { ...settings, ...updates } });
2380
2475
  };
@@ -2402,12 +2497,8 @@ function FormSettingsPanel({ schema, onUpdate }) {
2402
2497
  SettingsSelect,
2403
2498
  {
2404
2499
  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
- ],
2500
+ value: effectiveDisplayMode,
2501
+ options: displayModeOptions,
2411
2502
  onChange: (v) => updateSettings({ displayMode: v })
2412
2503
  }
2413
2504
  ),
@@ -2785,6 +2876,873 @@ function FieldGroup({ label, children }) {
2785
2876
  children
2786
2877
  ] });
2787
2878
  }
2879
+ var PreviewErrorBoundary = class extends Component {
2880
+ state = { error: null };
2881
+ static getDerivedStateFromError(error) {
2882
+ return { error };
2883
+ }
2884
+ componentDidCatch(error, info) {
2885
+ console.error("[FormBuilder Preview]", error, info);
2886
+ }
2887
+ componentDidUpdate(prevProps) {
2888
+ if (prevProps.resetKey !== this.props.resetKey && this.state.error) {
2889
+ this.setState({ error: null });
2890
+ }
2891
+ }
2892
+ render() {
2893
+ if (this.state.error) {
2894
+ return /* @__PURE__ */ jsxs("div", { className: "p-4 text-destructive text-sm font-mono", children: [
2895
+ /* @__PURE__ */ jsx("strong", { children: "Render Error:" }),
2896
+ " ",
2897
+ this.state.error.message
2898
+ ] });
2899
+ }
2900
+ return this.props.children;
2901
+ }
2902
+ };
2903
+ function FormPreviewPanel({ schema, preview }) {
2904
+ const hasSections = schema.sections && schema.sections.length > 0;
2905
+ const hasFields = hasSections && schema.sections.some((s) => s.questions.length > 0);
2906
+ if (!hasFields) {
2907
+ 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: [
2908
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: "No fields to preview" }),
2909
+ /* @__PURE__ */ jsx("p", { className: "text-xs", children: "Add fields in the Design view to see a live preview here." })
2910
+ ] }) });
2911
+ }
2912
+ 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(
2913
+ FormEngineRenderer,
2914
+ {
2915
+ schema,
2916
+ theme: preview?.theme,
2917
+ components: preview?.components,
2918
+ onReady: preview?.onReady,
2919
+ onFieldChange: preview?.onFieldChange,
2920
+ onSubmit: preview?.onSubmit ?? (() => {
2921
+ })
2922
+ }
2923
+ ) }) }) });
2924
+ }
2925
+
2926
+ // src/form-builder/utils/validation-markers.ts
2927
+ var MarkerSeverity = {
2928
+ Error: 8};
2929
+ function errorsToMarkers(errors) {
2930
+ return errors.map((error) => ({
2931
+ severity: MarkerSeverity.Error,
2932
+ message: error.message,
2933
+ startLineNumber: error.line ?? 1,
2934
+ startColumn: error.column ?? 1,
2935
+ endLineNumber: error.endLine ?? error.line ?? 1,
2936
+ endColumn: error.endColumn ?? error.column ?? 1,
2937
+ source: "FieldCraft Schema Validator"
2938
+ }));
2939
+ }
2940
+ var Editor = lazy(() => import('@monaco-editor/react').then((m) => ({ default: m.default })));
2941
+ function MonacoWrapper({ value, onChange, errors }) {
2942
+ const editorRef = useRef(null);
2943
+ const monacoRef = useRef(null);
2944
+ const handleMount = (editor, monaco) => {
2945
+ editorRef.current = editor;
2946
+ monacoRef.current = monaco;
2947
+ };
2948
+ useEffect(() => {
2949
+ const editor = editorRef.current;
2950
+ const monaco = monacoRef.current;
2951
+ if (!editor || !monaco) return;
2952
+ const model = editor.getModel();
2953
+ if (!model) return;
2954
+ const markers = errorsToMarkers(errors);
2955
+ monaco.editor.setModelMarkers(model, "fieldcraft-schema", markers);
2956
+ }, [errors]);
2957
+ return /* @__PURE__ */ jsx(
2958
+ Editor,
2959
+ {
2960
+ height: "100%",
2961
+ language: "json",
2962
+ theme: "vs-dark",
2963
+ value,
2964
+ onChange: (val) => onChange(val ?? ""),
2965
+ onMount: handleMount,
2966
+ options: {
2967
+ minimap: { enabled: false },
2968
+ fontSize: 13,
2969
+ wordWrap: "on",
2970
+ formatOnPaste: true,
2971
+ automaticLayout: true,
2972
+ tabSize: 2,
2973
+ scrollBeyondLastLine: false,
2974
+ padding: { top: 12 }
2975
+ }
2976
+ }
2977
+ );
2978
+ }
2979
+ function JsonEditorPanel({ value, onChange, errors, onValidate }) {
2980
+ const handleChange = useCallback(
2981
+ (newValue) => {
2982
+ onChange(newValue);
2983
+ onValidate(newValue);
2984
+ },
2985
+ [onChange, onValidate]
2986
+ );
2987
+ const handleFormat = useCallback(() => {
2988
+ try {
2989
+ const formatted = JSON.stringify(JSON.parse(value), null, 2);
2990
+ onChange(formatted);
2991
+ } catch {
2992
+ }
2993
+ }, [value, onChange]);
2994
+ return /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col overflow-hidden", children: [
2995
+ 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: [
2996
+ errors.slice(0, 3).map((e, i) => /* @__PURE__ */ jsxs("div", { children: [
2997
+ e.line ? `Line ${e.line}: ` : "",
2998
+ e.path ? `${e.path}: ` : "",
2999
+ e.message
3000
+ ] }, i)),
3001
+ errors.length > 3 && /* @__PURE__ */ jsxs("div", { className: "text-muted-foreground", children: [
3002
+ "...and ",
3003
+ errors.length - 3,
3004
+ " more"
3005
+ ] })
3006
+ ] }),
3007
+ /* @__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: [
3008
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: errors.length === 0 ? "Valid schema" : `${errors.length} error${errors.length !== 1 ? "s" : ""}` }),
3009
+ /* @__PURE__ */ jsx(
3010
+ "button",
3011
+ {
3012
+ type: "button",
3013
+ onClick: handleFormat,
3014
+ className: "text-primary hover:underline",
3015
+ children: "Format JSON"
3016
+ }
3017
+ )
3018
+ ] }),
3019
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsx(
3020
+ Suspense,
3021
+ {
3022
+ fallback: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full text-sm text-muted-foreground", children: "Loading editor..." }),
3023
+ children: /* @__PURE__ */ jsx(MonacoWrapper, { value, onChange: handleChange, errors })
3024
+ }
3025
+ ) })
3026
+ ] });
3027
+ }
3028
+
3029
+ // src/form-builder/utils/logic-graph.ts
3030
+ function extractFieldRefs(expr) {
3031
+ const refs = [];
3032
+ if (expr.field) {
3033
+ refs.push({ field: expr.field, operator: expr.operator, value: expr.value });
3034
+ }
3035
+ if (expr.conditions) {
3036
+ for (const child of expr.conditions) {
3037
+ refs.push(...extractFieldRefs(child));
3038
+ }
3039
+ }
3040
+ return refs;
3041
+ }
3042
+ function buildLogicGraph(schema) {
3043
+ const nodes = [];
3044
+ const edges = [];
3045
+ const nodeIds = /* @__PURE__ */ new Set();
3046
+ for (const section of schema.sections) {
3047
+ nodes.push({
3048
+ id: section.id,
3049
+ label: section.title || "Untitled Section",
3050
+ type: "section",
3051
+ sectionId: section.id,
3052
+ sectionTitle: section.title || "Untitled Section",
3053
+ hasCondition: !!section.showIf
3054
+ });
3055
+ nodeIds.add(section.id);
3056
+ if (section.showIf) {
3057
+ const refs = extractFieldRefs(section.showIf);
3058
+ for (const ref of refs) {
3059
+ edges.push({ from: ref.field, to: section.id, edgeType: "showIf", operator: ref.operator, value: ref.value });
3060
+ }
3061
+ }
3062
+ if (section.onExit) {
3063
+ for (const rule of section.onExit.rules) {
3064
+ if (rule.jumpTo) {
3065
+ edges.push({ from: section.id, to: rule.jumpTo, edgeType: "onExit", jumpTo: rule.jumpTo });
3066
+ }
3067
+ }
3068
+ if (section.onExit.default) {
3069
+ edges.push({ from: section.id, to: section.onExit.default, edgeType: "onExit", jumpTo: section.onExit.default });
3070
+ }
3071
+ }
3072
+ for (const question of section.questions) {
3073
+ nodes.push({
3074
+ id: question.id,
3075
+ label: question.label || question.id,
3076
+ type: "field",
3077
+ fieldType: question.type,
3078
+ sectionId: section.id,
3079
+ sectionTitle: section.title || "Untitled Section",
3080
+ hasCondition: !!question.showIf
3081
+ });
3082
+ nodeIds.add(question.id);
3083
+ if (question.showIf) {
3084
+ const refs = extractFieldRefs(question.showIf);
3085
+ for (const ref of refs) {
3086
+ edges.push({ from: ref.field, to: question.id, edgeType: "showIf", operator: ref.operator, value: ref.value });
3087
+ }
3088
+ }
3089
+ }
3090
+ }
3091
+ const validEdges = edges.filter((e) => nodeIds.has(e.from) && nodeIds.has(e.to));
3092
+ return { nodes, edges: validEdges };
3093
+ }
3094
+ var SECTION_COLORS = [
3095
+ "#3b82f6",
3096
+ // blue
3097
+ "#8b5cf6",
3098
+ // violet
3099
+ "#06b6d4",
3100
+ // cyan
3101
+ "#f59e0b",
3102
+ // amber
3103
+ "#10b981",
3104
+ // emerald
3105
+ "#ef4444",
3106
+ // red
3107
+ "#ec4899",
3108
+ // pink
3109
+ "#6366f1"
3110
+ // indigo
3111
+ ];
3112
+ var FIELD_NODE_WIDTH = 220;
3113
+ var FIELD_NODE_HEIGHT = 52;
3114
+ var SECTION_HEADER_HEIGHT = 40;
3115
+ var SECTION_PADDING_X = 16;
3116
+ var SECTION_PADDING_TOP = 12;
3117
+ var SECTION_PADDING_BOTTOM = 16;
3118
+ var FIELD_GAP = 10;
3119
+ var SECTION_GAP = 80;
3120
+ function formatEdgeLabel(edge) {
3121
+ if (edge.edgeType === "onExit") return "jump";
3122
+ if (!edge.operator) return "";
3123
+ const op = edge.operator;
3124
+ const val = edge.value;
3125
+ if (op === "exists") return "has value";
3126
+ if (op === "notExists") return "is empty";
3127
+ if (val === void 0 || val === null || val === "") return op;
3128
+ const short = String(val).length > 12 ? String(val).slice(0, 12) + "\u2026" : String(val);
3129
+ return `${op} ${short}`;
3130
+ }
3131
+ function buildReactFlowGraph(schema) {
3132
+ const graph = buildLogicGraph(schema);
3133
+ const nodes = [];
3134
+ const edges = [];
3135
+ let sectionX = 0;
3136
+ let maxSectionHeight = 0;
3137
+ for (const section of schema.sections) {
3138
+ const fieldCount = section.questions.length;
3139
+ const contentHeight = SECTION_HEADER_HEIGHT + SECTION_PADDING_TOP + fieldCount * FIELD_NODE_HEIGHT + Math.max(0, fieldCount - 1) * FIELD_GAP + SECTION_PADDING_BOTTOM;
3140
+ if (contentHeight > maxSectionHeight) maxSectionHeight = contentHeight;
3141
+ }
3142
+ if (maxSectionHeight < SECTION_HEADER_HEIGHT + SECTION_PADDING_TOP + SECTION_PADDING_BOTTOM + FIELD_NODE_HEIGHT) {
3143
+ maxSectionHeight = SECTION_HEADER_HEIGHT + SECTION_PADDING_TOP + SECTION_PADDING_BOTTOM + FIELD_NODE_HEIGHT;
3144
+ }
3145
+ const sectionWidth = FIELD_NODE_WIDTH + SECTION_PADDING_X * 2;
3146
+ for (let sIdx = 0; sIdx < schema.sections.length; sIdx++) {
3147
+ const section = schema.sections[sIdx];
3148
+ const colorIndex = sIdx;
3149
+ nodes.push({
3150
+ id: section.id,
3151
+ type: "sectionNode",
3152
+ position: { x: sectionX, y: 0 },
3153
+ data: {
3154
+ label: section.title || "Untitled Section",
3155
+ sectionId: section.id,
3156
+ fieldCount: section.questions.length,
3157
+ hasCondition: !!section.showIf,
3158
+ hasExitLogic: !!section.onExit,
3159
+ colorIndex
3160
+ },
3161
+ style: { width: sectionWidth, height: maxSectionHeight }
3162
+ });
3163
+ for (let fIdx = 0; fIdx < section.questions.length; fIdx++) {
3164
+ const question = section.questions[fIdx];
3165
+ const fieldY = SECTION_HEADER_HEIGHT + SECTION_PADDING_TOP + fIdx * (FIELD_NODE_HEIGHT + FIELD_GAP);
3166
+ nodes.push({
3167
+ id: question.id,
3168
+ type: "fieldNode",
3169
+ position: { x: SECTION_PADDING_X, y: fieldY },
3170
+ parentId: section.id,
3171
+ extent: "parent",
3172
+ data: {
3173
+ label: question.label || question.id,
3174
+ fieldType: question.type,
3175
+ sectionId: section.id,
3176
+ hasCondition: !!question.showIf,
3177
+ colorIndex
3178
+ },
3179
+ style: { width: FIELD_NODE_WIDTH }
3180
+ });
3181
+ }
3182
+ if (sIdx < schema.sections.length - 1) {
3183
+ const nextSection = schema.sections[sIdx + 1];
3184
+ edges.push({
3185
+ id: `pipeline-${section.id}-${nextSection.id}`,
3186
+ source: section.id,
3187
+ target: nextSection.id,
3188
+ type: "pipelineEdge",
3189
+ data: { edgeType: "pipeline" }
3190
+ });
3191
+ }
3192
+ sectionX += sectionWidth + SECTION_GAP;
3193
+ }
3194
+ for (let i = 0; i < graph.edges.length; i++) {
3195
+ const edge = graph.edges[i];
3196
+ edges.push({
3197
+ id: `e-${edge.from}-${edge.to}-${i}`,
3198
+ source: edge.from,
3199
+ target: edge.to,
3200
+ type: "conditionEdge",
3201
+ animated: edge.edgeType === "showIf",
3202
+ style: edge.edgeType === "onExit" ? { strokeDasharray: "6 3", stroke: "#f59e0b" } : void 0,
3203
+ label: formatEdgeLabel(edge),
3204
+ data: {
3205
+ edgeType: edge.edgeType,
3206
+ operator: edge.operator,
3207
+ value: edge.value,
3208
+ sourceId: edge.from,
3209
+ targetId: edge.to
3210
+ }
3211
+ });
3212
+ }
3213
+ return { nodes, edges };
3214
+ }
3215
+ function SectionNodeInner({ data }) {
3216
+ const d = data;
3217
+ const color = SECTION_COLORS[d.colorIndex % SECTION_COLORS.length];
3218
+ return /* @__PURE__ */ jsxs("div", { className: "fc-flow-section-group", style: { borderColor: color }, children: [
3219
+ /* @__PURE__ */ jsx(Handle, { type: "target", position: Position.Left, className: "fc-flow-handle fc-flow-handle--section" }),
3220
+ /* @__PURE__ */ jsxs("div", { className: "fc-flow-section-group__header", style: { background: color }, children: [
3221
+ /* @__PURE__ */ jsx("span", { className: "fc-flow-section-group__label", children: d.label.length > 30 ? d.label.slice(0, 30) + "\u2026" : d.label }),
3222
+ /* @__PURE__ */ jsxs("span", { className: "fc-flow-section-group__meta", children: [
3223
+ d.fieldCount,
3224
+ " field",
3225
+ d.fieldCount !== 1 ? "s" : "",
3226
+ d.hasCondition && /* @__PURE__ */ jsx("span", { className: "fc-flow-section-group__badge", title: "Has showIf condition", children: "?" }),
3227
+ d.hasExitLogic && /* @__PURE__ */ jsx("span", { className: "fc-flow-section-group__badge fc-flow-section-group__badge--jump", title: "Has onExit jump", children: "\u2934" })
3228
+ ] })
3229
+ ] }),
3230
+ /* @__PURE__ */ jsx(Handle, { type: "source", position: Position.Right, className: "fc-flow-handle fc-flow-handle--section" })
3231
+ ] });
3232
+ }
3233
+ var SectionNode = memo(SectionNodeInner);
3234
+ function FieldNodeInner({ data }) {
3235
+ const d = data;
3236
+ const color = SECTION_COLORS[d.colorIndex % SECTION_COLORS.length];
3237
+ return /* @__PURE__ */ jsxs(
3238
+ "div",
3239
+ {
3240
+ className: "fc-flow-field",
3241
+ style: {
3242
+ borderColor: d.hasCondition ? color : void 0,
3243
+ borderWidth: d.hasCondition ? 2 : 1
3244
+ },
3245
+ children: [
3246
+ /* @__PURE__ */ jsx(Handle, { type: "target", position: Position.Left, className: "fc-flow-handle" }),
3247
+ /* @__PURE__ */ jsxs("div", { className: "fc-flow-field__content", children: [
3248
+ /* @__PURE__ */ jsx("span", { className: "fc-flow-field__label", children: d.label.length > 26 ? d.label.slice(0, 26) + "\u2026" : d.label }),
3249
+ /* @__PURE__ */ jsx("span", { className: "fc-flow-field__type", children: d.fieldType })
3250
+ ] }),
3251
+ d.hasCondition && /* @__PURE__ */ jsx("div", { className: "fc-flow-badge", style: { background: color }, title: "Has showIf condition", children: "?" }),
3252
+ /* @__PURE__ */ jsx(Handle, { type: "source", position: Position.Right, className: "fc-flow-handle" })
3253
+ ]
3254
+ }
3255
+ );
3256
+ }
3257
+ var FieldNode = memo(FieldNodeInner);
3258
+ var logicFlowNodeTypes = {
3259
+ sectionNode: SectionNode,
3260
+ fieldNode: FieldNode
3261
+ };
3262
+ function ConditionEdgeInner({
3263
+ id,
3264
+ sourceX,
3265
+ sourceY,
3266
+ targetX,
3267
+ targetY,
3268
+ sourcePosition,
3269
+ targetPosition,
3270
+ label,
3271
+ style,
3272
+ data,
3273
+ markerEnd
3274
+ }) {
3275
+ const [edgePath, labelX, labelY] = getBezierPath({
3276
+ sourceX,
3277
+ sourceY,
3278
+ sourcePosition,
3279
+ targetX,
3280
+ targetY,
3281
+ targetPosition
3282
+ });
3283
+ const isJump = data?.edgeType === "onExit";
3284
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3285
+ /* @__PURE__ */ jsx(
3286
+ BaseEdge,
3287
+ {
3288
+ id,
3289
+ path: edgePath,
3290
+ markerEnd,
3291
+ style: {
3292
+ stroke: isJump ? "#f59e0b" : "var(--primary, #3b82f6)",
3293
+ strokeWidth: 1.5,
3294
+ strokeOpacity: 0.7,
3295
+ ...style
3296
+ }
3297
+ }
3298
+ ),
3299
+ label && /* @__PURE__ */ jsx(EdgeLabelRenderer, { children: /* @__PURE__ */ jsx(
3300
+ "div",
3301
+ {
3302
+ className: "fc-flow-edge-label",
3303
+ style: {
3304
+ transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
3305
+ background: isJump ? "#f59e0b" : "var(--primary, #3b82f6)"
3306
+ },
3307
+ children: label
3308
+ }
3309
+ ) })
3310
+ ] });
3311
+ }
3312
+ var ConditionEdge = memo(ConditionEdgeInner);
3313
+ function PipelineEdgeInner({
3314
+ id,
3315
+ sourceX,
3316
+ sourceY,
3317
+ targetX,
3318
+ targetY,
3319
+ sourcePosition,
3320
+ targetPosition,
3321
+ markerEnd
3322
+ }) {
3323
+ const [edgePath] = getBezierPath({
3324
+ sourceX,
3325
+ sourceY,
3326
+ sourcePosition,
3327
+ targetX,
3328
+ targetY,
3329
+ targetPosition
3330
+ });
3331
+ return /* @__PURE__ */ jsx(
3332
+ BaseEdge,
3333
+ {
3334
+ id,
3335
+ path: edgePath,
3336
+ markerEnd,
3337
+ style: {
3338
+ stroke: "var(--muted-foreground, #71717a)",
3339
+ strokeWidth: 2,
3340
+ strokeOpacity: 0.4
3341
+ }
3342
+ }
3343
+ );
3344
+ }
3345
+ var PipelineEdge = memo(PipelineEdgeInner);
3346
+ var logicFlowEdgeTypes = {
3347
+ conditionEdge: ConditionEdge,
3348
+ pipelineEdge: PipelineEdge
3349
+ };
3350
+ function findQuestion2(schema, fieldId) {
3351
+ for (const section of schema.sections) {
3352
+ for (const q of section.questions) {
3353
+ if (q.id === fieldId) return q;
3354
+ }
3355
+ }
3356
+ return void 0;
3357
+ }
3358
+ function updateQuestionInSchema(schema, fieldId, updates) {
3359
+ return {
3360
+ ...schema,
3361
+ sections: schema.sections.map((section) => ({
3362
+ ...section,
3363
+ questions: section.questions.map(
3364
+ (q) => q.id === fieldId ? { ...q, ...updates } : q
3365
+ )
3366
+ }))
3367
+ };
3368
+ }
3369
+ function removeConditionForSource(showIf, sourceFieldId) {
3370
+ if (!showIf) return void 0;
3371
+ if (showIf.field === sourceFieldId) return void 0;
3372
+ if (showIf.field && showIf.field !== sourceFieldId) return showIf;
3373
+ if (showIf.conditions) {
3374
+ const filtered = showIf.conditions.map((c) => removeConditionForSource(c, sourceFieldId)).filter((c) => c !== void 0);
3375
+ if (filtered.length === 0) return void 0;
3376
+ if (filtered.length === 1) return filtered[0];
3377
+ return { ...showIf, conditions: filtered };
3378
+ }
3379
+ return showIf;
3380
+ }
3381
+ var defaultEdgeOptions = {
3382
+ markerEnd: {
3383
+ type: MarkerType.ArrowClosed,
3384
+ width: 16,
3385
+ height: 12
3386
+ }
3387
+ };
3388
+ function LogicFlowEditor({ schema, onChange, onClose }) {
3389
+ const noSections = schema.sections.length === 0;
3390
+ const flowGraph = useMemo(() => buildReactFlowGraph(schema), [schema]);
3391
+ const [nodes, setNodes, onNodesChange] = useNodesState(flowGraph.nodes);
3392
+ const [edges, setEdges, onEdgesChange] = useEdgesState(flowGraph.edges);
3393
+ const [selectedFieldId, setSelectedFieldId] = useState(null);
3394
+ const selectedQuestion = selectedFieldId ? findQuestion2(schema, selectedFieldId) : void 0;
3395
+ useEffect(() => {
3396
+ setNodes(flowGraph.nodes);
3397
+ setEdges(flowGraph.edges);
3398
+ }, [flowGraph, setNodes, setEdges]);
3399
+ const sectionIds = useMemo(() => {
3400
+ const ids = [];
3401
+ for (const section of schema.sections) {
3402
+ ids.push(section.id);
3403
+ }
3404
+ return ids;
3405
+ }, [schema]);
3406
+ const onNodeClick = useCallback(
3407
+ (_event, node) => {
3408
+ if (node.type === "fieldNode") {
3409
+ setSelectedFieldId(node.id);
3410
+ }
3411
+ },
3412
+ []
3413
+ );
3414
+ const onConnect = useCallback(
3415
+ (connection) => {
3416
+ if (!connection.source || !connection.target) return;
3417
+ const targetQuestion = findQuestion2(schema, connection.target);
3418
+ if (!targetQuestion) return;
3419
+ const newCondition = {
3420
+ field: connection.source,
3421
+ operator: "exists"
3422
+ };
3423
+ let updatedShowIf;
3424
+ const existing = targetQuestion.showIf;
3425
+ if (!existing) {
3426
+ updatedShowIf = { combine: "AND", conditions: [newCondition] };
3427
+ } else {
3428
+ updatedShowIf = {
3429
+ ...existing,
3430
+ conditions: [...existing.conditions ?? [], newCondition]
3431
+ };
3432
+ }
3433
+ const updatedSchema = updateQuestionInSchema(schema, connection.target, {
3434
+ showIf: updatedShowIf
3435
+ });
3436
+ onChange(updatedSchema);
3437
+ setSelectedFieldId(connection.target);
3438
+ },
3439
+ [schema, onChange]
3440
+ );
3441
+ const onEdgesDelete = useCallback(
3442
+ (deletedEdges) => {
3443
+ let updatedSchema = schema;
3444
+ for (const edge of deletedEdges) {
3445
+ const edgeData = edge.data;
3446
+ if (!edgeData) continue;
3447
+ const targetId = edge.target;
3448
+ const sourceId = edge.source;
3449
+ const edgeType = edgeData.edgeType;
3450
+ if (edgeType === "showIf") {
3451
+ const question = findQuestion2(updatedSchema, targetId);
3452
+ if (!question) continue;
3453
+ const updatedShowIf = removeConditionForSource(
3454
+ question.showIf,
3455
+ sourceId
3456
+ );
3457
+ updatedSchema = updateQuestionInSchema(updatedSchema, targetId, {
3458
+ showIf: updatedShowIf
3459
+ });
3460
+ }
3461
+ }
3462
+ onChange(updatedSchema);
3463
+ },
3464
+ [schema, onChange]
3465
+ );
3466
+ const handleConditionUpdate = useCallback(
3467
+ (updates) => {
3468
+ if (!selectedFieldId) return;
3469
+ const updatedSchema = updateQuestionInSchema(schema, selectedFieldId, updates);
3470
+ onChange(updatedSchema);
3471
+ },
3472
+ [schema, selectedFieldId, onChange]
3473
+ );
3474
+ const showIfCount = flowGraph.edges.filter(
3475
+ (e) => e.data?.edgeType === "showIf"
3476
+ ).length;
3477
+ const jumpCount = flowGraph.edges.filter(
3478
+ (e) => e.data?.edgeType === "onExit"
3479
+ ).length;
3480
+ return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex flex-col bg-background/95 backdrop-blur-sm", children: [
3481
+ /* @__PURE__ */ jsxs("div", { className: "h-12 shrink-0 flex items-center justify-between px-4 border-b border-border bg-card", children: [
3482
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3483
+ /* @__PURE__ */ jsx(GitBranch, { size: 16, className: "text-primary" }),
3484
+ /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold", children: "Logic Flow" }),
3485
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
3486
+ showIfCount,
3487
+ " condition",
3488
+ showIfCount !== 1 ? "s" : "",
3489
+ jumpCount > 0 && ` \xB7 ${jumpCount} jump${jumpCount !== 1 ? "s" : ""}`,
3490
+ " \xB7 ",
3491
+ sectionIds.length,
3492
+ " section",
3493
+ sectionIds.length !== 1 ? "s" : ""
3494
+ ] })
3495
+ ] }),
3496
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
3497
+ /* @__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" }),
3498
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", onClick: onClose, title: "Close logic flow", children: /* @__PURE__ */ jsx(X, { size: 16, strokeWidth: 1.75 }) })
3499
+ ] })
3500
+ ] }),
3501
+ 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: [
3502
+ /* @__PURE__ */ jsx(GitBranch, { size: 32, className: "mx-auto opacity-40" }),
3503
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: "No sections found" }),
3504
+ /* @__PURE__ */ jsx("p", { className: "text-xs max-w-xs", children: "Add sections and fields to your form to see the logic flow pipeline." })
3505
+ ] }) }) : /* @__PURE__ */ jsxs("div", { className: "flex-1 flex", children: [
3506
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxs(
3507
+ ReactFlow,
3508
+ {
3509
+ nodes,
3510
+ edges,
3511
+ onNodesChange,
3512
+ onEdgesChange,
3513
+ onNodeClick,
3514
+ onConnect,
3515
+ onEdgesDelete,
3516
+ nodeTypes: logicFlowNodeTypes,
3517
+ edgeTypes: logicFlowEdgeTypes,
3518
+ defaultEdgeOptions,
3519
+ fitView: true,
3520
+ fitViewOptions: { padding: 0.2 },
3521
+ deleteKeyCode: ["Backspace", "Delete"],
3522
+ proOptions: { hideAttribution: true },
3523
+ children: [
3524
+ /* @__PURE__ */ jsx(Background, { gap: 20, size: 1 }),
3525
+ /* @__PURE__ */ jsx(Controls, { showInteractive: false }),
3526
+ /* @__PURE__ */ jsx(
3527
+ MiniMap,
3528
+ {
3529
+ nodeColor: (node) => {
3530
+ const d = node.data;
3531
+ const idx = d.colorIndex ?? 0;
3532
+ return SECTION_COLORS[idx % SECTION_COLORS.length];
3533
+ },
3534
+ maskColor: "rgba(0, 0, 0, 0.3)"
3535
+ }
3536
+ )
3537
+ ]
3538
+ }
3539
+ ) }),
3540
+ selectedFieldId && selectedQuestion && /* @__PURE__ */ jsxs("div", { className: "w-72 border-l border-border bg-card overflow-auto", children: [
3541
+ /* @__PURE__ */ jsxs("div", { className: "px-3 py-3 border-b border-border flex items-center justify-between", children: [
3542
+ /* @__PURE__ */ jsxs("div", { children: [
3543
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold truncate", children: selectedQuestion.label || selectedQuestion.id }),
3544
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground", children: selectedQuestion.type })
3545
+ ] }),
3546
+ /* @__PURE__ */ jsx(
3547
+ Button,
3548
+ {
3549
+ variant: "ghost",
3550
+ size: "icon-xs",
3551
+ onClick: () => setSelectedFieldId(null),
3552
+ title: "Close panel",
3553
+ children: /* @__PURE__ */ jsx(X, { size: 14, strokeWidth: 1.75 })
3554
+ }
3555
+ )
3556
+ ] }),
3557
+ /* @__PURE__ */ jsx("div", { className: "px-3 py-3", children: /* @__PURE__ */ jsx(
3558
+ ConditionEditor,
3559
+ {
3560
+ question: selectedQuestion,
3561
+ schema,
3562
+ onUpdate: handleConditionUpdate
3563
+ }
3564
+ ) })
3565
+ ] })
3566
+ ] }),
3567
+ /* @__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: [
3568
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
3569
+ /* @__PURE__ */ jsx("div", { className: "w-5 h-0.5 rounded", style: { background: "var(--muted-foreground)", opacity: 0.4 } }),
3570
+ /* @__PURE__ */ jsx("span", { children: "flow" })
3571
+ ] }),
3572
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
3573
+ /* @__PURE__ */ jsx("div", { className: "w-5 h-0.5 bg-primary rounded" }),
3574
+ /* @__PURE__ */ jsx("span", { children: "showIf" })
3575
+ ] }),
3576
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
3577
+ /* @__PURE__ */ jsx("div", { className: "w-5 h-0.5 rounded", style: { background: "#f59e0b" } }),
3578
+ /* @__PURE__ */ jsx("span", { children: "jump" })
3579
+ ] }),
3580
+ sectionIds.map((id, i) => {
3581
+ const section = schema.sections.find((s) => s.id === id);
3582
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
3583
+ /* @__PURE__ */ jsx("div", { className: "w-3 h-3 rounded-sm", style: { background: SECTION_COLORS[i % SECTION_COLORS.length] } }),
3584
+ /* @__PURE__ */ jsx("span", { children: section?.title || id })
3585
+ ] }, id);
3586
+ })
3587
+ ] })
3588
+ ] });
3589
+ }
3590
+ function TemplateGallery({ templates, onSelect, onClose }) {
3591
+ const [search, setSearch] = useState("");
3592
+ const [activeCategory, setActiveCategory] = useState("all");
3593
+ const [confirmTemplate, setConfirmTemplate] = useState(null);
3594
+ const categories = useMemo(() => {
3595
+ const cats = /* @__PURE__ */ new Set();
3596
+ for (const t of templates) cats.add(t.meta.category);
3597
+ return ["all", ...Array.from(cats)];
3598
+ }, [templates]);
3599
+ const filtered = useMemo(() => {
3600
+ return templates.filter((t) => {
3601
+ const matchesCategory = activeCategory === "all" || t.meta.category === activeCategory;
3602
+ 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()));
3603
+ return matchesCategory && matchesSearch;
3604
+ });
3605
+ }, [templates, activeCategory, search]);
3606
+ const handleConfirm = () => {
3607
+ if (confirmTemplate) {
3608
+ onSelect(confirmTemplate);
3609
+ setConfirmTemplate(null);
3610
+ onClose();
3611
+ }
3612
+ };
3613
+ 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: [
3614
+ /* @__PURE__ */ jsxs("div", { className: "shrink-0 flex items-center justify-between px-4 py-3 border-b border-border", children: [
3615
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3616
+ /* @__PURE__ */ jsx(LayoutTemplate, { size: 18, className: "text-primary", strokeWidth: 1.75 }),
3617
+ /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold", children: "Template Gallery" }),
3618
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
3619
+ "(",
3620
+ templates.length,
3621
+ " templates)"
3622
+ ] })
3623
+ ] }),
3624
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", onClick: onClose, title: "Close", children: /* @__PURE__ */ jsx(X, { size: 16, strokeWidth: 1.75 }) })
3625
+ ] }),
3626
+ /* @__PURE__ */ jsxs("div", { className: "shrink-0 px-4 py-2 space-y-2 border-b border-border", children: [
3627
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
3628
+ /* @__PURE__ */ jsx(Search, { size: 14, className: "absolute left-2.5 top-1/2 -translate-y-1/2 text-muted-foreground", strokeWidth: 1.75 }),
3629
+ /* @__PURE__ */ jsx(
3630
+ "input",
3631
+ {
3632
+ type: "text",
3633
+ placeholder: "Search templates...",
3634
+ value: search,
3635
+ onChange: (e) => setSearch(e.target.value),
3636
+ 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"
3637
+ }
3638
+ )
3639
+ ] }),
3640
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 overflow-x-auto pb-0.5", children: categories.map((cat) => /* @__PURE__ */ jsx(
3641
+ "button",
3642
+ {
3643
+ type: "button",
3644
+ onClick: () => setActiveCategory(cat),
3645
+ 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"}`,
3646
+ children: cat === "all" ? "All" : cat.charAt(0).toUpperCase() + cat.slice(1)
3647
+ },
3648
+ cat
3649
+ )) })
3650
+ ] }),
3651
+ /* @__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(
3652
+ "button",
3653
+ {
3654
+ type: "button",
3655
+ onClick: () => setConfirmTemplate(template),
3656
+ className: "text-left p-3 rounded-lg border border-border bg-background hover:border-primary/50 hover:bg-primary/5 transition-colors group",
3657
+ children: [
3658
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2 mb-1.5", children: [
3659
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium text-foreground group-hover:text-primary transition-colors", children: template.meta.name }),
3660
+ /* @__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 })
3661
+ ] }),
3662
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mb-2 line-clamp-2", children: template.meta.description }),
3663
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
3664
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
3665
+ /* @__PURE__ */ jsx(FileText, { size: 11, strokeWidth: 1.75 }),
3666
+ template.meta.fieldCount,
3667
+ " field",
3668
+ template.meta.fieldCount !== 1 ? "s" : ""
3669
+ ] }),
3670
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
3671
+ /* @__PURE__ */ jsx(Layers, { size: 11, strokeWidth: 1.75 }),
3672
+ template.meta.sectionCount,
3673
+ " section",
3674
+ template.meta.sectionCount !== 1 ? "s" : ""
3675
+ ] })
3676
+ ] })
3677
+ ]
3678
+ },
3679
+ template.meta.id
3680
+ )) }) }),
3681
+ 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: [
3682
+ /* @__PURE__ */ jsxs("div", { className: "text-sm", children: [
3683
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Use " }),
3684
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: confirmTemplate.meta.name }),
3685
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "? This will replace the current form." })
3686
+ ] }),
3687
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
3688
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => setConfirmTemplate(null), children: "Cancel" }),
3689
+ /* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleConfirm, children: "Use Template" })
3690
+ ] })
3691
+ ] })
3692
+ ] }) });
3693
+ }
3694
+ function useDebouncedValidation(delayMs = 500) {
3695
+ const [errors, setErrors] = useState([]);
3696
+ const timerRef = useRef(null);
3697
+ const validate = useCallback(
3698
+ (text) => {
3699
+ if (timerRef.current) {
3700
+ clearTimeout(timerRef.current);
3701
+ }
3702
+ timerRef.current = setTimeout(() => {
3703
+ const result = [];
3704
+ let parsed;
3705
+ try {
3706
+ parsed = JSON.parse(text);
3707
+ } catch (err) {
3708
+ if (err instanceof SyntaxError) {
3709
+ const posMatch = err.message.match(/position\s+(\d+)/i);
3710
+ let line = 1;
3711
+ let column = 1;
3712
+ if (posMatch) {
3713
+ const pos = parseInt(posMatch[1], 10);
3714
+ const upToPos = text.slice(0, pos);
3715
+ line = (upToPos.match(/\n/g) || []).length + 1;
3716
+ column = pos - upToPos.lastIndexOf("\n");
3717
+ }
3718
+ result.push({ message: err.message, line, column, endLine: line, endColumn: column + 1 });
3719
+ } else {
3720
+ result.push({ message: "Invalid JSON" });
3721
+ }
3722
+ setErrors(result);
3723
+ return;
3724
+ }
3725
+ try {
3726
+ validateSchema(parsed);
3727
+ } catch (err) {
3728
+ if (err instanceof FormEngineSchemaError) {
3729
+ for (const issue of err.issues) {
3730
+ result.push({
3731
+ message: issue.message,
3732
+ path: issue.path?.join(".")
3733
+ });
3734
+ }
3735
+ } else if (err instanceof Error) {
3736
+ result.push({ message: err.message });
3737
+ }
3738
+ }
3739
+ setErrors(result);
3740
+ }, delayMs);
3741
+ },
3742
+ [delayMs]
3743
+ );
3744
+ return { errors, validate };
3745
+ }
2788
3746
  var ThemeCtx = createContext({});
2789
3747
  function useBuilderTheme() {
2790
3748
  return useContext(ThemeCtx);
@@ -2857,7 +3815,16 @@ var FormBuilderErrorBoundary = class extends Component {
2857
3815
  }
2858
3816
  };
2859
3817
  function FormBuilderCore(props) {
2860
- const { initialSchema = DEFAULT_SCHEMA, onChange, onSave, height = "100vh", theme, className, toolbarExtra, questionTypes, palette } = props;
3818
+ const { initialSchema = DEFAULT_SCHEMA, onChange, onSave, height = "100vh", theme, className, toolbarExtra, questionTypes, palette, preview, templates, schemaUrl } = props;
3819
+ const [viewMode, setViewMode] = useState("design");
3820
+ const [jsonText, setJsonText] = useState("");
3821
+ const [jsonSwitchError, setJsonSwitchError] = useState(null);
3822
+ const [showLogicMap, setShowLogicMap] = useState(false);
3823
+ const [showTemplateGallery, setShowTemplateGallery] = useState(false);
3824
+ const [saveValidationErrors, setSaveValidationErrors] = useState(null);
3825
+ const [schemaUrlLoading, setSchemaUrlLoading] = useState(!!schemaUrl);
3826
+ const [schemaUrlError, setSchemaUrlError] = useState(null);
3827
+ const { errors: jsonErrors, validate: validateJson } = useDebouncedValidation(400);
2861
3828
  const mergedQuestionTypes = questionTypes ? { ...QUESTION_TYPE_INFO, ...questionTypes } : QUESTION_TYPE_INFO;
2862
3829
  const builderState = useBuilderState(initialSchema);
2863
3830
  const dragDrop = useDragDrop(builderState);
@@ -2873,15 +3840,54 @@ function FormBuilderCore(props) {
2873
3840
  setMobilePanel("none");
2874
3841
  }
2875
3842
  }, [dragDrop.activeDragItem]);
3843
+ useEffect(() => {
3844
+ if (!schemaUrl) return;
3845
+ let cancelled = false;
3846
+ setSchemaUrlLoading(true);
3847
+ setSchemaUrlError(null);
3848
+ fetch(schemaUrl).then((res) => {
3849
+ if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
3850
+ return res.json();
3851
+ }).then((json) => {
3852
+ if (cancelled) return;
3853
+ try {
3854
+ const validated = validateSchema(json);
3855
+ builderState.resetSchema(validated);
3856
+ } catch (err) {
3857
+ if (err instanceof FormEngineSchemaError) {
3858
+ setSchemaUrlError(`Invalid schema: ${err.issues[0]?.message ?? "validation failed"}`);
3859
+ } else {
3860
+ setSchemaUrlError(err.message);
3861
+ }
3862
+ }
3863
+ setSchemaUrlLoading(false);
3864
+ }).catch((err) => {
3865
+ if (cancelled) return;
3866
+ setSchemaUrlError(err.message ?? "Failed to fetch schema");
3867
+ setSchemaUrlLoading(false);
3868
+ });
3869
+ return () => {
3870
+ cancelled = true;
3871
+ };
3872
+ }, [schemaUrl]);
2876
3873
  useEffect(() => {
2877
3874
  if (onChange && builderState.isDirty) {
2878
3875
  onChange(builderState.schema);
2879
3876
  }
2880
3877
  }, [builderState.schema, builderState.isDirty, onChange]);
2881
3878
  const handleSave = useCallback(() => {
2882
- if (onSave) {
3879
+ if (!onSave) return;
3880
+ try {
3881
+ validateSchema(builderState.schema);
3882
+ setSaveValidationErrors(null);
2883
3883
  onSave(builderState.schema);
2884
3884
  builderState.markClean();
3885
+ } catch (err) {
3886
+ if (err instanceof FormEngineSchemaError) {
3887
+ setSaveValidationErrors(err.issues.map((issue) => issue.message));
3888
+ } else {
3889
+ setSaveValidationErrors([err.message ?? "Unknown validation error"]);
3890
+ }
2885
3891
  }
2886
3892
  }, [onSave, builderState]);
2887
3893
  const handleExport = useCallback(() => {
@@ -2924,6 +3930,32 @@ ${details}`);
2924
3930
  },
2925
3931
  [builderState]
2926
3932
  );
3933
+ const handleViewModeChange = useCallback(
3934
+ (newMode) => {
3935
+ setJsonSwitchError(null);
3936
+ if (viewMode === "json" && newMode !== "json") {
3937
+ try {
3938
+ const parsed = JSON.parse(jsonText);
3939
+ const validated = validateSchema(parsed);
3940
+ builderState.resetSchema(validated);
3941
+ } catch (err) {
3942
+ if (err instanceof SyntaxError) {
3943
+ setJsonSwitchError("Cannot switch: JSON has syntax errors");
3944
+ } else if (err instanceof FormEngineSchemaError) {
3945
+ setJsonSwitchError(`Cannot switch: ${err.issues[0]?.message ?? "Invalid schema"}`);
3946
+ } else {
3947
+ setJsonSwitchError("Cannot switch: Invalid schema");
3948
+ }
3949
+ return;
3950
+ }
3951
+ }
3952
+ if (newMode === "json") {
3953
+ setJsonText(JSON.stringify(builderState.schema, null, 2));
3954
+ }
3955
+ setViewMode(newMode);
3956
+ },
3957
+ [viewMode, jsonText, builderState]
3958
+ );
2927
3959
  const handleKeyDown = useCallback(
2928
3960
  (e) => {
2929
3961
  const mod = e.metaKey || e.ctrlKey;
@@ -3025,8 +4057,74 @@ ${details}`);
3025
4057
  /* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "mx-2 h-5" }),
3026
4058
  builderState.isDirty && /* @__PURE__ */ jsx("span", { className: "text-xs text-primary", role: "status", children: "Unsaved changes" })
3027
4059
  ] }),
3028
- /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center", children: toolbarExtra }),
4060
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2", children: [
4061
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 bg-muted rounded-md p-0.5", children: [
4062
+ /* @__PURE__ */ jsxs(
4063
+ Button,
4064
+ {
4065
+ variant: viewMode === "design" ? "secondary" : "ghost",
4066
+ size: "sm",
4067
+ onClick: () => handleViewModeChange("design"),
4068
+ className: "gap-1.5 h-7 px-2.5 text-xs",
4069
+ children: [
4070
+ /* @__PURE__ */ jsx(PencilRuler, { size: 13, strokeWidth: 1.75 }),
4071
+ "Design"
4072
+ ]
4073
+ }
4074
+ ),
4075
+ /* @__PURE__ */ jsxs(
4076
+ Button,
4077
+ {
4078
+ variant: viewMode === "preview" ? "secondary" : "ghost",
4079
+ size: "sm",
4080
+ onClick: () => handleViewModeChange("preview"),
4081
+ className: "gap-1.5 h-7 px-2.5 text-xs",
4082
+ children: [
4083
+ /* @__PURE__ */ jsx(Eye, { size: 13, strokeWidth: 1.75 }),
4084
+ "Preview"
4085
+ ]
4086
+ }
4087
+ ),
4088
+ /* @__PURE__ */ jsxs(
4089
+ Button,
4090
+ {
4091
+ variant: viewMode === "json" ? "secondary" : "ghost",
4092
+ size: "sm",
4093
+ onClick: () => handleViewModeChange("json"),
4094
+ className: "gap-1.5 h-7 px-2.5 text-xs",
4095
+ children: [
4096
+ /* @__PURE__ */ jsx(Code, { size: 13, strokeWidth: 1.75 }),
4097
+ "JSON"
4098
+ ]
4099
+ }
4100
+ )
4101
+ ] }),
4102
+ toolbarExtra
4103
+ ] }),
3029
4104
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-1.5", children: [
4105
+ templates && templates.length > 0 && /* @__PURE__ */ jsx(
4106
+ Button,
4107
+ {
4108
+ variant: "ghost",
4109
+ size: "icon-sm",
4110
+ onClick: () => setShowTemplateGallery(true),
4111
+ title: "Browse templates",
4112
+ "aria-label": "Template gallery",
4113
+ children: /* @__PURE__ */ jsx(LayoutTemplate, { size: 15, strokeWidth: 1.75 })
4114
+ }
4115
+ ),
4116
+ /* @__PURE__ */ jsx(
4117
+ Button,
4118
+ {
4119
+ variant: "ghost",
4120
+ size: "icon-sm",
4121
+ onClick: () => setShowLogicMap(true),
4122
+ title: "View branching logic map",
4123
+ "aria-label": "Logic map",
4124
+ children: /* @__PURE__ */ jsx(GitBranch, { size: 15, strokeWidth: 1.75 })
4125
+ }
4126
+ ),
4127
+ /* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "mx-1 h-5" }),
3030
4128
  /* @__PURE__ */ jsx(
3031
4129
  Button,
3032
4130
  {
@@ -3070,6 +4168,47 @@ ${details}`);
3070
4168
  }
3071
4169
  ) }),
3072
4170
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-1", children: [
4171
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 bg-muted rounded-md p-0.5", children: [
4172
+ /* @__PURE__ */ jsxs(
4173
+ Button,
4174
+ {
4175
+ variant: viewMode === "design" ? "secondary" : "ghost",
4176
+ size: "sm",
4177
+ onClick: () => handleViewModeChange("design"),
4178
+ className: "gap-1 h-7 px-2 text-xs",
4179
+ children: [
4180
+ /* @__PURE__ */ jsx(PencilRuler, { size: 12, strokeWidth: 1.75 }),
4181
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Design" })
4182
+ ]
4183
+ }
4184
+ ),
4185
+ /* @__PURE__ */ jsxs(
4186
+ Button,
4187
+ {
4188
+ variant: viewMode === "preview" ? "secondary" : "ghost",
4189
+ size: "sm",
4190
+ onClick: () => handleViewModeChange("preview"),
4191
+ className: "gap-1 h-7 px-2 text-xs",
4192
+ children: [
4193
+ /* @__PURE__ */ jsx(Eye, { size: 12, strokeWidth: 1.75 }),
4194
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Preview" })
4195
+ ]
4196
+ }
4197
+ ),
4198
+ /* @__PURE__ */ jsxs(
4199
+ Button,
4200
+ {
4201
+ variant: viewMode === "json" ? "secondary" : "ghost",
4202
+ size: "sm",
4203
+ onClick: () => handleViewModeChange("json"),
4204
+ className: "gap-1 h-7 px-2 text-xs",
4205
+ children: [
4206
+ /* @__PURE__ */ jsx(Code, { size: 12, strokeWidth: 1.75 }),
4207
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "JSON" })
4208
+ ]
4209
+ }
4210
+ )
4211
+ ] }),
3073
4212
  toolbarExtra,
3074
4213
  /* @__PURE__ */ jsxs(Button, { onClick: handleSave, size: "sm", className: "border-0 shadow-sm fcb-glow ml-1", "aria-label": "Save form", children: [
3075
4214
  /* @__PURE__ */ jsx(Save, { size: 14, strokeWidth: 2 }),
@@ -3158,7 +4297,50 @@ ${details}`);
3158
4297
  "aria-hidden": "true"
3159
4298
  }
3160
4299
  ),
3161
- /* @__PURE__ */ jsxs("div", { className: "flex-1 flex overflow-hidden relative", children: [
4300
+ 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: [
4301
+ /* @__PURE__ */ jsx("span", { children: jsonSwitchError }),
4302
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setJsonSwitchError(null), className: "ml-2 hover:underline", children: "Dismiss" })
4303
+ ] }),
4304
+ 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: [
4305
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-1", children: [
4306
+ /* @__PURE__ */ jsxs("span", { className: "font-semibold text-destructive", children: [
4307
+ "Schema validation failed (",
4308
+ saveValidationErrors.length,
4309
+ " issue",
4310
+ saveValidationErrors.length !== 1 ? "s" : "",
4311
+ ")"
4312
+ ] }),
4313
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setSaveValidationErrors(null), className: "text-destructive hover:underline", children: "Dismiss" })
4314
+ ] }),
4315
+ /* @__PURE__ */ jsxs("ul", { className: "list-disc pl-4 text-destructive/80 space-y-0.5", children: [
4316
+ saveValidationErrors.slice(0, 10).map((msg, i) => /* @__PURE__ */ jsx("li", { children: msg }, i)),
4317
+ saveValidationErrors.length > 10 && /* @__PURE__ */ jsxs("li", { children: [
4318
+ "...and ",
4319
+ saveValidationErrors.length - 10,
4320
+ " more"
4321
+ ] })
4322
+ ] })
4323
+ ] }),
4324
+ 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: [
4325
+ /* @__PURE__ */ jsx("span", { className: "inline-block w-3 h-3 border-2 border-primary border-t-transparent rounded-full animate-spin" }),
4326
+ "Loading schema from URL..."
4327
+ ] }),
4328
+ 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: [
4329
+ /* @__PURE__ */ jsxs("span", { children: [
4330
+ "Failed to load schema: ",
4331
+ schemaUrlError
4332
+ ] }),
4333
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setSchemaUrlError(null), className: "ml-2 hover:underline", children: "Dismiss" })
4334
+ ] }),
4335
+ /* @__PURE__ */ jsx("div", { className: "flex-1 flex overflow-hidden relative", children: viewMode === "json" ? /* @__PURE__ */ jsx(
4336
+ JsonEditorPanel,
4337
+ {
4338
+ value: jsonText,
4339
+ onChange: setJsonText,
4340
+ errors: jsonErrors,
4341
+ onValidate: validateJson
4342
+ }
4343
+ ) : viewMode === "preview" ? /* @__PURE__ */ jsx(FormPreviewPanel, { schema: builderState.schema, preview }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3162
4344
  /* @__PURE__ */ jsx("div", { className: "hidden md:flex", children: /* @__PURE__ */ jsx(QuestionPalette, { questionTypes: mergedQuestionTypes, palette }) }),
3163
4345
  /* @__PURE__ */ jsx(FormCanvas, { builderState }),
3164
4346
  /* @__PURE__ */ jsx("div", { className: "hidden md:flex", children: /* @__PURE__ */ jsx(PropertiesPanel, { builderState }) }),
@@ -3172,11 +4354,27 @@ ${details}`);
3172
4354
  "aria-hidden": "true"
3173
4355
  }
3174
4356
  )
3175
- ] })
4357
+ ] }) })
3176
4358
  ]
3177
4359
  }
3178
4360
  ),
3179
- /* @__PURE__ */ jsx(DragOverlay, { dropAnimation: null, children: dragDrop.activeDragItem && /* @__PURE__ */ jsx(DragOverlayContent, { item: dragDrop.activeDragItem, schema: builderState.schema, questionTypes: mergedQuestionTypes }) })
4361
+ /* @__PURE__ */ jsx(DragOverlay, { dropAnimation: null, children: dragDrop.activeDragItem && /* @__PURE__ */ jsx(DragOverlayContent, { item: dragDrop.activeDragItem, schema: builderState.schema, questionTypes: mergedQuestionTypes }) }),
4362
+ showLogicMap && /* @__PURE__ */ jsx(
4363
+ LogicFlowEditor,
4364
+ {
4365
+ schema: builderState.schema,
4366
+ onChange: (updated) => builderState.resetSchema(updated),
4367
+ onClose: () => setShowLogicMap(false)
4368
+ }
4369
+ ),
4370
+ showTemplateGallery && templates && /* @__PURE__ */ jsx(
4371
+ TemplateGallery,
4372
+ {
4373
+ templates,
4374
+ onSelect: (template) => builderState.resetSchema(template.schema),
4375
+ onClose: () => setShowTemplateGallery(false)
4376
+ }
4377
+ )
3180
4378
  ]
3181
4379
  }
3182
4380
  ) });
@@ -3231,76 +4429,54 @@ var FormBuilder = requireLicense(FormBuilderInner, "FormBuilder");
3231
4429
 
3232
4430
  // src/form-builder/theme/presets.ts
3233
4431
  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
4432
+ background: "#0F1A1F",
4433
+ foreground: "#E8EFF1",
4434
+ card: "#16242A",
4435
+ primary: "#63BDB4",
4436
+ primaryForeground: "#0F1A1F",
4437
+ secondary: "#182F31",
4438
+ secondaryForeground: "#E8EFF1",
4439
+ muted: "#182F31",
4440
+ mutedForeground: "#8CA1A9",
4441
+ accent: "#182F31",
4442
+ accentForeground: "#E8EFF1",
4443
+ destructive: "#E08072",
4444
+ destructiveForeground: "#0F1A1F",
4445
+ border: "#2A3B42",
4446
+ input: "#2A3B42",
4447
+ ring: "#63BDB4",
4448
+ radius: "0px",
4449
+ surface: "#16242A",
4450
+ surfaceHover: "#182F31",
4451
+ canvas: "#0F1A1F",
4452
+ panel: "#16242A",
4453
+ borderStrong: "#2F4F4C",
4454
+ textDim: "#5E7680"
3279
4455
  };
3280
4456
  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"
4457
+ background: "#F4F7F8",
4458
+ foreground: "#12222A",
4459
+ card: "#FFFFFF",
4460
+ primary: "#1F6B6E",
4461
+ primaryForeground: "#FFFFFF",
4462
+ secondary: "#EDF3F2",
4463
+ secondaryForeground: "#12222A",
4464
+ muted: "#EDF3F2",
4465
+ mutedForeground: "#6A7B85",
4466
+ accent: "#EDF3F2",
4467
+ accentForeground: "#12222A",
4468
+ destructive: "#B04A3C",
4469
+ destructiveForeground: "#FFFFFF",
4470
+ border: "#DCE4E8",
4471
+ input: "#DCE4E8",
4472
+ ring: "#1F6B6E",
4473
+ radius: "0px",
4474
+ surface: "#FAFCFC",
4475
+ surfaceHover: "#EDF3F2",
4476
+ canvas: "#F4F7F8",
4477
+ panel: "#FFFFFF",
4478
+ borderStrong: "#B9D1CF",
4479
+ textDim: "#6A7B85"
3304
4480
  };
3305
4481
 
3306
4482
  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 };