acecalisto3 commited on
Commit
cf33581
·
verified ·
1 Parent(s): 18815a9

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +77 -260
index.html CHANGED
@@ -4,17 +4,12 @@
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>UIKitV3 Elite - Next-Gen Component Automator</title>
7
-
8
  <script src="https://cdn.tailwindcss.com"></script>
9
-
10
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/atom-one-dark.min.css">
11
-
12
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/purify.min.js"></script>
13
-
14
  <link rel="preconnect" href="https://fonts.googleapis.com">
15
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
16
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
17
-
18
  <style>
19
  /* Base styles with enhanced visuals */
20
  * {
@@ -22,7 +17,6 @@
22
  padding: 0;
23
  box-sizing: border-box;
24
  }
25
-
26
  body {
27
  font-family: 'Inter', sans-serif;
28
  background: #2a2a2a;
@@ -30,7 +24,6 @@
30
  position: relative;
31
  overflow-x: hidden;
32
  }
33
-
34
  /* TV Static background layer */
35
  body::before {
36
  content: '';
@@ -50,13 +43,11 @@
50
  pointer-events: none;
51
  z-index: 0;
52
  }
53
-
54
  @keyframes staticNoise {
55
  0%, 100% { opacity: 1; filter: contrast(1.05); }
56
  33% { opacity: 0.98; filter: contrast(1); }
57
  66% { opacity: 0.96; filter: contrast(1.02); }
58
  }
59
-
60
  /* Glass overlay at 55% opacity */
61
  body::after {
62
  content: '';
@@ -75,7 +66,6 @@
75
  z-index: 1;
76
  mix-blend-mode: overlay;
77
  }
78
-
79
  /* Animated background particles */
80
  .bg-particles {
81
  position: fixed;
@@ -86,7 +76,6 @@
86
  pointer-events: none;
87
  z-index: 0;
88
  }
89
-
90
  .particle {
91
  position: absolute;
92
  background: radial-gradient(circle, rgba(200, 140, 80, 0.15) 0%, transparent 70%);
@@ -94,14 +83,12 @@
94
  animation: float linear infinite;
95
  filter: blur(2px);
96
  }
97
-
98
  @keyframes float {
99
  0% { transform: translateY(100vh) scale(0); opacity: 0; }
100
  10% { opacity: 0.3; }
101
  90% { opacity: 0.3; }
102
  100% { transform: translateY(-100vh) scale(1); opacity: 0; }
103
  }
104
-
105
  /* Glassmorphism effect */
106
  .glass {
107
  background: rgba(20, 20, 20, 0.7);
@@ -110,7 +97,6 @@
110
  border: 1px solid rgba(200, 140, 80, 0.1);
111
  box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.6);
112
  }
113
-
114
  .glass-dark {
115
  background: rgba(18, 18, 18, 0.75);
116
  backdrop-filter: blur(50px) saturate(190%);
@@ -122,7 +108,6 @@
122
  inset 0 0 60px rgba(0, 0, 0, 0.3),
123
  0 0 1px rgba(200, 140, 80, 0.2);
124
  }
125
-
126
  .container {
127
  max-width: 100%;
128
  margin: 0;
@@ -132,9 +117,7 @@
132
  min-height: 100vh;
133
  width: 100%;
134
  }
135
-
136
  /* === SLIDING PANEL LAYOUT CSS === */
137
-
138
  /* Sliding Module Base Styles */
139
  .slide-module {
140
  position: fixed;
@@ -156,7 +139,6 @@
156
  display: flex;
157
  flex-direction: column;
158
  }
159
-
160
  /* Left-side modules (Components, Properties, Code Output) */
161
  .slide-module.left {
162
  left: 48px; /* <-- FIX: Changed from 0 to 48px */
@@ -164,11 +146,9 @@
164
  border-right: 1px solid rgba(200, 140, 80, 0.25);
165
  transform: translateX(-100%);
166
  }
167
-
168
  .slide-module.left.open {
169
  transform: translateX(0);
170
  }
171
-
172
  /* Right-side module (Live Preview) */
173
  .slide-module.right {
174
  right: 48px; /* <-- FIX: Changed from 0 to 48px */
@@ -177,23 +157,19 @@
177
  border-left: 1px solid rgba(200, 140, 80, 0.25);
178
  transform: translateX(100%);
179
  }
180
-
181
  .slide-module.right.open {
182
  transform: translateX(0);
183
  }
184
-
185
  /* Make open panels cover the tabs (No longer needed, but keep for safety) */
186
  .slide-module.open {
187
- z-index: 160;
188
  }
189
-
190
  /* Module content area */
191
  .module-content {
192
  flex: 1;
193
  overflow-y: auto;
194
  padding: 2rem;
195
  }
196
-
197
  /* Module header */
198
  .module-header {
199
  padding: 1.5rem 2rem;
@@ -201,7 +177,6 @@
201
  background: rgba(200, 140, 80, 0.05);
202
  flex-shrink: 0;
203
  }
204
-
205
  /* Module Tabs */
206
  .module-tab {
207
  position: fixed;
@@ -226,14 +201,12 @@
226
  inset 0 1px 0 rgba(255, 255, 255, 0.05),
227
  0 0 1px rgba(200, 140, 80, 0.2);
228
  }
229
-
230
  /* Left tabs (Components, Properties, Code Output) */
231
  .module-tab.left {
232
  left: 0;
233
  border-left: none;
234
  border-radius: 0 8px 8px 0;
235
  }
236
-
237
  .module-tab.left:hover {
238
  background: rgba(25, 25, 25, 0.95);
239
  border-color: rgba(200, 140, 80, 0.5);
@@ -242,14 +215,12 @@
242
  inset 0 1px 0 rgba(255, 255, 255, 0.07),
243
  0 0 16px rgba(200, 140, 80, 0.2);
244
  }
245
-
246
  /* Right tab (Live Preview) */
247
  .module-tab.right {
248
  right: 0;
249
  border-right: none;
250
  border-radius: 8px 0 0 8px;
251
  }
252
-
253
  .module-tab.right:hover {
254
  background: rgba(25, 25, 25, 0.95);
255
  border-color: rgba(200, 140, 80, 0.5);
@@ -258,11 +229,9 @@
258
  inset 0 1px 0 rgba(255, 255, 255, 0.07),
259
  0 0 16px rgba(200, 140, 80, 0.2);
260
  }
261
-
262
  .module-tab-icon {
263
  font-size: 24px;
264
  }
265
-
266
  .module-tab-text {
267
  writing-mode: vertical-rl;
268
  text-orientation: mixed;
@@ -272,14 +241,12 @@
272
  letter-spacing: 0.5px;
273
  text-transform: uppercase;
274
  }
275
-
276
  /* Position tabs at different heights for left modules */
277
  #components-tab { top: 20%; }
278
  #properties-tab { top: 50%; }
279
  #code-tab { top: 80%; }
280
-
281
  /* === END NEW LAYOUT CSS === */
282
-
283
  /* === Pinstripe Separators === */
284
  .pinstripe {
285
  position: fixed;
@@ -290,23 +257,18 @@
290
  z-index: 140; /* Below tabs (150), above closed panels (100) */
291
  box-shadow: 0 0 12px rgba(0, 0, 0, 0.4);
292
  }
293
-
294
  .pinstripe.left {
295
  left: 48px; /* Aligned with the inner edge of left tabs */
296
  }
297
-
298
  .pinstripe.right {
299
  right: 48px; /* Aligned with the inner edge of right tab */
300
  }
301
  /* === END NEW CSS === */
302
-
303
-
304
  /* Enhanced dropdown with smooth animations */
305
  .dropdown {
306
  position: relative;
307
  display: inline-block;
308
  }
309
-
310
  .dropdown-content {
311
  display: none;
312
  position: absolute;
@@ -321,12 +283,10 @@
321
  max-height: 400px;
322
  overflow-y: auto;
323
  }
324
-
325
  @keyframes dropdownSlide {
326
  from { opacity: 0; transform: translateY(-10px); }
327
  to { opacity: 1; transform: translateY(0); }
328
  }
329
-
330
  .dropdown-content a {
331
  color: white;
332
  padding: 12px 20px;
@@ -335,24 +295,20 @@
335
  transition: all 0.3s ease;
336
  border-left: 3px solid transparent;
337
  }
338
-
339
  .dropdown-content a:hover {
340
  background: linear-gradient(90deg, rgba(200, 140, 80, 0.15), transparent);
341
  border-left-color: rgba(200, 140, 80, 0.6);
342
  padding-left: 25px;
343
  }
344
-
345
  .dropdown.show .dropdown-content {
346
  display: block;
347
  }
348
-
349
  /* Enhanced button with hover effects */
350
  .btn-primary {
351
  position: relative;
352
  overflow: hidden;
353
  transition: all 0.3s ease;
354
  }
355
-
356
  .btn-primary::before {
357
  content: '';
358
  position: absolute;
@@ -365,17 +321,14 @@
365
  transform: translate(-50%, -50%);
366
  transition: width 0.6s, height 0.6s;
367
  }
368
-
369
  .btn-primary:hover::before {
370
  width: 300px;
371
  height: 300px;
372
  }
373
-
374
  .btn-primary span {
375
  position: relative;
376
  z-index: 1;
377
  }
378
-
379
  /* Loading spinner styles */
380
  .loader {
381
  border: 3px solid rgba(200, 140, 80, 0.1);
@@ -386,52 +339,43 @@
386
  animation: spin 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55) infinite;
387
  margin: 20px auto;
388
  }
389
-
390
  @keyframes spin {
391
  0% { transform: rotate(0deg); }
392
  100% { transform: rotate(360deg); }
393
  }
394
-
395
  /* Custom scrollbar */
396
  ::-webkit-scrollbar {
397
  width: 8px;
398
  height: 8px;
399
  }
400
-
401
  ::-webkit-scrollbar-track {
402
  background: rgba(30, 30, 30, 0.5);
403
  border-radius: 10px;
404
  }
405
-
406
  ::-webkit-scrollbar-thumb {
407
  background: linear-gradient(135deg, rgba(180, 120, 60, 0.4) 0%, rgba(200, 140, 80, 0.6) 100%);
408
  border-radius: 10px;
409
  transition: all 0.3s ease;
410
  }
411
-
412
  ::-webkit-scrollbar-thumb:hover {
413
  background: linear-gradient(135deg, rgba(200, 140, 80, 0.6) 0%, rgba(220, 160, 100, 0.7) 100%);
414
  }
415
-
416
  /* Input field enhancements */
417
  .input-field {
418
  transition: all 0.3s ease;
419
  position: relative;
420
  }
421
-
422
  .input-field:focus {
423
  transform: translateY(-2px);
424
  box-shadow: 0 10px 30px rgba(200, 140, 80, 0.2);
425
  border-color: rgba(200, 140, 80, 0.4);
426
  }
427
-
428
  /* Header text animations with SVG path tracing */
429
  .header-title {
430
  display: inline-block;
431
  position: relative;
432
  overflow: visible;
433
  }
434
-
435
  .header-svg-container {
436
  position: absolute;
437
  top: 0;
@@ -441,7 +385,6 @@
441
  pointer-events: none;
442
  z-index: 10;
443
  }
444
-
445
  .header-path {
446
  fill: none;
447
  stroke: rgba(200, 140, 80, 0.6);
@@ -453,36 +396,30 @@
453
  animation: tracePath 3s ease forwards;
454
  filter: drop-shadow(0 0 8px rgba(200, 140, 80, 0.4));
455
  }
456
-
457
  @keyframes tracePath {
458
  to {
459
  stroke-dashoffset: 0;
460
  }
461
  }
462
-
463
  .header-title .char {
464
  display: inline-block;
465
  opacity: 0;
466
  position: relative;
467
  animation: charScribe 0.5s ease forwards;
468
  }
469
-
470
  @keyframes charScribe {
471
  0% { opacity: 0; filter: blur(4px); transform: scale(0.8); }
472
  50% { opacity: 0.6; filter: blur(2px); }
473
  100% { opacity: 1; filter: blur(0); transform: scale(1); text-shadow: 0 0 10px rgba(200, 140, 80, 0.3); }
474
  }
475
-
476
  .subtitle-text {
477
  opacity: 0;
478
  transform: translateY(10px);
479
  animation: subtitleFade 1s ease forwards 2.5s;
480
  }
481
-
482
  @keyframes subtitleFade {
483
  to { opacity: 1; transform: translateY(0); }
484
  }
485
-
486
  /* Icon button styles */
487
  .icon-btn {
488
  width: 36px;
@@ -497,24 +434,20 @@
497
  cursor: pointer;
498
  transition: all 0.3s ease;
499
  }
500
-
501
  .icon-btn:hover {
502
  background: rgba(200, 140, 80, 0.2);
503
  border-color: rgba(200, 140, 80, 0.4);
504
  transform: scale(1.05);
505
  box-shadow: 0 4px 12px rgba(200, 140, 80, 0.2);
506
  }
507
-
508
  .icon-btn:active {
509
  transform: scale(0.95);
510
  }
511
-
512
  /* Platform export dropdown */
513
  .platform-dropdown {
514
  position: relative;
515
  display: inline-block;
516
  }
517
-
518
  .platform-options {
519
  display: none;
520
  position: absolute;
@@ -532,11 +465,9 @@
532
  max-height: 500px;
533
  overflow-y: auto;
534
  }
535
-
536
  .platform-options.show {
537
  display: block;
538
  }
539
-
540
  .platform-option {
541
  padding: 12px 16px;
542
  color: white;
@@ -547,12 +478,10 @@
547
  gap: 10px;
548
  border-left: 3px solid transparent;
549
  }
550
-
551
  .platform-option:hover {
552
  background: linear-gradient(90deg, rgba(200, 140, 80, 0.15), transparent);
553
  border-left-color: rgba(200, 140, 80, 0.6);
554
  }
555
-
556
  .platform-icon {
557
  width: 24px;
558
  height: 24px;
@@ -563,7 +492,6 @@
563
  border-radius: 6px;
564
  font-size: 12px;
565
  }
566
-
567
  /* Animation presets */
568
  .animate-fadeIn { animation: fadeIn 0.5s ease; }
569
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@@ -583,14 +511,11 @@
583
  @keyframes rotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
584
  .animate-scale { animation: scale 0.6s ease; }
585
  @keyframes scale { from { transform: scale(0.5); } to { transform: scale(1); } }
586
-
587
-
588
  /* Tooltip */
589
  .tooltip {
590
  position: relative;
591
  display: inline-block;
592
  }
593
-
594
  .tooltip .tooltiptext {
595
  visibility: hidden;
596
  background-color: rgba(0, 0, 0, 0.9);
@@ -608,12 +533,10 @@
608
  white-space: nowrap;
609
  font-size: 12px;
610
  }
611
-
612
  .tooltip:hover .tooltiptext {
613
  visibility: visible;
614
  opacity: 1;
615
  }
616
-
617
  /* Code block enhancements */
618
  pre {
619
  border-radius: 12px;
@@ -621,7 +544,7 @@
621
  max-height: calc(100vh - 250px); /* Adjust max-height for panel view */
622
  overflow: auto;
623
  }
624
-
625
  /* Notification toast */
626
  .toast {
627
  position: fixed;
@@ -636,15 +559,12 @@
636
  display: none;
637
  animation: toastSlide 0.3s ease;
638
  }
639
-
640
  .toast.show { display: block; }
641
  .toast.error { background: rgba(239, 68, 68, 0.95); }
642
-
643
  @keyframes toastSlide {
644
  from { transform: translateX(400px); opacity: 0; }
645
  to { transform: translateX(0); opacity: 1; }
646
  }
647
-
648
  /* Animation selection (from 'broken' file) */
649
  .animation-grid {
650
  display: grid;
@@ -652,7 +572,6 @@
652
  gap: 10px;
653
  margin-top: 10px;
654
  }
655
-
656
  .animation-option {
657
  padding: 10px;
658
  background: rgba(200, 140, 80, 0.05);
@@ -664,33 +583,28 @@
664
  font-size: 12px;
665
  color: rgba(200, 140, 80, 0.9);
666
  }
667
-
668
  .animation-option:hover {
669
  background: rgba(200, 140, 80, 0.15);
670
  border-color: rgba(200, 140, 80, 0.4);
671
  transform: scale(1.05);
672
  }
673
-
674
  .animation-option.selected {
675
  background: rgba(200, 140, 80, 0.2);
676
  border-color: rgba(200, 140, 80, 0.6);
677
  box-shadow: 0 0 10px rgba(200, 140, 80, 0.2);
678
  }
679
-
680
  /* Color Palette Controls */
681
  .color-palette-section {
682
  margin-top: 20px;
683
  padding-top: 20px;
684
  border-top: 1px solid rgba(200, 140, 80, 0.15);
685
  }
686
-
687
  .color-palette-grid {
688
  display: grid;
689
  grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
690
  gap: 12px;
691
  margin-top: 12px;
692
  }
693
-
694
  .color-swatch {
695
  position: relative;
696
  height: 60px;
@@ -700,18 +614,15 @@
700
  border: 2px solid rgba(200, 140, 80, 0.2);
701
  overflow: hidden;
702
  }
703
-
704
  .color-swatch:hover {
705
  transform: scale(1.05);
706
  border-color: rgba(200, 140, 80, 0.5);
707
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
708
  }
709
-
710
  .color-swatch.selected {
711
  border-color: rgba(200, 140, 80, 0.8);
712
  box-shadow: 0 0 16px rgba(200, 140, 80, 0.4);
713
  }
714
-
715
  .color-swatch-label {
716
  position: absolute;
717
  bottom: 0;
@@ -726,14 +637,12 @@
726
  text-transform: uppercase;
727
  letter-spacing: 0.5px;
728
  }
729
-
730
  .custom-color-input {
731
  display: flex;
732
  gap: 8px;
733
  align-items: center;
734
  margin-top: 12px;
735
  }
736
-
737
  .custom-color-input input[type="color"] {
738
  width: 50px;
739
  height: 40px;
@@ -742,7 +651,6 @@
742
  cursor: pointer;
743
  background: transparent;
744
  }
745
-
746
  .custom-color-input input[type="text"] {
747
  flex: 1;
748
  padding: 8px 12px;
@@ -753,7 +661,6 @@
753
  font-size: 12px;
754
  font-family: monospace;
755
  }
756
-
757
  /* Responsive adjustments */
758
  @media (max-width: 1024px) {
759
  .slide-module.left {
@@ -763,7 +670,6 @@
763
  width: 75vw;
764
  }
765
  }
766
-
767
  @media (max-width: 768px) {
768
  .slide-module.left {
769
  width: 90vw;
@@ -784,17 +690,12 @@
784
  <body>
785
  <div class="pinstripe left"></div>
786
  <div class="pinstripe right"></div>
787
-
788
  <div class="bg-particles" id="particles"></div>
789
-
790
  <div class="toast" id="toast">
791
  <span id="toast-message">Action completed!</span>
792
  </div>
793
-
794
  <div id="app"></div>
795
-
796
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
797
-
798
  <script>
799
  // === GLOBAL STATE ===
800
  let previewEnabled = false; // Start with preview disabled as panel is closed
@@ -805,9 +706,7 @@
805
  let selectedColorPalette = 'indigo';
806
  let customColor = '#6366f1';
807
  let selectedAnimation = 'none';
808
-
809
  // === UTILITY FUNCTIONS ===
810
-
811
  // XSS Protection using DOMPurify
812
  function sanitizeHTML(html) {
813
  if (window.DOMPurify) {
@@ -818,7 +717,6 @@
818
  }
819
  return html;
820
  }
821
-
822
  // Show toast notification
823
  function showToast(message, isError = false) {
824
  const toast = document.getElementById('toast');
@@ -831,13 +729,11 @@
831
  toast.classList.remove('show');
832
  }, 3000);
833
  }
834
-
835
  // Create animated particles
836
  function createParticles() {
837
  const particlesContainer = document.getElementById('particles');
838
  if (!particlesContainer) return;
839
  const particleCount = 8;
840
-
841
  for (let i = 0; i < particleCount; i++) {
842
  const particle = document.createElement('div');
843
  particle.className = 'particle';
@@ -849,7 +745,6 @@
849
  particlesContainer.appendChild(particle);
850
  }
851
  }
852
-
853
  // === RENDER MAIN APP ===
854
  function renderApp() {
855
  // This HTML structure is from 'uikit-tim-hf-backup-broken.html'
@@ -861,27 +756,22 @@
861
  <p class="subtitle-text text-gray-500 text-md tracking-wide" style="animation-delay: 2.7s; margin-top: 1rem;">Click the tabs on the edges</p>
862
  </div>
863
  </div>
864
-
865
  <div class="module-tab left" id="components-tab" data-module="components">
866
  <span class="module-tab-icon">🎨</span>
867
  <span class="module-tab-text">Components</span>
868
  </div>
869
-
870
  <div class="module-tab left" id="properties-tab" data-module="properties">
871
  <span class="module-tab-icon">⚙️</span>
872
  <span class="module-tab-text">Properties</span>
873
  </div>
874
-
875
  <div class="module-tab left" id="code-tab" data-module="code">
876
  <span class="module-tab-icon">💻</span>
877
  <span class="module-tab-text">Code Output</span>
878
  </div>
879
-
880
  <div class="module-tab right" id="preview-tab" data-module="preview">
881
  <span class="module-tab-icon">👁️</span>
882
  <span class="module-tab-text">Live Preview</span>
883
  </div>
884
-
885
  <div class="slide-module left" id="components-module">
886
  <div class="module-header">
887
  <h2 class="text-xl font-semibold text-gray-200 flex items-center gap-3">
@@ -919,7 +809,6 @@
919
  </div>
920
  </div>
921
  </div>
922
-
923
  <div class="slide-module left" id="properties-module">
924
  <div class="module-header">
925
  <h2 class="text-xl font-semibold text-gray-200 flex items-center gap-3">
@@ -933,7 +822,6 @@
933
  <button id="add-input-button" class="mt-6 bg-gradient-to-r from-gray-800 to-gray-900 hover:from-gray-700 hover:to-gray-800 text-gray-200 font-semibold py-2 px-5 rounded-lg transition duration-300 border border-gray-700 hover:border-amber-900/30 w-full">
934
  + Add Property
935
  </button>
936
-
937
  <div class="animation-section mt-8 pt-6 border-t border-gray-700/50">
938
  <h3 class="text-md font-semibold text-gray-300 mb-3">Entry Animation</h3>
939
  <div class="animation-grid" id="animation-grid">
@@ -949,7 +837,6 @@
949
  <div class="animation-option" data-animation="scale">Scale</div>
950
  </div>
951
  </div>
952
-
953
  <div class="color-palette-section mt-8">
954
  <h3 class="text-md font-semibold text-gray-300 mb-2">Color Palette</h3>
955
  <p class="text-xs text-gray-400 mb-3">Select or customize colors for your components</p>
@@ -989,7 +876,6 @@
989
  </div>
990
  </div>
991
  </div>
992
-
993
  <div class="slide-module left" id="code-module">
994
  <div class="module-header">
995
  <div class="flex justify-between items-center w-full">
@@ -1057,7 +943,6 @@
1057
  </div>
1058
  </div>
1059
  </div>
1060
-
1061
  <div class="slide-module right" id="preview-module">
1062
  <div class="module-header">
1063
  <div class="flex justify-between items-center w-full">
@@ -1090,15 +975,12 @@
1090
  </div>
1091
  </div>`;
1092
  }
1093
-
1094
  // Animate header text with SVG path tracing
1095
  function animateHeaderText() {
1096
  const title = document.getElementById('main-title');
1097
  if (!title) return;
1098
-
1099
  const text = 'UIKitV3 Elite';
1100
  const chars = text.split('');
1101
-
1102
  // Create SVG path for tracing effect
1103
  const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
1104
  svg.setAttribute('class', 'header-svg-container');
@@ -1108,18 +990,15 @@
1108
  svg.style.left = '0';
1109
  svg.style.width = '100%';
1110
  svg.style.height = '100%';
1111
-
1112
  // Create a decorative path that traces under the text
1113
  const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
1114
  const pathData = 'M 50 80 Q 150 60, 250 70 T 450 65 Q 550 68, 650 75 T 750 70';
1115
  path.setAttribute('d', pathData);
1116
  path.setAttribute('class', 'header-path');
1117
  svg.appendChild(path);
1118
-
1119
  // Clear and create character spans
1120
  title.innerHTML = '';
1121
  title.appendChild(svg);
1122
-
1123
  chars.forEach((char, index) => {
1124
  const span = document.createElement('span');
1125
  span.className = 'char';
@@ -1128,13 +1007,10 @@
1128
  title.appendChild(span);
1129
  });
1130
  }
1131
-
1132
  // === EVENT HANDLERS ===
1133
-
1134
  function toggleDropdown() {
1135
  document.getElementById('component-dropdown').classList.toggle('show');
1136
  }
1137
-
1138
  function selectOption(event) {
1139
  event.preventDefault();
1140
  if (event.target.dataset.value) {
@@ -1144,7 +1020,6 @@
1144
  document.getElementById('inputs-container').innerHTML = '';
1145
  currentComponent = value;
1146
  addDefaultInputs(value);
1147
-
1148
  if (previewEnabled) {
1149
  updateLivePreview();
1150
  }
@@ -1152,12 +1027,10 @@
1152
  toggleModule('properties');
1153
  }
1154
  }
1155
-
1156
  function addDefaultInputs(component) {
1157
  const container = document.getElementById('inputs-container');
1158
  if (!container) return;
1159
  let defaultProps = [];
1160
-
1161
  switch(component) {
1162
  case 'Button':
1163
  defaultProps = [
@@ -1234,18 +1107,15 @@
1234
  ];
1235
  break;
1236
  }
1237
-
1238
  defaultProps.forEach(prop => {
1239
  addInput(prop.key, prop.value);
1240
  });
1241
  }
1242
-
1243
  function addInput(key = '', value = '') {
1244
  const container = document.getElementById('inputs-container');
1245
  if (!container) return;
1246
  const inputGroup = document.createElement('div');
1247
  inputGroup.className = 'flex items-center gap-3';
1248
-
1249
  inputGroup.innerHTML = `
1250
  <input type="text" class="property-key input-field bg-black/50 text-gray-200 p-3 rounded-lg flex-1 border border-gray-700 focus:outline-none focus:ring-1 focus:ring-amber-900/40 focus:border-amber-900/40" placeholder="Property" value="${key}">
1251
  <input type="text" class="property-value input-field bg-black/50 text-gray-200 p-3 rounded-lg flex-[2] border border-gray-700 focus:outline-none focus:ring-1 focus:ring-amber-900/40 focus:border-amber-900/40" placeholder="Value" value="${value}">
@@ -1253,37 +1123,30 @@
1253
 
1254
  </button>
1255
  `;
1256
-
1257
  container.appendChild(inputGroup);
1258
-
1259
  // Add event listener to remove button
1260
  inputGroup.querySelector('.remove-input-button').addEventListener('click', removeInput);
1261
-
1262
  // === FIX: Attach listeners regardless of whether preview is open ===
1263
  // This ensures live refresh works even if panel was closed when prop was added.
1264
  inputGroup.querySelector('.property-key').addEventListener('input', debouncedLivePreview);
1265
  inputGroup.querySelector('.property-value').addEventListener('input', debouncedLivePreview);
1266
  }
1267
-
1268
  function removeInput(event) {
1269
  event.target.closest('.flex').remove();
1270
  if (previewEnabled) {
1271
  updateLivePreview();
1272
  }
1273
  }
1274
-
1275
  function debouncedLivePreview() {
1276
  clearTimeout(livePreviewDebounce);
1277
  livePreviewDebounce = setTimeout(() => {
1278
  updateLivePreview();
1279
  }, 500);
1280
  }
1281
-
1282
  function getSelectedAnimation() {
1283
  const selected = document.querySelector('.animation-option.selected');
1284
  return selected ? selected.dataset.animation : 'none';
1285
  }
1286
-
1287
  function collectProperties() {
1288
  const container = document.getElementById('inputs-container');
1289
  if (!container) return {};
@@ -1302,33 +1165,26 @@
1302
  }
1303
  return properties;
1304
  }
1305
-
1306
  function generateCode() {
1307
  const component = document.getElementById('selected-option').textContent;
1308
  if (component === 'Select Component') {
1309
  showToast('Please select a component first.', true);
1310
  return;
1311
  }
1312
-
1313
  const properties = collectProperties();
1314
  const animation = getSelectedAnimation();
1315
  const codeOutput = document.getElementById('generated-code');
1316
  const loadingSpinner = document.getElementById('loading-spinner');
1317
  const copyButton = document.getElementById('copy-button');
1318
-
1319
  if (!codeOutput || !loadingSpinner || !copyButton) return;
1320
-
1321
  codeOutput.textContent = '';
1322
  copyButton.textContent = 'Copy';
1323
  loadingSpinner.style.display = 'block';
1324
-
1325
  setTimeout(() => {
1326
  let generatedHtml = '';
1327
-
1328
  try {
1329
  generatedHtml = generateComponentCode(component, properties, animation);
1330
  codeOutput.textContent = generatedHtml;
1331
-
1332
  if (window.hljs) {
1333
  try {
1334
  hljs.highlightElement(codeOutput);
@@ -1336,28 +1192,22 @@
1336
  console.error("highlight.js failed:", e);
1337
  }
1338
  }
1339
-
1340
  showToast('Code generated successfully!');
1341
  // Automatically open the code panel
1342
  toggleModule('code');
1343
-
1344
  } catch (error) {
1345
  generatedHtml = `<div class="text-red-400">Error generating code: ${error.message}</div>`;
1346
  codeOutput.textContent = generatedHtml;
1347
  showToast('Error generating code', true);
1348
  }
1349
-
1350
  loadingSpinner.style.display = 'none';
1351
-
1352
  if (previewEnabled) {
1353
  updateLivePreview();
1354
  }
1355
  }, 800);
1356
  }
1357
-
1358
  function generateComponentCode(component, props, animation) {
1359
  let html = '';
1360
-
1361
  switch(component) {
1362
  case 'Button':
1363
  html = generateButtonCode(props);
@@ -1398,15 +1248,12 @@
1398
  default:
1399
  html = `<div class="text-red-400">Error: Component generator not found.</div>`;
1400
  }
1401
-
1402
  // Add animation if selected
1403
  if (animation && animation !== 'none') {
1404
  html = addAnimationToHTML(html, animation);
1405
  }
1406
-
1407
  return html;
1408
  }
1409
-
1410
  function addAnimationToHTML(html, animation) {
1411
  // Add animation class to the root element
1412
  const animationStyles = getAnimationStyles(animation);
@@ -1419,7 +1266,6 @@
1419
  return html.replace(/<([a-z0-9]+)/i, `<$1 class="animate-${animation}" style="${animationStyles}"`);
1420
  }
1421
  }
1422
-
1423
  function getAnimationStyles(animation) {
1424
  const animations = {
1425
  fadeIn: 'animation: fadeIn 0.6s ease;',
@@ -1434,26 +1280,22 @@
1434
  };
1435
  return animations[animation] || '';
1436
  }
1437
-
1438
  // === COMPONENT GENERATORS ===
1439
-
1440
  function generateButtonCode(props) {
 
1441
  const text = props.text || 'Button';
1442
  const size = props.size || 'medium';
1443
- const colors = getColorValues();
1444
- const colorClasses = `bg-gradient-to-r hover:opacity-90`;
1445
- const inlineStyle = `style="background: linear-gradient(to right, ${colors.from}, ${colors.to});"`;
1446
  let sizeClasses = 'py-3 px-6 text-base';
1447
  switch(size) {
1448
  case 'small': sizeClasses = 'py-2 px-4 text-sm'; break;
1449
  case 'large': sizeClasses = 'py-4 px-8 text-lg'; break;
1450
  }
1451
- return `<button class="${colorClasses} ${sizeClasses} text-white font-bold rounded-xl shadow-lg transition duration-300 transform hover:scale-105" ${inlineStyle}>
1452
  ${text}
1453
  </button>`;
1454
  }
1455
-
1456
  function generateInputCode(props) {
 
1457
  const placeholder = props.placeholder || '';
1458
  const type = props.type || 'text';
1459
  const label = props.label || '';
@@ -1468,13 +1310,13 @@
1468
  ${labelHtml}<input
1469
  type="${type}"
1470
  id="generated-input"
1471
- class="bg-gray-800 text-white p-3 rounded-xl w-full border border-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition duration-300"
1472
  placeholder="${placeholder}"
1473
  >
1474
  </div>`;
1475
  }
1476
-
1477
  function generateCardCode(props) {
 
1478
  const title = props.title || 'Card Title';
1479
  const content = props.content || 'Card content goes here.';
1480
  const imageUrl = props.imageUrl || '';
@@ -1488,23 +1330,23 @@
1488
  >
1489
  `;
1490
  }
1491
- return `<div class="max-w-sm bg-gradient-to-br from-gray-800 to-gray-900 rounded-2xl shadow-2xl overflow-hidden border border-gray-700 transition duration-300 transform hover:scale-105">
1492
  ${imageHtml}<div class="p-6">
1493
- <h3 class="text-2xl font-bold text-white mb-3">${title}</h3>
1494
  <p class="text-gray-300 leading-relaxed">${content}</p>
1495
  </div>
1496
  </div>`;
1497
  }
1498
-
1499
  function generateNavbarCode(props) {
 
1500
  const brand = props.brand || 'Brand';
1501
  const links = (props.links || 'Home,About').split(',').map(link => link.trim());
1502
- let linksHtml = links.map(link => `<a href="#" class="text-gray-300 hover:text-white px-4 py-2 rounded-lg transition duration-300 hover:bg-gray-700">${link}</a>`).join('\n ');
1503
  return `<nav class="bg-gradient-to-r from-gray-900 to-gray-800 shadow-2xl border-b border-gray-700">
1504
  <div class="max-w-7xl mx-auto px-6 py-4">
1505
  <div class="flex items-center justify-between">
1506
  <div class="flex-shrink-0">
1507
- <span class="text-white font-black text-2xl bg-gradient-to-r from-indigo-400 to-purple-400 bg-clip-text text-transparent">${brand}</span>
1508
  </div>
1509
  <div class="hidden md:flex items-center space-x-2">
1510
  ${linksHtml}
@@ -1520,21 +1362,21 @@
1520
  </div>
1521
  </nav>`;
1522
  }
1523
-
1524
  function generateModalCode(props) {
 
1525
  const title = props.title || 'Modal Title';
1526
  const body = props.body || 'Modal body text goes here.';
1527
  const footerButtons = (props.footer || 'Close').split(',').map(btn => btn.trim());
1528
  let buttonsHtml = footerButtons.map((btnText, index) => {
1529
  const isPrimary = (index === footerButtons.length - 1) && footerButtons.length > 1;
1530
  const colorClasses = isPrimary
1531
- ? 'bg-gradient-to-r from-indigo-600 to-purple-600 hover:from-indigo-700 hover:to-purple-700'
1532
  : 'bg-gradient-to-r from-gray-600 to-gray-700 hover:from-gray-700 hover:to-gray-800';
1533
  return `<button class="${colorClasses} text-white font-bold py-2 px-6 rounded-lg transition duration-300 transform hover:scale-105">${btnText}</button>`;
1534
- }).join('\n ');
1535
  return `<div class="fixed inset-0 bg-black bg-opacity-70 backdrop-blur-sm z-50 flex items-center justify-center p-4">
1536
  <div class="bg-gradient-to-br from-gray-800 to-gray-900 rounded-2xl shadow-2xl max-w-lg w-full border border-gray-700 overflow-hidden">
1537
- <div class="flex justify-between items-center p-6 border-b border-gray-700 bg-gradient-to-r from-indigo-900/30 to-purple-900/30">
1538
  <h3 class="text-2xl font-bold text-white">${title}</h3>
1539
  <button class="text-gray-400 hover:text-white transition duration-300 transform hover:rotate-90">
1540
  <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@@ -1551,114 +1393,139 @@
1551
  </div>
1552
  </div>`;
1553
  }
1554
-
1555
  function generateAlertCode(props) {
 
1556
  const text = props.text || 'Alert text.';
1557
  const type = props.type || 'info';
1558
- let bgClass = 'from-blue-900/80 to-blue-800/80 border-blue-500';
 
 
1559
  let textClass = 'text-blue-200';
 
1560
  let iconSvg = `<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>`;
1561
  switch(type) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1562
  case 'success':
1563
- bgClass = 'from-green-900/80 to-green-800/80 border-green-500';
 
 
1564
  textClass = 'text-green-200';
 
1565
  iconSvg = `<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg>`;
1566
  break;
1567
  case 'warning':
1568
- bgClass = 'from-yellow-900/80 to-yellow-800/80 border-yellow-500';
 
 
1569
  textClass = 'text-yellow-200';
 
1570
  iconSvg = `<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path></svg>`;
1571
  break;
1572
  case 'danger':
1573
- bgClass = 'from-red-900/80 to-red-800/80 border-red-500';
 
 
1574
  textClass = 'text-red-200';
 
1575
  iconSvg = `<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path></svg>`;
1576
  break;
1577
  }
 
1578
  return `<div class="flex p-5 rounded-xl bg-gradient-to-r ${bgClass} ${textClass} border-l-4 shadow-lg backdrop-blur-sm" role="alert">
1579
- <span class="flex-shrink-0">${iconSvg}</span>
1580
  <div class="ml-4 text-sm font-semibold">${text}</div>
1581
  <button class="ml-auto text-white hover:opacity-75 transition duration-300">
1582
  <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
1583
  </button>
1584
  </div>`;
1585
  }
1586
-
1587
  function generateBadgeCode(props) {
1588
- const text = props.text || 'Badge';
1589
  const colors = getColorValues();
1590
- const inlineStyle = `style="background: linear-gradient(to right, ${colors.from}, ${colors.to});"`;
1591
- return `<span class="inline-flex items-center px-4 py-1.5 rounded-full text-xs font-bold text-white shadow-lg transition duration-300 transform hover:scale-110" ${inlineStyle}>
1592
  ${text}
1593
  </span>`;
1594
  }
1595
-
1596
  function generateBreadcrumbCode(props) {
 
1597
  const links = (props.links || 'Home,Page').split(',').map(link => link.trim());
1598
  const separatorSvg = `<svg class="w-5 h-5 text-gray-500" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path></svg>`;
1599
  let linksHtml = links.map((link, index) => {
1600
  const isLast = index === links.length - 1;
1601
  if (isLast) {
1602
- return `<li aria-current="page"><div class="flex items-center gap-2">${index > 0 ? separatorSvg : ''}<span class="text-sm font-semibold text-indigo-400">${link}</span></div></li>`;
1603
  } else {
1604
  return `<li><div class="flex items-center gap-2">${index > 0 ? separatorSvg : ''}<a href="#" class="text-sm font-medium text-gray-300 hover:text-white transition duration-300">${link}</a></div></li>`;
1605
  }
1606
- }).join('\n ');
1607
  return `<nav class="flex bg-gray-800/50 backdrop-blur-sm px-5 py-3 rounded-xl border border-gray-700" aria-label="Breadcrumb">
1608
  <ol class="inline-flex items-center space-x-1">${linksHtml}</ol>
1609
  </nav>`;
1610
  }
1611
-
1612
  function generateTabsCode(props) {
 
1613
  const tabs = (props.tabs || 'Tab 1,Tab 2').split(',').map(tab => tab.trim());
1614
  const activeTab = props.active || tabs[0];
1615
  let tabsHtml = tabs.map(tab => {
1616
  const isActive = tab === activeTab;
1617
- const activeClasses = 'text-indigo-400 border-indigo-400 bg-indigo-900/20';
1618
  const inactiveClasses = 'text-gray-400 border-transparent hover:text-gray-300 hover:border-gray-500';
1619
  return `<li><a href="#" class="inline-block px-6 py-3 border-b-2 rounded-t-lg transition duration-300 ${isActive ? activeClasses : inactiveClasses}">${tab}</a></li>`;
1620
- }).join('\n ');
1621
  return `<div class="bg-gray-800/50 backdrop-blur-sm rounded-xl border border-gray-700 overflow-hidden">
1622
  <ul class="flex flex-wrap text-sm font-medium text-center border-b border-gray-700">${tabsHtml}</ul>
1623
  </div>`;
1624
  }
1625
-
1626
  function generatePaginationCode(props) {
 
1627
  const items = (props.items || '1,2,3').split(',').map(item => item.trim());
1628
  let itemsHtml = items.map(item => {
1629
  const isText = isNaN(parseInt(item));
1630
  const classes = isText ? 'px-4' : 'w-10';
1631
- return `<li><a href="#" class="flex items-center justify-center ${classes} h-10 leading-tight text-gray-300 bg-gray-800 border border-gray-700 rounded-lg hover:bg-gray-700 hover:text-white transition duration-300 transform hover:scale-110">${item}</a></li>`;
1632
- }).join('\n ');
1633
  return `<nav aria-label="Page navigation"><ul class="inline-flex items-center gap-2">${itemsHtml}</ul></nav>`;
1634
  }
1635
-
1636
  function generateHeroCode(props) {
 
1637
  const title = props.title || 'Welcome to the Future';
1638
  const subtitle = props.subtitle || 'Build amazing things with modern components';
1639
  const buttonText = props.buttonText || 'Get Started';
1640
- return `<div class="relative overflow-hidden bg-gradient-to-br from-indigo-900 via-purple-900 to-pink-900 py-24 px-6">
1641
  <div class="absolute inset-0 bg-black/30"></div>
1642
  <div class="relative max-w-4xl mx-auto text-center">
1643
  <h1 class="text-6xl font-black text-white mb-6 leading-tight">${title}</h1>
1644
  <p class="text-xl text-gray-200 mb-10 max-w-2xl mx-auto">${subtitle}</p>
1645
- <button class="bg-gradient-to-r from-pink-600 to-purple-600 hover:from-pink-700 hover:to-purple-700 text-white font-bold py-4 px-10 rounded-xl text-lg shadow-2xl transition duration-300 transform hover:scale-105">
1646
  ${buttonText}
1647
  </button>
1648
  </div>
1649
  </div>`;
1650
  }
1651
-
1652
  function generateFooterCode(props) {
 
1653
  const company = props.company || 'MyCompany';
1654
  const links = (props.links || 'About,Blog,Careers,Contact').split(',').map(link => link.trim());
1655
  const copyright = props.copyright || '2024 MyCompany. All rights reserved.';
1656
- let linksHtml = links.map(link => `<a href="#" class="text-gray-400 hover:text-white transition duration-300">${link}</a>`).join('\n ');
1657
  return `<footer class="bg-gradient-to-br from-gray-900 to-gray-800 border-t border-gray-700">
1658
  <div class="max-w-7xl mx-auto px-6 py-12">
1659
  <div class="grid grid-cols-1 md:grid-cols-3 gap-8">
1660
  <div>
1661
- <h3 class="text-2xl font-black text-white mb-4 bg-gradient-to-r from-indigo-400 to-purple-400 bg-clip-text text-transparent">${company}</h3>
1662
  <p class="text-gray-400">Building the future, one component at a time.</p>
1663
  </div>
1664
  <div>
@@ -1678,9 +1545,7 @@
1678
  </div>
1679
  </footer>`;
1680
  }
1681
-
1682
  // === PLATFORM EXPORT ===
1683
-
1684
  function exportToPlatform(platform, html) {
1685
  let exported = '';
1686
  switch(platform) {
@@ -1697,7 +1562,6 @@
1697
  }
1698
  return exported;
1699
  }
1700
-
1701
  function convertToReact(html) {
1702
  let react = html
1703
  .replace(/class=/g, 'className=')
@@ -1707,38 +1571,29 @@
1707
  .replace(/stroke-linejoin=/g, 'strokeLinejoin=')
1708
  .replace(/fill-rule=/g, 'fillRule=')
1709
  .replace(/clip-rule=/g, 'clipRule=');
1710
- return `import React from 'react';\n\nexport default function Component() {\n return (\n ${react}\n );\n}\n\n// For Next.js, this component is ready to use!\n// Import Tailwind CSS in your _app.js or layout.js`;
1711
  }
1712
-
1713
  function convertToVue(html) {
1714
- return `<template>\n ${html}\n</template>\n\n<` + `script setup>\n// Vue 3 Composition API\n// For Nuxt.js, this component is ready to use!\n</` + `script>\n\n<style scoped>\n/* Add any component-specific styles here */\n</style>`;
1715
  }
1716
-
1717
  function convertToWordPress(html) {
1718
- return `<?php\n/**\n * Custom Component Block\n */\n?>\n\n<div class="wp-block-custom-component">\n ${html}\n</div>\n\n<?php\n// Add this to your theme's functions.php:\n// Make sure to enqueue Tailwind CSS\n?>`;
1719
  }
1720
-
1721
  function convertToJoomla(html) {
1722
- return `<?php\n/**\n * @package Joomla.Site\n * @subpackage mod_custom_component\n */\n\ndefined('_JEXEC') or die;\n?>\n\n<div class="mod-custom-component">\n ${html}\n</div>\n\n\n`;
1723
  }
1724
-
1725
  function convertToShopify(html) {
1726
- return `{% comment %}\n Shopify Liquid Template\n Section: custom-component\n{% endcomment %}\n\n<div class="custom-component-section">\n ${html}\n</div>\n\n{% schema %}\n{\n "name": "Custom Component",\n "settings": [],\n "presets": [{\n "name": "Custom Component"\n }]\n}\n{% endschema %}\n\n`;
1727
  }
1728
-
1729
  function convertToAngular(html) {
1730
- return `import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-custom-component',\n template: \\\`\n ${html}\n \\\`,\n styles: [\\\`\n /* Component-specific styles */\n \\\`]\n})\nexport class CustomComponent {\n constructor() {}\n}\n\n// Make sure to add Tailwind CSS to your Angular project\n// via angular.json or styles.css`;
1731
  }
1732
-
1733
  function convertToSvelte(html) {
1734
- return `<` + `script>\n // Svelte component\n // For SvelteKit, this component is ready to use!\n</` + `script>\n\n${html}\n\n<style>\n /* Add any component-specific styles here */\n</style>`;
1735
  }
1736
-
1737
  // === LIVE PREVIEW ===
1738
-
1739
  function updateLivePreview() {
1740
  if (!previewEnabled) return;
1741
-
1742
  const component = document.getElementById('selected-option').textContent;
1743
  const viewerContent = document.getElementById('viewer-content');
1744
  if (component === 'Select Component') {
@@ -1749,15 +1604,13 @@
1749
  `;
1750
  return;
1751
  }
1752
-
1753
  const properties = collectProperties();
1754
  const animation = getSelectedAnimation();
1755
  const html = generateComponentCode(component, properties, animation);
1756
  const sanitized = sanitizeHTML(html);
1757
-
1758
  viewerContent.innerHTML = sanitized;
1759
  }
1760
-
1761
  function toggleEditable() {
1762
  isEditable = !isEditable;
1763
  const viewerContent = document.getElementById('viewer-content');
@@ -1766,25 +1619,21 @@
1766
  // Add a visual indicator for editable mode
1767
  viewerContent.style.outline = isEditable ? '2px dashed rgba(200, 140, 80, 0.5)' : 'none';
1768
  viewerContent.style.boxShadow = isEditable ? 'inset 0 0 20px rgba(0,0,0,0.3)' : 'none';
1769
-
1770
  if (isEditable) {
1771
  showToast('Edit mode enabled');
1772
  } else {
1773
  showToast('Edit mode disabled');
1774
  }
1775
  }
1776
-
1777
  function copyToClipboard() {
1778
  const codeEl = document.getElementById('generated-code');
1779
  if (!codeEl) return;
1780
  const code = codeEl.textContent;
1781
  const copyButton = document.getElementById('copy-button');
1782
-
1783
  if (!code || code.includes('Generated code will appear here')) {
1784
  showToast('No code to copy', true);
1785
  return;
1786
  }
1787
-
1788
  navigator.clipboard.writeText(code).then(() => {
1789
  if (copyButton) copyButton.textContent = 'Copied!';
1790
  showToast('Code copied to clipboard!');
@@ -1796,9 +1645,9 @@
1796
  showToast('Failed to copy code', true);
1797
  });
1798
  }
1799
-
1800
  // === NEW MODULE TOGGLE FUNCTIONALITY ===
1801
-
1802
  /**
1803
  * Toggles a slide-out module.
1804
  * If a module on the left is opened, all other left modules are closed.
@@ -1810,15 +1659,12 @@
1810
  console.error('Module not found:', `${moduleName}-module`);
1811
  return;
1812
  }
1813
-
1814
  const isLeft = module.classList.contains('left');
1815
  const side = isLeft ? 'left' : 'right';
1816
  const wasOpen = module.classList.contains('open');
1817
  const statusEl = document.getElementById('preview-status');
1818
-
1819
  // 1. Get all modules on the same side
1820
  const sideModules = document.querySelectorAll(`.slide-module.${side}`);
1821
-
1822
  // 2. If it was already open, just close it.
1823
  if (wasOpen) {
1824
  module.classList.remove('open');
@@ -1833,7 +1679,6 @@
1833
  });
1834
  // ...and then open this one.
1835
  module.classList.add('open');
1836
-
1837
  if (moduleName === 'preview') {
1838
  previewEnabled = true;
1839
  if (statusEl) statusEl.style.display = 'inline';
@@ -1841,7 +1686,6 @@
1841
  }
1842
  }
1843
  }
1844
-
1845
  function attachModuleListeners() {
1846
  // Module tab click handlers
1847
  const tabs = document.querySelectorAll('.module-tab');
@@ -1853,36 +1697,28 @@
1853
  });
1854
  });
1855
  }
1856
-
1857
  // === ATTACH EVENT LISTENERS (DOM-CONTENT-LOADED SAFE) ===
1858
  function attachGlobalEventListeners() {
1859
  const dropdownButton = document.getElementById('dropdown-button');
1860
  const dropdownContent = document.getElementById('dropdown-content');
1861
  if (dropdownButton) dropdownButton.addEventListener('click', toggleDropdown);
1862
  if (dropdownContent) dropdownContent.addEventListener('click', selectOption);
1863
-
1864
  document.addEventListener('click', (e) => {
1865
  const dropdown = document.getElementById('component-dropdown');
1866
  if (dropdown && !dropdown.contains(e.target)) {
1867
  dropdown.classList.remove('show');
1868
  }
1869
  });
1870
-
1871
  const addInputBtn = document.getElementById('add-input-button');
1872
  if (addInputBtn) addInputBtn.addEventListener('click', () => addInput());
1873
-
1874
  const genCodeBtn = document.getElementById('generate-code-button');
1875
  if (genCodeBtn) genCodeBtn.addEventListener('click', generateCode);
1876
-
1877
  const copyBtn = document.getElementById('copy-button');
1878
  if (copyBtn) copyBtn.addEventListener('click', copyToClipboard);
1879
-
1880
  const toggleEditBtn = document.getElementById('toggle-editable');
1881
  if (toggleEditBtn) toggleEditBtn.addEventListener('click', toggleEditable);
1882
-
1883
  const refreshBtn = document.getElementById('refresh-preview');
1884
  if (refreshBtn) refreshBtn.addEventListener('click', updateLivePreview);
1885
-
1886
  // Animation selection
1887
  document.querySelectorAll('.animation-option').forEach(option => {
1888
  option.addEventListener('click', function() {
@@ -1896,39 +1732,32 @@
1896
  }
1897
  });
1898
  });
1899
-
1900
  // Platform export dropdown
1901
  const platformButton = document.getElementById('platform-button');
1902
  const platformOptions = document.getElementById('platform-options');
1903
-
1904
  if(platformButton) {
1905
  platformButton.addEventListener('click', (e) => {
1906
  e.stopPropagation();
1907
  if(platformOptions) platformOptions.classList.toggle('show');
1908
  });
1909
  }
1910
-
1911
  document.addEventListener('click', (e) => {
1912
  if (platformButton && !platformButton.contains(e.target) && platformOptions && !platformOptions.contains(e.target)) {
1913
  platformOptions.classList.remove('show');
1914
  }
1915
  });
1916
-
1917
  document.querySelectorAll('.platform-option').forEach(option => {
1918
  option.addEventListener('click', function() {
1919
  const platform = this.dataset.platform;
1920
  const codeEl = document.getElementById('generated-code');
1921
  if (!codeEl) return;
1922
  const currentCode = codeEl.textContent;
1923
-
1924
  if (!currentCode || currentCode.includes('Generated code will appear here')) {
1925
  showToast('Generate code first before exporting', true);
1926
  return;
1927
  }
1928
-
1929
  const exported = exportToPlatform(platform, currentCode);
1930
  codeEl.textContent = exported;
1931
-
1932
  if (window.hljs) {
1933
  try {
1934
  hljs.highlightElement(codeEl);
@@ -1936,13 +1765,11 @@
1936
  console.error("highlight.js failed:", e);
1937
  }
1938
  }
1939
-
1940
  if (platformOptions) platformOptions.classList.remove('show');
1941
  showToast(`Exported to ${this.textContent.trim()}`);
1942
  });
1943
  });
1944
  }
1945
-
1946
  // === COLOR PALETTE FUNCTIONALITY ===
1947
  function initColorPalette() {
1948
  // Color swatch selection
@@ -1954,24 +1781,20 @@
1954
  if (previewEnabled) updateLivePreview();
1955
  });
1956
  });
1957
-
1958
  // Custom color picker
1959
  const colorPicker = document.getElementById('custom-color-picker');
1960
  const colorHex = document.getElementById('custom-color-hex');
1961
  const applyBtn = document.getElementById('apply-custom-color');
1962
-
1963
  if (colorPicker && colorHex) {
1964
  colorPicker.addEventListener('input', (e) => {
1965
  colorHex.value = e.target.value;
1966
  });
1967
-
1968
  colorHex.addEventListener('input', (e) => {
1969
  const hex = e.target.value;
1970
  if (/^#[0-9A-F]{6}$/i.test(hex)) {
1971
  colorPicker.value = hex;
1972
  }
1973
  });
1974
-
1975
  applyBtn.addEventListener('click', () => {
1976
  customColor = colorPicker.value;
1977
  selectedColorPalette = 'custom';
@@ -1981,7 +1804,6 @@
1981
  });
1982
  }
1983
  }
1984
-
1985
  // Get color values based on selected palette
1986
  function getColorValues() {
1987
  const palettes = {
@@ -1997,9 +1819,7 @@
1997
  };
1998
  return palettes[selectedColorPalette] || palettes.indigo;
1999
  }
2000
-
2001
  // === INITIALIZATION ===
2002
-
2003
  document.addEventListener('DOMContentLoaded', () => {
2004
  try {
2005
  renderApp();
@@ -2008,18 +1828,15 @@
2008
  attachModuleListeners(); // Attach the module tab listeners
2009
  initColorPalette();
2010
  createParticles();
2011
-
2012
  // Open the components panel by default to guide the user
2013
  setTimeout(() => {
2014
  toggleModule('components');
2015
  }, 500);
2016
-
2017
  } catch (e) {
2018
  console.error('❌ App initialization failed:', e);
2019
  document.getElementById('app').innerHTML = '<div style="color: white; padding: 50px;">Error: ' + e.message + '</div>';
2020
  }
2021
  });
2022
-
2023
  </script>
2024
  </body>
2025
  </html>
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>UIKitV3 Elite - Next-Gen Component Automator</title>
 
7
  <script src="https://cdn.tailwindcss.com"></script>
 
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/atom-one-dark.min.css">
 
9
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/purify.min.js"></script>
 
10
  <link rel="preconnect" href="https://fonts.googleapis.com">
11
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
 
13
  <style>
14
  /* Base styles with enhanced visuals */
15
  * {
 
17
  padding: 0;
18
  box-sizing: border-box;
19
  }
 
20
  body {
21
  font-family: 'Inter', sans-serif;
22
  background: #2a2a2a;
 
24
  position: relative;
25
  overflow-x: hidden;
26
  }
 
27
  /* TV Static background layer */
28
  body::before {
29
  content: '';
 
43
  pointer-events: none;
44
  z-index: 0;
45
  }
 
46
  @keyframes staticNoise {
47
  0%, 100% { opacity: 1; filter: contrast(1.05); }
48
  33% { opacity: 0.98; filter: contrast(1); }
49
  66% { opacity: 0.96; filter: contrast(1.02); }
50
  }
 
51
  /* Glass overlay at 55% opacity */
52
  body::after {
53
  content: '';
 
66
  z-index: 1;
67
  mix-blend-mode: overlay;
68
  }
 
69
  /* Animated background particles */
70
  .bg-particles {
71
  position: fixed;
 
76
  pointer-events: none;
77
  z-index: 0;
78
  }
 
79
  .particle {
80
  position: absolute;
81
  background: radial-gradient(circle, rgba(200, 140, 80, 0.15) 0%, transparent 70%);
 
83
  animation: float linear infinite;
84
  filter: blur(2px);
85
  }
 
86
  @keyframes float {
87
  0% { transform: translateY(100vh) scale(0); opacity: 0; }
88
  10% { opacity: 0.3; }
89
  90% { opacity: 0.3; }
90
  100% { transform: translateY(-100vh) scale(1); opacity: 0; }
91
  }
 
92
  /* Glassmorphism effect */
93
  .glass {
94
  background: rgba(20, 20, 20, 0.7);
 
97
  border: 1px solid rgba(200, 140, 80, 0.1);
98
  box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.6);
99
  }
 
100
  .glass-dark {
101
  background: rgba(18, 18, 18, 0.75);
102
  backdrop-filter: blur(50px) saturate(190%);
 
108
  inset 0 0 60px rgba(0, 0, 0, 0.3),
109
  0 0 1px rgba(200, 140, 80, 0.2);
110
  }
 
111
  .container {
112
  max-width: 100%;
113
  margin: 0;
 
117
  min-height: 100vh;
118
  width: 100%;
119
  }
 
120
  /* === SLIDING PANEL LAYOUT CSS === */
 
121
  /* Sliding Module Base Styles */
122
  .slide-module {
123
  position: fixed;
 
139
  display: flex;
140
  flex-direction: column;
141
  }
 
142
  /* Left-side modules (Components, Properties, Code Output) */
143
  .slide-module.left {
144
  left: 48px; /* <-- FIX: Changed from 0 to 48px */
 
146
  border-right: 1px solid rgba(200, 140, 80, 0.25);
147
  transform: translateX(-100%);
148
  }
 
149
  .slide-module.left.open {
150
  transform: translateX(0);
151
  }
 
152
  /* Right-side module (Live Preview) */
153
  .slide-module.right {
154
  right: 48px; /* <-- FIX: Changed from 0 to 48px */
 
157
  border-left: 1px solid rgba(200, 140, 80, 0.25);
158
  transform: translateX(100%);
159
  }
 
160
  .slide-module.right.open {
161
  transform: translateX(0);
162
  }
 
163
  /* Make open panels cover the tabs (No longer needed, but keep for safety) */
164
  .slide-module.open {
165
+ z-index: 160;
166
  }
 
167
  /* Module content area */
168
  .module-content {
169
  flex: 1;
170
  overflow-y: auto;
171
  padding: 2rem;
172
  }
 
173
  /* Module header */
174
  .module-header {
175
  padding: 1.5rem 2rem;
 
177
  background: rgba(200, 140, 80, 0.05);
178
  flex-shrink: 0;
179
  }
 
180
  /* Module Tabs */
181
  .module-tab {
182
  position: fixed;
 
201
  inset 0 1px 0 rgba(255, 255, 255, 0.05),
202
  0 0 1px rgba(200, 140, 80, 0.2);
203
  }
 
204
  /* Left tabs (Components, Properties, Code Output) */
205
  .module-tab.left {
206
  left: 0;
207
  border-left: none;
208
  border-radius: 0 8px 8px 0;
209
  }
 
210
  .module-tab.left:hover {
211
  background: rgba(25, 25, 25, 0.95);
212
  border-color: rgba(200, 140, 80, 0.5);
 
215
  inset 0 1px 0 rgba(255, 255, 255, 0.07),
216
  0 0 16px rgba(200, 140, 80, 0.2);
217
  }
 
218
  /* Right tab (Live Preview) */
219
  .module-tab.right {
220
  right: 0;
221
  border-right: none;
222
  border-radius: 8px 0 0 8px;
223
  }
 
224
  .module-tab.right:hover {
225
  background: rgba(25, 25, 25, 0.95);
226
  border-color: rgba(200, 140, 80, 0.5);
 
229
  inset 0 1px 0 rgba(255, 255, 255, 0.07),
230
  0 0 16px rgba(200, 140, 80, 0.2);
231
  }
 
232
  .module-tab-icon {
233
  font-size: 24px;
234
  }
 
235
  .module-tab-text {
236
  writing-mode: vertical-rl;
237
  text-orientation: mixed;
 
241
  letter-spacing: 0.5px;
242
  text-transform: uppercase;
243
  }
 
244
  /* Position tabs at different heights for left modules */
245
  #components-tab { top: 20%; }
246
  #properties-tab { top: 50%; }
247
  #code-tab { top: 80%; }
248
+
249
  /* === END NEW LAYOUT CSS === */
 
250
  /* === Pinstripe Separators === */
251
  .pinstripe {
252
  position: fixed;
 
257
  z-index: 140; /* Below tabs (150), above closed panels (100) */
258
  box-shadow: 0 0 12px rgba(0, 0, 0, 0.4);
259
  }
 
260
  .pinstripe.left {
261
  left: 48px; /* Aligned with the inner edge of left tabs */
262
  }
 
263
  .pinstripe.right {
264
  right: 48px; /* Aligned with the inner edge of right tab */
265
  }
266
  /* === END NEW CSS === */
 
 
267
  /* Enhanced dropdown with smooth animations */
268
  .dropdown {
269
  position: relative;
270
  display: inline-block;
271
  }
 
272
  .dropdown-content {
273
  display: none;
274
  position: absolute;
 
283
  max-height: 400px;
284
  overflow-y: auto;
285
  }
 
286
  @keyframes dropdownSlide {
287
  from { opacity: 0; transform: translateY(-10px); }
288
  to { opacity: 1; transform: translateY(0); }
289
  }
 
290
  .dropdown-content a {
291
  color: white;
292
  padding: 12px 20px;
 
295
  transition: all 0.3s ease;
296
  border-left: 3px solid transparent;
297
  }
 
298
  .dropdown-content a:hover {
299
  background: linear-gradient(90deg, rgba(200, 140, 80, 0.15), transparent);
300
  border-left-color: rgba(200, 140, 80, 0.6);
301
  padding-left: 25px;
302
  }
 
303
  .dropdown.show .dropdown-content {
304
  display: block;
305
  }
 
306
  /* Enhanced button with hover effects */
307
  .btn-primary {
308
  position: relative;
309
  overflow: hidden;
310
  transition: all 0.3s ease;
311
  }
 
312
  .btn-primary::before {
313
  content: '';
314
  position: absolute;
 
321
  transform: translate(-50%, -50%);
322
  transition: width 0.6s, height 0.6s;
323
  }
 
324
  .btn-primary:hover::before {
325
  width: 300px;
326
  height: 300px;
327
  }
 
328
  .btn-primary span {
329
  position: relative;
330
  z-index: 1;
331
  }
 
332
  /* Loading spinner styles */
333
  .loader {
334
  border: 3px solid rgba(200, 140, 80, 0.1);
 
339
  animation: spin 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55) infinite;
340
  margin: 20px auto;
341
  }
 
342
  @keyframes spin {
343
  0% { transform: rotate(0deg); }
344
  100% { transform: rotate(360deg); }
345
  }
 
346
  /* Custom scrollbar */
347
  ::-webkit-scrollbar {
348
  width: 8px;
349
  height: 8px;
350
  }
 
351
  ::-webkit-scrollbar-track {
352
  background: rgba(30, 30, 30, 0.5);
353
  border-radius: 10px;
354
  }
 
355
  ::-webkit-scrollbar-thumb {
356
  background: linear-gradient(135deg, rgba(180, 120, 60, 0.4) 0%, rgba(200, 140, 80, 0.6) 100%);
357
  border-radius: 10px;
358
  transition: all 0.3s ease;
359
  }
 
360
  ::-webkit-scrollbar-thumb:hover {
361
  background: linear-gradient(135deg, rgba(200, 140, 80, 0.6) 0%, rgba(220, 160, 100, 0.7) 100%);
362
  }
 
363
  /* Input field enhancements */
364
  .input-field {
365
  transition: all 0.3s ease;
366
  position: relative;
367
  }
 
368
  .input-field:focus {
369
  transform: translateY(-2px);
370
  box-shadow: 0 10px 30px rgba(200, 140, 80, 0.2);
371
  border-color: rgba(200, 140, 80, 0.4);
372
  }
 
373
  /* Header text animations with SVG path tracing */
374
  .header-title {
375
  display: inline-block;
376
  position: relative;
377
  overflow: visible;
378
  }
 
379
  .header-svg-container {
380
  position: absolute;
381
  top: 0;
 
385
  pointer-events: none;
386
  z-index: 10;
387
  }
 
388
  .header-path {
389
  fill: none;
390
  stroke: rgba(200, 140, 80, 0.6);
 
396
  animation: tracePath 3s ease forwards;
397
  filter: drop-shadow(0 0 8px rgba(200, 140, 80, 0.4));
398
  }
 
399
  @keyframes tracePath {
400
  to {
401
  stroke-dashoffset: 0;
402
  }
403
  }
 
404
  .header-title .char {
405
  display: inline-block;
406
  opacity: 0;
407
  position: relative;
408
  animation: charScribe 0.5s ease forwards;
409
  }
 
410
  @keyframes charScribe {
411
  0% { opacity: 0; filter: blur(4px); transform: scale(0.8); }
412
  50% { opacity: 0.6; filter: blur(2px); }
413
  100% { opacity: 1; filter: blur(0); transform: scale(1); text-shadow: 0 0 10px rgba(200, 140, 80, 0.3); }
414
  }
 
415
  .subtitle-text {
416
  opacity: 0;
417
  transform: translateY(10px);
418
  animation: subtitleFade 1s ease forwards 2.5s;
419
  }
 
420
  @keyframes subtitleFade {
421
  to { opacity: 1; transform: translateY(0); }
422
  }
 
423
  /* Icon button styles */
424
  .icon-btn {
425
  width: 36px;
 
434
  cursor: pointer;
435
  transition: all 0.3s ease;
436
  }
 
437
  .icon-btn:hover {
438
  background: rgba(200, 140, 80, 0.2);
439
  border-color: rgba(200, 140, 80, 0.4);
440
  transform: scale(1.05);
441
  box-shadow: 0 4px 12px rgba(200, 140, 80, 0.2);
442
  }
 
443
  .icon-btn:active {
444
  transform: scale(0.95);
445
  }
 
446
  /* Platform export dropdown */
447
  .platform-dropdown {
448
  position: relative;
449
  display: inline-block;
450
  }
 
451
  .platform-options {
452
  display: none;
453
  position: absolute;
 
465
  max-height: 500px;
466
  overflow-y: auto;
467
  }
 
468
  .platform-options.show {
469
  display: block;
470
  }
 
471
  .platform-option {
472
  padding: 12px 16px;
473
  color: white;
 
478
  gap: 10px;
479
  border-left: 3px solid transparent;
480
  }
 
481
  .platform-option:hover {
482
  background: linear-gradient(90deg, rgba(200, 140, 80, 0.15), transparent);
483
  border-left-color: rgba(200, 140, 80, 0.6);
484
  }
 
485
  .platform-icon {
486
  width: 24px;
487
  height: 24px;
 
492
  border-radius: 6px;
493
  font-size: 12px;
494
  }
 
495
  /* Animation presets */
496
  .animate-fadeIn { animation: fadeIn 0.5s ease; }
497
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
 
511
  @keyframes rotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
512
  .animate-scale { animation: scale 0.6s ease; }
513
  @keyframes scale { from { transform: scale(0.5); } to { transform: scale(1); } }
 
 
514
  /* Tooltip */
515
  .tooltip {
516
  position: relative;
517
  display: inline-block;
518
  }
 
519
  .tooltip .tooltiptext {
520
  visibility: hidden;
521
  background-color: rgba(0, 0, 0, 0.9);
 
533
  white-space: nowrap;
534
  font-size: 12px;
535
  }
 
536
  .tooltip:hover .tooltiptext {
537
  visibility: visible;
538
  opacity: 1;
539
  }
 
540
  /* Code block enhancements */
541
  pre {
542
  border-radius: 12px;
 
544
  max-height: calc(100vh - 250px); /* Adjust max-height for panel view */
545
  overflow: auto;
546
  }
547
+
548
  /* Notification toast */
549
  .toast {
550
  position: fixed;
 
559
  display: none;
560
  animation: toastSlide 0.3s ease;
561
  }
 
562
  .toast.show { display: block; }
563
  .toast.error { background: rgba(239, 68, 68, 0.95); }
 
564
  @keyframes toastSlide {
565
  from { transform: translateX(400px); opacity: 0; }
566
  to { transform: translateX(0); opacity: 1; }
567
  }
 
568
  /* Animation selection (from 'broken' file) */
569
  .animation-grid {
570
  display: grid;
 
572
  gap: 10px;
573
  margin-top: 10px;
574
  }
 
575
  .animation-option {
576
  padding: 10px;
577
  background: rgba(200, 140, 80, 0.05);
 
583
  font-size: 12px;
584
  color: rgba(200, 140, 80, 0.9);
585
  }
 
586
  .animation-option:hover {
587
  background: rgba(200, 140, 80, 0.15);
588
  border-color: rgba(200, 140, 80, 0.4);
589
  transform: scale(1.05);
590
  }
 
591
  .animation-option.selected {
592
  background: rgba(200, 140, 80, 0.2);
593
  border-color: rgba(200, 140, 80, 0.6);
594
  box-shadow: 0 0 10px rgba(200, 140, 80, 0.2);
595
  }
 
596
  /* Color Palette Controls */
597
  .color-palette-section {
598
  margin-top: 20px;
599
  padding-top: 20px;
600
  border-top: 1px solid rgba(200, 140, 80, 0.15);
601
  }
 
602
  .color-palette-grid {
603
  display: grid;
604
  grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
605
  gap: 12px;
606
  margin-top: 12px;
607
  }
 
608
  .color-swatch {
609
  position: relative;
610
  height: 60px;
 
614
  border: 2px solid rgba(200, 140, 80, 0.2);
615
  overflow: hidden;
616
  }
 
617
  .color-swatch:hover {
618
  transform: scale(1.05);
619
  border-color: rgba(200, 140, 80, 0.5);
620
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
621
  }
 
622
  .color-swatch.selected {
623
  border-color: rgba(200, 140, 80, 0.8);
624
  box-shadow: 0 0 16px rgba(200, 140, 80, 0.4);
625
  }
 
626
  .color-swatch-label {
627
  position: absolute;
628
  bottom: 0;
 
637
  text-transform: uppercase;
638
  letter-spacing: 0.5px;
639
  }
 
640
  .custom-color-input {
641
  display: flex;
642
  gap: 8px;
643
  align-items: center;
644
  margin-top: 12px;
645
  }
 
646
  .custom-color-input input[type="color"] {
647
  width: 50px;
648
  height: 40px;
 
651
  cursor: pointer;
652
  background: transparent;
653
  }
 
654
  .custom-color-input input[type="text"] {
655
  flex: 1;
656
  padding: 8px 12px;
 
661
  font-size: 12px;
662
  font-family: monospace;
663
  }
 
664
  /* Responsive adjustments */
665
  @media (max-width: 1024px) {
666
  .slide-module.left {
 
670
  width: 75vw;
671
  }
672
  }
 
673
  @media (max-width: 768px) {
674
  .slide-module.left {
675
  width: 90vw;
 
690
  <body>
691
  <div class="pinstripe left"></div>
692
  <div class="pinstripe right"></div>
 
693
  <div class="bg-particles" id="particles"></div>
 
694
  <div class="toast" id="toast">
695
  <span id="toast-message">Action completed!</span>
696
  </div>
 
697
  <div id="app"></div>
 
698
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
 
699
  <script>
700
  // === GLOBAL STATE ===
701
  let previewEnabled = false; // Start with preview disabled as panel is closed
 
706
  let selectedColorPalette = 'indigo';
707
  let customColor = '#6366f1';
708
  let selectedAnimation = 'none';
 
709
  // === UTILITY FUNCTIONS ===
 
710
  // XSS Protection using DOMPurify
711
  function sanitizeHTML(html) {
712
  if (window.DOMPurify) {
 
717
  }
718
  return html;
719
  }
 
720
  // Show toast notification
721
  function showToast(message, isError = false) {
722
  const toast = document.getElementById('toast');
 
729
  toast.classList.remove('show');
730
  }, 3000);
731
  }
 
732
  // Create animated particles
733
  function createParticles() {
734
  const particlesContainer = document.getElementById('particles');
735
  if (!particlesContainer) return;
736
  const particleCount = 8;
 
737
  for (let i = 0; i < particleCount; i++) {
738
  const particle = document.createElement('div');
739
  particle.className = 'particle';
 
745
  particlesContainer.appendChild(particle);
746
  }
747
  }
 
748
  // === RENDER MAIN APP ===
749
  function renderApp() {
750
  // This HTML structure is from 'uikit-tim-hf-backup-broken.html'
 
756
  <p class="subtitle-text text-gray-500 text-md tracking-wide" style="animation-delay: 2.7s; margin-top: 1rem;">Click the tabs on the edges</p>
757
  </div>
758
  </div>
 
759
  <div class="module-tab left" id="components-tab" data-module="components">
760
  <span class="module-tab-icon">🎨</span>
761
  <span class="module-tab-text">Components</span>
762
  </div>
 
763
  <div class="module-tab left" id="properties-tab" data-module="properties">
764
  <span class="module-tab-icon">⚙️</span>
765
  <span class="module-tab-text">Properties</span>
766
  </div>
 
767
  <div class="module-tab left" id="code-tab" data-module="code">
768
  <span class="module-tab-icon">💻</span>
769
  <span class="module-tab-text">Code Output</span>
770
  </div>
 
771
  <div class="module-tab right" id="preview-tab" data-module="preview">
772
  <span class="module-tab-icon">👁️</span>
773
  <span class="module-tab-text">Live Preview</span>
774
  </div>
 
775
  <div class="slide-module left" id="components-module">
776
  <div class="module-header">
777
  <h2 class="text-xl font-semibold text-gray-200 flex items-center gap-3">
 
809
  </div>
810
  </div>
811
  </div>
 
812
  <div class="slide-module left" id="properties-module">
813
  <div class="module-header">
814
  <h2 class="text-xl font-semibold text-gray-200 flex items-center gap-3">
 
822
  <button id="add-input-button" class="mt-6 bg-gradient-to-r from-gray-800 to-gray-900 hover:from-gray-700 hover:to-gray-800 text-gray-200 font-semibold py-2 px-5 rounded-lg transition duration-300 border border-gray-700 hover:border-amber-900/30 w-full">
823
  + Add Property
824
  </button>
 
825
  <div class="animation-section mt-8 pt-6 border-t border-gray-700/50">
826
  <h3 class="text-md font-semibold text-gray-300 mb-3">Entry Animation</h3>
827
  <div class="animation-grid" id="animation-grid">
 
837
  <div class="animation-option" data-animation="scale">Scale</div>
838
  </div>
839
  </div>
 
840
  <div class="color-palette-section mt-8">
841
  <h3 class="text-md font-semibold text-gray-300 mb-2">Color Palette</h3>
842
  <p class="text-xs text-gray-400 mb-3">Select or customize colors for your components</p>
 
876
  </div>
877
  </div>
878
  </div>
 
879
  <div class="slide-module left" id="code-module">
880
  <div class="module-header">
881
  <div class="flex justify-between items-center w-full">
 
943
  </div>
944
  </div>
945
  </div>
 
946
  <div class="slide-module right" id="preview-module">
947
  <div class="module-header">
948
  <div class="flex justify-between items-center w-full">
 
975
  </div>
976
  </div>`;
977
  }
 
978
  // Animate header text with SVG path tracing
979
  function animateHeaderText() {
980
  const title = document.getElementById('main-title');
981
  if (!title) return;
 
982
  const text = 'UIKitV3 Elite';
983
  const chars = text.split('');
 
984
  // Create SVG path for tracing effect
985
  const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
986
  svg.setAttribute('class', 'header-svg-container');
 
990
  svg.style.left = '0';
991
  svg.style.width = '100%';
992
  svg.style.height = '100%';
 
993
  // Create a decorative path that traces under the text
994
  const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
995
  const pathData = 'M 50 80 Q 150 60, 250 70 T 450 65 Q 550 68, 650 75 T 750 70';
996
  path.setAttribute('d', pathData);
997
  path.setAttribute('class', 'header-path');
998
  svg.appendChild(path);
 
999
  // Clear and create character spans
1000
  title.innerHTML = '';
1001
  title.appendChild(svg);
 
1002
  chars.forEach((char, index) => {
1003
  const span = document.createElement('span');
1004
  span.className = 'char';
 
1007
  title.appendChild(span);
1008
  });
1009
  }
 
1010
  // === EVENT HANDLERS ===
 
1011
  function toggleDropdown() {
1012
  document.getElementById('component-dropdown').classList.toggle('show');
1013
  }
 
1014
  function selectOption(event) {
1015
  event.preventDefault();
1016
  if (event.target.dataset.value) {
 
1020
  document.getElementById('inputs-container').innerHTML = '';
1021
  currentComponent = value;
1022
  addDefaultInputs(value);
 
1023
  if (previewEnabled) {
1024
  updateLivePreview();
1025
  }
 
1027
  toggleModule('properties');
1028
  }
1029
  }
 
1030
  function addDefaultInputs(component) {
1031
  const container = document.getElementById('inputs-container');
1032
  if (!container) return;
1033
  let defaultProps = [];
 
1034
  switch(component) {
1035
  case 'Button':
1036
  defaultProps = [
 
1107
  ];
1108
  break;
1109
  }
 
1110
  defaultProps.forEach(prop => {
1111
  addInput(prop.key, prop.value);
1112
  });
1113
  }
 
1114
  function addInput(key = '', value = '') {
1115
  const container = document.getElementById('inputs-container');
1116
  if (!container) return;
1117
  const inputGroup = document.createElement('div');
1118
  inputGroup.className = 'flex items-center gap-3';
 
1119
  inputGroup.innerHTML = `
1120
  <input type="text" class="property-key input-field bg-black/50 text-gray-200 p-3 rounded-lg flex-1 border border-gray-700 focus:outline-none focus:ring-1 focus:ring-amber-900/40 focus:border-amber-900/40" placeholder="Property" value="${key}">
1121
  <input type="text" class="property-value input-field bg-black/50 text-gray-200 p-3 rounded-lg flex-[2] border border-gray-700 focus:outline-none focus:ring-1 focus:ring-amber-900/40 focus:border-amber-900/40" placeholder="Value" value="${value}">
 
1123
 
1124
  </button>
1125
  `;
 
1126
  container.appendChild(inputGroup);
 
1127
  // Add event listener to remove button
1128
  inputGroup.querySelector('.remove-input-button').addEventListener('click', removeInput);
 
1129
  // === FIX: Attach listeners regardless of whether preview is open ===
1130
  // This ensures live refresh works even if panel was closed when prop was added.
1131
  inputGroup.querySelector('.property-key').addEventListener('input', debouncedLivePreview);
1132
  inputGroup.querySelector('.property-value').addEventListener('input', debouncedLivePreview);
1133
  }
 
1134
  function removeInput(event) {
1135
  event.target.closest('.flex').remove();
1136
  if (previewEnabled) {
1137
  updateLivePreview();
1138
  }
1139
  }
 
1140
  function debouncedLivePreview() {
1141
  clearTimeout(livePreviewDebounce);
1142
  livePreviewDebounce = setTimeout(() => {
1143
  updateLivePreview();
1144
  }, 500);
1145
  }
 
1146
  function getSelectedAnimation() {
1147
  const selected = document.querySelector('.animation-option.selected');
1148
  return selected ? selected.dataset.animation : 'none';
1149
  }
 
1150
  function collectProperties() {
1151
  const container = document.getElementById('inputs-container');
1152
  if (!container) return {};
 
1165
  }
1166
  return properties;
1167
  }
 
1168
  function generateCode() {
1169
  const component = document.getElementById('selected-option').textContent;
1170
  if (component === 'Select Component') {
1171
  showToast('Please select a component first.', true);
1172
  return;
1173
  }
 
1174
  const properties = collectProperties();
1175
  const animation = getSelectedAnimation();
1176
  const codeOutput = document.getElementById('generated-code');
1177
  const loadingSpinner = document.getElementById('loading-spinner');
1178
  const copyButton = document.getElementById('copy-button');
 
1179
  if (!codeOutput || !loadingSpinner || !copyButton) return;
 
1180
  codeOutput.textContent = '';
1181
  copyButton.textContent = 'Copy';
1182
  loadingSpinner.style.display = 'block';
 
1183
  setTimeout(() => {
1184
  let generatedHtml = '';
 
1185
  try {
1186
  generatedHtml = generateComponentCode(component, properties, animation);
1187
  codeOutput.textContent = generatedHtml;
 
1188
  if (window.hljs) {
1189
  try {
1190
  hljs.highlightElement(codeOutput);
 
1192
  console.error("highlight.js failed:", e);
1193
  }
1194
  }
 
1195
  showToast('Code generated successfully!');
1196
  // Automatically open the code panel
1197
  toggleModule('code');
 
1198
  } catch (error) {
1199
  generatedHtml = `<div class="text-red-400">Error generating code: ${error.message}</div>`;
1200
  codeOutput.textContent = generatedHtml;
1201
  showToast('Error generating code', true);
1202
  }
 
1203
  loadingSpinner.style.display = 'none';
 
1204
  if (previewEnabled) {
1205
  updateLivePreview();
1206
  }
1207
  }, 800);
1208
  }
 
1209
  function generateComponentCode(component, props, animation) {
1210
  let html = '';
 
1211
  switch(component) {
1212
  case 'Button':
1213
  html = generateButtonCode(props);
 
1248
  default:
1249
  html = `<div class="text-red-400">Error: Component generator not found.</div>`;
1250
  }
 
1251
  // Add animation if selected
1252
  if (animation && animation !== 'none') {
1253
  html = addAnimationToHTML(html, animation);
1254
  }
 
1255
  return html;
1256
  }
 
1257
  function addAnimationToHTML(html, animation) {
1258
  // Add animation class to the root element
1259
  const animationStyles = getAnimationStyles(animation);
 
1266
  return html.replace(/<([a-z0-9]+)/i, `<$1 class="animate-${animation}" style="${animationStyles}"`);
1267
  }
1268
  }
 
1269
  function getAnimationStyles(animation) {
1270
  const animations = {
1271
  fadeIn: 'animation: fadeIn 0.6s ease;',
 
1280
  };
1281
  return animations[animation] || '';
1282
  }
 
1283
  // === COMPONENT GENERATORS ===
 
1284
  function generateButtonCode(props) {
1285
+ const colors = getColorValues();
1286
  const text = props.text || 'Button';
1287
  const size = props.size || 'medium';
 
 
 
1288
  let sizeClasses = 'py-3 px-6 text-base';
1289
  switch(size) {
1290
  case 'small': sizeClasses = 'py-2 px-4 text-sm'; break;
1291
  case 'large': sizeClasses = 'py-4 px-8 text-lg'; break;
1292
  }
1293
+ return `<button class="bg-gradient-to-r from-[${colors.from}] to-[${colors.to}] hover:opacity-90 ${sizeClasses} text-white font-bold rounded-xl shadow-lg transition duration-300 transform hover:scale-105">
1294
  ${text}
1295
  </button>`;
1296
  }
 
1297
  function generateInputCode(props) {
1298
+ const colors = getColorValues();
1299
  const placeholder = props.placeholder || '';
1300
  const type = props.type || 'text';
1301
  const label = props.label || '';
 
1310
  ${labelHtml}<input
1311
  type="${type}"
1312
  id="generated-input"
1313
+ class="bg-gray-800 text-white p-3 rounded-xl w-full border border-gray-700 focus:outline-none focus:ring-2 focus:ring-[${colors.from}] transition duration-300"
1314
  placeholder="${placeholder}"
1315
  >
1316
  </div>`;
1317
  }
 
1318
  function generateCardCode(props) {
1319
+ const colors = getColorValues();
1320
  const title = props.title || 'Card Title';
1321
  const content = props.content || 'Card content goes here.';
1322
  const imageUrl = props.imageUrl || '';
 
1330
  >
1331
  `;
1332
  }
1333
+ return `<div class="max-w-sm bg-gradient-to-br from-gray-800 to-gray-900 rounded-2xl shadow-2xl overflow-hidden border border-[${colors.from}/50] transition duration-300 transform hover:scale-105">
1334
  ${imageHtml}<div class="p-6">
1335
+ <h3 class="text-2xl font-bold bg-gradient-to-r from-[${colors.from}] to-[${colors.to}] bg-clip-text text-transparent mb-3">${title}</h3>
1336
  <p class="text-gray-300 leading-relaxed">${content}</p>
1337
  </div>
1338
  </div>`;
1339
  }
 
1340
  function generateNavbarCode(props) {
1341
+ const colors = getColorValues();
1342
  const brand = props.brand || 'Brand';
1343
  const links = (props.links || 'Home,About').split(',').map(link => link.trim());
1344
+ let linksHtml = links.map(link => `<a href="#" class="text-gray-300 hover:text-white px-4 py-2 rounded-lg transition duration-300 hover:bg-gray-700">${link}</a>`).join('\n ');
1345
  return `<nav class="bg-gradient-to-r from-gray-900 to-gray-800 shadow-2xl border-b border-gray-700">
1346
  <div class="max-w-7xl mx-auto px-6 py-4">
1347
  <div class="flex items-center justify-between">
1348
  <div class="flex-shrink-0">
1349
+ <span class="text-white font-black text-2xl bg-gradient-to-r from-[${colors.from}] to-[${colors.to}] bg-clip-text text-transparent">${brand}</span>
1350
  </div>
1351
  <div class="hidden md:flex items-center space-x-2">
1352
  ${linksHtml}
 
1362
  </div>
1363
  </nav>`;
1364
  }
 
1365
  function generateModalCode(props) {
1366
+ const colors = getColorValues();
1367
  const title = props.title || 'Modal Title';
1368
  const body = props.body || 'Modal body text goes here.';
1369
  const footerButtons = (props.footer || 'Close').split(',').map(btn => btn.trim());
1370
  let buttonsHtml = footerButtons.map((btnText, index) => {
1371
  const isPrimary = (index === footerButtons.length - 1) && footerButtons.length > 1;
1372
  const colorClasses = isPrimary
1373
+ ? `bg-gradient-to-r from-[${colors.to}] to-[${colors.from}] hover:from-[${colors.hover}] hover:to-[${colors.to}]`
1374
  : 'bg-gradient-to-r from-gray-600 to-gray-700 hover:from-gray-700 hover:to-gray-800';
1375
  return `<button class="${colorClasses} text-white font-bold py-2 px-6 rounded-lg transition duration-300 transform hover:scale-105">${btnText}</button>`;
1376
+ }).join('\n ');
1377
  return `<div class="fixed inset-0 bg-black bg-opacity-70 backdrop-blur-sm z-50 flex items-center justify-center p-4">
1378
  <div class="bg-gradient-to-br from-gray-800 to-gray-900 rounded-2xl shadow-2xl max-w-lg w-full border border-gray-700 overflow-hidden">
1379
+ <div class="flex justify-between items-center p-6 border-b border-gray-700 bg-gradient-to-r from-[${colors.from}/30] to-[${colors.to}/30]">
1380
  <h3 class="text-2xl font-bold text-white">${title}</h3>
1381
  <button class="text-gray-400 hover:text-white transition duration-300 transform hover:rotate-90">
1382
  <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
 
1393
  </div>
1394
  </div>`;
1395
  }
 
1396
  function generateAlertCode(props) {
1397
+ const colors = getColorValues();
1398
  const text = props.text || 'Alert text.';
1399
  const type = props.type || 'info';
1400
+ let bgFrom = '#1e40af/80';
1401
+ let bgTo = '#1d4ed8/80';
1402
+ let borderColor = '#3b82f6';
1403
  let textClass = 'text-blue-200';
1404
+ let iconClass = 'text-blue-400';
1405
  let iconSvg = `<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>`;
1406
  switch(type) {
1407
+ case 'primary':
1408
+ bgFrom = `${colors.from}/80`;
1409
+ bgTo = `${colors.to}/80`;
1410
+ borderColor = colors.from;
1411
+ textClass = 'text-white';
1412
+ iconClass = `text-[${colors.from}]`;
1413
+ break;
1414
+ case 'info':
1415
+ bgFrom = '#1e40af/80';
1416
+ bgTo = '#1d4ed8/80';
1417
+ borderColor = '#3b82f6';
1418
+ textClass = 'text-blue-200';
1419
+ iconClass = 'text-blue-400';
1420
+ break;
1421
  case 'success':
1422
+ bgFrom = '#065f46/80';
1423
+ bgTo = '#047857/80';
1424
+ borderColor = '#10b981';
1425
  textClass = 'text-green-200';
1426
+ iconClass = 'text-green-400';
1427
  iconSvg = `<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg>`;
1428
  break;
1429
  case 'warning':
1430
+ bgFrom = '#92400e/80';
1431
+ bgTo = '#854d0e/80';
1432
+ borderColor = '#f59e0b';
1433
  textClass = 'text-yellow-200';
1434
+ iconClass = 'text-yellow-400';
1435
  iconSvg = `<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path></svg>`;
1436
  break;
1437
  case 'danger':
1438
+ bgFrom = '#991b1b/80';
1439
+ bgTo = '#b91c1c/80';
1440
+ borderColor = '#ef4444';
1441
  textClass = 'text-red-200';
1442
+ iconClass = 'text-red-400';
1443
  iconSvg = `<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path></svg>`;
1444
  break;
1445
  }
1446
+ const bgClass = `from-[${bgFrom}] to-[${bgTo}] border-[${borderColor}]`;
1447
  return `<div class="flex p-5 rounded-xl bg-gradient-to-r ${bgClass} ${textClass} border-l-4 shadow-lg backdrop-blur-sm" role="alert">
1448
+ <span class="flex-shrink-0 ${iconClass}">${iconSvg}</span>
1449
  <div class="ml-4 text-sm font-semibold">${text}</div>
1450
  <button class="ml-auto text-white hover:opacity-75 transition duration-300">
1451
  <svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
1452
  </button>
1453
  </div>`;
1454
  }
 
1455
  function generateBadgeCode(props) {
 
1456
  const colors = getColorValues();
1457
+ const text = props.text || 'Badge';
1458
+ return `<span class="inline-flex items-center px-4 py-1.5 rounded-full text-xs font-bold text-white shadow-lg transition duration-300 transform hover:scale-110 bg-gradient-to-r from-[${colors.from}] to-[${colors.to}]">
1459
  ${text}
1460
  </span>`;
1461
  }
 
1462
  function generateBreadcrumbCode(props) {
1463
+ const colors = getColorValues();
1464
  const links = (props.links || 'Home,Page').split(',').map(link => link.trim());
1465
  const separatorSvg = `<svg class="w-5 h-5 text-gray-500" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path></svg>`;
1466
  let linksHtml = links.map((link, index) => {
1467
  const isLast = index === links.length - 1;
1468
  if (isLast) {
1469
+ return `<li aria-current="page"><div class="flex items-center gap-2">${index > 0 ? separatorSvg : ''}<span class="text-sm font-semibold text-[${colors.from}]">${link}</span></div></li>`;
1470
  } else {
1471
  return `<li><div class="flex items-center gap-2">${index > 0 ? separatorSvg : ''}<a href="#" class="text-sm font-medium text-gray-300 hover:text-white transition duration-300">${link}</a></div></li>`;
1472
  }
1473
+ }).join('\n ');
1474
  return `<nav class="flex bg-gray-800/50 backdrop-blur-sm px-5 py-3 rounded-xl border border-gray-700" aria-label="Breadcrumb">
1475
  <ol class="inline-flex items-center space-x-1">${linksHtml}</ol>
1476
  </nav>`;
1477
  }
 
1478
  function generateTabsCode(props) {
1479
+ const colors = getColorValues();
1480
  const tabs = (props.tabs || 'Tab 1,Tab 2').split(',').map(tab => tab.trim());
1481
  const activeTab = props.active || tabs[0];
1482
  let tabsHtml = tabs.map(tab => {
1483
  const isActive = tab === activeTab;
1484
+ const activeClasses = `text-[${colors.from}] border-[${colors.from}] bg-[${colors.from}/20]`;
1485
  const inactiveClasses = 'text-gray-400 border-transparent hover:text-gray-300 hover:border-gray-500';
1486
  return `<li><a href="#" class="inline-block px-6 py-3 border-b-2 rounded-t-lg transition duration-300 ${isActive ? activeClasses : inactiveClasses}">${tab}</a></li>`;
1487
+ }).join('\n ');
1488
  return `<div class="bg-gray-800/50 backdrop-blur-sm rounded-xl border border-gray-700 overflow-hidden">
1489
  <ul class="flex flex-wrap text-sm font-medium text-center border-b border-gray-700">${tabsHtml}</ul>
1490
  </div>`;
1491
  }
 
1492
  function generatePaginationCode(props) {
1493
+ const colors = getColorValues();
1494
  const items = (props.items || '1,2,3').split(',').map(item => item.trim());
1495
  let itemsHtml = items.map(item => {
1496
  const isText = isNaN(parseInt(item));
1497
  const classes = isText ? 'px-4' : 'w-10';
1498
+ return `<li><a href="#" class="flex items-center justify-center ${classes} h-10 leading-tight text-gray-300 bg-gray-800 border border-gray-700 rounded-lg hover:bg-[${colors.from}/20] hover:text-[${colors.from}] transition duration-300 transform hover:scale-110">${item}</a></li>`;
1499
+ }).join('\n ');
1500
  return `<nav aria-label="Page navigation"><ul class="inline-flex items-center gap-2">${itemsHtml}</ul></nav>`;
1501
  }
 
1502
  function generateHeroCode(props) {
1503
+ const colors = getColorValues();
1504
  const title = props.title || 'Welcome to the Future';
1505
  const subtitle = props.subtitle || 'Build amazing things with modern components';
1506
  const buttonText = props.buttonText || 'Get Started';
1507
+ return `<div class="relative overflow-hidden bg-gradient-to-br from-[${colors.from}] via-[${colors.to}] to-[${colors.hover}] py-24 px-6">
1508
  <div class="absolute inset-0 bg-black/30"></div>
1509
  <div class="relative max-w-4xl mx-auto text-center">
1510
  <h1 class="text-6xl font-black text-white mb-6 leading-tight">${title}</h1>
1511
  <p class="text-xl text-gray-200 mb-10 max-w-2xl mx-auto">${subtitle}</p>
1512
+ <button class="bg-gradient-to-r from-[${colors.from}] to-[${colors.to}] hover:from-[${colors.hover}] hover:to-[${colors.from}] text-white font-bold py-4 px-10 rounded-xl text-lg shadow-2xl transition duration-300 transform hover:scale-105">
1513
  ${buttonText}
1514
  </button>
1515
  </div>
1516
  </div>`;
1517
  }
 
1518
  function generateFooterCode(props) {
1519
+ const colors = getColorValues();
1520
  const company = props.company || 'MyCompany';
1521
  const links = (props.links || 'About,Blog,Careers,Contact').split(',').map(link => link.trim());
1522
  const copyright = props.copyright || '2024 MyCompany. All rights reserved.';
1523
+ let linksHtml = links.map(link => `<a href="#" class="text-gray-400 hover:text-white transition duration-300">${link}</a>`).join('\n ');
1524
  return `<footer class="bg-gradient-to-br from-gray-900 to-gray-800 border-t border-gray-700">
1525
  <div class="max-w-7xl mx-auto px-6 py-12">
1526
  <div class="grid grid-cols-1 md:grid-cols-3 gap-8">
1527
  <div>
1528
+ <h3 class="text-2xl font-black text-white mb-4 bg-gradient-to-r from-[${colors.from}] to-[${colors.to}] bg-clip-text text-transparent">${company}</h3>
1529
  <p class="text-gray-400">Building the future, one component at a time.</p>
1530
  </div>
1531
  <div>
 
1545
  </div>
1546
  </footer>`;
1547
  }
 
1548
  // === PLATFORM EXPORT ===
 
1549
  function exportToPlatform(platform, html) {
1550
  let exported = '';
1551
  switch(platform) {
 
1562
  }
1563
  return exported;
1564
  }
 
1565
  function convertToReact(html) {
1566
  let react = html
1567
  .replace(/class=/g, 'className=')
 
1571
  .replace(/stroke-linejoin=/g, 'strokeLinejoin=')
1572
  .replace(/fill-rule=/g, 'fillRule=')
1573
  .replace(/clip-rule=/g, 'clipRule=');
1574
+ return `import React from 'react';\n\nexport default function Component() {\n return (\n ${react}\n );\n}\n\n// For Next.js, this component is ready to use!\n// Import Tailwind CSS in your _app.js or layout.js`;
1575
  }
 
1576
  function convertToVue(html) {
1577
+ return `<template>\n ${html}\n</template>\n\n<` + `script setup>\n// Vue 3 Composition API\n// For Nuxt.js, this component is ready to use!\n</` + `script>\n\n<style scoped>\n/* Add any component-specific styles here */\n</style>`;
1578
  }
 
1579
  function convertToWordPress(html) {
1580
+ return `<?php\n/**\n * Custom Component Block\n */\n?>\n\n<div class="wp-block-custom-component">\n ${html}\n</div>\n\n<?php\n// Add this to your theme's functions.php:\n// Make sure to enqueue Tailwind CSS\n?>`;
1581
  }
 
1582
  function convertToJoomla(html) {
1583
+ return `<?php\n/**\n * @package Joomla.Site\n * @subpackage mod_custom_component\n */\n\ndefined('_JEXEC') or die;\n?>\n\n<div class="mod-custom-component">\n ${html}\n</div>\n\n\n`;
1584
  }
 
1585
  function convertToShopify(html) {
1586
+ return `{% comment %}\n Shopify Liquid Template\n Section: custom-component\n{% endcomment %}\n\n<div class="custom-component-section">\n ${html}\n</div>\n\n{% schema %}\n{\n "name": "Custom Component",\n "settings": [],\n "presets": [{\n "name": "Custom Component"\n }]\n}\n{% endschema %}\n\n`;
1587
  }
 
1588
  function convertToAngular(html) {
1589
+ return `import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-custom-component',\n template: \\\`\n ${html}\n \\\`,\n styles: [\\\`\n /* Component-specific styles */\n \\\`]\n})\nexport class CustomComponent {\n constructor() {}\n}\n\n// Make sure to add Tailwind CSS to your Angular project\n// via angular.json or styles.css`;
1590
  }
 
1591
  function convertToSvelte(html) {
1592
+ return `<` + `script>\n // Svelte component\n // For SvelteKit, this component is ready to use!\n</` + `script>\n\n${html}\n\n<style>\n /* Add any component-specific styles here */\n</style>`;
1593
  }
 
1594
  // === LIVE PREVIEW ===
 
1595
  function updateLivePreview() {
1596
  if (!previewEnabled) return;
 
1597
  const component = document.getElementById('selected-option').textContent;
1598
  const viewerContent = document.getElementById('viewer-content');
1599
  if (component === 'Select Component') {
 
1604
  `;
1605
  return;
1606
  }
 
1607
  const properties = collectProperties();
1608
  const animation = getSelectedAnimation();
1609
  const html = generateComponentCode(component, properties, animation);
1610
  const sanitized = sanitizeHTML(html);
1611
+
1612
  viewerContent.innerHTML = sanitized;
1613
  }
 
1614
  function toggleEditable() {
1615
  isEditable = !isEditable;
1616
  const viewerContent = document.getElementById('viewer-content');
 
1619
  // Add a visual indicator for editable mode
1620
  viewerContent.style.outline = isEditable ? '2px dashed rgba(200, 140, 80, 0.5)' : 'none';
1621
  viewerContent.style.boxShadow = isEditable ? 'inset 0 0 20px rgba(0,0,0,0.3)' : 'none';
 
1622
  if (isEditable) {
1623
  showToast('Edit mode enabled');
1624
  } else {
1625
  showToast('Edit mode disabled');
1626
  }
1627
  }
 
1628
  function copyToClipboard() {
1629
  const codeEl = document.getElementById('generated-code');
1630
  if (!codeEl) return;
1631
  const code = codeEl.textContent;
1632
  const copyButton = document.getElementById('copy-button');
 
1633
  if (!code || code.includes('Generated code will appear here')) {
1634
  showToast('No code to copy', true);
1635
  return;
1636
  }
 
1637
  navigator.clipboard.writeText(code).then(() => {
1638
  if (copyButton) copyButton.textContent = 'Copied!';
1639
  showToast('Code copied to clipboard!');
 
1645
  showToast('Failed to copy code', true);
1646
  });
1647
  }
1648
+
1649
  // === NEW MODULE TOGGLE FUNCTIONALITY ===
1650
+
1651
  /**
1652
  * Toggles a slide-out module.
1653
  * If a module on the left is opened, all other left modules are closed.
 
1659
  console.error('Module not found:', `${moduleName}-module`);
1660
  return;
1661
  }
 
1662
  const isLeft = module.classList.contains('left');
1663
  const side = isLeft ? 'left' : 'right';
1664
  const wasOpen = module.classList.contains('open');
1665
  const statusEl = document.getElementById('preview-status');
 
1666
  // 1. Get all modules on the same side
1667
  const sideModules = document.querySelectorAll(`.slide-module.${side}`);
 
1668
  // 2. If it was already open, just close it.
1669
  if (wasOpen) {
1670
  module.classList.remove('open');
 
1679
  });
1680
  // ...and then open this one.
1681
  module.classList.add('open');
 
1682
  if (moduleName === 'preview') {
1683
  previewEnabled = true;
1684
  if (statusEl) statusEl.style.display = 'inline';
 
1686
  }
1687
  }
1688
  }
 
1689
  function attachModuleListeners() {
1690
  // Module tab click handlers
1691
  const tabs = document.querySelectorAll('.module-tab');
 
1697
  });
1698
  });
1699
  }
 
1700
  // === ATTACH EVENT LISTENERS (DOM-CONTENT-LOADED SAFE) ===
1701
  function attachGlobalEventListeners() {
1702
  const dropdownButton = document.getElementById('dropdown-button');
1703
  const dropdownContent = document.getElementById('dropdown-content');
1704
  if (dropdownButton) dropdownButton.addEventListener('click', toggleDropdown);
1705
  if (dropdownContent) dropdownContent.addEventListener('click', selectOption);
 
1706
  document.addEventListener('click', (e) => {
1707
  const dropdown = document.getElementById('component-dropdown');
1708
  if (dropdown && !dropdown.contains(e.target)) {
1709
  dropdown.classList.remove('show');
1710
  }
1711
  });
 
1712
  const addInputBtn = document.getElementById('add-input-button');
1713
  if (addInputBtn) addInputBtn.addEventListener('click', () => addInput());
 
1714
  const genCodeBtn = document.getElementById('generate-code-button');
1715
  if (genCodeBtn) genCodeBtn.addEventListener('click', generateCode);
 
1716
  const copyBtn = document.getElementById('copy-button');
1717
  if (copyBtn) copyBtn.addEventListener('click', copyToClipboard);
 
1718
  const toggleEditBtn = document.getElementById('toggle-editable');
1719
  if (toggleEditBtn) toggleEditBtn.addEventListener('click', toggleEditable);
 
1720
  const refreshBtn = document.getElementById('refresh-preview');
1721
  if (refreshBtn) refreshBtn.addEventListener('click', updateLivePreview);
 
1722
  // Animation selection
1723
  document.querySelectorAll('.animation-option').forEach(option => {
1724
  option.addEventListener('click', function() {
 
1732
  }
1733
  });
1734
  });
 
1735
  // Platform export dropdown
1736
  const platformButton = document.getElementById('platform-button');
1737
  const platformOptions = document.getElementById('platform-options');
 
1738
  if(platformButton) {
1739
  platformButton.addEventListener('click', (e) => {
1740
  e.stopPropagation();
1741
  if(platformOptions) platformOptions.classList.toggle('show');
1742
  });
1743
  }
 
1744
  document.addEventListener('click', (e) => {
1745
  if (platformButton && !platformButton.contains(e.target) && platformOptions && !platformOptions.contains(e.target)) {
1746
  platformOptions.classList.remove('show');
1747
  }
1748
  });
 
1749
  document.querySelectorAll('.platform-option').forEach(option => {
1750
  option.addEventListener('click', function() {
1751
  const platform = this.dataset.platform;
1752
  const codeEl = document.getElementById('generated-code');
1753
  if (!codeEl) return;
1754
  const currentCode = codeEl.textContent;
 
1755
  if (!currentCode || currentCode.includes('Generated code will appear here')) {
1756
  showToast('Generate code first before exporting', true);
1757
  return;
1758
  }
 
1759
  const exported = exportToPlatform(platform, currentCode);
1760
  codeEl.textContent = exported;
 
1761
  if (window.hljs) {
1762
  try {
1763
  hljs.highlightElement(codeEl);
 
1765
  console.error("highlight.js failed:", e);
1766
  }
1767
  }
 
1768
  if (platformOptions) platformOptions.classList.remove('show');
1769
  showToast(`Exported to ${this.textContent.trim()}`);
1770
  });
1771
  });
1772
  }
 
1773
  // === COLOR PALETTE FUNCTIONALITY ===
1774
  function initColorPalette() {
1775
  // Color swatch selection
 
1781
  if (previewEnabled) updateLivePreview();
1782
  });
1783
  });
 
1784
  // Custom color picker
1785
  const colorPicker = document.getElementById('custom-color-picker');
1786
  const colorHex = document.getElementById('custom-color-hex');
1787
  const applyBtn = document.getElementById('apply-custom-color');
 
1788
  if (colorPicker && colorHex) {
1789
  colorPicker.addEventListener('input', (e) => {
1790
  colorHex.value = e.target.value;
1791
  });
 
1792
  colorHex.addEventListener('input', (e) => {
1793
  const hex = e.target.value;
1794
  if (/^#[0-9A-F]{6}$/i.test(hex)) {
1795
  colorPicker.value = hex;
1796
  }
1797
  });
 
1798
  applyBtn.addEventListener('click', () => {
1799
  customColor = colorPicker.value;
1800
  selectedColorPalette = 'custom';
 
1804
  });
1805
  }
1806
  }
 
1807
  // Get color values based on selected palette
1808
  function getColorValues() {
1809
  const palettes = {
 
1819
  };
1820
  return palettes[selectedColorPalette] || palettes.indigo;
1821
  }
 
1822
  // === INITIALIZATION ===
 
1823
  document.addEventListener('DOMContentLoaded', () => {
1824
  try {
1825
  renderApp();
 
1828
  attachModuleListeners(); // Attach the module tab listeners
1829
  initColorPalette();
1830
  createParticles();
 
1831
  // Open the components panel by default to guide the user
1832
  setTimeout(() => {
1833
  toggleModule('components');
1834
  }, 500);
 
1835
  } catch (e) {
1836
  console.error('❌ App initialization failed:', e);
1837
  document.getElementById('app').innerHTML = '<div style="color: white; padding: 50px;">Error: ' + e.message + '</div>';
1838
  }
1839
  });
 
1840
  </script>
1841
  </body>
1842
  </html>