view.html
html
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
  {{template "head"}}
  {{with gallery.ViewedDeck}}
  <title>{{.Title}} - SkyDeck</title>
  <meta name="description" content="{{.Description}}">
  <meta property="og:title" content="{{.Title}} - SkyDeck">
  <meta property="og:description" content="{{.Description}}">
  {{if .CoverImage}}<meta property="og:image" content="{{.CoverImage}}">{{end}}
  {{else}}
  <title>Deck Not Found - SkyDeck</title>
  {{end}}
  <!-- View page styles -->
  <link rel="stylesheet" href="/public/styles/view.css" />
</head>
<body class="min-h-screen bg-base-200">
  {{template "navbar"}}
  {{with gallery.ViewedDeck}}
  {{template "deck-header" .}}
  <!-- Slides viewer -->
  <div class="container mx-auto max-w-5xl p-4">
    <div id="slides-viewer" class="space-y-8">
      <!-- Slides rendered by JS -->
    </div>
  </div>
  <script>
    const deckContent = "{{.Content}}";
    const deckId = "{{.ID}}";
    document.addEventListener('DOMContentLoaded', () => {
      const content = typeof deckContent === 'string' ? JSON.parse(deckContent) : deckContent;
      // Apply theme from deck to slide containers
      const theme = content.theme || 'dark';
      renderSlides(content, theme);
      hljs.highlightAll();
    });
    function renderSlides(content, theme) {
      const container = document.getElementById('slides-viewer');
      container.innerHTML = content.slides.map((slide, i) => `
        <div class="slide-container bg-base-100 rounded-xl shadow-2xl overflow-hidden" data-theme="${theme}">
          <div class="p-8 h-full">
            ${slide.blocks.map(block => renderBlock(block)).join('')}
          </div>
        </div>
        <div class="text-center text-base-content/40 text-sm">Slide ${i + 1} of ${content.slides.length}</div>
      `).join('');
    }
    function renderBlock(block) {
      switch (block.type) {
        case 'heading':
          const sizes = { 1: 'text-4xl', 2: 'text-3xl', 3: 'text-2xl' };
          return `<h${block.level} class="${sizes[block.level] || sizes[1]} font-bold text-base-content mb-4">${escapeHtml(block.content)}</h${block.level}>`;
        case 'text':
          return `<p class="text-lg text-base-content/80 mb-4">${escapeHtml(block.content)}</p>`;
        case 'code':
          return `<pre class="rounded-lg overflow-hidden mb-4"><code class="language-${block.language || 'javascript'}">${escapeHtml(block.content)}</code></pre>`;
        case 'image':
          return block.url ? `
            <figure class="mb-4">
              <img src="${escapeHtml(block.url)}" alt="${escapeHtml(block.caption || '')}" class="max-w-full rounded-lg mx-auto" />
              ${block.caption ? `<figcaption class="text-center text-sm text-base-content/50 mt-2">${escapeHtml(block.caption)}</figcaption>` : ''}
            </figure>
          ` : '';
        case 'list':
          const tag = block.style === 'number' ? 'ol' : 'ul';
          return `<${tag} class="list-${block.style === 'number' ? 'decimal' : 'disc'} list-inside text-lg text-base-content/80 mb-4 space-y-1">
            ${(block.items || []).map(item => `<li>${escapeHtml(item)}</li>`).join('')}
          </${tag}>`;
        case 'quote':
          return `
            <blockquote class="border-l-4 border-primary pl-4 py-2 mb-4">
              <p class="text-lg italic text-base-content/80">${escapeHtml(block.content)}</p>
              ${block.author ? `<cite class="text-sm text-base-content/50 block mt-2">— ${escapeHtml(block.author)}</cite>` : ''}
            </blockquote>
          `;
        case 'divider':
          return `<hr class="border-base-content/10 my-6" />`;
        default:
          return '';
      }
    }
    function escapeHtml(text) {
      if (!text) return '';
      const div = document.createElement('div');
      div.textContent = text;
      return div.innerHTML;
    }
    async function forkDeck() {
      try {
        const resp = await fetch(`/api/gallery/${deckId}/fork`, { method: 'POST' });
        if (!resp.ok) throw new Error('Fork failed');
        const fork = await resp.json();
        window.location.href = `/editor/${fork.ID}`;
      } catch (e) {
        alert('Failed to fork deck');
      }
    }
  </script>
  {{else}}
  <!-- Not found -->
  <div class="min-h-screen flex items-center justify-center">
    <div class="text-center">
      <div class="text-6xl mb-4">📊</div>
      <h1 class="text-2xl font-bold mb-2">Deck Not Found</h1>
      <p class="text-base-content/60 mb-4">This deck doesn't exist or isn't published.</p>
      <a href="/" class="btn btn-primary">Back to Gallery</a>
    </div>
  </div>
  {{end}}
</body>
</html>
72f0edf

Add rich text formatting and block styling to editor

Connor McCutcheon
@connor
0 stars

Pitch decks to help promote Skyscape Apps

Sign in to comment Sign In