mirror of
https://github.com/nexu-io/open-design.git
synced 2026-05-31 19:04:39 +07:00
The 2026-05 plugins library rebuild introduced /plugins/skills/, /plugins/systems/, /plugins/templates/ and a unified detail route /plugins/<manifest-slug>/, but the old /skills/, /systems/, /templates/ catalogs were left live in parallel. Two equivalent page trees split SEO equity, and the homepage, footer, quickstart, agents, official and blog pages all still linked to the old routes. Retire the legacy generators and 301 every old URL to its new plugins equivalent so inbound links and search equity are preserved: - Remove the /skills, /systems, /templates page generators (English + [locale] wrappers) and the now-orphaned skill-row component, and prune the skills/systems/templates branches from the [locale]/[...path] catch-all (it now renders only craft + blog). - Add the migration block to public/_redirects. Detail slugs differ from the old folder names (new slugs are manifest-name based, e.g. design-system-<x>, example-<x>), so systems/templates use a prefixed splat plus a short degrade list, and skills map the 27 with a template equivalent explicitly while the ~110 instruction-only skills and all mode/scenario/category facet pages degrade to the section landing. 'replicate' is forced to the section to avoid colliding with the design-system of the same name. Locale variants (zh, zh-tw, ja, ko) strip to the section. - Repoint in-site links to /plugins/* across page.tsx (footer, work, labs pills), info-page-i18n.ts (en + zh + sourceNames), official, quickstart, agents, blog and html-anything, and update the sitemap serialize priority list. The system-card keeps linking through /systems/<slug>/ so the 8 systems without a detail page ride the redirect's degrade rather than pointing at a missing page. Verified with a full astro build: old routes no longer emit any HTML, the new section pages exist, _redirects is copied verbatim, and no in-site link targets a removed route (the remaining /systems/<slug>/ hrefs are the system cards that 301 by design). astro check passes.
93 lines
3.8 KiB
Text
93 lines
3.8 KiB
Text
---
|
|
import { getCollection } from 'astro:content';
|
|
import Layout from '../../_components/sub-page-layout.astro';
|
|
import type { HeaderProps } from '../../_components/header';
|
|
import { getCraftRecords } from '../../_lib/catalog';
|
|
import {
|
|
PREFIXED_LOCALES,
|
|
getCopy,
|
|
isLocale,
|
|
localizeCategory,
|
|
type Locale,
|
|
} from '../../_lib/i18n';
|
|
import '../../globals.css';
|
|
import '../../sub-pages.css';
|
|
|
|
// Localized routing only generates the `craft` and `blog` listing pages.
|
|
// Detail pages (individual posts, craft items, …) stay at canonical
|
|
// English URLs to keep the static build bounded; the localized chrome
|
|
// links straight to those canonical detail URLs.
|
|
export async function getStaticPaths() {
|
|
// The skills / systems / templates catalogs moved under `/plugins/*`.
|
|
// Their old localized listings are now 301'd by `public/_redirects`,
|
|
// so this catch-all only renders the localized `craft` and `blog`
|
|
// listings. Plugins itself is generated via short-code wrappers under
|
|
// `app/pages/[locale]/plugins/`, so it does NOT participate here.
|
|
const paths = ['craft', 'blog'];
|
|
|
|
return PREFIXED_LOCALES.flatMap((locale) =>
|
|
paths.map((path) => ({
|
|
params: { locale, path },
|
|
})),
|
|
);
|
|
}
|
|
|
|
const localeParam = Astro.params.locale;
|
|
const locale: Locale = isLocale(localeParam) ? localeParam : 'en';
|
|
const copy = getCopy(locale);
|
|
const pathParam = Astro.params.path ?? '';
|
|
const segments = pathParam.split('/').filter(Boolean);
|
|
|
|
const [craft, posts] = await Promise.all([
|
|
getCraftRecords(),
|
|
getCollection('blog'),
|
|
]);
|
|
// All cross-locale subpage links resolve to canonical (English) URLs.
|
|
const href = (path: string) => path;
|
|
const titleSuffix = 'Open Design';
|
|
const routeRoot = segments[0] ?? '';
|
|
|
|
const sortedPosts = posts.sort((a, b) => b.data.date.getTime() - a.data.date.getTime());
|
|
|
|
const pageTitle = routeRoot === 'craft'
|
|
? `${copy.craftTitle} — ${craft.length} | ${titleSuffix}`
|
|
: `${copy.blog} — ${titleSuffix}`;
|
|
|
|
const pageDescription = `Open Design ${routeRoot || 'landing'} page.`;
|
|
---
|
|
|
|
<Layout title={pageTitle} description={pageDescription} active={routeRoot as HeaderProps['active']}>
|
|
{routeRoot === 'blog' && (
|
|
<>
|
|
<header class='catalog-head'>
|
|
<span class='label'>{copy.blog}</span>
|
|
<h1 class='display'>{copy.blog}<span class='dot'>.</span></h1>
|
|
<p class='lead'>Notes to help you understand, explore, and build with Open Design.</p>
|
|
</header>
|
|
<section class='catalog-grid'>
|
|
<ol>
|
|
{sortedPosts.map((post, index) => (
|
|
<li class='catalog-row'>
|
|
<a href={href(`/blog/${post.id}/`)}>
|
|
<span class='row-index'>{String(index + 1).padStart(2, '0')}</span>
|
|
<span class='row-body'>
|
|
<span class='row-name'>{post.data.title}</span>
|
|
<span class='row-desc'>{post.data.summary}</span>
|
|
</span>
|
|
<span class='meta-tag'>{localizeCategory(post.data.category, locale)}</span>
|
|
</a>
|
|
</li>
|
|
))}
|
|
</ol>
|
|
</section>
|
|
</>
|
|
)}
|
|
|
|
{routeRoot === 'craft' && (
|
|
<>
|
|
<header class='catalog-head'><span class='label'>{copy.catalog} · Nº 03</span><h1 class='display'><em>{copy.craftTitle}</em> — {craft.length} rendering principles<span class='dot'>.</span></h1><p class='lead'>Quality rules for accessibility, motion, color, type, and state coverage.</p></header>
|
|
<section class='catalog-grid'><ol>{craft.map((item, index) => <li class='catalog-row'><a href={href(`/craft/${item.slug}/`)}><span class='row-index'>{String(index + 1).padStart(2, '0')}</span><span class='row-body'><span class='row-name'>{item.name}</span><span class='row-desc'>{item.summary}</span></span></a></li>)}</ol></section>
|
|
</>
|
|
)}
|
|
|
|
</Layout>
|