@mseep/webgl-mcp
Version:
A Model Context Protocol server for analyzing and optimizing WebGL games
155 lines (135 loc) • 4.68 kB
text/typescript
import { promises as fs } from 'fs';
import * as path from 'path';
export interface TemplateOptions {
title?: string;
loadingBar?: boolean;
loadingText?: boolean;
compression?: boolean;
memoryLimit?: number;
customStyles?: string;
customScripts?: string[];
}
export interface Template {
name: string;
description: string;
options: TemplateOptions;
defaults: TemplateOptions;
}
export class WebGLTemplateModifier {
constructor(private templatesPath: string) {}
public async modifyTemplate(options: TemplateOptions): Promise<void> {
try {
// Read the template HTML file
const indexPath = path.join(this.templatesPath, 'index.html');
let html = await fs.readFile(indexPath, 'utf-8');
// Apply modifications based on options
html = await this.applyTemplateModifications(html, options);
// Write back the modified template
await fs.writeFile(indexPath, html, 'utf-8');
// Handle additional files if needed
if (options.loadingBar || options.loadingText) {
await this.addLoadingIndicators(options);
}
if (options.compression) {
await this.setupCompression();
}
} catch (error) {
console.error('Error modifying template:', error);
throw error;
}
}
private async applyTemplateModifications(html: string, options: TemplateOptions): Promise<string> {
// Update title if provided
if (options.title) {
html = html.replace(/<title>.*?<\/title>/, `<title>${options.title}</title>`);
}
// Add custom styles
if (options.customStyles) {
const styleTag = `<style>${options.customStyles}</style>`;
html = html.replace('</head>', `${styleTag}\n</head>`);
}
// Add custom scripts
if (options.customScripts?.length) {
const scriptTags = options.customScripts
.map(script => `<script src="${script}"></script>`)
.join('\n');
html = html.replace('</body>', `${scriptTags}\n</body>`);
}
// Add memory limit configuration
if (options.memoryLimit) {
const memoryConfig = `
<script>
var TOTAL_MEMORY = ${options.memoryLimit * 1024 * 1024};
</script>`;
html = html.replace('</head>', `${memoryConfig}\n</head>`);
}
return html;
}
private async addLoadingIndicators(options: TemplateOptions): Promise<void> {
const loadingStyles = `
.webgl-loading-bar {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 200px;
height: 20px;
background: #f0f0f0;
border: 1px solid #333;
}
.webgl-loading-bar-fill {
width: 0%;
height: 100%;
background: #4CAF50;
transition: width 0.3s ease;
}
.webgl-loading-text {
position: absolute;
left: 50%;
top: calc(50% + 30px);
transform: translateX(-50%);
font-family: Arial, sans-serif;
color: #333;
}`;
const loadingScript = `
function updateLoadingProgress(progress) {
${options.loadingBar ? `
var fill = document.querySelector('.webgl-loading-bar-fill');
if (fill) fill.style.width = (progress * 100) + '%';
` : ''}
${options.loadingText ? `
var text = document.querySelector('.webgl-loading-text');
if (text) text.textContent = 'Loading: ' + Math.round(progress * 100) + '%';
` : ''}
}`;
// Write the loading indicator files
const stylesPath = path.join(this.templatesPath, 'loading-styles.css');
const scriptPath = path.join(this.templatesPath, 'loading-script.js');
await fs.writeFile(stylesPath, loadingStyles, 'utf-8');
await fs.writeFile(scriptPath, loadingScript, 'utf-8');
}
private async setupCompression(): Promise<void> {
// Add compression-related configurations and scripts
const compressionConfig = `
<script>
// Enable compression for runtime loading
var Module = {
compressedDataHandler: function(data) {
// Handle compressed data
return new Promise(function(resolve) {
// Decompress data using WebAssembly
resolve(data);
});
}
};
</script>`;
const indexPath = path.join(this.templatesPath, 'index.html');
let html = await fs.readFile(indexPath, 'utf-8');
html = html.replace('</head>', `${compressionConfig}\n</head>`);
await fs.writeFile(indexPath, html, 'utf-8');
}
public async getTemplatesForEngine(engineId: string): Promise<Template[]> {
// Implementation
return [];
}
}