Core TTS infrastructure: - sherpaTTS.ts: Sherpa ONNX integration for offline TTS - TTSErrorBoundary.tsx: Error boundary for TTS failures - ErrorBoundary.tsx: Generic error boundary component - VoiceIndicator.tsx: Visual indicator for voice activity - useSpeechRecognition.ts: Speech-to-text hook - DebugLogger.ts: Debug logging utility Features: - Offline voice synthesis (no internet needed) - Multiple voices support - Real-time voice activity indication - Error recovery and fallback - Debug logging for troubleshooting Tech stack: - Sherpa ONNX runtime - React Native Audio - Expo modules
176 lines
4.1 KiB
TypeScript
176 lines
4.1 KiB
TypeScript
/**
|
|
* Centralized Debug Logger
|
|
* Captures console logs, errors, warnings for display in Debug tab
|
|
*/
|
|
|
|
export interface LogEntry {
|
|
id: string;
|
|
timestamp: Date;
|
|
level: 'log' | 'warn' | 'error' | 'info';
|
|
category: string; // e.g., "TTS", "STT", "Chat", "System"
|
|
message: string;
|
|
data?: any;
|
|
}
|
|
|
|
class DebugLogger {
|
|
private logs: LogEntry[] = [];
|
|
private maxLogs = 500; // Keep last 500 logs
|
|
private listeners: Set<(logs: LogEntry[]) => void> = new Set();
|
|
|
|
// Original console methods
|
|
private originalConsole = {
|
|
log: console.log.bind(console),
|
|
warn: console.warn.bind(console),
|
|
error: console.error.bind(console),
|
|
info: console.info.bind(console),
|
|
};
|
|
|
|
constructor() {
|
|
this.interceptConsole();
|
|
}
|
|
|
|
/**
|
|
* Intercept console methods to capture logs
|
|
*/
|
|
private interceptConsole() {
|
|
console.log = (...args: any[]) => {
|
|
this.originalConsole.log(...args);
|
|
this.addLog('log', 'System', this.formatArgs(args));
|
|
};
|
|
|
|
console.warn = (...args: any[]) => {
|
|
this.originalConsole.warn(...args);
|
|
this.addLog('warn', 'System', this.formatArgs(args));
|
|
};
|
|
|
|
console.error = (...args: any[]) => {
|
|
this.originalConsole.error(...args);
|
|
this.addLog('error', 'System', this.formatArgs(args));
|
|
};
|
|
|
|
console.info = (...args: any[]) => {
|
|
this.originalConsole.info(...args);
|
|
this.addLog('info', 'System', this.formatArgs(args));
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Format console arguments into string
|
|
*/
|
|
private formatArgs(args: any[]): string {
|
|
return args
|
|
.map(arg => {
|
|
if (typeof arg === 'object') {
|
|
try {
|
|
return JSON.stringify(arg, null, 2);
|
|
} catch {
|
|
return String(arg);
|
|
}
|
|
}
|
|
return String(arg);
|
|
})
|
|
.join(' ');
|
|
}
|
|
|
|
/**
|
|
* Add a log entry
|
|
*/
|
|
private addLog(level: LogEntry['level'], category: string, message: string, data?: any) {
|
|
const entry: LogEntry = {
|
|
id: `${Date.now()}-${Math.random()}`,
|
|
timestamp: new Date(),
|
|
level,
|
|
category,
|
|
message,
|
|
data,
|
|
};
|
|
|
|
this.logs.push(entry);
|
|
|
|
// Trim old logs
|
|
if (this.logs.length > this.maxLogs) {
|
|
this.logs = this.logs.slice(-this.maxLogs);
|
|
}
|
|
|
|
// Notify listeners
|
|
this.notifyListeners();
|
|
}
|
|
|
|
/**
|
|
* Public logging methods with category support
|
|
*/
|
|
log(category: string, message: string, data?: any) {
|
|
this.originalConsole.log(`[${category}]`, message, data || '');
|
|
this.addLog('log', category, message, data);
|
|
}
|
|
|
|
warn(category: string, message: string, data?: any) {
|
|
this.originalConsole.warn(`[${category}]`, message, data || '');
|
|
this.addLog('warn', category, message, data);
|
|
}
|
|
|
|
error(category: string, message: string, data?: any) {
|
|
this.originalConsole.error(`[${category}]`, message, data || '');
|
|
this.addLog('error', category, message, data);
|
|
}
|
|
|
|
info(category: string, message: string, data?: any) {
|
|
this.originalConsole.info(`[${category}]`, message, data || '');
|
|
this.addLog('info', category, message, data);
|
|
}
|
|
|
|
/**
|
|
* Get all logs
|
|
*/
|
|
getLogs(): LogEntry[] {
|
|
return [...this.logs];
|
|
}
|
|
|
|
/**
|
|
* Get logs by category
|
|
*/
|
|
getLogsByCategory(category: string): LogEntry[] {
|
|
return this.logs.filter(log => log.category === category);
|
|
}
|
|
|
|
/**
|
|
* Clear all logs
|
|
*/
|
|
clear() {
|
|
this.logs = [];
|
|
this.notifyListeners();
|
|
}
|
|
|
|
/**
|
|
* Subscribe to log updates
|
|
*/
|
|
subscribe(listener: (logs: LogEntry[]) => void) {
|
|
this.listeners.add(listener);
|
|
return () => this.listeners.delete(listener);
|
|
}
|
|
|
|
/**
|
|
* Notify all listeners
|
|
*/
|
|
private notifyListeners() {
|
|
this.listeners.forEach(listener => listener(this.getLogs()));
|
|
}
|
|
|
|
/**
|
|
* Export logs as text
|
|
*/
|
|
exportAsText(): string {
|
|
return this.logs
|
|
.map(log => {
|
|
const time = log.timestamp.toLocaleTimeString();
|
|
const level = log.level.toUpperCase().padEnd(5);
|
|
const category = `[${log.category}]`.padEnd(12);
|
|
return `${time} ${level} ${category} ${log.message}`;
|
|
})
|
|
.join('\n');
|
|
}
|
|
}
|
|
|
|
// Singleton instance
|
|
export const debugLogger = new DebugLogger();
|