/** * Dedicated Custom Theme Editor Modal * Standalone modal with two-column layout: editor (left) + preview (right) * Opens on top of ThemeCustomizeModal for creating/editing custom themes. */ import React, { useState, useCallback, useMemo, useEffect } from 'react'; import { createPortal } from 'react-dom'; import { Trash2, X } from 'lucide-react'; import { TerminalTheme } from '../../domain/models'; import { useI18n } from '../../application/i18n/I18nProvider'; import { CustomThemeEditor } from './CustomThemeEditor'; import { Button } from '../ui/button'; import { isAppLockOverlayActive } from '../../infrastructure/appLockOverlayDom'; interface CustomThemeModalProps { open: boolean; theme: TerminalTheme; isNew: boolean; onSave: (theme: TerminalTheme) => void; onDelete?: (themeId: string) => void; onCancel: () => void; } // Minimal terminal preview for the right panel const MiniPreview: React.FC<{ theme: TerminalTheme }> = ({ theme }) => (
{/* Title bar */}
Terminal Preview
user@server : ~ $ neofetch
{' ,g$$P" """Y$$."". '}
{` ,$$P' `} OS : Ubuntu 22.04 LTS
{` '',$$P `} Kernel : 5.15.0-generic
{` d$$' `} Shell : bash 5.1.16
{` $$P `} Memory : 4.2G / 16G (26%)
 
{/* ANSI color palette */}
{[theme.colors.black, theme.colors.red, theme.colors.green, theme.colors.yellow, theme.colors.blue, theme.colors.magenta, theme.colors.cyan, theme.colors.white].map((c, i) => (
))}
{[theme.colors.brightBlack, theme.colors.brightRed, theme.colors.brightGreen, theme.colors.brightYellow, theme.colors.brightBlue, theme.colors.brightMagenta, theme.colors.brightCyan, theme.colors.brightWhite].map((c, i) => (
))}
 
user@server : ~ $  
); export const CustomThemeModal: React.FC = ({ open, theme: initialTheme, isNew, onSave, onDelete, onCancel, }) => { const { t } = useI18n(); const [editingTheme, setEditingTheme] = useState(initialTheme); // Reset when opened with a new theme React.useEffect(() => { if (open) { setEditingTheme({ ...initialTheme, colors: { ...initialTheme.colors } }); } }, [open, initialTheme]); const handleChange = useCallback((theme: TerminalTheme) => { setEditingTheme(theme); }, []); const handleSave = useCallback(() => { onSave(editingTheme); }, [editingTheme, onSave]); const handleDelete = useCallback(() => { onDelete?.(editingTheme.id); }, [editingTheme.id, onDelete]); // Dummy back handler — in the standalone modal, back = cancel const handleBack = useCallback(() => { onCancel(); }, [onCancel]); const themeInfo = useMemo(() => { return `${editingTheme.name} • ${editingTheme.type.toUpperCase()}`; }, [editingTheme.name, editingTheme.type]); // Handle Escape key — close child editor useEffect(() => { if (!open) return; const handleKeyDown = (e: KeyboardEvent) => { if (isAppLockOverlayActive()) return; if (e.key === 'Escape') { e.stopPropagation(); onCancel(); } }; document.addEventListener('keydown', handleKeyDown, true); // capture phase return () => document.removeEventListener('keydown', handleKeyDown, true); }, [open, onCancel]); if (!open) return null; const modalContent = (
{/* Backdrop — clicking it dismisses the modal */}
{/* Modal */}
e.stopPropagation()} > {/* Header */}

{isNew ? t('terminal.customTheme.newTitle') : t('terminal.customTheme.editTitle')}

{/* Body: Editor (left) + Preview (right) */}
{/* Left: Editor */}
{/* Right: Preview */}
{t('terminal.themeModal.livePreview')}
{themeInfo}
{/* Footer */}
{/* Delete button (only for existing themes) */} {!isNew && onDelete && ( )}
); return createPortal(modalContent, document.body); }; export default CustomThemeModal;