feat(frontend): add per-page SEO, 404 page, OG tags, and sitemap updates

- Replace react-helmet-async (React 19 incompatible) with native React 19
  document metadata hoisting across all pages
- Add per-page <title>, <meta name="description">, <link rel="canonical">,
  og:title, og:description, og:url to HomePage, ProductsPage, ProductDetailPage,
  AboutPage, DisclaimerPage, PrivacyPolicyPage
- Add JSON-LD SoftwareApplication schema to HomePage and BreadcrumbList to
  ProductDetailPage
- Add 404 NotFoundPage with noindex meta and canonical to home
- Add og:site_name, og:image, og:type, twitter:card/title/description/image
  to index.html as static fallback; remove duplicate static canonical
- Update sitemap.xml with product IDs 3321, 3324 and lastmod dates
- Add product screenshots to README and update description
- Add og-image.png placeholder

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Shekhar Vaidya 2026-05-12 20:35:48 +05:30
parent d0a70496da
commit 86da27cb8c
16 changed files with 830 additions and 729 deletions

View file

@ -21,9 +21,15 @@ MSDL replicates the session flow that Microsoft uses to serve ISO download links
---
## Screenshot
## Screenshots
> _Coming soon_
| Home | All Releases |
|---|---|
| ![Home page showing featured Windows releases](frontend/public/screenshots/msdl-1.jpg) | ![Products listing page with search](frontend/public/screenshots/msdl-2.jpg) |
| Product detail — language selector | Product detail — download links |
|---|---|
| ![Product detail page with language dropdown](frontend/public/screenshots/msdl-3.jpg) | ![Download links with expiry warning](frontend/public/screenshots/msdl-4.jpg) |
---

View file

@ -6,12 +6,17 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Windows ISO Downloader | Official Microsoft Images</title>
<meta name="description" content="Download official Microsoft Windows ISO files for Windows 11, 10, and 8.1. Fast, free, no registration required." />
<meta property="og:site_name" content="MSDL — Windows ISO Downloader" />
<meta property="og:title" content="Windows ISO Downloader | Official Microsoft Images" />
<meta property="og:description" content="Download official Microsoft Windows ISO files. Fast, free, no registration." />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://msdl.tech-latest.com/" />
<meta property="og:image" content="https://msdl.tech-latest.com/og-image.png" />
<meta name="twitter:card" content="summary_large_image" />
<link rel="canonical" href="https://msdl.tech-latest.com/" />
<meta name="theme-color" content="#111113" />
<meta name="twitter:title" content="Windows ISO Downloader | Official Microsoft Images" />
<meta name="twitter:description" content="Download official Microsoft Windows ISO files. Fast, free, no registration." />
<meta name="twitter:image" content="https://msdl.tech-latest.com/og-image.png" />
<meta name="theme-color" content="#111113" />
</head>
<body>
<div id="root"></div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 693 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

View file

@ -3,45 +3,52 @@
<!-- Main Static Pages -->
<url>
<loc>https://msdl.tech-latest.com/</loc>
<lastmod>2026-05-12</lastmod>
<priority>1.0</priority>
<changefreq>weekly</changefreq>
</url>
<url>
<loc>https://msdl.tech-latest.com/products</loc>
<lastmod>2026-05-12</lastmod>
<priority>0.9</priority>
<changefreq>weekly</changefreq>
</url>
<url>
<loc>https://msdl.tech-latest.com/about</loc>
<lastmod>2026-04-09</lastmod>
<priority>0.5</priority>
<changefreq>monthly</changefreq>
</url>
<url>
<loc>https://msdl.tech-latest.com/privacy-policy</loc>
<lastmod>2026-04-09</lastmod>
<priority>0.3</priority>
<changefreq>yearly</changefreq>
</url>
<url>
<loc>https://msdl.tech-latest.com/disclaimer</loc>
<lastmod>2026-04-09</lastmod>
<priority>0.3</priority>
<changefreq>yearly</changefreq>
</url>
<!-- Product Pages -->
<url><loc>https://msdl.tech-latest.com/product/48</loc><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/52</loc><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/2378</loc><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/2618</loc><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3113</loc><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3114</loc><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3115</loc><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3131</loc><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3132</loc><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3133</loc><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3262</loc><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3263</loc><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3264</loc><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3265</loc><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3266</loc><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3267</loc><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/48</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/52</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/2378</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/2618</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3113</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3114</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3115</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3131</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3132</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3133</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3262</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3263</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3264</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3265</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3266</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3267</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3321</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
<url><loc>https://msdl.tech-latest.com/product/3324</loc><lastmod>2026-05-12</lastmod><priority>0.8</priority><changefreq>monthly</changefreq></url>
</urlset>

View file

@ -1,4 +1,4 @@
import { Routes, Route, Link } from 'react-router-dom'
import { Routes, Route } from 'react-router-dom'
import { Toaster } from 'sonner'
import Dock from './components/Dock'
import SiteFooter from './components/SiteFooter'
@ -8,6 +8,7 @@ import ProductDetailPage from './pages/ProductDetailPage'
import AboutPage from './pages/AboutPage'
import PrivacyPolicyPage from './pages/PrivacyPolicyPage'
import DisclaimerPage from './pages/DisclaimerPage'
import NotFoundPage from './pages/NotFoundPage'
import ScrollToTop from './components/ScrollToTop'
export default function App() {
@ -22,15 +23,7 @@ export default function App() {
<Route path="/about" element={<AboutPage />} />
<Route path="/privacy-policy" element={<PrivacyPolicyPage />} />
<Route path="/disclaimer" element={<DisclaimerPage />} />
<Route path="*" element={
<div className="flex flex-col items-center justify-center min-h-[60vh] text-center px-4">
<h1 className="text-6xl font-bold text-white mb-4">404</h1>
<p className="text-zinc-400 mb-8">The page you're looking for doesn't exist.</p>
<Link to="/" className="px-6 py-2 bg-white text-black font-semibold rounded-full hover:bg-zinc-200 transition-colors">
Go Home
</Link>
</div>
} />
<Route path="*" element={<NotFoundPage />} />
</Routes>
</main>
<SiteFooter />

View file

@ -1,6 +1,7 @@
import { useEffect } from 'react'
import { motion } from 'motion/react'
const SITE_URL = 'https://msdl.tech-latest.com'
function Section({ title, children }: { title: string; children: React.ReactNode }) {
return (
<div className="space-y-2.5">
@ -11,107 +12,110 @@ function Section({ title, children }: { title: string; children: React.ReactNode
}
export default function AboutPage() {
useEffect(() => {
document.title = "About MSDL | Windows ISO Downloader"
const metaDesc = document.querySelector('meta[name="description"]')
if (metaDesc) metaDesc.setAttribute('content', "Learn about MSDL, an open-source tool for fetching official Windows ISOs directly from Microsoft.")
}, [])
return (
<div className="max-w-2xl mx-auto px-4 pt-12 pb-10">
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.35 }}
className="rounded-2xl border border-white/7 bg-[#111113] p-8 space-y-7"
>
<div>
<h1 className="text-2xl font-bold text-white mb-1">About MSDL</h1>
<p className="text-xs font-mono text-zinc-600">A TechLatest open-source project</p>
</div>
<>
<title>About MSDL | Windows ISO Downloader</title>
<meta name="description" content="Learn about MSDL, an open-source tool for fetching official Windows ISOs directly from Microsoft." />
<link rel="canonical" href={`${SITE_URL}/about`} />
<meta property="og:title" content="About MSDL | Windows ISO Downloader" />
<meta property="og:description" content="Learn about MSDL, an open-source tool for fetching official Windows ISOs directly from Microsoft." />
<meta property="og:url" content={`${SITE_URL}/about`} />
<Section title="What is this?">
<p>
MSDL (Microsoft Software Download Links) is a clean, open-source web tool for
obtaining official Windows ISO files directly from Microsoft's content delivery network
without needing a Windows machine, the Media Creation Tool, or a browser lock check.
</p>
<p>
There are no third-party hosts, no redirects, no registration, and no ads.
Every link you receive is exactly the same signed URL you would get from
Microsoft's own download page.
</p>
</Section>
<div className="max-w-2xl mx-auto px-4 pt-12 pb-10">
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.35 }}
className="rounded-2xl border border-white/7 bg-[#111113] p-8 space-y-7"
>
<div>
<h1 className="text-2xl font-bold text-white mb-1">About MSDL</h1>
<p className="text-xs font-mono text-zinc-600">A TechLatest open-source project</p>
</div>
<Section title="How it works">
<p>
Our backend replicates the session-based authentication flow that Microsoft uses to
serve download links to end users. The same approach is used by{' '}
<a
href="https://github.com/pbatard/Fido"
target="_blank"
rel="noopener noreferrer"
className="text-blue-400 hover:text-blue-300 transition-colors"
>
Fido
</a>{' '}
(the PowerShell script bundled with Rufus). The flow is:
</p>
<ol className="list-decimal list-inside space-y-1 text-zinc-500 text-sm">
<li>Register a session with Microsoft's tracking endpoint</li>
<li>Fetch and parse the MDT fingerprinting script</li>
<li>Call the SKU info API to retrieve available languages</li>
<li>Call the download links API using the warmed session to get signed CDN URLs</li>
</ol>
<p>
Links are IP-tied to the server and expire after 24 hours this is standard
Microsoft behaviour, not a limitation of MSDL.
</p>
</Section>
<Section title="What is this?">
<p>
MSDL (Microsoft Software Download Links) is a clean, open-source web tool for
obtaining official Windows ISO files directly from Microsoft's content delivery network
without needing a Windows machine, the Media Creation Tool, or a browser lock check.
</p>
<p>
There are no third-party hosts, no redirects, no registration, and no ads.
Every link you receive is exactly the same signed URL you would get from
Microsoft's own download page.
</p>
</Section>
<Section title="Data source">
<p>
All product data (release names, build numbers, and available architectures) is sourced
from Microsoft's official software download connector API:
</p>
<p>
<code className="text-[11px] font-mono bg-white/5 border border-white/7 px-2 py-0.5 rounded text-zinc-400 break-all">
www.microsoft.com/software-download-connector/api/
</code>
</p>
<p>
Product IDs are maintained manually based on Microsoft's release cadence.
Windows 11 25H2, 24H2, Windows 10 22H2, and Windows 8.1 are currently listed.
New releases are added as Microsoft publishes them.
</p>
</Section>
<Section title="How it works">
<p>
Our backend replicates the session-based authentication flow that Microsoft uses to
serve download links to end users. The same approach is used by{' '}
<a
href="https://github.com/pbatard/Fido"
target="_blank"
rel="noopener noreferrer"
className="text-blue-400 hover:text-blue-300 transition-colors"
>
Fido
</a>{' '}
(the PowerShell script bundled with Rufus). The flow is:
</p>
<ol className="list-decimal list-inside space-y-1 text-zinc-500 text-sm">
<li>Register a session with Microsoft's tracking endpoint</li>
<li>Fetch and parse the MDT fingerprinting script</li>
<li>Call the SKU info API to retrieve available languages</li>
<li>Call the download links API using the warmed session to get signed CDN URLs</li>
</ol>
<p>
Links are IP-tied to the server and expire after 24 hours this is standard
Microsoft behaviour, not a limitation of MSDL.
</p>
</Section>
<Section title="Credits">
<p>
The underlying session flow is inspired by{' '}
<a
href="https://github.com/pbatard/Fido"
target="_blank"
rel="noopener noreferrer"
className="text-blue-400 hover:text-blue-300 transition-colors"
>
Fido
</a>{' '}
by Pete Batard, the same mechanism powering Rufus's ISO download feature.
</p>
<p>
Built and maintained by{' '}
<a
href="https://tech-latest.com"
target="_blank"
rel="noopener noreferrer"
className="text-blue-400 hover:text-blue-300 transition-colors"
>
TechLatest
</a>.
</p>
</Section>
</motion.div>
</div>
<Section title="Data source">
<p>
All product data (release names, build numbers, and available architectures) is sourced
from Microsoft's official software download connector API:
</p>
<p>
<code className="text-[11px] font-mono bg-white/5 border border-white/7 px-2 py-0.5 rounded text-zinc-400 break-all">
www.microsoft.com/software-download-connector/api/
</code>
</p>
<p>
Product IDs are maintained manually based on Microsoft's release cadence.
Windows 11 25H2, 24H2, Windows 10 22H2, and Windows 8.1 are currently listed.
New releases are added as Microsoft publishes them.
</p>
</Section>
<Section title="Credits">
<p>
The underlying session flow is inspired by{' '}
<a
href="https://github.com/pbatard/Fido"
target="_blank"
rel="noopener noreferrer"
className="text-blue-400 hover:text-blue-300 transition-colors"
>
Fido
</a>{' '}
by Pete Batard, the same mechanism powering Rufus's ISO download feature.
</p>
<p>
Built and maintained by{' '}
<a
href="https://tech-latest.com"
target="_blank"
rel="noopener noreferrer"
className="text-blue-400 hover:text-blue-300 transition-colors"
>
TechLatest
</a>.
</p>
</Section>
</motion.div>
</div>
</>
)
}

View file

@ -1,6 +1,7 @@
import { useEffect } from 'react'
import { motion } from 'motion/react'
const SITE_URL = 'https://msdl.tech-latest.com'
function Section({ title, children }: { title: string; children: React.ReactNode }) {
return (
<div className="space-y-2">
@ -11,72 +12,75 @@ function Section({ title, children }: { title: string; children: React.ReactNode
}
export default function DisclaimerPage() {
useEffect(() => {
document.title = "Disclaimer | Windows ISO Downloader"
const metaDesc = document.querySelector('meta[name="description"]')
if (metaDesc) metaDesc.setAttribute('content', "Legal disclaimer for MSDL. Not affiliated with Microsoft Corporation.")
}, [])
return (
<div className="max-w-2xl mx-auto px-4 pt-12 pb-10">
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.35 }}
className="rounded-2xl border border-white/7 bg-[#111113] p-8 space-y-7"
>
<div>
<h1 className="text-2xl font-bold text-white mb-1">Disclaimer</h1>
<p className="text-xs font-mono text-zinc-600">Last updated: April 9, 2026</p>
</div>
<>
<title>Disclaimer | Windows ISO Downloader</title>
<meta name="description" content="Legal disclaimer for MSDL. Not affiliated with Microsoft Corporation." />
<link rel="canonical" href={`${SITE_URL}/disclaimer`} />
<meta property="og:title" content="Disclaimer | Windows ISO Downloader" />
<meta property="og:description" content="Legal disclaimer for MSDL. Not affiliated with Microsoft Corporation." />
<meta property="og:url" content={`${SITE_URL}/disclaimer`} />
<div className="p-4 rounded-xl border border-amber-500/15 bg-amber-500/6 text-amber-400/80 text-[12px] leading-relaxed">
This open-source project is <strong className="text-amber-400">not affiliated with, endorsed by, or sponsored by
Microsoft Corporation</strong> in any way. Windows is a registered trademark of Microsoft Corporation.
</div>
<div className="max-w-2xl mx-auto px-4 pt-12 pb-10">
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.35 }}
className="rounded-2xl border border-white/7 bg-[#111113] p-8 space-y-7"
>
<div>
<h1 className="text-2xl font-bold text-white mb-1">Disclaimer</h1>
<p className="text-xs font-mono text-zinc-600">Last updated: April 9, 2026</p>
</div>
<Section title="No affiliation with Microsoft">
<p>
MSDL is an independent, open-source project. It is not produced, approved, or supported
by Microsoft Corporation. The name "Windows" and the Windows logo are registered
trademarks of Microsoft Corporation.
</p>
</Section>
<div className="p-4 rounded-xl border border-amber-500/15 bg-amber-500/6 text-amber-400/80 text-[12px] leading-relaxed">
This open-source project is <strong className="text-amber-400">not affiliated with, endorsed by, or sponsored by
Microsoft Corporation</strong> in any way. Windows is a registered trademark of Microsoft Corporation.
</div>
<Section title="Purpose">
<p>
MSDL exists solely to make it easier for users to access official, unmodified Microsoft
Windows ISO files that Microsoft already makes freely available for download. All files
are served directly from Microsoft's own servers we do not host, mirror, or modify
any content.
</p>
</Section>
<Section title="No affiliation with Microsoft">
<p>
MSDL is an independent, open-source project. It is not produced, approved, or supported
by Microsoft Corporation. The name "Windows" and the Windows logo are registered
trademarks of Microsoft Corporation.
</p>
</Section>
<Section title="No warranty">
<p>
This tool is provided "as is" without warranty of any kind. We make no guarantees
about the availability of Microsoft's download links, the uptime of our backend, or
the compatibility of any downloaded ISO with your hardware configuration.
</p>
</Section>
<Section title="Purpose">
<p>
MSDL exists solely to make it easier for users to access official, unmodified Microsoft
Windows ISO files that Microsoft already makes freely available for download. All files
are served directly from Microsoft's own servers we do not host, mirror, or modify
any content.
</p>
</Section>
<Section title="Use at your own risk">
<p>
By using this tool, you agree that you are solely responsible for complying with
Microsoft's End User License Agreement (EULA) and any applicable laws in your jurisdiction.
Downloading Windows does not grant you a license to use it a valid product key or
digital license is required for activation.
</p>
</Section>
<Section title="No warranty">
<p>
This tool is provided "as is" without warranty of any kind. We make no guarantees
about the availability of Microsoft's download links, the uptime of our backend, or
the compatibility of any downloaded ISO with your hardware configuration.
</p>
</Section>
<Section title="Open source">
<p>
The source code is publicly available. You are free to inspect, fork, and self-host
this project under the terms of its open-source license. The project credits the
open-source Fido script by Pete Batard for the underlying session flow.
</p>
</Section>
</motion.div>
</div>
<Section title="Use at your own risk">
<p>
By using this tool, you agree that you are solely responsible for complying with
Microsoft's End User License Agreement (EULA) and any applicable laws in your jurisdiction.
Downloading Windows does not grant you a license to use it a valid product key or
digital license is required for activation.
</p>
</Section>
<Section title="Open source">
<p>
The source code is publicly available. You are free to inspect, fork, and self-host
this project under the terms of its open-source license. The project credits the
open-source Fido script by Pete Batard for the underlying session flow.
</p>
</Section>
</motion.div>
</div>
</>
)
}

View file

@ -8,6 +8,8 @@ import HowItWorks from '../components/HowItWorks'
import ComparisonTable from '../components/ComparisonTable'
import FAQAccordion from '../components/FAQAccordion'
const SITE_URL = 'https://msdl.tech-latest.com'
const featured = [
{
id: '3262',
@ -57,16 +59,21 @@ const cardVariant = {
show: { opacity: 1, y: 0, transition: { type: 'spring' as const, stiffness: 280, damping: 28 } },
}
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'SoftwareApplication',
name: 'MSDL — Windows ISO Downloader',
applicationCategory: 'UtilitiesApplication',
operatingSystem: 'Web',
url: SITE_URL,
description: 'Download official Microsoft Windows ISO files for Windows 11, 10, and 8.1. Direct links from Microsoft CDN. Free, no registration.',
offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' },
}
export default function HomePage() {
const [totalReleases, setTotalReleases] = useState(16)
useEffect(() => {
document.title = "Windows ISO Downloader | Official Microsoft Images"
const metaDesc = document.querySelector('meta[name="description"]')
if (metaDesc) {
metaDesc.setAttribute('content', "Download official Microsoft Windows ISO files. Fast, free, no registration required.")
}
fetch('/data/products.json')
.then(r => r.json())
.then((data: Record<string, { active?: boolean }>) => {
@ -77,98 +84,108 @@ export default function HomePage() {
}, [])
return (
<div className="max-w-4xl mx-auto px-5 pt-20 pb-16 relative" style={{ overflowX: 'hidden' }}>
<>
<title>Windows ISO Downloader | Official Microsoft Images</title>
<meta name="description" content="Download official Microsoft Windows ISO files for Windows 11, 10, and 8.1. Fast, free, no registration required." />
<link rel="canonical" href={`${SITE_URL}/`} />
<meta property="og:title" content="Windows ISO Downloader | Official Microsoft Images" />
<meta property="og:description" content="Download official Microsoft Windows ISO files. Fast, free, no registration." />
<meta property="og:url" content={`${SITE_URL}/`} />
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
{/* Ambient glow — radial-gradient used over blur-3xl to optimize Mobile Lighthouse (Speed Index bypass) */}
<div
className="fixed top-0 left-1/2 -translate-x-1/2 w-[800px] h-[400px] pointer-events-none -z-10"
style={{ background: 'radial-gradient(ellipse at center, rgba(59,130,246,0.10) 0%, transparent 60%)' }}
/>
<div className="max-w-4xl mx-auto px-5 pt-20 pb-16 relative" style={{ overflowX: 'hidden' }}>
{/* Hero */}
<motion.div
className="text-center mb-6"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<p className="text-[11px] font-mono font-semibold tracking-[0.22em] uppercase text-blue-400 mb-5">
Official Microsoft ISOs
</p>
<h1
className="font-bold text-white tracking-tight leading-tight mb-4"
style={{ fontSize: 'clamp(1.6rem, 7.5vw, 3.75rem)' }}
{/* Ambient glow — radial-gradient used over blur-3xl to optimize Mobile Lighthouse (Speed Index bypass) */}
<div
className="fixed top-0 left-1/2 -translate-x-1/2 w-[800px] h-[400px] pointer-events-none -z-10"
style={{ background: 'radial-gradient(ellipse at center, rgba(59,130,246,0.10) 0%, transparent 60%)' }}
/>
{/* Hero */}
<motion.div
className="text-center mb-6"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
Windows ISO <span className="text-white/50">Downloader</span>
</h1>
<p className="text-zinc-400 text-base max-w-sm mx-auto mb-6 leading-relaxed">
Direct links from Microsoft's CDN. No ads. No registration.
</p>
<div className="flex items-center justify-center gap-4 text-[12px] text-zinc-500">
<span className="flex items-center gap-1.5">
<span className="w-1.5 h-1.5 rounded-full bg-green-500 inline-block" />
Always official
</span>
<span className="text-zinc-700">·</span>
<span className="flex items-center gap-1.5">
<span className="w-1.5 h-1.5 rounded-full bg-blue-500 inline-block" />
Real-time links
</span>
<span className="text-zinc-700">·</span>
<span className="flex items-center gap-1.5">
<span className="w-1.5 h-1.5 rounded-full bg-zinc-600 inline-block" />
Free forever
</span>
<p className="text-[11px] font-mono font-semibold tracking-[0.22em] uppercase text-blue-400 mb-5">
Official Microsoft ISOs
</p>
<h1
className="font-bold text-white tracking-tight leading-tight mb-4"
style={{ fontSize: 'clamp(1.6rem, 7.5vw, 3.75rem)' }}
>
Windows ISO <span className="text-white/50">Downloader</span>
</h1>
<p className="text-zinc-400 text-base max-w-sm mx-auto mb-6 leading-relaxed">
Direct links from Microsoft's CDN. No ads. No registration.
</p>
<div className="flex items-center justify-center gap-4 text-[12px] text-zinc-500">
<span className="flex items-center gap-1.5">
<span className="w-1.5 h-1.5 rounded-full bg-green-500 inline-block" />
Always official
</span>
<span className="text-zinc-700">·</span>
<span className="flex items-center gap-1.5">
<span className="w-1.5 h-1.5 rounded-full bg-blue-500 inline-block" />
Real-time links
</span>
<span className="text-zinc-700">·</span>
<span className="flex items-center gap-1.5">
<span className="w-1.5 h-1.5 rounded-full bg-zinc-600 inline-block" />
Free forever
</span>
</div>
</motion.div>
{/* Stats bar */}
<div className="mb-10">
<StatsBar />
</div>
</motion.div>
{/* Stats bar */}
<div className="mb-10">
<StatsBar />
</div>
{/* Featured grid */}
<motion.div
className="grid grid-cols-1 sm:grid-cols-2 gap-3 mb-8"
variants={container}
initial="hidden"
animate="show"
>
{featured.map(product => (
<motion.div key={product.id} variants={cardVariant}>
<Link to={`/product/${product.id}`} className="block outline-none">
<ProductCard
{...product}
/>
</Link>
</motion.div>
))}
</motion.div>
{/* Browse all CTA */}
<motion.div
className="text-center"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.5 }}
>
<Link
to="/products"
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl border border-white/8 bg-white/4 text-sm text-zinc-400 hover:text-white hover:border-white/15 hover:bg-white/7 transition-all duration-200"
{/* Featured grid */}
<motion.div
className="grid grid-cols-1 sm:grid-cols-2 gap-3 mb-8"
variants={container}
initial="hidden"
animate="show"
>
Browse all {totalReleases} releases
<ArrowRight size={14} />
</Link>
</motion.div>
{featured.map(product => (
<motion.div key={product.id} variants={cardVariant}>
<Link to={`/product/${product.id}`} className="block outline-none">
<ProductCard
{...product}
/>
</Link>
</motion.div>
))}
</motion.div>
{/* How it works */}
<HowItWorks />
{/* Browse all CTA */}
<motion.div
className="text-center"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.5 }}
>
<Link
to="/products"
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-xl border border-white/8 bg-white/4 text-sm text-zinc-400 hover:text-white hover:border-white/15 hover:bg-white/7 transition-all duration-200"
>
Browse all {totalReleases} releases
<ArrowRight size={14} />
</Link>
</motion.div>
{/* Comparison table */}
<ComparisonTable />
{/* How it works */}
<HowItWorks />
{/* FAQ */}
<FAQAccordion />
</div>
{/* Comparison table */}
<ComparisonTable />
{/* FAQ */}
<FAQAccordion />
</div>
</>
)
}

View file

@ -0,0 +1,22 @@
import { useEffect } from 'react'
import { Link } from 'react-router-dom'
export default function NotFoundPage() {
useEffect(() => { window.scrollTo(0, 0) }, [])
return (
<>
<title>404 Not Found | Windows ISO Downloader</title>
<meta name="description" content="The page you are looking for does not exist." />
<meta name="robots" content="noindex" />
<link rel="canonical" href="https://msdl.tech-latest.com/" />
<div className="flex flex-col items-center justify-center min-h-[60vh] text-center px-4">
<h1 className="text-6xl font-bold text-white mb-4">404</h1>
<p className="text-zinc-400 mb-8">The page you're looking for doesn't exist.</p>
<Link to="/" className="px-6 py-2 bg-white text-black font-semibold rounded-full hover:bg-zinc-200 transition-colors">
Go Home
</Link>
</div>
</>
)
}

View file

@ -1,6 +1,7 @@
import { useEffect } from 'react'
import { motion } from 'motion/react'
const SITE_URL = 'https://msdl.tech-latest.com'
function Section({ title, children }: { title: string; children: React.ReactNode }) {
return (
<div className="space-y-2">
@ -11,75 +12,78 @@ function Section({ title, children }: { title: string; children: React.ReactNode
}
export default function PrivacyPolicyPage() {
useEffect(() => {
document.title = "Privacy Policy | Windows ISO Downloader"
const metaDesc = document.querySelector('meta[name="description"]')
if (metaDesc) metaDesc.setAttribute('content', "Privacy policy for MSDL. We log no data, use no trackers, and proxy no files.")
}, [])
return (
<div className="max-w-2xl mx-auto px-4 pt-12 pb-10">
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.35 }}
className="rounded-2xl border border-white/7 bg-[#111113] p-8 space-y-7"
>
<div>
<h1 className="text-2xl font-bold text-white mb-1">Privacy Policy</h1>
<p className="text-xs font-mono text-zinc-600">Last updated: April 9, 2026</p>
</div>
<>
<title>Privacy Policy | Windows ISO Downloader</title>
<meta name="description" content="Privacy policy for MSDL. We log no data, use no trackers, and proxy no files." />
<link rel="canonical" href={`${SITE_URL}/privacy-policy`} />
<meta property="og:title" content="Privacy Policy | Windows ISO Downloader" />
<meta property="og:description" content="Privacy policy for MSDL. We log no data, use no trackers, and proxy no files." />
<meta property="og:url" content={`${SITE_URL}/privacy-policy`} />
<Section title="No data collected">
<p>
MSDL does not collect, store, log, or share any personally identifiable information.
There are no user accounts, no cookies, no analytics trackers, and no third-party
advertising scripts of any kind.
</p>
</Section>
<div className="max-w-2xl mx-auto px-4 pt-12 pb-10">
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.35 }}
className="rounded-2xl border border-white/7 bg-[#111113] p-8 space-y-7"
>
<div>
<h1 className="text-2xl font-bold text-white mb-1">Privacy Policy</h1>
<p className="text-xs font-mono text-zinc-600">Last updated: April 9, 2026</p>
</div>
<Section title="How download links work">
<p>
When you request a download link, our backend server contacts Microsoft's software
download API on your behalf using a non-Windows browser user-agent (the same method
used by open-source tools like Rufus/Fido). Microsoft's servers return a time-limited,
IP-tied signed URL.
</p>
<p>
Our backend does not log your IP address, does not store the generated link, and does
not proxy the actual file download the ISO is downloaded directly from Microsoft's CDN.
</p>
</Section>
<Section title="No data collected">
<p>
MSDL does not collect, store, log, or share any personally identifiable information.
There are no user accounts, no cookies, no analytics trackers, and no third-party
advertising scripts of any kind.
</p>
</Section>
<Section title="Third-party services">
<p>
All Windows ISO files are hosted on Microsoft's official content delivery network
(<code className="text-zinc-400 text-[11px] font-mono bg-white/5 px-1 py-0.5 rounded">software.download.prss.microsoft.com</code>).
We are not responsible for Microsoft's privacy practices or link availability.
</p>
</Section>
<Section title="How download links work">
<p>
When you request a download link, our backend server contacts Microsoft's software
download API on your behalf using a non-Windows browser user-agent (the same method
used by open-source tools like Rufus/Fido). Microsoft's servers return a time-limited,
IP-tied signed URL.
</p>
<p>
Our backend does not log your IP address, does not store the generated link, and does
not proxy the actual file download the ISO is downloaded directly from Microsoft's CDN.
</p>
</Section>
<Section title="Open source">
<p>
The full source code for both the frontend and backend is publicly available.
You can inspect, audit, or self-host the entire stack.
</p>
</Section>
<Section title="Third-party services">
<p>
All Windows ISO files are hosted on Microsoft's official content delivery network
(<code className="text-zinc-400 text-[11px] font-mono bg-white/5 px-1 py-0.5 rounded">software.download.prss.microsoft.com</code>).
We are not responsible for Microsoft's privacy practices or link availability.
</p>
</Section>
<Section title="Contact">
<p>
Questions? Reach us via{' '}
<a
href="https://tech-latest.com/contact-us/"
target="_blank"
rel="noopener noreferrer"
className="text-blue-400 hover:text-blue-300 transition-colors"
>
TechLatest
</a>.
</p>
</Section>
</motion.div>
</div>
<Section title="Open source">
<p>
The full source code for both the frontend and backend is publicly available.
You can inspect, audit, or self-host the entire stack.
</p>
</Section>
<Section title="Contact">
<p>
Questions? Reach us via{' '}
<a
href="https://tech-latest.com/contact-us/"
target="_blank"
rel="noopener noreferrer"
className="text-blue-400 hover:text-blue-300 transition-colors"
>
TechLatest
</a>.
</p>
</Section>
</motion.div>
</div>
</>
)
}

View file

@ -10,6 +10,7 @@ import Aria2Tip from '../components/Aria2Tip'
import RelatedReleases from '../components/RelatedReleases'
const API_BASE = import.meta.env.VITE_API_URL ?? 'http://localhost:3002'
const SITE_URL = 'https://msdl.tech-latest.com'
interface ProductCatalogEntry {
name: string
@ -21,7 +22,7 @@ interface ProductCatalogEntry {
function WinLogo({ className }: { className?: string }) {
return (
<svg viewBox="0 0 88 88" className={className} fill="currentColor">
<svg viewBox="0 0 88 88" className={className} fill="currentColor" aria-hidden="true">
<path d="M0 12.402l35.687-4.86.016 34.423-35.67.203zm35.67 33.529l.028 34.453L.028 75.48.026 45.7zm4.326-39.025L87.314 0v41.527l-47.318.376zm47.329 39.349-.011 41.34-47.318-6.678-.066-34.739z" />
</svg>
)
@ -56,10 +57,39 @@ export default function ProductDetailPage() {
const [isNotFound, setIsNotFound] = useState(false)
const [isValidated, setIsValidated] = useState(false)
const [hasCatalogError, setHasCatalogError] = useState(false)
const [meta, setMeta] = useState<{ badge: string; archs: string[]; active: boolean }>({ badge: '', archs: [], active: false })
const [related, setRelated] = useState<{ id: string; label: string }[]>([])
// Derived SEO values
const pageTitle = isNotFound
? 'Product Not Found | Windows ISO Downloader'
: hasCatalogError
? 'Error | Windows ISO Downloader'
: productName
? `${productName}${!meta.active ? ' (Discontinued)' : ''} ISO Download | Windows ISO Downloader`
: 'Loading... | Windows ISO Downloader'
const pageDescription = isNotFound
? 'The requested product could not be found.'
: hasCatalogError
? 'Failed to load product catalog.'
: productName
? `Download official ISO image for ${productName}. Direct links pulled securely from Microsoft CDN servers.`
: ''
const canonicalUrl = `${SITE_URL}/product/${productId}`
const breadcrumbJsonLd = productName && !isNotFound && !hasCatalogError ? {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: [
{ '@type': 'ListItem', position: 1, name: 'Home', item: SITE_URL },
{ '@type': 'ListItem', position: 2, name: 'All Releases', item: `${SITE_URL}/products` },
{ '@type': 'ListItem', position: 3, name: productName, item: canonicalUrl },
],
} : null
// Load product name + build string
useEffect(() => {
setBuildStr('')
@ -83,13 +113,10 @@ export default function ProductDetailPage() {
})
.then((data: Record<string, ProductCatalogEntry>) => {
const product = data[productId!]
if (!product || !product.name) {
setIsNotFound(true)
setProductName('Product Not Found')
document.title = 'Product Not Found | Windows ISO Downloader'
const descMeta = document.querySelector('meta[name="description"]')
if (descMeta) descMeta.setAttribute('content', 'The requested product could not be found.')
return
}
@ -97,22 +124,15 @@ export default function ProductDetailPage() {
setProductName(name)
const isActive = product.active !== false
setMeta({ badge: product.badge || '', archs: product.archs || [], active: isActive })
setRelated((product.related || []).map(rId => ({
id: rId,
label: data[rId]?.name || `Product ${rId}`
})))
if (isActive) {
setIsValidated(true)
}
// Dynamic SEO injection
document.title = `${name} ISO Download | Windows ISO Downloader`
const descMeta = document.querySelector('meta[name="description"]')
if (descMeta) {
descMeta.setAttribute('content', `Download official ISO image for ${name}. Direct links pulled securely from Microsoft CDN servers.`)
}
// Extract build from parentheses e.g. "(26200.6584)"
const match = name.match(/\(([^)]+)\)/)
@ -124,7 +144,7 @@ export default function ProductDetailPage() {
})
}, [productId])
// Fetch languages on mount
// Fetch languages
useEffect(() => {
if (!productId || !isValidated || !meta.active) return
setIsLoadingLangs(true)
@ -138,7 +158,6 @@ export default function ProductDetailPage() {
if (data.error) throw new Error(data.error)
if (!data.Skus?.length) throw new Error('No languages found for this product.')
setLanguages(data.Skus)
// Default to English if present
const english = data.Skus.find((s: Sku) =>
s.LocalizedLanguage.toLowerCase().includes('english') &&
!s.LocalizedLanguage.toLowerCase().includes('international')
@ -176,301 +195,313 @@ export default function ProductDetailPage() {
setTimeout(() => setCopiedUri(null), 2000)
}
// First download link URI for aria2 tip
const firstUri = downloadLinks[0]?.Uri
return (
<div className="max-w-xl mx-auto px-5 pt-12 pb-10">
{/* Back */}
<button
onClick={() => navigate(-1)}
className="flex items-center gap-1.5 text-sm text-zinc-500 hover:text-zinc-300 mb-8 transition-colors"
>
<ArrowLeft size={15} />
Back
</button>
<>
<title>{pageTitle}</title>
{pageDescription && <meta name="description" content={pageDescription} />}
<link rel="canonical" href={canonicalUrl} />
<meta property="og:title" content={pageTitle} />
{pageDescription && <meta property="og:description" content={pageDescription} />}
<meta property="og:url" content={canonicalUrl} />
{isNotFound && <meta name="robots" content="noindex" />}
{breadcrumbJsonLd && (
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }} />
)}
<motion.div
initial={{ opacity: 0, y: 14 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.35 }}
>
{/* Product header */}
<div className="flex items-start gap-4 mb-8">
<div className="w-12 h-12 rounded-xl bg-white/5 border border-white/8 flex items-center justify-center flex-shrink-0">
<WinLogo className="w-6 h-6 text-white/50" />
</div>
<div className="min-w-0">
<h1 className="text-2xl font-bold text-white leading-tight tracking-tight">
{productName || `Product ${productId}`}
</h1>
{buildStr && (
<p className="text-[12px] font-mono text-zinc-500 mt-0.5">Build {buildStr}</p>
)}
{meta.archs.length > 0 && (
<div className="flex gap-1.5 mt-2">
{meta.archs.map(arch => (
<span key={arch} className="text-[10px] font-mono font-medium px-1.5 py-0.5 rounded bg-white/5 border border-white/8 text-zinc-500">
{arch}
</span>
))}
{meta.badge && (
<span className="text-[10px] font-mono font-semibold px-2 py-0.5 rounded-full bg-blue-500/10 border border-blue-500/20 text-blue-400">
{meta.badge}
</span>
)}
</div>
)}
</div>
</div>
<div className="max-w-xl mx-auto px-5 pt-12 pb-10">
{/* Back */}
<button
onClick={() => navigate(-1)}
className="flex items-center gap-1.5 text-sm text-zinc-500 hover:text-zinc-300 mb-8 transition-colors"
>
<ArrowLeft size={15} />
Back
</button>
{/* Main card */}
<div className="rounded-2xl border border-white/7 bg-[#111113] p-6 space-y-5">
{!productName && !isNotFound && !hasCatalogError ? (
<div className="space-y-4">
<div className="flex items-center gap-2.5 text-xs text-zinc-400 font-medium tracking-wide">
<motion.span
animate={{ rotate: 360 }}
transition={{ duration: 1, repeat: Infinity, ease: 'linear' }}
className="inline-block w-4 h-4 border-2 border-zinc-500/30 border-t-zinc-500 rounded-full"
/>
Loading catalog...
</div>
<div className="space-y-3">
<div className="h-3 w-20 bg-white/6 rounded animate-pulse" />
<div className="h-11 bg-white/6 rounded-xl animate-pulse" />
<div className="h-11 bg-white/5 rounded-xl animate-pulse" />
</div>
</div>
) : hasCatalogError ? (
<div className="flex flex-col items-center justify-center py-6 text-center">
<WifiOff size={32} className="text-zinc-600 mb-4" />
<h2 className="text-lg font-semibold text-white mb-2">Network Error</h2>
<p className="text-zinc-500 text-sm max-w-xs px-4 leading-relaxed mb-6">
Failed to load the product catalog. Please check your internet connection or try refreshing the page.
</p>
<button onClick={() => window.location.reload()} className="px-4 py-2 rounded-xl bg-white/5 border border-white/8 text-sm font-medium text-white hover:bg-white/10 transition-colors">
Retry
</button>
<motion.div
initial={{ opacity: 0, y: 14 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.35 }}
>
{/* Product header */}
<div className="flex items-start gap-4 mb-8">
<div className="w-12 h-12 rounded-xl bg-white/5 border border-white/8 flex items-center justify-center flex-shrink-0">
<WinLogo className="w-6 h-6 text-white/50" />
</div>
) : isNotFound ? (
<div className="flex flex-col items-center justify-center py-6 text-center">
<AlertTriangle size={32} className="text-zinc-600 mb-4" />
<h2 className="text-lg font-semibold text-white mb-2">Product Not Found</h2>
<p className="text-zinc-500 text-sm max-w-xs px-4 leading-relaxed mb-6">
The product ID you requested ({productId}) does not exist or has been removed from the catalog.
</p>
<div className="flex items-center justify-center gap-3 w-full">
<Link to="/" className="flex-1 max-w-[140px] px-4 py-2.5 rounded-xl bg-white/5 border border-white/8 text-sm font-medium text-white hover:bg-white/10 transition-colors">
Home
</Link>
<Link to="/products" className="flex-1 max-w-[140px] px-4 py-2.5 rounded-xl bg-blue-500 text-sm font-medium text-white hover:bg-blue-600 transition-colors shadow-lg">
Browse all
<div className="min-w-0">
<h1 className="text-2xl font-bold text-white leading-tight tracking-tight">
{productName || `Product ${productId}`}
</h1>
{buildStr && (
<p className="text-[12px] font-mono text-zinc-500 mt-0.5">Build {buildStr}</p>
)}
{meta.archs.length > 0 && (
<div className="flex gap-1.5 mt-2">
{meta.archs.map(arch => (
<span key={arch} className="text-[10px] font-mono font-medium px-1.5 py-0.5 rounded bg-white/5 border border-white/8 text-zinc-500">
{arch}
</span>
))}
{meta.badge && (
<span className="text-[10px] font-mono font-semibold px-2 py-0.5 rounded-full bg-blue-500/10 border border-blue-500/20 text-blue-400">
{meta.badge}
</span>
)}
</div>
)}
</div>
</div>
{/* Main card */}
<div className="rounded-2xl border border-white/7 bg-[#111113] p-6 space-y-5">
{!productName && !isNotFound && !hasCatalogError ? (
<div className="space-y-4">
<div className="flex items-center gap-2.5 text-xs text-zinc-400 font-medium tracking-wide">
<motion.span
animate={{ rotate: 360 }}
transition={{ duration: 1, repeat: Infinity, ease: 'linear' }}
className="inline-block w-4 h-4 border-2 border-zinc-500/30 border-t-zinc-500 rounded-full"
/>
Loading catalog...
</div>
<div className="space-y-3">
<div className="h-3 w-20 bg-white/6 rounded animate-pulse" />
<div className="h-11 bg-white/6 rounded-xl animate-pulse" />
<div className="h-11 bg-white/5 rounded-xl animate-pulse" />
</div>
</div>
) : hasCatalogError ? (
<div className="flex flex-col items-center justify-center py-6 text-center">
<WifiOff size={32} className="text-zinc-600 mb-4" />
<h2 className="text-lg font-semibold text-white mb-2">Network Error</h2>
<p className="text-zinc-500 text-sm max-w-xs px-4 leading-relaxed mb-6">
Failed to load the product catalog. Please check your internet connection or try refreshing the page.
</p>
<button onClick={() => window.location.reload()} className="px-4 py-2 rounded-xl bg-white/5 border border-white/8 text-sm font-medium text-white hover:bg-white/10 transition-colors">
Retry
</button>
</div>
) : isNotFound ? (
<div className="flex flex-col items-center justify-center py-6 text-center">
<AlertTriangle size={32} className="text-zinc-600 mb-4" />
<h2 className="text-lg font-semibold text-white mb-2">Product Not Found</h2>
<p className="text-zinc-500 text-sm max-w-xs px-4 leading-relaxed mb-6">
The product ID you requested ({productId}) does not exist or has been removed from the catalog.
</p>
<div className="flex items-center justify-center gap-3 w-full">
<Link to="/" className="flex-1 max-w-[140px] px-4 py-2.5 rounded-xl bg-white/5 border border-white/8 text-sm font-medium text-white hover:bg-white/10 transition-colors">
Home
</Link>
<Link to="/products" className="flex-1 max-w-[140px] px-4 py-2.5 rounded-xl bg-blue-500 text-sm font-medium text-white hover:bg-blue-600 transition-colors shadow-lg">
Browse all
</Link>
</div>
</div>
) : !meta.active ? (
<div className="flex flex-col items-center justify-center py-6 text-center">
<AlertTriangle size={32} className="text-amber-500/50 mb-4" />
<h2 className="text-lg font-semibold text-white mb-2">Product Discontinued</h2>
<p className="text-zinc-500 text-sm max-w-sm px-4 leading-relaxed mb-6">
This release is no longer hosted on Microsoft's official CDN and cannot be downloaded.
</p>
<Link to="/products" className="px-4 py-2.5 rounded-xl bg-white/5 border border-white/8 text-sm font-medium text-white hover:bg-white/10 transition-colors">
Browse other releases
</Link>
</div>
</div>
) : !meta.active ? (
<div className="flex flex-col items-center justify-center py-6 text-center">
<AlertTriangle size={32} className="text-amber-500/50 mb-4" />
<h2 className="text-lg font-semibold text-white mb-2">Product Discontinued</h2>
<p className="text-zinc-500 text-sm max-w-sm px-4 leading-relaxed mb-6">
This release is no longer hosted on Microsoft's official CDN and cannot be downloaded.
</p>
<Link to="/products" className="px-4 py-2.5 rounded-xl bg-white/5 border border-white/8 text-sm font-medium text-white hover:bg-white/10 transition-colors">
Browse other releases
</Link>
</div>
) : isLoadingLangs ? (
<div className="space-y-4">
<div className="flex items-center gap-2.5 text-xs text-zinc-400 font-medium tracking-wide">
<motion.span
animate={{ rotate: 360 }}
transition={{ duration: 1, repeat: Infinity, ease: 'linear' }}
className="inline-block w-4 h-4 border-2 border-zinc-500/30 border-t-zinc-500 rounded-full"
/>
Connecting to Microsoft CDN...
) : isLoadingLangs ? (
<div className="space-y-4">
<div className="flex items-center gap-2.5 text-xs text-zinc-400 font-medium tracking-wide">
<motion.span
animate={{ rotate: 360 }}
transition={{ duration: 1, repeat: Infinity, ease: 'linear' }}
className="inline-block w-4 h-4 border-2 border-zinc-500/30 border-t-zinc-500 rounded-full"
/>
Connecting to Microsoft CDN...
</div>
<div className="space-y-3">
<div className="h-3 w-20 bg-white/6 rounded animate-pulse" />
<div className="h-11 bg-white/6 rounded-xl animate-pulse" />
<div className="h-11 bg-white/5 rounded-xl animate-pulse" />
</div>
</div>
<div className="space-y-3">
<div className="h-3 w-20 bg-white/6 rounded animate-pulse" />
<div className="h-11 bg-white/6 rounded-xl animate-pulse" />
<div className="h-11 bg-white/5 rounded-xl animate-pulse" />
) : error && !languages.length ? (
<div className="flex items-start gap-3 p-4 rounded-xl bg-red-500/8 border border-red-500/15 text-red-400 text-sm">
<AlertTriangle size={15} className="flex-shrink-0 mt-0.5" />
<div>
<p className="font-medium mb-0.5">Failed to load languages</p>
<p className="text-red-400/70 text-xs">{error}</p>
</div>
</div>
</div>
) : error && !languages.length ? (
<div className="flex items-start gap-3 p-4 rounded-xl bg-red-500/8 border border-red-500/15 text-red-400 text-sm">
<AlertTriangle size={15} className="flex-shrink-0 mt-0.5" />
<div>
<p className="font-medium mb-0.5">Failed to load languages</p>
<p className="text-red-400/70 text-xs">{error}</p>
</div>
</div>
) : (
<>
{/* Language selector */}
<div>
<label className="block text-[10px] font-mono font-semibold uppercase tracking-widest text-zinc-600 mb-2">
Language
</label>
<Select.Root
value={selectedSku?.Id}
onValueChange={val => {
const sku = languages.find(l => l.Id === val)
if (sku) setSelectedSku(sku)
setDownloadLinks([])
setError(null)
}}
) : (
<>
{/* Language selector */}
<div>
<label className="block text-[10px] font-mono font-semibold uppercase tracking-widest text-zinc-600 mb-2">
Language
</label>
<Select.Root
value={selectedSku?.Id}
onValueChange={val => {
const sku = languages.find(l => l.Id === val)
if (sku) setSelectedSku(sku)
setDownloadLinks([])
setError(null)
}}
>
<Select.Trigger className="w-full flex items-center justify-between px-4 py-3 rounded-xl bg-white/5 border border-white/7 text-sm text-white hover:bg-white/7 hover:border-white/12 focus:outline-none transition-all cursor-pointer">
<Select.Value placeholder="Select language..." />
<Select.Icon>
<ChevronDown size={14} className="text-zinc-500" />
</Select.Icon>
</Select.Trigger>
<Select.Portal>
<Select.Content
className="z-[100] w-[var(--radix-select-trigger-width)] max-h-72 overflow-auto rounded-xl border border-white/9 bg-[#131316] backdrop-blur-xl shadow-2xl text-sm"
position="popper"
sideOffset={6}
>
<Select.Viewport className="p-1">
{languages.map(lang => (
<Select.Item
key={lang.Id}
value={lang.Id}
className="relative flex items-center justify-between px-3 py-2.5 rounded-lg text-zinc-400 data-[highlighted]:text-white data-[highlighted]:bg-white/6 cursor-pointer outline-none transition-colors"
>
<Select.ItemText>{lang.LocalizedLanguage}</Select.ItemText>
<Select.ItemIndicator>
<Check size={13} className="text-blue-400" />
</Select.ItemIndicator>
</Select.Item>
))}
</Select.Viewport>
</Select.Content>
</Select.Portal>
</Select.Root>
</div>
{/* CTA */}
<button
onClick={handleGetLinks}
disabled={!selectedSku || isFetching}
className="w-full flex items-center justify-center gap-2 py-3 rounded-xl bg-blue-500 text-white text-sm font-semibold hover:bg-blue-600 disabled:opacity-40 disabled:cursor-not-allowed transition-all shadow-[0_0_20px_rgba(59,130,246,0.2)] hover:shadow-[0_0_24px_rgba(59,130,246,0.3)]"
>
<Select.Trigger className="w-full flex items-center justify-between px-4 py-3 rounded-xl bg-white/5 border border-white/7 text-sm text-white hover:bg-white/7 hover:border-white/12 focus:outline-none transition-all cursor-pointer">
<Select.Value placeholder="Select language..." />
<Select.Icon>
<ChevronDown size={14} className="text-zinc-500" />
</Select.Icon>
</Select.Trigger>
{isFetching ? (
<>
<motion.span
animate={{ rotate: 360 }}
transition={{ duration: 1, repeat: Infinity, ease: 'linear' }}
className="inline-block w-4 h-4 border-2 border-white/30 border-t-white rounded-full"
/>
Fetching links
</>
) : (
<>
<Download size={15} />
Get Download Links
</>
)}
</button>
</>
)}
<Select.Portal>
<Select.Content
className="z-[100] w-[var(--radix-select-trigger-width)] max-h-72 overflow-auto rounded-xl border border-white/9 bg-[#131316] backdrop-blur-xl shadow-2xl text-sm"
position="popper"
sideOffset={6}
>
<Select.Viewport className="p-1">
{languages.map(lang => (
<Select.Item
key={lang.Id}
value={lang.Id}
className="relative flex items-center justify-between px-3 py-2.5 rounded-lg text-zinc-400 data-[highlighted]:text-white data-[highlighted]:bg-white/6 cursor-pointer outline-none transition-colors"
>
<Select.ItemText>{lang.LocalizedLanguage}</Select.ItemText>
<Select.ItemIndicator>
<Check size={13} className="text-blue-400" />
</Select.ItemIndicator>
</Select.Item>
))}
</Select.Viewport>
</Select.Content>
</Select.Portal>
</Select.Root>
</div>
{/* Error after fetch */}
<AnimatePresence>
{error && languages.length > 0 && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
className="flex items-start gap-3 p-4 rounded-xl bg-red-500/8 border border-red-500/15 text-red-400 text-sm"
>
<AlertTriangle size={15} className="flex-shrink-0 mt-0.5" />
<span>{error}</span>
</motion.div>
)}
</AnimatePresence>
{/* CTA */}
<button
onClick={handleGetLinks}
disabled={!selectedSku || isFetching}
className="w-full flex items-center justify-center gap-2 py-3 rounded-xl bg-blue-500 text-white text-sm font-semibold hover:bg-blue-600 disabled:opacity-40 disabled:cursor-not-allowed transition-all shadow-[0_0_20px_rgba(59,130,246,0.2)] hover:shadow-[0_0_24px_rgba(59,130,246,0.3)]"
>
{isFetching ? (
<>
<motion.span
animate={{ rotate: 360 }}
transition={{ duration: 1, repeat: Infinity, ease: 'linear' }}
className="inline-block w-4 h-4 border-2 border-white/30 border-t-white rounded-full"
/>
Fetching links
</>
) : (
<>
<Download size={15} />
Get Download Links
</>
)}
</button>
{/* Download links */}
<AnimatePresence>
{downloadLinks.length > 0 && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
transition={{ type: 'spring', stiffness: 300, damping: 28 }}
>
<div className="pt-4 border-t border-white/6 space-y-3">
{/* Warning */}
<div className="flex items-center gap-2 p-3 rounded-lg bg-amber-500/6 border border-amber-500/12 text-amber-400/80 text-[11px]">
<AlertTriangle size={12} className="flex-shrink-0" />
Links expire in 24 hours · IP-tied · Use a download manager for full speed
</div>
{/* Links */}
{downloadLinks.map(link => {
const filename = link.Uri.split('/').pop()?.split('?')[0] ?? 'download.iso'
const arch = link.Architecture || archFromUri(link.Uri)
const isCopied = copiedUri === link.Uri
return (
<div
key={link.Uri}
className="flex items-center justify-between gap-3 p-3.5 rounded-xl bg-white/3 border border-white/5 hover:border-white/9 transition-colors"
>
<div className="min-w-0">
<p className="text-[12px] font-mono text-white truncate">{filename}</p>
<p className="text-[11px] text-zinc-600 mt-0.5">{arch}</p>
</div>
<div className="flex items-center gap-2 flex-shrink-0">
<button
onClick={() => handleCopy(link.Uri)}
className="p-2 rounded-lg text-zinc-600 hover:text-white hover:bg-white/6 transition-all"
title="Copy link"
>
{isCopied
? <Check size={13} className="text-green-400" />
: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.8} className="w-3.5 h-3.5" aria-hidden="true">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
</svg>
}
</button>
<a
href={link.Uri}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-blue-500/12 border border-blue-500/20 text-blue-400 text-[11px] font-medium hover:bg-blue-500/20 transition-all"
>
<Download size={11} />
{arch}
<ExternalLink size={10} className="opacity-50" />
</a>
</div>
</div>
)
})}
</div>
</motion.div>
)}
</AnimatePresence>
</div>
{(!isNotFound && !hasCatalogError) && (
<>
{/* System requirements */}
<SystemRequirements isWin11={isWin11(productId!)} />
{/* aria2 tip */}
{meta.active && <Aria2Tip downloadUrl={firstUri} />}
{/* Related releases */}
{related.length > 0 && (
<RelatedReleases current={productId!} items={related} />
)}
</>
)}
{/* Error after fetch */}
<AnimatePresence>
{error && languages.length > 0 && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
className="flex items-start gap-3 p-4 rounded-xl bg-red-500/8 border border-red-500/15 text-red-400 text-sm"
>
<AlertTriangle size={15} className="flex-shrink-0 mt-0.5" />
<span>{error}</span>
</motion.div>
)}
</AnimatePresence>
{/* Download links */}
<AnimatePresence>
{downloadLinks.length > 0 && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
transition={{ type: 'spring', stiffness: 300, damping: 28 }}
>
<div className="pt-4 border-t border-white/6 space-y-3">
{/* Warning */}
<div className="flex items-center gap-2 p-3 rounded-lg bg-amber-500/6 border border-amber-500/12 text-amber-400/80 text-[11px]">
<AlertTriangle size={12} className="flex-shrink-0" />
Links expire in 24 hours · IP-tied · Use a download manager for full speed
</div>
{/* Links */}
{downloadLinks.map(link => {
const filename = link.Uri.split('/').pop()?.split('?')[0] ?? 'download.iso'
const arch = link.Architecture || archFromUri(link.Uri)
const isCopied = copiedUri === link.Uri
return (
<div
key={link.Uri}
className="flex items-center justify-between gap-3 p-3.5 rounded-xl bg-white/3 border border-white/5 hover:border-white/9 transition-colors"
>
<div className="min-w-0">
<p className="text-[12px] font-mono text-white truncate">{filename}</p>
<p className="text-[11px] text-zinc-600 mt-0.5">{arch}</p>
</div>
<div className="flex items-center gap-2 flex-shrink-0">
<button
onClick={() => handleCopy(link.Uri)}
className="p-2 rounded-lg text-zinc-600 hover:text-white hover:bg-white/6 transition-all"
title="Copy link"
>
{isCopied
? <Check size={13} className="text-green-400" />
: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.8} className="w-3.5 h-3.5">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
</svg>
}
</button>
<a
href={link.Uri}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg bg-blue-500/12 border border-blue-500/20 text-blue-400 text-[11px] font-medium hover:bg-blue-500/20 transition-all"
>
<Download size={11} />
{arch}
<ExternalLink size={10} className="opacity-50" />
</a>
</div>
</div>
)
})}
</div>
</motion.div>
)}
</AnimatePresence>
</div>
{(!isNotFound && !hasCatalogError) && (
<>
{/* System requirements */}
<SystemRequirements isWin11={isWin11(productId!)} />
{/* aria2 tip */}
{meta.active && <Aria2Tip downloadUrl={firstUri} />}
{/* Related releases */}
{related.length > 0 && (
<RelatedReleases current={productId!} items={related} />
)}
</>
)}
</motion.div>
</div>
</motion.div>
</div>
</>
)
}

View file

@ -4,6 +4,8 @@ import { motion } from 'motion/react'
import { Search, ArrowRight } from 'lucide-react'
import type { Product } from '../types'
const SITE_URL = 'https://msdl.tech-latest.com'
const quickSearches = [
{ label: 'Windows 11 25H2', query: '25H2' },
{ label: 'Windows 11 24H2', query: '24H2' },
@ -15,11 +17,8 @@ export default function ProductsPage() {
const [products, setProducts] = useState<Product[]>([])
const [isLoading, setIsLoading] = useState(true)
const [query, setQuery] = useState('')
useEffect(() => {
document.title = "All Windows Releases | Windows ISO Downloader"
const metaDesc = document.querySelector('meta[name="description"]')
if (metaDesc) metaDesc.setAttribute('content', "Browse all official Microsoft Windows ISO releases available for direct download.")
fetch('/data/products.json')
.then(r => r.json())
.then((data: Record<string, { name: string; active?: boolean }>) => {
@ -36,93 +35,102 @@ export default function ProductsPage() {
)
return (
<div className="max-w-3xl mx-auto px-4 pt-12 pb-10">
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
>
<h1 className="text-3xl font-bold text-white mb-1">All Products</h1>
<p className="text-white/40 text-sm mb-6">
{products.length} versions available
</p>
<>
<title>All Windows Releases | Windows ISO Downloader</title>
<meta name="description" content="Browse all official Microsoft Windows ISO releases available for direct download. Windows 11, 10, and 8.1." />
<link rel="canonical" href={`${SITE_URL}/products`} />
<meta property="og:title" content="All Windows ISO Releases | Windows ISO Downloader" />
<meta property="og:description" content="Browse all official Microsoft Windows ISO releases available for direct download." />
<meta property="og:url" content={`${SITE_URL}/products`} />
{/* Search */}
<div className="relative mb-4">
<Search size={16} className="absolute left-3.5 top-1/2 -translate-y-1/2 text-white/30" />
<input
type="text"
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="Search Windows versions..."
className="w-full bg-white/5 border border-white/8 rounded-xl pl-10 pr-4 py-3 text-sm text-white placeholder:text-white/25 focus:outline-none focus:border-white/20 focus:bg-white/8 transition-all"
/>
</div>
<div className="max-w-3xl mx-auto px-4 pt-12 pb-10">
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
>
<h1 className="text-3xl font-bold text-white mb-1">All Windows ISO Releases</h1>
<p className="text-white/40 text-sm mb-6">
{products.length} versions available
</p>
{/* Quick filters */}
<div className="flex flex-wrap gap-2 mb-6">
{quickSearches.map(s => (
<button
key={s.label}
onClick={() => setQuery(s.query)}
className="px-3 py-1.5 rounded-lg text-xs font-medium border border-white/8 bg-white/4 text-white/50 hover:text-white hover:border-white/15 hover:bg-white/8 transition-all"
>
{s.label}
</button>
))}
{query && (
<button
onClick={() => setQuery('')}
className="px-3 py-1.5 rounded-lg text-xs font-medium border border-white/8 bg-white/4 text-white/40 hover:text-white transition-all"
>
Clear ×
</button>
)}
</div>
{/* List */}
{isLoading ? (
<div className="space-y-2">
{[...Array(8)].map((_, i) => (
<div key={i} className="h-14 rounded-xl bg-white/4 animate-pulse" />
))}
{/* Search */}
<div className="relative mb-4">
<Search size={16} className="absolute left-3.5 top-1/2 -translate-y-1/2 text-white/30" />
<input
type="text"
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="Search Windows versions..."
className="w-full bg-white/5 border border-white/8 rounded-xl pl-10 pr-4 py-3 text-sm text-white placeholder:text-white/25 focus:outline-none focus:border-white/20 focus:bg-white/8 transition-all"
/>
</div>
) : filtered.length === 0 ? (
<p className="text-white/30 text-sm text-center py-12">No products found.</p>
) : (
<motion.div
className="space-y-1.5"
initial="hidden"
animate="show"
variants={{ show: { transition: { staggerChildren: 0.03 } } }}
>
{filtered.map(product => (
<motion.div
key={product.id}
variants={{
hidden: { opacity: 0, x: -8 },
show: { opacity: 1, x: 0, transition: { type: 'spring', stiffness: 300, damping: 28 } },
}}
{/* Quick filters */}
<div className="flex flex-wrap gap-2 mb-6">
{quickSearches.map(s => (
<button
key={s.label}
onClick={() => setQuery(s.query)}
className="px-3 py-1.5 rounded-lg text-xs font-medium border border-white/8 bg-white/4 text-white/50 hover:text-white hover:border-white/15 hover:bg-white/8 transition-all"
>
<Link
to={`/product/${product.id}`}
className="group w-full flex items-center justify-between px-4 py-3.5 rounded-xl border border-white/6 bg-white/3 hover:bg-white/6 hover:border-white/12 transition-all text-left"
>
<div className="flex items-center gap-3">
<svg viewBox="0 0 88 88" className="w-5 h-5 text-white/30 flex-shrink-0 group-hover:text-white/50 transition-colors" fill="currentColor">
<path d="M0 12.402l35.687-4.86.016 34.423-35.67.203zm35.67 33.529l.028 34.453L.028 75.48.026 45.7zm4.326-39.025L87.314 0v41.527l-47.318.376zm47.329 39.349-.011 41.34-47.318-6.678-.066-34.739z" />
</svg>
<span className="text-sm text-white/70 group-hover:text-white transition-colors font-medium">
{product.name}
</span>
</div>
<ArrowRight size={14} className="text-white/20 group-hover:text-white/50 group-hover:translate-x-0.5 transition-all" />
</Link>
</motion.div>
{s.label}
</button>
))}
</motion.div>
)}
</motion.div>
</div>
{query && (
<button
onClick={() => setQuery('')}
className="px-3 py-1.5 rounded-lg text-xs font-medium border border-white/8 bg-white/4 text-white/40 hover:text-white transition-all"
>
Clear ×
</button>
)}
</div>
{/* List */}
{isLoading ? (
<div className="space-y-2">
{[...Array(8)].map((_, i) => (
<div key={i} className="h-14 rounded-xl bg-white/4 animate-pulse" />
))}
</div>
) : filtered.length === 0 ? (
<p className="text-white/30 text-sm text-center py-12">No products found.</p>
) : (
<motion.div
className="space-y-1.5"
initial="hidden"
animate="show"
variants={{ show: { transition: { staggerChildren: 0.03 } } }}
>
{filtered.map(product => (
<motion.div
key={product.id}
variants={{
hidden: { opacity: 0, x: -8 },
show: { opacity: 1, x: 0, transition: { type: 'spring', stiffness: 300, damping: 28 } },
}}
>
<Link
to={`/product/${product.id}`}
className="group w-full flex items-center justify-between px-4 py-3.5 rounded-xl border border-white/6 bg-white/3 hover:bg-white/6 hover:border-white/12 transition-all text-left"
>
<div className="flex items-center gap-3">
<svg viewBox="0 0 88 88" className="w-5 h-5 text-white/30 flex-shrink-0 group-hover:text-white/50 transition-colors" fill="currentColor" aria-hidden="true">
<path d="M0 12.402l35.687-4.86.016 34.423-35.67.203zm35.67 33.529l.028 34.453L.028 75.48.026 45.7zm4.326-39.025L87.314 0v41.527l-47.318.376zm47.329 39.349-.011 41.34-47.318-6.678-.066-34.739z" />
</svg>
<span className="text-sm text-white/70 group-hover:text-white transition-colors font-medium">
{product.name}
</span>
</div>
<ArrowRight size={14} className="text-white/20 group-hover:text-white/50 group-hover:translate-x-0.5 transition-all" />
</Link>
</motion.div>
))}
</motion.div>
)}
</motion.div>
</div>
</>
)
}