diff --git a/assets/activity-with-corect-midddle.jpg b/assets/activity-with-corect-midddle.jpg new file mode 100644 index 0000000..c88e473 Binary files /dev/null and b/assets/activity-with-corect-midddle.jpg differ diff --git a/assets/chat-with-incorrect-middle.jpg b/assets/chat-with-incorrect-middle.jpg new file mode 100644 index 0000000..5e51c97 Binary files /dev/null and b/assets/chat-with-incorrect-middle.jpg differ diff --git a/src/components/activity/activity-panel.tsx b/src/components/activity/activity-panel.tsx index 8804a94..337a76b 100644 --- a/src/components/activity/activity-panel.tsx +++ b/src/components/activity/activity-panel.tsx @@ -6,8 +6,8 @@ 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 { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Separator } from '@/components/ui/separator'; +import { cn } from '@/lib/utils'; type AgentStatus = 'active' | 'stale' | 'stuck' | 'dead'; @@ -20,6 +20,7 @@ interface AgentRosterEntry { interface ActivityPanelProps { issues: BeadIssue[]; + collapsed?: boolean; } const AGENT_LABEL = 'gt:agent'; @@ -99,17 +100,6 @@ function formatRelativeTime(timestamp: string): string { return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); } -// Get status badge variant -function getStatusVariant(status: AgentStatus): 'default' | 'secondary' | 'outline' | 'destructive' { - switch (status) { - case 'active': return 'default'; - case 'stale': return 'secondary'; - case 'stuck': return 'outline'; - case 'dead': return 'destructive'; - default: return 'secondary'; - } -} - // Get event kind icon/color function getEventKindInfo(kind: string): { label: string; color: string } { const events: Record = { @@ -131,7 +121,7 @@ function getInitials(name: string): string { return name.split(/[-_\s]/).map(p => p[0]).join('').toUpperCase().slice(0, 2); } -export function ActivityPanel({ issues }: ActivityPanelProps) { +export function ActivityPanel({ issues, collapsed = false }: ActivityPanelProps) { const [activities, setActivities] = useState([]); const [isLoading, setIsLoading] = useState(true); @@ -182,6 +172,44 @@ export function ActivityPanel({ issues }: 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 */} +
+ {agentRoster.slice(0, 5).map(agent => ( +
+ + + {getInitials(agent.name)} + + +
+
+ ))} +
+ + {/* Divider */} +
+ + {/* Mini Activity Dots (Just visual pulse) */} +
+ {/* Just show a few recent activity dots as a visual "heartbeat" */} + {activities.slice(0, 5).map((act) => ( +
+ ))} +
+
+ ); + } + return (
{/* AGENT ROSTER SECTION */} @@ -294,4 +322,4 @@ export function ActivityPanel({ issues }: ActivityPanelProps) {
); -} +} \ No newline at end of file diff --git a/src/components/shared/right-panel.tsx b/src/components/shared/right-panel.tsx index 76eb885..1ff5757 100644 --- a/src/components/shared/right-panel.tsx +++ b/src/components/shared/right-panel.tsx @@ -7,30 +7,49 @@ import { useUrlState } from '../../hooks/use-url-state'; export interface RightPanelProps { children?: ReactNode; + rail?: ReactNode; isOpen?: boolean; } -export function RightPanel({ children, isOpen: externalIsOpen }: RightPanelProps) { +export function RightPanel({ children, rail, isOpen: externalIsOpen }: RightPanelProps) { const { isMobile, isDesktop } = useResponsive(); const { panel, togglePanel } = useUrlState(); const isOpen = externalIsOpen ?? (panel === 'open'); + + // Calculate width based on content (Standard 17rem vs Chat Mode ~26rem) + // If rail is present, we are in "Chat Mode" (Wide Panel + Rail) + // If no rail, we are in "Activity Mode" (Standard Panel) + const panelWidth = isOpen ? (rail ? '26rem' : '17rem') : '0'; if (isDesktop) { return (
{isOpen && ( -
- {children || Right Panel} -
+ <> + {/* Main Content (Chat or Activity) */} +
+
+ {/* Remove default padding to allow edge-to-edge chat */} + {children || Right Panel} +
+
+ + {/* Side Rail (Mini Activity - Only if provided) */} + {rail && ( +
+ {rail} +
+ )} + )}
); @@ -109,4 +128,4 @@ export function RightPanel({ children, isOpen: externalIsOpen }: RightPanelProps ); } -export default RightPanel; +export default RightPanel; \ No newline at end of file diff --git a/src/components/shared/thread-drawer.tsx b/src/components/shared/thread-drawer.tsx index a2fb1a1..f475455 100644 --- a/src/components/shared/thread-drawer.tsx +++ b/src/components/shared/thread-drawer.tsx @@ -10,6 +10,7 @@ interface ThreadDrawerProps { title: string; id: string; items?: ThreadItem[]; + embedded?: boolean; // New prop for embedded mode } // Sample data for demo @@ -37,23 +38,24 @@ const SAMPLE_ITEMS: ThreadItem[] = [ }, ]; -export function ThreadDrawer({ isOpen, onClose, title, id, items = SAMPLE_ITEMS }: ThreadDrawerProps) { +export function ThreadDrawer({ isOpen, onClose, title, id, items = SAMPLE_ITEMS, embedded = false }: ThreadDrawerProps) { const [comment, setComment] = useState(''); if (!isOpen) return null; return (
{/* Header */}
@@ -78,13 +80,13 @@ export function ThreadDrawer({ isOpen, onClose, title, id, items = SAMPLE_ITEMS
{/* Thread Content */} -
+
{/* Compose */}
@@ -93,7 +95,7 @@ export function ThreadDrawer({ isOpen, onClose, title, id, items = SAMPLE_ITEMS value={comment} onChange={(e) => setComment(e.target.value)} placeholder="Add a comment..." - className="flex-1 px-3 py-2 rounded-md text-sm" + className="flex-1 px-3 py-2 rounded-md text-sm outline-none focus:ring-1 focus:ring-teal-500/50 transition-all" style={{ backgroundColor: 'var(--color-bg-input)', color: 'var(--color-text-primary)', @@ -106,7 +108,7 @@ export function ThreadDrawer({ isOpen, onClose, title, id, items = SAMPLE_ITEMS }} />
); -} +} \ No newline at end of file diff --git a/src/components/shared/unified-shell.tsx b/src/components/shared/unified-shell.tsx index 637ab96..9cefe07 100644 --- a/src/components/shared/unified-shell.tsx +++ b/src/components/shared/unified-shell.tsx @@ -52,14 +52,32 @@ export function UnifiedShell({ setDrawer('closed'); }; - // Thread drawer - shows when card selected - const isDrawerOpen = drawer === 'open' && (!!taskId || !!swarmId); + // Chat Mode Logic: If a card is selected (drawer='open'), we show Chat + Mini Activity Rail + const isChatOpen = drawer === 'open' && (!!taskId || !!swarmId); const drawerTitle = selectedSocialCard?.title || selectedSwarmCard?.title || ''; const drawerId = taskId || swarmId || ''; - const renderRightPanel = () => { - return ; - }; + // Right Panel Content Logic + // - Chat Mode: Main = Chat, Rail = Activity(Collapsed) + // - Default: Main = Activity, Rail = None + const rightPanelMain = isChatOpen ? ( + + ) : ( + + ); + + const rightPanelRail = isChatOpen ? ( + + ) : undefined; + + // Grid Layout: Expand Right Panel width when Chat is open + const rightPanelWidth = isChatOpen ? '26rem' : '17rem'; const renderMiddleContent = () => { if (view === 'graph') { @@ -103,42 +121,28 @@ export function UnifiedShell({ {/* TOP BAR: 3rem fixed */} - {/* MAIN AREA: CSS Grid [13rem | 1fr | 17rem] */} + {/* MAIN AREA: CSS Grid [13rem | 1fr | RightPanel] */}
{/* LEFT PANEL: 13rem channel tree */} - {/* MIDDLE CONTENT: flex-1 - contains card grid AND thread drawer */} -
+ {/* MIDDLE CONTENT: flex-1 */} +
{renderMiddleContent()} - - {/* THREAD DRAWER: Inside middle section, attached to right edge */} - {isDrawerOpen && ( -
- -
- )}
- {/* RIGHT PANEL: 17rem - Always shows Activity (bb-ui2.29) */} - - {renderRightPanel()} + {/* RIGHT PANEL: Dynamic Content + Optional Rail */} + + {rightPanelMain}
- - {/* MOBILE NAV: Bottom tab bar */}
); -} +} \ No newline at end of file