From e716ca55f0459b9f01d8a9d8732925e8e33ccdcc Mon Sep 17 00:00:00 2001 From: Stijnus <72551117+Stijnus@users.noreply.github.com> Date: Thu, 12 Dec 2024 23:46:41 +0100 Subject: [PATCH 01/14] Add Logo icons LLM's Added the Next.js Image component import for optimized image loading Modified the provider item layout to include an icon next to the provider name Used template literals to dynamically load the correct icon based on the provider name Added proper sizing (24x24) for consistent icon display Added dark:invert class to ensure icons are visible in dark mode (assuming they're dark by default) --- app/components/settings/providers/ProvidersTab.tsx | 9 ++++++++- public/icons/Anthropic.svg | 4 ++++ public/icons/Cohere.svg | 4 ++++ public/icons/Deepseek.svg | 5 +++++ public/icons/Google.svg | 4 ++++ public/icons/Groq.svg | 4 ++++ public/icons/HuggingFace.svg | 4 ++++ public/icons/LMStudio.svg | 5 +++++ public/icons/Mistral.svg | 4 ++++ public/icons/Ollama.svg | 4 ++++ public/icons/OpenAI.svg | 4 ++++ public/icons/OpenAILike.svg | 4 ++++ public/icons/OpenRouter.svg | 4 ++++ public/icons/Together.svg | 4 ++++ public/icons/xAI.svg | 5 +++++ 15 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 public/icons/Anthropic.svg create mode 100644 public/icons/Cohere.svg create mode 100644 public/icons/Deepseek.svg create mode 100644 public/icons/Google.svg create mode 100644 public/icons/Groq.svg create mode 100644 public/icons/HuggingFace.svg create mode 100644 public/icons/LMStudio.svg create mode 100644 public/icons/Mistral.svg create mode 100644 public/icons/Ollama.svg create mode 100644 public/icons/OpenAI.svg create mode 100644 public/icons/OpenAILike.svg create mode 100644 public/icons/OpenRouter.svg create mode 100644 public/icons/Together.svg create mode 100644 public/icons/xAI.svg diff --git a/app/components/settings/providers/ProvidersTab.tsx b/app/components/settings/providers/ProvidersTab.tsx index 309afb8..9e9da36 100644 --- a/app/components/settings/providers/ProvidersTab.tsx +++ b/app/components/settings/providers/ProvidersTab.tsx @@ -49,7 +49,14 @@ export default function ProvidersTab() { className="flex flex-col mb-2 provider-item hover:bg-bolt-elements-bg-depth-3 p-4 rounded-lg border border-bolt-elements-borderColor " >
- {provider.name} +
+ {`${provider.name} + {provider.name} +
+ + + diff --git a/public/icons/Cohere.svg b/public/icons/Cohere.svg new file mode 100644 index 0000000..c7531c4 --- /dev/null +++ b/public/icons/Cohere.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/Deepseek.svg b/public/icons/Deepseek.svg new file mode 100644 index 0000000..c3a4049 --- /dev/null +++ b/public/icons/Deepseek.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icons/Google.svg b/public/icons/Google.svg new file mode 100644 index 0000000..79b262a --- /dev/null +++ b/public/icons/Google.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/Groq.svg b/public/icons/Groq.svg new file mode 100644 index 0000000..b198fb4 --- /dev/null +++ b/public/icons/Groq.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/HuggingFace.svg b/public/icons/HuggingFace.svg new file mode 100644 index 0000000..6a70cd8 --- /dev/null +++ b/public/icons/HuggingFace.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/LMStudio.svg b/public/icons/LMStudio.svg new file mode 100644 index 0000000..d8d9887 --- /dev/null +++ b/public/icons/LMStudio.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icons/Mistral.svg b/public/icons/Mistral.svg new file mode 100644 index 0000000..7526259 --- /dev/null +++ b/public/icons/Mistral.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/Ollama.svg b/public/icons/Ollama.svg new file mode 100644 index 0000000..dd63997 --- /dev/null +++ b/public/icons/Ollama.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/OpenAI.svg b/public/icons/OpenAI.svg new file mode 100644 index 0000000..5af0871 --- /dev/null +++ b/public/icons/OpenAI.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/OpenAILike.svg b/public/icons/OpenAILike.svg new file mode 100644 index 0000000..20e3813 --- /dev/null +++ b/public/icons/OpenAILike.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/OpenRouter.svg b/public/icons/OpenRouter.svg new file mode 100644 index 0000000..7cfe36b --- /dev/null +++ b/public/icons/OpenRouter.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/Together.svg b/public/icons/Together.svg new file mode 100644 index 0000000..a2975f1 --- /dev/null +++ b/public/icons/Together.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/xAI.svg b/public/icons/xAI.svg new file mode 100644 index 0000000..5a57ea5 --- /dev/null +++ b/public/icons/xAI.svg @@ -0,0 +1,5 @@ + + + + + From e39f16e436a14b1cc41401050aae4713dbec8cb3 Mon Sep 17 00:00:00 2001 From: Stijnus <72551117+Stijnus@users.noreply.github.com> Date: Fri, 13 Dec 2024 01:11:35 +0100 Subject: [PATCH 02/14] Settings UI enhancement Date & Time Display Added a real-time clock component in the sidebar Event Logs System Implemented an EventLogsTab component for system monitoring Provides a structured way to: Track user interactions Monitor system events Display activity history --- app/components/chat/ImportFolderButton.tsx | 23 +++ app/components/settings/SettingsWindow.tsx | 16 +- .../settings/chat-history/ChatHistoryTab.tsx | 14 +- .../settings/connections/ConnectionsTab.tsx | 5 + .../settings/event-logs/EventLogsTab.tsx | 145 +++++++++++++++++ .../settings/features/FeaturesTab.tsx | 6 +- .../settings/providers/ProvidersTab.tsx | 22 ++- app/components/sidebar/Menu.client.tsx | 27 +++- app/lib/hooks/useSettings.tsx | 27 +++- app/lib/persistence/useChatHistory.ts | 4 + app/lib/stores/logs.ts | 149 ++++++++++++++++++ app/lib/stores/settings.ts | 2 + app/lib/stores/theme.ts | 5 +- app/root.tsx | 19 ++- app/utils/constants.ts | 12 +- 15 files changed, 450 insertions(+), 26 deletions(-) create mode 100644 app/components/settings/event-logs/EventLogsTab.tsx create mode 100644 app/lib/stores/logs.ts diff --git a/app/components/chat/ImportFolderButton.tsx b/app/components/chat/ImportFolderButton.tsx index 6cbfcac..c75200e 100644 --- a/app/components/chat/ImportFolderButton.tsx +++ b/app/components/chat/ImportFolderButton.tsx @@ -3,6 +3,7 @@ import type { Message } from 'ai'; import { toast } from 'react-toastify'; import { MAX_FILES, isBinaryFile, shouldIncludeFile } from '~/utils/fileUtils'; import { createChatFromFolder } from '~/utils/folderImport'; +import { logStore } from '~/lib/stores/logs'; // Assuming logStore is imported from this location interface ImportFolderButtonProps { className?: string; @@ -16,9 +17,15 @@ export const ImportFolderButton: React.FC = ({ classNam const allFiles = Array.from(e.target.files || []); if (allFiles.length > MAX_FILES) { + const error = new Error(`Too many files: ${allFiles.length}`); + logStore.logError('File import failed - too many files', error, { + fileCount: allFiles.length, + maxFiles: MAX_FILES, + }); toast.error( `This folder contains ${allFiles.length.toLocaleString()} files. This product is not yet optimized for very large projects. Please select a folder with fewer than ${MAX_FILES.toLocaleString()} files.`, ); + return; } @@ -31,7 +38,10 @@ export const ImportFolderButton: React.FC = ({ classNam const filteredFiles = allFiles.filter((file) => shouldIncludeFile(file.webkitRelativePath)); if (filteredFiles.length === 0) { + const error = new Error('No valid files found'); + logStore.logError('File import failed - no valid files', error, { folderName }); toast.error('No files found in the selected folder'); + return; } @@ -48,11 +58,18 @@ export const ImportFolderButton: React.FC = ({ classNam .map((f) => f.file.webkitRelativePath.split('/').slice(1).join('/')); if (textFiles.length === 0) { + const error = new Error('No text files found'); + logStore.logError('File import failed - no text files', error, { folderName }); toast.error('No text files found in the selected folder'); + return; } if (binaryFilePaths.length > 0) { + logStore.logWarning(`Skipping binary files during import`, { + folderName, + binaryCount: binaryFilePaths.length, + }); toast.info(`Skipping ${binaryFilePaths.length} binary files`); } @@ -62,8 +79,14 @@ export const ImportFolderButton: React.FC = ({ classNam await importChat(folderName, [...messages]); } + logStore.logSystem('Folder imported successfully', { + folderName, + textFileCount: textFiles.length, + binaryFileCount: binaryFilePaths.length, + }); toast.success('Folder imported successfully'); } catch (error) { + logStore.logError('Failed to import folder', error, { folderName }); console.error('Failed to import folder:', error); toast.error('Failed to import folder'); } finally { diff --git a/app/components/settings/SettingsWindow.tsx b/app/components/settings/SettingsWindow.tsx index b7b368d..aae0a86 100644 --- a/app/components/settings/SettingsWindow.tsx +++ b/app/components/settings/SettingsWindow.tsx @@ -10,6 +10,7 @@ import ProvidersTab from './providers/ProvidersTab'; import { useSettings } from '~/lib/hooks/useSettings'; import FeaturesTab from './features/FeaturesTab'; import DebugTab from './debug/DebugTab'; +import EventLogsTab from './event-logs/EventLogsTab'; import ConnectionsTab from './connections/ConnectionsTab'; interface SettingsProps { @@ -17,11 +18,10 @@ interface SettingsProps { onClose: () => void; } -type TabType = 'chat-history' | 'providers' | 'features' | 'debug' | 'connection'; +type TabType = 'chat-history' | 'providers' | 'features' | 'debug' | 'event-logs' | 'connection'; -// Providers that support base URL configuration export const SettingsWindow = ({ open, onClose }: SettingsProps) => { - const { debug } = useSettings(); + const { debug, eventLogs } = useSettings(); const [activeTab, setActiveTab] = useState('chat-history'); const tabs: { id: TabType; label: string; icon: string; component?: ReactElement }[] = [ @@ -39,6 +39,16 @@ export const SettingsWindow = ({ open, onClose }: SettingsProps) => { }, ] : []), + ...(eventLogs + ? [ + { + id: 'event-logs' as TabType, + label: 'Event Logs', + icon: 'i-ph:list-bullets', + component: , + }, + ] + : []), ]; return ( diff --git a/app/components/settings/chat-history/ChatHistoryTab.tsx b/app/components/settings/chat-history/ChatHistoryTab.tsx index e96f0d8..76a0f21 100644 --- a/app/components/settings/chat-history/ChatHistoryTab.tsx +++ b/app/components/settings/chat-history/ChatHistoryTab.tsx @@ -4,6 +4,7 @@ import { toast } from 'react-toastify'; import { db, deleteById, getAll } from '~/lib/persistence'; import { classNames } from '~/utils/classNames'; import styles from '~/components/settings/Settings.module.scss'; +import { logStore } from '~/lib/stores/logs'; // Import logStore for event logging export default function ChatHistoryTab() { const navigate = useNavigate(); @@ -22,7 +23,10 @@ export default function ChatHistoryTab() { const handleDeleteAllChats = async () => { if (!db) { + const error = new Error('Database is not available'); + logStore.logError('Failed to delete chats - DB unavailable', error); toast.error('Database is not available'); + return; } @@ -30,13 +34,12 @@ export default function ChatHistoryTab() { setIsDeleting(true); const allChats = await getAll(db); - - // Delete all chats one by one await Promise.all(allChats.map((chat) => deleteById(db!, chat.id))); - + logStore.logSystem('All chats deleted successfully', { count: allChats.length }); toast.success('All chats deleted successfully'); navigate('/', { replace: true }); } catch (error) { + logStore.logError('Failed to delete chats', error); toast.error('Failed to delete chats'); console.error(error); } finally { @@ -46,7 +49,10 @@ export default function ChatHistoryTab() { const handleExportAllChats = async () => { if (!db) { + const error = new Error('Database is not available'); + logStore.logError('Failed to export chats - DB unavailable', error); toast.error('Database is not available'); + return; } @@ -58,8 +64,10 @@ export default function ChatHistoryTab() { }; downloadAsJson(exportData, `all-chats-${new Date().toISOString()}.json`); + logStore.logSystem('Chats exported successfully', { count: allChats.length }); toast.success('Chats exported successfully'); } catch (error) { + logStore.logError('Failed to export chats', error); toast.error('Failed to export chats'); console.error(error); } diff --git a/app/components/settings/connections/ConnectionsTab.tsx b/app/components/settings/connections/ConnectionsTab.tsx index 32d0fa0..8338395 100644 --- a/app/components/settings/connections/ConnectionsTab.tsx +++ b/app/components/settings/connections/ConnectionsTab.tsx @@ -1,6 +1,7 @@ import React, { useState } from 'react'; import { toast } from 'react-toastify'; import Cookies from 'js-cookie'; +import { logStore } from '~/lib/stores/logs'; export default function ConnectionsTab() { const [githubUsername, setGithubUsername] = useState(Cookies.get('githubUsername') || ''); @@ -9,6 +10,10 @@ export default function ConnectionsTab() { const handleSaveConnection = () => { Cookies.set('githubUsername', githubUsername); Cookies.set('githubToken', githubToken); + logStore.logSystem('GitHub connection settings updated', { + username: githubUsername, + hasToken: !!githubToken, + }); toast.success('GitHub credentials saved successfully!'); }; diff --git a/app/components/settings/event-logs/EventLogsTab.tsx b/app/components/settings/event-logs/EventLogsTab.tsx new file mode 100644 index 0000000..d8766c7 --- /dev/null +++ b/app/components/settings/event-logs/EventLogsTab.tsx @@ -0,0 +1,145 @@ +import React, { useCallback, useEffect, useState, useMemo } from 'react'; +import { useSettings } from '~/lib/hooks/useSettings'; +import { toast } from 'react-toastify'; +import { Switch } from '~/components/ui/Switch'; +import { logStore, type LogEntry } from '~/lib/stores/logs'; + +export default function EventLogsTab() { + const {} = useSettings(); + const [logLevel, setLogLevel] = useState('info'); + const [autoScroll, setAutoScroll] = useState(true); + const [searchQuery, setSearchQuery] = useState(''); + const [, forceUpdate] = useState({}); + + useEffect(() => { + // Add some initial logs for testing + logStore.logSystem('System started', { version: '1.0.0' }); + logStore.logWarning('High memory usage detected', { memoryUsage: '85%' }); + logStore.logError('Failed to connect to provider', new Error('Connection timeout'), { provider: 'OpenAI' }); + }, []); + + const handleClearLogs = useCallback(() => { + if (confirm('Are you sure you want to clear all logs?')) { + logStore.clearLogs(); + toast.success('Logs cleared successfully'); + forceUpdate({}); // Force a re-render after clearing logs + } + }, []); + + const handleExportLogs = useCallback(() => { + try { + const logText = logStore + .getLogs() + .map( + (log) => + `[${log.level.toUpperCase()}] ${log.timestamp} - ${log.message}${ + log.details ? '\nDetails: ' + JSON.stringify(log.details, null, 2) : '' + }`, + ) + .join('\n\n'); + + const blob = new Blob([logText], { type: 'text/plain' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `event-logs-${new Date().toISOString()}.txt`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + toast.success('Logs exported successfully'); + } catch (error) { + toast.error('Failed to export logs'); + console.error('Export error:', error); + } + }, []); + + const filteredLogs = useMemo(() => { + return logStore.getFilteredLogs(logLevel, undefined, searchQuery); + }, [logLevel, searchQuery]); + + const getLevelColor = (level: LogEntry['level']) => { + switch (level) { + case 'info': + return 'text-blue-500'; + case 'warning': + return 'text-yellow-500'; + case 'error': + return 'text-red-500'; + case 'debug': + return 'text-gray-500'; + default: + return 'text-bolt-elements-textPrimary'; + } + }; + + return ( +
+
+
+

Event Logs

+
+ Auto-scroll + +
+
+ +
+ + setSearchQuery(e.target.value)} + className="bg-bolt-elements-bg-depth-2 text-bolt-elements-textPrimary rounded-lg px-3 py-1.5 text-sm flex-1" + /> + + +
+
+ +
+ {filteredLogs.length === 0 ? ( +
No logs found
+ ) : ( + filteredLogs.map((log, index) => ( +
+
+ [{log.level.toUpperCase()}] + {new Date(log.timestamp).toLocaleString()} + {log.message} +
+ {log.details && ( +
+                  {JSON.stringify(log.details, null, 2)}
+                
+ )} +
+ )) + )} +
+
+ ); +} diff --git a/app/components/settings/features/FeaturesTab.tsx b/app/components/settings/features/FeaturesTab.tsx index 0b4fa75..9c9e4a0 100644 --- a/app/components/settings/features/FeaturesTab.tsx +++ b/app/components/settings/features/FeaturesTab.tsx @@ -3,7 +3,7 @@ import { Switch } from '~/components/ui/Switch'; import { useSettings } from '~/lib/hooks/useSettings'; export default function FeaturesTab() { - const { debug, enableDebugMode, isLocalModel, enableLocalModels } = useSettings(); + const { debug, enableDebugMode, isLocalModel, enableLocalModels, eventLogs, enableEventLogs } = useSettings(); return (
@@ -12,6 +12,10 @@ export default function FeaturesTab() { Debug Info
+
+ Event Logs + +
diff --git a/app/components/settings/providers/ProvidersTab.tsx b/app/components/settings/providers/ProvidersTab.tsx index 9e9da36..edcf6f9 100644 --- a/app/components/settings/providers/ProvidersTab.tsx +++ b/app/components/settings/providers/ProvidersTab.tsx @@ -3,6 +3,7 @@ import { Switch } from '~/components/ui/Switch'; import { useSettings } from '~/lib/hooks/useSettings'; import { LOCAL_PROVIDERS, URL_CONFIGURABLE_PROVIDERS } from '~/lib/stores/settings'; import type { IProviderConfig } from '~/types/model'; +import { logStore } from '~/lib/stores/logs'; export default function ProvidersTab() { const { providers, updateProviderSettings, isLocalModel } = useSettings(); @@ -60,7 +61,15 @@ export default function ProvidersTab() { updateProviderSettings(provider.name, { ...provider.settings, enabled })} + onCheckedChange={(enabled) => { + updateProviderSettings(provider.name, { ...provider.settings, enabled }); + + if (enabled) { + logStore.logProvider(`Provider ${provider.name} enabled`, { provider: provider.name }); + } else { + logStore.logProvider(`Provider ${provider.name} disabled`, { provider: provider.name }); + } + }} />
{/* Base URL input for configurable providers */} @@ -70,9 +79,14 @@ export default function ProvidersTab() { - updateProviderSettings(provider.name, { ...provider.settings, baseUrl: e.target.value }) - } + onChange={(e) => { + const newBaseUrl = e.target.value; + updateProviderSettings(provider.name, { ...provider.settings, baseUrl: newBaseUrl }); + logStore.logProvider(`Base URL updated for ${provider.name}`, { + provider: provider.name, + baseUrl: newBaseUrl, + }); + }} placeholder={`Enter ${provider.name} base URL`} className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor" /> diff --git a/app/components/sidebar/Menu.client.tsx b/app/components/sidebar/Menu.client.tsx index 38ffb54..ac578d6 100644 --- a/app/components/sidebar/Menu.client.tsx +++ b/app/components/sidebar/Menu.client.tsx @@ -11,6 +11,7 @@ import { logger } from '~/utils/logger'; import { HistoryItem } from './HistoryItem'; import { binDates } from './date-binning'; import { useSearchFilter } from '~/lib/hooks/useSearchFilter'; +import { ClockIcon } from '@heroicons/react/24/outline'; const menuVariants = { closed: { @@ -35,6 +36,25 @@ const menuVariants = { type DialogContent = { type: 'delete'; item: ChatHistoryItem } | null; +function CurrentDateTime() { + const [dateTime, setDateTime] = useState(new Date()); + + useEffect(() => { + const timer = setInterval(() => { + setDateTime(new Date()); + }, 60000); // Update every minute + + return () => clearInterval(timer); + }, []); + + return ( +
+ + {dateTime.toLocaleDateString()} {dateTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} +
+ ); +} + export const Menu = () => { const { duplicateCurrentChat, exportChat } = useChatHistory(); const menuRef = useRef(null); @@ -126,18 +146,17 @@ export const Menu = () => { variants={menuVariants} className="flex selection-accent flex-col side-menu fixed top-0 w-[350px] h-full bg-bolt-elements-background-depth-2 border-r rounded-r-3xl border-bolt-elements-borderColor z-sidebar shadow-xl shadow-bolt-elements-sidebar-dropdownShadow text-sm" > -
{/* Placeholder */}
+
{/* Spacer for top margin */} +
-
([]); @@ -39,6 +47,13 @@ export function useSettings() { isDebugMode.set(savedDebugMode === 'true'); } + // load event logs from cookies + const savedEventLogs = Cookies.get('isEventLogsEnabled'); + + if (savedEventLogs) { + isEventLogsEnabled.set(savedEventLogs === 'true'); + } + // load local models from cookies const savedLocalModels = Cookies.get('isLocalModelsEnabled'); @@ -80,11 +95,19 @@ export function useSettings() { const enableDebugMode = useCallback((enabled: boolean) => { isDebugMode.set(enabled); + logStore.logSystem(`Debug mode ${enabled ? 'enabled' : 'disabled'}`); Cookies.set('isDebugEnabled', String(enabled)); }, []); + const enableEventLogs = useCallback((enabled: boolean) => { + isEventLogsEnabled.set(enabled); + logStore.logSystem(`Event logs ${enabled ? 'enabled' : 'disabled'}`); + Cookies.set('isEventLogsEnabled', String(enabled)); + }, []); + const enableLocalModels = useCallback((enabled: boolean) => { isLocalModelsEnabled.set(enabled); + logStore.logSystem(`Local models ${enabled ? 'enabled' : 'disabled'}`); Cookies.set('isLocalModelsEnabled', String(enabled)); }, []); @@ -94,6 +117,8 @@ export function useSettings() { updateProviderSettings, debug, enableDebugMode, + eventLogs, + enableEventLogs, isLocalModel, enableLocalModels, }; diff --git a/app/lib/persistence/useChatHistory.ts b/app/lib/persistence/useChatHistory.ts index ca44b4b..0a8eeb5 100644 --- a/app/lib/persistence/useChatHistory.ts +++ b/app/lib/persistence/useChatHistory.ts @@ -4,6 +4,7 @@ import { atom } from 'nanostores'; import type { Message } from 'ai'; import { toast } from 'react-toastify'; import { workbenchStore } from '~/lib/stores/workbench'; +import { logStore } from '~/lib/stores/logs'; // Import logStore import { getMessages, getNextId, @@ -43,6 +44,8 @@ export function useChatHistory() { setReady(true); if (persistenceEnabled) { + const error = new Error('Chat persistence is unavailable'); + logStore.logError('Chat persistence initialization failed', error); toast.error('Chat persistence is unavailable'); } @@ -69,6 +72,7 @@ export function useChatHistory() { setReady(true); }) .catch((error) => { + logStore.logError('Failed to load chat messages', error); toast.error(error.message); }); } diff --git a/app/lib/stores/logs.ts b/app/lib/stores/logs.ts new file mode 100644 index 0000000..b2c62dc --- /dev/null +++ b/app/lib/stores/logs.ts @@ -0,0 +1,149 @@ +import { atom, map } from 'nanostores'; +import Cookies from 'js-cookie'; +import { createScopedLogger } from '~/utils/logger'; + +const logger = createScopedLogger('LogStore'); + +export interface LogEntry { + id: string; + timestamp: string; + level: 'info' | 'warning' | 'error' | 'debug'; + message: string; + details?: Record; + category: 'system' | 'provider' | 'user' | 'error'; +} + +const MAX_LOGS = 1000; // Maximum number of logs to keep in memory + +class LogStore { + private _logs = map>({}); + showLogs = atom(false); + + constructor() { + // Load saved logs from cookies on initialization + this._loadLogs(); + } + + private _loadLogs() { + const savedLogs = Cookies.get('eventLogs'); + + if (savedLogs) { + try { + const parsedLogs = JSON.parse(savedLogs); + this._logs.set(parsedLogs); + } catch (error) { + logger.error('Failed to parse logs from cookies:', error); + } + } + } + + private _saveLogs() { + const currentLogs = this._logs.get(); + Cookies.set('eventLogs', JSON.stringify(currentLogs)); + } + + private _generateId(): string { + return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; + } + + private _trimLogs() { + const currentLogs = Object.entries(this._logs.get()); + + if (currentLogs.length > MAX_LOGS) { + const sortedLogs = currentLogs.sort( + ([, a], [, b]) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(), + ); + const newLogs = Object.fromEntries(sortedLogs.slice(0, MAX_LOGS)); + this._logs.set(newLogs); + } + } + + addLog( + message: string, + level: LogEntry['level'] = 'info', + category: LogEntry['category'] = 'system', + details?: Record, + ) { + const id = this._generateId(); + const entry: LogEntry = { + id, + timestamp: new Date().toISOString(), + level, + message, + details, + category, + }; + + this._logs.setKey(id, entry); + this._trimLogs(); + this._saveLogs(); + + return id; + } + + // System events + logSystem(message: string, details?: Record) { + return this.addLog(message, 'info', 'system', details); + } + + // Provider events + logProvider(message: string, details?: Record) { + return this.addLog(message, 'info', 'provider', details); + } + + // User actions + logUserAction(message: string, details?: Record) { + return this.addLog(message, 'info', 'user', details); + } + + // Error events + logError(message: string, error?: Error | unknown, details?: Record) { + const errorDetails = { + ...(details || {}), + error: + error instanceof Error + ? { + message: error.message, + stack: error.stack, + } + : error, + }; + return this.addLog(message, 'error', 'error', errorDetails); + } + + // Warning events + logWarning(message: string, details?: Record) { + return this.addLog(message, 'warning', 'system', details); + } + + // Debug events + logDebug(message: string, details?: Record) { + return this.addLog(message, 'debug', 'system', details); + } + + clearLogs() { + this._logs.set({}); + this._saveLogs(); + } + + getLogs() { + return Object.values(this._logs.get()).sort( + (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(), + ); + } + + getFilteredLogs(level?: LogEntry['level'], category?: LogEntry['category'], searchQuery?: string) { + return this.getLogs().filter((log) => { + const matchesLevel = !level || level === 'debug' || log.level === level; + const matchesCategory = !category || log.category === category; + const matchesSearch = + !searchQuery || + log.message.toLowerCase().includes(searchQuery.toLowerCase()) || + JSON.stringify(log.details).toLowerCase().includes(searchQuery.toLowerCase()); + + return matchesLevel && matchesCategory && matchesSearch; + }); + } +} + +export const logStore = new LogStore(); diff --git a/app/lib/stores/settings.ts b/app/lib/stores/settings.ts index 31564e6..667f049 100644 --- a/app/lib/stores/settings.ts +++ b/app/lib/stores/settings.ts @@ -43,4 +43,6 @@ export const providersStore = map(initialProviderSettings); export const isDebugMode = atom(false); +export const isEventLogsEnabled = atom(false); + export const isLocalModelsEnabled = atom(true); diff --git a/app/lib/stores/theme.ts b/app/lib/stores/theme.ts index 4f3e47b..e70cb15 100644 --- a/app/lib/stores/theme.ts +++ b/app/lib/stores/theme.ts @@ -1,4 +1,5 @@ import { atom } from 'nanostores'; +import { logStore } from './logs'; export type Theme = 'dark' | 'light'; @@ -26,10 +27,8 @@ function initStore() { export function toggleTheme() { const currentTheme = themeStore.get(); const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; - themeStore.set(newTheme); - + logStore.logSystem(`Theme changed to ${newTheme} mode`); localStorage.setItem(kTheme, newTheme); - document.querySelector('html')?.setAttribute('data-theme', newTheme); } diff --git a/app/root.tsx b/app/root.tsx index 31eb387..23978f6 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -78,6 +78,23 @@ export function Layout({ children }: { children: React.ReactNode }) { ); } +import { logStore } from './lib/stores/logs'; + export default function App() { - return ; + const theme = useStore(themeStore); + + useEffect(() => { + logStore.logSystem('Application initialized', { + theme, + platform: navigator.platform, + userAgent: navigator.userAgent, + timestamp: new Date().toISOString(), + }); + }, []); + + return ( + + + + ); } diff --git a/app/utils/constants.ts b/app/utils/constants.ts index 240b4b9..61e709b 100644 --- a/app/utils/constants.ts +++ b/app/utils/constants.ts @@ -2,6 +2,7 @@ import Cookies from 'js-cookie'; import type { ModelInfo, OllamaApiResponse, OllamaModel } from './types'; import type { ProviderInfo, IProviderSetting } from '~/types/model'; import { createScopedLogger } from './logger'; +import { logStore } from '~/lib/stores/logs'; export const WORK_DIR_NAME = 'project'; export const WORK_DIR = `/home/${WORK_DIR_NAME}`; @@ -373,12 +374,6 @@ const getOllamaBaseUrl = (settings?: IProviderSetting) => { }; async function getOllamaModels(apiKeys?: Record, settings?: IProviderSetting): Promise { - /* - * if (typeof window === 'undefined') { - * return []; - * } - */ - try { const baseUrl = getOllamaBaseUrl(settings); const response = await fetch(`${baseUrl}/api/tags`); @@ -391,7 +386,9 @@ async function getOllamaModels(apiKeys?: Record, settings?: IPro maxTokenAllowed: 8000, })); } catch (e: any) { + logStore.logError('Failed to get Ollama models', e, { baseUrl: settings?.baseUrl }); logger.warn('Failed to get Ollama models: ', e.message || ''); + return []; } } @@ -480,7 +477,9 @@ async function getLMStudioModels(_apiKeys?: Record, settings?: I provider: 'LMStudio', })); } catch (e: any) { + logStore.logError('Failed to get LMStudio models', e, { baseUrl: settings?.baseUrl }); logger.warn('Failed to get LMStudio models: ', e.message || ''); + return []; } } @@ -499,6 +498,7 @@ async function initializeModelList(providerSettings?: Record Date: Fri, 13 Dec 2024 01:40:58 +0100 Subject: [PATCH 03/14] Event logs bug fix minor improvements download logs, auto scroll, clear logs --- .../settings/event-logs/EventLogsTab.tsx | 110 ++++++++++++------ .../settings/providers/ProvidersTab.tsx | 6 +- app/lib/stores/logs.ts | 2 +- 3 files changed, 78 insertions(+), 40 deletions(-) diff --git a/app/components/settings/event-logs/EventLogsTab.tsx b/app/components/settings/event-logs/EventLogsTab.tsx index d8766c7..be2a2d2 100644 --- a/app/components/settings/event-logs/EventLogsTab.tsx +++ b/app/components/settings/event-logs/EventLogsTab.tsx @@ -3,14 +3,34 @@ import { useSettings } from '~/lib/hooks/useSettings'; import { toast } from 'react-toastify'; import { Switch } from '~/components/ui/Switch'; import { logStore, type LogEntry } from '~/lib/stores/logs'; +import { useStore } from '@nanostores/react'; export default function EventLogsTab() { const {} = useSettings(); + const showLogs = useStore(logStore.showLogs); const [logLevel, setLogLevel] = useState('info'); const [autoScroll, setAutoScroll] = useState(true); const [searchQuery, setSearchQuery] = useState(''); const [, forceUpdate] = useState({}); + const filteredLogs = useMemo(() => { + const logs = logStore.getLogs(); + return logs.filter((log) => { + const matchesLevel = !logLevel || log.level === logLevel; + const matchesSearch = + !searchQuery || + log.message.toLowerCase().includes(searchQuery.toLowerCase()) || + JSON.stringify(log.details).toLowerCase().includes(searchQuery.toLowerCase()); + + return matchesLevel && matchesSearch; + }); + }, [logLevel, searchQuery]); + + // Effect to initialize showLogs + useEffect(() => { + logStore.showLogs.set(true); + }, []); + useEffect(() => { // Add some initial logs for testing logStore.logSystem('System started', { version: '1.0.0' }); @@ -18,6 +38,14 @@ export default function EventLogsTab() { logStore.logError('Failed to connect to provider', new Error('Connection timeout'), { provider: 'OpenAI' }); }, []); + useEffect(() => { + const container = document.querySelector('.logs-container'); + + if (container && autoScroll) { + container.scrollTop = container.scrollHeight; + } + }, [filteredLogs, autoScroll]); + const handleClearLogs = useCallback(() => { if (confirm('Are you sure you want to clear all logs?')) { logStore.clearLogs(); @@ -54,10 +82,6 @@ export default function EventLogsTab() { } }, []); - const filteredLogs = useMemo(() => { - return logStore.getFilteredLogs(logLevel, undefined, searchQuery); - }, [logLevel, searchQuery]); - const getLevelColor = (level: LogEntry['level']) => { switch (level) { case 'info': @@ -76,15 +100,23 @@ export default function EventLogsTab() { return (
-
+ {/* Title and Toggles Row */} +

Event Logs

-
- Auto-scroll - +
+
+ Show Actions + logStore.showLogs.set(checked)} /> +
+
+ Auto-scroll + +
-
+ {/* Controls Row */} +
- setSearchQuery(e.target.value)} - className="bg-bolt-elements-bg-depth-2 text-bolt-elements-textPrimary rounded-lg px-3 py-1.5 text-sm flex-1" - /> - - +
+ setSearchQuery(e.target.value)} + className="w-full bg-bolt-elements-bg-depth-2 text-bolt-elements-textPrimary rounded-lg px-3 py-1.5 text-sm" + /> +
+ {showLogs && ( +
+ + +
+ )}
-
+
{filteredLogs.length === 0 ? (
No logs found
) : ( @@ -126,13 +164,17 @@ export default function EventLogsTab() { key={index} className="text-sm mb-3 font-mono border-b border-bolt-elements-borderColor pb-2 last:border-0" > -
- [{log.level.toUpperCase()}] - {new Date(log.timestamp).toLocaleString()} - {log.message} +
+ + [{log.level.toUpperCase()}] + + + {new Date(log.timestamp).toLocaleString()} + + {log.message}
{log.details && ( -
+                
                   {JSON.stringify(log.details, null, 2)}
                 
)} diff --git a/app/components/settings/providers/ProvidersTab.tsx b/app/components/settings/providers/ProvidersTab.tsx index edcf6f9..0efbc66 100644 --- a/app/components/settings/providers/ProvidersTab.tsx +++ b/app/components/settings/providers/ProvidersTab.tsx @@ -51,11 +51,7 @@ export default function ProvidersTab() { >
- {`${provider.name} + {`${provider.name} {provider.name}
>({}); - showLogs = atom(false); + showLogs = atom(true); constructor() { // Load saved logs from cookies on initialization From b16aab1f167d91903592e33357ae38e304ca7f65 Mon Sep 17 00:00:00 2001 From: Stijnus <72551117+Stijnus@users.noreply.github.com> Date: Fri, 13 Dec 2024 09:17:35 +0100 Subject: [PATCH 04/14] Update debug tab to check against fork --- app/components/settings/debug/DebugTab.tsx | 511 ++++++++++++++++-- .../settings/event-logs/EventLogsTab.tsx | 28 +- app/types/global.d.ts | 8 + 3 files changed, 500 insertions(+), 47 deletions(-) diff --git a/app/components/settings/debug/DebugTab.tsx b/app/components/settings/debug/DebugTab.tsx index 7a84ec1..f37d1d4 100644 --- a/app/components/settings/debug/DebugTab.tsx +++ b/app/components/settings/debug/DebugTab.tsx @@ -2,68 +2,493 @@ import React, { useCallback, useEffect, useState } from 'react'; import { useSettings } from '~/lib/hooks/useSettings'; import commit from '~/commit.json'; -const versionHash = commit.commit; // Get the version hash from commit.json +interface ProviderStatus { + name: string; + enabled: boolean; + isLocal: boolean; + isRunning: boolean | null; + error?: string; + lastChecked: Date; + responseTime?: number; + url: string | null; +} + +interface SystemInfo { + os: string; + browser: string; + screen: string; + language: string; + timezone: string; + memory: string; + cores: number; +} + +interface IProviderConfig { + name: string; + settings: { + enabled: boolean; + }; +} + +const LOCAL_PROVIDERS = ['Ollama', 'LMStudio', 'OpenAILike']; +const versionHash = commit.commit; +const GITHUB_URLS = { + original: 'https://api.github.com/repos/Stijnus/bolt.new-any-llm/commits/main', + fork: 'https://api.github.com/repos/Stijnus/bolt.new-any-llm/commits/main', +}; + +function getSystemInfo(): SystemInfo { + const formatBytes = (bytes: number): string => { + if (bytes === 0) { + return '0 Bytes'; + } + + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; + }; + + return { + os: navigator.platform, + browser: navigator.userAgent.split(' ').slice(-1)[0], + screen: `${window.screen.width}x${window.screen.height}`, + language: navigator.language, + timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, + memory: formatBytes(performance?.memory?.jsHeapSizeLimit || 0), + cores: navigator.hardwareConcurrency || 0, + }; +} + +const checkProviderStatus = async (url: string | null, providerName: string): Promise => { + if (!url) { + console.log(`[Debug] No URL provided for ${providerName}`); + return { + name: providerName, + enabled: false, + isLocal: true, + isRunning: false, + error: 'No URL configured', + lastChecked: new Date(), + url: null, + }; + } + + console.log(`[Debug] Checking status for ${providerName} at ${url}`); + + const startTime = performance.now(); + + try { + if (providerName.toLowerCase() === 'ollama') { + // Special check for Ollama root endpoint + try { + console.log(`[Debug] Checking Ollama root endpoint: ${url}`); + + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout + + const response = await fetch(url, { + signal: controller.signal, + headers: { + Accept: 'text/plain,application/json', + }, + }); + clearTimeout(timeoutId); + + const text = await response.text(); + console.log(`[Debug] Ollama root response:`, text); + + if (text.includes('Ollama is running')) { + console.log(`[Debug] Ollama running confirmed via root endpoint`); + return { + name: providerName, + enabled: false, + isLocal: true, + isRunning: true, + lastChecked: new Date(), + responseTime: performance.now() - startTime, + url, + }; + } + } catch (error) { + console.log(`[Debug] Ollama root check failed:`, error); + + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + + if (errorMessage.includes('aborted')) { + return { + name: providerName, + enabled: false, + isLocal: true, + isRunning: false, + error: 'Connection timeout', + lastChecked: new Date(), + responseTime: performance.now() - startTime, + url, + }; + } + } + } + + // Try different endpoints based on provider + const checkUrls = [`${url}/api/health`, `${url}/v1/models`]; + console.log(`[Debug] Checking additional endpoints:`, checkUrls); + + const results = await Promise.all( + checkUrls.map(async (checkUrl) => { + try { + console.log(`[Debug] Trying endpoint: ${checkUrl}`); + + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 5000); + + const response = await fetch(checkUrl, { + signal: controller.signal, + headers: { + Accept: 'application/json', + }, + }); + clearTimeout(timeoutId); + + const ok = response.ok; + console.log(`[Debug] Endpoint ${checkUrl} response:`, ok); + + if (ok) { + try { + const data = await response.json(); + console.log(`[Debug] Endpoint ${checkUrl} data:`, data); + } catch { + console.log(`[Debug] Could not parse JSON from ${checkUrl}`); + } + } + + return ok; + } catch (error) { + console.log(`[Debug] Endpoint ${checkUrl} failed:`, error); + return false; + } + }), + ); + + const isRunning = results.some((result) => result); + console.log(`[Debug] Final status for ${providerName}:`, isRunning); + + return { + name: providerName, + enabled: false, + isLocal: true, + isRunning, + lastChecked: new Date(), + responseTime: performance.now() - startTime, + url, + }; + } catch (error) { + console.log(`[Debug] Provider check failed for ${providerName}:`, error); + return { + name: providerName, + enabled: false, + isLocal: true, + isRunning: false, + error: error instanceof Error ? error.message : 'Unknown error', + lastChecked: new Date(), + responseTime: performance.now() - startTime, + url, + }; + } +}; export default function DebugTab() { const { providers } = useSettings(); - const [activeProviders, setActiveProviders] = useState([]); + const [activeProviders, setActiveProviders] = useState([]); + const [updateMessage, setUpdateMessage] = useState(''); + const [systemInfo] = useState(getSystemInfo()); + const [isCheckingUpdate, setIsCheckingUpdate] = useState(false); + + const updateProviderStatuses = async () => { + if (!providers) { + return; + } + + try { + const entries = Object.entries(providers) as [string, IProviderConfig][]; + const statuses = entries + .filter(([, provider]) => LOCAL_PROVIDERS.includes(provider.name)) + .map(async ([, provider]) => { + const envVarName = + provider.name.toLowerCase() === 'ollama' + ? 'OLLAMA_API_BASE_URL' + : provider.name.toLowerCase() === 'lmstudio' + ? 'LMSTUDIO_API_BASE_URL' + : `REACT_APP_${provider.name.toUpperCase()}_URL`; + + // Access environment variables through import.meta.env + const url = import.meta.env[envVarName] || null; + console.log(`[Debug] Using URL for ${provider.name}:`, url, `(from ${envVarName})`); + + const status = await checkProviderStatus(url, provider.name); + + return { + ...status, + enabled: provider.settings.enabled ?? false, + }; + }); + + Promise.all(statuses).then(setActiveProviders); + } catch (error) { + console.error('[Debug] Failed to update provider statuses:', error); + } + }; + useEffect(() => { - setActiveProviders( - Object.entries(providers) - .filter(([_key, provider]) => provider.settings.enabled) - .map(([_key, provider]) => provider.name), - ); + updateProviderStatuses(); + + const interval = setInterval(updateProviderStatuses, 30000); + + return () => clearInterval(interval); }, [providers]); + const handleCheckForUpdate = useCallback(async () => { + if (isCheckingUpdate) { + return; + } + + try { + setIsCheckingUpdate(true); + setUpdateMessage('Checking for updates...'); + + const [originalResponse, forkResponse] = await Promise.all([ + fetch(GITHUB_URLS.original), + fetch(GITHUB_URLS.fork), + ]); + + if (!originalResponse.ok || !forkResponse.ok) { + throw new Error('Failed to fetch repository information'); + } + + const [originalData, forkData] = await Promise.all([ + originalResponse.json() as Promise<{ sha: string }>, + forkResponse.json() as Promise<{ sha: string }>, + ]); + + const originalCommitHash = originalData.sha; + const forkCommitHash = forkData.sha; + const isForked = versionHash === forkCommitHash && forkCommitHash !== originalCommitHash; + + if (originalCommitHash !== versionHash) { + setUpdateMessage( + `Update available from original repository!\n` + + `Current: ${versionHash.slice(0, 7)}${isForked ? ' (forked)' : ''}\n` + + `Latest: ${originalCommitHash.slice(0, 7)}`, + ); + } else { + setUpdateMessage('You are on the latest version from the original repository'); + } + } catch (error) { + setUpdateMessage('Failed to check for updates'); + console.error('[Debug] Failed to check for updates:', error); + } finally { + setIsCheckingUpdate(false); + } + }, [isCheckingUpdate]); + const handleCopyToClipboard = useCallback(() => { const debugInfo = { - OS: navigator.platform, - Browser: navigator.userAgent, - ActiveFeatures: activeProviders, - BaseURLs: { - Ollama: process.env.REACT_APP_OLLAMA_URL, - OpenAI: process.env.REACT_APP_OPENAI_URL, - LMStudio: process.env.REACT_APP_LM_STUDIO_URL, - }, + System: systemInfo, + Providers: activeProviders.map((provider) => ({ + name: provider.name, + enabled: provider.enabled, + isLocal: provider.isLocal, + running: provider.isRunning, + error: provider.error, + lastChecked: provider.lastChecked, + responseTime: provider.responseTime, + url: provider.url, + })), Version: versionHash, + Timestamp: new Date().toISOString(), }; navigator.clipboard.writeText(JSON.stringify(debugInfo, null, 2)).then(() => { alert('Debug information copied to clipboard!'); }); - }, [providers]); + }, [activeProviders, systemInfo]); return ( -
-

Debug Tab

- +
+
+

Debug Information

+
+ + +
+
-

System Information

-

OS: {navigator.platform}

-

Browser: {navigator.userAgent}

+ {updateMessage && ( +
+

{updateMessage}

+ {updateMessage.includes('Update available') && ( +
+

To update:

+
    +
  1. + Pull the latest changes:{' '} + git pull upstream main +
  2. +
  3. + Install any new dependencies:{' '} + npm install +
  4. +
  5. Restart the application
  6. +
+
+ )} +
+ )} -

Active Features

-
    - {activeProviders.map((name) => ( -
  • - {name} -
  • - ))} -
+
+
+

System Information

+
+
+
+

Operating System

+

{systemInfo.os}

+
+
+

Browser

+

{systemInfo.browser}

+
+
+

Screen Resolution

+

{systemInfo.screen}

+
+
+

Language

+

{systemInfo.language}

+
+
+

Timezone

+

{systemInfo.timezone}

+
+
+

CPU Cores

+

{systemInfo.cores}

+
+
+
+

Version

+

+ {versionHash.slice(0, 7)} + + ({new Date().toLocaleDateString()}) + +

+
+
+
-

Base URLs

-
    -
  • Ollama: {process.env.REACT_APP_OLLAMA_URL}
  • -
  • OpenAI: {process.env.REACT_APP_OPENAI_URL}
  • -
  • LM Studio: {process.env.REACT_APP_LM_STUDIO_URL}
  • -
+
+

Local LLM Status

+
+
+ {activeProviders.map((provider) => ( +
+
+
+
+
+
+
+

{provider.name}

+ {provider.url && ( +

+ {provider.url} +

+ )} +
+
+
+ + {provider.enabled ? 'Enabled' : 'Disabled'} + + {provider.enabled && ( + + {provider.isRunning ? 'Running' : 'Not Running'} + + )} +
+
-

Version Information

-

Version Hash: {versionHash}

+
+ {/* Status Details */} +
+ + Last checked: {new Date(provider.lastChecked).toLocaleTimeString()} + + {provider.responseTime && ( + + Response time: {Math.round(provider.responseTime)}ms + + )} +
+ + {/* Error Message */} + {provider.error && ( +
+ Error: {provider.error} +
+ )} + + {/* Connection Info */} + {provider.url && ( +
+ Endpoints checked: +
    +
  • {provider.url} (root)
  • +
  • {provider.url}/api/health
  • +
  • {provider.url}/v1/models
  • +
+
+ )} +
+
+ ))} + {activeProviders.length === 0 && ( +
No local LLMs configured
+ )} +
+
+
+
); } diff --git a/app/components/settings/event-logs/EventLogsTab.tsx b/app/components/settings/event-logs/EventLogsTab.tsx index be2a2d2..55c3d66 100644 --- a/app/components/settings/event-logs/EventLogsTab.tsx +++ b/app/components/settings/event-logs/EventLogsTab.tsx @@ -32,10 +32,30 @@ export default function EventLogsTab() { }, []); useEffect(() => { - // Add some initial logs for testing - logStore.logSystem('System started', { version: '1.0.0' }); - logStore.logWarning('High memory usage detected', { memoryUsage: '85%' }); - logStore.logError('Failed to connect to provider', new Error('Connection timeout'), { provider: 'OpenAI' }); + // System info logs + logStore.logSystem('Application initialized', { + version: process.env.NEXT_PUBLIC_APP_VERSION, + environment: process.env.NODE_ENV, + }); + + // Debug logs for system state + logStore.logDebug('System configuration loaded', { + runtime: 'Next.js', + features: ['AI Chat', 'Event Logging'], + }); + + // Warning logs for potential issues + logStore.logWarning('Resource usage threshold approaching', { + memoryUsage: '75%', + cpuLoad: '60%', + }); + + // Error logs with detailed context + logStore.logError('API connection failed', new Error('Connection timeout'), { + endpoint: '/api/chat', + retryCount: 3, + lastAttempt: new Date().toISOString(), + }); }, []); useEffect(() => { diff --git a/app/types/global.d.ts b/app/types/global.d.ts index 6a03036..e0ad664 100644 --- a/app/types/global.d.ts +++ b/app/types/global.d.ts @@ -3,3 +3,11 @@ interface Window { webkitSpeechRecognition: typeof SpeechRecognition; SpeechRecognition: typeof SpeechRecognition; } + +interface Performance { + memory?: { + jsHeapSizeLimit: number; + totalJSHeapSize: number; + usedJSHeapSize: number; + }; +} From b1a3e3993ee013b58cee6f02f2e05429d3b3a592 Mon Sep 17 00:00:00 2001 From: Stijnus <72551117+Stijnus@users.noreply.github.com> Date: Fri, 13 Dec 2024 09:22:21 +0100 Subject: [PATCH 05/14] debug fixes --- .husky/pre-commit | 5 +++++ app/commit.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 1aab67d..45ed10d 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -23,4 +23,9 @@ if ! pnpm lint; then exit 1 fi +# Update commit.json with the latest commit hash +COMMIT_HASH=$(git rev-parse HEAD) +echo "{ \"commit\": \"$COMMIT_HASH\" }" > app/commit.json +git add app/commit.json + echo "👍 All good! Committing changes..." diff --git a/app/commit.json b/app/commit.json index 64e78b6..45cf76f 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "4b36601061652ec2ec3cb1f1d5c7cc5649690bbb" } +{ "commit": "b16aab1f167d91903592e33357ae38e304ca7f65" } From c0c12752267586ba61fdb423a9644a2dbb8ba04c Mon Sep 17 00:00:00 2001 From: Stijnus Date: Fri, 13 Dec 2024 11:33:29 +0100 Subject: [PATCH 06/14] minor bug fixes --- .husky/pre-commit | 36 +- app/commit.json | 2 +- app/components/settings/debug/DebugTab.tsx | 6 +- package-lock.json | 26039 +++++++++++++++++++ package.json | 13 +- pnpm-lock.yaml | 681 +- 6 files changed, 26419 insertions(+), 358 deletions(-) create mode 100644 package-lock.json diff --git a/.husky/pre-commit b/.husky/pre-commit index 45ed10d..b95e00d 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -2,30 +2,42 @@ echo "🔍 Running pre-commit hook to check the code looks good... 🔍" +# Load NVM if available (useful for managing Node.js versions) export NVM_DIR="$HOME/.nvm" -[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # Load nvm if you're using i +[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" -echo "Running typecheck..." -which pnpm - -if ! pnpm typecheck; then - echo "❌ Type checking failed! Please review TypeScript types." - echo "Once you're done, don't forget to add your changes to the commit! 🚀" - echo "Typecheck exit code: $?" - exit 1 +# Ensure `pnpm` is available +echo "Checking if pnpm is available..." +if ! command -v pnpm >/dev/null 2>&1; then + echo "❌ pnpm not found! Please ensure pnpm is installed and available in PATH." + exit 1 fi +# Run typecheck +echo "Running typecheck..." +if ! pnpm typecheck; then + echo "❌ Type checking failed! Please review TypeScript types." + echo "Once you're done, don't forget to add your changes to the commit! 🚀" + exit 1 +fi + +# Run lint echo "Running lint..." if ! pnpm lint; then - echo "❌ Linting failed! 'pnpm lint:fix' will help you fix the easy ones." + echo "❌ Linting failed! Run 'pnpm lint:fix' to fix the easy issues." echo "Once you're done, don't forget to add your beautification to the commit! 🤩" - echo "lint exit code: $?" exit 1 fi # Update commit.json with the latest commit hash +echo "Updating commit.json with the latest commit hash..." COMMIT_HASH=$(git rev-parse HEAD) +if [ $? -ne 0 ]; then + echo "❌ Failed to get commit hash. Ensure you are in a git repository." + exit 1 +fi + echo "{ \"commit\": \"$COMMIT_HASH\" }" > app/commit.json git add app/commit.json -echo "👍 All good! Committing changes..." +echo "👍 All checks passed! Committing changes..." diff --git a/app/commit.json b/app/commit.json index 45cf76f..696f6c0 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "b16aab1f167d91903592e33357ae38e304ca7f65" } +{ "commit": "b1a3e3993ee013b58cee6f02f2e05429d3b3a592" } diff --git a/app/components/settings/debug/DebugTab.tsx b/app/components/settings/debug/DebugTab.tsx index f37d1d4..e18607d 100644 --- a/app/components/settings/debug/DebugTab.tsx +++ b/app/components/settings/debug/DebugTab.tsx @@ -33,7 +33,7 @@ interface IProviderConfig { const LOCAL_PROVIDERS = ['Ollama', 'LMStudio', 'OpenAILike']; const versionHash = commit.commit; const GITHUB_URLS = { - original: 'https://api.github.com/repos/Stijnus/bolt.new-any-llm/commits/main', + original: 'https://api.github.com/repos/stackblitz-labs/bolt.diy/commits/main', fork: 'https://api.github.com/repos/Stijnus/bolt.new-any-llm/commits/main', }; @@ -327,7 +327,7 @@ export default function DebugTab() {