Patrick Dunn

MonacoLanguageStudio

Dashboards

About

Monaco Language Studio Professional language definition toolkit

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds MonacoLanguageStudio to your project and fits it to your theme.

Code

How MonacoLanguageStudio is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

import React, { useState, useEffect } from 'react';
import { FileCode, Eye, Download, Upload, Check, AlertCircle, Copy, Palette, Code2 } from 'lucide-react';

const MonacoLanguageStudio = () => {
  const [input, setInput] = useState('');
  const [parsed, setParsed] = useState(null);
  const [error, setError] = useState('');
  const [activeTab, setActiveTab] = useState('parse');
  const [beautified, setBeautified] = useState('');
  const [validation, setValidation] = useState([]);

  const parseAMDModule = (code) => {
    try {
      const defineMatch = code.match(/define\s*\(\s*["']([^"']+)["']\s*,\s*\[([^\]]+)\]\s*,\s*\(?\s*function\s*\(([^)]*)\)\s*\{([\s\S]*)\}\s*\)?\s*\)/);
      
      if (!defineMatch) {
        throw new Error('Not a valid AMD module definition');
      }

      const [, moduleName, dependencies, params, body] = defineMatch;
      
      const confMatch = body.match(/const\s+n\s*=\s*e\.conf/);
      const langMatch = body.match(/const\s+g\s*=\s*\{([\s\S]*?)\};/);
      
      if (!langMatch) {
        throw new Error('Could not extract language definition');
      }

      const langObjStr = '{' + langMatch[1] + '}';
      
      const keywordsMatch = langObjStr.match(/keywords:\s*\[([^\]]+)\]/);
      const keywords = keywordsMatch ? 
        keywordsMatch[1].split(',').map(k => k.trim().replace(/['"]/g, '')) : [];

      const operatorsMatch = langObjStr.match(/operators:\s*e\.language\.operators/);
      
      return {
        moduleName: moduleName.trim(),
        dependencies: dependencies.split(',').map(d => d.trim().replace(/['"]/g, '')),
        parameters: params.split(',').map(p => p.trim()),
        language: {
          keywords,
          hasOperators: !!operatorsMatch,
          defaultToken: 'invalid',
          tokenPostfix: '.js'
        }
      };
    } catch (err) {
      throw new Error(`Parse error: ${err.message}`);
    }
  };

  const validateLanguageDef = (def) => {
    const issues = [];
    
    if (!def) {
      issues.push({ type: 'error', message: 'No definition provided' });
      return issues;
    }

    if (!def.language) {
      issues.push({ type: 'error', message: 'Missing language object' });
      return issues;
    }

    if (!def.language.keywords || def.language.keywords.length === 0) {
      issues.push({ type: 'warning', message: 'No keywords defined' });
    }

    if (!def.language.defaultToken) {
      issues.push({ type: 'warning', message: 'No defaultToken specified' });
    }

    if (def.language.keywords && def.language.keywords.length > 100) {
      issues.push({ type: 'info', message: `Large keyword set: ${def.language.keywords.length} keywords` });
    }

    if (issues.length === 0) {
      issues.push({ type: 'success', message: 'Language definition is valid' });
    }

    return issues;
  };

  const beautifyDefinition = (def) => {
    if (!def) return '';

    const langDef = {
      id: def.moduleName.split('/').pop(),
      keywords: def.language.keywords,
      defaultToken: def.language.defaultToken,
      tokenPostfix: def.language.tokenPostfix,
      hasOperators: def.language.hasOperators
    };

    return JSON.stringify(langDef, null, 2);
  };

  const generateMonacoLanguage = (def) => {
    if (!def) return '';

    const langId = def.moduleName.split('/').pop().replace(/^javascript-.*$/, 'javascript');
    
    return `// Monaco Editor Language Definition for ${langId}
monaco.languages.register({ id: '${langId}' });

monaco.languages.setMonarchTokensProvider('${langId}', {
  defaultToken: '${def.language.defaultToken}',
  tokenPostfix: '${def.language.tokenPostfix}',
  
  keywords: [
    ${def.language.keywords.map(k => `'${k}'`).join(',\n    ')}
  ],
  
  operators: [
    '<=', '>=', '==', '!=', '===', '!==', '=>',
    '+', '-', '**', '*', '/', '%',
    '++', '--', '<<', '>>', '>>>', '&', '|', '^', '!',
    '~', '&&', '||', '??', '?', ':', '=',
    '+=', '-=', '*=', '/=', '%=', '&=', '|=',
    '^=', '<<=', '>>=', '>>>='
  ],
  
  symbols: /[=><!~?:&|+\\-*\\/\\^%]+/,
  
  tokenizer: {
    root: [
      [/[a-z_$][\\w$]*/, {
        cases: {
          '@keywords': 'keyword',
          '@default': 'identifier'
        }
      }],
      [/[A-Z][\\w\\$]*/, 'type.identifier'],
      { include: '@whitespace' },
      [/[{}()\\[\\]]/, '@brackets'],
      [/@symbols/, {
        cases: {
          '@operators': 'operator',
          '@default': ''
        }
      }],
      [/\\d*\\.\\d+([eE][\\-+]?\\d+)?/, 'number.float'],
      [/0[xX][0-9a-fA-F]+/, 'number.hex'],
      [/\\d+/, 'number'],
      [/["']([^"'\\\\]|\\\\.)*["']/, 'string']
    ],
    
    whitespace: [
      [/[ \\t\\r\\n]+/, ''],
      [/\\/\\*/, 'comment', '@comment'],
      [/\\/\\/.*$/, 'comment']
    ],
    
    comment: [
      [/[^\\/*]+/, 'comment'],
      [/\\*\\//, 'comment', '@pop'],
      [/[\\/*]/, 'comment']
    ]
  }
});`;
  };

  const handleParse = () => {
    try {
      const result = parseAMDModule(input);
      setParsed(result);
      setBeautified(beautifyDefinition(result));
      setValidation(validateLanguageDef(result));
      setError('');
    } catch (err) {
      setError(err.message);
      setParsed(null);
      setBeautified('');
      setValidation([]);
    }
  };

  const handleFileUpload = (e) => {
    const file = e.target.files[0];
    if (file) {
      const reader = new FileReader();
      reader.onload = (event) => {
        setInput(event.target.result);
      };
      reader.readAsText(file);
    }
  };

  const copyToClipboard = (text) => {
    navigator.clipboard.writeText(text);
  };

  const downloadFile = (content, filename) => {
    const blob = new Blob([content], { type: 'text/javascript' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    a.click();
    URL.revokeObjectURL(url);
  };

  useEffect(() => {
    if (input) {
      handleParse();
    }
  }, [input]);

  const getValidationIcon = (type) => {
    switch (type) {
      case 'success': return <Check className="w-4 h-4 text-emerald-500" />;
      case 'error': return <AlertCircle className="w-4 h-4 text-red-400" />;
      case 'warning': return <AlertCircle className="w-4 h-4 text-amber-400" />;
      default: return <AlertCircle className="w-4 h-4 text-blue-400" />;
    }
  };

  return (
    <div className="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 p-6">
      <style>{`
        .carbon-texture {
          background-color: linear-gradient(135deg, rgba(90,188,40,0.5) 0%, rgba(40,81,90,0.7) 100%);
          background-image: url('https://pub-f741fb7b98024df48a8a6d8ac4804414.r2.dev/PILL_TRI.webp');
          background-size: 115px 95px;
          background-repeat: repeat;
          position: relative;
        }
        .carbon-texture::before {
          content: '';
          position: absolute;
          inset: 1;
          background-image: url('https://pub-f741fb7b98024df48a8a6d8ac4804414.r2.dev/PILL_TRI.webp');
          background-size: 1000px -100px;
          background-repeat: repeat;
          opacity: 80.;
          mix-blend-mode: hard-light;
          pointer-events: none;
        }
        .carbon-texture::after {
          content: '';
          position: absolute;
          inset: 1;
          background: linear-gradient(135deg, rgba(0,1,0,0.1) 0%, rgba(0,1,0,0.7) 100%);
          pointer-events: none;
        }
        .carbon-content {
          position: relative;
          z-index: 1;
        }
      `}</style>
      
      <div className="max-w-7xl mx-auto">
        <div className="bg-black rounded-3xl shadow-2xl overflow-hidden">
          {/* Header */}
          <div className="relative overflow-hidden">
            <div className="absolute inset-0 bg-gradient-to-br from-gray-400 to-gray-500 opacity-90"></div>
            <div className="absolute inset-0 bg-gradient-to-b from-black/30 to-transparent"></div>
            <div className="relative p-8">
              <div className="flex items-center gap-4">
                <div className="w-16 h-16 rounded-3xl bg-black/20 backdrop-blur-sm flex items-center justify-center border border-black/40">
                  <FileCode className="w-8 h-8 text-white" />
                </div>
                <div>
                  <h1 className="text-4xl font-bold text-white tracking-tight">Monaco Language Studio</h1>
                  <p className="text-white/90 mt-2 text-lg">Professional language definition toolkit</p>
                </div>
              </div>
            </div>
          </div>

          {/* Input Section - Carbon Fiber Background */}
          <div className="carbon-texture p-8">
            <div className="carbon-content">
              <div className="flex items-center gap-4 mb-6">
                <label className="relative group cursor-pointer">
                  <div className="px-6 py-3 bg-gradient-to-br from-gray-400 to-gray-500 hover:from-gray-500 hover:to-gray-600 text-white rounded-2xl transition-all duration-300 flex items-center gap-3 shadow-lg">
                    <Upload className="w-5 h-5" />
                    <span className="font-medium">Upload Module</span>
                  </div>
                  <input type="file" accept=".js,.ts" onChange={handleFileUpload} className="hidden" />
                </label>
                <span className="text-white drop-shadow-lg text-sm font-medium">or paste your AMD module definition below</span>
              </div>
              
              <textarea
                value={input}
                onChange={(e) => setInput(e.target.value)}
                placeholder="Paste your AMD module definition here..."
                className="w-full h-56 bg-black/40 backdrop-blur-sm text-gray-100 placeholder-gray-400 rounded-2xl p-6 font-mono text-sm border-2 border-red/20 focus:border-red/40 focus:outline-none resize-none shadow-2xl transition-colors"
              />

              {error && (
                <div className="mt-4 p-4 bg-red-900/80 backdrop-blur-sm border-2 border-red-500/50 rounded-2xl flex items-center gap-3 text-red-100">
                  <AlertCircle className="w-5 h-5 flex-shrink-0" />
                  <span className="font-medium">{error}</span>
                </div>
              )}
            </div>
          </div>

          {/* Tabs - Carbon Fiber Background */}
          {parsed && (
            <div className="carbon-texture border-b-2 border-gray-800/30">
              <div className="carbon-content flex gap-2 px-8">
                {['parse', 'validate', 'beautify', 'generate'].map((tab) => (
                  <button
                    key={tab}
                    onClick={() => setActiveTab(tab)}
                    className={`px-8 py-4 font-semibold transition-all duration-300 relative ${
                      activeTab === tab
                        ? 'text-white drop-shadow-lg'
                        : 'text-gray-300 hover:text-red'
                    }`}
                  >
                    {tab.charAt(0).toUpperCase() + tab.slice(1)}
                    {activeTab === tab && (
                      <div className="absolute bottom-0 left-0 right-0 h-1 bg-gradient-to-r from-gray-300 to-white rounded-full shadow-lg"></div>
                    )}
                  </button>
                ))}
              </div>
            </div>
          )}

          {/* Content - Carbon Fiber Background */}
          {parsed && (
            <div className="carbon-texture p-8">
              <div className="carbon-content">
                {activeTab === 'parse' && (
                  <div className="space-y-6">
                    <div className="bg-black/40 backdrop-blur-md rounded-2xl p-6 border-2 border-white/20 shadow-2xl">
                      <h3 className="text-white font-bold text-lg mb-4 drop-shadow-lg">Module Information</h3>
                      <div className="space-y-3 text-sm">
                        <div className="flex gap-3">
                          <span className="text-gray-300 font-semibold min-w-32">Module:</span>
                          <span className="text-white font-mono">{parsed.moduleName}</span>
                        </div>
                        <div className="flex gap-3">
                          <span className="text-gray-300 font-semibold min-w-32">Dependencies:</span>
                          <span className="text-white font-mono">{parsed.dependencies.join(', ')}</span>
                        </div>
                      </div>
                    </div>

                    <div className="bg-black/40 backdrop-blur-md rounded-2xl p-6 border-2 border-red/20 shadow-2xl">
                      <h3 className="text-white font-bold text-lg mb-4 drop-shadow-lg">Keywords ({parsed.language.keywords.length})</h3>
                      <div className="flex flex-wrap gap-2 max-h-80 overflow-y-auto">
                        {parsed.language.keywords.map((kw, i) => (
                          <span key={i} className="px-4 py-2 bg-gray-700/60 backdrop-blur-sm text-gray-100 rounded-full text-sm font-medium shadow-lg border border-gray-500/50">
                            {kw}
                          </span>
                        ))}
                      </div>
                    </div>
                  </div>
                )}

                {activeTab === 'validate' && (
                  <div className="space-y-4">
                    {validation.map((issue, i) => (
                      <div
                        key={i}
                        className={`p-5 backdrop-blur-md rounded-2xl flex items-center gap-4 border-2 shadow-2xl ${
                          issue.type === 'success' ? 'bg-emerald-900/60 border-emerald-500/50' :
                          issue.type === 'error' ? 'bg-red-900/60 border-red-500/50' :
                          issue.type === 'warning' ? 'bg-amber-900/60 border-amber-500/50' :
                          'bg-blue-900/60 border-blue-500/50'
                        }`}
                      >
                        {getValidationIcon(issue.type)}
                        <span className={`font-medium ${
                          issue.type === 'success' ? 'text-emerald-100' :
                          issue.type === 'error' ? 'text-red-100' :
                          issue.type === 'warning' ? 'text-amber-100' :
                          'text-blue-100'
                        }`}>{issue.message}</span>
                      </div>
                    ))}
                  </div>
                )}

                {activeTab === 'beautify' && (
                  <div className="space-y-6">
                    <div className="flex gap-3">
                      <button
                        onClick={() => copyToClipboard(beautified)}
                        className="flex items-center gap-2 px-6 py-3 bg-gradient-to-br from-gray-400 to-gray-500 hover:from-gray-500 hover:to-gray-600 text-white rounded-2xl transition-all duration-300 shadow-lg font-medium"
                      >
                        <Copy className="w-4 h-4" />
                        Copy JSON
                      </button>
                      <button
                        onClick={() => downloadFile(beautified, 'language-definition.json')}
                        className="flex items-center gap-2 px-6 py-3 bg-black/40 hover:bg-black/60 backdrop-blur-sm text-red rounded-2xl transition-all duration-300 shadow-lg border-2 border-red/20 font-medium"
                      >
                        <Download className="w-4 h-4" />
                        Download
                      </button>
                    </div>
                    <pre className="bg-black/40 backdrop-blur-md text-gray-100 rounded-2xl p-6 overflow-x-auto border-2 border-white/20 font-mono text-sm shadow-2xl">
                      {beautified}
                    </pre>
                  </div>
                )}

                {activeTab === 'generate' && (
                  <div className="space-y-6">
                    <div className="flex gap-3">
                      <button
                        onClick={() => copyToClipboard(generateMonacoLanguage(parsed))}
                        className="flex items-center gap-2 px-6 py-3 bg-gradient-to-br from-gray-400 to-gray-500 hover:from-gray-500 hover:to-gray-600 text-red rounded-2xl transition-all duration-300 shadow-lg font-medium"
                      >
                        <Copy className="w-4 h-4" />
                        Copy Code
                      </button>
                      <button
                        onClick={() => downloadFile(generateMonacoLanguage(parsed), 'monaco-language.js')}
                        className="flex items-center gap-2 px-6 py-3 bg-black/40 hover:bg-black/60 backdrop-blur-sm text-red rounded-2xl transition-all duration-300 shadow-lg border-2 border-white/20 font-medium"
                      >
                        <Download className="w-4 h-4" />
                        Download
                      </button>
                    </div>
                    <pre className="bg-black/40 backdrop-blur-md text-gray-100 rounded-2xl p-6 overflow-x-auto border-2 border-white/20 font-mono text-sm max-h-96 shadow-2xl">
                      {generateMonacoLanguage(parsed)}
                    </pre>
                  </div>
                )}
              </div>
            </div>
          )}

          {!parsed && !error && (
            <div className="carbon-texture p-20 text-center">
              <div className="carbon-content">
                <div className="w-20 h-20 mx-auto mb-6 rounded-3xl bg-black/40 backdrop-blur-md flex items-center justify-center border-2 border-white/20 shadow-2xl">
                  <Code2 className="w-10 h-10 text-gray-300" />
                </div>
                <p className="text-white drop-shadow-lg text-lg font-medium">Upload or paste an AMD module definition to begin</p>
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

export default MonacoLanguageStudio;

More by Patrick Dunn

View all
  • Wilderness

    Wilderness

    891
  • sneak

    sneak

    449
  • Robot Flyby

    Robot Flyby

    247
  • Desert Drift

    Desert Drift

    175
  • Tidal Cursor

    Tidal Cursor

    147
  • Bloodline

    Bloodline

    120
  • Add Requests

    Add Requests

    3
  • Glass Spiral

    Glass Spiral

    2

More in Dashboards

View all
  • Dashboard with Collapsible Sidebar

    Dashboard with Collapsible Sidebar

    2.4K
  • Sidebar

    Sidebar

    235
  • SaaS Template

    SaaS Template

    2.4K
  • Stats Bento

    Stats Bento

    1.1K
  • Financial Bento Grid

    Financial Bento Grid

    71
  • Stats cards with links

    Stats cards with links

    33
  • Course Design Cards

    Course Design Cards

    1.1K
  • Chat template

    Chat template

    598