""" Renders a ui_spec dict to GitHub-flavored markdown. Used by the Custom GPT Action path (Path A) where real interactive UI is not available. Interactive behaviour degrades to descriptive markdown. """ def render(spec: dict | None) -> str | None: if not spec: return None component = spec.get("component", "") props = spec.get("props", {}) renderers = { "form": _form, "chart": _chart, "card": _card, "table": _table, "figures": _figures, } fn = renderers.get(component) return fn(props) if fn else f"*[{component} — view in full UI for interactivity]*" # ── form ───────────────────────────────────────────────────────────────────── def _form(props: dict) -> str: lines: list[str] = [] title = props.get("title") if title: lines.append(f"**{title}**\n") for f in props.get("fields", []): label = f.get("label") or f.get("name", "") ftype = f.get("type", "text") required = " *(required)*" if f.get("required") else "" hint = f" — {f['hint']}" if f.get("hint") else "" lines.append(f"- **{label}** `{ftype}`{required}{hint}") submit = props.get("submitLabel", "Submit") lines.append(f"\n> *(Use the full UI widget to fill and submit — **{submit}**)*") return "\n".join(lines) # ── chart ───────────────────────────────────────────────────────────────────── def _chart(props: dict) -> str: title = props.get("title", "Chart") chart_type = props.get("type", "") lines = [f"**{title}** *(interactive {chart_type} chart in full UI)*\n"] data = props.get("data", {}) labels = data.get("labels", []) datasets = data.get("datasets", []) if labels and datasets: header = "| Label | " + " | ".join(ds.get("label", "Value") for ds in datasets) + " |" sep = "|---|" + "---|" * len(datasets) lines += [header, sep] for i, lbl in enumerate(labels): cells = [str(ds.get("data", [])[i]) if i < len(ds.get("data", [])) else "" for ds in datasets] lines.append(f"| {lbl} | " + " | ".join(cells) + " |") return "\n".join(lines) # ── card ────────────────────────────────────────────────────────────────────── def _card(props: dict) -> str: lines: list[str] = [] if title := props.get("title"): lines.append(f"### {title}") if body := props.get("body"): lines.append(body) for item in props.get("items", []): if isinstance(item, dict): key = item.get("label") or item.get("key", "") lines.append(f"- **{key}**: {item.get('value', '')}") else: lines.append(f"- {item}") return "\n".join(lines) # ── table ───────────────────────────────────────────────────────────────────── def _table(props: dict) -> str: columns = props.get("columns", []) rows = props.get("rows", []) if not columns: return "*[empty table]*" header = "| " + " | ".join(columns) + " |" sep = "|" + "---|" * len(columns) lines = [header, sep] for row in rows: if isinstance(row, dict): cells = [str(row.get(col, "")) for col in columns] elif isinstance(row, list): cells = [str(c) for c in row] else: cells = [str(row)] lines.append("| " + " | ".join(cells) + " |") return "\n".join(lines) # ── figures ─────────────────────────────────────────────────────────────────── def _figures(props: dict) -> str: lines: list[str] = [] if title := props.get("title"): lines.append(f"**{title}**\n") for f in props.get("fields", []): label = f.get("label") or f.get("name", "") value = f.get("value", 0) unit = f" {f['unit']}" if f.get("unit") else "" lines.append(f"- **{label}**: {value}{unit}") cta = props.get("cta", "Analyze my figures") lines.append(f"\n> *(Enter figures in the widget then click **{cta}**)*") return "\n".join(lines)