shethjenil commited on
Commit
2670f9f
Β·
verified Β·
1 Parent(s): d54ae29

Delete ui.py

Browse files
Files changed (1) hide show
  1. ui.py +0 -722
ui.py DELETED
@@ -1,722 +0,0 @@
1
- import threading
2
- import queue
3
- from collections import deque
4
- from dataclasses import dataclass, field
5
- from typing import Dict, Optional, Tuple, Any
6
-
7
- from playwright.sync_api import sync_playwright, Page, BrowserContext
8
- from playwright_stealth import Stealth
9
- from PIL import Image
10
- from io import BytesIO
11
-
12
- import gradio as gr
13
-
14
- # ─────────────────────────────
15
- # Global Queues (worker <-> UI)
16
- # ─────────────────────────────
17
- task_queue: "queue.Queue[dict]" = queue.Queue()
18
- result_queue: "queue.Queue[Tuple[str, Optional[Image.Image]]]" = queue.Queue()
19
-
20
- # ─────────────────────────────
21
- # Browser State + Lock
22
- # ─────────────────────────────
23
-
24
- @dataclass
25
- class BrowserState:
26
- running: bool = False
27
- thread: Optional[threading.Thread] = None
28
- pages: Dict[str, Page] = field(default_factory=dict) # tab_name -> Page
29
- active_page: Optional[str] = None
30
- network_logs: deque = field(default_factory=lambda: deque(maxlen=500))
31
- console_logs: deque = field(default_factory=lambda: deque(maxlen=500))
32
- recording: bool = False
33
- macro: list = field(default_factory=list)
34
- tab_counter: int = 0 # to generate stable names
35
-
36
-
37
- BROWSER_STATE = BrowserState()
38
- BROWSER_LOCK = threading.Lock()
39
-
40
-
41
- # ─────────────────────────────
42
- # Helper: Safe screenshot
43
- # ─────────────────────────────
44
-
45
- def take_screenshot(page: Optional[Page]) -> Optional[Image.Image]:
46
- if page is None:
47
- return None
48
- try:
49
- img_bytes = page.screenshot()
50
- return Image.open(BytesIO(img_bytes))
51
- except Exception:
52
- return None
53
-
54
-
55
- # ─────────────────────────────
56
- # Worker Class (Playwright)
57
- # ─────────────────────────────
58
-
59
- class PlaywrightWorker:
60
- def __init__(self, state: BrowserState):
61
- self.state = state
62
- self.context: Optional[BrowserContext] = None
63
-
64
- # ---- Console & Network hooks ----
65
- def _attach_console_listener(self, page: Page):
66
- def on_console(msg):
67
- with BROWSER_LOCK:
68
- self.state.console_logs.append(f"[{msg.type}] {msg.text}")
69
- page.on("console", on_console)
70
-
71
- def _attach_network_listeners(self, context: BrowserContext):
72
- def on_request(request):
73
- with BROWSER_LOCK:
74
- self.state.network_logs.append(f"[REQUEST] {request.method} {request.url}")
75
-
76
- def on_response(response):
77
- with BROWSER_LOCK:
78
- self.state.network_logs.append(f"[RESPONSE] {response.status} {response.url}")
79
-
80
- context.on("request", on_request)
81
- context.on("response", on_response)
82
-
83
- # ---- Tabs Management helpers ----
84
- def _create_new_tab(self) -> Tuple[str, Page]:
85
- page = self.context.new_page()
86
- with BROWSER_LOCK:
87
- self.state.tab_counter += 1
88
- tab_name = f"Tab-{self.state.tab_counter}"
89
- self.state.pages[tab_name] = page
90
- self.state.active_page = tab_name
91
- self._attach_console_listener(page)
92
- return tab_name, page
93
-
94
- def _get_active_page(self) -> Optional[Page]:
95
- with BROWSER_LOCK:
96
- name = self.state.active_page
97
- page = self.state.pages.get(name) if name else None
98
- return page
99
-
100
- # ---- Main loop ----
101
- def run(self):
102
- with Stealth().use_sync(sync_playwright()) as p:
103
- browser = p.chromium.launch(headless=True, args=["--no-sandbox"])
104
- self.context = browser.new_context()
105
- self._attach_network_listeners(self.context)
106
-
107
- # default first tab
108
- tab_name, first_page = self._create_new_tab()
109
-
110
- # Command dispatcher
111
- handlers = {
112
- "eval": self.handle_eval,
113
- "goto": self.handle_goto,
114
- "click": self.handle_click,
115
- "click_xy": self.handle_click_xy,
116
- "type": self.handle_type,
117
- "new_tab": self.handle_new_tab,
118
- "close_tab": self.handle_close_tab,
119
- "switch_tab": self.handle_switch_tab,
120
- "inspect": self.handle_inspect,
121
- "get_network_logs": self.handle_get_network_logs,
122
- "get_console_logs": self.handle_get_console_logs,
123
- "clear_logs": self.handle_clear_logs,
124
- "start_record": self.handle_start_record,
125
- "stop_record": self.handle_stop_record,
126
- "play_macro": self.handle_play_macro,
127
- }
128
-
129
- while True:
130
- task = task_queue.get()
131
- cmd = task.get("cmd")
132
-
133
- if cmd == "__EXIT__":
134
- break
135
-
136
- # default outputs
137
- result_text = ""
138
- screenshot = None
139
-
140
- try:
141
- # decide active page per command
142
- page = self._get_active_page()
143
-
144
- # macro recording (only for top-level calls, not from_macro)
145
- recordable_cmds = {
146
- "goto", "click", "type", "new_tab", "close_tab", "switch_tab", "click_xy"
147
- }
148
- if (
149
- not task.get("from_macro", False)
150
- and cmd in recordable_cmds
151
- ):
152
- with BROWSER_LOCK:
153
- if self.state.recording:
154
- # store a shallow copy without from_macro
155
- rec = {k: v for k, v in task.items() if k != "from_macro"}
156
- self.state.macro.append(rec)
157
-
158
- handler = handlers.get(cmd, None)
159
- if handler is None:
160
- result_text = f"Unknown command: {cmd}"
161
- screenshot = take_screenshot(page)
162
- else:
163
- result_text, screenshot = handler(task, page)
164
-
165
- except Exception as e:
166
- result_text = f"Error: {type(e).__name__}: {e}"
167
- # screenshot remains None on error
168
-
169
- result_queue.put((result_text, screenshot))
170
-
171
- # graceful shutdown
172
- try:
173
- browser.close()
174
- except Exception:
175
- pass
176
-
177
-
178
- # ─────────────────────────────
179
- # Command Handlers
180
- # ─────────────────────────────
181
-
182
- def handle_eval(self, task: dict, page: Optional[Page]):
183
- """
184
- Evaluate python code in a restricted environment.
185
- ⚠ Still not perfectly 'secure', only for trusted usage.
186
- """
187
- if page is None:
188
- return "No active page.", None
189
-
190
- code = task.get("code", "")
191
- safe_globals = {"__builtins__": {}} # no builtins
192
- safe_locals = {}
193
-
194
- try:
195
- result = eval(code, safe_globals, safe_locals)
196
- except Exception as e:
197
- return f"Eval error: {type(e).__name__}: {e}", take_screenshot(page)
198
-
199
- text = f"Eval result: {result!r}"
200
- return text, take_screenshot(page)
201
-
202
- def handle_goto(self, task: dict, page: Optional[Page]):
203
- if page is None:
204
- return "No active page.", None
205
- url = task.get("url", "")
206
- try:
207
- page.goto(url)
208
- return f"Navigated to {url}", take_screenshot(page)
209
- except Exception as e:
210
- return f"Goto error: {e}", take_screenshot(page)
211
-
212
- def handle_click(event: gr.SelectData, click_type, last_screenshot):
213
- x, y = event.index
214
- img_w, img_h = last_screenshot.size
215
-
216
- task_queue.put({
217
- "cmd": "click_xy",
218
- "x": x,
219
- "y": y,
220
- "img_w": img_w,
221
- "img_h": img_h,
222
- "click_type": click_type
223
- })
224
- return result_queue.get()
225
-
226
- def handle_click_xy(self, task: dict, page: Optional[Page]):
227
- if page is None:
228
- return "No active page.", None
229
-
230
- x = task.get("x")
231
- y = task.get("y")
232
- img_w = task.get("img_w")
233
- img_h = task.get("img_h")
234
- click_type = task.get("click_type", "left")
235
-
236
- # viewport size
237
- vp = page.viewport_size or {"width": img_w, "height": img_h}
238
-
239
- real_x = x * (vp["width"] / img_w)
240
- real_y = y * (vp["height"] / img_h)
241
-
242
- if click_type == "left":
243
- page.mouse.click(real_x, real_y)
244
- elif click_type == "double":
245
- page.mouse.dblclick(real_x, real_y)
246
- elif click_type == "right":
247
- page.mouse.click(real_x, real_y, button="right")
248
- elif click_type == "hover":
249
- page.mouse.move(real_x, real_y)
250
-
251
- return f"{click_type} click at {real_x},{real_y}", take_screenshot(page)
252
-
253
- def handle_type(self, task: dict, page: Optional[Page]):
254
- if page is None:
255
- return "No active page.", None
256
- selector = task.get("selector", "")
257
- text = task.get("text", "")
258
- try:
259
- page.fill(selector, text)
260
- return f"Typed into {selector}: {text}", take_screenshot(page)
261
- except Exception as e:
262
- return f"Type error: {e}", take_screenshot(page)
263
-
264
- def handle_new_tab(self, task: dict, page: Optional[Page]):
265
- tab_name, new_page = self._create_new_tab()
266
- return f"Opened new tab: {tab_name}", take_screenshot(new_page)
267
-
268
- def handle_close_tab(self, task: dict, page: Optional[Page]):
269
- name = task.get("tab", "")
270
- with BROWSER_LOCK:
271
- if name in self.state.pages:
272
- try:
273
- self.state.pages[name].close()
274
- except Exception:
275
- pass
276
- del self.state.pages[name]
277
- msg = f"Closed {name}"
278
- # re-select active tab
279
- if self.state.active_page == name:
280
- if self.state.pages:
281
- self.state.active_page = list(self.state.pages.keys())[0]
282
- else:
283
- self.state.active_page = None
284
- else:
285
- msg = f"Tab {name} not found."
286
-
287
- active_name = self.state.active_page
288
- active_page = self.state.pages.get(active_name) if active_name else None
289
-
290
- return msg, take_screenshot(active_page)
291
-
292
- def handle_switch_tab(self, task: dict, page: Optional[Page]):
293
- name = task.get("tab", "")
294
- with BROWSER_LOCK:
295
- if name in self.state.pages:
296
- self.state.active_page = name
297
- active_page = self.state.pages[name]
298
- msg = f"Switched to {name}"
299
- else:
300
- active_page = self._get_active_page()
301
- msg = f"Tab {name} not found."
302
-
303
- return msg, take_screenshot(active_page)
304
-
305
- def handle_inspect(self, task: dict, page: Optional[Page]):
306
- if page is None:
307
- return "No active page.", None
308
- selector = task.get("selector", "")
309
- try:
310
- el = page.query_selector(selector)
311
- if not el:
312
- return f"No element found for selector: {selector}", take_screenshot(page)
313
-
314
- inner_text = el.inner_text()
315
- inner_html = el.inner_html()
316
- attrs = page.evaluate(
317
- """(el) => {
318
- const out = {};
319
- for (const a of el.attributes) out[a.name] = a.value;
320
- return out;
321
- }""",
322
- el
323
- )
324
- xpath = page.evaluate(
325
- """(el) => {
326
- function getXPath(node) {
327
- if (node.id)
328
- return 'id(\"' + node.id + '\")';
329
- if (node === document.body)
330
- return '/html/body';
331
- let ix = 0;
332
- const siblings = node.parentNode ? node.parentNode.childNodes : [];
333
- for (let i=0; i<siblings.length; i++) {
334
- const sibling = siblings[i];
335
- if (sibling === node)
336
- return getXPath(node.parentNode) + '/' + node.tagName.toLowerCase() + '[' + (ix+1) + ']';
337
- if (sibling.nodeType === 1 && sibling.tagName === node.tagName)
338
- ix++;
339
- }
340
- }
341
- return getXPath(el);
342
- }""",
343
- el
344
- )
345
-
346
- info = (
347
- f"Selector: {selector}\n"
348
- f"XPath: {xpath}\n\n"
349
- f"Inner Text:\n{inner_text}\n\n"
350
- f"Attributes:\n{attrs}\n\n"
351
- f"Inner HTML (truncated):\n{inner_html[:1000]}"
352
- )
353
- return info, take_screenshot(page)
354
- except Exception as e:
355
- return f"Inspect error: {e}", take_screenshot(page)
356
-
357
- def handle_get_network_logs(self, task: dict, page: Optional[Page]):
358
- with BROWSER_LOCK:
359
- logs = list(self.state.network_logs)[-100:]
360
- text = "=== Network Logs (last 100) ===\n" + "\n".join(logs)
361
- return text, take_screenshot(page)
362
-
363
- def handle_get_console_logs(self, task: dict, page: Optional[Page]):
364
- with BROWSER_LOCK:
365
- logs = list(self.state.console_logs)[-100:]
366
- text = "=== Console Logs (last 100) ===\n" + "\n".join(logs)
367
- return text, take_screenshot(page)
368
-
369
- def handle_clear_logs(self, task: dict, page: Optional[Page]):
370
- with BROWSER_LOCK:
371
- self.state.network_logs.clear()
372
- self.state.console_logs.clear()
373
- return "Network & console logs cleared.", take_screenshot(page)
374
-
375
- def handle_start_record(self, task: dict, page: Optional[Page]):
376
- with BROWSER_LOCK:
377
- self.state.recording = True
378
- self.state.macro = []
379
- return "Macro recording started.", take_screenshot(page)
380
-
381
- def handle_stop_record(self, task: dict, page: Optional[Page]):
382
- with BROWSER_LOCK:
383
- self.state.recording = False
384
- steps = len(self.state.macro)
385
- return f"Macro recording stopped. {steps} steps recorded.", take_screenshot(page)
386
-
387
- def handle_play_macro(self, task: dict, page: Optional[Page]):
388
- with BROWSER_LOCK:
389
- macro_steps = list(self.state.macro)
390
-
391
- if not macro_steps:
392
- return "Macro is empty.", take_screenshot(self._get_active_page())
393
-
394
- last_result = ""
395
- current_page = self._get_active_page()
396
-
397
- for step in macro_steps:
398
- step_cmd = dict(step)
399
- step_cmd["from_macro"] = True
400
- cmd = step_cmd.get("cmd")
401
-
402
- # use same handlers (no re-queue, direct call)
403
- if cmd == "goto":
404
- current_page = self._get_active_page()
405
- last_result, _ = self.handle_goto(step_cmd, current_page)
406
- elif cmd == "click":
407
- current_page = self._get_active_page()
408
- last_result, _ = self.handle_click(step_cmd, current_page)
409
- elif cmd == "type":
410
- current_page = self._get_active_page()
411
- last_result, _ = self.handle_type(step_cmd, current_page)
412
- elif cmd == "new_tab":
413
- last_result, _ = self.handle_new_tab(step_cmd, current_page)
414
- current_page = self._get_active_page()
415
- elif cmd == "close_tab":
416
- last_result, _ = self.handle_close_tab(step_cmd, current_page)
417
- current_page = self._get_active_page()
418
- elif cmd == "switch_tab":
419
- last_result, _ = self.handle_switch_tab(step_cmd, current_page)
420
- current_page = self._get_active_page()
421
- elif cmd == "click_xy":
422
- current_page = self._get_active_page()
423
- last_result, _ = self.handle_click_xy(step_cmd, current_page)
424
-
425
- final_page = self._get_active_page()
426
- return f"Macro executed. {len(macro_steps)} steps.\nLast step: {last_result}", take_screenshot(final_page)
427
-
428
-
429
- # ─────────────────────────────
430
- # Worker spawn / shutdown helpers
431
- # ─────────────────────────────
432
-
433
- def start_worker_thread():
434
- worker = PlaywrightWorker(BROWSER_STATE)
435
- t = threading.Thread(target=worker.run, daemon=True)
436
- t.start()
437
- with BROWSER_LOCK:
438
- BROWSER_STATE.thread = t
439
- BROWSER_STATE.running = True
440
- # reset state
441
- BROWSER_STATE.pages.clear()
442
- BROWSER_STATE.active_page = None
443
- BROWSER_STATE.network_logs.clear()
444
- BROWSER_STATE.console_logs.clear()
445
- BROWSER_STATE.macro.clear()
446
- BROWSER_STATE.recording = False
447
- BROWSER_STATE.tab_counter = 0
448
-
449
-
450
- def stop_worker_thread():
451
- with BROWSER_LOCK:
452
- if not BROWSER_STATE.running:
453
- return
454
- BROWSER_STATE.running = False
455
- t = BROWSER_STATE.thread
456
-
457
- task_queue.put({"cmd": "__EXIT__"})
458
- if t is not None:
459
- t.join(timeout=5)
460
-
461
-
462
- # ─────────────────────────────
463
- # Frontend functions (Gradio)
464
- # ─────────────────────────────
465
-
466
- def start_browser():
467
- with BROWSER_LOCK:
468
- running = BROWSER_STATE.running
469
-
470
- if running:
471
- with BROWSER_LOCK:
472
- tabs = list(BROWSER_STATE.pages.keys())
473
- active = BROWSER_STATE.active_page
474
- return "Browser is already running.", None, gr.update(choices=tabs, value=active)
475
-
476
- start_worker_thread()
477
- # first tab name predictable is Tab-1; but worker will create it on start
478
- return "Browser Started!", None, gr.update(choices=["Tab-1"], value="Tab-1")
479
-
480
-
481
- def stop_browser():
482
- with BROWSER_LOCK:
483
- running = BROWSER_STATE.running
484
-
485
- if not running:
486
- return "Browser is not running.", None, gr.update(choices=[], value=None)
487
-
488
- stop_worker_thread()
489
- with BROWSER_LOCK:
490
- BROWSER_STATE.pages.clear()
491
- BROWSER_STATE.active_page = None
492
-
493
- return "Browser Closed!", None, gr.update(choices=[], value=None)
494
-
495
-
496
- def execute_code(code):
497
- with BROWSER_LOCK:
498
- if not BROWSER_STATE.running:
499
- return "Start browser first.", None
500
- task_queue.put({"cmd": "eval", "code": code})
501
- return result_queue.get()
502
-
503
-
504
- def navigate(url):
505
- with BROWSER_LOCK:
506
- if not BROWSER_STATE.running:
507
- return "Start browser first.", None
508
- task_queue.put({"cmd": "goto", "url": url})
509
- return result_queue.get()
510
-
511
-
512
- def click(selector):
513
- with BROWSER_LOCK:
514
- if not BROWSER_STATE.running:
515
- return "Start browser first.", None
516
- task_queue.put({"cmd": "click", "selector": selector})
517
- return result_queue.get()
518
-
519
-
520
- def type_text(selector, text):
521
- with BROWSER_LOCK:
522
- if not BROWSER_STATE.running:
523
- return "Start browser first.", None
524
- task_queue.put({"cmd": "type", "selector": selector, "text": text})
525
- return result_queue.get()
526
-
527
-
528
- def new_tab():
529
- with BROWSER_LOCK:
530
- if not BROWSER_STATE.running:
531
- return "Start browser first.", None
532
- task_queue.put({"cmd": "new_tab"})
533
- r, screenshot = result_queue.get()
534
- with BROWSER_LOCK:
535
- tabs = list(BROWSER_STATE.pages.keys())
536
- active = BROWSER_STATE.active_page
537
- return r, screenshot, gr.update(choices=tabs, value=active)
538
-
539
-
540
- def close_tab(tab):
541
- with BROWSER_LOCK:
542
- if not BROWSER_STATE.running:
543
- return "Start browser first.", None
544
- task_queue.put({"cmd": "close_tab", "tab": tab})
545
- r, screenshot = result_queue.get()
546
- with BROWSER_LOCK:
547
- tabs = list(BROWSER_STATE.pages.keys())
548
- active = BROWSER_STATE.active_page
549
- return r, screenshot, gr.update(choices=tabs, value=active)
550
-
551
-
552
- def switch_tab(tab):
553
- with BROWSER_LOCK:
554
- if not BROWSER_STATE.running:
555
- return "Start browser first.", None
556
- task_queue.put({"cmd": "switch_tab", "tab": tab})
557
- r, screenshot = result_queue.get()
558
- with BROWSER_LOCK:
559
- tabs = list(BROWSER_STATE.pages.keys())
560
- active = BROWSER_STATE.active_page
561
- return r, screenshot, gr.update(choices=tabs, value=active)
562
-
563
-
564
- def inspect_element(selector):
565
- with BROWSER_LOCK:
566
- if not BROWSER_STATE.running:
567
- return "Start browser first.", None
568
- task_queue.put({"cmd": "inspect", "selector": selector})
569
- return result_queue.get()
570
-
571
-
572
- def handle_click(event: gr.SelectData, click_type):
573
- x, y = event.index
574
- with BROWSER_LOCK:
575
- if not BROWSER_STATE.running:
576
- return "Start browser first.", None
577
-
578
- task_queue.put({
579
- "cmd": "click_xy",
580
- "x": x,
581
- "y": y,
582
- "click_type": click_type
583
- })
584
- return result_queue.get()
585
-
586
-
587
- def show_network_logs():
588
- with BROWSER_LOCK:
589
- if not BROWSER_STATE.running:
590
- return "Start browser first.", None
591
- task_queue.put({"cmd": "get_network_logs"})
592
- return result_queue.get()
593
-
594
-
595
- def show_console_logs():
596
- with BROWSER_LOCK:
597
- if not BROWSER_STATE.running:
598
- return "Start browser first.", None
599
- task_queue.put({"cmd": "get_console_logs"})
600
- return result_queue.get()
601
-
602
-
603
- def clear_logs():
604
- with BROWSER_LOCK:
605
- if not BROWSER_STATE.running:
606
- return "Start browser first.", None
607
- task_queue.put({"cmd": "clear_logs"})
608
- return result_queue.get()
609
-
610
-
611
- def start_recording():
612
- with BROWSER_LOCK:
613
- if not BROWSER_STATE.running:
614
- return "Start browser first.", None
615
- task_queue.put({"cmd": "start_record"})
616
- return result_queue.get()
617
-
618
-
619
- def stop_recording():
620
- with BROWSER_LOCK:
621
- if not BROWSER_STATE.running:
622
- return "Start browser first.", None
623
- task_queue.put({"cmd": "stop_record"})
624
- return result_queue.get()
625
-
626
-
627
- def play_macro():
628
- with BROWSER_LOCK:
629
- if not BROWSER_STATE.running:
630
- return "Start browser first.", None
631
- task_queue.put({"cmd": "play_macro"})
632
- return result_queue.get()
633
-
634
-
635
- # ─────────────────────────────
636
- # Gradio UI (slightly cleaner layout)
637
- # ─────────────────────────────
638
-
639
- with gr.Blocks() as app:
640
- gr.Markdown("## πŸ”₯ Advanced Playwright Control Panel (DOM + Logs + Macro RPA)")
641
-
642
- with gr.Row():
643
- start_btn = gr.Button("Open Browser")
644
- stop_btn = gr.Button("Close Browser")
645
-
646
- tabs_dropdown = gr.Dropdown(label="Tabs", choices=[], value=None)
647
-
648
- with gr.Tab("Browse"):
649
- with gr.Row():
650
- url_box = gr.Textbox(label="URL", scale=4)
651
- nav_btn = gr.Button("Go", scale=1)
652
-
653
- with gr.Row():
654
- sel_box = gr.Textbox(label="Selector (CSS)", scale=3)
655
- click_btn = gr.Button("Click", scale=1)
656
- type_box = gr.Textbox(label="Type Text", scale=3)
657
- type_btn = gr.Button("Type", scale=1)
658
-
659
- with gr.Tab("Inspect / Code"):
660
- with gr.Row():
661
- inspect_sel = gr.Textbox(label="Inspect Selector (CSS)")
662
- inspect_btn = gr.Button("Inspect + Generate XPath")
663
- code_input = gr.TextArea(label="Python Code (eval in Playwright worker - restricted)")
664
- run_btn = gr.Button("Run Code")
665
-
666
- with gr.Tab("Logs"):
667
- with gr.Row():
668
- net_btn = gr.Button("Show Network Logs")
669
- cons_btn = gr.Button("Show Console Logs")
670
- clear_logs_btn = gr.Button("Clear Logs")
671
-
672
- with gr.Tab("Tabs & Macros"):
673
- with gr.Row():
674
- new_tab_btn = gr.Button("New Tab")
675
- close_tab_btn = gr.Button("Close Selected Tab")
676
- switch_tab_btn = gr.Button("Switch Tab")
677
-
678
- with gr.Row():
679
- start_rec_btn = gr.Button("Start Macro Recording")
680
- stop_rec_btn = gr.Button("Stop Recording")
681
- play_macro_btn = gr.Button("Play Recorded Macro")
682
-
683
- output_text = gr.TextArea(label="Output")
684
- output_image = gr.Image(label="Screenshot")
685
- click_type = gr.Radio(
686
- ["left", "double", "right", "hover"],
687
- value="left",
688
- label="Click Type"
689
- )
690
-
691
- output_image.select(
692
- handle_click,
693
- inputs=[click_type, output_image],
694
- outputs=[output_text, output_image]
695
- )
696
-
697
- # Bindings
698
- start_btn.click(start_browser, outputs=[output_text, output_image, tabs_dropdown])
699
- stop_btn.click(stop_browser, outputs=[output_text, output_image, tabs_dropdown])
700
-
701
- new_tab_btn.click(new_tab, outputs=[output_text, output_image, tabs_dropdown])
702
- close_tab_btn.click(close_tab, inputs=tabs_dropdown, outputs=[output_text, output_image, tabs_dropdown])
703
- switch_tab_btn.click(switch_tab, inputs=tabs_dropdown, outputs=[output_text, output_image, tabs_dropdown])
704
-
705
- nav_btn.click(navigate, inputs=url_box, outputs=[output_text, output_image])
706
- click_btn.click(click, inputs=sel_box, outputs=[output_text, output_image])
707
- type_btn.click(type_text, inputs=[sel_box, type_box], outputs=[output_text, output_image])
708
-
709
- run_btn.click(execute_code, inputs=code_input, outputs=[output_text, output_image])
710
- inspect_btn.click(inspect_element, inputs=inspect_sel, outputs=[output_text, output_image])
711
-
712
- net_btn.click(show_network_logs, outputs=[output_text, output_image])
713
- cons_btn.click(show_console_logs, outputs=[output_text, output_image])
714
- clear_logs_btn.click(clear_logs, outputs=[output_text, output_image])
715
-
716
- start_rec_btn.click(start_recording, outputs=[output_text, output_image])
717
- stop_rec_btn.click(stop_recording, outputs=[output_text, output_image])
718
- play_macro_btn.click(play_macro, outputs=[output_text, output_image])
719
-
720
-
721
- if __name__ == "__main__":
722
- app.launch()