mirror of
https://github.com/stackblitz-labs/bolt.diy.git
synced 2025-06-18 01:49:07 +01:00
# 🚀 Release v1.0.0 ## What's Changed 🌟 ### 🎨 UI/UX Improvements - **Dark Mode Support** - Implemented comprehensive dark theme across all components - Enhanced contrast and readability in dark mode - Added smooth theme transitions - Optimized dialog overlays and backdrops ### 🛠️ Settings Panel - **Data Management** - Added chat history export/import functionality - Implemented settings backup and restore - Added secure data deletion with confirmations - Added profile customization options - **Provider Management** - Added comprehensive provider configuration - Implemented URL-configurable providers - Added local model support (Ollama, LMStudio) - Added provider health checks - Added provider status indicators - **Ollama Integration** - Added Ollama Model Manager with real-time updates - Implemented model version tracking - Added bulk update capability - Added progress tracking for model updates - Displays model details (parameter size, quantization) - **GitHub Integration** - Added GitHub connection management - Implemented secure token storage - Added connection state persistence - Real-time connection status updates - Proper error handling and user feedback ### 📊 Event Logging - **System Monitoring** - Added real-time event logging system - Implemented log filtering by type (info, warning, error, debug) - Added log export functionality - Added auto-scroll and search capabilities - Enhanced log visualization with color coding ### 💫 Animations & Interactions - Added smooth page transitions - Implemented loading states with spinners - Added micro-interactions for better feedback - Enhanced button hover and active states - Added motion effects for UI elements ### 🔐 Security Features - Secure token storage - Added confirmation dialogs for destructive actions - Implemented data validation - Added file size and type validation - Secure connection management ### ♿️ Accessibility - Improved keyboard navigation - Enhanced screen reader support - Added ARIA labels and descriptions - Implemented focus management - Added proper dialog accessibility ### 🎯 Developer Experience - Added comprehensive debug information - Implemented system status monitoring - Added version control integration - Enhanced error handling and reporting - Added detailed logging system --- ## 🔧 Technical Details - **Frontend Stack** - React 18 with TypeScript - Framer Motion for animations - TailwindCSS for styling - Radix UI for accessible components - **State Management** - Local storage for persistence - React hooks for state - Custom stores for global state - **API Integration** - GitHub API integration - Ollama API integration - Provider API management - Error boundary implementation ## 📝 Notes - Initial release focusing on core functionality and user experience - Enhanced dark mode support across all components - Improved accessibility and keyboard navigation - Added comprehensive logging and debugging tools - Implemented robust error handling and user feedback
155 lines
4.0 KiB
TypeScript
155 lines
4.0 KiB
TypeScript
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<string, any>;
|
|
category: 'system' | 'provider' | 'user' | 'error';
|
|
}
|
|
|
|
const MAX_LOGS = 1000; // Maximum number of logs to keep in memory
|
|
|
|
class LogStore {
|
|
private _logs = map<Record<string, LogEntry>>({});
|
|
showLogs = atom(true);
|
|
|
|
constructor() {
|
|
// Load saved logs from cookies on initialization
|
|
this._loadLogs();
|
|
}
|
|
|
|
// Expose the logs store for subscription
|
|
get logs() {
|
|
return this._logs;
|
|
}
|
|
|
|
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<string, any>,
|
|
) {
|
|
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<string, any>) {
|
|
return this.addLog(message, 'info', 'system', details);
|
|
}
|
|
|
|
// Provider events
|
|
logProvider(message: string, details?: Record<string, any>) {
|
|
return this.addLog(message, 'info', 'provider', details);
|
|
}
|
|
|
|
// User actions
|
|
logUserAction(message: string, details?: Record<string, any>) {
|
|
return this.addLog(message, 'info', 'user', details);
|
|
}
|
|
|
|
// Error events
|
|
logError(message: string, error?: Error | unknown, details?: Record<string, any>) {
|
|
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<string, any>) {
|
|
return this.addLog(message, 'warning', 'system', details);
|
|
}
|
|
|
|
// Debug events
|
|
logDebug(message: string, details?: Record<string, any>) {
|
|
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();
|