ginipick commited on
Commit
413edfb
Β·
verified Β·
1 Parent(s): 2755a30

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +230 -82
app.py CHANGED
@@ -19,6 +19,14 @@ import pandas as pd
19
  import chardet
20
  import gradio as gr
21
 
 
 
 
 
 
 
 
 
22
  # 예제 ν…œν”Œλ¦Ώ μ •μ˜
23
  EXAMPLE_TOPICS = {
24
  "λΉ„μ¦ˆλ‹ˆμŠ€ μ œμ•ˆμ„œ": "AI 기반 고객 μ„œλΉ„μŠ€ μžλ™ν™” ν”Œλž«νΌ 투자 μ œμ•ˆ",
@@ -37,6 +45,7 @@ current_slides_data = []
37
  current_topic = ""
38
  current_template = ""
39
  current_theme = ""
 
40
 
41
  # λ””μžμΈ ν…Œλ§ˆ μ •μ˜
42
  DESIGN_THEMES = {
@@ -107,7 +116,7 @@ DESIGN_THEMES = {
107
  }
108
  }
109
 
110
- # μŠ€νƒ€μΌ μ •μ˜ (ν‘œμ§€μ™€ μ’…λ£Œ μŠ¬λΌμ΄λ“œ μŠ€νƒ€μΌ μΆ”κ°€)
111
  STYLE_TEMPLATES = {
112
  "Title Slide (Hero)": {
113
  "name": "Title Slide",
@@ -143,7 +152,8 @@ STYLE_TEMPLATES = {
143
  "name": "Business Process",
144
  "description": "End-to-end business workflow with clear phases",
145
  "use_case": "ν”„λ‘œμ„ΈμŠ€ μ„€λͺ…, 단계별 μ§„ν–‰",
146
- "example": "A detailed hand-drawn diagram illustrating an end-to-end business workflow with Market Analysis, Strategy Development, Product Design, Implementation, and Post-Launch Review phases. Clear directional arrows, iconography for each component, vibrant educational yet professional style"
 
147
  },
148
  "Industrial Design": {
149
  "name": "Product Design",
@@ -203,20 +213,22 @@ STYLE_TEMPLATES = {
203
  "name": "Flowchart",
204
  "description": "Vibrant flowchart with minimalistic icons",
205
  "use_case": "μ˜μ‚¬κ²°μ •, ν”„λ‘œμ„ΈμŠ€ 흐름",
206
- "example": "FLOWCHART DESIGN: A hand-drawn style flowchart, vibrant colors, minimalistic icons showing process flow from START to END with decision points, branches, and clear directional arrows"
 
207
  }
208
  }
209
 
210
  # PPT ν…œν”Œλ¦Ώ μ •μ˜ (λ™μ μœΌλ‘œ μŠ¬λΌμ΄λ“œ 수 μ‘°μ • κ°€λŠ₯)
 
211
  PPT_TEMPLATES = {
212
  "λΉ„μ¦ˆλ‹ˆμŠ€ μ œμ•ˆμ„œ": {
213
  "description": "투자 유치, 사업 μ œμ•ˆμš©",
214
  "core_slides": [
215
- {"title": "λͺ©μ°¨", "style": "Flowchart", "prompt_hint": "ν”„λ ˆμ  ν…Œμ΄μ…˜ ꡬ쑰"},
216
  {"title": "문제 μ •μ˜", "style": "Colorful Mind Map", "prompt_hint": "ν˜„μž¬ μ‹œμž₯의 문제점"},
217
  {"title": "ν˜„ν™© 뢄석", "style": "Elegant SWOT Quadrant", "prompt_hint": "강점, 약점, 기회, μœ„ν˜‘"},
218
  {"title": "μ†”λ£¨μ…˜", "style": "Industrial Design", "prompt_hint": "μ œν’ˆ/μ„œλΉ„μŠ€ 컨셉"},
219
- {"title": "ν”„λ‘œμ„ΈμŠ€", "style": "Business Workflow", "prompt_hint": "μ‹€ν–‰ 단계"},
220
  {"title": "일정", "style": "Timeline Ribbon", "prompt_hint": "μ£Όμš” λ§ˆμΌμŠ€ν†€"}
221
  ],
222
  "optional_slides": [
@@ -226,8 +238,8 @@ PPT_TEMPLATES = {
226
  {"title": "νŒ€ μ†Œκ°œ", "style": "Colorful Mind Map", "prompt_hint": "핡심 νŒ€μ›κ³Ό μ—­λŸ‰"},
227
  {"title": "재무 κ³„νš", "style": "KPI Dashboard", "prompt_hint": "μ˜ˆμƒ 맀좜과 손읡"},
228
  {"title": "μœ„ν—˜ 관리", "style": "Risk Heat Map", "prompt_hint": "μ£Όμš” λ¦¬μŠ€ν¬μ™€ λŒ€μ‘"},
229
- {"title": "νŒŒνŠΈλ„ˆμ‹­", "style": "Business Workflow", "prompt_hint": "μ „λž΅μ  제휴"},
230
- {"title": "기술 μŠ€νƒ", "style": "Flowchart", "prompt_hint": "핡심 기술 ꡬ쑰"},
231
  {"title": "고객 사둀", "style": "Industrial Design", "prompt_hint": "성곡 사둀"},
232
  {"title": "μ„±μž₯ μ „λž΅", "style": "Timeline Ribbon", "prompt_hint": "ν™•μž₯ κ³„νš"},
233
  {"title": "투자 ν™œμš©", "style": "Pyramid/Funnel", "prompt_hint": "자금 μ‚¬μš© κ³„νš"},
@@ -240,7 +252,7 @@ PPT_TEMPLATES = {
240
  {"title": "μ œν’ˆ 컨셉", "style": "Industrial Design", "prompt_hint": "μ œν’ˆ λ””μžμΈ"},
241
  {"title": "μ‚¬μš©μž λ‹ˆμ¦ˆ", "style": "Colorful Mind Map", "prompt_hint": "고객 페인포인트"},
242
  {"title": "κΈ°λŠ₯ μ†Œκ°œ", "style": "Mobile App Mockup", "prompt_hint": "UI/UX ν™”λ©΄"},
243
- {"title": "μž‘λ™ 원리", "style": "Flowchart", "prompt_hint": "κΈ°λŠ₯ ν”Œλ‘œμš°"},
244
  {"title": "μ‹œμž₯ ν¬μ§€μ…˜", "style": "3D Bubble Chart", "prompt_hint": "κ²½μŸμ‚¬ 비ꡐ"},
245
  {"title": "μΆœμ‹œ 일정", "style": "Timeline Ribbon", "prompt_hint": "런칭 λ‘œλ“œλ§΅"}
246
  ],
@@ -248,7 +260,7 @@ PPT_TEMPLATES = {
248
  {"title": "νƒ€κ²Ÿ 고객", "style": "Colorful Mind Map", "prompt_hint": "μ£Όμš” 고객측"},
249
  {"title": "가격 μ •μ±…", "style": "Pyramid/Funnel", "prompt_hint": "가격 μ „λž΅"},
250
  {"title": "기술 μš°μœ„", "style": "Industrial Design", "prompt_hint": "핡심 기술"},
251
- {"title": "μ‚¬μš© μ‹œλ‚˜λ¦¬μ˜€", "style": "Business Workflow", "prompt_hint": "ν™œμš© 사둀"},
252
  {"title": "고객 ν›„κΈ°", "style": "KPI Dashboard", "prompt_hint": "μ‚¬μš©μž 평가"},
253
  {"title": "판맀 채널", "style": "Value Chain", "prompt_hint": "μœ ν†΅ μ „λž΅"},
254
  {"title": "λ§ˆμΌ€νŒ… μ „λž΅", "style": "Timeline Ribbon", "prompt_hint": "홍보 κ³„νš"},
@@ -258,7 +270,7 @@ PPT_TEMPLATES = {
258
  "ν”„λ‘œμ νŠΈ 보고": {
259
  "description": "μ§„ν–‰ 상황, μ„±κ³Ό 보고용",
260
  "core_slides": [
261
- {"title": "ν”„λ‘œμ νŠΈ κ°œμš”", "style": "Business Workflow", "prompt_hint": "전체 ν”„λ‘œμ„ΈμŠ€"},
262
  {"title": "μ§„ν–‰ ν˜„ν™©", "style": "Gantt Chart", "prompt_hint": "μž‘μ—… 일정"},
263
  {"title": "리슀크 관리", "style": "Risk Heat Map", "prompt_hint": "μœ„ν—˜ μš”μ†Œ"},
264
  {"title": "μ„±κ³Ό μ§€ν‘œ", "style": "KPI Dashboard", "prompt_hint": "달성 싀적"},
@@ -269,7 +281,7 @@ PPT_TEMPLATES = {
269
  {"title": "νŒ€ μ„±κ³Ό", "style": "3D Bubble Chart", "prompt_hint": "νŒ€λ³„ 기여도"},
270
  {"title": "이슈 관리", "style": "Risk Heat Map", "prompt_hint": "μ£Όμš” 이슈"},
271
  {"title": "κ°œμ„  사항", "style": "Colorful Mind Map", "prompt_hint": "ν”„λ‘œμ„ΈμŠ€ κ°œμ„ "},
272
- {"title": "κ΅ν›ˆ", "style": "Business Workflow", "prompt_hint": "배운 점"}
273
  ]
274
  },
275
  "μ „λž΅ 기획": {
@@ -285,8 +297,8 @@ PPT_TEMPLATES = {
285
  "optional_slides": [
286
  {"title": "μ‹œμž₯ 전망", "style": "3D Bubble Chart", "prompt_hint": "미래 μ‹œμž₯"},
287
  {"title": "ν˜μ‹  λ°©ν–₯", "style": "Industrial Design", "prompt_hint": "ν˜μ‹  μ „λž΅"},
288
- {"title": "쑰직 λ³€ν™”", "style": "Business Workflow", "prompt_hint": "쑰직 개편"},
289
- {"title": "λ””μ§€ν„Έ μ „ν™˜", "style": "Flowchart", "prompt_hint": "DX μ „λž΅"},
290
  {"title": "지속가λŠ₯μ„±", "style": "Timeline Ribbon", "prompt_hint": "ESG μ „λž΅"}
291
  ]
292
  },
@@ -485,9 +497,17 @@ IMPORTANT Style Guidelines:
485
  - Follow the user's instructions while maintaining this style
486
 
487
  Emoji Guidelines:
488
- - Professional emojis: πŸ“Š 🎯 πŸ’‘ πŸš€ ⚑ πŸ” πŸ“ˆ πŸ’° πŸ† πŸ”§ 🌐 πŸ” ⭐ 🎨 πŸ“± 🀝 πŸ“ πŸŽͺ πŸ—οΈ 🌱
489
  - Match emoji to content meaning
490
- - Use different emojis for variety"""
 
 
 
 
 
 
 
 
491
 
492
  user_message = f"""Topic: {topic}
493
  Slide Title: {slide_title}
@@ -523,37 +543,57 @@ Rewrite the content following the instruction above while maintaining concise, n
523
  result = response.json()
524
  content = result['choices'][0]['message']['content'].strip()
525
 
526
- # Parse content
 
 
527
  lines = content.split('\n')
528
  subtitle = ""
529
  bullet_points = []
530
 
531
  for line in lines:
532
- if line.startswith("Subtitle:"):
533
- subtitle = line.replace("Subtitle:", "").strip()
534
- elif line.strip().startswith("β€’"):
535
- bullet_points.append(line.strip())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
536
 
537
  # ν•œκΈ€λ‘œ λ²ˆμ—­μ΄ ν•„μš”ν•œ 경우
538
  if any(ord('κ°€') <= ord(char) <= ord('힣') for char in topic):
539
  subtitle = translate_content_to_korean(subtitle)
540
- # 뢈릿 ν¬μΈνŠΈλŠ” 이λͺ¨μ§€λ₯Ό μœ μ§€ν•˜λ©΄μ„œ λ²ˆμ—­
541
  translated_bullets = []
542
  for point in bullet_points:
543
- # 이λͺ¨μ§€μ™€ ν…μŠ€νŠΈ 뢄리
544
- parts = point.split(' ', 2)
545
- if len(parts) >= 3 and len(parts[1]) <= 2: # 이λͺ¨μ§€κ°€ μžˆλŠ” 경우
546
- emoji = parts[1]
547
- text = ' '.join(parts[2:])
548
  translated_text = translate_content_to_korean_concise(text)
549
  translated_bullets.append(f"β€’ {emoji} {translated_text}")
550
  else:
551
- translated_bullets.append(translate_content_to_korean_concise(point))
 
552
  bullet_points = translated_bullets
553
 
554
  return {
555
  "subtitle": subtitle,
556
- "bullet_points": bullet_points[:5]
557
  }
558
  else:
559
  return {
@@ -731,13 +771,13 @@ Emoji Guidelines:
731
  πŸ—οΈ (building/development), 🌱 (sustainability/growth)
732
  - Each bullet should have a different, relevant emoji
733
 
734
- Output format:
735
  Subtitle: [subtitle here]
736
- β€’ 🎯 [Point 1 - noun ending or fragment]
737
- β€’ πŸ“Š [Point 2 - noun ending or fragment]
738
- β€’ πŸ’‘ [Point 3 - noun ending or fragment]
739
- β€’ πŸš€ [Point 4 - noun ending or fragment]
740
- β€’ ⚑ [Point 5 - noun ending or fragment]"""
741
 
742
  user_message = f"""Topic: {topic}
743
  Slide Title: {slide_title}
@@ -780,16 +820,41 @@ Context: {slide_context}"""
780
  result = response.json()
781
  content = result['choices'][0]['message']['content'].strip()
782
 
783
- # Parse content
 
 
784
  lines = content.split('\n')
785
  subtitle = ""
786
  bullet_points = []
787
 
788
  for line in lines:
789
- if line.startswith("Subtitle:"):
790
- subtitle = line.replace("Subtitle:", "").strip()
791
- elif line.strip().startswith("β€’"):
792
- bullet_points.append(line.strip())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
793
 
794
  # ν•œκΈ€λ‘œ λ²ˆμ—­μ΄ ν•„μš”ν•œ 경우
795
  if any(ord('κ°€') <= ord(char) <= ord('힣') for char in topic):
@@ -797,22 +862,28 @@ Context: {slide_context}"""
797
  # 뢈릿 ν¬μΈνŠΈλŠ” 이λͺ¨μ§€λ₯Ό μœ μ§€ν•˜λ©΄μ„œ λ²ˆμ—­
798
  translated_bullets = []
799
  for point in bullet_points:
800
- # 이λͺ¨μ§€μ™€ ν…μŠ€νŠΈ 뢄리
801
- parts = point.split(' ', 2)
802
- if len(parts) >= 3 and len(parts[1]) <= 2: # 이λͺ¨μ§€κ°€ μžˆλŠ” 경우
803
- emoji = parts[1]
804
- text = ' '.join(parts[2:])
 
 
 
805
  translated_text = translate_content_to_korean_concise(text)
806
  translated_bullets.append(f"β€’ {emoji} {translated_text}")
807
  else:
808
- translated_bullets.append(translate_content_to_korean_concise(point))
 
 
809
  bullet_points = translated_bullets
810
 
811
  return {
812
  "subtitle": subtitle,
813
- "bullet_points": bullet_points[:5] # μ΅œλŒ€ 5개
814
  }
815
  else:
 
816
  return {
817
  "subtitle": slide_title,
818
  "bullet_points": ["β€’ πŸ“Œ λ‚΄μš© 생성 λΆˆκ°€"] * 5
@@ -1050,10 +1121,84 @@ def translate_to_english(text: str) -> str:
1050
  return text
1051
 
1052
  def generate_image(prompt: str, seed: int = 10, slide_info: str = "") -> Tuple[Image.Image, str]:
1053
- """Replicate APIλ₯Ό μ‚¬μš©ν•΄ 이미지 생성"""
1054
  print(f"\n[이미지 생성] {slide_info}")
1055
  print(f"[이미지 생성] ν”„λ‘¬ν”„νŠΈ: {prompt[:50]}...")
1056
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1057
  try:
1058
  english_prompt = translate_to_english(prompt)
1059
 
@@ -1995,6 +2140,10 @@ def generate_ppt_with_content(topic: str, template_name: str, custom_slides: Lis
1995
  results = []
1996
  preview_html = ""
1997
 
 
 
 
 
1998
  # μ—…λ‘œλ“œλœ 파일 λ‚΄μš© 읽기
1999
  uploaded_content = ""
2000
  if uploaded_file is not None:
@@ -2078,7 +2227,7 @@ def generate_ppt_with_content(topic: str, template_name: str, custom_slides: Lis
2078
  slide_context, uploaded_content
2079
  )
2080
 
2081
- # 이미지 생성
2082
  slide_seed = seed + i
2083
  img, used_prompt = generate_image(prompt, slide_seed, slide_info)
2084
 
@@ -2154,9 +2303,8 @@ def generate_ppt_with_content(topic: str, template_name: str, custom_slides: Lis
2154
  final_status = f"### πŸŽ‰ 생성 μ™„λ£Œ! 총 {total_slides}개 μŠ¬λΌμ΄λ“œ 쀑 {successful}개 성곡"
2155
 
2156
  # νŽΈμ§‘ κ°€λŠ₯ν•œ μŠ¬λΌμ΄λ“œ 데이터λ₯Ό μ „μ—­ λ³€μˆ˜λ‘œ μ €μž₯
2157
- global current_slides_data, current_topic, current_template, current_theme
2158
  current_slides_data = results
2159
- current_topic = topic
2160
  current_template = template_name
2161
  current_theme = theme_name
2162
 
@@ -2164,6 +2312,8 @@ def generate_ppt_with_content(topic: str, template_name: str, custom_slides: Lis
2164
  final_status += f"\n\n### πŸ“₯ PPTX 파일이 μ€€λΉ„λ˜μ—ˆμŠ΅λ‹ˆλ‹€!"
2165
  final_status += f"\n\nπŸ’‘ **각 μŠ¬λΌμ΄λ“œλ₯Ό νŽΈμ§‘ν•œ ν›„ 'νŽΈμ§‘ μ™„λ£Œ ν›„ μ΅œμ’… λ‹€μš΄λ‘œλ“œ' λ²„νŠΌμ„ ν΄λ¦­ν•˜μ„Έμš”**"
2166
  final_status += f"\n\n🎀 **λ°œν‘œμž λ…ΈνŠΈκ°€ 각 μŠ¬λΌμ΄λ“œμ— ν¬ν•¨λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€!**"
 
 
2167
 
2168
  yield preview_html, final_status, pptx_path
2169
 
@@ -2228,6 +2378,7 @@ with gr.Blocks(title="PPT 이미지 생성기", theme=gr.themes.Soft(), css="""
2228
  - πŸ“ **파일 μ—…λ‘œλ“œ** 지원 (PDF/CSV/TXT)
2229
  - πŸ” **μ›Ή 검색** κΈ°λŠ₯ (Brave Search)
2230
  - 🎚️ **μŠ¬λΌμ΄λ“œ 수 쑰절** (6-20μž₯)
 
2231
  """)
2232
 
2233
  # API 토큰 μƒνƒœ 확인
@@ -2238,6 +2389,8 @@ with gr.Blocks(title="PPT 이미지 생성기", theme=gr.themes.Soft(), css="""
2238
  token_status.append("⚠️ FRIENDLI_TOKEN ν™˜κ²½ λ³€μˆ˜κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.")
2239
  if not BRAVE_API_TOKEN:
2240
  token_status.append("ℹ️ BAPI_TOKEN이 μ—†μ–΄ μ›Ή 검색 κΈ°λŠ₯이 λΉ„ν™œμ„±ν™”λ©λ‹ˆλ‹€.")
 
 
2241
 
2242
  if token_status:
2243
  gr.Markdown("\n".join(token_status))
@@ -2339,6 +2492,11 @@ with gr.Blocks(title="PPT 이미지 생성기", theme=gr.themes.Soft(), css="""
2339
  - "숫자 포함": ꡬ체적 μˆ˜μΉ˜λ‚˜ 톡계 μΆ”κ°€
2340
  - "μ „λ¬Έμ μœΌλ‘œ": 업계 μ „λ¬Έ μš©μ–΄ μ‚¬μš©
2341
  - "μž„νŒ©νŠΈ 있게": 강쑰점과 핡심 μ„±κ³Ό 뢀각
 
 
 
 
 
2342
  """)
2343
 
2344
  # PPTX λ‹€μš΄λ‘œλ“œ μ˜μ—­
@@ -2436,7 +2594,10 @@ with gr.Blocks(title="PPT 이미지 생성기", theme=gr.themes.Soft(), css="""
2436
  info += f"생성될 μŠ¬λΌμ΄λ“œ ({len(slides)}μž₯):\n"
2437
  for i, slide in enumerate(slides):
2438
  style_info = STYLE_TEMPLATES.get(slide['style'], {})
2439
- info += f"{i+1}. {slide['title']} - {style_info.get('use_case', '')}\n"
 
 
 
2440
 
2441
  return info
2442
  return ""
@@ -2483,72 +2644,59 @@ with gr.Blocks(title="PPT 이미지 생성기", theme=gr.themes.Soft(), css="""
2483
  yield preview, status, pptx_file
2484
 
2485
  # 이벀트 μ—°κ²°
2486
- # --- 이벀트 μ—°κ²° -------------------------------------------------
2487
  example_btn.click(
2488
  fn=load_example,
2489
  inputs=[template_select],
2490
  outputs=[topic_input]
2491
  )
2492
-
2493
  theme_select.change(
2494
  fn=update_theme_preview,
2495
  inputs=[theme_select],
2496
  outputs=[theme_preview]
2497
  )
2498
-
2499
  template_select.change(
2500
  fn=update_template_info,
2501
  inputs=[template_select, slide_count],
2502
  outputs=[template_info]
2503
  )
2504
-
2505
  slide_count.change(
2506
  fn=update_template_info,
2507
  inputs=[template_select, slide_count],
2508
  outputs=[template_info]
2509
  )
2510
-
2511
  custom_slide_count.change(
2512
  fn=update_custom_slides_visibility,
2513
  inputs=[custom_slide_count],
2514
- outputs=[
2515
- comp
2516
- for slide in custom_slides_components
2517
- for comp in (slide["title"], slide["style"], slide["hint"])
2518
- ]
2519
  )
2520
-
2521
  # μ‚¬μš©μž μ •μ˜ μŠ¬λΌμ΄λ“œ μž…λ ₯ μ»΄ν¬λ„ŒνŠΈ 평탄화
2522
  all_custom_inputs = []
2523
  for slide in custom_slides_components:
2524
  all_custom_inputs.extend([slide["title"], slide["style"], slide["hint"]])
2525
-
2526
  generate_btn.click(
2527
  fn=generate_ppt_handler,
2528
  inputs=[
2529
- topic_input,
2530
- template_select,
2531
- theme_select,
2532
- slide_count,
2533
- seed_input,
2534
- file_upload,
2535
- use_web_search,
2536
- custom_slide_count,
2537
- ]
2538
- + all_custom_inputs,
2539
- outputs=[preview_output, status_output, download_file],
2540
  )
2541
-
2542
  # 초기 λ‘œλ“œμ‹œ ν…œν”Œλ¦Ώ/ν…Œλ§ˆ 정보 ν‘œμ‹œ
2543
  demo.load(
2544
- fn=lambda: (
2545
- update_template_info(template_select.value, slide_count.value),
2546
- update_theme_preview(theme_select.value),
2547
- ),
2548
  inputs=[],
2549
- outputs=[template_info, theme_preview],
2550
  )
2551
 
2552
- # --- μ•± μ‹€ν–‰ --------------------------------------------------------
2553
  if __name__ == "__main__":
2554
- demo.launch(ssr_mode=False)
 
19
  import chardet
20
  import gradio as gr
21
 
22
+ # ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° 생성기 μž„ν¬νŠΈ μΆ”κ°€
23
+ try:
24
+ from process_flow_generator import generate_process_flow_for_ppt
25
+ PROCESS_FLOW_AVAILABLE = True
26
+ except ImportError:
27
+ PROCESS_FLOW_AVAILABLE = False
28
+ print("[κ²½κ³ ] process_flow_generatorλ₯Ό μž„ν¬νŠΈν•  수 μ—†μŠ΅λ‹ˆλ‹€. ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° λ‹€μ΄μ–΄κ·Έλž¨ κΈ°λŠ₯이 λΉ„ν™œμ„±ν™”λ©λ‹ˆλ‹€.")
29
+
30
  # 예제 ν…œν”Œλ¦Ώ μ •μ˜
31
  EXAMPLE_TOPICS = {
32
  "λΉ„μ¦ˆλ‹ˆμŠ€ μ œμ•ˆμ„œ": "AI 기반 고객 μ„œλΉ„μŠ€ μžλ™ν™” ν”Œλž«νΌ 투자 μ œμ•ˆ",
 
45
  current_topic = ""
46
  current_template = ""
47
  current_theme = ""
48
+ uploaded_content = "" # μ „μ—­ λ³€μˆ˜λ‘œ μΆ”κ°€
49
 
50
  # λ””μžμΈ ν…Œλ§ˆ μ •μ˜
51
  DESIGN_THEMES = {
 
116
  }
117
  }
118
 
119
+ # μŠ€νƒ€μΌ μ •μ˜ (ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° μΆ”κ°€)
120
  STYLE_TEMPLATES = {
121
  "Title Slide (Hero)": {
122
  "name": "Title Slide",
 
152
  "name": "Business Process",
153
  "description": "End-to-end business workflow with clear phases",
154
  "use_case": "ν”„λ‘œμ„ΈμŠ€ μ„€λͺ…, 단계별 μ§„ν–‰",
155
+ "example": "A detailed hand-drawn diagram illustrating an end-to-end business workflow with Market Analysis, Strategy Development, Product Design, Implementation, and Post-Launch Review phases. Clear directional arrows, iconography for each component, vibrant educational yet professional style",
156
+ "is_process_flow": True # ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° λ‹€μ΄μ–΄κ·Έλž¨ μ‚¬μš© ν‘œμ‹œ
157
  },
158
  "Industrial Design": {
159
  "name": "Product Design",
 
213
  "name": "Flowchart",
214
  "description": "Vibrant flowchart with minimalistic icons",
215
  "use_case": "μ˜μ‚¬κ²°μ •, ν”„λ‘œμ„ΈμŠ€ 흐름",
216
+ "example": "FLOWCHART DESIGN: A hand-drawn style flowchart, vibrant colors, minimalistic icons showing process flow from START to END with decision points, branches, and clear directional arrows",
217
+ "is_process_flow": True # ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° λ‹€μ΄μ–΄κ·Έλž¨ μ‚¬μš© ν‘œμ‹œ
218
  }
219
  }
220
 
221
  # PPT ν…œν”Œλ¦Ώ μ •μ˜ (λ™μ μœΌλ‘œ μŠ¬λΌμ΄λ“œ 수 μ‘°μ • κ°€λŠ₯)
222
+ # PPT_TEMPLATES μˆ˜μ • - ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš°λŠ” νŠΉμ • μŠ¬λΌμ΄λ“œμ—λ§Œ 적용
223
  PPT_TEMPLATES = {
224
  "λΉ„μ¦ˆλ‹ˆμŠ€ μ œμ•ˆμ„œ": {
225
  "description": "투자 유치, 사업 μ œμ•ˆμš©",
226
  "core_slides": [
227
+ {"title": "λͺ©μ°¨", "style": "Flowchart", "prompt_hint": "ν”„λ ˆμ  ν…Œμ΄μ…˜ ꡬ쑰"}, # 이건 λͺ©μ°¨μš© ν”Œλ‘œμš°μ°¨νŠΈ
228
  {"title": "문제 μ •μ˜", "style": "Colorful Mind Map", "prompt_hint": "ν˜„μž¬ μ‹œμž₯의 문제점"},
229
  {"title": "ν˜„ν™© 뢄석", "style": "Elegant SWOT Quadrant", "prompt_hint": "강점, 약점, 기회, μœ„ν˜‘"},
230
  {"title": "μ†”λ£¨μ…˜", "style": "Industrial Design", "prompt_hint": "μ œν’ˆ/μ„œλΉ„μŠ€ 컨셉"},
231
+ {"title": "ν”„λ‘œμ„ΈμŠ€", "style": "Business Workflow", "prompt_hint": "μ‹€ν–‰ 단계"}, # μ—¬κΈ°μ„œλ§Œ ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš°
232
  {"title": "일정", "style": "Timeline Ribbon", "prompt_hint": "μ£Όμš” λ§ˆμΌμŠ€ν†€"}
233
  ],
234
  "optional_slides": [
 
238
  {"title": "νŒ€ μ†Œκ°œ", "style": "Colorful Mind Map", "prompt_hint": "핡심 νŒ€μ›κ³Ό μ—­λŸ‰"},
239
  {"title": "재무 κ³„νš", "style": "KPI Dashboard", "prompt_hint": "μ˜ˆμƒ 맀좜과 손읡"},
240
  {"title": "μœ„ν—˜ 관리", "style": "Risk Heat Map", "prompt_hint": "μ£Όμš” λ¦¬μŠ€ν¬μ™€ λŒ€μ‘"},
241
+ {"title": "νŒŒνŠΈλ„ˆμ‹­", "style": "Value Chain", "prompt_hint": "μ „λž΅μ  제휴"}, # Business Workflow λŒ€μ‹  Value Chain
242
+ {"title": "기술 μŠ€νƒ", "style": "Industrial Design", "prompt_hint": "핡심 기술 ꡬ쑰"}, # Flowchart λŒ€μ‹ 
243
  {"title": "고객 사둀", "style": "Industrial Design", "prompt_hint": "성곡 사둀"},
244
  {"title": "μ„±μž₯ μ „λž΅", "style": "Timeline Ribbon", "prompt_hint": "ν™•μž₯ κ³„νš"},
245
  {"title": "투자 ν™œμš©", "style": "Pyramid/Funnel", "prompt_hint": "자금 μ‚¬μš© κ³„νš"},
 
252
  {"title": "μ œν’ˆ 컨셉", "style": "Industrial Design", "prompt_hint": "μ œν’ˆ λ””μžμΈ"},
253
  {"title": "μ‚¬μš©μž λ‹ˆμ¦ˆ", "style": "Colorful Mind Map", "prompt_hint": "고객 페인포인트"},
254
  {"title": "κΈ°λŠ₯ μ†Œκ°œ", "style": "Mobile App Mockup", "prompt_hint": "UI/UX ν™”λ©΄"},
255
+ {"title": "μž‘λ™ 원리", "style": "Flowchart", "prompt_hint": "κΈ°λŠ₯ ν”Œλ‘œμš°"}, # μ—¬κΈ°μ„œλ§Œ ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš°
256
  {"title": "μ‹œμž₯ ν¬μ§€μ…˜", "style": "3D Bubble Chart", "prompt_hint": "κ²½μŸμ‚¬ 비ꡐ"},
257
  {"title": "μΆœμ‹œ 일정", "style": "Timeline Ribbon", "prompt_hint": "런칭 λ‘œλ“œλ§΅"}
258
  ],
 
260
  {"title": "νƒ€κ²Ÿ 고객", "style": "Colorful Mind Map", "prompt_hint": "μ£Όμš” 고객측"},
261
  {"title": "가격 μ •μ±…", "style": "Pyramid/Funnel", "prompt_hint": "가격 μ „λž΅"},
262
  {"title": "기술 μš°μœ„", "style": "Industrial Design", "prompt_hint": "핡심 기술"},
263
+ {"title": "μ‚¬μš© μ‹œλ‚˜λ¦¬μ˜€", "style": "Mobile App Mockup", "prompt_hint": "ν™œμš© 사둀"}, # Business Workflow λŒ€μ‹ 
264
  {"title": "고객 ν›„κΈ°", "style": "KPI Dashboard", "prompt_hint": "μ‚¬μš©μž 평가"},
265
  {"title": "판맀 채널", "style": "Value Chain", "prompt_hint": "μœ ν†΅ μ „λž΅"},
266
  {"title": "λ§ˆμΌ€νŒ… μ „λž΅", "style": "Timeline Ribbon", "prompt_hint": "홍보 κ³„νš"},
 
270
  "ν”„λ‘œμ νŠΈ 보고": {
271
  "description": "μ§„ν–‰ 상황, μ„±κ³Ό 보고용",
272
  "core_slides": [
273
+ {"title": "ν”„λ‘œμ νŠΈ κ°œμš”", "style": "Business Workflow", "prompt_hint": "전체 ν”„λ‘œμ„ΈμŠ€"}, # μ—¬κΈ°μ„œλ§Œ
274
  {"title": "μ§„ν–‰ ν˜„ν™©", "style": "Gantt Chart", "prompt_hint": "μž‘μ—… 일정"},
275
  {"title": "리슀크 관리", "style": "Risk Heat Map", "prompt_hint": "μœ„ν—˜ μš”μ†Œ"},
276
  {"title": "μ„±κ³Ό μ§€ν‘œ", "style": "KPI Dashboard", "prompt_hint": "달성 싀적"},
 
281
  {"title": "νŒ€ μ„±κ³Ό", "style": "3D Bubble Chart", "prompt_hint": "νŒ€λ³„ 기여도"},
282
  {"title": "이슈 관리", "style": "Risk Heat Map", "prompt_hint": "μ£Όμš” 이슈"},
283
  {"title": "κ°œμ„  사항", "style": "Colorful Mind Map", "prompt_hint": "ν”„λ‘œμ„ΈμŠ€ κ°œμ„ "},
284
+ {"title": "κ΅ν›ˆ", "style": "Colorful Mind Map", "prompt_hint": "배운 점"} # Business Workflow λŒ€μ‹ 
285
  ]
286
  },
287
  "μ „λž΅ 기획": {
 
297
  "optional_slides": [
298
  {"title": "μ‹œμž₯ 전망", "style": "3D Bubble Chart", "prompt_hint": "미래 μ‹œμž₯"},
299
  {"title": "ν˜μ‹  λ°©ν–₯", "style": "Industrial Design", "prompt_hint": "ν˜μ‹  μ „λž΅"},
300
+ {"title": "쑰직 λ³€ν™”", "style": "Value Chain", "prompt_hint": "쑰직 개편"}, # Business Workflow λŒ€μ‹ 
301
+ {"title": "λ””μ§€ν„Έ μ „ν™˜", "style": "Industrial Design", "prompt_hint": "DX μ „λž΅"}, # Flowchart λŒ€μ‹ 
302
  {"title": "지속가λŠ₯μ„±", "style": "Timeline Ribbon", "prompt_hint": "ESG μ „λž΅"}
303
  ]
304
  },
 
497
  - Follow the user's instructions while maintaining this style
498
 
499
  Emoji Guidelines:
500
+ - Professional emojis: πŸ“Š 🎯 πŸ’‘ πŸš€ ⚑ πŸ” πŸ“ˆ πŸ’° πŸ† πŸ”§ 🌐 πŸ” ⭐ 🎨 πŸ“± 🀝 πŸ“ πŸŽ–οΈ πŸ—οΈ 🌱
501
  - Match emoji to content meaning
502
+ - Use different emojis for variety
503
+
504
+ Output format (EXACTLY FOLLOW THIS FORMAT):
505
+ Subtitle: [subtitle here]
506
+ - 🎯 [Point 1]
507
+ - πŸ“Š [Point 2]
508
+ - πŸ’‘ [Point 3]
509
+ - πŸš€ [Point 4]
510
+ - ⚑ [Point 5]"""
511
 
512
  user_message = f"""Topic: {topic}
513
  Slide Title: {slide_title}
 
543
  result = response.json()
544
  content = result['choices'][0]['message']['content'].strip()
545
 
546
+ print(f"[AI μž¬μž‘μ„±] LLM 응닡:\n{content}")
547
+
548
+ # Parse content - 더 κ°•κ±΄ν•œ νŒŒμ‹±
549
  lines = content.split('\n')
550
  subtitle = ""
551
  bullet_points = []
552
 
553
  for line in lines:
554
+ line = line.strip()
555
+ if not line:
556
+ continue
557
+
558
+ # Subtitle νŒŒμ‹±
559
+ if line.lower().startswith("subtitle:") or line.startswith("Subtitle:"):
560
+ subtitle = line.split(':', 1)[1].strip()
561
+ # Bullet point νŒŒμ‹±
562
+ elif line.startswith("β€’") or line.startswith("-") or (len(line) > 2 and line[1] == ' ' and ord(line[0]) >= 128):
563
+ if not line.startswith("β€’"):
564
+ line = "β€’ " + line.lstrip("- ")
565
+ bullet_points.append(line)
566
+
567
+ # κΈ°λ³Έκ°’ 처리
568
+ if not subtitle:
569
+ subtitle = f"{slide_title} κ°œμš”"
570
+
571
+ while len(bullet_points) < 5:
572
+ bullet_points.append(f"β€’ πŸ“Œ 포인트 {len(bullet_points) + 1}")
573
+
574
+ bullet_points = bullet_points[:5]
575
 
576
  # ν•œκΈ€λ‘œ λ²ˆμ—­μ΄ ν•„μš”ν•œ 경우
577
  if any(ord('κ°€') <= ord(char) <= ord('힣') for char in topic):
578
  subtitle = translate_content_to_korean(subtitle)
579
+ # 뢈릿 포인트 λ²ˆμ—­
580
  translated_bullets = []
581
  for point in bullet_points:
582
+ clean_point = point.replace('β€’', '').strip()
583
+
584
+ if len(clean_point) > 0 and clean_point[0] in 'πŸŽ―πŸ“ŠπŸ’‘πŸš€βš‘πŸ”πŸ“ˆπŸ’°πŸ†πŸ”§πŸŒπŸ”β­πŸŽ¨πŸ“±πŸ€πŸ“πŸŽ–οΈπŸ—οΈπŸŒ±':
585
+ emoji = clean_point[0]
586
+ text = clean_point[1:].strip()
587
  translated_text = translate_content_to_korean_concise(text)
588
  translated_bullets.append(f"β€’ {emoji} {translated_text}")
589
  else:
590
+ translated_bullets.append(f"β€’ {translate_content_to_korean_concise(clean_point)}")
591
+
592
  bullet_points = translated_bullets
593
 
594
  return {
595
  "subtitle": subtitle,
596
+ "bullet_points": bullet_points
597
  }
598
  else:
599
  return {
 
771
  πŸ—οΈ (building/development), 🌱 (sustainability/growth)
772
  - Each bullet should have a different, relevant emoji
773
 
774
+ Output format (EXACTLY FOLLOW THIS FORMAT):
775
  Subtitle: [subtitle here]
776
+ - 🎯 [Point 1 - noun ending or fragment]
777
+ - πŸ“Š [Point 2 - noun ending or fragment]
778
+ - πŸ’‘ [Point 3 - noun ending or fragment]
779
+ - πŸš€ [Point 4 - noun ending or fragment]
780
+ - ⚑ [Point 5 - noun ending or fragment]"""
781
 
782
  user_message = f"""Topic: {topic}
783
  Slide Title: {slide_title}
 
820
  result = response.json()
821
  content = result['choices'][0]['message']['content'].strip()
822
 
823
+ print(f"[μŠ¬λΌμ΄λ“œ λ‚΄μš©] LLM 응닡:\n{content}")
824
+
825
+ # Parse content - 더 κ°•κ±΄ν•œ νŒŒμ‹±
826
  lines = content.split('\n')
827
  subtitle = ""
828
  bullet_points = []
829
 
830
  for line in lines:
831
+ line = line.strip()
832
+ if not line:
833
+ continue
834
+
835
+ # Subtitle νŒŒμ‹±
836
+ if line.lower().startswith("subtitle:") or line.startswith("Subtitle:"):
837
+ subtitle = line.split(':', 1)[1].strip()
838
+ # Bullet point νŒŒμ‹± - β€’ λ˜λŠ” - 둜 μ‹œμž‘ν•˜λŠ” 경우 λͺ¨λ‘ 처리
839
+ elif line.startswith("β€’") or line.startswith("-") or (len(line) > 2 and line[1] == ' ' and ord(line[0]) >= 128):
840
+ # 이미 β€’ κ°€ 있으면 κ·ΈλŒ€λ‘œ, μ—†μœΌλ©΄ μΆ”κ°€
841
+ if not line.startswith("β€’"):
842
+ line = "β€’ " + line.lstrip("- ")
843
+ bullet_points.append(line)
844
+
845
+ # λ§Œμ•½ subtitle이 μ—†μœΌλ©΄ κΈ°λ³Έκ°’ μ‚¬μš©
846
+ if not subtitle:
847
+ subtitle = f"{slide_title} κ°œμš”"
848
+
849
+ # λ§Œμ•½ bullet_pointsκ°€ 5개 미만이면 κΈ°λ³Έκ°’ μΆ”κ°€
850
+ while len(bullet_points) < 5:
851
+ bullet_points.append(f"β€’ πŸ“Œ 포인트 {len(bullet_points) + 1}")
852
+
853
+ # 5개만 선택
854
+ bullet_points = bullet_points[:5]
855
+
856
+ print(f"[μŠ¬λΌμ΄λ“œ λ‚΄μš©] νŒŒμ‹±λœ subtitle: {subtitle}")
857
+ print(f"[μŠ¬λΌμ΄λ“œ λ‚΄μš©] νŒŒμ‹±λœ bullets: {bullet_points}")
858
 
859
  # ν•œκΈ€λ‘œ λ²ˆμ—­μ΄ ν•„μš”ν•œ 경우
860
  if any(ord('κ°€') <= ord(char) <= ord('힣') for char in topic):
 
862
  # 뢈릿 ν¬μΈνŠΈλŠ” 이λͺ¨μ§€λ₯Ό μœ μ§€ν•˜λ©΄μ„œ λ²ˆμ—­
863
  translated_bullets = []
864
  for point in bullet_points:
865
+ # β€’ μ œκ±°ν•˜κ³  λ‚΄μš©λ§Œ μΆ”μΆœ
866
+ clean_point = point.replace('β€’', '').strip()
867
+
868
+ # 이λͺ¨μ§€μ™€ ν…μŠ€νŠΈ 뢄리 (첫 번째 곡백을 κΈ°μ€€μœΌλ‘œ)
869
+ if len(clean_point) > 0 and clean_point[0] in 'πŸŽ―πŸ“ŠπŸ’‘πŸš€βš‘πŸ”πŸ“ˆπŸ’°πŸ†πŸ”§πŸŒπŸ”β­πŸŽ¨πŸ“±πŸ€πŸ“πŸŽ–οΈπŸ—οΈπŸŒ±':
870
+ # 이λͺ¨μ§€κ°€ μžˆλŠ” 경우
871
+ emoji = clean_point[0]
872
+ text = clean_point[1:].strip()
873
  translated_text = translate_content_to_korean_concise(text)
874
  translated_bullets.append(f"β€’ {emoji} {translated_text}")
875
  else:
876
+ # 이λͺ¨μ§€κ°€ μ—†λŠ” 경우
877
+ translated_bullets.append(f"β€’ {translate_content_to_korean_concise(clean_point)}")
878
+
879
  bullet_points = translated_bullets
880
 
881
  return {
882
  "subtitle": subtitle,
883
+ "bullet_points": bullet_points
884
  }
885
  else:
886
+ print(f"[μŠ¬λΌμ΄λ“œ λ‚΄μš©] API 였λ₯˜: {response.status_code}")
887
  return {
888
  "subtitle": slide_title,
889
  "bullet_points": ["β€’ πŸ“Œ λ‚΄μš© 생성 λΆˆκ°€"] * 5
 
1121
  return text
1122
 
1123
  def generate_image(prompt: str, seed: int = 10, slide_info: str = "") -> Tuple[Image.Image, str]:
1124
+ """Replicate APIλ₯Ό μ‚¬μš©ν•΄ 이미지 생성 λ˜λŠ” ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° λ‹€μ΄μ–΄κ·Έλž¨ 생성"""
1125
  print(f"\n[이미지 생성] {slide_info}")
1126
  print(f"[이미지 생성] ν”„λ‘¬ν”„νŠΈ: {prompt[:50]}...")
1127
 
1128
+ global current_topic, uploaded_content
1129
+
1130
+ # ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° λ‹€μ΄μ–΄κ·Έλž¨ 생성 쑰건 확인
1131
+ should_generate_process_flow = False
1132
+
1133
+ # μŠ¬λΌμ΄λ“œ μ •λ³΄μ—μ„œ 제λͺ© μΆ”μΆœ
1134
+ slide_title = ""
1135
+ if ":" in slide_info:
1136
+ # "μŠ¬λΌμ΄λ“œ 5: ν”„λ‘œμ„ΈμŠ€" ν˜•νƒœμ—μ„œ "ν”„λ‘œμ„ΈμŠ€" μΆ”μΆœ
1137
+ parts = slide_info.split(":")
1138
+ if len(parts) >= 2:
1139
+ slide_title = parts[1].strip()
1140
+
1141
+ print(f"[이미지 생성] μΆ”μΆœλœ μŠ¬λΌμ΄λ“œ 제λͺ©: '{slide_title}'")
1142
+
1143
+ # 1. ν”„λ‘œμ„ΈμŠ€ κ΄€λ ¨ ν‚€μ›Œλ“œκ°€ μŠ¬λΌμ΄λ“œ 제λͺ©μ— ν¬ν•¨λ˜μ–΄ μžˆλŠ”μ§€ 확인
1144
+ process_keywords = [
1145
+ "ν”„λ‘œμ„ΈμŠ€", "μž‘λ™", "ν”Œλ‘œμš°", "흐름", "μ›Œν¬ν”Œλ‘œμš°",
1146
+ "절차", "단계", "처리", "μ§„ν–‰", "κ°œμš”"
1147
+ ]
1148
+
1149
+ # 2. μŠ€νƒ€μΌ 확인
1150
+ is_workflow_style = False
1151
+ if any(style in prompt for style in ["Business Workflow", "Flowchart", "Business Process"]):
1152
+ is_workflow_style = True
1153
+ print(f"[이미지 생성] Business Workflow λ˜λŠ” Flowchart μŠ€νƒ€μΌ 감지")
1154
+
1155
+ # 3. μŠ¬λΌμ΄λ“œ 제λͺ©μ— ν”„λ‘œμ„ΈμŠ€ ν‚€μ›Œλ“œκ°€ μžˆκ±°λ‚˜, μŠ€νƒ€μΌμ΄ μ›Œν¬ν”Œλ‘œμš°μΈ 경우
1156
+ title_has_process = any(keyword in slide_title for keyword in process_keywords)
1157
+ prompt_has_process = any(keyword in prompt.lower() for keyword in ["process", "flow", "workflow"])
1158
+
1159
+ print(f"[이미지 생성] 제λͺ©μ— ν”„λ‘œμ„ΈμŠ€ ν‚€μ›Œλ“œ: {title_has_process}")
1160
+ print(f"[이미지 생성] μ›Œν¬ν”Œλ‘œμš° μŠ€νƒ€μΌ: {is_workflow_style}")
1161
+ print(f"[이미지 생성] ν”„λ‘¬ν”„νŠΈμ— ν”„λ‘œμ„ΈμŠ€ ν‚€μ›Œλ“œ: {prompt_has_process}")
1162
+
1163
+ # 쑰건 μ™„ν™”: 제λͺ©μ— ν”„λ‘œμ„ΈμŠ€ ν‚€μ›Œλ“œκ°€ οΏ½οΏ½κ³ , μŠ€νƒ€μΌμ΄ λ§žκ±°λ‚˜ ν”„λ‘¬ν”„νŠΈμ— κ΄€λ ¨ ν‚€μ›Œλ“œκ°€ 있으면 생성
1164
+ if title_has_process and (is_workflow_style or prompt_has_process):
1165
+ should_generate_process_flow = True
1166
+ print(f"[이미지 생성] βœ… ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° λ‹€μ΄μ–΄κ·Έλž¨ 생성 쑰건 μΆ©μ‘±!")
1167
+
1168
+ # νŠΉλ³„ μΌ€μ΄μŠ€: "λͺ©μ°¨"λŠ” ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš°κ°€ μ•„λ‹Œ 일반 μ΄λ―Έμ§€λ‘œ
1169
+ if "λͺ©μ°¨" in slide_title:
1170
+ should_generate_process_flow = False
1171
+ print(f"[이미지 생성] λͺ©μ°¨λŠ” ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° μ œμ™Έ")
1172
+
1173
+ # ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° λ‹€μ΄μ–΄κ·Έλž¨ 생성
1174
+ if PROCESS_FLOW_AVAILABLE and should_generate_process_flow:
1175
+ try:
1176
+ print("[이미지 생성] πŸ”§ ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° λ‹€μ΄μ–΄κ·Έλž¨ 생성 μ‹œμž‘...")
1177
+
1178
+ # μŠ¬λΌμ΄λ“œ μ»¨ν…μŠ€νŠΈλ₯Ό 기반으둜 μ μ ˆν•œ ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° 생성
1179
+ img = generate_process_flow_for_ppt(
1180
+ topic=current_topic,
1181
+ context=slide_info,
1182
+ style="Business Workflow"
1183
+ )
1184
+
1185
+ if isinstance(img, Image.Image):
1186
+ print("[이미지 생성] βœ… ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° λ‹€μ΄μ–΄κ·Έλž¨ 생성 성곡!")
1187
+ return img, "Process flow diagram generated with Korean support"
1188
+ else:
1189
+ print("[이미지 생성] ❌ ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° 생성 μ‹€νŒ¨, 일반 μ΄λ―Έμ§€λ‘œ λŒ€μ²΄")
1190
+ except Exception as e:
1191
+ print(f"[이미지 생성] ❌ ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° 생성 였λ₯˜: {str(e)}")
1192
+ import traceback
1193
+ traceback.print_exc()
1194
+ # μ‹€νŒ¨μ‹œ 일반 이미지 μƒμ„±μœΌλ‘œ 폴백
1195
+ else:
1196
+ if not PROCESS_FLOW_AVAILABLE:
1197
+ print("[이미지 생성] ⚠️ ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° 생성기λ₯Ό μ‚¬μš©ν•  수 μ—†μŒ")
1198
+ else:
1199
+ print("[이미지 생성] ℹ️ ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° 생성 쑰건 λ―ΈμΆ©μ‘±, 일반 이미지 생성")
1200
+
1201
+ # κΈ°μ‘΄ 이미지 생성 둜직
1202
  try:
1203
  english_prompt = translate_to_english(prompt)
1204
 
 
2140
  results = []
2141
  preview_html = ""
2142
 
2143
+ # μ „μ—­ λ³€μˆ˜ μ—…λ°μ΄νŠΈ
2144
+ global current_topic, uploaded_content
2145
+ current_topic = topic
2146
+
2147
  # μ—…λ‘œλ“œλœ 파일 λ‚΄μš© 읽기
2148
  uploaded_content = ""
2149
  if uploaded_file is not None:
 
2227
  slide_context, uploaded_content
2228
  )
2229
 
2230
+ # 이미지 생성 (ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° λ‹€μ΄μ–΄κ·Έλž¨ 지원 포함)
2231
  slide_seed = seed + i
2232
  img, used_prompt = generate_image(prompt, slide_seed, slide_info)
2233
 
 
2303
  final_status = f"### πŸŽ‰ 생성 μ™„λ£Œ! 총 {total_slides}개 μŠ¬λΌμ΄λ“œ 쀑 {successful}개 성곡"
2304
 
2305
  # νŽΈμ§‘ κ°€λŠ₯ν•œ μŠ¬λΌμ΄λ“œ 데이터λ₯Ό μ „μ—­ λ³€μˆ˜λ‘œ μ €μž₯
2306
+ global current_slides_data, current_template, current_theme
2307
  current_slides_data = results
 
2308
  current_template = template_name
2309
  current_theme = theme_name
2310
 
 
2312
  final_status += f"\n\n### πŸ“₯ PPTX 파일이 μ€€λΉ„λ˜μ—ˆμŠ΅λ‹ˆλ‹€!"
2313
  final_status += f"\n\nπŸ’‘ **각 μŠ¬λΌμ΄λ“œλ₯Ό νŽΈμ§‘ν•œ ν›„ 'νŽΈμ§‘ μ™„λ£Œ ν›„ μ΅œμ’… λ‹€μš΄λ‘œλ“œ' λ²„νŠΌμ„ ν΄λ¦­ν•˜μ„Έμš”**"
2314
  final_status += f"\n\n🎀 **λ°œν‘œμž λ…ΈνŠΈκ°€ 각 μŠ¬λΌμ΄λ“œμ— ν¬ν•¨λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€!**"
2315
+ if PROCESS_FLOW_AVAILABLE:
2316
+ final_status += f"\n\nπŸ”§ **ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° λ‹€μ΄μ–΄κ·Έλž¨μ΄ μžλ™μœΌλ‘œ μƒμ„±λ©λ‹ˆλ‹€ (ν•œκΈ€ 지원)**"
2317
 
2318
  yield preview_html, final_status, pptx_path
2319
 
 
2378
  - πŸ“ **파일 μ—…λ‘œλ“œ** 지원 (PDF/CSV/TXT)
2379
  - πŸ” **μ›Ή 검색** κΈ°λŠ₯ (Brave Search)
2380
  - 🎚️ **μŠ¬λΌμ΄λ“œ 수 쑰절** (6-20μž₯)
2381
+ - πŸ”§ **ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° λ‹€μ΄μ–΄κ·Έλž¨** μžλ™ 생성 (ν•œκΈ€ 지원)
2382
  """)
2383
 
2384
  # API 토큰 μƒνƒœ 확인
 
2389
  token_status.append("⚠️ FRIENDLI_TOKEN ν™˜κ²½ λ³€μˆ˜κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.")
2390
  if not BRAVE_API_TOKEN:
2391
  token_status.append("ℹ️ BAPI_TOKEN이 μ—†μ–΄ μ›Ή 검색 κΈ°λŠ₯이 λΉ„ν™œμ„±ν™”λ©λ‹ˆλ‹€.")
2392
+ if not PROCESS_FLOW_AVAILABLE:
2393
+ token_status.append("ℹ️ process_flow_generatorκ°€ μ—†μ–΄ ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° λ‹€μ΄μ–΄κ·Έλž¨μ΄ λΉ„ν™œμ„±ν™”λ©λ‹ˆλ‹€.")
2394
 
2395
  if token_status:
2396
  gr.Markdown("\n".join(token_status))
 
2492
  - "숫자 포함": ꡬ체적 μˆ˜μΉ˜λ‚˜ 톡계 μΆ”κ°€
2493
  - "μ „λ¬Έμ μœΌλ‘œ": 업계 μ „λ¬Έ μš©μ–΄ μ‚¬μš©
2494
  - "μž„νŒ©νŠΈ 있게": 강쑰점과 핡심 μ„±κ³Ό 뢀각
2495
+
2496
+ ### πŸ”§ ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° λ‹€μ΄μ–΄κ·Έλž¨:
2497
+ - **Business Workflow** λ˜λŠ” **Flowchart** μŠ€νƒ€μΌ 선택 μ‹œ μžλ™μœΌλ‘œ ν”„λ‘œμ„ΈμŠ€ ν”Œλ‘œμš° λ‹€μ΄μ–΄κ·Έλž¨ 생성
2498
+ - ν•œκΈ€ ν…μŠ€νŠΈλ₯Ό μ™„λ²½ν•˜κ²Œ μ§€μ›ν•˜λŠ” λ‹€μ΄μ–΄κ·Έλž¨
2499
+ - ν”„λ‘œμ„ΈμŠ€, μ˜μ‚¬κ²°μ •, μž…μΆœλ ₯ λ“± λ‹€μ–‘ν•œ λ…Έλ“œ νƒ€μž… 지원
2500
  """)
2501
 
2502
  # PPTX λ‹€μš΄λ‘œλ“œ μ˜μ—­
 
2594
  info += f"생성될 μŠ¬λΌμ΄λ“œ ({len(slides)}μž₯):\n"
2595
  for i, slide in enumerate(slides):
2596
  style_info = STYLE_TEMPLATES.get(slide['style'], {})
2597
+ info += f"{i+1}. {slide['title']} - {style_info.get('use_case', '')}"
2598
+ if style_info.get('is_process_flow'):
2599
+ info += " πŸ”§"
2600
+ info += "\n"
2601
 
2602
  return info
2603
  return ""
 
2644
  yield preview, status, pptx_file
2645
 
2646
  # 이벀트 μ—°κ²°
 
2647
  example_btn.click(
2648
  fn=load_example,
2649
  inputs=[template_select],
2650
  outputs=[topic_input]
2651
  )
2652
+
2653
  theme_select.change(
2654
  fn=update_theme_preview,
2655
  inputs=[theme_select],
2656
  outputs=[theme_preview]
2657
  )
2658
+
2659
  template_select.change(
2660
  fn=update_template_info,
2661
  inputs=[template_select, slide_count],
2662
  outputs=[template_info]
2663
  )
2664
+
2665
  slide_count.change(
2666
  fn=update_template_info,
2667
  inputs=[template_select, slide_count],
2668
  outputs=[template_info]
2669
  )
2670
+
2671
  custom_slide_count.change(
2672
  fn=update_custom_slides_visibility,
2673
  inputs=[custom_slide_count],
2674
+ outputs=[comp for slide in custom_slides_components
2675
+ for comp in [slide["title"], slide["style"], slide["hint"]]]
 
 
 
2676
  )
2677
+
2678
  # μ‚¬μš©μž μ •μ˜ μŠ¬λΌμ΄λ“œ μž…λ ₯ μ»΄ν¬λ„ŒνŠΈ 평탄화
2679
  all_custom_inputs = []
2680
  for slide in custom_slides_components:
2681
  all_custom_inputs.extend([slide["title"], slide["style"], slide["hint"]])
2682
+
2683
  generate_btn.click(
2684
  fn=generate_ppt_handler,
2685
  inputs=[
2686
+ topic_input, template_select, theme_select, slide_count,
2687
+ seed_input, file_upload, use_web_search, custom_slide_count
2688
+ ] + all_custom_inputs,
2689
+ outputs=[preview_output, status_output, download_file]
 
 
 
 
 
 
 
2690
  )
2691
+
2692
  # 초기 λ‘œλ“œμ‹œ ν…œν”Œλ¦Ώ/ν…Œλ§ˆ 정보 ν‘œμ‹œ
2693
  demo.load(
2694
+ fn=lambda: (update_template_info(template_select.value, slide_count.value),
2695
+ update_theme_preview(theme_select.value)),
 
 
2696
  inputs=[],
2697
+ outputs=[template_info, theme_preview]
2698
  )
2699
 
2700
+ # μ•± μ‹€ν–‰
2701
  if __name__ == "__main__":
2702
+ demo.launch(ssr_mode=False)