[dyad] Added social media tag analysis - wrote 5 file(s)

This commit is contained in:
[dyad]
2026-01-20 16:17:44 +01:00
parent 5c489c6ec7
commit e5d6580987
5 changed files with 194 additions and 0 deletions

View File

@@ -58,6 +58,28 @@ export async function extractMetaData(url: string, keyword?: string) {
const canonical = $('link[rel="canonical"]').attr("href") || null;
const robots = $('meta[name="robots"]').attr("content") || null;
// Social Tags
const openGraph = {
title: $('meta[property="og:title"]').attr("content") || title,
description:
$('meta[property="og:description"]').attr("content") || description,
image: $('meta[property="og:image"]').attr("content") || image,
url: $('meta[property="og:url"]').attr("content") || null,
siteName: $('meta[property="og:site_name"]').attr("content") || null,
type: $('meta[property="og:type"]').attr("content") || null,
};
const twitter = {
card: $('meta[name="twitter:card"]').attr("content") || null,
title: $('meta[name="twitter:title"]').attr("content") || openGraph.title,
description:
$('meta[name="twitter:description"]').attr("content") ||
openGraph.description,
image: $('meta[name="twitter:image"]').attr("content") || openGraph.image,
site: $('meta[name="twitter:site"]').attr("content") || null,
creator: $('meta[name="twitter:creator"]').attr("content") || null,
};
const faqData: FaqItem[] = [];
const schemaData: any[] = [];
$('script[type="application/ld+json"]').each((i, el) => {
@@ -177,6 +199,8 @@ export async function extractMetaData(url: string, keyword?: string) {
image,
canonical,
robots,
openGraph,
twitter,
faq: faqData.length > 0 ? faqData : null,
schema: schemaData.length > 0 ? schemaData : null,
headlines: headlines.length > 0 ? headlines : null,

View File

@@ -13,6 +13,7 @@ import { getLengthIndicatorColor, type IndicatorColor } from "@/lib/analysis";
import { SchemaDisplay } from "./schema-display";
import { MetaFormInputs } from "./meta-form-inputs";
import { AnalysisTab } from "./analysis-tab";
import { SocialTab } from "./social-tab";
import type { MetaData } from "@/lib/types";
export function MetaForm() {
@@ -90,6 +91,23 @@ export function MetaForm() {
}
}
// Social Tab
let socialColor: IndicatorColor = "gray";
if (metaData.openGraph && metaData.twitter) {
const og = metaData.openGraph;
const tw = metaData.twitter;
const hasOgBasics = og.title && og.description && og.image;
const hasTwBasics = tw.card && tw.title && tw.description && tw.image;
if (hasOgBasics && hasTwBasics) {
socialColor = "green";
} else if (hasOgBasics || (tw.card && tw.title)) {
socialColor = "yellow";
} else {
socialColor = "red";
}
}
// FAQ Tab
let faqColor: IndicatorColor = "gray";
if (metaData.faq && metaData.faq.length > 0) {
@@ -106,6 +124,7 @@ export function MetaForm() {
analysis: analysisColor,
headlines: headlinesColor,
images: imagesColor,
social: socialColor,
faq: faqColor,
schema: schemaColor,
};
@@ -181,6 +200,12 @@ export function MetaForm() {
Images
</TabsTrigger>
)}
{metaData.openGraph && metaData.twitter && (
<TabsTrigger value="social">
{tabColors && <TabIndicator color={tabColors.social} />}
Social
</TabsTrigger>
)}
{metaData.faq && metaData.faq.length > 0 && (
<TabsTrigger value="faq">
{tabColors && <TabIndicator color={tabColors.faq} />}
@@ -238,6 +263,15 @@ export function MetaForm() {
</TabsContent>
)}
{metaData.openGraph && metaData.twitter && (
<TabsContent value="social">
<SocialTab
openGraph={metaData.openGraph}
twitter={metaData.twitter}
/>
</TabsContent>
)}
{metaData.faq && metaData.faq.length > 0 && (
<TabsContent value="faq">
<Card className="w-full shadow-lg rounded-lg">

View File

@@ -0,0 +1,53 @@
"use client";
import * as React from "react";
import { ImageOff, Link as LinkIcon } from "lucide-react";
interface SocialPreviewProps {
platform: "Open Graph" | "Twitter";
title: string;
description: string;
image: string | null;
url: string | null;
}
export function SocialPreview({
platform,
title,
description,
image,
url,
}: SocialPreviewProps) {
const [imageError, setImageError] = React.useState(false);
const displayUrl = url ? new URL(url).hostname : "example.com";
return (
<div className="border rounded-lg overflow-hidden bg-card shadow-sm max-w-lg mx-auto">
<div className="aspect-video bg-muted flex items-center justify-center">
{image && !imageError ? (
<img
src={image}
alt={`${platform} preview`}
className="w-full h-full object-cover"
onError={() => setImageError(true)}
/>
) : (
<ImageOff className="h-10 w-10 text-muted-foreground" />
)}
</div>
<div className="p-4 border-t bg-background">
<p className="text-sm text-muted-foreground uppercase flex items-center gap-1.5">
<LinkIcon className="h-3 w-3" />
{displayUrl}
</p>
<h3 className="font-semibold text-card-foreground mt-1 truncate">
{title}
</h3>
<p className="text-sm text-muted-foreground mt-1 line-clamp-2">
{description}
</p>
</div>
</div>
);
}

View File

@@ -0,0 +1,63 @@
"use client";
import { Card, CardContent } from "@/components/ui/card";
import { SocialPreview } from "./social-preview";
import type { OpenGraphData, TwitterData } from "@/lib/types";
interface SocialTabProps {
openGraph: OpenGraphData;
twitter: TwitterData;
}
const DataRow = ({ label, value }: { label: string; value: string | null }) => {
if (!value) return null;
return (
<div className="flex justify-between items-center py-2 border-b text-sm">
<span className="text-muted-foreground">{label}</span>
<span className="font-medium text-right">{value}</span>
</div>
);
};
export function SocialTab({ openGraph, twitter }: SocialTabProps) {
return (
<Card className="w-full shadow-lg rounded-lg">
<CardContent className="p-6 grid grid-cols-1 lg:grid-cols-2 gap-8">
<div className="space-y-6">
<h3 className="text-xl font-bold text-card-foreground">
Open Graph
</h3>
<SocialPreview
platform="Open Graph"
title={openGraph.title}
description={openGraph.description}
image={openGraph.image}
url={openGraph.url}
/>
<div className="space-y-1">
<DataRow label="og:type" value={openGraph.type} />
<DataRow label="og:site_name" value={openGraph.siteName} />
<DataRow label="og:url" value={openGraph.url} />
</div>
</div>
<div className="space-y-6">
<h3 className="text-xl font-bold text-card-foreground">
Twitter Card
</h3>
<SocialPreview
platform="Twitter"
title={twitter.title}
description={twitter.description}
image={twitter.image}
url={openGraph.url}
/>
<div className="space-y-1">
<DataRow label="twitter:card" value={twitter.card} />
<DataRow label="twitter:site" value={twitter.site} />
<DataRow label="twitter:creator" value={twitter.creator} />
</div>
</div>
</CardContent>
</Card>
);
}

View File

@@ -1,5 +1,23 @@
import type { HeadlineNode, ImageAltData } from "@/app/actions";
export interface OpenGraphData {
title: string;
description: string;
image: string | null;
url: string | null;
siteName: string | null;
type: string | null;
}
export interface TwitterData {
card: string | null;
title: string;
description: string;
image: string | null;
site: string | null;
creator: string | null;
}
export interface MetaData {
title: string;
description: string;
@@ -12,4 +30,6 @@ export interface MetaData {
images?: ImageAltData[] | null;
canonical?: string | null;
robots?: string | null;
openGraph?: OpenGraphData;
twitter?: TwitterData;
}