form-page-builder 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,2395 @@
1
+ "use client";
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ FormBuilder: () => FormBuilder,
25
+ default: () => FormBuilder,
26
+ localStorageAdapter: () => localStorageAdapter
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+
30
+ // src/FormBuilder.tsx
31
+ var import_react8 = require("react");
32
+ var import_lucide_react19 = require("lucide-react");
33
+
34
+ // src/i18n/languages.ts
35
+ var DEFAULT_LANGUAGES = [
36
+ { code: "en", label: "EN" },
37
+ { code: "ja", label: "\u65E5\u672C\u8A9E" }
38
+ ];
39
+
40
+ // src/i18n/strings.ts
41
+ var DEFAULT_STRINGS = {
42
+ en: {
43
+ submit: "Submit",
44
+ selectPlaceholder: "Select...",
45
+ requiredError: "This field is required.",
46
+ invalidEmail: "Enter a valid email address.",
47
+ invalidPhone: "Enter a valid phone number.",
48
+ fixErrors: "Please fix the highlighted fields before submitting.",
49
+ submittedTitle: "Form submitted",
50
+ submittedBody: "Here's what would be sent to your backend:",
51
+ addFieldsHint: "Add fields in Build mode to see them here.",
52
+ close: "Close",
53
+ tooLong: (n) => `Must be ${n} characters or fewer.`,
54
+ tooSmall: (n) => `Must be at least ${n}.`,
55
+ tooLarge: (n) => `Must be at most ${n}.`
56
+ },
57
+ ja: {
58
+ submit: "\u9001\u4FE1",
59
+ selectPlaceholder: "\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044",
60
+ requiredError: "\u3053\u306E\u9805\u76EE\u306F\u5FC5\u9808\u3067\u3059\u3002",
61
+ invalidEmail: "\u6709\u52B9\u306A\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
62
+ invalidPhone: "\u6709\u52B9\u306A\u96FB\u8A71\u756A\u53F7\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
63
+ fixErrors: "\u5165\u529B\u5185\u5BB9\u3092\u3054\u78BA\u8A8D\u306E\u3046\u3048\u3001\u518D\u5EA6\u9001\u4FE1\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
64
+ submittedTitle: "\u9001\u4FE1\u5B8C\u4E86",
65
+ submittedBody: "\u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u306B\u9001\u4FE1\u3055\u308C\u308B\u5185\u5BB9\u306F\u4EE5\u4E0B\u306E\u901A\u308A\u3067\u3059\uFF1A",
66
+ addFieldsHint: "\u30D3\u30EB\u30C9\u30E2\u30FC\u30C9\u3067\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u8FFD\u52A0\u3059\u308B\u3068\u3001\u3053\u3053\u306B\u8868\u793A\u3055\u308C\u307E\u3059\u3002",
67
+ close: "\u9589\u3058\u308B",
68
+ tooLong: (n) => `${n}\u6587\u5B57\u4EE5\u5185\u3067\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u3002`,
69
+ tooSmall: (n) => `${n}\u4EE5\u4E0A\u306E\u5024\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u3002`,
70
+ tooLarge: (n) => `${n}\u4EE5\u4E0B\u306E\u5024\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
71
+ }
72
+ };
73
+
74
+ // src/i18n/chrome.ts
75
+ var CHROME = {
76
+ en: {
77
+ contentBlocks: "Content",
78
+ formFields: "Form Fields",
79
+ addingTo: "Adding to",
80
+ properties: "Properties",
81
+ selectFieldHint: "Select a field to edit its properties.",
82
+ label: "Label",
83
+ hideLabel: "Hide label",
84
+ inputType: "Input type",
85
+ content: "Content",
86
+ headingStyle: "Heading style",
87
+ typography: "Typography",
88
+ style: "Style",
89
+ align: "Align",
90
+ color: "Color",
91
+ imageUrl: "Image URL",
92
+ imageUrlHelper: "Upload the image on your own end (Drive, CDN, S3...) and paste the link here \u2014 this package doesn't host images.",
93
+ altText: "Alt text",
94
+ linkOptional: "Link (optional)",
95
+ shape: "Shape",
96
+ placeholder: "Placeholder",
97
+ maxLength: "Max length",
98
+ noLimit: "No limit",
99
+ minValue: "Min value",
100
+ maxValue: "Max value",
101
+ options: "Options",
102
+ addOption: "Add option",
103
+ defaultValue: "Default value",
104
+ defaultChecked: "Default checked",
105
+ checked: "Checked",
106
+ unchecked: "Unchecked",
107
+ none: "None",
108
+ requiredField: "Required field",
109
+ layout: "Layout",
110
+ width: "Width",
111
+ full: "Full",
112
+ labelPosition: "Label position",
113
+ above: "Above",
114
+ inline: "Inline",
115
+ verticalAlign: "Vertical align",
116
+ top: "Top",
117
+ middle: "Middle",
118
+ bottom: "Bottom",
119
+ icon: "Icon",
120
+ showIcon: "Show icon next to label",
121
+ deleteField: "Delete field",
122
+ build: "Build",
123
+ preview: "Preview",
124
+ newForm: "New",
125
+ myForms: "My forms",
126
+ save: "Save",
127
+ viewJson: "View JSON",
128
+ saving: "Saving...",
129
+ saved: "Saved",
130
+ saveFailed: "Save failed",
131
+ startNewForm: "Start a new form",
132
+ openSavedForm: "Open a saved form",
133
+ saveToLibraryTitle: "Save this form to your library",
134
+ sectionTitlePlaceholder: (n) => `Section ${n} title (optional)`,
135
+ noFieldsInSection: "No fields in this section yet \u2014 pick a field type on the left.",
136
+ addSection: "Add section",
137
+ moveUp: "Move up",
138
+ moveDown: "Move down",
139
+ duplicate: "Duplicate",
140
+ delete: "Delete",
141
+ deleteSection: "Delete section",
142
+ required: "required",
143
+ formJson: "Form JSON",
144
+ copy: "Copy",
145
+ copied: "Copied",
146
+ nothingSaved: 'Nothing saved yet. Click "Save" in the toolbar to store this form here.',
147
+ updated: "Updated",
148
+ open: "Open",
149
+ saveForm: "Save form",
150
+ formName: "Form name",
151
+ saveToLibrary: "Save to library",
152
+ loadingDraft: "Loading your draft...",
153
+ current: "current",
154
+ customColor: "Custom color",
155
+ fieldsCount: (n) => `${n} field${n === 1 ? "" : "s"}`,
156
+ formSettings: "Form settings",
157
+ submitLabel: "Submit button text",
158
+ submitMode: "Submit button",
159
+ combined: "Combined (one button)",
160
+ perSection: "Per section",
161
+ settings: "Form settings",
162
+ fieldTypeInput: "Input",
163
+ fieldTypeTextarea: "Textarea",
164
+ fieldTypeSelect: "Select",
165
+ fieldTypeRadio: "Radio group",
166
+ fieldTypeCheckboxGroup: "Checkbox group",
167
+ fieldTypeCheckbox: "Checkbox",
168
+ fieldTypeToggle: "Toggle",
169
+ fieldTypeParagraph: "Paragraph",
170
+ fieldTypeImage: "Image",
171
+ subtypeText: "Text",
172
+ subtypeEmail: "Email",
173
+ subtypePhone: "Phone",
174
+ subtypeNumber: "Number",
175
+ subtypePassword: "Password",
176
+ subtypeDate: "Date",
177
+ subtypeTime: "Time",
178
+ shapeSquare: "Square",
179
+ shapeCircle: "Circle",
180
+ shapeBanner: "Banner",
181
+ tagBody: "Body",
182
+ tagCaption: "Caption",
183
+ submitSectionTitle: "Submit",
184
+ submitStyle: "Submit button style",
185
+ buttonSize: "Size",
186
+ theme: "Theme",
187
+ primaryColor: "Primary color",
188
+ pageBackground: "Page background",
189
+ maxWidthPx: "Max width (px)",
190
+ reset: "Reset",
191
+ deviceLaptop: "Laptop",
192
+ deviceTablet: "Tablet",
193
+ deviceMobile: "Mobile",
194
+ untitledField: "Untitled field",
195
+ optionSeed: (n) => `Option ${n}`,
196
+ spacing: "Spacing",
197
+ spacingPagePadding: "Page padding",
198
+ spacingCanvasPadding: "Canvas padding",
199
+ spacingSectionGap: "Section gap",
200
+ spacingFieldGap: "Field gap",
201
+ spacingToolbarPadding: "Toolbar padding",
202
+ spacingPanelPadding: "Panel padding",
203
+ spacingTicketPadding: "Field padding"
204
+ },
205
+ ja: {
206
+ contentBlocks: "\u30B3\u30F3\u30C6\u30F3\u30C4",
207
+ formFields: "\u30D5\u30A9\u30FC\u30E0\u9805\u76EE",
208
+ addingTo: "\u8FFD\u52A0\u5148",
209
+ properties: "\u30D7\u30ED\u30D1\u30C6\u30A3",
210
+ selectFieldHint: "\u7DE8\u96C6\u3059\u308B\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
211
+ label: "\u30E9\u30D9\u30EB",
212
+ hideLabel: "\u30E9\u30D9\u30EB\u3092\u975E\u8868\u793A",
213
+ inputType: "\u5165\u529B\u30BF\u30A4\u30D7",
214
+ content: "\u5185\u5BB9",
215
+ headingStyle: "\u898B\u51FA\u3057\u30B9\u30BF\u30A4\u30EB",
216
+ typography: "\u6587\u5B57\u30B9\u30BF\u30A4\u30EB",
217
+ style: "\u30B9\u30BF\u30A4\u30EB",
218
+ align: "\u914D\u7F6E",
219
+ color: "\u8272",
220
+ imageUrl: "\u753B\u50CFURL",
221
+ imageUrlHelper: "\u753B\u50CF\u306F\u5404\u81EA\u3067\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\uFF08Drive\u3001CDN\u3001S3\u306A\u3069\uFF09\u3057\u3001\u305D\u306EURL\u3092\u3053\u3053\u306B\u8CBC\u308A\u4ED8\u3051\u3066\u304F\u3060\u3055\u3044\u3002\u672C\u30D1\u30C3\u30B1\u30FC\u30B8\u306F\u753B\u50CF\u3092\u4FDD\u5B58\u3057\u307E\u305B\u3093\u3002",
222
+ altText: "\u4EE3\u66FF\u30C6\u30AD\u30B9\u30C8",
223
+ linkOptional: "\u30EA\u30F3\u30AF\uFF08\u4EFB\u610F\uFF09",
224
+ shape: "\u5F62\u72B6",
225
+ placeholder: "\u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC",
226
+ maxLength: "\u6700\u5927\u6587\u5B57\u6570",
227
+ noLimit: "\u5236\u9650\u306A\u3057",
228
+ minValue: "\u6700\u5C0F\u5024",
229
+ maxValue: "\u6700\u5927\u5024",
230
+ options: "\u9078\u629E\u80A2",
231
+ addOption: "\u9078\u629E\u80A2\u3092\u8FFD\u52A0",
232
+ defaultValue: "\u521D\u671F\u5024",
233
+ defaultChecked: "\u521D\u671F\u9078\u629E",
234
+ checked: "\u30AA\u30F3",
235
+ unchecked: "\u30AA\u30D5",
236
+ none: "\u306A\u3057",
237
+ requiredField: "\u5FC5\u9808\u9805\u76EE",
238
+ layout: "\u30EC\u30A4\u30A2\u30A6\u30C8",
239
+ width: "\u5E45",
240
+ full: "\u5168\u5E45",
241
+ labelPosition: "\u30E9\u30D9\u30EB\u4F4D\u7F6E",
242
+ above: "\u4E0A",
243
+ inline: "\u6A2A\u4E26\u3073",
244
+ verticalAlign: "\u5782\u76F4\u4F4D\u7F6E",
245
+ top: "\u4E0A",
246
+ middle: "\u4E2D\u592E",
247
+ bottom: "\u4E0B",
248
+ icon: "\u30A2\u30A4\u30B3\u30F3",
249
+ showIcon: "\u30E9\u30D9\u30EB\u6A2A\u306B\u30A2\u30A4\u30B3\u30F3\u3092\u8868\u793A",
250
+ deleteField: "\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u524A\u9664",
251
+ build: "\u4F5C\u6210",
252
+ preview: "\u30D7\u30EC\u30D3\u30E5\u30FC",
253
+ newForm: "\u65B0\u898F",
254
+ myForms: "\u30DE\u30A4\u30D5\u30A9\u30FC\u30E0",
255
+ save: "\u4FDD\u5B58",
256
+ viewJson: "JSON\u3092\u8868\u793A",
257
+ saving: "\u4FDD\u5B58\u4E2D...",
258
+ saved: "\u4FDD\u5B58\u6E08\u307F",
259
+ saveFailed: "\u4FDD\u5B58\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
260
+ startNewForm: "\u65B0\u3057\u3044\u30D5\u30A9\u30FC\u30E0\u3092\u4F5C\u6210",
261
+ openSavedForm: "\u4FDD\u5B58\u6E08\u307F\u30D5\u30A9\u30FC\u30E0\u3092\u958B\u304F",
262
+ saveToLibraryTitle: "\u3053\u306E\u30D5\u30A9\u30FC\u30E0\u3092\u30E9\u30A4\u30D6\u30E9\u30EA\u306B\u4FDD\u5B58",
263
+ sectionTitlePlaceholder: (n) => `\u30BB\u30AF\u30B7\u30E7\u30F3${n}\u306E\u30BF\u30A4\u30C8\u30EB\uFF08\u4EFB\u610F\uFF09`,
264
+ noFieldsInSection: "\u3053\u306E\u30BB\u30AF\u30B7\u30E7\u30F3\u306B\u306F\u307E\u3060\u30D5\u30A3\u30FC\u30EB\u30C9\u304C\u3042\u308A\u307E\u305B\u3093\u3002\u5DE6\u306E\u30D1\u30CD\u30EB\u304B\u3089\u30D5\u30A3\u30FC\u30EB\u30C9\u3092\u9078\u3093\u3067\u304F\u3060\u3055\u3044\u3002",
265
+ addSection: "\u30BB\u30AF\u30B7\u30E7\u30F3\u3092\u8FFD\u52A0",
266
+ moveUp: "\u4E0A\u3078\u79FB\u52D5",
267
+ moveDown: "\u4E0B\u3078\u79FB\u52D5",
268
+ duplicate: "\u8907\u88FD",
269
+ delete: "\u524A\u9664",
270
+ deleteSection: "\u30BB\u30AF\u30B7\u30E7\u30F3\u3092\u524A\u9664",
271
+ required: "\u5FC5\u9808",
272
+ formJson: "\u30D5\u30A9\u30FC\u30E0JSON",
273
+ copy: "\u30B3\u30D4\u30FC",
274
+ copied: "\u30B3\u30D4\u30FC\u3057\u307E\u3057\u305F",
275
+ nothingSaved: "\u307E\u3060\u4FDD\u5B58\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002\u30C4\u30FC\u30EB\u30D0\u30FC\u306E\u300C\u4FDD\u5B58\u300D\u3092\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
276
+ updated: "\u66F4\u65B0\u65E5\u6642",
277
+ open: "\u958B\u304F",
278
+ saveForm: "\u30D5\u30A9\u30FC\u30E0\u3092\u4FDD\u5B58",
279
+ formName: "\u30D5\u30A9\u30FC\u30E0\u540D",
280
+ saveToLibrary: "\u30E9\u30A4\u30D6\u30E9\u30EA\u306B\u4FDD\u5B58",
281
+ loadingDraft: "\u4E0B\u66F8\u304D\u3092\u8AAD\u307F\u8FBC\u3093\u3067\u3044\u307E\u3059...",
282
+ current: "\u73FE\u5728\u7DE8\u96C6\u4E2D",
283
+ customColor: "\u30AB\u30B9\u30BF\u30E0\u30AB\u30E9\u30FC",
284
+ fieldsCount: (n) => `${n}\u4EF6\u306E\u30D5\u30A3\u30FC\u30EB\u30C9`,
285
+ formSettings: "\u30D5\u30A9\u30FC\u30E0\u8A2D\u5B9A",
286
+ submitLabel: "\u9001\u4FE1\u30DC\u30BF\u30F3\u306E\u30C6\u30AD\u30B9\u30C8",
287
+ submitMode: "\u9001\u4FE1\u30DC\u30BF\u30F3\u306E\u8868\u793A",
288
+ combined: "\u307E\u3068\u3081\u30661\u3064",
289
+ perSection: "\u30BB\u30AF\u30B7\u30E7\u30F3\u3054\u3068",
290
+ settings: "\u30D5\u30A9\u30FC\u30E0\u8A2D\u5B9A",
291
+ fieldTypeInput: "\u5165\u529B",
292
+ fieldTypeTextarea: "\u30C6\u30AD\u30B9\u30C8\u30A8\u30EA\u30A2",
293
+ fieldTypeSelect: "\u30BB\u30EC\u30AF\u30C8",
294
+ fieldTypeRadio: "\u30E9\u30B8\u30AA\u30DC\u30BF\u30F3",
295
+ fieldTypeCheckboxGroup: "\u30C1\u30A7\u30C3\u30AF\u30DC\u30C3\u30AF\u30B9\u30B0\u30EB\u30FC\u30D7",
296
+ fieldTypeCheckbox: "\u30C1\u30A7\u30C3\u30AF\u30DC\u30C3\u30AF\u30B9",
297
+ fieldTypeToggle: "\u30C8\u30B0\u30EB",
298
+ fieldTypeParagraph: "\u6BB5\u843D",
299
+ fieldTypeImage: "\u753B\u50CF",
300
+ subtypeText: "\u30C6\u30AD\u30B9\u30C8",
301
+ subtypeEmail: "\u30E1\u30FC\u30EB",
302
+ subtypePhone: "\u96FB\u8A71\u756A\u53F7",
303
+ subtypeNumber: "\u6570\u5024",
304
+ subtypePassword: "\u30D1\u30B9\u30EF\u30FC\u30C9",
305
+ subtypeDate: "\u65E5\u4ED8",
306
+ subtypeTime: "\u6642\u523B",
307
+ shapeSquare: "\u6B63\u65B9\u5F62",
308
+ shapeCircle: "\u5186\u5F62",
309
+ shapeBanner: "\u30D0\u30CA\u30FC",
310
+ tagBody: "\u672C\u6587",
311
+ tagCaption: "\u30AD\u30E3\u30D7\u30B7\u30E7\u30F3",
312
+ submitSectionTitle: "\u9001\u4FE1",
313
+ submitStyle: "\u9001\u4FE1\u30DC\u30BF\u30F3\u306E\u30B9\u30BF\u30A4\u30EB",
314
+ buttonSize: "\u30B5\u30A4\u30BA",
315
+ theme: "\u30C6\u30FC\u30DE",
316
+ primaryColor: "\u30E1\u30A4\u30F3\u30AB\u30E9\u30FC",
317
+ pageBackground: "\u30DA\u30FC\u30B8\u80CC\u666F\u8272",
318
+ maxWidthPx: "\u6700\u5927\u5E45\uFF08px\uFF09",
319
+ reset: "\u30EA\u30BB\u30C3\u30C8",
320
+ deviceLaptop: "\u30D1\u30BD\u30B3\u30F3",
321
+ deviceTablet: "\u30BF\u30D6\u30EC\u30C3\u30C8",
322
+ deviceMobile: "\u30E2\u30D0\u30A4\u30EB",
323
+ untitledField: "\u540D\u79F0\u672A\u8A2D\u5B9A\u306E\u30D5\u30A3\u30FC\u30EB\u30C9",
324
+ optionSeed: (n) => `\u9078\u629E\u80A2${n}`,
325
+ spacing: "\u4F59\u767D\u8A2D\u5B9A",
326
+ spacingPagePadding: "\u30DA\u30FC\u30B8\u4F59\u767D",
327
+ spacingCanvasPadding: "\u30AD\u30E3\u30F3\u30D0\u30B9\u4F59\u767D",
328
+ spacingSectionGap: "\u30BB\u30AF\u30B7\u30E7\u30F3\u9593\u9694",
329
+ spacingFieldGap: "\u30D5\u30A3\u30FC\u30EB\u30C9\u9593\u9694",
330
+ spacingToolbarPadding: "\u30C4\u30FC\u30EB\u30D0\u30FC\u4F59\u767D",
331
+ spacingPanelPadding: "\u30D1\u30CD\u30EB\u4F59\u767D",
332
+ spacingTicketPadding: "\u30D5\u30A3\u30FC\u30EB\u30C9\u5185\u4F59\u767D"
333
+ }
334
+ };
335
+
336
+ // src/lib/bilingual.ts
337
+ function bi(en = "", ja = "") {
338
+ return { en, ja };
339
+ }
340
+ function t(val, lang) {
341
+ if (val && typeof val === "object") return val[lang] || val.en || "";
342
+ return val || "";
343
+ }
344
+ function withLang(val, lang, value) {
345
+ const base = val && typeof val === "object" ? val : bi(typeof val === "string" ? val : "");
346
+ return { ...base, [lang]: value };
347
+ }
348
+
349
+ // src/lib/storage/localStorageAdapter.ts
350
+ function hasLocalStorage() {
351
+ return typeof window !== "undefined" && !!window.localStorage;
352
+ }
353
+ var localStorageAdapter = {
354
+ async get(key) {
355
+ if (!hasLocalStorage()) return null;
356
+ return window.localStorage.getItem(key);
357
+ },
358
+ async set(key, value) {
359
+ if (!hasLocalStorage()) return;
360
+ window.localStorage.setItem(key, value);
361
+ },
362
+ async delete(key) {
363
+ if (!hasLocalStorage()) return;
364
+ window.localStorage.removeItem(key);
365
+ }
366
+ };
367
+
368
+ // src/hooks/useTheme.ts
369
+ var import_react = require("react");
370
+
371
+ // src/theme/defaultTheme.ts
372
+ var DEFAULT_THEME = {
373
+ primary: "#5B5FEF",
374
+ primarySoft: "#EEF0FF",
375
+ danger: "#C4432E",
376
+ dangerSoft: "#FDECE4",
377
+ ink: "#1B1E24",
378
+ muted: "#9296A3",
379
+ border: "#E2E4E9",
380
+ surface: "#FFFFFF",
381
+ canvas: "#F5F6F8",
382
+ pageBackground: "#FFFFFF",
383
+ layout: { maxWidth: 640, pagePadding: 28, canvasPadding: 20, sectionGap: 20, fieldGap: 16, toolbarPadding: 10, panelPadding: 14, ticketPadding: 14 }
384
+ };
385
+
386
+ // src/hooks/useTheme.ts
387
+ function useTheme(themeOverrideProp) {
388
+ const baseTheme = {
389
+ ...DEFAULT_THEME,
390
+ ...themeOverrideProp || {},
391
+ layout: { ...DEFAULT_THEME.layout, ...themeOverrideProp && themeOverrideProp.layout || {} }
392
+ };
393
+ const [themeOverrides, setThemeOverrides] = (0, import_react.useState)({});
394
+ const theme = {
395
+ ...baseTheme,
396
+ ...themeOverrides,
397
+ layout: { ...baseTheme.layout, ...themeOverrides.layout || {} }
398
+ };
399
+ function updateThemeColor(key, value) {
400
+ setThemeOverrides((prev) => ({ ...prev, [key]: value }));
401
+ }
402
+ function updateThemeLayout(key, value) {
403
+ setThemeOverrides((prev) => ({ ...prev, layout: { ...prev.layout, [key]: value } }));
404
+ }
405
+ function resetTheme() {
406
+ setThemeOverrides({});
407
+ }
408
+ function replaceThemeOverrides(overrides) {
409
+ setThemeOverrides(overrides);
410
+ }
411
+ return { theme, themeOverrides, updateThemeColor, updateThemeLayout, resetTheme, replaceThemeOverrides };
412
+ }
413
+
414
+ // src/hooks/useFormDocument.ts
415
+ var import_react2 = require("react");
416
+
417
+ // src/constants/fieldTypes.ts
418
+ var import_lucide_react = require("lucide-react");
419
+ var FIELD_TYPES = [
420
+ { type: "paragraph", icon: import_lucide_react.FileText, isContent: true, defaultIcon: "FileText" },
421
+ { type: "image", icon: import_lucide_react.Image, isContent: true, isImage: true, defaultIcon: "Type" },
422
+ { type: "input", icon: import_lucide_react.Type, placeholder: true, hasSubtype: true, defaultIcon: "Type" },
423
+ { type: "textarea", icon: import_lucide_react.AlignLeft, placeholder: true, defaultIcon: "AlignLeft" },
424
+ { type: "select", icon: import_lucide_react.ChevronDown, options: true, defaultIcon: "ChevronDown" },
425
+ { type: "radio", icon: import_lucide_react.Circle, options: true, defaultIcon: "Circle" },
426
+ { type: "checkboxGroup", icon: import_lucide_react.ListChecks, options: true, multiValue: true, defaultIcon: "ListChecks" },
427
+ { type: "checkbox", icon: import_lucide_react.CheckSquare, boolean: true, defaultIcon: "CheckSquare" },
428
+ { type: "toggle", icon: import_lucide_react.ToggleLeft, boolean: true, defaultIcon: "ToggleLeft" }
429
+ ];
430
+ var TYPE_MAP = Object.fromEntries(FIELD_TYPES.map((f) => [f.type, f]));
431
+ var CONTENT_TYPES = FIELD_TYPES.filter((f) => f.isContent);
432
+ var FORM_TYPES = FIELD_TYPES.filter((f) => !f.isContent);
433
+ function getMeta(type) {
434
+ return TYPE_MAP[type] || TYPE_MAP.input;
435
+ }
436
+ var LEGACY_INPUT_TYPES = ["text", "email", "phone", "number", "password", "date"];
437
+ var INPUT_SUBTYPES = ["text", "email", "phone", "number", "password", "date", "time"];
438
+ var IMAGE_SHAPES = ["square", "circle", "banner"];
439
+ var FIELD_TYPE_CHROME_KEY = {
440
+ paragraph: "fieldTypeParagraph",
441
+ image: "fieldTypeImage",
442
+ input: "fieldTypeInput",
443
+ textarea: "fieldTypeTextarea",
444
+ select: "fieldTypeSelect",
445
+ radio: "fieldTypeRadio",
446
+ checkboxGroup: "fieldTypeCheckboxGroup",
447
+ checkbox: "fieldTypeCheckbox",
448
+ toggle: "fieldTypeToggle"
449
+ };
450
+ var INPUT_SUBTYPE_CHROME_KEY = {
451
+ text: "subtypeText",
452
+ email: "subtypeEmail",
453
+ phone: "subtypePhone",
454
+ number: "subtypeNumber",
455
+ password: "subtypePassword",
456
+ date: "subtypeDate",
457
+ time: "subtypeTime"
458
+ };
459
+ var IMAGE_SHAPE_CHROME_KEY = {
460
+ square: "shapeSquare",
461
+ circle: "shapeCircle",
462
+ banner: "shapeBanner"
463
+ };
464
+
465
+ // src/lib/id.ts
466
+ var genFormId = () => `form_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`;
467
+ var genSectionId = () => `section_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`;
468
+ var idCounter = 1;
469
+ var nextId = () => `field_${idCounter++}`;
470
+ function resyncIdCounter(fields) {
471
+ let max = 0;
472
+ fields.forEach((f) => {
473
+ const m = /^field_(\d+)$/.exec(f.id);
474
+ if (m) max = Math.max(max, parseInt(m[1], 10));
475
+ });
476
+ idCounter = Math.max(idCounter, max + 1);
477
+ }
478
+
479
+ // src/lib/fieldDefaults.ts
480
+ function defaultFieldFor(type, language, chrome) {
481
+ const meta = getMeta(type);
482
+ const seedLabel = chrome[FIELD_TYPE_CHROME_KEY[type]] || type;
483
+ const base = {
484
+ id: nextId(),
485
+ type,
486
+ label: withLang(bi(), language, seedLabel),
487
+ hideLabel: false,
488
+ width: "1/1",
489
+ verticalAlign: "top",
490
+ labelPosition: "top",
491
+ showIcon: false,
492
+ displayIcon: meta.defaultIcon
493
+ };
494
+ if (!meta.isContent) base.required = false;
495
+ if (meta.hasSubtype) base.inputType = "text";
496
+ if (meta.placeholder) base.placeholder = bi();
497
+ if (meta.options) {
498
+ base.options = [
499
+ { label: withLang(bi(), language, chrome.optionSeed(1)), value: "option_1" },
500
+ { label: withLang(bi(), language, chrome.optionSeed(2)), value: "option_2" }
501
+ ];
502
+ }
503
+ if (meta.isImage) {
504
+ base.src = "";
505
+ base.alt = bi();
506
+ base.link = "";
507
+ base.shape = "square";
508
+ } else if (meta.boolean) {
509
+ base.defaultValue = false;
510
+ } else if (meta.multiValue) {
511
+ base.defaultValue = [];
512
+ } else if (type === "paragraph") {
513
+ base.content = bi();
514
+ base.tag = "p";
515
+ base.fontSize = "md";
516
+ base.fontWeight = "normal";
517
+ base.fontStyle = "normal";
518
+ base.textAlign = "left";
519
+ base.color = "";
520
+ } else {
521
+ base.defaultValue = "";
522
+ }
523
+ return base;
524
+ }
525
+ function defaultSection(titleEn = "") {
526
+ return { id: genSectionId(), title: bi(titleEn, ""), background: "", collapsed: false, submitStyle: null, fields: [] };
527
+ }
528
+
529
+ // src/hooks/useFormDocument.ts
530
+ function useFormDocument({ language, chrome }) {
531
+ const [title, setTitle] = (0, import_react2.useState)(bi("Untitled form", ""));
532
+ const [submitLabel, setSubmitLabelState] = (0, import_react2.useState)(bi());
533
+ const [submitMode, setSubmitMode] = (0, import_react2.useState)("combined");
534
+ const [submitStyle, setSubmitStyleState] = (0, import_react2.useState)({ color: "", size: "md" });
535
+ const [sections, setSections] = (0, import_react2.useState)([defaultSection()]);
536
+ const [activeSectionId, setActiveSectionId] = (0, import_react2.useState)(null);
537
+ const [selectedId, setSelectedId] = (0, import_react2.useState)(null);
538
+ const allFields = sections.flatMap((s) => s.fields);
539
+ const selected = allFields.find((f) => f.id === selectedId) || null;
540
+ const activeSection = sections.find((s) => s.id === activeSectionId) || sections[0] || defaultSection();
541
+ function updateTitle(value) {
542
+ setTitle((prev) => withLang(prev, language, value));
543
+ }
544
+ function updateSubmitLabel(value) {
545
+ setSubmitLabelState((prev) => withLang(prev, language, value));
546
+ }
547
+ function updateSubmitStyle(patch) {
548
+ setSubmitStyleState((prev) => ({ ...prev, ...patch }));
549
+ }
550
+ function addSection() {
551
+ const s = defaultSection();
552
+ setSections((prev) => [...prev, s]);
553
+ setActiveSectionId(s.id);
554
+ }
555
+ function duplicateSection(sectionId) {
556
+ const newSectionId = genSectionId();
557
+ setSections((prev) => {
558
+ const idx = prev.findIndex((s) => s.id === sectionId);
559
+ if (idx === -1) return prev;
560
+ const copy = { ...prev[idx], id: newSectionId, fields: prev[idx].fields.map((f) => ({ ...f, id: nextId() })) };
561
+ const next = [...prev];
562
+ next.splice(idx + 1, 0, copy);
563
+ return next;
564
+ });
565
+ setActiveSectionId(newSectionId);
566
+ }
567
+ function deleteSection(sectionId) {
568
+ setSections((prev) => {
569
+ if (prev.length <= 1) return prev;
570
+ const next = prev.filter((s) => s.id !== sectionId);
571
+ setActiveSectionId((curr) => curr === sectionId ? next[0].id : curr);
572
+ return next;
573
+ });
574
+ }
575
+ function moveSection(sectionId, dir) {
576
+ setSections((prev) => {
577
+ const idx = prev.findIndex((s) => s.id === sectionId);
578
+ const swap = idx + dir;
579
+ if (swap < 0 || swap >= prev.length) return prev;
580
+ const next = [...prev];
581
+ [next[idx], next[swap]] = [next[swap], next[idx]];
582
+ return next;
583
+ });
584
+ }
585
+ function updateSectionTitle(sectionId, value) {
586
+ setSections((prev) => prev.map((s) => s.id === sectionId ? { ...s, title: withLang(s.title, language, value) } : s));
587
+ }
588
+ function updateSectionBackground(sectionId, color) {
589
+ setSections((prev) => prev.map((s) => s.id === sectionId ? { ...s, background: color } : s));
590
+ }
591
+ function toggleSectionCollapse(sectionId) {
592
+ setSections((prev) => prev.map((s) => s.id === sectionId ? { ...s, collapsed: !s.collapsed } : s));
593
+ }
594
+ function updateSectionSubmitStyle(sectionId, patch) {
595
+ setSections((prev) => prev.map((s) => s.id === sectionId ? { ...s, submitStyle: { ...s.submitStyle || submitStyle, ...patch } } : s));
596
+ }
597
+ function clearSectionSubmitStyle(sectionId) {
598
+ setSections((prev) => prev.map((s) => s.id === sectionId ? { ...s, submitStyle: null } : s));
599
+ }
600
+ function addField(type) {
601
+ const field = defaultFieldFor(type, language, chrome);
602
+ const targetId = activeSectionId || sections[0]?.id;
603
+ if (!targetId) return;
604
+ setSections((prev) => prev.map((s) => s.id === targetId ? { ...s, fields: [...s.fields, field] } : s));
605
+ setSelectedId(field.id);
606
+ }
607
+ function updateField(fieldId, patch) {
608
+ setSections((prev) => prev.map((s) => ({ ...s, fields: s.fields.map((f) => f.id === fieldId ? { ...f, ...patch } : f) })));
609
+ }
610
+ function deleteField(fieldId) {
611
+ setSections((prev) => prev.map((s) => ({ ...s, fields: s.fields.filter((f) => f.id !== fieldId) })));
612
+ if (selectedId === fieldId) setSelectedId(null);
613
+ }
614
+ function duplicateField(fieldId) {
615
+ const newId = nextId();
616
+ setSections((prev) => prev.map((s) => {
617
+ const idx = s.fields.findIndex((f) => f.id === fieldId);
618
+ if (idx === -1) return s;
619
+ const copy = { ...s.fields[idx], id: newId };
620
+ const fields = [...s.fields];
621
+ fields.splice(idx + 1, 0, copy);
622
+ return { ...s, fields };
623
+ }));
624
+ setSelectedId(newId);
625
+ }
626
+ function moveField(fieldId, dir) {
627
+ setSections((prev) => prev.map((s) => {
628
+ const idx = s.fields.findIndex((f) => f.id === fieldId);
629
+ if (idx === -1) return s;
630
+ const swap = idx + dir;
631
+ if (swap < 0 || swap >= s.fields.length) return s;
632
+ const fields = [...s.fields];
633
+ [fields[idx], fields[swap]] = [fields[swap], fields[idx]];
634
+ return { ...s, fields };
635
+ }));
636
+ }
637
+ function reorderWithinSection(sectionId, fromIdx, toIdx) {
638
+ if (fromIdx === toIdx || fromIdx == null || toIdx == null) return;
639
+ setSections((prev) => prev.map((s) => {
640
+ if (s.id !== sectionId) return s;
641
+ const fields = [...s.fields];
642
+ const [moved] = fields.splice(fromIdx, 1);
643
+ fields.splice(toIdx, 0, moved);
644
+ return { ...s, fields };
645
+ }));
646
+ }
647
+ function updateOption(fieldId, optIdx, patch) {
648
+ setSections((prev) => prev.map((s) => ({
649
+ ...s,
650
+ fields: s.fields.map((f) => {
651
+ if (f.id !== fieldId || !("options" in f) || !f.options) return f;
652
+ return { ...f, options: f.options.map((o, i) => i === optIdx ? { ...o, ...patch } : o) };
653
+ })
654
+ })));
655
+ }
656
+ function addOption(fieldId) {
657
+ setSections((prev) => prev.map((s) => ({
658
+ ...s,
659
+ fields: s.fields.map((f) => {
660
+ if (f.id !== fieldId || !("options" in f) || !f.options) return f;
661
+ const n = f.options.length + 1;
662
+ return { ...f, options: [...f.options, { label: withLang(bi(), language, chrome.optionSeed(n)), value: `option_${n}` }] };
663
+ })
664
+ })));
665
+ }
666
+ function removeOption(fieldId, optIdx) {
667
+ setSections((prev) => prev.map((s) => ({
668
+ ...s,
669
+ fields: s.fields.map((f) => f.id !== fieldId || !("options" in f) || !f.options ? f : { ...f, options: f.options.filter((_, i) => i !== optIdx) })
670
+ })));
671
+ }
672
+ function loadDocument(doc) {
673
+ resyncIdCounter(doc.sections.flatMap((s) => s.fields));
674
+ setSections(doc.sections);
675
+ setActiveSectionId(doc.sections[0]?.id || null);
676
+ setTitle(doc.title);
677
+ setSubmitLabelState(doc.submitLabel);
678
+ setSubmitMode(doc.submitMode);
679
+ setSubmitStyleState(doc.submitStyle);
680
+ setSelectedId(null);
681
+ }
682
+ function resetToBlank() {
683
+ const s = defaultSection();
684
+ setSections([s]);
685
+ setActiveSectionId(s.id);
686
+ setTitle(bi("Untitled form", ""));
687
+ setSubmitLabelState(bi());
688
+ setSubmitMode("combined");
689
+ setSubmitStyleState({ color: "", size: "md" });
690
+ setSelectedId(null);
691
+ }
692
+ return {
693
+ title,
694
+ submitLabel,
695
+ submitMode,
696
+ submitStyle,
697
+ sections,
698
+ activeSectionId,
699
+ selectedId,
700
+ allFields,
701
+ selected,
702
+ activeSection,
703
+ setActiveSectionId,
704
+ setSelectedId,
705
+ setSubmitMode,
706
+ setTitle,
707
+ updateTitle,
708
+ updateSubmitLabel,
709
+ updateSubmitStyle,
710
+ addSection,
711
+ duplicateSection,
712
+ deleteSection,
713
+ moveSection,
714
+ updateSectionTitle,
715
+ updateSectionBackground,
716
+ toggleSectionCollapse,
717
+ updateSectionSubmitStyle,
718
+ clearSectionSubmitStyle,
719
+ addField,
720
+ updateField,
721
+ deleteField,
722
+ duplicateField,
723
+ moveField,
724
+ reorderWithinSection,
725
+ updateOption,
726
+ addOption,
727
+ removeOption,
728
+ loadDocument,
729
+ resetToBlank
730
+ };
731
+ }
732
+
733
+ // src/hooks/usePersistence.ts
734
+ var import_react3 = require("react");
735
+
736
+ // src/lib/storage/keys.ts
737
+ var DRAFT_KEY = "form-page-builder:draft";
738
+ var INDEX_KEY = "form-page-builder:index";
739
+ var formKey = (id) => `form-page-builder:saved:${id}`;
740
+
741
+ // src/lib/migrate.ts
742
+ function migrateField(field) {
743
+ let f = { ...field };
744
+ if (LEGACY_INPUT_TYPES.includes(f.type)) f = { ...f, type: "input", inputType: f.type };
745
+ f.label = typeof f.label === "string" ? bi(f.label) : f.label || bi();
746
+ f.hideLabel = !!f.hideLabel;
747
+ if ("placeholder" in f) f.placeholder = typeof f.placeholder === "string" ? bi(f.placeholder) : f.placeholder || bi();
748
+ if ("content" in f) f.content = typeof f.content === "string" ? bi(f.content) : f.content || bi();
749
+ if ("alt" in f) f.alt = typeof f.alt === "string" ? bi(f.alt) : f.alt || bi();
750
+ if (f.options) f.options = f.options.map((o) => ({ ...o, label: typeof o.label === "string" ? bi(o.label) : o.label || bi() }));
751
+ if (f.type === "paragraph") {
752
+ f.tag = f.tag || "p";
753
+ f.fontSize = f.fontSize || "md";
754
+ f.fontWeight = f.fontWeight || "normal";
755
+ f.fontStyle = f.fontStyle || "normal";
756
+ f.textAlign = f.textAlign || "left";
757
+ f.color = f.color || "";
758
+ }
759
+ f.verticalAlign = f.verticalAlign || "top";
760
+ return f;
761
+ }
762
+ function migrateFields(fields) {
763
+ return (fields || []).map(migrateField);
764
+ }
765
+ function migrateDocument(raw) {
766
+ if (!raw) return null;
767
+ const title = typeof raw.title === "string" ? bi(raw.title) : raw.title || bi();
768
+ const submitLabel = typeof raw.submitLabel === "string" ? bi(raw.submitLabel) : raw.submitLabel || bi();
769
+ const submitMode = raw.submitMode === "perSection" ? "perSection" : "combined";
770
+ const submitStyle = raw.submitStyle || { color: "", size: "md" };
771
+ const themeOverrides = raw.themeOverrides || {};
772
+ if (raw.sections) {
773
+ return {
774
+ title,
775
+ submitLabel,
776
+ submitMode,
777
+ submitStyle,
778
+ themeOverrides,
779
+ sections: raw.sections.map((s) => ({
780
+ id: s.id || genSectionId(),
781
+ title: typeof s.title === "string" ? bi(s.title) : s.title || bi(),
782
+ background: s.background || "",
783
+ collapsed: !!s.collapsed,
784
+ submitStyle: s.submitStyle || null,
785
+ fields: migrateFields(s.fields || [])
786
+ }))
787
+ };
788
+ }
789
+ return { title, submitLabel, submitMode, submitStyle, themeOverrides, sections: [{ ...defaultSection(), fields: migrateFields(raw.fields || []) }] };
790
+ }
791
+
792
+ // src/hooks/usePersistence.ts
793
+ function usePersistence({
794
+ storage,
795
+ language,
796
+ document,
797
+ onLoadDocument,
798
+ onLoadThemeOverrides,
799
+ onTitleChange,
800
+ onNewForm,
801
+ ensureActiveSection
802
+ }) {
803
+ const [currentFormId, setCurrentFormId] = (0, import_react3.useState)(null);
804
+ const [loadingDraft, setLoadingDraft] = (0, import_react3.useState)(true);
805
+ const [saveState, setSaveState] = (0, import_react3.useState)("idle");
806
+ const [savedForms, setSavedForms] = (0, import_react3.useState)([]);
807
+ const [saveAsPrompt, setSaveAsPrompt] = (0, import_react3.useState)(null);
808
+ const autosaveTimer = (0, import_react3.useRef)(null);
809
+ const hasLoadedOnce = (0, import_react3.useRef)(false);
810
+ async function refreshLibrary() {
811
+ try {
812
+ const raw = await storage.get(INDEX_KEY);
813
+ const list = raw ? JSON.parse(raw) : [];
814
+ setSavedForms(list.sort((a, b) => b.updatedAt - a.updatedAt));
815
+ } catch (err) {
816
+ setSavedForms([]);
817
+ }
818
+ }
819
+ (0, import_react3.useEffect)(() => {
820
+ (async () => {
821
+ try {
822
+ const raw = await storage.get(DRAFT_KEY);
823
+ if (raw) {
824
+ const parsed = JSON.parse(raw);
825
+ const doc = migrateDocument(parsed);
826
+ if (doc) {
827
+ onLoadDocument(doc);
828
+ onLoadThemeOverrides(doc.themeOverrides);
829
+ }
830
+ if (parsed.currentFormId) setCurrentFormId(parsed.currentFormId);
831
+ } else {
832
+ ensureActiveSection();
833
+ }
834
+ } catch (err) {
835
+ ensureActiveSection();
836
+ } finally {
837
+ setLoadingDraft(false);
838
+ hasLoadedOnce.current = true;
839
+ }
840
+ })();
841
+ refreshLibrary();
842
+ }, []);
843
+ (0, import_react3.useEffect)(() => {
844
+ if (!hasLoadedOnce.current) return;
845
+ setSaveState("saving");
846
+ if (autosaveTimer.current) clearTimeout(autosaveTimer.current);
847
+ autosaveTimer.current = setTimeout(async () => {
848
+ try {
849
+ await storage.set(DRAFT_KEY, JSON.stringify({ ...document, currentFormId }));
850
+ setSaveState("saved");
851
+ } catch (err) {
852
+ setSaveState("error");
853
+ }
854
+ }, 600);
855
+ return () => {
856
+ if (autosaveTimer.current) clearTimeout(autosaveTimer.current);
857
+ };
858
+ }, [document.title, document.submitLabel, document.submitMode, document.submitStyle, document.themeOverrides, document.sections, currentFormId]);
859
+ async function saveAs(name) {
860
+ const id = genFormId();
861
+ const now = Date.now();
862
+ try {
863
+ const newTitle = bi(name, "");
864
+ await storage.set(formKey(id), JSON.stringify({ ...document, title: newTitle, id, updatedAt: now }));
865
+ const next = [...savedForms, { id, title: name, updatedAt: now }];
866
+ await storage.set(INDEX_KEY, JSON.stringify(next));
867
+ setSavedForms(next);
868
+ setCurrentFormId(id);
869
+ onTitleChange(newTitle);
870
+ setSaveAsPrompt(null);
871
+ } catch (err) {
872
+ alert("Couldn't save the form. Please try again.");
873
+ }
874
+ }
875
+ async function saveExisting() {
876
+ if (!currentFormId) {
877
+ setSaveAsPrompt({ open: true, suggestedName: t(document.title, language) });
878
+ return;
879
+ }
880
+ const now = Date.now();
881
+ try {
882
+ await storage.set(formKey(currentFormId), JSON.stringify({ ...document, id: currentFormId, updatedAt: now }));
883
+ const next = savedForms.map((f) => f.id === currentFormId ? { ...f, title: t(document.title, "en"), updatedAt: now } : f);
884
+ setSavedForms(next);
885
+ await storage.set(INDEX_KEY, JSON.stringify(next));
886
+ } catch (err) {
887
+ alert("Couldn't save the form. Please try again.");
888
+ }
889
+ }
890
+ async function loadForm(id) {
891
+ try {
892
+ const raw = await storage.get(formKey(id));
893
+ if (!raw) return;
894
+ const doc = migrateDocument(JSON.parse(raw));
895
+ if (!doc) return;
896
+ onLoadDocument(doc);
897
+ onLoadThemeOverrides(doc.themeOverrides);
898
+ setCurrentFormId(id);
899
+ } catch (err) {
900
+ alert("Couldn't load that form.");
901
+ }
902
+ }
903
+ async function deleteForm(id) {
904
+ try {
905
+ await storage.delete(formKey(id));
906
+ const next = savedForms.filter((f) => f.id !== id);
907
+ setSavedForms(next);
908
+ await storage.set(INDEX_KEY, JSON.stringify(next));
909
+ if (currentFormId === id) setCurrentFormId(null);
910
+ } catch (err) {
911
+ }
912
+ }
913
+ function newForm() {
914
+ onNewForm();
915
+ setCurrentFormId(null);
916
+ }
917
+ function dismissSaveAsPrompt() {
918
+ setSaveAsPrompt(null);
919
+ }
920
+ return {
921
+ loadingDraft,
922
+ saveState,
923
+ savedForms,
924
+ currentFormId,
925
+ saveAs,
926
+ saveExisting,
927
+ loadForm,
928
+ deleteForm,
929
+ refreshLibrary,
930
+ newForm,
931
+ saveAsPrompt,
932
+ dismissSaveAsPrompt
933
+ };
934
+ }
935
+
936
+ // src/hooks/useDragReorder.ts
937
+ var import_react4 = require("react");
938
+ function useDragReorder(reorderWithinSection) {
939
+ const dragRef = (0, import_react4.useRef)({ sectionId: null, index: null });
940
+ const [dragOverKey, setDragOverKey] = (0, import_react4.useState)(null);
941
+ function getDropZoneHandlers(sectionId, index) {
942
+ const dragKey = `${sectionId}:${index}`;
943
+ return {
944
+ onDragOver: (e) => {
945
+ e.preventDefault();
946
+ setDragOverKey(dragKey);
947
+ },
948
+ onDragLeave: () => setDragOverKey(null),
949
+ onDrop: () => {
950
+ if (dragRef.current.sectionId === sectionId) reorderWithinSection(sectionId, dragRef.current.index, index);
951
+ dragRef.current = { sectionId: null, index: null };
952
+ setDragOverKey(null);
953
+ }
954
+ };
955
+ }
956
+ function getDragHandleProps(sectionId, index) {
957
+ return {
958
+ draggable: true,
959
+ onDragStart: () => {
960
+ dragRef.current = { sectionId, index };
961
+ }
962
+ };
963
+ }
964
+ return { dragOverKey, getDropZoneHandlers, getDragHandleProps };
965
+ }
966
+
967
+ // src/components/Toolbar.tsx
968
+ var import_lucide_react4 = require("lucide-react");
969
+
970
+ // src/styles/styles.ts
971
+ var styles = {
972
+ app: { fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, sans-serif", background: "var(--fb-canvas)", borderRadius: 12, overflow: "hidden", border: "1px solid var(--fb-border)", color: "var(--fb-ink)", minHeight: 640, maxHeight: "100vh", display: "flex", flexDirection: "column" },
973
+ toolbar: { display: "flex", alignItems: "center", justifyContent: "space-between", padding: "var(--fb-space-toolbar) 16px", borderBottom: "1px solid var(--fb-border)", background: "var(--fb-surface)", flexWrap: "wrap", gap: 8, position: "relative" },
974
+ toolbarLeft: { display: "flex", alignItems: "center", gap: 10 },
975
+ logoMark: { width: 26, height: 26, borderRadius: 7, background: "var(--fb-primary)", color: "#fff", fontSize: 11, fontWeight: 700, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: "'JetBrains Mono', monospace", letterSpacing: "-0.5px" },
976
+ titleInput: { border: "none", outline: "none", fontSize: 14, fontWeight: 600, background: "transparent", color: "var(--fb-ink)", padding: "4px 6px", borderRadius: 6, minWidth: 160 },
977
+ toolbarRight: { display: "flex", alignItems: "center", gap: 6, flexWrap: "wrap" },
978
+ toolbarDivider: { width: 1, height: 20, background: "var(--fb-border)", margin: "0 4px" },
979
+ tabBtn: { display: "flex", alignItems: "center", gap: 6, padding: "6px 12px", borderRadius: 7, border: "1px solid transparent", background: "transparent", color: "#6B6E79", fontSize: 13, fontWeight: 500 },
980
+ tabBtnActive: { display: "flex", alignItems: "center", gap: 6, padding: "6px 12px", borderRadius: 7, border: "1px solid var(--fb-border)", background: "var(--fb-primary-soft)", color: "var(--fb-primary)", fontSize: 13, fontWeight: 600 },
981
+ ghostBtn: { display: "flex", alignItems: "center", gap: 6, padding: "6px 12px", borderRadius: 7, border: "1px solid var(--fb-border)", background: "var(--fb-surface)", color: "#4A4D57", fontSize: 13, fontWeight: 500 },
982
+ primaryBtn: { display: "flex", alignItems: "center", gap: 6, padding: "6px 13px", borderRadius: 7, border: "1px solid var(--fb-primary)", background: "var(--fb-primary)", color: "#fff", fontSize: 13, fontWeight: 600 },
983
+ saveStatus: { display: "flex", alignItems: "center", gap: 5, fontSize: 12, color: "var(--fb-muted)", minWidth: 60 },
984
+ countBadge: { fontSize: 10.5, fontWeight: 700, background: "var(--fb-primary-soft)", color: "var(--fb-primary)", borderRadius: 9, padding: "1px 6px", marginLeft: 2 },
985
+ loadingScreen: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", color: "var(--fb-muted)" },
986
+ libraryBody: { padding: "var(--fb-space-panel)", overflowY: "auto", display: "flex", flexDirection: "column", gap: 4 },
987
+ libraryRow: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 10, padding: "10px 8px", borderRadius: 8, border: "1px solid #F0F1F4" },
988
+ libraryRowTitle: { fontSize: 13.5, fontWeight: 600, color: "var(--fb-ink)", display: "flex", alignItems: "center", gap: 8, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
989
+ libraryRowMeta: { fontSize: 11.5, color: "var(--fb-muted)", marginTop: 2 },
990
+ currentBadge: { fontSize: 10, fontWeight: 700, color: "var(--fb-primary)", background: "var(--fb-primary-soft)", padding: "2px 6px", borderRadius: 5, flexShrink: 0 },
991
+ workArea: { display: "flex", flex: 1, minHeight: 0 },
992
+ palette: { width: 200, borderRight: "1px solid var(--fb-border)", background: "var(--fb-surface)", padding: "var(--fb-space-panel)", overflowY: "auto" },
993
+ panelHeading: { fontSize: 11, fontWeight: 700, letterSpacing: "0.06em", textTransform: "uppercase", color: "var(--fb-muted)", marginBottom: 10 },
994
+ activeSectionHint: { fontSize: 11.5, color: "var(--fb-muted)", marginBottom: 14, lineHeight: 1.4 },
995
+ paletteList: { display: "flex", flexDirection: "column", gap: 4 },
996
+ paletteItem: { display: "flex", alignItems: "center", gap: 9, padding: "8px 9px", borderRadius: 8, border: "1px solid transparent", background: "transparent", fontSize: 13, color: "#33353E", textAlign: "left" },
997
+ canvas: { flex: 1, padding: "var(--fb-space-canvas)", overflowY: "auto", background: "var(--fb-canvas)" },
998
+ sectionBlock: { borderRadius: 12, padding: "var(--fb-space-panel)", marginBottom: "var(--fb-space-section)", transition: "border-color 0.12s" },
999
+ sectionHeader: { display: "flex", alignItems: "center", gap: 8, marginBottom: 12, flexWrap: "wrap" },
1000
+ chevronBtn: { width: 22, height: 22, display: "flex", alignItems: "center", justifyContent: "center", border: "none", background: "transparent", color: "var(--fb-muted)", flexShrink: 0 },
1001
+ colorDot: { width: 10, height: 10, borderRadius: "50%", border: "1px solid var(--fb-border)", flexShrink: 0 },
1002
+ sectionTitleInput: { border: "none", outline: "none", background: "transparent", fontSize: 14, fontWeight: 700, color: "var(--fb-ink)", padding: "2px 0", minWidth: 120, flex: 1 },
1003
+ sectionHeaderActions: { display: "flex", alignItems: "center", gap: 6, flexWrap: "wrap", marginLeft: "auto" },
1004
+ sectionEmpty: { fontSize: 12.5, color: "var(--fb-muted)", padding: "18px 4px", textAlign: "center", border: "1px dashed var(--fb-border)", borderRadius: 8 },
1005
+ submitStyleRow: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", background: "#F7F8FA", border: "1px solid #F0F1F4", borderRadius: 8, padding: "6px 10px", marginBottom: 12 },
1006
+ miniLabel: { fontSize: 11, fontWeight: 600, color: "var(--fb-muted)" },
1007
+ resetLinkBtn: { display: "flex", alignItems: "center", gap: 3, fontSize: 11, color: "var(--fb-muted)", background: "transparent", border: "none", padding: "2px 4px" },
1008
+ addSectionBtn: { display: "flex", alignItems: "center", justifyContent: "center", gap: 6, width: "100%", padding: "10px", borderRadius: 10, border: "1px dashed #C7C9D6", background: "transparent", color: "var(--fb-primary)", fontSize: 13, fontWeight: 600 },
1009
+ swatchRow: { display: "flex", alignItems: "center", gap: 5, flexWrap: "wrap" },
1010
+ swatchBtn: { width: 20, height: 20, borderRadius: "50%", border: "1px solid var(--fb-border)", padding: 0 },
1011
+ swatchBtnActive: { border: "2px solid var(--fb-primary)" },
1012
+ colorPickerInput: { width: 22, height: 22, padding: 0, border: "none", borderRadius: "50%", overflow: "hidden", background: "none", cursor: "pointer" },
1013
+ fieldList: { display: "flex", flexWrap: "wrap", gap: "var(--fb-space-field)" },
1014
+ ticket: { display: "flex", alignItems: "stretch", background: "var(--fb-surface)", border: "1px solid var(--fb-border)", borderRadius: 10, position: "relative", transition: "border-color 0.12s, box-shadow 0.12s" },
1015
+ ticketSelected: { borderColor: "var(--fb-primary)", boxShadow: "0 0 0 3px var(--fb-primary-soft)" },
1016
+ ticketDragOver: { borderColor: "var(--fb-muted)" },
1017
+ ticketIndex: { fontFamily: "'JetBrains Mono', monospace", fontSize: 13, fontWeight: 700, color: "#C7C9D6", width: 34, display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 },
1018
+ ticketPerforation: { width: 0, borderLeft: "1px dashed var(--fb-border)" },
1019
+ ticketBody: { flex: 1, padding: "10px var(--fb-space-ticket)", minWidth: 0 },
1020
+ ticketTop: { display: "flex", alignItems: "center", gap: 7, marginBottom: 4 },
1021
+ typeBadge: { fontSize: 10.5, fontFamily: "'JetBrains Mono', monospace", color: "var(--fb-muted)" },
1022
+ requiredBadge: { fontSize: 10, fontWeight: 700, color: "#B4522F", background: "var(--fb-danger-soft)", padding: "2px 6px", borderRadius: 5, letterSpacing: "0.02em", marginLeft: "auto" },
1023
+ chipRow: { display: "flex", gap: 5, marginBottom: 7, flexWrap: "wrap" },
1024
+ miniBadge: { fontSize: 10, fontWeight: 600, color: "var(--fb-primary)", background: "var(--fb-primary-soft)", padding: "2px 6px", borderRadius: 5, letterSpacing: "0.02em" },
1025
+ segmented: { display: "flex", gap: 3, background: "#F0F1F4", padding: 3, borderRadius: 8, marginBottom: 4 },
1026
+ segmentedBtn: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", gap: 5, padding: "6px 8px", borderRadius: 6, border: "none", background: "transparent", color: "#6B6E79", fontSize: 12.5, fontWeight: 500 },
1027
+ segmentedBtnActive: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", gap: 5, padding: "6px 8px", borderRadius: 6, border: "none", background: "#fff", color: "var(--fb-ink)", fontSize: 12.5, fontWeight: 600, boxShadow: "0 1px 2px rgba(0,0,0,0.08)" },
1028
+ iconGrid: { display: "grid", gridTemplateColumns: "repeat(6, 1fr)", gap: 5, marginTop: 6 },
1029
+ iconGridBtn: { width: 30, height: 30, borderRadius: 7, border: "1px solid var(--fb-border)", background: "#fff", display: "flex", alignItems: "center", justifyContent: "center", color: "#6B6E79" },
1030
+ iconGridBtnActive: { width: 30, height: 30, borderRadius: 7, border: "1px solid var(--fb-primary)", background: "var(--fb-primary-soft)", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--fb-primary)" },
1031
+ styleToggleRow: { display: "flex", gap: 5, marginBottom: 4 },
1032
+ styleToggle: { width: 30, height: 30, borderRadius: 7, border: "1px solid var(--fb-border)", background: "#fff", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--fb-muted)" },
1033
+ styleToggleActive: { width: 30, height: 30, borderRadius: 7, border: "1px solid var(--fb-primary)", background: "var(--fb-primary-soft)", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--fb-primary)" },
1034
+ previewInput: { border: "1px solid #E9EAEE", borderRadius: 7, padding: "7px 10px", fontSize: 13, color: "var(--fb-muted)", background: "#FBFBFD", display: "flex", alignItems: "center", justifyContent: "space-between" },
1035
+ previewRadio: { display: "flex", alignItems: "center", gap: 6, fontSize: 13, color: "#4A4D57" },
1036
+ ticketActions: { display: "flex", flexDirection: "column", gap: 3, padding: 8, borderLeft: "1px solid #F0F1F4" },
1037
+ iconBtn: { width: 24, height: 24, borderRadius: 6, border: "1px solid #E9EAEE", background: "#fff", display: "flex", alignItems: "center", justifyContent: "center", color: "#6B6E79" },
1038
+ iconBtnDanger: { color: "var(--fb-danger)" },
1039
+ inspector: { width: 270, borderLeft: "1px solid var(--fb-border)", background: "var(--fb-surface)", padding: "var(--fb-space-panel)", overflowY: "auto" },
1040
+ inspectorEmpty: { fontSize: 13, color: "var(--fb-muted)", lineHeight: 1.5, marginTop: 6 },
1041
+ inspectorBody: { display: "flex", flexDirection: "column", gap: 4 },
1042
+ helperText: { fontSize: 11, color: "var(--fb-muted)", lineHeight: 1.4, margin: "2px 0 4px" },
1043
+ propLabel: { fontSize: 11.5, fontWeight: 600, color: "#6B6E79", marginTop: 12, marginBottom: 5 },
1044
+ propInput: { border: "1px solid var(--fb-border)", borderRadius: 7, padding: "7px 9px", fontSize: 13, color: "var(--fb-ink)", outline: "none", width: "100%", boxSizing: "border-box" },
1045
+ optionList: { display: "flex", flexDirection: "column", gap: 6 },
1046
+ optionRow: { display: "flex", gap: 6, alignItems: "center" },
1047
+ optionInput: { flex: 1, border: "1px solid var(--fb-border)", borderRadius: 6, padding: "6px 8px", fontSize: 12.5, outline: "none", minWidth: 0, boxSizing: "border-box" },
1048
+ addOptionBtn: { display: "flex", alignItems: "center", gap: 5, fontSize: 12.5, color: "var(--fb-primary)", background: "transparent", border: "1px dashed #C7C9D6", borderRadius: 7, padding: "6px 8px", justifyContent: "center", marginTop: 2 },
1049
+ toggleRow: { display: "flex", alignItems: "center", gap: 8, background: "transparent", border: "none", padding: "8px 0" },
1050
+ toggleTrack: { width: 30, height: 17, borderRadius: 20, position: "relative", transition: "background 0.15s", display: "inline-block" },
1051
+ toggleThumb: { position: "absolute", top: 2, left: 2, width: 13, height: 13, borderRadius: "50%", background: "#fff", transition: "transform 0.15s", boxShadow: "0 1px 2px rgba(0,0,0,0.2)" },
1052
+ inspectorDivider: { height: 1, background: "var(--fb-border)", margin: "14px 0 10px" },
1053
+ deleteFieldBtn: { display: "flex", alignItems: "center", gap: 6, fontSize: 12.5, color: "var(--fb-danger)", background: "var(--fb-danger-soft)", border: "none", borderRadius: 7, padding: "8px 10px", justifyContent: "center" },
1054
+ modalOverlay: { position: "fixed", inset: 0, background: "rgba(20,22,28,0.45)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 50 },
1055
+ modal: { background: "#fff", borderRadius: 12, width: 520, maxHeight: "70vh", display: "flex", flexDirection: "column", overflow: "hidden", boxShadow: "0 20px 60px rgba(0,0,0,0.25)" },
1056
+ modalHeader: { display: "flex", alignItems: "center", justifyContent: "space-between", padding: "12px 16px", borderBottom: "1px solid var(--fb-border)" },
1057
+ jsonPre: { margin: 0, padding: 16, fontSize: 12, lineHeight: 1.6, overflow: "auto", background: "#14171C", color: "#D6D8E0", fontFamily: "'JetBrains Mono', monospace" },
1058
+ previewWrap: { flex: 1, display: "flex", justifyContent: "center", padding: "var(--fb-space-page) 20px", overflowY: "auto", background: "var(--fb-canvas)" },
1059
+ previewToolbar: { width: "100%", display: "flex", gap: 10, marginBottom: 16, justifyContent: "center" },
1060
+ previewCard: { width: "100%", maxWidth: 480, background: "var(--fb-page-bg)", border: "1px solid var(--fb-border)", borderRadius: 12, padding: "var(--fb-space-page)" },
1061
+ previewTitle: { fontSize: 19, fontWeight: 700, margin: "0 0 20px" },
1062
+ previewLabel: { display: "block", fontSize: 13, fontWeight: 600, color: "#33353E", marginBottom: 6 },
1063
+ previewGrid: { display: "flex", flexWrap: "wrap", gap: "var(--fb-space-field)" },
1064
+ sectionRuntimeTitle: { fontSize: 15, fontWeight: 700, margin: "0 0 12px", color: "var(--fb-ink)" },
1065
+ imagePlaceholder: { display: "flex", alignItems: "center", justifyContent: "center", border: "1px dashed var(--fb-border)" },
1066
+ fieldError: { display: "flex", alignItems: "center", gap: 5, fontSize: 12, color: "var(--fb-danger)", marginTop: 5 },
1067
+ formErrorNote: { display: "flex", alignItems: "center", gap: 6, fontSize: 12.5, color: "var(--fb-danger)", marginTop: 10, justifyContent: "center" },
1068
+ submittedList: { display: "flex", flexDirection: "column", gap: 8, marginTop: 4 },
1069
+ submittedRow: { display: "flex", justifyContent: "space-between", gap: 12, fontSize: 13, padding: "6px 0", borderBottom: "1px solid var(--fb-border)" },
1070
+ submittedLabel: { color: "var(--fb-muted)", flexShrink: 0 },
1071
+ submittedValue: { color: "var(--fb-ink)", fontWeight: 600, textAlign: "right" },
1072
+ realInput: { width: "100%", border: "1px solid var(--fb-border)", borderRadius: 8, padding: "9px 11px", fontSize: 13.5, outline: "none", color: "var(--fb-ink)", boxSizing: "border-box" },
1073
+ realInputError: { borderColor: "var(--fb-danger)" },
1074
+ submitBtn: { marginTop: 8, color: "#fff", border: "none", borderRadius: 8, fontWeight: 600, width: "100%" },
1075
+ layoutPanel: { position: "absolute", top: "calc(100% + 6px)", right: 0, width: 260, background: "#fff", border: "1px solid var(--fb-border)", borderRadius: 10, boxShadow: "0 12px 32px rgba(0,0,0,0.18)", padding: 14, zIndex: 60, maxHeight: "70vh", overflowY: "auto" },
1076
+ layoutPanelHeader: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 4 },
1077
+ spacingRow: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8, padding: "4px 0" },
1078
+ spacingLabel: { fontSize: 12, color: "#4A4D57" },
1079
+ spacingInput: { width: 60, border: "1px solid var(--fb-border)", borderRadius: 6, padding: "4px 6px", fontSize: 12, textAlign: "right", outline: "none" }
1080
+ };
1081
+
1082
+ // src/components/Segmented.tsx
1083
+ var import_jsx_runtime = require("react/jsx-runtime");
1084
+ function Segmented({ options, value, onChange }) {
1085
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.segmented, children: options.map((opt) => {
1086
+ const Icon = opt.icon;
1087
+ const active = value === opt.value;
1088
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", style: active ? styles.segmentedBtnActive : styles.segmentedBtn, onClick: () => onChange(opt.value), children: [
1089
+ Icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { size: 13 }),
1090
+ opt.label
1091
+ ] }, opt.value);
1092
+ }) });
1093
+ }
1094
+
1095
+ // src/components/LayoutPopover.tsx
1096
+ var import_react5 = require("react");
1097
+ var import_lucide_react3 = require("lucide-react");
1098
+
1099
+ // src/constants/colors.ts
1100
+ var COLOR_SWATCHES = ["", "#1B1E24", "#5B5FEF", "#C4432E", "#0EA5A4", "#D97706"];
1101
+ var SECTION_BG_SWATCHES = ["", "#FFFFFF", "#F5F6F8", "#EEF0FF", "#FDF4E7", "#E9F7EF", "#FDECEC"];
1102
+ var BUTTON_COLOR_SWATCHES = ["", "#5B5FEF", "#0EA5A4", "#D97706", "#C4432E", "#1B1E24"];
1103
+
1104
+ // src/constants/layout.ts
1105
+ var import_lucide_react2 = require("lucide-react");
1106
+ var WIDTH_OPTIONS = [
1107
+ { value: "1/3", label: "1/3" },
1108
+ { value: "1/2", label: "1/2" },
1109
+ { value: "1/1", labelKey: "full" }
1110
+ ];
1111
+ var WIDTH_PERCENT = { "1/3": "33.3333%", "1/2": "50%", "1/1": "100%" };
1112
+ var ALIGN_MAP = { top: "flex-start", middle: "center", bottom: "flex-end" };
1113
+ function effectiveWidth(width, device) {
1114
+ if (device === "mobile") return "1/1";
1115
+ if (device === "tablet" && width === "1/3") return "1/2";
1116
+ return width || "1/1";
1117
+ }
1118
+ var SPACING_FIELDS = [
1119
+ { key: "pagePadding", labelKey: "spacingPagePadding" },
1120
+ { key: "canvasPadding", labelKey: "spacingCanvasPadding" },
1121
+ { key: "sectionGap", labelKey: "spacingSectionGap" },
1122
+ { key: "fieldGap", labelKey: "spacingFieldGap" },
1123
+ { key: "toolbarPadding", labelKey: "spacingToolbarPadding" },
1124
+ { key: "panelPadding", labelKey: "spacingPanelPadding" },
1125
+ { key: "ticketPadding", labelKey: "spacingTicketPadding" }
1126
+ ];
1127
+ function buildDeviceOptions(baseMaxWidth, chrome) {
1128
+ const mw = Number(baseMaxWidth) > 0 ? baseMaxWidth : DEFAULT_THEME.layout.maxWidth;
1129
+ return [
1130
+ { value: "laptop", label: chrome.deviceLaptop, icon: import_lucide_react2.Monitor, maxWidth: mw },
1131
+ { value: "tablet", label: chrome.deviceTablet, icon: import_lucide_react2.Tablet, maxWidth: Math.min(mw, 480) },
1132
+ { value: "mobile", label: chrome.deviceMobile, icon: import_lucide_react2.Smartphone, maxWidth: Math.min(mw, 340) }
1133
+ ];
1134
+ }
1135
+
1136
+ // src/components/LayoutPopover.tsx
1137
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1138
+ function LayoutPopover({ chrome, theme, updateThemeColor, updateThemeLayout, resetTheme }) {
1139
+ const [open, setOpen] = (0, import_react5.useState)(false);
1140
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { position: "relative" }, children: [
1141
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { style: styles.iconBtn, title: chrome.spacing, onClick: () => setOpen((v) => !v), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react3.Ruler, { size: 14 }) }),
1142
+ open && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles.layoutPanel, children: [
1143
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles.layoutPanelHeader, children: [
1144
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { fontWeight: 600, fontSize: 13 }, children: [
1145
+ chrome.theme,
1146
+ " / ",
1147
+ chrome.spacing
1148
+ ] }),
1149
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", style: styles.resetLinkBtn, onClick: resetTheme, children: [
1150
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react3.RotateCcw, { size: 11 }),
1151
+ " ",
1152
+ chrome.reset
1153
+ ] })
1154
+ ] }),
1155
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { style: styles.propLabel, children: chrome.primaryColor }),
1156
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles.swatchRow, children: [
1157
+ BUTTON_COLOR_SWATCHES.slice(1).map((c) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", title: c, style: { ...styles.swatchBtn, background: c, ...theme.primary === c ? styles.swatchBtnActive : {} }, onClick: () => updateThemeColor("primary", c) }, c)),
1158
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "color", value: theme.primary, onChange: (e) => updateThemeColor("primary", e.target.value), style: styles.colorPickerInput })
1159
+ ] }),
1160
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { style: styles.propLabel, children: chrome.pageBackground }),
1161
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles.swatchRow, children: [
1162
+ SECTION_BG_SWATCHES.filter(Boolean).map((c) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", title: c, style: { ...styles.swatchBtn, background: c, ...theme.pageBackground === c ? styles.swatchBtnActive : {} }, onClick: () => updateThemeColor("pageBackground", c) }, c)),
1163
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "color", value: theme.pageBackground, onChange: (e) => updateThemeColor("pageBackground", e.target.value), style: styles.colorPickerInput })
1164
+ ] }),
1165
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { style: styles.propLabel, children: chrome.maxWidthPx }),
1166
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "number", style: styles.propInput, value: theme.layout.maxWidth, onChange: (e) => updateThemeLayout("maxWidth", Number(e.target.value) || DEFAULT_THEME.layout.maxWidth) }),
1167
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: styles.inspectorDivider }),
1168
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: styles.panelHeading, children: chrome.spacing }),
1169
+ SPACING_FIELDS.map(({ key, labelKey }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles.spacingRow, children: [
1170
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: styles.spacingLabel, children: chrome[labelKey] }),
1171
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "number", style: styles.spacingInput, value: theme.layout[key], onChange: (e) => updateThemeLayout(key, Number(e.target.value) || 0) })
1172
+ ] }, key))
1173
+ ] })
1174
+ ] });
1175
+ }
1176
+
1177
+ // src/components/Toolbar.tsx
1178
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1179
+ function Toolbar({
1180
+ title,
1181
+ language,
1182
+ languages,
1183
+ mode,
1184
+ saveState,
1185
+ chrome,
1186
+ themeEditable,
1187
+ theme,
1188
+ savedFormsCount,
1189
+ onTitleChange,
1190
+ onLanguageChange,
1191
+ onModeChange,
1192
+ onOpenSettings,
1193
+ onNewForm,
1194
+ onOpenLibrary,
1195
+ onSaveExisting,
1196
+ onOpenJson,
1197
+ updateThemeColor,
1198
+ updateThemeLayout,
1199
+ resetTheme
1200
+ }) {
1201
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles.toolbar, children: [
1202
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles.toolbarLeft, children: [
1203
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: styles.logoMark, children: "FB" }),
1204
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { value: t(title, language), onChange: (e) => onTitleChange(e.target.value), style: styles.titleInput, "aria-label": "Form title" })
1205
+ ] }),
1206
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: styles.toolbarRight, children: [
1207
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: styles.saveStatus, children: [
1208
+ saveState === "saving" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1209
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react4.Loader2, { size: 12, className: "spin" }),
1210
+ " ",
1211
+ chrome.saving
1212
+ ] }),
1213
+ saveState === "saved" && chrome.saved,
1214
+ saveState === "error" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: { color: "var(--fb-danger)", display: "flex", alignItems: "center", gap: 4 }, children: [
1215
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react4.AlertCircle, { size: 12 }),
1216
+ " ",
1217
+ chrome.saveFailed
1218
+ ] })
1219
+ ] }),
1220
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: styles.toolbarDivider }),
1221
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react4.Languages, { size: 14, color: "var(--fb-muted)" }),
1222
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Segmented, { options: languages.map((l) => ({ value: l.code, label: l.label })), value: language, onChange: onLanguageChange }),
1223
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: styles.toolbarDivider }),
1224
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { style: mode === "build" ? styles.tabBtnActive : styles.tabBtn, onClick: () => onModeChange("build"), children: [
1225
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react4.Pencil, { size: 14 }),
1226
+ " ",
1227
+ chrome.build
1228
+ ] }),
1229
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { style: mode === "preview" ? styles.tabBtnActive : styles.tabBtn, onClick: () => onModeChange("preview"), children: [
1230
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react4.Eye, { size: 14 }),
1231
+ " ",
1232
+ chrome.preview
1233
+ ] }),
1234
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: styles.toolbarDivider }),
1235
+ themeEditable && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(LayoutPopover, { chrome, theme, updateThemeColor, updateThemeLayout, resetTheme }),
1236
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { style: styles.iconBtn, title: chrome.settings, onClick: onOpenSettings, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react4.Settings, { size: 14 }) }),
1237
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { style: styles.ghostBtn, onClick: onNewForm, title: chrome.startNewForm, children: [
1238
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react4.FilePlus2, { size: 14 }),
1239
+ " ",
1240
+ chrome.newForm
1241
+ ] }),
1242
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { style: styles.ghostBtn, onClick: onOpenLibrary, title: chrome.openSavedForm, children: [
1243
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react4.FolderOpen, { size: 14 }),
1244
+ " ",
1245
+ chrome.myForms,
1246
+ savedFormsCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: styles.countBadge, children: savedFormsCount })
1247
+ ] }),
1248
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { style: styles.primaryBtn, onClick: onSaveExisting, title: chrome.saveToLibraryTitle, children: [
1249
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react4.Save, { size: 14 }),
1250
+ " ",
1251
+ chrome.save
1252
+ ] }),
1253
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { style: styles.ghostBtn, onClick: onOpenJson, children: [
1254
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react4.Code2, { size: 14 }),
1255
+ " ",
1256
+ chrome.viewJson
1257
+ ] })
1258
+ ] })
1259
+ ] });
1260
+ }
1261
+
1262
+ // src/components/Palette.tsx
1263
+ var import_lucide_react5 = require("lucide-react");
1264
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1265
+ function Palette({ activeSectionLabel, chrome, onAddField }) {
1266
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.palette, children: [
1267
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: styles.activeSectionHint, children: [
1268
+ chrome.addingTo,
1269
+ " ",
1270
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: activeSectionLabel })
1271
+ ] }),
1272
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: styles.panelHeading, children: chrome.contentBlocks }),
1273
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: styles.paletteList, children: CONTENT_TYPES.map((f) => {
1274
+ const Icon = f.icon;
1275
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("button", { style: styles.paletteItem, onClick: () => onAddField(f.type), children: [
1276
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { size: 16, color: "var(--fb-primary)" }),
1277
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: chrome[FIELD_TYPE_CHROME_KEY[f.type]] }),
1278
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react5.Plus, { size: 13, color: "#A6A8B3", style: { marginLeft: "auto" } })
1279
+ ] }, f.type);
1280
+ }) }),
1281
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { ...styles.panelHeading, marginTop: 16 }, children: chrome.formFields }),
1282
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: styles.paletteList, children: FORM_TYPES.map((f) => {
1283
+ const Icon = f.icon;
1284
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("button", { style: styles.paletteItem, onClick: () => onAddField(f.type), children: [
1285
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { size: 16, color: "var(--fb-primary)" }),
1286
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: chrome[FIELD_TYPE_CHROME_KEY[f.type]] }),
1287
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react5.Plus, { size: 13, color: "#A6A8B3", style: { marginLeft: "auto" } })
1288
+ ] }, f.type);
1289
+ }) })
1290
+ ] });
1291
+ }
1292
+
1293
+ // src/components/Canvas.tsx
1294
+ var import_lucide_react12 = require("lucide-react");
1295
+
1296
+ // src/components/SectionCard.tsx
1297
+ var import_lucide_react11 = require("lucide-react");
1298
+
1299
+ // src/constants/submitStyle.ts
1300
+ var SUBMIT_SIZE_OPTIONS = [
1301
+ { value: "sm", label: "S" },
1302
+ { value: "md", label: "M" },
1303
+ { value: "lg", label: "L" }
1304
+ ];
1305
+ var SUBMIT_SIZE_STYLES = {
1306
+ sm: { padding: "7px 12px", fontSize: 12.5 },
1307
+ md: { padding: "10px 16px", fontSize: 13.5 },
1308
+ lg: { padding: "13px 22px", fontSize: 15 }
1309
+ };
1310
+ function resolveSubmitStyle(style) {
1311
+ const sizeStyle = SUBMIT_SIZE_STYLES[style?.size || "md"] || SUBMIT_SIZE_STYLES.md;
1312
+ return { ...sizeStyle, background: style?.color || "var(--fb-primary)" };
1313
+ }
1314
+
1315
+ // src/components/FieldTicket.tsx
1316
+ var import_lucide_react10 = require("lucide-react");
1317
+
1318
+ // src/components/fields/FieldBlock.tsx
1319
+ var import_lucide_react9 = require("lucide-react");
1320
+
1321
+ // src/constants/typography.ts
1322
+ var import_lucide_react6 = require("lucide-react");
1323
+ var FONT_SIZE_OPTIONS = [
1324
+ { value: "sm", label: "S", px: 12 },
1325
+ { value: "md", label: "M", px: 14 },
1326
+ { value: "lg", label: "L", px: 18 },
1327
+ { value: "xl", label: "XL", px: 24 }
1328
+ ];
1329
+ var TEXT_ALIGN_OPTIONS = [
1330
+ { value: "left", label: "", icon: import_lucide_react6.AlignLeft },
1331
+ { value: "center", label: "", icon: import_lucide_react6.AlignCenter },
1332
+ { value: "right", label: "", icon: import_lucide_react6.AlignRight }
1333
+ ];
1334
+ var PARAGRAPH_TAG_OPTIONS = ["h1", "h2", "h3", "p", "caption"];
1335
+ var TAG_PRESETS = {
1336
+ h1: { fontSize: "xl", fontWeight: "bold" },
1337
+ h2: { fontSize: "lg", fontWeight: "bold" },
1338
+ h3: { fontSize: "md", fontWeight: "bold" },
1339
+ p: { fontSize: "md", fontWeight: "normal" },
1340
+ caption: { fontSize: "sm", fontWeight: "normal" }
1341
+ };
1342
+ var TAG_TO_ELEMENT = { h1: "h1", h2: "h2", h3: "h3", p: "p", caption: "span" };
1343
+ var TAG_CHROME_KEY = { p: "tagBody", caption: "tagCaption" };
1344
+
1345
+ // src/constants/icons.ts
1346
+ var import_lucide_react7 = require("lucide-react");
1347
+ var ICON_LIBRARY = {
1348
+ Type: import_lucide_react7.Type,
1349
+ Mail: import_lucide_react7.Mail,
1350
+ Phone: import_lucide_react7.Phone,
1351
+ Hash: import_lucide_react7.Hash,
1352
+ Lock: import_lucide_react7.Lock,
1353
+ AlignLeft: import_lucide_react7.AlignLeft,
1354
+ ChevronDown: import_lucide_react7.ChevronDown,
1355
+ Circle: import_lucide_react7.Circle,
1356
+ CheckSquare: import_lucide_react7.CheckSquare,
1357
+ ListChecks: import_lucide_react7.ListChecks,
1358
+ ToggleLeft: import_lucide_react7.ToggleLeft,
1359
+ Calendar: import_lucide_react7.Calendar,
1360
+ Clock: import_lucide_react7.Clock,
1361
+ User: import_lucide_react7.User,
1362
+ MapPin: import_lucide_react7.MapPin,
1363
+ Building2: import_lucide_react7.Building2,
1364
+ Globe: import_lucide_react7.Globe,
1365
+ CreditCard: import_lucide_react7.CreditCard,
1366
+ FileText: import_lucide_react7.FileText,
1367
+ Star: import_lucide_react7.Star,
1368
+ Flag: import_lucide_react7.Flag,
1369
+ Home: import_lucide_react7.Home,
1370
+ Briefcase: import_lucide_react7.Briefcase
1371
+ };
1372
+
1373
+ // src/components/fields/ImageBlock.tsx
1374
+ var import_lucide_react8 = require("lucide-react");
1375
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1376
+ function buildImageStyle(field) {
1377
+ const aspectRatio = field.shape === "banner" ? "16 / 5" : "1 / 1";
1378
+ return {
1379
+ width: "100%",
1380
+ aspectRatio,
1381
+ objectFit: "cover",
1382
+ display: "block",
1383
+ borderRadius: field.shape === "circle" ? "50%" : 8,
1384
+ background: "var(--fb-canvas)"
1385
+ };
1386
+ }
1387
+ function ImageBlock({ field, lang }) {
1388
+ const shapeStyle = buildImageStyle(field);
1389
+ const img = field.src ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("img", { src: field.src, alt: t(field.alt, lang || "en"), style: shapeStyle }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { ...shapeStyle, ...styles.imagePlaceholder }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react8.Image, { size: 20, color: "var(--fb-muted)" }) });
1390
+ return field.link ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("a", { href: field.link, target: "_blank", rel: "noreferrer", style: { display: "block" }, children: img }) : img;
1391
+ }
1392
+
1393
+ // src/components/fields/FieldBlock.tsx
1394
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1395
+ function FieldBlock({ field, lang, onFieldChange, strings, chrome, error, isBuild }) {
1396
+ if (field.type === "image") return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ImageBlock, { field, lang });
1397
+ if (field.type === "paragraph") {
1398
+ const fontPx = FONT_SIZE_OPTIONS.find((f) => f.value === field.fontSize)?.px || 14;
1399
+ const Tag = TAG_TO_ELEMENT[field.tag] || "p";
1400
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Tag, { style: { margin: 0, whiteSpace: "pre-wrap", fontSize: fontPx, fontWeight: field.fontWeight === "bold" ? 700 : 400, fontStyle: field.fontStyle === "italic" ? "italic" : "normal", textAlign: field.textAlign || "left", color: field.color || "var(--fb-ink)", lineHeight: 1.6 }, children: t(field.content, lang) || "..." });
1401
+ }
1402
+ const meta = getMeta(field.type);
1403
+ const IconComp = field.showIcon && field.displayIcon ? ICON_LIBRARY[field.displayIcon] : null;
1404
+ const isInline = field.labelPosition === "inline" && !meta.boolean;
1405
+ const labelText = t(field.label, lang);
1406
+ const showLabel = !meta.boolean && !field.hideLabel;
1407
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1408
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexDirection: isInline ? "row" : "column", alignItems: isInline ? "center" : "stretch", gap: isInline ? 10 : 6 }, children: [
1409
+ showLabel && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { style: { ...styles.previewLabel, marginBottom: isInline ? 0 : 6, flexShrink: 0, width: isInline ? 120 : "auto", display: "flex", alignItems: "center", gap: 6 }, children: [
1410
+ IconComp && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(IconComp, { size: 14, color: "#6B6E79" }),
1411
+ labelText || (isBuild ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontStyle: "italic", color: "var(--fb-muted)" }, children: chrome?.untitledField || "Untitled field" }) : ""),
1412
+ field.required && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { color: "var(--fb-danger)" }, children: " *" })
1413
+ ] }),
1414
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { flex: 1, minWidth: 0 }, children: renderInteractive(field, lang, onFieldChange, IconComp, !!error, strings) })
1415
+ ] }),
1416
+ error && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: styles.fieldError, children: [
1417
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react9.CircleAlert, { size: 12 }),
1418
+ " ",
1419
+ error
1420
+ ] })
1421
+ ] });
1422
+ }
1423
+ function renderInteractive(field, lang, onFieldChange, IconComp, hasError, strings) {
1424
+ const value = field.defaultValue;
1425
+ const set = (v) => onFieldChange(field.id, { defaultValue: v });
1426
+ const inputStyle = hasError ? { ...styles.realInput, ...styles.realInputError } : styles.realInput;
1427
+ const common = { style: inputStyle, placeholder: t("placeholder" in field ? field.placeholder : void 0, lang), value: value || "", onChange: (e) => set(e.target.value) };
1428
+ if (field.type === "input") {
1429
+ switch (field.inputType) {
1430
+ case "number":
1431
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "number", ...common, min: field.min ?? void 0, max: field.max ?? void 0 });
1432
+ case "email":
1433
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "email", ...common });
1434
+ case "password":
1435
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "password", ...common, maxLength: field.maxLength || void 0 });
1436
+ case "phone":
1437
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "tel", ...common });
1438
+ case "date":
1439
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "date", ...common });
1440
+ case "time":
1441
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "time", ...common });
1442
+ default:
1443
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "text", ...common, maxLength: field.maxLength || void 0 });
1444
+ }
1445
+ }
1446
+ switch (field.type) {
1447
+ case "textarea":
1448
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("textarea", { ...common, rows: 3, maxLength: field.maxLength || void 0 });
1449
+ case "select":
1450
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("select", { style: inputStyle, value: value || "", onChange: (e) => set(e.target.value), children: [
1451
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: "", children: strings.selectPlaceholder }),
1452
+ (field.options || []).map((o) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: o.value, children: t(o.label, lang) }, o.value))
1453
+ ] });
1454
+ case "radio":
1455
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { display: "flex", gap: 16, flexWrap: "wrap", marginTop: 4 }, children: (field.options || []).map((o) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { style: styles.previewRadio, children: [
1456
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "radio", name: field.id, value: o.value, checked: value === o.value, onChange: (e) => set(e.target.value) }),
1457
+ t(o.label, lang)
1458
+ ] }, o.value)) });
1459
+ case "checkboxGroup": {
1460
+ const vals = Array.isArray(value) ? value : [];
1461
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { display: "flex", gap: 16, flexWrap: "wrap", marginTop: 4 }, children: (field.options || []).map((o) => {
1462
+ const checked = vals.includes(o.value);
1463
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { style: styles.previewRadio, children: [
1464
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "checkbox", checked, onChange: (e) => {
1465
+ const next = e.target.checked ? [...vals, o.value] : vals.filter((v) => v !== o.value);
1466
+ set(next);
1467
+ }, style: { accentColor: "var(--fb-primary)" } }),
1468
+ t(o.label, lang)
1469
+ ] }, o.value);
1470
+ }) });
1471
+ }
1472
+ case "checkbox":
1473
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { style: styles.previewRadio, children: [
1474
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "checkbox", checked: !!value, onChange: (e) => set(e.target.checked), style: { accentColor: "var(--fb-primary)" } }),
1475
+ IconComp && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(IconComp, { size: 14, color: "#6B6E79" }),
1476
+ t(field.label, lang),
1477
+ field.required && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { color: "var(--fb-danger)" }, children: " *" })
1478
+ ] });
1479
+ case "toggle":
1480
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { style: styles.previewRadio, children: [
1481
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "checkbox", checked: !!value, onChange: (e) => set(e.target.checked), style: { accentColor: "var(--fb-primary)" } }),
1482
+ IconComp && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(IconComp, { size: 14, color: "#6B6E79" }),
1483
+ t(field.label, lang)
1484
+ ] });
1485
+ default:
1486
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { type: "text", ...common });
1487
+ }
1488
+ }
1489
+
1490
+ // src/components/FieldTicket.tsx
1491
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1492
+ function FieldTicket({
1493
+ field,
1494
+ idx,
1495
+ isSelected,
1496
+ isDragOver,
1497
+ chrome,
1498
+ strings,
1499
+ language,
1500
+ onSelect,
1501
+ onFieldChange,
1502
+ onMoveField,
1503
+ onDuplicateField,
1504
+ onDeleteField,
1505
+ dropZoneHandlers,
1506
+ dragHandleProps
1507
+ }) {
1508
+ const meta = getMeta(field.type);
1509
+ const Icon = meta.icon;
1510
+ const widthPct = WIDTH_PERCENT[field.width || "1/1"];
1511
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1512
+ "div",
1513
+ {
1514
+ ...dropZoneHandlers,
1515
+ onClick: onSelect,
1516
+ style: {
1517
+ ...styles.ticket,
1518
+ flex: `1 1 calc(${widthPct} - 10px)`,
1519
+ minWidth: 230,
1520
+ alignSelf: ALIGN_MAP[field.verticalAlign || "top"],
1521
+ ...isSelected ? styles.ticketSelected : {},
1522
+ ...isDragOver ? styles.ticketDragOver : {}
1523
+ },
1524
+ children: [
1525
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: styles.ticketIndex, children: String(idx + 1).padStart(2, "0") }),
1526
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: styles.ticketPerforation }),
1527
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.ticketBody, children: [
1528
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.ticketTop, children: [
1529
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { ...dragHandleProps, style: { cursor: "grab", display: "flex" }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react10.GripVertical, { size: 14, color: "#C4C6D0" }) }),
1530
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Icon, { size: 13, color: "var(--fb-primary)" }),
1531
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: styles.typeBadge, children: [
1532
+ chrome[FIELD_TYPE_CHROME_KEY[field.type]] || field.type,
1533
+ field.type === "input" && field.inputType && field.inputType !== "text" ? ` \xB7 ${chrome[INPUT_SUBTYPE_CHROME_KEY[field.inputType]]}` : ""
1534
+ ] }),
1535
+ field.required && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.requiredBadge, children: chrome.required })
1536
+ ] }),
1537
+ field.width && field.width !== "1/1" || field.labelPosition === "inline" || field.verticalAlign !== "top" || field.hideLabel ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.chipRow, children: [
1538
+ field.width && field.width !== "1/1" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: styles.miniBadge, children: [
1539
+ chrome.width,
1540
+ " ",
1541
+ field.width
1542
+ ] }),
1543
+ field.labelPosition === "inline" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.miniBadge, children: chrome.inline }),
1544
+ field.verticalAlign !== "top" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.miniBadge, children: chrome[field.verticalAlign] }),
1545
+ field.hideLabel && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.miniBadge, children: chrome.hideLabel })
1546
+ ] }) : null,
1547
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { marginTop: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(FieldBlock, { field, lang: language, onFieldChange, strings, chrome, isBuild: true }) })
1548
+ ] }),
1549
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.ticketActions, onClick: (e) => e.stopPropagation(), children: [
1550
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: styles.iconBtn, title: chrome.moveUp, onClick: () => onMoveField(field.id, -1), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react10.ArrowUp, { size: 13 }) }),
1551
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: styles.iconBtn, title: chrome.moveDown, onClick: () => onMoveField(field.id, 1), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react10.ArrowDown, { size: 13 }) }),
1552
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: styles.iconBtn, title: chrome.duplicate, onClick: () => onDuplicateField(field.id), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react10.Copy, { size: 13 }) }),
1553
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: { ...styles.iconBtn, ...styles.iconBtnDanger }, title: chrome.delete, onClick: () => onDeleteField(field.id), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react10.Trash2, { size: 13 }) })
1554
+ ] })
1555
+ ]
1556
+ }
1557
+ );
1558
+ }
1559
+
1560
+ // src/components/SectionCard.tsx
1561
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1562
+ function SectionCard({
1563
+ section,
1564
+ sIdx,
1565
+ sectionsLength,
1566
+ isActive,
1567
+ submitMode,
1568
+ submitStyle,
1569
+ selectedId,
1570
+ dragOverKey,
1571
+ chrome,
1572
+ strings,
1573
+ language,
1574
+ onActivate,
1575
+ onToggleCollapse,
1576
+ onUpdateTitle,
1577
+ onUpdateBackground,
1578
+ onDuplicateSection,
1579
+ onMoveSection,
1580
+ onDeleteSection,
1581
+ onUpdateSectionSubmitStyle,
1582
+ onClearSectionSubmitStyle,
1583
+ onSelectField,
1584
+ onFieldChange,
1585
+ onMoveField,
1586
+ onDuplicateField,
1587
+ onDeleteField,
1588
+ getDropZoneHandlers,
1589
+ getDragHandleProps
1590
+ }) {
1591
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1592
+ "div",
1593
+ {
1594
+ style: { ...styles.sectionBlock, background: section.background || "transparent", border: isActive ? "1px solid var(--fb-primary)" : "1px dashed var(--fb-border)" },
1595
+ onClick: onActivate,
1596
+ children: [
1597
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: styles.sectionHeader, children: [
1598
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: styles.chevronBtn, onClick: (e) => {
1599
+ e.stopPropagation();
1600
+ onToggleCollapse();
1601
+ }, children: section.collapsed ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react11.ChevronRight, { size: 15 }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react11.ChevronDown, { size: 15 }) }),
1602
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { ...styles.colorDot, background: section.background || "var(--fb-canvas)" } }),
1603
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1604
+ "input",
1605
+ {
1606
+ style: styles.sectionTitleInput,
1607
+ placeholder: chrome.sectionTitlePlaceholder(sIdx + 1),
1608
+ value: t(section.title, language),
1609
+ onChange: (e) => onUpdateTitle(e.target.value),
1610
+ onClick: (e) => e.stopPropagation()
1611
+ }
1612
+ ),
1613
+ section.collapsed && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.miniBadge, children: chrome.fieldsCount(section.fields.length) }),
1614
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: styles.sectionHeaderActions, onClick: (e) => e.stopPropagation(), children: [
1615
+ !section.collapsed && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: styles.swatchRow, children: [
1616
+ SECTION_BG_SWATCHES.map((c) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", title: c || chrome.none, style: { ...styles.swatchBtn, background: c || "var(--fb-canvas)", ...section.background === c ? styles.swatchBtnActive : {} }, onClick: () => onUpdateBackground(c) }, c || "none")),
1617
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("input", { type: "color", title: chrome.customColor, value: /^#/.test(section.background) ? section.background : "#ffffff", onChange: (e) => onUpdateBackground(e.target.value), style: styles.colorPickerInput })
1618
+ ] }),
1619
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: styles.iconBtn, title: chrome.duplicate, onClick: onDuplicateSection, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react11.Copy, { size: 13 }) }),
1620
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: styles.iconBtn, title: chrome.moveUp, disabled: sIdx === 0, onClick: () => onMoveSection(-1), children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react11.ArrowUp, { size: 13 }) }),
1621
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: styles.iconBtn, title: chrome.moveDown, disabled: sIdx === sectionsLength - 1, onClick: () => onMoveSection(1), children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react11.ArrowDown, { size: 13 }) }),
1622
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: { ...styles.iconBtn, ...styles.iconBtnDanger }, title: chrome.deleteSection, disabled: sectionsLength <= 1, onClick: onDeleteSection, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react11.Trash2, { size: 13 }) })
1623
+ ] })
1624
+ ] }),
1625
+ !section.collapsed && submitMode === "perSection" && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: styles.submitStyleRow, onClick: (e) => e.stopPropagation(), children: [
1626
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: styles.miniLabel, children: chrome.submitStyle }),
1627
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: styles.swatchRow, children: BUTTON_COLOR_SWATCHES.map((c) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", title: c || chrome.none, style: { ...styles.swatchBtn, background: c || "var(--fb-primary)", ...(section.submitStyle?.color || "") === c ? styles.swatchBtnActive : {} }, onClick: () => onUpdateSectionSubmitStyle({ color: c }) }, c || "inherit")) }),
1628
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Segmented, { options: SUBMIT_SIZE_OPTIONS, value: section.submitStyle?.size || submitStyle.size, onChange: (v) => onUpdateSectionSubmitStyle({ size: v }) }),
1629
+ section.submitStyle && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("button", { type: "button", style: styles.resetLinkBtn, onClick: onClearSectionSubmitStyle, children: [
1630
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react11.RotateCcw, { size: 11 }),
1631
+ " ",
1632
+ chrome.reset
1633
+ ] })
1634
+ ] }),
1635
+ !section.collapsed && (section.fields.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: styles.sectionEmpty, children: chrome.noFieldsInSection }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: styles.fieldList, children: section.fields.map((field, idx) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1636
+ FieldTicket,
1637
+ {
1638
+ field,
1639
+ idx,
1640
+ sectionId: section.id,
1641
+ isSelected: field.id === selectedId,
1642
+ isDragOver: dragOverKey === `${section.id}:${idx}`,
1643
+ chrome,
1644
+ strings,
1645
+ language,
1646
+ onSelect: () => onSelectField(field.id),
1647
+ onFieldChange,
1648
+ onMoveField,
1649
+ onDuplicateField,
1650
+ onDeleteField,
1651
+ dropZoneHandlers: getDropZoneHandlers(section.id, idx),
1652
+ dragHandleProps: getDragHandleProps(section.id, idx)
1653
+ },
1654
+ field.id
1655
+ )) }))
1656
+ ]
1657
+ }
1658
+ );
1659
+ }
1660
+
1661
+ // src/components/Canvas.tsx
1662
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1663
+ function Canvas({
1664
+ sections,
1665
+ activeSectionId,
1666
+ submitMode,
1667
+ submitStyle,
1668
+ selectedId,
1669
+ dragOverKey,
1670
+ chrome,
1671
+ strings,
1672
+ language,
1673
+ onActivateSection,
1674
+ onToggleSectionCollapse,
1675
+ onUpdateSectionTitle,
1676
+ onUpdateSectionBackground,
1677
+ onDuplicateSection,
1678
+ onMoveSection,
1679
+ onDeleteSection,
1680
+ onUpdateSectionSubmitStyle,
1681
+ onClearSectionSubmitStyle,
1682
+ onAddSection,
1683
+ onSelectField,
1684
+ onFieldChange,
1685
+ onMoveField,
1686
+ onDuplicateField,
1687
+ onDeleteField,
1688
+ getDropZoneHandlers,
1689
+ getDragHandleProps
1690
+ }) {
1691
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: styles.canvas, children: [
1692
+ sections.map((section, sIdx) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1693
+ SectionCard,
1694
+ {
1695
+ section,
1696
+ sIdx,
1697
+ sectionsLength: sections.length,
1698
+ isActive: activeSectionId === section.id,
1699
+ submitMode,
1700
+ submitStyle,
1701
+ selectedId,
1702
+ dragOverKey,
1703
+ chrome,
1704
+ strings,
1705
+ language,
1706
+ onActivate: () => onActivateSection(section.id),
1707
+ onToggleCollapse: () => onToggleSectionCollapse(section.id),
1708
+ onUpdateTitle: (value) => onUpdateSectionTitle(section.id, value),
1709
+ onUpdateBackground: (color) => onUpdateSectionBackground(section.id, color),
1710
+ onDuplicateSection: () => onDuplicateSection(section.id),
1711
+ onMoveSection: (dir) => onMoveSection(section.id, dir),
1712
+ onDeleteSection: () => onDeleteSection(section.id),
1713
+ onUpdateSectionSubmitStyle: (patch) => onUpdateSectionSubmitStyle(section.id, patch),
1714
+ onClearSectionSubmitStyle: () => onClearSectionSubmitStyle(section.id),
1715
+ onSelectField: (fieldId) => onSelectField(section.id, fieldId),
1716
+ onFieldChange,
1717
+ onMoveField,
1718
+ onDuplicateField,
1719
+ onDeleteField,
1720
+ getDropZoneHandlers,
1721
+ getDragHandleProps
1722
+ },
1723
+ section.id
1724
+ )),
1725
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("button", { style: styles.addSectionBtn, onClick: onAddSection, children: [
1726
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react12.Layers, { size: 14 }),
1727
+ " ",
1728
+ chrome.addSection
1729
+ ] })
1730
+ ] });
1731
+ }
1732
+
1733
+ // src/components/Inspector.tsx
1734
+ var import_lucide_react13 = require("lucide-react");
1735
+
1736
+ // src/components/fields/DefaultValueEditor.tsx
1737
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1738
+ function DefaultValueEditor({ field, meta, lang, chrome, onChange }) {
1739
+ if (meta.isContent) return null;
1740
+ const f = field;
1741
+ if (meta.boolean) {
1742
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1743
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { style: styles.propLabel, children: chrome.defaultValue }),
1744
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("button", { style: styles.toggleRow, onClick: () => onChange({ defaultValue: !f.defaultValue }), children: [
1745
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: { ...styles.toggleTrack, background: f.defaultValue ? "var(--fb-primary)" : "var(--fb-border)" }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: { ...styles.toggleThumb, transform: f.defaultValue ? "translateX(16px)" : "translateX(0)" } }) }),
1746
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: { fontSize: 13, color: "#4A4D57" }, children: f.defaultValue ? chrome.checked : chrome.unchecked })
1747
+ ] })
1748
+ ] });
1749
+ }
1750
+ if (meta.multiValue) {
1751
+ const selectedVals = f.defaultValue || [];
1752
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1753
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { style: styles.propLabel, children: chrome.defaultChecked }),
1754
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: styles.optionList, children: (f.options || []).map((o) => {
1755
+ const checked = selectedVals.includes(o.value);
1756
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { style: { ...styles.previewRadio, cursor: "pointer" }, children: [
1757
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("input", { type: "checkbox", checked, onChange: (e) => {
1758
+ const next = e.target.checked ? [...selectedVals, o.value] : selectedVals.filter((v) => v !== o.value);
1759
+ onChange({ defaultValue: next });
1760
+ }, style: { accentColor: "var(--fb-primary)" } }),
1761
+ t(o.label, lang)
1762
+ ] }, o.value);
1763
+ }) })
1764
+ ] });
1765
+ }
1766
+ if (meta.options) {
1767
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1768
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { style: styles.propLabel, children: chrome.defaultValue }),
1769
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("select", { style: styles.propInput, value: f.defaultValue || "", onChange: (e) => onChange({ defaultValue: e.target.value }), children: [
1770
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: "", children: chrome.none }),
1771
+ (f.options || []).map((o) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: o.value, children: t(o.label, lang) }, o.value))
1772
+ ] })
1773
+ ] });
1774
+ }
1775
+ const htmlType = field.type === "input" && field.inputType === "number" ? "number" : field.type === "input" && field.inputType === "date" ? "date" : field.type === "input" && field.inputType === "time" ? "time" : "text";
1776
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1777
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { style: styles.propLabel, children: chrome.defaultValue }),
1778
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("input", { type: htmlType, style: styles.propInput, value: f.defaultValue || "", onChange: (e) => onChange({ defaultValue: e.target.value }) })
1779
+ ] });
1780
+ }
1781
+
1782
+ // src/components/Inspector.tsx
1783
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1784
+ function Inspector({ selected, language, chrome, onUpdateField, onDeleteField, onUpdateOption, onAddOption, onRemoveOption }) {
1785
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: styles.inspector, children: [
1786
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: styles.panelHeading, children: chrome.properties }),
1787
+ !selected ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: styles.inspectorEmpty, children: chrome.selectFieldHint }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1788
+ InspectorBody,
1789
+ {
1790
+ selected,
1791
+ language,
1792
+ chrome,
1793
+ onUpdateField,
1794
+ onDeleteField,
1795
+ onUpdateOption,
1796
+ onAddOption,
1797
+ onRemoveOption
1798
+ }
1799
+ )
1800
+ ] });
1801
+ }
1802
+ function InspectorBody({ selected, language, chrome, onUpdateField, onDeleteField, onUpdateOption, onAddOption, onRemoveOption }) {
1803
+ const meta = getMeta(selected.type);
1804
+ const f = selected;
1805
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: styles.inspectorBody, children: [
1806
+ !meta.isContent && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1807
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.label }),
1808
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { style: styles.propInput, value: t(selected.label, language), onChange: (e) => onUpdateField({ label: withLang(selected.label, language, e.target.value) }) }),
1809
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("label", { style: styles.toggleRow, children: [
1810
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { type: "checkbox", checked: !!selected.hideLabel, onChange: (e) => onUpdateField({ hideLabel: e.target.checked }), style: { width: 15, height: 15, accentColor: "var(--fb-primary)" } }),
1811
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { fontSize: 13, color: "#4A4D57" }, children: chrome.hideLabel })
1812
+ ] })
1813
+ ] }),
1814
+ meta.hasSubtype && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1815
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.inputType }),
1816
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("select", { style: styles.propInput, value: f.inputType || "text", onChange: (e) => onUpdateField({ inputType: e.target.value }), children: INPUT_SUBTYPES.map((s) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("option", { value: s, children: chrome[INPUT_SUBTYPE_CHROME_KEY[s]] }, s)) })
1817
+ ] }),
1818
+ selected.type === "paragraph" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1819
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.content }),
1820
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("textarea", { style: { ...styles.propInput, minHeight: 70, resize: "vertical" }, value: t(selected.content, language), onChange: (e) => onUpdateField({ content: withLang(selected.content, language, e.target.value) }) }),
1821
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: styles.inspectorDivider }),
1822
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: styles.panelHeading, children: chrome.typography }),
1823
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.headingStyle }),
1824
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Segmented, { options: PARAGRAPH_TAG_OPTIONS.map((v) => ({ value: v, label: TAG_CHROME_KEY[v] ? chrome[TAG_CHROME_KEY[v]] : v.toUpperCase() })), value: selected.tag || "p", onChange: (v) => onUpdateField({ tag: v, ...TAG_PRESETS[v] }) }),
1825
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.width }),
1826
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Segmented, { options: FONT_SIZE_OPTIONS, value: selected.fontSize || "md", onChange: (v) => onUpdateField({ fontSize: v }) }),
1827
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.style }),
1828
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: styles.styleToggleRow, children: [
1829
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", style: selected.fontWeight === "bold" ? styles.styleToggleActive : styles.styleToggle, onClick: () => onUpdateField({ fontWeight: selected.fontWeight === "bold" ? "normal" : "bold" }), children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react13.Bold, { size: 14 }) }),
1830
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", style: selected.fontStyle === "italic" ? styles.styleToggleActive : styles.styleToggle, onClick: () => onUpdateField({ fontStyle: selected.fontStyle === "italic" ? "normal" : "italic" }), children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react13.Italic, { size: 14 }) })
1831
+ ] }),
1832
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.align }),
1833
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Segmented, { options: TEXT_ALIGN_OPTIONS, value: selected.textAlign || "left", onChange: (v) => onUpdateField({ textAlign: v }) }),
1834
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.color }),
1835
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: styles.swatchRow, children: [
1836
+ COLOR_SWATCHES.map((c) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", title: c || chrome.none, style: { ...styles.swatchBtn, background: c || "var(--fb-ink)", ...selected.color === c ? styles.swatchBtnActive : {} }, onClick: () => onUpdateField({ color: c }) }, c || "default")),
1837
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { type: "color", value: selected.color || "#1B1E24", onChange: (e) => onUpdateField({ color: e.target.value }), style: styles.colorPickerInput })
1838
+ ] })
1839
+ ] }),
1840
+ selected.type === "image" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1841
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.imageUrl }),
1842
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { style: styles.propInput, placeholder: "https://...", value: selected.src || "", onChange: (e) => onUpdateField({ src: e.target.value }) }),
1843
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.helperText, children: chrome.imageUrlHelper }),
1844
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.altText }),
1845
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { style: styles.propInput, value: t(selected.alt, language), onChange: (e) => onUpdateField({ alt: withLang(selected.alt, language, e.target.value) }) }),
1846
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.linkOptional }),
1847
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { style: styles.propInput, placeholder: "https://...", value: selected.link || "", onChange: (e) => onUpdateField({ link: e.target.value }) }),
1848
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.shape }),
1849
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Segmented, { options: IMAGE_SHAPES.map((v) => ({ value: v, label: chrome[IMAGE_SHAPE_CHROME_KEY[v]] })), value: selected.shape || "square", onChange: (v) => onUpdateField({ shape: v }) }),
1850
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { style: styles.helperText, children: "Fills the field's own width (set below under Layout) and scales height automatically \u2014 always responsive, never overflows." })
1851
+ ] }),
1852
+ meta.placeholder && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1853
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.placeholder }),
1854
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { style: styles.propInput, value: t(f.placeholder, language), onChange: (e) => onUpdateField({ placeholder: withLang(f.placeholder, language, e.target.value) }) })
1855
+ ] }),
1856
+ selected.type === "input" && (selected.inputType === "text" || selected.inputType === "password" || !selected.inputType) && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1857
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.maxLength }),
1858
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { type: "number", style: styles.propInput, placeholder: chrome.noLimit, value: selected.maxLength || "", onChange: (e) => onUpdateField({ maxLength: e.target.value ? Number(e.target.value) : null }) })
1859
+ ] }),
1860
+ selected.type === "input" && selected.inputType === "number" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1861
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.minValue }),
1862
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { type: "number", style: styles.propInput, value: selected.min ?? "", onChange: (e) => onUpdateField({ min: e.target.value === "" ? null : Number(e.target.value) }) }),
1863
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.maxValue }),
1864
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { type: "number", style: styles.propInput, value: selected.max ?? "", onChange: (e) => onUpdateField({ max: e.target.value === "" ? null : Number(e.target.value) }) })
1865
+ ] }),
1866
+ selected.type === "textarea" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1867
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.maxLength }),
1868
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { type: "number", style: styles.propInput, placeholder: chrome.noLimit, value: selected.maxLength || "", onChange: (e) => onUpdateField({ maxLength: e.target.value ? Number(e.target.value) : null }) })
1869
+ ] }),
1870
+ meta.options && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1871
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.options }),
1872
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: styles.optionList, children: [
1873
+ (f.options || []).map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: styles.optionRow, children: [
1874
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { style: styles.optionInput, value: t(opt.label, language), onChange: (e) => onUpdateOption(i, { label: withLang(opt.label, language, e.target.value) }) }),
1875
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { style: styles.iconBtn, onClick: () => onRemoveOption(i), disabled: (f.options || []).length <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react13.X, { size: 13 }) })
1876
+ ] }, i)),
1877
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("button", { style: styles.addOptionBtn, onClick: onAddOption, children: [
1878
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react13.Plus, { size: 13 }),
1879
+ " ",
1880
+ chrome.addOption
1881
+ ] })
1882
+ ] })
1883
+ ] }),
1884
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(DefaultValueEditor, { field: selected, meta, lang: language, chrome, onChange: onUpdateField }),
1885
+ !meta.isContent && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("label", { style: styles.toggleRow, children: [
1886
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { type: "checkbox", checked: !!selected.required, onChange: (e) => onUpdateField({ required: e.target.checked }), style: { width: 15, height: 15, accentColor: "var(--fb-primary)" } }),
1887
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { fontSize: 13, color: "#4A4D57" }, children: chrome.requiredField })
1888
+ ] }),
1889
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: styles.inspectorDivider }),
1890
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: styles.panelHeading, children: chrome.layout }),
1891
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.width }),
1892
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Segmented, { options: WIDTH_OPTIONS.map((o) => ({ ...o, label: o.labelKey ? chrome[o.labelKey] : o.label || "" })), value: selected.width || "1/1", onChange: (v) => onUpdateField({ width: v }) }),
1893
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.verticalAlign }),
1894
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Segmented, { options: [{ value: "top", label: chrome.top }, { value: "middle", label: chrome.middle }, { value: "bottom", label: chrome.bottom }], value: selected.verticalAlign || "top", onChange: (v) => onUpdateField({ verticalAlign: v }) }),
1895
+ !meta.boolean && !meta.isContent && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1896
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { style: styles.propLabel, children: chrome.labelPosition }),
1897
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Segmented, { options: [{ value: "top", label: chrome.above }, { value: "inline", label: chrome.inline }], value: selected.labelPosition || "top", onChange: (v) => onUpdateField({ labelPosition: v }) })
1898
+ ] }),
1899
+ !meta.isContent && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1900
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: styles.inspectorDivider }),
1901
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: styles.panelHeading, children: chrome.icon }),
1902
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("label", { style: styles.toggleRow, children: [
1903
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { type: "checkbox", checked: !!selected.showIcon, onChange: (e) => onUpdateField({ showIcon: e.target.checked }), style: { width: 15, height: 15, accentColor: "var(--fb-primary)" } }),
1904
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { fontSize: 13, color: "#4A4D57" }, children: chrome.showIcon })
1905
+ ] }),
1906
+ selected.showIcon && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: styles.iconGrid, children: Object.keys(ICON_LIBRARY).map((key) => {
1907
+ const IconComp = ICON_LIBRARY[key];
1908
+ const active = selected.displayIcon === key;
1909
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", title: key, style: active ? styles.iconGridBtnActive : styles.iconGridBtn, onClick: () => onUpdateField({ displayIcon: key }), children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(IconComp, { size: 15 }) }, key);
1910
+ }) })
1911
+ ] }),
1912
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: styles.inspectorDivider }),
1913
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("button", { style: styles.deleteFieldBtn, onClick: onDeleteField, children: [
1914
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react13.Trash2, { size: 13 }),
1915
+ " ",
1916
+ chrome.deleteField
1917
+ ] })
1918
+ ] });
1919
+ }
1920
+
1921
+ // src/components/PreviewPane.tsx
1922
+ var import_react6 = require("react");
1923
+ var import_lucide_react14 = require("lucide-react");
1924
+
1925
+ // src/lib/validate.ts
1926
+ function validateField(field, value, strings) {
1927
+ const meta = getMeta(field.type);
1928
+ if (meta.isContent) return null;
1929
+ const isEmpty = value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0 || !!meta.boolean && !!field.required && value !== true;
1930
+ if (field.required && isEmpty) return strings.requiredError;
1931
+ if (isEmpty) return null;
1932
+ if (field.type === "input" && field.inputType === "email" && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value))) return strings.invalidEmail;
1933
+ if (field.type === "input" && field.inputType === "phone" && !/^[0-9+\-\s()]{7,20}$/.test(String(value))) return strings.invalidPhone;
1934
+ if (field.type === "input" && field.inputType === "number") {
1935
+ const num = Number(value);
1936
+ if (field.min !== void 0 && field.min !== null && field.min !== "" && num < Number(field.min)) return strings.tooSmall(field.min);
1937
+ if (field.max !== void 0 && field.max !== null && field.max !== "" && num > Number(field.max)) return strings.tooLarge(field.max);
1938
+ }
1939
+ if ((field.type === "textarea" || field.type === "input" && !["number", "date", "time"].includes(field.inputType)) && field.maxLength && String(value).length > field.maxLength) {
1940
+ return strings.tooLong(field.maxLength);
1941
+ }
1942
+ return null;
1943
+ }
1944
+
1945
+ // src/lib/format.ts
1946
+ function formatValue(field, lang) {
1947
+ const v = field.defaultValue;
1948
+ if (Array.isArray(v)) {
1949
+ if (v.length === 0) return "\u2014";
1950
+ return v.map((val) => t(field.options?.find((o) => o.value === val)?.label, lang) || val).join(", ");
1951
+ }
1952
+ if (typeof v === "boolean") return v ? "Yes" : "No";
1953
+ if (field.options && v) return t(field.options.find((o) => o.value === v)?.label, lang) || v;
1954
+ if (v === void 0 || v === null || v === "") return "\u2014";
1955
+ return String(v);
1956
+ }
1957
+
1958
+ // src/components/PreviewPane.tsx
1959
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1960
+ function PreviewPane({ title, sections, onFieldChange, language, strings, chrome, baseMaxWidth, submitLabel, submitMode, submitStyle }) {
1961
+ const [device, setDevice] = (0, import_react6.useState)("laptop");
1962
+ const [errors, setErrors] = (0, import_react6.useState)({});
1963
+ const [submitted, setSubmitted] = (0, import_react6.useState)(null);
1964
+ const deviceOptions = buildDeviceOptions(baseMaxWidth, chrome);
1965
+ const maxWidth = (deviceOptions.find((d) => d.value === device) || deviceOptions[0]).maxWidth;
1966
+ const allFields = sections.flatMap((s) => s.fields);
1967
+ const submitText = t(submitLabel, language) || strings.submit;
1968
+ const overallHasFormFields = allFields.some((f) => !getMeta(f.type).isContent);
1969
+ function runValidation(fields) {
1970
+ const errs = {};
1971
+ fields.forEach((f) => {
1972
+ const e = validateField(f, f.defaultValue, strings);
1973
+ if (e) errs[f.id] = e;
1974
+ });
1975
+ return errs;
1976
+ }
1977
+ function handleSubmitAll() {
1978
+ const errs = runValidation(allFields);
1979
+ setErrors(errs);
1980
+ if (Object.keys(errs).length > 0) {
1981
+ setSubmitted(null);
1982
+ return;
1983
+ }
1984
+ setSubmitted(allFields.filter((f) => !getMeta(f.type).isContent).map((f) => ({ label: t(f.label, language), value: formatValue(f, language) })));
1985
+ }
1986
+ function handleSubmitSection(section) {
1987
+ const errs = runValidation(section.fields);
1988
+ setErrors((prev) => {
1989
+ const next = { ...prev };
1990
+ section.fields.forEach((f) => delete next[f.id]);
1991
+ return { ...next, ...errs };
1992
+ });
1993
+ if (Object.keys(errs).length > 0) {
1994
+ setSubmitted(null);
1995
+ return;
1996
+ }
1997
+ setSubmitted(section.fields.filter((f) => !getMeta(f.type).isContent).map((f) => ({ label: t(f.label, language), value: formatValue(f, language) })));
1998
+ }
1999
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: styles.previewWrap, children: [
2000
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { width: "100%", maxWidth, display: "flex", flexDirection: "column", alignItems: "center" }, children: [
2001
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: styles.previewToolbar, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Segmented, { options: deviceOptions, value: device, onChange: setDevice }) }),
2002
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { ...styles.previewCard, maxWidth, width: "100%" }, children: [
2003
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { style: styles.previewTitle, children: t(title, language) }),
2004
+ allFields.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: { color: "var(--fb-muted)", fontSize: 14 }, children: strings.addFieldsHint }),
2005
+ sections.map((section) => {
2006
+ const sectionHasFormFields = section.fields.some((f) => !getMeta(f.type).isContent);
2007
+ const sectionHasErrors = section.fields.some((f) => errors[f.id]);
2008
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { background: section.background || "transparent", padding: section.background ? 16 : 0, borderRadius: section.background ? 10 : 0, marginBottom: "var(--fb-space-section)" }, children: [
2009
+ t(section.title, language) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { style: styles.sectionRuntimeTitle, children: t(section.title, language) }),
2010
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: styles.previewGrid, children: section.fields.map((field) => {
2011
+ const width = effectiveWidth(field.width, device);
2012
+ const flexBasis = `1 1 calc(${WIDTH_PERCENT[width]} - 14px)`;
2013
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: { flex: flexBasis, minWidth: 0, alignSelf: ALIGN_MAP[field.verticalAlign || "top"] }, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FieldBlock, { field, lang: language, onFieldChange, strings, chrome, error: errors[field.id] }) }, field.id);
2014
+ }) }),
2015
+ submitMode === "perSection" && sectionHasFormFields && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
2016
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { style: { ...styles.submitBtn, ...resolveSubmitStyle(section.submitStyle || submitStyle) }, onClick: () => handleSubmitSection(section), children: submitText }),
2017
+ sectionHasErrors && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { style: styles.formErrorNote, children: [
2018
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react14.CircleAlert, { size: 13 }),
2019
+ " ",
2020
+ strings.fixErrors
2021
+ ] })
2022
+ ] })
2023
+ ] }, section.id);
2024
+ }),
2025
+ submitMode === "combined" && overallHasFormFields && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { style: { ...styles.submitBtn, ...resolveSubmitStyle(submitStyle) }, onClick: handleSubmitAll, children: submitText }),
2026
+ submitMode === "combined" && Object.keys(errors).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { style: styles.formErrorNote, children: [
2027
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react14.CircleAlert, { size: 13 }),
2028
+ " ",
2029
+ strings.fixErrors
2030
+ ] })
2031
+ ] })
2032
+ ] }),
2033
+ submitted && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: styles.modalOverlay, onClick: () => setSubmitted(null), children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { ...styles.modal, width: 420 }, onClick: (e) => e.stopPropagation(), children: [
2034
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: styles.modalHeader, children: [
2035
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { style: { fontWeight: 600, fontSize: 14, display: "flex", alignItems: "center", gap: 7 }, children: [
2036
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react14.PartyPopper, { size: 15, color: "var(--fb-primary)" }),
2037
+ " ",
2038
+ strings.submittedTitle
2039
+ ] }),
2040
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { style: styles.iconBtn, onClick: () => setSubmitted(null), children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react14.X, { size: 16 }) })
2041
+ ] }),
2042
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { padding: 16 }, children: [
2043
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { style: { fontSize: 12.5, color: "var(--fb-muted)", marginTop: 0 }, children: strings.submittedBody }),
2044
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: styles.submittedList, children: submitted.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: styles.submittedRow, children: [
2045
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: styles.submittedLabel, children: row.label }),
2046
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: styles.submittedValue, children: row.value })
2047
+ ] }, i)) }),
2048
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { style: { ...styles.primaryBtn, width: "100%", justifyContent: "center", marginTop: 14 }, onClick: () => setSubmitted(null), children: strings.close })
2049
+ ] })
2050
+ ] }) })
2051
+ ] });
2052
+ }
2053
+
2054
+ // src/components/modals/JsonModal.tsx
2055
+ var import_lucide_react15 = require("lucide-react");
2056
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2057
+ function JsonModal({ chrome, jsonString, copied, onCopy, onClose }) {
2058
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: styles.modalOverlay, onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: styles.modal, onClick: (e) => e.stopPropagation(), children: [
2059
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: styles.modalHeader, children: [
2060
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { fontWeight: 600, fontSize: 14 }, children: chrome.formJson }),
2061
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", gap: 8 }, children: [
2062
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { style: styles.ghostBtn, onClick: onCopy, children: [
2063
+ copied ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react15.Check, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react15.ClipboardCopy, { size: 14 }),
2064
+ copied ? chrome.copied : chrome.copy
2065
+ ] }),
2066
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { style: styles.iconBtn, onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react15.X, { size: 16 }) })
2067
+ ] })
2068
+ ] }),
2069
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("pre", { style: styles.jsonPre, children: jsonString })
2070
+ ] }) });
2071
+ }
2072
+
2073
+ // src/components/modals/LibraryModal.tsx
2074
+ var import_lucide_react16 = require("lucide-react");
2075
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2076
+ function LibraryModal({ chrome, savedForms, currentFormId, onOpen, onDelete, onClose }) {
2077
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: styles.modalOverlay, onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: styles.modal, onClick: (e) => e.stopPropagation(), children: [
2078
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: styles.modalHeader, children: [
2079
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { style: { fontWeight: 600, fontSize: 14 }, children: chrome.myForms }),
2080
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { style: styles.iconBtn, onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react16.X, { size: 16 }) })
2081
+ ] }),
2082
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: styles.libraryBody, children: savedForms.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: styles.inspectorEmpty, children: chrome.nothingSaved }) : savedForms.map((f) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: styles.libraryRow, children: [
2083
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { minWidth: 0 }, children: [
2084
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: styles.libraryRowTitle, children: [
2085
+ f.title,
2086
+ f.id === currentFormId && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { style: styles.currentBadge, children: chrome.current })
2087
+ ] }),
2088
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: styles.libraryRowMeta, children: [
2089
+ chrome.updated,
2090
+ " ",
2091
+ new Date(f.updatedAt).toLocaleString()
2092
+ ] })
2093
+ ] }),
2094
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { display: "flex", gap: 6, flexShrink: 0 }, children: [
2095
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { style: styles.ghostBtn, onClick: () => onOpen(f.id), children: chrome.open }),
2096
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { style: { ...styles.iconBtn, ...styles.iconBtnDanger }, title: chrome.delete, onClick: () => onDelete(f.id), children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react16.Trash2, { size: 13 }) })
2097
+ ] })
2098
+ ] }, f.id)) })
2099
+ ] }) });
2100
+ }
2101
+
2102
+ // src/components/modals/SaveAsModal.tsx
2103
+ var import_react7 = require("react");
2104
+ var import_lucide_react17 = require("lucide-react");
2105
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2106
+ function SaveAsModal({ chrome, suggestedName, onSave, onClose }) {
2107
+ const [name, setName] = (0, import_react7.useState)(suggestedName);
2108
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: styles.modalOverlay, onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { ...styles.modal, width: 380 }, onClick: (e) => e.stopPropagation(), children: [
2109
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: styles.modalHeader, children: [
2110
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: { fontWeight: 600, fontSize: 14 }, children: chrome.saveForm }),
2111
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { style: styles.iconBtn, onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react17.X, { size: 16 }) })
2112
+ ] }),
2113
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { padding: 16 }, children: [
2114
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("label", { style: styles.propLabel, children: chrome.formName }),
2115
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("input", { autoFocus: true, style: styles.propInput, value: name, onChange: (e) => setName(e.target.value), onKeyDown: (e) => e.key === "Enter" && name.trim() && onSave(name.trim()) }),
2116
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("button", { style: { ...styles.primaryBtn, width: "100%", justifyContent: "center", marginTop: 14 }, disabled: !name.trim(), onClick: () => onSave(name.trim()), children: [
2117
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react17.Save, { size: 14 }),
2118
+ " ",
2119
+ chrome.saveToLibrary
2120
+ ] })
2121
+ ] })
2122
+ ] }) });
2123
+ }
2124
+
2125
+ // src/components/modals/SettingsModal.tsx
2126
+ var import_lucide_react18 = require("lucide-react");
2127
+ var import_jsx_runtime16 = require("react/jsx-runtime");
2128
+ function SettingsModal({
2129
+ chrome,
2130
+ strings,
2131
+ submitLabel,
2132
+ language,
2133
+ submitMode,
2134
+ submitStyle,
2135
+ onSubmitLabelChange,
2136
+ onSubmitModeChange,
2137
+ onSubmitStyleChange,
2138
+ onClose
2139
+ }) {
2140
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: styles.modalOverlay, onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { ...styles.modal, width: 400 }, onClick: (e) => e.stopPropagation(), children: [
2141
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: styles.modalHeader, children: [
2142
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { fontWeight: 600, fontSize: 14 }, children: chrome.formSettings }),
2143
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { style: styles.iconBtn, onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react18.X, { size: 16 }) })
2144
+ ] }),
2145
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { padding: 16 }, children: [
2146
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: styles.panelHeading, children: chrome.submitSectionTitle }),
2147
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("label", { style: styles.propLabel, children: chrome.submitLabel }),
2148
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("input", { style: styles.propInput, placeholder: strings.submit, value: t(submitLabel, language), onChange: (e) => onSubmitLabelChange(e.target.value) }),
2149
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("label", { style: styles.propLabel, children: chrome.submitMode }),
2150
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Segmented, { options: [{ value: "combined", label: chrome.combined }, { value: "perSection", label: chrome.perSection }], value: submitMode, onChange: onSubmitModeChange }),
2151
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: styles.inspectorDivider }),
2152
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: styles.panelHeading, children: chrome.submitStyle }),
2153
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("label", { style: styles.propLabel, children: chrome.color }),
2154
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: styles.swatchRow, children: [
2155
+ BUTTON_COLOR_SWATCHES.map((c) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", title: c || chrome.none, style: { ...styles.swatchBtn, background: c || "var(--fb-primary)", ...(submitStyle.color || "") === c ? styles.swatchBtnActive : {} }, onClick: () => onSubmitStyleChange({ color: c }) }, c || "default")),
2156
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("input", { type: "color", value: submitStyle.color || "#5B5FEF", onChange: (e) => onSubmitStyleChange({ color: e.target.value }), style: styles.colorPickerInput })
2157
+ ] }),
2158
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("label", { style: styles.propLabel, children: chrome.buttonSize }),
2159
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Segmented, { options: SUBMIT_SIZE_OPTIONS, value: submitStyle.size, onChange: (v) => onSubmitStyleChange({ size: v }) })
2160
+ ] })
2161
+ ] }) });
2162
+ }
2163
+
2164
+ // src/styles/globalCss.ts
2165
+ var css = `
2166
+ * { box-sizing: border-box; }
2167
+ button { font-family: inherit; cursor: pointer; }
2168
+ input, textarea, select { font-family: inherit; }
2169
+ @keyframes fb-spin { to { transform: rotate(360deg); } }
2170
+ .spin { animation: fb-spin 0.8s linear infinite; }
2171
+ `;
2172
+
2173
+ // src/FormBuilder.tsx
2174
+ var import_jsx_runtime17 = require("react/jsx-runtime");
2175
+ function FormBuilder({
2176
+ theme: themeOverrideProp,
2177
+ language: languageOverride,
2178
+ languages = DEFAULT_LANGUAGES,
2179
+ strings: stringsOverride,
2180
+ chrome: chromeOverride,
2181
+ themeEditable = false,
2182
+ storage: storageProp
2183
+ } = {}) {
2184
+ const storage = storageProp ?? localStorageAdapter;
2185
+ const { theme, updateThemeColor, updateThemeLayout, resetTheme, replaceThemeOverrides, themeOverrides } = useTheme(themeOverrideProp);
2186
+ const [language, setLanguage] = (0, import_react8.useState)(languageOverride || languages[0]?.code || "en");
2187
+ const strings = { ...DEFAULT_STRINGS[language] || DEFAULT_STRINGS.en, ...stringsOverride && stringsOverride[language] || {} };
2188
+ const chrome = { ...CHROME[language] || CHROME.en, ...chromeOverride && chromeOverride[language] || {} };
2189
+ const doc = useFormDocument({ language, chrome });
2190
+ const drag = useDragReorder(doc.reorderWithinSection);
2191
+ const [mode, setMode] = (0, import_react8.useState)("build");
2192
+ const [showJson, setShowJson] = (0, import_react8.useState)(false);
2193
+ const [showSettings, setShowSettings] = (0, import_react8.useState)(false);
2194
+ const [showLibrary, setShowLibrary] = (0, import_react8.useState)(false);
2195
+ const [copied, setCopied] = (0, import_react8.useState)(false);
2196
+ const persistence = usePersistence({
2197
+ storage,
2198
+ language,
2199
+ document: {
2200
+ title: doc.title,
2201
+ submitLabel: doc.submitLabel,
2202
+ submitMode: doc.submitMode,
2203
+ submitStyle: doc.submitStyle,
2204
+ themeOverrides,
2205
+ sections: doc.sections
2206
+ },
2207
+ onLoadDocument: doc.loadDocument,
2208
+ onLoadThemeOverrides: replaceThemeOverrides,
2209
+ onTitleChange: doc.setTitle,
2210
+ onNewForm: () => {
2211
+ doc.resetToBlank();
2212
+ resetTheme();
2213
+ },
2214
+ ensureActiveSection: () => doc.setActiveSectionId((prev) => prev ?? doc.sections[0]?.id ?? null)
2215
+ });
2216
+ const jsonDoc = {
2217
+ version: 4,
2218
+ title: doc.title,
2219
+ submitLabel: doc.submitLabel,
2220
+ submitMode: doc.submitMode,
2221
+ submitStyle: doc.submitStyle,
2222
+ themeOverrides,
2223
+ sections: doc.sections.map((s) => ({ id: s.id, title: s.title, background: s.background, collapsed: s.collapsed, submitStyle: s.submitStyle, fields: s.fields }))
2224
+ };
2225
+ const jsonString = JSON.stringify(jsonDoc, null, 2);
2226
+ function copyJson() {
2227
+ navigator.clipboard.writeText(jsonString).then(() => {
2228
+ setCopied(true);
2229
+ setTimeout(() => setCopied(false), 1500);
2230
+ }).catch(() => {
2231
+ });
2232
+ }
2233
+ const activeSectionIdx = doc.sections.findIndex((s) => s.id === doc.activeSection?.id);
2234
+ const activeSectionLabel = t(doc.activeSection?.title, language) || `#${activeSectionIdx + 1}`;
2235
+ const rootStyle = {
2236
+ ...styles.app,
2237
+ "--fb-primary": theme.primary,
2238
+ "--fb-primary-soft": theme.primarySoft,
2239
+ "--fb-danger": theme.danger,
2240
+ "--fb-danger-soft": theme.dangerSoft,
2241
+ "--fb-ink": theme.ink,
2242
+ "--fb-muted": theme.muted,
2243
+ "--fb-border": theme.border,
2244
+ "--fb-surface": theme.surface,
2245
+ "--fb-canvas": theme.canvas,
2246
+ "--fb-page-bg": theme.pageBackground,
2247
+ "--fb-space-page": `${theme.layout.pagePadding}px`,
2248
+ "--fb-space-canvas": `${theme.layout.canvasPadding}px`,
2249
+ "--fb-space-section": `${theme.layout.sectionGap}px`,
2250
+ "--fb-space-field": `${theme.layout.fieldGap}px`,
2251
+ "--fb-space-toolbar": `${theme.layout.toolbarPadding}px`,
2252
+ "--fb-space-panel": `${theme.layout.panelPadding}px`,
2253
+ "--fb-space-ticket": `${theme.layout.ticketPadding}px`
2254
+ };
2255
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: rootStyle, children: [
2256
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("style", { children: css }),
2257
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2258
+ Toolbar,
2259
+ {
2260
+ title: doc.title,
2261
+ language,
2262
+ languages,
2263
+ mode,
2264
+ saveState: persistence.saveState,
2265
+ chrome,
2266
+ themeEditable,
2267
+ theme,
2268
+ savedFormsCount: persistence.savedForms.length,
2269
+ onTitleChange: doc.updateTitle,
2270
+ onLanguageChange: setLanguage,
2271
+ onModeChange: setMode,
2272
+ onOpenSettings: () => setShowSettings(true),
2273
+ onNewForm: persistence.newForm,
2274
+ onOpenLibrary: () => setShowLibrary(true),
2275
+ onSaveExisting: persistence.saveExisting,
2276
+ onOpenJson: () => setShowJson(true),
2277
+ updateThemeColor,
2278
+ updateThemeLayout,
2279
+ resetTheme
2280
+ }
2281
+ ),
2282
+ persistence.loadingDraft ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: styles.loadingScreen, children: [
2283
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react19.Loader2, { size: 20, className: "spin" }),
2284
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: { marginTop: 8, fontSize: 13, color: "var(--fb-muted)" }, children: chrome.loadingDraft })
2285
+ ] }) : mode === "build" ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: styles.workArea, children: [
2286
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Palette, { activeSectionLabel, chrome, onAddField: doc.addField }),
2287
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2288
+ Canvas,
2289
+ {
2290
+ sections: doc.sections,
2291
+ activeSectionId: doc.activeSectionId,
2292
+ submitMode: doc.submitMode,
2293
+ submitStyle: doc.submitStyle,
2294
+ selectedId: doc.selectedId,
2295
+ dragOverKey: drag.dragOverKey,
2296
+ chrome,
2297
+ strings,
2298
+ language,
2299
+ onActivateSection: doc.setActiveSectionId,
2300
+ onToggleSectionCollapse: doc.toggleSectionCollapse,
2301
+ onUpdateSectionTitle: doc.updateSectionTitle,
2302
+ onUpdateSectionBackground: doc.updateSectionBackground,
2303
+ onDuplicateSection: doc.duplicateSection,
2304
+ onMoveSection: doc.moveSection,
2305
+ onDeleteSection: doc.deleteSection,
2306
+ onUpdateSectionSubmitStyle: doc.updateSectionSubmitStyle,
2307
+ onClearSectionSubmitStyle: doc.clearSectionSubmitStyle,
2308
+ onAddSection: doc.addSection,
2309
+ onSelectField: (sectionId, fieldId) => {
2310
+ doc.setSelectedId(fieldId);
2311
+ doc.setActiveSectionId(sectionId);
2312
+ },
2313
+ onFieldChange: doc.updateField,
2314
+ onMoveField: doc.moveField,
2315
+ onDuplicateField: doc.duplicateField,
2316
+ onDeleteField: doc.deleteField,
2317
+ getDropZoneHandlers: drag.getDropZoneHandlers,
2318
+ getDragHandleProps: drag.getDragHandleProps
2319
+ }
2320
+ ),
2321
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2322
+ Inspector,
2323
+ {
2324
+ selected: doc.selected,
2325
+ language,
2326
+ chrome,
2327
+ onUpdateField: (patch) => doc.selected && doc.updateField(doc.selected.id, patch),
2328
+ onDeleteField: () => doc.selected && doc.deleteField(doc.selected.id),
2329
+ onUpdateOption: (optIdx, patch) => doc.selected && doc.updateOption(doc.selected.id, optIdx, patch),
2330
+ onAddOption: () => doc.selected && doc.addOption(doc.selected.id),
2331
+ onRemoveOption: (optIdx) => doc.selected && doc.removeOption(doc.selected.id, optIdx)
2332
+ }
2333
+ )
2334
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2335
+ PreviewPane,
2336
+ {
2337
+ title: doc.title,
2338
+ sections: doc.sections,
2339
+ onFieldChange: doc.updateField,
2340
+ language,
2341
+ strings,
2342
+ chrome,
2343
+ baseMaxWidth: theme.layout.maxWidth,
2344
+ submitLabel: doc.submitLabel,
2345
+ submitMode: doc.submitMode,
2346
+ submitStyle: doc.submitStyle
2347
+ }
2348
+ ),
2349
+ showJson && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(JsonModal, { chrome, jsonString, copied, onCopy: copyJson, onClose: () => setShowJson(false) }),
2350
+ showLibrary && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2351
+ LibraryModal,
2352
+ {
2353
+ chrome,
2354
+ savedForms: persistence.savedForms,
2355
+ currentFormId: persistence.currentFormId,
2356
+ onOpen: async (id) => {
2357
+ await persistence.loadForm(id);
2358
+ setShowLibrary(false);
2359
+ },
2360
+ onDelete: persistence.deleteForm,
2361
+ onClose: () => setShowLibrary(false)
2362
+ }
2363
+ ),
2364
+ persistence.saveAsPrompt && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2365
+ SaveAsModal,
2366
+ {
2367
+ chrome,
2368
+ suggestedName: persistence.saveAsPrompt.suggestedName,
2369
+ onSave: persistence.saveAs,
2370
+ onClose: persistence.dismissSaveAsPrompt
2371
+ }
2372
+ ),
2373
+ showSettings && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2374
+ SettingsModal,
2375
+ {
2376
+ chrome,
2377
+ strings,
2378
+ submitLabel: doc.submitLabel,
2379
+ language,
2380
+ submitMode: doc.submitMode,
2381
+ submitStyle: doc.submitStyle,
2382
+ onSubmitLabelChange: doc.updateSubmitLabel,
2383
+ onSubmitModeChange: doc.setSubmitMode,
2384
+ onSubmitStyleChange: doc.updateSubmitStyle,
2385
+ onClose: () => setShowSettings(false)
2386
+ }
2387
+ )
2388
+ ] });
2389
+ }
2390
+ // Annotate the CommonJS export names for ESM import in node:
2391
+ 0 && (module.exports = {
2392
+ FormBuilder,
2393
+ localStorageAdapter
2394
+ });
2395
+ //# sourceMappingURL=index.cjs.map