formshell 1.0.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.js ADDED
@@ -0,0 +1,1070 @@
1
+ const i = {
2
+ // Main color palette (CSS styling for console.log)
3
+ colors: {
4
+ primary: "#6366f1",
5
+ // Indigo
6
+ secondary: "#8b5cf6",
7
+ // Purple
8
+ accent: "#ec4899",
9
+ // Pink
10
+ success: "#10b981",
11
+ // Green
12
+ error: "#ef4444",
13
+ // Red
14
+ warning: "#f59e0b",
15
+ // Orange
16
+ info: "#3b82f6",
17
+ // Blue
18
+ text: {
19
+ primary: "#f8fafc",
20
+ // Almost pure white
21
+ secondary: "#cbd5e1",
22
+ // Light gray
23
+ muted: "#64748b",
24
+ // Medium gray
25
+ dark: "#1e293b"
26
+ // Dark gray for background
27
+ },
28
+ background: {
29
+ main: "#0f172a",
30
+ // Dark blue
31
+ secondary: "#1e293b",
32
+ // Lighter dark blue
33
+ accent: "#334155"
34
+ // Blue gray
35
+ }
36
+ },
37
+ // Unicode icons for UI elements
38
+ icons: {
39
+ checkmark: "✓",
40
+ cross: "✗",
41
+ star: "★",
42
+ starEmpty: "☆",
43
+ arrow: "➤",
44
+ bullet: "◆",
45
+ bulletEmpty: "◇",
46
+ chevron: "▸",
47
+ heart: "♥",
48
+ info: "ℹ",
49
+ warning: "⚠",
50
+ question: "?",
51
+ cursor: "█",
52
+ cursorBlink: "▊",
53
+ spinner: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
54
+ },
55
+ // Box drawing characters (Unicode)
56
+ box: {
57
+ // Single lines
58
+ topLeft: "┌",
59
+ topRight: "┐",
60
+ bottomLeft: "└",
61
+ bottomRight: "┘",
62
+ horizontal: "─",
63
+ vertical: "│",
64
+ // Double lines (for emphasis)
65
+ doubleTopLeft: "╔",
66
+ doubleTopRight: "╗",
67
+ doubleBottomLeft: "╚",
68
+ doubleBottomRight: "╝",
69
+ doubleHorizontal: "═",
70
+ doubleVertical: "║",
71
+ // Progress bar characters
72
+ filled: "▓",
73
+ empty: "░",
74
+ halfFilled: "▒",
75
+ // Shadow/gradient characters
76
+ shadow: ["░", "▒", "▓"],
77
+ fade: ["⣀", "⣄", "⣤", "⣦", "⣶", "⣷", "⣿"]
78
+ },
79
+ // Text styles (using Unicode characters or ANSI)
80
+ text: {
81
+ bold: (n) => `\x1B[1m${n}\x1B[0m`,
82
+ italic: (n) => `\x1B[3m${n}\x1B[0m`,
83
+ underline: (n) => `\x1B[4m${n}\x1B[0m`,
84
+ // Alternative using Unicode mathematical characters (fallback)
85
+ boldUnicode: (n) => {
86
+ const e = {
87
+ a: "𝗮",
88
+ b: "𝗯",
89
+ c: "𝗰",
90
+ d: "𝗱",
91
+ e: "𝗲",
92
+ f: "𝗳",
93
+ g: "𝗴",
94
+ h: "𝗵",
95
+ i: "𝗶",
96
+ j: "𝗷",
97
+ k: "𝗸",
98
+ l: "𝗹",
99
+ m: "𝗺",
100
+ n: "𝗻",
101
+ o: "𝗼",
102
+ p: "𝗽",
103
+ q: "𝗾",
104
+ r: "𝗿",
105
+ s: "𝘀",
106
+ t: "𝘁",
107
+ u: "𝘂",
108
+ v: "𝘃",
109
+ w: "𝘄",
110
+ x: "𝘅",
111
+ y: "𝘆",
112
+ z: "𝘇",
113
+ A: "𝗔",
114
+ B: "𝗕",
115
+ C: "𝗖",
116
+ D: "𝗗",
117
+ E: "𝗘",
118
+ F: "𝗙",
119
+ G: "𝗚",
120
+ H: "𝗛",
121
+ I: "𝗜",
122
+ J: "𝗝",
123
+ K: "𝗞",
124
+ L: "𝗟",
125
+ M: "𝗠",
126
+ N: "𝗡",
127
+ O: "𝗢",
128
+ P: "𝗣",
129
+ Q: "𝗤",
130
+ R: "𝗥",
131
+ S: "𝗦",
132
+ T: "𝗧",
133
+ U: "𝗨",
134
+ V: "𝗩",
135
+ W: "𝗪",
136
+ X: "𝗫",
137
+ Y: "𝗬",
138
+ Z: "𝗭",
139
+ 0: "𝟬",
140
+ 1: "𝟭",
141
+ 2: "𝟮",
142
+ 3: "𝟯",
143
+ 4: "𝟰",
144
+ 5: "𝟱",
145
+ 6: "𝟲",
146
+ 7: "𝟳",
147
+ 8: "𝟴",
148
+ 9: "𝟵"
149
+ };
150
+ return n.split("").map((t) => e[t] || t).join("");
151
+ }
152
+ },
153
+ // Style presets for common elements
154
+ styles: {
155
+ title: {
156
+ color: "#6366f1",
157
+ fontSize: "20px",
158
+ fontWeight: "bold",
159
+ textShadow: "0 0 10px rgba(99, 102, 241, 0.5)"
160
+ },
161
+ subtitle: {
162
+ color: "#8b5cf6",
163
+ fontSize: "16px",
164
+ fontWeight: "600"
165
+ },
166
+ body: {
167
+ color: "#f8fafc",
168
+ fontSize: "14px",
169
+ lineHeight: "1.5"
170
+ },
171
+ success: {
172
+ color: "#10b981",
173
+ fontSize: "14px",
174
+ fontWeight: "bold"
175
+ },
176
+ error: {
177
+ color: "#ef4444",
178
+ fontSize: "14px",
179
+ fontWeight: "bold"
180
+ },
181
+ muted: {
182
+ color: "#64748b",
183
+ fontSize: "13px",
184
+ fontStyle: "italic"
185
+ },
186
+ highlight: {
187
+ color: "#ec4899",
188
+ fontSize: "14px",
189
+ fontWeight: "bold",
190
+ textShadow: "0 0 8px rgba(236, 72, 153, 0.4)"
191
+ }
192
+ },
193
+ // Helper to create CSS style string for console.log
194
+ createStyle: (n) => Object.entries(n).filter((e) => e[1] !== void 0).map(([e, t]) => `${e.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${t}`).join("; "),
195
+ // Preset messages with styles
196
+ format: {
197
+ title: (n) => [`%c${n}`, i.createStyle(i.styles.title)],
198
+ subtitle: (n) => [`%c${n}`, i.createStyle(i.styles.subtitle)],
199
+ body: (n) => [`%c${n}`, i.createStyle(i.styles.body)],
200
+ success: (n) => [`%c${i.icons.checkmark} ${n}`, i.createStyle(i.styles.success)],
201
+ error: (n) => [`%c${i.icons.cross} ${n}`, i.createStyle(i.styles.error)],
202
+ muted: (n) => [`%c${n}`, i.createStyle(i.styles.muted)],
203
+ highlight: (n) => [`%c${n}`, i.createStyle(i.styles.highlight)],
204
+ // Quick custom colors
205
+ colored: (n, e) => [`%c${n}`, `color: ${e}; font-size: 14px;`]
206
+ }
207
+ };
208
+ class d {
209
+ constructor(e) {
210
+ this.type = e.type, this.id = e.id, this.label = e.label, this.required = e.required !== !1, this.value = e.defaultValue ?? null, this.error = null, this.condition = "condition" in e ? e.condition : void 0;
211
+ }
212
+ /**
213
+ * Validate the field (to be overridden in subclasses)
214
+ */
215
+ validate(e) {
216
+ return this.required && (e == null || e === "") ? {
217
+ valid: !1,
218
+ error: "This field is required"
219
+ } : { valid: !0 };
220
+ }
221
+ /**
222
+ * Format the value for display
223
+ */
224
+ format(e) {
225
+ return String(e ?? "");
226
+ }
227
+ /**
228
+ * Get the current value
229
+ */
230
+ getValue() {
231
+ return this.value;
232
+ }
233
+ /**
234
+ * Set the value
235
+ */
236
+ setValue(e) {
237
+ const t = this.validate(e);
238
+ return t.valid ? (this.value = e, this.error = null, !0) : (this.error = t.error, !1);
239
+ }
240
+ }
241
+ class a extends d {
242
+ constructor(e) {
243
+ super(e), this.minLength = "minLength" in e ? e.minLength ?? null : null, this.maxLength = "maxLength" in e ? e.maxLength ?? null : null, this.pattern = "pattern" in e ? e.pattern ?? null : null;
244
+ }
245
+ validate(e) {
246
+ const t = super.validate(e);
247
+ if (!t.valid) return t;
248
+ if (!e && !this.required)
249
+ return { valid: !0 };
250
+ const r = String(e);
251
+ return this.minLength && r.length < this.minLength ? {
252
+ valid: !1,
253
+ error: `Minimum ${this.minLength} characters required`
254
+ } : this.maxLength && r.length > this.maxLength ? {
255
+ valid: !1,
256
+ error: `Maximum ${this.maxLength} characters allowed`
257
+ } : this.pattern && !this.pattern.test(r) ? {
258
+ valid: !1,
259
+ error: "Invalid format"
260
+ } : { valid: !0 };
261
+ }
262
+ }
263
+ class u extends d {
264
+ constructor(e) {
265
+ super(e), this.min = "min" in e && e.min !== void 0 ? e.min : null, this.max = "max" in e && e.max !== void 0 ? e.max : null, this.integer = "integer" in e ? e.integer ?? !1 : !1;
266
+ }
267
+ validate(e) {
268
+ const t = super.validate(e);
269
+ if (!t.valid) return t;
270
+ if (!e && !this.required)
271
+ return { valid: !0 };
272
+ const r = Number(e);
273
+ return isNaN(r) ? {
274
+ valid: !1,
275
+ error: "Must be a valid number"
276
+ } : this.integer && !Number.isInteger(r) ? {
277
+ valid: !1,
278
+ error: "Must be an integer"
279
+ } : this.min !== null && r < this.min ? {
280
+ valid: !1,
281
+ error: `Minimum value: ${this.min}`
282
+ } : this.max !== null && r > this.max ? {
283
+ valid: !1,
284
+ error: `Maximum value: ${this.max}`
285
+ } : { valid: !0 };
286
+ }
287
+ format(e) {
288
+ return e != null ? String(e) : "";
289
+ }
290
+ }
291
+ class V extends a {
292
+ constructor(e) {
293
+ super(e), this.pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
294
+ }
295
+ validate(e) {
296
+ const t = super.validate(e);
297
+ return t.valid ? t : {
298
+ valid: !1,
299
+ error: t.error === "Invalid format" ? "Invalid email address" : t.error
300
+ };
301
+ }
302
+ }
303
+ class I extends a {
304
+ constructor(e) {
305
+ super(e), this.pattern = /^https?:\/\/.+\..+/;
306
+ }
307
+ validate(e) {
308
+ const t = super.validate(e);
309
+ return t.valid ? t : {
310
+ valid: !1,
311
+ error: t.error === "Invalid format" ? "Invalid URL (must start with http:// or https://)" : t.error
312
+ };
313
+ }
314
+ }
315
+ class T extends a {
316
+ constructor(e) {
317
+ super(e), this.pattern = /^(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[012])\/(19|20)\d\d$/;
318
+ }
319
+ validate(e) {
320
+ const t = super.validate(e);
321
+ if (!t.valid)
322
+ return {
323
+ valid: !1,
324
+ error: t.error === "Invalid format" ? "Invalid date (format: DD/MM/YYYY)" : t.error
325
+ };
326
+ if (e && typeof e == "string" && this.pattern.test(e)) {
327
+ const [r, s, o] = e.split("/").map(Number), l = new Date(o, s - 1, r);
328
+ if (l.getDate() !== r || l.getMonth() !== s - 1 || l.getFullYear() !== o)
329
+ return {
330
+ valid: !1,
331
+ error: "Invalid date (format: DD/MM/YYYY)"
332
+ };
333
+ }
334
+ return t;
335
+ }
336
+ }
337
+ class S extends d {
338
+ constructor(e) {
339
+ super(e), this.options = "options" in e ? e.options ?? [] : [];
340
+ }
341
+ validate(e) {
342
+ const t = super.validate(e);
343
+ if (!t.valid) return t;
344
+ if (!e && !this.required)
345
+ return { valid: !0 };
346
+ if (typeof e == "number")
347
+ return e < 1 || e > this.options.length ? {
348
+ valid: !1,
349
+ error: `Choose a number between 1 and ${this.options.length}`
350
+ } : { valid: !0 };
351
+ const r = this.options.map(
352
+ (s) => typeof s == "string" ? s : s.value
353
+ );
354
+ return typeof e == "string" && !r.includes(e) ? {
355
+ valid: !1,
356
+ error: "Invalid choice"
357
+ } : { valid: !0 };
358
+ }
359
+ setValue(e) {
360
+ if (typeof e == "number") {
361
+ const t = e - 1;
362
+ if (t >= 0 && t < this.options.length) {
363
+ const r = this.options[t], s = typeof r == "string" ? r : r.value;
364
+ return super.setValue(s);
365
+ }
366
+ }
367
+ return super.setValue(e);
368
+ }
369
+ format(e) {
370
+ if (!e) return "";
371
+ const t = this.options.find(
372
+ (r) => (typeof r == "string" ? r : r.value) === e
373
+ );
374
+ return typeof t == "string" ? t : (t == null ? void 0 : t.label) || String(e);
375
+ }
376
+ }
377
+ class x extends d {
378
+ constructor(e) {
379
+ super(e), this.options = "options" in e ? e.options ?? [] : [], this.minChoices = "minChoices" in e ? e.minChoices ?? (this.required ? 1 : 0) : this.required ? 1 : 0, this.maxChoices = "maxChoices" in e ? e.maxChoices ?? this.options.length : this.options.length;
380
+ }
381
+ validate(e) {
382
+ if (!Array.isArray(e))
383
+ return this.required ? {
384
+ valid: !1,
385
+ error: "Select at least 1 option(s)"
386
+ } : { valid: !0 };
387
+ if (e.length < this.minChoices)
388
+ return {
389
+ valid: !1,
390
+ error: `Select at least ${this.minChoices} option(s)`
391
+ };
392
+ if (e.length > this.maxChoices)
393
+ return {
394
+ valid: !1,
395
+ error: `Select maximum ${this.maxChoices} option(s)`
396
+ };
397
+ const t = this.options.map(
398
+ (r) => typeof r == "string" ? r : r.value
399
+ );
400
+ for (const r of e)
401
+ if (!t.includes(r))
402
+ return {
403
+ valid: !1,
404
+ error: "One or more choices are invalid"
405
+ };
406
+ return { valid: !0 };
407
+ }
408
+ setValue(e) {
409
+ if (typeof e == "string") {
410
+ const t = e.split(/[,\s]+/).map((s) => s.trim()).filter((s) => s), r = [];
411
+ for (const s of t) {
412
+ const o = Number(s);
413
+ if (!isNaN(o) && o >= 1 && o <= this.options.length) {
414
+ const l = this.options[o - 1];
415
+ r.push(typeof l == "string" ? l : l.value);
416
+ }
417
+ }
418
+ return super.setValue(r);
419
+ }
420
+ return super.setValue(e);
421
+ }
422
+ format(e) {
423
+ return !Array.isArray(e) || e.length === 0 ? "No selection" : e.map((r) => {
424
+ const s = this.options.find(
425
+ (o) => (typeof o == "string" ? o : o.value) === r
426
+ );
427
+ return typeof s == "string" ? s : (s == null ? void 0 : s.label) || r;
428
+ }).join(", ");
429
+ }
430
+ }
431
+ class y extends u {
432
+ constructor(e) {
433
+ super(e), this.min = "min" in e && e.min !== void 0 ? e.min : 1, this.max = "max" in e && e.max !== void 0 ? e.max : 5, this.integer = !0;
434
+ }
435
+ validate(e) {
436
+ const t = super.validate(e);
437
+ return t.valid ? t : {
438
+ valid: !1,
439
+ error: t.error.includes("number") ? `Enter a number from ${this.min} to ${this.max}` : t.error
440
+ };
441
+ }
442
+ format(e) {
443
+ return !e || typeof e != "number" ? "" : `${i.icons.star.repeat(e) + i.icons.starEmpty.repeat((this.max ?? 5) - e)} (${e}/${this.max})`;
444
+ }
445
+ }
446
+ class C extends d {
447
+ constructor(e) {
448
+ super(e), this.value = e.defaultValue || null;
449
+ }
450
+ validate(e) {
451
+ const t = super.validate(e);
452
+ if (!t.valid) return t;
453
+ if (typeof e == "boolean")
454
+ return { valid: !0 };
455
+ if (typeof e == "string") {
456
+ const r = e.toLowerCase().trim();
457
+ if (["y", "yes", "s", "si", "sì", "n", "no"].includes(r))
458
+ return { valid: !0 };
459
+ }
460
+ return {
461
+ valid: !1,
462
+ error: "Answer with Y (yes) or N (no)"
463
+ };
464
+ }
465
+ setValue(e) {
466
+ if (typeof e == "string") {
467
+ const t = e.toLowerCase().trim();
468
+ if (["y", "yes", "s", "si", "sì"].includes(t))
469
+ return this.value = !0, this.error = null, !0;
470
+ if (["n", "no"].includes(t))
471
+ return this.value = !1, this.error = null, !0;
472
+ }
473
+ return typeof e == "boolean" ? (this.value = e, this.error = null, !0) : (this.error = "Answer with Y (yes) or N (no)", !1);
474
+ }
475
+ format(e) {
476
+ return e === !0 ? "Yes" : e === !1 ? "No" : "";
477
+ }
478
+ }
479
+ const L = {
480
+ create(n) {
481
+ const e = (n.type || "text").toLowerCase();
482
+ switch (e) {
483
+ case "text":
484
+ return new a(n);
485
+ case "number":
486
+ return new u(n);
487
+ case "email":
488
+ return new V(n);
489
+ case "url":
490
+ return new I(n);
491
+ case "date":
492
+ return new T(n);
493
+ case "choice":
494
+ return new S(n);
495
+ case "multiple-choice":
496
+ case "multiplechoice":
497
+ return new x(n);
498
+ case "rating":
499
+ return new y(n);
500
+ case "yesno":
501
+ case "yes-no":
502
+ return new C(n);
503
+ default:
504
+ return console.warn(`Unknown field type: ${e}, defaulting to text`), new a(n);
505
+ }
506
+ }
507
+ }, N = "\x1B";
508
+ class A {
509
+ constructor() {
510
+ this.cursorVisible = !0, this.cursorInterval = null;
511
+ }
512
+ /**
513
+ * Initialize the renderer and start cursor blinking
514
+ */
515
+ init() {
516
+ this.startCursorBlink();
517
+ }
518
+ /**
519
+ * Clear the console
520
+ */
521
+ clear() {
522
+ console.clear();
523
+ }
524
+ /**
525
+ * Start the cursor blinking animation
526
+ */
527
+ startCursorBlink() {
528
+ this.cursorInterval && clearInterval(this.cursorInterval), this.cursorInterval = window.setInterval(() => {
529
+ this.cursorVisible = !this.cursorVisible;
530
+ }, 530);
531
+ }
532
+ /**
533
+ * Stop the cursor animation
534
+ */
535
+ stopCursorBlink() {
536
+ this.cursorInterval && (clearInterval(this.cursorInterval), this.cursorInterval = null);
537
+ }
538
+ /**
539
+ * Get the current cursor character
540
+ */
541
+ getCursor() {
542
+ return this.cursorVisible ? i.icons.cursor : " ";
543
+ }
544
+ /**
545
+ * Create a decorative horizontal line
546
+ */
547
+ createHorizontalLine(e = 60, t = i.box.horizontal) {
548
+ return t.repeat(e);
549
+ }
550
+ /**
551
+ * Create a box with Unicode borders
552
+ */
553
+ createBox(e, t = 60, r = "single") {
554
+ const s = [], o = r === "double", l = o ? i.box.doubleTopLeft : i.box.topLeft, h = o ? i.box.doubleTopRight : i.box.topRight, c = o ? i.box.doubleBottomLeft : i.box.bottomLeft, v = o ? i.box.doubleBottomRight : i.box.bottomRight, m = o ? i.box.doubleHorizontal : i.box.horizontal, p = o ? i.box.doubleVertical : i.box.vertical;
555
+ return s.push(l + m.repeat(t - 2) + h), (Array.isArray(e) ? e : [e]).forEach(($) => {
556
+ const f = String($), w = new RegExp(`${N}\\[[0-9;]*m`, "g"), M = f.replace(w, ""), g = t - 2 - M.length, b = Math.floor(g / 2), E = g - b;
557
+ s.push(p + " ".repeat(b) + f + " ".repeat(E) + p);
558
+ }), s.push(c + m.repeat(t - 2) + v), s;
559
+ }
560
+ /**
561
+ * Create an elegant progress bar
562
+ */
563
+ createProgressBar(e, t, r = 40) {
564
+ const s = Math.round(e / t * 100), o = Math.round(e / t * r), l = r - o, h = i.box.filled.repeat(o), c = i.box.empty.repeat(l);
565
+ return {
566
+ bar: h + c,
567
+ percentage: s,
568
+ text: `${e}/${t}`
569
+ };
570
+ }
571
+ /**
572
+ * Render the main title with style
573
+ */
574
+ renderTitle(e) {
575
+ const [t, r] = i.format.title(e);
576
+ console.log(t, r);
577
+ }
578
+ /**
579
+ * Render the subtitle with style
580
+ */
581
+ renderSubtitle(e) {
582
+ const [t, r] = i.format.subtitle(e);
583
+ console.log(t, r);
584
+ }
585
+ /**
586
+ * Render normal text
587
+ */
588
+ renderText(e, t = null) {
589
+ if (t) {
590
+ const [r, s] = i.format.colored(e, t);
591
+ console.log(r, s);
592
+ } else {
593
+ const [r, s] = i.format.body(e);
594
+ console.log(r, s);
595
+ }
596
+ }
597
+ /**
598
+ * Render a success message
599
+ */
600
+ renderSuccess(e) {
601
+ const [t, r] = i.format.success(e);
602
+ console.log(t, r);
603
+ }
604
+ /**
605
+ * Render an error message
606
+ */
607
+ renderError(e) {
608
+ const [t, r] = i.format.error(e);
609
+ console.log(t, r);
610
+ }
611
+ /**
612
+ * Render muted/secondary text
613
+ */
614
+ renderMuted(e) {
615
+ const [t, r] = i.format.muted(e);
616
+ console.log(t, r);
617
+ }
618
+ /**
619
+ * Render highlighted text
620
+ */
621
+ renderHighlight(e) {
622
+ const [t, r] = i.format.highlight(e);
623
+ console.log(t, r);
624
+ }
625
+ /**
626
+ * Render a complete box
627
+ */
628
+ renderBox(e, t = 60, r = "single") {
629
+ this.createBox(e, t, r).forEach((o) => {
630
+ this.renderText(o, i.colors.text.secondary);
631
+ });
632
+ }
633
+ /**
634
+ * Render a separator line
635
+ */
636
+ renderSeparator(e = 60, t = i.box.horizontal) {
637
+ const r = this.createHorizontalLine(e, t);
638
+ this.renderMuted(r);
639
+ }
640
+ /**
641
+ * Render the progress bar with info
642
+ */
643
+ renderProgress(e, t, r = "Progress") {
644
+ const s = this.createProgressBar(e, t);
645
+ console.log(""), this.renderMuted(`${r}: ${s.text} (${s.percentage}%)`), this.renderHighlight(s.bar), console.log("");
646
+ }
647
+ /**
648
+ * Render a list of options
649
+ */
650
+ renderOptions(e, t = null) {
651
+ e.forEach((r, s) => {
652
+ const o = s + 1;
653
+ t === s ? this.renderHighlight(` ${o}. ${r}`) : this.renderText(` ${o}. ${r}`, i.colors.text.secondary);
654
+ });
655
+ }
656
+ /**
657
+ * Render an input field with cursor
658
+ */
659
+ renderInput(e, t = "", r = !0) {
660
+ const s = r ? this.getCursor() : "", o = t + s;
661
+ console.log(""), this.renderHighlight(`${i.icons.chevron} ${e}`), console.log(""), this.renderText(` ${o}`, i.colors.primary), console.log("");
662
+ }
663
+ /**
664
+ * Render a yes/no question
665
+ */
666
+ renderYesNo(e, t = null, r = "Yes", s = "No") {
667
+ console.log(""), this.renderHighlight(`${i.icons.question} ${e}`), console.log("");
668
+ const o = ` ${i.icons.bulletEmpty} Y - ${r}`, l = ` ${i.icons.bulletEmpty} N - ${s}`;
669
+ t === !0 ? (this.renderHighlight(o), this.renderMuted(l)) : t === !1 ? (this.renderMuted(o), this.renderHighlight(l)) : (this.renderMuted(o), this.renderMuted(l)), console.log("");
670
+ }
671
+ /**
672
+ * Render rating with stars
673
+ */
674
+ renderRating(e, t = 0, r = 5) {
675
+ console.log(""), this.renderHighlight(`${i.icons.star} ${e}`), console.log("");
676
+ let s = "";
677
+ for (let o = 1; o <= r; o++)
678
+ o <= t ? s += i.icons.star + " " : s += i.icons.starEmpty + " ";
679
+ this.renderText(` ${s}`, i.colors.warning), this.renderMuted(` (${t}/${r})`), console.log("");
680
+ }
681
+ /**
682
+ * Render a summary with key-value data
683
+ */
684
+ renderSummary(e, t) {
685
+ console.log(""), this.renderTitle(e), this.renderSeparator(60), console.log(""), Object.entries(t).forEach(([r, s]) => {
686
+ const o = r.charAt(0).toUpperCase() + r.slice(1);
687
+ this.renderText(`${i.icons.bullet} ${o}:`, i.colors.text.secondary), this.renderHighlight(` ${s}`), console.log("");
688
+ }), this.renderSeparator(60);
689
+ }
690
+ /**
691
+ * Render a help/commands message
692
+ */
693
+ renderHelp(e, t = "Available Commands") {
694
+ console.log(""), this.renderTitle(`📋 ${t}`), this.renderSeparator(60), console.log(""), e.forEach(({ command: r, description: s }) => {
695
+ this.renderHighlight(` ${i.icons.chevron} ${r}`), this.renderMuted(` ${s}`), console.log("");
696
+ }), this.renderSeparator(60);
697
+ }
698
+ /**
699
+ * Render an animated welcome message
700
+ */
701
+ renderWelcome(e, t = null) {
702
+ this.clear(), console.log(""), console.log(""), this.renderTitle(`✨ ${e} ✨`), t && (console.log(""), this.renderSubtitle(t)), console.log(""), this.renderSeparator(60, i.box.doubleHorizontal), console.log("");
703
+ }
704
+ /**
705
+ * Render a spinner/loader (static, not animated)
706
+ */
707
+ renderLoader(e = "Loading...") {
708
+ const t = i.icons.spinner[0];
709
+ this.renderText(`${t} ${e}`, i.colors.info);
710
+ }
711
+ /**
712
+ * Cleanup the renderer
713
+ */
714
+ destroy() {
715
+ this.stopCursorBlink();
716
+ }
717
+ }
718
+ class k {
719
+ constructor(e) {
720
+ this.title = e.title || "Form", this.subtitle = e.subtitle || null, this.endpoint = e.endpoint || null, this.onComplete = e.onComplete || null, this.steps = this.initializeSteps(e.steps || []), this.currentStepIndex = -1, this.renderer = new A(), this.started = !1, this.completed = !1, this.startTime = null, this.helpActive = !1, this.renderer.init(), this.showWelcome();
721
+ }
722
+ /**
723
+ * Initialize steps with appropriate field types
724
+ */
725
+ initializeSteps(e) {
726
+ return e.map((t, r) => {
727
+ const s = L.create(t);
728
+ return {
729
+ id: t.id || `step_${r}`,
730
+ field: s,
731
+ description: t.description ?? null,
732
+ answered: !1
733
+ };
734
+ });
735
+ }
736
+ /**
737
+ * Show welcome screen
738
+ */
739
+ showWelcome() {
740
+ this.renderer.clear(), console.log(""), this.renderer.renderTitle(this.title), this.subtitle && this.renderer.renderMuted(this.subtitle), console.log(""), this.renderer.renderMuted("Type formShell.start() to begin or formShell.help() for available commands"), console.log("");
741
+ }
742
+ /**
743
+ * Show help screen with commands.
744
+ * Can be called at any time without interrupting the form flow.
745
+ * Use formShell.continue() to resume where you left off.
746
+ */
747
+ help() {
748
+ this.helpActive = !0, this.renderer.clear(), console.log(""), this.renderer.renderTitle(this.title), this.subtitle && this.renderer.renderMuted(this.subtitle), console.log(""), this.renderer.renderHighlight("Commands:"), this.renderer.renderMuted(" formShell.start() Start the form"), this.renderer.renderMuted(" formShell.continue() Resume the form"), this.renderer.renderMuted(" formShell.answer(...) Answer and proceed"), this.renderer.renderMuted(" formShell.skip() Skip (if optional)"), this.renderer.renderMuted(" formShell.y() / formShell.n() Yes / No"), this.renderer.renderMuted(" formShell.back() Go back"), this.renderer.renderMuted(" formShell.submit() Submit (at the end)"), this.renderer.renderMuted(" formShell.reset() Start over"), this.renderer.renderMuted(" formShell.help() Show this help"), console.log(""), this.renderer.renderMuted("Type formShell.continue() to resume"), console.log("");
749
+ }
750
+ /**
751
+ * Start the form (only from welcome screen)
752
+ */
753
+ start() {
754
+ if (this.helpActive) {
755
+ this.renderer.renderError("Use formShell.continue() to resume the form first");
756
+ return;
757
+ }
758
+ if (this.started) {
759
+ this.renderer.renderError("Form already started. Use formShell.reset() to start over");
760
+ return;
761
+ }
762
+ const e = this.getNextVisibleStep(-1);
763
+ if (e >= this.steps.length) {
764
+ this.renderer.renderError("No visible steps in form");
765
+ return;
766
+ }
767
+ this.currentStepIndex = e, this.started = !0, this.startTime = Date.now(), this.renderCurrentStep();
768
+ }
769
+ /**
770
+ * Resume the form after viewing help
771
+ */
772
+ continue() {
773
+ if (!this.helpActive) {
774
+ this.started ? this.renderer.renderError("Use formShell.answer() to respond to the current question") : this.renderer.renderError("Use formShell.start() to begin the form");
775
+ return;
776
+ }
777
+ this.helpActive = !1, this.started ? this.completed ? this.showSummary() : this.renderCurrentStep() : this.showWelcome();
778
+ }
779
+ /**
780
+ * Check if a step should be shown based on its condition
781
+ */
782
+ shouldShowStep(e) {
783
+ const t = this.steps[e];
784
+ if (!t.field.condition)
785
+ return !0;
786
+ const r = this.collectData();
787
+ return t.field.condition(r);
788
+ }
789
+ /**
790
+ * Get next visible step index
791
+ */
792
+ getNextVisibleStep(e) {
793
+ for (let t = e + 1; t < this.steps.length; t++)
794
+ if (this.shouldShowStep(t))
795
+ return t;
796
+ return this.steps.length;
797
+ }
798
+ /**
799
+ * Get previous visible step index
800
+ */
801
+ getPreviousVisibleStep(e) {
802
+ for (let t = e - 1; t >= 0; t--)
803
+ if (this.shouldShowStep(t))
804
+ return t;
805
+ return -1;
806
+ }
807
+ /**
808
+ * Advance to next step (internal method)
809
+ */
810
+ _advanceStep() {
811
+ if (this.completed)
812
+ return;
813
+ const e = this.getNextVisibleStep(this.currentStepIndex);
814
+ e < this.steps.length ? (this.currentStepIndex = e, this.renderCurrentStep()) : (this.completed = !0, this.showSummary());
815
+ }
816
+ /**
817
+ * Go back to previous step
818
+ */
819
+ back() {
820
+ if (this.helpActive) {
821
+ this.renderer.renderError("Use formShell.continue() to resume the form first");
822
+ return;
823
+ }
824
+ const e = this.getPreviousVisibleStep(this.currentStepIndex);
825
+ if (e < 0) {
826
+ this.renderer.renderError("Already at first question. Use formShell.help() for commands");
827
+ return;
828
+ }
829
+ this.currentStepIndex = e, this.completed = !1, this.renderCurrentStep();
830
+ }
831
+ /**
832
+ * Answer the current question
833
+ */
834
+ answer(e) {
835
+ if (this.helpActive) {
836
+ this.renderer.renderError("Use formShell.continue() to resume the form first");
837
+ return;
838
+ }
839
+ if (!this.started) {
840
+ this.renderer.renderError("Use formShell.start() to begin the form");
841
+ return;
842
+ }
843
+ if (this.completed) {
844
+ this.renderer.renderError("Form already completed! Use formShell.submit() to send or formShell.reset() to start over");
845
+ return;
846
+ }
847
+ const t = this.steps[this.currentStepIndex], r = t.field;
848
+ r.setValue(e) ? (t.answered = !0, this.renderer.renderSuccess(`${r.format(r.getValue())}`), console.log(""), setTimeout(() => {
849
+ this._advanceStep();
850
+ }, 1e3)) : (this.renderer.renderError(r.error ?? "Invalid value"), console.log(""));
851
+ }
852
+ /**
853
+ * Shortcut to answer "Yes"
854
+ */
855
+ y() {
856
+ this.answer("y");
857
+ }
858
+ /**
859
+ * Shortcut to answer "No"
860
+ */
861
+ n() {
862
+ this.answer("n");
863
+ }
864
+ /**
865
+ * Skip the current question (only if not required)
866
+ */
867
+ skip() {
868
+ if (this.helpActive) {
869
+ this.renderer.renderError("Use formShell.continue() to resume the form first");
870
+ return;
871
+ }
872
+ if (!this.started) {
873
+ this.renderer.renderError("Use formShell.start() to begin the form");
874
+ return;
875
+ }
876
+ if (this.completed) {
877
+ this.renderer.renderError("Form already completed! Use formShell.submit() to send or formShell.reset() to start over");
878
+ return;
879
+ }
880
+ const e = this.steps[this.currentStepIndex], t = e.field;
881
+ if (t.required) {
882
+ this.renderer.renderError("This question is required and cannot be skipped"), console.log("");
883
+ return;
884
+ }
885
+ e.answered = !0, t.value = null, this.renderer.renderMuted("Skipped"), console.log(""), setTimeout(() => {
886
+ this._advanceStep();
887
+ }, 1e3);
888
+ }
889
+ /**
890
+ * Reset the form
891
+ */
892
+ reset() {
893
+ this.currentStepIndex = -1, this.started = !1, this.completed = !1, this.startTime = null, this.helpActive = !1, this.steps.forEach((e) => {
894
+ e.answered = !1, e.field.value = null, e.field.error = null;
895
+ }), this.showWelcome();
896
+ }
897
+ /**
898
+ * Submit data to server
899
+ */
900
+ async submit() {
901
+ if (this.helpActive) {
902
+ this.renderer.renderError("Use formShell.continue() to resume the form first");
903
+ return;
904
+ }
905
+ if (!this.completed) {
906
+ this.renderer.renderError("Complete all questions first!");
907
+ return;
908
+ }
909
+ const e = this.collectData();
910
+ console.log(""), this.renderer.renderMuted("Submitting...");
911
+ try {
912
+ if (this.endpoint) {
913
+ const t = await fetch(this.endpoint, {
914
+ method: "POST",
915
+ headers: {
916
+ "Content-Type": "application/json"
917
+ },
918
+ body: JSON.stringify(e)
919
+ });
920
+ if (!t.ok)
921
+ throw new Error(`HTTP error! status: ${t.status}`);
922
+ const r = await t.json();
923
+ this.renderer.renderSuccess("Submitted!"), console.log(""), this.onComplete && await this.onComplete(r), console.log("Response:", r);
924
+ } else
925
+ this.renderer.renderSuccess("Completed!"), console.log(""), console.log("Data:", e), this.onComplete && await this.onComplete(e);
926
+ } catch (t) {
927
+ const r = t instanceof Error ? t.message : String(t);
928
+ this.renderer.renderError(`Error: ${r}`), console.log("");
929
+ }
930
+ }
931
+ /**
932
+ * Collect all form data
933
+ */
934
+ collectData() {
935
+ const e = {};
936
+ return this.steps.forEach((t) => {
937
+ e[t.field.id] = t.field.getValue();
938
+ }), e;
939
+ }
940
+ /**
941
+ * Calculate completion percentage
942
+ */
943
+ getProgress() {
944
+ const e = this.steps.filter(
945
+ (r, s) => this.shouldShowStep(s)
946
+ ), t = e.filter((r) => r.answered).length;
947
+ return {
948
+ current: t,
949
+ total: e.length,
950
+ percentage: e.length > 0 ? Math.round(t / e.length * 100) : 0
951
+ };
952
+ }
953
+ /**
954
+ * Estimate remaining time
955
+ */
956
+ getEstimatedTime() {
957
+ if (!this.startTime || this.currentStepIndex < 1)
958
+ return null;
959
+ const t = (Date.now() - this.startTime) / (this.currentStepIndex + 1), r = this.steps.length - this.currentStepIndex - 1, s = t * r, o = Math.round(s / 1e3);
960
+ return o < 60 ? `~${o}s` : `~${Math.round(o / 60)}m`;
961
+ }
962
+ /**
963
+ * Render the current step
964
+ */
965
+ renderCurrentStep() {
966
+ this.renderer.clear();
967
+ const e = this.steps[this.currentStepIndex], t = e.field;
968
+ console.log("");
969
+ const r = this.getProgress(), s = this.getVisibleStepNumber(this.currentStepIndex), o = this.renderer.createProgressBar(
970
+ s,
971
+ r.total,
972
+ 30
973
+ );
974
+ this.renderer.renderMuted(`[${o.bar}] ${o.text}`), console.log(""), this.renderer.renderHighlight(`${s}. ${t.label}`), e.description && this.renderer.renderMuted(` ${e.description}`), console.log(""), this.renderField(t);
975
+ }
976
+ /**
977
+ * Get the visible step number (position among visible steps)
978
+ */
979
+ getVisibleStepNumber(e) {
980
+ let t = 0;
981
+ for (let r = 0; r <= e; r++)
982
+ this.shouldShowStep(r) && t++;
983
+ return t;
984
+ }
985
+ /**
986
+ * Render a field based on its type
987
+ */
988
+ renderField(e) {
989
+ if (e instanceof y) {
990
+ this.renderer.renderMuted(` formShell.answer(1-${e.max})`);
991
+ const t = i.icons.starEmpty.repeat(e.max ?? 5);
992
+ this.renderer.renderText(` ${t}`, i.colors.warning);
993
+ } else if (e instanceof S)
994
+ this.renderer.renderOptions(
995
+ e.options.map(
996
+ (t) => typeof t == "string" ? t : t.label
997
+ )
998
+ ), this.renderer.renderMuted(" formShell.answer(number)");
999
+ else if (e instanceof x)
1000
+ this.renderer.renderOptions(
1001
+ e.options.map(
1002
+ (t) => typeof t == "string" ? t : t.label
1003
+ )
1004
+ ), this.renderer.renderMuted(' formShell.answer("1,2,3")');
1005
+ else if (e.type === "yesno")
1006
+ this.renderer.renderMuted(" formShell.y() / formShell.n()");
1007
+ else {
1008
+ const t = this.getPlaceholder(e);
1009
+ this.renderer.renderMuted(` formShell.answer("${t}")`);
1010
+ }
1011
+ e.required || this.renderer.renderMuted(" formShell.skip() to skip"), console.log("");
1012
+ }
1013
+ /**
1014
+ * Get a placeholder for the field
1015
+ */
1016
+ getPlaceholder(e) {
1017
+ switch (e.type) {
1018
+ case "email":
1019
+ return "email@example.com";
1020
+ case "url":
1021
+ return "https://...";
1022
+ case "date":
1023
+ return "DD/MM/YYYY";
1024
+ case "number":
1025
+ return e instanceof u && e.min !== null ? `${e.min}-${e.max ?? "..."}` : "number";
1026
+ default:
1027
+ return "your answer";
1028
+ }
1029
+ }
1030
+ /**
1031
+ * Show final summary
1032
+ */
1033
+ showSummary() {
1034
+ this.renderer.clear(), console.log(""), this.renderer.renderTitle("Summary"), console.log("");
1035
+ let e = 1;
1036
+ this.steps.forEach((t) => {
1037
+ if (t.field.condition) {
1038
+ const l = this.collectData();
1039
+ if (!t.field.condition(l))
1040
+ return;
1041
+ }
1042
+ const r = t.field, s = r.getValue(), o = r.format(s);
1043
+ this.renderer.renderMuted(`${e}. ${r.label}`), this.renderer.renderHighlight(` ${o}`), e++;
1044
+ }), console.log(""), this.renderer.renderSuccess("Completed!"), this.renderer.renderMuted("formShell.submit() to send | formShell.back() to modify"), console.log("");
1045
+ }
1046
+ /**
1047
+ * Cleanup when form is destroyed
1048
+ */
1049
+ destroy() {
1050
+ this.renderer.destroy();
1051
+ }
1052
+ }
1053
+ export {
1054
+ d as BaseField,
1055
+ S as ChoiceField,
1056
+ T as DateField,
1057
+ V as EmailField,
1058
+ L as FieldFactory,
1059
+ k as FormShell,
1060
+ x as MultipleChoiceField,
1061
+ u as NumberField,
1062
+ y as RatingField,
1063
+ A as TUIRenderer,
1064
+ a as TextField,
1065
+ i as Theme,
1066
+ I as URLField,
1067
+ C as YesNoField,
1068
+ k as default
1069
+ };
1070
+ //# sourceMappingURL=index.js.map