diff --git a/src/app/[locale]/desktops/[slug]/page.tsx b/src/app/[locale]/desktops/[slug]/page.tsx index dfcabdd2..d2d00c76 100644 --- a/src/app/[locale]/desktops/[slug]/page.tsx +++ b/src/app/[locale]/desktops/[slug]/page.tsx @@ -90,6 +90,7 @@ export default async function DesktopPage({
{tShared('terms.by')}{' '} diff --git a/src/app/[locale]/vendors/[slug]/page.tsx b/src/app/[locale]/vendors/[slug]/page.tsx index 8ef38e6f..6b9dfead 100644 --- a/src/app/[locale]/vendors/[slug]/page.tsx +++ b/src/app/[locale]/vendors/[slug]/page.tsx @@ -144,6 +144,7 @@ export default async function VendorPage({
} -

- {cli.name} -

+
+

+ {cli.name} +

+ +

{content.answer}

diff --git a/src/components/product/ManifestEditLink.tsx b/src/components/product/ManifestEditLink.tsx new file mode 100644 index 00000000..c98826d5 --- /dev/null +++ b/src/components/product/ManifestEditLink.tsx @@ -0,0 +1,26 @@ +import { Github } from 'lucide-react' +import { useTranslations } from 'next-intl' +import { getManifestEditUrl, type ManifestCategory } from '@/lib/manifest-source' + +export interface ManifestEditLinkProps { + category: ManifestCategory + manifestId: string +} + +export function ManifestEditLink({ category, manifestId }: ManifestEditLinkProps) { + const tComponent = useTranslations('components.product') + const label = tComponent('productHero.editManifest') + + return ( + + + ) +} diff --git a/src/components/product/ProductHero.tsx b/src/components/product/ProductHero.tsx index e2a24f2a..fcff262b 100644 --- a/src/components/product/ProductHero.tsx +++ b/src/components/product/ProductHero.tsx @@ -2,8 +2,10 @@ import { useTranslations } from 'next-intl' import React from 'react' import { DeprecatedBadge } from '@/components/controls/DeprecatedBadge' import { VerifiedBadge } from '@/components/controls/VerifiedBadge' +import { ManifestEditLink } from '@/components/product/ManifestEditLink' import { Link } from '@/i18n/navigation' import { renderLicense } from '@/lib/license' +import type { ManifestCategory } from '@/lib/manifest-source' export interface ProductHeroProps { // Product identity @@ -11,7 +13,8 @@ export interface ProductHeroProps { description: React.ReactNode vendor?: string vendorHref?: string - category: 'CLI' | 'IDE' | 'DESKTOP' | 'EXTENSION' | 'PROVIDER' | 'MODEL' | 'VENDOR' + category: ManifestCategory + manifestId: string categoryLabel?: string // Optional custom label for the badge verified?: boolean // Whether the product is verified deprecated?: boolean // Whether the entity has been superseded or is no longer recommended @@ -63,6 +66,7 @@ export function ProductHero({ vendor, vendorHref, category, + manifestId, categoryLabel, verified = false, deprecated = false, @@ -103,6 +107,7 @@ export function ProductHero({

{name}

{verified && } {deprecated && } +
diff --git a/src/lib/manifest-source.ts b/src/lib/manifest-source.ts new file mode 100644 index 00000000..33fe2c16 --- /dev/null +++ b/src/lib/manifest-source.ts @@ -0,0 +1,23 @@ +export type ManifestCategory = + | 'CLI' + | 'DESKTOP' + | 'EXTENSION' + | 'IDE' + | 'MODEL' + | 'PROVIDER' + | 'VENDOR' + +const manifestDirectories: Record = { + CLI: 'clis', + DESKTOP: 'desktops', + EXTENSION: 'extensions', + IDE: 'ides', + MODEL: 'models', + PROVIDER: 'providers', + VENDOR: 'vendors', +} + +export function getManifestEditUrl(category: ManifestCategory, manifestId: string): string { + const directory = manifestDirectories[category] + return `https://github.com/aicodingstack/aicodingstack.io/edit/main/manifests/${directory}/${encodeURIComponent(manifestId)}.json` +} diff --git a/tests/manifest-source.test.ts b/tests/manifest-source.test.ts new file mode 100644 index 00000000..59090a1e --- /dev/null +++ b/tests/manifest-source.test.ts @@ -0,0 +1,34 @@ +import fs from 'node:fs' +import path from 'node:path' +import { describe, expect, it } from 'vitest' +import { getManifestEditUrl, type ManifestCategory } from '@/lib/manifest-source' + +const categories: Array<[ManifestCategory, string]> = [ + ['IDE', 'ides'], + ['CLI', 'clis'], + ['DESKTOP', 'desktops'], + ['EXTENSION', 'extensions'], + ['MODEL', 'models'], + ['PROVIDER', 'providers'], + ['VENDOR', 'vendors'], +] + +describe('manifest source links', () => { + it.each(categories)( + 'opens every %s manifest in the matching GitHub directory', + (category, directory) => { + const manifestFiles = fs + .readdirSync(path.join(process.cwd(), 'manifests', directory)) + .filter(file => file.endsWith('.json')) + + expect(manifestFiles.length).toBeGreaterThan(0) + + for (const file of manifestFiles) { + const manifestId = path.basename(file, '.json') + expect(getManifestEditUrl(category, manifestId)).toBe( + `https://github.com/aicodingstack/aicodingstack.io/edit/main/manifests/${directory}/${manifestId}.json` + ) + } + } + ) +}) diff --git a/translations/de/components/product.json b/translations/de/components/product.json index b5fc7597..dace02a9 100644 --- a/translations/de/components/product.json +++ b/translations/de/components/product.json @@ -44,6 +44,7 @@ "launch": "Starten" }, "productHero": { + "editManifest": "Diesen Eintrag auf GitHub bearbeiten", "runtime": "Laufzeit" }, "productPricing": { diff --git a/translations/en/components/product.json b/translations/en/components/product.json index 21499045..9edfc305 100644 --- a/translations/en/components/product.json +++ b/translations/en/components/product.json @@ -44,6 +44,7 @@ "launch": "Launch" }, "productHero": { + "editManifest": "Edit this entry on GitHub", "runtime": "Runtime" }, "productPricing": { diff --git a/translations/es/components/product.json b/translations/es/components/product.json index c1107891..9b52ac0c 100644 --- a/translations/es/components/product.json +++ b/translations/es/components/product.json @@ -44,6 +44,7 @@ "launch": "Iniciar" }, "productHero": { + "editManifest": "Editar esta entrada en GitHub", "runtime": "Tiempo de ejecución" }, "productPricing": { diff --git a/translations/fr/components/product.json b/translations/fr/components/product.json index 1fc9070b..0279b2d0 100644 --- a/translations/fr/components/product.json +++ b/translations/fr/components/product.json @@ -44,6 +44,7 @@ "launch": "Lancer" }, "productHero": { + "editManifest": "Modifier cette fiche sur GitHub", "runtime": "Exécution" }, "productPricing": { diff --git a/translations/id/components/product.json b/translations/id/components/product.json index 4b4c9d15..798a5a61 100644 --- a/translations/id/components/product.json +++ b/translations/id/components/product.json @@ -44,6 +44,7 @@ "launch": "Luncurkan" }, "productHero": { + "editManifest": "Edit entri ini di GitHub", "runtime": "Waktu proses" }, "productPricing": { diff --git a/translations/ja/components/product.json b/translations/ja/components/product.json index 1152a930..af3664e5 100644 --- a/translations/ja/components/product.json +++ b/translations/ja/components/product.json @@ -44,6 +44,7 @@ "launch": "起動" }, "productHero": { + "editManifest": "この項目をGitHubで編集", "runtime": "ランタイム" }, "productPricing": { diff --git a/translations/ko/components/product.json b/translations/ko/components/product.json index dedaaa93..caee2d71 100644 --- a/translations/ko/components/product.json +++ b/translations/ko/components/product.json @@ -44,6 +44,7 @@ "launch": "실행" }, "productHero": { + "editManifest": "GitHub에서 이 항목 수정", "runtime": "런타임" }, "productPricing": { diff --git a/translations/pt/components/product.json b/translations/pt/components/product.json index ec467ad1..03a7427f 100644 --- a/translations/pt/components/product.json +++ b/translations/pt/components/product.json @@ -44,6 +44,7 @@ "launch": "Iniciar" }, "productHero": { + "editManifest": "Editar esta entrada no GitHub", "runtime": "Ambiente de execução" }, "productPricing": { diff --git a/translations/ru/components/product.json b/translations/ru/components/product.json index 3445b4ab..0b52fab1 100644 --- a/translations/ru/components/product.json +++ b/translations/ru/components/product.json @@ -44,6 +44,7 @@ "launch": "Запустить" }, "productHero": { + "editManifest": "Изменить эту запись на GitHub", "runtime": "Среда выполнения" }, "productPricing": { diff --git a/translations/tr/components/product.json b/translations/tr/components/product.json index ef36702d..76d5f891 100644 --- a/translations/tr/components/product.json +++ b/translations/tr/components/product.json @@ -44,6 +44,7 @@ "launch": "Başlat" }, "productHero": { + "editManifest": "Bu kaydı GitHub'da düzenle", "runtime": "Çalışma Zamanı" }, "productPricing": { diff --git a/translations/zh-Hans/components/product.json b/translations/zh-Hans/components/product.json index 82952b89..0356d08d 100644 --- a/translations/zh-Hans/components/product.json +++ b/translations/zh-Hans/components/product.json @@ -44,6 +44,7 @@ "launch": "启动" }, "productHero": { + "editManifest": "在 GitHub 上编辑此条目", "runtime": "运行时" }, "productPricing": { diff --git a/translations/zh-Hant/components/product.json b/translations/zh-Hant/components/product.json index ede31515..6769d212 100644 --- a/translations/zh-Hant/components/product.json +++ b/translations/zh-Hant/components/product.json @@ -44,6 +44,7 @@ "launch": "啟動" }, "productHero": { + "editManifest": "在 GitHub 上編輯此項目", "runtime": "執行期" }, "productPricing": {