diff --git a/app/globals.css b/app/globals.css index 7f2c280f..692cb0a5 100644 --- a/app/globals.css +++ b/app/globals.css @@ -154,6 +154,15 @@ flex-shrink: 0; } + /* Ensure flex-1 wrappers inside icons bar don't expand too much */ + .mobile-icons-bar-content .flex-1 { + flex: 1 1 0; + min-width: 0; + display: flex; + justify-content: center; + align-items: center; + } + /* .mobile-chat-section { flex: 1; diff --git a/components/header-search-button.tsx b/components/header-search-button.tsx index 7090a52c..66d9dfcc 100644 --- a/components/header-search-button.tsx +++ b/components/header-search-button.tsx @@ -183,7 +183,7 @@ export function HeaderSearchButton() { ) const mobileButton = ( - - + diff --git a/components/mobile-icons-bar.tsx b/components/mobile-icons-bar.tsx index 379764bc..8c1f1a99 100644 --- a/components/mobile-icons-bar.tsx +++ b/components/mobile-icons-bar.tsx @@ -16,7 +16,6 @@ import { } from 'lucide-react' import { History } from '@/components/history' import { MapToggle } from './map-toggle' -import { ModeToggle } from './mode-toggle' import { ProfileToggle } from './profile-toggle' import { useCalendarToggle } from './calendar-toggle-context' import { useUsageToggle } from './usage-toggle-context' @@ -48,12 +47,16 @@ export const MobileIconsBar: React.FC = ({ onAttachmentClic } return ( -
+
- - +
+ +
+
+ +
@@ -68,7 +71,6 @@ export const MobileIconsBar: React.FC = ({ onAttachmentClic -
) } diff --git a/components/profile-toggle.tsx b/components/profile-toggle.tsx index b942facf..1193fc60 100644 --- a/components/profile-toggle.tsx +++ b/components/profile-toggle.tsx @@ -65,9 +65,16 @@ export function ProfileToggle() { return } + // Mobile: show profile icon as a direct button that opens the profile view if (isMobile) { return ( - diff --git a/components/settings/components/settings.tsx b/components/settings/components/settings.tsx index b51ba70d..30992ac2 100644 --- a/components/settings/components/settings.tsx +++ b/components/settings/components/settings.tsx @@ -7,7 +7,9 @@ import { useRouter } from 'next/navigation' import { zodResolver } from '@hookform/resolvers/zod' import { useForm } from 'react-hook-form' import * as z from 'zod' -import { Loader2, Save, RotateCcw } from 'lucide-react' +import { Loader2, Save, RotateCcw, Sun, Moon, Earth } from 'lucide-react' +import { useTheme } from 'next-themes' +import { cn } from '@/lib/utils' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card' @@ -71,6 +73,12 @@ export function Settings({ initialTab = "system-prompt" }: SettingsProps) { const [currentTab, setCurrentTab] = useState(initialTab); const { mapProvider, setMapProvider } = useSettingsStore(); const { user, loading: authLoading } = useCurrentUser(); + const { theme, setTheme } = useTheme() + const [mounted, setMounted] = useState(false) + + useEffect(() => { + setMounted(true) + }, []) useEffect(() => { setCurrentTab(initialTab); @@ -153,6 +161,7 @@ export function Settings({ initialTab = "system-prompt" }: SettingsProps) { function onReset() { form.reset(defaultValues) + setTheme('earth') toast({ title: "Settings reset", description: "Your settings have been reset to default values.", @@ -163,8 +172,45 @@ export function Settings({ initialTab = "system-prompt" }: SettingsProps) {
+ {/* Theme selector placed above the tabs */} + + + Theme Selection + Select the theme for your planetary copilot + + +
+ {[ + { name: 'Light', value: 'light', icon: Sun }, + { name: 'Dark', value: 'dark', icon: Moon }, + { name: 'Earth', value: 'earth', icon: Earth }, + ].map((t) => { + const Icon = t.icon + const isActive = mounted && theme === t.value + return ( + + ) + })} +
+
+
+ - + System Prompt Model Selection User Management @@ -229,6 +275,8 @@ export function Settings({ initialTab = "system-prompt" }: SettingsProps) { + + diff --git a/components/settings/settings-view.tsx b/components/settings/settings-view.tsx index cddce42f..b9e94844 100644 --- a/components/settings/settings-view.tsx +++ b/components/settings/settings-view.tsx @@ -8,6 +8,7 @@ import { DownloadReportButton } from "@/components/download-report-button" export default function SettingsView() { const { toggleProfileSection, activeView } = useProfileToggle(); + // Appearance now shows the theme selector (above tabs) with system-prompt as default tab const initialTab = activeView === ProfileToggleEnum.Security ? "user-management" : "system-prompt"; const handleClose = () => { diff --git a/tests/header.spec.ts b/tests/header.spec.ts index 55065420..23091a82 100644 --- a/tests/header.spec.ts +++ b/tests/header.spec.ts @@ -3,16 +3,21 @@ import { test, expect } from '@playwright/test'; test.describe('Header and Navigation', () => { test.beforeEach(async ({ page }) => { await page.goto('/'); + try { + await page.locator('text=Later').click({ timeout: 3000 }); + } catch (e) {} }); - test('should toggle the theme', async ({ page }) => { - await page.click('[data-testid="theme-toggle"]'); - await page.click('[data-testid="theme-dark"]'); + test('should toggle the theme in settings', async ({ page }) => { + // Theme selector is now placed above the settings tabs + await page.click('[data-testid="profile-toggle"]'); + await page.click('[data-testid="profile-settings"]'); + + await page.click('[data-testid="theme-select-dark"]'); const html = page.locator('html'); await expect(html).toHaveClass(/(^|\s)dark(\s|$)/); - await page.click('[data-testid="theme-toggle"]'); - await page.click('[data-testid="theme-light"]'); + await page.click('[data-testid="theme-select-light"]'); await expect(html).not.toHaveClass(/(^|\s)dark(\s|$)/); }); diff --git a/tests/mobile.spec.ts b/tests/mobile.spec.ts index d138f4f6..3cb07d8e 100644 --- a/tests/mobile.spec.ts +++ b/tests/mobile.spec.ts @@ -9,9 +9,9 @@ test.describe('Mobile UI', () => { await page.locator('[data-testid="chat-input"]').waitFor({ state: 'visible', timeout: 30000 }); }); - test('profile toggle button should be disabled', async ({ page }) => { - // Check that the profile toggle is disabled on mobile - await expect(page.locator('.mobile-icons-bar-content [data-testid="profile-toggle"]')).toBeDisabled(); + test('profile toggle button should be enabled on mobile', async ({ page }) => { + // Check that the profile toggle is enabled and clickable on mobile + await expect(page.locator('.mobile-icons-bar-content [data-testid="profile-toggle"]')).toBeEnabled(); }); test('should have an enabled submit button', async ({ page }) => { diff --git a/tests/responsive.spec.ts b/tests/responsive.spec.ts index a53fcd6e..e3129237 100644 --- a/tests/responsive.spec.ts +++ b/tests/responsive.spec.ts @@ -5,6 +5,9 @@ test.describe('Responsive design - Desktop', () => { test.beforeEach(async ({ page }) => { await page.goto('/'); + try { + await page.locator('text=Later').click({ timeout: 3000 }); + } catch (e) {} }); test('should display desktop layout', async ({ page }) => { @@ -18,7 +21,7 @@ test.describe('Responsive design - Desktop', () => { }); test('should display full header with all elements', async ({ page }) => { - await expect(page.locator('[data-testid="theme-toggle"]')).toBeVisible(); + await expect(page.locator('[data-testid="history-button"]').first()).toBeVisible(); await expect(page.locator('[data-testid="profile-toggle"]')).toBeVisible(); await expect(page.locator('[data-testid="calendar-toggle"]')).toBeVisible(); }); @@ -44,6 +47,9 @@ test.describe('Responsive design - Tablet', () => { test.beforeEach(async ({ page }) => { await page.goto('/'); + try { + await page.locator('text=Later').click({ timeout: 3000 }); + } catch (e) {} }); test('should display tablet layout', async ({ page }) => { @@ -96,6 +102,9 @@ test.describe('Responsive design - Mobile', () => { test.beforeEach(async ({ page }) => { await page.goto('/'); + try { + await page.locator('text=Later').click({ timeout: 3000 }); + } catch (e) {} }); test('should display mobile layout', async ({ page }) => { @@ -195,6 +204,9 @@ test.describe('Responsive design - Small Mobile', () => { test.beforeEach(async ({ page }) => { await page.goto('/'); + try { + await page.locator('text=Later').click({ timeout: 3000 }); + } catch (e) {} }); test('should work on very small screens', async ({ page }) => { @@ -229,6 +241,9 @@ test.describe('Responsive design - Large Desktop', () => { test.beforeEach(async ({ page }) => { await page.goto('/'); + try { + await page.locator('text=Later').click({ timeout: 3000 }); + } catch (e) {} }); test('should utilize large screen space', async ({ page }) => {