browser-gradio / app.py
broadfield-dev's picture
Create app.py
3819331 verified
raw
history blame
10.5 kB
# app.py
import gradio as gr
import time
import datetime
# --- Core Browser Logic (Slightly modified for Gradio) ---
# Changes:
# - Removed print() statements. Methods now return status/log messages.
# - Removed time.sleep() as Gradio handles user feedback.
class Tab:
"""Represents a single browser tab with its own history."""
def __init__(self, homepage):
self.history = []
self.current_index = -1
self.homepage = homepage
self.navigate(self.homepage)
def navigate(self, url):
"""Navigates to a new URL, clearing any 'forward' history."""
if self.current_index < len(self.history) - 1:
self.history = self.history[:self.current_index + 1]
self.history.append(url)
self.current_index += 1
return f"Navigating to {url}..."
def go_back(self):
if self.current_index > 0:
self.current_index -= 1
return f"Going back to {self.current_url()}"
return "No more history to go back to."
def go_forward(self):
if self.current_index < len(self.history) - 1:
self.current_index += 1
return f"Going forward to {self.current_url()}"
return "No more history to go forward to."
def current_url(self):
return self.history[self.current_index] if self.current_index != -1 else "about:blank"
class PseudoBrowser:
"""Simulates a web browser's core functionality."""
def __init__(self):
self.homepage = "https://www.startpage.com"
self.tabs = [Tab(self.homepage)]
self.active_tab_index = 0
self.bookmarks = set()
self.global_history = []
self.cache = {}
def _get_active_tab(self):
return self.tabs[self.active_tab_index]
def _fetch_content(self, url, refresh=False):
"""Simulates fetching content, using a cache."""
log = ""
if url in self.cache and not refresh:
log += f"⚡️ Loading '{url}' from cache.\n"
content = self.cache[url]
else:
log += f"☁️ Fetching '{url}' from network...\n"
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>"
self.cache[url] = content
return content, log
def open_url(self, url):
tab = self._get_active_tab()
log = tab.navigate(url)
self.global_history.append((datetime.datetime.now(), url))
content, fetch_log = self._fetch_content(url)
return content, log + "\n" + fetch_log
def back(self):
tab = self._get_active_tab()
log = tab.go_back()
content, fetch_log = self._fetch_content(tab.current_url())
return content, log + "\n" + fetch_log
def forward(self):
tab = self._get_active_tab()
log = tab.go_forward()
content, fetch_log = self._fetch_content(tab.current_url())
return content, log + "\n" + fetch_log
def refresh(self):
tab = self._get_active_tab()
url = tab.current_url()
log = f"🔄 Refreshing {url}..."
content, fetch_log = self._fetch_content(url, refresh=True)
return content, log + "\n" + fetch_log
def new_tab(self):
new_tab = Tab(self.homepage)
self.tabs.append(new_tab)
self.active_tab_index = len(self.tabs) - 1
content, fetch_log = self._fetch_content(new_tab.current_url())
return content, f"✨ New tab opened.\n" + fetch_log
def close_tab(self):
if len(self.tabs) == 1:
return None, "❌ Cannot close the last tab!"
closed_tab = self.tabs.pop(self.active_tab_index)
log = f"💣 Tab ({closed_tab.current_url()}) closed."
if self.active_tab_index >= len(self.tabs):
self.active_tab_index = len(self.tabs) - 1
active_tab = self._get_active_tab()
content, fetch_log = self._fetch_content(active_tab.current_url())
return content, log + "\n" + fetch_log
def switch_tab(self, tab_label):
# The label is "Tab 0: https://..."
index = int(tab_label.split(":")[0].replace("Tab", "").strip())
if 0 <= index < len(self.tabs):
self.active_tab_index = index
tab = self._get_active_tab()
content, fetch_log = self._fetch_content(tab.current_url())
return content, f"Switched to Tab {index}.\n" + fetch_log
return None, "❌ Invalid tab index."
def add_bookmark(self):
url = self._get_active_tab().current_url()
if url not in self.bookmarks:
self.bookmarks.add(url)
return f"⭐ Bookmarked: {url}"
return f"ℹ️ Already bookmarked: {url}"
# --- Gradio UI and Event Handlers ---
def update_ui_components(browser: PseudoBrowser):
"""Generates all UI component values from the browser state."""
# Main Content
active_tab = browser._get_active_tab()
content, _ = browser._fetch_content(active_tab.current_url())
url_text = active_tab.current_url()
# Tab Selector
tab_choices = [f"Tab {i}: {tab.current_url()}" for i, tab in enumerate(browser.tabs)]
active_tab_label = f"Tab {browser.active_tab_index}: {active_tab.current_url()}"
# History
history_md = "### 📜 Global History\n"
if not browser.global_history:
history_md += "_(empty)_"
else:
for ts, url in reversed(browser.global_history[-10:]): # Show last 10
history_md += f"- `{ts.strftime('%H:%M:%S')}`: {url}\n"
# Bookmarks
bookmarks_md = "### 📚 Bookmarks\n"
if not browser.bookmarks:
bookmarks_md += "_(empty)_"
else:
for bm in sorted(list(browser.bookmarks)):
bookmarks_md += f"- ⭐ {bm}\n"
return {
html_view: gr.HTML(value=content),
url_textbox: gr.Textbox(value=url_text),
tab_selector: gr.Radio(choices=tab_choices, value=active_tab_label, label="Active Tabs"),
history_display: gr.Markdown(history_md),
bookmarks_display: gr.Markdown(bookmarks_md)
}
# --- Event Handlers ---
def handle_go(browser_state, url):
content, log = browser_state.open_url(url)
return {
**update_ui_components(browser_state),
log_display: gr.Textbox(log)
}
def handle_back(browser_state):
content, log = browser_state.back()
return {
**update_ui_components(browser_state),
log_display: gr.Textbox(log)
}
def handle_forward(browser_state):
content, log = browser_state.forward()
return {
**update_ui_components(browser_state),
log_display: gr.Textbox(log)
}
def handle_refresh(browser_state):
content, log = browser_state.refresh()
return {
**update_ui_components(browser_state),
log_display: gr.Textbox(log)
}
def handle_new_tab(browser_state):
content, log = browser_state.new_tab()
return {
**update_ui_components(browser_state),
log_display: gr.Textbox(log)
}
def handle_close_tab(browser_state):
content, log = browser_state.close_tab()
# Update UI components. If content is None, it means the last tab wasn't closed.
updated_components = update_ui_components(browser_state)
if content is not None:
updated_components[html_view] = gr.HTML(value=content)
return {
**updated_components,
log_display: gr.Textbox(log)
}
def handle_switch_tab(browser_state, tab_label):
content, log = browser_state.switch_tab(tab_label)
return {
**update_ui_components(browser_state),
log_display: gr.Textbox(log)
}
def handle_add_bookmark(browser_state):
log = browser_state.add_bookmark()
return {
**update_ui_components(browser_state),
log_display: gr.Textbox(log)
}
# --- Gradio Interface Layout ---
with gr.Blocks(theme=gr.themes.Soft(), title="Pseudo Browser") as demo:
browser_state = gr.State(PseudoBrowser())
gr.Markdown("# 🌐 Pseudo Browser Demo")
gr.Markdown("A simulation of a web browser's state and actions, built with Python and Gradio.")
with gr.Row():
with gr.Column(scale=3):
# URL and Navigation Controls
with gr.Row():
back_btn = gr.Button("◀")
forward_btn = gr.Button("▶")
refresh_btn = gr.Button("🔄")
url_textbox = gr.Textbox(label="URL", placeholder="https://example.com", interactive=True)
go_btn = gr.Button("Go", variant="primary")
bookmark_btn = gr.Button("⭐")
# Main "rendered" content view
html_view = gr.HTML(value="<html><body><h1>Welcome!</h1></body></html>")
# Log display
log_display = gr.Textbox(label="Log", interactive=False)
with gr.Column(scale=1):
# Tab Management
with gr.Row():
new_tab_btn = gr.Button("➕ New Tab")
close_tab_btn = gr.Button("❌ Close Active Tab")
tab_selector = gr.Radio(choices=[], label="Active Tabs", interactive=True)
# Accordion for History and Bookmarks
with gr.Accordion("History & Bookmarks", open=True):
history_display = gr.Markdown("...")
bookmarks_display = gr.Markdown("...")
# --- Component Wiring ---
# Define all outputs that can be updated
outputs = [html_view, url_textbox, tab_selector, history_display, bookmarks_display, log_display]
# Initial load
demo.load(
fn=lambda state: {**update_ui_components(state), log_display: "🚀 Browser Initialized!"},
inputs=[browser_state],
outputs=outputs
)
# Wire up buttons
go_btn.click(handle_go, [browser_state, url_textbox], outputs)
url_textbox.submit(handle_go, [browser_state, url_textbox], outputs)
back_btn.click(handle_back, [browser_state], outputs)
forward_btn.click(handle_forward, [browser_state], outputs)
refresh_btn.click(handle_refresh, [browser_state], outputs)
new_tab_btn.click(handle_new_tab, [browser_state], outputs)
close_tab_btn.click(handle_close_tab, [browser_state], outputs)
tab_selector.input(handle_switch_tab, [browser_state, tab_selector], outputs)
bookmark_btn.click(handle_add_bookmark, [browser_state], outputs)
demo.launch()