jens-l commited on
Commit
90df85c
·
2 Parent(s): d92c7c3 73a48d3

Merge branch 'main' of github.com:HelmholtzW/likable

Browse files
Files changed (2) hide show
  1. app.py +144 -148
  2. sandbox/app.py +69 -0
app.py CHANGED
@@ -1,3 +1,7 @@
 
 
 
 
1
  import gradio as gr
2
 
3
  from planning_agent import GradioPlanningAgent
@@ -85,78 +89,86 @@ strategy. Check the detailed view for the complete plan!"""
85
  return history, ""
86
 
87
 
88
- # Mock functions for preview and code
89
- def get_preview_content():
90
- """Return HTML content for preview"""
91
- return """
92
- <div style="padding: 20px; font-family: Arial, sans-serif;">
93
- <h2>Preview - Generated App</h2>
94
- <div style="border: 1px solid #ddd; padding: 15px; margin: 10px 0;
95
- border-radius: 8px;">
96
- <h3>Todo App</h3>
97
- <input type="text" placeholder="Add a new task..."
98
- style="width: 70%; padding: 8px; margin-right: 10px;">
99
- <button style="padding: 8px 15px; background: #007bff; color: white;
100
- border: none; border-radius: 4px;">Add</button>
101
- <ul style="margin-top: 15px; list-style-type: none; padding: 0;">
102
- <li style="padding: 8px; border-bottom: 1px solid #eee;">
103
- ✓ Learn Gradio</li>
104
- <li style="padding: 8px; border-bottom: 1px solid #eee;">
105
- ✓ Build awesome UIs</li>
106
- <li style="padding: 8px; border-bottom: 1px solid #eee;">
107
- ⬜ Deploy to HuggingFace</li>
108
- </ul>
109
- </div>
110
- <p style="color: #666; font-size: 14px;">
111
- This is a live preview of your generated application.</p>
112
- </div>
113
- """
114
-
115
-
116
- def get_code_content():
117
- """Return code content for the code view"""
118
- return """import gradio as gr
119
-
120
- def add_task(new_task, tasks):
121
- if new_task.strip():
122
- tasks.append(new_task)
123
- return "", tasks
124
-
125
- def create_todo_app():
126
- with gr.Blocks() as app:
127
- gr.Markdown("# Todo App")
128
-
129
- with gr.Row():
130
- new_task = gr.Textbox(
131
- placeholder="Add a new task...",
132
- scale=3,
133
- container=False
134
- )
135
- add_btn = gr.Button("Add", scale=1)
136
 
137
- task_list = gr.Dataframe(
138
- headers=["Tasks"],
139
- datatype=["str"],
140
- interactive=False
141
- )
142
 
143
- add_btn.click(
144
- add_task,
145
- inputs=[new_task, task_list],
146
- outputs=[new_task, task_list]
147
- )
 
 
 
 
 
 
 
 
 
148
 
149
- new_task.submit(
150
- add_task,
151
- inputs=[new_task, task_list],
152
- outputs=[new_task, task_list]
153
  )
154
 
155
- return app
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
- if __name__ == "__main__":
158
- demo = create_todo_app()
159
- demo.launch()"""
 
 
 
 
 
 
160
 
161
 
162
  # Create the main Lovable-style UI
@@ -164,105 +176,89 @@ def create_lovable_ui():
164
  with gr.Blocks(
165
  title="💗Likable",
166
  theme=gr.themes.Soft(),
 
 
167
  ) as demo:
168
  gr.Markdown("# 💗Likable")
169
  # gr.Markdown(
170
  # "*It's almost Lovable - Build Gradio apps using only a chat interface*"
171
  # )
172
 
173
- with gr.Tabs():
174
- # Preview Tab
175
- with gr.TabItem("Preview"):
176
- with gr.Row(elem_classes="main-container", equal_height=True):
177
- # Left side - Chat Interface
178
- with gr.Column(scale=1, elem_classes="chat-container"):
179
- chatbot_preview = gr.Chatbot(
180
- height=500,
181
- show_copy_button=True,
182
- avatar_images=(None, "🤖"),
183
- type="messages",
184
- )
185
-
186
- with gr.Row():
187
- msg_input_preview = gr.Textbox(
188
- placeholder="Describe what you want to build...",
189
- scale=4,
190
- container=False,
191
- )
192
- send_btn_preview = gr.Button(
193
- "Send", scale=1, variant="primary"
194
- )
195
-
196
- # Right side - Preview Content
197
- with gr.Column(scale=4, elem_classes="content-container"):
198
- # with gr.Row():
199
- # gr.Button(
200
- # "Deploy to HF Spaces", variant="secondary", scale=1
201
- # )
202
-
203
- gr.HTML(value=get_preview_content())
204
-
205
- # Code Tab
206
- with gr.TabItem("Code"):
207
- with gr.Row(elem_classes="main-container", equal_height=True):
208
- # Left side - Chat Interface
209
- with gr.Column(scale=1, elem_classes="chat-container"):
210
- chatbot_code = gr.Chatbot(
211
- height=500,
212
- show_copy_button=True,
213
- avatar_images=(None, "🤖"),
214
- type="messages",
215
  )
216
-
217
- with gr.Row():
218
- msg_input_code = gr.Textbox(
219
- placeholder="Describe what you want to build...",
220
- scale=4,
221
- container=False,
222
- )
223
- send_btn_code = gr.Button(
224
- "Send", scale=1, variant="primary"
225
- )
226
-
227
- # Right side - Code Content
228
- with gr.Column(scale=4, elem_classes="content-container"):
229
- # with gr.Row():
230
- # gr.Button(
231
- # "Deploy to HF Spaces", variant="secondary", scale=1
232
- # )
233
-
234
- gr.Code(
235
- value=get_code_content(),
236
  language="python",
237
- lines=20,
238
- wrap_lines=True,
239
- show_label=False,
 
 
240
  )
241
 
242
- # Event handlers for Preview tab
243
- msg_input_preview.submit(
244
- ai_response_with_planning,
245
- inputs=[msg_input_preview, chatbot_preview],
246
- outputs=[chatbot_preview, msg_input_preview],
247
- )
248
 
249
- send_btn_preview.click(
250
- ai_response_with_planning,
251
- inputs=[msg_input_preview, chatbot_preview],
252
- outputs=[chatbot_preview, msg_input_preview],
 
 
 
 
 
253
  )
254
 
255
- # Event handlers for Code tab
256
- msg_input_code.submit(
257
  ai_response_with_planning,
258
- inputs=[msg_input_code, chatbot_code],
259
- outputs=[chatbot_code, msg_input_code],
260
  )
261
 
262
- send_btn_code.click(
263
  ai_response_with_planning,
264
- inputs=[msg_input_code, chatbot_code],
265
- outputs=[chatbot_code, msg_input_code],
266
  )
267
 
268
  return demo
 
1
+ import importlib.util
2
+ import os
3
+ import sys
4
+
5
  import gradio as gr
6
 
7
  from planning_agent import GradioPlanningAgent
 
89
  return history, ""
90
 
91
 
92
+ def load_file(path):
93
+ if path is None:
94
+ return ""
95
+ # path is a string like "subdir/example.py"
96
+ with open(path, encoding="utf-8") as f:
97
+ return f.read()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
 
 
 
 
 
99
 
100
+ def save_file(path, new_text):
101
+ if path is None:
102
+ gr.Warning("⚠️ No file selected.")
103
+ try:
104
+ with open(path, "w", encoding="utf-8") as f:
105
+ f.write(new_text)
106
+ gr.Info(f"✅ Saved to: {path.split('sandbox/')[-1]}")
107
+ except Exception as e:
108
+ gr.Error(f"❌ Error saving: {e}")
109
+
110
+
111
+ def load_and_render_app():
112
+ """Load and render the Gradio app from sandbox/app.py"""
113
+ app_path = "sandbox/app.py"
114
 
115
+ if not os.path.exists(app_path):
116
+ return gr.HTML(
117
+ "<div style='padding: 20px; color: red;'>❌ No app.py found in \
118
+ sandbox directory</div>"
119
  )
120
 
121
+ try:
122
+ # Read the app code
123
+ with open(app_path, encoding="utf-8") as f:
124
+ app_code = f.read()
125
+
126
+ # Create a temporary module
127
+ spec = importlib.util.spec_from_loader("dynamic_app", loader=None)
128
+ module = importlib.util.module_from_spec(spec)
129
+
130
+ # Add current directory to sys.path if not already there
131
+ if os.getcwd() not in sys.path:
132
+ sys.path.insert(0, os.getcwd())
133
+
134
+ # Execute the code in the module's namespace
135
+ exec(app_code, module.__dict__)
136
+
137
+ # Look for common app creation patterns
138
+ app_instance = None
139
+
140
+ # Try to find the app instance
141
+ if hasattr(module, "demo"):
142
+ app_instance = module.demo
143
+ elif hasattr(module, "app"):
144
+ app_instance = module.app
145
+ elif hasattr(module, "interface"):
146
+ app_instance = module.interface
147
+ else:
148
+ # Look for any Gradio Blocks or Interface objects
149
+ for _, obj in module.__dict__.items():
150
+ if isinstance(obj, gr.Blocks | gr.Interface):
151
+ app_instance = obj
152
+ break
153
+
154
+ if app_instance is None:
155
+ return gr.HTML(
156
+ "<div style='padding: 20px; color: orange;'>⚠️ No Gradio app found. \
157
+ Make sure your app.py creates a Gradio Blocks or Interface object.</div>"
158
+ )
159
+
160
+ # Return the app instance to be rendered
161
+ return app_instance
162
 
163
+ except Exception as e:
164
+ error_html = f"""
165
+ <div style='padding: 20px; color: red; font-family: monospace;'>
166
+ ❌ Error loading app:<br>
167
+ <pre style='background: #f5f5f5; padding: 10px; margin-top: 10px; \
168
+ border-radius: 4px;'>{str(e)}</pre>
169
+ </div>
170
+ """
171
+ return gr.HTML(error_html)
172
 
173
 
174
  # Create the main Lovable-style UI
 
176
  with gr.Blocks(
177
  title="💗Likable",
178
  theme=gr.themes.Soft(),
179
+ fill_height=True,
180
+ fill_width=True,
181
  ) as demo:
182
  gr.Markdown("# 💗Likable")
183
  # gr.Markdown(
184
  # "*It's almost Lovable - Build Gradio apps using only a chat interface*"
185
  # )
186
 
187
+ with gr.Row(elem_classes="main-container"):
188
+ # Left side - Chat Interface
189
+ with gr.Column(scale=1, elem_classes="chat-container"):
190
+ chatbot = gr.Chatbot(
191
+ show_copy_button=True,
192
+ avatar_images=(None, "🤖"),
193
+ bubble_full_width=False,
194
+ height="75vh",
195
+ )
196
+
197
+ with gr.Row():
198
+ msg_input = gr.Textbox(
199
+ placeholder="Describe what you want to build...",
200
+ scale=4,
201
+ container=False,
202
+ )
203
+ send_btn = gr.Button("Send", scale=1, variant="primary")
204
+
205
+ # Right side - Preview/Code Toggle
206
+ with gr.Column(scale=4, elem_classes="preview-container"):
207
+ with gr.Tab("Preview"):
208
+ # Create a trigger for refreshing the preview
209
+ refresh_trigger = gr.State(value=0)
210
+
211
+ # Use gr.render for dynamic app rendering
212
+ @gr.render(inputs=refresh_trigger)
213
+ def render_preview(trigger_value):
214
+ return load_and_render_app()
215
+
216
+ with gr.Tab("Code"):
217
+ with gr.Row():
218
+ save_btn = gr.Button("Save", size="sm")
219
+ with gr.Row(equal_height=True):
220
+ file_explorer = gr.FileExplorer(
221
+ scale=1,
222
+ file_count="single",
223
+ value="app.py",
224
+ root_dir="sandbox",
 
 
 
 
225
  )
226
+ code_editor = gr.Code(
227
+ scale=3,
228
+ value=load_file("sandbox/app.py"),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  language="python",
230
+ visible=True,
231
+ interactive=True,
232
+ # lines=27,
233
+ # max_lines=27,
234
+ autocomplete=True,
235
  )
236
 
237
+ # Event handlers
238
+ file_explorer.change(fn=load_file, inputs=file_explorer, outputs=code_editor)
 
 
 
 
239
 
240
+ def save_and_refresh(path, new_text, current_trigger):
241
+ save_file(path, new_text)
242
+ # Increment trigger to refresh the preview
243
+ return current_trigger + 1
244
+
245
+ save_btn.click(
246
+ fn=save_and_refresh,
247
+ inputs=[file_explorer, code_editor, refresh_trigger],
248
+ outputs=[refresh_trigger],
249
  )
250
 
251
+ # Event handlers for chat
252
+ msg_input.submit(
253
  ai_response_with_planning,
254
+ inputs=[msg_input, chatbot],
255
+ outputs=[chatbot, msg_input],
256
  )
257
 
258
+ send_btn.click(
259
  ai_response_with_planning,
260
+ inputs=[msg_input, chatbot],
261
+ outputs=[chatbot, msg_input],
262
  )
263
 
264
  return demo
sandbox/app.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ # Global list to store tasks
4
+ tasks = []
5
+
6
+
7
+ def add_task(task_text):
8
+ """Add a new task to the list"""
9
+ if task_text.strip():
10
+ tasks.append({"text": task_text.strip(), "completed": False})
11
+ return update_task_display(), ""
12
+
13
+
14
+ def toggle_task(task_index):
15
+ """Toggle completion status of a task"""
16
+ if 0 <= task_index < len(tasks):
17
+ tasks[task_index]["completed"] = not tasks[task_index]["completed"]
18
+ return update_task_display()
19
+
20
+
21
+ def delete_task(task_index):
22
+ """Delete a task from the list"""
23
+ if 0 <= task_index < len(tasks):
24
+ tasks.pop(task_index)
25
+ return update_task_display()
26
+
27
+
28
+ def update_task_display():
29
+ """Update the task display"""
30
+ if not tasks:
31
+ return "No tasks yet!"
32
+
33
+ display_text = ""
34
+ for i, task in enumerate(tasks):
35
+ status = "✓" if task["completed"] else "○"
36
+ display_text += f"{i}: {status} {task['text']}\n"
37
+ return display_text
38
+
39
+
40
+ # Create Gradio interface
41
+ with gr.Blocks(title="Simple To-Do List", theme=gr.themes.Monochrome()) as app:
42
+ gr.Markdown("# Simple To-Do List")
43
+
44
+ with gr.Row():
45
+ task_input = gr.Textbox(
46
+ placeholder="Enter a new task...", label="New Task", scale=3
47
+ )
48
+ add_btn = gr.Button("Add Task", scale=1)
49
+
50
+ task_display = gr.Textbox(
51
+ value="No tasks yet!", label="Tasks", lines=10, interactive=False
52
+ )
53
+
54
+ with gr.Row():
55
+ task_index = gr.Number(label="Task Number", value=0, precision=0)
56
+ toggle_btn = gr.Button("Toggle Complete")
57
+ delete_btn = gr.Button("Delete Task")
58
+
59
+ # Event handlers
60
+ add_btn.click(add_task, inputs=[task_input], outputs=[task_display, task_input])
61
+
62
+ task_input.submit(add_task, inputs=[task_input], outputs=[task_display, task_input])
63
+
64
+ toggle_btn.click(toggle_task, inputs=[task_index], outputs=[task_display])
65
+
66
+ delete_btn.click(delete_task, inputs=[task_index], outputs=[task_display])
67
+
68
+ if __name__ == "__main__":
69
+ app.launch()