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

Complete Refactored Code

Browse files

Improvement:

Constants: Define constants at the top for model names and safety settings to improve readability and maintainability.
Function for Image Saving: While a small task, encapsulating temporary file saving could be a good practice for larger applications. For this case, it's fine as is.
Redundant pathlib for temp_image_path: You create a Path object but then pass it to read_bytes() which expects a string or Path-like object. It's okay, but str(temp_image_path) could be used for clarity if needed by other functions, although read_bytes() handles Path directly.

Improvement: Streamlit reruns the script from top to bottom whenever there's an interaction. Re-initializing the GenerativeModel every time is inefficient.

Change: Use

@st
.cache_resource to cache the model.

Improvement: The chat_session should ideally be managed within st.session_state to maintain a persistent conversation with the model across Streamlit reruns for a single user, if you intend to have a continued conversation. While your current use case is step-by-step generation, it's a good practice for chat applications.

Files changed (1) hide show
  1. app.py +141 -63
app.py CHANGED
@@ -2,13 +2,20 @@ import streamlit as st
2
  import pathlib
3
  from PIL import Image
4
  import google.generativeai as genai
 
 
 
 
 
 
 
 
 
5
 
6
- # Configure the API key directly in the script
7
- API_KEY = 'YOUR KEY'
8
  genai.configure(api_key=API_KEY)
9
 
10
- # Generation configuration
11
- generation_config = {
12
  "temperature": 1,
13
  "top_p": 0.95,
14
  "top_k": 64,
@@ -16,94 +23,165 @@ generation_config = {
16
  "response_mime_type": "text/plain",
17
  }
18
 
19
- # Safety settings
20
- safety_settings = [
21
  {"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"},
22
  {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"},
23
  {"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_NONE"},
24
  {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"},
25
  ]
26
 
27
- # Model name
28
  MODEL_NAME = "gemini-1.5-pro-latest"
29
 
30
- # Framework selection (e.g., Tailwind, Bootstrap, etc.)
31
- framework = "Regular CSS use flex grid etc" # Change this to "Bootstrap" or any other framework as needed
32
-
33
- # Create the model
34
- model = genai.GenerativeModel(
35
- model_name=MODEL_NAME,
36
- safety_settings=safety_settings,
37
- generation_config=generation_config,
38
- )
39
-
40
- # Start a chat session
41
- chat_session = model.start_chat(history=[])
42
-
43
- # Function to send a message to the model
44
- def send_message_to_model(message, image_path):
 
 
 
 
 
45
  image_input = {
46
  'mime_type': 'image/jpeg',
47
- 'data': pathlib.Path(image_path).read_bytes()
48
  }
49
- response = chat_session.send_message([message, image_input])
50
- return response.text
51
-
52
- # Streamlit app
 
 
 
 
 
53
  def main():
54
- st.title("Gemini 1.5 Pro, Images to Code 👨‍💻 ")
55
- st.subheader('Made with ❤️ by [KhulnaSoft](https://x.com/khulnasoft)')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
 
57
- uploaded_file = st.file_uploader("Choose an image...", type=["jpg", "jpeg", "png"])
 
 
 
58
 
59
  if uploaded_file is not None:
60
  try:
61
  # Load and display the image
62
  image = Image.open(uploaded_file)
63
- st.image(image, caption='Uploaded Image.', use_column_width=True)
64
 
65
  # Convert image to RGB mode if it has an alpha channel
66
  if image.mode == 'RGBA':
67
  image = image.convert('RGB')
68
 
69
  # Save the uploaded image temporarily
70
- temp_image_path = pathlib.Path("temp_image.jpg")
71
  image.save(temp_image_path, format="JPEG")
72
 
73
- # Generate UI description
74
- if st.button("Code UI"):
75
- st.write("🧑‍💻 Looking at your UI...")
76
- 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."
77
- description = send_message_to_model(prompt, temp_image_path)
78
- st.write(description)
79
-
80
- # Refine the description
81
- st.write("🔍 Refining description with visual comparison...")
82
- 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}"
83
- refined_description = send_message_to_model(refine_prompt, temp_image_path)
84
- st.write(refined_description)
85
-
86
- # Generate HTML
87
- st.write("🛠️ Generating website...")
88
- html_prompt = f"Create an HTML file based on the following UI description, using the UI elements described in the previous response. Include {framework} CSS within the HTML file to style the elements. Make sure the colors used are the same as the original UI. The UI needs to be responsive and mobile-first, matching the original UI as closely as possible. Do not include any explanations or comments. Avoid using ```html. and ``` at the end. ONLY return the HTML code with inline CSS. Here is the refined description: {refined_description}"
89
- initial_html = send_message_to_model(html_prompt, temp_image_path)
90
- st.code(initial_html, language='html')
91
-
92
- # Refine HTML
93
- st.write("🔧 Refining website...")
94
- refine_html_prompt = 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. ONLY return the refined HTML code with inline CSS. Avoid using ```html. and ``` at the end. Here is the initial HTML: {initial_html}"
95
- refined_html = send_message_to_model(refine_html_prompt, temp_image_path)
96
- st.code(refined_html, language='html')
97
-
98
- # Save the refined HTML to a file
99
- with open("index.html", "w") as file:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  file.write(refined_html)
101
- st.success("HTML file 'index.html' has been created.")
102
 
103
- # Provide download link for HTML
104
- st.download_button(label="Download HTML", data=refined_html, file_name="index.html", mime="text/html")
 
 
 
 
 
 
105
  except Exception as e:
106
- st.error(f"An error occurred: {e}")
 
 
 
 
 
 
 
 
 
107
 
108
  if __name__ == "__main__":
109
  main()
 
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,
 
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"},
30
  {"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_NONE"},
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,
43
+ safety_settings=SAFETY_SETTINGS,
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
 
186
  if __name__ == "__main__":
187
  main()