welcome screen polishing.

frdel committed Jan 31, 2026 at 17:47 UTC 1c83af88377b73ff0982dce164f3a7124acf49ea
5 files changed +130 -117
python/extensions/banners/_20_missing_api_key.py
+3 -3
@@ -54,9 +54,9 @@ class MissingApiKeyCheck(Extension):
54 "id": "missing-api-key",
55 "type": "error",
56 "priority": 100,
57 - "title": "Missing API Key",
58 - "html": f"""No API key configured for: {model_list}.
59 - Agent Zero will not be able to function properly.
57 + "title": "Missing LLM API Key for current settings",
58 + "html": f"""No API key configured for: {model_list}.<br>
59 + Agent Zero will not be able to function properly unless you provide an API key or change your settings.<br>
60 <a href="#" onclick="document.getElementById('settings').click(); return false;">
61 Add your API key</a> in Settings → External Services → API Keys.""",
62 "dismissible": False,
webui/components/sidebar/chats/chats-store.js
+10 -7
@@ -27,11 +27,10 @@ const model = {
27 },
28
29 init() {
30 - // Initialize from localStorage
31 - const lastSelectedChat = localStorage.getItem("lastSelectedChat");
30 + // Initialize from sessionStorage
31 + const lastSelectedChat = sessionStorage.getItem("lastSelectedChat");
32 if (lastSelectedChat) {
33 this.selectChat(lastSelectedChat);
34 - // this.selected = lastSelectedChat;
34 }
35 },
36
@@ -260,11 +259,15 @@ const model = {
259
260 // Set selected context
261 setSelected(contextId) {
263 - this.selected = contextId;
264 - this.selectedContext = this.contexts.find((ctx) => ctx.id === contextId);
262 + this.selected = contextId || "";
263 + this.selectedContext = this.contexts.find((ctx) => ctx.id === this.selected);
264 // if not found in contexts, try to find in tasks < not nice, will need refactor later
266 - if(!this.selectedContext) this.selectedContext = tasksStore.tasks.find((ctx) => ctx.id === contextId);
267 - localStorage.setItem("lastSelectedChat", contextId);
265 + if(!this.selectedContext) this.selectedContext = tasksStore.tasks.find((ctx) => ctx.id === this.selected);
266 + if (this.selected) {
267 + sessionStorage.setItem("lastSelectedChat", this.selected);
268 + } else {
269 + sessionStorage.removeItem("lastSelectedChat");
270 + }
271 },
272
273 // Restart the backend
webui/components/welcome/welcome-screen.html
+106 -92
@@ -10,88 +10,71 @@
10 <div x-data x-show="$store.welcomeStore && $store.welcomeStore.isVisible">
11 <template x-if="$store.welcomeStore">
12 <div x-data="$store.welcomeStore" class="welcome-container">
13 - <!-- Agent Zero Logo -->
14 - <div class="welcome-logo-container">
15 - <img src="./public/darkSymbol.svg" alt="Agent Zero Logo" class="welcome-logo" />
16 - </div>
13 + <div class="welcome-content">
14 + <!-- Action Cards -->
15 + <div class="welcome-actions">
16 + <div class="welcome-action-card" @click="executeAction('new-chat')">
17 + <span class="material-symbols-outlined welcome-action-icon">add_circle</span>
18 + <h3 class="welcome-action-title">New Chat</h3>
19 + </div>
20
18 - <!-- Welcome Title -->
19 - <h1 class="welcome-title">Welcome to Agent Zero</h1>
20 - <p class="welcome-subtitle">
21 - Start a new conversation or explore the features below.
22 - </p>
23 -
24 - <!-- Action Cards -->
25 - <div class="welcome-actions">
26 - <div class="welcome-action-card" @click="executeAction('new-chat')">
27 - <span class="material-symbols-outlined welcome-action-icon">add_circle</span>
28 - <h3 class="welcome-action-title">New Chat</h3>
29 - <p class="welcome-action-description">
30 - Start a new conversation
31 - </p>
32 - </div>
21 + <div class="welcome-action-card" @click="executeAction('projects')">
22 + <span class="material-symbols-outlined welcome-action-icon">folder</span>
23 + <h3 class="welcome-action-title">Projects</h3>
24 + </div>
25 + <div class="welcome-action-card" @click="executeAction('memory')">
26 + <span class="material-symbols-outlined welcome-action-icon">memory</span>
27 + <h3 class="welcome-action-title">Memory</h3>
28 + </div>
29
34 - <div class="welcome-action-card" @click="executeAction('projects')">
35 - <span class="material-symbols-outlined welcome-action-icon">folder</span>
36 - <h3 class="welcome-action-title">Projects</h3>
37 - <p class="welcome-action-description">
38 - Manage your projects
39 - </p>
40 - </div>
41 - <div class="welcome-action-card" @click="executeAction('memory')">
42 - <span class="material-symbols-outlined welcome-action-icon">memory</span>
43 - <h3 class="welcome-action-title">Memory</h3>
44 - <p class="welcome-action-description">
45 - Open Memory Dashboard
46 - </p>
47 - </div>
48 - <div class="welcome-action-card" @click="executeAction('settings')">
49 - <span class="material-symbols-outlined welcome-action-icon">settings</span>
50 - <h3 class="welcome-action-title">Settings</h3>
51 - <p class="welcome-action-description">
52 - Configure Agent Zero
53 - </p>
54 - </div>
55 - <div class="welcome-action-card" @click="executeAction('website')">
56 - <span class="material-symbols-outlined welcome-action-icon">language</span>
57 - <h3 class="welcome-action-title">Visit Website</h3>
58 - <p class="welcome-action-description">
59 - Learn more about Agent Zero
60 - </p>
61 - </div>
62 - <div class="welcome-action-card" @click="executeAction('github')">
63 - <span class="material-symbols-outlined welcome-action-icon">code</span>
64 - <h3 class="welcome-action-title">Visit GitHub</h3>
65 - <p class="welcome-action-description">
66 - View source code and documentation
67 - </p>
30 + <div class="welcome-action-card" @click="executeAction('scheduler')">
31 + <span class="material-symbols-outlined welcome-action-icon">schedule</span>
32 + <h3 class="welcome-action-title">Scheduler</h3>
33 + </div>
34 + <div class="welcome-action-card" @click="executeAction('settings')">
35 + <span class="material-symbols-outlined welcome-action-icon">settings</span>
36 + <h3 class="welcome-action-title">Settings</h3>
37 + </div>
38 +
39 + <div class="welcome-action-card" @click="executeAction('files')">
40 + <span class="material-symbols-outlined welcome-action-icon">folder_open</span>
41 + <h3 class="welcome-action-title">Files</h3>
42 + </div>
43 + <div class="welcome-action-card" @click="executeAction('website')">
44 + <span class="material-symbols-outlined welcome-action-icon">language</span>
45 + <h3 class="welcome-action-title">Visit Website</h3>
46 + </div>
47 + <div class="welcome-action-card" @click="executeAction('github')">
48 + <span class="material-symbols-outlined welcome-action-icon">code</span>
49 + <h3 class="welcome-action-title">Visit GitHub</h3>
50 + </div>
51 </div>
69 - </div>
52
71 - <!-- Banner Section -->
72 - <div class="welcome-banners" x-show="banners && banners.length > 0">
73 - <template x-for="banner in sortedBanners" :key="banner.id">
74 - <div class="welcome-banner" :class="getBannerClass(banner.type)">
75 - <!-- Banner Icon -->
76 - <div class="welcome-banner-icon">
77 - <span class="material-symbols-outlined" x-text="getBannerIcon(banner.type)"></span>
78 - </div>
79 -
80 - <!-- Banner Content -->
81 - <div class="welcome-banner-content">
82 - <div class="welcome-banner-title" x-text="banner.title"></div>
83 - <div class="welcome-banner-html" x-html="banner.html"></div>
53 + <!-- Banner Section -->
54 + <div class="welcome-banners" x-show="banners && banners.length > 0">
55 + <template x-for="banner in sortedBanners" :key="banner.id">
56 + <div class="welcome-banner" :class="getBannerClass(banner.type)">
57 + <!-- Banner Icon -->
58 + <div class="welcome-banner-icon">
59 + <span class="material-symbols-outlined" x-text="getBannerIcon(banner.type)"></span>
60 + </div>
61 +
62 + <!-- Banner Content -->
63 + <div class="welcome-banner-content">
64 + <div class="welcome-banner-title" x-text="banner.title"></div>
65 + <div class="welcome-banner-html" x-html="banner.html"></div>
66 + </div>
67 +
68 + <!-- Dismiss Button -->
69 + <button class="welcome-banner-dismiss"
70 + x-show="banner.dismissible !== false"
71 + @click.stop="dismissBanner(banner.id)"
72 + title="Dismiss">
73 + <span class="material-symbols-outlined">close</span>
74 + </button>
75 </div>
85 -
86 - <!-- Dismiss Button -->
87 - <button class="welcome-banner-dismiss"
88 - x-show="banner.dismissible !== false"
89 - @click.stop="dismissBanner(banner.id)"
90 - title="Dismiss">
91 - <span class="material-symbols-outlined">close</span>
92 - </button>
93 - </div>
94 - </template>
76 + </template>
77 + </div>
78 </div>
79 </div>
80 </template>
@@ -109,6 +92,39 @@
92 background: var(--color-background);
93 color: var(--color-text);
94 min-height: 100vh;
95 + position: relative;
96 + overflow: hidden;
97 + }
98 +
99 + .welcome-container::before {
100 + content: "";
101 + position: absolute;
102 + inset: 0;
103 + background-image: url("./public/darkSymbol.svg");
104 + background-repeat: no-repeat;
105 + background-position: center;
106 + background-size: 80% auto;
107 + opacity: 0.07;
108 + pointer-events: none;
109 + }
110 +
111 + .light-mode .welcome-container::before {
112 + filter: invert(0.55);
113 + opacity: 0.06;
114 + }
115 +
116 + .dark-mode .welcome-container::before {
117 + filter: invert(0.85);
118 + opacity: 0.08;
119 + }
120 +
121 + .welcome-content {
122 + position: relative;
123 + z-index: 1;
124 + width: 100%;
125 + display: flex;
126 + flex-direction: column;
127 + align-items: center;
128 }
129
130 /* Welcome container styles */
@@ -153,9 +169,9 @@
169
170 .welcome-actions {
171 display: grid;
156 - grid-template-columns: repeat(3, 1fr);
157 - gap: 1.2rem;
158 - max-width: 520px;
172 + grid-template-columns: repeat(4, 1fr);
173 + gap: 1rem;
174 + max-width: 640px;
175 width: 100%;
176 margin: 1.5rem 0;
177 }
@@ -164,7 +180,7 @@
180 background: var(--color-panel);
181 border: 1px solid var(--color-border);
182 border-radius: 12px;
167 - padding: 1.5rem;
183 + padding: 0.6rem;
184 transition: all 0.3s ease;
185 cursor: pointer;
186 text-decoration: none;
@@ -173,8 +189,8 @@
189 flex-direction: column;
190 align-items: center;
191 text-align: center;
176 - min-height: 10rem;
192 justify-content: center;
193 + aspect-ratio: 1 / 1;
194 }
195
196 .welcome-action-card:hover {
@@ -183,15 +199,13 @@
199 }
200
201 .welcome-action-icon {
186 - font-size: 2rem;
187 - margin-bottom: 0;
202 + font-size: 2.1rem;
203 + margin-bottom: 0.25rem;
204 color: var(--color-highlight-dark);
205 }
206
191 - .welcome-action-description {
192 - font-size: 0.8rem;
193 - color: var(--color-secondary);
194 - line-height: 1.3;
207 + .welcome-action-title {
208 + font-size: 0.9rem;
209 }
210
211 /* Banner Styles */
@@ -199,7 +213,7 @@
213 display: flex;
214 flex-direction: column;
215 gap: 0.75rem;
202 - max-width: 520px;
216 + max-width: 680px;
217 width: 100%;
218 margin-top: 1.5rem;
219 }
@@ -383,14 +397,14 @@
397 }
398
399 .welcome-actions {
386 - grid-template-columns: 1fr;
400 + grid-template-columns: repeat(2, 1fr);
401 gap: 1rem;
402 margin-bottom: 2rem;
403 }
404
405 .welcome-action-card {
392 - padding: 1.5rem;
393 - min-height: 120px;
406 + padding: 1rem;
407 + aspect-ratio: 1 / 1;
408 }
409
410 .welcome-action-icon {
webui/components/welcome/welcome-store.js
+7
@@ -3,6 +3,7 @@ import { getContext } from "/index.js";
3 import { store as chatsStore } from "/components/sidebar/chats/chats-store.js";
4 import { store as memoryStore } from "/components/modals/memory/memory-dashboard-store.js";
5 import { store as projectsStore } from "/components/projects/projects-store.js";
6 +import { store as chatInputStore } from "/components/chat/input/input-store.js";
7 import * as API from "/js/api.js";
8
9 const model = {
@@ -176,6 +177,9 @@ const model = {
177 case "new-chat":
178 chatsStore.newChat();
179 break;
180 + case "scheduler":
181 + window.openModal("modals/scheduler/scheduler-modal.html");
182 + break;
183 case "settings":
184 // Open settings modal
185 const settingsButton = document.getElementById("settings");
@@ -189,6 +193,9 @@ const model = {
193 case "memory":
194 memoryStore.openModal();
195 break;
196 + case "files":
197 + chatInputStore.browseFiles();
198 + break;
199 case "website":
200 window.open("https://agent-zero.ai", "_blank");
201 break;
webui/index.js
+4 -15
@@ -387,20 +387,7 @@ export async function poll() {
387 }
388 }
389 } else {
390 - const welcomeStore =
391 - globalThis.Alpine && typeof globalThis.Alpine.store === "function"
392 - ? globalThis.Alpine.store("welcomeStore")
393 - : null;
394 - const welcomeVisible = Boolean(welcomeStore && welcomeStore.isVisible);
395 -
396 - // No context selected, try to select the first available item unless welcome screen is active
397 - if (!welcomeVisible && contexts.length > 0) {
398 - const firstChatId = chatsStore.firstId();
399 - if (firstChatId) {
400 - setContext(firstChatId);
401 - chatsStore.setSelected(firstChatId);
402 - }
403 - }
390 + // No context selected: keep it that way so the welcome screen stays visible.
391 }
392
393 lastLogVersion = response.log_version;
@@ -528,7 +515,9 @@ export const deselectChat = function () {
515 // Clear current context to show welcome screen
516 setContext(null);
517
531 - // Clear localStorage selections so we don't auto-restore
518 + // Clear selections so we don't auto-restore
519 + sessionStorage.removeItem("lastSelectedChat");
520 + sessionStorage.removeItem("lastSelectedTask");
521 localStorage.removeItem("lastSelectedChat");
522 localStorage.removeItem("lastSelectedTask");
523