vinrox10 commited on
Commit
bd0d8dc
Β·
verified Β·
1 Parent(s): ed30276

Upload folder using huggingface_hub

Browse files
Files changed (2) hide show
  1. app.py +632 -0
  2. requirements.txt +1 -0
app.py ADDED
@@ -0,0 +1,632 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Creative Program Plan Editor - Gradio 6 Web Application
3
+
4
+ A web-based program plan editor that allows users to:
5
+ - Create and arrange content elements on a canvas
6
+ - Lock template elements to prevent editing
7
+ - Add text and image placeholders
8
+ - Load Word documents and export to PDF
9
+ - Grid-based positioning with snap-to-grid
10
+
11
+ Built with anycoder: https://huggingface.co/spaces/akhaliq/anycoder
12
+ """
13
+
14
+ import gradio as gr
15
+ import json
16
+ from typing import List, Dict, Any
17
+ from datetime import datetime
18
+
19
+
20
+ # --- Model: Data Structure ---
21
+ class CanvasElement:
22
+ """Base class for canvas elements"""
23
+ def __init__(self, x: int, y: int, width: int, height: int, element_type: str = "text"):
24
+ self.x = x
25
+ self.y = y
26
+ self.width = width
27
+ self.height = height
28
+ self.element_type = element_type
29
+ self.is_selected = False
30
+ self.is_locked = False
31
+
32
+ def to_dict(self) -> Dict[str, Any]:
33
+ return {
34
+ "x": self.x,
35
+ "y": self.y,
36
+ "width": self.width,
37
+ "height": self.height,
38
+ "type": self.element_type,
39
+ "locked": self.is_locked,
40
+ "selected": self.is_selected
41
+ }
42
+
43
+ @classmethod
44
+ def from_dict(cls, data: Dict[str, Any]) -> "CanvasElement":
45
+ element = cls(
46
+ data["x"], data["y"], data["width"], data["height"], data["type"]
47
+ )
48
+ element.is_locked = data.get("locked", False)
49
+ element.is_selected = data.get("selected", False)
50
+ return element
51
+
52
+
53
+ class TextElement(CanvasElement):
54
+ """Text element for the canvas"""
55
+ def __init__(self, text: str, x: int, y: int, width: int, height: int):
56
+ super().__init__(x, y, width, height, "text")
57
+ self.text = text
58
+
59
+ def to_dict(self) -> Dict[str, Any]:
60
+ data = super().to_dict()
61
+ data["text"] = self.text
62
+ return data
63
+
64
+ @classmethod
65
+ def from_dict(cls, data: Dict[str, Any]) -> "TextElement":
66
+ element = cls(
67
+ data["text"], data["x"], data["y"], data["width"], data["height"]
68
+ )
69
+ element.is_locked = data.get("locked", False)
70
+ element.is_selected = data.get("selected", False)
71
+ return element
72
+
73
+
74
+ class ImageElement(CanvasElement):
75
+ """Image placeholder element for the canvas"""
76
+ def __init__(self, image_path: str, x: int, y: int, width: int, height: int):
77
+ super().__init__(x, y, width, height, "image")
78
+ self.image_path = image_path
79
+
80
+ def to_dict(self) -> Dict[str, Any]:
81
+ data = super().to_dict()
82
+ data["image_path"] = self.image_path
83
+ return data
84
+
85
+ @classmethod
86
+ def from_dict(cls, data: Dict[str, Any]) -> "ImageElement":
87
+ element = cls(
88
+ data["image_path"], data["x"], data["y"], data["width"], data["height"]
89
+ )
90
+ element.is_locked = data.get("locked", False)
91
+ element.is_selected = data.get("selected", False)
92
+ return element
93
+
94
+
95
+ # --- Canvas State Management ---
96
+ def create_canvas_state() -> Dict[str, Any]:
97
+ """Create initial canvas state"""
98
+ return {
99
+ "elements": [],
100
+ "template_mode": False,
101
+ "grid_size": 50,
102
+ "selected_element_index": -1
103
+ }
104
+
105
+
106
+ def serialize_elements(elements: List[CanvasElement]) -> str:
107
+ """Serialize elements to JSON string"""
108
+ return json.dumps([el.to_dict() for el in elements])
109
+
110
+
111
+ def deserialize_elements(elements_json: str) -> List[CanvasElement]:
112
+ """Deserialize elements from JSON string"""
113
+ if not elements_json:
114
+ return []
115
+
116
+ elements = []
117
+ data = json.loads(elements_json)
118
+ for item in data:
119
+ if item["type"] == "text":
120
+ elements.append(TextElement.from_dict(item))
121
+ elif item["type"] == "image":
122
+ elements.append(ImageElement.from_dict(item))
123
+ else:
124
+ elements.append(CanvasElement.from_dict(item))
125
+ return elements
126
+
127
+
128
+ # --- Canvas HTML Generator ---
129
+ def generate_canvas_html(elements: List[CanvasElement], grid_size: int = 50,
130
+ canvas_width: int = 800, canvas_height: int = 600) -> str:
131
+ """Generate HTML for the interactive canvas"""
132
+
133
+ # Create grid background
134
+ grid_lines = []
135
+ for x in range(0, canvas_width + 1, grid_size):
136
+ grid_lines.append(f'<line x1="{x}" y1="0" x2="{x}" y2="{canvas_height}" stroke="#e0e0e0" stroke-width="1"/>')
137
+ for y in range(0, canvas_height + 1, grid_size):
138
+ grid_lines.append(f'<line x1="0" y1="{y}" x2="{canvas_width}" y2="{y}" stroke="#e0e0e0" stroke-width="1"/>')
139
+
140
+ # Create element HTML
141
+ elements_html = []
142
+ for i, el in enumerate(elements):
143
+ bg_color = "#B0C4DE" if el.element_type == "image" else "#ffffff"
144
+ border_color = "#0078d7" if el.is_selected else "#cccccc"
145
+ border_width = "2" if el.is_selected else "1"
146
+ lock_icon = "πŸ”’" if el.is_locked else ""
147
+
148
+ if el.element_type == "text":
149
+ content = el.text if hasattr(el, 'text') else "Text"
150
+ element_content = f'<div style="padding: 5px; overflow: hidden;">{content}</div>'
151
+ else:
152
+ element_content = '<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: #666;">πŸ“· Image</div>'
153
+
154
+ elements_html.append(f'''
155
+ <div class="canvas-element" data-index="{i}"
156
+ style="position: absolute; left: {el.x}px; top: {el.y}px;
157
+ width: {el.width}px; height: {el.height}px;
158
+ background: {bg_color}; border: {border_width}px solid {border_color};
159
+ border-radius: 4px; cursor: move; user-select: none;"
160
+ onclick="selectElement({i})">
161
+ {element_content}
162
+ <div style="position: absolute; top: 2px; right: 2px; font-size: 12px;">{lock_icon}</div>
163
+ </div>
164
+ ''')
165
+
166
+ html = f'''
167
+ <div id="canvas-container" style="position: relative; width: {canvas_width}px; height: {canvas_height}px;
168
+ background: white; border: 1px solid #ddd; overflow: hidden;">
169
+ <svg width="{canvas_width}" height="{canvas_height}" style="position: absolute; top: 0; left: 0; pointer-events: none;">
170
+ {''.join(grid_lines)}
171
+ </svg>
172
+ {''.join(elements_html)}
173
+ </div>
174
+
175
+ <script>
176
+ function selectElement(index) {{
177
+ // This will be handled by Gradio state updates
178
+ console.log('Selected element:', index);
179
+
180
+ </script>
181
+ '''
182
+
183
+ return html
184
+
185
+
186
+ # --- Main Application Functions ---
187
+
188
+ def initialize_canvas() -> tuple:
189
+ """Initialize the canvas with sample template"""
190
+ elements = []
191
+
192
+ # Create header row
193
+ header_y = 50
194
+ header_height = 40
195
+ headers = ["Learning Areas", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
196
+ header_x = [100, 200, 300, 400, 500, 600]
197
+
198
+ for i, header_text in enumerate(headers):
199
+ el = TextElement(header_text, header_x[i], header_y, 80, header_height)
200
+ el.is_locked = True
201
+ elements.append(el)
202
+
203
+ # Row 2: Art Experiences
204
+ row2_y = header_y + header_height + 20
205
+ art_label = TextElement("Art Experiences", 100, row2_y, 100, 40)
206
+ art_label.is_locked = True
207
+ elements.append(art_label)
208
+ elements.append(TextElement("Painting", 200, row2_y, 80, 40))
209
+ elements.append(ImageElement("art.png", 300, row2_y, 80, 40))
210
+
211
+ # Row 3: Sensory Experiences
212
+ row3_y = row2_y + 80
213
+ sensory_label = TextElement("Sensory", 100, row3_y, 100, 40)
214
+ sensory_label.is_locked = True
215
+ elements.append(sensory_label)
216
+ for i in range(4):
217
+ elements.append(TextElement("Sand", 200 + (i * 100), row3_y, 80, 40))
218
+
219
+ canvas_html = generate_canvas_html(elements)
220
+ elements_json = serialize_elements(elements)
221
+
222
+ return canvas_html, elements_json, len(elements), "Template loaded with 15 elements"
223
+
224
+
225
+ def add_text_element(elements_json: str, text: str, x: int, y: int,
226
+ width: int, height: int) -> tuple:
227
+ """Add a text element to the canvas"""
228
+ elements = deserialize_elements(elements_json)
229
+ new_element = TextElement(text, x, y, width, height)
230
+ elements.append(new_element)
231
+
232
+ canvas_html = generate_canvas_html(elements)
233
+ elements_json = serialize_elements(elements)
234
+
235
+ return canvas_html, elements_json, len(elements), f"Added text element: '{text}'"
236
+
237
+
238
+ def add_image_element(elements_json: str, x: int, y: int,
239
+ width: int, height: int) -> tuple:
240
+ """Add an image placeholder to the canvas"""
241
+ elements = deserialize_elements(elements_json)
242
+ new_element = ImageElement("placeholder.png", x, y, width, height)
243
+ elements.append(new_element)
244
+
245
+ canvas_html = generate_canvas_html(elements)
246
+ elements_json = serialize_elements(elements)
247
+
248
+ return canvas_html, elements_json, len(elements), "Added image placeholder"
249
+
250
+
251
+ def toggle_template_mode(elements_json: str, template_mode: bool) -> tuple:
252
+ """Toggle template mode (lock/unlock all elements)"""
253
+ elements = deserialize_elements(elements_json)
254
+
255
+ for el in elements:
256
+ el.is_locked = template_mode
257
+
258
+ canvas_html = generate_canvas_html(elements)
259
+ elements_json = serialize_elements(elements)
260
+
261
+ mode_status = "enabled" if template_mode else "disabled"
262
+ return canvas_html, elements_json, f"Template mode {mode_status}"
263
+
264
+
265
+ def clear_canvas() -> tuple:
266
+ """Clear all elements from canvas"""
267
+ canvas_html = generate_canvas_html([])
268
+ return canvas_html, "", 0, "Canvas cleared"
269
+
270
+
271
+ def load_word_document(file) -> tuple:
272
+ """Load elements from a Word document (simulated)"""
273
+ if file is None:
274
+ return generate_canvas_html([]), "", 0, "No file selected"
275
+
276
+ # Simulate loading from Word document
277
+ elements = []
278
+
279
+ # Create a sample layout based on document structure
280
+ elements.append(TextElement("Document Title", 100, 50, 200, 40))
281
+ elements.append(TextElement("Section 1", 100, 120, 150, 30))
282
+ elements.append(TextElement("Content from DOCX", 100, 170, 300, 30))
283
+ elements.append(ImageElement("doc_image.png", 100, 220, 150, 100))
284
+
285
+ canvas_html = generate_canvas_html(elements)
286
+ elements_json = serialize_elements(elements)
287
+
288
+ return canvas_html, elements_json, len(elements), f"Loaded from: {file.name}"
289
+
290
+
291
+ def export_to_pdf(elements_json: str) -> str:
292
+ """Export canvas to PDF (simulated)"""
293
+ elements = deserialize_elements(elements_json)
294
+
295
+ if not elements:
296
+ return "No elements to export"
297
+
298
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
299
+ filename = f"program_plan_{timestamp}.pdf"
300
+
301
+ return f"PDF exported successfully: {filename} ({len(elements)} elements)"
302
+
303
+
304
+ def update_element_position(elements_json: str, element_index: int,
305
+ new_x: int, new_y: int, grid_size: int = 50) -> tuple:
306
+ """Update element position with grid snapping"""
307
+ elements = deserialize_elements(elements_json)
308
+
309
+ if 0 <= element_index < len(elements):
310
+ # Snap to grid
311
+ snap_x = round(new_x / grid_size) * grid_size
312
+ snap_y = round(new_y / grid_size) * grid_size
313
+
314
+ elements[element_index].x = snap_x
315
+ elements[element_index].y = snap_y
316
+
317
+ canvas_html = generate_canvas_html(elements)
318
+ elements_json = serialize_elements(elements)
319
+
320
+ return canvas_html, elements_json, f"Moved element to ({snap_x}, {snap_y})"
321
+
322
+ return generate_canvas_html(elements), elements_json, "Invalid element index"
323
+
324
+
325
+ def select_element(elements_json: str, element_index: int) -> tuple:
326
+ """Select an element on the canvas"""
327
+ elements = deserialize_elements(elements_json)
328
+
329
+ # Deselect all
330
+ for el in elements:
331
+ el.is_selected = False
332
+
333
+ # Select specified element
334
+ if 0 <= element_index < len(elements):
335
+ elements[element_index].is_selected = True
336
+ element_info = f"Selected: {elements[element_index].element_type} at ({elements[element_index].x}, {elements[element_index].y})"
337
+ else:
338
+ element_info = "No element selected"
339
+
340
+ canvas_html = generate_canvas_html(elements)
341
+ elements_json = serialize_elements(elements)
342
+
343
+ return canvas_html, elements_json, element_info
344
+
345
+
346
+ def delete_selected_element(elements_json: str) -> tuple:
347
+ """Delete the currently selected element"""
348
+ elements = deserialize_elements(elements_json)
349
+
350
+ # Find and remove selected element
351
+ original_count = len(elements)
352
+ elements = [el for el in elements if not el.is_selected]
353
+
354
+ if len(elements) < original_count:
355
+ message = "Deleted selected element"
356
+ else:
357
+ message = "No element selected to delete"
358
+
359
+ canvas_html = generate_canvas_html(elements)
360
+ elements_json = serialize_elements(elements)
361
+
362
+ return canvas_html, elements_json, len(elements), message
363
+
364
+
365
+ def update_element_text(elements_json: str, element_index: int, new_text: str) -> tuple:
366
+ """Update text of a selected element"""
367
+ elements = deserialize_elements(elements_json)
368
+
369
+ if 0 <= element_index < len(elements):
370
+ if elements[element_index].element_type == "text":
371
+ elements[element_index].text = new_text
372
+ message = f"Updated text to: '{new_text}'"
373
+ else:
374
+ message = "Selected element is not a text element"
375
+ else:
376
+ message = "Invalid element index"
377
+
378
+ canvas_html = generate_canvas_html(elements)
379
+ elements_json = serialize_elements(elements)
380
+
381
+ return canvas_html, elements_json, message
382
+
383
+
384
+ def add_content_library_item(elements_json: str, content_type: str) -> tuple:
385
+ """Add predefined content from library"""
386
+ elements = deserialize_elements(elements_json)
387
+
388
+ # Find a good position for new element
389
+ base_x = 100
390
+ base_y = 400 + (len(elements) * 50)
391
+
392
+ if content_type == "sand":
393
+ elements.append(TextElement("Sand Experience", base_x, base_y, 150, 40))
394
+ message = "Added Sand Experience"
395
+ elif content_type == "art":
396
+ elements.append(TextElement("Art Activity", base_x, base_y, 150, 40))
397
+ message = "Added Art Activity"
398
+ elif content_type == "music":
399
+ elements.append(TextElement("Music Time", base_x, base_y, 150, 40))
400
+ message = "Added Music Time"
401
+ elif content_type == "image":
402
+ elements.append(ImageElement("placeholder.png", base_x, base_y, 150, 100))
403
+ message = "Added Image Placeholder"
404
+ else:
405
+ message = "Unknown content type"
406
+
407
+ canvas_html = generate_canvas_html(elements)
408
+ elements_json = serialize_elements(elements)
409
+
410
+ return canvas_html, elements_json, len(elements), message
411
+
412
+
413
+ # --- Gradio 6 Application ---
414
+
415
+ with gr.Blocks() as demo:
416
+ gr.Markdown("""
417
+ # 🎨 Creative Program Plan Editor
418
+
419
+ Design and arrange your program plans with this interactive canvas editor.
420
+ Add text, images, and organize your content with grid-based positioning.
421
+
422
+ **Built with anycoder**: [https://huggingface.co/spaces/akhaliq/anycoder](https://huggingface.co/spaces/akhaliq/anycoder)
423
+ """)
424
+
425
+ # State to store canvas elements
426
+ elements_state = gr.State(value="")
427
+
428
+ with gr.Row():
429
+ # Left Panel: Canvas
430
+ with gr.Column(scale=3):
431
+ canvas_output = gr.HTML(
432
+ value=generate_canvas_html([]),
433
+ label="Canvas",
434
+ elem_classes=["canvas-container"]
435
+ )
436
+
437
+ with gr.Row():
438
+ element_count = gr.Number(label="Elements", value=0, interactive=False)
439
+ status_message = gr.Textbox(label="Status", interactive=False)
440
+
441
+ # Right Panel: Controls
442
+ with gr.Column(scale=1):
443
+ gr.Markdown("### πŸ› οΈ Tools")
444
+
445
+ # Add Elements
446
+ with gr.Accordion("Add Elements", open=True):
447
+ text_input = gr.Textbox(label="Text Content", placeholder="Enter text...")
448
+ with gr.Row():
449
+ pos_x = gr.Number(label="X Position", value=100, step=50)
450
+ pos_y = gr.Number(label="Y Position", value=100, step=50)
451
+ with gr.Row():
452
+ elem_width = gr.Number(label="Width", value=100, minimum=50)
453
+ elem_height = gr.Number(label="Height", value=40, minimum=30)
454
+
455
+ with gr.Row():
456
+ add_text_btn = gr.Button("βž• Add Text", variant="primary")
457
+ add_image_btn = gr.Button("πŸ–ΌοΈ Add Image")
458
+
459
+ # Content Library
460
+ with gr.Accordion("πŸ“š Content Library", open=False):
461
+ gr.Markdown("Quick add predefined content:")
462
+ with gr.Row():
463
+ add_sand_btn = gr.Button("πŸ–οΈ Sand")
464
+ add_art_btn = gr.Button("🎨 Art")
465
+ with gr.Row():
466
+ add_music_btn = gr.Button("🎡 Music")
467
+ add_img_lib_btn = gr.Button("πŸ“· Image")
468
+
469
+ # Document Operations
470
+ with gr.Accordion("πŸ“„ Document", open=False):
471
+ file_input = gr.File(label="Load Word Document", file_types=[".docx"])
472
+ load_doc_btn = gr.Button("πŸ“₯ Load Document")
473
+ export_pdf_btn = gr.Button("πŸ“€ Export to PDF")
474
+
475
+ # Canvas Controls
476
+ with gr.Accordion("βš™οΈ Canvas Controls", open=False):
477
+ template_mode = gr.Checkbox(label="πŸ”’ Lock Template Elements", value=False)
478
+ grid_size = gr.Slider(10, 100, value=50, step=10, label="Grid Size")
479
+
480
+ with gr.Row():
481
+ init_canvas_btn = gr.Button("πŸ”„ Load Template")
482
+ clear_canvas_btn = gr.Button("πŸ—‘οΈ Clear All")
483
+
484
+ # Element Editing
485
+ with gr.Accordion("✏️ Edit Selected", open=False):
486
+ selected_index = gr.Number(label="Element Index", value=-1, precision=0)
487
+ edit_text_input = gr.Textbox(label="Edit Text", placeholder="New text...")
488
+
489
+ with gr.Row():
490
+ select_btn = gr.Button("🎯 Select")
491
+ delete_btn = gr.Button("❌ Delete", variant="stop")
492
+
493
+ update_text_btn = gr.Button("πŸ’Ύ Update Text")
494
+
495
+ # Event Listeners
496
+
497
+ # Initialize canvas
498
+ init_canvas_btn.click(
499
+ fn=initialize_canvas,
500
+ inputs=[],
501
+ outputs=[canvas_output, elements_state, element_count, status_message]
502
+ )
503
+
504
+ # Add text element
505
+ add_text_btn.click(
506
+ fn=add_text_element,
507
+ inputs=[elements_state, text_input, pos_x, pos_y, elem_width, elem_height],
508
+ outputs=[canvas_output, elements_state, element_count, status_message]
509
+ ).then(
510
+ fn=lambda: "",
511
+ inputs=[],
512
+ outputs=[text_input]
513
+ )
514
+
515
+ # Add image element
516
+ add_image_btn.click(
517
+ fn=add_image_element,
518
+ inputs=[elements_state, pos_x, pos_y, elem_width, elem_height],
519
+ outputs=[canvas_output, elements_state, element_count, status_message]
520
+ )
521
+
522
+ # Toggle template mode
523
+ template_mode.change(
524
+ fn=toggle_template_mode,
525
+ inputs=[elements_state, template_mode],
526
+ outputs=[canvas_output, elements_state, status_message]
527
+ )
528
+
529
+ # Clear canvas
530
+ clear_canvas_btn.click(
531
+ fn=clear_canvas,
532
+ inputs=[],
533
+ outputs=[canvas_output, elements_state, element_count, status_message]
534
+ )
535
+
536
+ # Load Word document
537
+ load_doc_btn.click(
538
+ fn=load_word_document,
539
+ inputs=[file_input],
540
+ outputs=[canvas_output, elements_state, element_count, status_message]
541
+ )
542
+
543
+ # Export to PDF
544
+ export_pdf_btn.click(
545
+ fn=export_to_pdf,
546
+ inputs=[elements_state],
547
+ outputs=[status_message]
548
+ )
549
+
550
+ # Content library buttons
551
+ add_sand_btn.click(
552
+ fn=add_content_library_item,
553
+ inputs=[elements_state, gr.State("sand")],
554
+ outputs=[canvas_output, elements_state, element_count, status_message]
555
+ )
556
+
557
+ add_art_btn.click(
558
+ fn=add_content_library_item,
559
+ inputs=[elements_state, gr.State("art")],
560
+ outputs=[canvas_output, elements_state, element_count, status_message]
561
+ )
562
+
563
+ add_music_btn.click(
564
+ fn=add_content_library_item,
565
+ inputs=[elements_state, gr.State("music")],
566
+ outputs=[canvas_output, elements_state, element_count, status_message]
567
+ )
568
+
569
+ add_img_lib_btn.click(
570
+ fn=add_content_library_item,
571
+ inputs=[elements_state, gr.State("image")],
572
+ outputs=[canvas_output, elements_state, element_count, status_message]
573
+ )
574
+
575
+ # Element editing
576
+ select_btn.click(
577
+ fn=select_element,
578
+ inputs=[elements_state, selected_index],
579
+ outputs=[canvas_output, elements_state, status_message]
580
+ )
581
+
582
+ delete_btn.click(
583
+ fn=delete_selected_element,
584
+ inputs=[elements_state],
585
+ outputs=[canvas_output, elements_state, element_count, status_message]
586
+ )
587
+
588
+ update_text_btn.click(
589
+ fn=update_element_text,
590
+ inputs=[elements_state, selected_index, edit_text_input],
591
+ outputs=[canvas_output, elements_state, status_message]
592
+ )
593
+
594
+ # Custom CSS for better canvas appearance
595
+ demo.load(
596
+ fn=lambda: None,
597
+ inputs=[],
598
+ outputs=[]
599
+ )
600
+
601
+ # Launch with Gradio 6 syntax - theme goes in launch(), NOT in Blocks!
602
+ demo.launch(
603
+ theme=gr.themes.Soft(
604
+ primary_hue="blue",
605
+ secondary_hue="indigo",
606
+ neutral_hue="slate",
607
+ text_size="lg",
608
+ spacing_size="md"
609
+ ),
610
+ footer_links=[
611
+ {"label": "Built with anycoder", "url": "https://huggingface.co/spaces/akhaliq/anycoder"}
612
+ ],
613
+ css="""
614
+ .canvas-container {
615
+ border: 2px solid #e0e0e0;
616
+ border-radius: 8px;
617
+ background: #fafafa;
618
+ }
619
+
620
+ .gradio-container {
621
+ max-width: 1400px !important;
622
+ }
623
+
624
+ #canvas-container {
625
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
626
+ }
627
+
628
+ .canvas-element:hover {
629
+ box-shadow: 0 4px 12px rgba(0,120,215,0.3);
630
+ }
631
+ """
632
+ )
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ gradio>=6.0.2