Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8fe3a05d8b |
@@ -58,7 +58,7 @@ class MultiAgentSelector(Widget):
|
||||
|
||||
def compose(self):
|
||||
yield Header(show_clock=True, icon="⚙")
|
||||
title_text = Static("🖥️ Agent Selector", id="selector_title")
|
||||
title_text = Static("🖥️ Agent Selector", id="selector_title")
|
||||
title_text.styles.margin = (0, 0, 0, 1)
|
||||
yield title_text
|
||||
|
||||
@@ -175,7 +175,7 @@ class MultiAgentSelector(Widget):
|
||||
match_list.add_option((name, name))
|
||||
unmatched_label = self.query_one("#unmatched_label", Static)
|
||||
if unmatched:
|
||||
unmatched_label.update(f"âš ï¸ No matches for: {', '.join(unmatched)}")
|
||||
unmatched_label.update(f"No matches for: {', '.join(unmatched)}")
|
||||
else:
|
||||
unmatched_label.update("")
|
||||
|
||||
|
||||
@@ -23,8 +23,10 @@ import webbrowser
|
||||
from textual.containers import Horizontal, Vertical, VerticalScroll
|
||||
from textual.message import Message
|
||||
from textual.widget import Widget
|
||||
from textual.widgets import Button, Rule, Static
|
||||
from textual.widgets import Button, Input, Rule, Static, Switch
|
||||
|
||||
from utils.configmanager import get_user_value, save_user_config
|
||||
from utils.setup import get_base_directory
|
||||
from utils.versionchecker import (
|
||||
RELEASES_PAGE_URL,
|
||||
UpdateCheckResult,
|
||||
@@ -81,6 +83,36 @@ class SettingsWidget(Widget):
|
||||
#light_themes_col {
|
||||
margin-left: 1;
|
||||
}
|
||||
|
||||
/* Telemetry section */
|
||||
#telemetry_row {
|
||||
height: auto;
|
||||
margin: 1 0;
|
||||
}
|
||||
|
||||
#telemetry_label {
|
||||
width: auto;
|
||||
margin-right: 1;
|
||||
}
|
||||
|
||||
#telemetry_url_container {
|
||||
height: auto;
|
||||
margin: 1 0;
|
||||
}
|
||||
|
||||
#telemetry_url_label {
|
||||
width: auto;
|
||||
margin-right: 1;
|
||||
}
|
||||
|
||||
#telemetry_url_input {
|
||||
width: 1fr;
|
||||
}
|
||||
|
||||
.settings_description {
|
||||
color: $text-muted;
|
||||
margin-bottom: 1;
|
||||
}
|
||||
"""
|
||||
|
||||
class ThemeSelected(Message):
|
||||
@@ -146,6 +178,40 @@ class SettingsWidget(Widget):
|
||||
|
||||
yield Rule()
|
||||
|
||||
# Telemetry Section
|
||||
with Vertical(id="telemetry_section") as telemetry:
|
||||
telemetry.styles.height = "auto"
|
||||
yield Static(
|
||||
"📊 Telemetry Settings",
|
||||
id="telemetry_title",
|
||||
classes="settings_section_title",
|
||||
)
|
||||
|
||||
telemetry_enabled = get_user_value("TELEMETRY", bool, False)
|
||||
|
||||
with Horizontal(id="telemetry_row"):
|
||||
yield Static("Enable Telemetry: ", id="telemetry_label")
|
||||
yield Switch(value=telemetry_enabled, id="telemetry_switch")
|
||||
|
||||
# URL input container - shown only when telemetry is enabled
|
||||
if telemetry_enabled:
|
||||
telemetry_url = get_user_value("TELEM_URL", str, "")
|
||||
with Horizontal(id="telemetry_url_container"):
|
||||
yield Static("Telemetry URL: ", id="telemetry_url_label")
|
||||
yield Input(
|
||||
value=telemetry_url,
|
||||
placeholder="https://your-telemetry-endpoint.com",
|
||||
id="telemetry_url_input",
|
||||
)
|
||||
|
||||
yield Static(
|
||||
"Help improve Loxide by sending anonymous usage data",
|
||||
id="telemetry_description",
|
||||
classes="settings_description",
|
||||
)
|
||||
|
||||
yield Rule()
|
||||
|
||||
# Theme Section - Three columns: Dark 1, Dark 2, Light
|
||||
with Vertical(id="themes_section") as themes:
|
||||
themes.styles.height = "auto"
|
||||
@@ -231,6 +297,85 @@ class SettingsWidget(Widget):
|
||||
self.post_message(self.ThemeSelected(theme_name))
|
||||
event.stop()
|
||||
|
||||
def on_switch_changed(self, event: Switch.Changed) -> None:
|
||||
"""Handle telemetry switch toggle."""
|
||||
if event.switch.id == "telemetry_switch":
|
||||
enabled = event.value
|
||||
self._persist_telemetry_setting(enabled)
|
||||
|
||||
if enabled:
|
||||
self._mount_telemetry_url_input()
|
||||
else:
|
||||
self._unmount_telemetry_url_input()
|
||||
|
||||
def on_input_changed(self, event: Input.Changed) -> None:
|
||||
"""Handle telemetry URL input changes."""
|
||||
if event.input.id == "telemetry_url_input":
|
||||
self._persist_telemetry_url(event.value)
|
||||
|
||||
def _persist_telemetry_setting(self, enabled: bool) -> None:
|
||||
"""Store the telemetry opt-in/out setting in the user's config."""
|
||||
base_dir = get_base_directory()
|
||||
config_dir = base_dir / "config"
|
||||
|
||||
try:
|
||||
save_user_config(config_dir, {"TELEMETRY": enabled})
|
||||
logger.debug("Updated user config with TELEMETRY=%s", enabled)
|
||||
status = "enabled" if enabled else "disabled"
|
||||
self.app.notify(f"📊 Telemetry {status}", timeout=2)
|
||||
except Exception as exc:
|
||||
logger.error("Failed to save TELEMETRY setting: %s", exc)
|
||||
self.app.notify(f"⚠️ Failed to save setting: {exc}", severity="warning")
|
||||
|
||||
def _persist_telemetry_url(self, url: str) -> None:
|
||||
"""Store the telemetry URL in the user's config."""
|
||||
base_dir = get_base_directory()
|
||||
config_dir = base_dir / "config"
|
||||
|
||||
try:
|
||||
save_user_config(config_dir, {"TELEM_URL": url})
|
||||
logger.debug("Updated user config with TELEM_URL=%s", url)
|
||||
except Exception as exc:
|
||||
logger.error("Failed to save TELEM_URL setting: %s", exc)
|
||||
|
||||
def _mount_telemetry_url_input(self) -> None:
|
||||
"""Mount the telemetry URL input container."""
|
||||
try:
|
||||
# Check if already mounted
|
||||
self.query_one("#telemetry_url_container")
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
telemetry_url = get_user_value("TELEM_URL", str, "")
|
||||
|
||||
# Create container with children
|
||||
container = Horizontal(
|
||||
Static("Telemetry URL: ", id="telemetry_url_label"),
|
||||
Input(
|
||||
value=telemetry_url,
|
||||
placeholder="https://your-telemetry-endpoint.com",
|
||||
id="telemetry_url_input",
|
||||
),
|
||||
id="telemetry_url_container",
|
||||
)
|
||||
|
||||
# Mount after the telemetry_row within telemetry_section
|
||||
try:
|
||||
telemetry_section = self.query_one("#telemetry_section")
|
||||
telemetry_row = self.query_one("#telemetry_row")
|
||||
telemetry_section.mount(container, after=telemetry_row)
|
||||
except Exception as exc:
|
||||
logger.error("Failed to mount telemetry URL input: %s", exc)
|
||||
|
||||
def _unmount_telemetry_url_input(self) -> None:
|
||||
"""Unmount the telemetry URL input container."""
|
||||
try:
|
||||
container = self.query_one("#telemetry_url_container")
|
||||
container.remove()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _check_for_updates(self) -> None:
|
||||
"""Check for updates and update UI."""
|
||||
if self._checking:
|
||||
@@ -497,6 +642,36 @@ class ThemeSelector(Widget):
|
||||
#light_themes_col {
|
||||
margin-left: 1;
|
||||
}
|
||||
|
||||
/* Telemetry section */
|
||||
#telemetry_row {
|
||||
height: auto;
|
||||
margin: 1 0;
|
||||
}
|
||||
|
||||
#telemetry_label {
|
||||
width: auto;
|
||||
margin-right: 1;
|
||||
}
|
||||
|
||||
#telemetry_url_container {
|
||||
height: auto;
|
||||
margin: 1 0;
|
||||
}
|
||||
|
||||
#telemetry_url_label {
|
||||
width: auto;
|
||||
margin-right: 1;
|
||||
}
|
||||
|
||||
#telemetry_url_input {
|
||||
width: 1fr;
|
||||
}
|
||||
|
||||
.settings_description {
|
||||
color: $text-muted;
|
||||
margin-bottom: 1;
|
||||
}
|
||||
"""
|
||||
|
||||
class ThemeSelected(Message):
|
||||
|
||||
@@ -357,7 +357,7 @@ Loxide is designed for three primary user classes with varying levels of experti
|
||||
|
||||
#### 2.4.3 Network Requirements
|
||||
|
||||
- Outbound HTTPS (port 443) to Airlock server
|
||||
- Outbound to Airlock server
|
||||
- Outbound HTTPS to Gitea instance (development only)
|
||||
- No inbound connections required
|
||||
- Proxy support via standard environment variables
|
||||
|
||||
@@ -34,7 +34,7 @@ import requests
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Current application version - UPDATE THIS ON EACH RELEASE
|
||||
__version__ = "1.0.0"
|
||||
__version__ = "1.1.0"
|
||||
|
||||
# Gitea release API configuration
|
||||
GITEA_API_BASE = "https://git.racooncity.org/api/v1"
|
||||
|
||||
Reference in New Issue
Block a user