rU-ShawJI-07 commited on
Commit
e0fe29d
·
verified ·
1 Parent(s): 32467cc

Update image.py

Browse files
Files changed (1) hide show
  1. image.py +81 -85
image.py CHANGED
@@ -12,10 +12,10 @@ import logging
12
  logging.basicConfig(level=logging.INFO)
13
  logger = logging.getLogger(__name__)
14
 
15
- # Define symbolic variables for polynomial and linear system solving
16
  x, y = sp.symbols('x y')
17
 
18
- # Initialize Pix2Text model globally to avoid reloading
19
  try:
20
  p2t_model = Pix2Text.from_config()
21
  logger.info("Pix2Text model loaded successfully")
@@ -28,43 +28,46 @@ def clean_latex_expression(latex_str):
28
  if not latex_str:
29
  return ""
30
 
31
- # Remove LaTeX delimiters and normalize
32
  latex_str = latex_str.strip()
33
  latex_str = re.sub(r'^\$\$|\$\$$', '', latex_str) # Remove $$ delimiters
34
  latex_str = re.sub(r'\\[a-zA-Z]+\{([^}]*)\}', r'\1', latex_str) # Remove LaTeX commands
35
  latex_str = re.sub(r'\\{2,}', r'\\', latex_str) # Fix multiple backslashes
36
  latex_str = re.sub(r'\s+', ' ', latex_str) # Normalize whitespace
37
  latex_str = re.sub(r'\^{([^}]+)}', r'**\1', latex_str) # Convert x^{n} to x**n
38
- latex_str = re.sub(r'(\d+|\w)\s*([xy])', r'\1*\2', latex_str) # Add multiplication sign: 2x -> 2*x
39
  latex_str = re.sub(r'\s*([+\-*/=])\s*', r'\1', latex_str) # Remove spaces around operators
40
- latex_str = latex_str.replace('=', '-') # Move right-hand side to left for SymPy
 
 
41
  return latex_str.strip()
42
 
43
  def parse_equation_type(latex_str):
44
- """Determine if the equation is polynomial or linear system"""
45
  try:
46
  cleaned = clean_latex_expression(latex_str)
47
- # Check for system indicators
48
- if '\\\\' in latex_str or '\n' in latex_str or 'system' in latex_str.lower():
49
- return 'linear_system'
50
-
51
- # Parse with SymPy to determine type
 
 
 
 
 
52
  try:
53
  expr = sp.sympify(cleaned.split('-')[0] if '-' in cleaned else cleaned)
54
- if x in expr.free_symbols and y in expr.free_symbols:
55
- return 'linear_system'
56
- elif x in expr.free_symbols:
57
  degree = sp.degree(expr, x)
58
- return 'polynomial' if degree > 0 else 'linear_system'
 
 
59
  else:
60
- return 'polynomial'
61
  except:
62
- # Fallback to regex-based detection
63
  if 'x**' in cleaned or '^' in latex_str:
64
  return 'polynomial'
65
- elif 'x' in cleaned and 'y' in cleaned:
66
- return 'linear_system'
67
- return 'polynomial'
68
  except Exception as e:
69
  logger.error(f"Error determining equation type: {e}")
70
  return 'polynomial'
@@ -74,28 +77,27 @@ def extract_polynomial_coefficients(latex_str):
74
  try:
75
  cleaned = clean_latex_expression(latex_str)
76
  if '-' in cleaned:
77
- cleaned = cleaned.split('-')[0].strip()
78
-
79
- # Parse with SymPy
80
  expr = sp.sympify(cleaned, evaluate=False)
81
- if x not in expr.free_symbols:
82
- raise ValueError("No variable x found in expression")
83
 
84
- # Get polynomial degree
85
- degree = sp.degree(expr, x)
86
  if degree < 1 or degree > 8:
87
  raise ValueError(f"Polynomial degree {degree} is out of supported range (1-8)")
88
-
89
- # Extract coefficients
90
- poly = sp.Poly(expr, x)
91
- coeffs = [float(poly.coeff_monomial(x**i)) for i in range(degree, -1, -1)]
92
 
93
  return {
94
  "type": "polynomial",
95
  "degree": degree,
96
  "coeffs": " ".join(map(str, coeffs)),
97
  "latex": latex_str,
98
- "success": True
 
99
  }
100
  except Exception as e:
101
  logger.error(f"Error extracting polynomial coefficients: {e}")
@@ -105,35 +107,32 @@ def extract_polynomial_coefficients(latex_str):
105
  "coeffs": "1 0 0",
106
  "latex": latex_str,
107
  "success": False,
108
- "error": str(e)
 
109
  }
110
 
111
  def extract_linear_system_coefficients(latex_str):
112
  """Extract linear system coefficients from LaTeX string"""
113
  try:
114
  cleaned = clean_latex_expression(latex_str)
115
- equations = re.split(r'\\\\|\n|;', cleaned)
116
  if len(equations) < 2:
117
  equations = re.split(r'(?<=[0-9])\s*(?=[+-]?\s*[0-9]*[xy])', cleaned)
118
 
119
- if len(equations) < 2:
120
- raise ValueError("Could not find two equations in system")
121
-
122
  eq1_str = equations[0].strip()
123
  eq2_str = equations[1].strip()
124
 
125
  def parse_linear_eq(eq_str):
126
- # Convert to standard form ax + by = c
127
  if '-' not in eq_str:
128
  raise ValueError("No equals sign (converted to '-') found")
129
-
130
  left, right = eq_str.split('-')
131
  expr = sp.sympify(left) - sp.sympify(right or '0')
132
-
133
  a = float(expr.coeff(x, 1)) if expr.coeff(x, 1) else 0
134
  b = float(expr.coeff(y, 1)) if expr.coeff(y, 1) else 0
135
  c = float(-expr.as_coefficients_dict()[1]) if 1 in expr.as_coefficients_dict() else 0
136
-
137
  return f"{a} {b} {c}"
138
 
139
  eq1_coeffs = parse_linear_eq(eq1_str)
@@ -163,18 +162,17 @@ def extract_equation_from_image(image_file):
163
  if p2t_model is None:
164
  return {
165
  "type": "error",
166
- "latex": "Pix2Text model not loaded. Please check installation.",
167
  "success": False
168
  }
169
 
170
  if image_file is None:
171
  return {
172
  "type": "error",
173
- "latex": "No image file provided.",
174
  "success": False
175
  }
176
 
177
- # Open and process the image
178
  if isinstance(image_file, str):
179
  image = Image.open(image_file)
180
  else:
@@ -189,7 +187,7 @@ def extract_equation_from_image(image_file):
189
  if not result or result.strip() == "":
190
  return {
191
  "type": "error",
192
- "latex": "No text or formulas detected in the image.",
193
  "success": False
194
  }
195
 
@@ -198,13 +196,19 @@ def extract_equation_from_image(image_file):
198
  eq_type = parse_equation_type(result)
199
  if eq_type == 'polynomial':
200
  return extract_polynomial_coefficients(result)
201
- else:
202
  return extract_linear_system_coefficients(result)
 
 
 
 
 
 
203
  except Exception as e:
204
  logger.error(f"Error processing image: {e}")
205
  return {
206
  "type": "error",
207
- "latex": f"Error processing image: {str(e)}",
208
  "success": False
209
  }
210
 
@@ -316,6 +320,8 @@ def solve_extracted_equation(eq_data, real_only):
316
  if eq_data["type"] == "polynomial":
317
  return solve_polynomial(eq_data["degree"], eq_data["coeffs"], real_only)
318
  elif eq_data["type"] == "linear":
 
 
319
  return solve_linear_system_from_coeffs(eq_data["eq1_coeffs"], eq_data["eq2_coeffs"])
320
  else:
321
  return "❌ Unknown equation type", None, ""
@@ -331,49 +337,46 @@ def image_tab():
331
  file_types=[".pdf", ".png", ".jpg", ".jpeg"],
332
  file_count="single"
333
  )
334
- image_upload_btn = gr.Button("📤 Process Image")
335
 
336
  gr.Markdown("**Supported Formats:** .pdf, .png, .jpg, .jpeg")
337
 
338
  with gr.Row():
339
  real_image_checkbox = gr.Checkbox(label="Show Only Real Roots (for Polynomials)", value=False)
340
- preview_image_btn = gr.Button("🔍 Preview Equation")
341
 
342
  image_equation_display = gr.Markdown()
343
 
344
  with gr.Row():
345
- confirm_image_btn = gr.Button("Display Solution", visible=False)
346
- edit_image_btn = gr.Button("✏️ Make Changes Manually", visible=False)
347
 
348
  edit_latex_input = gr.Textbox(label="Edit LaTeX Equation", visible=False, lines=3)
349
- save_edit_btn = gr.Button("💾 Save Changes", visible=False)
350
 
351
  image_steps_md = gr.Markdown()
352
  image_plot_output = gr.Plot()
353
- image_error_box = gr.Textbox(label="Status", visible=True, interactive=False)
354
  extracted_eq_state = gr.State()
355
 
356
  def handle_image_upload(image_file):
357
  """Handle image upload and initial processing"""
358
  if image_file is None:
359
- return "⚠️ Please upload an image.", None, "", None, None
360
 
361
  try:
362
  eq_data = extract_equation_from_image(image_file)
363
  if eq_data["success"]:
364
- status = " Image processed successfully. Click 'Preview Equation' to see the extracted equation."
365
  else:
366
- status = f"⚠️ Processing completed with issues: {eq_data.get('error', 'Unknown error')}"
367
-
368
- return status, eq_data, "", None, None
369
  except Exception as e:
370
- return f"❌ Error processing image: {str(e)}", None, "", None, None
371
 
372
  image_upload_btn.click(
373
  fn=handle_image_upload,
374
  inputs=[image_input],
375
- outputs=[image_error_box, extracted_eq_state, image_equation_display,
376
- image_steps_md, image_plot_output]
377
  )
378
 
379
  def preview_image_equation(eq_data, real_only):
@@ -387,16 +390,14 @@ def image_tab():
387
 
388
  if eq_data["type"] == "polynomial":
389
  eq_type_display = "Polynomial Equation"
390
- details = f"Degree: {eq_data['degree']}, Coefficients: {eq_data['coeffs']}"
391
- else:
392
  eq_type_display = "Linear System"
393
- details = f"Equation 1: {eq_data['eq1_coeffs']}, Equation 2: {eq_data['eq2_coeffs']}"
394
-
 
395
  preview_text = f"""
396
  ### ✅ Confirm {eq_type_display}
397
  **Extracted LaTeX:** {eq_data['latex']}
398
- **Parsed Details:** {details}
399
- **Status:** {'✅ Successfully parsed' if eq_data.get('success', True) else '⚠️ Parsing had issues but proceeding with defaults'}
400
  """
401
 
402
  return (preview_text, gr.update(visible=True), gr.update(visible=True), "", None)
@@ -411,25 +412,18 @@ def image_tab():
411
  def confirm_image_solution(eq_data, real_only):
412
  """Confirm and solve the extracted equation"""
413
  if eq_data is None or eq_data["type"] == "error":
414
- return "⚠️ No valid equation to solve.", None, "No equation available"
415
 
416
  try:
417
- if eq_data["type"] == "polynomial":
418
- steps, plot, error = solve_polynomial(eq_data["degree"], eq_data["coeffs"], real_only)
419
- return steps, plot, error if error else "✅ Solution completed"
420
- elif eq_data["type"] == "linear":
421
- eq_latex, steps, plot, error = solve_linear_system_from_coeffs(
422
- eq_data["eq1_coeffs"], eq_data["eq2_coeffs"])
423
- return steps, plot, error if error else "✅ Solution completed"
424
- else:
425
- return "❌ Unknown equation type", None, "Unknown equation type"
426
  except Exception as e:
427
- return f"❌ Error solving equation: {str(e)}", None, str(e)
428
 
429
  confirm_image_btn.click(
430
  fn=confirm_image_solution,
431
  inputs=[extracted_eq_state, real_image_checkbox],
432
- outputs=[image_steps_md, image_plot_output, image_error_box]
433
  )
434
 
435
  def enable_manual_edit(eq_data):
@@ -456,27 +450,29 @@ def image_tab():
456
  """Save manual changes and solve"""
457
  try:
458
  if not latex_input or latex_input.strip() == "":
459
- return "⚠️ Please enter a valid equation.", None, "Empty input"
460
 
461
  eq_type = parse_equation_type(latex_input)
462
  if eq_type == 'polynomial':
463
  eq_data = extract_polynomial_coefficients(latex_input)
464
  steps, plot, error = solve_polynomial(eq_data["degree"], eq_data["coeffs"], real_only)
465
- else:
466
  eq_data = extract_linear_system_coefficients(latex_input)
467
  eq_latex, steps, plot, error = solve_linear_system_from_coeffs(
468
  eq_data["eq1_coeffs"], eq_data["eq2_coeffs"])
 
 
469
 
470
- return steps, plot, error if error else "✅ Manual equation solved"
471
  except Exception as e:
472
- return f"❌ Error parsing manual input: {str(e)}", None, str(e)
473
 
474
  save_edit_btn.click(
475
  fn=save_manual_changes,
476
  inputs=[edit_latex_input, real_image_checkbox],
477
- outputs=[image_steps_md, image_plot_output, image_error_box]
478
  )
479
 
480
  return (image_input, image_upload_btn, real_image_checkbox, preview_image_btn,
481
  image_equation_display, confirm_image_btn, edit_image_btn, edit_latex_input,
482
- save_edit_btn, image_steps_md, image_plot_output, image_error_box, extracted_eq_state)
 
12
  logging.basicConfig(level=logging.INFO)
13
  logger = logging.getLogger(__name__)
14
 
15
+ # Define symbolic variables
16
  x, y = sp.symbols('x y')
17
 
18
+ # Initialize Pix2Text model globally
19
  try:
20
  p2t_model = Pix2Text.from_config()
21
  logger.info("Pix2Text model loaded successfully")
 
28
  if not latex_str:
29
  return ""
30
 
 
31
  latex_str = latex_str.strip()
32
  latex_str = re.sub(r'^\$\$|\$\$$', '', latex_str) # Remove $$ delimiters
33
  latex_str = re.sub(r'\\[a-zA-Z]+\{([^}]*)\}', r'\1', latex_str) # Remove LaTeX commands
34
  latex_str = re.sub(r'\\{2,}', r'\\', latex_str) # Fix multiple backslashes
35
  latex_str = re.sub(r'\s+', ' ', latex_str) # Normalize whitespace
36
  latex_str = re.sub(r'\^{([^}]+)}', r'**\1', latex_str) # Convert x^{n} to x**n
37
+ latex_str = re.sub(r'(\d*\.?\d+)\s*([xy])', r'\1*\2', latex_str) # Add multiplication: 1.0x -> 1.0*x
38
  latex_str = re.sub(r'\s*([+\-*/=])\s*', r'\1', latex_str) # Remove spaces around operators
39
+ if '=' in latex_str:
40
+ left, right = latex_str.split('=')
41
+ latex_str = f"{left} - ({right})" # Move right-hand side to left
42
  return latex_str.strip()
43
 
44
  def parse_equation_type(latex_str):
45
+ """Determine if the equation is polynomial (single-variable) or linear system (two-variable)"""
46
  try:
47
  cleaned = clean_latex_expression(latex_str)
48
+ if not cleaned:
49
+ return 'polynomial'
50
+
51
+ # Check for two-variable system
52
+ if 'y' in cleaned and 'x' in cleaned:
53
+ if '\\\\' in latex_str or '\n' in latex_str or len(re.split(r'\\\\|\n|;', latex_str)) >= 2:
54
+ return 'linear_system'
55
+ return 'linear' # Single equation with x and y
56
+
57
+ # Check for single-variable polynomial
58
  try:
59
  expr = sp.sympify(cleaned.split('-')[0] if '-' in cleaned else cleaned)
60
+ if x in expr.free_symbols and y not in expr.free_symbols:
 
 
61
  degree = sp.degree(expr, x)
62
+ return 'polynomial' if degree > 0 else 'linear'
63
+ elif x not in expr.free_symbols and y in expr.free_symbols:
64
+ return 'polynomial' # Treat as polynomial in y if x is absent
65
  else:
66
+ return 'polynomial' # Default to polynomial if no clear variables
67
  except:
 
68
  if 'x**' in cleaned or '^' in latex_str:
69
  return 'polynomial'
70
+ return 'polynomial' # Fallback to polynomial
 
 
71
  except Exception as e:
72
  logger.error(f"Error determining equation type: {e}")
73
  return 'polynomial'
 
77
  try:
78
  cleaned = clean_latex_expression(latex_str)
79
  if '-' in cleaned:
80
+ cleaned = cleaned.split('-')[0].strip() # Use left side for polynomial
81
+
 
82
  expr = sp.sympify(cleaned, evaluate=False)
83
+ if x not in expr.free_symbols and y not in expr.free_symbols:
84
+ raise ValueError("No variable (x or y) found in expression")
85
 
86
+ variable = x if x in expr.free_symbols else y
87
+ degree = sp.degree(expr, variable)
88
  if degree < 1 or degree > 8:
89
  raise ValueError(f"Polynomial degree {degree} is out of supported range (1-8)")
90
+
91
+ poly = sp.Poly(expr, variable)
92
+ coeffs = [float(poly.coeff_monomial(variable**i)) for i in range(degree, -1, -1)]
 
93
 
94
  return {
95
  "type": "polynomial",
96
  "degree": degree,
97
  "coeffs": " ".join(map(str, coeffs)),
98
  "latex": latex_str,
99
+ "success": True,
100
+ "variable": str(variable)
101
  }
102
  except Exception as e:
103
  logger.error(f"Error extracting polynomial coefficients: {e}")
 
107
  "coeffs": "1 0 0",
108
  "latex": latex_str,
109
  "success": False,
110
+ "error": str(e),
111
+ "variable": "x"
112
  }
113
 
114
  def extract_linear_system_coefficients(latex_str):
115
  """Extract linear system coefficients from LaTeX string"""
116
  try:
117
  cleaned = clean_latex_expression(latex_str)
118
+ equations = re.split(r'\\\\|\n|;', latex_str)
119
  if len(equations) < 2:
120
  equations = re.split(r'(?<=[0-9])\s*(?=[+-]?\s*[0-9]*[xy])', cleaned)
121
 
122
+ if len(equations) < 2 or 'y' not in cleaned or 'x' not in cleaned:
123
+ raise ValueError("Could not find two equations or two variables (x, y) in system")
124
+
125
  eq1_str = equations[0].strip()
126
  eq2_str = equations[1].strip()
127
 
128
  def parse_linear_eq(eq_str):
 
129
  if '-' not in eq_str:
130
  raise ValueError("No equals sign (converted to '-') found")
 
131
  left, right = eq_str.split('-')
132
  expr = sp.sympify(left) - sp.sympify(right or '0')
 
133
  a = float(expr.coeff(x, 1)) if expr.coeff(x, 1) else 0
134
  b = float(expr.coeff(y, 1)) if expr.coeff(y, 1) else 0
135
  c = float(-expr.as_coefficients_dict()[1]) if 1 in expr.as_coefficients_dict() else 0
 
136
  return f"{a} {b} {c}"
137
 
138
  eq1_coeffs = parse_linear_eq(eq1_str)
 
162
  if p2t_model is None:
163
  return {
164
  "type": "error",
165
+ "latex": "Pix2Text model not loaded. Please check installation.",
166
  "success": False
167
  }
168
 
169
  if image_file is None:
170
  return {
171
  "type": "error",
172
+ "latex": "No image file provided.",
173
  "success": False
174
  }
175
 
 
176
  if isinstance(image_file, str):
177
  image = Image.open(image_file)
178
  else:
 
187
  if not result or result.strip() == "":
188
  return {
189
  "type": "error",
190
+ "latex": "No text or formulas detected in the image.",
191
  "success": False
192
  }
193
 
 
196
  eq_type = parse_equation_type(result)
197
  if eq_type == 'polynomial':
198
  return extract_polynomial_coefficients(result)
199
+ elif eq_type == 'linear_system':
200
  return extract_linear_system_coefficients(result)
201
+ else:
202
+ return {
203
+ "type": "error",
204
+ "latex": f"Unsupported equation type detected: {eq_type}",
205
+ "success": False
206
+ }
207
  except Exception as e:
208
  logger.error(f"Error processing image: {e}")
209
  return {
210
  "type": "error",
211
+ "latex": f"Error processing image: {str(e)}",
212
  "success": False
213
  }
214
 
 
320
  if eq_data["type"] == "polynomial":
321
  return solve_polynomial(eq_data["degree"], eq_data["coeffs"], real_only)
322
  elif eq_data["type"] == "linear":
323
+ return "❌ Single linear equation not supported. Please upload a system of equations.", None, ""
324
+ elif eq_data["type"] == "linear_system":
325
  return solve_linear_system_from_coeffs(eq_data["eq1_coeffs"], eq_data["eq2_coeffs"])
326
  else:
327
  return "❌ Unknown equation type", None, ""
 
337
  file_types=[".pdf", ".png", ".jpg", ".jpeg"],
338
  file_count="single"
339
  )
340
+ image_upload_btn = gr.Button("Process Image")
341
 
342
  gr.Markdown("**Supported Formats:** .pdf, .png, .jpg, .jpeg")
343
 
344
  with gr.Row():
345
  real_image_checkbox = gr.Checkbox(label="Show Only Real Roots (for Polynomials)", value=False)
346
+ preview_image_btn = gr.Button("Preview Equation")
347
 
348
  image_equation_display = gr.Markdown()
349
 
350
  with gr.Row():
351
+ confirm_image_btn = gr.Button("Display Solution", visible=False)
352
+ edit_image_btn = gr.Button("Make Changes Manually", visible=False)
353
 
354
  edit_latex_input = gr.Textbox(label="Edit LaTeX Equation", visible=False, lines=3)
355
+ save_edit_btn = gr.Button("Save Changes", visible=False)
356
 
357
  image_steps_md = gr.Markdown()
358
  image_plot_output = gr.Plot()
 
359
  extracted_eq_state = gr.State()
360
 
361
  def handle_image_upload(image_file):
362
  """Handle image upload and initial processing"""
363
  if image_file is None:
364
+ return "", None, "", None, None
365
 
366
  try:
367
  eq_data = extract_equation_from_image(image_file)
368
  if eq_data["success"]:
369
+ return "", eq_data, "", None, None
370
  else:
371
+ return "", eq_data, "", None, None
 
 
372
  except Exception as e:
373
+ return "", None, "", None, None
374
 
375
  image_upload_btn.click(
376
  fn=handle_image_upload,
377
  inputs=[image_input],
378
+ outputs=[image_equation_display, extracted_eq_state, image_steps_md,
379
+ image_plot_output, edit_latex_input]
380
  )
381
 
382
  def preview_image_equation(eq_data, real_only):
 
390
 
391
  if eq_data["type"] == "polynomial":
392
  eq_type_display = "Polynomial Equation"
393
+ elif eq_data["type"] == "linear_system":
 
394
  eq_type_display = "Linear System"
395
+ else:
396
+ eq_type_display = "Unknown Equation Type"
397
+
398
  preview_text = f"""
399
  ### ✅ Confirm {eq_type_display}
400
  **Extracted LaTeX:** {eq_data['latex']}
 
 
401
  """
402
 
403
  return (preview_text, gr.update(visible=True), gr.update(visible=True), "", None)
 
412
  def confirm_image_solution(eq_data, real_only):
413
  """Confirm and solve the extracted equation"""
414
  if eq_data is None or eq_data["type"] == "error":
415
+ return "⚠️ No valid equation to solve.", None, ""
416
 
417
  try:
418
+ steps, plot, error = solve_extracted_equation(eq_data, real_only)
419
+ return steps, plot, ""
 
 
 
 
 
 
 
420
  except Exception as e:
421
+ return f"❌ Error solving equation: {str(e)}", None, ""
422
 
423
  confirm_image_btn.click(
424
  fn=confirm_image_solution,
425
  inputs=[extracted_eq_state, real_image_checkbox],
426
+ outputs=[image_steps_md, image_plot_output, image_equation_display]
427
  )
428
 
429
  def enable_manual_edit(eq_data):
 
450
  """Save manual changes and solve"""
451
  try:
452
  if not latex_input or latex_input.strip() == "":
453
+ return "⚠️ Please enter a valid equation.", None, ""
454
 
455
  eq_type = parse_equation_type(latex_input)
456
  if eq_type == 'polynomial':
457
  eq_data = extract_polynomial_coefficients(latex_input)
458
  steps, plot, error = solve_polynomial(eq_data["degree"], eq_data["coeffs"], real_only)
459
+ elif eq_type == 'linear_system':
460
  eq_data = extract_linear_system_coefficients(latex_input)
461
  eq_latex, steps, plot, error = solve_linear_system_from_coeffs(
462
  eq_data["eq1_coeffs"], eq_data["eq2_coeffs"])
463
+ else:
464
+ return "❌ Unsupported equation type", None, ""
465
 
466
+ return steps, plot, ""
467
  except Exception as e:
468
+ return f"❌ Error parsing manual input: {str(e)}", None, ""
469
 
470
  save_edit_btn.click(
471
  fn=save_manual_changes,
472
  inputs=[edit_latex_input, real_image_checkbox],
473
+ outputs=[image_steps_md, image_plot_output, image_equation_display]
474
  )
475
 
476
  return (image_input, image_upload_btn, real_image_checkbox, preview_image_btn,
477
  image_equation_display, confirm_image_btn, edit_image_btn, edit_latex_input,
478
+ save_edit_btn, image_steps_md, image_plot_output, extracted_eq_state)