Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 71b4465595 | |||
| 9b63e5368e | |||
| 8f265d00ba | |||
| 5a3a099b94 | |||
| 1f6f5dd08c | |||
| 6e532f64f5 |
@@ -71,6 +71,31 @@ defineEmits<{
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Live Thinking -->
|
||||
<section class="modal-section">
|
||||
<h3 class="section-label">Live Thinking</h3>
|
||||
<div class="thinking-panel">
|
||||
<div class="thinking-orbiter">
|
||||
<span class="orbiter-dot dot-blue"></span>
|
||||
<span class="orbiter-dot dot-violet"></span>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
<div v-if="!agent.thinkingStream?.length" class="thinking-placeholder">
|
||||
Waiting for thought stream...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Footer Stats -->
|
||||
<div class="modal-footer">
|
||||
<span class="footer-badge">Runtime: {{ runtime }}</span>
|
||||
@@ -241,6 +266,91 @@ defineEmits<{
|
||||
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.2); box-shadow: 0 0 8px rgba(139,124,246,0.05); }
|
||||
50% { border-color: rgba(139, 124, 246, 0.35); box-shadow: 0 0 16px rgba(139,124,246,0.1); }
|
||||
}
|
||||
|
||||
.thinking-orbiter {
|
||||
position: absolute;
|
||||
inset: -8px;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
animation: spin-orbiter 4s linear infinite;
|
||||
}
|
||||
@keyframes spin-orbiter {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
.orbiter-dot {
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
filter: blur(1px);
|
||||
}
|
||||
.dot-blue {
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: #3b82f6;
|
||||
box-shadow: 0 0 10px #3b82f6, 0 0 20px rgba(59,130,246,0.5);
|
||||
}
|
||||
.dot-violet {
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 50%);
|
||||
background: #8b7cf6;
|
||||
box-shadow: 0 0 10px #8b7cf6, 0 0 20px rgba(139,124,246,0.5);
|
||||
}
|
||||
|
||||
.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 */
|
||||
.work-feed {
|
||||
display: flex;
|
||||
|
||||
@@ -334,7 +334,7 @@ onUnmounted(() => {
|
||||
<!-- Cards Layer (above SVG) -->
|
||||
<div class="cards-layer">
|
||||
<!-- Hero: Iris centered top -->
|
||||
<div class="hero-slot" data-agent-id="iris">
|
||||
<div class="hero-slot" :data-agent-id="hero.id">
|
||||
<article
|
||||
class="agent-card hero-card"
|
||||
:style="{
|
||||
|
||||
@@ -14,6 +14,7 @@ export interface AgentNodeData {
|
||||
active: boolean
|
||||
runtimeSeconds: number
|
||||
workingFeed: string[]
|
||||
thinkingStream?: Array<{ time: string; text: string }>
|
||||
}
|
||||
|
||||
export interface MissionData {
|
||||
@@ -81,6 +82,7 @@ export function useDashboardData() {
|
||||
|
||||
// Agent runtimes (simulated)
|
||||
const agentStartTimes = reactive<Record<string, number>>({
|
||||
iris: now - 28800000,
|
||||
developer: now - 3600000,
|
||||
devops: now - 1800000,
|
||||
researcher: now - 2700000,
|
||||
@@ -98,6 +100,31 @@ export function useDashboardData() {
|
||||
|
||||
// Agents
|
||||
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',
|
||||
name: 'Developer',
|
||||
@@ -117,6 +144,11 @@ export function useDashboardData() {
|
||||
'Implementing room generation algorithm',
|
||||
'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',
|
||||
@@ -137,6 +169,11 @@ export function useDashboardData() {
|
||||
'Added .dockerignore for node_modules',
|
||||
'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',
|
||||
@@ -156,6 +193,11 @@ export function useDashboardData() {
|
||||
'Documented SignalR limitations',
|
||||
'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',
|
||||
@@ -176,6 +218,11 @@ export function useDashboardData() {
|
||||
'Approved RoomValidator',
|
||||
'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,11 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted } from 'vue'
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
import MissionCard from '../components/dashboard/MissionCard.vue'
|
||||
import OperationsFeed from '../components/dashboard/OperationsFeed.vue'
|
||||
import TeamNetwork from '../components/dashboard/TeamNetwork.vue'
|
||||
import ChatPanel from '../components/dashboard/ChatPanel.vue'
|
||||
import QueuePanel from '../components/dashboard/QueuePanel.vue'
|
||||
import AgentModal from '../components/dashboard/AgentModal.vue'
|
||||
import { useDashboardData } from '../composables/useDashboardData'
|
||||
import type { AgentNodeData } from '../../composables/useDashboardData'
|
||||
|
||||
const {
|
||||
agents, missions, feedEntries, chatMessages,
|
||||
@@ -14,6 +16,13 @@ const {
|
||||
sendChat, removeQueueItem, moveQueueItem, changeQueuePriority,
|
||||
} = 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)
|
||||
onUnmounted(stopRuntime)
|
||||
|
||||
@@ -58,10 +67,12 @@ function onQueueExecuteNow(id: string): void {
|
||||
</div>
|
||||
|
||||
<TeamNetwork
|
||||
hero-id="iris"
|
||||
:agents="agents"
|
||||
:iris-runtime="irisRuntime"
|
||||
:get-agent-runtime="getAgentRuntime"
|
||||
:iris-focus="irisFocus"
|
||||
@select="onAgentSelect"
|
||||
/>
|
||||
|
||||
<!-- Legend -->
|
||||
@@ -84,6 +95,13 @@ function onQueueExecuteNow(id: string): void {
|
||||
<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" />
|
||||
</div>
|
||||
|
||||
<AgentModal
|
||||
v-if="selectedAgent"
|
||||
:agent="selectedAgent"
|
||||
:runtime="getAgentRuntime(selectedAgent.id)"
|
||||
@close="selectedAgent = null"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user