Milhaud commited on
Commit
cb78f99
·
1 Parent(s): e376086

refactor: remove commented code and improve function documentation

Browse files
Files changed (1) hide show
  1. app.py +19 -45
app.py CHANGED
@@ -38,7 +38,6 @@ class SVGAnimationGenerator:
38
 
39
  def parse_svg(self, svg_content: str) -> dict:
40
  try:
41
- # Remove namespace definitions and simplify the <svg> tag
42
  svg_content = re.sub(r'xmlns[^=]*="[^"]*"', "", svg_content)
43
  svg_content = re.sub(r"<svg[^>]*>", "<svg>", svg_content)
44
  return {"svg_content": svg_content}
@@ -49,7 +48,6 @@ class SVGAnimationGenerator:
49
  try:
50
  svg_content = parsed_svg["svg_content"]
51
 
52
- # Convert SVG to PNG for MLLM analysis
53
  image_media_type, image_data = svg_to_png_base64(svg_content)
54
 
55
  if not image_data:
@@ -90,7 +88,6 @@ class SVGAnimationGenerator:
90
  decomposed_svg_match = re.search(
91
  r"<decomposed_svg>(.*?)</decomposed_svg>", response_text, re.DOTALL
92
  )
93
- # 오타 수정: suggenstions → suggestions
94
  animation_suggestions_match = re.search(
95
  r"<animation_suggestions>(.*?)</animation_suggestions>",
96
  response_text,
@@ -146,7 +143,6 @@ class SVGAnimationGenerator:
146
  decomposed_svg_match = re.search(
147
  r"<decomposed_svg>(.*?)</decomposed_svg>", response_text, re.DOTALL
148
  )
149
- # 오타 수정: suggenstions → suggestions
150
  animation_suggestions_match = re.search(
151
  r"<animation_suggestions>(.*?)</animation_suggestions>",
152
  response_text,
@@ -157,7 +153,6 @@ class SVGAnimationGenerator:
157
  decomposed_svg_text = decomposed_svg_match.group(1).strip()
158
  animation_suggestions = animation_suggestions_match.group(1).strip()
159
 
160
- # Create viewer HTML with XSS protection
161
  viewer_html = create_svg_viewer_html(decomposed_svg_text)
162
 
163
  return decomposed_svg_text, animation_suggestions, viewer_html
@@ -207,28 +202,20 @@ class SVGAnimationGenerator:
207
 
208
 
209
  def _sanitize_svg(svg: str) -> str:
210
- """안전한 미리보기를 위해 script 태그/inline on* 핸들러 제거"""
211
- # <script>...</script> 제거
212
  svg = re.sub(r"<\s*script\b[^>]*>.*?<\s*/\s*script\s*>", "", svg, flags=re.I | re.S)
213
- # onload, onclick 등 inline 핸들러 제거
214
  svg = re.sub(r"\son[a-zA-Z]+\s*=\s*(['\"]).*?\1", "", svg, flags=re.I | re.S)
215
  return svg
216
 
217
 
218
  def _fix_svg_markup(svg: str) -> str:
219
- """뷰어에 맞게 SVG 속성 정리(크기/비율/문제 스타일 수정)"""
220
  s = svg
221
-
222
- # preserveAspectRatio 보정
223
  s = s.replace('preserveAspectRatio="none"', 'preserveAspectRatio="xMidYMid meet"')
224
-
225
- # position:absolute 등 문제 스타일 제거/대체
226
  s = s.replace(
227
  'style="display: block; overflow: hidden; position: absolute; left: 0px; top: 0px;"',
228
  'style="display:block; width:100%; height:100%;"',
229
  )
230
-
231
- # width/height 절대값 → 상대값
232
  s = re.sub(r'width="[^"]+"', 'width="100%"', s, count=1)
233
  s = re.sub(r'height="[^"]+"', 'height="100%"', s, count=1)
234
 
@@ -430,13 +417,10 @@ def create_svg_viewer_html(svg_content: str) -> str:
430
 
431
 
432
  def _extract_path_from_gradio_file(svg_file) -> str | None:
433
- # gr.File 값이 문자열 경로인 경우
434
  if isinstance(svg_file, (str, pathlib.Path)):
435
  return str(svg_file)
436
- # dict 형태({ 'name': '/tmp/...' })로 들어오는 경우
437
  if isinstance(svg_file, dict) and "name" in svg_file:
438
  return svg_file["name"]
439
- # NamedString 등 파일 객체에 name 속성이 있는 경우
440
  if hasattr(svg_file, "name"):
441
  return svg_file.name
442
  return None
@@ -492,14 +476,13 @@ def predict_decompose_group(svg_file, svg_text, object_name):
492
  decomposed_svg = decompose_result["svg_content"]
493
  animation_suggestions = decompose_result["animation_suggestions"]
494
 
495
- # Create viewer HTML
496
  decomposed_svg_viewer = create_svg_viewer_html(decomposed_svg)
497
 
498
  return (
499
- decomposed_svg, # For svg_content_hidden
500
- decomposed_svg, # For groups_summary (분석 결과 표시)
501
- animation_suggestions, # For animation_suggestion
502
- decomposed_svg_viewer, # For decomposed_svg_viewer
503
  )
504
 
505
 
@@ -509,7 +492,6 @@ def update_preview_from_html(html_content: str) -> str:
509
  return create_error_html("⚠️ HTML content is empty")
510
 
511
  try:
512
- # Create iframe HTML - only escape quotes for srcdoc attribute
513
  safe_html_content = html_content.replace('"', "&quot;")
514
  preview_html = f"""
515
  <div style='padding: 20px; width:100%; height:520px; background: #fff; border: 1px solid #eee; border-radius: 8px; display: block;'>
@@ -549,7 +531,6 @@ def create_animation_preview(animation_desc: str, svg_content: str) -> tuple:
549
  error_html = create_error_html("❌ Failed to generate animation HTML")
550
  return error_html, animation_response
551
 
552
- # Save the HTML content to the output directory
553
  output_dir = "output"
554
  os.makedirs(output_dir, exist_ok=True)
555
  html_path = os.path.join(output_dir, "animation_preview.html")
@@ -557,7 +538,6 @@ def create_animation_preview(animation_desc: str, svg_content: str) -> tuple:
557
  f.write(html_content)
558
  print(f"Animation preview saved to: {html_path}")
559
 
560
- # Create iframe HTML - only escape quotes for srcdoc attribute
561
  fixed_html_content = fix_html_styles_for_preview(html_content)
562
  safe_html_content = fixed_html_content.replace('"', "&quot;")
563
 
@@ -594,15 +574,13 @@ example_list = {
594
 
595
 
596
  def load_example(example_choice):
597
- # Find the selected example in the categories
598
  for category, examples in example_list.items():
599
  for example in examples:
600
  if example[1] == example_choice:
601
- return example[0], example[1] # Return both file path and object name
602
  return None, None
603
 
604
 
605
- # Flatten choices for dropdown
606
  example_choices = [
607
  example[1] for category in example_list.values() for example in category
608
  ]
@@ -624,7 +602,6 @@ with demo:
624
  lines=8.4,
625
  )
626
 
627
- # Add example dropdown
628
  example_dropdown = gr.Dropdown(
629
  choices=example_choices, label="Try an Example", value=None
630
  )
@@ -639,7 +616,6 @@ with demo:
639
  value="corgi",
640
  )
641
 
642
- # Add dropdown change event
643
  example_dropdown.change(
644
  fn=load_example,
645
  inputs=[example_dropdown],
@@ -715,31 +691,30 @@ with demo:
715
  label="Output HTML",
716
  lines=10,
717
  placeholder="Generated HTML will appear here. You can edit this HTML and see live preview.",
718
- interactive=True, # Make sure it's editable
719
  )
720
 
721
- # Event handlers
722
  process_btn.click(
723
  fn=predict_decompose_group,
724
  inputs=[svg_file, svg_text, object_name],
725
  outputs=[
726
- svg_content_hidden, # Store decomposed SVG for later use
727
- groups_summary, # Show analysis results
728
- animation_suggestion, # Show animation suggestions
729
- decomposed_svg_viewer, # Show SVG preview
730
  ],
731
  )
732
 
733
  groups_feedback_btn.click(
734
  fn=generator.feedback_decompose_group,
735
  inputs=[
736
- svg_content_hidden, # Pass the SVG content directly
737
- groups_feedback, # Pass the feedback text
738
  ],
739
  outputs=[
740
- svg_content_hidden, # Update hidden SVG content
741
- animation_suggestion, # Update animation suggestions
742
- decomposed_svg_viewer, # Update SVG preview
743
  ],
744
  )
745
 
@@ -750,12 +725,11 @@ with demo:
750
  svg_content_hidden,
751
  ],
752
  outputs=[
753
- animation_preview, # Animation preview HTML
754
- output_html, # Raw HTML output
755
  ],
756
  )
757
 
758
- # Real-time preview update when HTML is manually edited
759
  output_html.change(
760
  fn=update_preview_from_html,
761
  inputs=[output_html],
 
38
 
39
  def parse_svg(self, svg_content: str) -> dict:
40
  try:
 
41
  svg_content = re.sub(r'xmlns[^=]*="[^"]*"', "", svg_content)
42
  svg_content = re.sub(r"<svg[^>]*>", "<svg>", svg_content)
43
  return {"svg_content": svg_content}
 
48
  try:
49
  svg_content = parsed_svg["svg_content"]
50
 
 
51
  image_media_type, image_data = svg_to_png_base64(svg_content)
52
 
53
  if not image_data:
 
88
  decomposed_svg_match = re.search(
89
  r"<decomposed_svg>(.*?)</decomposed_svg>", response_text, re.DOTALL
90
  )
 
91
  animation_suggestions_match = re.search(
92
  r"<animation_suggestions>(.*?)</animation_suggestions>",
93
  response_text,
 
143
  decomposed_svg_match = re.search(
144
  r"<decomposed_svg>(.*?)</decomposed_svg>", response_text, re.DOTALL
145
  )
 
146
  animation_suggestions_match = re.search(
147
  r"<animation_suggestions>(.*?)</animation_suggestions>",
148
  response_text,
 
153
  decomposed_svg_text = decomposed_svg_match.group(1).strip()
154
  animation_suggestions = animation_suggestions_match.group(1).strip()
155
 
 
156
  viewer_html = create_svg_viewer_html(decomposed_svg_text)
157
 
158
  return decomposed_svg_text, animation_suggestions, viewer_html
 
202
 
203
 
204
  def _sanitize_svg(svg: str) -> str:
205
+ """Script tag/inline on*Handler removal for safe preview"""
 
206
  svg = re.sub(r"<\s*script\b[^>]*>.*?<\s*/\s*script\s*>", "", svg, flags=re.I | re.S)
 
207
  svg = re.sub(r"\son[a-zA-Z]+\s*=\s*(['\"]).*?\1", "", svg, flags=re.I | re.S)
208
  return svg
209
 
210
 
211
  def _fix_svg_markup(svg: str) -> str:
212
+ """SVG attribute to fit the viewer (size/ratio/problem style modification)"""
213
  s = svg
 
 
214
  s = s.replace('preserveAspectRatio="none"', 'preserveAspectRatio="xMidYMid meet"')
 
 
215
  s = s.replace(
216
  'style="display: block; overflow: hidden; position: absolute; left: 0px; top: 0px;"',
217
  'style="display:block; width:100%; height:100%;"',
218
  )
 
 
219
  s = re.sub(r'width="[^"]+"', 'width="100%"', s, count=1)
220
  s = re.sub(r'height="[^"]+"', 'height="100%"', s, count=1)
221
 
 
417
 
418
 
419
  def _extract_path_from_gradio_file(svg_file) -> str | None:
 
420
  if isinstance(svg_file, (str, pathlib.Path)):
421
  return str(svg_file)
 
422
  if isinstance(svg_file, dict) and "name" in svg_file:
423
  return svg_file["name"]
 
424
  if hasattr(svg_file, "name"):
425
  return svg_file.name
426
  return None
 
476
  decomposed_svg = decompose_result["svg_content"]
477
  animation_suggestions = decompose_result["animation_suggestions"]
478
 
 
479
  decomposed_svg_viewer = create_svg_viewer_html(decomposed_svg)
480
 
481
  return (
482
+ decomposed_svg,
483
+ decomposed_svg,
484
+ animation_suggestions,
485
+ decomposed_svg_viewer,
486
  )
487
 
488
 
 
492
  return create_error_html("⚠️ HTML content is empty")
493
 
494
  try:
 
495
  safe_html_content = html_content.replace('"', "&quot;")
496
  preview_html = f"""
497
  <div style='padding: 20px; width:100%; height:520px; background: #fff; border: 1px solid #eee; border-radius: 8px; display: block;'>
 
531
  error_html = create_error_html("❌ Failed to generate animation HTML")
532
  return error_html, animation_response
533
 
 
534
  output_dir = "output"
535
  os.makedirs(output_dir, exist_ok=True)
536
  html_path = os.path.join(output_dir, "animation_preview.html")
 
538
  f.write(html_content)
539
  print(f"Animation preview saved to: {html_path}")
540
 
 
541
  fixed_html_content = fix_html_styles_for_preview(html_content)
542
  safe_html_content = fixed_html_content.replace('"', "&quot;")
543
 
 
574
 
575
 
576
  def load_example(example_choice):
 
577
  for category, examples in example_list.items():
578
  for example in examples:
579
  if example[1] == example_choice:
580
+ return example[0], example[1]
581
  return None, None
582
 
583
 
 
584
  example_choices = [
585
  example[1] for category in example_list.values() for example in category
586
  ]
 
602
  lines=8.4,
603
  )
604
 
 
605
  example_dropdown = gr.Dropdown(
606
  choices=example_choices, label="Try an Example", value=None
607
  )
 
616
  value="corgi",
617
  )
618
 
 
619
  example_dropdown.change(
620
  fn=load_example,
621
  inputs=[example_dropdown],
 
691
  label="Output HTML",
692
  lines=10,
693
  placeholder="Generated HTML will appear here. You can edit this HTML and see live preview.",
694
+ interactive=True,
695
  )
696
 
 
697
  process_btn.click(
698
  fn=predict_decompose_group,
699
  inputs=[svg_file, svg_text, object_name],
700
  outputs=[
701
+ svg_content_hidden,
702
+ groups_summary,
703
+ animation_suggestion,
704
+ decomposed_svg_viewer,
705
  ],
706
  )
707
 
708
  groups_feedback_btn.click(
709
  fn=generator.feedback_decompose_group,
710
  inputs=[
711
+ svg_content_hidden,
712
+ groups_feedback,
713
  ],
714
  outputs=[
715
+ svg_content_hidden,
716
+ animation_suggestion,
717
+ decomposed_svg_viewer,
718
  ],
719
  )
720
 
 
725
  svg_content_hidden,
726
  ],
727
  outputs=[
728
+ animation_preview,
729
+ output_html,
730
  ],
731
  )
732
 
 
733
  output_html.change(
734
  fn=update_preview_from_html,
735
  inputs=[output_html],