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