Skip to content

Commit df5fbd8

Browse files
ericyangpanclaude
andcommitted
chore: update generated metadata and page imports
- Regenerate metadata files (clis.ts, extensions.ts, ides.ts, models.ts, providers.ts, vendors.ts) - Update github-stars.json with latest data - Update .gitignore with new patterns - Update FETCH_GITHUB_STARS.md documentation - Update page imports across all routes to use refactored components - Update sitemap.ts and seo-helpers.ts for new pages 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 3bf397c commit df5fbd8

File tree

31 files changed

+399
-523
lines changed

31 files changed

+399
-523
lines changed

.gitignore

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,11 @@
2424
npm-debug.log*
2525
yarn-debug.log*
2626
yarn-error.log*
27+
scripts/validated-urls.log
2728

2829
# local env files
2930
.env*.local
3031

31-
# vercel
32-
.vercel
33-
3432
# typescript
3533
*.tsbuildinfo
3634
next-env.d.ts

data/github-stars.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"augment-code": null,
55
"claude-code": 43.1,
66
"cline": 52.6,
7-
"continue": 29.9,
7+
"continue": 30,
8+
"droid": null,
89
"github-copilot": null,
910
"jetbrains-junie": null,
1011
"kilo-code": 12.6,
@@ -15,15 +16,15 @@
1516
"amazon-q-developer-cli": 1.8,
1617
"amp-cli": null,
1718
"augment-code-cli": 0.1,
18-
"claude-code": 43.1,
19+
"claude-code-cli": 43.1,
1920
"cline-cli": null,
2021
"codebuddy-cli": null,
2122
"codex": 51.1,
22-
"continue-cli": 29.9,
23+
"continue-cli": 30,
2324
"droid-cli": null,
24-
"gemini-cli": 83.9,
25+
"gemini-cli": 84.1,
2526
"github-copilot-cli": 5.4,
26-
"kilocode-cli": 12.6,
27+
"kilo-code-cli": 12.6,
2728
"kimi-cli": 3.2,
2829
"kode": 3.6,
2930
"neovate-code": 1.1,
@@ -34,13 +35,12 @@
3435
"codebuddy": null,
3536
"codeflicker": null,
3637
"cursor": 31.7,
37-
"droid": null,
3838
"intellij-idea": 19.1,
3939
"kiro": 2.3,
4040
"qoder": null,
4141
"trae": null,
42-
"vscode": 178.8,
42+
"vscode": 178.9,
4343
"windsurf": null,
44-
"zed": 70.1
44+
"zed": 70.2
4545
}
4646
}

docs/FETCH_GITHUB_STARS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ With authentication, you get 5,000 requests per hour.
3939

4040
#### Without GitHub Token
4141
```bash
42-
node scripts/fetch-github-stars.mjs
42+
node scripts/fetch/index.mjs github-stars
4343
```
4444

4545
#### With GitHub Token
4646
```bash
47-
GITHUB_TOKEN=your_github_token_here node scripts/fetch-github-stars.mjs
47+
GITHUB_TOKEN=your_github_token_here node scripts/fetch/index.mjs github-stars
4848
```
4949

5050
## Getting a GitHub Token

src/app/[locale]/articles/[slug]/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export default async function ArticlePage({ params }: Props) {
5959
return (
6060
<>
6161
<Header />
62-
<div className="max-w-[900px] mx-auto px-[var(--spacing-md)] py-[var(--spacing-xl)]">
62+
<div className="max-w-[1400px] mx-auto px-[var(--spacing-md)] py-[var(--spacing-xl)]">
6363
<h1 className="text-[2.5rem] font-semibold tracking-[-0.03em] mb-[var(--spacing-sm)]">
6464
{article.title}
6565
</h1>
@@ -85,7 +85,7 @@ export default async function ArticlePage({ params }: Props) {
8585

8686
{/* Breadcrumb */}
8787
<section className="py-[var(--spacing-sm)] bg-[var(--color-hover)] border-b border-[var(--color-border)]">
88-
<div className="max-w-[900px] mx-auto px-[var(--spacing-md)]">
88+
<div className="max-w-[1400px] mx-auto px-[var(--spacing-md)]">
8989
<nav className="flex items-center gap-[var(--spacing-xs)] text-[0.8125rem]">
9090
<Link
9191
href="/articles"
@@ -100,7 +100,7 @@ export default async function ArticlePage({ params }: Props) {
100100
</section>
101101

102102
{/* Article Content */}
103-
<article className="max-w-[900px] mx-auto px-[var(--spacing-md)] py-[var(--spacing-xl)]">
103+
<article className="max-w-[1400px] mx-auto px-[var(--spacing-md)] py-[var(--spacing-xl)]">
104104
{/* Article Header */}
105105
<header className="mb-[var(--spacing-xl)]">
106106
<h1 className="text-[2.5rem] font-semibold tracking-[-0.03em] mb-[var(--spacing-sm)] leading-tight">

src/app/[locale]/articles/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import Link from 'next/link'
21
import { getTranslations } from 'next-intl/server'
32
import Footer from '@/components/Footer'
43
import Header from '@/components/Header'
4+
import { Link } from '@/i18n/navigation'
55
import { getArticles } from '@/lib/generated/articles'
66
import { buildCanonicalUrl, buildOpenGraph, buildTitle, buildTwitterCard } from '@/lib/metadata'
77

src/app/[locale]/clis/[slug]/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import Link from 'next/link'
21
import { notFound } from 'next/navigation'
32
import { getTranslations } from 'next-intl/server'
43
import { BackToNavigation } from '@/components/controls/BackToNavigation'
@@ -8,6 +7,7 @@ import Header from '@/components/Header'
87
import { JsonLd } from '@/components/JsonLd'
98
import { ProductCommands, ProductHero, ProductLinks, ProductPricing } from '@/components/product'
109
import type { Locale } from '@/i18n/config'
10+
import { Link } from '@/i18n/navigation'
1111
import { clisData as clis } from '@/lib/generated'
1212
import { getGithubStars } from '@/lib/generated/github-stars'
1313
import { translateLicenseText } from '@/lib/license'
@@ -180,7 +180,7 @@ export default async function CLIPage({
180180
{/* Related IDE */}
181181
{cli.ide && (
182182
<section className="py-[var(--spacing-lg)] border-b border-[var(--color-border)]">
183-
<div className="max-w-[800px] mx-auto px-[var(--spacing-md)]">
183+
<div className="max-w-[1400px] mx-auto px-[var(--spacing-md)]">
184184
<Link
185185
href={`ides/${cli.ide}`}
186186
className="block border border-[var(--color-border)] p-[var(--spacing-md)] hover:border-[var(--color-border-strong)] transition-all hover:-translate-y-0.5 group"

src/app/[locale]/clis/comparison/page.client.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
'use client'
22

33
import { Download, FileText, Github, Home, Linkedin, Twitter, Youtube } from 'lucide-react'
4-
import Link from 'next/link'
54
import { useTranslations } from 'next-intl'
65
import { useEffect, useRef, useState } from 'react'
76
import ComparisonTable, { type ComparisonColumn } from '@/components/ComparisonTable'
87
import { AppleIcon, LinuxIcon, WindowsIcon } from '@/components/controls/PlatformIcons'
98
import Footer from '@/components/Footer'
109
import Header from '@/components/Header'
10+
import { Link } from '@/i18n/navigation'
1111
import { clisData as clis } from '@/lib/generated'
1212
import { getGithubStars } from '@/lib/generated/github-stars'
1313
import { renderLicense } from '@/lib/license'
@@ -310,7 +310,7 @@ export default function CLIComparisonPageClient({ locale }: Props) {
310310
{/* Fixed Breadcrumb (when scrolled) */}
311311
{isBreadcrumbFixed && (
312312
<section className="fixed top-[60px] left-0 right-0 z-30 py-[var(--spacing-sm)] bg-[var(--color-hover)] border-b border-[var(--color-border)] shadow-sm">
313-
<div className="max-w-[1200px] mx-auto px-[var(--spacing-md)]">
313+
<div className="max-w-[1400px] mx-auto px-[var(--spacing-md)]">
314314
<nav className="flex items-center gap-[var(--spacing-xs)] text-[0.8125rem]">
315315
<Link
316316
href={`/${locale}/ai-coding-stack`}
@@ -339,7 +339,7 @@ export default function CLIComparisonPageClient({ locale }: Props) {
339339
ref={breadcrumbRef}
340340
className="py-[var(--spacing-sm)] bg-[var(--color-hover)] border-b border-[var(--color-border)]"
341341
>
342-
<div className="max-w-[1200px] mx-auto px-[var(--spacing-md)]">
342+
<div className="max-w-[1400px] mx-auto px-[var(--spacing-md)]">
343343
<nav
344344
className={`flex items-center gap-[var(--spacing-xs)] text-[0.8125rem] ${isBreadcrumbFixed ? 'invisible' : ''}`}
345345
>
@@ -366,7 +366,7 @@ export default function CLIComparisonPageClient({ locale }: Props) {
366366

367367
{/* Page Header */}
368368
<section className="py-[var(--spacing-lg)] border-[var(--color-border)]">
369-
<div className="max-w-[1200px] mx-auto px-[var(--spacing-md)]">
369+
<div className="max-w-[1400px] mx-auto px-[var(--spacing-md)]">
370370
<h1 className="text-[2.5rem] font-semibold tracking-[-0.04em] mb-[var(--spacing-sm)]">
371371
{tComparison('clis.title')}
372372
</h1>
@@ -378,7 +378,7 @@ export default function CLIComparisonPageClient({ locale }: Props) {
378378

379379
{/* Comparison Table */}
380380
<section className="pb-[var(--spacing-lg)] border-b border-[var(--color-border)]">
381-
<div className="max-w-[1200px] mx-auto px-[var(--spacing-md)]">
381+
<div className="max-w-[1400px] mx-auto px-[var(--spacing-md)]">
382382
<ComparisonTable
383383
items={clis as unknown as Record<string, unknown>[]}
384384
columns={columns}
@@ -390,7 +390,7 @@ export default function CLIComparisonPageClient({ locale }: Props) {
390390

391391
{/* Back Navigation */}
392392
<section className="py-[var(--spacing-lg)] border-b border-[var(--color-border)]">
393-
<div className="max-w-[1200px] mx-auto px-[var(--spacing-md)]">
393+
<div className="max-w-[1400px] mx-auto px-[var(--spacing-md)]">
394394
<Link
395395
href={`/${locale}/clis`}
396396
className="inline-flex items-center gap-[var(--spacing-xs)] text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text)] transition-colors"

src/app/[locale]/clis/page.client.tsx

Lines changed: 87 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
'use client'
22

3-
import Link from 'next/link'
43
import { useTranslations } from 'next-intl'
54
import { useMemo, useState } from 'react'
65
import FilterSortBar from '@/components/controls/FilterSortBar'
76
import Footer from '@/components/Footer'
87
import Header from '@/components/Header'
9-
import StackSidebar from '@/components/sidebar/StackSidebar'
8+
import StackTabs from '@/components/navigation/StackTabs'
109
import type { Locale } from '@/i18n/config'
10+
import { Link } from '@/i18n/navigation'
1111
import { clisData } from '@/lib/generated'
1212
import { translateLicenseText } from '@/lib/license'
1313
import { localizeManifestItems } from '@/lib/manifest-i18n'
@@ -22,6 +22,7 @@ export default function CLIsPageClient({ locale }: Props) {
2222
const [sortOrder, setSortOrder] = useState<'default' | 'name-asc' | 'name-desc'>('default')
2323
const [licenseFilters, setLicenseFilters] = useState<string[]>([])
2424
const [platformFilters, setPlatformFilters] = useState<string[]>([])
25+
const [searchQuery, setSearchQuery] = useState('')
2526

2627
// Localize CLIs
2728
const localizedClis = useMemo(() => {
@@ -35,6 +36,27 @@ export default function CLIsPageClient({ locale }: Props) {
3536
const filteredAndSortedClis = useMemo(() => {
3637
let result = [...localizedClis]
3738

39+
// Apply search filter (search in name and i18n fields)
40+
if (searchQuery.trim()) {
41+
const query = searchQuery.toLowerCase()
42+
result = result.filter(cli => {
43+
// Search in main name
44+
if (cli.name.toLowerCase().includes(query)) return true
45+
// Search in i18n names if available
46+
if (cli.i18n) {
47+
return Object.values(cli.i18n).some(
48+
translation =>
49+
typeof translation === 'object' &&
50+
translation !== null &&
51+
'name' in translation &&
52+
typeof translation.name === 'string' &&
53+
translation.name.toLowerCase().includes(query)
54+
)
55+
}
56+
return false
57+
})
58+
}
59+
3860
// Apply license filter
3961
if (licenseFilters.length > 0) {
4062
result = result.filter(cli => {
@@ -67,79 +89,79 @@ export default function CLIsPageClient({ locale }: Props) {
6789
// 'default' keeps the original order
6890

6991
return result
70-
}, [localizedClis, sortOrder, licenseFilters, platformFilters])
92+
}, [localizedClis, sortOrder, licenseFilters, platformFilters, searchQuery])
7193

7294
return (
7395
<>
7496
<Header />
7597

7698
<div className="max-w-[1400px] mx-auto px-[var(--spacing-md)] py-[var(--spacing-lg)]">
77-
<div className="flex gap-[var(--spacing-lg)]">
78-
<StackSidebar activeStack="clis" locale={locale} />
79-
80-
{/* Main Content */}
81-
<main className="flex-1">
82-
<div className="mb-[var(--spacing-lg)]">
83-
<div className="flex items-start justify-between mb-[var(--spacing-sm)]">
84-
<h1 className="text-[2rem] font-semibold tracking-[-0.03em]">
85-
<span className="text-[var(--color-text-muted)] font-light mr-[var(--spacing-xs)]">
86-
{'//'}
87-
</span>
88-
{t('title')}
89-
</h1>
99+
{/* Main Content */}
100+
<main className="w-full">
101+
<div className="mb-[var(--spacing-lg)]">
102+
<div className="flex items-start justify-between mb-[var(--spacing-sm)]">
103+
<h1 className="text-[2rem] font-semibold tracking-[-0.03em]">
104+
<span className="text-[var(--color-text-muted)] font-light mr-[var(--spacing-xs)]">
105+
{'//'}
106+
</span>
107+
{t('title')}
108+
</h1>
109+
<Link
110+
href={`/${locale}/clis/comparison`}
111+
className="text-sm px-[var(--spacing-md)] py-[var(--spacing-xs)] border border-[var(--color-border)] hover:border-[var(--color-border-strong)] transition-colors"
112+
>
113+
{t('compareAll')}
114+
</Link>
115+
</div>
116+
<p className="text-base text-[var(--color-text-secondary)] font-light">
117+
{t('subtitle')}
118+
</p>
119+
</div>
120+
121+
<StackTabs activeStack="clis" locale={locale} />
122+
123+
<FilterSortBar
124+
sortOrder={sortOrder}
125+
onSortChange={setSortOrder}
126+
licenseFilters={licenseFilters}
127+
onLicenseFiltersChange={setLicenseFilters}
128+
platformFilters={platformFilters}
129+
onPlatformFiltersChange={setPlatformFilters}
130+
searchQuery={searchQuery}
131+
onSearchChange={setSearchQuery}
132+
/>
133+
134+
{filteredAndSortedClis.length === 0 ? (
135+
<div className="text-center py-[var(--spacing-xl)] text-[var(--color-text-secondary)]">
136+
{t('noMatches')}
137+
</div>
138+
) : (
139+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-[var(--spacing-md)]">
140+
{filteredAndSortedClis.map(cli => (
90141
<Link
91-
href={`/${locale}/clis/comparison`}
92-
className="text-sm px-[var(--spacing-md)] py-[var(--spacing-xs)] border border-[var(--color-border)] hover:border-[var(--color-border-strong)] transition-colors"
142+
key={cli.name}
143+
href={`/${locale}/clis/${cli.id}`}
144+
className="block border border-[var(--color-border)] p-[var(--spacing-md)] hover:border-[var(--color-border-strong)] transition-all hover:-translate-y-0.5 group flex flex-col"
93145
>
94-
{t('compareAll')}
146+
<div className="flex justify-between items-start mb-[var(--spacing-sm)]">
147+
<h3 className="text-lg font-semibold tracking-tight">{cli.name}</h3>
148+
<span className="text-lg text-[var(--color-text-muted)] group-hover:text-[var(--color-text)] group-hover:translate-x-1 transition-all">
149+
150+
</span>
151+
</div>
152+
<p className="text-sm leading-relaxed text-[var(--color-text-secondary)] mb-[var(--spacing-md)] font-light min-h-[4rem]">
153+
{cli.description}
154+
</p>
155+
<div className="flex items-center gap-[var(--spacing-xs)] text-xs text-[var(--color-text-muted)] mt-auto">
156+
<span>{cli.vendor}</span>
157+
<span className="text-[var(--color-border)]"></span>
158+
<span>{translateLicenseText(cli.license, tGlobal)}</span>
159+
</div>
95160
</Link>
96-
</div>
97-
<p className="text-base text-[var(--color-text-secondary)] font-light">
98-
{t('subtitle')}
99-
</p>
161+
))}
100162
</div>
101-
102-
<FilterSortBar
103-
sortOrder={sortOrder}
104-
onSortChange={setSortOrder}
105-
licenseFilters={licenseFilters}
106-
onLicenseFiltersChange={setLicenseFilters}
107-
platformFilters={platformFilters}
108-
onPlatformFiltersChange={setPlatformFilters}
109-
/>
110-
111-
{filteredAndSortedClis.length === 0 ? (
112-
<div className="text-center py-[var(--spacing-xl)] text-[var(--color-text-secondary)]">
113-
{t('noMatches')}
114-
</div>
115-
) : (
116-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-[var(--spacing-md)]">
117-
{filteredAndSortedClis.map(cli => (
118-
<Link
119-
key={cli.name}
120-
href={`/${locale}/clis/${cli.id}`}
121-
className="block border border-[var(--color-border)] p-[var(--spacing-md)] hover:border-[var(--color-border-strong)] transition-all hover:-translate-y-0.5 group flex flex-col"
122-
>
123-
<div className="flex justify-between items-start mb-[var(--spacing-sm)]">
124-
<h3 className="text-lg font-semibold tracking-tight">{cli.name}</h3>
125-
<span className="text-lg text-[var(--color-text-muted)] group-hover:text-[var(--color-text)] group-hover:translate-x-1 transition-all">
126-
127-
</span>
128-
</div>
129-
<p className="text-sm leading-relaxed text-[var(--color-text-secondary)] mb-[var(--spacing-md)] font-light min-h-[4rem]">
130-
{cli.description}
131-
</p>
132-
<div className="flex items-center gap-[var(--spacing-xs)] text-xs text-[var(--color-text-muted)] mt-auto">
133-
<span>{cli.vendor}</span>
134-
<span className="text-[var(--color-border)]"></span>
135-
<span>{translateLicenseText(cli.license, tGlobal)}</span>
136-
</div>
137-
</Link>
138-
))}
139-
</div>
140-
)}
141-
</main>
142-
</div>
163+
)}
164+
</main>
143165
</div>
144166

145167
<Footer />

0 commit comments

Comments
 (0)