Nischal Subedi
commited on
Commit
·
80f99f6
1
Parent(s):
f743e73
UI v34
Browse files
app.py
CHANGED
@@ -249,7 +249,7 @@ Answer:"""
|
|
249 |
if not api_key or not api_key.strip() or not api_key.startswith("sk-"):
|
250 |
return "<div class='error-message'><span class='error-icon'>⚠️</span>Please provide a valid OpenAI API key (starting with 'sk-'). <a href='https://platform.openai.com/api-keys' target='_blank'>OpenAI</a>.</div>"
|
251 |
if not state or state == "Select a state..." or "Error" in state:
|
252 |
-
return "<div class='error-message'><span class='error-icon'>⚠️</span>Please select a valid state from the
|
253 |
if not query or not query.strip():
|
254 |
return "<div class='error-message'><span class='error-icon'>⚠️</span>Please enter your question in the text box.</div>"
|
255 |
|
@@ -269,14 +269,15 @@ Answer:"""
|
|
269 |
try:
|
270 |
available_states_list = self.get_states()
|
271 |
# DEBUG: Print states to console to verify if data is loaded
|
272 |
-
print(f"DEBUG: States loaded for
|
273 |
# Ensure "Select a state..." is always the first option
|
274 |
-
|
275 |
-
|
|
|
276 |
except Exception as e: # Catch-all for safety
|
277 |
-
print(f"DEBUG: Error loading states for
|
278 |
-
|
279 |
-
|
280 |
|
281 |
|
282 |
# Define example queries, filtering based on available states
|
@@ -447,15 +448,17 @@ Answer:"""
|
|
447 |
transform: translateY(-3px) !important; /* More pronounced lift */
|
448 |
}
|
449 |
|
450 |
-
/* NEW: Class to center content
|
451 |
-
.
|
452 |
-
|
453 |
-
|
454 |
-
|
|
|
|
|
455 |
}
|
456 |
|
457 |
/* Section titles (h3 inside markdown) */
|
458 |
-
.sub-section-title
|
459 |
font-family: 'Poppins', sans-serif !important;
|
460 |
font-size: 1.7rem !important; /* Slightly larger */
|
461 |
font-weight: 700 !important; /* Bolder */
|
@@ -463,9 +466,12 @@ Answer:"""
|
|
463 |
margin: 0 0 1.25rem 0 !important; /* More space below title */
|
464 |
padding-bottom: 0.75rem !important;
|
465 |
border-bottom: 2px solid var(--border-color) !important; /* Underline effect */
|
466 |
-
display:
|
|
|
|
|
|
|
|
|
467 |
letter-spacing: -0.01em !important;
|
468 |
-
/* To center this inline-block, its parent (.text-center-markdown) must have text-align: center */
|
469 |
}
|
470 |
|
471 |
/* Specific styling for the welcome/disclaimer markdown content */
|
@@ -480,17 +486,11 @@ Answer:"""
|
|
480 |
}
|
481 |
|
482 |
/* Improved input styling with clear boundaries and focus */
|
483 |
-
.gradio-textbox
|
484 |
margin-bottom: 0.75rem !important;
|
485 |
}
|
486 |
-
/* Directly targeting internal Gradio input elements for specific background/border */
|
487 |
.gradio-textbox textarea,
|
488 |
-
.gradio-textbox input
|
489 |
-
/* Specific targeting for dropdown elements */
|
490 |
-
.gradio-dropdown > div.primary-wrap > div > input, /* The input field within the dropdown */
|
491 |
-
.gradio-dropdown > div.primary-wrap, /* The main clickable wrapper of the dropdown */
|
492 |
-
.gradio-dropdown > div.scroll-hide /* The actual dropdown options list container */
|
493 |
-
{
|
494 |
background-color: var(--background-primary) !important; /* Explicitly set background to primary cream */
|
495 |
border: 2px solid var(--border-color) !important; /* Clear border */
|
496 |
border-radius: 8px !important;
|
@@ -501,39 +501,88 @@ Answer:"""
|
|
501 |
transition: border-color 0.2s ease, box-shadow 0.2s ease !important; /* Smooth transitions */
|
502 |
box-shadow: var(--shadow-sm) !important;
|
503 |
}
|
504 |
-
/* Focus styles */
|
505 |
.gradio-textbox textarea:focus,
|
506 |
-
.gradio-textbox input:focus
|
507 |
-
.gradio-dropdown > div.primary-wrap > div > input:focus, /* Focus for dropdown input */
|
508 |
-
.gradio-dropdown > div.primary-wrap.focused { /* Focus for dropdown wrapper */
|
509 |
outline: none !important;
|
510 |
border-color: var(--border-focus) !important; /* Distinct border on focus */
|
511 |
box-shadow: 0 0 0 4px rgba(255, 140, 0, 0.2) !important; /* Broader, softer glow on focus */
|
512 |
}
|
513 |
-
|
514 |
-
|
515 |
-
|
|
|
|
|
|
|
516 |
}
|
517 |
-
|
518 |
-
.gradio-
|
519 |
-
|
520 |
-
.gradio-dropdown .secondary-wrap > div[aria-selected="true"] {
|
521 |
-
background-color: var(--background-secondary) !important; /* Highlight on hover/focus/selection */
|
522 |
-
color: var(--primary-color) !important; /* Primary color for selected text */
|
523 |
}
|
524 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
525 |
|
526 |
-
/*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
527 |
.gradio-textbox label,
|
528 |
-
.gradio-
|
529 |
font-weight: 600 !important; /* Bolder labels */
|
530 |
color: var(--text-primary) !important;
|
531 |
font-size: 1rem !important;
|
532 |
margin-bottom: 0.6rem !important;
|
533 |
display: block !important;
|
|
|
534 |
}
|
535 |
-
/* Info text styling below inputs (
|
536 |
-
.gr-prose {
|
537 |
font-size: 0.9rem !important;
|
538 |
color: var(--text-secondary) !important;
|
539 |
margin-top: 0.4rem !important; /* More space for info text */
|
@@ -765,7 +814,7 @@ Answer:"""
|
|
765 |
.app-header-tagline {
|
766 |
font-size: 1rem !important;
|
767 |
}
|
768 |
-
.
|
769 |
font-size: 1.4rem !important;
|
770 |
}
|
771 |
.input-row {
|
@@ -794,19 +843,24 @@ Answer:"""
|
|
794 |
with gr.Blocks(css=custom_css, title="Landlord-Tenant Rights Assistant") as demo:
|
795 |
# Header Section - uses gr.Group for distinct card-like styling
|
796 |
with gr.Group(elem_classes="app-header-wrapper"):
|
797 |
-
# Using
|
798 |
-
#
|
799 |
-
gr.Markdown(
|
800 |
-
|
801 |
-
|
|
|
|
|
|
|
|
|
|
|
802 |
|
803 |
# Main Dashboard Container - acts as a column to stack various sections
|
804 |
with gr.Column(elem_classes="main-dashboard-container"):
|
805 |
|
806 |
# Introduction and Disclaimer Card
|
807 |
with gr.Group(elem_classes="dashboard-card-section"):
|
808 |
-
# Apply '
|
809 |
-
gr.Markdown("<h3 class='
|
810 |
gr.Markdown(
|
811 |
"""
|
812 |
Navigate landlord-tenant laws with ease. This assistant provides detailed, state-specific answers grounded in legal authority.
|
@@ -817,8 +871,8 @@ Answer:"""
|
|
817 |
|
818 |
# OpenAI API Key Input Card
|
819 |
with gr.Group(elem_classes="dashboard-card-section"):
|
820 |
-
# Apply '
|
821 |
-
gr.Markdown("<h3 class='
|
822 |
api_key_input = gr.Textbox(
|
823 |
label="API Key",
|
824 |
type="password", # Hides the input for security
|
@@ -830,8 +884,8 @@ Answer:"""
|
|
830 |
|
831 |
# Query Input and State Selection Card
|
832 |
with gr.Group(elem_classes="dashboard-card-section"):
|
833 |
-
# Apply '
|
834 |
-
gr.Markdown("<h3 class='
|
835 |
with gr.Row(elem_classes="input-row"): # Row for side-by-side query and state
|
836 |
with gr.Column(elem_classes="input-field", scale=3): # Query text area takes more space
|
837 |
query_input = gr.Textbox(
|
@@ -841,13 +895,12 @@ Answer:"""
|
|
841 |
max_lines=8,
|
842 |
elem_classes=["input-field-group"]
|
843 |
)
|
844 |
-
with gr.Column(elem_classes="input-field", scale=1): # State
|
845 |
-
state_input = gr.
|
846 |
label="Select State",
|
847 |
-
choices=
|
848 |
-
value=
|
849 |
-
|
850 |
-
elem_classes=["input-field-group"]
|
851 |
)
|
852 |
with gr.Row(elem_classes="button-row"): # Row for action buttons
|
853 |
clear_button = gr.Button("Clear", variant="secondary", elem_classes=["gr-button-secondary"])
|
@@ -855,8 +908,8 @@ Answer:"""
|
|
855 |
|
856 |
# Output Display Card - Using gr.HTML for better animation control
|
857 |
with gr.Group(elem_classes="dashboard-card-section"):
|
858 |
-
# Apply '
|
859 |
-
gr.Markdown("<h3 class='
|
860 |
output = gr.HTML( # Changed to gr.HTML to wrap content with animation class
|
861 |
value="<div class='placeholder'>The answer will appear here after submitting your query.</div>",
|
862 |
elem_classes="output-content-wrapper" # Custom class for output styling
|
@@ -864,8 +917,8 @@ Answer:"""
|
|
864 |
|
865 |
# Example Questions Section
|
866 |
with gr.Group(elem_classes="dashboard-card-section examples-section"):
|
867 |
-
# Apply '
|
868 |
-
gr.Markdown("<h3 class='
|
869 |
if example_queries:
|
870 |
gr.Examples(
|
871 |
examples=example_queries,
|
@@ -897,7 +950,7 @@ Answer:"""
|
|
897 |
fn=lambda: (
|
898 |
"", # Clear API key input
|
899 |
"", # Clear query input
|
900 |
-
|
901 |
"<div class='placeholder'>Inputs cleared. Ready for your next question.</div>" # Reset output message
|
902 |
),
|
903 |
inputs=[],
|
|
|
249 |
if not api_key or not api_key.strip() or not api_key.startswith("sk-"):
|
250 |
return "<div class='error-message'><span class='error-icon'>⚠️</span>Please provide a valid OpenAI API key (starting with 'sk-'). <a href='https://platform.openai.com/api-keys' target='_blank'>OpenAI</a>.</div>"
|
251 |
if not state or state == "Select a state..." or "Error" in state:
|
252 |
+
return "<div class='error-message'><span class='error-icon'>⚠️</span>Please select a valid state from the list.</div>" # Changed dropdown to list
|
253 |
if not query or not query.strip():
|
254 |
return "<div class='error-message'><span class='error-icon'>⚠️</span>Please enter your question in the text box.</div>"
|
255 |
|
|
|
269 |
try:
|
270 |
available_states_list = self.get_states()
|
271 |
# DEBUG: Print states to console to verify if data is loaded
|
272 |
+
print(f"DEBUG: States loaded for selection: {available_states_list}")
|
273 |
# Ensure "Select a state..." is always the first option
|
274 |
+
# For Radio, we might want a distinct initial "no selection" state
|
275 |
+
radio_choices = ["Select a state..."] + (available_states_list if available_states_list and "Error" not in available_states_list[0] else ["Error: States unavailable"])
|
276 |
+
initial_value_radio = radio_choices[0] # Set initial value to the prompt
|
277 |
except Exception as e: # Catch-all for safety
|
278 |
+
print(f"DEBUG: Error loading states for selection: {e}")
|
279 |
+
radio_choices = ["Error: Critical failure loading states"]
|
280 |
+
initial_value_radio = radio_choices[0]
|
281 |
|
282 |
|
283 |
# Define example queries, filtering based on available states
|
|
|
448 |
transform: translateY(-3px) !important; /* More pronounced lift */
|
449 |
}
|
450 |
|
451 |
+
/* NEW: Class for Markdown blocks to center their content */
|
452 |
+
.full-width-center {
|
453 |
+
display: flex !important;
|
454 |
+
justify-content: center !important;
|
455 |
+
align-items: center !important;
|
456 |
+
width: 100% !important;
|
457 |
+
flex-direction: column !important; /* Ensure content stacks vertically if needed */
|
458 |
}
|
459 |
|
460 |
/* Section titles (h3 inside markdown) */
|
461 |
+
.section-title { /* Renamed from sub-section-title for clarity */
|
462 |
font-family: 'Poppins', sans-serif !important;
|
463 |
font-size: 1.7rem !important; /* Slightly larger */
|
464 |
font-weight: 700 !important; /* Bolder */
|
|
|
466 |
margin: 0 0 1.25rem 0 !important; /* More space below title */
|
467 |
padding-bottom: 0.75rem !important;
|
468 |
border-bottom: 2px solid var(--border-color) !important; /* Underline effect */
|
469 |
+
display: block !important; /* Ensure it's a block-level element for proper width/border */
|
470 |
+
text-align: center !important; /* Ensure the text within the h3 is centered */
|
471 |
+
width: fit-content !important; /* Make it shrink-wrap its content */
|
472 |
+
margin-left: auto !important; /* Auto margins to center block-level element */
|
473 |
+
margin-right: auto !important;
|
474 |
letter-spacing: -0.01em !important;
|
|
|
475 |
}
|
476 |
|
477 |
/* Specific styling for the welcome/disclaimer markdown content */
|
|
|
486 |
}
|
487 |
|
488 |
/* Improved input styling with clear boundaries and focus */
|
489 |
+
.gradio-textbox {
|
490 |
margin-bottom: 0.75rem !important;
|
491 |
}
|
|
|
492 |
.gradio-textbox textarea,
|
493 |
+
.gradio-textbox input {
|
|
|
|
|
|
|
|
|
|
|
494 |
background-color: var(--background-primary) !important; /* Explicitly set background to primary cream */
|
495 |
border: 2px solid var(--border-color) !important; /* Clear border */
|
496 |
border-radius: 8px !important;
|
|
|
501 |
transition: border-color 0.2s ease, box-shadow 0.2s ease !important; /* Smooth transitions */
|
502 |
box-shadow: var(--shadow-sm) !important;
|
503 |
}
|
504 |
+
/* Focus styles for textboxes */
|
505 |
.gradio-textbox textarea:focus,
|
506 |
+
.gradio-textbox input:focus {
|
|
|
|
|
507 |
outline: none !important;
|
508 |
border-color: var(--border-focus) !important; /* Distinct border on focus */
|
509 |
box-shadow: 0 0 0 4px rgba(255, 140, 0, 0.2) !important; /* Broader, softer glow on focus */
|
510 |
}
|
511 |
+
|
512 |
+
/* Styling for the radio button group (state selection) */
|
513 |
+
.gradio-radio {
|
514 |
+
/* Adjust padding/margins for overall radio group container */
|
515 |
+
padding: 0 !important; /* Remove any default padding */
|
516 |
+
margin-top: 0.5rem !important; /* Add a little space above */
|
517 |
}
|
518 |
+
|
519 |
+
.gradio-radio .gr-radio-input { /* Targeting the actual radio button inputs (hidden) */
|
520 |
+
display: none !important;
|
|
|
|
|
|
|
521 |
}
|
522 |
|
523 |
+
.gradio-radio label {
|
524 |
+
/* Style the clickable area for each radio option */
|
525 |
+
display: flex !important; /* Use flexbox for internal alignment */
|
526 |
+
justify-content: center !important; /* Center content horizontally */
|
527 |
+
align-items: center !important;
|
528 |
+
padding: 0.75rem 1rem !important;
|
529 |
+
border: 2px solid var(--border-color) !important;
|
530 |
+
border-radius: 8px !important;
|
531 |
+
background-color: var(--background-primary) !important; /* Matches card background */
|
532 |
+
color: var(--text-primary) !important;
|
533 |
+
font-weight: 500 !important;
|
534 |
+
cursor: pointer !important;
|
535 |
+
transition: all 0.2s ease-out !important;
|
536 |
+
box-shadow: var(--shadow-sm) !important;
|
537 |
+
margin: 0.25rem 0 !important; /* Small vertical margin between options */
|
538 |
+
width: 100% !important; /* Ensure options take full width of their column */
|
539 |
+
box-sizing: border-box !important; /* Include padding/border in width */
|
540 |
+
}
|
541 |
|
542 |
+
/* Style the text/content within the radio label */
|
543 |
+
.gradio-radio label span.text-lg { /* Gradio uses text-lg for the label text by default */
|
544 |
+
font-weight: 600 !important; /* Make text bold */
|
545 |
+
color: var(--text-primary) !important;
|
546 |
+
font-size: 0.98rem !important; /* Match input text size */
|
547 |
+
}
|
548 |
+
|
549 |
+
/* Hover effect for radio options */
|
550 |
+
.gradio-radio label:hover {
|
551 |
+
background-color: var(--background-secondary) !important; /* Slightly darker cream on hover */
|
552 |
+
border-color: var(--primary-color) !important; /* Highlight border with primary color */
|
553 |
+
box-shadow: var(--shadow-md) !important;
|
554 |
+
transform: translateY(-2px) !important;
|
555 |
+
}
|
556 |
+
|
557 |
+
/* Selected state for radio options */
|
558 |
+
.gradio-radio label.selected { /* Gradio adds a 'selected' class to the label of the chosen option */
|
559 |
+
background-color: var(--primary-color) !important; /* Primary color for selected item */
|
560 |
+
color: white !important; /* White text on selected */
|
561 |
+
border-color: var(--primary-hover) !important;
|
562 |
+
box-shadow: var(--shadow-md) !important;
|
563 |
+
transform: translateY(-1px) !important;
|
564 |
+
}
|
565 |
+
.gradio-radio label.selected span.text-lg {
|
566 |
+
color: white !important; /* Ensure text is white when selected */
|
567 |
+
}
|
568 |
+
/* Gradio's internal wrapper for radio buttons, ensure it doesn't add unwanted padding */
|
569 |
+
.gradio-radio .gr-form {
|
570 |
+
padding: 0 !important;
|
571 |
+
}
|
572 |
+
|
573 |
+
|
574 |
+
/* Label styling for better readability (for Query, State labels) */
|
575 |
.gradio-textbox label,
|
576 |
+
.gradio-radio label.gr-form-label { /* Target the main label for the radio group */
|
577 |
font-weight: 600 !important; /* Bolder labels */
|
578 |
color: var(--text-primary) !important;
|
579 |
font-size: 1rem !important;
|
580 |
margin-bottom: 0.6rem !important;
|
581 |
display: block !important;
|
582 |
+
text-align: left !important; /* Ensure these labels are left-aligned */
|
583 |
}
|
584 |
+
/* Info text styling below inputs (e.g., for API Key) */
|
585 |
+
.gr-prose { /* This class is used by Gradio for info text */
|
586 |
font-size: 0.9rem !important;
|
587 |
color: var(--text-secondary) !important;
|
588 |
margin-top: 0.4rem !important; /* More space for info text */
|
|
|
814 |
.app-header-tagline {
|
815 |
font-size: 1rem !important;
|
816 |
}
|
817 |
+
.section-title {
|
818 |
font-size: 1.4rem !important;
|
819 |
}
|
820 |
.input-row {
|
|
|
843 |
with gr.Blocks(css=custom_css, title="Landlord-Tenant Rights Assistant") as demo:
|
844 |
# Header Section - uses gr.Group for distinct card-like styling
|
845 |
with gr.Group(elem_classes="app-header-wrapper"):
|
846 |
+
# Using a single Markdown component for header for easier robust centering
|
847 |
+
# The 'full-width-center' class applies flexbox centering to the Markdown content
|
848 |
+
gr.Markdown(
|
849 |
+
"""
|
850 |
+
<span class='app-header-logo'>⚖️</span>
|
851 |
+
<h1 class='app-header-title'>Landlord-Tenant Rights Assistant</h1>
|
852 |
+
<p class='app-header-tagline'>Empowering You with State-Specific Legal Insights</p>
|
853 |
+
""",
|
854 |
+
elem_classes="full-width-center"
|
855 |
+
)
|
856 |
|
857 |
# Main Dashboard Container - acts as a column to stack various sections
|
858 |
with gr.Column(elem_classes="main-dashboard-container"):
|
859 |
|
860 |
# Introduction and Disclaimer Card
|
861 |
with gr.Group(elem_classes="dashboard-card-section"):
|
862 |
+
# Apply 'full-width-center' for section titles
|
863 |
+
gr.Markdown("<h3 class='section-title'>Welcome & Disclaimer</h3>", elem_classes="full-width-center")
|
864 |
gr.Markdown(
|
865 |
"""
|
866 |
Navigate landlord-tenant laws with ease. This assistant provides detailed, state-specific answers grounded in legal authority.
|
|
|
871 |
|
872 |
# OpenAI API Key Input Card
|
873 |
with gr.Group(elem_classes="dashboard-card-section"):
|
874 |
+
# Apply 'full-width-center' for section titles
|
875 |
+
gr.Markdown("<h3 class='section-title'>OpenAI API Key</h3>", elem_classes="full-width-center")
|
876 |
api_key_input = gr.Textbox(
|
877 |
label="API Key",
|
878 |
type="password", # Hides the input for security
|
|
|
884 |
|
885 |
# Query Input and State Selection Card
|
886 |
with gr.Group(elem_classes="dashboard-card-section"):
|
887 |
+
# Apply 'full-width-center' for section titles
|
888 |
+
gr.Markdown("<h3 class='section-title'>Ask Your Question</h3>", elem_classes="full-width-center")
|
889 |
with gr.Row(elem_classes="input-row"): # Row for side-by-side query and state
|
890 |
with gr.Column(elem_classes="input-field", scale=3): # Query text area takes more space
|
891 |
query_input = gr.Textbox(
|
|
|
895 |
max_lines=8,
|
896 |
elem_classes=["input-field-group"]
|
897 |
)
|
898 |
+
with gr.Column(elem_classes="input-field", scale=1): # State radio buttons take less space
|
899 |
+
state_input = gr.Radio(
|
900 |
label="Select State",
|
901 |
+
choices=radio_choices,
|
902 |
+
value=initial_value_radio,
|
903 |
+
elem_classes=["input-field-group", "gradio-radio-custom"] # Added custom class for specific styling
|
|
|
904 |
)
|
905 |
with gr.Row(elem_classes="button-row"): # Row for action buttons
|
906 |
clear_button = gr.Button("Clear", variant="secondary", elem_classes=["gr-button-secondary"])
|
|
|
908 |
|
909 |
# Output Display Card - Using gr.HTML for better animation control
|
910 |
with gr.Group(elem_classes="dashboard-card-section"):
|
911 |
+
# Apply 'full-width-center' for section titles
|
912 |
+
gr.Markdown("<h3 class='section-title'>Legal Assistant's Response</h3>", elem_classes="full-width-center")
|
913 |
output = gr.HTML( # Changed to gr.HTML to wrap content with animation class
|
914 |
value="<div class='placeholder'>The answer will appear here after submitting your query.</div>",
|
915 |
elem_classes="output-content-wrapper" # Custom class for output styling
|
|
|
917 |
|
918 |
# Example Questions Section
|
919 |
with gr.Group(elem_classes="dashboard-card-section examples-section"):
|
920 |
+
# Apply 'full-width-center' for section titles
|
921 |
+
gr.Markdown("<h3 class='section-title'>Example Questions</h3>", elem_classes="full-width-center")
|
922 |
if example_queries:
|
923 |
gr.Examples(
|
924 |
examples=example_queries,
|
|
|
950 |
fn=lambda: (
|
951 |
"", # Clear API key input
|
952 |
"", # Clear query input
|
953 |
+
initial_value_radio, # Reset state radio to default prompt
|
954 |
"<div class='placeholder'>Inputs cleared. Ready for your next question.</div>" # Reset output message
|
955 |
),
|
956 |
inputs=[],
|