khulnasoft commited on
Commit
ced0116
·
verified ·
1 Parent(s): a51a732

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +110 -118
app.py CHANGED
@@ -2,28 +2,27 @@ import streamlit as st
2
  import pathlib
3
  from PIL import Image
4
  import google.generativeai as genai
5
- import os # Import os for environment variables
 
6
 
7
- # --- Configuration ---
8
- try:
9
- # Ensure GOOGLE_API_KEY is set in Streamlit secrets or as an environment variable
10
- API_KEY = st.secrets["GOOGLE_API_KEY"]
11
- except KeyError:
12
  st.error("Google API Key not found. Please set it in Streamlit secrets or as an environment variable (`GOOGLE_API_KEY`).")
13
- st.stop() # Stop the app if API key is missing
14
 
15
  genai.configure(api_key=API_KEY)
16
 
17
- # Generation configuration for the Gemini model
18
  GENERATION_CONFIG = {
19
- "temperature": 1,
20
- "top_p": 0.95,
21
- "top_k": 64,
22
  "max_output_tokens": 8192,
23
  "response_mime_type": "text/plain",
24
  }
25
 
26
- # Safety settings for the Gemini model
27
  SAFETY_SETTINGS = [
28
  {"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"},
29
  {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"},
@@ -31,12 +30,11 @@ SAFETY_SETTINGS = [
31
  {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"},
32
  ]
33
 
34
- # Model name constant
35
  MODEL_NAME = "gemini-1.5-pro-latest"
36
 
37
- # --- Model Initialization ---
38
- @st.cache_resource # Cache the model to avoid re-initializing on every rerun
39
- def load_gemini_model():
40
  """Loads and caches the Google GenerativeModel."""
41
  return genai.GenerativeModel(
42
  model_name=MODEL_NAME,
@@ -44,142 +42,136 @@ def load_gemini_model():
44
  generation_config=GENERATION_CONFIG,
45
  )
46
 
47
- model = load_gemini_model()
48
-
49
- # Initialize chat session in Streamlit's session state
50
- # This ensures the chat history persists across reruns for a single user
51
- if "chat_session" not in st.session_state:
52
- st.session_state.chat_session = model.start_chat(history=[])
53
 
54
  # --- Helper Function for Model Communication ---
55
- def send_message_to_model(message: str, image_path: pathlib.Path) -> str:
56
- """Sends a message and an image to the Gemini model and returns the response."""
57
- image_input = {
58
- 'mime_type': 'image/jpeg',
59
- 'data': image_path.read_bytes()
60
- }
61
  try:
62
- response = st.session_state.chat_session.send_message([message, image_input])
63
  return response.text
64
  except Exception as e:
65
  st.error(f"Error communicating with the Gemini model: {e}")
66
- st.exception(e) # Display full traceback for debugging
67
  return "An error occurred during AI model communication. Please try again or check your API key."
68
 
69
  # --- Streamlit App ---
70
  def main():
71
  """Main function to run the Streamlit application."""
72
- st.set_page_config(page_title="Gemini 1.5 Pro: Images to Code", layout="wide") # Set a wider layout
 
73
  st.title("Gemini 1.5 Pro: Images to Code 👨‍💻")
74
- st.markdown('Made with ❤️ by [KhulnaSoft](https://x.com/khulnasoft)')
75
 
76
  st.info("Upload an image of a UI design, and I'll generate the corresponding HTML and CSS code for you!")
77
 
78
- # Framework selection using a selectbox
79
  framework_options = {
80
- "Regular CSS (Flexbox/Grid)": "Regular CSS use flex grid etc",
81
- "Bootstrap": "Bootstrap",
82
- "Tailwind CSS": "Tailwind CSS",
83
- "Materialize CSS": "Materialize CSS"
84
  }
85
- selected_framework_name = st.selectbox(
86
  "Choose your preferred CSS framework:",
87
  options=list(framework_options.keys()),
88
  help="This will influence the CSS generated within your HTML file."
89
  )
90
- framework = framework_options[selected_framework_name]
91
 
92
  uploaded_file = st.file_uploader("Upload a UI image (JPG, JPEG, PNG):", type=["jpg", "jpeg", "png"])
93
-
94
- # temp_image_path is declared outside the try block to ensure it's accessible for cleanup
95
- temp_image_path = pathlib.Path("temp_image.jpg")
96
-
97
- if uploaded_file is not None:
 
 
 
 
98
  try:
99
- # Load and display the image
100
- image = Image.open(uploaded_file)
101
  st.image(image, caption='Uploaded UI Image', use_column_width=True)
102
-
103
- # Convert image to RGB mode if it has an alpha channel
104
- if image.mode == 'RGBA':
105
- image = image.convert('RGB')
106
-
107
- # Save the uploaded image temporarily
108
  image.save(temp_image_path, format="JPEG")
109
-
110
- st.markdown("---") # Visual separator
111
-
112
- # Button to trigger the generation process
113
- if st.button("Generate UI Code", help="Click to initiate the multi-step code generation."):
114
- st.subheader("Code Generation Process:")
115
-
116
- # Step 1: Generate initial UI description
117
- with st.spinner("Step 1/4: Describing your UI elements and colors..."):
118
- prompt = "Describe this UI in accurate details. When you reference a UI element put its name and bounding box in the format: [object name (y_min, x_min, y_max, x_max)]. Also Describe the color of the elements."
119
- description = send_message_to_model(prompt, temp_image_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  st.success("UI Description Generated!")
121
- with st.expander("See Initial UI Description"):
122
- st.text(description)
123
-
124
- # Step 2: Refine the description
125
- with st.spinner("Step 2/4: Refining description with visual comparison..."):
126
- refine_prompt = f"Compare the described UI elements with the provided image and identify any missing elements or inaccuracies. Also Describe the color of the elements. Provide a refined and accurate description of the UI elements based on this comparison. Here is the initial description: {description}"
127
- refined_description = send_message_to_model(refine_prompt, temp_image_path)
128
- st.success("UI Description Refined!")
129
- with st.expander("See Refined UI Description"):
130
  st.text(refined_description)
131
-
132
- # Step 3: Generate initial HTML
133
- with st.spinner("Step 3/4: Generating initial HTML with CSS..."):
134
- html_prompt = (
135
- f"Create an HTML file based on the following UI description, using the UI elements described in the previous response. "
136
- f"Include {framework} CSS within the HTML file to style the elements. "
137
- f"Make sure the colors used are the same as the original UI. "
138
- f"The UI needs to be responsive and mobile-first, matching the original UI as closely as possible. "
139
- f"Do not include any explanations or comments. Avoid using ```html and ``` at the end. "
140
- f"ONLY return the HTML code with inline CSS. Here is the refined description: {refined_description}"
141
- )
142
- initial_html = send_message_to_model(html_prompt, temp_image_path)
143
- st.success("Initial HTML Generated!")
144
- st.subheader("Initial Generated HTML:")
145
- st.code(initial_html, language='html')
146
-
147
- # Step 4: Refine HTML
148
- with st.spinner("Step 4/4: Refining the generated HTML code..."):
149
- refine_html_prompt = (
150
- f"Validate the following HTML code based on the UI description and image and provide a refined version of the HTML code with {framework} CSS that improves accuracy, responsiveness, and adherence to the original design. "
151
- f"ONLY return the refined HTML code with inline CSS. Avoid using ```html and ``` at the end. "
152
- f"Here is the initial HTML: {initial_html}"
153
- )
154
- refined_html = send_message_to_model(refine_html_prompt, temp_image_path)
155
  st.success("HTML Refined Successfully!")
156
  st.subheader("Refined Generated HTML:")
157
  st.code(refined_html, language='html')
158
-
159
- st.markdown("---") # Final separator
160
- st.success("All steps completed! Your `index.html` file is ready for download.")
161
-
162
- # Save the refined HTML to a file and provide download link
163
- with open("index.html", "w", encoding="utf-8") as file: # Specify encoding
164
- file.write(refined_html)
165
-
166
- st.download_button(
167
- label="Download index.html",
168
- data=refined_html,
169
- file_name="index.html",
170
- mime="text/html"
171
- )
172
- st.info("You can open the downloaded `index.html` file in your web browser to view the generated UI.")
173
-
 
174
  except Exception as e:
175
  st.error(f"An unexpected error occurred: {e}")
176
- st.exception(e) # Displays the full traceback
177
-
178
  finally:
179
- # Clean up the temporary image file whether an error occurred or not
180
- if temp_image_path.exists():
181
- os.remove(temp_image_path)
182
- # st.success("Temporary image file removed.") # Can uncomment for debugging
183
  else:
184
  st.write("Please upload an image to start generating UI code.")
185
 
 
2
  import pathlib
3
  from PIL import Image
4
  import google.generativeai as genai
5
+ import os
6
+ import shutil
7
 
8
+ # --- Constants & Configuration ---
9
+ # Use a more robust way to handle the API key
10
+ API_KEY = st.secrets.get("GOOGLE_API_KEY") or os.getenv("GOOGLE_API_KEY")
11
+
12
+ if not API_KEY:
13
  st.error("Google API Key not found. Please set it in Streamlit secrets or as an environment variable (`GOOGLE_API_KEY`).")
14
+ st.stop()
15
 
16
  genai.configure(api_key=API_KEY)
17
 
 
18
  GENERATION_CONFIG = {
19
+ "temperature": 0.8, # Slightly lower temperature for more predictable code
20
+ "top_p": 0.9,
21
+ "top_k": 40,
22
  "max_output_tokens": 8192,
23
  "response_mime_type": "text/plain",
24
  }
25
 
 
26
  SAFETY_SETTINGS = [
27
  {"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"},
28
  {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"},
 
30
  {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"},
31
  ]
32
 
 
33
  MODEL_NAME = "gemini-1.5-pro-latest"
34
 
35
+ # --- Model & Chat Session Management ---
36
+ @st.cache_resource
37
+ def get_gemini_model():
38
  """Loads and caches the Google GenerativeModel."""
39
  return genai.GenerativeModel(
40
  model_name=MODEL_NAME,
 
42
  generation_config=GENERATION_CONFIG,
43
  )
44
 
45
+ def get_chat_session():
46
+ """Initializes and returns a chat session, caching it in Streamlit's session state."""
47
+ if "chat_session" not in st.session_state:
48
+ st.session_state.chat_session = get_gemini_model().start_chat(history=[])
49
+ return st.session_state.chat_session
 
50
 
51
  # --- Helper Function for Model Communication ---
52
+ def send_message_with_image(chat_session, message: str, image_path: pathlib.Path) -> str:
53
+ """Sends a message and an image to the Gemini model."""
 
 
 
 
54
  try:
55
+ response = chat_session.send_message([message, genai.upload_file(image_path)])
56
  return response.text
57
  except Exception as e:
58
  st.error(f"Error communicating with the Gemini model: {e}")
59
+ st.exception(e)
60
  return "An error occurred during AI model communication. Please try again or check your API key."
61
 
62
  # --- Streamlit App ---
63
  def main():
64
  """Main function to run the Streamlit application."""
65
+ st.set_page_config(page_title="Gemini 1.5 Pro: Images to Code 👨‍💻", layout="wide", initial_sidebar_state="collapsed")
66
+
67
  st.title("Gemini 1.5 Pro: Images to Code 👨‍💻")
68
+ st.markdown('Made with ❤️ by [KhulnaSoft](https://x.com/khulnasoft) and Enhanced by Google')
69
 
70
  st.info("Upload an image of a UI design, and I'll generate the corresponding HTML and CSS code for you!")
71
 
72
+ # Framework selection
73
  framework_options = {
74
+ "Regular CSS (Flexbox/Grid)": "Regular CSS with flexbox and grid layouts",
75
+ "Bootstrap": "Bootstrap framework with its utility classes",
76
+ "Tailwind CSS": "Tailwind CSS with its utility classes",
77
+ "Materialize CSS": "Materialize CSS framework"
78
  }
79
+ selected_framework = st.selectbox(
80
  "Choose your preferred CSS framework:",
81
  options=list(framework_options.keys()),
82
  help="This will influence the CSS generated within your HTML file."
83
  )
 
84
 
85
  uploaded_file = st.file_uploader("Upload a UI image (JPG, JPEG, PNG):", type=["jpg", "jpeg", "png"])
86
+
87
+ # Use a temporary directory for file handling
88
+ temp_dir = pathlib.Path("temp_uploads")
89
+ if not temp_dir.exists():
90
+ temp_dir.mkdir()
91
+
92
+ temp_image_path = temp_dir / "uploaded_image.jpg"
93
+
94
+ if uploaded_file:
95
  try:
96
+ # Load, display, and save the image
97
+ image = Image.open(uploaded_file).convert('RGB')
98
  st.image(image, caption='Uploaded UI Image', use_column_width=True)
 
 
 
 
 
 
99
  image.save(temp_image_path, format="JPEG")
100
+
101
+ st.markdown("---")
102
+
103
+ # Use st.form for a better user experience with a single button
104
+ with st.form("code_generation_form"):
105
+ st.subheader("Generate UI Code")
106
+ st.markdown(f"**Selected Framework:** `{selected_framework}`")
107
+ submitted = st.form_submit_button("Generate Code")
108
+
109
+ if submitted:
110
+ # Use a single progress bar for all steps
111
+ progress_bar = st.progress(0, text="Starting code generation...")
112
+
113
+ chat_session = get_chat_session()
114
+ framework_instruction = framework_options[selected_framework]
115
+
116
+ # Step 1: Generate & Refine Description
117
+ progress_bar.progress(25, text="Step 1/2: Analyzing UI and generating description...")
118
+ prompt_description = (
119
+ f"Describe this UI in accurate details, including UI elements, their bounding boxes in the format: "
120
+ f"[object name (y_min, x_min, y_max, x_max)], and their colors. "
121
+ f"Then, refine this description by comparing it to the image for any missing elements or inaccuracies. "
122
+ f"Provide a final, refined, and accurate description based on this analysis."
123
+ )
124
+ refined_description = send_message_with_image(chat_session, prompt_description, temp_image_path)
125
+
126
+ if not refined_description.startswith("An error occurred"):
127
  st.success("UI Description Generated!")
128
+ with st.expander("See Generated UI Description"):
 
 
 
 
 
 
 
 
129
  st.text(refined_description)
130
+ else:
131
+ st.error("Failed to generate UI description.")
132
+ return
133
+
134
+ # Step 2: Generate & Refine HTML
135
+ progress_bar.progress(75, text="Step 2/2: Generating and refining HTML/CSS code...")
136
+ prompt_html = (
137
+ f"Create a single HTML file based on the following UI description: '{refined_description}'. "
138
+ f"Use {framework_instruction} to style the elements. The UI must be responsive and "
139
+ f"mobile-first, matching the original design as closely as possible. "
140
+ f"Validate the generated code and provide a refined version that improves accuracy, "
141
+ f"responsiveness, and adherence to the original design. "
142
+ f"ONLY return the complete, refined HTML code. Do not include any explanations, comments, or ```html block."
143
+ )
144
+ refined_html = send_message_with_image(chat_session, prompt_html, temp_image_path)
145
+
146
+ if not refined_html.startswith("An error occurred"):
 
 
 
 
 
 
 
147
  st.success("HTML Refined Successfully!")
148
  st.subheader("Refined Generated HTML:")
149
  st.code(refined_html, language='html')
150
+
151
+ st.markdown("---")
152
+ st.success("All steps completed! Your `index.html` file is ready for download.")
153
+
154
+ # Provide download link
155
+ st.download_button(
156
+ label="Download index.html",
157
+ data=refined_html,
158
+ file_name="index.html",
159
+ mime="text/html"
160
+ )
161
+ st.info("You can open the downloaded `index.html` file in your web browser to view the generated UI.")
162
+ else:
163
+ st.error("Failed to generate HTML code.")
164
+
165
+ progress_bar.empty() # Hide the progress bar
166
+
167
  except Exception as e:
168
  st.error(f"An unexpected error occurred: {e}")
169
+ st.exception(e)
170
+
171
  finally:
172
+ if temp_dir.exists():
173
+ shutil.rmtree(temp_dir) # Use shutil to remove the directory and its contents
174
+
 
175
  else:
176
  st.write("Please upload an image to start generating UI code.")
177