From b97f4f0668e0572ed45473faf63cf581d77432c5 Mon Sep 17 00:00:00 2001
From: "cubecraft-agents[bot]"
<3458173+cubecraft-agents[bot]@users.noreply.github.com>
Date: Sun, 26 Apr 2026 13:00:20 +0000
Subject: [PATCH] CUB-48: Add Agent Status Badge component with pulse
animations
- Colored pill badge for Active, Idle, Thinking, Error, Offline statuses
- Color mapping uses CSS custom properties (--status-active, etc.)
- Pulse animations: Active 2s, Thinking 3s, Error 0.8s, Idle/Offline static
- Respects prefers-reduced-motion for accessibility
- Standalone component with OnPush change detection
- Barrel export via index.ts
---
.../agent-status-badge.component.html | 12 ++
.../agent-status-badge.component.scss | 143 ++++++++++++++++++
.../agent-status-badge.component.ts | 51 +++++++
.../components/agent-status-badge/index.ts | 6 +
4 files changed, 212 insertions(+)
create mode 100644 frontend/src/app/components/agent-status-badge/agent-status-badge.component.html
create mode 100644 frontend/src/app/components/agent-status-badge/agent-status-badge.component.scss
create mode 100644 frontend/src/app/components/agent-status-badge/agent-status-badge.component.ts
create mode 100644 frontend/src/app/components/agent-status-badge/index.ts
diff --git a/frontend/src/app/components/agent-status-badge/agent-status-badge.component.html b/frontend/src/app/components/agent-status-badge/agent-status-badge.component.html
new file mode 100644
index 0000000..91d4544
--- /dev/null
+++ b/frontend/src/app/components/agent-status-badge/agent-status-badge.component.html
@@ -0,0 +1,12 @@
+
+
+
+
+ {{ statusLabels[status] }}
+
+
\ No newline at end of file
diff --git a/frontend/src/app/components/agent-status-badge/agent-status-badge.component.scss b/frontend/src/app/components/agent-status-badge/agent-status-badge.component.scss
new file mode 100644
index 0000000..d8e8935
--- /dev/null
+++ b/frontend/src/app/components/agent-status-badge/agent-status-badge.component.scss
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/app/components/agent-status-badge/agent-status-badge.component.ts b/frontend/src/app/components/agent-status-badge/agent-status-badge.component.ts
new file mode 100644
index 0000000..0a7519d
--- /dev/null
+++ b/frontend/src/app/components/agent-status-badge/agent-status-badge.component.ts
@@ -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
+ *
+ *
+ * ```
+ */
+@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 = {
+ 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}`;
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/app/components/agent-status-badge/index.ts b/frontend/src/app/components/agent-status-badge/index.ts
new file mode 100644
index 0000000..bd3aec6
--- /dev/null
+++ b/frontend/src/app/components/agent-status-badge/index.ts
@@ -0,0 +1,6 @@
+// ============================================================================
+// Agent Status Badge — Barrel Export
+// CUB-48
+// ============================================================================
+
+export { AgentStatusBadgeComponent } from './agent-status-badge.component';
\ No newline at end of file