feat(recipes): show the recipe base model in the modal header

Adds a base model pill at the front of the recipe modal's tags row,
showing the full base model name (cards keep the abbreviation since
their overlay width is constrained). Falls back to a dimmed Unknown so
the header layout does not shift when hydration fills the value in.
Hydration now also merges base_model. Translated in all 9 locales.
This commit is contained in:
Will Miao
2026-09-16 19:58:44 +08:00
parent a0a5b13ab0
commit 1b1a8d63db
14 changed files with 335 additions and 12 deletions
+32
View File
@@ -28,6 +28,38 @@
width: 100%;
}
/* Tags row: the base model badge shares one line with the compact tags. */
.recipe-tags-row {
display: flex;
align-items: center;
gap: var(--space-2);
width: 100%;
}
.recipe-tags-row #recipeTagsContainer {
flex: 1;
min-width: 0;
}
/* Header base model badge: reuses the card .base-model-label pill shape but
swaps the on-image overlay styling (text shadow, backdrop blur) for the
accent-tinted chip look used by resource rows in this modal. */
.recipe-base-model-badge {
flex-shrink: 0;
max-width: 160px;
text-shadow: none;
backdrop-filter: none;
background: oklch(var(--lora-accent) / 0.1);
color: var(--lora-accent);
padding: 2px 8px;
}
.recipe-base-model-badge.is-unknown {
background: var(--surface-subtle);
color: var(--text-color);
opacity: 0.6;
}
.recipe-modal-header h2 {
margin: 0 0 var(--space-1);
padding: var(--space-1);
+32
View File
@@ -494,6 +494,7 @@ class RecipeModal {
this.syncGenerationParams(hydratedRecipe.gen_params);
this.syncResourcesSection(hydratedRecipe);
this.syncHeaderActions();
this.syncBaseModelBadge();
this.syncMetaFooter();
// Show the modal
@@ -520,6 +521,32 @@ class RecipeModal {
}
}
/**
* Render the recipe-level base model badge in the header tags row.
* Unlike the width-constrained card overlay (which abbreviates), the
* modal has room for the full base model name — matching the model
* modal's info grid and this modal's resource rows. Falls back to a
* dimmed "Unknown" instead of hiding so the header layout does not
* shift when hydration fills the value in.
*/
syncBaseModelBadge() {
const badge = document.getElementById('recipeBaseModelBadge');
if (!badge) {
return;
}
const rawLabel = (this.currentRecipe?.base_model || '').trim();
const unknownLabel = translate('recipes.modal.metadata.unknown', {}, 'Unknown');
const baseModelLabel = rawLabel || unknownLabel;
const fieldLabel = translate('recipes.modal.metadata.baseModel', {}, 'Base Model');
badge.textContent = baseModelLabel;
badge.title = `${fieldLabel}: ${baseModelLabel}`;
badge.setAttribute('aria-label', badge.title);
badge.classList.toggle('is-unknown', !rawLabel);
badge.hidden = false;
}
/**
* Render the meta footer: clickable file location (opens the recipe JSON
* in the OS file manager) plus the truncated recipe ID with copy button.
@@ -661,6 +688,10 @@ class RecipeModal {
nextRecipe.has_workflow = fullRecipe.has_workflow;
}
if (fullRecipe.base_model !== undefined) {
nextRecipe.base_model = fullRecipe.base_model;
}
if (fullRecipe.checkpoint !== undefined) {
nextRecipe.checkpoint = fullRecipe.checkpoint;
} else {
@@ -718,6 +749,7 @@ class RecipeModal {
this.updateSourceUrlDisplay(this.currentRecipe.source_path || '');
}
this.syncHeaderActions();
this.syncBaseModelBadge();
this.syncMetaFooter();
}