Skip to main content
Glama

PopUI

by kelnishi
TsxWindow.html9.34 kB
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>${filename}</title> <!-- Load local React and ReactDOM --> <script src="assets://react.development.js"></script> <script src="assets://react-dom.development.js"></script> <!-- Load local Babel Standalone --> <script src="assets://babel.min.js"></script> <script src="assets://twind.umd.js"></script> <!-- <link href="styles://output.css" rel="stylesheet">--> <script src="assets://lucide.js"></script> </head> <body> <div id="root"></div> <!-- Embed the TSX code in a script that Babel will compile --> <script id="module-code" type="text/plain"> ${tsxCode} </script> <script> async function processTailwindJIT(componentCode) { try { // Get the twind objects from the window const twind = window.twind; // Create a virtual sheet manually since sheets.virtual() is not available const sheet = { target: '', insert(rule) { this.target += rule; } }; // Configure twind with the virtual sheet twind.setup({ sheet, preflight: true, // Include Tailwind's base styles mode: 'silent', // Don't warn about unknown classes theme: { extend: {} } }); // Use a regex that matches both static (double-quoted) and template literal (backtick) className values const regex = /className=(?:"([^"]*)"|\{`([^`]*)`\})/g; let allClasses = []; let match; while ((match = regex.exec(componentCode)) !== null) { // Use group 1 for static strings or group 2 for template literals let classStr = match[1] || match[2] || ''; // Extract dynamic tokens from interpolations (e.g. ${...}) let dynamicTokens = []; const interpolationRegex = /\$\{([^}]+)\}/g; let dynamicMatch; while ((dynamicMatch = interpolationRegex.exec(classStr)) !== null) { let token = dynamicMatch[1].trim(); // Split by whitespace in case the expression yields multiple tokens and remove surrounding quotes from each token dynamicTokens.push(...token.split(/\s+/).map(tok => tok.replace(/^['"]+|['"]+$/g, ''))); } // Remove interpolated expressions from the static part classStr = classStr.replace(/\$\{[^}]+}/g, '').trim(); let staticTokens = classStr ? classStr.split(/\s+/) : []; // Combine both static and dynamic tokens const tokens = staticTokens.concat(dynamicTokens); if (tokens.length > 0) { allClasses.push(...tokens); } } // Apply each token with twind; ignore any failures allClasses.forEach(token => { try { twind.tw(token); } catch (e) { // Ignore tokens that fail to compile } }); // Get the CSS content from the virtual sheet const cssText = sheet.target; // Return the CSS content return cssText; } catch (error) { console.error('Error processing Twind styles:', error); return ''; } } async function renderDynamicComponent() { // Get the inline TSX code const sourceCode = document.getElementById('module-code').textContent; // Generate JIT CSS for just this component const componentCSS = await processTailwindJIT(sourceCode); // Inject the CSS const styleEl = document.createElement('style'); styleEl.textContent = componentCSS; document.head.appendChild(styleEl); // Transform the code using Babel. We use the presets for React and TypeScript. const { code } = Babel.transform(sourceCode, { filename: '${filename}', // Provide a filename for Babel to resolve parsing correctly. presets: [ ['env', { modules: 'commonjs' }], // Transforms ES modules to CommonJS. 'react', 'typescript' ], }); // Create a require function that returns React when asked for it. const requireFn = (moduleName) => { if (moduleName === 'react') { return React; } if (moduleName === 'lucide-react') { const lucideProxy = {}; // Get all icon names from window.lucide const iconNames = Object.keys(window.lucide).filter( // Filter out non-array entries or prototype properties key => Array.isArray(window.lucide[key]) && !key.startsWith('__') && !key.startsWith('[[Prototype]]') ); console.log(`Creating ${iconNames.length} Lucide icon components`); // Create a component for each icon iconNames.forEach(iconName => { lucideProxy[iconName] = createSvgIconFromLucideData(iconName, window.lucide[iconName]); }); return lucideProxy; } throw new Error("Module not found: " + moduleName); }; // Helper function to create an SVG component from Lucide icon data function createSvgIconFromLucideData(iconName, iconData) { return React.forwardRef((props, ref) => { const { color = 'currentColor', size = 24, strokeWidth = 2, ...restProps } = props; return React.createElement( 'svg', { ref, xmlns: 'http://www.w3.org/2000/svg', width: size, height: size, viewBox: '0 0 24 24', fill: 'none', stroke: color, strokeWidth, strokeLinecap: 'round', strokeLinejoin: 'round', ...restProps }, // Process each element in the icon data array ...iconData.map((item, index) => { // Based on the structure we see in the console: // Each item is an array where first element is the element type (e.g. "path") // and second element contains attributes if (Array.isArray(item) && item.length >= 2) { const [elementType, attributes] = item; // Create the SVG element with its attributes return React.createElement( elementType, { key: `${iconName}-${index}`, ...processAttributes(attributes) } ); } // Handle case where item is just a string (e.g. "path") if (typeof item === 'string') { return React.createElement( item, { key: `${iconName}-${index}` } ); } return null; }) ); }); } // Helper to process attributes (handle the various formats seen in the console) function processAttributes(attrs) { if (!attrs) return {}; // If attrs is already an object, return it if (typeof attrs === 'object' && !Array.isArray(attrs)) { return attrs; } // If attrs is an array of key-value pairs like ["path", {...}] if (Array.isArray(attrs) && attrs.length >= 2 && typeof attrs[1] === 'object') { return attrs[1]; } // If attrs is something else, try to make sense of it const result = {}; // Handle case where we have [key, value] pairs if (Array.isArray(attrs)) { for (let i = 0; i < attrs.length; i += 2) { if (i + 1 < attrs.length) { const key = attrs[i]; const value = attrs[i + 1]; if (typeof key === 'string') { result[key] = value; } } } } return result; } const module = { exports: {} }; const exports = module.exports; new Function("require", "module", "exports", code)(requireFn, module, exports); // If there is a default export, use it; otherwise, pick the first export. const Component = module.exports.default || Object.values(module.exports)[0]; if (!Component) { console.error("No component was found in the module exports."); } else { const rootElement = document.getElementById("root"); if (rootElement) { const root = ReactDOM.createRoot(rootElement); // Check if Component is a class or forwardRef component const isClassComponent = Component.prototype && Component.prototype.isReactComponent; const isForwardRef = Component.$$typeof === Symbol.for('react.forward_ref'); if (isClassComponent || isForwardRef) { // For class components and forwardRef components, we can use refs root.render( React.createElement('div', { id: 'dynamic-component-container' }, React.createElement(Component, { ref: (instance) => { window.dynamicComponent = instance; } }) ) ); } else { // For regular function components, don't use refs const componentElement = React.createElement(Component, {}); window.dynamicComponent = componentElement; root.render( React.createElement('div', { id: 'dynamic-component-container' }, componentElement ) ); } } } } </script> <script> // Wait for the DOM to be ready document.addEventListener('DOMContentLoaded', async () => { await renderDynamicComponent(); }); </script> </body> </html>

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kelnishi/PopUI'

If you have feedback or need assistance with the MCP directory API, please join our Discord server