checkpoint

This commit is contained in:
Will Miao
2025-03-09 12:29:24 +08:00
parent e6aafe8773
commit 250e8445bb
8 changed files with 434 additions and 100 deletions

View File

@@ -0,0 +1,153 @@
.recipe-card {
background: var(--lora-surface);
border: 1px solid var(--lora-border);
border-radius: var(--border-radius-base);
backdrop-filter: blur(16px);
transition: transform 160ms ease-out;
aspect-ratio: 896/1152;
max-width: 260px;
margin: 0 auto;
position: relative;
overflow: hidden;
cursor: pointer;
}
.recipe-card:hover {
transform: translateY(-2px);
background: oklch(100% 0 0 / 0.6);
}
.recipe-card:focus-visible {
outline: 2px solid var(--lora-accent);
outline-offset: 2px;
}
.recipe-indicator {
position: absolute;
top: 8px;
left: 8px;
width: 24px;
height: 24px;
background: var(--lora-accent);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
z-index: 2;
font-size: 0.9em;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.recipe-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 12px;
margin-top: var(--space-2);
padding-top: 4px;
padding-bottom: 4px;
max-width: 1400px;
margin-left: auto;
margin-right: auto;
}
.card-preview {
position: relative;
width: 100%;
height: 100%;
border-radius: var(--border-radius-base);
overflow: hidden;
}
.card-preview img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: center top;
}
.card-header {
position: absolute;
top: 0;
left: 0;
right: 0;
background: linear-gradient(oklch(0% 0 0 / 0.75), transparent 85%);
backdrop-filter: blur(8px);
color: white;
padding: var(--space-1);
display: flex;
justify-content: space-between;
align-items: center;
z-index: 1;
min-height: 20px;
}
.base-model-wrapper {
display: flex;
align-items: center;
gap: 8px;
margin-left: 32px;
}
.card-actions {
display: flex;
gap: 8px;
}
.card-actions i {
cursor: pointer;
opacity: 0.8;
transition: opacity 0.2s ease;
}
.card-actions i:hover {
opacity: 1;
}
.card-footer {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(transparent 15%, oklch(0% 0 0 / 0.75));
backdrop-filter: blur(8px);
color: white;
padding: var(--space-1);
display: flex;
justify-content: space-between;
align-items: flex-start;
min-height: 32px;
gap: var(--space-1);
}
.lora-count {
display: flex;
align-items: center;
gap: 4px;
background: rgba(255, 255, 255, 0.2);
padding: 2px 8px;
border-radius: var(--border-radius-xs);
font-size: 0.85em;
}
/* 响应式设计 */
@media (max-width: 1400px) {
.recipe-grid {
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
}
.recipe-card {
max-width: 240px;
}
}
@media (max-width: 768px) {
.recipe-grid {
grid-template-columns: minmax(260px, 1fr);
}
.recipe-card {
max-width: 100%;
}
}

View File

@@ -118,39 +118,81 @@ class RecipeManager {
card.dataset.filePath = recipe.file_path;
card.dataset.title = recipe.title;
card.dataset.created = recipe.created_date;
console.log(recipe);
// Get base model from first lora if available
const baseModel = recipe.loras && recipe.loras.length > 0
? recipe.loras[0].baseModel
: '';
// 获取 base model
const baseModel = recipe.base_model || '';
// 确保 loras 数组存在
const lorasCount = recipe.loras ? recipe.loras.length : 0;
// Ensure file_url exists, fallback to file_path if needed
const imageUrl = recipe.file_url ||
(recipe.file_path ? `/loras_static/root1/preview/${recipe.file_path.split('/').pop()}` :
'/loras_static/images/no-preview.png');
card.innerHTML = `
<div class="recipe-indicator" title="Recipe">R</div>
<div class="card-preview">
<img src="${recipe.file_url || recipe.preview_url || '/loras_static/images/no-preview.png'}" alt="${recipe.title}">
<img src="${imageUrl}" alt="${recipe.title}">
<div class="card-header">
${baseModel ? `<span class="base-model-label" title="${baseModel}">${baseModel}</span>` : ''}
<div class="base-model-wrapper">
${baseModel ? `<span class="base-model-label" title="${baseModel}">${baseModel}</span>` : ''}
</div>
<div class="card-actions">
<i class="fas fa-share-alt" title="Share Recipe"></i>
<i class="fas fa-copy" title="Copy Recipe"></i>
<i class="fas fa-trash" title="Delete Recipe"></i>
</div>
</div>
<div class="card-footer">
<div class="model-info">
<span class="model-name">${recipe.title}</span>
</div>
<div class="lora-count" title="Number of LoRAs in this recipe">
<i class="fas fa-layer-group"></i> ${recipe.loras ? recipe.loras.length : 0}
<i class="fas fa-layer-group"></i> ${lorasCount}
</div>
</div>
</div>
`;
// Recipe card click event - will be implemented later
// Recipe card click event
card.addEventListener('click', () => {
console.log('Recipe clicked:', recipe);
// For future implementation: showRecipeDetails(recipe);
this.showRecipeDetails(recipe);
});
// Share button click event - prevent propagation to card
card.querySelector('.fa-share-alt')?.addEventListener('click', (e) => {
e.stopPropagation();
// TODO: Implement share functionality
showToast('Share functionality will be implemented later', 'info');
});
// Copy button click event - prevent propagation to card
card.querySelector('.fa-copy')?.addEventListener('click', (e) => {
e.stopPropagation();
// TODO: Implement copy functionality
showToast('Copy functionality will be implemented later', 'info');
});
// Delete button click event - prevent propagation to card
card.querySelector('.fa-trash')?.addEventListener('click', (e) => {
e.stopPropagation();
// TODO: Implement delete functionality
showToast('Delete functionality will be implemented later', 'info');
});
return card;
}
// Add a placeholder for recipe details method
showRecipeDetails(recipe) {
// TODO: Implement recipe details view
console.log('Recipe details:', recipe);
showToast(`Viewing ${recipe.title}`, 'info');
}
// Will be implemented later:
// - Recipe details view
// - Recipe tag filtering