File size: 22,480 Bytes
be8f1e2
 
75572cf
 
be8f1e2
 
 
 
75572cf
 
 
 
 
 
 
be8f1e2
 
 
 
75572cf
 
be8f1e2
 
 
 
 
 
 
 
75572cf
be8f1e2
75572cf
be8f1e2
 
 
 
 
75572cf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
be8f1e2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75572cf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
be8f1e2
 
 
 
 
 
 
 
 
75572cf
 
 
 
be8f1e2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75572cf
 
 
be8f1e2
 
75572cf
be8f1e2
 
 
75572cf
 
be8f1e2
 
 
75572cf
be8f1e2
 
75572cf
be8f1e2
 
75572cf
be8f1e2
 
75572cf
 
 
 
 
 
 
 
 
 
 
be8f1e2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75572cf
be8f1e2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75572cf
 
 
 
be8f1e2
 
 
 
 
 
 
 
 
 
75572cf
 
 
 
be8f1e2
 
75572cf
be8f1e2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75572cf
be8f1e2
 
 
75572cf
be8f1e2
 
 
 
 
 
75572cf
 
 
 
be8f1e2
 
 
 
 
 
 
75572cf
 
be8f1e2
 
 
75572cf
 
be8f1e2
 
 
75572cf
 
 
be8f1e2
75572cf
 
be8f1e2
 
75572cf
 
be8f1e2
 
75572cf
 
be8f1e2
 
 
 
 
75572cf
 
 
be8f1e2
 
 
 
 
 
 
 
 
 
 
 
75572cf
 
 
be8f1e2
 
 
 
 
 
75572cf
 
be8f1e2
 
 
75572cf
 
 
be8f1e2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75572cf
 
be8f1e2
75572cf
 
 
 
 
 
 
be8f1e2
 
75572cf
be8f1e2
 
75572cf
 
be8f1e2
75572cf
be8f1e2
 
75572cf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
be8f1e2
 
 
75572cf
 
 
 
 
be8f1e2
75572cf
be8f1e2
75572cf
 
 
 
 
 
be8f1e2
75572cf
 
 
 
 
 
 
 
 
 
 
 
 
be8f1e2
 
75572cf
 
be8f1e2
75572cf
 
 
 
 
be8f1e2
75572cf
be8f1e2
75572cf
 
be8f1e2
 
75572cf
 
 
 
 
 
be8f1e2
 
 
 
 
 
 
 
 
75572cf
 
 
 
 
 
 
be8f1e2
 
 
 
 
 
 
 
 
 
 
 
75572cf
 
 
 
 
 
be8f1e2
 
 
 
 
 
 
 
 
 
75572cf
 
 
 
be8f1e2
 
 
 
 
 
 
 
 
 
75572cf
 
be8f1e2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
import json
from pathlib import Path
from typing import Dict, List, Any, Tuple
from collections import defaultdict

class HTMLGenerator:
    """Generate beautiful HTML pages from UI element JSON data."""
    
    # Confidence threshold for visual indication
    LOW_CONFIDENCE_THRESHOLD = 0.6
    
    # Column thresholds for positioning (24-column grid, 0-indexed)
    LEFT_RIGHT_THRESHOLD = 12  # Center < 12 is left, >= 12 is right
    FULL_WIDTH_SPAN = 16  # Elements spanning > 16 columns are full-width
    
    def __init__(self, json_path: str):
        """Initialize with path to JSON file."""
        self.json_path = Path(json_path)
        self.data = self._load_json()
        self.metadata = self.data.get('metadata', {})
        self.grid_system = self.metadata.get('grid_system', {})
        self.elements = self._parse_elements()
        
    def _load_json(self) -> Dict:
        """Load and parse JSON file."""
        with open(self.json_path, 'r') as f:
            return json.load(f)
    
    def _parse_elements(self) -> List[Dict]:
        """Parse and sort elements by vertical position (top to bottom), then horizontal."""
        elements = self.data.get('elements', [])
        # Sort by row position (top to bottom), then column (left to right)
        return sorted(elements, key=lambda x: (
            x['grid_position']['start_row'],
            x['grid_position']['start_col']
        ))
    
    def _is_low_confidence(self, element: Dict) -> bool:
        """Check if element has low confidence."""
        return element.get('confidence', 1.0) < self.LOW_CONFIDENCE_THRESHOLD
    
    def _calculate_element_center_col(self, element: Dict) -> float:
        """Calculate the center column position of an element."""
        grid_pos = element['grid_position']
        return (grid_pos['start_col'] + grid_pos['end_col']) / 2
    
    def _get_element_colspan(self, element: Dict) -> int:
        """Get the column span of an element."""
        return element['grid_position']['colspan']
    
    def _classify_element_position(self, element: Dict) -> str:
        """
        Classify element as 'left', 'right', or 'center' based on position.
        
        Rules:
        - CENTER: spans > 16 columns OR spans across both halves significantly
        - LEFT: center column < 12
        - RIGHT: center column >= 12
        """
        center_col = self._calculate_element_center_col(element)
        colspan = self._get_element_colspan(element)
        grid_pos = element['grid_position']
        
        # Check if it's a full-width/center element
        if colspan > self.FULL_WIDTH_SPAN:
            return 'center'
        
        # Check if it spans across both halves (crosses the 11-12 boundary significantly)
        start_col = grid_pos['start_col']
        end_col = grid_pos['end_col']
        
        # If it starts in left half and ends in right half with significant overlap
        if start_col < self.LEFT_RIGHT_THRESHOLD and end_col >= self.LEFT_RIGHT_THRESHOLD:
            # If it spans at least 4 columns on each side, consider it center
            left_span = self.LEFT_RIGHT_THRESHOLD - start_col
            right_span = end_col - self.LEFT_RIGHT_THRESHOLD
            if left_span >= 4 and right_span >= 4:
                return 'center'
        
        # Otherwise, classify by center position
        if center_col < self.LEFT_RIGHT_THRESHOLD:
            return 'left'
        else:
            return 'right'
    
    def _group_elements_by_rows(self) -> List[List[Dict]]:
        """
        Group elements into row groups based on vertical proximity.
        Elements in similar row ranges are grouped together.
        """
        if not self.elements:
            return []
        
        row_groups = []
        current_group = []
        last_end_row = -1
        
        for element in self.elements:
            start_row = element['grid_position']['start_row']
            end_row = element['grid_position']['end_row']
            
            # If this element starts within or close to the last element's range, group it
            # Gap threshold: if there's more than 2 rows gap, start new group
            if current_group and start_row > last_end_row + 2:
                row_groups.append(current_group)
                current_group = []
            
            current_group.append(element)
            last_end_row = max(last_end_row, end_row)
        
        # Add the last group
        if current_group:
            row_groups.append(current_group)
        
        return row_groups
    
    def _get_gradient_colors(self) -> tuple:
        """Return primary gradient colors."""
        return ('#667eea', '#764ba2')
    
    def _generate_css(self) -> str:
        """Generate comprehensive CSS styling."""
        color1, color2 = self._get_gradient_colors()
        
        return f"""* {{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}}

body {{
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background: linear-gradient(135deg, {color1} 0%, {color2} 100%);
    min-height: 100vh;
    padding: 20px;
    line-height: 1.6;
}}

.container {{
    max-width: 1400px;
    margin: 0 auto;
    background: white;
    border-radius: 20px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
    overflow: hidden;
}}

/* Low Confidence Indicator */
.low-confidence {{
    position: relative;
    border: 2px dashed #ff9800 !important;
    background: repeating-linear-gradient(
        45deg,
        transparent,
        transparent 10px,
        rgba(255, 152, 0, 0.05) 10px,
        rgba(255, 152, 0, 0.05) 20px
    ) !important;
}}

.low-confidence::before {{
    content: "⚠ Low Confidence Detection";
    position: absolute;
    top: -12px;
    left: 10px;
    background: #ff9800;
    color: white;
    padding: 2px 10px;
    font-size: 11px;
    border-radius: 10px;
    font-weight: 600;
    z-index: 10;
}}

.low-confidence-inline {{
    border: 2px dashed #ff9800 !important;
    background-color: rgba(255, 152, 0, 0.08) !important;
    box-shadow: 0 0 10px rgba(255, 152, 0, 0.2) !important;
}}

/* Navbar Styling */
.navbar {{
    background: linear-gradient(90deg, {color1} 0%, {color2} 100%);
    padding: 30px 40px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}}

.navbar.low-confidence {{
    background: linear-gradient(90deg, #ff9800 0%, #f57c00 100%);
}}

.navbar h1 {{
    color: white;
    font-size: 28px;
    font-weight: 600;
}}

.nav-links {{
    display: flex;
    gap: 30px;
}}

.nav-links a {{
    color: white;
    text-decoration: none;
    font-size: 16px;
    transition: opacity 0.3s;
}}

.nav-links a:hover {{
    opacity: 0.8;
}}

/* Row Section - wraps each row group */
.row-section {{
    padding: 40px;
}}

.row-section:nth-child(even) {{
    background: #f8f9fa;
}}

/* Two Column Layout */
.two-column-layout {{
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 40px;
    align-items: start;
}}

.column-left {{
    display: flex;
    flex-direction: column;
    gap: 25px;
}}

.column-right {{
    display: flex;
    flex-direction: column;
    gap: 25px;
}}

/* Full Width Layout */
.full-width-layout {{
    display: flex;
    flex-direction: column;
    gap: 25px;
}}

/* Button Styling */
.button {{
    background: linear-gradient(135deg, {color1} 0%, {color2} 100%);
    color: white;
    border: none;
    padding: 18px 36px;
    font-size: 16px;
    font-weight: 600;
    border-radius: 12px;
    cursor: pointer;
    transition: all 0.3s;
    box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
    align-self: flex-start;
}}

.button:hover {{
    transform: translateY(-2px);
    box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
}}

.button:active {{
    transform: translateY(0);
}}

/* Checkbox Styling */
.checkbox-group {{
    background: #f8f9fa;
    padding: 25px;
    border-radius: 12px;
    border: 2px solid #e0e0e0;
}}

.checkbox-item {{
    display: flex;
    align-items: center;
    gap: 15px;
    margin-bottom: 15px;
}}

.checkbox-item:last-child {{
    margin-bottom: 0;
}}

input[type="checkbox"] {{
    width: 24px;
    height: 24px;
    cursor: pointer;
    accent-color: {color1};
}}

.checkbox-item label {{
    font-size: 16px;
    color: #333;
    cursor: pointer;
}}

/* Image Styling */
.image-container {{
    background: linear-gradient(135deg, {color1}22 0%, {color2}33 100%);
    border-radius: 12px;
    padding: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 250px;
    border: 2px dashed {color1};
}}

.image-placeholder {{
    text-align: center;
    color: {color1};
}}

.image-placeholder svg {{
    width: 100px;
    height: 100px;
    margin-bottom: 20px;
    opacity: 0.6;
}}

.image-placeholder p {{
    font-size: 18px;
    font-weight: 500;
}}

/* Text Styling */
.text-block {{
    background: white;
    padding: 25px;
    border-radius: 12px;
    border-left: 4px solid {color1};
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}}

.text-block h3 {{
    color: #333;
    margin-bottom: 10px;
    font-size: 20px;
}}

.text-block p {{
    color: #666;
    line-height: 1.6;
    font-size: 15px;
}}

/* Textfield Styling */
.textfield {{
    width: 100%;
    padding: 18px 24px;
    font-size: 16px;
    border: 2px solid #e0e0e0;
    border-radius: 12px;
    transition: all 0.3s;
}}

.textfield:focus {{
    outline: none;
    border-color: {color1};
    box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}}

/* Paragraph Styling */
.paragraph {{
    color: #555;
    line-height: 1.8;
    font-size: 16px;
    padding: 25px;
    background: white;
    border-radius: 12px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}}

.paragraph h2 {{
    color: #333;
    margin-bottom: 20px;
    font-size: 26px;
    border-bottom: 3px solid {color1};
    padding-bottom: 15px;
}}

.paragraph p {{
    margin-bottom: 15px;
}}

/* Responsive Design */
@media (max-width: 1024px) {{
    .two-column-layout {{
        grid-template-columns: 1fr;
    }}
}}

@media (max-width: 768px) {{
    .navbar {{
        flex-direction: column;
        gap: 20px;
        text-align: center;
    }}
    
    .nav-links {{
        flex-direction: column;
        gap: 15px;
    }}
    
    .row-section {{
        padding: 20px;
    }}
    
    .two-column-layout {{
        gap: 20px;
    }}
}}"""
    
    def _generate_navbar_html(self, element: Dict) -> str:
        """Generate navbar HTML."""
        confidence_class = ' low-confidence' if self._is_low_confidence(element) else ''
        confidence = element.get('confidence', 1.0)
        
        return f"""<nav class="navbar{confidence_class}">
    <h1>Modern Dashboard</h1>
    <div class="nav-links">
        <a href="#home">Home</a>
        <a href="#features">Features</a>
        <a href="#about">About</a>
        <a href="#contact">Contact</a>
    </div>
</nav>
<!-- Detection confidence: {confidence:.1%} -->"""
    
    def _generate_button_html(self, element: Dict) -> str:
        """Generate button HTML."""
        confidence_class = ' low-confidence-inline' if self._is_low_confidence(element) else ''
        return f'<button class="button{confidence_class}">Get Started</button>'
    
    def _generate_checkbox_html(self, element: Dict) -> str:
        """Generate checkbox group HTML."""
        confidence_class = ' low-confidence' if self._is_low_confidence(element) else ''
        
        return f"""<div class="checkbox-group{confidence_class}">
    <div class="checkbox-item">
        <input type="checkbox" id="option1_{element.get('id', '0')}" checked>
        <label for="option1_{element.get('id', '0')}">Enable notifications</label>
    </div>
    <div class="checkbox-item">
        <input type="checkbox" id="option2_{element.get('id', '0')}">
        <label for="option2_{element.get('id', '0')}">Auto-save changes</label>
    </div>
    <div class="checkbox-item">
        <input type="checkbox" id="option3_{element.get('id', '0')}" checked>
        <label for="option3_{element.get('id', '0')}">Dark mode</label>
    </div>
</div>"""
    
    def _generate_image_html(self, element: Dict) -> str:
        """Generate image placeholder HTML."""
        confidence_class = ' low-confidence' if self._is_low_confidence(element) else ''
        
        return f"""<div class="image-container{confidence_class}">
    <div class="image-placeholder">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
            <circle cx="8.5" cy="8.5" r="1.5"/>
            <polyline points="21 15 16 10 5 21"/>
        </svg>
        <p>Image Placeholder</p>
    </div>
</div>"""
    
    def _generate_text_html(self, element: Dict) -> str:
        """Generate text block HTML."""
        confidence_class = ' low-confidence' if self._is_low_confidence(element) else ''
        
        return f"""<div class="text-block{confidence_class}">
    <h3>Quick Tip</h3>
    <p>Use the search bar above to quickly find what you're looking for. All features are just a click away!</p>
</div>"""
    
    def _generate_textfield_html(self, element: Dict) -> str:
        """Generate textfield HTML."""
        confidence_class = ' low-confidence-inline' if self._is_low_confidence(element) else ''
        return f'<input type="text" class="textfield{confidence_class}" placeholder="Type Anything you want...">'
    
    def _generate_paragraph_html(self, element: Dict) -> str:
        """Generate paragraph HTML."""
        confidence_class = ' low-confidence' if self._is_low_confidence(element) else ''
        
        return f"""<div class="paragraph{confidence_class}">
    <h2>Welcome to Your Dashboard</h2>
    <p>This modern interface brings together all the tools you need in one beautiful, cohesive design. Our goal is to make your workflow as smooth and efficient as possible.</p>
    <p>With intuitive navigation and powerful features at your fingertips, you can focus on what matters most. Whether you're managing projects, analyzing data, or collaborating with your team, everything is designed to work seamlessly together.</p>
    <p>The responsive layout adapts to any screen size, ensuring you have a consistent experience across all your devices. Customize your settings using the options on the left to make this space truly yours.</p>
    <p>We've carefully crafted every element to provide both beauty and functionality. The clean design reduces clutter while maintaining all the power you need to be productive.</p>
    <p>Start exploring the features available to you, and discover how this platform can transform the way you work. If you need any help, our comprehensive documentation and support team are always available.</p>
</div>"""
    
    def _generate_element_html(self, element: Dict) -> str:
        """Generate HTML for a single element based on its type."""
        element_type = element['type'].lower()
        
        generators = {
            'navbar': self._generate_navbar_html,
            'button': self._generate_button_html,
            'checkbox': self._generate_checkbox_html,
            'image': self._generate_image_html,
            'text': self._generate_text_html,
            'textfield': self._generate_textfield_html,
            'paragraph': self._generate_paragraph_html
        }
        
        generator = generators.get(element_type)
        if generator:
            return generator(element)
        
        # Default fallback
        confidence_class = ' low-confidence' if self._is_low_confidence(element) else ''
        return f'<div class="default-element{confidence_class}">Element: {element_type}</div>'
    
    def _organize_layout(self) -> Dict[str, Any]:
        """
        Organize elements into layout sections.
        
        Returns:
            Dict with 'header' (navbar elements) and 'row_groups' (list of row sections)
        """
        layout = {
            'header': [],
            'row_groups': []
        }
        
        # Separate navbar elements from others
        non_navbar_elements = []
        for element in self.elements:
            if element['type'].lower() == 'navbar':
                layout['header'].append(element)
            else:
                non_navbar_elements.append(element)
        
        # Group non-navbar elements by rows
        if non_navbar_elements:
            # Temporarily set elements to non-navbar for grouping
            original_elements = self.elements
            self.elements = non_navbar_elements
            row_groups = self._group_elements_by_rows()
            self.elements = original_elements
            
            # Classify each row group
            for group in row_groups:
                left_elements = []
                right_elements = []
                center_elements = []
                
                for element in group:
                    position = self._classify_element_position(element)
                    if position == 'left':
                        left_elements.append(element)
                    elif position == 'right':
                        right_elements.append(element)
                    else:  # center
                        center_elements.append(element)
                
                layout['row_groups'].append({
                    'left': left_elements,
                    'right': right_elements,
                    'center': center_elements
                })
        
        return layout
    
    def _generate_row_section_html(self, row_data: Dict) -> str:
        """Generate HTML for a single row section."""
        left_elements = row_data['left']
        right_elements = row_data['right']
        center_elements = row_data['center']
        
        html_parts = []
        
        # If there are center elements, render them in full-width layout
        if center_elements:
            html_parts.append('<div class="full-width-layout">')
            for element in center_elements:
                html_parts.append(self._generate_element_html(element))
            html_parts.append('</div>')
        
        # If there are left or right elements, use two-column layout
        if left_elements or right_elements:
            html_parts.append('<div class="two-column-layout">')
            
            # Left column
            html_parts.append('<div class="column-left">')
            for element in left_elements:
                html_parts.append(self._generate_element_html(element))
            html_parts.append('</div>')
            
            # Right column
            html_parts.append('<div class="column-right">')
            for element in right_elements:
                html_parts.append(self._generate_element_html(element))
            html_parts.append('</div>')
            
            html_parts.append('</div>')
        
        return '\n'.join(html_parts)
    
    def _generate_body_html(self) -> str:
        """Generate the body HTML content using flexible row-based layout."""
        layout = self._organize_layout()
        
        html_parts = ['<div class="container">']
        
        # Header section (navbar only)
        for element in layout['header']:
            html_parts.append(self._generate_element_html(element))
        
        # Row sections
        for i, row_data in enumerate(layout['row_groups']):
            html_parts.append(f'<div class="row-section" data-row-group="{i}">')
            html_parts.append(self._generate_row_section_html(row_data))
            html_parts.append('</div>')
        
        html_parts.append('</div>')  # Close container
        
        return '\n'.join(html_parts)
    
    def generate_html(self, output_path: str = 'output.html'):
        """Generate complete HTML file."""
        css = self._generate_css()
        body = self._generate_body_html()
        
        # Count low confidence elements
        low_conf_count = sum(1 for e in self.elements if self._is_low_confidence(e))
        
        # Get layout stats
        layout = self._organize_layout()
        total_row_groups = len(layout['row_groups'])
        
        html_template = f"""<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Generated UI Layout</title>
    <style>
{css}
    </style>
</head>
<body>
{body}
    
    <!-- Generation Info -->
    <!-- Total elements: {len(self.elements)} -->
    <!-- Low confidence elements: {low_conf_count} -->
    <!-- Row groups: {total_row_groups} -->
    <!-- Grid: {self.grid_system.get('columns', 'N/A')}x{self.grid_system.get('rows', 'N/A')} -->
</body>
</html>"""
        
        # Write to file
        output_file = Path(output_path)
        output_file.write_text(html_template, encoding='utf-8')
        
        print(f"✓ HTML file generated successfully: {output_file.absolute()}")
        print(f"✓ Total elements processed: {len(self.elements)}")
        print(f"✓ Element types: {', '.join(set(e['type'] for e in self.elements))}")
        print(f"✓ Row groups created: {total_row_groups}")
        print(f"✓ Low confidence elements: {low_conf_count}/{len(self.elements)}")
        if low_conf_count > 0:
            print(f"  ⚠ Elements with confidence < {self.LOW_CONFIDENCE_THRESHOLD:.0%} are marked with orange dashed borders")
        
        return html_template


def main():
    """Main function to run the generator."""
    import sys
    
    # Check if JSON file path is provided
    if len(sys.argv) < 2:
        print("Usage: python CodingModule.py <json_file_path> [output_html_path]")
        print("Example: python CodingModule.py wireframe_output.json output.html")
        sys.exit(1)
    
    json_path = sys.argv[1]
    output_path = sys.argv[2] if len(sys.argv) > 2 else 'output.html'
    
    # Validate JSON file exists
    if not Path(json_path).exists():
        print(f"Error: JSON file not found: {json_path}")
        sys.exit(1)
    
    try:
        # Generate HTML
        generator = HTMLGenerator(json_path)
        generator.generate_html(output_path)
        
    except json.JSONDecodeError as e:
        print(f"Error: Invalid JSON file - {e}")
        sys.exit(1)
    except Exception as e:
        print(f"Error: {e}")
        sys.exit(1)


if __name__ == "__main__":
    main()