From e889bb35700611c0eca38f0ef01cf6a920fe5a62 Mon Sep 17 00:00:00 2001 From: "[dyad]" Date: Tue, 20 Jan 2026 14:22:07 +0100 Subject: [PATCH] [dyad] Added image alt tag analysis - wrote 3 file(s) --- src/app/actions.ts | 24 +++++++++ src/components/image-alt-display.tsx | 75 ++++++++++++++++++++++++++++ src/components/meta-form.tsx | 17 ++++++- 3 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 src/components/image-alt-display.tsx diff --git a/src/app/actions.ts b/src/app/actions.ts index dad6ec7..28ca0cd 100644 --- a/src/app/actions.ts +++ b/src/app/actions.ts @@ -15,6 +15,11 @@ export interface HeadlineNode { children: HeadlineNode[]; } +export interface ImageAltData { + src: string; + alt: string; +} + export async function extractMetaData(url: string, keyword?: string) { if (!url) { return { error: "URL is required." }; @@ -122,6 +127,24 @@ export async function extractMetaData(url: string, keyword?: string) { keywordCount = matches ? matches.length : 0; } + const imageAltData: ImageAltData[] = []; + $("img").each((i, el) => { + const src = $(el).attr("src"); + const alt = $(el).attr("alt") || ""; + + if (src) { + try { + const absoluteSrc = new URL(src, formattedUrl).href; + imageAltData.push({ + src: absoluteSrc, + alt: alt.trim(), + }); + } catch (e) { + // Ignore invalid URLs + } + } + }); + return { data: { title, @@ -131,6 +154,7 @@ export async function extractMetaData(url: string, keyword?: string) { headlines: headlines.length > 0 ? headlines : null, keyword: trimmedKeyword || null, keywordCount, + images: imageAltData.length > 0 ? imageAltData : null, }, }; } catch (error) { diff --git a/src/components/image-alt-display.tsx b/src/components/image-alt-display.tsx new file mode 100644 index 0000000..026921f --- /dev/null +++ b/src/components/image-alt-display.tsx @@ -0,0 +1,75 @@ +"use client"; + +import { useState } from "react"; +import type { ImageAltData } from "@/app/actions"; +import { Checkbox } from "@/components/ui/checkbox"; +import { Label } from "@/components/ui/label"; +import { Badge } from "@/components/ui/badge"; +import { ImageOff } from "lucide-react"; + +interface ImageAltDisplayProps { + images: ImageAltData[]; +} + +export function ImageAltDisplay({ images }: ImageAltDisplayProps) { + const [showMissingOnly, setShowMissingOnly] = useState(false); + const [imageErrors, setImageErrors] = useState>({}); + + const filteredImages = showMissingOnly + ? images.filter((img) => !img.alt) + : images; + + const handleImageError = (src: string) => { + setImageErrors((prev) => ({ ...prev, [src]: true })); + }; + + return ( +
+
+ setShowMissingOnly(!!checked)} + /> + +
+
+ {filteredImages.map((image, index) => ( +
+
+ {imageErrors[image.src] ? ( + + ) : ( + {image.alt handleImageError(image.src)} + /> + )} +
+
+

+ {image.src} +

+
+ {image.alt ? ( +

+ Alt: {image.alt} +

+ ) : ( + Missing alt text + )} +
+
+
+ ))} +
+
+ ); +} \ No newline at end of file diff --git a/src/components/meta-form.tsx b/src/components/meta-form.tsx index 4ea6ebb..cdfb4ea 100644 --- a/src/components/meta-form.tsx +++ b/src/components/meta-form.tsx @@ -14,7 +14,7 @@ import { ImageOff, Search, } from "lucide-react"; -import { extractMetaData, type HeadlineNode } from "@/app/actions"; +import { extractMetaData, type HeadlineNode, type ImageAltData } from "@/app/actions"; import { LengthIndicator } from "./length-indicator"; import { CopyButton } from "./copy-button"; import { SerpPreview } from "./serp-preview"; @@ -22,6 +22,7 @@ import { ResultsSkeleton } from "./results-skeleton"; import { FaqDisplay } from "./faq-display"; import { HeadlineTree } from "./headline-tree"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { ImageAltDisplay } from "./image-alt-display"; interface MetaData { title: string; @@ -31,6 +32,7 @@ interface MetaData { headlines?: HeadlineNode[] | null; keyword?: string | null; keywordCount?: number | null; + images?: ImageAltData[] | null; } export function MetaForm() { @@ -151,6 +153,9 @@ export function MetaForm() { {metaData.headlines && metaData.headlines.length > 0 && ( Headlines )} + {metaData.images && metaData.images.length > 0 && ( + Images + )} {metaData.faq && metaData.faq.length > 0 && ( FAQ )} @@ -335,6 +340,16 @@ export function MetaForm() { )} + {metaData.images && metaData.images.length > 0 && ( + + + + + + + + )} + {metaData.faq && metaData.faq.length > 0 && (