From 45e9eacbbb598e545390f2caabc72646e7bb0aab Mon Sep 17 00:00:00 2001 From: Anshul Jain Date: Mon, 27 Jul 2026 23:35:07 +0530 Subject: [PATCH] Implement in-browser code playground with live execution (issue #5024) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add interactive code playground component supporting JavaScript, HTML, and CSS: - CodePlayground.jsx: Dual-pane editor with console output and live preview - JavaScript execution with console.log/error/warn support - HTML live preview with event handler support - CSS preview with sample HTML rendering - Tab indentation, syntax-aware editing - Ctrl+Enter keyboard shortcut to run - CodePlayground.module.css: Professional split-view styling - Responsive grid layout - Color-coded console output (log/error/warn/info) - Smooth transitions and hover effects - Mobile-responsive stack view - Custom scrollbar styling - CodePlaygroundExample.mdx: Complete documentation with examples - Language support overview - Keyboard shortcuts reference - Usage examples for all languages - Accessibility features documented - Tips and limitations Features: ✓ Split editor/output panes with live execution ✓ Console output with message type indicators ✓ HTML preview in sandboxed iframe ✓ CSS preview with sample styling area ✓ Tab indentation support ✓ Quick action buttons: Run, Copy, Reset ✓ Keyboard accessibility ✓ Mobile responsive design ✓ Error handling and reporting --- docs/examples/CodePlaygroundExample.mdx | 137 +++++++++++ src/components/CodePlayground.jsx | 260 +++++++++++++++++++++ src/components/CodePlayground.module.css | 281 +++++++++++++++++++++++ 3 files changed, 678 insertions(+) create mode 100644 docs/examples/CodePlaygroundExample.mdx create mode 100644 src/components/CodePlayground.jsx create mode 100644 src/components/CodePlayground.module.css diff --git a/docs/examples/CodePlaygroundExample.mdx b/docs/examples/CodePlaygroundExample.mdx new file mode 100644 index 000000000..02ebbdd9b --- /dev/null +++ b/docs/examples/CodePlaygroundExample.mdx @@ -0,0 +1,137 @@ +--- +id: code-playground-example +title: Code Playground Component +description: Interactive code editor with live execution +--- + +import CodePlayground from '@site/src/components/CodePlayground'; + +# Code Playground + +The Code Playground component provides an interactive development environment for writing and executing code directly in the browser. It supports JavaScript, HTML, and CSS with live preview and console output. + +## Features + +- Live code execution in the browser +- Split-view editor and output panes +- Console output for JavaScript logging +- HTML/CSS live preview +- Syntax highlighting and code formatting +- Tab indentation support (press Tab key) +- Quick actions: Run, Copy, Reset +- Responsive mobile design +- Keyboard shortcuts (Ctrl+Enter to run) + +## Usage + +### JavaScript Playground + +```jsx + +``` + +### HTML Playground + +```jsx + +``` + +### CSS Playground + +```jsx + +``` + +### With Initial Code + +```jsx + +``` + +## JavaScript Playground Example + + + +## HTML Playground Example + + + +## CSS Playground Example + + + +## Keyboard Shortcuts + +| Shortcut | Action | +|----------|--------| +| `Ctrl+Enter` or `Cmd+Enter` | Run code | +| `Tab` | Insert indentation | +| `Ctrl+A` | Select all code | + +## Supported Languages + +### JavaScript +- Full ES6+ support with arrow functions, async/await +- Console logging (console.log, console.error, console.warn) +- Error handling and reporting +- Access to standard JavaScript APIs + +### HTML +- Full HTML5 support +- Inline styles and embedded scripts +- Live rendering preview +- Safe sandboxed execution + +### CSS +- Modern CSS3 features +- Gradients, animations, transforms +- Flexbox and Grid support +- Live style preview with sample HTML + +## Features + +**Live Execution**: Code runs immediately when you click Run or press Ctrl+Enter + +**Console Output**: JavaScript console logs appear in real-time with color-coded message types + +**Error Reporting**: Syntax and runtime errors are displayed with helpful messages + +**Copy to Clipboard**: Quickly copy your code with the Copy button + +**Reset**: Restore default example code with the Reset button + +**Mobile Responsive**: Stacks vertically on smaller screens + +**Sandbox Security**: HTML and CSS execute in an isolated iframe + +## Console Output Types + +- **log** - Standard console.log output +- **error** - Errors and exceptions in red +- **warn** - Warning messages in yellow +- **info** - Informational messages in blue + +## Accessibility + +- Keyboard navigation support +- ARIA labels for all buttons +- Clear error messages +- Screen reader friendly + +## Tips + +1. Use `console.log()` to debug JavaScript code +2. Press Tab to indent code and Shift+Tab to outdent +3. HTML preview supports onclick handlers and event listeners +4. CSS examples include sample HTML for styling demonstration +5. Use `console.error()` for intentional error logging + +## Limitations + +- JavaScript runs in the browser without Node.js APIs +- No access to external APIs (use mock data instead) +- HTML/CSS execute in a sandboxed iframe for security +- Console only shows text and simple JSON serialized objects diff --git a/src/components/CodePlayground.jsx b/src/components/CodePlayground.jsx new file mode 100644 index 000000000..8c2939788 --- /dev/null +++ b/src/components/CodePlayground.jsx @@ -0,0 +1,260 @@ +import React, { useState, useRef, useCallback, useEffect } from 'react'; +import styles from './CodePlayground.module.css'; + +export default function CodePlayground({ language = 'javascript', initialCode = '' }) { + const [code, setCode] = useState(initialCode || getDefaultCode(language)); + const [output, setOutput] = useState([]); + const [isRunning, setIsRunning] = useState(false); + const [error, setError] = useState(''); + const iframeRef = useRef(null); + const consoleRef = useRef([]); + + function getDefaultCode(lang) { + const defaults = { + javascript: `console.log('Hello, World!');\n\n// Try writing some JavaScript code\nconst sum = (a, b) => a + b;\nconsole.log(sum(5, 3));`, + html: `
\n

Hello, World!

\n

This is a live HTML preview

\n \n
`, + css: `body {\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100vh;\n font-family: Arial, sans-serif;\n margin: 0;\n}\n\n.card {\n background: white;\n padding: 40px;\n border-radius: 10px;\n box-shadow: 0 10px 30px rgba(0,0,0,0.3);\n text-align: center;\n}\n\n.card h1 {\n color: #333;\n margin-bottom: 10px;\n}\n\n.card p {\n color: #666;\n}`, + }; + return defaults[lang] || defaults.javascript; + } + + const executeCode = useCallback(() => { + setIsRunning(true); + setError(''); + consoleRef.current = []; + setOutput([]); + + if (language === 'javascript') { + executeJavaScript(); + } else if (language === 'html') { + executeHTML(); + } else if (language === 'css') { + executeCSS(); + } + + setIsRunning(false); + }, [code, language]); + + const executeJavaScript = () => { + try { + const originalLog = console.log; + const originalError = console.error; + const originalWarn = console.warn; + + console.log = (...args) => { + consoleRef.current.push({ + type: 'log', + message: args.map(arg => + typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg) + ).join(' ') + }); + originalLog(...args); + }; + + console.error = (...args) => { + consoleRef.current.push({ + type: 'error', + message: args.map(arg => String(arg)).join(' ') + }); + originalError(...args); + }; + + console.warn = (...args) => { + consoleRef.current.push({ + type: 'warn', + message: args.map(arg => String(arg)).join(' ') + }); + originalWarn(...args); + }; + + const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor; + const asyncCode = new AsyncFunction(code); + asyncCode().catch(err => { + consoleRef.current.push({ + type: 'error', + message: `Error: ${err.message}` + }); + }); + + console.log = originalLog; + console.error = originalError; + console.warn = originalWarn; + + setOutput([...consoleRef.current]); + } catch (err) { + setError(`Execution Error: ${err.message}`); + console.log = console.log; + console.error = console.error; + console.warn = console.warn; + } + }; + + const executeHTML = () => { + try { + if (iframeRef.current) { + const iframeDoc = iframeRef.current.contentDocument || iframeRef.current.contentWindow.document; + iframeDoc.open(); + iframeDoc.write(code); + iframeDoc.close(); + setOutput([{ type: 'info', message: 'HTML rendered successfully' }]); + } + } catch (err) { + setError(`Render Error: ${err.message}`); + } + }; + + const executeCSS = () => { + try { + if (iframeRef.current) { + const htmlContent = ` + + + + + + + +
+

CSS Preview

+

Your CSS styles are applied above

+
+ + + `; + const iframeDoc = iframeRef.current.contentDocument || iframeRef.current.contentWindow.document; + iframeDoc.open(); + iframeDoc.write(htmlContent); + iframeDoc.close(); + setOutput([{ type: 'info', message: 'CSS rendered successfully' }]); + } + } catch (err) { + setError(`Render Error: ${err.message}`); + } + }; + + const handleReset = () => { + setCode(getDefaultCode(language)); + setOutput([]); + setError(''); + }; + + const handleCopy = () => { + navigator.clipboard.writeText(code); + }; + + useEffect(() => { + if (language === 'html' || language === 'css') { + executeCode(); + } + }, []); + + const showIframe = language === 'html' || language === 'css'; + const showConsole = language === 'javascript'; + + return ( +
+
+

{language.toUpperCase()} Playground

+
+ + + +
+
+ +
+
+