first commit
ci / build (push) Failing after 23s
deploy / deploy (push) Failing after 10m12s

This commit is contained in:
soroush.asadi
2026-05-31 12:47:02 +03:30
commit add78d8460
100 changed files with 15221 additions and 0 deletions
+178
View File
@@ -0,0 +1,178 @@
'use client';
import Link from 'next/link';
import { motion } from 'framer-motion';
import { useLocale } from '@/lib/i18n/locale-context';
import type { PostContent, Block } from '@/lib/content/posts';
import { cn } from '@/lib/utils';
const FA_DIGITS = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'] as const;
const toFa = (s: string | number) =>
s.toString().replace(/\d/g, (d) => FA_DIGITS[Number(d)]);
type Meta = { title: string; category: string; readTime: number };
const ACCENT_TEXT: Record<PostContent['accent'], string> = {
electric: 'text-electric',
violet: 'text-violet',
magenta: 'text-magenta',
emerald: 'text-emerald',
cyan: 'text-cyan',
};
const ACCENT_BORDER: Record<PostContent['accent'], string> = {
electric: 'border-electric/30 bg-electric/5 text-electric',
violet: 'border-violet/30 bg-violet/5 text-violet',
magenta: 'border-magenta/30 bg-magenta/5 text-magenta',
emerald: 'border-emerald/30 bg-emerald/5 text-emerald',
cyan: 'border-cyan/30 bg-cyan/5 text-cyan',
};
export function BlogArticle({
meta,
content,
}: {
meta: { fa: Meta; en: Meta };
content: PostContent;
}) {
const { t, locale } = useLocale();
const m = meta[locale];
const body = content[locale];
const dir = locale === 'fa' ? 'rtl' : 'ltr';
const dateLabel = new Intl.DateTimeFormat(
locale === 'fa' ? 'fa-IR' : 'en-US',
{ year: 'numeric', month: 'long', day: 'numeric' },
).format(new Date(content.date));
return (
<article dir={dir} className="relative px-5 py-32 sm:px-8">
{/* Cover glow */}
<div
aria-hidden
className="pointer-events-none absolute inset-x-0 top-0 -z-10 h-80 bg-radial-aurora opacity-60"
/>
<div className="mx-auto max-w-3xl">
<Link
href="/#blog"
className="label-mono inline-flex items-center gap-2 text-slate-400 transition-colors hover:text-electric"
>
<span className={locale === 'fa' ? 'rotate-180' : ''}></span>
{t.nav.blog}
</Link>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, ease: [0.22, 1, 0.36, 1] }}
>
<div className="mt-7 flex flex-wrap items-center gap-3">
<span
className={cn(
'rounded-full border px-2.5 py-0.5 font-mono text-[0.65rem] uppercase tracking-wider',
ACCENT_BORDER[content.accent],
)}
>
{m.category}
</span>
<span className="font-mono text-[0.7rem] text-slate-500">
{dateLabel}
</span>
<span className="font-mono text-[0.7rem] text-slate-500">
{locale === 'fa' ? toFa(m.readTime) : m.readTime}{' '}
{t.blog.readTimeSuffix}
</span>
</div>
<h1
className={cn(
'mt-5 font-display text-[clamp(2rem,4.5vw,3.2rem)] font-extrabold leading-[1.08] tracking-tight text-white',
locale === 'fa' && 'font-fa',
)}
>
{m.title}
</h1>
<p className="mt-6 text-balance text-[clamp(1.05rem,1.8vw,1.3rem)] leading-relaxed text-slate-300">
{body.lead}
</p>
</motion.div>
{/* Body */}
<div className="mt-12 flex flex-col gap-6">
{body.blocks.map((block, i) => (
<BlockView key={i} block={block} accent={content.accent} />
))}
</div>
{/* CTA */}
<div className="mt-16 border-t border-white/5 pt-10">
<p className="text-slate-400">
{locale === 'fa'
? 'این موضوع به سیستم شما مربوط است؟ بیایید درباره‌اش صحبت کنیم.'
: 'Is this relevant to your system? Lets talk it through.'}
</p>
<div className="mt-5 flex flex-wrap gap-3">
<Link href="/#contact" className="btn-primary">
{t.hero.ctaPrimary}
</Link>
<Link href="/#blog" className="btn-ghost">
{t.nav.blog}
</Link>
</div>
</div>
</div>
</article>
);
}
function BlockView({
block,
accent,
}: {
block: Block;
accent: PostContent['accent'];
}) {
switch (block.k) {
case 'h2':
return (
<h2 className="mt-4 font-display text-[clamp(1.3rem,2.4vw,1.7rem)] font-semibold leading-snug text-white">
{block.t}
</h2>
);
case 'p':
return (
<p className="text-[1.02rem] leading-[1.85] text-slate-300">
{block.t}
</p>
);
case 'ul':
return (
<ul className="flex flex-col gap-2.5">
{block.items.map((it, i) => (
<li key={i} className="flex gap-3 text-[1.02rem] leading-relaxed text-slate-300">
<span className={cn('mt-2 h-1.5 w-1.5 shrink-0 rounded-full', `bg-current ${ACCENT_TEXT[accent]}`)} />
<span>{it}</span>
</li>
))}
</ul>
);
case 'quote':
return (
<blockquote
className={cn(
'my-2 border-s-2 ps-5 text-[1.1rem] font-medium italic leading-relaxed text-slate-200',
accent === 'magenta' ? 'border-magenta' : 'border-electric',
)}
>
{block.t}
</blockquote>
);
case 'code':
return (
<pre className="overflow-x-auto rounded-xl border border-white/10 bg-base-900/80 p-4 font-mono text-[0.85rem] leading-relaxed text-slate-200">
<code>{block.t}</code>
</pre>
);
}
}