"""UI components. Defect list rendering and shared visual helpers.""" from __future__ import annotations import base64 import functools import html import json import time from pathlib import Path import re from typing import Iterable from data.schemas import LABEL_DISPLAY_NAMES from data.preprocessing import image_to_data_uri @functools.lru_cache(maxsize=1) def _logo_html() -> str: path = Path(__file__).resolve().parents[1] / "assets" / "logo.jpg" if not path.exists(): return 'H' encoded = base64.b64encode(path.read_bytes()).decode("ascii") return ( '' ) COMPARE_DEFAULT_SPLIT = 50 HEADER_HTML = f"""
{_logo_html()}
Project Halide

Analog Film Diagnostic Workbench

Lonelyguyse1 halide-vision MiniCPM-V 4.6 Nemotron Mini 4B
""" EMPTY_STATE = '

Awaiting scan.

' REPORT_EMPTY_STATE = ( '
' 'Report' 'No scan analyzed yet' '

Results, evidence counts, and physical fixes will appear here after a GPU run.

' "
" ) LIGHTTABLE_EMPTY_STATE = ( '
' '
' '
Original
' '
Validated overlay
' "
" '
' 'Ready' 'No scan loaded' "
" "
" ) REPORT_SECTIONS = { "root cause": "Root cause", "evidence": "Evidence", "physical fixes": "Physical fixes", "confidence": "Confidence", "next inspection": "Next inspection", } def defect_pills_html(label_counts: dict[str, int]) -> str: """Render defect counts as colored pills.""" if not label_counts: return '

No validated defects detected.

' pills: list[str] = [] for label, count in sorted(label_counts.items(), key=lambda kv: -kv[1]): display = LABEL_DISPLAY_NAMES.get(label, label.replace("_", " ").title()) pills.append( f'' f"{html.escape(display)} {int(count)}" ) return '
' + "".join(pills) + "
" def compact_label_counts(label_counts: dict[str, int]) -> str: if not label_counts: return "none" parts = [] for label, count in sorted(label_counts.items(), key=lambda kv: (-kv[1], kv[0])): display = LABEL_DISPLAY_NAMES.get(label, label.replace("_", " ").title()) parts.append(f"{display}: {int(count)}") return ", ".join(parts) def defect_table_rows(result: dict | None) -> list[list[str]]: """Return rows for the evidence dataframe.""" if not result: return [] defects = (result.get("defects", {}) or {}).get("defects", []) or [] if not defects: return [["", "No validated defects", "", ""]] rows: list[list[str]] = [] for index, defect in enumerate(defects, start=1): label = str(defect.get("label", "")) display = LABEL_DISPLAY_NAMES.get(label, label.replace("_", " ").title()) bbox = defect.get("bbox", []) or [] if len(bbox) == 4: box_text = ", ".join(f"{float(v):.3f}" for v in bbox) else: box_text = "invalid" confidence = defect.get("confidence") confidence_text = ( "not emitted" if confidence is None else f"{float(confidence):.2f}" ) rows.append([str(index), display, confidence_text, box_text]) return rows def stats_html(result: dict) -> str: """Render a stats card with defect counts and timing.""" defects = result.get("defects", {}) or {} diagnosis = result.get("diagnosis", {}) or {} total = result.get("total_seconds", 0.0) or 0.0 vision_s = defects.get("inference_seconds", 0.0) or 0.0 reasoning_s = diagnosis.get("reasoning_seconds", 0.0) or 0.0 rows: list[str] = [] rows.append(_stat_row("Total defects", str(defects.get("defect_count", 0)))) rows.append(_stat_row("Dropped (invalid)", str(defects.get("dropped_count", 0)))) rows.append(_stat_row("Duplicates removed", str(defects.get("duplicate_count", 0)))) rows.append(_stat_row("Edge artifacts removed", str(defects.get("edge_artifact_count", 0)))) rows.append(_stat_row("CV assist boxes", str(defects.get("classical_assist_count", 0)))) rows.append(_stat_row("Resized for model", "yes" if defects.get("resized_for_model") else "no")) rows.append(_stat_row("Vision inference", f"{vision_s:.2f}s")) rows.append(_stat_row("Reasoning", f"{reasoning_s:.2f}s")) rows.append(_stat_row("Total", f"{total:.2f}s")) rows.append(_stat_row("Vision model", _truncate(defects.get("model_path", ""), 46))) rows.append(_stat_row("Reasoning model", _truncate(diagnosis.get("model_path", ""), 46))) return ( '
Run telemetry
' f'
{"".join(rows)}
' ) def _stat_row(label: str, value: str) -> str: return ( '
' f'{html.escape(label)}' f'{html.escape(value)}' "
" ) def _truncate(s: str, n: int) -> str: if len(s) <= n: return s return "..." + s[-(n - 3):] def diagnosis_html(text: str) -> str: """Render the Nemotron Markdown report into structured HTML.""" return render_markdown_report(text or "(no diagnosis produced)") def review_frame_html(original, annotated) -> str: """Render reliable full-size image links independent of Gradio fullscreen.""" original_uri = image_to_data_uri(original, max_side=1800, quality=92) overlay_uri = image_to_data_uri(annotated, max_side=1800, quality=92) return ( '
' 'Open original' 'Open overlay' "
" ) def comparison_viewer_html(original, annotated) -> str: """Render an aligned before/after viewer using the same image canvas.""" original_uri = image_to_data_uri(original, max_side=1800, quality=92) overlay_uri = image_to_data_uri(annotated, max_side=1800, quality=92) return ( '
' '
' f'' '
' f'' "
" '
' 'Original' 'Validated overlay' "
" '' "
" ) def render_markdown_report(text: str) -> str: """Render the constrained diagnosis Markdown used by Nemotron. This intentionally supports only the report shapes we request from the model: section headings, paragraphs, bullets, and numbered fixes. """ sections: list[dict[str, list[str] | str]] = [] current: dict[str, list[str] | str] = { "title": "Report", "lines": [], } for raw_line in text.replace("\r\n", "\n").split("\n"): line = raw_line.strip() if line.startswith("## "): if current["lines"]: sections.append(current) title_key = line[3:].strip().lower() current = { "title": REPORT_SECTIONS.get(title_key, line[3:].strip() or "Report"), "lines": [], } continue current["lines"].append(line) if current["lines"] or not sections: sections.append(current) rendered = [] for section in sections: title = html.escape(str(section["title"])) body = _render_report_lines(section["lines"]) # type: ignore[arg-type] rendered.append( '
' f'
{title}
' f'
{body}
' "
" ) return '
' + "".join(rendered) + "
" def _render_report_lines(lines: list[str]) -> str: blocks: list[str] = [] paragraph: list[str] = [] bullet_items: list[str] = [] ordered_items: list[str] = [] def flush_paragraph() -> None: if paragraph: blocks.append( "

" + " ".join(_render_inline(part) for part in paragraph if part) + "

" ) paragraph.clear() def flush_bullets() -> None: if bullet_items: items = "".join(f"
  • {item}
  • " for item in bullet_items) blocks.append(f"") bullet_items.clear() def flush_ordered() -> None: if ordered_items: items = "".join(f"
  • {item}
  • " for item in ordered_items) blocks.append(f"
      {items}
    ") ordered_items.clear() for line in lines: if not line: flush_paragraph() flush_bullets() flush_ordered() continue if line.startswith("### "): flush_paragraph() flush_bullets() flush_ordered() blocks.append( f'

    {_render_inline(line[4:].strip())}

    ' ) continue numbered = re.match(r"^\d+\.\s+(.*)$", line) if line.startswith("- "): flush_paragraph() flush_ordered() bullet_items.append(_render_inline(line[2:].strip())) elif numbered: flush_paragraph() flush_bullets() ordered_items.append(_render_inline(numbered.group(1).strip())) else: flush_bullets() flush_ordered() paragraph.append(line) flush_paragraph() flush_bullets() flush_ordered() return "".join(blocks) or '

    No report text.

    ' def _render_inline(text: str) -> str: escaped = html.escape(text) escaped = re.sub(r"`([^`]+)`", r"\1", escaped) escaped = re.sub(r"\*\*([^*]+)\*\*", r"\1", escaped) escaped = re.sub(r"__([^_]+)__", r"\1", escaped) escaped = re.sub(r"(?\1", escaped) escaped = re.sub(r"(?\1", escaped) return escaped def metadata_html(result: dict) -> str: """Render a compact metadata strip for the current run.""" meta = result.get("film_metadata", {}) or {} confidence = str(meta.get("metadata_confidence", "low") or "low") rows = [ _stat_row("Film stock", str(meta.get("film_type", "Unknown"))), _stat_row("Age", f"{meta.get('film_age_years', 0)} years"), _stat_row("Storage", str(meta.get("storage", "unknown"))), _stat_row("Scan DPI", str(meta.get("scan_resolution_dpi", "unknown"))), _stat_row("Metadata confidence", confidence.title()), ] return ( '
    Film dossier
    ' f'
    {"".join(rows)}
    ' ) def confidence_notice_html(result: dict) -> str: """Render a small evidence-quality notice from validation metadata.""" defects = result.get("defects", {}) or {} total = int(defects.get("defect_count", 0) or 0) dropped = int(defects.get("dropped_count", 0) or 0) duplicate = int(defects.get("duplicate_count", 0) or 0) meta = result.get("film_metadata", {}) or {} confidence = str(meta.get("metadata_confidence", "low") or "low").lower() confidence_values = [ defect.get("confidence") for defect in defects.get("defects", []) or [] if defect.get("confidence") is not None ] if total == 0: message = "No validated boxes were returned. Inspect the scan before assuming a film fault." tone = "neutral" elif not confidence_values: message = ( "Defect boxes passed schema validation. MiniCPM did not emit numeric " "per-box confidence for this run." ) tone = "good" elif confidence == "low": message = "Metadata is marked low confidence, so the diagnosis is driven mainly by visible defects." tone = "caution" elif dropped or duplicate: message = f"Schema checks removed {dropped} invalid boxes and {duplicate} duplicate boxes." tone = "caution" else: message = "Schema validation passed for the visible defect evidence." tone = "good" return f'
    {html.escape(message)}
    ' def run_state_html(result: dict | None) -> str: """Render a top-level status block for the current diagnosis.""" if not result: return ( '
    ' 'Ready' "Load a scan to begin inspection." "
    " ) defects = result.get("defects", {}) or {} count = int(defects.get("defect_count", 0) or 0) dropped = int(defects.get("dropped_count", 0) or 0) duplicates = int(defects.get("duplicate_count", 0) or 0) if count: title = f"{count} validated defect{'s' if count != 1 else ''}" tone = "active" else: title = "No validated defects" tone = "quiet" detail = ( f"{dropped} invalid removed, {duplicates} duplicate removed, " f"{float(result.get('total_seconds', 0.0) or 0.0):.2f}s total" ) return ( f'
    ' 'Current scan' f"{html.escape(title)}" f"{html.escape(detail)}" "
    " ) def history_label(entry: dict) -> str: """Return a compact plain-text label for the history dropdown.""" stamp = time.strftime( "%Y-%m-%d %H:%M", time.localtime(float(entry.get("created_at", 0) or 0)), ) film = str(entry.get("film_type", "Unknown")) count = int(entry.get("defect_count", 0) or 0) return f"{stamp} | {film} | {count} defects" def history_table_rows(entries: Iterable[dict]) -> list[list[str]]: rows: list[list[str]] = [] for entry in entries: stamp = time.strftime( "%Y-%m-%d %H:%M", time.localtime(float(entry.get("created_at", 0) or 0)), ) rows.append( [ stamp, str(entry.get("film_type", "Unknown")), str(int(entry.get("defect_count", 0) or 0)), compact_label_counts(entry.get("label_counts", {}) or {}), ] ) return rows def history_choices(entries: Iterable[dict]) -> list[tuple[str, str]]: return [(history_label(e), str(e.get("id", ""))) for e in entries if e.get("id")] def history_detail_html(entry: dict | None) -> str: if not entry: return '

    Select a diagnosis to review details.

    ' counts = entry.get("label_counts", {}) or {} raw = entry.get("raw_json", {}) or {} meta = raw.get("film_metadata", {}) if raw else {} confidence = meta.get("metadata_confidence", entry.get("metadata_confidence", "low")) preview = raw.get("preview", {}) if raw else {} preview_html = "" overlay_uri = str(preview.get("overlay", "") or "") original_uri = str(preview.get("original", "") or "") if overlay_uri.startswith("data:image/") or original_uri.startswith("data:image/"): hero_uri = overlay_uri if overlay_uri.startswith("data:image/") else original_uri preview_html = ( '
    ' f'' '
    ' ) if original_uri.startswith("data:image/"): preview_html += ( f'Original' ) if overlay_uri.startswith("data:image/"): preview_html += ( f'Overlay' ) preview_html += "
    " stamp = time.strftime( "%Y-%m-%d %H:%M", time.localtime(float(entry.get("created_at", 0) or 0)), ) header_rows = [ _stat_row("Saved", stamp), _stat_row("Film stock", str(entry.get("film_type", "Unknown"))), _stat_row("Age", f"{entry.get('film_age_years', '?')} years"), _stat_row("Storage", str(entry.get("storage", "?"))), _stat_row("Scan DPI", str(entry.get("scan_dpi", "?"))), _stat_row("Metadata confidence", str(confidence).title()), _stat_row("Vision model", _truncate(str(entry.get("vision_model", "")), 60)), _stat_row("Reasoning model", _truncate(str(entry.get("reasoning_model", "")), 60)), ] return ( '
    ' f"{preview_html}" f'
    {"".join(header_rows)}
    ' '
    Defects
    ' f"{defect_pills_html(counts)}" '
    Diagnosis
    ' f"{diagnosis_html(str(entry.get('diagnosis_text', '')))}" "
    " ) def raw_json_text(result_or_entry: dict | None) -> str: if not result_or_entry: return "{}" if "raw_json" in result_or_entry: payload = result_or_entry.get("raw_json") or {} else: payload = result_or_entry payload = _strip_preview(payload) return json.dumps(payload, indent=2, sort_keys=True) def _strip_preview(payload: dict) -> dict: if not isinstance(payload, dict): return {} clean = dict(payload) if "preview" in clean: preview = clean.get("preview") or {} if isinstance(preview, dict): clean["preview"] = { key: "[image data URI omitted from JSON view]" for key in preview } else: clean["preview"] = "[image data URI omitted from JSON view]" return clean __all__ = [ "HEADER_HTML", "EMPTY_STATE", "LIGHTTABLE_EMPTY_STATE", "REPORT_EMPTY_STATE", "compact_label_counts", "comparison_viewer_html", "confidence_notice_html", "defect_table_rows", "defect_pills_html", "diagnosis_html", "history_choices", "history_detail_html", "history_label", "history_table_rows", "metadata_html", "render_markdown_report", "run_state_html", "raw_json_text", "review_frame_html", "stats_html", ]