ui: show model API keys in main model settings form
Move API key input fields out of the collapsed Advanced Settings section in model field UI so keys are always visible when configuring chat/utility/embedding models. Keep existing API key behaviors (visibility toggle, reveal-on-show, dirty-state tracking, and persistence flow) unchanged while removing the extra click required to access keys.
Alessandro committed
Apr 21, 2026 at 06:10 UTC
cc05ece827bf038d2a8b5a32163e60757eca66b2
1 file changed
+53
-54
plugins/_model_config/webui/model-field.html
+53
-54
@@ -71,6 +71,59 @@
71
</div>
72
</div>
73
74
+ <!-- API key (store mode: config.html) -->
75
+ <template x-if="apiKeyMode === 'store'">
76
+ <div class="field">
77
+ <div class="field-label">
78
+ <div class="field-title">API key</div>
79
+ <div class="field-description">Authentication key for this provider. Shared across all model slots using the same provider.</div>
80
+ </div>
81
+ <div class="field-control" style="position:relative;" x-data="{ showKey: false }">
82
+ <input :type="showKey ? 'text' : 'password'"
83
+ :value="$store.modelConfig.apiKeyValues[_prov]"
84
+ :placeholder="$store.modelConfig.apiKeyStatus[_prov] ? '••••••••••••' : ''"
85
+ autocomplete="off"
86
+ @input="$store.modelConfig.setApiKeyValue(_prov, $el.value)"
87
+ style="padding-right:32px;" />
88
+ <span class="material-symbols-outlined eye-toggle"
89
+ @click="
90
+ showKey = !showKey;
91
+ const prov = _prov;
92
+ if (showKey && !$store.modelConfig.apiKeyValues[prov] && $store.modelConfig.apiKeyStatus[prov]) {
93
+ $store.modelConfig.revealApiKey(prov).then(v => { if (v) $store.modelConfig.apiKeyValues[prov] = v; });
94
+ }
95
+ "
96
+ x-text="showKey ? 'visibility' : 'visibility_off'"></span>
97
+ </div>
98
+ </div>
99
+ </template>
100
+
101
+ <!-- API key (inline mode: presets) -->
102
+ <template x-if="apiKeyMode === 'inline'">
103
+ <div class="field">
104
+ <div class="field-label">
105
+ <div class="field-title">API key</div>
106
+ <div class="field-description">Authentication key for this provider. Shared across all model slots using the same provider.</div>
107
+ </div>
108
+ <div class="field-control" style="position:relative;" x-data="{ showKey: false, _revealed: '' }">
109
+ <input :type="showKey ? 'text' : 'password'" x-model="model.api_key" autocomplete="off"
110
+ :placeholder="$store.modelConfig.apiKeyStatus[_prov] ? '••••••••••••' : ''"
111
+ style="padding-right:32px;" />
112
+ <span class="material-symbols-outlined eye-toggle"
113
+ @click="
114
+ showKey = !showKey;
115
+ if (showKey && !model.api_key && $store.modelConfig.apiKeyStatus[_prov]) {
116
+ $store.modelConfig.revealApiKey(_prov).then(v => { if (v) { model.api_key = v; _revealed = v; } });
117
+ }
118
+ if (!showKey && _revealed && model.api_key === _revealed) {
119
+ model.api_key = ''; _revealed = '';
120
+ }
121
+ "
122
+ x-text="showKey ? 'visibility' : 'visibility_off'"></span>
123
+ </div>
124
+ </div>
125
+ </template>
126
+
127
<!-- Advanced Settings (collapsed by default) -->
128
<div class="advanced-section" x-data="{ advOpen: false }">
129
<div class="advanced-toggle" @click="advOpen = !advOpen">
@@ -81,60 +134,6 @@
134
</div>
135
136
<div class="advanced-body" x-show="advOpen" x-transition.opacity>
84
-
85
- <!-- API key (store mode: config.html) -->
86
- <template x-if="apiKeyMode === 'store'">
87
- <div class="field">
88
- <div class="field-label">
89
- <div class="field-title">API key</div>
90
- <div class="field-description">Authentication key for this provider. Shared across all model slots using the same provider.</div>
91
- </div>
92
- <div class="field-control" style="position:relative;" x-data="{ showKey: false }">
93
- <input :type="showKey ? 'text' : 'password'"
94
- :value="$store.modelConfig.apiKeyValues[_prov]"
95
- :placeholder="$store.modelConfig.apiKeyStatus[_prov] ? '••••••••••••' : ''"
96
- autocomplete="off"
97
- @input="$store.modelConfig.setApiKeyValue(_prov, $el.value)"
98
- style="padding-right:32px;" />
99
- <span class="material-symbols-outlined eye-toggle"
100
- @click="
101
- showKey = !showKey;
102
- const prov = _prov;
103
- if (showKey && !$store.modelConfig.apiKeyValues[prov] && $store.modelConfig.apiKeyStatus[prov]) {
104
- $store.modelConfig.revealApiKey(prov).then(v => { if (v) $store.modelConfig.apiKeyValues[prov] = v; });
105
- }
106
- "
107
- x-text="showKey ? 'visibility' : 'visibility_off'"></span>
108
- </div>
109
- </div>
110
- </template>
111
-
112
- <!-- API key (inline mode: presets) -->
113
- <template x-if="apiKeyMode === 'inline'">
114
- <div class="field">
115
- <div class="field-label">
116
- <div class="field-title">API key</div>
117
- <div class="field-description">Authentication key for this provider. Shared across all model slots using the same provider.</div>
118
- </div>
119
- <div class="field-control" style="position:relative;" x-data="{ showKey: false, _revealed: '' }">
120
- <input :type="showKey ? 'text' : 'password'" x-model="model.api_key" autocomplete="off"
121
- :placeholder="$store.modelConfig.apiKeyStatus[_prov] ? '••••••••••••' : ''"
122
- style="padding-right:32px;" />
123
- <span class="material-symbols-outlined eye-toggle"
124
- @click="
125
- showKey = !showKey;
126
- if (showKey && !model.api_key && $store.modelConfig.apiKeyStatus[_prov]) {
127
- $store.modelConfig.revealApiKey(_prov).then(v => { if (v) { model.api_key = v; _revealed = v; } });
128
- }
129
- if (!showKey && _revealed && model.api_key === _revealed) {
130
- model.api_key = ''; _revealed = '';
131
- }
132
- "
133
- x-text="showKey ? 'visibility' : 'visibility_off'"></span>
134
- </div>
135
- </div>
136
- </template>
137
-
137
<!-- API base URL -->
138
<div class="field">
139
<div class="field-label">