broadfield-dev commited on
Commit
3819331
·
verified ·
1 Parent(s): d737c52

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +294 -0
app.py ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+
3
+ import gradio as gr
4
+ import time
5
+ import datetime
6
+
7
+ # --- Core Browser Logic (Slightly modified for Gradio) ---
8
+ # Changes:
9
+ # - Removed print() statements. Methods now return status/log messages.
10
+ # - Removed time.sleep() as Gradio handles user feedback.
11
+
12
+ class Tab:
13
+ """Represents a single browser tab with its own history."""
14
+ def __init__(self, homepage):
15
+ self.history = []
16
+ self.current_index = -1
17
+ self.homepage = homepage
18
+ self.navigate(self.homepage)
19
+
20
+ def navigate(self, url):
21
+ """Navigates to a new URL, clearing any 'forward' history."""
22
+ if self.current_index < len(self.history) - 1:
23
+ self.history = self.history[:self.current_index + 1]
24
+ self.history.append(url)
25
+ self.current_index += 1
26
+ return f"Navigating to {url}..."
27
+
28
+ def go_back(self):
29
+ if self.current_index > 0:
30
+ self.current_index -= 1
31
+ return f"Going back to {self.current_url()}"
32
+ return "No more history to go back to."
33
+
34
+ def go_forward(self):
35
+ if self.current_index < len(self.history) - 1:
36
+ self.current_index += 1
37
+ return f"Going forward to {self.current_url()}"
38
+ return "No more history to go forward to."
39
+
40
+ def current_url(self):
41
+ return self.history[self.current_index] if self.current_index != -1 else "about:blank"
42
+
43
+ class PseudoBrowser:
44
+ """Simulates a web browser's core functionality."""
45
+ def __init__(self):
46
+ self.homepage = "https://www.startpage.com"
47
+ self.tabs = [Tab(self.homepage)]
48
+ self.active_tab_index = 0
49
+ self.bookmarks = set()
50
+ self.global_history = []
51
+ self.cache = {}
52
+
53
+ def _get_active_tab(self):
54
+ return self.tabs[self.active_tab_index]
55
+
56
+ def _fetch_content(self, url, refresh=False):
57
+ """Simulates fetching content, using a cache."""
58
+ log = ""
59
+ if url in self.cache and not refresh:
60
+ log += f"⚡️ Loading '{url}' from cache.\n"
61
+ content = self.cache[url]
62
+ else:
63
+ log += f"☁️ Fetching '{url}' from network...\n"
64
+ content = f"<html><body><h1>Welcome to {url}</h1><p><i>Content 'rendered' at {datetime.datetime.now().strftime('%H:%M:%S')}</i></p></body></html>"
65
+ self.cache[url] = content
66
+ return content, log
67
+
68
+ def open_url(self, url):
69
+ tab = self._get_active_tab()
70
+ log = tab.navigate(url)
71
+ self.global_history.append((datetime.datetime.now(), url))
72
+ content, fetch_log = self._fetch_content(url)
73
+ return content, log + "\n" + fetch_log
74
+
75
+ def back(self):
76
+ tab = self._get_active_tab()
77
+ log = tab.go_back()
78
+ content, fetch_log = self._fetch_content(tab.current_url())
79
+ return content, log + "\n" + fetch_log
80
+
81
+ def forward(self):
82
+ tab = self._get_active_tab()
83
+ log = tab.go_forward()
84
+ content, fetch_log = self._fetch_content(tab.current_url())
85
+ return content, log + "\n" + fetch_log
86
+
87
+ def refresh(self):
88
+ tab = self._get_active_tab()
89
+ url = tab.current_url()
90
+ log = f"🔄 Refreshing {url}..."
91
+ content, fetch_log = self._fetch_content(url, refresh=True)
92
+ return content, log + "\n" + fetch_log
93
+
94
+ def new_tab(self):
95
+ new_tab = Tab(self.homepage)
96
+ self.tabs.append(new_tab)
97
+ self.active_tab_index = len(self.tabs) - 1
98
+ content, fetch_log = self._fetch_content(new_tab.current_url())
99
+ return content, f"✨ New tab opened.\n" + fetch_log
100
+
101
+ def close_tab(self):
102
+ if len(self.tabs) == 1:
103
+ return None, "❌ Cannot close the last tab!"
104
+
105
+ closed_tab = self.tabs.pop(self.active_tab_index)
106
+ log = f"💣 Tab ({closed_tab.current_url()}) closed."
107
+ if self.active_tab_index >= len(self.tabs):
108
+ self.active_tab_index = len(self.tabs) - 1
109
+
110
+ active_tab = self._get_active_tab()
111
+ content, fetch_log = self._fetch_content(active_tab.current_url())
112
+ return content, log + "\n" + fetch_log
113
+
114
+ def switch_tab(self, tab_label):
115
+ # The label is "Tab 0: https://..."
116
+ index = int(tab_label.split(":")[0].replace("Tab", "").strip())
117
+ if 0 <= index < len(self.tabs):
118
+ self.active_tab_index = index
119
+ tab = self._get_active_tab()
120
+ content, fetch_log = self._fetch_content(tab.current_url())
121
+ return content, f"Switched to Tab {index}.\n" + fetch_log
122
+ return None, "❌ Invalid tab index."
123
+
124
+ def add_bookmark(self):
125
+ url = self._get_active_tab().current_url()
126
+ if url not in self.bookmarks:
127
+ self.bookmarks.add(url)
128
+ return f"⭐ Bookmarked: {url}"
129
+ return f"ℹ️ Already bookmarked: {url}"
130
+
131
+ # --- Gradio UI and Event Handlers ---
132
+
133
+ def update_ui_components(browser: PseudoBrowser):
134
+ """Generates all UI component values from the browser state."""
135
+ # Main Content
136
+ active_tab = browser._get_active_tab()
137
+ content, _ = browser._fetch_content(active_tab.current_url())
138
+ url_text = active_tab.current_url()
139
+
140
+ # Tab Selector
141
+ tab_choices = [f"Tab {i}: {tab.current_url()}" for i, tab in enumerate(browser.tabs)]
142
+ active_tab_label = f"Tab {browser.active_tab_index}: {active_tab.current_url()}"
143
+
144
+ # History
145
+ history_md = "### 📜 Global History\n"
146
+ if not browser.global_history:
147
+ history_md += "_(empty)_"
148
+ else:
149
+ for ts, url in reversed(browser.global_history[-10:]): # Show last 10
150
+ history_md += f"- `{ts.strftime('%H:%M:%S')}`: {url}\n"
151
+
152
+ # Bookmarks
153
+ bookmarks_md = "### 📚 Bookmarks\n"
154
+ if not browser.bookmarks:
155
+ bookmarks_md += "_(empty)_"
156
+ else:
157
+ for bm in sorted(list(browser.bookmarks)):
158
+ bookmarks_md += f"- ⭐ {bm}\n"
159
+
160
+ return {
161
+ html_view: gr.HTML(value=content),
162
+ url_textbox: gr.Textbox(value=url_text),
163
+ tab_selector: gr.Radio(choices=tab_choices, value=active_tab_label, label="Active Tabs"),
164
+ history_display: gr.Markdown(history_md),
165
+ bookmarks_display: gr.Markdown(bookmarks_md)
166
+ }
167
+
168
+ # --- Event Handlers ---
169
+
170
+ def handle_go(browser_state, url):
171
+ content, log = browser_state.open_url(url)
172
+ return {
173
+ **update_ui_components(browser_state),
174
+ log_display: gr.Textbox(log)
175
+ }
176
+
177
+ def handle_back(browser_state):
178
+ content, log = browser_state.back()
179
+ return {
180
+ **update_ui_components(browser_state),
181
+ log_display: gr.Textbox(log)
182
+ }
183
+
184
+ def handle_forward(browser_state):
185
+ content, log = browser_state.forward()
186
+ return {
187
+ **update_ui_components(browser_state),
188
+ log_display: gr.Textbox(log)
189
+ }
190
+
191
+ def handle_refresh(browser_state):
192
+ content, log = browser_state.refresh()
193
+ return {
194
+ **update_ui_components(browser_state),
195
+ log_display: gr.Textbox(log)
196
+ }
197
+
198
+ def handle_new_tab(browser_state):
199
+ content, log = browser_state.new_tab()
200
+ return {
201
+ **update_ui_components(browser_state),
202
+ log_display: gr.Textbox(log)
203
+ }
204
+
205
+ def handle_close_tab(browser_state):
206
+ content, log = browser_state.close_tab()
207
+ # Update UI components. If content is None, it means the last tab wasn't closed.
208
+ updated_components = update_ui_components(browser_state)
209
+ if content is not None:
210
+ updated_components[html_view] = gr.HTML(value=content)
211
+
212
+ return {
213
+ **updated_components,
214
+ log_display: gr.Textbox(log)
215
+ }
216
+
217
+ def handle_switch_tab(browser_state, tab_label):
218
+ content, log = browser_state.switch_tab(tab_label)
219
+ return {
220
+ **update_ui_components(browser_state),
221
+ log_display: gr.Textbox(log)
222
+ }
223
+
224
+ def handle_add_bookmark(browser_state):
225
+ log = browser_state.add_bookmark()
226
+ return {
227
+ **update_ui_components(browser_state),
228
+ log_display: gr.Textbox(log)
229
+ }
230
+
231
+ # --- Gradio Interface Layout ---
232
+
233
+ with gr.Blocks(theme=gr.themes.Soft(), title="Pseudo Browser") as demo:
234
+ browser_state = gr.State(PseudoBrowser())
235
+
236
+ gr.Markdown("# 🌐 Pseudo Browser Demo")
237
+ gr.Markdown("A simulation of a web browser's state and actions, built with Python and Gradio.")
238
+
239
+ with gr.Row():
240
+ with gr.Column(scale=3):
241
+ # URL and Navigation Controls
242
+ with gr.Row():
243
+ back_btn = gr.Button("◀")
244
+ forward_btn = gr.Button("▶")
245
+ refresh_btn = gr.Button("🔄")
246
+ url_textbox = gr.Textbox(label="URL", placeholder="https://example.com", interactive=True)
247
+ go_btn = gr.Button("Go", variant="primary")
248
+ bookmark_btn = gr.Button("⭐")
249
+
250
+ # Main "rendered" content view
251
+ html_view = gr.HTML(value="<html><body><h1>Welcome!</h1></body></html>")
252
+
253
+ # Log display
254
+ log_display = gr.Textbox(label="Log", interactive=False)
255
+
256
+ with gr.Column(scale=1):
257
+ # Tab Management
258
+ with gr.Row():
259
+ new_tab_btn = gr.Button("➕ New Tab")
260
+ close_tab_btn = gr.Button("❌ Close Active Tab")
261
+ tab_selector = gr.Radio(choices=[], label="Active Tabs", interactive=True)
262
+
263
+ # Accordion for History and Bookmarks
264
+ with gr.Accordion("History & Bookmarks", open=True):
265
+ history_display = gr.Markdown("...")
266
+ bookmarks_display = gr.Markdown("...")
267
+
268
+ # --- Component Wiring ---
269
+
270
+ # Define all outputs that can be updated
271
+ outputs = [html_view, url_textbox, tab_selector, history_display, bookmarks_display, log_display]
272
+
273
+ # Initial load
274
+ demo.load(
275
+ fn=lambda state: {**update_ui_components(state), log_display: "🚀 Browser Initialized!"},
276
+ inputs=[browser_state],
277
+ outputs=outputs
278
+ )
279
+
280
+ # Wire up buttons
281
+ go_btn.click(handle_go, [browser_state, url_textbox], outputs)
282
+ url_textbox.submit(handle_go, [browser_state, url_textbox], outputs)
283
+ back_btn.click(handle_back, [browser_state], outputs)
284
+ forward_btn.click(handle_forward, [browser_state], outputs)
285
+ refresh_btn.click(handle_refresh, [browser_state], outputs)
286
+
287
+ new_tab_btn.click(handle_new_tab, [browser_state], outputs)
288
+ close_tab_btn.click(handle_close_tab, [browser_state], outputs)
289
+ tab_selector.input(handle_switch_tab, [browser_state, tab_selector], outputs)
290
+
291
+ bookmark_btn.click(handle_add_bookmark, [browser_state], outputs)
292
+
293
+
294
+ demo.launch()