Astro site with e-book reader for classic electronics notebooks. 15 Mims notebooks + 1 Ugly's Electrical Reference, served via Docker/Caddy at mims.l.supported.systems. PDFs tracked with git-lfs.
182 lines
7.7 KiB
Plaintext
182 lines
7.7 KiB
Plaintext
---
|
|
import Layout from '@/layouts/Layout.astro';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import EBookReader from '@/components/EBookReader';
|
|
import { getCollection, type CollectionEntry } from 'astro:content';
|
|
|
|
export async function getStaticPaths() {
|
|
const books = await getCollection('books');
|
|
return books
|
|
.filter(book => book.data.collection === 'uglys')
|
|
.map(book => ({
|
|
params: { slug: book.slug.split('/').pop() },
|
|
props: { book }
|
|
}));
|
|
}
|
|
|
|
interface Props {
|
|
book: CollectionEntry<'books'>;
|
|
}
|
|
|
|
const { book } = Astro.props;
|
|
const { title, shortTitle, description, topics, localPdf, coverImage, year, archiveOrgUrl, formats } = book.data;
|
|
|
|
// Get all uglys books for navigation
|
|
const allBooks = await getCollection('books');
|
|
const uglysBooks = allBooks
|
|
.filter(b => b.data.collection === 'uglys')
|
|
.sort((a, b) => a.data.sortOrder - b.data.sortOrder);
|
|
|
|
const currentIndex = uglysBooks.findIndex(b => b.slug === book.slug);
|
|
const prevBook = currentIndex > 0 ? uglysBooks[currentIndex - 1] : null;
|
|
const nextBook = currentIndex < uglysBooks.length - 1 ? uglysBooks[currentIndex + 1] : null;
|
|
---
|
|
|
|
<Layout title={shortTitle} description={description}>
|
|
<div class="space-y-8">
|
|
<!-- Breadcrumb -->
|
|
<div class="flex items-center gap-2 text-sm text-muted-foreground">
|
|
<a href="/" class="hover:text-foreground transition-colors">Home</a>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<polyline points="9 18 15 12 9 6"/>
|
|
</svg>
|
|
<a href="/uglys" class="hover:text-foreground transition-colors">Ugly's Collection</a>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<polyline points="9 18 15 12 9 6"/>
|
|
</svg>
|
|
<span class="text-foreground">{shortTitle}</span>
|
|
</div>
|
|
|
|
<!-- Compact Header -->
|
|
<div class="flex flex-col md:flex-row md:items-start gap-6">
|
|
<!-- Book Info -->
|
|
<div class="flex-1 space-y-4">
|
|
<div>
|
|
{year && (
|
|
<div class="inline-flex items-center gap-2 text-sm text-muted-foreground mb-2">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<rect width="18" height="18" x="3" y="4" rx="2" ry="2"/>
|
|
<line x1="16" x2="16" y1="2" y2="6"/>
|
|
<line x1="8" x2="8" y1="2" y2="6"/>
|
|
<line x1="3" x2="21" y1="10" y2="10"/>
|
|
</svg>
|
|
{year} Edition
|
|
</div>
|
|
)}
|
|
<h1 class="text-2xl md:text-3xl font-bold title-accent">{title}</h1>
|
|
<p class="text-muted-foreground mt-2">{description}</p>
|
|
</div>
|
|
|
|
<div class="flex flex-wrap gap-2">
|
|
{topics.slice(0, 5).map((topic) => (
|
|
<Badge variant="secondary" className="text-xs">
|
|
{topic.replace(/-/g, ' ')}
|
|
</Badge>
|
|
))}
|
|
{topics.length > 5 && (
|
|
<Badge variant="outline" className="text-xs">
|
|
+{topics.length - 5} more
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="flex flex-row md:flex-col gap-2">
|
|
<a
|
|
href={localPdf}
|
|
download
|
|
class="inline-flex items-center justify-center gap-2 px-4 py-2.5 bg-primary text-primary-foreground rounded-lg font-medium hover:bg-primary/90 transition-colors text-sm"
|
|
>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
|
<polyline points="7 10 12 15 17 10"/>
|
|
<line x1="12" x2="12" y1="15" y2="3"/>
|
|
</svg>
|
|
<span>Download</span>
|
|
</a>
|
|
|
|
{archiveOrgUrl && (
|
|
<a
|
|
href={archiveOrgUrl}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
class="inline-flex items-center justify-center gap-2 px-4 py-2.5 border border-border rounded-lg font-medium hover:bg-muted transition-colors text-sm"
|
|
>
|
|
<span>Archive.org</span>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
|
|
<polyline points="15 3 21 3 21 9"/>
|
|
<line x1="10" x2="21" y1="14" y2="3"/>
|
|
</svg>
|
|
</a>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- E-Book Reader -->
|
|
<div class="mt-6">
|
|
<EBookReader
|
|
pdfUrl={localPdf}
|
|
title={shortTitle}
|
|
coverImage={coverImage}
|
|
client:load
|
|
/>
|
|
</div>
|
|
|
|
<!-- Navigation -->
|
|
<div class="flex items-center justify-between pt-8 border-t border-border">
|
|
{prevBook ? (
|
|
<a
|
|
href={`/uglys/${prevBook.slug.split('/').pop()}`}
|
|
class="group flex items-center gap-3 p-3 -m-3 rounded-lg hover:bg-muted/50 transition-colors"
|
|
>
|
|
<div class="w-8 h-8 rounded-full bg-muted flex items-center justify-center group-hover:bg-primary/10 transition-colors">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground group-hover:text-primary transition-colors">
|
|
<polyline points="15 18 9 12 15 6"/>
|
|
</svg>
|
|
</div>
|
|
<div class="text-left">
|
|
<div class="text-xs text-muted-foreground">Previous</div>
|
|
<div class="text-sm font-medium text-foreground hidden sm:block">{prevBook.data.shortTitle}</div>
|
|
</div>
|
|
</a>
|
|
) : (
|
|
<div></div>
|
|
)}
|
|
|
|
<a
|
|
href="/uglys"
|
|
class="flex items-center gap-2 px-4 py-2 rounded-lg border border-border hover:bg-muted transition-colors text-sm"
|
|
>
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<rect width="7" height="7" x="3" y="3" rx="1"/>
|
|
<rect width="7" height="7" x="14" y="3" rx="1"/>
|
|
<rect width="7" height="7" x="14" y="14" rx="1"/>
|
|
<rect width="7" height="7" x="3" y="14" rx="1"/>
|
|
</svg>
|
|
<span class="hidden sm:inline">All References</span>
|
|
</a>
|
|
|
|
{nextBook ? (
|
|
<a
|
|
href={`/uglys/${nextBook.slug.split('/').pop()}`}
|
|
class="group flex items-center gap-3 p-3 -m-3 rounded-lg hover:bg-muted/50 transition-colors"
|
|
>
|
|
<div class="text-right">
|
|
<div class="text-xs text-muted-foreground">Next</div>
|
|
<div class="text-sm font-medium text-foreground hidden sm:block">{nextBook.data.shortTitle}</div>
|
|
</div>
|
|
<div class="w-8 h-8 rounded-full bg-muted flex items-center justify-center group-hover:bg-primary/10 transition-colors">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground group-hover:text-primary transition-colors">
|
|
<polyline points="9 18 15 12 9 6"/>
|
|
</svg>
|
|
</div>
|
|
</a>
|
|
) : (
|
|
<div></div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</Layout>
|