feat: donation banner, About tab, settings UI improvements
Some checks failed
Lint & Test / test (push) Has been cancelled

- Dismissible donation/open-source banner after 3+ sessions (30-day snooze)
- New About tab in Settings: version, repo link, license info
- Centralize project URLs (REPO_URL, DONATE_URL) in __init__.py, served via /health
- Center settings tab bar, reduce tab padding for 6-tab fit
- External URL save button: icon button instead of full-width text button
- Remove redundant settings footer close button
- Footer "Source Code" link replaced with "About" opening settings
- i18n keys for en/ru/zh
This commit is contained in:
2026-03-27 21:09:34 +03:00
parent f61a0206d4
commit f3d07fc47f
18 changed files with 442 additions and 49 deletions

View File

@@ -13,7 +13,7 @@ from typing import Optional
import psutil
from fastapi import APIRouter, Depends, HTTPException, Query
from wled_controller import __version__
from wled_controller import __version__, REPO_URL, DONATE_URL
from wled_controller.api.auth import AuthRequired, is_auth_enabled
from wled_controller.api.dependencies import (
get_audio_source_store,
@@ -54,7 +54,11 @@ logger = get_logger(__name__)
psutil.cpu_percent(interval=None)
# GPU monitoring (initialized once in utils.gpu, shared with metrics_history)
from wled_controller.utils.gpu import nvml_available as _nvml_available, nvml as _nvml, nvml_handle as _nvml_handle # noqa: E402
from wled_controller.utils.gpu import ( # noqa: E402
nvml_available as _nvml_available,
nvml as _nvml,
nvml_handle as _nvml_handle,
)
def _get_cpu_name() -> str | None:
@@ -77,9 +81,7 @@ def _get_cpu_name() -> str | None:
return line.split(":")[1].strip()
elif platform.system() == "Darwin":
return (
subprocess.check_output(
["sysctl", "-n", "machdep.cpu.brand_string"]
)
subprocess.check_output(["sysctl", "-n", "machdep.cpu.brand_string"])
.decode()
.strip()
)
@@ -107,6 +109,8 @@ async def health_check():
version=__version__,
demo_mode=get_config().demo,
auth_required=is_auth_enabled(),
repo_url=REPO_URL,
donate_url=DONATE_URL,
)
@@ -131,12 +135,22 @@ async def list_all_tags(_: AuthRequired):
"""Get all tags used across all entities."""
all_tags: set[str] = set()
from wled_controller.api.dependencies import get_asset_store
store_getters = [
get_device_store, get_output_target_store, get_color_strip_store,
get_picture_source_store, get_audio_source_store, get_value_source_store,
get_sync_clock_store, get_automation_store, get_scene_preset_store,
get_template_store, get_audio_template_store, get_pp_template_store,
get_pattern_template_store, get_asset_store,
get_device_store,
get_output_target_store,
get_color_strip_store,
get_picture_source_store,
get_audio_source_store,
get_value_source_store,
get_sync_clock_store,
get_automation_store,
get_scene_preset_store,
get_template_store,
get_audio_template_store,
get_pp_template_store,
get_pattern_template_store,
get_asset_store,
]
for getter in store_getters:
try:
@@ -209,15 +223,11 @@ async def get_displays(
except EntityNotFoundError as e:
raise HTTPException(status_code=404, detail=str(e))
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
logger.error("Failed to get displays: %s", e, exc_info=True)
raise HTTPException(
status_code=500,
detail="Internal server error"
)
raise HTTPException(status_code=500, detail="Internal server error")
@router.get("/api/v1/system/processes", response_model=ProcessListResponse, tags=["Config"])
@@ -235,10 +245,7 @@ async def get_running_processes(_: AuthRequired):
return ProcessListResponse(processes=sorted_procs, count=len(sorted_procs))
except Exception as e:
logger.error("Failed to get processes: %s", e, exc_info=True)
raise HTTPException(
status_code=500,
detail="Internal server error"
)
raise HTTPException(status_code=500, detail="Internal server error")
@router.get(
@@ -260,9 +267,7 @@ def get_system_performance(_: AuthRequired):
try:
util = _nvml.nvmlDeviceGetUtilizationRates(_nvml_handle)
mem_info = _nvml.nvmlDeviceGetMemoryInfo(_nvml_handle)
temp = _nvml.nvmlDeviceGetTemperature(
_nvml_handle, _nvml.NVML_TEMPERATURE_GPU
)
temp = _nvml.nvmlDeviceGetTemperature(_nvml_handle, _nvml.NVML_TEMPERATURE_GPU)
gpu = GpuInfo(
name=_nvml.nvmlDeviceGetName(_nvml_handle),
utilization=float(util.gpu),