From 560866e268e7b472633e2d09a245a1d2494744f5 Mon Sep 17 00:00:00 2001 From: ZenchantLive Date: Tue, 17 Feb 2026 12:53:30 -0800 Subject: [PATCH] social ui refresh: tiered left rail, borderless depth, status-colored right rail --- src/components/activity/activity-panel.tsx | 221 +++++++++++---- src/components/shared/left-panel.tsx | 252 ++++++++++------- src/components/shared/right-panel.tsx | 14 +- src/components/shared/top-bar.tsx | 12 +- src/components/social/social-card.tsx | 301 ++++++++++++--------- src/components/social/social-page.tsx | 268 ++++++++++-------- 6 files changed, 671 insertions(+), 397 deletions(-) diff --git a/src/components/activity/activity-panel.tsx b/src/components/activity/activity-panel.tsx index 309f8e7..5828d9c 100644 --- a/src/components/activity/activity-panel.tsx +++ b/src/components/activity/activity-panel.tsx @@ -4,13 +4,26 @@ import { useEffect, useState, useMemo } from 'react'; import type { BeadIssue } from '../../lib/types'; import type { ActivityEvent } from '../../lib/activity'; import { Avatar, AvatarFallback } from '@/components/ui/avatar'; -import { Badge } from '@/components/ui/badge'; import { ScrollArea } from '@/components/ui/scroll-area'; -import { Separator } from '@/components/ui/separator'; import { cn } from '@/lib/utils'; type AgentStatus = 'active' | 'stale' | 'stuck' | 'dead'; +type AgentTone = { + cardClass: string; + labelClass: string; + ringClass: string; + glowClass: string; +}; + +type EventTone = { + label: string; + labelClass: string; + dotClass: string; + cardClass: string; + idClass: string; +}; + interface AgentRosterEntry { name: string; status: AgentStatus; @@ -94,21 +107,136 @@ function formatRelativeTime(timestamp: string): string { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } -// Get event kind icon/color -function getEventKindInfo(kind: string): { label: string; color: string } { - const events: Record = { - created: { label: 'Created', color: 'text-emerald-500' }, - closed: { label: 'Closed', color: 'text-amber-500' }, - reopened: { label: 'Reopened', color: 'text-blue-500' }, - status_changed: { label: 'Status changed', color: 'text-cyan-500' }, - priority_changed: { label: 'Priority changed', color: 'text-purple-500' }, - assignee_changed: { label: 'Assigned', color: 'text-indigo-500' }, - heartbeat: { label: 'Heartbeat', color: 'text-muted-foreground' }, - dependency_added: { label: 'Dependency added', color: 'text-orange-500' }, - dependency_removed: { label: 'Dependency removed', color: 'text-red-500' }, +function getAgentTone(status: AgentStatus): AgentTone { + const tones: Record = { + active: { + cardClass: 'bg-[radial-gradient(circle_at_86%_18%,rgba(124,185,122,0.28),transparent_58%),rgba(45,64,47,0.74)]', + labelClass: 'text-[#7CB97A]', + ringClass: 'ring-[#7CB97A]/45', + glowClass: 'bg-[#7CB97A]/30', + }, + stale: { + cardClass: 'bg-[radial-gradient(circle_at_86%_18%,rgba(212,165,116,0.28),transparent_58%),rgba(73,61,46,0.74)]', + labelClass: 'text-[#D4A574]', + ringClass: 'ring-[#D4A574]/45', + glowClass: 'bg-[#D4A574]/30', + }, + stuck: { + cardClass: 'bg-[radial-gradient(circle_at_86%_18%,rgba(201,122,122,0.28),transparent_58%),rgba(74,52,54,0.76)]', + labelClass: 'text-[#C97A7A]', + ringClass: 'ring-[#C97A7A]/45', + glowClass: 'bg-[#C97A7A]/30', + }, + dead: { + cardClass: 'bg-[radial-gradient(circle_at_86%_18%,rgba(136,104,112,0.26),transparent_58%),rgba(60,55,60,0.74)]', + labelClass: 'text-[#A78A94]', + ringClass: 'ring-[#A78A94]/40', + glowClass: 'bg-[#A78A94]/25', + }, }; - - return events[kind] || { label: kind.replace(/_/g, ' '), color: 'text-muted-foreground' }; + + return tones[status]; +} + +// reopened=blue, closed=amber, created/opened=green, others semantic +function getEventTone(kind: string): EventTone { + const normalized = kind.toLowerCase(); + const byKind: Record = { + created: { + label: 'Created', + labelClass: 'text-[#7CB97A]', + dotClass: 'bg-[#7CB97A]', + cardClass: 'bg-[radial-gradient(circle_at_88%_18%,rgba(124,185,122,0.26),transparent_55%),rgba(42,62,44,0.68)]', + idClass: 'text-[#9ACB98]', + }, + opened: { + label: 'Opened', + labelClass: 'text-[#7CB97A]', + dotClass: 'bg-[#7CB97A]', + cardClass: 'bg-[radial-gradient(circle_at_88%_18%,rgba(124,185,122,0.26),transparent_55%),rgba(42,62,44,0.68)]', + idClass: 'text-[#9ACB98]', + }, + closed: { + label: 'Closed', + labelClass: 'text-[#D4A574]', + dotClass: 'bg-[#D4A574]', + cardClass: 'bg-[radial-gradient(circle_at_88%_18%,rgba(212,165,116,0.28),transparent_55%),rgba(66,56,44,0.7)]', + idClass: 'text-[#DAB891]', + }, + reopened: { + label: 'Reopened', + labelClass: 'text-[#5B95E8]', + dotClass: 'bg-[#5B95E8]', + cardClass: 'bg-[radial-gradient(circle_at_88%_18%,rgba(91,149,232,0.3),transparent_55%),rgba(42,51,66,0.7)]', + idClass: 'text-[#8DB4EF]', + }, + status_changed: { + label: 'Status changed', + labelClass: 'text-[#D4A574]', + dotClass: 'bg-[#D4A574]', + cardClass: 'bg-[radial-gradient(circle_at_88%_18%,rgba(212,165,116,0.24),transparent_55%),rgba(63,54,44,0.68)]', + idClass: 'text-[#DAB891]', + }, + priority_changed: { + label: 'Priority changed', + labelClass: 'text-[#D4A574]', + dotClass: 'bg-[#D4A574]', + cardClass: 'bg-[radial-gradient(circle_at_88%_18%,rgba(212,165,116,0.24),transparent_55%),rgba(63,54,44,0.68)]', + idClass: 'text-[#DAB891]', + }, + assignee_changed: { + label: 'Assigned', + labelClass: 'text-[#D4A574]', + dotClass: 'bg-[#D4A574]', + cardClass: 'bg-[radial-gradient(circle_at_88%_18%,rgba(212,165,116,0.24),transparent_55%),rgba(63,54,44,0.68)]', + idClass: 'text-[#DAB891]', + }, + dependency_added: { + label: 'Dependency added', + labelClass: 'text-[#D4A574]', + dotClass: 'bg-[#D4A574]', + cardClass: 'bg-[radial-gradient(circle_at_88%_18%,rgba(212,165,116,0.24),transparent_55%),rgba(63,54,44,0.68)]', + idClass: 'text-[#DAB891]', + }, + dependency_removed: { + label: 'Dependency removed', + labelClass: 'text-[#C97A7A]', + dotClass: 'bg-[#C97A7A]', + cardClass: 'bg-[radial-gradient(circle_at_88%_18%,rgba(201,122,122,0.24),transparent_55%),rgba(65,47,50,0.7)]', + idClass: 'text-[#D9A9A9]', + }, + heartbeat: { + label: 'Heartbeat', + labelClass: 'text-[#5BA8A0]', + dotClass: 'bg-[#5BA8A0]', + cardClass: 'bg-[radial-gradient(circle_at_88%_18%,rgba(91,168,160,0.26),transparent_55%),rgba(42,58,60,0.7)]', + idClass: 'text-[#8BC9C1]', + }, + commented: { + label: 'Commented', + labelClass: 'text-[#5BA8A0]', + dotClass: 'bg-[#5BA8A0]', + cardClass: 'bg-[radial-gradient(circle_at_88%_18%,rgba(91,168,160,0.26),transparent_55%),rgba(42,58,60,0.7)]', + idClass: 'text-[#8BC9C1]', + }, + comment_added: { + label: 'Commented', + labelClass: 'text-[#5BA8A0]', + dotClass: 'bg-[#5BA8A0]', + cardClass: 'bg-[radial-gradient(circle_at_88%_18%,rgba(91,168,160,0.26),transparent_55%),rgba(42,58,60,0.7)]', + idClass: 'text-[#8BC9C1]', + }, + }; + + return ( + byKind[normalized] || { + label: normalized.replace(/_/g, ' '), + labelClass: 'text-[#5BA8A0]', + dotClass: 'bg-[#5BA8A0]', + cardClass: 'bg-[radial-gradient(circle_at_88%_18%,rgba(91,168,160,0.24),transparent_55%),rgba(42,58,60,0.68)]', + idClass: 'text-[#8BC9C1]', + } + ); } function getInitials(name: string): string { @@ -164,24 +292,24 @@ export function ActivityPanel({ issues, collapsed = false }: ActivityPanelProps) }, []); const activeAgents = agentRoster.filter(a => a.status === 'active').length; - const staleAgents = agentRoster.filter(a => a.status === 'stale').length; - if (collapsed) { return ( -
+
{/* Collapsed Agent Icons with ZFC Rings */}
{agentRoster.slice(0, 6).map(agent => (
{getInitials(agent.name)} @@ -191,14 +319,14 @@ export function ActivityPanel({ issues, collapsed = false }: ActivityPanelProps) ))}
-
+
{/* Activity Pulses */}
{activities.slice(0, 8).map((act) => (
))}
@@ -207,15 +335,15 @@ export function ActivityPanel({ issues, collapsed = false }: ActivityPanelProps) } return ( -
+
{/* AGENT ROSTER SECTION */} -
+

Live Agents

-
+
{activeAgents} ONLINE
@@ -225,16 +353,16 @@ export function ActivityPanel({ issues, collapsed = false }: ActivityPanelProps) ) : (
{agentRoster.map(agent => ( -
+
- + {getInitials(agent.name)} @@ -245,7 +373,7 @@ export function ActivityPanel({ issues, collapsed = false }: ActivityPanelProps)
{agent.status} @@ -262,7 +390,7 @@ export function ActivityPanel({ issues, collapsed = false }: ActivityPanelProps) {/* ACTIVITY FEED SECTION */}
-
+

Telemetry Stream

@@ -280,19 +408,20 @@ export function ActivityPanel({ issues, collapsed = false }: ActivityPanelProps) ) : (
{activities.map((activity) => { - const eventInfo = getEventKindInfo(activity.kind); + const eventTone = getEventTone(activity.kind); return (
-
- -
+
- - {eventInfo.label} + + {eventTone.label} {formatRelativeTime(activity.timestamp)} @@ -304,12 +433,12 @@ export function ActivityPanel({ issues, collapsed = false }: ActivityPanelProps)

- + {activity.beadId} {activity.actor && (
-
+
{activity.actor[0].toUpperCase()}
{activity.actor} @@ -326,4 +455,4 @@ export function ActivityPanel({ issues, collapsed = false }: ActivityPanelProps)
); -} \ No newline at end of file +} diff --git a/src/components/shared/left-panel.tsx b/src/components/shared/left-panel.tsx index 428b109..2c17a77 100644 --- a/src/components/shared/left-panel.tsx +++ b/src/components/shared/left-panel.tsx @@ -77,11 +77,11 @@ function buildEpicTree(issues: BeadIssue[]): EpicNode[] { function StatusIndicator({ status }: { status: string }) { const styles = { - blocked: 'bg-rose-500 shadow-[0_0_8px_#f43f5e]', - in_progress: 'bg-amber-500 shadow-[0_0_8px_#f59e0b]', - ready: 'bg-teal-500 shadow-[0_0_8px_#14b8a6]', - done: 'bg-slate-500', - empty: 'bg-white/10' + blocked: 'bg-[#C97A7A] shadow-[0_0_8px_rgba(201,122,122,0.45)]', + in_progress: 'bg-[#D4A574] shadow-[0_0_8px_rgba(212,165,116,0.45)]', + ready: 'bg-[#7CB97A] shadow-[0_0_8px_rgba(124,185,122,0.45)]', + done: 'bg-[var(--status-closed)]', + empty: 'bg-white/10', }[status] || 'bg-slate-500'; return
; @@ -96,6 +96,9 @@ export function LeftPanel({ const { isDesktop, isTablet } = useResponsive(); const epicTree = useMemo(() => buildEpicTree(issues), [issues]); + const featuredEpics = useMemo(() => epicTree.slice(0, 2), [epicTree]); + const standardEpics = useMemo(() => epicTree.slice(2, 6), [epicTree]); + const compactEpics = useMemo(() => epicTree.slice(6), [epicTree]); const toggleEpic = (epicId: string) => { setExpandedEpics(prev => { @@ -116,7 +119,7 @@ export function LeftPanel({ if (isTablet) { return ( -
+
{epicTree.map(({ epic, status }) => ( + ); + } - return ( -
- + + {isExpanded && children.length > 0 && ( +
+ {children.slice(0, 5).map((child) => ( +
+ {child.id} +
+ {child.title} + +
+
+ ))} + {children.length > 5 && ( +
+{children.length - 5} more
+ )} +
)}
- -
- {epic.title} -
- - {/* Progress / Stats Bar */} -
-
-
-
-
-
- -
- {Math.round(((stats.closed + stats.in_progress) / (stats.total || 1)) * 100)}% Done - {stats.total} Tasks -
-
- - - {/* Sub-items (Tasks) */} - {isExpanded && children.length > 0 && ( -
- {children.slice(0, 5).map(child => ( -
- {child.id} -
- {child.title} - -
-
- ))} - {children.length > 5 && ( -
+{children.length - 5} more
- )} -
- )} + ); + })}
- ); - })} +
+ ))}
{/* Footer */} -
-