Spaces:
Sleeping
Sleeping
| 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}" | |
| ) | |
| 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') | |
| ) | |
| 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() | |