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>{{ slide_title }}</title> | |
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.25/dist/katex.min.css" integrity="sha384-WcoG4HRXMzYzfCgiyfrySxx90XSl2rxY5mnVY5TwtWE6KLrArNKn0T/mOgNL0Mmi" crossorigin="anonymous"> | |
| <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.25/dist/katex.min.js" integrity="sha384-J+9dG2KMoiR9hqcFao0IBLwxt6zpcyN68IgwzsCSkbreXUjmNVRhPFTssqdSGjwQ" crossorigin="anonymous"></script> | |
| <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.25/dist/contrib/auto-render.min.js" integrity="sha384-hCXGrW6PitJEwbkoStFjeJxv+fSOOQKOPbJxSfM6G5sWZjAyWhXiTIIAmQqnlLlh" crossorigin="anonymous"></script> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| font-family: 'Segoe UI', Arial, sans-serif; | |
| } | |
| body { | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| min-height: 100vh; | |
| background: #f5f5f5; | |
| margin: 0; | |
| padding: 0; | |
| } | |
| .slide { | |
| width: 1920px; | |
| height: 1080px; | |
| position: relative; | |
| overflow: hidden; | |
| background: white; | |
| } | |
| /* Left vertical accent bar */ | |
| .vertical-accent { | |
| position: absolute; | |
| left: 0; | |
| top: 0; | |
| bottom: 0; | |
| width: 60px; | |
| background: linear-gradient(180deg, {{ accent_color_1|default('#2f6fec') }} 0%, {{ accent_color_2|default('#1a5acc') }} 100%); | |
| z-index: 3; | |
| opacity: 0; | |
| transform: scaleY(0); | |
| transform-origin: top; | |
| transition: all 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) 0.3s; | |
| } | |
| .slide.animate .vertical-accent { | |
| opacity: 1; | |
| transform: scaleY(1); | |
| } | |
| /* Decorative elements on vertical bar */ | |
| .accent-dots { | |
| position: absolute; | |
| left: 0; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| width: 60px; | |
| height: 200px; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: space-around; | |
| padding: 20px 0; | |
| z-index: 4; | |
| } | |
| .accent-dot { | |
| width: 8px; | |
| height: 8px; | |
| background: rgba(255, 255, 255, 0.4); | |
| border-radius: 50%; | |
| opacity: 0; | |
| transform: scale(0); | |
| transition: all 0.4s ease-out; | |
| } | |
| .slide.animate .accent-dot { | |
| opacity: 1; | |
| transform: scale(1); | |
| } | |
| .slide.animate .accent-dot:nth-child(1) { transition-delay: 1.5s; } | |
| .slide.animate .accent-dot:nth-child(2) { transition-delay: 1.7s; } | |
| .slide.animate .accent-dot:nth-child(3) { transition-delay: 1.9s; } | |
| .slide.animate .accent-dot:nth-child(4) { transition-delay: 2.1s; } | |
| .slide.animate .accent-dot:nth-child(5) { transition-delay: 2.3s; } | |
| .accent-line { | |
| position: absolute; | |
| left: 0; | |
| bottom: 100px; | |
| width: 60px; | |
| height: 3px; | |
| background: rgba(255, 255, 255, 0.3); | |
| opacity: 0; | |
| transform: scaleX(0); | |
| transform-origin: left; | |
| transition: all 0.6s ease-out 2.5s; | |
| z-index: 4; | |
| } | |
| .slide.animate .accent-line { | |
| opacity: 1; | |
| transform: scaleX(1); | |
| } | |
| /* Decorative circles */ | |
| .deco-circle { | |
| position: absolute; | |
| border-radius: 50%; | |
| z-index: 0; | |
| opacity: 0; | |
| transition: opacity 0.8s ease-out; | |
| } | |
| .slide.animate .deco-circle { | |
| opacity: 1; | |
| } | |
| /* Main title section - top */ | |
| .header-section { | |
| position: absolute; | |
| top: 60px; | |
| left: 120px; | |
| right: 100px; | |
| z-index: 2; | |
| } | |
| .main-title { | |
| font-size: {{ title_font_size|default('64px') }}; | |
| font-weight: 800; | |
| color: {{ title_color|default('#1a1a1a') }}; | |
| margin-bottom: 10px; | |
| opacity: 0; | |
| transform: translateY(-30px); | |
| transition: all 0.8s ease-out 0.6s; | |
| } | |
| .slide.animate .main-title { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| .title-accent { | |
| width: 120px; | |
| height: 6px; | |
| background: linear-gradient(90deg, {{ accent_color_1|default('#2f6fec') }}, {{ accent_color_3|default('#8ab4f8') }}); | |
| border-radius: 3px; | |
| opacity: 0; | |
| transform: scaleX(0); | |
| transform-origin: left; | |
| transition: all 0.6s ease-out 1s; | |
| } | |
| .slide.animate .title-accent { | |
| opacity: 1; | |
| transform: scaleX(1); | |
| } | |
| /* Large image section - center left */ | |
| .image-section { | |
| position: absolute; | |
| top: 50%; | |
| left: 120px; | |
| transform: translateY(-50%); | |
| width: {{ image_width|default('920px') }}; | |
| height: {{ image_height|default('613px') }}; | |
| z-index: 2; | |
| opacity: 0; | |
| transform: translateY(-50%) translateX(-50px); | |
| transition: all 1s ease-out 1.3s; | |
| } | |
| .slide.animate .image-section { | |
| opacity: 1; | |
| transform: translateY(-50%) translateX(0); | |
| } | |
| .image-container { | |
| position: relative; | |
| width: 100%; | |
| height: 100%; | |
| border-radius: 16px; | |
| overflow: hidden; | |
| box-shadow: 0 30px 70px rgba(0, 0, 0, 0.2); | |
| } | |
| .main-image { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| } | |
| /* Gradient overlay on image bottom */ | |
| .image-gradient { | |
| position: absolute; | |
| bottom: 0; | |
| left: 0; | |
| right: 0; | |
| height: 120px; | |
| background: linear-gradient(to top, rgba(0, 0, 0, 0.5), transparent); | |
| z-index: 1; | |
| } | |
| /* Right content panel */ | |
| .content-panel { | |
| position: absolute; | |
| top: 200px; | |
| right: 100px; | |
| width: 760px; | |
| z-index: 2; | |
| } | |
| .subtitle { | |
| font-size: {{ subtitle_font_size|default('28px') }}; | |
| color: {{ subtitle_color|default('#2f6fec') }}; | |
| font-weight: 700; | |
| margin-bottom: 30px; | |
| opacity: 0; | |
| transform: translateX(30px); | |
| transition: all 0.8s ease-out 1.6s; | |
| } | |
| .slide.animate .subtitle { | |
| opacity: 1; | |
| transform: translateX(0); | |
| } | |
| /* Bullet points - vertical list */ | |
| .bullet-list { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 20px; | |
| margin-top: 20px; | |
| } | |
| .bullet-item { | |
| display: flex; | |
| align-items: flex-start; | |
| gap: 15px; | |
| padding: 18px 0; | |
| opacity: 0; | |
| transform: translateX(50px); | |
| transition: all 0.6s ease-out; | |
| } | |
| .bullet-icon { | |
| width: 10px; | |
| height: 10px; | |
| min-width: 10px; | |
| background: {{ bullet_icon_color|default('#2f6fec') }}; | |
| border-radius: 50%; | |
| margin-top: 20px; /* Pushes the bullet down for better vertical alignment */ | |
| } | |
| .bullet-text { | |
| font-size: {{ bullet_font_size|default('29px') }}; | |
| color: {{ bullet_text_color|default('#333') }}; | |
| line-height: 1.6; | |
| font-weight: 500; | |
| } | |
| .bullet-text .katex-display { | |
| margin-top: 0.5em; | |
| margin-bottom: 0.5em; | |
| } | |
| .bullet-text code { | |
| background-color: #f0f0f0; | |
| padding: 2px 6px; | |
| border-radius: 3px; | |
| font-family: 'Consolas', 'Monaco', monospace; | |
| font-size: 0.95em; | |
| color: #d63384; | |
| } | |
| .slide.animate .bullet-item:nth-child(1) { | |
| opacity: 1; | |
| transform: translateX(0); | |
| transition-delay: {{ bullet_1_delay|default('1.9s') }}; | |
| } | |
| .slide.animate .bullet-item:nth-child(2) { | |
| opacity: 1; | |
| transform: translateX(0); | |
| transition-delay: {{ bullet_2_delay|default('2.2s') }}; | |
| } | |
| .slide.animate .bullet-item:nth-child(3) { | |
| opacity: 1; | |
| transform: translateX(0); | |
| transition-delay: {{ bullet_3_delay|default('2.5s') }}; | |
| } | |
| .slide.animate .bullet-item:nth-child(4) { | |
| opacity: 1; | |
| transform: translateX(0); | |
| transition-delay: {{ bullet_4_delay|default('2.8s') }}; | |
| } | |
| .slide.animate .bullet-item:nth-child(5) { | |
| opacity: 1; | |
| transform: translateX(0); | |
| transition-delay: {{ bullet_5_delay|default('3.1s') }}; | |
| } | |
| .slide.animate .bullet-item:nth-child(6) { | |
| opacity: 1; | |
| transform: translateX(0); | |
| transition-delay: {{ bullet_6_delay|default('3.4s') }}; | |
| } | |
| /* Hover effect */ | |
| .bullet-item:hover { | |
| transform: translateX(5px); | |
| } | |
| /* Active animation */ | |
| @keyframes glow { | |
| 0%, 100% { transform: scale(1); } | |
| 50% { transform: scale(1.5); } | |
| } | |
| .bullet-item.active .bullet-icon { | |
| animation: glow 0.8s ease-in-out; | |
| } | |
| @keyframes pulse-bg { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.7; } | |
| } | |
| .bullet-item.active { | |
| animation: pulse-bg 0.8s ease-in-out; | |
| } | |
| /* Word highlighting styles - Professional continuous highlight */ | |
| .word { | |
| display: inline; | |
| transition: all 0.3s ease; | |
| padding: 2px 0px; | |
| border-radius: 3px; | |
| margin: 0; | |
| background-color: transparent; | |
| color: inherit; | |
| font-weight: inherit; | |
| position: relative; | |
| } | |
| /* Continuous professional highlight - stays until end */ | |
| /* Golden Yellow - Energetic & Positive */ | |
| .word.highlighted { | |
| background: linear-gradient(120deg, rgba(251, 191, 36, 0.25) 0%, rgba(245, 158, 11, 0.35) 100%); | |
| color: #78350f; /* Dark Amber */ | |
| font-weight: 600; | |
| padding: 2px 1px; | |
| border-radius: 4px; | |
| box-shadow: 0 2px 8px rgba(245, 158, 11, 0.2); | |
| } | |
| /* Inline code snippet (small code) - Styled like the dedicated code slide */ | |
| .inline-code-section { | |
| background: #f8f9fa; | |
| border: 2px solid #e3e8ef; | |
| border-radius: 10px; | |
| padding: 25px; | |
| margin-top: 1.5rem; | |
| box-shadow: 0 2px 8px rgba(47, 111, 236, 0.1); | |
| opacity: 0; | |
| transform: translateY(20px); | |
| transition: all 0.6s ease-out {{ code_snippet_delay }}; | |
| } | |
| .slide.animate .inline-code-section { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| .inline-code-header { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| margin-bottom: 20px; | |
| padding-bottom: 15px; | |
| border-bottom: 2px solid #e3e8ef; | |
| } | |
| .inline-code-title { | |
| font-size: 20px; | |
| font-weight: 600; | |
| color: {{ bullet_icon_color|default('#2f6fec') }}; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .inline-code-icon { | |
| width: 20px; | |
| height: 20px; | |
| } | |
| .inline-code-block { | |
| font-family: 'Fira Code', 'JetBrains Mono', 'Source Code Pro', 'Consolas', 'Monaco', 'Courier New', monospace; | |
| font-size: 19px; | |
| line-height: 1.7; | |
| color: #2d3748; | |
| } | |
| .inline-code-block .code-line { | |
| display: block; | |
| } | |
| /* === PYGMENTS SYNTAX HIGHLIGHTING FOR INLINE CODE === */ | |
| {{ pygments_css }} | |
| .corner-logo { | |
| position: absolute; | |
| bottom: 20px; | |
| right: 20px; | |
| width: 50px; | |
| height: auto; | |
| z-index: 4; | |
| opacity: 0; | |
| transition: opacity 0.5s ease-in {{ logo_delay }}; | |
| } | |
| .slide.animate .corner-logo { | |
| opacity: 1; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="slide" id="slide"> | |
| <!-- Left vertical accent bar --> | |
| <div class="vertical-accent"></div> | |
| <!-- Decorative elements on accent bar --> | |
| <div class="accent-dots"> | |
| <div class="accent-dot"></div> | |
| <div class="accent-dot"></div> | |
| <div class="accent-dot"></div> | |
| <div class="accent-dot"></div> | |
| <div class="accent-dot"></div> | |
| </div> | |
| <div class="accent-line"></div> | |
| <!-- Header with title --> | |
| <div class="header-section"> | |
| <h1 class="main-title">{{ main_title }}</h1> | |
| <div class="title-accent"></div> | |
| </div> | |
| <!-- Large image on left --> | |
| <div class="image-section"> | |
| <div class="image-container"> | |
| <img src="{{ image_path }}" alt="{{ image_alt|default('Tech Course') }}" class="main-image"> | |
| <div class="image-gradient"></div> | |
| </div> | |
| </div> | |
| <!-- Right content panel --> | |
| <div class="content-panel"> | |
| <h2 class="subtitle">{{ subtitle_text }}</h2> | |
| <div class="bullet-list"> | |
| {% if enable_word_highlighting %} | |
| <!-- Word-by-word rendering with highlighting --> | |
| {% for bullet_data in bullets_with_highlights %} | |
| {% if bullet_data.text %} | |
| {% set outer_index = loop.index0 %} | |
| <div class="bullet-item" data-bullet-index="{{ outer_index }}"> | |
| <div class="bullet-icon"></div> | |
| <div class="bullet-text"> | |
| {% for word_obj in bullet_data.word_data %} | |
| <span class="word" | |
| data-start="{{ word_obj.start }}" | |
| data-end="{{ word_obj.end }}" | |
| data-bullet="{{ outer_index }}" | |
| data-word-index="{{ loop.index0 }}" | |
| data-highlight="{{ 'true' if word_obj.highlight else 'false' }}">{{ word_obj.word | safe }}</span>{% if not loop.last %}<span> </span>{% endif %} | |
| {% endfor %} | |
| </div> | |
| </div> | |
| {% endif %} | |
| {% endfor %} | |
| {% else %} | |
| <!-- Fallback: Regular bullet rendering --> | |
| {% if bullet_point_1 %}<div class="bullet-item"><div class="bullet-icon"></div><div class="bullet-text">{{ bullet_point_1|safe }}</div></div>{% endif %} | |
| {% if bullet_point_2 %}<div class="bullet-item"><div class="bullet-icon"></div><div class="bullet-text">{{ bullet_point_2|safe }}</div></div>{% endif %} | |
| {% if bullet_point_3 %}<div class="bullet-item"><div class="bullet-icon"></div><div class="bullet-text">{{ bullet_point_3|safe }}</div></div>{% endif %} | |
| {% if bullet_point_4 %}<div class="bullet-item"><div class="bullet-icon"></div><div class="bullet-text">{{ bullet_point_4|safe }}</div></div>{% endif %} | |
| {% if bullet_point_5 %}<div class="bullet-item"><div class="bullet-icon"></div><div class="bullet-text">{{ bullet_point_5|safe }}</div></div>{% endif %} | |
| {% if bullet_point_6 %}<div class="bullet-item"><div class="bullet-icon"></div><div class="bullet-text">{{ bullet_point_6|safe }}</div></div>{% endif %} | |
| {% endif %} | |
| </div> | |
| {% if has_code_snippet %} | |
| <div class="inline-code-section"> | |
| <div class="inline-code-header"> | |
| <div class="inline-code-title"> | |
| <svg class="inline-code-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | |
| <path d="M8 6L4 10L8 14" stroke="{{ bullet_icon_color|default('#2f6fec') }}" stroke-width="2" stroke-linecap="round"/> | |
| <path d="M16 6L20 10L16 14" stroke="{{ bullet_icon_color|default('#2f6fec') }}" stroke-width="2" stroke-linecap="round"/> | |
| </svg> | |
| {{ code_snippet_label }} | |
| </div> | |
| </div> | |
| <div class="inline-code-block"> | |
| {{ code_snippet_content|safe }} | |
| </div> | |
| </div> | |
| {% endif %} | |
| </div> | |
| <!-- Bottom-right logo --> | |
| <img src="{{ logo_path }}" alt="Corner Logo" class="corner-logo"> | |
| </div> | |
| <script> | |
| // Configuration | |
| const ENABLE_WORD_HIGHLIGHTING = {{ 'true' if enable_word_highlighting else 'false' }}; | |
| const BULLET_DELAYS = {{ bullet_highlight_delays }}; | |
| const AUDIO_START_DELAY = 2500; // 2.5 seconds - matches animation delay before narration | |
| const EARLY_HIGHLIGHT_OFFSET = 0; | |
| // Auto-play animation on page load | |
| window.addEventListener('load', () => { | |
| const slide = document.getElementById('slide'); | |
| // Start animation immediately | |
| setTimeout(() => { | |
| slide.classList.add('animate'); | |
| if (ENABLE_WORD_HIGHLIGHTING) { | |
| // Word-level highlighting based on audio timings | |
| highlightWordsWithTiming(); | |
| } else { | |
| // Fallback: Bullet-level highlighting | |
| highlightBulletsOnly(); | |
| } | |
| }, 100); | |
| }); | |
| function highlightWordsWithTiming() { | |
| const wordsToHighlight = document.querySelectorAll('.word[data-highlight="true"]'); | |
| console.log(`[Word Highlighting] Found ${wordsToHighlight.length} word instances to highlight.`); | |
| wordsToHighlight.forEach((word, index) => { | |
| const startTime = parseFloat(word.dataset.start) * 1000; // Convert to ms from audio | |
| // Apply audio delay for better sync | |
| const adjustedStartTime = Math.max(0, startTime + AUDIO_START_DELAY); | |
| // Schedule highlight | |
| setTimeout(() => { | |
| word.classList.add('highlighted'); | |
| // Optional: log the highlight action for debugging | |
| // console.log(`[Highlight] "${word.textContent.trim()}" at ${(adjustedStartTime/1000).toFixed(2)}s (bullet ${word.dataset.bullet})`); | |
| }, adjustedStartTime); | |
| }); | |
| // Animate the bullet icons | |
| const bullets = document.querySelectorAll('.bullet-item'); | |
| bullets.forEach((bullet, index) => { | |
| if (index < BULLET_DELAYS.length) { | |
| setTimeout(() => { | |
| bullet.classList.add('active'); | |
| setTimeout(() => bullet.classList.remove('active'), 2000); // Animation duration | |
| }, BULLET_DELAYS[index]); | |
| } | |
| }); | |
| } | |
| function highlightBulletsOnly() { | |
| // Fallback bullet highlighting logic | |
| const bullets = document.querySelectorAll('.bullet-points li'); | |
| bullets.forEach((bullet, index) => { | |
| if (index < BULLET_DELAYS.length) { | |
| setTimeout(() => { | |
| bullet.classList.add('active'); | |
| setTimeout(() => bullet.classList.remove('active'), 1500); | |
| }, BULLET_DELAYS[index]); | |
| } | |
| }); | |
| } | |
| function setHighlightTheme(theme) { | |
| const highlightedWords = document.querySelectorAll('.word.highlighted'); | |
| highlightedWords.forEach(word => { | |
| word.classList.remove('blue-theme', 'green-theme', 'purple-theme'); | |
| if (theme !== 'default') { | |
| word.classList.add(`${theme}-theme`); | |
| } | |
| }); | |
| } | |
| </script> | |
| <script> | |
| document.addEventListener("DOMContentLoaded", function() { | |
| renderMathInElement(document.body, { | |
| delimiters: [ | |
| {left: "$$", right: "$$", display: true}, | |
| {left: "$", right: "$", display: false} | |
| ] | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> |