ginipick commited on
Commit
b16d98a
ยท
verified ยท
1 Parent(s): 940fa81

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +216 -215
app.py CHANGED
@@ -74,219 +74,224 @@ def page(lst, pg):
74
  def html(cards, pg, total):
75
  if not cards:
76
  return "<div style='text-align:center;padding:70px;color:#555;'>ํ‘œ์‹œํ•  ๋ฐฐํฌ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.</div>"
77
- css = r"""
78
- <style>
79
- html, body {
80
- height: 100%;
81
- overflow: hidden !important;
82
- margin: 0;
83
- padding: 0;
84
- font-family: Poppins, sans-serif;
85
- background: #f0f0f0;
86
- }
87
- #gradio-app, #component-0, #component-1, .gradio-container {
88
- height: 100vh !important;
89
- overflow: hidden !important;
90
- position: fixed !important;
91
- top: 0 !important;
92
- left: 0 !important;
93
- right: 0 !important;
94
- bottom: 0 !important;
95
- }
96
- .main-container {
97
- position: fixed;
98
- top: 0;
99
- left: 0;
100
- right: 0;
101
- bottom: 0;
102
- height: 100vh;
103
- width: 100vw;
104
- overflow: hidden;
105
- display: flex;
106
- flex-direction: column;
107
- padding-bottom: 70px; /* ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฒ„ํŠผ์„ ์œ„ํ•œ ๊ณต๊ฐ„ */
108
- box-sizing: border-box;
109
- }
110
- .grid-container {
111
- flex: 1;
112
- overflow: hidden;
113
- padding: 10px;
114
- box-sizing: border-box;
115
- margin-bottom: 70px; /* ๋ฒ„ํŠผ์„ ์œ„ํ•œ ๊ณต๊ฐ„ ํ™•๋ณด */
116
- }
117
- .grid {
118
- display: grid;
119
- grid-template-columns: repeat(3, 1fr);
120
- grid-template-rows: repeat(3, 1fr);
121
- gap: 10px;
122
- width: 100%;
123
- height: calc(100% - 70px); /* ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฒ„ํŠผ ์˜์—ญ ์ œ์™ธ */
124
- overflow: hidden;
125
- box-sizing: border-box;
126
- }
127
- .card {
128
- background: #fff;
129
- border-radius: 10px;
130
- overflow: hidden;
131
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
132
- display: flex;
133
- flex-direction: column;
134
- height: 100%;
135
- position: relative;
136
- }
137
- .hdr {
138
- padding: 8px;
139
- background: rgba(255,255,255,.9);
140
- border-bottom: 1px solid #eee;
141
- flex-shrink: 0;
142
- height: 40px;
143
- box-sizing: border-box;
144
- z-index: 2;
145
- }
146
- .ttl {
147
- margin: 0;
148
- font-size: 0.9rem;
149
- font-weight: 600;
150
- color: #333;
151
- white-space: nowrap;
152
- overflow: hidden;
153
- text-overflow: ellipsis;
154
- }
155
- .date {
156
- margin-top: 0;
157
- font-size: 0.7rem;
158
- color: #777;
159
- }
160
- .frame {
161
- position: absolute;
162
- top: 40px;
163
- left: 0;
164
- right: 0;
165
- bottom: 30px;
166
- overflow: hidden;
167
- }
168
- .frame iframe {
169
- position: absolute;
170
- top: 0;
171
- left: 0;
172
- width: 142.857%;
173
- height: 142.857%;
174
- transform: scale(0.7);
175
- transform-origin: top left;
176
- border: 0;
177
- }
178
- .foot {
179
- position: absolute;
180
- bottom: 0;
181
- left: 0;
182
- right: 0;
183
- padding: 6px;
184
- background: rgba(255,255,255,.9);
185
- text-align: right;
186
- height: 30px;
187
- box-sizing: border-box;
188
- z-index: 2;
189
- }
190
- .link {
191
- font-size: 0.8rem;
192
- font-weight: 600;
193
- color: #4a6dd8;
194
- text-decoration: none;
195
- }
196
- .navigation-buttons {
197
- position: fixed;
198
- bottom: 10px;
199
- left: 0;
200
- right: 0;
201
- display: flex;
202
- justify-content: center;
203
- gap: 30px;
204
- padding: 10px;
205
- z-index: 1000;
206
- background: transparent;
207
- height: 60px; /* ๋ฒ„ํŠผ ์˜์—ญ์˜ ๋†’์ด ๊ณ ์ • */
208
- }
209
- .navigation-buttons button {
210
- background: rgba(255, 255, 255, 0.9);
211
- border: 1px solid #ddd;
212
- box-shadow: 0 4px 12px rgba(0,0,0,0.2);
213
- padding: 10px 30px;
214
- border-radius: 30px;
215
- font-size: 1.2rem;
216
- font-weight: bold;
217
- cursor: pointer;
218
- transition: all 0.2s;
219
- }
220
- .navigation-buttons button:hover {
221
- background: #f0f0f0;
222
- transform: translateY(-2px);
223
- box-shadow: 0 6px 15px rgba(0,0,0,0.2);
224
- }
225
- footer, .footer {
226
- display: none !important;
227
- }
228
- .page-info {
229
- position: fixed;
230
- bottom: 5px;
231
- left: 0;
232
- right: 0;
233
- text-align: center;
234
- font-size: 0.8rem;
235
- color: #777;
236
- z-index: 999;
237
- }
238
 
239
- /* ๋ชจ๋ฐ”์ผ ์ตœ์ ํ™” */
240
- @media (max-width: 768px) {
241
- .grid {
242
- grid-template-columns: repeat(2, 1fr);
243
- grid-template-rows: repeat(5, 1fr);
244
- }
245
- }
246
- @media (max-width: 480px) {
247
- .grid {
248
- grid-template-columns: 1fr;
249
- }
250
- }
251
- </style>"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
 
253
- h = css + "<div class='main-container'><div class='grid-container'><div class='grid'>"
254
  for c in cards:
255
  date = datetime.datetime.fromtimestamp(int(c["ts"])).strftime("%Y-%m-%d")
256
  h += f"""
257
- <div class='card'>
258
- <div class='hdr'><p class='ttl'>{c['title']}</p><p class='date'>{date}</p></div>
259
- <div class='frame'><iframe src="{c['url']}" loading="lazy"
260
- allow="accelerometer; camera; encrypted-media; gyroscope;"></iframe></div>
261
- <div class='foot'><a class='link' href="{c['url']}" target="_blank">์›๋ณธโ†—</a></div>
262
- </div>"""
263
- h += "</div></div>"
264
-
265
- # ํŽ˜์ด์ง€ ์ •๋ณด
266
- h += f"<div class='page-info'>Page {pg} / {total}</div>"
 
 
 
267
 
268
- # ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฒ„ํŠผ์„ ์œ„ํ•œ ์ถ”๊ฐ€ ๊ณต๊ฐ„
269
- h += "<div style='height:60px;'></div></div>"
270
-
271
- # JavaScript๋กœ ์Šคํฌ๋กค ๋ฐฉ์ง€ ์ถ”๊ฐ€
272
- h += """
273
- <script>
274
- // ์Šคํฌ๋กค ๋ฐฉ์ง€
275
- document.addEventListener('DOMContentLoaded', function() {
276
- document.body.style.overflow = 'hidden';
277
- document.documentElement.style.overflow = 'hidden';
278
-
279
- // ํ„ฐ์น˜ ์ด๋ฒคํŠธ ๋ฐฉ์ง€ (๋ชจ๋ฐ”์ผ)
280
- document.addEventListener('touchmove', function(e) {
281
- e.preventDefault();
282
- }, { passive: false });
283
 
284
- // ์Šคํฌ๋กค ์ด๋ฒคํŠธ ๋ฐฉ์ง€
285
- document.addEventListener('wheel', function(e) {
286
- e.preventDefault();
287
- }, { passive: false });
288
- });
289
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  """
291
 
292
  return h
@@ -294,18 +299,14 @@ def html(cards, pg, total):
294
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 6. Gradio Blocks UI โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
295
  def build():
296
  _init_best()
297
- with gr.Blocks(title="Vibe Game Craft", css="html,body{margin:0;padding:0;overflow:hidden !important;} footer{display:none !important;} .gradio-container{overflow:hidden !important; height:100vh; position:fixed; top:0; left:0; right:0; bottom:0;}") as demo:
298
  # ์ƒํƒœ ๋ฐ ์ถœ๋ ฅ
299
  bp = gr.State(1)
300
- out = gr.HTML(elem_id="main-output")
301
-
302
- # ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฒ„ํŠผ์„ ์œ„ํ•œ ์ถ”๊ฐ€ ๊ณต๊ฐ„ (๋ณด์ด์ง€ ์•Š๋Š” ์š”์†Œ)
303
- gr.HTML("<div style='height:60px;'></div>")
304
 
305
- # ํŽ˜์ด์ง€ ๋„ค๋น„๊ฒŒ์ด์…˜
306
- with gr.Row(elem_classes="navigation-buttons"):
307
- b_prev = gr.Button("โ—€ ์ด์ „", size="lg")
308
- b_next = gr.Button("๋‹ค์Œ โ–ถ", size="lg")
309
 
310
  def show_best(p=1):
311
  d, t = page(_load_best(), p)
 
74
  def html(cards, pg, total):
75
  if not cards:
76
  return "<div style='text-align:center;padding:70px;color:#555;'>ํ‘œ์‹œํ•  ๋ฐฐํฌ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.</div>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
+ # ์ˆœ์ˆ˜ HTML๊ณผ CSS๋กœ ๊ตฌํ˜„
79
+ h = f"""
80
+ <!DOCTYPE html>
81
+ <html>
82
+ <head>
83
+ <meta charset="UTF-8">
84
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
85
+ <style>
86
+ * {{
87
+ margin: 0;
88
+ padding: 0;
89
+ box-sizing: border-box;
90
+ }}
91
+
92
+ body, html {{
93
+ width: 100%;
94
+ height: 100%;
95
+ overflow: hidden;
96
+ font-family: Arial, sans-serif;
97
+ background: #f0f0f0;
98
+ }}
99
+
100
+ #app {{
101
+ position: fixed;
102
+ top: 0;
103
+ left: 0;
104
+ width: 100%;
105
+ height: 100%;
106
+ display: flex;
107
+ flex-direction: column;
108
+ }}
109
+
110
+ #grid-container {{
111
+ flex: 1;
112
+ display: grid;
113
+ grid-template-columns: repeat(3, 1fr);
114
+ grid-template-rows: repeat(3, 1fr);
115
+ gap: 10px;
116
+ padding: 10px;
117
+ width: 100%;
118
+ height: 100%;
119
+ }}
120
+
121
+ .card {{
122
+ background: white;
123
+ border-radius: 10px;
124
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
125
+ display: flex;
126
+ flex-direction: column;
127
+ overflow: hidden;
128
+ position: relative;
129
+ height: 100%;
130
+ }}
131
+
132
+ .card-header {{
133
+ padding: 8px 12px;
134
+ font-size: 0.9rem;
135
+ font-weight: bold;
136
+ border-bottom: 1px solid #f0f0f0;
137
+ z-index: 2;
138
+ background: rgba(255,255,255,0.9);
139
+ overflow: hidden;
140
+ text-overflow: ellipsis;
141
+ white-space: nowrap;
142
+ }}
143
+
144
+ .card-date {{
145
+ font-size: 0.75rem;
146
+ color: #888;
147
+ margin-top: 2px;
148
+ }}
149
+
150
+ .card-frame {{
151
+ flex: 1;
152
+ position: relative;
153
+ overflow: hidden;
154
+ }}
155
+
156
+ .card-frame iframe {{
157
+ position: absolute;
158
+ top: 0;
159
+ left: 0;
160
+ width: 142.857%;
161
+ height: 142.857%;
162
+ transform: scale(0.7);
163
+ transform-origin: top left;
164
+ border: 0;
165
+ }}
166
+
167
+ .card-footer {{
168
+ padding: 6px 12px;
169
+ text-align: right;
170
+ font-size: 0.8rem;
171
+ border-top: 1px solid #f0f0f0;
172
+ background: rgba(255,255,255,0.9);
173
+ z-index: 2;
174
+ }}
175
+
176
+ .card-footer a {{
177
+ color: #4a6dd8;
178
+ text-decoration: none;
179
+ font-weight: bold;
180
+ }}
181
+
182
+ #nav-container {{
183
+ position: absolute;
184
+ bottom: 0;
185
+ left: 0;
186
+ width: 100%;
187
+ padding: 15px 0;
188
+ display: flex;
189
+ justify-content: center;
190
+ align-items: center;
191
+ gap: 20px;
192
+ z-index: 10;
193
+ }}
194
+
195
+ .nav-button {{
196
+ padding: 10px 25px;
197
+ background: rgba(255,255,255,0.9);
198
+ border: 1px solid #ddd;
199
+ border-radius: 50px;
200
+ font-size: 1.1rem;
201
+ font-weight: bold;
202
+ cursor: pointer;
203
+ box-shadow: 0 4px 10px rgba(0,0,0,0.15);
204
+ transition: all 0.2s ease;
205
+ color: #333;
206
+ }}
207
+
208
+ .nav-button:hover {{
209
+ transform: translateY(-2px);
210
+ box-shadow: 0 6px 15px rgba(0,0,0,0.18);
211
+ }}
212
+
213
+ .page-info {{
214
+ position: absolute;
215
+ bottom: 5px;
216
+ left: 0;
217
+ width: 100%;
218
+ text-align: center;
219
+ font-size: 0.8rem;
220
+ color: #888;
221
+ z-index: 9;
222
+ }}
223
+
224
+ /* ๋ฐฐ๊ฒฝ ๊ทธ๋ž˜๋””์–ธํŠธ ํšจ๊ณผ */
225
+ #bottom-gradient {{
226
+ position: absolute;
227
+ bottom: 0;
228
+ left: 0;
229
+ width: 100%;
230
+ height: 80px;
231
+ background: linear-gradient(to bottom, rgba(240,240,240,0) 0%, rgba(240,240,240,0.9) 70%);
232
+ z-index: 5;
233
+ pointer-events: none;
234
+ }}
235
+ </style>
236
+ </head>
237
+ <body>
238
+ <div id="app">
239
+ <div id="grid-container">
240
+ """
241
 
242
+ # ์นด๋“œ ์ถ”๊ฐ€
243
  for c in cards:
244
  date = datetime.datetime.fromtimestamp(int(c["ts"])).strftime("%Y-%m-%d")
245
  h += f"""
246
+ <div class="card">
247
+ <div class="card-header">
248
+ {c['title']}
249
+ <div class="card-date">{date}</div>
250
+ </div>
251
+ <div class="card-frame">
252
+ <iframe src="{c['url']}" loading="lazy" allow="accelerometer; camera; encrypted-media; gyroscope;"></iframe>
253
+ </div>
254
+ <div class="card-footer">
255
+ <a href="{c['url']}" target="_blank">์›๋ณธโ†—</a>
256
+ </div>
257
+ </div>
258
+ """
259
 
260
+ # ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฐ ํŽ˜์ด์ง€ ์ •๋ณด
261
+ h += f"""
262
+ </div>
263
+ <div id="bottom-gradient"></div>
264
+ <div id="nav-container">
265
+ <button class="nav-button" onclick="prevPage()">โ—€ ์ด์ „</button>
266
+ <button class="nav-button" onclick="nextPage()">๋‹ค์Œ โ–ถ</button>
267
+ </div>
268
+ <div class="page-info">Page {pg} / {total}</div>
269
+ </div>
 
 
 
 
 
270
 
271
+ <script>
272
+ function prevPage() {{
273
+ if ({pg} > 1) {{
274
+ document.getElementById('prev-button').click();
275
+ }}
276
+ }}
277
+
278
+ function nextPage() {{
279
+ if ({pg} < {total}) {{
280
+ document.getElementById('next-button').click();
281
+ }}
282
+ }}
283
+
284
+ // ์Šคํฌ๋กค ๋ฐฉ์ง€
285
+ document.addEventListener('wheel', function(e) {{
286
+ e.preventDefault();
287
+ }}, {{ passive: false }});
288
+
289
+ document.addEventListener('touchmove', function(e) {{
290
+ e.preventDefault();
291
+ }}, {{ passive: false }});
292
+ </script>
293
+ </body>
294
+ </html>
295
  """
296
 
297
  return h
 
299
  # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 6. Gradio Blocks UI โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
300
  def build():
301
  _init_best()
302
+ with gr.Blocks(title="Vibe Game Craft", css="footer{display:none !important;}") as demo:
303
  # ์ƒํƒœ ๋ฐ ์ถœ๋ ฅ
304
  bp = gr.State(1)
305
+ out = gr.HTML(elem_id="display-area", visible=True)
 
 
 
306
 
307
+ # ์ˆจ๊ฒจ์ง„ ๋ฒ„ํŠผ (JavaScript์—์„œ ํ˜ธ์ถœ์šฉ)
308
+ b_prev = gr.Button("์ด์ „", elem_id="prev-button", visible=False)
309
+ b_next = gr.Button("๋‹ค์Œ", elem_id="next-button", visible=False)
 
310
 
311
  def show_best(p=1):
312
  d, t = page(_load_best(), p)