| from __future__ import annotations |
|
|
| import gradio as gr |
|
|
| from decision_brief import create_brief, write_markdown_file |
|
|
|
|
| EXAMPLE_INPUT = """I want to create a useful app and publish it on Hugging Face and GitHub. |
| It should be practical, easy to understand, and not depend on paid APIs. |
| The first version needs a clean demo flow, a README, and a way for users to export the result.""" |
|
|
|
|
| CSS = """ |
| :root { |
| --background-fill-primary: #f7f4ed; |
| --background-fill-secondary: #ffffff; |
| --body-text-color: #1f2933; |
| --color-accent: #256f6c; |
| } |
| |
| body, |
| gradio-app, |
| .gradio-container { |
| background: #f7f4ed !important; |
| color: #1f2933 !important; |
| width: 100% !important; |
| min-width: 0 !important; |
| overflow-x: hidden !important; |
| } |
| |
| .gradio-container { |
| max-width: 1180px !important; |
| margin: 0 auto !important; |
| font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important; |
| overflow-x: hidden !important; |
| } |
| |
| .gradio-container * { |
| box-sizing: border-box !important; |
| } |
| |
| .gradio-container label, |
| .gradio-container span, |
| .gradio-container p, |
| .gradio-container textarea, |
| .gradio-container input { |
| color: #1f2933 !important; |
| } |
| |
| #hero { |
| padding: 28px 4px 8px; |
| } |
| |
| #hero h1 { |
| color: #182326; |
| font-size: clamp(32px, 5vw, 56px); |
| line-height: 1.02; |
| letter-spacing: 0; |
| margin-bottom: 10px; |
| } |
| |
| #hero p { |
| color: #50616a; |
| max-width: 760px; |
| font-size: 17px; |
| line-height: 1.6; |
| } |
| |
| .panel { |
| background: #ffffff !important; |
| border: 1px solid #ded8cb !important; |
| border-radius: 8px !important; |
| box-shadow: 0 10px 28px rgba(31, 41, 51, 0.07) !important; |
| } |
| |
| textarea, |
| input, |
| .wrap, |
| .block, |
| .form { |
| background: #ffffff !important; |
| } |
| |
| .block, |
| .form, |
| .column, |
| .row { |
| min-width: 0 !important; |
| } |
| |
| @media (max-width: 760px) { |
| #hero { |
| padding: 24px 4px 4px; |
| } |
| |
| #hero h1 { |
| font-size: 34px; |
| } |
| |
| .row { |
| flex-direction: column !important; |
| flex-wrap: nowrap !important; |
| } |
| |
| .gradio-container, |
| .gradio-container main, |
| .gradio-container .wrap, |
| .gradio-container .contain, |
| .gradio-container .html-container, |
| .gradio-container .prose, |
| .column, |
| .row, |
| .form, |
| .block, |
| fieldset, |
| textarea { |
| width: 100% !important; |
| max-width: 100% !important; |
| } |
| |
| .gradio-container main { |
| padding-left: 14px !important; |
| padding-right: 14px !important; |
| } |
| |
| fieldset .wrap { |
| display: flex !important; |
| flex-wrap: wrap !important; |
| } |
| |
| fieldset label { |
| flex: 1 1 160px !important; |
| } |
| } |
| |
| button.primary-button { |
| background: #256f6c !important; |
| border-color: #256f6c !important; |
| } |
| |
| #score table { |
| font-size: 14px; |
| } |
| """ |
|
|
|
|
| def run_builder(problem: str, mode: str, impact: int, effort: int, confidence: int, constraints: str): |
| result = create_brief(problem, mode, impact, effort, confidence, constraints) |
| score_rows = [[name, score, note] for name, score, note in result.scorecard] |
| file_path = write_markdown_file(result.markdown, result.title) |
| return ( |
| result.markdown, |
| score_rows, |
| "\n".join(f"- {item}" for item in result.actions), |
| "\n".join(f"- {item}" for item in result.risks), |
| result.csv_text, |
| file_path, |
| ) |
|
|
|
|
| with gr.Blocks(title="Decision Brief Builder", css=CSS, theme=gr.themes.Soft()) as demo: |
| gr.HTML( |
| """ |
| <section id="hero"> |
| <h1>Decision Brief Builder</h1> |
| <p>Turn a messy problem, launch idea, or project blocker into a concise brief with a recommendation, next actions, risks, open questions, and an exportable Markdown file.</p> |
| </section> |
| """ |
| ) |
|
|
| with gr.Row(equal_height=False): |
| with gr.Column(scale=5, elem_classes="panel"): |
| problem = gr.Textbox( |
| label="Situation", |
| value=EXAMPLE_INPUT, |
| lines=9, |
| placeholder="Paste the problem, decision, launch idea, or project notes.", |
| ) |
| constraints = gr.Textbox( |
| label="Constraints", |
| value="No API keys. Must be useful as a public demo. Should be easy to publish.", |
| lines=3, |
| placeholder="Budget, time, privacy, audience, deadline, platforms, or quality bar.", |
| ) |
| mode = gr.Radio( |
| ["Launch plan", "Product decision", "Project triage", "Personal decision"], |
| value="Launch plan", |
| label="Brief type", |
| ) |
| with gr.Row(): |
| impact = gr.Slider(0, 100, value=78, step=1, label="Impact") |
| effort = gr.Slider(0, 100, value=42, step=1, label="Effort") |
| confidence = gr.Slider(0, 100, value=70, step=1, label="Confidence") |
| build = gr.Button("Build brief", variant="primary", elem_classes="primary-button") |
|
|
| with gr.Column(scale=6): |
| markdown = gr.Markdown(label="Brief", elem_classes="panel") |
| with gr.Row(): |
| actions = gr.Markdown(label="Actions", elem_classes="panel") |
| risks = gr.Markdown(label="Risks", elem_classes="panel") |
|
|
| with gr.Row(): |
| score = gr.Dataframe( |
| headers=["Metric", "Score", "Note"], |
| datatype=["str", "number", "str"], |
| label="Scorecard", |
| interactive=False, |
| elem_id="score", |
| ) |
| csv_output = gr.Textbox(label="Scorecard CSV", lines=8, interactive=False) |
| download = gr.File(label="Download Markdown brief") |
|
|
| build.click( |
| fn=run_builder, |
| inputs=[problem, mode, impact, effort, confidence, constraints], |
| outputs=[markdown, score, actions, risks, csv_output, download], |
| queue=False, |
| ) |
| demo.load( |
| fn=run_builder, |
| inputs=[problem, mode, impact, effort, confidence, constraints], |
| outputs=[markdown, score, actions, risks, csv_output, download], |
| queue=False, |
| ) |
|
|
|
|
| if __name__ == "__main__": |
| demo.launch() |
|
|