102 lines · 3.4 KB · TypeScript
RawDownload
/** * SessionTabContent - Renders session content with loading/error states. * Each session tab has its own instance to preserve state. */ import { useEffect } from 'react'; import { useStore } from '@renderer/store';import { AlertCircle, RefreshCw } from 'lucide-react';import { useShallow } from 'zustand/react/shallow'; import { MiddlePanel } from './MiddlePanel'; import type { Tab } from '@renderer/types/tabs'; export const SessionTabContent = ({  tab,  isActive,}: Readonly<{ tab: Tab; isActive: boolean }>): React.JSX.Element => {  const { fetchSessionDetail, closeTab, initTabUIState } = useStore(    useShallow((s) => ({      fetchSessionDetail: s.fetchSessionDetail,      closeTab: s.closeTab,      initTabUIState: s.initTabUIState,    }))  );   // Read loading/error from per-tab data, falling back to global state  const { sessionDetailError, sessionDetailLoading } = useStore(    useShallow((s) => {      const td = s.tabSessionData[tab.id];      return {        sessionDetailError: td?.sessionDetailError ?? s.sessionDetailError,        sessionDetailLoading: td?.sessionDetailLoading ?? s.sessionDetailLoading,      };    })  );   // Initialize per-tab UI state when this tab is first mounted  useEffect(() => {    initTabUIState(tab.id);  }, [tab.id, initTabUIState]);   // Only show loading/error states when this tab is active  if (!isActive) {    return (      <div className="flex min-w-0 flex-1 flex-col overflow-hidden bg-claude-dark-surface">        <MiddlePanel tabId={tab.id} />      </div>    );  }   if (sessionDetailError) {    return (      <div className="flex flex-1 items-center justify-center bg-claude-dark-bg">        <div className="p-8 text-center">          <AlertCircle className="mx-auto mb-4 size-12 text-red-500/70" />          <h3 className="mb-2 text-lg font-medium text-claude-dark-text">Failed to load session</h3>          <p className="mb-4 max-w-md text-sm text-claude-dark-text-secondary">            {sessionDetailError}          </p>          <div className="flex justify-center gap-3">            <button              onClick={() => {                if (tab.projectId && tab.sessionId) {                  void fetchSessionDetail(tab.projectId, tab.sessionId, tab.id);                }              }}              className="flex items-center gap-2 rounded-md border border-claude-dark-border bg-claude-dark-surface px-4 py-2 text-sm transition-colors hover:bg-claude-dark-border"            >              <RefreshCw className="size-4" />              Retry            </button>            <button              onClick={() => closeTab(tab.id)}              className="px-4 py-2 text-sm text-claude-dark-text-secondary transition-colors hover:text-claude-dark-text"            >              Close tab            </button>          </div>        </div>      </div>    );  }   if (sessionDetailLoading) {    return (      <div className="flex flex-1 items-center justify-center bg-claude-dark-bg">        <div className="text-center">          <div className="mx-auto mb-4 size-8 animate-spin rounded-full border-2 border-claude-dark-text-secondary border-t-claude-dark-text" />          <p className="text-sm text-claude-dark-text-secondary">Loading session...</p>        </div>      </div>    );  }   return (    <div className="flex min-w-0 flex-1 flex-col overflow-hidden bg-claude-dark-surface">      <MiddlePanel tabId={tab.id} />    </div>  );};