File size: 4,029 Bytes
cc3db62
 
f7fce54
 
 
 
 
cc3db62
 
 
f7fce54
cc3db62
f7fce54
 
 
 
 
cc3db62
 
 
f7fce54
 
 
 
 
 
 
 
 
cc3db62
 
 
f7fce54
cc3db62
 
 
f7fce54
cc3db62
f7fce54
cc3db62
 
 
 
f7fce54
 
 
 
 
 
 
 
 
 
 
 
 
cc3db62
f7fce54
cc3db62
 
f7fce54
cc3db62
f7fce54
cc3db62
 
f7fce54
 
 
 
 
 
 
 
 
 
 
cc3db62
 
 
 
f7fce54
cc3db62
 
f7fce54
 
cc3db62
 
f7fce54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
from fasthtml.common import *

def PageLayout(title: str, content: list):
    """
    The Base Layout.
    Injects PicoCSS (Styling), PrismJS (Syntax Highlight), and HTMX (Interactivity).
    """
    return Html(
        Head(
            Title(title),
            # 1. PicoCSS: Semantic minimalist framework
            Link(rel="stylesheet", href="https://cdn.jsdelivr.net/npm/@picocss/pico@latest/css/pico.min.css"),
            
            # 2. HTMX (CRITICAL FIX: Prevents page reloads)
            Script(src="https://unpkg.com/htmx.org@1.9.10"),

            # 3. PrismJS: For beautiful JSON highlighting
            Link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css"),
            Script(src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"),
            Script(src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-json.min.js"),
            
            # Custom Styles
            Style("""
                body { max-width: 900px; margin: 0 auto; padding: 20px; background-color: #11191f; }
                .json-box { border-radius: 8px; overflow: hidden; margin-top: 20px; }
                h1 { color: #00E5FF; }
                button.contrast { background-color: #00E5FF; border-color: #00E5FF; color: black; font-weight: bold; }
                .error-box { background: #3d1a1a; color: #ff8080; padding: 15px; border-radius: 8px; border: 1px solid #ff4d4d; }
            """)
        ),
        Body(
            Header(
                H1("🛡️ Structura"),
                P("The Unbreakable Data Architect", style="opacity: 0.7;"),
                style="border-bottom: 1px solid #333; padding-bottom: 20px; margin-bottom: 40px;"
            ),
            Main(*content),
            Footer(
                P("Powered by PydanticAI & Gemini 2.5", style="text-align: center; opacity: 0.5; font-size: 0.8rem; margin-top: 50px;")
            )
        )
    )

# ... (Rest of components.py remains the same)
def HeroSection():
    return Div(
        H2("Turn Chaos into Structure"),
        P("Paste messy emails, invoices, or resumes below. Get guaranteed, type-safe JSON back."),
        style="margin-bottom: 30px;"
    )

def ExtractionForm():
    """
    The Interactive Form.
    Uses HTMX (hx-post) to swap the result area without a page reload.
    """
    return Form(
        Label("Raw Input Data"),
        Textarea(
            name="text_input", 
            placeholder="e.g. Invoice #909 from CloudFix for $500...",
            rows=8,
            style="font-family: monospace; background: #0b0f13; color: white; border: 1px solid #333;"
        ),
        
        Grid(
            Label("Target Schema", 
                Select(
                    Option("Invoice Extraction", value="invoice"),
                    Option("Resume Parsing", value="resume"), 
                    Option("Generic Data", value="generic"),
                    name="schema_type"
                )
            ),
            # Empty div for grid balance or future features
            Div() 
        ),
        
        Button("Extract Structure ⚡", cls="contrast", type="submit"),
        
        # HTMX Configuration
        hx_post="/extract",
        hx_target="#result-area",
        hx_swap="innerHTML",
        hx_indicator="#loading"
    )

def LoadingIndicator():
    return Div("⚙️ AI is processing...", id="loading", cls="htmx-indicator", style="color: #00E5FF; margin-top: 10px;")

def SuccessDisplay(json_str: str):
    return Div(
        H4("✅ Validated Output", style="color: #4ade80; margin-bottom: 10px;"),
        Div(
            Pre(Code(json_str, cls="language-json")),
            cls="json-box"
        ),
        P("Schema Verified by PydanticAI", style="color: grey; font-size: 0.8rem; margin-top: 10px; text-align: right;")
    )

def ErrorDisplay(error_msg: str):
    return Div(
        H4("❌ Extraction Failed"),
        P(error_msg),
        cls="error-box"
    )