Files
Netcatty/components/ThemeSelectPanel.tsx
T
bincxz b272ae799c Enhances terminal settings customization and theme logic
Introduces granular terminal appearance and behavior controls,
including font ligatures, weights, cursor shape, mouse and keyboard
actions, and accessibility options, with persistent storage.

Replaces default terminal themes with new Netcatty variants and
updates theme selection logic to prioritize host-specific settings.

Improves settings dialog UI for better organization and clarity.
2025-12-12 02:38:16 +08:00

158 lines
5.7 KiB
TypeScript

import { Users } from 'lucide-react';
import React, { useMemo, useState } from 'react';
import { TERMINAL_THEMES } from '../infrastructure/config/terminalThemes';
import { cn } from '../lib/utils';
import { TerminalTheme } from '../types';
import {
AsidePanel,
AsidePanelContent,
} from './ui/aside-panel';
import { ScrollArea } from './ui/scroll-area';
interface ThemeSelectPanelProps {
open: boolean;
selectedThemeId?: string;
onSelect: (themeId: string) => void;
onClose: () => void;
onBack?: () => void;
showBackButton?: boolean;
}
// Mini terminal preview component
const TerminalPreview: React.FC<{ theme: TerminalTheme; isSelected: boolean }> = ({
theme,
isSelected
}) => {
return (
<div
className={cn(
"w-16 h-10 rounded-md overflow-hidden border-2 flex-shrink-0",
isSelected ? "border-primary" : "border-transparent"
)}
style={{ backgroundColor: theme.colors.background }}
>
<div className="p-1 text-[4px] font-mono leading-tight" style={{ color: theme.colors.foreground }}>
<div>
<span style={{ color: theme.colors.green }}>$</span>{' '}
<span style={{ color: theme.colors.cyan }}>ls</span>
</div>
<div className="flex gap-0.5 flex-wrap">
<span style={{ color: theme.colors.blue }}>dir/</span>
<span style={{ color: theme.colors.green }}>file</span>
</div>
<div>
<span style={{ color: theme.colors.green }}>$</span>{' '}
<span
className="inline-block w-1 h-1.5"
style={{ backgroundColor: theme.colors.cursor }}
/>
</div>
</div>
</div>
);
};
const ThemeSelectPanel: React.FC<ThemeSelectPanelProps> = ({
open,
selectedThemeId,
onSelect,
onClose,
onBack,
showBackButton = true,
}) => {
// Reserved for future hover preview feature
const [_hoveredThemeId, setHoveredThemeId] = useState<string | null>(null);
// Group themes by type - reserved for future sectioned view
const _groupedThemes = useMemo(() => {
const dark = TERMINAL_THEMES.filter(t => t.type === 'dark');
const light = TERMINAL_THEMES.filter(t => t.type === 'light');
return { dark, light };
}, []);
// Find selected theme info - reserved for displaying selection details
const _selectedTheme = useMemo(() => {
return TERMINAL_THEMES.find(t => t.id === selectedThemeId);
}, [selectedThemeId]);
const renderThemeItem = (theme: TerminalTheme) => {
const isSelected = theme.id === selectedThemeId;
return (
<button
key={theme.id}
className={cn(
"w-full flex items-center gap-3 px-3 py-2.5 rounded-lg transition-colors text-left",
isSelected
? "bg-primary/10"
: "hover:bg-secondary/50"
)}
onClick={() => onSelect(theme.id)}
onMouseEnter={() => setHoveredThemeId(theme.id)}
onMouseLeave={() => setHoveredThemeId(null)}
>
<TerminalPreview theme={theme} isSelected={isSelected} />
<div className="flex-1 min-w-0">
<div className={cn(
"text-sm font-medium truncate",
isSelected && "text-primary"
)}>
{theme.name}
</div>
{/* Show usage stats or badge */}
<div className="flex items-center gap-1 text-xs text-muted-foreground">
{theme.id === 'netcatty-dark' && (
<span className="text-muted-foreground">Default</span>
)}
{theme.id === 'netcatty-light' && (
<>
<Users size={10} />
<span>Light mode</span>
</>
)}
{theme.id === 'flexoki-dark' && (
<span className="text-xs">new</span>
)}
{theme.id === 'flexoki-light' && (
<span className="text-xs">new</span>
)}
{theme.id.startsWith('kanagawa') && (
<>
<Users size={10} />
<span>{Math.floor(Math.random() * 20000)}</span>
</>
)}
{theme.id.startsWith('hacker') && (
<>
<Users size={10} />
<span>{Math.floor(Math.random() * 15000)}</span>
</>
)}
</div>
</div>
</button>
);
};
return (
<AsidePanel
open={open}
onClose={onClose}
title="Select Color Theme"
showBackButton={showBackButton}
onBack={onBack}
>
<AsidePanelContent className="p-0">
<ScrollArea className="h-full">
<div className="py-2">
{/* All themes in a single list */}
{TERMINAL_THEMES.map(renderThemeItem)}
</div>
</ScrollArea>
</AsidePanelContent>
</AsidePanel>
);
};
export default ThemeSelectPanel;