From f091409f7e6570b20684b413f1f7694926e91b9b Mon Sep 17 00:00:00 2001 From: Stijnus <72551117+Stijnus@users.noreply.github.com> Date: Mon, 3 Feb 2025 01:04:23 +0100 Subject: [PATCH] Avatar Fix , control pannel UI fix --- .../@settings/core/ControlPanel.tsx | 189 ++++++------ .../shared/components/TabManagement.tsx | 17 +- .../@settings/tabs/debug/DebugTab.tsx | 290 ++++++++++++++---- .../providers/local/LocalProvidersTab.tsx | 15 +- app/components/chat/Messages.client.tsx | 17 +- app/components/sidebar/Menu.client.tsx | 25 +- app/lib/stores/settings.ts | 3 +- 7 files changed, 392 insertions(+), 164 deletions(-) diff --git a/app/components/@settings/core/ControlPanel.tsx b/app/components/@settings/core/ControlPanel.tsx index 3ccc3be0..c0e19035 100644 --- a/app/components/@settings/core/ControlPanel.tsx +++ b/app/components/@settings/core/ControlPanel.tsx @@ -22,6 +22,7 @@ import type { TabType, TabVisibilityConfig, Profile } from './types'; import { TAB_LABELS, DEFAULT_TAB_CONFIG } from './constants'; import { DialogTitle } from '~/components/ui/Dialog'; import { AvatarDropdown } from './AvatarDropdown'; +import BackgroundRays from '~/components/ui/BackgroundRays'; // Import all tab components import ProfileTab from '~/components/@settings/tabs/profile/ProfileTab'; @@ -83,7 +84,7 @@ const TAB_DESCRIPTIONS: Record = { }; // Beta status for experimental features -const BETA_TABS = new Set(['task-manager', 'service-status']); +const BETA_TABS = new Set(['task-manager', 'service-status', 'update', 'local-providers']); const BetaLabel = () => (
@@ -415,108 +416,114 @@ export const ControlPanel = ({ open, onClose }: ControlPanelProps) => { 'rounded-2xl shadow-2xl', 'border border-[#E5E5E5] dark:border-[#1A1A1A]', 'flex flex-col overflow-hidden', + 'relative', )} initial={{ opacity: 0, scale: 0.95, y: 20 }} animate={{ opacity: 1, scale: 1, y: 0 }} exit={{ opacity: 0, scale: 0.95, y: 20 }} transition={{ duration: 0.2 }} > - {/* Header */} -
-
- {(activeTab || showTabManagement) && ( +
+ +
+
+ {/* Header */} +
+
+ {(activeTab || showTabManagement) && ( + + )} + + {showTabManagement ? 'Tab Management' : activeTab ? TAB_LABELS[activeTab] : 'Control Panel'} + +
+ +
+ {/* Mode Toggle */} +
+ +
+ + {/* Avatar and Dropdown */} +
+ +
+ + {/* Close Button */} +
+
+ + {/* Content */} +
- {showTabManagement ? 'Tab Management' : activeTab ? TAB_LABELS[activeTab] : 'Control Panel'} - -
- -
- {/* Mode Toggle */} -
- -
- - {/* Avatar and Dropdown */} -
- -
- - {/* Close Button */} - -
-
- - {/* Content */} -
- - {showTabManagement ? ( - - ) : activeTab ? ( - getTabComponent(activeTab) - ) : ( - - - {(visibleTabs as TabWithDevType[]).map((tab: TabWithDevType) => ( - - handleTabClick(tab.id as TabType)} - isActive={activeTab === tab.id} - hasUpdate={getTabUpdateStatus(tab.id)} - statusMessage={getStatusMessage(tab.id)} - description={TAB_DESCRIPTIONS[tab.id]} - isLoading={loadingTab === tab.id} - className="h-full relative" - > - {BETA_TABS.has(tab.id) && } - - - ))} - - - )} - + + {showTabManagement ? ( + + ) : activeTab ? ( + getTabComponent(activeTab) + ) : ( + + + {(visibleTabs as TabWithDevType[]).map((tab: TabWithDevType) => ( + + handleTabClick(tab.id as TabType)} + isActive={activeTab === tab.id} + hasUpdate={getTabUpdateStatus(tab.id)} + statusMessage={getStatusMessage(tab.id)} + description={TAB_DESCRIPTIONS[tab.id]} + isLoading={loadingTab === tab.id} + className="h-full relative" + > + {BETA_TABS.has(tab.id) && } + + + ))} + + + )} + +
diff --git a/app/components/@settings/shared/components/TabManagement.tsx b/app/components/@settings/shared/components/TabManagement.tsx index 112dac53..ec6acece 100644 --- a/app/components/@settings/shared/components/TabManagement.tsx +++ b/app/components/@settings/shared/components/TabManagement.tsx @@ -44,6 +44,14 @@ const OPTIONAL_USER_TABS: TabType[] = ['profile', 'settings', 'task-manager', 's // All available tabs for user mode const ALL_USER_TABS = [...DEFAULT_USER_TABS, ...OPTIONAL_USER_TABS]; +// Define which tabs are beta +const BETA_TABS = new Set(['task-manager', 'service-status', 'update', 'local-providers']); + +// Beta label component +const BetaLabel = () => ( + BETA +); + export const TabManagement = () => { const [searchQuery, setSearchQuery] = useState(''); const tabConfiguration = useStore(tabConfigurationStore); @@ -217,9 +225,12 @@ export const TabManagement = () => {
-

- {TAB_LABELS[tab.id]} -

+
+

+ {TAB_LABELS[tab.id]} +

+ {BETA_TABS.has(tab.id) && } +

{tab.visible ? 'Visible in user mode' : 'Hidden in user mode'}

diff --git a/app/components/@settings/tabs/debug/DebugTab.tsx b/app/components/@settings/tabs/debug/DebugTab.tsx index 453dfe3a..ae7a8339 100644 --- a/app/components/@settings/tabs/debug/DebugTab.tsx +++ b/app/components/@settings/tabs/debug/DebugTab.tsx @@ -9,6 +9,7 @@ import { ScrollArea } from '~/components/ui/ScrollArea'; import { Badge } from '~/components/ui/Badge'; import { Dialog, DialogRoot, DialogTitle } from '~/components/ui/Dialog'; import { jsPDF } from 'jspdf'; +import { useSettings } from '~/lib/hooks/useSettings'; interface SystemInfo { os: string; @@ -138,6 +139,11 @@ interface OllamaServiceStatus { isRunning: boolean; lastChecked: Date; error?: string; + models?: Array<{ + name: string; + size: string; + quantization: string; + }>; } interface ExportFormat { @@ -232,6 +238,8 @@ export default function DebugTab() { performance: false, }); + const { isLocalModel, providers } = useSettings(); + // Subscribe to logStore updates const logs = useStore(logStore.logs); const errorLogs = useMemo(() => { @@ -1093,41 +1101,48 @@ export default function DebugTab() { ]; // Add Ollama health check function - const checkOllamaHealth = async () => { + const checkOllamaStatus = useCallback(async () => { try { - const response = await fetch('http://127.0.0.1:11434/api/version'); - const isHealthy = response.ok; + // First check if service is running + const versionResponse = await fetch('http://127.0.0.1:11434/api/version'); + + if (!versionResponse.ok) { + throw new Error('Service not running'); + } + + // Then fetch installed models + const modelsResponse = await fetch('http://127.0.0.1:11434/api/tags'); + + const modelsData = (await modelsResponse.json()) as { + models: Array<{ name: string; size: string; quantization: string }>; + }; setOllamaStatus({ - isRunning: isHealthy, + isRunning: true, lastChecked: new Date(), - error: isHealthy ? undefined : 'Ollama service is not responding', + models: modelsData.models, }); - - return isHealthy; } catch { setOllamaStatus({ isRunning: false, + error: 'Connection failed', lastChecked: new Date(), - error: 'Failed to connect to Ollama service', + models: undefined, }); - return false; } - }; - - // Add Ollama health check effect - useEffect(() => { - const checkHealth = async () => { - await checkOllamaHealth(); - }; - - checkHealth(); - - const interval = setInterval(checkHealth, 30000); // Check every 30 seconds - - return () => clearInterval(interval); }, []); + // Monitor isLocalModel changes and check status periodically + useEffect(() => { + // Check immediately when isLocalModel changes + checkOllamaStatus(); + + // Set up periodic checks every 10 seconds + const intervalId = setInterval(checkOllamaStatus, 10000); + + return () => clearInterval(intervalId); + }, [isLocalModel, checkOllamaStatus]); + // Replace the existing export button with this new component const ExportButton = () => { const [isOpen, setIsOpen] = useState(false); @@ -1199,60 +1214,225 @@ export default function DebugTab() { ); }; + // Add helper function to get Ollama status text and color + const getOllamaStatus = () => { + const ollamaProvider = providers?.Ollama; + const isOllamaEnabled = ollamaProvider?.settings?.enabled; + + if (!isLocalModel) { + return { + status: 'Disabled', + color: 'text-red-500', + bgColor: 'bg-red-500', + message: 'Local models are disabled in settings', + }; + } + + if (!isOllamaEnabled) { + return { + status: 'Disabled', + color: 'text-red-500', + bgColor: 'bg-red-500', + message: 'Ollama provider is disabled in settings', + }; + } + + if (!ollamaStatus.isRunning) { + return { + status: 'Not Running', + color: 'text-red-500', + bgColor: 'bg-red-500', + message: ollamaStatus.error || 'Ollama service is not running', + }; + } + + const modelCount = ollamaStatus.models?.length ?? 0; + + return { + status: 'Running', + color: 'text-green-500', + bgColor: 'bg-green-500', + message: `Ollama service is running with ${modelCount} installed models (Provider: Enabled)`, + }; + }; + + // Add type for status result + type StatusResult = { + status: string; + color: string; + bgColor: string; + message: string; + }; + + const status = getOllamaStatus() as StatusResult; + return (
{/* Quick Stats Banner */}
- {/* Add Ollama Service Status Card */} -
-
Ollama Service
+ {/* Ollama Service Status Card */} +
+
+
+
Ollama Service
+
- - {ollamaStatus.isRunning ? 'Running' : 'Not Running'} + + {status.status === 'Running' &&
} + {status.status === 'Not Running' &&
} + {status.status === 'Disabled' &&
} + {status.status}
-
+
+
+ {status.message} +
+ {ollamaStatus.models && ollamaStatus.models.length > 0 && ( +
+
+
+ Installed Models +
+ {ollamaStatus.models.map((model) => ( +
+
+ {model.name} + + ({Math.round(parseInt(model.size) / 1024 / 1024)}MB, {model.quantization}) + +
+ ))} +
+ )} +
+
Last checked: {ollamaStatus.lastChecked.toLocaleTimeString()}
-
-
Memory Usage
-
- {systemInfo?.memory.percentage}% + {/* Memory Usage Card */} +
+
+
+
Memory Usage
- -
- -
-
Page Load Time
-
- {systemInfo ? (systemInfo.performance.timing.loadTime / 1000).toFixed(2) + 's' : '-'} +
+ 80 + ? 'text-red-500' + : (systemInfo?.memory?.percentage ?? 0) > 60 + ? 'text-yellow-500' + : 'text-green-500', + )} + > + {systemInfo?.memory?.percentage ?? 0}% +
-
- DOM Ready: {systemInfo ? (systemInfo.performance.timing.domReadyTime / 1000).toFixed(2) + 's' : '-'} + 80 + ? '[&>div]:bg-red-500' + : (systemInfo?.memory?.percentage ?? 0) > 60 + ? '[&>div]:bg-yellow-500' + : '[&>div]:bg-green-500', + )} + /> +
+
+ Used: {systemInfo?.memory.used ?? '0 GB'} / {systemInfo?.memory.total ?? '0 GB'}
-
-
Network Speed
-
- {systemInfo?.network.downlink || '-'} Mbps + {/* Page Load Time Card */} +
+
+
+
Page Load Time
+
+
+ 2000 + ? 'text-red-500' + : (systemInfo?.performance.timing.loadTime ?? 0) > 1000 + ? 'text-yellow-500' + : 'text-green-500', + )} + > + {systemInfo ? (systemInfo.performance.timing.loadTime / 1000).toFixed(2) : '-'}s + +
+
+
+ DOM Ready: {systemInfo ? (systemInfo.performance.timing.domReadyTime / 1000).toFixed(2) : '-'}s
-
RTT: {systemInfo?.network.rtt || '-'} ms
-
-
Errors
-
{errorLogs.length}
+ {/* Network Speed Card */} +
+
+
+
Network Speed
+
+
+ + {systemInfo?.network.downlink ?? '-'} Mbps + +
+
+
+ RTT: {systemInfo?.network.rtt ?? '-'} ms +
+
+ + {/* Errors Card */} +
+
+
+
Errors
+
+
+ 0 ? 'text-red-500' : 'text-green-500')} + > + {errorLogs.length} + +
+
+
0 ? 'i-ph:warning text-red-500' : 'i-ph:check-circle text-green-500', + )} + /> + {errorLogs.length > 0 ? 'Errors detected' : 'No errors detected'} +
diff --git a/app/components/@settings/tabs/providers/local/LocalProvidersTab.tsx b/app/components/@settings/tabs/providers/local/LocalProvidersTab.tsx index fc7630a3..df986302 100644 --- a/app/components/@settings/tabs/providers/local/LocalProvidersTab.tsx +++ b/app/components/@settings/tabs/providers/local/LocalProvidersTab.tsx @@ -87,19 +87,10 @@ export default function LocalProvidersTab() { .map(([key, value]) => { const provider = value as IProviderConfig; const envKey = providerBaseUrlEnvKeys[key]?.baseUrlKey; - - // Get environment URL safely const envUrl = envKey ? (import.meta.env[envKey] as string | undefined) : undefined; - console.log(`Checking env URL for ${key}:`, { - envKey, - envUrl, - currentBaseUrl: provider.settings.baseUrl, - }); - - // If there's an environment URL and no base URL set, update it + // Set base URL if provided by environment if (envUrl && !provider.settings.baseUrl) { - console.log(`Setting base URL for ${key} from env:`, envUrl); updateProviderSettings(key, { ...provider.settings, baseUrl: envUrl, @@ -414,7 +405,9 @@ export default function LocalProvidersTab() {
-

Local AI Models

+
+

Local AI Models

+

Configure and manage your local AI providers

diff --git a/app/components/chat/Messages.client.tsx b/app/components/chat/Messages.client.tsx index 36f387ed..1e6b64bf 100644 --- a/app/components/chat/Messages.client.tsx +++ b/app/components/chat/Messages.client.tsx @@ -8,6 +8,8 @@ import { db, chatId } from '~/lib/persistence/useChatHistory'; import { forkChat } from '~/lib/persistence/db'; import { toast } from 'react-toastify'; import WithTooltip from '~/components/ui/Tooltip'; +import { useStore } from '@nanostores/react'; +import { profileStore } from '~/lib/stores/profile'; interface MessagesProps { id?: string; @@ -24,6 +26,7 @@ export const Messages = React.forwardRef((props: const [isUserInteracting, setIsUserInteracting] = useState(false); const [lastScrollTop, setLastScrollTop] = useState(0); const [shouldAutoScroll, setShouldAutoScroll] = useState(true); + const profile = useStore(profileStore); // Check if we should auto-scroll based on scroll position const checkShouldAutoScroll = () => { @@ -166,8 +169,18 @@ export const Messages = React.forwardRef((props: })} > {isUserMessage && ( -
-
+
+ {profile?.avatar ? ( + {profile?.username + ) : ( +
+ )}
)}
diff --git a/app/components/sidebar/Menu.client.tsx b/app/components/sidebar/Menu.client.tsx index 48ba2e8b..59c1fc22 100644 --- a/app/components/sidebar/Menu.client.tsx +++ b/app/components/sidebar/Menu.client.tsx @@ -12,6 +12,8 @@ import { HistoryItem } from './HistoryItem'; import { binDates } from './date-binning'; import { useSearchFilter } from '~/lib/hooks/useSearchFilter'; import { classNames } from '~/utils/classNames'; +import { useStore } from '@nanostores/react'; +import { profileStore } from '~/lib/stores/profile'; const menuVariants = { closed: { @@ -65,6 +67,7 @@ export const Menu = () => { const [open, setOpen] = useState(false); const [dialogContent, setDialogContent] = useState(null); const [isSettingsOpen, setIsSettingsOpen] = useState(false); + const profile = useStore(profileStore); const { filteredItems: filteredList, handleSearchChange } = useSearchFilter({ items: list, @@ -169,7 +172,27 @@ export const Menu = () => { isSettingsOpen ? 'z-40' : 'z-sidebar', )} > -
+
+
+
+ + {profile?.username || 'Guest User'} + +
+ {profile?.avatar ? ( + {profile?.username + ) : ( +
+ )} +
+
+
diff --git a/app/lib/stores/settings.ts b/app/lib/stores/settings.ts index 97802a0b..d8b1ca18 100644 --- a/app/lib/stores/settings.ts +++ b/app/lib/stores/settings.ts @@ -90,7 +90,8 @@ const getInitialProviderSettings = (): ProviderSetting => { initialSettings[provider.name] = { ...provider, settings: { - enabled: true, + // Local providers should be disabled by default + enabled: !LOCAL_PROVIDERS.includes(provider.name), }, }; });