mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-07-08 11:56:59 +00:00
Add icons to cookbook engine filter
This commit is contained in:
@@ -468,7 +468,7 @@ function _hwfitShowError(list, host, detail) {
|
||||
if (rb) rb.addEventListener('click', () => { _resetGpuToggleState(); _hwfitFetch(true); });
|
||||
}
|
||||
|
||||
// Client-side "Engine" filter (llama.cpp / vLLM / SGLang / Ollama). Empty =
|
||||
// Client-side "Engine" filter (llama.cpp / vLLM / SGLang / Ollama / Diffusers). Empty =
|
||||
// show all. Uses the same _detectBackend() the serve commands use, so what you
|
||||
// filter to is exactly what would be launched. Pure view filter — no refetch
|
||||
// needed. Ollama rows are merged into the main list (see _ensureOllamaLib +
|
||||
@@ -1804,6 +1804,83 @@ export function _expandModelRow(row, modelData) {
|
||||
|
||||
}
|
||||
|
||||
const _HWFIT_ENGINE_GLYPHS = {
|
||||
'': '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="4" y1="6" x2="20" y2="6"></line><line x1="4" y1="12" x2="20" y2="12"></line><line x1="4" y1="18" x2="20" y2="18"></line><circle cx="8" cy="6" r="2" fill="currentColor" stroke="none"></circle><circle cx="16" cy="12" r="2" fill="currentColor" stroke="none"></circle><circle cx="10" cy="18" r="2" fill="currentColor" stroke="none"></circle></svg>',
|
||||
vllm: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4l7 16 7-16"></path><path d="M14 4l4 9 3-9"></path></svg>',
|
||||
sglang: '<span aria-hidden="true" style="display:block;width:14px;height:14px;background:currentColor;-webkit-mask:url(/static/icons/sglang-mark.png) center/contain no-repeat;mask:url(/static/icons/sglang-mark.png) center/contain no-repeat;"></span>',
|
||||
llamacpp: '<svg width="14" height="14" viewBox="0 0 600 600" fill="none" aria-hidden="true"><path d="M600 392L504.249 558L504.137 557.929C487.252 584.069 458.193 600 426.864 600H120L240 392H600Z" fill="currentColor"></path><path d="M240 392H0L199.602 46.0254C216.032 17.5463 246.411 0 279.29 0H466.154L240 392Z" fill="currentColor"></path></svg>',
|
||||
ollama: '<span aria-hidden="true" style="display:block;width:14px;height:14px;background:currentColor;-webkit-mask:url(/static/icons/ollama-mark-crop.png) center/contain no-repeat;mask:url(/static/icons/ollama-mark-crop.png) center/contain no-repeat;"></span>',
|
||||
diffusers: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="4"></circle><path d="M12 2v3M12 19v3M2 12h3M19 12h3M5 5l2 2M17 17l2 2M5 19l2-2M17 7l2-2"></path></svg>',
|
||||
};
|
||||
|
||||
function _hwfitEngineGlyph(value) {
|
||||
return _HWFIT_ENGINE_GLYPHS[value] || _HWFIT_ENGINE_GLYPHS[''];
|
||||
}
|
||||
|
||||
function _bindHwfitEnginePicker(engine) {
|
||||
const wrap = engine?.closest('.hwfit-engine-wrap');
|
||||
const btn = wrap?.querySelector('[data-hwfit-engine-btn]');
|
||||
const menu = wrap?.querySelector('[data-hwfit-engine-menu]');
|
||||
const icon = wrap?.querySelector('[data-hwfit-engine-icon]');
|
||||
const label = wrap?.querySelector('[data-hwfit-engine-label]');
|
||||
if (!engine || !wrap || !btn || !menu || wrap.dataset.enginePickerBound) return;
|
||||
wrap.dataset.enginePickerBound = '1';
|
||||
|
||||
const setOpen = (open) => {
|
||||
menu.hidden = !open;
|
||||
btn.setAttribute('aria-expanded', open ? 'true' : 'false');
|
||||
};
|
||||
const currentLabel = () => {
|
||||
const opt = Array.from(engine.options).find((o) => o.value === engine.value);
|
||||
return opt?.textContent || 'Engine';
|
||||
};
|
||||
const syncButton = () => {
|
||||
if (label) label.textContent = currentLabel();
|
||||
if (icon) icon.innerHTML = _hwfitEngineGlyph(engine.value);
|
||||
menu.querySelectorAll('[data-hwfit-engine-value]').forEach((item) => {
|
||||
const active = item.dataset.hwfitEngineValue === engine.value;
|
||||
item.classList.toggle('active', active);
|
||||
item.setAttribute('aria-selected', active ? 'true' : 'false');
|
||||
});
|
||||
};
|
||||
const renderMenu = () => {
|
||||
menu.innerHTML = Array.from(engine.options).map((opt) => (
|
||||
`<button type="button" role="option" class="hwfit-engine-item" data-hwfit-engine-value="${opt.value}">`
|
||||
+ `<span class="hwfit-engine-item-icon" aria-hidden="true">${_hwfitEngineGlyph(opt.value)}</span>`
|
||||
+ `<span class="hwfit-engine-item-label">${opt.textContent}</span>`
|
||||
+ '</button>'
|
||||
)).join('');
|
||||
menu.querySelectorAll('[data-hwfit-engine-value]').forEach((item) => {
|
||||
item.addEventListener('click', (ev) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
const next = item.dataset.hwfitEngineValue || '';
|
||||
if (engine.value !== next) {
|
||||
engine.value = next;
|
||||
engine.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
syncButton();
|
||||
setOpen(false);
|
||||
});
|
||||
});
|
||||
syncButton();
|
||||
};
|
||||
|
||||
btn.addEventListener('click', (ev) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
setOpen(menu.hidden);
|
||||
});
|
||||
engine.addEventListener('change', syncButton);
|
||||
document.addEventListener('click', (ev) => {
|
||||
if (!wrap.contains(ev.target)) setOpen(false);
|
||||
});
|
||||
document.addEventListener('keydown', (ev) => {
|
||||
if (ev.key === 'Escape') setOpen(false);
|
||||
});
|
||||
renderMenu();
|
||||
}
|
||||
|
||||
export function _hwfitInit() {
|
||||
const uc = document.getElementById('hwfit-usecase');
|
||||
const sort = document.getElementById('hwfit-sort');
|
||||
@@ -1819,6 +1896,7 @@ export function _hwfitInit() {
|
||||
// Engine filter is a pure client-side view filter over the already-fetched
|
||||
// list (HF + Ollama merged), so just re-render from cache.
|
||||
const engine = document.getElementById('hwfit-engine');
|
||||
if (engine) _bindHwfitEnginePicker(engine);
|
||||
if (engine) engine.addEventListener('change', () => {
|
||||
const list = document.getElementById('hwfit-list');
|
||||
if (list && _hwfitCache && Array.isArray(_hwfitCache.models)) {
|
||||
|
||||
@@ -2692,13 +2692,20 @@ function _renderRecipes() {
|
||||
// levers (Engine / Quant / Context) live to the right.
|
||||
html += '<input type="text" class="cookbook-field-input hwfit-search" id="hwfit-search" placeholder="Search models..." style="flex:1;" />';
|
||||
html += '<span class="hwfit-engine-wrap">';
|
||||
html += '<select class="cookbook-field-input hwfit-engine" id="hwfit-engine" style="height:28px;" title="Filter by serving engine">';
|
||||
html += '<select class="cookbook-field-input hwfit-engine" id="hwfit-engine" style="display:none;" title="Filter by serving engine">';
|
||||
html += '<option value="">Engine</option>';
|
||||
html += '<option value="llamacpp">llama.cpp</option>';
|
||||
html += '<option value="ollama">Ollama</option>';
|
||||
html += '<option value="vllm">vLLM</option>';
|
||||
html += '<option value="sglang">SGLang</option>';
|
||||
html += '<option value="diffusers">Diffusers</option>';
|
||||
html += '</select>';
|
||||
html += '<button type="button" class="cookbook-field-input hwfit-engine-btn" data-hwfit-engine-btn aria-haspopup="listbox" aria-expanded="false" title="Filter by serving engine">';
|
||||
html += '<span class="hwfit-engine-btn-icon" data-hwfit-engine-icon aria-hidden="true"></span>';
|
||||
html += '<span class="hwfit-engine-btn-label" data-hwfit-engine-label>Engine</span>';
|
||||
html += '<svg class="hwfit-engine-caret" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="6 9 12 15 18 9"></polyline></svg>';
|
||||
html += '</button>';
|
||||
html += '<div class="hwfit-engine-menu" data-hwfit-engine-menu role="listbox" hidden></div>';
|
||||
html += '<span class="hwfit-help-chip hwfit-help-chip-inline hwfit-engine-help" title="Rule of thumb: GGUF on single GPU / CPU+RAM → llama.cpp (or Ollama). Safetensors on multi-GPU NVIDIA → vLLM. SGLang is a vLLM-class alternative, sometimes faster on big-MoE / long-context.">?</span>';
|
||||
html += '</span>';
|
||||
// Quant (Q4/Q8/…). Default is "All" so the list shows the best-scoring
|
||||
|
||||
@@ -1390,7 +1390,7 @@ function _rerenderCachedModels() {
|
||||
// Row 2b: Diffusers settings
|
||||
const diffDtypeOpts = ['bfloat16','float16','float32'].map(d => `<option value="${d}"${sv('diff_dtype','bfloat16')===d?' selected':''}>${d}</option>`).join('');
|
||||
const deviceMapOpts = ['balanced','auto','sequential'].map(d => `<option value="${d}"${sv('diff_device_map','balanced')===d?' selected':''}>${d}</option>`).join('');
|
||||
panelHtml += `<div class="hwfit-serve-row hwfit-backend-diffusers">`;
|
||||
panelHtml += `<div class="hwfit-serve-row hwfit-backend-diffusers hwfit-diff-settings-row">`;
|
||||
panelHtml += `<label>Dtype${_h('Precision. bfloat16 recommended for Flux, float16 for SD')} <select class="hwfit-sf" data-field="diff_dtype">${diffDtypeOpts}</select></label>`;
|
||||
panelHtml += `<label>Device Map${_h('How to place model on GPUs. balanced = split evenly')} <select class="hwfit-sf" data-field="diff_device_map">${deviceMapOpts}</select></label>`;
|
||||
panelHtml += `<label>Steps${_h('Default inference steps. More = better quality, slower')} <input type="text" class="hwfit-sf" data-field="diff_steps" value="${esc(sv('diff_steps', ''))}" placeholder="auto" /></label>`;
|
||||
@@ -1495,11 +1495,11 @@ function _rerenderCachedModels() {
|
||||
panelHtml += `<label class="hwfit-sf-cb hwfit-spec-group"><input type="checkbox" class="hwfit-sf" data-field="llama_speculative_mtp"${sv('llama_speculative_mtp',false)?' checked':''} /> MTP Spec${_h('llama.cpp native MTP speculative decoding: --spec-type draft-mtp. Requires a GGUF with MTP heads.')} <input type="number" class="hwfit-sf hwfit-spec-tokens hwfit-spec-tokens-bare" data-field="llama_spec_tokens" value="${esc(sv('llama_spec_tokens', '3'))}" min="1" max="10" title="--spec-draft-n-max" /></label>`;
|
||||
panelHtml += `</div>`;
|
||||
// Row 3b: Checkboxes (diffusers)
|
||||
panelHtml += `<div class="hwfit-serve-checks hwfit-backend-diffusers">`;
|
||||
panelHtml += `<div class="hwfit-serve-checks hwfit-backend-diffusers hwfit-diff-checks-row">`;
|
||||
panelHtml += `<label class="hwfit-sf-cb"><input type="checkbox" class="hwfit-sf" data-field="diff_offload"${sv('diff_offload',false)?' checked':''} /> CPU Offload${_h('Offload parts of model to CPU RAM to save VRAM. Slower but fits larger models')}</label>`;
|
||||
panelHtml += `<label class="hwfit-sf-cb"><input type="checkbox" class="hwfit-sf" data-field="diff_attention_slicing"${sv('diff_attention_slicing',false)?' checked':''} /> Attention Slicing${_h('Slice attention computation to reduce peak VRAM. Slower')}</label>`;
|
||||
panelHtml += `<label class="hwfit-sf-cb"><input type="checkbox" class="hwfit-sf" data-field="diff_vae_slicing"${sv('diff_vae_slicing',false)?' checked':''} /> VAE Slicing${_h('Process VAE in slices. Reduces VRAM for high-res images')}</label>`;
|
||||
panelHtml += `</div><div class="hwfit-serve-row hwfit-backend-diffusers">`;
|
||||
panelHtml += `</div><div class="hwfit-serve-row hwfit-backend-diffusers hwfit-diff-harmonize-row">`;
|
||||
panelHtml += `<label>Harmonize GPU${_h('Separate GPU for img2img/harmonize. Leave empty to use same GPU')}<input type="text" class="hwfit-sf" data-field="diff_harmonize_gpu" value="${esc(sv('diff_harmonize_gpu', ''))}" placeholder="auto" style="width:50px;" /></label>`;
|
||||
panelHtml += `</div>`;
|
||||
// Model-specific optimizations. The checks row always renders for the
|
||||
|
||||
+89
-1
@@ -20479,7 +20479,7 @@ body.gallery-selecting .gallery-dl-btn,
|
||||
width: 100%;
|
||||
}
|
||||
.hwfit-serve-cmd-details {
|
||||
margin: 6px 0 0;
|
||||
margin: 4px 0 0;
|
||||
}
|
||||
.hwfit-serve-cmd-summary {
|
||||
display: flex;
|
||||
@@ -22201,6 +22201,7 @@ body.gallery-selecting .gallery-dl-btn,
|
||||
.hwfit-toolbar .hwfit-usecase { min-width: 70px; flex-shrink: 0; }
|
||||
.hwfit-toolbar .hwfit-quant { min-width: 0; width: 78px; flex-shrink: 0; font-size: 10px; }
|
||||
.hwfit-toolbar .hwfit-engine { min-width: 0; width: 86px; flex-shrink: 0; font-size: 10px; }
|
||||
.hwfit-toolbar .hwfit-engine-btn { min-width: 0; width: 86px; flex-shrink: 0; font-size: 10px; }
|
||||
.hwfit-toolbar .hwfit-search { flex: 1; min-width: 80px; }
|
||||
/* Lower-opacity "Search models..." placeholder so it reads as a hint, not
|
||||
a label — matches the muted form-field feel of the inline filters. */
|
||||
@@ -22271,6 +22272,83 @@ body.gallery-selecting .gallery-dl-btn,
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
.hwfit-engine-btn {
|
||||
height: 28px;
|
||||
padding: 0 30px 0 7px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
color: var(--fg);
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
}
|
||||
.hwfit-engine-btn-icon,
|
||||
.hwfit-engine-item-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--accent, var(--red));
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.hwfit-engine-btn-label {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.hwfit-engine-caret {
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
opacity: 0.55;
|
||||
pointer-events: none;
|
||||
}
|
||||
.hwfit-engine-menu {
|
||||
position: absolute;
|
||||
top: calc(100% + 4px);
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 120;
|
||||
padding: 4px;
|
||||
background: var(--panel, var(--bg));
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 6px 20px rgba(0,0,0,0.22);
|
||||
}
|
||||
.hwfit-engine-item {
|
||||
width: 100%;
|
||||
min-height: 26px;
|
||||
padding: 5px 7px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
color: var(--fg);
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-radius: 5px;
|
||||
font: inherit;
|
||||
font-size: 11px;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.hwfit-engine-item:hover,
|
||||
.hwfit-engine-item.active {
|
||||
background: color-mix(in srgb, var(--fg) 8%, transparent);
|
||||
}
|
||||
.hwfit-engine-item-label {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.hwfit-quant-wrap .hwfit-quant,
|
||||
.hwfit-engine-wrap .hwfit-engine {
|
||||
/* Make room for the ? on the right edge, in addition to the native chevron. */
|
||||
@@ -24056,6 +24134,16 @@ details.hwfit-serve-advanced > .hwfit-llama-monitor-row,
|
||||
details.hwfit-serve-advanced > .hwfit-llama-checks-row {
|
||||
margin-top: -18px !important;
|
||||
}
|
||||
details.hwfit-serve-advanced > .hwfit-diff-settings-row {
|
||||
row-gap: 2px;
|
||||
}
|
||||
details.hwfit-serve-advanced > .hwfit-diff-checks-row,
|
||||
details.hwfit-serve-advanced > .hwfit-diff-harmonize-row {
|
||||
margin-top: -10px !important;
|
||||
}
|
||||
details.hwfit-serve-advanced > .hwfit-diff-checks-row {
|
||||
row-gap: 2px;
|
||||
}
|
||||
details.hwfit-serve-advanced > .hwfit-serve-row.hwfit-vram-monitor {
|
||||
gap: 4px !important;
|
||||
grid-template-columns: max-content minmax(0, 1fr);
|
||||
|
||||
Reference in New Issue
Block a user