akhaliq HF Staff commited on
Commit
792ed16
·
1 Parent(s): 275180a

add react support

Browse files
Files changed (1) hide show
  1. app.py +261 -13
app.py CHANGED
@@ -38,7 +38,7 @@ from dashscope.utils.oss_utils import check_and_upload_local
38
 
39
  # Gradio supported languages for syntax highlighting
40
  GRADIO_SUPPORTED_LANGUAGES = [
41
- "python", "json", "html"
42
  ]
43
 
44
  def get_gradio_language(language):
@@ -49,6 +49,8 @@ def get_gradio_language(language):
49
  return "python"
50
  if language == "comfyui":
51
  return "json"
 
 
52
  return language if language in GRADIO_SUPPORTED_LANGUAGES else None
53
 
54
  # Search/Replace Constants
@@ -1863,6 +1865,78 @@ Requirements:
1863
  IMPORTANT: Always include "Built with anycoder" as clickable text in the header/top section of your application that links to https://huggingface.co/spaces/akhaliq/anycoder
1864
  """
1865
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1866
 
1867
  # Gradio system prompts will be dynamically populated by update_gradio_system_prompts()
1868
  GRADIO_SYSTEM_PROMPT = ""
@@ -3068,6 +3142,22 @@ def parse_svelte_output(text):
3068
  results['src/app.css'] = css_match.group(1).strip()
3069
  return results
3070
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3071
  def format_svelte_output(files):
3072
  """Format Svelte files into === filename === sections (generic)."""
3073
  return format_multipage_output(files)
@@ -6213,6 +6303,8 @@ Generate the exact search/replace blocks needed to make these changes."""
6213
  system_prompt = GradioFollowUpSystemPrompt
6214
  elif language == "svelte":
6215
  system_prompt = FollowUpSystemPrompt # Use generic follow-up for Svelte
 
 
6216
  else:
6217
  system_prompt = FollowUpSystemPrompt
6218
  else:
@@ -6224,6 +6316,8 @@ Generate the exact search/replace blocks needed to make these changes."""
6224
  system_prompt = TRANSFORMERS_JS_SYSTEM_PROMPT
6225
  elif language == "svelte":
6226
  system_prompt = SVELTE_SYSTEM_PROMPT
 
 
6227
  elif language == "gradio":
6228
  system_prompt = GRADIO_SYSTEM_PROMPT
6229
  elif language == "streamlit":
@@ -8338,7 +8432,7 @@ with gr.Blocks(
8338
  )
8339
  # Language dropdown for code generation (add Streamlit and Gradio as first-class options)
8340
  language_choices = [
8341
- "html", "gradio", "transformers.js", "streamlit", "comfyui"
8342
  ]
8343
  language_dropdown = gr.Dropdown(
8344
  choices=language_choices,
@@ -8513,6 +8607,28 @@ with gr.Blocks(
8513
  static_code_5_4 = gr.Code(language="html", lines=18, interactive=True, label="file 4")
8514
  with gr.Tab("file 5") as static_tab_5_5:
8515
  static_code_5_5 = gr.Code(language="html", lines=18, interactive=True, label="file 5")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8516
  # Removed Import Logs tab for cleaner UI
8517
  # History tab hidden per user request
8518
  # with gr.Tab("History"):
@@ -8647,7 +8763,60 @@ with gr.Blocks(
8647
  gr.update(value=files.get('index.html', '')),
8648
  gr.update(value=files.get('index.js', '')),
8649
  gr.update(value=files.get('style.css', '')),
 
 
 
 
 
 
 
 
 
 
 
8650
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8651
  else:
8652
  return [
8653
  gr.update(visible=True), # code_output shown
@@ -8655,12 +8824,23 @@ with gr.Blocks(
8655
  gr.update(),
8656
  gr.update(),
8657
  gr.update(),
 
 
 
 
 
 
 
 
 
 
 
8658
  ]
8659
 
8660
  language_dropdown.change(
8661
  toggle_editors,
8662
  inputs=[language_dropdown, code_output],
8663
- outputs=[code_output, tjs_group, tjs_html_code, tjs_js_code, tjs_css_code],
8664
  )
8665
 
8666
  # Toggle Python multi-file editors for Gradio/Streamlit
@@ -9279,6 +9459,7 @@ with gr.Blocks(
9279
  language_to_sdk_map = {
9280
  "gradio": "gradio",
9281
  "streamlit": "docker", # Use 'docker' for Streamlit Spaces
 
9282
  "html": "static",
9283
  "transformers.js": "static", # Transformers.js uses static SDK
9284
  "svelte": "static", # Svelte uses static SDK
@@ -9299,22 +9480,89 @@ with gr.Blocks(
9299
  )
9300
  except Exception as e:
9301
  return gr.update(value=f"Error creating Space: {e}", visible=True)
9302
- # Streamlit/docker logic
9303
- if sdk == "docker":
9304
  try:
9305
  # For new spaces, duplicate the template first
9306
  if not is_update:
9307
- # Use duplicate_space to create a Streamlit template space
9308
  from huggingface_hub import duplicate_space
9309
 
9310
- # Duplicate the streamlit template space
9311
- duplicated_repo = duplicate_space(
9312
- from_id="streamlit/streamlit-template-space",
9313
- to_id=space_name.strip(),
9314
- token=token.token,
9315
- exist_ok=True
9316
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9317
 
 
9318
  # Generate requirements.txt for Streamlit apps and upload only if needed
9319
  import_statements = extract_import_statements(code)
9320
  requirements_content = generate_requirements_txt_with_llm(import_statements)
 
38
 
39
  # Gradio supported languages for syntax highlighting
40
  GRADIO_SUPPORTED_LANGUAGES = [
41
+ "python", "json", "html", "javascript"
42
  ]
43
 
44
  def get_gradio_language(language):
 
49
  return "python"
50
  if language == "comfyui":
51
  return "json"
52
+ if language == "react":
53
+ return "javascript"
54
  return language if language in GRADIO_SUPPORTED_LANGUAGES else None
55
 
56
  # Search/Replace Constants
 
1865
  IMPORTANT: Always include "Built with anycoder" as clickable text in the header/top section of your application that links to https://huggingface.co/spaces/akhaliq/anycoder
1866
  """
1867
 
1868
+ REACT_SYSTEM_PROMPT = """You are an expert React and Next.js developer creating a modern Next.js application.
1869
+
1870
+ **🚨 CRITICAL: DO NOT Generate README.md Files**
1871
+ |- NEVER generate README.md files under any circumstances
1872
+ |- A template README.md is automatically provided and will be overridden by the deployment system
1873
+ |- Generating a README.md will break the deployment process
1874
+
1875
+ You will generate a Next.js project with TypeScript/JSX components. Follow this exact structure:
1876
+
1877
+ Project Structure:
1878
+ - Dockerfile (Docker configuration for deployment)
1879
+ - package.json (dependencies and scripts)
1880
+ - next.config.js (Next.js configuration)
1881
+ - postcss.config.js (PostCSS configuration)
1882
+ - tailwind.config.js (Tailwind CSS configuration)
1883
+ - components/[Component files as needed]
1884
+ - pages/_app.js (Next.js app wrapper)
1885
+ - pages/index.js (home page)
1886
+ - pages/api/[API routes as needed]
1887
+ - styles/globals.css (global styles)
1888
+
1889
+ Output format (CRITICAL):
1890
+ - Return ONLY a series of file sections, each starting with a filename line:
1891
+ === Dockerfile ===
1892
+ ...file content...
1893
+
1894
+ === package.json ===
1895
+ ...file content...
1896
+
1897
+ (repeat for all files)
1898
+ - Do NOT wrap files in Markdown code fences or use === markers inside file content
1899
+
1900
+ CRITICAL Requirements:
1901
+ 1. Always include a Dockerfile configured for Node.js deployment
1902
+ 2. Use Next.js with TypeScript/JSX (.jsx files for components)
1903
+ 3. Include Tailwind CSS for styling (in postcss.config.js and tailwind.config.js)
1904
+ 4. Create necessary components in the components/ directory
1905
+ 5. Create API routes in pages/api/ directory for backend logic
1906
+ 6. pages/_app.js should import and use globals.css
1907
+ 7. pages/index.js should be the main entry point
1908
+ 8. Keep package.json with essential dependencies
1909
+ 9. Use modern React patterns and best practices
1910
+ 10. Make the application fully responsive
1911
+ 11. Include proper error handling and loading states
1912
+ 12. Follow accessibility best practices
1913
+
1914
+ Dockerfile Requirements:
1915
+ - Use Node.js 18+ base image
1916
+ - Install dependencies with npm install
1917
+ - Run "npm run build" to build Next.js app
1918
+ - Expose port 3000
1919
+ - Start with "npm start"
1920
+
1921
+ IMPORTANT: Always include "Built with anycoder" as clickable text in the header/top section of your application that links to https://huggingface.co/spaces/akhaliq/anycoder
1922
+ """
1923
+
1924
+ REACT_FOLLOW_UP_SYSTEM_PROMPT = """You are an expert React and Next.js developer modifying an existing Next.js application.
1925
+ The user wants to apply changes based on their request.
1926
+ You MUST output ONLY the changes required using the following SEARCH/REPLACE block format. Do NOT output the entire file.
1927
+ Explain the changes briefly *before* the blocks if necessary, but the code changes THEMSELVES MUST be within the blocks.
1928
+
1929
+ Format Rules:
1930
+ 1. Start with <<<<<<< SEARCH
1931
+ 2. Include the exact lines that need to be changed (with full context, at least 3 lines before and after)
1932
+ 3. Follow with =======
1933
+ 4. Include the replacement lines
1934
+ 5. End with >>>>>>> REPLACE
1935
+ 6. Generate multiple blocks if multiple sections need changes
1936
+
1937
+ IMPORTANT: Always include "Built with anycoder" as clickable text in the header/top section of your application that links to https://huggingface.co/spaces/akhaliq/anycoder"""
1938
+
1939
+
1940
 
1941
  # Gradio system prompts will be dynamically populated by update_gradio_system_prompts()
1942
  GRADIO_SYSTEM_PROMPT = ""
 
3142
  results['src/app.css'] = css_match.group(1).strip()
3143
  return results
3144
 
3145
+ def parse_react_output(text):
3146
+ """Parse React/Next.js output to extract individual files.
3147
+
3148
+ Supports multi-file sections using === filename === sections.
3149
+ """
3150
+ if not text:
3151
+ return {}
3152
+
3153
+ # Use the generic multipage parser
3154
+ try:
3155
+ files = parse_multipage_html_output(text) or {}
3156
+ except Exception:
3157
+ files = {}
3158
+
3159
+ return files if isinstance(files, dict) and files else {}
3160
+
3161
  def format_svelte_output(files):
3162
  """Format Svelte files into === filename === sections (generic)."""
3163
  return format_multipage_output(files)
 
6303
  system_prompt = GradioFollowUpSystemPrompt
6304
  elif language == "svelte":
6305
  system_prompt = FollowUpSystemPrompt # Use generic follow-up for Svelte
6306
+ elif language == "react":
6307
+ system_prompt = REACT_FOLLOW_UP_SYSTEM_PROMPT
6308
  else:
6309
  system_prompt = FollowUpSystemPrompt
6310
  else:
 
6316
  system_prompt = TRANSFORMERS_JS_SYSTEM_PROMPT
6317
  elif language == "svelte":
6318
  system_prompt = SVELTE_SYSTEM_PROMPT
6319
+ elif language == "react":
6320
+ system_prompt = REACT_SYSTEM_PROMPT
6321
  elif language == "gradio":
6322
  system_prompt = GRADIO_SYSTEM_PROMPT
6323
  elif language == "streamlit":
 
8432
  )
8433
  # Language dropdown for code generation (add Streamlit and Gradio as first-class options)
8434
  language_choices = [
8435
+ "html", "gradio", "transformers.js", "streamlit", "comfyui", "react"
8436
  ]
8437
  language_dropdown = gr.Dropdown(
8438
  choices=language_choices,
 
8607
  static_code_5_4 = gr.Code(language="html", lines=18, interactive=True, label="file 4")
8608
  with gr.Tab("file 5") as static_tab_5_5:
8609
  static_code_5_5 = gr.Code(language="html", lines=18, interactive=True, label="file 5")
8610
+ # React Next.js multi-file editors (hidden by default)
8611
+ with gr.Group(visible=False) as react_group:
8612
+ with gr.Tabs():
8613
+ with gr.Tab("Dockerfile"):
8614
+ react_code_dockerfile = gr.Code(language="bash", lines=15, interactive=True, label="Dockerfile")
8615
+ with gr.Tab("package.json"):
8616
+ react_code_package_json = gr.Code(language="json", lines=20, interactive=True, label="package.json")
8617
+ with gr.Tab("next.config.js"):
8618
+ react_code_next_config = gr.Code(language="javascript", lines=15, interactive=True, label="next.config.js")
8619
+ with gr.Tab("postcss.config.js"):
8620
+ react_code_postcss_config = gr.Code(language="javascript", lines=10, interactive=True, label="postcss.config.js")
8621
+ with gr.Tab("tailwind.config.js"):
8622
+ react_code_tailwind_config = gr.Code(language="javascript", lines=15, interactive=True, label="tailwind.config.js")
8623
+ with gr.Tab("pages/_app.js"):
8624
+ react_code_pages_app = gr.Code(language="javascript", lines=15, interactive=True, label="pages/_app.js")
8625
+ with gr.Tab("pages/index.js"):
8626
+ react_code_pages_index = gr.Code(language="javascript", lines=20, interactive=True, label="pages/index.js")
8627
+ with gr.Tab("components/ChatApp.jsx"):
8628
+ react_code_components = gr.Code(language="javascript", lines=25, interactive=True, label="components/ChatApp.jsx")
8629
+ with gr.Tab("styles/globals.css"):
8630
+ react_code_styles = gr.Code(language="css", lines=20, interactive=True, label="styles/globals.css")
8631
+
8632
  # Removed Import Logs tab for cleaner UI
8633
  # History tab hidden per user request
8634
  # with gr.Tab("History"):
 
8763
  gr.update(value=files.get('index.html', '')),
8764
  gr.update(value=files.get('index.js', '')),
8765
  gr.update(value=files.get('style.css', '')),
8766
+ # React group hidden
8767
+ gr.update(visible=False),
8768
+ gr.update(),
8769
+ gr.update(),
8770
+ gr.update(),
8771
+ gr.update(),
8772
+ gr.update(),
8773
+ gr.update(),
8774
+ gr.update(),
8775
+ gr.update(),
8776
+ gr.update(),
8777
  ]
8778
+ elif language == "react":
8779
+ files = parse_react_output(code_text or "")
8780
+ # Show react group if we have files, else show single code editor
8781
+ editors_visible = True if files else False
8782
+ if editors_visible:
8783
+ return [
8784
+ gr.update(visible=False), # code_output hidden
8785
+ gr.update(visible=False), # tjs_group hidden
8786
+ gr.update(),
8787
+ gr.update(),
8788
+ gr.update(),
8789
+ # React group shown
8790
+ gr.update(visible=editors_visible), # react_group shown
8791
+ gr.update(value=files.get('Dockerfile', '')),
8792
+ gr.update(value=files.get('package.json', '')),
8793
+ gr.update(value=files.get('next.config.js', '')),
8794
+ gr.update(value=files.get('postcss.config.js', '')),
8795
+ gr.update(value=files.get('tailwind.config.js', '')),
8796
+ gr.update(value=files.get('pages/_app.js', '')),
8797
+ gr.update(value=files.get('pages/index.js', '')),
8798
+ gr.update(value=files.get('components/ChatApp.jsx', '')),
8799
+ gr.update(value=files.get('styles/globals.css', '')),
8800
+ ]
8801
+ else:
8802
+ return [
8803
+ gr.update(visible=True), # code_output shown
8804
+ gr.update(visible=False), # tjs_group hidden
8805
+ gr.update(),
8806
+ gr.update(),
8807
+ gr.update(),
8808
+ # React group hidden
8809
+ gr.update(visible=False),
8810
+ gr.update(),
8811
+ gr.update(),
8812
+ gr.update(),
8813
+ gr.update(),
8814
+ gr.update(),
8815
+ gr.update(),
8816
+ gr.update(),
8817
+ gr.update(),
8818
+ gr.update(),
8819
+ ]
8820
  else:
8821
  return [
8822
  gr.update(visible=True), # code_output shown
 
8824
  gr.update(),
8825
  gr.update(),
8826
  gr.update(),
8827
+ # React group hidden
8828
+ gr.update(visible=False),
8829
+ gr.update(),
8830
+ gr.update(),
8831
+ gr.update(),
8832
+ gr.update(),
8833
+ gr.update(),
8834
+ gr.update(),
8835
+ gr.update(),
8836
+ gr.update(),
8837
+ gr.update(),
8838
  ]
8839
 
8840
  language_dropdown.change(
8841
  toggle_editors,
8842
  inputs=[language_dropdown, code_output],
8843
+ outputs=[code_output, tjs_group, tjs_html_code, tjs_js_code, tjs_css_code, react_group, react_code_dockerfile, react_code_package_json, react_code_next_config, react_code_postcss_config, react_code_tailwind_config, react_code_pages_app, react_code_pages_index, react_code_components, react_code_styles],
8844
  )
8845
 
8846
  # Toggle Python multi-file editors for Gradio/Streamlit
 
9459
  language_to_sdk_map = {
9460
  "gradio": "gradio",
9461
  "streamlit": "docker", # Use 'docker' for Streamlit Spaces
9462
+ "react": "docker", # Use 'docker' for React/Next.js Spaces
9463
  "html": "static",
9464
  "transformers.js": "static", # Transformers.js uses static SDK
9465
  "svelte": "static", # Svelte uses static SDK
 
9480
  )
9481
  except Exception as e:
9482
  return gr.update(value=f"Error creating Space: {e}", visible=True)
9483
+ # Streamlit/React/docker logic
9484
+ if sdk == "docker" and language in ["streamlit", "react"]:
9485
  try:
9486
  # For new spaces, duplicate the template first
9487
  if not is_update:
9488
+ # Use duplicate_space to create a Streamlit or React template space
9489
  from huggingface_hub import duplicate_space
9490
 
9491
+ if language == "react":
9492
+ # Duplicate the React template space
9493
+ duplicated_repo = duplicate_space(
9494
+ from_id="akhaliq/next-js-template",
9495
+ to_id=space_name.strip(),
9496
+ token=token.token,
9497
+ exist_ok=True
9498
+ )
9499
+ else:
9500
+ # Duplicate the streamlit template space
9501
+ duplicated_repo = duplicate_space(
9502
+ from_id="streamlit/streamlit-template-space",
9503
+ to_id=space_name.strip(),
9504
+ token=token.token,
9505
+ exist_ok=True
9506
+ )
9507
+
9508
+ # Handle React or Streamlit deployment
9509
+ if language == "react":
9510
+ # Parse React/Next.js files
9511
+ files = parse_react_output(code)
9512
+ if not files:
9513
+ return gr.update(value="Error: Could not parse React output. Please regenerate the code.", visible=True)
9514
+
9515
+ # Upload React files
9516
+ import tempfile
9517
+ import time
9518
+
9519
+ for file_name, file_content in files.items():
9520
+ if not file_content:
9521
+ continue
9522
+
9523
+ success = False
9524
+ last_error = None
9525
+ max_attempts = 3
9526
+
9527
+ for attempt in range(max_attempts):
9528
+ try:
9529
+ with tempfile.NamedTemporaryFile("w", suffix=f".{file_name.split('.')[-1]}", delete=False) as f:
9530
+ f.write(file_content)
9531
+ temp_path = f.name
9532
+
9533
+ api.upload_file(
9534
+ path_or_fileobj=temp_path,
9535
+ path_in_repo=file_name,
9536
+ repo_id=repo_id,
9537
+ repo_type="space"
9538
+ )
9539
+ success = True
9540
+ break
9541
+
9542
+ except Exception as e:
9543
+ last_error = e
9544
+ error_msg = str(e)
9545
+ if "403 Forbidden" in error_msg and "write token" in error_msg:
9546
+ return gr.update(value=f"Error: Permission denied. Please ensure you have write access to {repo_id} and your token has the correct permissions.", visible=True)
9547
+
9548
+ if attempt < max_attempts - 1:
9549
+ time.sleep(2)
9550
+ finally:
9551
+ import os
9552
+ if 'temp_path' in locals():
9553
+ os.unlink(temp_path)
9554
+
9555
+ if not success:
9556
+ return gr.update(value=f"Error uploading {file_name}: {last_error}", visible=True)
9557
+
9558
+ # Add anycoder tag to existing README
9559
+ add_anycoder_tag_to_readme(api, repo_id)
9560
+
9561
+ space_url = f"https://huggingface.co/spaces/{repo_id}"
9562
+ action_text = "Updated" if is_update else "Deployed"
9563
+ return gr.update(value=f"✅ {action_text}! [Open your React Space here]({space_url})", visible=True)
9564
 
9565
+ # Streamlit logic
9566
  # Generate requirements.txt for Streamlit apps and upload only if needed
9567
  import_statements = extract_import_statements(code)
9568
  requirements_content = generate_requirements_txt_with_llm(import_statements)