Compare commits

..

12 Commits

Author SHA1 Message Date
devops 3599513128 chore: bump version to v0.2.23 [skip ci] 2026-06-09 20:36:05 +00:00
developer 7dd8f53f2f fix: Dots rechts vom Thinking-Text + Glow intensiver + Link-Button
CI - Build & Test / Backend (.NET) (push) Successful in 25s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 16s
CI - Build & Test / Security Check (push) Successful in 2s
- Dots (blau+lila) inline rechts vom aktuellen Thinking-Eintrag, rotierend
- Orbiter-Rahmen entfernt
- Panel-Glow verstärkt (border 0.5, shadow 24px+40px)
- ExternalLink-Button rechts vom Agent-Namen → /agents/{id}
2026-06-09 22:35:15 +02:00
devops 90bb7251e3 chore: bump version to v0.2.22 [skip ci] 2026-06-09 20:31:44 +00:00
developer e57bef95e5 fix: mehr Abstand Iris↔Grid + Linien enger gebündelt
CI - Build & Test / Backend (.NET) (push) Successful in 23s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 16s
CI - Build & Test / Security Check (push) Successful in 2s
- gap: 32px → 64px (doppelter Vertikalraum)
- startX: 0.30+0.40 → 0.38+0.24 (enger unter Iris)
- cp1y: startY+40 → startY+70 (tiefer vor Spread)
- cp2x: ±50 → ±35 (sanftere Card-Annäherung)
2026-06-09 22:30:55 +02:00
devops 71b4465595 chore: bump version to v0.2.21 [skip ci] 2026-06-09 20:28:41 +00:00
developer 9b63e5368e feat: Live Thinking Panel im AgentModal
CI - Build & Test / Backend (.NET) (push) Successful in 22s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 15s
CI - Build & Test / Security Check (push) Successful in 3s
- Scrollbarer Thinking-Stream (slide-in von links)
- Pulsierender Rahmen mit Glow-Effekt
- Blau+Violett Dots rotieren im Uhrzeigersinn
- thinkingStream in AgentNodeData + Beispieldaten für alle 5 Agenten
2026-06-09 22:27:53 +02:00
devops 8f265d00ba chore: bump version to v0.2.20 [skip ci] 2026-06-09 20:24:07 +00:00
developer 5a3a099b94 fix: Iris als Hero im AI Team Network – Hierarchie korrigiert
CI - Build & Test / Backend (.NET) (push) Successful in 24s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 16s
CI - Build & Test / Security Check (push) Successful in 2s
- Iris zu agents[] hinzugefügt (Position 0)
- hero-id='iris' an TeamNetwork übergeben
- Hero-Slot data-agent-id dynamisch (:data-agent-id='hero.id')
2026-06-09 22:23:17 +02:00
devops 1f6f5dd08c chore: bump version to v0.2.19 [skip ci] 2026-06-09 20:20:03 +00:00
developer 6e532f64f5 fix: AgentModal bei Klick auf Card verdrahtet
CI - Build & Test / Backend (.NET) (push) Successful in 26s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 17s
CI - Build & Test / Security Check (push) Successful in 3s
- @select Handler auf TeamNetwork
- selectedAgent State + onAgentSelect()
- AgentModal importiert und gerendert (v-if selectedAgent)
- Close via X oder Overlay
2026-06-09 22:19:12 +02:00
devops 7154c30b99 chore: bump version to v0.2.18 [skip ci] 2026-06-09 20:13:46 +00:00
developer ffe7baba78 fix: Vollbreite-Layout – max-width/margin entfernt, Content füllt Desktop-Breite
CI - Build & Test / Backend (.NET) (push) Successful in 24s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 16s
CI - Build & Test / Security Check (push) Successful in 2s
- .content: max-width:1320px + margin:auto entfernt
- padding reduziert: 36px 34px → 16px 16px
- Middle-Col (1fr) im Dashboard nutzt jetzt volle Restbreite
2026-06-09 22:12:55 +02:00
6 changed files with 192 additions and 9 deletions
+1 -1
View File
@@ -1 +1 @@
0.2.17 0.2.23
@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { X } from '@lucide/vue' import { X, ExternalLink } from '@lucide/vue'
import type { AgentNodeData } from '../../composables/useDashboardData' import type { AgentNodeData } from '../../composables/useDashboardData'
defineProps<{ defineProps<{
@@ -27,6 +27,9 @@ defineEmits<{
<span class="modal-role">{{ agent.role }}</span> <span class="modal-role">{{ agent.role }}</span>
</div> </div>
</div> </div>
<a :href="`/agents/${agent.id}`" class="agent-link-btn" title="Open agent config">
<ExternalLink :size="14" />
</a>
<button class="modal-close-btn" @click="$emit('close')" aria-label="Close"> <button class="modal-close-btn" @click="$emit('close')" aria-label="Close">
<X :size="16" /> <X :size="16" />
</button> </button>
@@ -71,6 +74,31 @@ defineEmits<{
</div> </div>
</section> </section>
<!-- Live Thinking -->
<section class="modal-section">
<h3 class="section-label">Live Thinking</h3>
<div class="thinking-panel">
<div class="thinking-stream" ref="thinkingStreamRef">
<div
v-for="(msg, idx) in agent.thinkingStream"
:key="idx"
class="thinking-entry"
:style="{ animationDelay: `${idx * 0.05}s` }"
>
<span class="entry-time">{{ msg.time }}</span>
<span class="entry-text">{{ msg.text }}</span>
<span class="thinking-dots" v-if="idx === agent.thinkingStream.length - 1">
<span class="thinking-dot blue"></span>
<span class="thinking-dot violet"></span>
</span>
</div>
<div v-if="!agent.thinkingStream?.length" class="thinking-placeholder">
Waiting for thought stream...
</div>
</div>
</div>
</section>
<!-- Footer Stats --> <!-- Footer Stats -->
<div class="modal-footer"> <div class="modal-footer">
<span class="footer-badge">Runtime: {{ runtime }}</span> <span class="footer-badge">Runtime: {{ runtime }}</span>
@@ -168,6 +196,25 @@ defineEmits<{
color: #6b7385; color: #6b7385;
font-weight: 500; font-weight: 500;
} }
.agent-link-btn {
display: grid;
place-items: center;
width: 32px;
height: 32px;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 8px;
background: transparent;
color: #6b7385;
cursor: pointer;
transition: all 0.2s;
flex-shrink: 0;
text-decoration: none;
}
.agent-link-btn:hover {
border-color: var(--agent-color);
color: var(--agent-color);
}
.modal-close-btn { .modal-close-btn {
width: 32px; width: 32px;
height: 32px; height: 32px;
@@ -241,6 +288,77 @@ defineEmits<{
transition: width 0.5s ease; transition: width 0.5s ease;
} }
/* Live Thinking */
.thinking-panel {
position: relative;
border: 1px solid rgba(139, 124, 246, 0.2);
border-radius: 12px;
padding: 14px;
background: rgba(12, 16, 22, 0.6);
overflow: hidden;
animation: panel-pulse 2.5s ease-in-out infinite;
}
@keyframes panel-pulse {
0%, 100% { border-color: rgba(139, 124, 246, 0.25); box-shadow: 0 0 12px rgba(139,124,246,0.08); }
50% { border-color: rgba(139, 124, 246, 0.5); box-shadow: 0 0 24px rgba(139,124,246,0.18), 0 0 40px rgba(139,124,246,0.06); }
}
.thinking-dots {
display: inline-flex;
gap: 6px;
margin-left: 8px;
flex-shrink: 0;
animation: spin-dots 2s linear infinite;
}
.thinking-dot {
width: 7px;
height: 7px;
border-radius: 50%;
}
.thinking-dot.blue { background: #3b82f6; box-shadow: 0 0 8px #3b82f6; }
.thinking-dot.violet { background: #8b7cf6; box-shadow: 0 0 8px #8b7cf6; }
@keyframes spin-dots {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.thinking-stream {
max-height: 160px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 6px;
position: relative;
z-index: 1;
}
.thinking-entry {
display: flex;
gap: 10px;
align-items: baseline;
animation: slide-in-right 0.3s ease-out both;
font-size: 10px;
}
@keyframes slide-in-right {
from { opacity: 0; transform: translateX(-16px); }
to { opacity: 1; transform: translateX(0); }
}
.entry-time {
font-size: 8.5px;
color: #6b7385;
flex-shrink: 0;
font-variant-numeric: tabular-nums;
min-width: 42px;
}
.entry-text {
color: #9ea5b3;
line-height: 1.4;
}
.thinking-placeholder {
font-size: 10px;
color: #4a5160;
font-style: italic;
}
/* Working Feed */ /* Working Feed */
.work-feed { .work-feed {
display: flex; display: flex;
@@ -118,7 +118,7 @@ const connectionPaths = computed<Record<string, ConnectionPath | null>>(() => {
// Spread start points across Iris bottom edge (30%-70% range) // Spread start points across Iris bottom edge (30%-70% range)
const t = total > 1 ? idx / (total - 1) : 0.5 const t = total > 1 ? idx / (total - 1) : 0.5
const startX = iris.left + iris.width * (0.30 + t * 0.40) const startX = iris.left + iris.width * (0.38 + t * 0.24)
const startY = iris.bottom - 1 const startY = iris.bottom - 1
// Determine column: left or right of Iris center // Determine column: left or right of Iris center
@@ -130,8 +130,8 @@ const connectionPaths = computed<Record<string, ConnectionPath | null>>(() => {
// Bézier control points // Bézier control points
const cp1x = startX const cp1x = startX
const cp1y = startY + 40 const cp1y = startY + 70
const cp2x = endX + (isLeftColumn ? 50 : -50) const cp2x = endX + (isLeftColumn ? 35 : -35)
const cp2y = endY - 10 const cp2y = endY - 10
const d = `M ${startX} ${startY} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${endX} ${endY}` const d = `M ${startX} ${startY} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${endX} ${endY}`
@@ -334,7 +334,7 @@ onUnmounted(() => {
<!-- Cards Layer (above SVG) --> <!-- Cards Layer (above SVG) -->
<div class="cards-layer"> <div class="cards-layer">
<!-- Hero: Iris centered top --> <!-- Hero: Iris centered top -->
<div class="hero-slot" data-agent-id="iris"> <div class="hero-slot" :data-agent-id="hero.id">
<article <article
class="agent-card hero-card" class="agent-card hero-card"
:style="{ :style="{
@@ -445,7 +445,7 @@ onUnmounted(() => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 32px; gap: 64px;
} }
.hero-slot { .hero-slot {
@@ -14,6 +14,7 @@ export interface AgentNodeData {
active: boolean active: boolean
runtimeSeconds: number runtimeSeconds: number
workingFeed: string[] workingFeed: string[]
thinkingStream?: Array<{ time: string; text: string }>
} }
export interface MissionData { export interface MissionData {
@@ -81,6 +82,7 @@ export function useDashboardData() {
// Agent runtimes (simulated) // Agent runtimes (simulated)
const agentStartTimes = reactive<Record<string, number>>({ const agentStartTimes = reactive<Record<string, number>>({
iris: now - 28800000,
developer: now - 3600000, developer: now - 3600000,
devops: now - 1800000, devops: now - 1800000,
researcher: now - 2700000, researcher: now - 2700000,
@@ -98,6 +100,31 @@ export function useDashboardData() {
// Agents // Agents
const agents = ref<AgentNodeData[]>([ const agents = ref<AgentNodeData[]>([
{
id: 'iris',
name: 'Iris',
role: 'Chief of Staff',
description: 'Koordiniert, delegiert, hält das Team tight. Die erste Anlaufstelle zwischen Boss und Maschine.',
color: '#8b7cf6',
icon: 'bot',
currentTask: 'Orchestrating Nexus Dashboard redesign',
goal: 'Complete Mission Control v3',
progress: 85,
workload: 55,
active: true,
runtimeSeconds: 28800,
workingFeed: [
'Analyzed user feedback on Dashboard',
'Delegated card redesign to Developer',
'Verifying full-width layout deployment',
'Reviewing AgentModal integration',
],
thinkingStream: [
{ time: '22:24', text: 'Analysing constraint: full-width layout' },
{ time: '22:25', text: 'Removing max-width from global CSS' },
{ time: '22:26', text: 'Verifying Dashboard grid reflow' },
],
},
{ {
id: 'developer', id: 'developer',
name: 'Developer', name: 'Developer',
@@ -117,6 +144,11 @@ export function useDashboardData() {
'Implementing room generation algorithm', 'Implementing room generation algorithm',
'Writing unit tests for RoomFactory', 'Writing unit tests for RoomFactory',
], ],
thinkingStream: [
{ time: '22:22', text: 'Parsing dungeon spec from Iris' },
{ time: '22:23', text: 'Designing RoomFactory interface' },
{ time: '22:24', text: 'Implementing corridor connection logic' },
],
}, },
{ {
id: 'devops', id: 'devops',
@@ -137,6 +169,11 @@ export function useDashboardData() {
'Added .dockerignore for node_modules', 'Added .dockerignore for node_modules',
'Testing incremental builds', 'Testing incremental builds',
], ],
thinkingStream: [
{ time: '22:20', text: 'Checking build cache hit rates' },
{ time: '22:21', text: 'Benchmarking multi-stage vs single-stage' },
{ time: '22:22', text: 'Calculating potential speedup from caching' },
],
}, },
{ {
id: 'researcher', id: 'researcher',
@@ -156,6 +193,11 @@ export function useDashboardData() {
'Documented SignalR limitations', 'Documented SignalR limitations',
'Prototyping WebSocket fallback', 'Prototyping WebSocket fallback',
], ],
thinkingStream: [
{ time: '22:18', text: 'Cross-referencing WebSocket latency benchmarks' },
{ time: '22:19', text: 'Checking SSE browser support matrix' },
{ time: '22:20', text: 'Drafting recommendation summary' },
],
}, },
{ {
id: 'reviewer', id: 'reviewer',
@@ -176,6 +218,11 @@ export function useDashboardData() {
'Approved RoomValidator', 'Approved RoomValidator',
'Running integration tests', 'Running integration tests',
], ],
thinkingStream: [
{ time: '22:15', text: 'Analyzing DungeonController PR diff' },
{ time: '22:16', text: 'Checking RoomValidator edge cases' },
{ time: '22:17', text: 'Verifying integration test coverage' },
],
}, },
]) ])
+1 -1
View File
@@ -41,7 +41,7 @@ main { min-width: 0; }
.connection.live { color: var(--green); } .connection.live { color: var(--green); }
.connection.preview { color: #e6b75d; } .connection.preview { color: #e6b75d; }
.ask, .refresh { display: flex; align-items: center; gap: 7px; padding: 8px 11px; border: 1px solid #37315e; border-radius: 7px; background: #18152a; color: #c4bbff; font-size: 10px; cursor: pointer; } .ask, .refresh { display: flex; align-items: center; gap: 7px; padding: 8px 11px; border: 1px solid #37315e; border-radius: 7px; background: #18152a; color: #c4bbff; font-size: 10px; cursor: pointer; }
.content { max-width: 1320px; margin: auto; padding: 36px 34px 60px; } .content { padding: 16px 16px 60px; }
.page-heading { display: flex; justify-content: space-between; align-items: end; margin-bottom: 28px; } .page-heading { display: flex; justify-content: space-between; align-items: end; margin-bottom: 28px; }
.eyebrow, .kicker { color: #7065c8; font-size: 9px; font-weight: 700; letter-spacing: .18em; } .eyebrow, .kicker { color: #7065c8; font-size: 9px; font-weight: 700; letter-spacing: .18em; }
h1 { margin: 7px 0 5px; font-size: 27px; letter-spacing: -.04em; } h1 { margin: 7px 0 5px; font-size: 27px; letter-spacing: -.04em; }
+19 -1
View File
@@ -1,11 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, onUnmounted } from 'vue' import { onMounted, onUnmounted, ref } from 'vue'
import MissionCard from '../components/dashboard/MissionCard.vue' import MissionCard from '../components/dashboard/MissionCard.vue'
import OperationsFeed from '../components/dashboard/OperationsFeed.vue' import OperationsFeed from '../components/dashboard/OperationsFeed.vue'
import TeamNetwork from '../components/dashboard/TeamNetwork.vue' import TeamNetwork from '../components/dashboard/TeamNetwork.vue'
import ChatPanel from '../components/dashboard/ChatPanel.vue' import ChatPanel from '../components/dashboard/ChatPanel.vue'
import QueuePanel from '../components/dashboard/QueuePanel.vue' import QueuePanel from '../components/dashboard/QueuePanel.vue'
import AgentModal from '../components/dashboard/AgentModal.vue'
import { useDashboardData } from '../composables/useDashboardData' import { useDashboardData } from '../composables/useDashboardData'
import type { AgentNodeData } from '../../composables/useDashboardData'
const { const {
agents, missions, feedEntries, chatMessages, agents, missions, feedEntries, chatMessages,
@@ -14,6 +16,13 @@ const {
sendChat, removeQueueItem, moveQueueItem, changeQueuePriority, sendChat, removeQueueItem, moveQueueItem, changeQueuePriority,
} = useDashboardData() } = useDashboardData()
const selectedAgent = ref<AgentNodeData | null>(null)
function onAgentSelect(id: string) {
const agent = agents.value.find(a => a.id === id)
if (agent) selectedAgent.value = agent
}
onMounted(startRuntime) onMounted(startRuntime)
onUnmounted(stopRuntime) onUnmounted(stopRuntime)
@@ -58,10 +67,12 @@ function onQueueExecuteNow(id: string): void {
</div> </div>
<TeamNetwork <TeamNetwork
hero-id="iris"
:agents="agents" :agents="agents"
:iris-runtime="irisRuntime" :iris-runtime="irisRuntime"
:get-agent-runtime="getAgentRuntime" :get-agent-runtime="getAgentRuntime"
:iris-focus="irisFocus" :iris-focus="irisFocus"
@select="onAgentSelect"
/> />
<!-- Legend --> <!-- Legend -->
@@ -84,6 +95,13 @@ function onQueueExecuteNow(id: string): void {
<ChatPanel :messages="chatMessages" :iris-busy="irisBusy" :iris-focus="irisFocus" @send="onChatSend" /> <ChatPanel :messages="chatMessages" :iris-busy="irisBusy" :iris-focus="irisFocus" @send="onChatSend" />
<QueuePanel :items="queue" @remove="removeQueueItem" @move-up="onQueueMoveUp" @move-down="onQueueMoveDown" @change-priority="changeQueuePriority" @execute-now="onQueueExecuteNow" /> <QueuePanel :items="queue" @remove="removeQueueItem" @move-up="onQueueMoveUp" @move-down="onQueueMoveDown" @change-priority="changeQueuePriority" @execute-now="onQueueExecuteNow" />
</div> </div>
<AgentModal
v-if="selectedAgent"
:agent="selectedAgent"
:runtime="getAgentRuntime(selectedAgent.id)"
@close="selectedAgent = null"
/>
</div> </div>
</template> </template>