mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-11-01 18:52:21 +01:00
feat(admin): rename model endpoints inline (#6970)
* feat(admin): rename model endpoints inline * fix(cookbook): keep renamed endpoint names on relaunch Relaunching a model at an existing endpoint URL reset the endpoint name to the launched repo's short name, undoing a rename made in Added Models. Only replace the name when it is still a name the Cookbook generates for one of the endpoint's pinned models, so endpoints that were never renamed still follow the newly served model. --------- Co-authored-by: Alexandre Teixeira <general@alexandreteixeira.dev>
This commit is contained in:
co-authored by
Alexandre Teixeira
parent
c4fdd7ad5b
commit
8450793fe6
@@ -52,6 +52,45 @@
|
||||
gap: 8px;
|
||||
}
|
||||
.admin-user-name { font-size: 13px; font-weight: 500; }
|
||||
/* Inline endpoint rename: the input keeps the name's typography so the row doesn't shift */
|
||||
.adm-ep-rename-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0 2px;
|
||||
color: inherit;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
opacity: 0.45;
|
||||
}
|
||||
.adm-ep-rename-btn:hover,
|
||||
.adm-ep-rename-btn:focus-visible { opacity: 0.9; }
|
||||
@media (hover: hover) {
|
||||
.adm-ep-rename-btn { opacity: 0; }
|
||||
.admin-user-row:hover .adm-ep-rename-btn { opacity: 0.45; }
|
||||
.admin-user-row .adm-ep-rename-btn:hover,
|
||||
.adm-ep-rename-btn:focus-visible { opacity: 0.9; }
|
||||
}
|
||||
.admin-user-row input.adm-ep-rename-input {
|
||||
width: auto;
|
||||
field-sizing: content;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
padding: 0 0 1px;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--red);
|
||||
border-radius: 0;
|
||||
outline: none;
|
||||
}
|
||||
.admin-user-info:has(.adm-ep-name, .adm-ep-rename-input) { min-width: 0; }
|
||||
.adm-ep-name {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
/* Privilege panel */
|
||||
.admin-priv-panel {
|
||||
max-height: 600px;
|
||||
|
||||
+66
-1
@@ -1074,7 +1074,8 @@ async function loadEndpoints() {
|
||||
<span class="adm-check-dot adm-endpoint-select-dot" aria-hidden="true"></span>
|
||||
</label>` : ''}
|
||||
<span class="adm-ep-row-logo" style="display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;flex-shrink:0;opacity:0.9;">${providerLogoFromUrl(ep.base_url) || ''}</span>
|
||||
<span class="admin-user-name">${esc(epTitle)}</span>
|
||||
<span class="admin-user-name adm-ep-name" title="${esc(epTitle)}">${esc(epTitle)}</span>
|
||||
${_endpointSelectMode || (isChatgptAccount && ep.account_label) ? '' : `<button type="button" class="adm-ep-rename-btn" data-adm-rename-ep="${esc(ep.id)}" data-adm-ep-title="${esc(epTitle)}" title="Rename" aria-label="Rename endpoint"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.83 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg></button>`}
|
||||
${ep.model_type === 'image' ? '<span class="admin-badge" style="background:color-mix(in srgb, var(--accent) 20%, transparent);color:var(--accent);">Image</span>' : ''}
|
||||
${kindLabel ? `<span class="admin-badge">${esc(kindLabel)}</span>` : ''}
|
||||
${statusBadge}
|
||||
@@ -1204,6 +1205,70 @@ async function loadEndpoints() {
|
||||
loadEndpoints();
|
||||
});
|
||||
});
|
||||
queryAll('[data-adm-rename-ep]').forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const nameEl = btn.previousElementSibling;
|
||||
if (!nameEl || !nameEl.classList.contains('admin-user-name')) return;
|
||||
const original = btn.dataset.admEpTitle || '';
|
||||
const input = document.createElement('input');
|
||||
input.type = 'text';
|
||||
input.value = original;
|
||||
input.placeholder = 'Endpoint name';
|
||||
input.setAttribute('aria-label', 'Endpoint name');
|
||||
input.maxLength = 64;
|
||||
input.autocomplete = 'off';
|
||||
input.setAttribute('data-bwignore', '');
|
||||
input.className = 'admin-user-name adm-ep-rename-input';
|
||||
input.style.minWidth = `min(${nameEl.getBoundingClientRect().width}px, 100%)`;
|
||||
nameEl.replaceWith(input);
|
||||
btn.style.visibility = 'hidden';
|
||||
input.focus();
|
||||
input.select();
|
||||
let done = false;
|
||||
const restore = () => {
|
||||
input.replaceWith(nameEl);
|
||||
btn.style.visibility = '';
|
||||
};
|
||||
const commit = async () => {
|
||||
if (done) return;
|
||||
done = true;
|
||||
const name = input.value.trim();
|
||||
if (!name || name === original) { restore(); return; }
|
||||
const epId = btn.dataset.admRenameEp;
|
||||
if (data.some(ep => String(ep.id) !== epId && (ep.name || '').trim().toLowerCase() === name.toLowerCase())) {
|
||||
uiModule.showError(`An endpoint named "${name}" already exists`);
|
||||
done = false;
|
||||
input.focus();
|
||||
return;
|
||||
}
|
||||
input.disabled = true;
|
||||
try {
|
||||
const res = await fetch(`/api/model-endpoints/${encodeURIComponent(epId)}`, {
|
||||
method: 'PATCH',
|
||||
credentials: 'same-origin',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => ({}));
|
||||
throw new Error(data.detail || 'Failed to rename endpoint');
|
||||
}
|
||||
} catch (err) {
|
||||
uiModule.showError(err.message || 'Failed to rename endpoint');
|
||||
restore();
|
||||
return;
|
||||
}
|
||||
await _refreshAfterEndpointChange();
|
||||
loadEndpoints();
|
||||
};
|
||||
input.addEventListener('blur', commit);
|
||||
input.addEventListener('keydown', (ev) => {
|
||||
if (ev.key === 'Enter') { ev.preventDefault(); input.blur(); }
|
||||
if (ev.key === 'Escape') { ev.preventDefault(); ev.stopPropagation(); done = true; restore(); }
|
||||
});
|
||||
});
|
||||
});
|
||||
queryAll('[data-adm-copy-url]').forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
Reference in New Issue
Block a user