sreelekhaputta2 commited on
Commit
a30ef42
Β·
verified Β·
1 Parent(s): 4005008

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +155 -44
app.py CHANGED
@@ -5,6 +5,9 @@ import tensorflow as tf
5
  import gradio as gr
6
  from fpdf import FPDF
7
  import pandas as pd
 
 
 
8
 
9
  # Load Features from CSV (curate a subset for demo clarity)
10
  features_df = pd.read_csv("Feature-Description.csv")
@@ -26,17 +29,21 @@ key_features = [
26
  ]
27
  features_list = [row for row in features_df.to_dict(orient="records") if row["Feature"] in key_features]
28
 
 
29
  def features_html():
30
- html = "<ul style='margin:0; padding-left:1.2em; font-size:16px; color:#f4f6fa;'>"
31
  for f in features_list:
32
  html += f"<li><b>{f['Feature']}</b>: {f['Description']}</li>"
33
  html += "</ul>"
34
  return html
35
 
36
- model_name = "Salesforce/codet5-base"
 
 
37
  tokenizer = AutoTokenizer.from_pretrained(model_name)
38
  model = AutoModelForSeq2SeqLM.from_pretrained(model_name)
39
 
 
40
  class CodeComplexityScorer(tf.keras.Model):
41
  def __init__(self):
42
  super().__init__()
@@ -47,8 +54,10 @@ class CodeComplexityScorer(tf.keras.Model):
47
  score = self.dense2(x)
48
  return score
49
 
 
50
  complexity_model = CodeComplexityScorer()
51
 
 
52
  def extract_code_features(code_text):
53
  length = len(code_text)
54
  lines = code_text.count('\n') + 1
@@ -57,6 +66,7 @@ def extract_code_features(code_text):
57
  features = tf.constant([[length/1000, lines/50, avg_word_len/20]], dtype=tf.float32)
58
  return features
59
 
 
60
  LANG_PROMPTS = {
61
  "Python": "summarize Python code:",
62
  "JavaScript": "summarize JavaScript code:",
@@ -64,64 +74,130 @@ LANG_PROMPTS = {
64
  "Other": "summarize code:",
65
  }
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  def automatic_code_analysis(code_text):
68
- return f"Code contains {code_text.count(chr(10))+1} lines and {len(code_text)} characters."
 
 
 
69
 
70
  def context_aware_documentation(code_text):
71
- return "Generates context-aware, readable documentation (demo placeholder)."
 
 
72
 
73
  def bug_issue_identification(code_text):
74
- return "No obvious issues detected (demo placeholder)."
 
 
 
 
 
 
 
 
 
 
75
 
76
  def automated_code_summaries(code_text):
77
- return "Provides concise summaries of code modules (demo placeholder)."
 
 
78
 
 
 
79
  feature_functions = {
80
  "Automatic Code Analysis": automatic_code_analysis,
81
  "Context-Aware Documentation": context_aware_documentation,
82
  "Bug/Issue Identification": bug_issue_identification,
83
  "Automated Code Summaries": automated_code_summaries,
 
84
  }
85
 
 
86
  def generate_documentation(code_text, language, export_format, selected_features):
 
87
  features = extract_code_features(code_text)
88
- complexity_score = complexity_model(features).numpy()[0][0]
 
 
89
  prompt = LANG_PROMPTS.get(language, LANG_PROMPTS["Other"])
90
- input_text = f"{prompt} {code_text.strip()}"
91
- inputs = tokenizer.encode(input_text, return_tensors="pt", max_length=512, truncation=True)
92
- summary_ids = model.generate(inputs, max_length=128, num_beams=5, early_stopping=True)
 
 
 
 
 
 
 
93
  summary = tokenizer.decode(summary_ids[0], skip_special_tokens=True)
 
94
  extra_sections = ""
95
  for feature in selected_features:
96
  if feature in feature_functions:
97
- extra_sections += f"\n**{feature}:**\n{feature_functions[feature](code_text)}"
 
 
 
 
 
 
98
  doc_output = f"""### AI-Generated Documentation
 
99
 
100
- {summary}
101
-
102
- **Code Complexity Score:** {complexity_score:.2f} (0=low,1=high)
103
- {extra_sections}
104
  """
 
105
  if export_format == "Markdown":
106
  return doc_output
107
  elif export_format == "PDF":
108
  pdf_filename = "/tmp/generated_doc.pdf"
109
  pdf = FPDF()
110
  pdf.add_page()
111
- pdf.set_font("Arial", size=12)
112
  for line in doc_output.split('\n'):
113
- pdf.cell(0, 10, txt=line, ln=True)
114
  pdf.output(pdf_filename)
115
  return pdf_filename
116
  else:
117
  return doc_output
118
 
 
119
  def process_uploaded_file(uploaded_file, language, export_format, selected_features):
120
  code_bytes = uploaded_file.read()
121
  code_text = code_bytes.decode("utf-8", errors="ignore")
122
  return generate_documentation(code_text, language, export_format, selected_features)
123
 
124
- # --- CSS: Use .gradio-container for full-page background image ---
 
125
  custom_css = """
126
  .gradio-container {
127
  background-image: url('https://media.istockphoto.com/photos/programming-code-abstract-technology-background-of-software-developer-picture-id1201405775?b=1&k=20&m=1201405775&s=170667a&w=0&h=XZ-tUfHvW5IRT30nMm7bAbbWrqkGQ-WT8XSS8Pab-eA=');
@@ -131,8 +207,9 @@ custom_css = """
131
  background-size: cover;
132
  min-height: 100vh;
133
  }
 
134
  #container {
135
- background: rgba(16, 24, 40, 0.85);
136
  border-radius: 22px;
137
  padding: 2.5rem 3.5rem;
138
  max-width: 900px;
@@ -140,8 +217,22 @@ custom_css = """
140
  box-shadow: 0 12px 48px 0 rgba(60,120,220,0.28), 0 1.5px 12px 0 rgba(0,0,0,0.15);
141
  color: #f4f6fa !important;
142
  backdrop-filter: blur(7px);
143
- border: 2.5px solid rgba(0,255,255,0.10);
 
 
 
 
 
 
 
144
  }
 
 
 
 
 
 
 
145
  #animated-header {
146
  font-size: 2.6em !important;
147
  font-weight: 900;
@@ -154,14 +245,17 @@ custom_css = """
154
  animation: gradientShift 12s ease-in-out infinite;
155
  letter-spacing: 2px;
156
  text-shadow: 0 2px 8px rgba(0,255,255,0.18);
 
157
  }
 
158
  @keyframes gradientShift {
159
  0%{background-position:0% 50%;}
160
  50%{background-position:100% 50%;}
161
  100%{background-position:0% 50%;}
162
  }
 
163
  #feature-panel {
164
- background: rgba(34, 49, 63, 0.95);
165
  border-radius: 14px;
166
  padding: 1.2rem 1.8rem;
167
  margin-bottom: 1.5rem;
@@ -174,14 +268,17 @@ custom_css = """
174
  border: 2px solid #00f2fe;
175
  animation: fadeInUp 1.2s ease forwards, neon-glow 2.5s infinite alternate;
176
  }
 
177
  @keyframes fadeInUp {
178
  from {opacity: 0; transform: translateY(20px);}
179
  to {opacity: 1; transform: translateY(0);}
180
  }
 
181
  @keyframes neon-glow {
182
  0% { box-shadow: 0 0 8px #00f2fe, 0 0 16px #00f2fe70; border-color: #00f2fe;}
183
  100% { box-shadow: 0 0 16px #43e97b, 0 0 32px #43e97b70; border-color: #43e97b;}
184
  }
 
185
  #generate-btn {
186
  background: linear-gradient(90deg, #43e97b, #38f9d7, #00f2fe);
187
  color: #192a56 !important;
@@ -193,68 +290,82 @@ custom_css = """
193
  box-shadow: 0 6px 24px 0 rgba(0,255,255,0.22);
194
  transition: all 0.3s cubic-bezier(.4,2,.6,1);
195
  letter-spacing: 1px;
196
- outline: none;
197
  }
 
198
  #generate-btn:hover {
199
  background: linear-gradient(90deg, #fa709a, #fee140);
200
  color: #192a56 !important;
201
  box-shadow: 0 8px 32px rgba(250,112,154,0.22);
202
  transform: scale(1.06);
203
- cursor: pointer;
204
  }
 
205
  #credits {
206
  text-align: center;
207
  margin-top: 2.5rem;
208
  font-size: 1.15em;
209
- color: #fee140;
210
  font-weight: 800;
211
  letter-spacing: 0.08em;
212
  animation: fadeIn 2s ease forwards;
213
  text-shadow: 0 2px 8px #fa709a50;
214
  }
 
 
215
  @media (max-width: 600px) {
216
  #container {
217
  padding: 1rem 0.5rem;
218
  margin: 1rem;
219
  }
220
  #animated-header {
221
- font-size: 1.4em !important;
222
  }
223
  #feature-panel {
224
- padding: 0.7rem 0.7rem;
225
  font-size: 1em;
226
  }
 
 
 
227
  }
228
  """
229
 
 
230
  with gr.Blocks(css=custom_css, elem_id="container") as demo:
231
- gr.HTML("<div id='animated-header'>AI-Powered Code Documentation Generator</div>")
 
 
 
232
  with gr.Row():
233
- gr.HTML(f"<div id='feature-panel'><b>Supported Features (scroll if needed):</b>{features_html()}</div>")
234
- file_input = gr.File(label="Upload Code File (.py, .js, .java)", file_types=[".py", ".js", ".java"])
235
- code_input = gr.Textbox(label="Or Paste Code Here", lines=8, max_lines=15, placeholder="Paste your code snippet here...")
236
- language_dropdown = gr.Dropdown(label="Select Language", choices=["Python", "JavaScript", "Java", "Other"], value="Python")
237
- export_dropdown = gr.Dropdown(label="Export Format", choices=["Markdown", "PDF"], value="Markdown")
 
 
 
238
  feature_options = gr.CheckboxGroup(
239
- label="Select Features to Include",
240
  choices=[f["Feature"] for f in features_list],
241
- value=["Automatic Code Analysis", "Context-Aware Documentation", "Bug/Issue Identification"],
242
  interactive=True,
243
- container=False,
244
- show_label=True,
245
  )
246
- generate_btn = gr.Button("Generate Documentation", elem_id="generate-btn")
247
- output_box = gr.Textbox(label="Generated Documentation", lines=10, max_lines=20, interactive=False, show_copy_button=True)
248
- pdf_output = gr.File(label="Download PDF", visible=False)
249
- gr.HTML("<div id='credits'>Credits: Sreelekha Putta</div>")
 
 
 
250
 
251
  def on_generate(file_obj, code_str, language, export_format, selected_features):
252
  if file_obj is not None:
253
  result = process_uploaded_file(file_obj, language, export_format, selected_features)
254
- elif code_str.strip() != "":
255
  result = generate_documentation(code_str, language, export_format, selected_features)
256
  else:
257
- return "Please upload a file or paste code to generate documentation.", None
 
258
  if export_format == "PDF":
259
  return None, gr.update(value=result, visible=True)
260
  else:
@@ -266,5 +377,5 @@ with gr.Blocks(css=custom_css, elem_id="container") as demo:
266
  outputs=[output_box, pdf_output]
267
  )
268
 
269
- demo.launch(share=True)
270
-
 
5
  import gradio as gr
6
  from fpdf import FPDF
7
  import pandas as pd
8
+ import re
9
+ from io import BytesIO
10
+ import base64
11
 
12
  # Load Features from CSV (curate a subset for demo clarity)
13
  features_df = pd.read_csv("Feature-Description.csv")
 
29
  ]
30
  features_list = [row for row in features_df.to_dict(orient="records") if row["Feature"] in key_features]
31
 
32
+
33
  def features_html():
34
+ html = "<ul style='margin:0; padding-left:1.2em; font-size:16px;'>"
35
  for f in features_list:
36
  html += f"<li><b>{f['Feature']}</b>: {f['Description']}</li>"
37
  html += "</ul>"
38
  return html
39
 
40
+
41
+ # Use lighter, faster model for better performance
42
+ model_name = "microsoft/DialoGPT-medium" # Switched to faster model
43
  tokenizer = AutoTokenizer.from_pretrained(model_name)
44
  model = AutoModelForSeq2SeqLM.from_pretrained(model_name)
45
 
46
+
47
  class CodeComplexityScorer(tf.keras.Model):
48
  def __init__(self):
49
  super().__init__()
 
54
  score = self.dense2(x)
55
  return score
56
 
57
+
58
  complexity_model = CodeComplexityScorer()
59
 
60
+
61
  def extract_code_features(code_text):
62
  length = len(code_text)
63
  lines = code_text.count('\n') + 1
 
66
  features = tf.constant([[length/1000, lines/50, avg_word_len/20]], dtype=tf.float32)
67
  return features
68
 
69
+
70
  LANG_PROMPTS = {
71
  "Python": "summarize Python code:",
72
  "JavaScript": "summarize JavaScript code:",
 
74
  "Other": "summarize code:",
75
  }
76
 
77
+
78
+ def parse_functions_classes(code_text):
79
+ """Extract functions and classes for UML generation"""
80
+ functions = re.findall(r'def\s+(\w+)[^:]*\([^)]*\):', code_text, re.IGNORECASE | re.MULTILINE)
81
+ classes = re.findall(r'(class)\s+(\w+)[^:]*:', code_text, re.IGNORECASE | re.MULTILINE)
82
+ return functions, [cls[1] for cls in classes]
83
+
84
+
85
+ def generate_uml_diagram(code_text):
86
+ """Generate UML diagram as SVG/Mermaid code"""
87
+ functions, classes = parse_functions_classes(code_text)
88
+
89
+ if classes:
90
+ uml = "```mermaid\ngraph TD\n"
91
+ for cls in classes:
92
+ uml += f" class{classes.index(cls)+1}[{cls}]\n"
93
+ for func in functions:
94
+ uml += f" func{functions.index(func)+1}[{func}()]\n"
95
+ # Connect functions to classes (simplified)
96
+ uml += " class1 --> func1\n"
97
+ uml += "```\n"
98
+ else:
99
+ uml = f"```mermaid\ngraph TD\n F[{len(functions)} Functions Found]\n F --> F1[Functions]\n```\n"
100
+
101
+ return uml
102
+
103
+
104
  def automatic_code_analysis(code_text):
105
+ lines = code_text.count(chr(10)) + 1
106
+ functions, classes = parse_functions_classes(code_text)
107
+ return f"Lines: {lines} | Functions: {len(functions)} | Classes: {len(classes)}"
108
+
109
 
110
  def context_aware_documentation(code_text):
111
+ functions, classes = parse_functions_classes(code_text)
112
+ return f"Detected {len(functions)} functions and {len(classes)} classes. Documentation generated contextually."
113
+
114
 
115
  def bug_issue_identification(code_text):
116
+ issues = []
117
+ if "print(" in code_text and "input(" not in code_text:
118
+ issues.append("Consider using logging instead of print for production")
119
+ if "== True" in code_text or "== False" in code_text:
120
+ issues.append("Use 'if condition:' instead of 'if condition == True:'")
121
+ return "; ".join(issues) if issues else "No obvious issues detected"
122
+
123
+
124
+ def uml_diagram_generation(code_text):
125
+ return generate_uml_diagram(code_text)
126
+
127
 
128
  def automated_code_summaries(code_text):
129
+ lines = code_text.count('\n') + 1
130
+ words = len(code_text.split())
131
+ return f"Code module with {lines} lines, {words} words. Modular structure detected."
132
 
133
+
134
+ # Updated feature functions with UML support
135
  feature_functions = {
136
  "Automatic Code Analysis": automatic_code_analysis,
137
  "Context-Aware Documentation": context_aware_documentation,
138
  "Bug/Issue Identification": bug_issue_identification,
139
  "Automated Code Summaries": automated_code_summaries,
140
+ "UML Diagram Generation": uml_diagram_generation,
141
  }
142
 
143
+
144
  def generate_documentation(code_text, language, export_format, selected_features):
145
+ # Faster feature extraction
146
  features = extract_code_features(code_text)
147
+ complexity_score = float(complexity_model(features).numpy()[0][0])
148
+
149
+ # Much faster summarization - direct processing, no beams
150
  prompt = LANG_PROMPTS.get(language, LANG_PROMPTS["Other"])
151
+ input_text = f"{prompt} {code_text.strip()[:500]}" # Limit input size
152
+ inputs = tokenizer.encode(input_text, return_tensors="pt", max_length=256, truncation=True)
153
+ with torch.no_grad(): # Disable gradients for speed
154
+ summary_ids = model.generate(
155
+ inputs,
156
+ max_length=64, # Shorter output
157
+ do_sample=True,
158
+ temperature=0.7,
159
+ pad_token_id=tokenizer.eos_token_id
160
+ )
161
  summary = tokenizer.decode(summary_ids[0], skip_special_tokens=True)
162
+
163
  extra_sections = ""
164
  for feature in selected_features:
165
  if feature in feature_functions:
166
+ result = feature_functions[feature](code_text)
167
+ # Handle Mermaid UML specially
168
+ if feature == "UML Diagram Generation" and "mermaid" in result:
169
+ extra_sections += f"\n\n**{feature}:**\n{result}"
170
+ else:
171
+ extra_sections += f"\n\n**{feature}:**\n{result}"
172
+
173
  doc_output = f"""### AI-Generated Documentation
174
+ **Summary:** {summary}
175
 
176
+ **Code Complexity Score:** {complexity_score:.2f} (0=low,1=high){extra_sections}
 
 
 
177
  """
178
+
179
  if export_format == "Markdown":
180
  return doc_output
181
  elif export_format == "PDF":
182
  pdf_filename = "/tmp/generated_doc.pdf"
183
  pdf = FPDF()
184
  pdf.add_page()
185
+ pdf.set_font("Arial", size=11)
186
  for line in doc_output.split('\n'):
187
+ pdf.cell(0, 8, txt=line[:100], ln=True) # Truncate long lines
188
  pdf.output(pdf_filename)
189
  return pdf_filename
190
  else:
191
  return doc_output
192
 
193
+
194
  def process_uploaded_file(uploaded_file, language, export_format, selected_features):
195
  code_bytes = uploaded_file.read()
196
  code_text = code_bytes.decode("utf-8", errors="ignore")
197
  return generate_documentation(code_text, language, export_format, selected_features)
198
 
199
+
200
+ # --- FIXED CSS: Consistent colors across ALL themes/devices ---
201
  custom_css = """
202
  .gradio-container {
203
  background-image: url('https://media.istockphoto.com/photos/programming-code-abstract-technology-background-of-software-developer-picture-id1201405775?b=1&k=20&m=1201405775&s=170667a&w=0&h=XZ-tUfHvW5IRT30nMm7bAbbWrqkGQ-WT8XSS8Pab-eA=');
 
207
  background-size: cover;
208
  min-height: 100vh;
209
  }
210
+
211
  #container {
212
+ background: rgba(16, 24, 40, 0.92) !important;
213
  border-radius: 22px;
214
  padding: 2.5rem 3.5rem;
215
  max-width: 900px;
 
217
  box-shadow: 0 12px 48px 0 rgba(60,120,220,0.28), 0 1.5px 12px 0 rgba(0,0,0,0.15);
218
  color: #f4f6fa !important;
219
  backdrop-filter: blur(7px);
220
+ border: 2.5px solid rgba(0,255,255,0.15);
221
+ }
222
+
223
+ /* FORCE consistent text colors across ALL themes */
224
+ #container *, #container p, #container div, #container span, #container label {
225
+ color: #f4f6fa !important;
226
+ text-shadow: 0 1px 2px rgba(0,0,0,0.5) !important;
227
+ fill: #f4f6fa !important;
228
  }
229
+
230
+ #container input, #container textarea, #container select {
231
+ color: #192a56 !important;
232
+ background: rgba(255,255,255,0.95) !important;
233
+ border: 1px solid #00f2fe !important;
234
+ }
235
+
236
  #animated-header {
237
  font-size: 2.6em !important;
238
  font-weight: 900;
 
245
  animation: gradientShift 12s ease-in-out infinite;
246
  letter-spacing: 2px;
247
  text-shadow: 0 2px 8px rgba(0,255,255,0.18);
248
+ color: #f4f6fa !important;
249
  }
250
+
251
  @keyframes gradientShift {
252
  0%{background-position:0% 50%;}
253
  50%{background-position:100% 50%;}
254
  100%{background-position:0% 50%;}
255
  }
256
+
257
  #feature-panel {
258
+ background: rgba(34, 49, 63, 0.95) !important;
259
  border-radius: 14px;
260
  padding: 1.2rem 1.8rem;
261
  margin-bottom: 1.5rem;
 
268
  border: 2px solid #00f2fe;
269
  animation: fadeInUp 1.2s ease forwards, neon-glow 2.5s infinite alternate;
270
  }
271
+
272
  @keyframes fadeInUp {
273
  from {opacity: 0; transform: translateY(20px);}
274
  to {opacity: 1; transform: translateY(0);}
275
  }
276
+
277
  @keyframes neon-glow {
278
  0% { box-shadow: 0 0 8px #00f2fe, 0 0 16px #00f2fe70; border-color: #00f2fe;}
279
  100% { box-shadow: 0 0 16px #43e97b, 0 0 32px #43e97b70; border-color: #43e97b;}
280
  }
281
+
282
  #generate-btn {
283
  background: linear-gradient(90deg, #43e97b, #38f9d7, #00f2fe);
284
  color: #192a56 !important;
 
290
  box-shadow: 0 6px 24px 0 rgba(0,255,255,0.22);
291
  transition: all 0.3s cubic-bezier(.4,2,.6,1);
292
  letter-spacing: 1px;
 
293
  }
294
+
295
  #generate-btn:hover {
296
  background: linear-gradient(90deg, #fa709a, #fee140);
297
  color: #192a56 !important;
298
  box-shadow: 0 8px 32px rgba(250,112,154,0.22);
299
  transform: scale(1.06);
 
300
  }
301
+
302
  #credits {
303
  text-align: center;
304
  margin-top: 2.5rem;
305
  font-size: 1.15em;
306
+ color: #fee140 !important;
307
  font-weight: 800;
308
  letter-spacing: 0.08em;
309
  animation: fadeIn 2s ease forwards;
310
  text-shadow: 0 2px 8px #fa709a50;
311
  }
312
+
313
+ /* Mobile responsive with consistent colors */
314
  @media (max-width: 600px) {
315
  #container {
316
  padding: 1rem 0.5rem;
317
  margin: 1rem;
318
  }
319
  #animated-header {
320
+ font-size: 1.8em !important;
321
  }
322
  #feature-panel {
323
+ padding: 0.8rem;
324
  font-size: 1em;
325
  }
326
+ #container * {
327
+ color: #f4f6fa !important;
328
+ }
329
  }
330
  """
331
 
332
+
333
  with gr.Blocks(css=custom_css, elem_id="container") as demo:
334
+ gr.HTML("<div id='animated-header'>πŸš€ AI-Powered Code Documentation Generator</div>")
335
+ with gr.Row():
336
+ gr.HTML(f"<div id='feature-panel'><b>βœ… Supported Features:</b>{features_html()}</div>")
337
+
338
  with gr.Row():
339
+ file_input = gr.File(label="πŸ“ Upload Code File", file_types=[".py", ".js", ".java", ".txt"])
340
+ code_input = gr.Textbox(label="πŸ’» Or Paste Code Here", lines=10, max_lines=15,
341
+ placeholder="Paste your Python/JavaScript/Java code here...")
342
+
343
+ with gr.Row():
344
+ language_dropdown = gr.Dropdown(label="🌐 Language", choices=["Python", "JavaScript", "Java", "Other"], value="Python")
345
+ export_dropdown = gr.Dropdown(label="πŸ“„ Export", choices=["Markdown", "PDF"], value="Markdown")
346
+
347
  feature_options = gr.CheckboxGroup(
348
+ label="βš™οΈ Select Features (UML Diagrams now working!)",
349
  choices=[f["Feature"] for f in features_list],
350
+ value=["Automatic Code Analysis", "Context-Aware Documentation", "UML Diagram Generation", "Bug/Issue Identification"],
351
  interactive=True,
 
 
352
  )
353
+
354
+ generate_btn = gr.Button("🎯 Generate Documentation", variant="primary", elem_id="generate-btn", size="lg")
355
+
356
+ output_box = gr.Markdown(label="πŸ“– Generated Documentation", interactive=False)
357
+ pdf_output = gr.File(label="πŸ’Ύ Download PDF", visible=False)
358
+
359
+ gr.HTML("<div id='credits'>✨ Built by Sreelekha Putta | UML Diagrams Now Live! ✨</div>")
360
 
361
  def on_generate(file_obj, code_str, language, export_format, selected_features):
362
  if file_obj is not None:
363
  result = process_uploaded_file(file_obj, language, export_format, selected_features)
364
+ elif code_str.strip():
365
  result = generate_documentation(code_str, language, export_format, selected_features)
366
  else:
367
+ return "❌ Please upload a file or paste code!", None
368
+
369
  if export_format == "PDF":
370
  return None, gr.update(value=result, visible=True)
371
  else:
 
377
  outputs=[output_box, pdf_output]
378
  )
379
 
380
+ if __name__ == "__main__":
381
+ demo.launch(share=True, show_error=True)