diff --git a/frontend/src/viewers/ArtifactView.test.tsx b/frontend/src/viewers/ArtifactView.test.tsx
index ccf02393..2fe33ffc 100644
--- a/frontend/src/viewers/ArtifactView.test.tsx
+++ b/frontend/src/viewers/ArtifactView.test.tsx
@@ -26,9 +26,23 @@ test("question renders markdown headings", () => {
expect(screen.getByText("Domanda")).toBeInTheDocument();
});
-test("unknown kind falls back to formatted JSON", () => {
- render();
- expect(screen.getByText(/"a": 1/)).toBeInTheDocument();
+test("unknown object kind renders as formatted sections, never raw JSON", () => {
+ render(
+ ,
+ );
+ // Humanized section labels, values as text/bullets — and NO raw JSON braces.
+ expect(screen.getByText("Recap")).toBeInTheDocument();
+ expect(screen.getByText("Chiarimenti risolti")).toBeInTheDocument();
+ expect(screen.getByText("finestra_temporale — 5 anni")).toBeInTheDocument();
+ expect(screen.queryByText(/"recap"/)).not.toBeInTheDocument();
});
test("schema_linking renders the schema viewer", async () => {
diff --git a/frontend/src/viewers/ArtifactView.tsx b/frontend/src/viewers/ArtifactView.tsx
index 14903191..c5bc5ba6 100644
--- a/frontend/src/viewers/ArtifactView.tsx
+++ b/frontend/src/viewers/ArtifactView.tsx
@@ -69,10 +69,53 @@ function cteNames(raw: unknown): string[] | null {
return null;
}
-function JsonFallback({ value }: { value: unknown }): ReactElement {
- const text = typeof value === "string" ? value : JSON.stringify(value, null, 2);
+// "recap" -> "Recap", "chiarimenti_risolti" -> "Chiarimenti risolti"
+function humanizeKey(k: string): string {
+ const spaced = k.replace(/[_-]+/g, " ").trim();
+ return spaced.charAt(0).toUpperCase() + spaced.slice(1);
+}
+
+/** Renders any artifact payload as readable text — NEVER raw JSON. Objects become
+ * labelled sections, arrays become bullet lists, primitives become paragraphs;
+ * strings render as markdown. Nested structures recurse. */
+function StructuredValue({ value }: { value: unknown }): ReactElement {
+ if (value === null || value === undefined || value === "") {
+ return
—
;
+ }
+ if (typeof value === "string") return ;
+ if (typeof value === "number" || typeof value === "boolean") {
+ return {String(value)}
;
+ }
+ if (Array.isArray(value)) {
+ const allPrimitive = value.every((v) => v === null || typeof v !== "object");
+ if (allPrimitive) {
+ return (
+
+ {value.map((item, i) => - {String(item)}
)}
+
+ );
+ }
+ return (
+
+ {value.map((item, i) => (
+
+
+
+ ))}
+
+ );
+ }
return (
- {text}
+
+ {Object.entries(value as Record).map(([k, v]) => (
+
+
+ {humanizeKey(k)}
+
+
+
+ ))}
+
);
}
@@ -104,5 +147,6 @@ export function ArtifactView({ artifact }: { artifact: ArtifactData }): ReactEle
const md = toMarkdown(data);
if (md !== null) return ;
}
- return ;
+ // Fallback for any other kind / shape: formatted text, never raw JSON.
+ return ;
}
diff --git a/frontend/src/viewers/MarkdownView.tsx b/frontend/src/viewers/MarkdownView.tsx
index aa5d0032..93fd5e33 100644
--- a/frontend/src/viewers/MarkdownView.tsx
+++ b/frontend/src/viewers/MarkdownView.tsx
@@ -42,7 +42,7 @@ const components: Components = {
export function MarkdownView({ source }: { source: string }) {
return (
-