ruff formatting and fixes

This commit is contained in:
2025-09-22 13:55:52 -05:00
parent fd4e0c8a14
commit ee521a9f60
11 changed files with 195 additions and 38 deletions

View File

@@ -3,13 +3,13 @@
This module provides services for listening to and collecting network This module provides services for listening to and collecting network
announces from the Reticulum network. announces from the Reticulum network.
""" """
import time import time
from dataclasses import dataclass from dataclasses import dataclass
import RNS import RNS
@dataclass @dataclass
class Announce: class Announce:
"""Represents a Reticulum network announce. """Represents a Reticulum network announce.
@@ -21,6 +21,7 @@ class Announce:
display_name: str | None display_name: str | None
timestamp: int timestamp: int
class AnnounceService: class AnnounceService:
"""Service to listen for Reticulum announces and collect them. """Service to listen for Reticulum announces and collect them.
@@ -60,7 +61,11 @@ class AnnounceService:
except UnicodeDecodeError: except UnicodeDecodeError:
pass pass
announce = Announce(destination_hash.hex(), display_name, ts) announce = Announce(destination_hash.hex(), display_name, ts)
self.announces = [ann for ann in self.announces if ann.destination_hash != announce.destination_hash] self.announces = [
ann
for ann in self.announces
if ann.destination_hash != announce.destination_hash
]
self.announces.insert(0, announce) self.announces.insert(0, announce)
if self.update_callback: if self.update_callback:
self.update_callback(self.announces) self.update_callback(self.announces)

View File

@@ -3,6 +3,7 @@
This module provides the entry point and platform-specific launchers for the This module provides the entry point and platform-specific launchers for the
Ren Browser, a browser for the Reticulum Network built with Flet. Ren Browser, a browser for the Reticulum Network built with Flet.
""" """
import argparse import argparse
import flet as ft import flet as ft
@@ -15,25 +16,44 @@ from ren_browser.ui.ui import build_ui
RENDERER = "plaintext" RENDERER = "plaintext"
RNS_CONFIG_DIR = None RNS_CONFIG_DIR = None
async def main(page: Page): async def main(page: Page):
"""Initialize and launch the Ren Browser application. """Initialize and launch the Ren Browser application.
Sets up the loading screen, initializes Reticulum network, Sets up the loading screen, initializes Reticulum network,
and builds the main UI. and builds the main UI.
""" """
page.title = "Ren Browser"
page.theme_mode = ft.ThemeMode.DARK
loader = ft.Container( loader = ft.Container(
expand=True, expand=True,
alignment=ft.alignment.center, alignment=ft.alignment.center,
bgcolor=ft.Colors.SURFACE,
content=ft.Column( content=ft.Column(
[ft.ProgressRing(), ft.Text("Initializing reticulum network")], [
ft.ProgressRing(color=ft.Colors.PRIMARY, width=50, height=50),
ft.Container(height=20),
ft.Text(
"Initializing Reticulum Network...",
size=16,
color=ft.Colors.ON_SURFACE,
text_align=ft.TextAlign.CENTER,
),
],
alignment=ft.MainAxisAlignment.CENTER, alignment=ft.MainAxisAlignment.CENTER,
horizontal_alignment=ft.CrossAxisAlignment.CENTER, horizontal_alignment=ft.CrossAxisAlignment.CENTER,
spacing=10,
), ),
) )
page.add(loader) page.add(loader)
page.update() page.update()
def init_ret(): def init_ret():
import time
time.sleep(0.5)
# Initialize storage system # Initialize storage system
storage = initialize_storage(page) storage = initialize_storage(page)
@@ -45,6 +65,7 @@ async def main(page: Page):
try: try:
# Set up logging capture first, before RNS init # Set up logging capture first, before RNS init
import ren_browser.logs import ren_browser.logs
ren_browser.logs.setup_rns_logging() ren_browser.logs.setup_rns_logging()
RNS.Reticulum(str(config_dir)) RNS.Reticulum(str(config_dir))
except (OSError, ValueError): except (OSError, ValueError):
@@ -55,14 +76,31 @@ async def main(page: Page):
page.run_thread(init_ret) page.run_thread(init_ret)
def run(): def run():
"""Run Ren Browser with command line argument parsing.""" """Run Ren Browser with command line argument parsing."""
global RENDERER, RNS_CONFIG_DIR global RENDERER, RNS_CONFIG_DIR
parser = argparse.ArgumentParser(description="Ren Browser") parser = argparse.ArgumentParser(description="Ren Browser")
parser.add_argument("-r", "--renderer", choices=["plaintext", "micron"], default=RENDERER, help="Select renderer (plaintext or micron)") parser.add_argument(
parser.add_argument("-w", "--web", action="store_true", help="Launch in web browser mode") "-r",
parser.add_argument("-p", "--port", type=int, default=None, help="Port for web server") "--renderer",
parser.add_argument("-c", "--config-dir", type=str, default=None, help="RNS config directory (default: ~/.reticulum/)") choices=["plaintext", "micron"],
default=RENDERER,
help="Select renderer (plaintext or micron)",
)
parser.add_argument(
"-w", "--web", action="store_true", help="Launch in web browser mode"
)
parser.add_argument(
"-p", "--port", type=int, default=None, help="Port for web server"
)
parser.add_argument(
"-c",
"--config-dir",
type=str,
default=None,
help="RNS config directory (default: ~/.reticulum/)",
)
args = parser.parse_args() args = parser.parse_args()
RENDERER = args.renderer RENDERER = args.renderer
@@ -71,6 +109,7 @@ def run():
RNS_CONFIG_DIR = args.config_dir RNS_CONFIG_DIR = args.config_dir
else: else:
import pathlib import pathlib
RNS_CONFIG_DIR = str(pathlib.Path.home() / ".reticulum") RNS_CONFIG_DIR = str(pathlib.Path.home() / ".reticulum")
if args.web: if args.web:
@@ -81,33 +120,41 @@ def run():
else: else:
ft.app(main) ft.app(main)
if __name__ == "__main__": if __name__ == "__main__":
run() run()
def web(): def web():
"""Launch Ren Browser in web mode.""" """Launch Ren Browser in web mode."""
ft.app(main, view=AppView.WEB_BROWSER) ft.app(main, view=AppView.WEB_BROWSER)
def android(): def android():
"""Launch Ren Browser in Android mode.""" """Launch Ren Browser in Android mode."""
ft.app(main, view=AppView.FLET_APP_WEB) ft.app(main, view=AppView.FLET_APP_WEB)
def ios(): def ios():
"""Launch Ren Browser in iOS mode.""" """Launch Ren Browser in iOS mode."""
ft.app(main, view=AppView.FLET_APP_WEB) ft.app(main, view=AppView.FLET_APP_WEB)
def run_dev(): def run_dev():
"""Launch Ren Browser in desktop mode.""" """Launch Ren Browser in desktop mode."""
ft.app(main) ft.app(main)
def web_dev(): def web_dev():
"""Launch Ren Browser in web mode.""" """Launch Ren Browser in web mode."""
ft.app(main, view=AppView.WEB_BROWSER) ft.app(main, view=AppView.WEB_BROWSER)
def android_dev(): def android_dev():
"""Launch Ren Browser in Android mode.""" """Launch Ren Browser in Android mode."""
ft.app(main, view=AppView.FLET_APP_WEB) ft.app(main, view=AppView.FLET_APP_WEB)
def ios_dev(): def ios_dev():
"""Launch Ren Browser in iOS mode.""" """Launch Ren Browser in iOS mode."""
ft.app(main, view=AppView.FLET_APP_WEB) ft.app(main, view=AppView.FLET_APP_WEB)

View File

@@ -3,6 +3,7 @@
Provides keyboard event handling and delegation to tab manager Provides keyboard event handling and delegation to tab manager
and UI components. and UI components.
""" """
import flet as ft import flet as ft

View File

@@ -3,6 +3,7 @@
Provides centralized logging for application events, errors, and Provides centralized logging for application events, errors, and
Reticulum network activities. Reticulum network activities.
""" """
import datetime import datetime
import RNS import RNS
@@ -12,6 +13,7 @@ ERROR_LOGS: list[str] = []
RET_LOGS: list[str] = [] RET_LOGS: list[str] = []
_original_rns_log = RNS.log _original_rns_log = RNS.log
def log_ret(msg, *args, **kwargs): def log_ret(msg, *args, **kwargs):
"""Log Reticulum messages with timestamp. """Log Reticulum messages with timestamp.
@@ -25,6 +27,7 @@ def log_ret(msg, *args, **kwargs):
RET_LOGS.append(f"[{timestamp}] {msg}") RET_LOGS.append(f"[{timestamp}] {msg}")
return _original_rns_log(msg, *args, **kwargs) return _original_rns_log(msg, *args, **kwargs)
def setup_rns_logging(): def setup_rns_logging():
"""Set up RNS log replacement. Call this after RNS.Reticulum initialization.""" """Set up RNS log replacement. Call this after RNS.Reticulum initialization."""
global _original_rns_log global _original_rns_log
@@ -33,6 +36,7 @@ def setup_rns_logging():
_original_rns_log = RNS.log _original_rns_log = RNS.log
RNS.log = log_ret RNS.log = log_ret
def log_error(msg: str): def log_error(msg: str):
"""Log error messages to both error and application logs. """Log error messages to both error and application logs.
@@ -44,6 +48,7 @@ def log_error(msg: str):
ERROR_LOGS.append(f"[{timestamp}] {msg}") ERROR_LOGS.append(f"[{timestamp}] {msg}")
APP_LOGS.append(f"[{timestamp}] ERROR: {msg}") APP_LOGS.append(f"[{timestamp}] ERROR: {msg}")
def log_app(msg: str): def log_app(msg: str):
"""Log application messages. """Log application messages.

View File

@@ -3,6 +3,7 @@
Handles downloading pages from the Reticulum network using Handles downloading pages from the Reticulum network using
the nomadnetwork protocol. the nomadnetwork protocol.
""" """
import threading import threading
import time import time
from dataclasses import dataclass from dataclasses import dataclass
@@ -10,7 +11,6 @@ from dataclasses import dataclass
import RNS import RNS
@dataclass @dataclass
class PageRequest: class PageRequest:
"""Represents a request for a page from the Reticulum network. """Represents a request for a page from the Reticulum network.
@@ -22,6 +22,7 @@ class PageRequest:
page_path: str page_path: str
field_data: dict | None = None field_data: dict | None = None
class PageFetcher: class PageFetcher:
"""Fetcher to download pages from the Reticulum network.""" """Fetcher to download pages from the Reticulum network."""
@@ -43,7 +44,9 @@ class PageFetcher:
Exception: If no path to destination or identity not found. Exception: If no path to destination or identity not found.
""" """
RNS.log(f"PageFetcher: starting fetch of {req.page_path} from {req.destination_hash}") RNS.log(
f"PageFetcher: starting fetch of {req.page_path} from {req.destination_hash}"
)
dest_bytes = bytes.fromhex(req.destination_hash) dest_bytes = bytes.fromhex(req.destination_hash)
if not RNS.Transport.has_path(dest_bytes): if not RNS.Transport.has_path(dest_bytes):
RNS.Transport.request_path(dest_bytes) RNS.Transport.request_path(dest_bytes)
@@ -79,9 +82,16 @@ class PageFetcher:
ev.set() ev.set()
link.set_link_established_callback( link.set_link_established_callback(
lambda link: link.request(req.page_path, req.field_data, response_callback=on_response, failed_callback=on_failed) lambda link: link.request(
req.page_path,
req.field_data,
response_callback=on_response,
failed_callback=on_failed,
)
) )
ev.wait(timeout=15) ev.wait(timeout=15)
data_str = result["data"] or "No content received" data_str = result["data"] or "No content received"
RNS.log(f"PageFetcher: received data for {req.destination_hash}:{req.page_path}") RNS.log(
f"PageFetcher: received data for {req.destination_hash}:{req.page_path}"
)
return data_str return data_str

View File

@@ -3,6 +3,7 @@
Provides rendering capabilities for micron markup content, Provides rendering capabilities for micron markup content,
currently implemented as a placeholder. currently implemented as a placeholder.
""" """
import flet as ft import flet as ft

View File

@@ -2,6 +2,7 @@
Provides fallback rendering for plaintext content and source viewing. Provides fallback rendering for plaintext content and source viewing.
""" """
import flet as ft import flet as ft

View File

@@ -3,6 +3,7 @@
Provides persistent storage for configuration, bookmarks, history, Provides persistent storage for configuration, bookmarks, history,
and other application data across different platforms. and other application data across different platforms.
""" """
import json import json
import os import os
import pathlib import pathlib
@@ -38,7 +39,9 @@ class StorageManager:
if os.name == "posix" and "ANDROID_ROOT" in os.environ: if os.name == "posix" and "ANDROID_ROOT" in os.environ:
# Android - use app's private files directory # Android - use app's private files directory
storage_dir = pathlib.Path("/data/data/com.ren_browser/files") storage_dir = pathlib.Path("/data/data/com.ren_browser/files")
elif hasattr(os, "uname") and "iOS" in str(getattr(os, "uname", lambda: "")()).replace("iPhone", "iOS"): elif hasattr(os, "uname") and "iOS" in str(
getattr(os, "uname", lambda: "")()
).replace("iPhone", "iOS"):
# iOS - use app's documents directory # iOS - use app's documents directory
storage_dir = pathlib.Path.home() / "Documents" / "ren_browser" storage_dir = pathlib.Path.home() / "Documents" / "ren_browser"
else: else:
@@ -46,7 +49,9 @@ class StorageManager:
if "APPDATA" in os.environ: # Windows if "APPDATA" in os.environ: # Windows
storage_dir = pathlib.Path(os.environ["APPDATA"]) / "ren_browser" storage_dir = pathlib.Path(os.environ["APPDATA"]) / "ren_browser"
elif "XDG_CONFIG_HOME" in os.environ: # Linux XDG standard elif "XDG_CONFIG_HOME" in os.environ: # Linux XDG standard
storage_dir = pathlib.Path(os.environ["XDG_CONFIG_HOME"]) / "ren_browser" storage_dir = (
pathlib.Path(os.environ["XDG_CONFIG_HOME"]) / "ren_browser"
)
else: else:
storage_dir = pathlib.Path.home() / ".ren_browser" storage_dir = pathlib.Path.home() / ".ren_browser"
@@ -58,6 +63,7 @@ class StorageManager:
self._storage_dir.mkdir(parents=True, exist_ok=True) self._storage_dir.mkdir(parents=True, exist_ok=True)
except (OSError, PermissionError): except (OSError, PermissionError):
import tempfile import tempfile
self._storage_dir = pathlib.Path(tempfile.gettempdir()) / "ren_browser" self._storage_dir = pathlib.Path(tempfile.gettempdir()) / "ren_browser"
self._storage_dir.mkdir(parents=True, exist_ok=True) self._storage_dir.mkdir(parents=True, exist_ok=True)
@@ -71,6 +77,7 @@ class StorageManager:
# Check for global override from app # Check for global override from app
try: try:
from ren_browser.app import RNS_CONFIG_DIR from ren_browser.app import RNS_CONFIG_DIR
if RNS_CONFIG_DIR: if RNS_CONFIG_DIR:
return pathlib.Path(RNS_CONFIG_DIR) return pathlib.Path(RNS_CONFIG_DIR)
except ImportError: except ImportError:
@@ -111,7 +118,9 @@ class StorageManager:
try: try:
if self.page and hasattr(self.page, "client_storage"): if self.page and hasattr(self.page, "client_storage"):
self.page.client_storage.set("ren_browser_config", config_content) self.page.client_storage.set("ren_browser_config", config_content)
self.page.client_storage.set("ren_browser_config_error", f"File save failed: {error}") self.page.client_storage.set(
"ren_browser_config_error", f"File save failed: {error}"
)
return True return True
try: try:
@@ -122,6 +131,7 @@ class StorageManager:
pass pass
import tempfile import tempfile
temp_path = pathlib.Path(tempfile.gettempdir()) / "ren_browser_config.txt" temp_path = pathlib.Path(tempfile.gettempdir()) / "ren_browser_config.txt"
temp_path.write_text(config_content, encoding="utf-8") temp_path.write_text(config_content, encoding="utf-8")
return True return True
@@ -163,7 +173,9 @@ class StorageManager:
json.dump(bookmarks, f, indent=2) json.dump(bookmarks, f, indent=2)
if self.page and hasattr(self.page, "client_storage"): if self.page and hasattr(self.page, "client_storage"):
self.page.client_storage.set("ren_browser_bookmarks", json.dumps(bookmarks)) self.page.client_storage.set(
"ren_browser_bookmarks", json.dumps(bookmarks)
)
return True return True
except Exception: except Exception:
@@ -267,6 +279,7 @@ def get_rns_config_directory() -> str:
"""Get the RNS config directory, checking for global override.""" """Get the RNS config directory, checking for global override."""
try: try:
from ren_browser.app import RNS_CONFIG_DIR from ren_browser.app import RNS_CONFIG_DIR
if RNS_CONFIG_DIR: if RNS_CONFIG_DIR:
return RNS_CONFIG_DIR return RNS_CONFIG_DIR
except ImportError: except ImportError:

View File

@@ -3,6 +3,7 @@
Provides tab creation, switching, and content management functionality Provides tab creation, switching, and content management functionality
for the browser interface. for the browser interface.
""" """
from types import SimpleNamespace from types import SimpleNamespace
import flet as ft import flet as ft
@@ -25,15 +26,26 @@ class TabsManager:
""" """
import ren_browser.app as app_module import ren_browser.app as app_module
self.page = page self.page = page
self.manager = SimpleNamespace(tabs=[], index=0) self.manager = SimpleNamespace(tabs=[], index=0)
self.tab_bar = ft.Row(spacing=4) self.tab_bar = ft.Row(spacing=4)
self.content_container = ft.Container(expand=True, bgcolor=ft.Colors.BLACK, padding=ft.padding.all(5)) self.content_container = ft.Container(
expand=True, bgcolor=ft.Colors.BLACK, padding=ft.padding.all(5)
)
default_content = render_micron("Welcome to Ren Browser") if app_module.RENDERER == "micron" else render_plaintext("Welcome to Ren Browser") default_content = (
render_micron("Welcome to Ren Browser")
if app_module.RENDERER == "micron"
else render_plaintext("Welcome to Ren Browser")
)
self._add_tab_internal("Home", default_content) self._add_tab_internal("Home", default_content)
self.add_btn = ft.IconButton(ft.Icons.ADD, tooltip="New Tab", on_click=self._on_add_click) self.add_btn = ft.IconButton(
self.close_btn = ft.IconButton(ft.Icons.CLOSE, tooltip="Close Tab", on_click=self._on_close_click) ft.Icons.ADD, tooltip="New Tab", on_click=self._on_add_click
)
self.close_btn = ft.IconButton(
ft.Icons.CLOSE, tooltip="Close Tab", on_click=self._on_close_click
)
self.tab_bar.controls.extend([self.add_btn, self.close_btn]) self.tab_bar.controls.extend([self.add_btn, self.close_btn])
self.select_tab(0) self.select_tab(0)
@@ -43,9 +55,13 @@ class TabsManager:
value=title, value=title,
expand=True, expand=True,
text_style=ft.TextStyle(size=12), text_style=ft.TextStyle(size=12),
content_padding=ft.padding.only(top=8, bottom=8, left=8, right=8) content_padding=ft.padding.only(top=8, bottom=8, left=8, right=8),
)
go_btn = ft.IconButton(
ft.Icons.OPEN_IN_BROWSER,
tooltip="Load URL",
on_click=lambda e, i=idx: self._on_tab_go(e, i),
) )
go_btn = ft.IconButton(ft.Icons.OPEN_IN_BROWSER, tooltip="Load URL", on_click=lambda e, i=idx: self._on_tab_go(e, i))
content_control = content content_control = content
tab_content = ft.Column( tab_content = ft.Column(
expand=True, expand=True,
@@ -53,13 +69,15 @@ class TabsManager:
content_control, content_control,
], ],
) )
self.manager.tabs.append({ self.manager.tabs.append(
"title": title, {
"url_field": url_field, "title": title,
"go_btn": go_btn, "url_field": url_field,
"content_control": content_control, "go_btn": go_btn,
"content": tab_content, "content_control": content_control,
}) "content": tab_content,
}
)
btn = ft.Container( btn = ft.Container(
content=ft.Text(title), content=ft.Text(title),
on_click=lambda e, i=idx: self.select_tab(i), on_click=lambda e, i=idx: self.select_tab(i),
@@ -74,7 +92,12 @@ class TabsManager:
title = f"Tab {len(self.manager.tabs) + 1}" title = f"Tab {len(self.manager.tabs) + 1}"
content_text = f"Content for {title}" content_text = f"Content for {title}"
import ren_browser.app as app_module import ren_browser.app as app_module
content = render_micron(content_text) if app_module.RENDERER == "micron" else render_plaintext(content_text)
content = (
render_micron(content_text)
if app_module.RENDERER == "micron"
else render_plaintext(content_text)
)
self._add_tab_internal(title, content) self._add_tab_internal(title, content)
self.select_tab(len(self.manager.tabs) - 1) self.select_tab(len(self.manager.tabs) - 1)
self.page.update() self.page.update()
@@ -114,7 +137,12 @@ class TabsManager:
return return
placeholder_text = f"Loading content for {url}" placeholder_text = f"Loading content for {url}"
import ren_browser.app as app_module import ren_browser.app as app_module
new_control = render_micron(placeholder_text) if app_module.RENDERER == "micron" else render_plaintext(placeholder_text)
new_control = (
render_micron(placeholder_text)
if app_module.RENDERER == "micron"
else render_plaintext(placeholder_text)
)
tab["content_control"] = new_control tab["content_control"] = new_control
tab["content"].controls[0] = new_control tab["content"].controls[0] = new_control
if self.manager.index == idx: if self.manager.index == idx:

View File

@@ -3,6 +3,7 @@
Provides configuration management, log viewing, and storage Provides configuration management, log viewing, and storage
information display. information display.
""" """
import flet as ft import flet as ft
from ren_browser.logs import ERROR_LOGS, RET_LOGS from ren_browser.logs import ERROR_LOGS, RET_LOGS
@@ -35,12 +36,20 @@ def open_settings_tab(page: ft.Page, tab_manager):
try: try:
success = storage.save_config(config_field.value) success = storage.save_config(config_field.value)
if success: if success:
page.snack_bar = ft.SnackBar(ft.Text("Config saved successfully. Please restart the app."), open=True) page.snack_bar = ft.SnackBar(
ft.Text("Config saved successfully. Please restart the app."),
open=True,
)
else: else:
page.snack_bar = ft.SnackBar(ft.Text("Error saving config: Storage operation failed"), open=True) page.snack_bar = ft.SnackBar(
ft.Text("Error saving config: Storage operation failed"), open=True
)
except Exception as ex: except Exception as ex:
page.snack_bar = ft.SnackBar(ft.Text(f"Error saving config: {ex}"), open=True) page.snack_bar = ft.SnackBar(
ft.Text(f"Error saving config: {ex}"), open=True
)
page.update() page.update()
save_btn = ft.ElevatedButton("Save and Restart", on_click=on_save_config) save_btn = ft.ElevatedButton("Save and Restart", on_click=on_save_config)
error_field = ft.TextField( error_field = ft.TextField(
label="Error Logs", label="Error Logs",
@@ -69,22 +78,29 @@ def open_settings_tab(page: ft.Page, tab_manager):
) )
content_placeholder = ft.Container(expand=True) content_placeholder = ft.Container(expand=True)
def show_config(ev): def show_config(ev):
content_placeholder.content = config_field content_placeholder.content = config_field
page.update() page.update()
def show_errors(ev): def show_errors(ev):
error_field.value = "\n".join(ERROR_LOGS) or "No errors logged." error_field.value = "\n".join(ERROR_LOGS) or "No errors logged."
content_placeholder.content = error_field content_placeholder.content = error_field
page.update() page.update()
def show_ret_logs(ev): def show_ret_logs(ev):
ret_field.value = "\n".join(RET_LOGS) or "No Reticulum logs." ret_field.value = "\n".join(RET_LOGS) or "No Reticulum logs."
content_placeholder.content = ret_field content_placeholder.content = ret_field
page.update() page.update()
def show_storage_info(ev): def show_storage_info(ev):
storage_info = storage.get_storage_info() storage_info = storage.get_storage_info()
storage_field.value = "\n".join([f"{key}: {value}" for key, value in storage_info.items()]) storage_field.value = "\n".join(
[f"{key}: {value}" for key, value in storage_info.items()]
)
content_placeholder.content = storage_field content_placeholder.content = storage_field
page.update() page.update()
def refresh_current_view(ev): def refresh_current_view(ev):
# Refresh the currently displayed content # Refresh the currently displayed content
if content_placeholder.content == error_field: if content_placeholder.content == error_field:
@@ -95,12 +111,15 @@ def open_settings_tab(page: ft.Page, tab_manager):
show_storage_info(ev) show_storage_info(ev)
elif content_placeholder.content == config_field: elif content_placeholder.content == config_field:
show_config(ev) show_config(ev)
btn_config = ft.ElevatedButton("Config", on_click=show_config) btn_config = ft.ElevatedButton("Config", on_click=show_config)
btn_errors = ft.ElevatedButton("Errors", on_click=show_errors) btn_errors = ft.ElevatedButton("Errors", on_click=show_errors)
btn_ret = ft.ElevatedButton("Ret Logs", on_click=show_ret_logs) btn_ret = ft.ElevatedButton("Ret Logs", on_click=show_ret_logs)
btn_storage = ft.ElevatedButton("Storage", on_click=show_storage_info) btn_storage = ft.ElevatedButton("Storage", on_click=show_storage_info)
btn_refresh = ft.ElevatedButton("Refresh", on_click=refresh_current_view) btn_refresh = ft.ElevatedButton("Refresh", on_click=refresh_current_view)
button_row = ft.Row(controls=[btn_config, btn_errors, btn_ret, btn_storage, btn_refresh]) button_row = ft.Row(
controls=[btn_config, btn_errors, btn_ret, btn_storage, btn_refresh]
)
content_placeholder.content = config_field content_placeholder.content = config_field
settings_content = ft.Column( settings_content = ft.Column(
expand=True, expand=True,

View File

@@ -3,6 +3,7 @@
Builds the complete browser interface including tabs, navigation, Builds the complete browser interface including tabs, navigation,
announce handling, and content rendering. announce handling, and content rendering.
""" """
import flet as ft import flet as ft
from flet import Page from flet import Page
@@ -27,10 +28,12 @@ def build_ui(page: Page):
page_fetcher = PageFetcher() page_fetcher = PageFetcher()
announce_list = ft.ListView(expand=True, spacing=1) announce_list = ft.ListView(expand=True, spacing=1)
def update_announces(ann_list): def update_announces(ann_list):
announce_list.controls.clear() announce_list.controls.clear()
for ann in ann_list: for ann in ann_list:
label = ann.display_name or ann.destination_hash label = ann.display_name or ann.destination_hash
def on_click_ann(e, dest=ann.destination_hash, disp=ann.display_name): def on_click_ann(e, dest=ann.destination_hash, disp=ann.display_name):
title = disp or "Anonymous" title = disp or "Anonymous"
full_url = f"{dest}:/page/index.mu" full_url = f"{dest}:/page/index.mu"
@@ -41,12 +44,14 @@ def build_ui(page: Page):
tab["url_field"].value = full_url tab["url_field"].value = full_url
tab_manager.select_tab(idx) tab_manager.select_tab(idx)
page.update() page.update()
def fetch_and_update(): def fetch_and_update():
req = PageRequest(destination_hash=dest, page_path="/page/index.mu") req = PageRequest(destination_hash=dest, page_path="/page/index.mu")
try: try:
result = page_fetcher.fetch_page(req) result = page_fetcher.fetch_page(req)
except Exception as ex: except Exception as ex:
import ren_browser.app as app_module import ren_browser.app as app_module
app_module.log_error(str(ex)) app_module.log_error(str(ex))
result = f"Error: {ex}" result = f"Error: {ex}"
try: try:
@@ -62,13 +67,21 @@ def build_ui(page: Page):
if tab_manager.manager.index == idx: if tab_manager.manager.index == idx:
tab_manager.content_container.content = tab["content"] tab_manager.content_container.content = tab["content"]
page.update() page.update()
page.run_thread(fetch_and_update) page.run_thread(fetch_and_update)
announce_list.controls.append(ft.TextButton(label, on_click=on_click_ann)) announce_list.controls.append(ft.TextButton(label, on_click=on_click_ann))
page.update() page.update()
AnnounceService(update_callback=update_announces) AnnounceService(update_callback=update_announces)
page.drawer = ft.NavigationDrawer( page.drawer = ft.NavigationDrawer(
controls=[ controls=[
ft.Text("Announcements", weight=ft.FontWeight.BOLD, text_align=ft.TextAlign.CENTER, expand=True), ft.Text(
"Announcements",
weight=ft.FontWeight.BOLD,
text_align=ft.TextAlign.CENTER,
expand=True,
),
ft.Divider(), ft.Divider(),
announce_list, announce_list,
], ],
@@ -76,12 +89,22 @@ def build_ui(page: Page):
page.appbar.leading = ft.IconButton( page.appbar.leading = ft.IconButton(
ft.Icons.MENU, ft.Icons.MENU,
tooltip="Toggle sidebar", tooltip="Toggle sidebar",
on_click=lambda e: (setattr(page.drawer, "open", not page.drawer.open), page.update()), on_click=lambda e: (
setattr(page.drawer, "open", not page.drawer.open),
page.update(),
),
) )
tab_manager = TabsManager(page) tab_manager = TabsManager(page)
from ren_browser.ui.settings import open_settings_tab from ren_browser.ui.settings import open_settings_tab
page.appbar.actions = [ft.IconButton(ft.Icons.SETTINGS, tooltip="Settings", on_click=lambda e: open_settings_tab(page, tab_manager))]
page.appbar.actions = [
ft.IconButton(
ft.Icons.SETTINGS,
tooltip="Settings",
on_click=lambda e: open_settings_tab(page, tab_manager),
)
]
Shortcuts(page, tab_manager) Shortcuts(page, tab_manager)
url_bar = ft.Row( url_bar = ft.Row(
controls=[ controls=[
@@ -91,15 +114,19 @@ def build_ui(page: Page):
) )
page.appbar.title = url_bar page.appbar.title = url_bar
orig_select_tab = tab_manager.select_tab orig_select_tab = tab_manager.select_tab
def _select_tab_and_update_url(i): def _select_tab_and_update_url(i):
orig_select_tab(i) orig_select_tab(i)
tab = tab_manager.manager.tabs[i] tab = tab_manager.manager.tabs[i]
url_bar.controls.clear() url_bar.controls.clear()
url_bar.controls.extend([tab["url_field"], tab["go_btn"]]) url_bar.controls.extend([tab["url_field"], tab["go_btn"]])
page.update() page.update()
tab_manager.select_tab = _select_tab_and_update_url tab_manager.select_tab = _select_tab_and_update_url
def _update_content_width(e=None): def _update_content_width(e=None):
tab_manager.content_container.width = page.width tab_manager.content_container.width = page.width
_update_content_width() _update_content_width()
page.on_resized = lambda e: (_update_content_width(), page.update()) page.on_resized = lambda e: (_update_content_width(), page.update())
main_area = ft.Column( main_area = ft.Column(