openfree commited on
Commit
357ce1c
·
verified ·
1 Parent(s): 9dc500b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +321 -7
app.py CHANGED
@@ -1001,16 +1001,200 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1001
  max-width: 800px;
1002
  margin: 0 auto;
1003
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1004
  </style>
1005
 
1006
  <div class="app-header">
1007
  <h1>🎮 Vibe Game Craft</h1>
1008
  <p>설명을 입력하면 웹 기반 HTML5, JavaScript, CSS 게임을 생성합니다. 직관적인 인터페이스로 쉽게 게임을 만들고, 실시간으로 미리보기를 확인하세요.</p>
1009
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1010
  """)
1011
 
1012
  history = gr.State([])
1013
  setting = gr.State({"system": SystemPrompt})
 
 
 
 
 
 
 
 
1014
 
1015
  with ms.Application() as app:
1016
  with antd.ConfigProvider():
@@ -1090,10 +1274,18 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1090
  )
1091
  gr.HTML('<div class="help-text">💡 원하는 게임의 설명을 입력하세요. 예: "테트리스 게임 제작해줘."</div>')
1092
 
1093
- # ── (4) 배포 결과 영역 ──
1094
- with antd.Flex(vertical=True, gap="small", elem_classes="deploy-result-container"):
1095
- gr.HTML('<div class="deploy-result-title">📤 배포 결과</div>')
1096
- deploy_result = gr.HTML(elem_classes="deploy-result-box")
 
 
 
 
 
 
 
 
1097
 
1098
 
1099
  # ---- 이벤트 / 콜백 ----
@@ -1166,10 +1358,132 @@ with gr.Blocks(css_paths=["app.css"], theme=theme) as demo:
1166
  )
1167
 
1168
  # (H) '배포' 버튼 => Vercel
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1169
  deploy_btn.click(
1170
- fn=lambda code: deploy_to_vercel(remove_code_block(code)) if code else "<div class='deploy-error'>코드가 없습니다.</div>",
1171
- inputs=[code_output],
1172
- outputs=[deploy_result]
1173
  )
1174
 
1175
  # ------------------------
 
1001
  max-width: 800px;
1002
  margin: 0 auto;
1003
  }
1004
+
1005
+ /* 배포 알림 스타일 - 상단에 고정 표시 */
1006
+ .deploy-alert {
1007
+ position: fixed;
1008
+ top: 10px;
1009
+ left: 50%;
1010
+ transform: translateX(-50%);
1011
+ z-index: 9999;
1012
+ background: rgba(255, 255, 255, 0.95);
1013
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
1014
+ border-radius: 10px;
1015
+ padding: 15px 25px;
1016
+ min-width: 300px;
1017
+ text-align: center;
1018
+ animation: slideDown 0.5s ease-out;
1019
+ }
1020
+
1021
+ .deploy-alert.success {
1022
+ border-left: 5px solid #34c759;
1023
+ }
1024
+
1025
+ .deploy-alert.error {
1026
+ border-left: 5px solid #ff3b30;
1027
+ }
1028
+
1029
+ .deploy-alert h3 {
1030
+ margin: 0 0 10px 0;
1031
+ font-size: 16px;
1032
+ }
1033
+
1034
+ .deploy-url {
1035
+ background: #f5f8ff;
1036
+ padding: 10px;
1037
+ border-radius: 6px;
1038
+ margin: 10px 0;
1039
+ word-break: break-all;
1040
+ font-weight: bold;
1041
+ }
1042
+
1043
+ .deploy-url a {
1044
+ color: #0066cc;
1045
+ text-decoration: none;
1046
+ }
1047
+
1048
+ @keyframes slideDown {
1049
+ from { transform: translate(-50%, -50px); opacity: 0; }
1050
+ to { transform: translate(-50%, 0); opacity: 1; }
1051
+ }
1052
+
1053
+ /* 하단 고정 배포 상태 바 */
1054
+ .deploy-status-bar {
1055
+ position: fixed;
1056
+ bottom: 0;
1057
+ left: 0;
1058
+ width: 100%;
1059
+ background: rgba(255, 255, 255, 0.95);
1060
+ border-top: 1px solid #ddd;
1061
+ padding: 10px 20px;
1062
+ z-index: 1000;
1063
+ display: flex;
1064
+ align-items: center;
1065
+ justify-content: center;
1066
+ }
1067
+
1068
+ .deploy-status-content {
1069
+ background: #f0f9ff;
1070
+ border-radius: 8px;
1071
+ padding: 10px 15px;
1072
+ display: flex;
1073
+ align-items: center;
1074
+ max-width: 800px;
1075
+ width: 100%;
1076
+ }
1077
+
1078
+ .deploy-status-content.success {
1079
+ background: #f0fff4;
1080
+ border-left: 4px solid #34c759;
1081
+ }
1082
+
1083
+ .deploy-status-content.error {
1084
+ background: #fff0f0;
1085
+ border-left: 4px solid #ff3b30;
1086
+ }
1087
+
1088
+ .status-icon {
1089
+ font-size: 18px;
1090
+ margin-right: 10px;
1091
+ }
1092
+
1093
+ .status-message {
1094
+ flex: 1;
1095
+ }
1096
+
1097
+ .status-url {
1098
+ font-weight: bold;
1099
+ color: #0066cc;
1100
+ margin-left: 15px;
1101
+ text-decoration: underline;
1102
+ word-break: break-all;
1103
+ }
1104
  </style>
1105
 
1106
  <div class="app-header">
1107
  <h1>🎮 Vibe Game Craft</h1>
1108
  <p>설명을 입력하면 웹 기반 HTML5, JavaScript, CSS 게임을 생성합니다. 직관적인 인터페이스로 쉽게 게임을 만들고, 실시간으로 미리보기를 확인하세요.</p>
1109
  </div>
1110
+
1111
+ <!-- 배포 알림 - 기본적으로 숨겨져 있음 -->
1112
+ <div id="deploy-alert" style="display:none;" class="deploy-alert">
1113
+ <h3 id="deploy-alert-title">배포 상태</h3>
1114
+ <div id="deploy-alert-content"></div>
1115
+ </div>
1116
+
1117
+ <!-- 하단 고정 배포 상태 바 - 기본적으로 숨겨져 있음 -->
1118
+ <div id="deploy-status-bar" style="display:none;" class="deploy-status-bar">
1119
+ <div id="deploy-status-content" class="deploy-status-content">
1120
+ <span id="status-icon" class="status-icon">🔄</span>
1121
+ <span id="status-message" class="status-message">배포 중...</span>
1122
+ <a id="status-url" class="status-url" href="#" target="_blank" style="display:none;"></a>
1123
+ </div>
1124
+ </div>
1125
+
1126
+ <script>
1127
+ // 배포 알림 표시 함수
1128
+ function showDeployAlert(type, title, content, url) {
1129
+ const alertEl = document.getElementById('deploy-alert');
1130
+ const titleEl = document.getElementById('deploy-alert-title');
1131
+ const contentEl = document.getElementById('deploy-alert-content');
1132
+
1133
+ // 타입에 따른 스타일 설정
1134
+ alertEl.className = 'deploy-alert ' + type;
1135
+ titleEl.textContent = title;
1136
+
1137
+ // URL이 있으면 링크 포함
1138
+ if (url) {
1139
+ contentEl.innerHTML = `
1140
+ <p>${content}</p>
1141
+ <div class="deploy-url">
1142
+ <a href="${url}" target="_blank">${url}</a>
1143
+ </div>
1144
+ `;
1145
+ } else {
1146
+ contentEl.innerHTML = `<p>${content}</p>`;
1147
+ }
1148
+
1149
+ // 알림 표시
1150
+ alertEl.style.display = 'block';
1151
+
1152
+ // 5초 후 자동으로 숨김
1153
+ setTimeout(() => {
1154
+ alertEl.style.display = 'none';
1155
+ }, 8000);
1156
+ }
1157
+
1158
+ // 배포 상태 바 업데이트 함수
1159
+ function updateDeployStatus(type, message, url) {
1160
+ const statusBar = document.getElementById('deploy-status-bar');
1161
+ const statusContent = document.getElementById('deploy-status-content');
1162
+ const statusIcon = document.getElementById('status-icon');
1163
+ const statusMessage = document.getElementById('status-message');
1164
+ const statusUrl = document.getElementById('status-url');
1165
+
1166
+ // 상태 타입에 따른 설정
1167
+ statusContent.className = 'deploy-status-content ' + type;
1168
+
1169
+ if (type === 'success') {
1170
+ statusIcon.textContent = '✅';
1171
+ statusUrl.href = url;
1172
+ statusUrl.textContent = url;
1173
+ statusUrl.style.display = 'inline';
1174
+ } else if (type === 'error') {
1175
+ statusIcon.textContent = '❌';
1176
+ statusUrl.style.display = 'none';
1177
+ } else {
1178
+ statusIcon.textContent = '🔄';
1179
+ statusUrl.style.display = 'none';
1180
+ }
1181
+
1182
+ statusMessage.textContent = message;
1183
+ statusBar.style.display = 'flex';
1184
+ }
1185
+ </script>
1186
  """)
1187
 
1188
  history = gr.State([])
1189
  setting = gr.State({"system": SystemPrompt})
1190
+
1191
+ # 배포 상태를 저장할 상태 변수
1192
+ deploy_status = gr.State({
1193
+ "is_deployed": False,
1194
+ "status": "",
1195
+ "url": "",
1196
+ "message": ""
1197
+ })
1198
 
1199
  with ms.Application() as app:
1200
  with antd.ConfigProvider():
 
1274
  )
1275
  gr.HTML('<div class="help-text">💡 원하는 게임의 설명을 입력하세요. 예: "테트리스 게임 제작해줘."</div>')
1276
 
1277
+ # ── (4) 배포 결과 영역 - 이제 JavaScript로 동적 업데이트 ──
1278
+ deploy_result_html = gr.HTML("""
1279
+ <div class="deploy-section">
1280
+ <div class="deploy-header">📤 배포 결과</div>
1281
+ <div id="deploy-result-box" class="deploy-result-box">
1282
+ <div class="no-deploy">아직 배포된 게임이 없습니다.</div>
1283
+ </div>
1284
+ </div>
1285
+ """)
1286
+
1287
+ # JavaScript 실행을 위한 HTML (비출력용)
1288
+ js_trigger = gr.HTML(elem_id="js-trigger", visible=False)
1289
 
1290
 
1291
  # ---- 이벤트 / 콜백 ----
 
1358
  )
1359
 
1360
  # (H) '배포' 버튼 => Vercel
1361
+ # 수정된 배포 처리 함수
1362
+ def handle_deploy(code, deploy_status):
1363
+ if not code:
1364
+ # JavaScript 실행 코드: 상태 업데이트
1365
+ js_code = """
1366
+ <script>
1367
+ showDeployAlert('error', '⚠️ 배포 실패', '배포할 코드가 없습니다. 먼저 게임 코드를 생성해주세요.');
1368
+ document.getElementById('deploy-result-box').innerHTML = `
1369
+ <div class="deploy-error">
1370
+ <div class="error-icon">⚠️</div>
1371
+ <div class="error-message">배포할 코드가 없습니다.</div>
1372
+ </div>
1373
+ `;
1374
+ </script>
1375
+ """
1376
+
1377
+ return js_code, {
1378
+ "is_deployed": False,
1379
+ "status": "error",
1380
+ "message": "배포할 코드가 없습니다.",
1381
+ "url": ""
1382
+ }
1383
+
1384
+ try:
1385
+ # 배포 시작을 알리는 JavaScript
1386
+ yield """
1387
+ <script>
1388
+ updateDeployStatus('loading', '게임을 Vercel에 배포 중입니다...', '');
1389
+ document.getElementById('deploy-result-box').innerHTML = `
1390
+ <div class="deploy-loading">
1391
+ <div class="loading-spinner"></div>
1392
+ <div class="loading-message">Vercel에 배포 중입니다...</div>
1393
+ </div>
1394
+ `;
1395
+ </script>
1396
+ """, deploy_status
1397
+
1398
+ # 코드 전처리
1399
+ clean_code = remove_code_block(code)
1400
+
1401
+ # Vercel에 배포
1402
+ project_name = ''.join(random.choice(string.ascii_lowercase) for i in range(6))
1403
+ result = deploy_to_vercel(clean_code)
1404
+
1405
+ # 성공적으로 배포된 경우
1406
+ if isinstance(result, dict) and result.get("status") == "success":
1407
+ url = result.get("url")
1408
+ # JavaScript 실행 코드: ��포 성공 표시
1409
+ js_code = f"""
1410
+ <script>
1411
+ showDeployAlert('success', '✅ 배포 완료!', '게임이 성공적으로 배포되었습니다.', '{url}');
1412
+ updateDeployStatus('success', '배포 완료!', '{url}');
1413
+ document.getElementById('deploy-result-box').innerHTML = `
1414
+ <div class="deploy-success">
1415
+ <div class="success-icon">✅</div>
1416
+ <div class="success-message">배포 완료!</div>
1417
+ <div class="url-box">
1418
+ <a href="{url}" target="_blank">{url}</a>
1419
+ <button class="copy-btn" onclick="navigator.clipboard.writeText('{url}')">복사</button>
1420
+ </div>
1421
+ </div>
1422
+ `;
1423
+ </script>
1424
+ """
1425
+
1426
+ return js_code, {
1427
+ "is_deployed": True,
1428
+ "status": "success",
1429
+ "url": url,
1430
+ "message": "배포 완료!"
1431
+ }
1432
+
1433
+ # 실패한 경우
1434
+ error_msg = result.get("message", "알 수 없는 오류")
1435
+ if isinstance(result, str):
1436
+ error_msg = result
1437
+
1438
+ # JavaScript 실행 코드: 오류 표시
1439
+ js_code = f"""
1440
+ <script>
1441
+ showDeployAlert('error', '⚠️ 배포 실패', '{error_msg}');
1442
+ updateDeployStatus('error', '배포 실패: {error_msg}', '');
1443
+ document.getElementById('deploy-result-box').innerHTML = `
1444
+ <div class="deploy-error">
1445
+ <div class="error-icon">⚠️</div>
1446
+ <div class="error-message">배포 실패: {error_msg}</div>
1447
+ </div>
1448
+ `;
1449
+ </script>
1450
+ """
1451
+
1452
+ return js_code, {
1453
+ "is_deployed": False,
1454
+ "status": "error",
1455
+ "message": error_msg,
1456
+ "url": ""
1457
+ }
1458
+
1459
+ except Exception as e:
1460
+ error_msg = str(e)
1461
+ # JavaScript 실행 코드: 예외 표시
1462
+ js_code = f"""
1463
+ <script>
1464
+ showDeployAlert('error', '⚠️ 시스템 오류', '{error_msg}');
1465
+ updateDeployStatus('error', '시스템 오류: {error_msg}', '');
1466
+ document.getElementById('deploy-result-box').innerHTML = `
1467
+ <div class="deploy-error">
1468
+ <div class="error-icon">⚠️</div>
1469
+ <div class="error-message">시스템 오류: {error_msg}</div>
1470
+ </div>
1471
+ `;
1472
+ </script>
1473
+ """
1474
+
1475
+ return js_code, {
1476
+ "is_deployed": False,
1477
+ "status": "error",
1478
+ "message": error_msg,
1479
+ "url": ""
1480
+ }
1481
+
1482
+ # 배포 버튼 클릭 시 JavaScript 트리거와 상태 업데이트
1483
  deploy_btn.click(
1484
+ fn=handle_deploy,
1485
+ inputs=[code_output, deploy_status],
1486
+ outputs=[js_trigger, deploy_status]
1487
  )
1488
 
1489
  # ------------------------