import * as fs from 'fs'; import * as http from 'http'; import * as path from 'path'; import { createLogger } from '../../logger'; const logger = createLogger('main:texthooker'); export class Texthooker { private server: http.Server | null = null; public isRunning(): boolean { return this.server !== null; } public start(port: number): http.Server | null { const texthookerPath = this.getTexthookerPath(); if (!texthookerPath) { logger.error('texthooker-ui not found'); return null; } this.server = http.createServer((req, res) => { const urlPath = (req.url || '/').split('?')[0] ?? '/'; const filePath = path.join(texthookerPath, urlPath === '/' ? 'index.html' : urlPath); const ext = path.extname(filePath); const mimeTypes: Record = { '.html': 'text/html', '.js': 'application/javascript', '.css': 'text/css', '.json': 'application/json', '.png': 'image/png', '.svg': 'image/svg+xml', '.ttf': 'font/ttf', '.woff': 'font/woff', '.woff2': 'font/woff2', }; fs.readFile(filePath, (err, data) => { if (err) { res.writeHead(404); res.end('Not found'); return; } res.writeHead(200, { 'Content-Type': mimeTypes[ext] || 'text/plain' }); res.end(data); }); }); this.server.listen(port, '127.0.0.1', () => { logger.info(`Texthooker server running at http://127.0.0.1:${port}`); }); return this.server; } public stop(): void { if (this.server) { this.server.close(); this.server = null; } } private getTexthookerPath(): string | null { const searchPaths = [ path.join(__dirname, '..', '..', '..', 'vendor', 'texthooker-ui', 'docs'), path.join(process.resourcesPath, 'app', 'vendor', 'texthooker-ui', 'docs'), ]; for (const candidate of searchPaths) { if (fs.existsSync(path.join(candidate, 'index.html'))) { return candidate; } } return null; } }