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; | |
| } | |
| /* Semicircle layers - initially off-screen */ | |
| .circle-large { | |
| position: absolute; | |
| top: 50%; | |
| left: 0; | |
| width: 950px; | |
| height: 1240px; | |
| background: {{ circle_large_color }}; | |
| border-radius: 50%; | |
| transform: translate(-135%, -50%); | |
| z-index: 1; | |
| transition: transform 1s cubic-bezier(0.34, 1.56, 0.64, 1); | |
| } | |
| .circle-small { | |
| position: absolute; | |
| top: 50%; | |
| left: 0; | |
| width: 830px; | |
| height: 1130px; | |
| background: {{ circle_small_color }}; | |
| border-radius: 50%; | |
| transform: translate(-140%, -50%); | |
| z-index: 2; | |
| transition: transform 1s cubic-bezier(0.34, 1.56, 0.64, 1) 0.2s; | |
| } | |
| /* Animated state */ | |
| .slide.animate .circle-large { | |
| transform: translate(-35%, -50%); | |
| } | |
| .slide.animate .circle-small { | |
| transform: translate(-40%, -50%); | |
| } | |
| /* Title - initially hidden */ | |
| .title-container { | |
| position: absolute; | |
| top: 50%; | |
| left: 250px; | |
| transform: translate(-50%, -50%); | |
| text-align: center; | |
| z-index: 3; | |
| width: 540px; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| opacity: 0; | |
| transition: opacity 0.8s ease-in 0.5s; | |
| } | |
| .slide.animate .title-container { | |
| opacity: 1; | |
| } | |
| .title { | |
| font-size: clamp(1.2rem, 4vh, 5rem); | |
| font-weight: 700; | |
| color: #000000; | |
| line-height: 1.2; | |
| word-break: break-word; | |
| text-align: center; | |
| max-width: 90%; | |
| } | |
| /* Content Section */ | |
| .content { | |
| position: absolute; | |
| top: 50%; | |
| left: 672px; | |
| transform: translateY(-50%); | |
| z-index: 3; | |
| width: 1150px; | |
| color: #222; | |
| max-height: 800px; | |
| overflow: hidden; | |
| } | |
| .subtitle { | |
| font-size: 34px; | |
| color: #000000; | |
| margin-bottom: 1.5rem; | |
| opacity: 0; | |
| transform: translateX(20px); | |
| transition: all 0.6s ease-out 1s; | |
| } | |
| .slide.animate .subtitle { | |
| opacity: 1; | |
| transform: translateX(0); | |
| } | |
| .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_color }}; | |
| border-radius: 50%; | |
| margin-top: 20px; /* Pushes the bullet down for better vertical alignment */ | |
| } | |
| .bullet-text { | |
| font-size: 28px; /* Reduced font size for more space */ | |
| color: #000000; | |
| line-height: 1.6; | |
| font-weight: 500; | |
| width: 100%; | |
| max-width: 1050px; | |
| } | |
| /* Individual bullet timing */ | |
| .slide.animate .bullet-item:nth-child(1) { | |
| opacity: 1; | |
| transform: translateX(0); | |
| transition-delay: {{ bullet_1_delay }}; | |
| } | |
| .slide.animate .bullet-item:nth-child(2) { | |
| opacity: 1; | |
| transform: translateX(0); | |
| transition-delay: {{ bullet_2_delay }}; | |
| } | |
| .slide.animate .bullet-item:nth-child(3) { | |
| opacity: 1; | |
| transform: translateX(0); | |
| transition-delay: {{ bullet_3_delay }}; | |
| } | |
| .slide.animate .bullet-item:nth-child(4) { | |
| opacity: 1; | |
| transform: translateX(0); | |
| transition-delay: {{ bullet_4_delay }}; | |
| } | |
| .slide.animate .bullet-item:nth-child(5) { | |
| opacity: 1; | |
| transform: translateX(0); | |
| transition-delay: {{ bullet_5_delay }}; | |
| } | |
| .slide.animate .bullet-item:nth-child(6) { | |
| opacity: 1; | |
| transform: translateX(0); | |
| transition-delay: {{ bullet_6_delay }}; | |
| } | |
| /* Inline code within bullets */ | |
| .inline-code-span { | |
| background-color: #f0f0f0; | |
| padding: 2px 6px; | |
| border-radius: 3px; | |
| font-family: 'Consolas', 'Monaco', monospace; | |
| font-size: 0.95em; | |
| color: #d63384; | |
| } | |
| .bullet-content code { | |
| background-color: #f0f0f0; | |
| padding: 2px 6px; | |
| border-radius: 3px; | |
| font-family: 'Consolas', 'Monaco', monospace; | |
| font-size: 0.95em; | |
| color: #d63384; | |
| } | |
| /* 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_color }}; | |
| 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; | |
| } | |
| /* 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: 500; /* Matched font-weight to prevent width change */ | |
| padding: 3px 2px; /* Adjusted padding for a better visual */ | |
| border-radius: 4px; | |
| box-shadow: 0 2px 8px rgba(245, 158, 11, 0.2); | |
| } | |
| /* 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; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="slide" id="slide"> | |
| <!-- Semicircle shapes --> | |
| <div class="circle-large"></div> | |
| <div class="circle-small"></div> | |
| <!-- Title inside semicircle --> | |
| <div class="title-container"> | |
| <h1 class="title">{{ main_title }}</h1> | |
| </div> | |
| <!-- Right side content --> | |
| <div class="content"> | |
| <p class="subtitle">{{ subtitle_text }}</p> | |
| <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_color }}" stroke-width="2" stroke-linecap="round"/> | |
| <path d="M16 6L20 10L16 14" stroke="{{ bullet_color }}" 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 | |
| // 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> | |