books / app.py
vinay-jose's picture
Update app.py
b3ee970 verified
from fasthtml.common import *
from monsterui.all import *
import frontmatter
import pathlib
app, rt = fast_app(hdrs=Theme.green.headers())
def load_book(file_path):
"""Load and parse a book's markdown file"""
with open(file_path) as f:
post = frontmatter.load(f)
return post
def create_book_page(post):
"""Create the detailed book page using our template"""
metadata = post.metadata
return Container(
DivCentered(
Card(
DivCentered(
H1(metadata["title"],
cls="text-transparent bg-clip-text bg-gradient-to-r from-primary via-muted to-primary transition-all duration-1000 hover:scale-105",
style="font-size: 2.5rem; font-weight: 700; -webkit-text-stroke: 0.5px rgba(0, 0, 0, 0.7);"),
H2(metadata["author"], cls=(TextT.muted)),
A(
Img(
src=metadata['cover_img_url'],
cls="rounded-lg hover:scale-105 shadow-lg transition-all duration-1000",
style="width:300px"
),
cls="rounded-lg overflow-hidden",
href=metadata['book_url']
),
cls="text-center space-y-6"
),
DivCentered(
Section(
DivHStacked(
Label(f"πŸ“… {metadata['date'].strftime('%B %d, %Y')}", cls=LabelT.secondary),
Label(f"πŸ“š {metadata['genre']}", cls=LabelT.secondary),
cls="space-x-2"
),
cls=SectionT.xs
),
cls="mb-6"
),
Div(
render_md(
post.content,
class_map={
'h3': f'text-transparent bg-clip-text bg-gradient-to-r from-primary to-secondary {TextT.xl} {TextT.bold} mb-6 mt-8',
'ul': f'{ListT.disc} space-y-4 mb-8',
'li': f'{TextT.lg} {TextT.normal} leading-relaxed',
'ul ul': f'{ListT.circle} ml-8 mt-4',
'ul ul li': f'{TextT.normal} leading-relaxed',
'p': f'{TextT.lg} {TextT.normal} mb-4',
'img': 'rounded-lg shadow-md hover:shadow-xl transition-shadow duration-200',
'*[@class="gallery"]': 'flex flex-row items-center justify-center gap-8'
}
),
cls = 'space-y-6'
),
cls=CardT.default
)
),
cls=(ContainerT.lg, 'p-8'),
style="position: relative; overflow: hidden;",
)
def create_book_card(metadata, filename):
"""Create a card for the book listing"""
return A(
Card(
DivCentered(
Img(
src=metadata['cover_img_url'],
cls="rounded-lg shadow-md hover:scale-105 transition-all duration-300",
style="width:200px"
),
H3(metadata['title'],
cls="text-transparent bg-clip-text bg-gradient-to-r from-primary to-secondary"),
P(metadata['author'], cls=TextT.muted),
DivHStacked(
Label(metadata['genre'], cls=LabelT.secondary),
Label(metadata['date'].strftime('%B %d, %Y'), cls=LabelT.secondary),
cls="mt-4 space-x-2"
),
cls="space-y-4 p-4"
),
cls=(CardT.hover, "transition-all duration-300 hover:shadow-xl")
),
href=f"/book/{filename}"
)
@rt
def index():
"""Homepage with grid of book cards"""
# Load all book files
books_path = pathlib.Path('books')
book_files = list(books_path.glob('*.md'))
# Create cards for each book
book_cards = []
for file in book_files:
post = load_book(file)
book_cards.append(create_book_card(post.metadata, file.stem))
return Container(
Grid(*book_cards,
cols_sm=1, cols_md=2, cols_lg=3, cols_xl=4,
gap=6),
cls=(ContainerT.xl, 'p-8')
)
@rt("/book/{filename}")
def get(filename: str):
"""Individual book page"""
try:
books_path = pathlib.Path('books')
book_file = books_path / f"{filename}.md"
if not book_file.exists():
raise FileNotFoundError
post = load_book(book_file)
return create_book_page(post)
except (FileNotFoundError, ValueError) as e:
return Container(
DivCentered(
H1("Book Not Found", cls=TextT.error),
P("Sorry, we couldn't find the book you're looking for."),
A("Return to Library", href="/", cls=ButtonT.primary),
cls="space-y-6 py-12"
)
)
serve()