mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-02-15 05:07:35 +00:00
- Replace `src/App.jsx` with `src/App.tsx` and move route-level UI orchestration into `src/components/app/AppContent.tsx`. This separates provider/bootstrap concerns from runtime app layout logic, keeps route definitions minimal, and improves readability of the root app entry. - Introduce `src/hooks/useProjectsState.ts` to centralize project/session/sidebar state management previously embedded in `App.jsx`. This keeps the existing behavior for: project loading, Cursor session hydration, WebSocket `loading_progress` handling, additive-update protection for active sessions, URL-based session selection, sidebar refresh/delete/new-session flows. The hook now exposes a typed `sidebarSharedProps` contract and typed handlers used by `AppContent`. - Introduce `src/hooks/useSessionProtection.ts` for active/processing session lifecycle logic. This preserves session-protection behavior while isolating `activeSessions`, `processingSessions`, and temporary-session replacement into a dedicated reusable hook. - Replace monolithic `src/components/Sidebar.jsx` with typed `src/components/Sidebar.tsx` as a thin orchestrator. `Sidebar.tsx` now focuses on wiring controller state/actions, modal visibility, collapsed mode, and version modal behavior instead of rendering every UI branch inline. - Add `src/hooks/useSidebarController.ts` to encapsulate sidebar interaction/state logic. This includes expand/collapse state, inline project/session editing state, project starring/sorting/filtering, lazy session pagination, delete confirmations, rename/delete actions, refresh state, and mobile touch click handling. - Add strongly typed sidebar domain models in `src/components/sidebar/types.ts` and move sidebar-derived helpers into `src/components/sidebar/utils.ts`. Utility coverage now includes: session provider normalization, session view-model creation (name/time/activity/message count), project sorting/filtering, task indicator status derivation, starred-project persistence and readbacks. - Split sidebar rendering into focused components under `src/components/sidebar/`: `SidebarContent.tsx` for top-level sidebar layout composition. `SidebarProjectList.tsx` for project-state branching and project iteration. `SidebarProjectsState.tsx` for loading/empty/no-search-result placeholders. `SidebarProjectItem.tsx` for per-project desktop/mobile header rendering and actions. `SidebarProjectSessions.tsx` for expanded session area, skeletons, pagination, and new-session controls. `SidebarSessionItem.tsx` for per-session desktop/mobile item rendering and session actions. `SessionProviderIcon.tsx` for provider icon normalization. `SidebarHeader.tsx`, `SidebarFooter.tsx`, `SidebarCollapsed.tsx`, and `SidebarModals.tsx` as dedicated typed UI surfaces. This keeps rendering responsibilities local and significantly improves traceability. - Convert shared UI primitives from JSX to TSX: `src/components/ui/button.tsx`, `src/components/ui/input.tsx`, `src/components/ui/badge.tsx`, `src/components/ui/scroll-area.tsx`. These now provide typed props/variants (`forwardRef` where appropriate) while preserving existing class/behavior. - Add shared app typings in `src/types/app.ts` for projects/sessions/websocket/loading contracts used by new hooks/components. - Add global window declarations in `src/types/global.d.ts` for `__ROUTER_BASENAME__`, `refreshProjects`, and `openSettings`, removing implicit `any` usage for global integration points. - Update `src/main.jsx` to import `App.tsx` and keep app bootstrap consistent with the TS migration. - Update `src/components/QuickSettingsPanel.jsx` to self-resolve mobile state via `useDeviceSettings` (remove `isMobile` prop dependency), and update `src/components/ChatInterface.jsx` to render `QuickSettingsPanel` directly. This reduces prop drilling and keeps quick settings colocated with chat UI concerns.
95 lines
4.2 KiB
TypeScript
95 lines
4.2 KiB
TypeScript
import { Settings } from 'lucide-react';
|
|
import type { TFunction } from 'i18next';
|
|
import type { ReleaseInfo } from '../../types/sharedTypes';
|
|
import { Button } from '../ui/button';
|
|
|
|
type SidebarFooterProps = {
|
|
updateAvailable: boolean;
|
|
releaseInfo: ReleaseInfo | null;
|
|
latestVersion: string | null;
|
|
onShowVersionModal: () => void;
|
|
onShowSettings: () => void;
|
|
t: TFunction;
|
|
};
|
|
|
|
export default function SidebarFooter({
|
|
updateAvailable,
|
|
releaseInfo,
|
|
latestVersion,
|
|
onShowVersionModal,
|
|
onShowSettings,
|
|
t,
|
|
}: SidebarFooterProps) {
|
|
return (
|
|
<>
|
|
{updateAvailable && (
|
|
<div className="md:p-2 border-t border-border/50 flex-shrink-0">
|
|
<div className="hidden md:block">
|
|
<Button
|
|
variant="ghost"
|
|
className="w-full justify-start gap-3 p-3 h-auto font-normal text-left hover:bg-blue-50 dark:hover:bg-blue-900/20 transition-colors duration-200 border border-blue-200 dark:border-blue-700 rounded-lg mb-2"
|
|
onClick={onShowVersionModal}
|
|
>
|
|
<div className="relative">
|
|
<svg className="w-4 h-4 text-blue-600 dark:text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3m0 0l3-3m-3 3V10" />
|
|
</svg>
|
|
<div className="absolute -top-1 -right-1 w-2 h-2 bg-blue-500 rounded-full animate-pulse" />
|
|
</div>
|
|
<div className="min-w-0 flex-1">
|
|
<div className="text-sm font-medium text-blue-700 dark:text-blue-300">
|
|
{releaseInfo?.title || `Version ${latestVersion}`}
|
|
</div>
|
|
<div className="text-xs text-blue-600 dark:text-blue-400">{t('version.updateAvailable')}</div>
|
|
</div>
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="md:hidden p-3 pb-2">
|
|
<button
|
|
className="w-full h-12 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-700 rounded-xl flex items-center justify-start gap-3 px-4 active:scale-[0.98] transition-all duration-150"
|
|
onClick={onShowVersionModal}
|
|
>
|
|
<div className="relative">
|
|
<svg className="w-5 h-5 text-blue-600 dark:text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3m0 0l3-3m-3 3V10" />
|
|
</svg>
|
|
<div className="absolute -top-1 -right-1 w-2 h-2 bg-blue-500 rounded-full animate-pulse" />
|
|
</div>
|
|
<div className="min-w-0 flex-1 text-left">
|
|
<div className="text-sm font-medium text-blue-700 dark:text-blue-300">
|
|
{releaseInfo?.title || `Version ${latestVersion}`}
|
|
</div>
|
|
<div className="text-xs text-blue-600 dark:text-blue-400">{t('version.updateAvailable')}</div>
|
|
</div>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className="md:p-2 md:border-t md:border-border flex-shrink-0">
|
|
<div className="md:hidden p-4 pb-20 border-t border-border/50">
|
|
<button
|
|
className="w-full h-14 bg-muted/50 hover:bg-muted/70 rounded-2xl flex items-center justify-start gap-4 px-4 active:scale-[0.98] transition-all duration-150"
|
|
onClick={onShowSettings}
|
|
>
|
|
<div className="w-10 h-10 rounded-2xl bg-background/80 flex items-center justify-center">
|
|
<Settings className="w-5 h-5 text-muted-foreground" />
|
|
</div>
|
|
<span className="text-lg font-medium text-foreground">{t('actions.settings')}</span>
|
|
</button>
|
|
</div>
|
|
|
|
<Button
|
|
variant="ghost"
|
|
className="hidden md:flex w-full justify-start gap-2 p-2 h-auto font-normal text-muted-foreground hover:text-foreground hover:bg-accent transition-colors duration-200"
|
|
onClick={onShowSettings}
|
|
>
|
|
<Settings className="w-3 h-3" />
|
|
<span className="text-xs">{t('actions.settings')}</span>
|
|
</Button>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|