From 3c5c0b523743d3ca20a7c36524bed9c5922be9e1 Mon Sep 17 00:00:00 2001 From: Payal2907 Date: Sat, 25 Jul 2026 12:48:36 +0530 Subject: [PATCH] feat(compare): add markdown export and shareable URLs --- src/pages/ComparePage.jsx | 82 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/src/pages/ComparePage.jsx b/src/pages/ComparePage.jsx index 6e7e38e..bdd4ede 100644 --- a/src/pages/ComparePage.jsx +++ b/src/pages/ComparePage.jsx @@ -1,7 +1,7 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useMemo } from 'react'; import { useApp } from '@/context/AppContext'; import { getRepo, getContributors, getCommunityProfile, searchRepos } from '@/api/github'; -import { FiPlus, FiX, FiSearch, FiStar, FiGitBranch, FiAlertCircle, FiShield, FiUsers, FiClock, FiTrash2 } from 'react-icons/fi'; +import { FiPlus, FiX, FiSearch, FiStar, FiGitBranch, FiAlertCircle, FiShield, FiUsers, FiClock, FiTrash2, FiCopy, FiShare2, } from 'react-icons/fi'; import { formatNumber } from '@/utils/format'; function formatDate(d) { @@ -47,6 +47,23 @@ export default function ComparePage() { loadAll(); }, [compareList]); + useEffect(() => { + const params = new URLSearchParams(window.loacation.search); + + const repos = params.get("repos"); + + if (!repos) return; + + repos.split(",").forEach(async(fullName) => { + const [owner, repo] = fullName.split("/"); + + try{ + const repository = await getRepo(owner, repo); + addToCompare(repository); + } catch (_) {} + }); + }, [addToCompare]); + const handleSearch = async (e) => { e.preventDefault(); if (!searchQuery.trim()) return; @@ -79,6 +96,47 @@ export default function ComparePage() { { key: 'created', label: 'Created', icon: , getValue: (d) => formatDate(d?.repo?.created_at) }, ]; + const markdownTable = useMemo(() => { + if (compareList.length === 0) return ""; + + let md = "| Metric |"; + + compareList.forEach(repo => { + md += ` ${repo.name} |`; + }); + + md += "\n|---|"; + compareList.forEach(() => { + md += "---|"; + }); + + COMPARE_METRICS.forEach(metric => { + md += `\n| ${metric.label} |`; + + compareList.forEach(repo => { + md += ` ${metric.getValue(repoData[repo.full_name])} |`; + }); + }); + + return md; +}, [compareList, repoData]); + +const copyMarkdown = async () => { + await navigator.clipboard.writeText(markdownTable); + alert("Markdown copied!"); +}; + +const shareComparison = async () => { + const repos = compareList.map(r => r.full_name).join(","); + + const url = + `${window.location.origin}/compare?repos=${encodeURIComponent(repos)}`; + + await navigator.clipboard.writeText(url); + + alert("Shareable link copied!"); +}; + return (
@@ -174,6 +232,26 @@ export default function ComparePage() { )}
)} + +
+ + + +
+ {/* Comparison Table */} {compareList.length > 0 && Object.keys(repoData).length > 0 && !loading && (