steinad's picture
Initial commit
adca48b
/**
* PIPS - Main Application Bootstrap
* Initializes the modular PIPS application
*/
// Import core modules
import { Logger } from './core/logger.js';
import { appState } from './core/state.js';
import { socketManager } from './network/socket.js';
import { storageManager } from './core/storage.js';
// Import UI modules
import { domManager } from './ui/dom-manager.js';
import { messageManager } from './ui/message-manager.js';
import { settingsManager } from './ui/settings-manager.js';
import { sessionManager } from './ui/session-manager.js';
import { imageHandler } from './ui/image-handler.js';
// Import handlers
import { socketEventHandlers } from './handlers/socket-handlers.js';
// Global error handlers
window.addEventListener('error', (event) => {
Logger.error('Global JavaScript error:', event.error);
Logger.error('Error message:', event.message);
Logger.error('Error filename:', event.filename);
Logger.error('Error line:', event.lineno);
Logger.error('Error column:', event.colno);
});
window.addEventListener('unhandledrejection', (event) => {
Logger.error('Unhandled promise rejection:', event.reason);
});
/**
* Main Application class - Coordinates all modules
*/
class PIPSApplication {
constructor() {
this.isInitialized = false;
}
async initialize() {
if (this.isInitialized) {
Logger.warn('App', 'Application already initialized');
return;
}
try {
Logger.log('App', 'Initializing PIPS application...');
// Initialize managers
domManager.setupDOMReferences();
await domManager.initializeIcons();
// Set up event handlers
socketManager.initialize();
sessionManager.initialize();
settingsManager.initialize();
imageHandler.initialize();
// Load user settings from storage
settingsManager.loadUserSettingsFromStorage();
// Perform first-run seeding of default sessions
await this.performFirstRunSeeding();
// Set up additional image features
imageHandler.setupPasteHandler();
// Set up core functionality event listeners
this.setupCoreEventListeners();
// Register *all* socket & connection handlers BEFORE connecting
this.setupSocketHandlers();
// Connect socket *after* handlers are registered
await socketManager.initialize();
// Set initial status
domManager.updateStatus('Connecting to PIPS server...', 'info');
this.isInitialized = true;
Logger.log('App', 'PIPS application initialized successfully');
} catch (error) {
Logger.error('App', 'Error during initialization:', error);
domManager.updateStatus('Failed to initialize application', 'error');
}
}
async performFirstRunSeeding() {
try {
// Check if this is the first run
if (!localStorage.getItem('pips_first_run_completed')) {
Logger.debug('App', 'First run detected, seeding default sessions...');
try {
const result = await storageManager.importSessionsFromUrl('/static/default_sessions/builtin_sessions.json');
Logger.log('App', `Seeded ${result.imported} default sessions successfully`);
// Track which sessions are defaults by storing their IDs
if (result.imported > 0) {
const sessions = storageManager.loadSessions();
const defaultSessionIds = Object.keys(sessions);
storageManager.saveDefaultSessionIds(defaultSessionIds);
Logger.debug('App', `Tracked ${defaultSessionIds.length} default session IDs`);
}
// Mark first run as completed
localStorage.setItem('pips_first_run_completed', 'yes');
localStorage.setItem('pips_default_sessions_loaded', new Date().toISOString());
} catch (error) {
Logger.warn('App', 'Could not load default sessions (this is normal in development):', error.message);
// Still mark as completed to avoid repeated attempts
localStorage.setItem('pips_first_run_completed', 'yes');
}
} else {
Logger.debug('App', 'Not first run, skipping default session seeding');
}
} catch (error) {
Logger.error('App', 'Error during first-run seeding:', error);
}
}
setupCoreEventListeners() {
// Core problem solving functionality
domManager.getElement('solveBtn')?.addEventListener('click', () => this.solveProblem());
domManager.getElement('interruptBtn')?.addEventListener('click', () => this.interruptSolving());
domManager.getElement('downloadBtn')?.addEventListener('click', () => messageManager.downloadChat());
Logger.debug('App', 'Core event listeners set up');
// Set up emergency cleanup handler for page unload
window.addEventListener('beforeunload', () => {
Logger.debug('App', 'Page unloading - performing emergency cleanup');
sessionManager.emergencyCleanupAndSave();
});
}
setupSocketHandlers() {
console.log('[DEBUG] Setting up socket handlers...');
// Register all socket event handlers (these are real Socket.IO events)
const eventHandlers = socketEventHandlers.getEventHandlers();
console.log('[DEBUG] Event handlers to register:', Object.keys(eventHandlers));
socketManager.registerEventHandlers(eventHandlers);
// Register connection handlers (these are internal socketManager events)
const connectionHandlers = socketEventHandlers.getConnectionHandlers();
console.log('[DEBUG] Connection handlers to register:', Object.keys(connectionHandlers));
Object.entries(connectionHandlers).forEach(([event, handler]) => {
socketManager.on(event, handler);
});
Logger.debug('App', 'Socket event handlers set up successfully');
}
// Core functionality methods
solveProblem() {
const questionInput = domManager.getElement('questionInput');
const text = questionInput?.value.trim();
if (!text) {
domManager.updateStatus('Please enter a problem description', 'warning');
return;
}
// Check if the current session is used and should be read-only
if (appState.currentSessionData && sessionManager.isSessionUsed(appState.currentSessionData)) {
domManager.updateStatus('This session has been used. Please start a new session to solve another problem.', 'warning');
// Automatically start a new session
sessionManager.startNewSession();
return;
}
// Get image data if available
const imageData = imageHandler.getImageForSubmission();
// Handle session creation/management through session manager
const sessionId = sessionManager.handleSolveProblem(text, imageData);
// Send current settings to server first to ensure PIPS mode is included
settingsManager.sendCurrentSettingsToServer();
// Send problem to server
socketManager.send('solve_problem', {
text: text,
image: imageData,
session_id: sessionId
});
Logger.debug('App', 'Problem submitted for solving');
}
interruptSolving() {
Logger.debug('App', 'Interrupt button clicked');
socketManager.send('interrupt_solving');
domManager.updateStatus('Interrupting current task...', 'warning');
}
// Global method for message expansion (called from HTML)
toggleExpandMessage(button) {
messageManager.toggleExpandMessage(button);
}
// Global methods for session management (called from HTML)
get sessionManager() {
return sessionManager;
}
// Expose modules for debugging and external access
getModules() {
return {
domManager,
messageManager,
settingsManager,
sessionManager,
imageHandler,
socketEventHandlers,
appState,
socketManager,
storageManager
};
}
}
// Initialize application when DOM is ready
document.addEventListener('DOMContentLoaded', async () => {
Logger.log('DOM content loaded');
try {
const app = new PIPSApplication();
await app.initialize();
// Store app instance globally for debugging and HTML callbacks
window.pipsApp = app;
// Also expose key functions globally for HTML access
window.toggleExpandMessage = (button) => app.toggleExpandMessage(button);
} catch (error) {
Logger.error('Failed to initialize PIPS application:', error);
}
});