auto refresh welcome screen on settings change
frdel committed
Feb 10, 2026 at 12:17 UTC
69cb4f131a43b35d618eaa085738160aced8f67d
3 files changed
+62
-63
python/helpers/settings.py
+3
-3
@@ -490,7 +490,7 @@ def get_default_settings() -> Settings:
490
return Settings(
491
version=_get_version(),
492
chat_model_provider=get_default_value("chat_model_provider", "openrouter"),
493
- chat_model_name=get_default_value("chat_model_name", "moonshotai/kimi-k2.5"),
493
+ chat_model_name=get_default_value("chat_model_name", "anthropic/claude-sonnet-4.5"),
494
chat_model_api_base=get_default_value("chat_model_api_base", ""),
495
chat_model_kwargs=get_default_value("chat_model_kwargs", {"temperature": "0"}),
496
chat_model_ctx_length=get_default_value("chat_model_ctx_length", 100000),
@@ -500,7 +500,7 @@ def get_default_settings() -> Settings:
500
chat_model_rl_input=get_default_value("chat_model_rl_input", 0),
501
chat_model_rl_output=get_default_value("chat_model_rl_output", 0),
502
util_model_provider=get_default_value("util_model_provider", "openrouter"),
503
- util_model_name=get_default_value("util_model_name", "z-ai/glm-4.7-flash"),
503
+ util_model_name=get_default_value("util_model_name", "google/gemini-3-flash-preview"),
504
util_model_api_base=get_default_value("util_model_api_base", ""),
505
util_model_ctx_length=get_default_value("util_model_ctx_length", 100000),
506
util_model_ctx_input=get_default_value("util_model_ctx_input", 0.7),
@@ -515,7 +515,7 @@ def get_default_settings() -> Settings:
515
embed_model_rl_requests=get_default_value("embed_model_rl_requests", 0),
516
embed_model_rl_input=get_default_value("embed_model_rl_input", 0),
517
browser_model_provider=get_default_value("browser_model_provider", "openrouter"),
518
- browser_model_name=get_default_value("browser_model_name", "moonshotai/kimi-k2.5"),
518
+ browser_model_name=get_default_value("browser_model_name", "anthropic/claude-sonnet-4.5"),
519
browser_model_api_base=get_default_value("browser_model_api_base", ""),
520
browser_model_vision=get_default_value("browser_model_vision", True),
521
browser_model_rl_requests=get_default_value("browser_model_rl_requests", 0),
webui/components/welcome/welcome-screen.html
+2
-2
@@ -7,9 +7,9 @@
7
</head>
8
9
<body>
10
- <div x-data x-show="$store.welcomeStore && $store.welcomeStore.isVisible">
10
+ <div x-data >
11
<template x-if="$store.welcomeStore">
12
- <div x-data="$store.welcomeStore" class="welcome-container">
12
+ <div x-data="$store.welcomeStore" x-create="$store.welcomeStore.onCreate()" class="welcome-container">
13
<div class="welcome-content">
14
<!-- Action Cards -->
15
<div class="welcome-actions">
webui/components/welcome/welcome-store.js
+57
-58
@@ -8,48 +8,28 @@ import * as API from "/js/api.js";
8
9
const model = {
10
// State
11
- isVisible: true,
11
banners: [],
12
bannersLoading: false,
13
lastBannerRefresh: 0,
14
hasDismissedBanners: false,
15
17
- init() {
18
- // Initialize visibility based on current context
19
- this.updateVisibility();
20
-
21
- if (this.isVisible) {
22
- this.refreshBanners();
23
- }
24
-
25
- // Watch for context changes with faster polling for immediate response
26
- setInterval(() => {
27
- this.updateVisibility();
28
- }, 50); // 50ms for very responsive updates
16
+ get isVisible() {
17
+ return !chatsStore.selected;
18
},
19
31
- // Update visibility based on current context
32
- updateVisibility() {
33
- const hasContext = !!getContext();
34
- const wasVisible = this.isVisible;
35
- this.isVisible = !hasContext;
20
+ init() {
21
+ // Reload banners when settings change
22
+ document.addEventListener("settings-updated", () => {
23
+ this.refreshBanners(true);
24
+ });
25
+ },
26
37
- if (this.isVisible && !wasVisible) {
27
+ onCreate() {
28
+ if (this.isVisible) {
29
this.refreshBanners();
30
}
31
},
32
42
- // Hide welcome screen
43
- hide() {
44
- this.isVisible = false;
45
- },
46
-
47
- // Show welcome screen
48
- show() {
49
- this.isVisible = true;
50
- this.refreshBanners();
51
- },
52
-
33
// Build frontend context to send to backend
34
buildFrontendContext() {
35
return {
@@ -83,8 +63,12 @@ const model = {
63
64
// Get list of dismissed banner IDs from storage
65
getDismissedBannerIds() {
86
- const permanent = JSON.parse(localStorage.getItem('dismissed_banners') || '[]');
87
- const temporary = JSON.parse(sessionStorage.getItem('dismissed_banners') || '[]');
66
+ const permanent = JSON.parse(
67
+ localStorage.getItem("dismissed_banners") || "[]",
68
+ );
69
+ const temporary = JSON.parse(
70
+ sessionStorage.getItem("dismissed_banners") || "[]",
71
+ );
72
return new Set([...permanent, ...temporary]);
73
},
74
@@ -92,19 +76,27 @@ const model = {
76
mergeBanners(frontendBanners, backendBanners) {
77
const dismissed = this.getDismissedBannerIds();
78
const bannerMap = new Map();
95
-
79
+
80
for (const banner of frontendBanners) {
97
- if (banner.id && (banner.dismissible === false || !dismissed.has(banner.id))) {
81
+ if (
82
+ banner.id &&
83
+ (banner.dismissible === false || !dismissed.has(banner.id))
84
+ ) {
85
bannerMap.set(banner.id, banner);
86
}
87
}
88
for (const banner of backendBanners) {
102
- if (banner.id && (banner.dismissible === false || !dismissed.has(banner.id))) {
89
+ if (
90
+ banner.id &&
91
+ (banner.dismissible === false || !dismissed.has(banner.id))
92
+ ) {
93
bannerMap.set(banner.id, banner);
94
}
95
}
106
-
107
- return Array.from(bannerMap.values()).sort((a, b) => (b.priority || 0) - (a.priority || 0));
96
+
97
+ return Array.from(bannerMap.values()).sort(
98
+ (a, b) => (b.priority || 0) - (a.priority || 0),
99
+ );
100
},
101
102
// Refresh banners: frontend checks → backend checks → merge
@@ -113,19 +105,24 @@ const model = {
105
if (!force && now - this.lastBannerRefresh < 1000) return;
106
this.lastBannerRefresh = now;
107
this.bannersLoading = true;
116
-
108
+
109
try {
110
const frontendContext = this.buildFrontendContext();
111
const frontendBanners = this.runFrontendBannerChecks();
120
- const backendBanners = await this.runBackendBannerChecks(frontendBanners, frontendContext);
112
+ const backendBanners = await this.runBackendBannerChecks(
113
+ frontendBanners,
114
+ frontendContext,
115
+ );
116
117
const dismissed = this.getDismissedBannerIds();
118
const loadIds = new Set(
119
[...frontendBanners, ...backendBanners]
125
- .filter(b => b?.id && b.dismissible !== false)
126
- .map(b => b.id)
120
+ .filter((b) => b?.id && b.dismissible !== false)
121
+ .map((b) => b.id),
122
+ );
123
+ this.hasDismissedBanners = Array.from(loadIds).some((id) =>
124
+ dismissed.has(id),
125
);
128
- this.hasDismissedBanners = Array.from(loadIds).some(id => dismissed.has(id));
126
127
this.banners = this.mergeBanners(frontendBanners, backendBanners);
128
} catch (error) {
@@ -138,29 +135,31 @@ const model = {
135
},
136
137
get sortedBanners() {
141
- return [...this.banners].sort((a, b) => (b.priority || 0) - (a.priority || 0));
138
+ return [...this.banners].sort(
139
+ (a, b) => (b.priority || 0) - (a.priority || 0),
140
+ );
141
},
142
143
/**
144
* Dismiss a banner by ID.
146
- *
145
+ *
146
* Usage:
147
* dismissBanner('banner-id') - Temporary dismiss (sessionStorage, cleared on browser close)
148
* dismissBanner('banner-id', true) - Permanent dismiss (localStorage, persists across sessions)
150
- *
149
+ *
150
* Dismissed banners are filtered out in mergeBanners() and won't appear until storage is cleared.
152
- *
151
+ *
152
* @param {string} bannerId - The unique ID of the banner to dismiss
153
* @param {boolean} permanent - If true, store in localStorage; if false, store in sessionStorage
154
*/
155
dismissBanner(bannerId, permanent = false) {
157
- this.banners = this.banners.filter(b => b.id !== bannerId);
158
-
156
+ this.banners = this.banners.filter((b) => b.id !== bannerId);
157
+
158
const storage = permanent ? localStorage : sessionStorage;
160
- const dismissed = JSON.parse(storage.getItem('dismissed_banners') || '[]');
159
+ const dismissed = JSON.parse(storage.getItem("dismissed_banners") || "[]");
160
if (!dismissed.includes(bannerId)) {
161
dismissed.push(bannerId);
163
- storage.setItem('dismissed_banners', JSON.stringify(dismissed));
162
+ storage.setItem("dismissed_banners", JSON.stringify(dismissed));
163
}
164
165
this.hasDismissedBanners = this.getDismissedBannerIds().size > 0;
@@ -175,20 +174,20 @@ const model = {
174
175
getBannerClass(type) {
176
const classes = {
178
- info: 'banner-info',
179
- warning: 'banner-warning',
180
- error: 'banner-error',
177
+ info: "banner-info",
178
+ warning: "banner-warning",
179
+ error: "banner-error",
180
};
182
- return classes[type] || 'banner-info';
181
+ return classes[type] || "banner-info";
182
},
183
184
getBannerIcon(type) {
185
const icons = {
187
- info: 'info',
188
- warning: 'warning',
189
- error: 'error',
186
+ info: "info",
187
+ warning: "warning",
188
+ error: "error",
189
};
191
- return icons[type] || 'info';
190
+ return icons[type] || "info";
191
},
192
193
// Execute an action by ID