import gradio as gr from datetime import datetime from utils import generate_cover_letter import os # Custom CSS for modern UI css = """ :root { --primary: #4361ee; --secondary: #3f37c9; --accent: #4895ef; --light: #f8f9fa; --dark: #212529; } body { font-family: 'Segoe UI', system-ui, sans-serif; background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%); min-height: 100vh; margin: 0; padding: 20px; } .container { max-width: 1200px; margin: 0 auto; } .header { background: linear-gradient(rgba(0,0,0,0.7), rgba(0,0,0,0.7)), url('file/assets/header.jpg'); background-size: cover; color: white; padding: 2rem; border-radius: 12px; text-align: center; margin-bottom: 2rem; } .step-card { background: white; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.08); padding: 1.5rem; margin-bottom: 1.5rem; } .step-indicator { display: flex; justify-content: center; margin-bottom: 2rem; } .step { width: 36px; height: 36px; border-radius: 50%; background: var(--accent); color: white; display: flex; align-items: center; justify-content: center; font-weight: bold; margin: 0 10px; position: relative; } .step.active { background: var(--secondary); transform: scale(1.1); } .step:not(:last-child)::after { content: ''; position: absolute; width: 40px; height: 2px; background: #ddd; left: 100%; } h2 { color: var(--primary); border-bottom: 2px solid var(--accent); padding-bottom: 0.5rem; margin-top: 0; } .btn-primary { background: var(--primary) !important; border: none !important; padding: 12px 24px !important; font-size: 16px !important; border-radius: 8px !important; transition: all 0.3s !important; } .btn-primary:hover { background: var(--secondary) !important; transform: translateY(-2px); box-shadow: 0 4px 12px rgba(67, 97, 238, 0.3) !important; } .output-card { background: white; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.08); padding: 2rem; margin-top: 2rem; } .tips { background: #e3f2fd; border-left: 4px solid var(--accent); padding: 1rem; border-radius: 4px; font-size: 0.9rem; } .footer { text-align: center; margin-top: 2rem; color: #6c757d; font-size: 0.9rem; } """ # Initialize steps current_step = gr.State(1) def next_step(): return current_step.value + 1 def generate_letter(job_desc, resume, name, email, phone, company, manager, tone): # Generate cover letter body using NLP letter_body = generate_cover_letter(job_desc, resume, tone) # Format final letter today = datetime.today().strftime("%B %d, %Y") return f""" {today} {manager or "Hiring Manager"} {company or "Company Name"} Dear {manager.split()[0] + ',' if manager else "Hiring Manager,"} {letter_body} Sincerely, {name} {email} | {phone} """, current_step.value + 1 def update_step(step): return step with gr.Blocks(css=css, title="AI Cover Letter Generator") as demo: current_step = gr.Variable(1) with gr.Column(elem_classes=["container"]): # Header section with gr.Column(elem_classes=["header"]): gr.Markdown("# ✉️ AI Cover Letter Generator") gr.Markdown("Create personalized cover letters in seconds") # Step indicator with gr.Column(elem_classes=["step-indicator"]): gr.Markdown("### Create in 3 Steps") with gr.Row(): step1 = gr.Markdown("1", elem_classes=["step", "active"]) step2 = gr.Markdown("2", elem_classes=["step"]) step3 = gr.Markdown("3", elem_classes=["step"]) # Step 1: Personal Details with gr.Column(visible=True) as step1_section: with gr.Column(elem_classes=["step-card"]): gr.Markdown("### 👤 Your Information") with gr.Row(): name = gr.Textbox(label="Full Name", placeholder="John Doe") email = gr.Textbox(label="Email", placeholder="john@example.com") with gr.Row(): phone = gr.Textbox(label="Phone", placeholder="(123) 456-7890") tone = gr.Dropdown( label="Writing Tone", choices=["Professional", "Enthusiastic", "Formal", "Friendly"], value="Professional" ) # Step 2: Job & Resume Details with gr.Column(visible=False) as step2_section: with gr.Column(elem_classes=["step-card"]): gr.Markdown("### 💼 Job & Company Details") with gr.Row(): company = gr.Textbox(label="Company Name", placeholder="Acme Inc") manager = gr.Textbox(label="Hiring Manager (Optional)", placeholder="Sarah Johnson") job_desc = gr.Textbox( label="Job Description", lines=5, placeholder="Paste the job description here...", info="Copy-paste the full job posting for best results" ) gr.Markdown("### 📄 Your Resume") resume = gr.Textbox( label="Paste your resume content", lines=8, placeholder="Skills:\n- Python\n- Project Management\n\nExperience:\n- ABC Corp (2020-2023)...", info="Include key skills and experiences" ) with gr.Accordion("💡 Resume Tips", open=False): gr.Markdown("- Include specific skills mentioned in the job description\n- Add quantifiable achievements\n- Keep it concise (300-500 words)") # Step 3: Generated Letter with gr.Column(visible=False) as step3_section: with gr.Column(elem_classes=["output-card"]): gr.Markdown("### 📜 Your Custom Cover Letter") output = gr.Textbox(label="", lines=15, interactive=True) with gr.Row(): download = gr.Button("📥 Download as TXT") new_letter = gr.Button("🔄 Generate New Letter") with gr.Column(elem_classes=["tips"]): gr.Markdown("**Before using:**\n1. Review for accuracy\n2. Personalize company-specific details\n3. Check length (200-300 words)") # Navigation buttons with gr.Row(): back_btn = gr.Button("← Back", visible=False) next_btn = gr.Button("Continue →", elem_classes=["btn-primary"]) submit_btn = gr.Button("✨ Generate Cover Letter", visible=False, elem_classes=["btn-primary"]) # Footer gr.Markdown("---") with gr.Column(elem_classes=["footer"]): gr.Markdown("⚠️ Always review generated content before sending") gr.Markdown("Made with ❤️ using Transformers | GPT-2 Model") # Step navigation logic next_btn.click( next_step, inputs=None, outputs=current_step ).then( update_step, inputs=current_step, outputs=[step1, step2, step3] ).then( lambda step: { step1_section: gr.update(visible=step == 1), step2_section: gr.update(visible=step == 2), step3_section: gr.update(visible=step == 3), back_btn: gr.update(visible=step > 1), next_btn: gr.update(visible=step < 2), submit_btn: gr.update(visible=step == 2) }, inputs=current_step, outputs=[step1_section, step2_section, step3_section, back_btn, next_btn, submit_btn] ) back_btn.click( lambda step: step - 1, inputs=current_step, outputs=current_step ).then( update_step, inputs=current_step, outputs=[step1, step2, step3] ).then( lambda step: { step1_section: gr.update(visible=step == 1), step2_section: gr.update(visible=step == 2), step3_section: gr.update(visible=step == 3), back_btn: gr.update(visible=step > 1), next_btn: gr.update(visible=step < 2), submit_btn: gr.update(visible=step == 2) }, inputs=current_step, outputs=[step1_section, step2_section, step3_section, back_btn, next_btn, submit_btn] ) submit_btn.click( generate_letter, inputs=[job_desc, resume, name, email, phone, company, manager, tone], outputs=[output, current_step] ).then( update_step, inputs=current_step, outputs=[step1, step2, step3] ).then( lambda step: { step1_section: gr.update(visible=step == 1), step2_section: gr.update(visible=step == 2), step3_section: gr.update(visible=step == 3), back_btn: gr.update(visible=step > 1), next_btn: gr.update(visible=step < 2), submit_btn: gr.update(visible=step == 2) }, inputs=current_step, outputs=[step1_section, step2_section, step3_section, back_btn, next_btn, submit_btn] ) download.click( lambda text: { "name": "cover_letter.txt", "data": text }, inputs=output, outputs=gr.File() ) new_letter.click( lambda: 1, inputs=None, outputs=current_step ).then( update_step, inputs=current_step, outputs=[step1, step2, step3] ).then( lambda step: { step1_section: gr.update(visible=step == 1), step2_section: gr.update(visible=step == 2), step3_section: gr.update(visible=step == 3), back_btn: gr.update(visible=step > 1), next_btn: gr.update(visible=step < 2), submit_btn: gr.update(visible=step == 2) }, inputs=current_step, outputs=[step1_section, step2_section, step3_section, back_btn, next_btn, submit_btn] ) if __name__ == "__main__": demo.launch()