|
@@ -4,17 +4,7 @@ import {useSSE} from '@/composables/useSSE'
|
|
|
import {getClients} from '@/api/client'
|
|
import {getClients} from '@/api/client'
|
|
|
import type {PortState, StatusMessage} from '@/types'
|
|
import type {PortState, StatusMessage} from '@/types'
|
|
|
import PortStatusCard from '@/components/PortStatusCard.vue'
|
|
import PortStatusCard from '@/components/PortStatusCard.vue'
|
|
|
-import ReasoningCard from '@/components/ReasoningCard.vue'
|
|
|
|
|
-import BusyCard from '@/components/BusyCard.vue'
|
|
|
|
|
-import IdleCard from '@/components/IdleCard.vue'
|
|
|
|
|
-import RetryCard from '@/components/RetryCard.vue'
|
|
|
|
|
-import PendingCard from '@/components/PendingCard.vue'
|
|
|
|
|
-import UsingToolCard from '@/components/UsingToolCard.vue'
|
|
|
|
|
-import RunningCard from '@/components/RunningCard.vue'
|
|
|
|
|
-import CompletedCard from '@/components/CompletedCard.vue'
|
|
|
|
|
-import SessionCompletedCard from '@/components/SessionCompletedCard.vue'
|
|
|
|
|
-import PermissionCard from '@/components/PermissionCard.vue'
|
|
|
|
|
-import ErrorCard from '@/components/ErrorCard.vue'
|
|
|
|
|
|
|
+import StatusCard from '@/components/StatusCard.vue'
|
|
|
|
|
|
|
|
const { connected, onMessage, onDisconnect } = useSSE('/api/events')
|
|
const { connected, onMessage, onDisconnect } = useSSE('/api/events')
|
|
|
|
|
|
|
@@ -34,11 +24,6 @@ function sendNotification(port: number, status: string) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const removeListener = onMessage((msg: StatusMessage) => {
|
|
const removeListener = onMessage((msg: StatusMessage) => {
|
|
|
- const prev = ports.get(msg.port)
|
|
|
|
|
- if (prev && msg.code === 'session_completed' && prev.code !== 'session_completed' && !notified.has(msg.port)) {
|
|
|
|
|
- notified.add(msg.port)
|
|
|
|
|
- sendNotification(msg.port, msg.status)
|
|
|
|
|
- }
|
|
|
|
|
if (msg.code === 'idle') {
|
|
if (msg.code === 'idle') {
|
|
|
notified.delete(msg.port)
|
|
notified.delete(msg.port)
|
|
|
}
|
|
}
|
|
@@ -119,23 +104,20 @@ onUnmounted(removeDisconnectListener)
|
|
|
<a-empty v-if="portList.length === 0" description="等待端口状态推送..." />
|
|
<a-empty v-if="portList.length === 0" description="等待端口状态推送..." />
|
|
|
|
|
|
|
|
<div class="port-grid">
|
|
<div class="port-grid">
|
|
|
- <PortStatusCard v-for="port in portList" :key="port.port" :state="port" />
|
|
|
|
|
|
|
+ <div v-for="port in portList" :key="port.port" class="port-item">
|
|
|
|
|
+ <StatusCard :code="port.code" class="port-anim"/>
|
|
|
|
|
+ <PortStatusCard :state="port" class="port-info"/>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div style="margin-top: 24px;">
|
|
<div style="margin-top: 24px;">
|
|
|
- <h3 style="margin-bottom: 12px; color: var(--text-secondary);">调试:动画卡片预览</h3>
|
|
|
|
|
- <div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px;">
|
|
|
|
|
- <ReasoningCard style="grid-column: span 2;"/>
|
|
|
|
|
- <BusyCard style="grid-column: span 2;"/>
|
|
|
|
|
- <IdleCard style="grid-column: span 2;"/>
|
|
|
|
|
- <RetryCard style="grid-column: span 2;"/>
|
|
|
|
|
- <PendingCard style="grid-column: span 2;"/>
|
|
|
|
|
- <UsingToolCard style="grid-column: span 2;"/>
|
|
|
|
|
- <RunningCard style="grid-column: span 2;"/>
|
|
|
|
|
- <CompletedCard style="grid-column: span 2;"/>
|
|
|
|
|
- <SessionCompletedCard style="grid-column: span 2;"/>
|
|
|
|
|
- <PermissionCard style="grid-column: span 2;"/>
|
|
|
|
|
- <ErrorCard style="grid-column: span 2;"/>
|
|
|
|
|
|
|
+ <h3 style="margin-bottom: 12px; color: var(--text-secondary);">动画卡片预览</h3>
|
|
|
|
|
+ <div class="preview-grid">
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-for="code in (['reasoning','busy','idle','retry','pending','using_tool','running','completed','permission','error'] as const)"
|
|
|
|
|
+ :key="code" class="preview-item">
|
|
|
|
|
+ <StatusCard :code="code"/>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -192,10 +174,37 @@ onUnmounted(removeDisconnectListener)
|
|
|
|
|
|
|
|
.port-grid {
|
|
.port-grid {
|
|
|
display: grid;
|
|
display: grid;
|
|
|
- grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
|
|
|
|
|
+ grid-template-columns: 1fr;
|
|
|
gap: 16px;
|
|
gap: 16px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.port-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+ align-items: stretch;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.port-anim {
|
|
|
|
|
+ aspect-ratio: 3 / 1;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.port-info {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.preview-grid {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
|
|
|
+ gap: 16px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.preview-item {
|
|
|
|
|
+ aspect-ratio: 3 / 1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
@media (max-width: 767px) {
|
|
@media (max-width: 767px) {
|
|
|
.stats-bar {
|
|
.stats-bar {
|
|
|
flex-wrap: wrap;
|
|
flex-wrap: wrap;
|
|
@@ -221,7 +230,7 @@ onUnmounted(removeDisconnectListener)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.port-grid {
|
|
.port-grid {
|
|
|
- grid-template-columns: repeat(2, 1fr);
|
|
|
|
|
|
|
+ grid-template-columns: 1fr;
|
|
|
gap: 12px;
|
|
gap: 12px;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|