feat: ship agent-first mission control v0.2.57
This commit is contained in:
@@ -1,24 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* AlertBar — Status-Übersicht im V2 Dashboard
|
||||
*
|
||||
* Props:
|
||||
* activeCount – Agents mit status 'work'
|
||||
* thinkCount – Agents mit status 'think'
|
||||
* idleCount – Agents mit status 'idle'
|
||||
* blockerCount – Blocker-Anzahl
|
||||
* todayCost – Kosten heute (z.B. "$6.40")
|
||||
* todayTokens – Token heute (z.B. "282k")
|
||||
*/
|
||||
import { icons } from '../../../composables/icons'
|
||||
import { CircleDollarSign } from '@lucide/vue'
|
||||
|
||||
defineProps<{
|
||||
activeCount: number
|
||||
thinkCount: number
|
||||
idleCount: number
|
||||
blockerCount: number
|
||||
todayCost: string
|
||||
todayTokens: string
|
||||
todayCost: string | null
|
||||
todayTokens: string | null
|
||||
blockerLabel?: string
|
||||
}>()
|
||||
|
||||
@@ -28,97 +17,113 @@ defineEmits<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="alertbar glass-panel">
|
||||
<!-- Active (arbeitet) -->
|
||||
<div class="seg">
|
||||
<span class="dot work"></span>
|
||||
<span class="seg-label">{{ activeCount }} arbeiten</span>
|
||||
<section class="alertbar glass-panel" aria-label="Operationsstatus">
|
||||
<div class="status-cluster">
|
||||
<span class="seg" :aria-label="`${activeCount} Agents arbeiten`">
|
||||
<span class="dot work" aria-hidden="true"></span>
|
||||
<strong>{{ activeCount }}</strong>
|
||||
<span class="seg-label">aktiv</span>
|
||||
</span>
|
||||
|
||||
<span class="seg" :aria-label="`${thinkCount} Agents planen`">
|
||||
<span class="dot think" aria-hidden="true"></span>
|
||||
<strong>{{ thinkCount }}</strong>
|
||||
<span class="seg-label">planen</span>
|
||||
</span>
|
||||
|
||||
<span class="seg idle-seg" :aria-label="`${idleCount} Agents sind bereit`">
|
||||
<span class="dot idle" aria-hidden="true"></span>
|
||||
<strong>{{ idleCount }}</strong>
|
||||
<span class="seg-label">bereit</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Think (plant) -->
|
||||
<div class="seg">
|
||||
<span class="dot think"></span>
|
||||
<span class="seg-label">{{ thinkCount }} planen</span>
|
||||
<div
|
||||
v-if="todayCost || todayTokens"
|
||||
class="cost-seg"
|
||||
:aria-label="`Gemeldete Gateway-Telemetrie: ${todayCost || 'keine Kosten'}, ${todayTokens || 'keine'} Token`"
|
||||
>
|
||||
<CircleDollarSign class="seg-icon" :size="13" aria-hidden="true" />
|
||||
<span class="today-label">Gemeldet</span>
|
||||
<strong v-if="todayCost" class="cost-value">{{ todayCost }}</strong>
|
||||
<span v-if="todayTokens" class="token-value"><template v-if="todayCost">· </template>{{ todayTokens }} Token</span>
|
||||
</div>
|
||||
|
||||
<!-- Idle (bereit) -->
|
||||
<div class="seg">
|
||||
<span class="dot idle"></span>
|
||||
<span class="seg-label">{{ idleCount }} bereit</span>
|
||||
</div>
|
||||
|
||||
<!-- Separator -->
|
||||
<div class="sep"></div>
|
||||
|
||||
<!-- Kosten heute -->
|
||||
<div class="seg tx2">
|
||||
<span class="seg-icon" v-html="icons.coin || ''"></span>
|
||||
heute <span class="cost-value">{{ todayCost }}</span> · {{ todayTokens }}
|
||||
</div>
|
||||
|
||||
<!-- Blocker Alert (rechts) -->
|
||||
<button
|
||||
v-if="blockerCount > 0"
|
||||
class="blk"
|
||||
type="button"
|
||||
:aria-label="blockerLabel || `${blockerCount} Blocker`"
|
||||
@click="$emit('blockerClick')"
|
||||
>
|
||||
<span class="dot block"></span>
|
||||
{{ blockerLabel || `${blockerCount} Blocker` }}
|
||||
<span class="dot block" aria-hidden="true"></span>
|
||||
<span class="blocker-full">{{ blockerLabel || `${blockerCount} Blocker` }}</span>
|
||||
<span class="blocker-short">{{ blockerCount }} Blocker</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.alertbar {
|
||||
min-height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 6px 8px 6px 12px;
|
||||
border-radius: var(--r);
|
||||
flex: 0 0 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.status-cluster {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
padding: 11px 16px;
|
||||
border-radius: var(--r);
|
||||
flex-wrap: wrap;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.seg {
|
||||
display: flex;
|
||||
.seg,
|
||||
.cost-seg {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12.5px;
|
||||
font-weight: 600;
|
||||
color: var(--tx);
|
||||
gap: 6px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.seg-label {
|
||||
font-family: var(--font-body);
|
||||
font-size: 11.5px;
|
||||
color: var(--tx-2);
|
||||
}
|
||||
|
||||
.seg-icon :deep(svg) {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
flex: 0 0 auto;
|
||||
color: var(--a-mid);
|
||||
.seg strong {
|
||||
color: var(--tx);
|
||||
font-family: var(--font-mono-v2);
|
||||
font-size: 11px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.tx2 .seg-icon :deep(svg) {
|
||||
.seg-icon {
|
||||
display: flex;
|
||||
color: var(--tx-3);
|
||||
}
|
||||
|
||||
.seg-icon :deep(svg) {
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.dot.work {
|
||||
background: var(--st-work);
|
||||
box-shadow: 0 0 0 0 rgba(61,220,151,.55);
|
||||
animation: pulse-work 1.8s infinite;
|
||||
}
|
||||
|
||||
.dot.think {
|
||||
background: var(--st-think);
|
||||
box-shadow: 0 0 0 0 rgba(52,214,245,.55);
|
||||
animation: pulse-think 1.8s infinite;
|
||||
}
|
||||
|
||||
@@ -128,19 +133,18 @@ defineEmits<{
|
||||
|
||||
.dot.block {
|
||||
background: var(--st-block);
|
||||
box-shadow: 0 0 0 0 rgba(251,113,133,.55);
|
||||
animation: pulse-block 1.8s infinite;
|
||||
}
|
||||
|
||||
.sep {
|
||||
width: 1px;
|
||||
height: 20px;
|
||||
background: var(--line-2);
|
||||
flex: 0 0 auto;
|
||||
.cost-seg {
|
||||
min-width: 0;
|
||||
padding-left: 16px;
|
||||
border-left: 1px solid var(--line-2);
|
||||
}
|
||||
|
||||
.cost-value {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-family: var(--font-mono-v2);
|
||||
font-size: 11px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
background: var(--grad);
|
||||
-webkit-background-clip: text;
|
||||
@@ -148,45 +152,105 @@ defineEmits<{
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.token-value {
|
||||
color: var(--tx-3);
|
||||
font-family: var(--font-mono-v2);
|
||||
font-size: 10.5px;
|
||||
}
|
||||
|
||||
.blk {
|
||||
margin-left: auto;
|
||||
max-width: 42%;
|
||||
min-height: 32px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
padding: 6px 12px;
|
||||
gap: 8px;
|
||||
padding: 0 11px;
|
||||
border-radius: 9px;
|
||||
background: rgba(251,113,133,.12);
|
||||
border: 1px solid rgba(251,113,133,.3);
|
||||
font-size: 12.5px;
|
||||
font-weight: 600;
|
||||
background: var(--status-block-bg);
|
||||
border: 1px solid var(--status-block-line);
|
||||
color: #fda4b0;
|
||||
cursor: pointer;
|
||||
transition: background .15s;
|
||||
font-family: 'Manrope', sans-serif;
|
||||
transition: background .15s, border-color .15s;
|
||||
font-family: var(--font-body);
|
||||
font-size: 11.5px;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.blk:hover {
|
||||
background: rgba(251,113,133,.22);
|
||||
background: rgba(251, 113, 133, .18);
|
||||
border-color: rgba(251, 113, 133, .42);
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.blk:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: var(--focus-ring);
|
||||
}
|
||||
|
||||
.blocker-full {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.blocker-short {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.alertbar {
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
padding: 10px;
|
||||
gap: 10px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.seg {
|
||||
flex: 0 0 calc(50% - 4px);
|
||||
.status-cluster {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.sep {
|
||||
.cost-seg {
|
||||
margin-left: auto;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.today-label,
|
||||
.token-value,
|
||||
.blocker-full {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.blocker-short {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.blk {
|
||||
margin-left: 0;
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 460px) {
|
||||
.alertbar {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.status-cluster {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.idle-seg,
|
||||
.cost-seg {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.blk {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.dot {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user