Spaces:
Running
Running
import streamlit as st | |
from pathlib import Path | |
import os | |
# Load custom CSS | |
def load_css(): | |
css_file = Path(__file__).parent / "custom.css" | |
if css_file.exists(): | |
with open(css_file) as f: | |
st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True) | |
else: | |
st.warning("Custom CSS file not found. Some styles may be missing.") | |
# Header component | |
def header(): | |
st.markdown(""" | |
<div class="main-header"> | |
<h1 class="title-text">Historical OCR Workshop</h1> | |
</div> | |
""", unsafe_allow_html=True) | |
# Create a page wrapper similar to the React component | |
def page_wrapper(content_function, current_module=1): | |
""" | |
Creates a consistent page layout with navigation | |
Args: | |
content_function: Function that renders the page content | |
current_module: Current module number (1-6) | |
""" | |
# Load custom CSS | |
load_css() | |
# Display header | |
header() | |
# Ensure session state for navigation | |
if 'current_module' not in st.session_state: | |
st.session_state.current_module = current_module | |
# Main content area with bottom padding for the nav | |
st.markdown('<div class="main-content">', unsafe_allow_html=True) | |
# Call the content function to render the module content | |
content_function() | |
# Add spacer for fixed nav | |
st.markdown('<div class="footer-spacer"></div>', unsafe_allow_html=True) | |
# Navigation | |
render_navigation(current_module) | |
st.markdown('</div>', unsafe_allow_html=True) | |
# Navigation component | |
def render_navigation(current_module): | |
# Define modules names like in React | |
modules = ['Introduction', 'Historical Context', 'Methodology', 'Case Studies', 'Interactive OCR', 'Conclusion'] | |
# Navigation container | |
st.markdown(f""" | |
<div class="nav-container"> | |
<div class="nav-buttons"> | |
{prev_button_html(current_module, modules)} | |
{next_button_html(current_module, modules)} | |
</div> | |
<div class="nav-dots"> | |
{nav_dots_html(current_module, modules)} | |
</div> | |
</div> | |
""", unsafe_allow_html=True) | |
# Previous button HTML | |
def prev_button_html(current_module, modules): | |
if current_module > 1: | |
prev_module = current_module - 1 | |
return f""" | |
<button class="prev-button" | |
onclick="document.getElementById('nav_prev_{prev_module}').click()" | |
aria-label="Go to previous module: {modules[prev_module-1]}"> | |
β Previous | |
</button> | |
""" | |
return "" | |
# Next button HTML | |
def next_button_html(current_module, modules): | |
if current_module < len(modules): | |
next_module = current_module + 1 | |
return f""" | |
<button class="next-button" | |
onclick="document.getElementById('nav_next_{next_module}').click()" | |
aria-label="Go to next module: {modules[next_module-1]}"> | |
Next β | |
</button> | |
""" | |
return "" | |
# Navigation dots HTML | |
def nav_dots_html(current_module, modules): | |
dots_html = "" | |
for i, name in enumerate(modules, 1): | |
active_class = "active" if i == current_module else "" | |
dots_html += f""" | |
<a class="nav-dot {active_class}" | |
onclick="document.getElementById('nav_dot_{i}').click()" | |
aria-current="{i == current_module}" | |
aria-label="Go to module {i}: {name}"> | |
{i} | |
</a> | |
""" | |
return dots_html | |
# Helper functions for container styles | |
def gray_container(content, padding="1.5rem"): | |
"""Renders content in a gray container with consistent styling""" | |
st.markdown(f'<div class="content-container" style="padding:{padding};">{content}</div>', unsafe_allow_html=True) | |
def blue_container(content, padding="1.5rem"): | |
"""Renders content in a blue container with consistent styling""" | |
st.markdown(f'<div class="blue-container" style="padding:{padding};">{content}</div>', unsafe_allow_html=True) | |
def yellow_container(content, padding="1.5rem"): | |
"""Renders content in a yellow container with consistent styling""" | |
st.markdown(f'<div class="yellow-container" style="padding:{padding};">{content}</div>', unsafe_allow_html=True) | |
def card_grid(cards): | |
""" | |
Renders a responsive grid of cards | |
Args: | |
cards: List of HTML strings for each card | |
""" | |
grid_html = '<div class="card-grid">' | |
for card in cards: | |
grid_html += f'<div class="card">{card}</div>' | |
grid_html += '</div>' | |
st.markdown(grid_html, unsafe_allow_html=True) | |
def module_card(number, title, description): | |
"""Creates a styled module card""" | |
return f""" | |
<div class="module-card"> | |
<div class="module-number">Module {number}</div> | |
<div class="module-title">{title}</div> | |
<p>{description}</p> | |
</div> | |
""" | |
def key_concept(content): | |
"""Renders a key concept box""" | |
st.markdown(f'<div class="key-concept">{content}</div>', unsafe_allow_html=True) | |
def research_question(content): | |
"""Renders a research question box""" | |
st.markdown(f'<div class="research-question">{content}</div>', unsafe_allow_html=True) | |
def quote(content, author=""): | |
"""Renders a quote with optional author""" | |
quote_html = f'<div class="quote-container">{content}' | |
if author: | |
quote_html += f'<br/><br/><span style="font-size:0.9rem; text-align:right; display:block;">β {author}</span>' | |
quote_html += '</div>' | |
st.markdown(quote_html, unsafe_allow_html=True) | |
def tool_container(content): | |
"""Renders content in a tool container""" | |
st.markdown(f'<div class="tool-container">{content}</div>', unsafe_allow_html=True) | |
def upload_container(content): | |
"""Renders content in an upload container""" | |
st.markdown(f'<div class="upload-container">{content}</div>', unsafe_allow_html=True) |