ruff formatting and fixes
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user