Implement Embedded Slide Presentations in Markdown Render Pipeline

Summary

Implemented a comprehensive embedded slide presentation system that allows Reveal.js presentations to be embedded directly within markdown documents using custom code block syntax. The system supports configuration options, theme customization, and seamless integration with the existing markdown rendering pipeline.

Why Care

This feature transforms static documentation into interactive, engaging content by allowing slide presentations to be embedded anywhere in markdown files. It's particularly valuable for technical specifications, educational content, and documentation that benefits from visual presentation formats. The system maintains the simplicity of markdown while adding rich presentation capabilities without requiring separate slide authoring tools.

Implementation

Changes Made

Core Components Added

  • /src/components/SlidesEmbed.astro - Main embed component that renders iframe containers for slide presentations
  • /src/pages/slides/embed/[...slug].astro - Dynamic route handler for embedded presentation rendering
  • /src/generated-content/slides/css-animation-systems.md - Comprehensive CSS animation systems presentation content

Markdown Processing Pipeline Integration

  • /src/components/markdown/AstroMarkdown.astro (lines 980-1028) - Added 'slides' case to code block processing with configuration parsing and component rendering

Content Collection Configuration

  • /src/content.config.ts (lines 58-66) - Enhanced slides collection with proper slug generation from filenames and frontmatter

Specification Documentation

  • /Maintain-Embeddable-Slides.md - Complete technical specification for the embedded slides system
  • /src/generated-content/specs/Maintain-a-CSS-Animation-System.md (lines 32-39) - Integration example demonstrating the embed syntax

Slide Content Files Enhanced

  • /src/generated-content/slides/docker-intro.md - Added slug: docker-intro
  • /src/generated-content/slides/git-basics.md - Added slug: git-basics
  • /src/generated-content/slides/sample-presentation.md - Added slug: sample-presentation
  • /src/generated-content/slides/typescript-fundamentals.md - Added slug: typescript-fundamentals

Technical Details

Custom Markdown Syntax

The system introduces a new code block type slides with YAML-style configuration:
markdown
```slides
theme: black
transition: slide
controls: true
progress: true

- [[slides/css-animation-systems.md|CSS Animation Systems]]
text

### Architecture Flow

```mermaid
graph TD
    A[Markdown Document] --> B[AstroMarkdown.astro Parser]
    B --> C{Code Block Type?}
    C -->|slides| D[Parse Configuration]
    D --> E[Extract Slide Links]
    E --> F[SlidesEmbed Component]
    F --> G[Generate Embed URL]
    G --> H[Render iframe]
    H --> I[/slides/embed/slug Route]
    I --> J[Load Slide Content]
    J --> K[MarkdownSlideDeck Layout]
    K --> L[Reveal.js Presentation]

Configuration Parser Implementation

javascript
// /src/components/markdown/AstroMarkdown.astro (lines 985-1020)
case 'slides':
  const lines = value.trim().split('\n');
  const config = {};
  const slides = [];
  let configSection = true;
  
  for (const line of lines) {
    const trimmedLine = line.trim();
    if (!trimmedLine) continue;
    
    if (trimmedLine.startsWith('- [[')) {
      configSection = false;
      const linkMatch = trimmedLine.match(/\[\[(.*?)\|(.*?)\]\]/);
      if (linkMatch) {
        slides.push({
          path: linkMatch[1],
          title: linkMatch[2]
        });
      }
    } else if (configSection) {
      // Parse both "key: value" and "key=value" syntax
      const colonMatch = trimmedLine.match(/^(\w+):\s*(.+)$/);
      const equalsMatch = trimmedLine.match(/^(\w+)=(.+)$/);
      
      if (colonMatch) {
        config[colonMatch[1]] = colonMatch[2].trim();
      } else if (equalsMatch) {
        config[equalsMatch[1]] = equalsMatch[2].trim();
      }
    }
  }
  
  return <SlidesEmbed slides={slides} config={config} />;

URL Structure and Routing

  • Embed URLs: /slides/embed/{slug} with query parameters for configuration
  • Direct URLs: /slides/markdown/{slug} for standalone presentations
  • Path-based parameters: Uses Astro's [...slug].astro dynamic routing

Filesystem vs Collections Approach

Initially attempted to use Astro content collections but encountered slug generation issues. Resolved by switching to direct filesystem reading:
javascript
// /src/pages/slides/embed/[...slug].astro (lines 41-69)
try {
  const slidesDir = path.join(process.cwd(), 'src', 'generated-content', 'slides');
  const fullPath = path.join(slidesDir, `${slidePath}.md`);
  
  const fileContent = await readFile(fullPath, 'utf-8');
  
  // Parse frontmatter and content
  const frontmatterMatch = fileContent.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
  
  if (frontmatterMatch) {
    const frontmatterText = frontmatterMatch[1];
    combinedMarkdown = frontmatterMatch[2];
    
    const titleMatch = frontmatterText.match(/title:\s*(.+)/);
    if (titleMatch) {
      title = titleMatch[1].trim().replace(/['"]/g, '');
    }
  }
} catch (error) {
  combinedMarkdown = `# Slide Not Found\n\nThe requested slide "${slidePath}" could not be loaded.`;
}

Integration Points

Markdown Processing Chain

The slides processor integrates with the existing markdown rendering pipeline in AstroMarkdown.astro, sitting alongside other custom code block processors like mermaid diagrams and code galleries.

Component Architecture

  • SlidesEmbed.astro: Handles iframe generation and URL construction
  • MarkdownSlideDeck.astro: Existing layout component reused for consistency
  • Reveal.js Integration: Uses CDN-hosted Reveal.js for presentation functionality

Content Management

  • Slides stored in /src/generated-content/slides/ directory
  • Each slide file requires frontmatter with title and slug fields
  • Uses standard markdown with --- separators for slide breaks

Configuration System

Supports both inline and block configuration styles:
markdown
# Block style
```slides
theme: dark
transition: fade

- [[slides/example.md|Example]]

Inline style

text

## Documentation

### Usage Examples

**Basic Embed:**
```markdown
```slides
- [[slides/my-presentation.md|My Presentation]]
text

**With Configuration:**
```markdown
```slides
theme: black
transition: slide
controls: true
progress: true

- [[slides/advanced-topic.md|Advanced Topic]]
text

### API Integration

The embed system exposes configuration through URL parameters:
- `theme`: Reveal.js theme name
- `transition`: Slide transition style
- `controls`: Show/hide navigation controls
- `progress`: Show/hide progress bar
- `autoSlide`: Auto-advance timing (milliseconds)
- `loop`: Enable/disable presentation looping

### File Structure
src/ ├── components/ │ ├── SlidesEmbed.astro │ └── markdown/ │ └── AstroMarkdown.astro (modified) ├── pages/slides/ │ └── embed/ │ └── [...slug].astro └── generated-content/slides/ ├── css-animation-systems.md ├── docker-intro.md ├── git-basics.md └── typescript-fundamentals.md
text

### Performance Considerations

- Iframe lazy loading implemented with `loading="lazy"`
- Static file serving for slide content
- Minimal JavaScript footprint using Reveal.js CDN
- Server-side rendering for embed containers

### Browser Compatibility

- Modern browsers supporting iframe and ES6 features
- Responsive design with mobile-specific height adjustments
- Keyboard navigation support through Reveal.js
- Print/PDF export capabilities maintained

This implementation provides a robust, extensible foundation for embedded presentations while maintaining the simplicity and flexibility that makes markdown-based authoring effective.