import { useState } from 'react'; import { Upload, X } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { invoke } from '@tauri-apps/api/core'; import { open } from '@tauri-apps/plugin-dialog'; import { useInstalledThemesStore } from '../../store/installedThemesStore'; import { validateThemePackage, type ValidatedTheme } from '../../utils/themes/validateThemePackage'; import { showToast } from '../../utils/ui/toast'; import ConfirmModal from '../ConfirmModal'; /** * Import a community theme from a local `.zip` (manifest.json + theme.css). * Rust extracts the two entries (size-capped, outside the webview); the full * store contract validation then runs before the theme is persisted. Anything * off-contract is rejected with the exact reasons listed. */ export function ThemeImportSection() { const { t } = useTranslation(); const install = useInstalledThemesStore(s => s.install); const [importErrors, setImportErrors] = useState(null); const [importing, setImporting] = useState(false); // A validated-but-not-yet-installed theme, awaiting the user's confirmation. const [pending, setPending] = useState(null); const handleImport = async () => { setImportErrors(null); let selected: string | null = null; try { const picked = await open({ multiple: false, directory: false, filters: [{ name: 'Theme', extensions: ['zip'] }], }); if (typeof picked === 'string') selected = picked; } catch { return; // dialog dismissed / unavailable } if (!selected) return; setImporting(true); try { const files = await invoke<{ manifest: string; css: string }>('import_theme_zip', { path: selected }); const result = validateThemePackage(files.manifest, files.css); if (!result.ok) { setImportErrors(result.errors); return; } // Validated — confirm with the user (name + author) before persisting. setPending(result.theme); } catch (e) { setImportErrors([String(e)]); } finally { setImporting(false); } }; const confirmInstall = () => { if (!pending) return; install({ ...pending, installedAt: Date.now() }); showToast(t('settings.themeImportSuccess', { name: pending.name }), 4000, 'success'); setPending(null); }; return (
{importErrors && (
{t('settings.themeImportErrorTitle')}

{t('settings.themeImportErrorBody')}

{/* The raw contract diagnostics — useful to theme authors / bug reports, tucked away so end users aren't confronted with token names. */}
{t('settings.themeImportErrorDetails')}
    {importErrors.map((e, i) => (
  • {e}
  • ))}
)} setPending(null)} />
); }