From ab6008daf9ca3ffee327bdb6cd4b2a19c4d3cb2b Mon Sep 17 00:00:00 2001 From: Usman Baig Date: Sun, 22 Mar 2026 20:52:50 +0100 Subject: [PATCH] fix(pagespeed): parse markdown links + handle more audit item fields - AuditDescription: converts [text](url) to clickable links - AuditItem: handles href, text/linkText, source.url from PSI API --- app/sites/[id]/pagespeed/page.tsx | 144 +++++++++++++++++++----------- 1 file changed, 93 insertions(+), 51 deletions(-) diff --git a/app/sites/[id]/pagespeed/page.tsx b/app/sites/[id]/pagespeed/page.tsx index af19026..8d047ae 100644 --- a/app/sites/[id]/pagespeed/page.tsx +++ b/app/sites/[id]/pagespeed/page.tsx @@ -590,63 +590,17 @@ function AuditRow({ audit }: { audit: AuditSummary }) {
- {/* Description */} + {/* Description with parsed markdown links */} {audit.description && ( -

{audit.description}

+

+ +

)} {/* Items list */} {audit.details && Array.isArray(audit.details) && audit.details.length > 0 && (
{audit.details.slice(0, 10).map((item: Record, idx: number) => ( -
- {/* Element screenshot */} - {item.node?.screenshot?.data && ( - - )} - {/* Content */} -
- {/* Label / node explanation */} - {(item.node?.nodeLabel || item.label || item.groupLabel) && ( -
- {item.node?.nodeLabel || item.label || item.groupLabel} -
- )} - {/* URL */} - {item.url && ( -
{item.url}
- )} - {/* HTML snippet */} - {item.node?.snippet && ( - {item.node.snippet} - )} - {/* Statistic-type items */} - {!item.url && !item.node && item.statistic && ( - {item.statistic} - )} -
- {/* Metrics on the right */} -
- {item.wastedBytes != null && ( -
- {item.wastedBytes < 1024 ? `${item.wastedBytes} B` : `${(item.wastedBytes / 1024).toFixed(1)} KiB`} -
- )} - {item.totalBytes != null && !item.wastedBytes && ( -
- {item.totalBytes < 1024 ? `${item.totalBytes} B` : `${(item.totalBytes / 1024).toFixed(1)} KiB`} -
- )} - {item.wastedMs != null && ( -
- {item.wastedMs < 1000 ? `${Math.round(item.wastedMs)}ms` : `${(item.wastedMs / 1000).toFixed(1)}s`} -
- )} -
-
+ ))} {audit.details.length > 10 && (

+ {audit.details.length - 10} more items

@@ -658,6 +612,94 @@ function AuditRow({ audit }: { audit: AuditSummary }) { ) } +// * Parse markdown-style links [text](url) into clickable tags +function AuditDescription({ text }: { text: string }) { + const parts = text.split(/(\[[^\]]+\]\([^)]+\))/g) + return ( + <> + {parts.map((part, i) => { + const match = part.match(/^\[([^\]]+)\]\(([^)]+)\)$/) + if (match) { + return ( + + {match[1]} + + ) + } + return {part} + })} + + ) +} + +// * Render a single audit detail item — handles various field types from the PSI API +function AuditItem({ item }: { item: Record }) { + // * Determine the primary label + const label = item.node?.nodeLabel || item.label || item.groupLabel || item.source?.url || null + // * URL can be in item.url or item.href + const url = item.url || item.href || null + // * Text content (used by SEO audits like "link text") + const text = item.text || item.linkText || null + + return ( +
+ {/* Element screenshot */} + {item.node?.screenshot?.data && ( + + )} + {/* Content */} +
+ {label && ( +
+ {label} +
+ )} + {url && ( +
{url}
+ )} + {text && ( +
{text}
+ )} + {item.node?.snippet && ( + {item.node.snippet} + )} + {/* Fallback for items with only string values we haven't handled */} + {!label && !url && !text && !item.node && item.statistic && ( + {item.statistic} + )} +
+ {/* Metrics on the right */} +
+ {item.wastedBytes != null && ( +
+ {item.wastedBytes < 1024 ? `${item.wastedBytes} B` : `${(item.wastedBytes / 1024).toFixed(1)} KiB`} +
+ )} + {item.totalBytes != null && !item.wastedBytes && ( +
+ {item.totalBytes < 1024 ? `${item.totalBytes} B` : `${(item.totalBytes / 1024).toFixed(1)} KiB`} +
+ )} + {item.wastedMs != null && ( +
+ {item.wastedMs < 1000 ? `${Math.round(item.wastedMs)}ms` : `${(item.wastedMs / 1000).toFixed(1)}s`} +
+ )} +
+
+ ) +} + // * Skeleton loading state function PageSpeedSkeleton() { return (