diff --git a/src/components/ThemeSelector.tsx b/src/components/ThemeSelector.tsx index dfa94967..5dbcbaff 100644 --- a/src/components/ThemeSelector.tsx +++ b/src/components/ThemeSelector.tsx @@ -1,5 +1,5 @@ -import { Check, Paintbrush, Globe } from 'lucide-react'; -import { Link } from 'react-router-dom'; +import { Check, Globe, Plus } from 'lucide-react'; +import { Link, useNavigate } from 'react-router-dom'; import { type Theme } from '@/contexts/AppContext'; import { useTheme } from '@/hooks/useTheme'; import { useCurrentUser } from '@/hooks/useCurrentUser'; @@ -78,6 +78,9 @@ export function ThemeSelector() { const { theme, customTheme, setTheme, applyCustomTheme } = useTheme(); const { user } = useCurrentUser(); const userThemesQuery = useUserThemes(user?.pubkey); + const navigate = useNavigate(); + + const hasUserThemes = (userThemesQuery.data?.length ?? 0) > 0; const builtinOptions: { id: Theme; label: string }[] = [ { id: 'system', label: 'System' }, @@ -98,8 +101,79 @@ export function ThemeSelector() { }; return ( -
-
+
+ + {/* ── My Themes section ── */} + {user && ( +
+

My Themes

+
+ {/* Create new custom theme */} + +
+ + New +
+

+ Create Theme +

+ + + {/* User's published themes */} + {userThemesQuery.data?.map((userTheme) => { + const isActive = theme === 'custom' && customTheme && JSON.stringify(customTheme) === JSON.stringify(userTheme.tokens); + + return ( + + ); + })} + + {/* Browse public themes */} + +
+ + Browse +
+

+ Public Themes +

+ +
+
+ )} + + {/* ── Presets section ── */} +
+

+ {hasUserThemes ? 'Presets' : 'Themes'} +

+
{builtinOptions.map((option) => { if (option.id === 'system') { const isActive = theme === 'system'; @@ -195,19 +269,6 @@ export function ThemeSelector() { ); })} - {/* Active custom theme (if it doesn't match any preset) */} - {theme === 'custom' && customTheme && !presetOptions.some(p => isPresetActive(p.tokens)) && ( - - )} - {/* Preset buttons */} {presetOptions.map((preset) => { const isActive = isPresetActive(preset.tokens); @@ -234,55 +295,23 @@ export function ThemeSelector() { ); })} - {/* User's published themes */} - {userThemesQuery.data?.map((userTheme) => { - const isActive = theme === 'custom' && customTheme && JSON.stringify(customTheme) === JSON.stringify(userTheme.tokens); - - return ( - - ); - })} - - {/* Browse public themes */} - -
- - Browse -
-

- Public Themes -

- + {/* Browse public themes — shown in presets section when user has no custom themes */} + {!user && ( + +
+ + Browse +
+

+ Public Themes +

+ + )}
- - {/* Customize link */} - - - Customize your own theme - +
); } diff --git a/src/pages/ThemeBuilderPage.tsx b/src/pages/ThemeBuilderPage.tsx index dd7833c9..b0eab633 100644 --- a/src/pages/ThemeBuilderPage.tsx +++ b/src/pages/ThemeBuilderPage.tsx @@ -95,9 +95,10 @@ export function ThemeBuilderPage() { const { user } = useCurrentUser(); const { publishTheme, setActiveTheme, deleteTheme, isPending: isPublishing } = usePublishTheme(); - // Check if we're importing from a profile + // Check if we're importing from a profile or editing an existing theme const importPubkey = searchParams.get('import'); const importThemeId = searchParams.get('theme'); + const editIdentifier = searchParams.get('edit'); // Check if the user currently has a published active profile theme const ownActiveTheme = useActiveProfileTheme(user?.pubkey); @@ -135,6 +136,17 @@ export function ThemeBuilderPage() { if (match) setActiveEditingTheme(match); }, [_userThemes.data]); // eslint-disable-line react-hooks/exhaustive-deps + // Load a specific theme for editing via ?edit= param + useEffect(() => { + if (!editIdentifier || !_userThemes.data) return; + const target = _userThemes.data.find(t => t.identifier === editIdentifier); + if (target && activeEditingTheme?.identifier !== editIdentifier) { + setTokens(target.tokens); + setAutoDerive(false); + setActiveEditingTheme(target); + } + }, [editIdentifier, _userThemes.data]); // eslint-disable-line react-hooks/exhaustive-deps + // Clear editing context when starting fresh const handleNewTheme = useCallback(() => { setActiveEditingTheme(null);