mgbam commited on
Commit
46cc050
·
verified ·
1 Parent(s): 59c1021

Update code_processing.py

Browse files
Files changed (1) hide show
  1. code_processing.py +644 -855
code_processing.py CHANGED
@@ -1,907 +1,696 @@
1
- """
2
- Advanced UI components and interface management for the AnyCoder application.
3
- """
4
-
5
- import os
6
- import gradio as gr
7
- import uuid
8
- import webbrowser
9
- import urllib.parse
10
- from typing import Dict, List, Optional, Tuple, Any, Union
11
  from pathlib import Path
12
 
13
- from config import (
14
- THEME_CONFIGS, DEMO_LIST, AVAILABLE_MODELS, DEFAULT_MODEL, DEFAULT_MODEL_NAME,
15
- get_saved_theme, save_theme_preference, get_gradio_language
16
- )
17
- from utils import extract_text_from_file, process_image_for_model
18
- from web_utils import check_hf_space_url, parse_repo_or_model_url
19
- from code_processing import (
20
- parse_transformers_js_output, format_transformers_js_output,
21
- parse_svelte_output, format_svelte_output,
22
- parse_multipage_html_output, format_multipage_output, validate_and_autofix_files,
23
- inline_multipage_into_single_preview
24
- )
25
 
26
- class AdvancedUIManager:
27
- """Manages advanced UI components and interactions"""
28
-
29
- def __init__(self):
30
- self.current_theme = get_saved_theme()
31
- self.demo_categories = self._organize_demos_by_category()
32
- self.model_categories = self._organize_models_by_category()
33
-
34
- def _organize_demos_by_category(self) -> Dict[str, List[Dict]]:
35
- """Organize demo examples by category for better UI"""
36
- categories = {}
37
- for demo in DEMO_LIST:
38
- category = demo.get('category', 'General')
39
- if category not in categories:
40
- categories[category] = []
41
- categories[category].append(demo)
42
- return categories
43
 
44
- def _organize_models_by_category(self) -> Dict[str, List[Dict]]:
45
- """Organize models by category for advanced selection UI"""
46
- categories = {}
47
- for model in AVAILABLE_MODELS:
48
- category = model.get('category', 'General')
49
- if category not in categories:
50
- categories[category] = []
51
- categories[category].append(model)
52
- return categories
53
 
54
- def create_advanced_model_selector(self) -> gr.Group:
55
- """Create an advanced model selector with categories and features"""
56
- with gr.Group() as model_group:
57
- gr.Markdown("### 🤖 AI Model Selection", elem_classes=["model-header"])
58
-
59
- with gr.Accordion("Model Categories", open=False, elem_classes=["model-accordion"]):
60
- model_tabs = gr.Tabs()
61
-
62
- with model_tabs:
63
- for category, models in self.model_categories.items():
64
- with gr.Tab(category, elem_classes=["model-tab"]):
65
- for model in models:
66
- vision_icon = "👁️" if model.get('supports_vision', False) else ""
67
- model_btn = gr.Button(
68
- f"{vision_icon} {model['name']}",
69
- variant="secondary",
70
- elem_classes=["model-button"],
71
- size="sm"
72
- )
73
- gr.Markdown(
74
- f"*{model['description']}*",
75
- elem_classes=["model-description"]
76
- )
77
-
78
- model_dropdown = gr.Dropdown(
79
- choices=[model['name'] for model in AVAILABLE_MODELS],
80
- value=DEFAULT_MODEL_NAME,
81
- label="Selected Model",
82
- elem_classes=["model-dropdown"],
83
- container=True,
84
- scale=2
85
- )
86
-
87
- with gr.Row():
88
- model_info_btn = gr.Button("ℹ️", size="sm", elem_classes=["info-button"])
89
- model_refresh_btn = gr.Button("🔄", size="sm", elem_classes=["refresh-button"])
90
-
91
- return model_group, model_dropdown, model_info_btn, model_refresh_btn
92
 
93
- def create_advanced_demo_gallery(self) -> gr.Group:
94
- """Create an advanced demo gallery with categories and search"""
95
- with gr.Group() as demo_group:
96
- gr.Markdown("### 🎯 Project Templates", elem_classes=["demo-header"])
97
-
98
- demo_search = gr.Textbox(
99
- placeholder="Search templates...",
100
- label="",
101
- elem_classes=["demo-search"],
102
- container=False
103
- )
104
-
105
- demo_filter = gr.Dropdown(
106
- choices=["All Categories"] + list(self.demo_categories.keys()),
107
- value="All Categories",
108
- label="Category Filter",
109
- elem_classes=["demo-filter"]
110
- )
111
-
112
- with gr.Group(elem_classes=["demo-gallery"]):
113
- demo_cards = []
114
- for category, demos in self.demo_categories.items():
115
- with gr.Group(visible=True, elem_classes=[f"demo-category-{category.lower().replace(' ', '-')}"]):
116
- gr.Markdown(f"#### {category}", elem_classes=["category-header"])
117
-
118
- with gr.Row():
119
- for demo in demos:
120
- complexity_color = {
121
- "Beginner": "success",
122
- "Intermediate": "secondary",
123
- "Advanced": "stop"
124
- }.get(demo.get('complexity', 'Intermediate'), 'secondary')
125
-
126
- demo_card = gr.Button(
127
- f"**{demo['title']}**\n{demo['description'][:100]}...",
128
- variant=complexity_color,
129
- elem_classes=["demo-card"],
130
- size="lg"
131
- )
132
- demo_cards.append((demo_card, demo))
133
-
134
- return demo_group, demo_cards, demo_search, demo_filter
135
 
136
- def create_advanced_media_controls(self) -> gr.Group:
137
- """Create advanced media generation controls with smart layouts"""
138
- with gr.Group() as media_group:
139
- gr.Markdown("### 🎨 AI Media Generation", elem_classes=["media-header"])
140
-
141
- with gr.Accordion("Media Options", open=False, elem_classes=["media-accordion"]):
142
- with gr.Tabs() as media_tabs:
143
- # Text to Image Tab
144
- with gr.Tab("🖼️ Text → Image", elem_classes=["media-tab"]):
145
- text_to_image_toggle = gr.Checkbox(
146
- label="Generate Images from Text",
147
- value=False,
148
- elem_classes=["media-toggle"]
149
- )
150
- text_to_image_prompt = gr.Textbox(
151
- label="Image Description",
152
- placeholder="A modern, minimalist dashboard with clean lines and soft colors",
153
- lines=2,
154
- visible=False,
155
- elem_classes=["media-prompt"]
156
- )
157
-
158
- with gr.Row(visible=False) as t2i_advanced:
159
- t2i_style = gr.Dropdown(
160
- choices=["Photorealistic", "Illustration", "Digital Art", "Sketch", "Abstract"],
161
- value="Digital Art",
162
- label="Style"
163
- )
164
- t2i_quality = gr.Slider(
165
- minimum=0.5,
166
- maximum=1.0,
167
- value=0.9,
168
- step=0.1,
169
- label="Quality"
170
- )
171
-
172
- # Image to Image Tab
173
- with gr.Tab("🎭 Image → Image", elem_classes=["media-tab"]):
174
- image_to_image_toggle = gr.Checkbox(
175
- label="Transform Uploaded Images",
176
- value=False,
177
- elem_classes=["media-toggle"]
178
- )
179
- generation_image_input = gr.Image(
180
- label="Source Image",
181
- visible=False,
182
- elem_classes=["media-image"]
183
- )
184
- image_to_image_prompt = gr.Textbox(
185
- label="Transformation Description",
186
- placeholder="Transform this into a futuristic version",
187
- lines=2,
188
- visible=False,
189
- elem_classes=["media-prompt"]
190
- )
191
-
192
- # Image to Video Tab
193
- with gr.Tab("🎬 Image → Video", elem_classes=["media-tab"]):
194
- image_to_video_toggle = gr.Checkbox(
195
- label="Generate Video from Image",
196
- value=False,
197
- elem_classes=["media-toggle"]
198
- )
199
- image_to_video_prompt = gr.Textbox(
200
- label="Motion Description",
201
- placeholder="The person starts walking forward with a confident stride",
202
- lines=2,
203
- visible=False,
204
- elem_classes=["media-prompt"]
205
- )
206
-
207
- # Text to Video Tab
208
- with gr.Tab("📹 Text → Video", elem_classes=["media-tab"]):
209
- text_to_video_toggle = gr.Checkbox(
210
- label="Generate Video from Text",
211
- value=False,
212
- elem_classes=["media-toggle"]
213
- )
214
- text_to_video_prompt = gr.Textbox(
215
- label="Video Description",
216
- placeholder="A time-lapse of a city skyline transitioning from day to night",
217
- lines=2,
218
- visible=False,
219
- elem_classes=["media-prompt"]
220
- )
221
-
222
- # Text to Music Tab
223
- with gr.Tab("🎵 Text → Music", elem_classes=["media-tab"]):
224
- text_to_music_toggle = gr.Checkbox(
225
- label="Generate Background Music",
226
- value=False,
227
- elem_classes=["media-toggle"]
228
- )
229
- text_to_music_prompt = gr.Textbox(
230
- label="Music Description",
231
- placeholder="Upbeat electronic background music for a tech demo",
232
- lines=2,
233
- visible=False,
234
- elem_classes=["media-prompt"]
235
- )
236
-
237
- with gr.Row(visible=False) as music_controls:
238
- music_duration = gr.Slider(
239
- minimum=10,
240
- maximum=120,
241
- value=30,
242
- step=5,
243
- label="Duration (seconds)"
244
- )
245
- music_style = gr.Dropdown(
246
- choices=["Electronic", "Orchestral", "Ambient", "Upbeat", "Cinematic"],
247
- value="Electronic",
248
- label="Style"
249
- )
250
-
251
- # Create visibility toggle handlers
252
- media_components = {
253
- 'text_to_image_toggle': text_to_image_toggle,
254
- 'text_to_image_prompt': text_to_image_prompt,
255
- 't2i_advanced': t2i_advanced,
256
- 'image_to_image_toggle': image_to_image_toggle,
257
- 'generation_image_input': generation_image_input,
258
- 'image_to_image_prompt': image_to_image_prompt,
259
- 'image_to_video_toggle': image_to_video_toggle,
260
- 'image_to_video_prompt': image_to_video_prompt,
261
- 'text_to_video_toggle': text_to_video_toggle,
262
- 'text_to_video_prompt': text_to_video_prompt,
263
- 'text_to_music_toggle': text_to_music_toggle,
264
- 'text_to_music_prompt': text_to_music_prompt,
265
- 'music_controls': music_controls,
266
  }
267
 
268
- return media_group, media_components
269
-
270
- def create_advanced_code_editor(self) -> gr.Group:
271
- """Create advanced code editor with multi-file support and intelligent features"""
272
- with gr.Group() as editor_group:
273
- gr.Markdown("### 💻 Generated Code", elem_classes=["editor-header"])
274
-
275
- with gr.Row():
276
- with gr.Column(scale=3):
277
- # Main code editor
278
- code_output = gr.Code(
279
- language="html",
280
- lines=25,
281
- interactive=True,
282
- label="Generated Code",
283
- elem_classes=["main-editor"]
284
- )
285
-
286
- # Multi-file editors (initially hidden)
287
- with gr.Group(visible=False, elem_classes=["multi-file-group"]) as transformers_group:
288
- gr.Markdown("#### Transformers.js Files")
289
- with gr.Tabs():
290
- with gr.Tab("index.html"):
291
- tjs_html_code = gr.Code(language="html", lines=20, interactive=True)
292
- with gr.Tab("index.js"):
293
- tjs_js_code = gr.Code(language="javascript", lines=20, interactive=True)
294
- with gr.Tab("style.css"):
295
- tjs_css_code = gr.Code(language="css", lines=20, interactive=True)
296
-
297
- with gr.Group(visible=False, elem_classes=["multi-file-group"]) as svelte_group:
298
- gr.Markdown("#### Svelte Files")
299
- with gr.Tabs():
300
- with gr.Tab("App.svelte"):
301
- svelte_app_code = gr.Code(language="javascript", lines=20, interactive=True)
302
- with gr.Tab("app.css"):
303
- svelte_css_code = gr.Code(language="css", lines=20, interactive=True)
304
-
305
- with gr.Column(scale=1, elem_classes=["editor-sidebar"]):
306
- # Code actions
307
- with gr.Group(elem_classes=["code-actions"]):
308
- gr.Markdown("**Actions**")
309
-
310
- code_format_btn = gr.Button("🎨 Format Code", size="sm")
311
- code_download_btn = gr.Button("💾 Download", size="sm")
312
- code_share_btn = gr.Button("🔗 Share", size="sm")
313
- code_deploy_btn = gr.Button("🚀 Deploy", size="sm", variant="primary")
314
-
315
- # Code statistics
316
- with gr.Group(elem_classes=["code-stats"]):
317
- gr.Markdown("**Statistics**")
318
- code_stats = gr.JSON(
319
- value={"lines": 0, "characters": 0, "files": 1},
320
- label="",
321
- elem_classes=["stats-display"]
322
- )
323
-
324
- editor_components = {
325
- 'code_output': code_output,
326
- 'transformers_group': transformers_group,
327
- 'tjs_html_code': tjs_html_code,
328
- 'tjs_js_code': tjs_js_code,
329
- 'tjs_css_code': tjs_css_code,
330
- 'svelte_group': svelte_group,
331
- 'svelte_app_code': svelte_app_code,
332
- 'svelte_css_code': svelte_css_code,
333
- 'code_format_btn': code_format_btn,
334
- 'code_download_btn': code_download_btn,
335
- 'code_share_btn': code_share_btn,
336
- 'code_deploy_btn': code_deploy_btn,
337
- 'code_stats': code_stats
338
- }
339
 
340
- return editor_group, editor_components
341
 
342
- def create_advanced_preview_panel(self) -> gr.Group:
343
- """Create advanced preview panel with multiple view modes"""
344
- with gr.Group() as preview_group:
345
- gr.Markdown("### 👁️ Live Preview", elem_classes=["preview-header"])
346
-
347
- with gr.Row():
348
- preview_mode = gr.Radio(
349
- choices=["Desktop", "Tablet", "Mobile"],
350
- value="Desktop",
351
- label="View Mode",
352
- elem_classes=["preview-mode"]
353
- )
354
-
355
- preview_refresh_btn = gr.Button("🔄", size="sm", elem_classes=["preview-refresh"])
356
- preview_fullscreen_btn = gr.Button("⛶", size="sm", elem_classes=["preview-fullscreen"])
357
- preview_qr_btn = gr.Button("📱", size="sm", elem_classes=["preview-qr"])
358
-
359
- # Main preview area
360
- sandbox = gr.HTML(
361
- label="Live Preview",
362
- elem_classes=["preview-sandbox"]
363
- )
364
-
365
- # Preview status bar
366
- with gr.Row(elem_classes=["preview-status"]):
367
- preview_status = gr.Markdown("Ready", elem_classes=["status-text"])
368
- preview_metrics = gr.JSON(
369
- value={"load_time": "0ms", "size": "0KB", "elements": 0},
370
- label="",
371
- elem_classes=["preview-metrics"]
372
- )
373
-
374
- preview_components = {
375
- 'sandbox': sandbox,
376
- 'preview_mode': preview_mode,
377
- 'preview_refresh_btn': preview_refresh_btn,
378
- 'preview_fullscreen_btn': preview_fullscreen_btn,
379
- 'preview_qr_btn': preview_qr_btn,
380
- 'preview_status': preview_status,
381
- 'preview_metrics': preview_metrics
382
- }
383
-
384
- return preview_group, preview_components
385
 
386
- def create_chat_interface(self) -> gr.Group:
387
- """Create advanced chat-based interface for beta mode"""
388
- with gr.Group() as chat_group:
389
- gr.Markdown("### 💬 AI Assistant Chat", elem_classes=["chat-header"])
390
-
391
- # Main chat area
392
- chatbot = gr.Chatbot(
393
- type="messages",
394
- show_label=False,
395
- height=400,
396
- layout="bubble",
397
- bubble_full_width=False,
398
- group_consecutive_messages=True,
399
- elem_classes=["main-chatbot"],
400
- avatar_images=("🧑‍💻", "🤖")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
  )
402
-
403
- # Chat input with advanced features
404
- with gr.Row():
405
- chat_input = gr.MultimodalTextbox(
406
- placeholder="Describe what you want to build... (supports text, images, and files)",
407
- submit_btn=True,
408
- stop_btn=False,
409
- show_label=False,
410
- sources=["upload", "microphone"],
411
- elem_classes=["chat-input"],
412
- scale=4
413
- )
414
-
415
- chat_voice_btn = gr.Button("🎤", size="sm", elem_classes=["voice-button"])
416
-
417
- # Chat controls
418
- with gr.Row():
419
- chat_clear_btn = gr.ClearButton([chat_input, chatbot], size="sm")
420
- chat_export_btn = gr.Button("📤 Export Chat", size="sm")
421
- chat_settings_btn = gr.Button("⚙️", size="sm")
422
-
423
- # Quick actions
424
- with gr.Row(elem_classes=["chat-quick-actions"]):
425
- quick_actions = [
426
- ("🌐 Website", "Create a modern website"),
427
- ("📱 App", "Build a mobile app"),
428
- ("📊 Dashboard", "Design a data dashboard"),
429
- ("🎮 Game", "Create an interactive game")
430
- ]
431
-
432
- quick_buttons = []
433
- for icon_text, prompt in quick_actions:
434
- btn = gr.Button(icon_text, size="sm", elem_classes=["quick-action"])
435
- quick_buttons.append((btn, prompt))
436
-
437
- chat_components = {
438
- 'chatbot': chatbot,
439
- 'chat_input': chat_input,
440
- 'chat_voice_btn': chat_voice_btn,
441
- 'chat_clear_btn': chat_clear_btn,
442
- 'chat_export_btn': chat_export_btn,
443
- 'chat_settings_btn': chat_settings_btn,
444
- 'quick_buttons': quick_buttons
445
- }
446
 
447
- return chat_group, chat_components
448
-
449
- def create_advanced_sidebar(self) -> gr.Column:
450
- """Create advanced sidebar with intelligent organization"""
451
- with gr.Column(elem_classes=["advanced-sidebar"]) as sidebar:
452
- # Header with user info
453
- with gr.Group(elem_classes=["sidebar-header"]):
454
- login_button = gr.LoginButton(elem_classes=["login-btn"])
455
-
456
- with gr.Row():
457
- beta_toggle = gr.Checkbox(
458
- value=False,
459
- label="🧪 Beta Chat Mode",
460
- info="Try our new conversational interface",
461
- elem_classes=["beta-toggle"]
462
- )
463
-
464
- # Chat interface (hidden by default)
465
- chat_group, chat_components = self.create_chat_interface()
466
- chat_group.visible = False
 
 
 
 
 
467
 
468
- # Classic controls
469
- with gr.Group(elem_classes=["classic-controls"]) as classic_group:
470
- # Project input section
471
- with gr.Group(elem_classes=["input-section"]):
472
- gr.Markdown("### 🎯 Project Description")
473
- input_textbox = gr.Textbox(
474
- label="What would you like to build?",
475
- placeholder="Describe your application in detail...",
476
- lines=3,
477
- elem_classes=["main-input"]
478
- )
479
-
480
- # Advanced model selector
481
- model_group, model_dropdown, model_info_btn, model_refresh_btn = self.create_advanced_model_selector()
482
-
483
- # Language and framework selection
484
- with gr.Group(elem_classes=["language-section"]):
485
- gr.Markdown("### ⚙️ Technology Stack")
486
-
487
- language_dropdown = gr.Dropdown(
488
- choices=[
489
- "HTML/CSS/JS", "React", "Vue", "Svelte", "Streamlit",
490
- "Gradio", "Python", "Transformers.js", "Node.js", "FastAPI"
491
- ],
492
- value="HTML/CSS/JS",
493
- label="Framework",
494
- elem_classes=["language-dropdown"]
495
- )
496
-
497
- with gr.Row():
498
- responsive_toggle = gr.Checkbox(
499
- label="📱 Responsive Design",
500
- value=True,
501
- elem_classes=["feature-toggle"]
502
- )
503
- dark_mode_toggle = gr.Checkbox(
504
- label="🌙 Dark Mode Support",
505
- value=False,
506
- elem_classes=["feature-toggle"]
507
- )
508
-
509
- # File inputs section
510
- with gr.Group(elem_classes=["file-section"]):
511
- gr.Markdown("### 📂 Input Sources")
512
-
513
- file_input = gr.File(
514
- label="Reference Files",
515
- file_types=[".pdf", ".txt", ".md", ".csv", ".docx", ".jpg", ".png"],
516
- elem_classes=["file-input"]
517
- )
518
-
519
- website_url_input = gr.Textbox(
520
- label="Website to Redesign",
521
- placeholder="https://example.com",
522
- elem_classes=["url-input"]
523
- )
524
-
525
- image_input = gr.Image(
526
- label="UI Design Reference",
527
- visible=False,
528
- elem_classes=["image-input"]
529
- )
530
-
531
- # Media generation controls
532
- media_group, media_components = self.create_advanced_media_controls()
533
-
534
- # Advanced options
535
- with gr.Accordion("🔧 Advanced Options", open=False, elem_classes=["advanced-options"]):
536
- search_toggle = gr.Checkbox(
537
- label="🌐 Enable Web Search",
538
- value=False,
539
- info="Use real-time web search for latest information"
540
- )
541
-
542
- optimization_level = gr.Slider(
543
- minimum=1,
544
- maximum=5,
545
- value=3,
546
- step=1,
547
- label="Code Optimization Level",
548
- info="Higher values produce more optimized code"
549
- )
550
-
551
- accessibility_toggle = gr.Checkbox(
552
- label="♿ Accessibility Focus",
553
- value=True,
554
- info="Prioritize accessibility best practices"
555
- )
556
-
557
- # Generation controls
558
- with gr.Row(elem_classes=["generation-controls"]):
559
- generate_btn = gr.Button(
560
- "✨ Generate Code",
561
- variant="primary",
562
- size="lg",
563
- elem_classes=["generate-button"]
564
- )
565
-
566
- with gr.Column(scale=1):
567
- clear_btn = gr.Button("🗑️", size="sm", elem_classes=["clear-button"])
568
- stop_btn = gr.Button("⏹️", size="sm", elem_classes=["stop-button"])
569
-
570
- # Demo gallery
571
- demo_group, demo_cards, demo_search, demo_filter = self.create_advanced_demo_gallery()
572
-
573
- # Project import section
574
- with gr.Group(elem_classes=["import-section"]):
575
- gr.Markdown("### 📥 Import Project")
576
-
577
- import_url = gr.Textbox(
578
- label="Project URL",
579
- placeholder="GitHub, HuggingFace Space, or Model URL",
580
- elem_classes=["import-input"]
581
- )
582
-
583
- with gr.Row():
584
- import_btn = gr.Button("Import", size="sm")
585
- import_status = gr.Markdown("", visible=False)
586
-
587
- # Deployment section
588
- with gr.Group(elem_classes=["deploy-section"], visible=False) as deploy_group:
589
- gr.Markdown("### 🚀 Deploy Application")
590
-
591
- space_name_input = gr.Textbox(
592
- label="App Name",
593
- placeholder="my-awesome-app",
594
- elem_classes=["space-name-input"]
595
- )
596
-
597
- sdk_dropdown = gr.Dropdown(
598
- choices=["Static HTML", "Gradio", "Streamlit", "Transformers.js", "Svelte"],
599
- value="Static HTML",
600
- label="Deployment Type",
601
- elem_classes=["sdk-dropdown"]
602
- )
603
-
604
- deploy_btn = gr.Button(
605
- "🚀 Deploy to HuggingFace",
606
- variant="primary",
607
- elem_classes=["deploy-button"]
608
- )
609
-
610
- deploy_status = gr.Markdown("", visible=False, elem_classes=["deploy-status"])
611
-
612
- # Collect all components for return
613
- sidebar_components = {
614
- 'login_button': login_button,
615
- 'beta_toggle': beta_toggle,
616
- 'chat_group': chat_group,
617
- 'chat_components': chat_components,
618
- 'classic_group': classic_group,
619
- 'input_textbox': input_textbox,
620
- 'model_dropdown': model_dropdown,
621
- 'model_info_btn': model_info_btn,
622
- 'model_refresh_btn': model_refresh_btn,
623
- 'language_dropdown': language_dropdown,
624
- 'responsive_toggle': responsive_toggle,
625
- 'dark_mode_toggle': dark_mode_toggle,
626
- 'file_input': file_input,
627
- 'website_url_input': website_url_input,
628
- 'image_input': image_input,
629
- 'media_components': media_components,
630
- 'search_toggle': search_toggle,
631
- 'optimization_level': optimization_level,
632
- 'accessibility_toggle': accessibility_toggle,
633
- 'generate_btn': generate_btn,
634
- 'clear_btn': clear_btn,
635
- 'stop_btn': stop_btn,
636
- 'demo_cards': demo_cards,
637
- 'demo_search': demo_search,
638
- 'demo_filter': demo_filter,
639
- 'import_url': import_url,
640
- 'import_btn': import_btn,
641
- 'import_status': import_status,
642
- 'deploy_group': deploy_group,
643
- 'space_name_input': space_name_input,
644
- 'sdk_dropdown': sdk_dropdown,
645
- 'deploy_btn': deploy_btn,
646
- 'deploy_status': deploy_status
647
- }
648
 
649
- return sidebar, sidebar_components
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
650
 
651
- class ThemeManager:
652
- """Manages application themes and styling"""
653
 
654
  @staticmethod
655
- def get_custom_css() -> str:
656
- """Get custom CSS for advanced styling"""
657
- return """
658
- /* Advanced AnyCoder Styling */
659
-
660
- .advanced-sidebar {
661
- background: linear-gradient(145deg, #f8fafc 0%, #f1f5f9 100%);
662
- border-radius: 12px;
663
- padding: 16px;
664
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
665
  }
666
 
667
- .sidebar-header {
668
- background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
669
- color: white;
670
- padding: 12px;
671
- border-radius: 8px;
672
- margin-bottom: 16px;
673
- }
674
-
675
- .main-input {
676
- border-radius: 8px;
677
- border: 2px solid #e5e7eb;
678
- transition: border-color 0.2s ease;
679
- }
680
-
681
- .main-input:focus {
682
- border-color: #3b82f6;
683
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
684
- }
685
 
686
- .generate-button {
687
- background: linear-gradient(135deg, #10b981 0%, #059669 100%);
688
- border: none;
689
- border-radius: 8px;
690
- font-weight: 600;
691
- text-transform: uppercase;
692
- letter-spacing: 0.5px;
693
- transition: all 0.2s ease;
694
- }
695
 
696
- .generate-button:hover {
697
- transform: translateY(-1px);
698
- box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
699
- }
700
 
701
- .demo-card {
702
- background: white;
703
- border: 1px solid #e5e7eb;
704
- border-radius: 8px;
705
- padding: 12px;
706
- transition: all 0.2s ease;
707
- text-align: left;
708
- min-height: 100px;
709
- }
710
 
711
- .demo-card:hover {
712
- transform: translateY(-2px);
713
- box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
714
- border-color: #3b82f6;
715
- }
 
716
 
717
- .model-button {
718
- background: white;
719
- border: 1px solid #d1d5db;
720
- border-radius: 6px;
721
- margin: 4px 0;
722
- transition: all 0.2s ease;
723
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
724
 
725
- .model-button:hover {
726
- background: #f9fafb;
727
- border-color: #3b82f6;
728
- }
729
 
730
- .code-actions {
731
- background: #f8fafc;
732
- border-radius: 8px;
733
- padding: 12px;
734
- border: 1px solid #e5e7eb;
735
- }
736
 
737
- .preview-sandbox {
738
- border: 2px solid #e5e7eb;
739
- border-radius: 8px;
740
- overflow: hidden;
741
- }
 
742
 
743
- .chat-input {
744
- border-radius: 20px;
745
- border: 2px solid #e5e7eb;
746
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
747
 
748
- .main-chatbot {
749
- border: 1px solid #e5e7eb;
750
- border-radius: 12px;
751
- background: #fafafa;
752
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
753
 
754
- .media-toggle {
755
- color: #374151;
756
- font-weight: 500;
757
- }
 
 
 
 
758
 
759
- .media-prompt {
760
- border-radius: 6px;
761
- border: 1px solid #d1d5db;
762
- }
763
 
764
- .quick-action {
765
- background: linear-gradient(135deg, #f3f4f6 0%, #e5e7eb 100%);
766
- border: 1px solid #d1d5db;
767
- border-radius: 6px;
768
- font-size: 12px;
769
- transition: all 0.2s ease;
770
- }
771
 
772
- .quick-action:hover {
773
- background: linear-gradient(135deg, #e5e7eb 0%, #d1d5db 100%);
774
- transform: translateY(-1px);
775
- }
776
 
777
- .feature-toggle {
778
- font-size: 13px;
779
- color: #6b7280;
780
- }
 
 
781
 
782
- .stats-display {
783
- font-size: 11px;
784
- background: #f9fafb;
785
- border-radius: 4px;
786
- padding: 8px;
787
- }
788
 
789
- .preview-metrics {
790
- font-size: 10px;
791
- color: #6b7280;
792
- }
793
 
794
- .category-header {
795
- color: #374151;
796
- font-weight: 600;
797
- margin: 8px 0;
798
- padding-bottom: 4px;
799
- border-bottom: 1px solid #e5e7eb;
800
- }
801
-
802
- .deploy-button {
803
- background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);
804
- color: white;
805
- font-weight: 600;
806
- }
807
-
808
- .beta-toggle {
809
- background: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%);
810
- color: white;
811
- padding: 4px 8px;
812
- border-radius: 6px;
813
- font-size: 12px;
814
- }
815
- """
816
 
817
  @staticmethod
818
- def apply_theme_updates(theme_name: str) -> Dict[str, Any]:
819
- """Apply theme and return UI updates"""
820
- if theme_name in THEME_CONFIGS:
821
- save_theme_preference(theme_name)
822
 
823
- restart_message = f"""
824
- 🎨 **Theme Applied:** {theme_name}
 
825
 
826
- **{THEME_CONFIGS[theme_name]['description']}**
827
 
828
- Your theme preference has been saved. Some advanced styling may require a restart to fully apply.
829
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
830
 
831
- return {
832
- 'theme_status': gr.update(value=restart_message, visible=True),
833
- 'current_theme': theme_name
834
- }
 
 
 
 
 
835
 
836
- return {'theme_status': gr.update(visible=False), 'current_theme': theme_name}
837
 
838
- class DeploymentManager:
839
- """Manages application deployment to various platforms"""
840
 
841
  @staticmethod
842
- def create_deployment_options() -> Dict[str, Any]:
843
- """Create deployment options for different platforms"""
844
- platforms = {
845
- 'huggingface': {
846
- 'name': 'HuggingFace Spaces',
847
- 'icon': '🤗',
848
- 'description': 'Deploy to HuggingFace Spaces with automatic hosting',
849
- 'sdks': ['Static', 'Gradio', 'Streamlit', 'Docker']
850
- },
851
- 'netlify': {
852
- 'name': 'Netlify',
853
- 'icon': '🌐',
854
- 'description': 'Deploy static sites with continuous deployment',
855
- 'sdks': ['Static']
856
- },
857
- 'vercel': {
858
- 'name': 'Vercel',
859
- 'icon': '▲',
860
- 'description': 'Deploy with edge functions and optimized performance',
861
- 'sdks': ['Static', 'Next.js']
862
- }
863
- }
864
-
865
- return platforms
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
866
 
867
  @staticmethod
868
- def prepare_deployment_files(code: str, language: str, framework: str) -> Dict[str, str]:
869
- """Prepare files for deployment based on the framework"""
870
- files = {}
871
-
872
- if language == "transformers.js":
873
- # Parse and prepare transformers.js files
874
- parsed_files = parse_transformers_js_output(code)
875
- files.update(parsed_files)
876
-
877
- elif language == "svelte":
878
- # Parse and prepare Svelte files
879
- parsed_files = parse_svelte_output(code)
880
- files.update(parsed_files)
881
-
882
- elif language == "html":
883
- # Handle multi-page or single HTML
884
- parsed_files = parse_multipage_html_output(code)
885
- if parsed_files:
886
- files.update(validate_and_autofix_files(parsed_files))
887
- else:
888
- files['index.html'] = code
889
-
890
- else:
891
- # Single file deployment
892
- extension_map = {
893
- 'python': '.py',
894
- 'javascript': '.js',
895
- 'typescript': '.ts',
896
- 'css': '.css'
897
- }
898
-
899
- filename = f"app{extension_map.get(language, '.txt')}"
900
- files[filename] = code
901
-
902
- return files
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
903
 
904
- # Global UI manager instance
905
- ui_manager = AdvancedUIManager()
906
- theme_manager = ThemeManager()
907
- deployment_manager = DeploymentManager()
 
1
+ import re
2
+ import base64
3
+ import json
4
+ from typing import Dict, List, Optional, Tuple, Union
 
 
 
 
 
 
5
  from pathlib import Path
6
 
7
+ from utils import apply_search_replace_changes, validate_video_html
8
+ from media_generation import generate_image_with_qwen, generate_image_to_image, generate_video_from_image, generate_video_from_text, generate_music_from_text
9
+ from config import SEARCH_START, DIVIDER, REPLACE_END
 
 
 
 
 
 
 
 
 
10
 
11
+ class CodeProcessor:
12
+ """Handles processing and transformation of various code formats"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
+ @staticmethod
15
+ def is_streamlit_code(code: str) -> bool:
16
+ """Check if Python code is a Streamlit app"""
17
+ if not code:
18
+ return False
19
+ lowered = code.lower()
20
+ return ("import streamlit" in lowered) or ("from streamlit" in lowered) or ("st." in code and "streamlit" in lowered)
 
 
21
 
22
+ @staticmethod
23
+ def is_gradio_code(code: str) -> bool:
24
+ """Check if Python code is a Gradio app"""
25
+ if not code:
26
+ return False
27
+ lowered = code.lower()
28
+ return (
29
+ "import gradio" in lowered or
30
+ "from gradio" in lowered or
31
+ "gr.Interface(" in code or
32
+ "gr.Blocks(" in code
33
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
+ @staticmethod
36
+ def extract_html_document(text: str) -> str:
37
+ """Extract HTML document from text, ignoring planning notes"""
38
+ if not text:
39
+ return text
40
+ lower = text.lower()
41
+ idx = lower.find("<!doctype html")
42
+ if idx == -1:
43
+ idx = lower.find("<html")
44
+ return text[idx:] if idx != -1 else text
45
+
46
+ class TransformersJSProcessor:
47
+ """Handles Transformers.js specific code processing"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
+ @staticmethod
50
+ def parse_transformers_js_output(text: str) -> Dict[str, str]:
51
+ """Parse transformers.js output and extract the three files"""
52
+ files = {
53
+ 'index.html': '',
54
+ 'index.js': '',
55
+ 'style.css': ''
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  }
57
 
58
+ if not text:
59
+ return files
60
+
61
+ # Multiple patterns for different code block variations
62
+ html_patterns = [
63
+ r'```html\s*\n([\s\S]*?)(?:```|\Z)',
64
+ r'```htm\s*\n([\s\S]*?)(?:```|\Z)',
65
+ r'```\s*(?:index\.html|html)\s*\n([\s\S]*?)(?:```|\Z)'
66
+ ]
67
+
68
+ js_patterns = [
69
+ r'```javascript\s*\n([\s\S]*?)(?:```|\Z)',
70
+ r'```js\s*\n([\s\S]*?)(?:```|\Z)',
71
+ r'```\s*(?:index\.js|javascript|js)\s*\n([\s\S]*?)(?:```|\Z)'
72
+ ]
73
+
74
+ css_patterns = [
75
+ r'```css\s*\n([\s\S]*?)(?:```|\Z)',
76
+ r'```\s*(?:style\.css|css)\s*\n([\s\S]*?)(?:```|\Z)'
77
+ ]
78
+
79
+ # Extract content using patterns
80
+ for pattern in html_patterns:
81
+ html_match = re.search(pattern, text, re.IGNORECASE)
82
+ if html_match:
83
+ files['index.html'] = html_match.group(1).strip()
84
+ break
85
+
86
+ for pattern in js_patterns:
87
+ js_match = re.search(pattern, text, re.IGNORECASE)
88
+ if js_match:
89
+ files['index.js'] = js_match.group(1).strip()
90
+ break
91
+
92
+ for pattern in css_patterns:
93
+ css_match = re.search(pattern, text, re.IGNORECASE)
94
+ if css_match:
95
+ files['style.css'] = css_match.group(1).strip()
96
+ break
97
+
98
+ # Fallback: support === filename === format
99
+ if not (files['index.html'] and files['index.js'] and files['style.css']):
100
+ fallback_files = MultipageProcessor.parse_multipage_html_output(text)
101
+ for key in files.keys():
102
+ if key in fallback_files:
103
+ files[key] = fallback_files[key]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
+ return files
106
 
107
+ @staticmethod
108
+ def format_transformers_js_output(files: Dict[str, str]) -> str:
109
+ """Format the three files into a single display string"""
110
+ output = []
111
+ output.append("=== index.html ===")
112
+ output.append(files.get('index.html', ''))
113
+ output.append("\n=== index.js ===")
114
+ output.append(files.get('index.js', ''))
115
+ output.append("\n=== style.css ===")
116
+ output.append(files.get('style.css', ''))
117
+ return '\n'.join(output)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
 
119
+ @staticmethod
120
+ def build_transformers_inline_html(files: Dict[str, str]) -> str:
121
+ """Merge transformers.js files into a single HTML document"""
122
+ html = files.get('index.html') or ''
123
+ js = files.get('index.js') or ''
124
+ css = files.get('style.css') or ''
125
+
126
+ # Normalize JS imports to stable CDN
127
+ cdn_url = "https://cdn.jsdelivr.net/npm/@huggingface/[email protected]"
128
+
129
+ def _normalize_imports(_code: str) -> str:
130
+ if not _code:
131
+ return _code or ""
132
+ _code = re.sub(r"from\s+['\"]@huggingface/transformers['\"]", f"from '{cdn_url}'", _code)
133
+ _code = re.sub(r"from\s+['\"]@xenova/transformers['\"]", f"from '{cdn_url}'", _code)
134
+ _code = re.sub(r"from\s+['\"]https://cdn.jsdelivr.net/npm/@huggingface/transformers@[^'\"]+['\"]", f"from '{cdn_url}'", _code)
135
+ _code = re.sub(r"from\s+['\"]https://cdn.jsdelivr.net/npm/@xenova/transformers@[^'\"]+['\"]", f"from '{cdn_url}'", _code)
136
+ return _code
137
+
138
+ # Extract and merge inline module scripts
139
+ inline_modules = []
140
+ try:
141
+ for _m in re.finditer(r"<script\b[^>]*type=[\"']module[\"'][^>]*>([\s\S]*?)</script>", html, flags=re.IGNORECASE):
142
+ inline_modules.append(_m.group(1))
143
+ if inline_modules:
144
+ html = re.sub(r"<script\b[^>]*type=[\"']module[\"'][^>]*>[\s\S]*?</script>\s*", "", html, flags=re.IGNORECASE)
145
+ except Exception:
146
+ pass
147
+
148
+ # Combine JS code
149
+ combined_js_parts = []
150
+ if inline_modules:
151
+ combined_js_parts.append("\n\n".join(inline_modules))
152
+ if js:
153
+ combined_js_parts.append(js)
154
+ js = "\n\n".join([p for p in combined_js_parts if (p and p.strip())])
155
+ js = _normalize_imports(js)
156
+
157
+ # Add prelude for better compatibility
158
+ if js.strip():
159
+ prelude = (
160
+ f"import {{ env }} from '{cdn_url}';\n"
161
+ "try { env.useBrowserCache = false; } catch (e) {}\n"
162
+ "try { if (env && env.backends && env.backends.onnx && env.backends.onnx.wasm) { env.backends.onnx.wasm.numThreads = 1; env.backends.onnx.wasm.proxy = false; } } catch (e) {}\n"
163
+ f"(async () => {{ try {{ if (typeof globalThis.transformers === 'undefined') {{ const m = await import('{cdn_url}'); globalThis.transformers = m; }} }} catch (e) {{}} }})();\n"
164
+ )
165
+ js = prelude + js
166
+
167
+ # Create minimal shell if needed
168
+ doc = html.strip()
169
+ if not doc or ('<html' not in doc.lower()):
170
+ doc = (
171
+ "<!DOCTYPE html>\n"
172
+ "<html>\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Transformers.js App</title>\n</head>\n"
173
+ "<body>\n<div id=\"app\"></div>\n</body>\n</html>"
174
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
 
176
+ # Remove local file references
177
+ doc = re.sub(r"<link[^>]+href=\"[^\"]*style\.css\"[^>]*>\s*", "", doc, flags=re.IGNORECASE)
178
+ doc = re.sub(r"<script[^>]+src=\"[^\"]*index\.js\"[^>]*>\s*</script>\s*", "", doc, flags=re.IGNORECASE)
179
+
180
+ # Inline CSS
181
+ if css:
182
+ style_tag = f"<style>\n{css}\n</style>"
183
+ if '</head>' in doc.lower():
184
+ match = re.search(r"</head>", doc, flags=re.IGNORECASE)
185
+ if match:
186
+ idx = match.start()
187
+ doc = doc[:idx] + style_tag + doc[idx:]
188
+ else:
189
+ match = re.search(r"<body[^>]*>", doc, flags=re.IGNORECASE)
190
+ if match:
191
+ idx = match.end()
192
+ doc = doc[:idx] + "\n" + style_tag + doc[idx:]
193
+ else:
194
+ doc = style_tag + doc
195
+
196
+ # Inline JS with debugging and cleanup
197
+ if js:
198
+ script_tag = f"<script type=\"module\">\n{js}\n</script>"
199
+ debug_overlay = TransformersJSProcessor._create_debug_overlay()
200
+ cleanup_tag = TransformersJSProcessor._create_cleanup_script()
201
 
202
+ match = re.search(r"</body>", doc, flags=re.IGNORECASE)
203
+ if match:
204
+ idx = match.start()
205
+ doc = doc[:idx] + debug_overlay + script_tag + cleanup_tag + doc[idx:]
206
+ else:
207
+ doc = doc + debug_overlay + script_tag + cleanup_tag
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
 
209
+ return doc
210
+
211
+ @staticmethod
212
+ def _create_debug_overlay() -> str:
213
+ """Create debug overlay for transformers.js apps"""
214
+ return (
215
+ "<style>\n"
216
+ "#anycoder-debug{position:fixed;left:0;right:0;bottom:0;max-height:45%;overflow:auto;"
217
+ "background:rgba(0,0,0,.85);color:#9eff9e;padding:.5em;font:12px/1.4 monospace;z-index:2147483647;display:none}"
218
+ "#anycoder-debug pre{margin:0;white-space:pre-wrap;word-break:break-word}"
219
+ "</style>\n"
220
+ "<div id=\"anycoder-debug\"></div>\n"
221
+ "<script>\n"
222
+ "(function(){\n"
223
+ " const el = document.getElementById('anycoder-debug');\n"
224
+ " function show(){ if(el && el.style.display!=='block'){ el.style.display='block'; } }\n"
225
+ " function log(msg){ try{ show(); const pre=document.createElement('pre'); pre.textContent=msg; el.appendChild(pre);}catch(e){} }\n"
226
+ " const origError = console.error.bind(console);\n"
227
+ " console.error = function(){ origError.apply(console, arguments); try{ log('console.error: ' + Array.from(arguments).map(a=>{try{return (typeof a==='string')?a:JSON.stringify(a);}catch(e){return String(a);}}).join(' ')); }catch(e){} };\n"
228
+ " window.addEventListener('error', e => { log('window.onerror: ' + (e && e.message ? e.message : 'Unknown error')); });\n"
229
+ " window.addEventListener('unhandledrejection', e => { try{ const r=e && e.reason; log('unhandledrejection: ' + (r && (r.message || JSON.stringify(r)))); }catch(err){ log('unhandledrejection'); } });\n"
230
+ "})();\n"
231
+ "</script>"
232
+ )
233
+
234
+ @staticmethod
235
+ def _create_cleanup_script() -> str:
236
+ """Create cleanup script for transformers.js apps"""
237
+ return (
238
+ "<script>\n"
239
+ "(function(){\n"
240
+ " function cleanup(){\n"
241
+ " try { if (window.caches && caches.keys) { caches.keys().then(keys => keys.forEach(k => caches.delete(k))); } } catch(e){}\n"
242
+ " try { if (window.indexedDB && indexedDB.databases) { indexedDB.databases().then(dbs => dbs.forEach(db => db && db.name && indexedDB.deleteDatabase(db.name))); } } catch(e){}\n"
243
+ " }\n"
244
+ " window.addEventListener('pagehide', cleanup, { once: true });\n"
245
+ " window.addEventListener('beforeunload', cleanup, { once: true });\n"
246
+ "})();\n"
247
+ "</script>"
248
+ )
249
 
250
+ class SvelteProcessor:
251
+ """Handles Svelte specific code processing"""
252
 
253
  @staticmethod
254
+ def parse_svelte_output(text: str) -> Dict[str, str]:
255
+ """Parse Svelte output to extract individual files"""
256
+ files = {
257
+ 'src/App.svelte': '',
258
+ 'src/app.css': ''
 
 
 
 
 
259
  }
260
 
261
+ if not text:
262
+ return files
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
 
264
+ # Extract using code block patterns
265
+ svelte_pattern = r'```svelte\s*\n([\s\S]+?)\n```'
266
+ css_pattern = r'```css\s*\n([\s\S]+?)\n```'
 
 
 
 
 
 
267
 
268
+ svelte_match = re.search(svelte_pattern, text, re.IGNORECASE)
269
+ css_match = re.search(css_pattern, text, re.IGNORECASE)
 
 
270
 
271
+ if svelte_match:
272
+ files['src/App.svelte'] = svelte_match.group(1).strip()
273
+ if css_match:
274
+ files['src/app.css'] = css_match.group(1).strip()
 
 
 
 
 
275
 
276
+ # Fallback: support === filename === format
277
+ if not (files['src/App.svelte'] and files['src/app.css']):
278
+ fallback_files = MultipageProcessor.parse_multipage_html_output(text)
279
+ for key in files.keys():
280
+ if key in fallback_files:
281
+ files[key] = fallback_files[key]
282
 
283
+ return files
284
+
285
+ @staticmethod
286
+ def format_svelte_output(files: Dict[str, str]) -> str:
287
+ """Format Svelte files into a single display string"""
288
+ output = []
289
+ output.append("=== src/App.svelte ===")
290
+ output.append(files.get('src/App.svelte', ''))
291
+ output.append("\n=== src/app.css ===")
292
+ output.append(files.get('src/app.css', ''))
293
+ return '\n'.join(output)
294
+
295
+ class MultipageProcessor:
296
+ """Handles multi-page HTML projects"""
297
+
298
+ @staticmethod
299
+ def parse_multipage_html_output(text: str) -> Dict[str, str]:
300
+ """Parse multi-page HTML output formatted as === filename === sections"""
301
+ if not text:
302
+ return {}
303
 
304
+ from utils import remove_code_block
305
+ cleaned = remove_code_block(text)
306
+ files: Dict[str, str] = {}
 
307
 
308
+ pattern = re.compile(r"^===\s*([^=\n]+?)\s*===\s*\n([\s\S]*?)(?=\n===\s*[^=\n]+?\s*===|\Z)", re.MULTILINE)
 
 
 
 
 
309
 
310
+ for m in pattern.finditer(cleaned):
311
+ name = m.group(1).strip()
312
+ content = m.group(2).strip()
313
+ # Remove accidental trailing fences
314
+ content = re.sub(r"^```\w*\s*\n|\n```\s*$", "", content)
315
+ files[name] = content
316
 
317
+ return files
318
+
319
+ @staticmethod
320
+ def format_multipage_output(files: Dict[str, str]) -> str:
321
+ """Format files back into === filename === sections"""
322
+ if not isinstance(files, dict) or not files:
323
+ return ""
324
+
325
+ # Order with index.html first
326
+ ordered_paths = []
327
+ if 'index.html' in files:
328
+ ordered_paths.append('index.html')
329
+ for path in sorted(files.keys()):
330
+ if path == 'index.html':
331
+ continue
332
+ ordered_paths.append(path)
333
+
334
+ parts: List[str] = []
335
+ for path in ordered_paths:
336
+ parts.append(f"=== {path} ===")
337
+ parts.append((files.get(path) or '').rstrip())
338
+
339
+ return "\n".join(parts)
340
+
341
+ @staticmethod
342
+ def validate_and_autofix_files(files: Dict[str, str]) -> Dict[str, str]:
343
+ """Ensure minimal contract for multi-file sites"""
344
+ if not isinstance(files, dict) or not files:
345
+ return files or {}
346
+
347
+ normalized: Dict[str, str] = {}
348
+ for k, v in files.items():
349
+ safe_key = k.strip().lstrip('/')
350
+ normalized[safe_key] = v
351
+
352
+ html_files = [p for p in normalized.keys() if p.lower().endswith('.html')]
353
+ has_index = 'index.html' in normalized
354
+
355
+ # Create index.html if missing but other HTML files exist
356
+ if not has_index and html_files:
357
+ links = '\n'.join([f"<li><a href=\"{p}\">{p}</a></li>" for p in html_files])
358
+ normalized['index.html'] = (
359
+ "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\"/>\n"
360
+ "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n"
361
+ "<title>Site Index</title>\n</head>\n<body>\n<h1>Site</h1>\n<ul>\n"
362
+ + links + "\n</ul>\n</body>\n</html>"
363
+ )
364
 
365
+ # Collect asset references
366
+ asset_refs: set[str] = set()
367
+ patterns = [
368
+ re.compile(r"<link[^>]+href=\"([^\"]+)\""),
369
+ re.compile(r"<script[^>]+src=\"([^\"]+)\""),
370
+ re.compile(r"<img[^>]+src=\"([^\"]+)\""),
371
+ re.compile(r"<a[^>]+href=\"([^\"]+)\"")
372
+ ]
373
+
374
+ for path, content in list(normalized.items()):
375
+ if not path.lower().endswith('.html'):
376
+ continue
377
+ for patt in patterns:
378
+ for m in patt.finditer(content or ""):
379
+ ref = (m.group(1) or "").strip()
380
+ if not ref or ref.startswith(('http://', 'https://', 'data:', '#')):
381
+ continue
382
+ asset_refs.add(ref.lstrip('/'))
383
+
384
+ # Add minimal stubs for missing references
385
+ for ref in list(asset_refs):
386
+ if ref not in normalized:
387
+ if ref.lower().endswith('.css'):
388
+ normalized[ref] = "/* generated stub */\n"
389
+ elif ref.lower().endswith('.js'):
390
+ normalized[ref] = "// generated stub\n"
391
+ elif ref.lower().endswith('.html'):
392
+ normalized[ref] = (
393
+ "<!DOCTYPE html>\n<html lang=\"en\">\n<head><meta charset=\"utf-8\"/><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/><title>Page</title></head>\n"
394
+ "<body><main><h1>Placeholder page</h1><p>This page was auto-created to satisfy an internal link.</p></main></body>\n</html>"
395
+ )
396
 
397
+ return normalized
398
+
399
+ @staticmethod
400
+ def inline_multipage_into_single_preview(files: Dict[str, str]) -> str:
401
+ """Inline local CSS/JS for iframe preview"""
402
+ html = files.get('index.html', '')
403
+ if not html:
404
+ return ""
405
 
406
+ doc = html
 
 
 
407
 
408
+ # Inline CSS links
409
+ def _inline_css(match):
410
+ href = match.group(1)
411
+ if href in files:
412
+ return f"<style>\n{files[href]}\n</style>"
413
+ return match.group(0)
 
414
 
415
+ doc = re.sub(r"<link[^>]+href=\"([^\"]+)\"[^>]*/?>", _inline_css, doc, flags=re.IGNORECASE)
 
 
 
416
 
417
+ # Inline JS scripts
418
+ def _inline_js(match):
419
+ src = match.group(1)
420
+ if src in files:
421
+ return f"<script>\n{files[src]}\n</script>"
422
+ return match.group(0)
423
 
424
+ doc = re.sub(r"<script[^>]+src=\"([^\"]+)\"[^>]*>\s*</script>", _inline_js, doc, flags=re.IGNORECASE)
 
 
 
 
 
425
 
426
+ # Add client-side navigation for other pages
427
+ MultipageProcessor._add_client_side_navigation(doc, files)
 
 
428
 
429
+ return doc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
 
431
  @staticmethod
432
+ def _add_client_side_navigation(doc: str, files: Dict[str, str]) -> str:
433
+ """Add client-side navigation for multi-page preview"""
434
+ try:
435
+ html_pages = {k: v for k, v in files.items() if k.lower().endswith('.html')}
436
 
437
+ # Extract body content for each page
438
+ _index_body = re.search(r"<body[^>]*>([\s\S]*?)</body>", doc, flags=re.IGNORECASE)
439
+ html_pages['index.html'] = _index_body.group(1) if _index_body else doc
440
 
441
+ encoded = base64.b64encode(json.dumps(html_pages).encode('utf-8')).decode('ascii')
442
 
443
+ nav_script = (
444
+ "<script>\n"
445
+ "(function(){\n"
446
+ f" const MP_FILES = JSON.parse(atob('{encoded}'));\n"
447
+ " function extractBody(html){\n"
448
+ " try {\n"
449
+ " const doc = new DOMParser().parseFromString(html, 'text/html');\n"
450
+ " const title = doc.querySelector('title'); if (title) document.title = title.textContent || document.title;\n"
451
+ " return doc.body ? doc.body.innerHTML : html;\n"
452
+ " } catch(e){ return html; }\n"
453
+ " }\n"
454
+ " function loadPage(path){\n"
455
+ " if (!MP_FILES[path]) return false;\n"
456
+ " const bodyHTML = extractBody(MP_FILES[path]);\n"
457
+ " document.body.innerHTML = bodyHTML;\n"
458
+ " attach();\n"
459
+ " try { history.replaceState({}, '', '#'+path); } catch(e){}\n"
460
+ " return true;\n"
461
+ " }\n"
462
+ " function clickHandler(e){\n"
463
+ " const a = e.target && e.target.closest ? e.target.closest('a') : null;\n"
464
+ " if (!a) return;\n"
465
+ " const href = a.getAttribute('href') || '';\n"
466
+ " if (!href || href.startsWith('#') || /^https?:/i.test(href) || href.startsWith('mailto:') || href.startsWith('tel:')) return;\n"
467
+ " const clean = href.split('#')[0].split('?')[0];\n"
468
+ " if (MP_FILES[clean]) { e.preventDefault(); loadPage(clean); }\n"
469
+ " }\n"
470
+ " function attach(){ document.removeEventListener('click', clickHandler, true); document.addEventListener('click', clickHandler, true); }\n"
471
+ " document.addEventListener('DOMContentLoaded', function(){ attach(); const initial = (location.hash||'').slice(1); if (initial && MP_FILES[initial]) loadPage(initial); }, { once:true });\n"
472
+ "})();\n"
473
+ "</script>"
474
+ )
475
 
476
+ m = re.search(r"</body>", doc, flags=re.IGNORECASE)
477
+ if m:
478
+ i = m.start()
479
+ doc = doc[:i] + nav_script + doc[i:]
480
+ else:
481
+ doc = doc + nav_script
482
+
483
+ except Exception:
484
+ pass # Non-fatal in preview
485
 
486
+ return doc
487
 
488
+ class MediaIntegrator:
489
+ """Handles integration of generated media into code"""
490
 
491
  @staticmethod
492
+ def apply_generated_media_to_html(html_content: str, user_prompt: str,
493
+ enable_text_to_image: bool = False,
494
+ enable_image_to_image: bool = False,
495
+ input_image_data=None,
496
+ image_to_image_prompt: Optional[str] = None,
497
+ text_to_image_prompt: Optional[str] = None,
498
+ enable_image_to_video: bool = False,
499
+ image_to_video_prompt: Optional[str] = None,
500
+ session_id: Optional[str] = None,
501
+ enable_text_to_video: bool = False,
502
+ text_to_video_prompt: Optional[str] = None,
503
+ enable_text_to_music: bool = False,
504
+ text_to_music_prompt: Optional[str] = None,
505
+ token=None) -> str:
506
+ """Apply media generation to HTML content"""
507
+
508
+ # Detect multi-page structure
509
+ is_multipage = False
510
+ multipage_files = {}
511
+ entry_html_path = None
512
+
513
+ try:
514
+ multipage_files = MultipageProcessor.parse_multipage_html_output(html_content) or {}
515
+ if multipage_files:
516
+ is_multipage = True
517
+ entry_html_path = 'index.html' if 'index.html' in multipage_files else next((p for p in multipage_files.keys() if p.lower().endswith('.html')), None)
518
+ except Exception:
519
+ pass
520
+
521
+ result = multipage_files.get(entry_html_path, html_content) if is_multipage and entry_html_path else html_content
522
+
523
+ try:
524
+ # Process media generation based on priority
525
+ if enable_image_to_video and input_image_data is not None:
526
+ result = MediaIntegrator._apply_image_to_video(result, user_prompt, image_to_video_prompt, input_image_data, session_id, token)
527
+ elif enable_text_to_video:
528
+ result = MediaIntegrator._apply_text_to_video(result, user_prompt, text_to_video_prompt, session_id, token)
529
+ elif enable_text_to_music:
530
+ result = MediaIntegrator._apply_text_to_music(result, user_prompt, text_to_music_prompt, session_id, token)
531
+ elif enable_image_to_image and input_image_data is not None:
532
+ result = MediaIntegrator._apply_image_to_image(result, user_prompt, image_to_image_prompt, input_image_data, token)
533
+ elif enable_text_to_image:
534
+ result = MediaIntegrator._apply_text_to_image(result, user_prompt, text_to_image_prompt, token)
535
+ except Exception as e:
536
+ print(f"[MediaApply] Error during media generation: {str(e)}")
537
+
538
+ # Return updated content
539
+ if is_multipage and entry_html_path:
540
+ multipage_files[entry_html_path] = result
541
+ return MultipageProcessor.format_multipage_output(multipage_files)
542
+
543
+ return result
544
 
545
  @staticmethod
546
+ def _apply_image_to_video(html_content: str, user_prompt: str, prompt: Optional[str],
547
+ input_image_data, session_id: Optional[str], token) -> str:
548
+ """Apply image-to-video generation"""
549
+ i2v_prompt = (prompt or user_prompt or "").strip()
550
+ print(f"[MediaApply] Applying image-to-video with prompt: {i2v_prompt}")
551
+
552
+ try:
553
+ video_html_tag = generate_video_from_image(input_image_data, i2v_prompt, session_id=session_id, token=token)
554
+ if not video_html_tag.startswith("Error") and validate_video_html(video_html_tag):
555
+ return MediaIntegrator._place_media_in_html(html_content, video_html_tag, "video")
556
+ except Exception as e:
557
+ print(f"[MediaApply] Image-to-video generation failed: {str(e)}")
558
+
559
+ return html_content
560
+
561
+ @staticmethod
562
+ def _apply_text_to_video(html_content: str, user_prompt: str, prompt: Optional[str],
563
+ session_id: Optional[str], token) -> str:
564
+ """Apply text-to-video generation"""
565
+ t2v_prompt = (prompt or user_prompt or "").strip()
566
+ print(f"[MediaApply] Applying text-to-video with prompt: {t2v_prompt}")
567
+
568
+ try:
569
+ video_html_tag = generate_video_from_text(t2v_prompt, session_id=session_id, token=token)
570
+ if not video_html_tag.startswith("Error") and validate_video_html(video_html_tag):
571
+ return MediaIntegrator._place_media_in_html(html_content, video_html_tag, "video")
572
+ except Exception as e:
573
+ print(f"[MediaApply] Text-to-video generation failed: {str(e)}")
574
+
575
+ return html_content
576
+
577
+ @staticmethod
578
+ def _apply_text_to_music(html_content: str, user_prompt: str, prompt: Optional[str],
579
+ session_id: Optional[str], token) -> str:
580
+ """Apply text-to-music generation"""
581
+ t2m_prompt = (prompt or user_prompt or "").strip()
582
+ print(f"[MediaApply] Applying text-to-music with prompt: {t2m_prompt}")
583
+
584
+ try:
585
+ audio_html_tag = generate_music_from_text(t2m_prompt, session_id=session_id, token=token)
586
+ if not audio_html_tag.startswith("Error"):
587
+ return MediaIntegrator._place_media_in_html(html_content, audio_html_tag, "audio")
588
+ except Exception as e:
589
+ print(f"[MediaApply] Text-to-music generation failed: {str(e)}")
590
+
591
+ return html_content
592
+
593
+ @staticmethod
594
+ def _apply_image_to_image(html_content: str, user_prompt: str, prompt: Optional[str],
595
+ input_image_data, token) -> str:
596
+ """Apply image-to-image generation"""
597
+ i2i_prompt = (prompt or user_prompt or "").strip()
598
+ print(f"[MediaApply] Applying image-to-image with prompt: {i2i_prompt}")
599
+
600
+ try:
601
+ image_html_tag = generate_image_to_image(input_image_data, i2i_prompt, token=token)
602
+ if not image_html_tag.startswith("Error"):
603
+ return MediaIntegrator._place_media_in_html(html_content, image_html_tag, "image")
604
+ except Exception as e:
605
+ print(f"[MediaApply] Image-to-image generation failed: {str(e)}")
606
+
607
+ return html_content
608
+
609
+ @staticmethod
610
+ def _apply_text_to_image(html_content: str, user_prompt: str, prompt: Optional[str], token) -> str:
611
+ """Apply text-to-image generation"""
612
+ t2i_prompt = (prompt or user_prompt or "").strip()
613
+ print(f"[MediaApply] Applying text-to-image with prompt: {t2i_prompt}")
614
+
615
+ try:
616
+ image_html_tag = generate_image_with_qwen(t2i_prompt, 0, token=token)
617
+ if not image_html_tag.startswith("Error"):
618
+ return MediaIntegrator._place_media_in_html(html_content, image_html_tag, "image")
619
+ except Exception as e:
620
+ print(f"[MediaApply] Text-to-image generation failed: {str(e)}")
621
+
622
+ return html_content
623
+
624
+ @staticmethod
625
+ def _place_media_in_html(html_content: str, media_html: str, media_type: str) -> str:
626
+ """Place generated media in appropriate location in HTML"""
627
+ # Find good insertion points
628
+ insertion_patterns = [
629
+ r'(<main[^>]*>)',
630
+ r'(<section[^>]*class="[^"]*hero[^"]*"[^>]*>)',
631
+ r'(<div[^>]*class="[^"]*container[^"]*"[^>]*>)',
632
+ r'(<body[^>]*>)'
633
+ ]
634
+
635
+ for pattern in insertion_patterns:
636
+ match = re.search(pattern, html_content, re.IGNORECASE)
637
+ if match:
638
+ insertion_point = match.end()
639
+ container_class = "video-container" if media_type == "video" else f"{media_type}-container"
640
+ media_with_container = f'\n <div class="{container_class}" style="margin: 20px 0; text-align: center;">\n {media_html}\n </div>'
641
+ return html_content[:insertion_point] + media_with_container + html_content[insertion_point:]
642
+
643
+ # Fallback: append before closing body
644
+ body_close = html_content.rfind('</body>')
645
+ if body_close != -1:
646
+ return html_content[:body_close] + f'\n {media_html}\n' + html_content[body_close:]
647
+
648
+ # Last resort: append at end
649
+ return html_content + f'\n{media_html}'
650
+
651
+ # Export main functions and classes
652
+ code_processor = CodeProcessor()
653
+ transformers_processor = TransformersJSProcessor()
654
+ svelte_processor = SvelteProcessor()
655
+ multipage_processor = MultipageProcessor()
656
+ media_integrator = MediaIntegrator()
657
+
658
+ # Main exports
659
+ def is_streamlit_code(code: str) -> bool:
660
+ return code_processor.is_streamlit_code(code)
661
+
662
+ def is_gradio_code(code: str) -> bool:
663
+ return code_processor.is_gradio_code(code)
664
+
665
+ def extract_html_document(text: str) -> str:
666
+ return code_processor.extract_html_document(text)
667
+
668
+ def parse_transformers_js_output(text: str) -> Dict[str, str]:
669
+ return transformers_processor.parse_transformers_js_output(text)
670
+
671
+ def format_transformers_js_output(files: Dict[str, str]) -> str:
672
+ return transformers_processor.format_transformers_js_output(files)
673
+
674
+ def build_transformers_inline_html(files: Dict[str, str]) -> str:
675
+ return transformers_processor.build_transformers_inline_html(files)
676
+
677
+ def parse_svelte_output(text: str) -> Dict[str, str]:
678
+ return svelte_processor.parse_svelte_output(text)
679
+
680
+ def format_svelte_output(files: Dict[str, str]) -> str:
681
+ return svelte_processor.format_svelte_output(files)
682
+
683
+ def parse_multipage_html_output(text: str) -> Dict[str, str]:
684
+ return multipage_processor.parse_multipage_html_output(text)
685
+
686
+ def format_multipage_output(files: Dict[str, str]) -> str:
687
+ return multipage_processor.format_multipage_output(files)
688
+
689
+ def validate_and_autofix_files(files: Dict[str, str]) -> Dict[str, str]:
690
+ return multipage_processor.validate_and_autofix_files(files)
691
+
692
+ def inline_multipage_into_single_preview(files: Dict[str, str]) -> str:
693
+ return multipage_processor.inline_multipage_into_single_preview(files)
694
 
695
+ def apply_generated_media_to_html(html_content: str, user_prompt: str, **kwargs) -> str:
696
+ return media_integrator.apply_generated_media_to_html(html_content, user_prompt, **kwargs)