Model Switcher UI improvements: highlight color, notification removal, preset display & validation

keyboardstaff committed Mar 16, 2026 at 23:38 UTC 5acdd4d3025841d8df6c6d9dadd6472c491a3a47
3 files changed +54 -15
plugins/_model_config/extensions/webui/chat-input-start/model-switcher.html
+49 -11
@@ -28,7 +28,6 @@
28 <template x-if="$store.modelConfig.switcherOverride">
29 <div class="model-switcher-item revert" @click="
30 $store.modelConfig.clearOverrideSwitch($store.chats?.selected || '');
31 - justToast('Reverted to default model', 'info');
31 showDropdown = false;
32 ">
33 <span class="material-symbols-outlined" style="font-size: 15px;">undo</span>
@@ -48,12 +47,31 @@
47 :class="{ 'active': $store.modelConfig.switcherOverride?.preset_name === preset.name }"
48 @click="
49 $store.modelConfig.selectPresetSwitch($store.chats?.selected || '', preset.name);
51 - justToast('Switched to ' + preset.name, 'success');
50 showDropdown = false;
51 ">
52 <div class="model-switcher-preset-name" x-text="preset.name"></div>
55 - <div class="model-switcher-preset-detail"
56 - x-text="$store.modelConfig.getPresetSummary(preset)"></div>
53 + <div class="model-switcher-preset-models">
54 + <template x-if="preset.chat?.name">
55 + <div class="model-switcher-model-row">
56 + <span class="model-switcher-model-label">Main</span>
57 + <span class="model-switcher-model-value">
58 + <span x-text="preset.chat.provider" style="opacity:0.5;"></span>
59 + <span style="opacity:0.3; margin:0 3px;">/</span>
60 + <span x-text="preset.chat.name"></span>
61 + </span>
62 + </div>
63 + </template>
64 + <template x-if="preset.utility?.name">
65 + <div class="model-switcher-model-row">
66 + <span class="model-switcher-model-label">Utility</span>
67 + <span class="model-switcher-model-value">
68 + <span x-text="preset.utility.provider" style="opacity:0.5;"></span>
69 + <span style="opacity:0.3; margin:0 3px;">/</span>
70 + <span x-text="preset.utility.name"></span>
71 + </span>
72 + </div>
73 + </template>
74 + </div>
75 </div>
76 </template>
77 </div>
@@ -95,8 +113,8 @@
113 color: var(--color-text-secondary, #666);
114 }
115 .model-switcher-btn.has-override {
98 - border-color: var(--color-accent, #4a9eff);
99 - color: var(--color-accent, #4a9eff);
116 + border-color: var(--color-highlight);
117 + color: var(--color-highlight);
118 }
119 .model-switcher-label {
120 max-width: 160px;
@@ -128,8 +146,7 @@
146 background: var(--color-background-hover, rgba(255,255,255,0.06));
147 }
148 .model-switcher-item.active {
131 - background: var(--color-accent-bg, rgba(74, 158, 255, 0.12));
132 - color: var(--color-accent, #4a9eff);
149 + background: color-mix(in srgb, var(--color-highlight) 12%, transparent);
150 }
151 .model-switcher-item.revert {
152 display: flex;
@@ -153,9 +170,30 @@
170 .model-switcher-preset-name {
171 font-weight: 500;
172 }
156 - .model-switcher-preset-detail {
173 + .model-switcher-preset-models {
174 + display: flex;
175 + flex-direction: column;
176 + gap: 2px;
177 + margin-top: 4px;
178 + }
179 + .model-switcher-model-row {
180 + display: flex;
181 + align-items: center;
182 + gap: 6px;
183 font-size: 0.7rem;
158 - opacity: 0.6;
159 - margin-top: 1px;
184 + opacity: 0.7;
185 + }
186 + .model-switcher-model-label {
187 + width: 40px;
188 + flex-shrink: 0;
189 + font-weight: 500;
190 + opacity: 0.7;
191 + }
192 + .model-switcher-model-value {
193 + flex: 1;
194 + min-width: 0;
195 + overflow: hidden;
196 + text-overflow: ellipsis;
197 + white-space: nowrap;
198 }
199 </style>
plugins/_model_config/webui/config.html
+4 -3
@@ -50,7 +50,7 @@
50 <span class="preset-card-summary" x-show="!expanded"
51 x-text="(preset.chat?.provider ? preset.chat.provider + '/' : '') + (preset.chat?.name || '')"></span>
52 <button class="text-button preset-delete-btn"
53 - @click.stop="config.model_presets = config.model_presets.filter((_, i) => i !== idx)"
53 + @click.stop="$confirmClick($event, () => { config.model_presets = config.model_presets.filter((_, i) => i !== idx) })"
54 title="Remove preset">
55 <span class="material-symbols-outlined" style="font-size:16px;">close</span>
56 </button>
@@ -62,7 +62,8 @@
62 <div class="field-title">Preset name</div>
63 <div class="field-description">Display name shown in the model switcher dropdown.</div>
64 </div>
65 - <div class="field-control"><input type="text" x-model="preset.name" placeholder="e.g. GPT-4o, Claude Sonnet" /></div>
65 + <div class="field-control"><input type="text" x-model="preset.name" placeholder="e.g. GPT-4o, Claude Sonnet"
66 + @blur="if (!preset.name.trim()) preset.name = 'Preset ' + (idx + 1)" required /></div>
67 </div>
68
69 <div class="preset-subheader">Main Model</div>
@@ -221,7 +222,7 @@
222 @click="
223 if (!config.model_presets) config.model_presets = [];
224 config.model_presets = [...config.model_presets, {
224 - name: '',
225 + name: 'Preset ' + (config.model_presets.length + 1),
226 chat: { provider: config.chat_model.provider || '', name: config.chat_model.name || '', api_key: '', api_base: config.chat_model.api_base || '' },
227 utility: { provider: '', name: '', api_key: '', api_base: '' }
228 }]">
plugins/_model_config/webui/model-config-store.js
+1 -1
@@ -175,7 +175,7 @@ export const store = createStore("modelConfig", {
175 const cfgData = await this._fetchConfigData();
176 const chatCfg = cfgData.config?.chat_model || {};
177 result.allowed = !!chatCfg.allow_chat_override;
178 - result.presets = cfgData.config?.model_presets || [];
178 + result.presets = (cfgData.config?.model_presets || []).filter(p => p.name);
179 if (!result.allowed) return result;
180 if (contextId) {
181 const overRes = await fetchApi(`${API_BASE}/model_override`, {