docs: consolidate documentation into organized structure

- Move 12 markdown files from root to docs/ subdirectories
- Organize documentation by category:
  • docs/troubleshooting/ (1 file)  - Technical troubleshooting guides
  • docs/deployment/      (4 files) - Deployment and security documentation
  • docs/guides/          (3 files) - Feature-specific guides
  • docs/planning/        (4 files) - Planning and improvement proposals

Root directory cleanup:
- Reduced from 16 to 4 markdown files in root
- Only essential project files remain:
  • CLAUDE.md (AI instructions)
  • README.md (Main project readme)
  • CLEANUP_PLAN.md (Current cleanup plan)
  • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements)

This improves:
 Documentation discoverability
 Logical organization by purpose
 Clean root directory
 Better maintainability
This commit is contained in:
2025-10-05 11:05:04 +02:00
parent 887847dde6
commit 5050c7d73a
36686 changed files with 196456 additions and 12398919 deletions

View File

@@ -0,0 +1,291 @@
/**
* Hot Reload Client for Development
* Connects to SSE stream and handles reload events
*/
export class HotReload {
constructor(options = {}) {
this.url = options.url || '/dev/hot-reload';
this.reconnectDelay = options.reconnectDelay || 5000;
this.maxReconnectAttempts = options.maxReconnectAttempts || 10;
this.debug = options.debug || false;
this.eventSource = null;
this.reconnectAttempts = 0;
this.reconnectTimer = null;
// Only enable in development
if (this.isDevelopment()) {
this.connect();
this.setupVisibilityHandlers();
}
}
/**
* Check if we're in development mode
*/
isDevelopment() {
return window.location.hostname === 'localhost'
|| window.location.hostname === '127.0.0.1'
|| window.location.hostname.includes('.local');
}
/**
* Connect to SSE stream
*/
connect() {
if (this.eventSource) {
return;
}
this.log('Connecting to Hot Reload server...');
this.eventSource = new EventSource(this.url);
// Connection opened
this.eventSource.addEventListener('open', () => {
this.log('Hot Reload connected');
this.reconnectAttempts = 0;
this.showNotification('Hot Reload Connected', 'success');
});
// Handle reload events
this.eventSource.addEventListener('reload', (event) => {
const data = JSON.parse(event.data);
this.handleReload(data);
});
// Handle heartbeat
this.eventSource.addEventListener('heartbeat', () => {
this.log('Heartbeat received');
});
// Handle close
this.eventSource.addEventListener('close', () => {
this.log('Server requested close');
this.disconnect();
});
// Handle errors
this.eventSource.addEventListener('error', (error) => {
this.log('Connection error', error);
this.disconnect();
this.scheduleReconnect();
});
}
/**
* Disconnect from SSE stream
*/
disconnect() {
if (this.eventSource) {
this.eventSource.close();
this.eventSource = null;
}
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
}
/**
* Schedule reconnection attempt
*/
scheduleReconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
this.log('Max reconnection attempts reached');
this.showNotification('Hot Reload disconnected', 'error');
return;
}
this.reconnectAttempts++;
const delay = Math.min(this.reconnectDelay * this.reconnectAttempts, 30000);
this.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
this.reconnectTimer = setTimeout(() => {
this.connect();
}, delay);
}
/**
* Handle reload event from server
*/
handleReload(data) {
this.log('Reload event received', data);
switch (data.type) {
case 'css':
this.reloadCSS(data.file);
break;
case 'hmr':
this.handleHMR(data);
break;
case 'partial':
this.reloadPartial(data);
break;
case 'full':
default:
this.reloadPage();
break;
}
}
/**
* Reload CSS without page refresh
*/
reloadCSS(file) {
this.log('Reloading CSS:', file);
const links = document.querySelectorAll('link[rel="stylesheet"]');
const timestamp = new Date().getTime();
links.forEach(link => {
const href = link.getAttribute('href');
if (href) {
// Remove existing timestamp
const cleanHref = href.split('?')[0];
// Add new timestamp to force reload
link.setAttribute('href', `${cleanHref}?t=${timestamp}`);
}
});
this.showNotification('CSS updated', 'info', 2000);
}
/**
* Handle Hot Module Replacement
*/
handleHMR(data) {
this.log('HMR not yet implemented, falling back to full reload');
this.reloadPage();
}
/**
* Reload partial content
*/
reloadPartial(data) {
this.log('Partial reload not yet implemented, falling back to full reload');
this.reloadPage();
}
/**
* Reload the entire page
*/
reloadPage() {
this.log('Reloading page...');
this.showNotification('Reloading...', 'info', 500);
setTimeout(() => {
window.location.reload();
}, 500);
}
/**
* Show notification to user
*/
showNotification(message, type = 'info', duration = 3000) {
// Remove existing notification
const existing = document.getElementById('hot-reload-notification');
if (existing) {
existing.remove();
}
// Create notification element
const notification = document.createElement('div');
notification.id = 'hot-reload-notification';
notification.textContent = message;
notification.className = `hot-reload-notification hot-reload-notification--${type}`;
// Style the notification
notification.style.cssText = `
position: fixed;
bottom: 20px;
right: 20px;
padding: 12px 20px;
border-radius: 6px;
font-family: system-ui, -apple-system, sans-serif;
font-size: 14px;
font-weight: 500;
z-index: 999999;
transition: opacity 0.3s ease;
pointer-events: none;
max-width: 300px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
`;
// Type-specific styles
switch(type) {
case 'success':
notification.style.backgroundColor = '#10b981';
notification.style.color = '#ffffff';
break;
case 'error':
notification.style.backgroundColor = '#ef4444';
notification.style.color = '#ffffff';
break;
case 'warning':
notification.style.backgroundColor = '#f59e0b';
notification.style.color = '#ffffff';
break;
default:
notification.style.backgroundColor = '#3b82f6';
notification.style.color = '#ffffff';
}
document.body.appendChild(notification);
// Auto-remove after duration
if (duration > 0) {
setTimeout(() => {
notification.style.opacity = '0';
setTimeout(() => {
notification.remove();
}, 300);
}, duration);
}
}
/**
* Setup visibility handlers for reconnection
*/
setupVisibilityHandlers() {
// Reconnect when tab becomes visible
document.addEventListener('visibilitychange', () => {
if (!document.hidden && !this.eventSource) {
this.log('Tab became visible, reconnecting...');
this.connect();
}
});
// Disconnect when window is closed
window.addEventListener('beforeunload', () => {
this.disconnect();
});
}
/**
* Debug logging
*/
log(...args) {
if (this.debug) {
console.log('[HotReload]', ...args);
}
}
}
// Auto-initialize if in development
if (typeof window !== 'undefined') {
window.addEventListener('DOMContentLoaded', () => {
// Only initialize if not already initialized
if (!window.__hotReload) {
window.__hotReload = new HotReload({
debug: true // Enable debug in development
});
}
});
}