PraneshJs commited on
Commit
1ab6b60
·
verified ·
1 Parent(s): 4041b58

added app.py

Browse files
Files changed (1) hide show
  1. app.py +681 -0
app.py CHANGED
@@ -0,0 +1,681 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import json
4
+ import requests
5
+ from dotenv import load_dotenv
6
+ import gradio as gr
7
+ import modelscope_studio.components.antd as antd
8
+ import modelscope_studio.components.base as ms
9
+ import modelscope_studio.components.pro as pro
10
+
11
+
12
+ load_dotenv()
13
+
14
+ # ---------- CONFIG ----------
15
+ API_KEY=os.getenv("KEY") # <-- set this env var
16
+ MODEL="deepseek/deepseek-r1-0528:free" # change if you have a specific model on OpenRouter
17
+ ENDPOINT ="https://openrouter.ai/api/v1/chat/completions"
18
+
19
+
20
+ SYSTEM_PROMPT = """You are an expert on frontend design, you will always respond to web design tasks.
21
+ Your task is to create a website according to the user's request using either native HTML or React framework.
22
+ When choosing implementation framework, you should follow these rules:
23
+ [Implementation Rules]
24
+ 1. You should use HTML by default.
25
+ 2. When the user requires HTML, choose HTML to implement the request.
26
+ 3. If the user requires a library that is not installed in current react environment, please use HTML and tell the user the reason.
27
+ 4. After choosing the implementation framework, please follow the corresponding instruction.
28
+
29
+
30
+ [HTML Instruction]
31
+ You are a powerful code editing assistant capable of writing code and creating artifacts in conversations with users, or modifying and updating existing artifacts as requested by users.
32
+ All code is written in a single code block to form a complete code file for display, without separating HTML and JavaScript code. An artifact refers to a runnable complete code snippet, you prefer to integrate and output such complete runnable code rather than breaking it down into several code blocks. For certain types of code, they can render graphical interfaces in a UI window. After generation, please check the code execution again to ensure there are no errors in the output.
33
+ Do not use localStorage as it is not supported by current environment.
34
+ Output only the HTML, without any additional descriptive text.
35
+ use cdn links for external libraries.
36
+
37
+
38
+ [React Instruction]
39
+ You are an expert on frontend design, you will always respond to web design tasks.
40
+ Your task is to create a website using a SINGLE static React JSX file, which exports a default component. This code will go directly into the App.jsx file and will be used to render the website.
41
+
42
+ ## Common Design Principles
43
+
44
+ Regardless of the technology used, follow these principles for all designs:
45
+
46
+ ### General Design Guidelines:
47
+ - Create a stunning, contemporary, and highly functional website based on the user's request
48
+ - Implement a cohesive design language throughout the entire website/application
49
+ - Choose a carefully selected, harmonious color palette that enhances the overall aesthetic
50
+ - Create a clear visual hierarchy with proper typography to improve readability
51
+ - Incorporate subtle animations and transitions to add polish and improve user experience
52
+ - Ensure proper spacing and alignment using appropriate layout techniques
53
+ - Implement responsive design principles to ensure the website looks great on all device sizes
54
+ - Use modern UI patterns like cards, gradients, and subtle shadows to add depth and visual interest
55
+ - Incorporate whitespace effectively to create a clean, uncluttered design
56
+ - For images, use placeholder images from services like https://placehold.co/
57
+
58
+ ## React Design Guidelines
59
+
60
+ ### Implementation Requirements:
61
+ - Ensure the React app is a single page application
62
+ - DO NOT include any external libraries, frameworks, or dependencies outside of what is already installed
63
+ - Utilize TailwindCSS for styling, focusing on creating a visually appealing and responsive layout
64
+ - Avoid using arbitrary values (e.g., `h-[600px]`). Stick to Tailwind's predefined classes for consistency
65
+ - Use mock data instead of making HTTP requests or API calls to external services
66
+ - Utilize Tailwind's typography classes to create a clear visual hierarchy and improve readability
67
+ - Ensure proper spacing and alignment using Tailwind's margin, padding, and flexbox/grid classes
68
+ - Do not use localStorage as it is not supported by current environment.
69
+
70
+ ### Installed Libraries:
71
+ You can use these installed libraries if required.
72
+ - **lucide-react**: Lightweight SVG icon library with 1000+ icons. Import as `import { IconName } from "lucide-react"`. Perfect for buttons, navigation, status indicators, and decorative elements.
73
+ - **recharts**: Declarative charting library built on D3. Import components like `import { LineChart, BarChart } from "recharts"`. Use for data visualization, analytics dashboards, and statistical displays.
74
+ - **framer-motion**: Production-ready motion library for React. Import as `import { motion } from "framer-motion"`. Use for animations, page transitions, hover effects, and interactive micro-interactions.
75
+ - **p5.js** : JavaScript library for creative coding and generative art. Usage: import p5 from "p5". Create interactive visuals, animations, sound-driven experiences, and artistic simulations.
76
+ - **three, @react-three/fiber, @react-three/drei**: 3D graphics library with React renderer and helpers. Import as `import { Canvas } from "@react-three/fiber"` and `import { OrbitControls } from "@react-three/drei"`. Use for 3D scenes, visualizations, and immersive experiences.
77
+
78
+ Remember to only return code for the App.jsx file and nothing else. The resulting application should be visually impressive, highly functional, and something users would be proud to showcase.
79
+ """
80
+
81
+ EXAMPLES = [
82
+ {
83
+ "title":
84
+ "Bouncing ball",
85
+ "description":
86
+ "Make a page in HTML that shows an animation of a ball bouncing in a rotating hypercube.",
87
+ },
88
+ {
89
+ "title": "Pokémon SVG",
90
+ "description":
91
+ "Help me to generate an SVG of 5 Pokémons, include details."
92
+ },
93
+ {
94
+ "title":
95
+ "Strawberry card",
96
+ "description":
97
+ """How many "r"s are in the word "strawberry"? Make a cute little card!"""
98
+ },
99
+ {
100
+ "title":
101
+ "TODO list",
102
+ "description":
103
+ "I want a TODO list that allows me to add tasks, delete tasks, and I would like the overall color theme to be purple."
104
+ },
105
+ ]
106
+
107
+ DEFAULT_LOCALE = 'en_US'
108
+
109
+ DEFAULT_THEME = {
110
+ "token": {
111
+ "colorPrimary": "#6A57FF",
112
+ }
113
+ }
114
+
115
+ react_imports = {
116
+ "react": "https://esm.sh/react@18.2.0",
117
+ "react/": "https://esm.sh/react@18.2.0/",
118
+ "react-dom": "https://esm.sh/react-dom@18.2.0",
119
+ "react-dom/": "https://esm.sh/react-dom@18.2.0/",
120
+
121
+ # UI and Animation Libraries
122
+ "lucide-react": "https://esm.sh/lucide-react@0.294.0",
123
+ "framer-motion": "https://esm.sh/framer-motion@10.16.4",
124
+ "@heroicons/react": "https://esm.sh/@heroicons/react@2.0.18",
125
+ "tailwind-merge": "https://esm.sh/tailwind-merge@1.14.0",
126
+ "class-variance-authority": "https://esm.sh/class-variance-authority@0.7.0",
127
+ "clsx": "https://esm.sh/clsx@2.0.0",
128
+
129
+ # Data Visualization
130
+ "recharts": "https://esm.sh/recharts@2.9.0",
131
+ "d3": "https://esm.sh/d3@7.8.5",
132
+
133
+ # Creative and 3D Libraries
134
+ "p5": "https://esm.sh/p5@1.7.0",
135
+ "three": "https://esm.sh/three@0.158.0",
136
+ "@react-three/fiber": "https://esm.sh/@react-three/fiber@8.15.11",
137
+ "@react-three/drei": "https://esm.sh/@react-three/drei@9.88.7",
138
+
139
+ # Canvas and Graphics
140
+ "konva": "https://esm.sh/konva@9.2.3",
141
+ "react-konva": "https://esm.sh/react-konva@18.2.10",
142
+
143
+ # Physics and Simulation
144
+ "matter-js": "https://esm.sh/matter-js@0.19.0",
145
+
146
+ # Styling and UI Utils
147
+ "@tailwindcss/browser": "https://esm.sh/@tailwindcss/browser@0.4.0",
148
+ "tailwindcss": "https://esm.sh/tailwindcss@3.3.5",
149
+ "@tailwindcss/typography": "https://esm.sh/@tailwindcss/typography@0.5.10",
150
+
151
+ # State Management
152
+ "zustand": "https://esm.sh/zustand@4.4.6",
153
+ "jotai": "https://esm.sh/jotai@2.5.1",
154
+
155
+ # Utils and Helpers
156
+ "date-fns": "https://esm.sh/date-fns@2.30.0",
157
+ "lodash": "https://esm.sh/lodash@4.17.21",
158
+ "uuid": "https://esm.sh/uuid@9.0.1",
159
+
160
+ # Form Handling
161
+ "react-hook-form": "https://esm.sh/react-hook-form@7.48.2",
162
+ "zod": "https://esm.sh/zod@3.22.4"
163
+ }
164
+
165
+ # ---------- UTIL ----------
166
+ def get_generated_files(text):
167
+ patterns = {
168
+ 'html': r'```html\n(.+?)\n```',
169
+ 'jsx': r'```jsx\n(.+?)\n```',
170
+ 'tsx': r'```tsx\n(.+?)\n```',
171
+ 'ts': r'```ts\n(.+?)\n```',
172
+ 'js': r'```js\n(.+?)\n```',
173
+ }
174
+ result = {}
175
+ for ext, pattern in patterns.items():
176
+ matches = re.findall(pattern, text, re.DOTALL)
177
+ if matches:
178
+ content = '\n'.join(matches).strip()
179
+ # pick canonical filename
180
+ filename = "index." + ("tsx" if ext in ["tsx", "jsx"] else ext)
181
+ result[filename] = content
182
+ if len(result) == 0:
183
+ result["index.html"] = text.strip()
184
+ return result
185
+
186
+
187
+ def call_openrouter_chat(messages, model=MODEL, endpoint=ENDPOINT, api_key=API_KEY):
188
+ if not api_key:
189
+ raise RuntimeError("OPENROUTER_API_KEY environment variable is not set.")
190
+
191
+ headers = {
192
+ "Authorization": f"Bearer {api_key}",
193
+ "Content-Type": "application/json",
194
+ }
195
+ payload = {
196
+ "model": model,
197
+ "messages": messages,
198
+ # you can tune max_tokens, temperature, etc. if needed
199
+ # "max_tokens": 1600,
200
+ "temperature": 0.9,
201
+ }
202
+ resp = requests.post(endpoint, headers=headers, json=payload, timeout=60)
203
+ resp.raise_for_status()
204
+ return resp.json()
205
+
206
+
207
+ # ---------- EVENTS CLASS ----------
208
+ class GradioEvents:
209
+
210
+ @staticmethod
211
+ def generate_code(input_value, system_prompt_input_value, state_value):
212
+
213
+ # initial UI state while we call the API
214
+ yield {
215
+ output_loading: gr.update(spinning=True),
216
+ state_tab: gr.update(active_key="loading"),
217
+ output: gr.update(value=None)
218
+ }
219
+
220
+ if input_value is None:
221
+ input_value = ""
222
+
223
+ # Build messages: keep history, ensure system prompt presence
224
+ messages = [{
225
+ "role": "system",
226
+ "content": SYSTEM_PROMPT+" always use html if user not specifies any thing make sure u dont use black as background color" if not system_prompt_input_value else system_prompt_input_value
227
+ }] + state_value.get("history", [])
228
+
229
+ messages.append({"role": "user", "content": input_value})
230
+
231
+ try:
232
+ data = call_openrouter_chat(messages=messages)
233
+ except Exception as e:
234
+ err_text = f"Error contacting OpenRouter API: {str(e)}"
235
+ # set state and show error
236
+ state_value["history"] = messages + [{"role": "assistant", "content": err_text}]
237
+ yield {
238
+ output: gr.update(value=err_text),
239
+ state_tab: gr.update(active_key="render"),
240
+ output_loading: gr.update(spinning=False),
241
+ state: gr.update(value=state_value)
242
+ }
243
+ return
244
+
245
+ # Attempt to extract assistant content robustly
246
+ assistant_content = None
247
+ try:
248
+ # Common OpenRouter shape: choices[0].message.content
249
+ if "choices" in data and isinstance(data["choices"], list) and len(data["choices"]) > 0:
250
+ choice = data["choices"][0]
251
+ if isinstance(choice, dict) and "message" in choice and isinstance(choice["message"], dict):
252
+ assistant_content = choice["message"].get("content") or choice["message"].get("text")
253
+ # fallback: direct 'text' or 'content' fields
254
+ if not assistant_content:
255
+ assistant_content = choice.get("text") or choice.get("content")
256
+ # final fallback: top-level 'text' or 'generated_text'
257
+ if not assistant_content:
258
+ assistant_content = data.get("text") or data.get("generated_text") or json.dumps(data)
259
+ except Exception:
260
+ assistant_content = json.dumps(data)
261
+
262
+ # update history
263
+ state_value["history"] = messages + [{"role": "assistant", "content": assistant_content}]
264
+
265
+ generated_files = get_generated_files(assistant_content)
266
+ react_code = generated_files.get("index.tsx") or generated_files.get("index.jsx") or generated_files.get("index.js")
267
+ html_code = generated_files.get("index.html")
268
+
269
+ # Completed - return UI update
270
+ yield {
271
+ output: gr.update(value=assistant_content),
272
+ download_content: gr.update(value=react_code or html_code),
273
+ state_tab: gr.update(active_key="render"),
274
+ output_loading: gr.update(spinning=False),
275
+ sandbox: gr.update(
276
+ template="react" if react_code else "html",
277
+ imports=react_imports if react_code else {},
278
+ value={
279
+ "./index.tsx": """import Demo from './demo.tsx'
280
+ import "@tailwindcss/browser"
281
+
282
+ export default Demo
283
+ """,
284
+ "./demo.tsx": react_code
285
+ } if react_code else {"./index.html": html_code}),
286
+ state: gr.update(value=state_value)
287
+ }
288
+
289
+ @staticmethod
290
+ def select_example(example: dict):
291
+ return lambda: gr.update(value=example["description"])
292
+
293
+ @staticmethod
294
+ def close_modal():
295
+ return gr.update(open=False)
296
+
297
+ @staticmethod
298
+ def open_modal():
299
+ return gr.update(open=True)
300
+
301
+ @staticmethod
302
+ def disable_btns(btns: list):
303
+ return lambda: [gr.update(disabled=True) for _ in btns]
304
+
305
+ @staticmethod
306
+ def enable_btns(btns: list):
307
+ return lambda: [gr.update(disabled=False) for _ in btns]
308
+
309
+ @staticmethod
310
+ def update_system_prompt(system_prompt_input_value, state_value):
311
+ state_value["system_prompt"] = system_prompt_input_value
312
+ return gr.update(value=state_value)
313
+
314
+ @staticmethod
315
+ def reset_system_prompt(state_value):
316
+ return gr.update(value=state_value["system_prompt"])
317
+
318
+ @staticmethod
319
+ def render_history(statue_value):
320
+ return gr.update(value=statue_value["history"])
321
+
322
+ @staticmethod
323
+ def clear_history(state_value):
324
+ # modelscope_studio may have a toast; if not, this still works
325
+ try:
326
+ antd.message.success("History Cleared.")
327
+ except Exception:
328
+ pass
329
+ state_value["history"] = []
330
+ return gr.update(value=state_value)
331
+
332
+
333
+ # ---------- CSS ----------
334
+ css = """
335
+ /* Full screen container adjustments */
336
+ .gradio-container {
337
+ max-width: 100% !important;
338
+ padding: 0 !important;
339
+ }
340
+
341
+ #coder-artifacts {
342
+ min-height: 100vh;
343
+ padding: 1rem;
344
+ }
345
+
346
+ /* Output container adjustments */
347
+ #coder-artifacts .output-empty,
348
+ #coder-artifacts .output-loading {
349
+ display: flex;
350
+ flex-direction: column;
351
+ align-items: center;
352
+ justify-content: center;
353
+ width: 100%;
354
+ min-height: 85vh;
355
+ }
356
+
357
+ #coder-artifacts #output-container {
358
+ height: 85vh !important;
359
+ }
360
+
361
+ #coder-artifacts #output-container .ms-gr-ant-tabs-content,
362
+ #coder-artifacts #output-container .ms-gr-ant-tabs-tabpane {
363
+ height: 100%;
364
+ }
365
+
366
+ #coder-artifacts .output-html {
367
+ display: flex;
368
+ flex-direction: column;
369
+ width: 100%;
370
+ height: 100%;
371
+ min-height: 85vh;
372
+ }
373
+
374
+ #coder-artifacts .output-html > iframe {
375
+ flex: 1;
376
+ min-height: 85vh;
377
+ }
378
+
379
+ /* Code drawer adjustments */
380
+ #coder-artifacts-code-drawer {
381
+ height: 100vh !important;
382
+ }
383
+
384
+ #coder-artifacts-code-drawer .output-code {
385
+ flex: 1;
386
+ height: 100%;
387
+ }
388
+
389
+ #coder-artifacts-code-drawer .output-code .ms-gr-ant-spin-nested-loading {
390
+ min-height: 100%;
391
+ }
392
+
393
+ /* History drawer adjustments */
394
+ .history_chatbot {
395
+ height: 85vh !important;
396
+ }
397
+
398
+ /* Responsive adjustments */
399
+ @media (max-width: 768px) {
400
+ #coder-artifacts {
401
+ padding: 0.5rem;
402
+ }
403
+
404
+ #coder-artifacts #output-container,
405
+ #coder-artifacts .output-html,
406
+ #coder-artifacts .output-html > iframe {
407
+ min-height: 70vh;
408
+ }
409
+ }
410
+ """
411
+
412
+ # ---------- GRADIO UI ----------
413
+ with gr.Blocks(css=css) as demo:
414
+ # Global State
415
+ state = gr.State({"system_prompt": "", "history": []})
416
+ with ms.Application(elem_id="coder-artifacts") as app:
417
+ with antd.ConfigProvider(theme=DEFAULT_THEME, locale=DEFAULT_LOCALE):
418
+ with ms.AutoLoading():
419
+ with antd.Row(gutter=[32, 12],
420
+ elem_style=dict(marginTop=20),
421
+ align="stretch"):
422
+ # Left Column
423
+ with antd.Col(span=24, md=8):
424
+ with antd.Flex(vertical=True, gap="middle", wrap=True):
425
+ with antd.Flex(justify="center",
426
+ align="center",
427
+ vertical=True,
428
+ gap="middle"):
429
+ # antd.Image(
430
+ # "https://img.alicdn.com/imgextra/i2/O1CN01KDhOma1DUo8oa7OIU_!!6000000000220-1-tps-240-240.gif",
431
+ # width=200,
432
+ # height=200,
433
+ # preview=False)
434
+ antd.Typography.Title(
435
+ "My-v0",
436
+ level=1,
437
+ elem_style=dict(fontSize=24))
438
+ # Input
439
+ input = antd.Input.Textarea(
440
+ size="large",
441
+ allow_clear=True,
442
+ auto_size=dict(minRows=2, maxRows=6),
443
+ placeholder=
444
+ "Describe the web application you want to create",
445
+ elem_id="input-container")
446
+ # Input Notes
447
+ with antd.Flex(justify="space-between"):
448
+ antd.Typography.Text(
449
+ "Note: The model supports multi-round dialogue, you can make the model generate interfaces by returning React or HTML code.",
450
+ strong=True,
451
+ type="warning")
452
+
453
+ tour_btn = antd.Button("Usage Tour",
454
+ variant="filled",
455
+ color="default")
456
+ # Submit Button
457
+ submit_btn = antd.Button("Submit",
458
+ type="primary",
459
+ block=True,
460
+ size="large",
461
+ elem_id="submit-btn")
462
+
463
+ antd.Divider("Settings")
464
+
465
+ # Settings Area
466
+ with antd.Space(size="small",
467
+ wrap=True,
468
+ elem_id="settings-area"):
469
+ history_btn = antd.Button(
470
+ "📜 History",
471
+ type="default",
472
+ elem_id="history-btn",
473
+ )
474
+ cleat_history_btn = antd.Button(
475
+ "🧹 Clear History", danger=True)
476
+
477
+ antd.Divider("Examples")
478
+
479
+ # Examples
480
+ with antd.Flex(gap="small", wrap=True):
481
+ for example in EXAMPLES:
482
+ with antd.Card(
483
+ elem_style=dict(
484
+ flex="1 1 fit-content"),
485
+ hoverable=True) as example_card:
486
+ antd.Card.Meta(
487
+ title=example['title'],
488
+ description=example['description'])
489
+
490
+ example_card.click(
491
+ fn=GradioEvents.select_example(
492
+ example),
493
+ outputs=[input])
494
+
495
+ # Right Column
496
+ with antd.Col(span=24, md=16):
497
+ with antd.Card(
498
+ title="Output",
499
+ elem_style=dict(
500
+ height="85vh",
501
+ display="flex",
502
+ flexDirection="column",
503
+ margin=0,
504
+ borderRadius="8px",
505
+ boxShadow="0 4px 6px -1px rgb(0 0 0 / 0.1)"
506
+ ),
507
+ styles=dict(
508
+ body=dict(
509
+ height=0,
510
+ flex=1,
511
+ padding=0
512
+ )
513
+ ),
514
+ elem_id="output-container"):
515
+ # Output Container Extra
516
+ with ms.Slot("extra"):
517
+ with ms.Div(elem_id="output-container-extra"):
518
+ with antd.Button(
519
+ "Download Code",
520
+ type="link",
521
+ href_target="_blank",
522
+ disabled=True,
523
+ ) as download_btn:
524
+ with ms.Slot("icon"):
525
+ antd.Icon("DownloadOutlined")
526
+ download_content = gr.Text(visible=False)
527
+
528
+ view_code_btn = antd.Button(
529
+ "🧑‍💻 View Code", type="primary")
530
+ # Output Content
531
+ with antd.Tabs(
532
+ elem_style=dict(height="100%"),
533
+ active_key="empty",
534
+ render_tab_bar="() => null") as state_tab:
535
+ with antd.Tabs.Item(key="empty"):
536
+ antd.Empty(
537
+ description=
538
+ "Enter your request to generate code",
539
+ elem_classes="output-empty")
540
+ with antd.Tabs.Item(key="loading"):
541
+ with antd.Spin(
542
+ tip="Generating code...",
543
+ size="large",
544
+ elem_classes="output-loading"):
545
+ # placeholder
546
+ ms.Div()
547
+ with antd.Tabs.Item(key="render"):
548
+ sandbox = pro.WebSandbox(
549
+ height="100%",
550
+ elem_classes="output-html",
551
+ template="html",
552
+ )
553
+
554
+ # Modals and Drawers
555
+ with antd.Modal(open=False,
556
+ title="System Prompt",
557
+ width="800px") as system_prompt_modal:
558
+ system_prompt_input = antd.Input.Textarea(
559
+ value="",
560
+ size="large",
561
+ placeholder="Enter your system prompt here",
562
+ allow_clear=True,
563
+ auto_size=dict(minRows=4, maxRows=14))
564
+
565
+ with antd.Drawer(
566
+ open=False,
567
+ title="Output Code",
568
+ placement="right",
569
+ get_container=
570
+ "() => document.querySelector('.gradio-container')",
571
+ elem_id="coder-artifacts-code-drawer",
572
+ styles=dict(
573
+ body=dict(display="flex",
574
+ flexDirection="column-reverse")),
575
+ width="750px") as output_code_drawer:
576
+ with ms.Div(elem_classes="output-code"):
577
+ with antd.Spin(spinning=False) as output_loading:
578
+ output = ms.Markdown()
579
+
580
+ with antd.Drawer(
581
+ open=False,
582
+ title="Chat History",
583
+ placement="left",
584
+ get_container=
585
+ "() => document.querySelector('.gradio-container')",
586
+ width="750px") as history_drawer:
587
+ history_output = gr.Chatbot(
588
+ show_label=False,
589
+ type="messages",
590
+ height='100%',
591
+ elem_classes="history_chatbot")
592
+ # Tour
593
+ with antd.Tour(open=False) as usage_tour:
594
+ antd.Tour.Step(
595
+ title="Step 1",
596
+ description=
597
+ "Describe the web application you want to create.",
598
+ get_target=
599
+ "() => document.querySelector('#input-container')")
600
+ antd.Tour.Step(
601
+ title="Step 2",
602
+ description="Click the submit button.",
603
+ get_target=
604
+ "() => document.querySelector('#submit-btn')")
605
+ antd.Tour.Step(
606
+ title="Step 3",
607
+ description="Wait for the result.",
608
+ get_target=
609
+ "() => document.querySelector('#output-container')"
610
+ )
611
+ antd.Tour.Step(
612
+ title="Step 4",
613
+ description=
614
+ "Download the generated HTML here or view the code.",
615
+ get_target=
616
+ "() => document.querySelector('#output-container-extra')"
617
+ )
618
+ antd.Tour.Step(
619
+ title="Additional Settings",
620
+ description="You can change chat history here.",
621
+ get_target=
622
+ "() => document.querySelector('#settings-area')")
623
+
624
+ # Event Handler wiring
625
+ gr.on(fn=GradioEvents.close_modal,
626
+ triggers=[usage_tour.close, usage_tour.finish],
627
+ outputs=[usage_tour])
628
+ tour_btn.click(fn=GradioEvents.open_modal, outputs=[usage_tour])
629
+
630
+ system_prompt_modal.ok(GradioEvents.update_system_prompt,
631
+ inputs=[system_prompt_input, state],
632
+ outputs=[state]).then(fn=GradioEvents.close_modal,
633
+ outputs=[system_prompt_modal])
634
+
635
+ system_prompt_modal.cancel(GradioEvents.close_modal,
636
+ outputs=[system_prompt_modal]).then(
637
+ fn=GradioEvents.reset_system_prompt,
638
+ inputs=[state],
639
+ outputs=[system_prompt_input])
640
+ output_code_drawer.close(fn=GradioEvents.close_modal,
641
+ outputs=[output_code_drawer])
642
+ cleat_history_btn.click(fn=GradioEvents.clear_history,
643
+ inputs=[state],
644
+ outputs=[state])
645
+ history_btn.click(fn=GradioEvents.open_modal,
646
+ outputs=[history_drawer
647
+ ]).then(fn=GradioEvents.render_history,
648
+ inputs=[state],
649
+ outputs=[history_output])
650
+ history_drawer.close(fn=GradioEvents.close_modal, outputs=[history_drawer])
651
+
652
+ download_btn.click(fn=None,
653
+ inputs=[download_content],
654
+ js="""(content) => {
655
+ const blob = new Blob([content], { type: 'text/plain' })
656
+ const url = URL.createObjectURL(blob)
657
+ const a = document.createElement('a')
658
+ a.href = url
659
+ a.download = 'output.txt'
660
+ a.click()
661
+ }""")
662
+ view_code_btn.click(fn=GradioEvents.open_modal,
663
+ outputs=[output_code_drawer])
664
+ submit_btn.click(
665
+ fn=GradioEvents.open_modal,
666
+ outputs=[output_code_drawer],
667
+ ).then(fn=GradioEvents.disable_btns([submit_btn, download_btn]),
668
+ outputs=[submit_btn, download_btn]).then(
669
+ fn=GradioEvents.generate_code,
670
+ inputs=[input, system_prompt_input, state],
671
+ outputs=[
672
+ output, state_tab, sandbox, download_content,
673
+ output_loading, state
674
+ ]).then(fn=GradioEvents.enable_btns([submit_btn, download_btn]),
675
+ outputs=[submit_btn, download_btn
676
+ ]).then(fn=GradioEvents.close_modal,
677
+ outputs=[output_code_drawer])
678
+
679
+ if __name__ == "__main__":
680
+ # tune queue params as you like
681
+ demo.queue(default_concurrency_limit=10, max_size=50).launch(ssr_mode=False, max_threads=100,pwa=True)