[dyad] Improved schema display - wrote 2 file(s)

This commit is contained in:
[dyad]
2026-01-20 14:50:38 +01:00
parent be6a3a24ec
commit f901165a29
2 changed files with 183 additions and 20 deletions

View File

@@ -0,0 +1,146 @@
"use client";
import React from "react";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
Link as LinkIcon,
Type,
Image as ImageIcon,
User,
Building,
Calendar,
HelpCircle,
Hash,
Text,
} from "lucide-react";
const keyMappings: { [key: string]: string } = {
"@type": "Type",
"@context": "Context",
"@id": "ID",
name: "Name",
headline: "Headline",
description: "Description",
author: "Author",
publisher: "Publisher",
mainEntityOfPage: "Main Page",
image: "Image",
datePublished: "Date Published",
dateModified: "Date Modified",
acceptedAnswer: "Answer",
mainEntity: "Main Content",
url: "URL",
text: "Text",
question: "Question",
answer: "Answer",
logo: "Logo",
telephone: "Telephone",
email: "Email",
};
const keyIcons: { [key: string]: React.ElementType } = {
"@type": Type,
"@id": Hash,
url: LinkIcon,
image: ImageIcon,
logo: ImageIcon,
author: User,
publisher: Building,
datePublished: Calendar,
dateModified: Calendar,
question: HelpCircle,
text: Text,
};
const renderValue = (value: any): React.ReactNode => {
if (typeof value === "string") {
if (value.startsWith("http://") || value.startsWith("https://")) {
try {
const url = new URL(value);
return (
<a
href={value}
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 hover:underline break-all inline-flex items-center gap-1.5"
>
<LinkIcon className="h-3 w-3 flex-shrink-0" />
<span>
{url.hostname}
{url.pathname.length > 1 ? "/..." : ""}
</span>
</a>
);
} catch (e) {
return <span className="break-all">{value}</span>;
}
}
return <span className="break-words">{value}</span>;
}
if (typeof value === "object" && value !== null) {
if (Array.isArray(value)) {
return (
<div className="flex flex-col gap-2 pl-4 border-l ml-2 mt-2">
{value.map((item, index) => (
<div key={index}>{renderValue(item)}</div>
))}
</div>
);
}
return <SchemaObjectRenderer data={value} isNested={true} />;
}
return <span>{String(value)}</span>;
};
const SchemaObjectRenderer = ({
data,
isNested = false,
}: {
data: any;
isNested?: boolean;
}) => {
const content = (
<div className="space-y-3">
{Object.entries(data).map(([key, value]) => {
if (key === "@context") return null;
const label =
keyMappings[key] || key.charAt(0).toUpperCase() + key.slice(1);
const Icon = keyIcons[key];
return (
<div
key={key}
className="flex flex-col sm:flex-row sm:items-start gap-1 sm:gap-2"
>
<div className="flex items-center gap-2 font-semibold text-sm text-muted-foreground w-full sm:w-40 flex-shrink-0">
{Icon && <Icon className="h-4 w-4" />}
<span>{label}:</span>
</div>
<div className="flex-grow text-sm text-foreground pl-6 sm:pl-0">
{key === "@type" ? (
<Badge variant="outline">{value as string}</Badge>
) : (
renderValue(value)
)}
</div>
</div>
);
})}
</div>
);
if (isNested) {
return (
<div className="p-3 border rounded-md bg-background/50">{content}</div>
);
}
return content;
};
export function PrettySchemaDisplay({ schema }: { schema: any }) {
return <SchemaObjectRenderer data={schema} />;
}

View File

@@ -7,6 +7,7 @@ import {
AccordionTrigger,
} from "@/components/ui/accordion";
import { CopyButton } from "./copy-button";
import { PrettySchemaDisplay } from "./pretty-schema-display";
interface SchemaDisplayProps {
schemas: any[];
@@ -14,27 +15,43 @@ interface SchemaDisplayProps {
export function SchemaDisplay({ schemas }: SchemaDisplayProps) {
return (
<Accordion type="single" collapsible className="w-full">
<div className="space-y-4">
<div className="flex justify-between items-center p-4 bg-muted/50 rounded-lg">
<p className="text-sm font-medium">
Found {schemas.length} schema block{schemas.length > 1 ? "s" : ""} on
this page.
</p>
<p className="text-sm text-muted-foreground">Expand to see details.</p>
</div>
<Accordion type="single" collapsible className="w-full space-y-4">
{schemas.map((schema, index) => {
const schemaType = schema["@type"] || `Schema Block ${index + 1}`;
const schemaJson = JSON.stringify(schema, null, 2);
return (
<AccordionItem value={`item-${index}`} key={index}>
<AccordionTrigger>{schemaType}</AccordionTrigger>
<AccordionContent>
<div className="relative bg-muted/50 p-4 rounded-md">
<div className="absolute top-2 right-2">
<AccordionItem
value={`item-${index}`}
key={index}
className="border rounded-lg bg-background shadow-sm"
>
<AccordionTrigger className="px-6 hover:no-underline">
<div className="flex items-center justify-between w-full">
<span className="font-semibold">{schemaType}</span>
<div className="flex items-center gap-2 mr-2">
<span className="text-xs text-muted-foreground hidden sm:inline">
Copy Raw JSON
</span>
<CopyButton textToCopy={schemaJson} />
</div>
<pre className="text-sm overflow-x-auto">
<code>{schemaJson}</code>
</pre>
</div>
</AccordionTrigger>
<AccordionContent className="p-6 pt-0">
<PrettySchemaDisplay schema={schema} />
</AccordionContent>
</AccordionItem>
);
})}
</Accordion>
</div>
);
}