Added Telemetry Toggle
This commit is contained in:
@@ -58,7 +58,7 @@ class MultiAgentSelector(Widget):
|
|||||||
|
|
||||||
def compose(self):
|
def compose(self):
|
||||||
yield Header(show_clock=True, icon="⚙")
|
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)
|
title_text.styles.margin = (0, 0, 0, 1)
|
||||||
yield title_text
|
yield title_text
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ class MultiAgentSelector(Widget):
|
|||||||
match_list.add_option((name, name))
|
match_list.add_option((name, name))
|
||||||
unmatched_label = self.query_one("#unmatched_label", Static)
|
unmatched_label = self.query_one("#unmatched_label", Static)
|
||||||
if unmatched:
|
if unmatched:
|
||||||
unmatched_label.update(f"âš ï¸ No matches for: {', '.join(unmatched)}")
|
unmatched_label.update(f"No matches for: {', '.join(unmatched)}")
|
||||||
else:
|
else:
|
||||||
unmatched_label.update("")
|
unmatched_label.update("")
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,10 @@ import webbrowser
|
|||||||
from textual.containers import Horizontal, Vertical, VerticalScroll
|
from textual.containers import Horizontal, Vertical, VerticalScroll
|
||||||
from textual.message import Message
|
from textual.message import Message
|
||||||
from textual.widget import Widget
|
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 (
|
from utils.versionchecker import (
|
||||||
RELEASES_PAGE_URL,
|
RELEASES_PAGE_URL,
|
||||||
UpdateCheckResult,
|
UpdateCheckResult,
|
||||||
@@ -81,6 +83,36 @@ class SettingsWidget(Widget):
|
|||||||
#light_themes_col {
|
#light_themes_col {
|
||||||
margin-left: 1;
|
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):
|
class ThemeSelected(Message):
|
||||||
@@ -146,6 +178,40 @@ class SettingsWidget(Widget):
|
|||||||
|
|
||||||
yield Rule()
|
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
|
# Theme Section - Three columns: Dark 1, Dark 2, Light
|
||||||
with Vertical(id="themes_section") as themes:
|
with Vertical(id="themes_section") as themes:
|
||||||
themes.styles.height = "auto"
|
themes.styles.height = "auto"
|
||||||
@@ -231,6 +297,85 @@ class SettingsWidget(Widget):
|
|||||||
self.post_message(self.ThemeSelected(theme_name))
|
self.post_message(self.ThemeSelected(theme_name))
|
||||||
event.stop()
|
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:
|
def _check_for_updates(self) -> None:
|
||||||
"""Check for updates and update UI."""
|
"""Check for updates and update UI."""
|
||||||
if self._checking:
|
if self._checking:
|
||||||
@@ -497,6 +642,36 @@ class ThemeSelector(Widget):
|
|||||||
#light_themes_col {
|
#light_themes_col {
|
||||||
margin-left: 1;
|
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):
|
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
|
#### 2.4.3 Network Requirements
|
||||||
|
|
||||||
- Outbound HTTPS (port 443) to Airlock server
|
- Outbound to Airlock server
|
||||||
- Outbound HTTPS to Gitea instance (development only)
|
- Outbound HTTPS to Gitea instance (development only)
|
||||||
- No inbound connections required
|
- No inbound connections required
|
||||||
- Proxy support via standard environment variables
|
- Proxy support via standard environment variables
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import requests
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Current application version - UPDATE THIS ON EACH RELEASE
|
# Current application version - UPDATE THIS ON EACH RELEASE
|
||||||
__version__ = "1.0.0"
|
__version__ = "1.1.0"
|
||||||
|
|
||||||
# Gitea release API configuration
|
# Gitea release API configuration
|
||||||
GITEA_API_BASE = "https://git.racooncity.org/api/v1"
|
GITEA_API_BASE = "https://git.racooncity.org/api/v1"
|
||||||
|
|||||||
Reference in New Issue
Block a user