MailDebuggerPanel.tsx32.6 KBView on GitHub import {
GripHorizontal,
Inbox,
Mail,
MessageSquare,
Network,
Database,
RefreshCw,
TestTube,
Users,
X,
History,
Briefcase,
Calendar,
Layout,
Bot,
Bug,
Palette,
Video,
FileText,
Layers,
Snowflake,
MessagesSquare,
} from 'lucide-react';
import { useSelectedThreadData, useSelectedThreadId, useCedarStore } from '@/modules/store';
import { selectMergedFeed } from '@/modules/inbox/store/inboxSlice';
import { CRMTab } from '@/modules/cedar-os/src/cedar-os-components/debugger/CRMTab';
import { useAgentExecutionsStore } from '@/modules/aop/store/agentExecutionsSlice';
import { AnimatePresence, motion, useDragControls } from 'motion/react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/ui/tabs';
import { useSession } from '@/modules/auth/utils/auth-client';
import { selectChatThreadId } from '@/modules/ux/uxSlice';
import { AgentExecutionsDebuggerTab } from './AgentExecutionsDebuggerTab';
import { useAOPs } from '@/modules/aop/hooks/use-aops';
import { CurrentThreadDebuggerTab } from './CurrentThreadDebuggerTab';
import { ConversationsDebuggerTab } from './ConversationsDebuggerTab';
import { AgentCanvasDebuggerTab } from './AgentCanvasDebuggerTab';
import Container from '@/containers/Container';
import { CalendarDebuggerTab } from './CalendarDebuggerTab';
import { cn } from '@/styles/stylingUtils';
import { ThreadsDebuggerTab } from './ThreadsDebuggerTab';
import { InboxDebuggerTab } from './InboxDebuggerTab';
import { TestDebuggerTab } from './TestDebuggerTab';
import { AOPDebuggerTab } from './AOPDebuggerTab';
import { UXDebuggerTab } from './UXDebuggerTab';
import { NetworkTab } from '@/modules/cedar-os/src/cedar-os-components/debugger/NetworkTab';
import { MessagesTab } from '@/modules/cedar-os/src/cedar-os-components/debugger/MessagesTab';
import { AgentContextDebuggerTab } from './AgentContextDebuggerTab';
import { MeetingsDebuggerTab } from './MeetingsDebuggerTab';
import { ConversationDocsDebuggerTab } from './ConversationDocsDebuggerTab';
import { TimelineDebuggerTab } from './TimelineDebuggerTab';
import { FreezesDebuggerTab } from './FreezesDebuggerTab';
import { setEnabled as setDeepDebuggerEnabled, isEnabled as isDeepDebuggerEnabled } from '@/modules/debugger/deepDebugger/buffer';
import { ChatThreadsDebuggerTab } from './ChatThreadsDebuggerTab';
import type {
DebugLogEntry,
Message as DebuggerMessage,
} from '@/modules/cedar-os/src/cedar-os-components/debugger/types';
interface MailDebuggerPanelProps {
/** Optional class name */
className?: string;
}
export const MailDebuggerPanel: React.FC<MailDebuggerPanelProps> = ({ className }) => {
const { data: session } = useSession();
const [isExpanded, setIsExpanded] = useState(false);
const [activeTab, setActiveTab] = useState('timeline');
const [copiedId, setCopiedId] = useState<string | null>(null);
// Get mail data from stores/hooks using new Zustand architecture
const currentThreadId = useSelectedThreadId();
const threadData = useSelectedThreadData();
const allConversationsMap = useCedarStore((state) => state.conversations);
const activeConversationId = useCedarStore((state) => state.activeConversationId);
const allThreadDataMap = useCedarStore((state) => state.threadData);
const allAgentExecutions = useAgentExecutionsStore((state) => state.executions);
const currentConversationList = useCedarStore((state) => state.currentConversationList);
// Panel dimensions state - hardcoded defaults
const [panelWidth, setPanelWidth] = useState(600);
const [panelHeight, setPanelHeight] = useState(450);
// Hardcoded minimum and maximum sizes
const minWidth = 400;
const minHeight = 300;
const calculatedMaxWidth = typeof window !== 'undefined' ? window.innerWidth * 0.8 : 1000;
const calculatedMaxHeight = typeof window !== 'undefined' ? window.innerHeight * 0.8 : 800;
// Resize state
const [isResizing, setIsResizing] = useState<
null | 'width' | 'height' | 'both' | 'bottom-left' | 'bottom-right'
>(null);
const dragStartX = useRef(0);
const dragStartY = useRef(0);
const dragStartWidth = useRef(0);
const dragStartHeight = useRef(0);
// Drag controls for both collapsed and expanded states
const dragControls = useDragControls();
const constraintsRef = useRef<HTMLDivElement>(null);
// Get conversation data
const currentConversationId = activeConversationId;
const allConversations = allConversationsMap;
const conversationsCount = Object.keys(allConversations).length;
// Get threads from Zustand store (new architecture)
const threadsCount = Object.keys(allThreadDataMap).length;
const inboxItemsCount = useCedarStore(selectMergedFeed).items.length;
const agentExecutionsCount = Object.keys(allAgentExecutions).length;
// Get AOP state
const { data: aopsData } = useAOPs();
const aopsCount = aopsData?.aops?.length ?? 0;
// Get CRM state
const crmColumns = useCedarStore((state) => state.columns);
const crmColumnsCount = Object.keys(crmColumns).length;
// Get Calendar state
const calendars = useCedarStore((state) => state.calendars);
const calendarEvents = useCedarStore((state) => state.calendarEvents);
const selectedCalendarEventId = useCedarStore((state) => state.selectedCalendarEventId);
const calendarEventToConversation = useCedarStore((state) => state.calendarEventToConversation);
const calendarsCount = calendars.length;
const calendarEventsCount = calendarEvents.length;
// Get UX state
const sidePanelMode = useCedarStore((state) => state.sidePanelMode);
const navigation = useCedarStore((state) => state.navigation);
const chatThreadId = useCedarStore(selectChatThreadId);
const chatThreadsCount = useCedarStore((state) => Object.keys(state.threadMap).length);
const activeCanvasId = useCedarStore((state) => state.activeCanvasId);
const isGlobalCanvasOpen = useCedarStore((state) => state.isGlobalCanvasOpen);
const activeView = useCedarStore((state) => state.getActiveView());
const commandStack = useCedarStore((state) => state.commandStack);
const commandModalItemId = useCedarStore((state) => state.commandModalItemId);
// Get file-system document state (single source of truth — agenda docs live
// here too, mirrored via `setDocument` from the agenda hydration path).
const documents = useCedarStore((state) => state.documents);
const documentsCount = Object.keys(documents).length;
// Get document save log state
const docSaveEvents = useCedarStore((state) => state.docSaveEvents);
const docSaveErrorCount = docSaveEvents.filter((e) => e.event.type === 'flush-error').length;
// Get Canvas state counts (from canvasSlice, active canvas)
const activeCanvas = useCedarStore((state) =>
state.activeCanvasId ? state.canvasesById[state.activeCanvasId] : undefined,
);
const agentCanvasDataCount = Object.keys(activeCanvas?.data?.conversationEntries ?? {}).length;
const pendingDraftsCount = Object.values(activeCanvas?.data?.conversationEntries ?? {}).filter(
(e) => e.pendingDraft?.reviewStatus === 'pending',
).length;
// Get debug state (merged from cedar-os DebuggerPanel)
const agentConnectionLogs = useCedarStore(
(state) => state.agentConnectionLogs,
) as DebugLogEntry[];
const debugMessages = useCedarStore((state) => state.messages) as DebuggerMessage[];
// Resize handlers
const handleMouseMove = useCallback(
(e: MouseEvent) => {
if (!isResizing) return;
let newWidth = panelWidth;
let newHeight = panelHeight;
if (
isResizing === 'width' ||
isResizing === 'both' ||
isResizing === 'bottom-left' ||
isResizing === 'bottom-right'
) {
let deltaX = e.clientX - dragStartX.current;
// For bottom-left corner, invert the deltaX since dragging left should increase width
if (isResizing === 'bottom-left') {
deltaX = -deltaX;
}
newWidth = Math.max(
minWidth,
Math.min(calculatedMaxWidth, dragStartWidth.current + deltaX),
);
setPanelWidth(newWidth);
}
if (
isResizing === 'height' ||
isResizing === 'both' ||
isResizing === 'bottom-left' ||
isResizing === 'bottom-right'
) {
// For bottom corners, dragging down increases height
const deltaY = e.clientY - dragStartY.current;
newHeight = Math.max(
minHeight,
Math.min(calculatedMaxHeight, dragStartHeight.current + deltaY),
);
setPanelHeight(newHeight);
}
},
[
isResizing,
minWidth,
minHeight,
calculatedMaxWidth,
calculatedMaxHeight,
panelWidth,
panelHeight,
],
);
const handleMouseUp = useCallback(() => {
setIsResizing(null);
if (typeof document !== 'undefined') {
document.body.style.userSelect = '';
document.body.style.webkitUserSelect = '';
}
}, []);
useEffect(() => {
if (isResizing) {
if (typeof document !== 'undefined') {
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
document.body.style.userSelect = 'none';
document.body.style.webkitUserSelect = 'none';
}
}
return () => {
if (typeof document !== 'undefined') {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
}
};
}, [isResizing, handleMouseMove, handleMouseUp]);
const startResize = (
direction: 'width' | 'height' | 'both' | 'bottom-left' | 'bottom-right',
e: React.MouseEvent,
) => {
e.preventDefault();
e.stopPropagation(); // Prevent drag from interfering
setIsResizing(direction);
dragStartX.current = e.clientX;
dragStartY.current = e.clientY;
dragStartWidth.current = panelWidth;
dragStartHeight.current = panelHeight;
};
const handleCopy = (text: string, id: string) => {
navigator.clipboard.writeText(text);
setCopiedId(id);
setTimeout(() => setCopiedId(null), 2000);
};
const handleRefresh = () => {
// Force refresh of current data
window.location.reload();
};
// Calculate proper panel position when expanding
const getExpandedPanelPosition = () => {
if (typeof window === 'undefined') {
return { x: 0, y: 0 };
}
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
const margin = 20;
const x = windowWidth - panelWidth - margin;
const y = windowHeight - panelHeight - margin;
return { x, y };
};
const expandedPosition = getExpandedPanelPosition();
// Show debugger panel in development OR for @cedarcopilot.com email addresses
const shouldShowDebugger =
process.env.NODE_ENV === 'development' || session?.user?.email?.endsWith('@cedarcopilot.com');
// Enable deep-debugger capture for cedarcopilot users in prod (dev already enables it at module load).
useEffect(() => {
if (shouldShowDebugger && !isDeepDebuggerEnabled()) {
setDeepDebuggerEnabled(true);
}
}, [shouldShowDebugger]);
if (!shouldShowDebugger) return null;
return (
<>
{/* Constraints container */}
<motion.div
ref={constraintsRef}
className="pointer-events-none fixed inset-0"
style={{ zIndex: 9998 }}
/>
<AnimatePresence mode="wait">
{!isExpanded ? (
// Collapsed state - Fixed top-right corner section
<motion.div
id="mail-debugger-panel-collapsed"
key=[redacted]
initial={{ scale: 0, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0, opacity: 0 }}
whileHover={{ scale: 1.1 }}
transition={{ type: 'spring', damping: 20, stiffness: 300 }}
onClick={() => setIsExpanded(true)}
style={{
position: 'fixed',
bottom: 0,
right: 0,
zIndex: 9999,
}}
className={cn('cursor-pointer select-none', className)}
>
<Container className="flex h-8 w-10 items-center justify-center rounded-tl-lg">
<Bug className="h-4 w-4" />
</Container>
</motion.div>
) : (
// Expanded state - full panel with Container
<motion.div
id="mail-debugger-panel-expanded"
key=[redacted]
drag
dragControls={dragControls}
dragConstraints={constraintsRef}
dragElastic={0.2}
dragMomentum={false}
dragListener={false}
initial={{ scale: 0.8, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.8, opacity: 0 }}
transition={{ type: 'spring', damping: 20, stiffness: 300 }}
style={{
position: 'fixed',
x: expandedPosition.x,
y: expandedPosition.y,
zIndex: 9999,
width: panelWidth,
height: panelHeight,
cursor: isResizing
? isResizing === 'width'
? 'col-resize'
: isResizing === 'height'
? 'row-resize'
: 'nwse-resize'
: 'default',
}}
className={cn('flex flex-col', className)}
>
<Container className="flex h-full flex-col" shadowSize="l">
{/* Draggable Header */}
<div
className="flex cursor-move select-none items-center justify-between border-b border-gray-200 px-2 py-1 dark:border-gray-700"
onPointerDown={(e) => dragControls.start(e)}
style={{ touchAction: 'none' }}
>
<div className="flex items-center gap-1.5">
<GripHorizontal className="h-3 w-3 text-gray-400" />
<Mail className="h-3.5 w-3.5 text-blue-600 dark:text-blue-400" />
<span className="text-xs font-medium">Mail Debugger</span>
<div
className={cn(
'rounded-full px-1.5 py-0.5 text-xs',
currentThreadId
? 'bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300'
: 'bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300',
)}
>
{currentThreadId ? 'Active Thread' : 'No Thread'}
</div>
</div>
<div className="flex items-center gap-1">
<button
onClick={handleRefresh}
className="rounded p-1 transition-colors hover:bg-gray-100 dark:hover:bg-gray-800"
title="Refresh data"
>
<RefreshCw className="h-3 w-3" />
</button>
<button
onClick={() => setIsExpanded(false)}
className="rounded p-1 transition-colors hover:bg-gray-100 dark:hover:bg-gray-800"
>
<X className="h-3 w-3" />
</button>
</div>
</div>
{/* Tabs Content */}
<Tabs
value={activeTab}
onValueChange={setActiveTab}
className="flex flex-1 flex-col overflow-hidden"
>
{/* Three rows of tabs */}
<div className="mx-2 mt-1 flex flex-col gap-1">
{/* Row 1: Data + Docs tabs */}
<TabsList className="grid h-8 w-full grid-cols-6">
<TabsTrigger
value="currentThread"
className="flex items-center gap-1 px-2 py-1 text-xs"
>
<MessageSquare className="h-3 w-3" />
Current
{currentThreadId && (
<span className="ml-0.5 flex h-4 min-w-[16px] items-center justify-center rounded-full bg-blue-100 px-1 py-0.5 text-xs leading-none text-blue-700 dark:bg-blue-900 dark:text-blue-300">
1
</span>
)}
</TabsTrigger>
<TabsTrigger
value="threads"
className="flex items-center gap-1 px-2 py-1 text-xs"
>
<Inbox className="h-3 w-3" />
Threads
{threadsCount > 0 && (
<span className="ml-0.5 flex h-4 min-w-[16px] items-center justify-center rounded-full bg-green-100 px-1 py-0.5 text-xs leading-none text-green-700 dark:bg-green-900 dark:text-green-300">
{threadsCount}
</span>
)}
</TabsTrigger>
<TabsTrigger
value="inbox"
className="flex items-center gap-1 px-2 py-1 text-xs"
>
<Inbox className="h-3 w-3" />
Inbox
{inboxItemsCount > 0 && (
<span className="ml-0.5 flex h-4 min-w-[16px] items-center justify-center rounded-full bg-sky-100 px-1 py-0.5 text-xs leading-none text-sky-700 dark:bg-sky-900 dark:text-sky-300">
{inboxItemsCount}
</span>
)}
</TabsTrigger>
<TabsTrigger
value="conversations"
className="flex items-center gap-1 px-2 py-1 text-xs"
>
<Users className="h-3 w-3" />
Convos
{conversationsCount > 0 && (
<span className="ml-0.5 flex h-4 min-w-[16px] items-center justify-center rounded-full bg-purple-100 px-1 py-0.5 text-xs leading-none text-purple-700 dark:bg-purple-900 dark:text-purple-300">
{conversationsCount}
</span>
)}
</TabsTrigger>
<TabsTrigger
value="conversationDocs"
className="flex items-center gap-1 px-2 py-1 text-xs"
>
<FileText className="h-3 w-3" />
Docs
{documentsCount > 0 && (
<span className={cn(
'ml-0.5 flex h-4 min-w-[16px] items-center justify-center rounded-full px-1 py-0.5 text-xs leading-none',
docSaveErrorCount > 0
? 'bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-300'
: 'bg-teal-100 text-teal-700 dark:bg-teal-900 dark:text-teal-300',
)}>
{docSaveErrorCount > 0 ? docSaveErrorCount : documentsCount}
</span>
)}
</TabsTrigger>
<TabsTrigger
value="chatThreads"
className="flex items-center gap-1 px-2 py-1 text-xs"
>
<MessagesSquare className="h-3 w-3" />
Chat
{chatThreadsCount > 0 && (
<span className="ml-0.5 flex h-4 min-w-[16px] items-center justify-center rounded-full bg-blue-100 px-1 py-0.5 text-xs leading-none text-blue-700 dark:bg-blue-900 dark:text-blue-300">
{chatThreadsCount}
</span>
)}
</TabsTrigger>
</TabsList>
{/* Row 2: Feature + Canvas tabs */}
<TabsList className="grid h-8 w-full grid-cols-6">
<TabsTrigger value="aop" className="flex items-center gap-1 px-2 py-1 text-xs">
<Bot className="h-3 w-3" />
AOP
{aopsCount > 0 && (
<span className="ml-0.5 flex h-4 min-w-[16px] items-center justify-center rounded-full bg-amber-100 px-1 py-0.5 text-xs leading-none text-amber-700 dark:bg-amber-900 dark:text-amber-300">
{aopsCount}
</span>
)}
</TabsTrigger>
<TabsTrigger value="crm" className="flex items-center gap-1 px-2 py-1 text-xs">
<Briefcase className="h-3 w-3" />
CRM
{crmColumnsCount > 0 && (
<span className="ml-0.5 flex h-4 min-w-[16px] items-center justify-center rounded-full bg-indigo-100 px-1 py-0.5 text-xs leading-none text-indigo-700 dark:bg-indigo-900 dark:text-indigo-300">
{crmColumnsCount}
</span>
)}
</TabsTrigger>
<TabsTrigger
value="agentExecutions"
className="flex items-center gap-1 px-2 py-1 text-xs"
>
<History className="h-3 w-3" />
Agents
{agentExecutionsCount > 0 && (
<span className="ml-0.5 flex h-4 min-w-[16px] items-center justify-center rounded-full bg-cyan-100 px-1 py-0.5 text-xs leading-none text-cyan-700 dark:bg-cyan-900 dark:text-cyan-300">
{agentExecutionsCount}
</span>
)}
</TabsTrigger>
<TabsTrigger
value="calendar"
className="flex items-center gap-1 px-2 py-1 text-xs"
>
<Calendar className="h-3 w-3" />
Cal
{calendarsCount + calendarEventsCount > 0 && (
<span className="ml-0.5 flex h-4 min-w-[16px] items-center justify-center rounded-full bg-emerald-100 px-1 py-0.5 text-xs leading-none text-emerald-700 dark:bg-emerald-900 dark:text-emerald-300">
{calendarsCount + calendarEventsCount}
</span>
)}
</TabsTrigger>
<TabsTrigger value="ux" className="flex items-center gap-1 px-2 py-1 text-xs">
<Layout className="h-3 w-3" />
UX
</TabsTrigger>
<TabsTrigger
value="agentCanvas"
className="flex items-center gap-1 px-2 py-1 text-xs"
>
<Palette className="h-3 w-3" />
Canvas
{agentCanvasDataCount + pendingDraftsCount > 0 && (
<span className="ml-0.5 flex h-4 min-w-[16px] items-center justify-center rounded-full bg-violet-100 px-1 py-0.5 text-xs leading-none text-violet-700 dark:bg-violet-900 dark:text-violet-300">
{agentCanvasDataCount + pendingDraftsCount}
</span>
)}
</TabsTrigger>
</TabsList>
{/* Row 0: Deep debugger (timeline + freezes) */}
<TabsList className="grid h-8 w-full grid-cols-2">
<TabsTrigger
value="timeline"
className="flex items-center gap-1 px-2 py-1 text-xs"
>
<Layers className="h-3 w-3" />
Timeline
</TabsTrigger>
<TabsTrigger
value="freezes"
className="flex items-center gap-1 px-2 py-1 text-xs"
>
<Snowflake className="h-3 w-3" />
Freezes
</TabsTrigger>
</TabsList>
{/* Row 3: Cedar-OS agent + utility tabs */}
<TabsList className="grid h-8 w-full grid-cols-5">
<TabsTrigger
value="network"
className="flex items-center gap-1 px-2 py-1 text-xs"
>
<Network className="h-3 w-3" />
Network
{agentConnectionLogs.length > 0 && (
<span className="ml-0.5 flex h-4 min-w-[16px] items-center justify-center rounded-full bg-purple-100 px-1 py-0.5 text-xs leading-none text-purple-700 dark:bg-purple-900 dark:text-purple-300">
{agentConnectionLogs.length}
</span>
)}
</TabsTrigger>
<TabsTrigger
value="messages"
className="flex items-center gap-1 px-2 py-1 text-xs"
>
<MessageSquare className="h-3 w-3" />
Messages
{debugMessages.length > 0 && (
<span className="ml-0.5 flex h-4 min-w-[16px] items-center justify-center rounded-full bg-blue-100 px-1 py-0.5 text-xs leading-none text-blue-700 dark:bg-blue-900 dark:text-blue-300">
{debugMessages.length}
</span>
)}
</TabsTrigger>
<TabsTrigger
value="agentContext"
className="flex items-center gap-1 px-2 py-1 text-xs"
>
<Database className="h-3 w-3" />
Context
</TabsTrigger>
<TabsTrigger
value="test"
className="flex items-center gap-1 px-2 py-1 text-xs"
>
<TestTube className="h-3 w-3" />
Test
</TabsTrigger>
<TabsTrigger
value="meetings"
className="flex items-center gap-1 px-2 py-1 text-xs"
>
<Video className="h-3 w-3" />
Meetings
</TabsTrigger>
</TabsList>
</div>
<div className="flex-1 overflow-hidden">
<TabsContent value="timeline" className="h-full">
<TimelineDebuggerTab />
</TabsContent>
<TabsContent value="freezes" className="h-full">
<FreezesDebuggerTab />
</TabsContent>
<TabsContent value="currentThread" className="h-full">
<CurrentThreadDebuggerTab
currentThreadId={currentThreadId}
currentConversationId={currentConversationId}
threadData={threadData}
conversationData={
currentConversationId ? allConversations[currentConversationId] : undefined
}
currentConversationList={currentConversationList}
onCopy={handleCopy}
copiedId={copiedId}
/>
</TabsContent>
<TabsContent value="threads" className="h-full">
<ThreadsDebuggerTab
threads={allThreadDataMap}
onCopy={handleCopy}
copiedId={copiedId}
/>
</TabsContent>
<TabsContent value="inbox" className="h-full">
<InboxDebuggerTab onCopy={handleCopy} copiedId={copiedId} />
</TabsContent>
<TabsContent value="conversations" className="h-full">
<ConversationsDebuggerTab
conversations={allConversations}
onCopy={handleCopy}
copiedId={copiedId}
/>
</TabsContent>
<TabsContent value="agentExecutions" className="h-full">
<AgentExecutionsDebuggerTab
executions={allAgentExecutions}
onCopy={handleCopy}
copiedId={copiedId}
/>
</TabsContent>
<TabsContent value="network" className="h-full">
<NetworkTab
logs={agentConnectionLogs}
onCopy={handleCopy}
copiedId={copiedId}
/>
</TabsContent>
<TabsContent value="messages" className="h-full">
<MessagesTab
messages={debugMessages}
onCopy={handleCopy}
copiedId={copiedId}
/>
</TabsContent>
<TabsContent value="agentContext" className="h-full">
<AgentContextDebuggerTab
onCopy={handleCopy}
copiedId={copiedId}
/>
</TabsContent>
<TabsContent value="aop" className="h-full">
<AOPDebuggerTab onCopy={handleCopy} copiedId={copiedId} />
</TabsContent>
<TabsContent value="crm" className="h-full">
<CRMTab onCopy={handleCopy} copiedId={copiedId} />
</TabsContent>
<TabsContent value="agentCanvas" className="h-full">
<AgentCanvasDebuggerTab />
</TabsContent>
<TabsContent value="calendar" className="h-full">
<CalendarDebuggerTab
calendars={calendars}
calendarEvents={calendarEvents}
selectedCalendarEventId={selectedCalendarEventId}
calendarEventToConversation={calendarEventToConversation}
onCopy={handleCopy}
copiedId={copiedId}
/>
</TabsContent>
<TabsContent value="ux" className="h-full">
<UXDebuggerTab
sidePanelMode={sidePanelMode}
navigation={navigation}
chatThreadId={chatThreadId}
activeCanvasId={activeCanvasId}
isGlobalCanvasOpen={isGlobalCanvasOpen}
activeView={activeView}
commandStack={commandStack}
commandModalItemId={commandModalItemId}
onCopy={handleCopy}
copiedId={copiedId}
/>
</TabsContent>
<TabsContent value="test" className="h-full">
<TestDebuggerTab />
</TabsContent>
<TabsContent value="meetings" className="h-full">
<MeetingsDebuggerTab onCopy={handleCopy} copiedId={copiedId} />
</TabsContent>
<TabsContent value="conversationDocs" className="h-full">
<ConversationDocsDebuggerTab onCopy={handleCopy} copiedId={copiedId} />
</TabsContent>
<TabsContent value="chatThreads" className="h-full">
<ChatThreadsDebuggerTab onCopy={handleCopy} copiedId={copiedId} />
</TabsContent>
</div>
</Tabs>
{/* Resize handles for bottom corners */}
<>
{/* Bottom-left corner handle */}
<div
className="group absolute bottom-0 left-0 h-4 w-4 cursor-nwse-resize hover:bg-blue-400/30"
onMouseDown={(e) => startResize('bottom-left', e)}
></div>
{/* Bottom-right corner handle */}
<div
className="group absolute bottom-0 right-0 h-4 w-4 cursor-nwse-resize hover:bg-blue-400/30"
onMouseDown={(e) => startResize('bottom-right', e)}
></div>
{/* Bottom edge handle for height only */}
<div
className="absolute bottom-0 left-4 right-4 h-1 cursor-row-resize hover:bg-blue-400/30"
onMouseDown={(e) => startResize('height', e)}
/>
{/* Right edge handle for width only */}
<div
className="absolute bottom-4 right-0 top-12 w-1 cursor-col-resize hover:bg-blue-400/30"
onMouseDown={(e) => startResize('width', e)}
/>
</>
</Container>
</motion.div>
)}
</AnimatePresence>
</>
);
};