Spaces:
Runtime error
Runtime error
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>{{ title }} — Code Example</title> | |
| <link | |
| href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Plus+Jakarta+Sans:wght@300;400;600;700;800&display=swap" | |
| rel="stylesheet"> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet" /> | |
| <style> | |
| :root { | |
| /* Matching component_detail.html theme */ | |
| --primary: #4f46e5; | |
| --primary-light: #818cf8; | |
| --accent: #ec4899; | |
| --text-main: #0f172a; | |
| --text-muted: #64748b; | |
| --glass-bg: rgba(255, 255, 255, 0.75); | |
| --editor-bg: #1e293b; | |
| /* Slate 800 - softer than pure black */ | |
| --editor-border: rgba(255, 255, 255, 0.1); | |
| --font-main: 'Plus Jakarta Sans', sans-serif; | |
| --font-code: 'JetBrains Mono', monospace; | |
| } | |
| * { | |
| box-sizing: border-box; | |
| margin: 0; | |
| padding: 0; | |
| } | |
| body { | |
| font-family: var(--font-main); | |
| background: #eef2f6; | |
| background-image: | |
| radial-gradient(at 0% 0%, rgba(99, 102, 241, 0.15) 0px, transparent 50%), | |
| radial-gradient(at 100% 100%, rgba(236, 72, 153, 0.15) 0px, transparent 50%); | |
| color: var(--text-main); | |
| width: 100vw; | |
| height: 100vh; | |
| overflow: hidden; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .slide-wrap { | |
| width: 1920px; | |
| height: 1080px; | |
| background: var(--glass-bg); | |
| backdrop-filter: blur(20px); | |
| box-shadow: | |
| 0 25px 50px -12px rgba(0, 0, 0, 0.1), | |
| 0 0 0 1px rgba(255, 255, 255, 0.5) inset; | |
| border-radius: 24px; | |
| overflow: hidden; | |
| position: relative; | |
| display: flex; | |
| } | |
| /* Sidebar decoration - matching component_detail */ | |
| .sidebar-decor { | |
| width: 12px; | |
| height: 100%; | |
| background: linear-gradient(180deg, var(--primary) 0%, var(--accent) 100%); | |
| flex-shrink: 0; | |
| } | |
| .slide { | |
| flex: 1; | |
| padding: 80px 100px; | |
| display: flex; | |
| flex-direction: column; | |
| position: relative; | |
| z-index: 10; | |
| } | |
| /* Header */ | |
| .header { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| margin-bottom: 50px; | |
| animation: slideInDown 0.6s ease-out; | |
| } | |
| @keyframes slideInDown { | |
| from { | |
| opacity: 0; | |
| transform: translateY(-30px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .header-accent { | |
| height: 6px; | |
| width: 240px; | |
| background: linear-gradient(90deg, #4f46e5 0%, #818cf8 50%, transparent 100%); | |
| border-radius: 4px; | |
| } | |
| .title-section { | |
| margin-bottom: 40px; | |
| animation: slideInLeft 0.7s ease-out 0.2s both; | |
| } | |
| @keyframes slideInLeft { | |
| from { | |
| opacity: 0; | |
| transform: translateX(-40px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateX(0); | |
| } | |
| } | |
| .main-title { | |
| font-size: 64px; | |
| line-height: 1.1; | |
| font-weight: 800; | |
| color: var(--text-main); | |
| letter-spacing: -0.03em; | |
| margin-bottom: 16px; | |
| } | |
| .subtitle { | |
| font-size: 28px; | |
| color: var(--text-muted); | |
| font-weight: 500; | |
| } | |
| /* Code Editor Window */ | |
| .code-window { | |
| flex: 1; | |
| background: var(--editor-bg); | |
| border-radius: 16px; | |
| border: 1px solid var(--editor-border); | |
| box-shadow: | |
| 0 20px 40px rgba(0, 0, 0, 0.2), | |
| 0 0 0 1px rgba(255, 255, 255, 0.05) inset; | |
| overflow: hidden; | |
| display: flex; | |
| flex-direction: column; | |
| animation: fadeIn 0.7s ease-out 0.5s both; | |
| } | |
| @keyframes fadeIn { | |
| from { | |
| opacity: 0; | |
| transform: translateY(20px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| /* Window Header */ | |
| .window-header { | |
| padding: 18px 28px; | |
| background: rgba(15, 23, 42, 0.6); | |
| border-bottom: 1px solid rgba(255, 255, 255, 0.05); | |
| display: flex; | |
| align-items: center; | |
| gap: 16px; | |
| } | |
| .window-dots { | |
| display: flex; | |
| gap: 8px; | |
| } | |
| .dot { | |
| width: 12px; | |
| height: 12px; | |
| border-radius: 50%; | |
| } | |
| .dot-red { | |
| background: #ff5f56; | |
| } | |
| .dot-yellow { | |
| background: #ffbd2e; | |
| } | |
| .dot-green { | |
| background: #27c93f; | |
| } | |
| .file-name { | |
| font-family: var(--font-code); | |
| font-size: 14px; | |
| color: #94a3b8; | |
| font-weight: 500; | |
| } | |
| .language-badge { | |
| margin-left: auto; | |
| font-family: var(--font-code); | |
| font-size: 12px; | |
| color: var(--primary-light); | |
| background: rgba(79, 70, 229, 0.15); | |
| padding: 4px 12px; | |
| border-radius: 6px; | |
| text-transform: uppercase; | |
| letter-spacing: 0.5px; | |
| font-weight: 600; | |
| } | |
| /* Code Content Area */ | |
| .code-content { | |
| flex: 1; | |
| padding: 40px; | |
| overflow-y: auto; | |
| overflow-x: hidden; | |
| position: relative; | |
| } | |
| /* Hide scrollbar */ | |
| .code-content::-webkit-scrollbar { | |
| width: 8px; | |
| } | |
| .code-content::-webkit-scrollbar-track { | |
| background: transparent; | |
| } | |
| .code-content::-webkit-scrollbar-thumb { | |
| background: rgba(255, 255, 255, 0.1); | |
| border-radius: 4px; | |
| } | |
| /* Code Styling */ | |
| code[class*="language-"], | |
| pre[class*="language-"] { | |
| font-family: var(--font-code) ; | |
| font-size: 20px ; | |
| line-height: 1.7 ; | |
| text-shadow: none ; | |
| background: transparent ; | |
| margin: 0 ; | |
| padding: 0 ; | |
| } | |
| /* Smooth cursor - no blinking during typing */ | |
| .cursor { | |
| display: inline-block; | |
| width: 10px; | |
| height: 22px; | |
| background: var(--accent); | |
| vertical-align: middle; | |
| margin-left: 3px; | |
| opacity: 1; | |
| } | |
| .cursor.blink { | |
| animation: blink 1s infinite; | |
| } | |
| @keyframes blink { | |
| 0%, | |
| 49% { | |
| opacity: 1; | |
| } | |
| 50%, | |
| 100% { | |
| opacity: 0; | |
| } | |
| } | |
| /* Background decorations */ | |
| .bg-shape { | |
| position: absolute; | |
| z-index: 1; | |
| opacity: 0.04; | |
| pointer-events: none; | |
| } | |
| .bg-shape-1 { | |
| width: 600px; | |
| height: 600px; | |
| background: radial-gradient(circle, var(--primary) 0%, transparent 70%); | |
| top: -200px; | |
| right: -200px; | |
| } | |
| .bg-shape-2 { | |
| width: 400px; | |
| height: 400px; | |
| background: radial-gradient(circle, var(--accent) 0%, transparent 70%); | |
| bottom: -150px; | |
| left: -100px; | |
| } | |
| /* Line numbers (optional) */ | |
| .line-numbers { | |
| counter-reset: line; | |
| } | |
| .line-numbers .line::before { | |
| counter-increment: line; | |
| content: counter(line); | |
| display: inline-block; | |
| width: 40px; | |
| color: rgba(255, 255, 255, 0.3); | |
| text-align: right; | |
| margin-right: 24px; | |
| user-select: none; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="slide-wrap"> | |
| <div class="sidebar-decor"></div> | |
| <div class="bg-shape bg-shape-1"></div> | |
| <div class="bg-shape bg-shape-2"></div> | |
| <div class="slide"> | |
| <div class="header"> | |
| <div class="header-accent"></div> | |
| </div> | |
| <div class="title-section"> | |
| <h1 class="main-title">{{ title }}</h1> | |
| {% if subtitle %} | |
| <p class="subtitle">{{ subtitle }}</p> | |
| {% endif %} | |
| </div> | |
| <div class="code-window"> | |
| <div class="window-header"> | |
| <div class="window-dots"> | |
| <div class="dot dot-red"></div> | |
| <div class="dot dot-yellow"></div> | |
| <div class="dot dot-green"></div> | |
| </div> | |
| <span class="file-name">{{ filename | default('implementation.js') }}</span> | |
| <span class="language-badge">{{ language | default('javascript') }}</span> | |
| </div> | |
| <div class="code-content" id="scroller"> | |
| <pre><code id="code-display" class="language-{{ language | default('javascript') }}"></code><span id="cursor" class="cursor"></span></pre> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script> | |
| <script> | |
| // Configuration | |
| const sourceCode = {{ code_source | tojson }}; | |
| const typingSpeed = {{ typing_speed | default (25) }}; | |
| const highlightInterval = {{ highlight_interval | default (10) }}; // Highlight every N chars | |
| // DOM Elements | |
| const codeDisplay = document.getElementById('code-display'); | |
| const scroller = document.getElementById('scroller'); | |
| const cursor = document.getElementById('cursor'); | |
| let charIndex = 0; | |
| let highlightQueued = false; | |
| // Smooth typing without flickering | |
| function typeWriter() { | |
| if (charIndex < sourceCode.length) { | |
| // 1. Append character directly to textContent (no DOM manipulation = no flicker) | |
| codeDisplay.textContent += sourceCode.charAt(charIndex); | |
| // 2. Queue syntax highlighting (not every character to prevent flicker) | |
| // Only highlight at intervals or on structural characters | |
| const char = sourceCode.charAt(charIndex); | |
| const isStructuralChar = ['\n', ';', '{', '}', '(', ')'].includes(char); | |
| const isInterval = charIndex % highlightInterval === 0; | |
| if ((isStructuralChar || isInterval) && !highlightQueued) { | |
| highlightQueued = true; | |
| // Use requestAnimationFrame for smooth rendering | |
| requestAnimationFrame(() => { | |
| Prism.highlightElement(codeDisplay); | |
| highlightQueued = false; | |
| }); | |
| } | |
| // 3. Smooth auto-scroll | |
| if (scroller.scrollHeight > scroller.clientHeight) { | |
| scroller.scrollTop = scroller.scrollHeight; | |
| } | |
| charIndex++; | |
| // 4. Variable speed for natural feel | |
| let delay = typingSpeed; | |
| if (char === '\n') delay += 80; // Pause at newlines | |
| if (char === ';') delay += 40; // Small pause at semicolons | |
| setTimeout(typeWriter, delay); | |
| } else { | |
| // 5. Final highlight pass | |
| Prism.highlightElement(codeDisplay); | |
| // 6. Start cursor blinking | |
| cursor.classList.add('blink'); | |
| // 7. Optional: Auto-run code after typing | |
| {% if auto_run %} | |
| setTimeout(executeCode, 500); | |
| {% endif %} | |
| } | |
| } | |
| {% if auto_run %} | |
| // Code execution (optional feature) | |
| function executeCode() { | |
| console.log("=== Code Execution Started ==="); | |
| try { | |
| // Create isolated scope | |
| const codeToRun = codeDisplay.textContent; | |
| // For demo: just eval the code | |
| // In production, you might send this to a backend sandbox | |
| eval(codeToRun); | |
| console.log("=== Execution Completed ==="); | |
| } catch (error) { | |
| console.error("Runtime Error:", error.message); | |
| } | |
| } | |
| {% endif %} | |
| // Start typing animation after page loads | |
| window.addEventListener('load', () => { | |
| // Small delay to let animations settle | |
| setTimeout(typeWriter, 800); | |
| }); | |
| // Prevent console spam in production | |
| if (!{{ auto_run | default ('false') | lower }}) { | |
| console.log = () => { }; | |
| console.error = () => { }; | |
| } | |
| </script> | |
| </body> | |
| </html> |