<!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>
Add rich text formatting and block styling to editor
Pitch decks to help promote Skyscape Apps