feat(ui): add custom model ID input to model picker dropdown (fixes #444)

This commit is contained in:
Hermes Agent
2026-04-14 20:56:56 +00:00
parent 7b0fb246ee
commit 12949a2771
3 changed files with 27 additions and 0 deletions

View File

@@ -50,6 +50,8 @@ const LOCALES = {
model_unavailable_title: 'This model is no longer in your current provider list', model_unavailable_title: 'This model is no longer in your current provider list',
provider_mismatch_warning: (m,p)=>`"${m}" may not work with your configured provider (${p}). Send anyway, or run \`hermes model\` in your terminal to switch.`, provider_mismatch_warning: (m,p)=>`"${m}" may not work with your configured provider (${p}). Send anyway, or run \`hermes model\` in your terminal to switch.`,
provider_mismatch_label: 'Provider mismatch', provider_mismatch_label: 'Provider mismatch',
model_custom_label: 'Custom model ID',
model_custom_placeholder: 'e.g. openai/gpt-5.4',
// commands.js // commands.js
cmd_help: 'List available commands', cmd_help: 'List available commands',
cmd_clear: 'Clear conversation messages', cmd_clear: 'Clear conversation messages',
@@ -456,6 +458,8 @@ const LOCALES = {
model_unavailable_title: 'Este modelo ya no está en tu lista actual de proveedores', model_unavailable_title: 'Este modelo ya no está en tu lista actual de proveedores',
provider_mismatch_warning: (m,p)=>`"${m}" puede no funcionar con tu proveedor configurado (${p}). Envía de todas formas, o ejecuta \`hermes model\` en la terminal para cambiar.`, provider_mismatch_warning: (m,p)=>`"${m}" puede no funcionar con tu proveedor configurado (${p}). Envía de todas formas, o ejecuta \`hermes model\` en la terminal para cambiar.`,
provider_mismatch_label: 'Proveedor incompatible', provider_mismatch_label: 'Proveedor incompatible',
model_custom_label: 'ID de modelo personalizado',
model_custom_placeholder: 'p. ej. openai/gpt-5.4',
// commands.js // commands.js
cmd_help: 'Listar los comandos disponibles', cmd_help: 'Listar los comandos disponibles',
cmd_clear: 'Borrar los mensajes de la conversación', cmd_clear: 'Borrar los mensajes de la conversación',
@@ -1058,6 +1062,8 @@ const LOCALES = {
model_unavailable_title: '\u8fd9\u4e2a\u6a21\u578b\u5df2\u7ecf\u4e0d\u5728\u5f53\u524d provider \u5217\u8868\u4e2d', model_unavailable_title: '\u8fd9\u4e2a\u6a21\u578b\u5df2\u7ecf\u4e0d\u5728\u5f53\u524d provider \u5217\u8868\u4e2d',
provider_mismatch_warning: (m,p)=>`\"${m}\" \u53ef\u80fd\u65e0\u6cd5\u5728\u5f53\u524d\u914d\u7f6e\u7684\u63d0\u4f9b\u5546 (${p}) \u4e0b\u5de5\u4f5c\u3002\u76f4\u63a5\u53d1\u9001\uff0c\u6216\u5728\u7ec8\u7aef\u8fd0\u884c \`hermes model\` \u5207\u6362\u3002`, provider_mismatch_warning: (m,p)=>`\"${m}\" \u53ef\u80fd\u65e0\u6cd5\u5728\u5f53\u524d\u914d\u7f6e\u7684\u63d0\u4f9b\u5546 (${p}) \u4e0b\u5de5\u4f5c\u3002\u76f4\u63a5\u53d1\u9001\uff0c\u6216\u5728\u7ec8\u7aef\u8fd0\u884c \`hermes model\` \u5207\u6362\u3002`,
provider_mismatch_label: '\u63d0\u4f9b\u5546\u4e0d\u5339\u914d', provider_mismatch_label: '\u63d0\u4f9b\u5546\u4e0d\u5339\u914d',
model_custom_label: '\u81ea\u5b9a\u4e49\u6a21\u578b ID',
model_custom_placeholder: '\u4f8b\u5982 openai/gpt-5.4',
// commands.js // commands.js
cmd_help: '\u67e5\u770b\u53ef\u7528\u547d\u4ee4', cmd_help: '\u67e5\u770b\u53ef\u7528\u547d\u4ee4',
cmd_clear: '\u6e05\u7a7a\u5f53\u524d\u5bf9\u8bdd\u6d88\u606f', cmd_clear: '\u6e05\u7a7a\u5f53\u524d\u5bf9\u8bdd\u6d88\u606f',

View File

@@ -719,6 +719,12 @@
.model-opt.active{background:rgba(124,185,255,.1);} .model-opt.active{background:rgba(124,185,255,.1);}
.model-opt-name{display:block;font-size:13px;color:var(--text);font-weight:500;line-height:1.25;} .model-opt-name{display:block;font-size:13px;color:var(--text);font-weight:500;line-height:1.25;}
.model-opt-id{display:block;font-size:10px;color:var(--muted);line-height:1.3;opacity:.72;word-break:break-word;} .model-opt-id{display:block;font-size:10px;color:var(--muted);line-height:1.3;opacity:.72;word-break:break-word;}
.model-custom-sep{padding-top:4px;border-top:1px solid var(--border);margin-top:4px;}
.model-custom-row{display:flex;align-items:center;gap:6px;padding:6px 10px 8px;}
.model-custom-input{flex:1;background:var(--code-bg);border:1px solid var(--border2);border-radius:6px;color:var(--text);padding:5px 8px;font-size:12px;outline:none;font-family:inherit;min-width:0;}
.model-custom-input:focus{border-color:rgba(124,185,255,.5);}
.model-custom-btn{flex-shrink:0;width:24px;height:24px;border:1px solid var(--border2);border-radius:6px;background:transparent;color:var(--muted);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;transition:color .12s,border-color .12s;}
.model-custom-btn:hover{color:var(--blue);border-color:rgba(124,185,255,.4);}
.ws-opt{padding:10px 14px;cursor:pointer;transition:background .12s;display:flex;flex-direction:column;gap:4px;align-items:flex-start;} .ws-opt{padding:10px 14px;cursor:pointer;transition:background .12s;display:flex;flex-direction:column;gap:4px;align-items:flex-start;}
.ws-opt:hover{background:rgba(255,255,255,.07);} .ws-opt:hover{background:rgba(255,255,255,.07);}
.ws-opt.active{background:rgba(124,185,255,.1);} .ws-opt.active{background:rgba(124,185,255,.1);}

View File

@@ -238,6 +238,21 @@ function renderModelDropdown(){
dd.appendChild(row); dd.appendChild(row);
} }
} }
// Custom model ID input — lets users type any model not in the curated list
const _custSep=document.createElement('div');
_custSep.className='model-group model-custom-sep';
_custSep.textContent=t('model_custom_label')||'Custom model ID';
dd.appendChild(_custSep);
const _custRow=document.createElement('div');
_custRow.className='model-custom-row';
_custRow.innerHTML=`<input class="model-custom-input" type="text" placeholder="${esc(t('model_custom_placeholder')||'e.g. openai/gpt-5.4')}" spellcheck="false" autocomplete="off"><button class="model-custom-btn" title="Use this model">${li('plus',12)}</button>`;
const _ci=_custRow.querySelector('.model-custom-input');
const _cb=_custRow.querySelector('.model-custom-btn');
const _applyCustom=()=>{const v=_ci.value.trim();if(!v)return;selectModelFromDropdown(v);_ci.value='';};
_cb.onclick=_applyCustom;
_ci.addEventListener('keydown',e=>{if(e.key==='Enter'){e.preventDefault();_applyCustom();}if(e.key==='Escape'){closeModelDropdown();}});
_ci.addEventListener('click',e=>e.stopPropagation());
dd.appendChild(_custRow);
} }
async function selectModelFromDropdown(value){ async function selectModelFromDropdown(value){