Merge remote-tracking branch 'origin/agent/rex/CUB-48-agent-status-badge' into agent/rex/CUB-45-agent-card-final

This commit is contained in:
cubecraft-agents[bot]
2026-04-26 13:46:56 +00:00
4 changed files with 212 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
<!-- Agent Status Badge: colored pill with pulse animation -->
<span
class="status-badge"
[class]="statusClass"
[attr.aria-label]="statusLabels[status] + ' status'"
[attr.role]="'status'"
>
<span class="status-badge__dot"></span>
<span *ngIf="showLabel" class="status-badge__label">
{{ statusLabels[status] }}
</span>
</span>

View File

@@ -0,0 +1,143 @@
// ============================================================================
// Agent Status Badge — Pill Style with Pulse Animation
// Per CUB-48: Color mapping + animation durations:
// Active → --color-primary (#38BDF8) 2s pulse
// Idle → --color-secondary (#2DD4BF) no animation
// Thinking → --color-accent (#A78BFA) 3s pulse
// Error → --color-danger (#F87171) 0.8s pulse
// ============================================================================
// ---------------------------------------------------------------------------
// Badge Container
// ---------------------------------------------------------------------------
.status-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 12px;
border-radius: 999px;
font-size: 12px;
font-weight: 500;
letter-spacing: 0.02em;
line-height: 1;
white-space: nowrap;
user-select: none;
// --- Status-specific backgrounds & text ---
&--active {
background-color: var(--status-active-bg);
color: var(--status-active);
}
&--idle {
background-color: var(--status-idle-bg);
color: var(--status-idle);
}
&--thinking {
background-color: var(--status-thinking-bg);
color: var(--status-thinking);
}
&--error {
background-color: var(--status-error-bg);
color: var(--status-error);
}
&--offline {
background-color: rgba(100, 116, 139, 0.12);
color: var(--status-offline);
}
}
// ---------------------------------------------------------------------------
// Status Dot (inner indicator circle)
// ---------------------------------------------------------------------------
.status-badge__dot {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
.status-badge--active & {
background-color: var(--status-active);
animation: badge-pulse-active 2s ease-in-out infinite;
}
.status-badge--idle & {
background-color: var(--status-idle);
// Idle: no animation — steady dot
}
.status-badge--thinking & {
background-color: var(--status-thinking);
animation: badge-pulse-thinking 3s ease-in-out infinite;
}
.status-badge--error & {
background-color: var(--status-error);
animation: badge-pulse-error 0.8s ease-in-out infinite;
}
.status-badge--offline & {
background-color: var(--status-offline);
// Offline: no animation
}
}
// ---------------------------------------------------------------------------
// Label Text
// ---------------------------------------------------------------------------
.status-badge__label {
color: inherit;
}
// ---------------------------------------------------------------------------
// Badge Pulse Keyframes
// ---------------------------------------------------------------------------
// These are scoped to the badge component rather than reusing the global
// .status-dot animations, because the badge pulse is subtler (scale + opacity
// blend for a pill context vs. standalone dot context).
// ---------------------------------------------------------------------------
@keyframes badge-pulse-active {
0%,
100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.7;
transform: scale(1.3);
}
}
@keyframes badge-pulse-thinking {
0%,
100% {
opacity: 0.8;
}
50% {
opacity: 0.4;
}
}
@keyframes badge-pulse-error {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.4;
}
}
// ---------------------------------------------------------------------------
// Accessibility: Reduced Motion
// ---------------------------------------------------------------------------
@media (prefers-reduced-motion: reduce) {
.status-badge__dot {
animation: none !important;
}
}

View File

@@ -0,0 +1,51 @@
// ============================================================================
// Agent Status Badge Component
// Per CUB-48: Colored pill badge with pulse animation for agent statuses.
// Displays Active, Idle, Thinking, or Error with correct color mapping.
// ============================================================================
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AgentStatus } from '../../models';
/**
* Reusable status badge that renders a colored pill with label text
* and a subtle pulse animation for Active, Thinking, and Error states.
*
* Usage:
* ```html
* <app-agent-status-badge status="active" />
* <app-agent-status-badge status="error" />
* ```
*/
@Component({
selector: 'app-agent-status-badge',
standalone: true,
imports: [CommonModule],
templateUrl: './agent-status-badge.component.html',
styleUrl: './agent-status-badge.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AgentStatusBadgeComponent {
/** Agent status to display. Maps to a color and animation. */
@Input({ required: true })
status!: AgentStatus;
/** Whether to show the status label text alongside the dot. Defaults to true. */
@Input()
showLabel = true;
/** Mapping from status to human-readable label. */
readonly statusLabels: Record<AgentStatus, string> = {
active: 'Active',
idle: 'Idle',
thinking: 'Thinking',
error: 'Error',
offline: 'Offline',
};
/** CSS class string for the badge container based on current status. */
get statusClass(): string {
return `status-badge--${this.status}`;
}
}

View File

@@ -0,0 +1,6 @@
// ============================================================================
// Agent Status Badge — Barrel Export
// CUB-48
// ============================================================================
export { AgentStatusBadgeComponent } from './agent-status-badge.component';