Puhuvat päät agenteille: ! ja puhekupla vuorovaikutuksen lisäämiseksi
This commit is contained in:
Binary file not shown.
34
network-poc/static/avatars/README.md
Normal file
34
network-poc/static/avatars/README.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Kipinä Agentic Playground - Animaatioiden käyttöönotto
|
||||||
|
|
||||||
|
Koska Kipinä-verkon agenttien avatarit tällä erää ovat staattisia PNG-kuvatiedostoja, käyttöliittymä hyödyntää CSS-pohjaista pomppimisilmiötä (sekä pulppuavaa 💬 puhekuplaa) "puhumisen" merkkinä. Olemme kuitenkin koodanneet taustalle piilotetun tuen aivioiduille videoloopeille myöhempää käyttöä varten!
|
||||||
|
|
||||||
|
Näin saat UI:n tukemaan oikeasti animoituja kasvoja/videoita.
|
||||||
|
|
||||||
|
## 1. Luo Animoidut GIF-tiedostot
|
||||||
|
Valitse mikä tahansa ulkoinen AI-työkalu (kuten HeyGen, Pika v1.0, tai Midjourney+Runway yhdistelmä) ja muunna avatar-kuvat (esim. `kettu_notext.png`) 3-5 sekunnin kestäviksi GIF-loopeiksi. Hahmon leuka tulisi pyöriä tai naama vääntyillä puhuessaan.
|
||||||
|
|
||||||
|
## 2. Nimeä Tiedostot Oikein ja Lisää Ne Kansioon
|
||||||
|
Siirrä uudet GIF-animaatiot samaan kansioon alkuperäisten kuvien kanssa. Muuta niiden nimi siten, että se päättyy tunnisteeseen `_puhuva.gif`.
|
||||||
|
|
||||||
|
Esimerkkejä:
|
||||||
|
- Koodari `kipina_notext.png` → `kipina_notext_puhuva.gif`
|
||||||
|
- Manageri `karhunpentu.png` → `karhunpentu_puhuva.gif`
|
||||||
|
- Asiakas `kettu_notext.png` → `kettu_notext_puhuva.gif`
|
||||||
|
|
||||||
|
## 3. Aktivoi Koodi
|
||||||
|
Käännä Kipinä Playground -ohjaimen JavaScript-koodista piilotettu ominaisuus päälle.
|
||||||
|
|
||||||
|
Etsi tiedostosta `../index.html` (noin riviltä 1084, `updatePromptEditor`-funktiosta):
|
||||||
|
```javascript
|
||||||
|
// Piilotettu ominaisuus: Puhuvien videoiden / gif-animaatioiden kytkentä
|
||||||
|
window.USE_ANIMATED_GIFS = false;
|
||||||
|
```
|
||||||
|
Muuta tuo `false` arvoon `true`:
|
||||||
|
```javascript
|
||||||
|
window.USE_ANIMATED_GIFS = true;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Mitä logiikka tekee?**
|
||||||
|
Aina kun valitset agentin kaaviosta, koodi korvaa aktiivisen kuvakkeen lopussa olevan `.png` -päätteen sanalla `_puhuva.gif` – lennosta! Jos poistut agentin valinnasta tai valitset jonkun toisen, koodi vaihtaa kuvan välittömästi takaisin staattiseen `.png`-versioon ja sulkee ilmentymän suun.
|
||||||
|
|
||||||
|
Näin saat kaikkien asiantuntijoiden face-track looppeja hallittua yhdellä kädenkäänteellä.
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
|
|
||||||
<defs>
|
|
||||||
<radialGradient id="bgGlow" cx="50%" cy="50%" r="50%">
|
|
||||||
<stop offset="0%" stop-color="#2a3f5a"/>
|
|
||||||
<stop offset="100%" stop-color="#0a121d"/>
|
|
||||||
</radialGradient>
|
|
||||||
<filter id="neonGold" x="-50%" y="-50%" width="200%" height="200%">
|
|
||||||
<feDropShadow dx="0" dy="0" stdDeviation="6" flood-color="#ffca28" flood-opacity="0.8"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="neonBlue" x="-50%" y="-50%" width="200%" height="200%">
|
|
||||||
<feDropShadow dx="0" dy="0" stdDeviation="5" flood-color="#4fc3f7" flood-opacity="0.9"/>
|
|
||||||
</filter>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- Background Base -->
|
|
||||||
<rect width="200" height="200" rx="30" fill="url(#bgGlow)"/>
|
|
||||||
|
|
||||||
<!-- Swarm Connectivity Lines -->
|
|
||||||
<g stroke="#4fc3f7" stroke-width="1.5" opacity="0.4" filter="url(#neonBlue)">
|
|
||||||
<line x1="100" y1="100" x2="30" y2="40" />
|
|
||||||
<line x1="100" y1="100" x2="170" y2="40" />
|
|
||||||
<line x1="100" y1="100" x2="40" y2="160" />
|
|
||||||
<line x1="100" y1="100" x2="160" y2="160" />
|
|
||||||
<line x1="100" y1="100" x2="190" y2="100" />
|
|
||||||
<line x1="100" y1="100" x2="10" y2="100" />
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Gentle Crystal Core -->
|
|
||||||
<path d="M 100 40 L 130 100 L 100 160 L 70 100 Z" fill="#1b2a4a" stroke="#ffca28" stroke-width="3" filter="url(#neonGold)"/>
|
|
||||||
<path d="M 100 40 L 130 100 L 100 120 Z" fill="#2a3f5a" opacity="0.8"/>
|
|
||||||
<path d="M 100 40 L 70 100 L 100 120 Z" fill="#0a121d" opacity="0.6"/>
|
|
||||||
|
|
||||||
<!-- Floating Swarm Nodes -->
|
|
||||||
<circle cx="30" cy="40" r="5" fill="#4fc3f7" filter="url(#neonBlue)"/>
|
|
||||||
<circle cx="170" cy="40" r="5" fill="#4fc3f7" filter="url(#neonBlue)"/>
|
|
||||||
<circle cx="40" cy="160" r="5" fill="#4fc3f7" filter="url(#neonBlue)"/>
|
|
||||||
<circle cx="160" cy="160" r="5" fill="#4fc3f7" filter="url(#neonBlue)"/>
|
|
||||||
<circle cx="190" cy="100" r="4" fill="#ffca28" filter="url(#neonGold)"/>
|
|
||||||
<circle cx="10" cy="100" r="4" fill="#ffca28" filter="url(#neonGold)"/>
|
|
||||||
|
|
||||||
<circle cx="100" cy="100" r="10" fill="#ffca28" filter="url(#neonGold)"/>
|
|
||||||
<circle cx="100" cy="100" r="4" fill="#ffffff"/>
|
|
||||||
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.0 KiB |
@@ -1,54 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
|
|
||||||
<defs>
|
|
||||||
<radialGradient id="bgGlow" cx="50%" cy="50%" r="50%">
|
|
||||||
<stop offset="0%" stop-color="#0a2e23"/>
|
|
||||||
<stop offset="100%" stop-color="#090e10"/>
|
|
||||||
</radialGradient>
|
|
||||||
<filter id="neonMint" x="-50%" y="-50%" width="200%" height="200%">
|
|
||||||
<feDropShadow dx="0" dy="0" stdDeviation="6" flood-color="#0fffa3" flood-opacity="0.8"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="neonSun" x="-50%" y="-50%" width="200%" height="200%">
|
|
||||||
<feDropShadow dx="0" dy="0" stdDeviation="5" flood-color="#ffb142" flood-opacity="0.9"/>
|
|
||||||
</filter>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- Background Base -->
|
|
||||||
<rect width="200" height="200" rx="30" fill="url(#bgGlow)"/>
|
|
||||||
|
|
||||||
<!-- Cyberpunk soft coding runic rings in background -->
|
|
||||||
<circle cx="100" cy="100" r="80" fill="none" stroke="#0fffa3" stroke-width="1" opacity="0.1" filter="url(#neonMint)"/>
|
|
||||||
<circle cx="100" cy="100" r="85" fill="none" stroke="#0fffa3" stroke-width="0.5" stroke-dasharray="4 8" opacity="0.2"/>
|
|
||||||
|
|
||||||
<!-- Lizard Gentle Head Shape -->
|
|
||||||
<path d="M 100 30 C 160 30, 180 80, 150 140 C 120 180, 80 180, 50 140 C 20 80, 40 30, 100 30 Z" fill="#1b2a26" />
|
|
||||||
<path d="M 100 40 C 150 40, 160 80, 140 130 C 120 160, 80 160, 60 130 C 40 80, 50 40, 100 40 Z" fill="#243831" />
|
|
||||||
|
|
||||||
<!-- Code-scale back-ridges (Cyber implants but cute) -->
|
|
||||||
<path d="M 60 15 L 75 35 M 100 10 L 100 30 M 140 15 L 125 35" stroke="#0fffa3" stroke-width="4" stroke-linecap="round" filter="url(#neonMint)"/>
|
|
||||||
|
|
||||||
<!-- Big Lovely Lizard Eyes mapped to sides -->
|
|
||||||
<ellipse cx="45" cy="85" rx="20" ry="30" fill="#090e10" transform="rotate(-15 45 85)"/>
|
|
||||||
<ellipse cx="155" cy="85" rx="20" ry="30" fill="#090e10" transform="rotate(15 155 85)"/>
|
|
||||||
|
|
||||||
<!-- Neon Glowing Pupils -->
|
|
||||||
<circle cx="45" cy="85" r="10" fill="#0fffa3" filter="url(#neonMint)"/>
|
|
||||||
<circle cx="155" cy="85" r="10" fill="#0fffa3" filter="url(#neonMint)"/>
|
|
||||||
<circle cx="42" cy="78" r="4" fill="#ffffff" opacity="0.9"/>
|
|
||||||
<circle cx="152" cy="78" r="4" fill="#ffffff" opacity="0.9"/>
|
|
||||||
|
|
||||||
<!-- Adorable Snout & Neural Nose-bridge -->
|
|
||||||
<path d="M 100 45 L 100 100" stroke="#ffb142" stroke-width="2" opacity="0.5" filter="url(#neonSun)"/>
|
|
||||||
<circle cx="100" cy="100" r="4" fill="#ffb142" filter="url(#neonSun)"/>
|
|
||||||
|
|
||||||
<path d="M 80 145 C 90 155, 110 155, 120 145" fill="none" stroke="#0fffa3" stroke-width="3" stroke-linecap="round" filter="url(#neonMint)"/>
|
|
||||||
|
|
||||||
<!-- Cute little cheek blushes in neon -->
|
|
||||||
<ellipse cx="65" cy="115" rx="8" ry="4" fill="#ffb142" opacity="0.6" filter="url(#neonSun)"/>
|
|
||||||
<ellipse cx="135" cy="115" rx="8" ry="4" fill="#ffb142" opacity="0.6" filter="url(#neonSun)"/>
|
|
||||||
|
|
||||||
<!-- Floating Brackets (Coder vibe) -->
|
|
||||||
<text x="15" y="150" fill="#0fffa3" font-size="24" font-family="monospace" opacity="0.5" filter="url(#neonMint)">{</text>
|
|
||||||
<text x="170" y="60" fill="#0fffa3" font-size="24" font-family="monospace" opacity="0.5" filter="url(#neonMint)">}</text>
|
|
||||||
<text x="160" y="160" fill="#ffb142" font-size="18" font-family="monospace" opacity="0.5" filter="url(#neonSun)"></></text>
|
|
||||||
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 3.0 KiB |
@@ -1,66 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
|
|
||||||
<defs>
|
|
||||||
<radialGradient id="bgGlow" cx="50%" cy="50%" r="50%">
|
|
||||||
<stop offset="0%" stop-color="#3d155f"/>
|
|
||||||
<stop offset="100%" stop-color="#090a0f"/>
|
|
||||||
</radialGradient>
|
|
||||||
<radialGradient id="eyeReflection" cx="40%" cy="40%" r="40%">
|
|
||||||
<stop offset="0%" stop-color="#ffffff" stop-opacity="0.9"/>
|
|
||||||
<stop offset="100%" stop-color="#ffffff" stop-opacity="0"/>
|
|
||||||
</radialGradient>
|
|
||||||
<filter id="neonPink" x="-50%" y="-50%" width="200%" height="200%">
|
|
||||||
<feDropShadow dx="0" dy="0" stdDeviation="6" flood-color="#ff71ce" flood-opacity="0.8"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="neonCyan" x="-50%" y="-50%" width="200%" height="200%">
|
|
||||||
<feDropShadow dx="0" dy="0" stdDeviation="5" flood-color="#05d9e8" flood-opacity="0.9"/>
|
|
||||||
</filter>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- Background Base -->
|
|
||||||
<rect width="200" height="200" rx="30" fill="url(#bgGlow)"/>
|
|
||||||
|
|
||||||
<!-- Cyberpunk background grid elements -->
|
|
||||||
<g stroke="#05d9e8" stroke-width="0.5" opacity="0.3">
|
|
||||||
<path d="M 20 40 L 180 40 M 20 80 L 180 80 M 20 120 L 180 120 M 20 160 L 180 160"/>
|
|
||||||
<path d="M 40 20 L 40 180 M 80 20 L 80 180 M 120 20 L 120 180 M 160 20 L 160 180"/>
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<!-- Fluffy Gentle Raccoon Base -->
|
|
||||||
<path d="M 100 40 C 150 40, 170 80, 160 120 C 145 160, 55 160, 40 120 C 30 80, 50 40, 100 40 Z" fill="#2d3340" />
|
|
||||||
|
|
||||||
<!-- Ears -->
|
|
||||||
<path d="M 45 60 C 25 35, 30 15, 60 30 Z" fill="#1b212b" stroke="#ff71ce" stroke-width="2" filter="url(#neonPink)"/>
|
|
||||||
<path d="M 155 60 C 175 35, 170 15, 140 30 Z" fill="#1b212b" stroke="#05d9e8" stroke-width="2" filter="url(#neonCyan)"/>
|
|
||||||
|
|
||||||
<path d="M 50 55 C 35 40, 40 25, 55 35 Z" fill="#ff71ce" opacity="0.6" filter="url(#neonPink)"/>
|
|
||||||
<path d="M 150 55 C 165 40, 160 25, 145 35 Z" fill="#05d9e8" opacity="0.6" filter="url(#neonCyan)"/>
|
|
||||||
|
|
||||||
<!-- Bandit Mask (Cyber-goggles vibe but soft) -->
|
|
||||||
<path d="M 30 95 C 60 70, 140 70, 170 95 C 180 105, 140 140, 100 130 C 60 140, 20 105, 30 95 Z" fill="#0b0e14" opacity="0.8"/>
|
|
||||||
<path d="M 35 95 C 65 75, 135 75, 165 95" fill="none" stroke="#ff71ce" stroke-width="2" filter="url(#neonPink)" opacity="0.3"/>
|
|
||||||
|
|
||||||
<!-- Big Gentle Anime-style Eyes -->
|
|
||||||
<ellipse cx="65" cy="100" rx="16" ry="22" fill="#05d9e8" filter="url(#neonCyan)"/>
|
|
||||||
<ellipse cx="135" cy="100" rx="16" ry="22" fill="#ff71ce" filter="url(#neonPink)"/>
|
|
||||||
<ellipse cx="65" cy="100" rx="12" ry="18" fill="#111"/>
|
|
||||||
<ellipse cx="135" cy="100" rx="12" ry="18" fill="#111"/>
|
|
||||||
<!-- Glints -->
|
|
||||||
<circle cx="58" cy="92" r="6" fill="url(#eyeReflection)"/>
|
|
||||||
<circle cx="128" cy="92" r="6" fill="url(#eyeReflection)"/>
|
|
||||||
<circle cx="72" cy="108" r="3" fill="#fff" opacity="0.6"/>
|
|
||||||
<circle cx="142" cy="108" r="3" fill="#fff" opacity="0.6"/>
|
|
||||||
|
|
||||||
<!-- Cute Muzzle & Nose -->
|
|
||||||
<path d="M 80 120 C 100 150, 120 120, 100 140 Z" fill="#e5e5e5"/>
|
|
||||||
<path d="M 90 140 C 90 135, 110 135, 110 140 C 110 148, 90 148, 90 140 Z" fill="#ff71ce" filter="url(#neonPink)"/>
|
|
||||||
<path d="M 95 145 C 100 152, 105 145, 105 145" fill="none" stroke="#2d3340" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
|
|
||||||
<!-- Cyberpunk Gentle Neck Ring/Scarf -->
|
|
||||||
<path d="M 50 150 C 80 170, 120 170, 150 150" fill="none" stroke="#05d9e8" stroke-width="6" stroke-linecap="round" filter="url(#neonCyan)"/>
|
|
||||||
|
|
||||||
<!-- Little Floating Data Motes -->
|
|
||||||
<circle cx="30" cy="50" r="3" fill="#ff71ce" filter="url(#neonPink)"/>
|
|
||||||
<circle cx="170" cy="130" r="2" fill="#05d9e8" filter="url(#neonCyan)"/>
|
|
||||||
<circle cx="150" cy="40" r="4" fill="#ff71ce" filter="url(#neonPink)"/>
|
|
||||||
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 3.5 KiB |
@@ -1,49 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
|
|
||||||
<defs>
|
|
||||||
<radialGradient id="bgGlow" cx="50%" cy="50%" r="50%">
|
|
||||||
<stop offset="0%" stop-color="#140722"/>
|
|
||||||
<stop offset="100%" stop-color="#0a0512"/>
|
|
||||||
</radialGradient>
|
|
||||||
<filter id="neonViolet" x="-50%" y="-50%" width="200%" height="200%">
|
|
||||||
<feDropShadow dx="0" dy="0" stdDeviation="6" flood-color="#b829ea" flood-opacity="0.8"/>
|
|
||||||
</filter>
|
|
||||||
<filter id="neonTeal" x="-50%" y="-50%" width="200%" height="200%">
|
|
||||||
<feDropShadow dx="0" dy="0" stdDeviation="5" flood-color="#1cf0eb" flood-opacity="0.9"/>
|
|
||||||
</filter>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- Background Base -->
|
|
||||||
<rect width="200" height="200" rx="30" fill="url(#bgGlow)"/>
|
|
||||||
|
|
||||||
<!-- Dreamy Cyber-waves -->
|
|
||||||
<path d="M 0 160 Q 50 180, 100 150 T 200 160 M 0 180 Q 50 160, 100 190 T 200 170" fill="none" stroke="#b829ea" stroke-width="2" opacity="0.2" filter="url(#neonViolet)"/>
|
|
||||||
|
|
||||||
<!-- Sloth Head Fluffy Contour -->
|
|
||||||
<path d="M 50 60 C 20 90, 40 160, 100 160 C 160 160, 180 90, 150 60 C 130 30, 70 30, 50 60 Z" fill="#231a31" />
|
|
||||||
|
|
||||||
<!-- Face Mask (Gentle Heart-like shape) -->
|
|
||||||
<path d="M 70 80 C 60 120, 90 140, 100 140 C 110 140, 140 120, 130 80 C 120 50, 80 50, 70 80 Z" fill="#3c304d" />
|
|
||||||
|
|
||||||
<!-- Cozy VR Sleep-Mask / Cyber-visor -->
|
|
||||||
<path d="M 50 80 Q 100 100, 150 80 Q 140 110, 100 115 Q 60 110, 50 80 Z" fill="#0d0914" stroke="#b829ea" stroke-width="2" filter="url(#neonViolet)"/>
|
|
||||||
|
|
||||||
<!-- Sweet Dreamy Digital Lines inside Visor -->
|
|
||||||
<path d="M 70 88 C 80 95, 90 95, 95 88 M 130 88 C 120 95, 110 95, 105 88" fill="none" stroke="#1cf0eb" stroke-width="3" stroke-linecap="round" filter="url(#neonTeal)"/>
|
|
||||||
|
|
||||||
<!-- Cute Sleepy Snout -->
|
|
||||||
<circle cx="100" cy="130" r="6" fill="#140722"/>
|
|
||||||
<path d="M 90 140 C 95 145, 105 145, 110 140" fill="none" stroke="#1cf0eb" stroke-width="2.5" stroke-linecap="round" filter="url(#neonTeal)"/>
|
|
||||||
|
|
||||||
<!-- Sleepy Zzzs and Star nodes -->
|
|
||||||
<text x="140" y="40" fill="#b829ea" font-size="20" font-family="sans-serif" font-weight="bold" filter="url(#neonViolet)">Z</text>
|
|
||||||
<text x="160" y="25" fill="#1cf0eb" font-size="14" font-family="sans-serif" font-weight="bold" filter="url(#neonTeal)">z</text>
|
|
||||||
<text x="175" y="15" fill="#b829ea" font-size="10" font-family="sans-serif" font-weight="bold" filter="url(#neonViolet)">z</text>
|
|
||||||
|
|
||||||
<circle cx="40" cy="50" r="2" fill="#1cf0eb" filter="url(#neonTeal)"/>
|
|
||||||
<circle cx="50" cy="30" r="1.5" fill="#b829ea" filter="url(#neonViolet)"/>
|
|
||||||
|
|
||||||
<!-- Cybernetic headset nodes -->
|
|
||||||
<circle cx="45" cy="85" r="8" fill="#140722" stroke="#1cf0eb" stroke-width="2" filter="url(#neonTeal)"/>
|
|
||||||
<circle cx="155" cy="85" r="8" fill="#140722" stroke="#1cf0eb" stroke-width="2" filter="url(#neonTeal)"/>
|
|
||||||
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.7 KiB |
@@ -393,6 +393,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
perspective: 1000px;
|
perspective: 1000px;
|
||||||
|
padding: 25px 50px;
|
||||||
}
|
}
|
||||||
.org-level {
|
.org-level {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -439,6 +440,18 @@
|
|||||||
border-color: rgba(240, 246, 252, 0.3);
|
border-color: rgba(240, 246, 252, 0.3);
|
||||||
box-shadow: 0 12px 20px rgba(0,0,0,0.4);
|
box-shadow: 0 12px 20px rgba(0,0,0,0.4);
|
||||||
}
|
}
|
||||||
|
@keyframes idle-breathe {
|
||||||
|
0%, 100% { transform: translateY(0) scale(1); }
|
||||||
|
50% { transform: translateY(-2px) scale(1.01); }
|
||||||
|
}
|
||||||
|
@keyframes talking-head {
|
||||||
|
0% { transform: scale(1.05) scaleY(1) translateY(0); }
|
||||||
|
25% { transform: scale(1.05) scaleY(0.96) scaleX(1.02) translateY(2px); }
|
||||||
|
50% { transform: scale(1.05) scaleY(1.02) scaleX(0.98) translateY(-2px); }
|
||||||
|
75% { transform: scale(1.05) scaleY(0.97) scaleX(1.01) translateY(1px); }
|
||||||
|
100% { transform: scale(1.05) scaleY(1) translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
.avatar-card img {
|
.avatar-card img {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
height: 80px;
|
height: 80px;
|
||||||
@@ -448,7 +461,10 @@
|
|||||||
transition: all 0.4s ease;
|
transition: all 0.4s ease;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
background: #010409;
|
background: #010409;
|
||||||
|
animation: idle-breathe 4s infinite ease-in-out;
|
||||||
|
transform-origin: bottom center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-card.active, .avatar-card.selected {
|
.avatar-card.active, .avatar-card.selected {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateY(-8px) scale(1.05);
|
transform: translateY(-8px) scale(1.05);
|
||||||
@@ -457,10 +473,134 @@
|
|||||||
box-shadow: 0 16px 24px rgba(0,0,0,0.5), 0 0 20px rgba(88, 166, 255, 0.3);
|
box-shadow: 0 16px 24px rgba(0,0,0,0.5), 0 0 20px rgba(88, 166, 255, 0.3);
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
.avatar-card.active img, .avatar-card.selected img {
|
|
||||||
|
.avatar-card.selected img {
|
||||||
border-color: var(--accent-color);
|
border-color: var(--accent-color);
|
||||||
box-shadow: 0 0 25px rgba(88, 166, 255, 0.5);
|
box-shadow: 0 0 25px rgba(88, 166, 255, 0.5);
|
||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-card.active img {
|
||||||
|
border-color: var(--accent-color);
|
||||||
|
box-shadow: 0 0 25px rgba(88, 166, 255, 0.8);
|
||||||
|
animation: talking-head 0.4s infinite ease-in-out;
|
||||||
|
transform-origin: bottom center;
|
||||||
|
}
|
||||||
|
@keyframes talking-head-gallery {
|
||||||
|
0% { transform: scaleY(1) translateY(0); }
|
||||||
|
25% { transform: scaleY(0.94) scaleX(1.04) translateY(3px); }
|
||||||
|
50% { transform: scaleY(1.04) scaleX(0.96) translateY(-3px); }
|
||||||
|
75% { transform: scaleY(0.96) scaleX(1.02) translateY(1px); }
|
||||||
|
100% { transform: scaleY(1) translateY(0); }
|
||||||
|
}
|
||||||
|
.gallery-head {
|
||||||
|
width: 55px;
|
||||||
|
height: 55px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 2px solid rgba(240, 246, 252, 0.1);
|
||||||
|
object-fit: cover;
|
||||||
|
background: #010409;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
opacity: 0.4;
|
||||||
|
filter: grayscale(80%);
|
||||||
|
}
|
||||||
|
.gallery-head.active {
|
||||||
|
opacity: 1;
|
||||||
|
filter: grayscale(0%);
|
||||||
|
border-color: var(--accent-color);
|
||||||
|
box-shadow: 0 0 15px rgba(88, 166, 255, 0.5);
|
||||||
|
animation: talking-head-gallery 0.4s infinite ease-in-out;
|
||||||
|
transform-origin: bottom center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes confused-shake {
|
||||||
|
0% { transform: translateX(0); }
|
||||||
|
25% { transform: translateX(-2px) rotate(-3deg); }
|
||||||
|
50% { transform: translateX(0); }
|
||||||
|
75% { transform: translateX(2px) rotate(3deg); }
|
||||||
|
100% { transform: translateX(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-head-wrap[data-tooltip]::before {
|
||||||
|
content: attr(data-tooltip);
|
||||||
|
position: absolute;
|
||||||
|
bottom: 110%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background: rgba(13, 17, 23, 0.95);
|
||||||
|
color: #f0f6fc;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 11px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
width: 140px;
|
||||||
|
text-align: left;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
z-index: 100;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
box-shadow: 0 4px 12px rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
.gallery-head-wrap:hover[data-tooltip]:not([data-tooltip=""])::before { opacity: 1; }
|
||||||
|
|
||||||
|
/* Yhteiset kuplasäännöt */
|
||||||
|
.gallery-head-wrap.state-question::after,
|
||||||
|
.gallery-head-wrap.state-alert::after,
|
||||||
|
.gallery-head-wrap.active:not(.state-question):not(.state-alert)::after {
|
||||||
|
position: absolute;
|
||||||
|
top: -10px;
|
||||||
|
right: -10px;
|
||||||
|
font-size: 14px;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* State: Kysymys (Sininen ?) */
|
||||||
|
.gallery-head-wrap.state-question::after {
|
||||||
|
content: '?';
|
||||||
|
color: #ffffff;
|
||||||
|
font-weight: 900;
|
||||||
|
font-family: system-ui, -apple-system, sans-serif;
|
||||||
|
animation: speech-pulse 1s infinite alternate;
|
||||||
|
background: #1f6feb; border: 1px solid #58a6ff;
|
||||||
|
}
|
||||||
|
.gallery-head.state-question {
|
||||||
|
border-color: #58a6ff; box-shadow: 0 0 15px rgba(88, 166, 255, 0.4);
|
||||||
|
animation: confused-shake 2s infinite ease-in-out; filter: grayscale(10%); opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* State: Alert (Punainen !) */
|
||||||
|
.gallery-head-wrap.state-alert::after {
|
||||||
|
content: '!';
|
||||||
|
color: #ffffff;
|
||||||
|
font-weight: 900;
|
||||||
|
font-family: system-ui, -apple-system, sans-serif;
|
||||||
|
animation: speech-pulse 0.5s infinite alternate;
|
||||||
|
background: #da3633; border: 1px solid #ff7b72;
|
||||||
|
}
|
||||||
|
.gallery-head.state-alert {
|
||||||
|
border-color: #ff4444; box-shadow: 0 0 15px rgba(255, 68, 68, 0.5);
|
||||||
|
animation: confused-shake 0.5s infinite; filter: grayscale(30%); opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery-head-wrap { position: relative; display: inline-block; cursor: help; }
|
||||||
|
@keyframes speech-pulse {
|
||||||
|
0% { transform: scale(0.8) translateY(0); opacity: 0.6; }
|
||||||
|
50% { transform: scale(1.1) translateY(-2px); opacity: 1; }
|
||||||
|
100% { transform: scale(0.8) translateY(0); opacity: 0.6; }
|
||||||
|
}
|
||||||
|
.gallery-head-wrap.active:not(.state-question):not(.state-alert)::after {
|
||||||
|
content: '💬';
|
||||||
|
animation: speech-pulse 0.8s infinite ease-in-out;
|
||||||
|
background: #0d1117;
|
||||||
|
border: 1px solid var(--accent-color);
|
||||||
}
|
}
|
||||||
.avatar-name { font-weight: 700; font-size: 13px; color: #f0f6fc; letter-spacing: 0.5px; margin-bottom: 2px; }
|
.avatar-name { font-weight: 700; font-size: 13px; color: #f0f6fc; letter-spacing: 0.5px; margin-bottom: 2px; }
|
||||||
.avatar-role { font-size: 10px; color: #8b949e; text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600; line-height: 1.2; word-wrap: break-word; }
|
.avatar-role { font-size: 10px; color: #8b949e; text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600; line-height: 1.2; word-wrap: break-word; }
|
||||||
@@ -523,7 +663,7 @@
|
|||||||
#metrics-grid { grid-template-columns: 1fr 1fr !important; }
|
#metrics-grid { grid-template-columns: 1fr 1fr !important; }
|
||||||
|
|
||||||
/* Org chart mobile tweaks */
|
/* Org chart mobile tweaks */
|
||||||
.org-chart { padding-left: 0; padding-right: 0; }
|
.org-chart { padding: 20px 10px; }
|
||||||
.org-branch { display: none; }
|
.org-branch { display: none; }
|
||||||
.org-connector { margin-bottom: 10px; height: 20px; }
|
.org-connector { margin-bottom: 10px; height: 20px; }
|
||||||
.org-level { flex-wrap: wrap; justify-content: center; gap: 15px !important; }
|
.org-level { flex-wrap: wrap; justify-content: center; gap: 15px !important; }
|
||||||
@@ -859,75 +999,96 @@
|
|||||||
<span id="agent-status" style="font-size:12px;color:var(--success-color)">Monitoring Active</span>
|
<span id="agent-status" style="font-size:12px;color:var(--success-color)">Monitoring Active</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="org-chart">
|
<div class="workspace-split" style="display:flex; gap:20px; align-items:flex-start; flex-wrap: wrap;">
|
||||||
<!-- Taso 1 -->
|
<!-- LEFT COLUMN: Org chart & Prompt Editor -->
|
||||||
<div class="org-level">
|
<div style="flex:1; min-width:300px; overflow-x:auto;">
|
||||||
<div class="avatar-card" id="avatar-client" data-agent="client" onclick="selectAgent('client')">
|
<div class="org-chart">
|
||||||
<img src="/avatars/kettu_notext.png" alt="Asiakas (Kettu)">
|
<!-- Taso 1 -->
|
||||||
<div class="avatar-name">Asiakas</div>
|
<div class="org-level">
|
||||||
<div class="avatar-role">Tuoteomistaja</div>
|
<div class="avatar-card" id="avatar-client" data-agent="client" onclick="selectAgent('client')">
|
||||||
|
<img src="/avatars/kettu_notext.png" alt="Asiakas (Kettu)">
|
||||||
|
<div class="avatar-name">Asiakas</div>
|
||||||
|
<div class="avatar-role">Tuoteomistaja</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="org-connector"></div>
|
||||||
|
|
||||||
|
<!-- Taso 2 -->
|
||||||
|
<div class="org-level" style="position: relative;">
|
||||||
|
<!-- Tarkkailija laitetaan erilleen kauemmas sivuun jotta se näyttää itsenäiseltä valvojalta -->
|
||||||
|
<div class="avatar-card" id="avatar-observer" data-agent="observer" onclick="selectAgent('observer')" style="position: absolute; right: calc(50% + 350px); top: 0;">
|
||||||
|
<img src="/avatars/aikuinen_susi.png" alt="Tarkkailija (Aikuinen Susi)">
|
||||||
|
<div class="avatar-name">Tarkkailija</div>
|
||||||
|
<div class="avatar-role">Laadunvalvonta</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="avatar-card" id="avatar-kpn" data-agent="manager" onclick="selectAgent('manager')">
|
||||||
|
<img src="/avatars/karhunpentu.png" alt="Manageri (Karhunpentu)">
|
||||||
|
<div class="avatar-name">Manageri</div>
|
||||||
|
<div class="avatar-role">KPN CLI</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="org-connector"></div>
|
||||||
|
|
||||||
|
<div class="org-branch"></div>
|
||||||
|
|
||||||
|
<!-- Taso 3 -->
|
||||||
|
<div class="org-level" style="gap: 20px;">
|
||||||
|
<div class="avatar-card" id="avatar-coder" data-agent="coder" onclick="selectAgent('coder')">
|
||||||
|
<img src="/avatars/kipina_notext.png" alt="Koodari (Salamanteri)">
|
||||||
|
<div class="avatar-name">Koodari</div>
|
||||||
|
<div class="avatar-role">SOFTAKEHITYS</div>
|
||||||
|
</div>
|
||||||
|
<div class="avatar-card" id="avatar-data" data-agent="data" onclick="selectAgent('data')">
|
||||||
|
<img src="/avatars/pesukarhu_notext.png" alt="Data-Agentti (Pesukarhu)">
|
||||||
|
<div class="avatar-name">Data</div>
|
||||||
|
<div class="avatar-role">Tietokannat</div>
|
||||||
|
</div>
|
||||||
|
<div class="avatar-card" id="avatar-qa" data-agent="qa" onclick="selectAgent('qa')">
|
||||||
|
<img src="/avatars/susi_notext.png" alt="QA (Pikkususi)">
|
||||||
|
<div class="avatar-name">QA</div>
|
||||||
|
<div class="avatar-role">Testaus</div>
|
||||||
|
</div>
|
||||||
|
<div class="avatar-card" id="avatar-tester" data-agent="tester" onclick="selectAgent('tester')">
|
||||||
|
<img src="/avatars/laiskiainen_notext.png" alt="DevOps (Laiskiainen)">
|
||||||
|
<div class="avatar-name">DevOps</div>
|
||||||
|
<div class="avatar-role">Käyttöönotto</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Prompt Editor -->
|
||||||
|
<div class="agent-prompt-editor" id="agent-prompt-editor" style="margin-top:20px;">
|
||||||
|
<div class="agent-prompt-label">
|
||||||
|
<strong id="agent-prompt-name">—</strong>
|
||||||
|
<span id="agent-prompt-saved" style="color:var(--success-color);opacity:0;transition:opacity 0.3s">Tallennettu</span>
|
||||||
|
</div>
|
||||||
|
<textarea id="agent-prompt-text" placeholder="Kirjoita system prompt..."></textarea>
|
||||||
|
<div id="shared-prompt-section" style="display:none;margin-top:8px;font-size:12px;color:#8b949e">
|
||||||
|
Yhteinen konteksti liitetään jokaisen valitun agentin oman promptin alkuun.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="org-connector"></div>
|
<!-- RIGHT COLUMN: Puhuvat Päät Gallery -->
|
||||||
|
<div style="flex-basis:150px; flex-shrink:0;">
|
||||||
<!-- Taso 2 -->
|
<div style="background:rgba(1, 4, 9, 0.6); border:1px solid var(--border-color); border-radius:6px; padding:12px; height: 100%;">
|
||||||
<div class="org-level" style="position: relative;">
|
<div id="all-heads-gallery" style="display:flex; flex-wrap:wrap; gap:10px; justify-content:center;">
|
||||||
<!-- Tarkkailija laitetaan erilleen kauemmas sivuun jotta se näyttää itsenäiseltä valvojalta -->
|
<div class="gallery-head-wrap" id="wrap-client"><img src="/avatars/kettu_notext.png" id="gallery-client" class="gallery-head" alt="Asiakas"></div>
|
||||||
<div class="avatar-card" id="avatar-observer" data-agent="observer" onclick="selectAgent('observer')" style="position: absolute; right: calc(50% + 350px); top: 0;">
|
<div class="gallery-head-wrap" id="wrap-observer"><img src="/avatars/aikuinen_susi.png" id="gallery-observer" class="gallery-head" alt="Tarkkailija"></div>
|
||||||
<img src="/avatars/aikuinen_susi.png" alt="Tarkkailija (Aikuinen Susi)">
|
<div class="gallery-head-wrap" id="wrap-manager"><img src="/avatars/karhunpentu.png" id="gallery-manager" class="gallery-head" alt="Manageri"></div>
|
||||||
<div class="avatar-name">Tarkkailija</div>
|
<div class="gallery-head-wrap" id="wrap-coder"><img src="/avatars/kipina_notext.png" id="gallery-coder" class="gallery-head" alt="Koodari"></div>
|
||||||
<div class="avatar-role">Laadunvalvonta</div>
|
<div class="gallery-head-wrap" id="wrap-data"><img src="/avatars/pesukarhu_notext.png" id="gallery-data" class="gallery-head" alt="Data"></div>
|
||||||
</div>
|
<div class="gallery-head-wrap" id="wrap-qa"><img src="/avatars/susi_notext.png" id="gallery-qa" class="gallery-head" alt="QA"></div>
|
||||||
|
<div class="gallery-head-wrap" id="wrap-tester"><img src="/avatars/laiskiainen_notext.png" id="gallery-tester" class="gallery-head" alt="DevOps"></div>
|
||||||
<div class="avatar-card" id="avatar-kpn" data-agent="manager" onclick="selectAgent('manager')">
|
</div>
|
||||||
<img src="/avatars/karhunpentu.png" alt="Manageri (Karhunpentu)">
|
|
||||||
<div class="avatar-name">Manageri</div>
|
|
||||||
<div class="avatar-role">KPN CLI</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="org-connector"></div>
|
|
||||||
|
|
||||||
<div class="org-branch"></div>
|
|
||||||
|
|
||||||
<!-- Taso 3 -->
|
|
||||||
<div class="org-level" style="gap: 20px;">
|
|
||||||
<div class="avatar-card" id="avatar-coder" data-agent="coder" onclick="selectAgent('coder')">
|
|
||||||
<img src="/avatars/kipina_notext.png" alt="Koodari (Salamanteri)">
|
|
||||||
<div class="avatar-name">Koodari</div>
|
|
||||||
<div class="avatar-role">Ohjelmistokehitys</div>
|
|
||||||
</div>
|
|
||||||
<div class="avatar-card" id="avatar-data" data-agent="data" onclick="selectAgent('data')">
|
|
||||||
<img src="/avatars/pesukarhu_notext.png" alt="Data-Agentti (Pesukarhu)">
|
|
||||||
<div class="avatar-name">Data</div>
|
|
||||||
<div class="avatar-role">Tietokannat</div>
|
|
||||||
</div>
|
|
||||||
<div class="avatar-card" id="avatar-qa" data-agent="qa" onclick="selectAgent('qa')">
|
|
||||||
<img src="/avatars/susi_notext.png" alt="QA (Pikkususi)">
|
|
||||||
<div class="avatar-name">QA</div>
|
|
||||||
<div class="avatar-role">Testaus</div>
|
|
||||||
</div>
|
|
||||||
<div class="avatar-card" id="avatar-tester" data-agent="tester" onclick="selectAgent('tester')">
|
|
||||||
<img src="/avatars/laiskiainen_notext.png" alt="DevOps (Laiskiainen)">
|
|
||||||
<div class="avatar-name">DevOps</div>
|
|
||||||
<div class="avatar-role">Käyttöönotto</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="agent-prompt-editor" id="agent-prompt-editor">
|
<div class="terminal-panel" id="agent-terminal" style="margin-top: 20px;">
|
||||||
<div class="agent-prompt-label">
|
|
||||||
<strong id="agent-prompt-name">—</strong>
|
|
||||||
<span id="agent-prompt-saved" style="color:var(--success-color);opacity:0;transition:opacity 0.3s">Tallennettu</span>
|
|
||||||
</div>
|
|
||||||
<textarea id="agent-prompt-text" placeholder="Kirjoita system prompt..."></textarea>
|
|
||||||
<div id="shared-prompt-section" style="display:none;margin-top:8px;font-size:12px;color:#8b949e">
|
|
||||||
Yhteinen konteksti liitetään jokaisen valitun agentin oman promptin alkuun.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="terminal-panel" id="agent-terminal">
|
|
||||||
<div class="terminal-line"><span class="terminal-prompt">$</span> kpn hub connect wss://localhost</div>
|
<div class="terminal-line"><span class="terminal-prompt">$</span> kpn hub connect wss://localhost</div>
|
||||||
<div class="terminal-line" style="color:#a5d6ff"> ✓ Yhdistetty Kipinä Hubiin</div>
|
<div class="terminal-line" style="color:#a5d6ff"> ✓ Yhdistetty Kipinä Hubiin</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -981,6 +1142,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Piilotettu ominaisuus: Puhuvien videoiden / gif-animaatioiden kytkentä
|
||||||
|
// Aseta true kun olet luonut _puhuva.gif tiedostot /avatars -kansioon
|
||||||
|
window.USE_ANIMATED_GIFS = false;
|
||||||
|
|
||||||
|
// Update gallery heads
|
||||||
|
document.querySelectorAll('.gallery-head').forEach(el => {
|
||||||
|
el.classList.remove('active');
|
||||||
|
if (window.USE_ANIMATED_GIFS) {
|
||||||
|
el.src = el.src.replace('_puhuva.gif', '.png');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.querySelectorAll('.gallery-head-wrap').forEach(el => el.classList.remove('active'));
|
||||||
|
selectedAgents.forEach(agent => {
|
||||||
|
const gel = document.getElementById('gallery-' + agent);
|
||||||
|
const wrap = document.getElementById('wrap-' + agent);
|
||||||
|
if (gel) {
|
||||||
|
gel.classList.add('active');
|
||||||
|
if (window.USE_ANIMATED_GIFS) {
|
||||||
|
gel.src = gel.src.replace('.png', '_puhuva.gif');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (wrap) wrap.classList.add('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
checkAgentConfusion();
|
||||||
|
|
||||||
if (selectedAgents.size === 0) {
|
if (selectedAgents.size === 0) {
|
||||||
editor.classList.remove('visible');
|
editor.classList.remove('visible');
|
||||||
return;
|
return;
|
||||||
@@ -988,6 +1175,9 @@
|
|||||||
|
|
||||||
editor.classList.add('visible');
|
editor.classList.add('visible');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (selectedAgents.size === 1) {
|
if (selectedAgents.size === 1) {
|
||||||
const agent = [...selectedAgents][0];
|
const agent = [...selectedAgents][0];
|
||||||
const cfg = agentPrompts[agent];
|
const cfg = agentPrompts[agent];
|
||||||
@@ -1062,10 +1252,74 @@
|
|||||||
localStorage.setItem('kpn-shared-prompt', e.target.value);
|
localStorage.setItem('kpn-shared-prompt', e.target.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkAgentConfusion();
|
||||||
|
|
||||||
saved.style.opacity = '1';
|
saved.style.opacity = '1';
|
||||||
clearTimeout(saved._t);
|
clearTimeout(saved._t);
|
||||||
saved._t = setTimeout(() => saved.style.opacity = '0', 1500);
|
saved._t = setTimeout(() => saved.style.opacity = '0', 1500);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function checkAgentConfusion() {
|
||||||
|
Object.keys(agentPrompts).forEach(agent => {
|
||||||
|
const prompt = agentPrompts[agent].prompt || "";
|
||||||
|
const wrap = document.getElementById('wrap-' + agent);
|
||||||
|
const gel = document.getElementById('gallery-' + agent);
|
||||||
|
if (!wrap || !gel) return;
|
||||||
|
|
||||||
|
// Nollataan tilat
|
||||||
|
wrap.classList.remove('state-question', 'state-alert');
|
||||||
|
gel.classList.remove('state-question', 'state-alert');
|
||||||
|
wrap.removeAttribute('data-tooltip');
|
||||||
|
|
||||||
|
const pLow = prompt.toLowerCase();
|
||||||
|
const agentTitle = (agentPrompts[agent].name.split(' — ')[0] || "AGENTTI").toUpperCase();
|
||||||
|
|
||||||
|
// Hälytys / Virhe
|
||||||
|
if (pLow.includes('todo') || pLow.includes('viallinen') || pLow.includes('virhe')) {
|
||||||
|
wrap.classList.add('state-alert');
|
||||||
|
gel.classList.add('state-alert');
|
||||||
|
wrap.setAttribute('data-tooltip', `❗ ${agentTitle}: "Kriittinen virhe ohjeistuksessa!"\n(Koodissa tai promptissa esiintyy teksti TODO, viallinen tai virhe. Korjaa ohje välittömästi.)`);
|
||||||
|
}
|
||||||
|
// Kysyttävää / Hämmennys
|
||||||
|
else if (prompt.trim().length <= 15 || prompt.includes('?')) {
|
||||||
|
wrap.classList.add('state-question');
|
||||||
|
gel.classList.add('state-question');
|
||||||
|
|
||||||
|
const questions = {
|
||||||
|
client: 'Millaisia uusia ominaisuuksia tuotteessa pitäisi olla?',
|
||||||
|
manager: 'Kuka asiantuntijoista ottaa vastuulleen tämän taskin?',
|
||||||
|
coder: 'Käytetäänkö tässä komponentissa uusinta React-ohjetta?',
|
||||||
|
data: 'Millainen tietorakenne käyttäjästä tallennetaan kantaan?',
|
||||||
|
qa: 'Onko tälle koodille olemassa jo kattavat yksikkötestit?',
|
||||||
|
tester: 'Mihin haluaisit julkaista tämän laiteympäristön version?',
|
||||||
|
observer: 'Mitä laatumetriikkoja minun tulisi ensisijaisesti painottaa?'
|
||||||
|
};
|
||||||
|
const exampleQ = questions[agent] || 'Mitä minun pitäisi tehdä seuraavaksi?';
|
||||||
|
|
||||||
|
const reason = prompt.trim().length <= 15 ? 'Määrittely on tällä hetkellä liian lyhyt.' : 'Ohje on jätetty avoimeksi (? -merkki).';
|
||||||
|
wrap.setAttribute('data-tooltip', `❓ ${agentTitle}: "${exampleQ}"\n\n(Agentti odottaa päätöstäsi: ${reason})`);
|
||||||
|
}
|
||||||
|
// Normaali keskustelu aktiivisena
|
||||||
|
else if (selectedAgents.has(agent)) {
|
||||||
|
// Haetaan satunnainen "toinen agentti", johon viitata
|
||||||
|
// Tehdään tästä deterministinen agentin nimen perusteella, ettei vilku
|
||||||
|
const targets = {
|
||||||
|
client: 'Managerin',
|
||||||
|
manager: 'Asiakkaan',
|
||||||
|
coder: 'DevOpsin',
|
||||||
|
data: 'Koodarin',
|
||||||
|
qa: 'Data-Agentin',
|
||||||
|
tester: 'QA:n',
|
||||||
|
observer: 'Managerin'
|
||||||
|
};
|
||||||
|
const targetName = targets[agent] || 'Koodarin';
|
||||||
|
wrap.setAttribute('data-tooltip', `💬 ${agentTitle}: "Hei, minäkin haluan osallistua!\nVoisin tehdä ${targetName} asiaan tällaisen toiminnallisuuden!"`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tarkistetaan heti alussa
|
||||||
|
setTimeout(checkAgentConfusion, 100);
|
||||||
|
|
||||||
window.switchMainTab = function(tab) {
|
window.switchMainTab = function(tab) {
|
||||||
document.querySelectorAll('.main-panel').forEach(p => p.classList.remove('active'));
|
document.querySelectorAll('.main-panel').forEach(p => p.classList.remove('active'));
|
||||||
|
|||||||
Reference in New Issue
Block a user