from __future__ import annotations from html import escape from jawbreaker.schema import ScamAnalysis DNA_LABELS = { "Impersonates": "Who they pretend to be", "Pressure": "How they pressure you", "Ask": "What they want", "Risk": "What could happen", } VERDICT_COPY = { "dangerous": ("CRITICAL: Scam Detected", "Result"), "suspicious": ("WARNING: Suspicious Pattern Found", "Result"), "needs_check": ("REVIEW: Verify Before Acting", "Result"), "safe": ("CLEAR: No Strong Scam Pattern", "Result"), } RISK_WINDOW_CLASS = { "dangerous": "risk-dangerous", "suspicious": "risk-suspicious", "needs_check": "risk-needs_check", "safe": "risk-safe", } RISK_BADGE = { "dangerous": "DANGER", "suspicious": "SUSPECT", "needs_check": "CHECK", "safe": "CLEAR", } SCAN_STEPS = [ "Preparing the safety model...", "Reading the message...", "Checking scam pressure signals...", "Building the safest next step...", "Writing the report...", ] def humanize(text: str) -> str: cleaned = str(text or "").replace("_", " ").replace("-", " ") words = " ".join(cleaned.split()) if not words: return "Unknown" aliases = { "credential theft": "credential theft", "credential request": "credential request", "unknown urgent action": "urgent action", "unknown urgent payment": "urgent payment", "unknown website link": "suspicious link", "fake payment portal": "fake payment portal", "legitimate company": "legitimate company", "sense of urgency": "urgency", "click link and verify": "click a link and verify", "safe benign": "safe message", "needs check": "needs check", } lowered = words.lower() return aliases.get(lowered, words) def build_copy_plan(message: str, analysis: ScamAnalysis) -> str: cleaned = " ".join(message.strip().split()) if len(cleaned) > 500: cleaned = cleaned[:497].rstrip() + "..." risk = analysis.risk_level.replace("_", " ") scam_type = humanize(analysis.scam_type) risk_line = f"Jawbreaker marked it as {risk}" if scam_type and scam_type != "none": risk_line += f" ({scam_type})" return ( "Can you check this message with me before I do anything?\n\n" f"Message I received:\n\"{cleaned}\"\n\n" f"{risk_line}.\n" f"Safest next step: {analysis.safest_action}\n\n" "I have not clicked any links, replied, or sent anything." ) def render_window(title: str, body: str, class_name: str = "") -> str: classes = f"retro-window {class_name}".strip() return f"""
{escape(title)}
{body}
""" def render_analysis_html(message: str, analysis: ScamAnalysis) -> str: if not message.strip(): return """
Ready to help

SYSTEM STANDING BY

Jawbreaker is ready to shield your loved ones from digital fraud.

Paste any text message, email, or DM on the left. The local model will evaluate risk factors, unpack the scam strategy, and deliver a plain-English protection plan.

How to use it

1. Copy a text message from your phone or an email that feels off.

2. Paste it into the input area on the left of this screen.

3. Click RUN SCAM DETECTOR to analyze it with private local AI.

""" tactic_html = "".join(f"{escape(humanize(tactic))}" for tactic in analysis.tactics) dna_html = "".join( f"""
{escape(DNA_LABELS.get(label, label))}
{escape(humanize(value))}
""" for label, value in analysis.scam_dna.items() ) memory_html = f"

Memory: {escape(analysis.similar_memory)}

" if analysis.similar_memory else "" verdict_title, verdict_file = VERDICT_COPY[analysis.risk_level] verdict_subtitle = humanize(analysis.summary.replace("This looks dangerous: likely ", "Likely ").rstrip(".")) risk_class = RISK_WINDOW_CLASS[analysis.risk_level] verdict = f"""

{escape(verdict_title)}

{escape(verdict_subtitle)}.

{memory_html} """ dna = f"""
{dna_html}
{tactic_html or "none found"}
""" copy_plan = build_copy_plan(message, analysis) copy_preview = escape(copy_plan) remedy = f"""

RECOMMENDED ACTION:

{escape(humanize(analysis.safest_action))}

{copy_preview}
""" return f"""
{render_window(verdict_file, verdict, f"verdict-window {risk_class}")} {render_window("How this scam works", dna, "dna-window")} {render_window("What to do next", remedy, "action-window")}
""" def render_scanning_html(active_step: int = 0, progress: int = 12) -> str: progress = max(0, min(100, progress)) filled = max(1, round(progress / 100 * 12)) bar = "█" * filled + "░" * (12 - filled) lines = [] for index, step in enumerate(SCAN_STEPS): if index < active_step: class_name = "terminal-done" prefix = "✓" elif index == active_step: class_name = "terminal-active" prefix = ">" else: class_name = "terminal-muted" prefix = ">" lines.append(f'

{prefix} {escape(step)}

') return """
Checking the message

[%s] %s%% COMPLETE

%s
""" % (bar, progress, "\n".join(lines)) def render_memory_html(analysis: ScamAnalysis, memory: list[dict]) -> str: if not memory: return render_window( "Recent checks", "

No messages checked yet this session.

", "memory-card muted", ) items = "".join( f"""
{escape(item.get('summary', ''))} {escape(RISK_BADGE.get(item.get('risk_level', ''), item.get('risk_level', '')))}
""" for item in memory[-5:] ) return render_window("Recent checks", f"

Session scam memory

{items}", "memory-card")