feat(architecture): implement consultant review r4 (stream separation, nav upgrade, internal cta, schema & anchor fixes)
Deploy / build (push) Successful in 25s
Deploy / build (push) Successful in 25s
This commit is contained in:
1 parent
cc28722c79
commit
419c962e2f
31 files changed
+286
-42
No files matched your search
@@ -64,7 +64,7 @@ const writeUps = (await getCollection('posts', isEn)).length;
|
||||
|
||||
<div class="dual-track-grid">
|
||||
<!-- Track A: For Business Owners & SMEs -->
|
||||
<div class="track-card track-business">
|
||||
<div class="track-card track-business" id="sme">
|
||||
<div class="track-header">
|
||||
<span class="track-badge">🏢 For Business Owners & Founders</span>
|
||||
<h3>Custom Web Systems, AI Automation & Self-Hosted Infrastructure</h3>
|
||||
@@ -102,7 +102,7 @@ const writeUps = (await getCollection('posts', isEn)).length;
|
||||
</div>
|
||||
|
||||
<!-- Track B: For Tech Leads & Recruiters -->
|
||||
<div class="track-card track-hiring">
|
||||
<div class="track-card track-hiring" id="hiring">
|
||||
<div class="track-header">
|
||||
<span class="track-badge">👨💻 For Hiring Managers & Tech Leads</span>
|
||||
<h3>Full-Stack, DevOps & AI Systems Engineering Roles</h3>
|
||||
|
||||
+46
-5
@@ -7,6 +7,8 @@ import { SITE } from '../config';
|
||||
import { sortForListing } from '../lib/sort';
|
||||
|
||||
const posts = sortForListing(await getCollection('posts', isEn));
|
||||
const engineeringPosts = posts.filter((p) => p.data.stream !== 'creative-lab');
|
||||
const creativeLabPosts = posts.filter((p) => p.data.stream === 'creative-lab');
|
||||
|
||||
// Featured picks: fixed slugs, not "newest 4", so the front page stays stable after a
|
||||
// publish and keeps answering "who is this and what has he actually run" (scale →
|
||||
@@ -112,7 +114,7 @@ const featuredMetrics: Record<string, string> = {
|
||||
<div class="hero-cta">
|
||||
<a class="btn btn-primary" href="#featured">Read the engineering case studies ↓</a>
|
||||
<a class="btn" href="/about/">Hiring? Profile & stack →</a>
|
||||
<a class="btn" href={SITE.website} target="_blank" rel="noopener">Malaysian SME? How I work ↗</a>
|
||||
<a class="btn" href="/about/#sme">Malaysian SME? How I work ↗</a>
|
||||
</div>
|
||||
<p class="hero-note">
|
||||
{posts.length} posts in English and 中文 · <a href="/rss.xml">RSS</a>
|
||||
@@ -179,7 +181,7 @@ const featuredMetrics: Record<string, string> = {
|
||||
|
||||
<section>
|
||||
<div class="section-head-with-nav">
|
||||
<h2>Latest posts</h2>
|
||||
<h2>Latest engineering & DevOps posts</h2>
|
||||
<div class="section-nav-pills" aria-label="Category shortcuts">
|
||||
<a class="section-nav-pill" href="/categories/case-studies/">case-studies</a>
|
||||
<a class="section-nav-pill" href="/categories/devops/">devops</a>
|
||||
@@ -188,11 +190,50 @@ const featuredMetrics: Record<string, string> = {
|
||||
<a class="section-nav-pill section-nav-all" href="/posts/">All {posts.length} →</a>
|
||||
</div>
|
||||
</div>
|
||||
{posts.length === 0 ? (
|
||||
{engineeringPosts.length === 0 ? (
|
||||
<p><em>No posts yet — check back soon.</em></p>
|
||||
) : (
|
||||
<PostList posts={posts} limit={6} />
|
||||
<PostList posts={engineeringPosts} limit={6} />
|
||||
)}
|
||||
<p class="archive-meta"><a href="/posts/">All {posts.length} posts</a> · <a href="/tags/docker/">tag pages</a></p>
|
||||
</section>
|
||||
|
||||
{creativeLabPosts.length > 0 && (
|
||||
<section class="creative-lab-strip">
|
||||
<div class="lab-strip-header">
|
||||
<div class="lab-strip-title-wrap">
|
||||
<span class="lab-strip-badge">⚡ GPU Node</span>
|
||||
<h2>Local GPU & Multimodal Pipelines (RTX 3060 Lab)</h2>
|
||||
</div>
|
||||
<p class="lab-strip-sub">
|
||||
High-throughput audio synthesis, forced-alignment subtitling, and diffusion workflows running on our self-hosted GPU node.
|
||||
</p>
|
||||
</div>
|
||||
<div class="lab-cards-grid">
|
||||
{creativeLabPosts.slice(0, 3).map((post) => {
|
||||
const cleanSlug = post.slug.replace(/^zh\//, '');
|
||||
return (
|
||||
<a class="lab-card" href={`/posts/${post.slug}/`}>
|
||||
<div class="lab-card-thumb">
|
||||
<img
|
||||
src={`/og/thumb/${cleanSlug}-400.webp`}
|
||||
alt=""
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
width="400"
|
||||
height="210"
|
||||
onerror="this.style.display='none'"
|
||||
/>
|
||||
</div>
|
||||
<div class="lab-card-body">
|
||||
<span class="lab-card-tag">{post.data.category}</span>
|
||||
<h3>{post.data.title}</h3>
|
||||
<p>{post.data.description}</p>
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<p class="archive-meta"><a href="/posts/">All {posts.length} posts</a> · <a href="/tags/docker/">tag pages</a></p>
|
||||
</section>
|
||||
)}
|
||||
</BaseLayout>
|
||||
@@ -8,6 +8,7 @@ import { getCollection, render } from 'astro:content';
|
||||
import { postLang } from '../../lib/lang';
|
||||
import { countWords, readingMinutes } from '../../lib/reading-time';
|
||||
import { tagCounts, tagLink } from '../../lib/tags';
|
||||
import { categoryLabel } from '../../lib/categories';
|
||||
import { SITE } from '../../config';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
@@ -57,12 +58,12 @@ const translation = post.data.translation
|
||||
const switchHref = translation ? `/posts/${translation}/` : undefined;
|
||||
const altLocaleUrl = translation ? new URL(`/posts/${translation}/`, SITE.url).href : undefined;
|
||||
|
||||
// BlogPosting structured data — gives Google a rich-result-eligible node and
|
||||
// BlogPosting / TechArticle structured data — gives Google a rich-result-eligible node and
|
||||
// links the article to the site-wide Person node (E-E-A-T).
|
||||
const postUrl = new URL(`/posts/${post.slug}/`, SITE.url).href;
|
||||
const jsonLd = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BlogPosting',
|
||||
'@type': post.data.summary ? 'TechArticle' : 'BlogPosting',
|
||||
mainEntityOfPage: { '@type': 'WebPage', '@id': postUrl },
|
||||
headline: post.data.title,
|
||||
description: post.data.description,
|
||||
@@ -76,6 +77,15 @@ const jsonLd = {
|
||||
articleSection: post.data.category,
|
||||
keywords: post.data.tags.join(', '),
|
||||
wordCount: words,
|
||||
...(post.data.summary
|
||||
? {
|
||||
dependencies: post.data.summary.stack.join(', '),
|
||||
audience: {
|
||||
'@type': 'Audience',
|
||||
audienceType: post.data.summary.audience,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
---
|
||||
|
||||
@@ -103,7 +113,7 @@ const jsonLd = {
|
||||
<span class="sep">·</span>
|
||||
<span>{minutes} {lang === 'zh' ? '分钟' : 'min read'}</span>
|
||||
<span class="sep">·</span>
|
||||
<a class="tag" href={lang === 'zh' ? `/zh/categories/${post.data.category}/` : `/categories/${post.data.category}/`}>{post.data.category}</a>
|
||||
<a class="tag" href={lang === 'zh' ? `/zh/categories/${post.data.category}/` : `/categories/${post.data.category}/`}>{categoryLabel(post.data.category, lang)}</a>
|
||||
{post.data.tags.map((t) => {
|
||||
const href = tagLink(t, tagCount, lang);
|
||||
return href ? <a class="tag" href={href}>{t}</a> : <span class="tag">{t}</span>;
|
||||
@@ -154,7 +164,7 @@ const jsonLd = {
|
||||
decoding="async"
|
||||
/>
|
||||
<div class="related-body">
|
||||
<span class="related-badge">{p.data.category}</span>
|
||||
<span class="related-badge">{categoryLabel(p.data.category, lang)}</span>
|
||||
<h3>{p.data.title}</h3>
|
||||
<time datetime={p.data.pubDate.toISOString()}>
|
||||
{fmt(p.data.pubDate)}
|
||||
|
||||
@@ -63,7 +63,7 @@ const writeUps = (await getCollection('posts', isZh)).length;
|
||||
|
||||
<div class="dual-track-grid">
|
||||
<!-- 轨道 A: 面向企业主与创业者 -->
|
||||
<div class="track-card track-business">
|
||||
<div class="track-card track-business" id="sme">
|
||||
<div class="track-header">
|
||||
<span class="track-badge">🏢 致企业主与业务负责人</span>
|
||||
<h3>网站与业务系统定制 · 24/7 AI 获客客服 · 私有化部署降本</h3>
|
||||
@@ -100,7 +100,7 @@ const writeUps = (await getCollection('posts', isZh)).length;
|
||||
</div>
|
||||
|
||||
<!-- 轨道 B: 面向技术主管与招聘方 -->
|
||||
<div class="track-card track-hiring">
|
||||
<div class="track-card track-hiring" id="hiring">
|
||||
<div class="track-header">
|
||||
<span class="track-badge">👨💻 致技术主管与招聘方</span>
|
||||
<h3>全栈开发、DevOps 运维与 AI 系统架构工程角色</h3>
|
||||
|
||||
@@ -5,8 +5,11 @@ import { getCollection } from 'astro:content';
|
||||
import { isZh } from '../../lib/lang';
|
||||
import { SITE } from '../../config';
|
||||
import { sortForListing } from '../../lib/sort';
|
||||
import { categoryLabel } from '../../lib/categories';
|
||||
|
||||
const posts = sortForListing(await getCollection('posts', isZh));
|
||||
const engineeringPosts = posts.filter((p) => p.data.stream !== 'creative-lab');
|
||||
const creativeLabPosts = posts.filter((p) => p.data.stream === 'creative-lab');
|
||||
|
||||
// Same four topics as the English front page, in their Chinese versions, so the two
|
||||
// front pages tell the same story (scale → automation → observability → a delivered
|
||||
@@ -113,7 +116,7 @@ const featuredMetrics: Record<string, string> = {
|
||||
<div class="hero-cta">
|
||||
<a class="btn btn-primary" href="#featured">先看工程案例 ↓</a>
|
||||
<a class="btn" href="/zh/about/">招聘方:技术栈与履历 →</a>
|
||||
<a class="btn" href={SITE.website} target="_blank" rel="noopener">中小企业:合作方式 ↗</a>
|
||||
<a class="btn" href="/zh/about/#sme">中小企业:合作方式 ↗</a>
|
||||
</div>
|
||||
<p class="hero-note">
|
||||
共 {posts.length} 篇中文文章 · <a href="/">English</a> · <a href="/rss.xml">RSS</a>
|
||||
@@ -162,7 +165,7 @@ const featuredMetrics: Record<string, string> = {
|
||||
</div>
|
||||
<div class="bento-content">
|
||||
<div class="bento-top-meta">
|
||||
<span class="featured-badge">{post.data.category}</span>
|
||||
<span class="featured-badge">{categoryLabel(post.data.category, 'zh')}</span>
|
||||
<span class="bento-metric-pill">
|
||||
<span class="pulse-dot"></span>
|
||||
{metric}
|
||||
@@ -180,20 +183,59 @@ const featuredMetrics: Record<string, string> = {
|
||||
|
||||
<section>
|
||||
<div class="section-head-with-nav">
|
||||
<h2>最新文章</h2>
|
||||
<h2>最新生产与运维文章</h2>
|
||||
<div class="section-nav-pills" aria-label="分类快捷入口">
|
||||
<a class="section-nav-pill" href="/zh/categories/case-studies/">工程案例</a>
|
||||
<a class="section-nav-pill" href="/zh/categories/devops/">运维</a>
|
||||
<a class="section-nav-pill" href="/zh/categories/tutorials/">教程</a>
|
||||
<a class="section-nav-pill" href="/zh/categories/notes/">笔记</a>
|
||||
<a class="section-nav-pill" href="/zh/categories/devops/">运维架构</a>
|
||||
<a class="section-nav-pill" href="/zh/categories/tutorials/">实战教程</a>
|
||||
<a class="section-nav-pill" href="/zh/categories/notes/">排障手记</a>
|
||||
<a class="section-nav-pill section-nav-all" href="/zh/posts/">全部 {posts.length} 篇 →</a>
|
||||
</div>
|
||||
</div>
|
||||
{posts.length === 0 ? (
|
||||
{engineeringPosts.length === 0 ? (
|
||||
<p><em>中文文章即将发布,敬请期待。</em></p>
|
||||
) : (
|
||||
<PostList posts={posts} lang="zh" limit={6} />
|
||||
<PostList posts={engineeringPosts} lang="zh" limit={6} />
|
||||
)}
|
||||
<p class="archive-meta"><a href="/zh/posts/">全部 {posts.length} 篇</a></p>
|
||||
</section>
|
||||
|
||||
{creativeLabPosts.length > 0 && (
|
||||
<section class="creative-lab-strip">
|
||||
<div class="lab-strip-header">
|
||||
<div class="lab-strip-title-wrap">
|
||||
<span class="lab-strip-badge">⚡ 本地 GPU 算力</span>
|
||||
<h2>本地 GPU 算力与多模态工程实验室</h2>
|
||||
</div>
|
||||
<p class="lab-strip-sub">
|
||||
基于本地 RTX 3060 生产节点的语音克隆、强制对齐字幕与音视频批量生成管线实录。
|
||||
</p>
|
||||
</div>
|
||||
<div class="lab-cards-grid">
|
||||
{creativeLabPosts.slice(0, 3).map((post) => {
|
||||
const cleanSlug = post.slug.replace(/^zh\//, '');
|
||||
return (
|
||||
<a class="lab-card" href={`/posts/${post.slug}/`}>
|
||||
<div class="lab-card-thumb">
|
||||
<img
|
||||
src={`/og/thumb/${cleanSlug}-400.webp`}
|
||||
alt=""
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
width="400"
|
||||
height="210"
|
||||
onerror="this.style.display='none'"
|
||||
/>
|
||||
</div>
|
||||
<div class="lab-card-body">
|
||||
<span class="lab-card-tag">{categoryLabel(post.data.category, 'zh')}</span>
|
||||
<h3>{post.data.title}</h3>
|
||||
<p>{post.data.description}</p>
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<p class="archive-meta"><a href="/zh/posts/">全部 {posts.length} 篇</a></p>
|
||||
</section>
|
||||
)}
|
||||
</BaseLayout>
|
||||
Reference in new issue
Block a user