From 1eb6e735a45b4452998fbc453c285fceadfc92c5 Mon Sep 17 00:00:00 2001 From: Rafael Uzarowski Date: Sat, 22 Mar 2025 18:09:24 +0100 Subject: [PATCH] feat: Tabbed Settings - implement support and move existing settings to 3 tab categories --- python/helpers/settings.py | 11 ++- python/helpers/task_scheduler.py | 10 +-- webui/css/settings.css | 144 ++++++++++++++++++++++++++++++- webui/index.html | 22 ++++- webui/js/settings.js | 31 ++++++- 5 files changed, 205 insertions(+), 13 deletions(-) diff --git a/python/helpers/settings.py b/python/helpers/settings.py index fc7a41ce2..12b7c8a5d 100644 --- a/python/helpers/settings.py +++ b/python/helpers/settings.py @@ -1,4 +1,3 @@ -import asyncio import json import os import re @@ -90,6 +89,7 @@ class SettingsSection(TypedDict, total=False): title: str description: str fields: list[SettingsField] + tab: str # Indicates which tab this section belongs to class SettingsOutput(TypedDict): @@ -206,6 +206,7 @@ def convert_out(settings: Settings) -> SettingsOutput: "title": "Chat Model", "description": "Selection and settings for main chat model used by Agent Zero", "fields": chat_model_fields, + "tab": "agent", } # main model section @@ -275,6 +276,7 @@ def convert_out(settings: Settings) -> SettingsOutput: "title": "Utility model", "description": "Smaller, cheaper, faster model for handling utility tasks like organizing memory, preparing prompts, summarizing.", "fields": util_model_fields, + "tab": "agent", } # embedding model section @@ -334,6 +336,7 @@ def convert_out(settings: Settings) -> SettingsOutput: "title": "Embedding Model", "description": "Settings for the embedding model used by Agent Zero.", "fields": embed_model_fields, + "tab": "agent", } # embedding model section @@ -383,6 +386,7 @@ def convert_out(settings: Settings) -> SettingsOutput: "title": "Web Browser Model", "description": "Settings for the web browser model. Agent Zero uses browser-use agentic framework to handle web interactions.", "fields": browser_model_fields, + "tab": "agent", } # # Memory settings section @@ -447,6 +451,7 @@ def convert_out(settings: Settings) -> SettingsOutput: "title": "Authentication", "description": "Settings for authentication to use Agent Zero Web UI.", "fields": auth_fields, + "tab": "external", } # api keys model section @@ -476,6 +481,7 @@ def convert_out(settings: Settings) -> SettingsOutput: "title": "API Keys", "description": "API keys for model providers and services used by Agent Zero.", "fields": api_keys_fields, + "tab": "external", } # Agent config section @@ -528,6 +534,7 @@ def convert_out(settings: Settings) -> SettingsOutput: "title": "Agent Config", "description": "Agent parameters.", "fields": agent_fields, + "tab": "agent", } dev_fields: list[SettingsField] = [] @@ -593,6 +600,7 @@ def convert_out(settings: Settings) -> SettingsOutput: "title": "Development", "description": "Parameters for A0 framework development. RFCs (remote function calls) are used to call functions on another A0 instance. You can develop and debug A0 natively on your local system while redirecting some functions to A0 instance in docker. This is crucial for development as A0 needs to run in standardized environment to support all features.", "fields": dev_fields, + "tab": "developer", } # Speech to text section @@ -664,6 +672,7 @@ def convert_out(settings: Settings) -> SettingsOutput: "title": "Speech to Text", "description": "Voice transcription preferences and server turn detection settings.", "fields": stt_fields, + "tab": "agent", } # Add the section to the result diff --git a/python/helpers/task_scheduler.py b/python/helpers/task_scheduler.py index bda20d0d0..bce416cbe 100644 --- a/python/helpers/task_scheduler.py +++ b/python/helpers/task_scheduler.py @@ -320,15 +320,15 @@ class TaskScheduler: async def _run_task_wrapper(task: Union[ScheduledTask, AdHocTask]): if task.state != "idle": - self._printer.print(f"Scheduler Task {task.name} state is '{task.state}', skipping") + self._printer.print(f"Scheduler Task '{task.name}' state is '{task.state}', skipping") return if task.state == "running": - self._printer.print(f"Scheduler Task {task.name} already running, skipping") + self._printer.print(f"Scheduler Task '{task.name}' already running, skipping") return try: - self._printer.print(f"Scheduler Task {task.name} started") + self._printer.print(f"Scheduler Task '{task.name}' started") task.update(state="running") await self._tasks.save() @@ -374,7 +374,7 @@ class TaskScheduler: await self._persist_chat(task, context) except Exception as e: - self._printer.print(f"Scheduler Task {task.name} failed: {e}") + self._printer.print(f"Scheduler Task '{task.name}' failed: {e}") task.update(last_result=f"ERROR: {str(e)}") if agent: agent.handle_critical_exception(e) @@ -387,7 +387,7 @@ class TaskScheduler: ) await self._tasks.save() except Exception as e: - self._printer.print(f"Scheduler Task {task.name} failed to save: {e}") + self._printer.print(f"Scheduler Task '{task.name}' failed to save: {e}") deferred_task = DeferredTask(thread_name=self.__class__.__name__) deferred_task.start_task(_run_task_wrapper, task) diff --git a/webui/css/settings.css b/webui/css/settings.css index c0ed0859a..e5c1c97be 100644 --- a/webui/css/settings.css +++ b/webui/css/settings.css @@ -214,7 +214,7 @@ nav ul li a img { } } -@media (max-width: 640px) { +@media (max-width: 640px) { nav ul { grid-template-columns: repeat(2, 1.2fr); } @@ -224,17 +224,155 @@ nav ul li a img { nav ul { grid-template-columns: 1fr; } - + nav ul li a { flex-direction: row; justify-content: flex-start; gap: 1rem; padding: 0.75rem 1rem; } - + nav ul li a img { margin-bottom: 0; width: 30px; height: 30px; } } + +/* Settings Tab Styles */ +.settings-tabs-container { + width: 100%; + margin-bottom: 8px; + padding: 0; + margin-top: 20px; + position: relative; + overflow: visible; +} + +.settings-tabs { + display: flex; + width: 100%; + position: relative; + gap: 5px; + border-bottom: 3px solid var(--color-border); + justify-content: flex-start; + padding-left: 20px; + padding-top: 8px; + overflow-x: auto; + overflow-y: hidden; + scrollbar-width: none; + -ms-overflow-style: none; + white-space: nowrap; + -webkit-overflow-scrolling: touch; +} + +.settings-tabs::-webkit-scrollbar { + display: none; +} + +.settings-tabs::before, +.settings-tabs::after { + content: ''; + position: absolute; + top: 2px; + bottom: 3px; + width: 20px; + pointer-events: none; + z-index: 2; + opacity: 0.7; +} + +.settings-tabs::before { + left: 0; + background: linear-gradient(to right, var(--color-panel), transparent); +} + +.settings-tabs::after { + right: 0; + background: linear-gradient(to left, var(--color-panel), transparent); +} + +.settings-tab { + padding: 8px 16px; + cursor: pointer; + position: relative; + color: var(--color-text); + border: 2px solid var(--color-border); + border-bottom: none; + border-radius: 8px 8px 0 0; + transition: all 0.3s ease; + background-color: var(--color-panel); + margin-bottom: -3px; + z-index: 1; + min-width: min-content; + width: auto; + max-width: 100px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex-shrink: 0; +} + +.settings-tab:not(.active) { + opacity: 0.8; + border-bottom: 3px solid var(--color-border); + background-color: rgba(255, 255, 255, 0.03); +} + +.settings-tab.active { + color: var(--color-border); + border-color: var(--color-border); + box-shadow: + 0 -4px 8px -2px var(--color-border) 0.5, + 4px 0 8px -2px var(--color-border) 0.5, + -4px 0 8px -2px var(--color-border) 0.5; + font-weight: bold; + background-color: var(--color-panel); +} + +/* Light mode overrides */ +.light-mode .settings-tab.active { + color: var(--color-border); + box-shadow: + 0 -4px 8px -2px var(--color-border) 0.5, + 4px 0 8px -2px var(--color-border) 0.5, + -4px 0 8px -2px var(--color-border) 0.5; +} + +.light-mode .settings-tab:not(.active) { + background-color: rgba(0, 0, 0, 0.03); +} + +.light-mode .settings-tabs::before { + background: linear-gradient(to right, var(--color-panel-light), transparent); +} + +.light-mode .settings-tabs::after { + background: linear-gradient(to left, var(--color-panel-light), transparent); +} + +/* Responsive Design for Settings Tabs */ +@media (max-width: 640px) { + .settings-tabs { + gap: 2px; + padding-left: 10px; + } + + .settings-tab { + padding: 6px 12px; + font-size: 0.9rem; + } +} + +@media (max-width: 480px) { + .settings-tabs { + padding-left: 5px; + } + + .settings-tab { + flex: 0 0 auto; + text-align: center; + min-width: 60px; + max-width: 80px; + } +} diff --git a/webui/index.html b/webui/index.html index 29a2a5a26..99acf9526 100644 --- a/webui/index.html +++ b/webui/index.html @@ -458,10 +458,28 @@