feat(ui): generate avatars (#80)

* feat(ui): generate avatars

Signed-off-by: mudler <mudler@localai.io>

* Show a placeholder if the image is not ready

Signed-off-by: mudler <mudler@localai.io>

* feat(avatar): generate prompt first

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

---------

Signed-off-by: mudler <mudler@localai.io>
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
Ettore Di Giacinto
2025-03-22 20:50:31 +01:00
committed by GitHub
parent c1ac7b675a
commit 3a921f6241
5 changed files with 168 additions and 2 deletions

View File

@@ -5,6 +5,45 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Agent List</title>
{{template "views/partials/header"}}
<style>
.avatar-placeholder {
width: 96px;
height: 96px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #2a2a2a, #3a3a3a);
color: var(--primary);
font-size: 1.5rem;
margin-bottom: 1rem;
border: 2px solid var(--primary);
box-shadow: var(--neon-glow);
position: relative;
overflow: hidden;
}
.avatar-placeholder::after {
content: "";
position: absolute;
width: 100%;
height: 4px;
background: var(--primary);
bottom: 0;
left: 0;
animation: loading-progress 2s infinite linear;
}
@keyframes loading-progress {
0% { width: 0; }
50% { width: 100%; }
100% { width: 0; }
}
.placeholder-text {
z-index: 1;
}
</style>
</head>
<body>
{{template "views/partials/menu"}}
@@ -65,8 +104,15 @@
{{ range .Agents }}
<div hx-ext="sse" data-agent-name="{{.}}" class="card">
<div class="flex flex-col items-center text-center p-4">
<!--<img src="/public/agent_{{.}}.png" alt="{{.}}" class="w-24 h-24 rounded-full mb-4"
style="border: 2px solid var(--primary); box-shadow: var(--neon-glow);">-->
<div class="avatar-container mb-4">
<img src="/avatars/{{.}}.png" alt="{{.}}" class="w-24 h-24 rounded-full"
style="border: 2px solid var(--primary); box-shadow: var(--neon-glow); display: none;"
onload="this.style.display = 'block'; this.nextElementSibling.style.display = 'none';"
onerror="this.style.display = 'none'; this.nextElementSibling.style.display = 'flex';">
<div class="avatar-placeholder">
<span class="placeholder-text"><i class="fas fa-sync fa-spin"></i></span>
</div>
</div>
<h2>{{.}}</h2>
<div class="mb-4 flex items-center justify-center">
<span class="badge {{ if eq (index $status .) true }}badge-primary{{ else }}badge-secondary{{ end }} mr-2">
@@ -117,6 +163,22 @@
<script>
document.addEventListener('DOMContentLoaded', function() {
// Image loading handler
document.querySelectorAll('.avatar-container img').forEach(img => {
// Check if image is already cached
if (img.complete) {
if (img.naturalHeight === 0) {
// Image failed to load
img.style.display = 'none';
img.nextElementSibling.style.display = 'flex';
} else {
// Image loaded successfully
img.style.display = 'block';
img.nextElementSibling.style.display = 'none';
}
}
// onload and onerror handlers are already in the HTML
});
const importSection = document.getElementById('import-section');
const toggleImport = document.getElementById('toggle-import');