Skip to content

Commit f7fce54

Browse files
authored
HTMX fix
A critical fix to prevent page reload
1 parent 17b209e commit f7fce54

1 file changed

Lines changed: 72 additions & 39 deletions

File tree

app/components.py

Lines changed: 72 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,106 @@
11
from fasthtml.common import *
22

3-
def Layout(title, content):
4-
"""Global Page Layout with PicoCSS"""
3+
def PageLayout(title: str, content: list):
4+
"""
5+
The Base Layout.
6+
Injects PicoCSS (Styling), PrismJS (Syntax Highlight), and HTMX (Interactivity).
7+
"""
58
return Html(
69
Head(
710
Title(title),
8-
# PicoCSS: Minimalist Semantic CSS
11+
# 1. PicoCSS: Semantic minimalist framework
912
Link(rel="stylesheet", href="https://cdn.jsdelivr.net/npm/@picocss/pico@latest/css/pico.min.css"),
10-
# PrismJS: For beautiful JSON Syntax Highlighting
13+
14+
# 2. HTMX (CRITICAL FIX: Prevents page reloads)
15+
Script(src="https://unpkg.com/htmx.org@1.9.10"),
16+
17+
# 3. PrismJS: For beautiful JSON highlighting
1118
Link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css"),
1219
Script(src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"),
1320
Script(src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-json.min.js"),
14-
Style("body { max-width: 800px; margin: 0 auto; padding: 20px; }")
21+
22+
# Custom Styles
23+
Style("""
24+
body { max-width: 900px; margin: 0 auto; padding: 20px; background-color: #11191f; }
25+
.json-box { border-radius: 8px; overflow: hidden; margin-top: 20px; }
26+
h1 { color: #00E5FF; }
27+
button.contrast { background-color: #00E5FF; border-color: #00E5FF; color: black; font-weight: bold; }
28+
.error-box { background: #3d1a1a; color: #ff8080; padding: 15px; border-radius: 8px; border: 1px solid #ff4d4d; }
29+
""")
1530
),
1631
Body(
1732
Header(
18-
H1("🛡️ Structura", style="color: #00E5FF; margin-bottom: 0;"),
33+
H1("🛡️ Structura"),
1934
P("The Unbreakable Data Architect", style="opacity: 0.7;"),
2035
style="border-bottom: 1px solid #333; padding-bottom: 20px; margin-bottom: 40px;"
2136
),
22-
Main(content),
37+
Main(*content),
2338
Footer(
24-
P("Powered by PydanticAI & Gemini 2.5", style="text-align: center; opacity: 0.5; font-size: 0.8rem;")
39+
P("Powered by PydanticAI & Gemini 2.5", style="text-align: center; opacity: 0.5; font-size: 0.8rem; margin-top: 50px;")
2540
)
2641
)
2742
)
2843

29-
def InputForm():
30-
"""The HTMX-powered Input Form"""
44+
# ... (Rest of components.py remains the same)
45+
def HeroSection():
46+
return Div(
47+
H2("Turn Chaos into Structure"),
48+
P("Paste messy emails, invoices, or resumes below. Get guaranteed, type-safe JSON back."),
49+
style="margin-bottom: 30px;"
50+
)
51+
52+
def ExtractionForm():
53+
"""
54+
The Interactive Form.
55+
Uses HTMX (hx-post) to swap the result area without a page reload.
56+
"""
3157
return Form(
32-
Label("Raw Input Data", cls="label"),
58+
Label("Raw Input Data"),
3359
Textarea(
3460
name="text_input",
35-
placeholder="Paste messy text here (Email, Invoice, Job Post)...",
61+
placeholder="e.g. Invoice #909 from CloudFix for $500...",
3662
rows=8,
37-
style="font-family: monospace;"
63+
style="font-family: monospace; background: #0b0f13; color: white; border: 1px solid #333;"
3864
),
3965

40-
Label("Target Schema", cls="label"),
41-
Select(
42-
Option("Invoice Extraction", value="invoice"),
43-
Option("Resume Parsing", value="resume"),
44-
Option("Email Summary", value="email"),
45-
name="schema_type"
66+
Grid(
67+
Label("Target Schema",
68+
Select(
69+
Option("Invoice Extraction", value="invoice"),
70+
Option("Resume Parsing", value="resume"),
71+
Option("Generic Data", value="generic"),
72+
name="schema_type"
73+
)
74+
),
75+
# Empty div for grid balance or future features
76+
Div()
4677
),
4778

4879
Button("Extract Structure ⚡", cls="contrast", type="submit"),
4980

50-
# HTMX Magic: Swaps the result div without reloading page
81+
# HTMX Configuration
5182
hx_post="/extract",
5283
hx_target="#result-area",
53-
hx_swap="innerHTML"
84+
hx_swap="innerHTML",
85+
hx_indicator="#loading"
5486
)
5587

56-
def ResultDisplay(json_data=None, error=None):
57-
"""Renders the output or error state"""
58-
if error:
59-
return Div(
60-
H4("❌ Extraction Failed"),
61-
P(error),
62-
style="background: #3d1a1a; color: #ff8080; padding: 15px; border-radius: 8px; border: 1px solid #ff4d4d;"
63-
)
64-
65-
if json_data:
66-
return Div(
67-
H4("✅ Validated JSON Output"),
68-
# Pre/Code blocks triggered PrismJS highlighting
69-
Pre(Code(json_data, cls="language-json")),
70-
P("Schema validation passed.", style="color: #00E5FF; font-size: 0.8rem; margin-top: 10px;")
71-
)
72-
73-
return Div(id="result-area") # Empty placeholder
88+
def LoadingIndicator():
89+
return Div("⚙️ AI is processing...", id="loading", cls="htmx-indicator", style="color: #00E5FF; margin-top: 10px;")
90+
91+
def SuccessDisplay(json_str: str):
92+
return Div(
93+
H4("✅ Validated Output", style="color: #4ade80; margin-bottom: 10px;"),
94+
Div(
95+
Pre(Code(json_str, cls="language-json")),
96+
cls="json-box"
97+
),
98+
P("Schema Verified by PydanticAI", style="color: grey; font-size: 0.8rem; margin-top: 10px; text-align: right;")
99+
)
100+
101+
def ErrorDisplay(error_msg: str):
102+
return Div(
103+
H4("❌ Extraction Failed"),
104+
P(error_msg),
105+
cls="error-box"
106+
)

0 commit comments

Comments
 (0)