UNPKG

@mseep/webgl-mcp

Version:

A Model Context Protocol server for analyzing and optimizing WebGL games

277 lines (270 loc) 10.4 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.GrokadeTemplateManager = void 0; const fs_1 = require("fs"); const path_1 = __importDefault(require("path")); const zlib_1 = __importDefault(require("zlib")); const util_1 = require("util"); const gzip = (0, util_1.promisify)(zlib_1.default.gzip); class GrokadeTemplateManager { constructor(templatePath) { this.defaultConfig = { scaleToFit: true, optimizeForPixelArt: false, centerCanvas: true, showLoadingBar: true, showLoadingText: true, clickToPlay: true, fullscreenButton: true, showScrollbars: false, mobileOptimized: true, forceMobileFullscreen: true, compressionEnabled: true, maxFileSize: 200 * 1024 * 1024, // 200MB maxTotalFiles: 1000, maxFilenameLength: 240, allowedFileTypes: [ '.html', '.js', '.css', '.wasm', '.jpg', '.jpeg', '.png', '.webp', '.svg', '.wav', '.ogg', '.mp3', '.glsl', '.vert', '.frag', '.json', '.txt' ], httpsRequired: true }; this.templatePath = templatePath; } applyTemplate() { return __awaiter(this, arguments, void 0, function* (config = {}) { const finalConfig = Object.assign(Object.assign({}, this.defaultConfig), config); try { yield this.validateTemplate(); yield this.injectTemplateStyles(finalConfig); yield this.setupLoadingIndicators(finalConfig); yield this.configureCompression(finalConfig); yield this.setupMobileSupport(finalConfig); yield this.injectSecurityHeaders(); } catch (error) { console.error('Error applying template:', error); throw error; } }); } validateTemplate() { return __awaiter(this, void 0, void 0, function* () { const indexPath = path_1.default.join(this.templatePath, 'index.html'); if (!(yield this.fileExists(indexPath))) { throw new Error('Template must contain an index.html file'); } }); } injectTemplateStyles(config) { return __awaiter(this, void 0, void 0, function* () { const styles = ` <style> #grokade-container { position: absolute; ${config.centerCanvas ? 'left: 50%; top: 50%; transform: translate(-50%, -50%);' : ''} width: 100%; height: 100%; overflow: ${config.showScrollbars ? 'auto' : 'hidden'}; background: ${config.customBackground || '#000000'}; } #grokade-canvas { ${config.scaleToFit ? ` width: 100%; height: 100%; ${config.optimizeForPixelArt ? 'image-rendering: pixelated;' : ''} ` : ''} } #grokade-fullscreen-button { position: absolute; bottom: 10px; right: 10px; z-index: 100; padding: 8px 12px; background: rgba(0, 0, 0, 0.6); color: white; border: none; border-radius: 4px; cursor: pointer; } @media (max-width: 768px) { #grokade-container { ${config.forceMobileFullscreen ? 'position: fixed; width: 100vw; height: 100vh;' : ''} } } </style> `; const indexPath = path_1.default.join(this.templatePath, 'index.html'); let html = yield fs_1.promises.readFile(indexPath, 'utf-8'); html = html.replace('</head>', `${styles}\n</head>`); yield fs_1.promises.writeFile(indexPath, html, 'utf-8'); }); } setupLoadingIndicators(config) { return __awaiter(this, void 0, void 0, function* () { if (!config.showLoadingBar && !config.showLoadingText) return; const loadingHtml = ` <div id="grokade-loading-container"> ${config.showLoadingBar ? ` <div id="grokade-loading-bar"> <div id="grokade-loading-bar-fill"></div> </div> ` : ''} ${config.showLoadingText ? ` <div id="grokade-loading-text">Loading...</div> ` : ''} </div> `; const loadingStyles = ` <style> #grokade-loading-container { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); text-align: center; } #grokade-loading-bar { width: 200px; height: 20px; background: rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.3); border-radius: 10px; overflow: hidden; } #grokade-loading-bar-fill { width: 0%; height: 100%; background: #4CAF50; transition: width 0.3s ease; } #grokade-loading-text { margin-top: 10px; color: white; font-family: Arial, sans-serif; } </style> `; const loadingScript = ` <script> window.GrokadeLoader = { updateProgress: function(progress) { ${config.showLoadingBar ? ` const fill = document.getElementById('grokade-loading-bar-fill'); if (fill) fill.style.width = (progress * 100) + '%'; ` : ''} ${config.showLoadingText ? ` const text = document.getElementById('grokade-loading-text'); if (text) text.textContent = 'Loading: ' + Math.round(progress * 100) + '%'; ` : ''} } }; </script> `; const indexPath = path_1.default.join(this.templatePath, 'index.html'); let html = yield fs_1.promises.readFile(indexPath, 'utf-8'); html = html.replace('</head>', `${loadingStyles}\n${loadingScript}\n</head>`); html = html.replace('<body>', `<body>\n${loadingHtml}`); yield fs_1.promises.writeFile(indexPath, html, 'utf-8'); }); } configureCompression(config) { return __awaiter(this, void 0, void 0, function* () { if (!config.compressionEnabled) return; // Add compression configuration const compressionScript = ` <script> window.GrokadeCompression = { enabled: true, async decompressData(data) { // Handle compressed data using WebAssembly return data; } }; </script> `; const indexPath = path_1.default.join(this.templatePath, 'index.html'); let html = yield fs_1.promises.readFile(indexPath, 'utf-8'); html = html.replace('</head>', `${compressionScript}\n</head>`); yield fs_1.promises.writeFile(indexPath, html, 'utf-8'); }); } setupMobileSupport(config) { return __awaiter(this, void 0, void 0, function* () { if (!config.mobileOptimized) return; const mobileScript = ` <script> window.GrokadeMobile = { isMobile: /iPhone|iPad|iPod|Android/i.test(navigator.userAgent), init() { if (this.isMobile) { document.body.classList.add('grokade-mobile'); ${config.forceMobileFullscreen ? ` document.documentElement.style.overflow = 'hidden'; document.body.style.overflow = 'hidden'; ` : ''} } } }; window.GrokadeMobile.init(); </script> `; const indexPath = path_1.default.join(this.templatePath, 'index.html'); let html = yield fs_1.promises.readFile(indexPath, 'utf-8'); html = html.replace('</head>', `${mobileScript}\n</head>`); yield fs_1.promises.writeFile(indexPath, html, 'utf-8'); }); } injectSecurityHeaders() { return __awaiter(this, void 0, void 0, function* () { // These headers would typically be set by the server, but we'll document them here const headers = { 'Content-Security-Policy': "default-src 'self' https: data: 'unsafe-inline' 'unsafe-eval'", 'X-Frame-Options': 'SAMEORIGIN', 'X-Content-Type-Options': 'nosniff', 'Referrer-Policy': 'strict-origin-when-cross-origin' }; // Add a comment in the HTML documenting required headers const headerComment = ` <!-- Required Security Headers: ${Object.entries(headers).map(([key, value]) => `${key}: ${value}`).join('\n')} --> `; const indexPath = path_1.default.join(this.templatePath, 'index.html'); let html = yield fs_1.promises.readFile(indexPath, 'utf-8'); html = html.replace('<!DOCTYPE html>', `<!DOCTYPE html>\n${headerComment}`); yield fs_1.promises.writeFile(indexPath, html, 'utf-8'); }); } fileExists(filePath) { return __awaiter(this, void 0, void 0, function* () { try { yield fs_1.promises.access(filePath); return true; } catch (_a) { return false; } }); } } exports.GrokadeTemplateManager = GrokadeTemplateManager;