Implement three divination modes, learn pages, and PM2 deploy on port 3130.

Add liuyao/bazi/combined flows with shared calc and AI infrastructure, 64-gua learn routes, and update Ubuntu PM2 deployment docs for port 3130.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-10 20:19:49 +08:00
parent d141623070
commit fff77dac3f
41 changed files with 2590 additions and 385 deletions
+47
View File
@@ -0,0 +1,47 @@
import Link from "next/link";
import { notFound } from "next/navigation";
import PageShell from "@/components/page-shell";
import GuaFooter from "@/components/learn/gua-footer";
import MarkdownContent from "@/components/learn/markdown-content";
import {
getGuaName,
getGuaNumber,
listGuaMarks,
readLearnMarkdown,
stripFrontmatter,
} from "@/lib/content/zhouyi";
export async function generateStaticParams() {
const marks = await listGuaMarks("traditional");
return marks.map((guaMark) => ({ guaMark }));
}
export default async function GuaDetailPage({
params,
}: {
params: Promise<{ guaMark: string }>;
}) {
const { guaMark } = await params;
const marks = await listGuaMarks("traditional");
if (!marks.includes(guaMark)) {
notFound();
}
const raw = await readLearnMarkdown(guaMark, "traditional");
const content = stripFrontmatter(raw);
return (
<PageShell className="max-w-3xl px-4 py-8">
<div className="mb-6 text-sm text-muted-foreground">
<Link href="/learn" className="hover:text-foreground">
</Link>
</div>
<div className="mb-4 text-sm text-muted-foreground">
{getGuaNumber(guaMark)} · {getGuaName(guaMark)}
</div>
<MarkdownContent content={content} variant="traditional" />
<GuaFooter guaMark={guaMark} />
</PageShell>
);
}
+47
View File
@@ -0,0 +1,47 @@
import Link from "next/link";
import { notFound } from "next/navigation";
import PageShell from "@/components/page-shell";
import GuaFooter from "@/components/learn/gua-footer";
import MarkdownContent from "@/components/learn/markdown-content";
import {
getGuaName,
getGuaNumber,
listGuaMarks,
readLearnMarkdown,
stripFrontmatter,
} from "@/lib/content/zhouyi";
export async function generateStaticParams() {
const marks = await listGuaMarks("simplified");
return marks.map((guaMark) => ({ guaMark }));
}
export default async function GuaOtherDetailPage({
params,
}: {
params: Promise<{ guaMark: string }>;
}) {
const { guaMark } = await params;
const marks = await listGuaMarks("simplified");
if (!marks.includes(guaMark)) {
notFound();
}
const raw = await readLearnMarkdown(guaMark, "simplified");
const content = stripFrontmatter(raw);
return (
<PageShell className="max-w-3xl px-4 py-8">
<div className="mb-6 text-sm text-muted-foreground">
<Link href="/learn/other" className="hover:text-foreground">
</Link>
</div>
<div className="mb-4 text-sm text-muted-foreground">
{getGuaNumber(guaMark)} · {getGuaName(guaMark)}
</div>
<MarkdownContent content={content} variant="simplified" />
<GuaFooter guaMark={guaMark} />
</PageShell>
);
}
+55
View File
@@ -0,0 +1,55 @@
import Link from "next/link";
import PageShell from "@/components/page-shell";
import {
getGuaName,
getGuaNumber,
listGuaMarks,
} from "@/lib/content/zhouyi";
export default async function LearnOtherPage() {
const guaMarks = await listGuaMarks("simplified");
return (
<PageShell className="max-w-3xl px-4 py-8">
<div className="mb-6">
<h1 className="text-2xl font-bold"> · </h1>
<p className="mt-2 text-muted-foreground">
</p>
<Link
href="/learn"
className="mt-2 inline-block text-sm text-primary underline underline-offset-4"
>
</Link>
</div>
<div className="overflow-x-auto rounded-lg border">
<table className="w-full text-sm">
<thead className="bg-muted/50">
<tr>
<th className="px-4 py-2 text-left font-medium"></th>
<th className="px-4 py-2 text-left font-medium"></th>
</tr>
</thead>
<tbody>
{guaMarks.map((mark) => (
<tr key={mark} className="border-t">
<td className="px-4 py-2 font-mono text-muted-foreground">
{getGuaNumber(mark)}
</td>
<td className="px-4 py-2">
<Link
href={`/learn/other/${encodeURIComponent(mark)}`}
className="text-primary underline-offset-4 hover:underline"
>
{getGuaName(mark)}
</Link>
</td>
</tr>
))}
</tbody>
</table>
</div>
</PageShell>
);
}
+55
View File
@@ -0,0 +1,55 @@
import Link from "next/link";
import PageShell from "@/components/page-shell";
import {
getGuaName,
getGuaNumber,
listGuaMarks,
} from "@/lib/content/zhouyi";
export default async function LearnPage() {
const guaMarks = await listGuaMarks("traditional");
return (
<PageShell className="max-w-3xl px-4 py-8">
<div className="mb-6">
<h1 className="text-2xl font-bold"></h1>
<p className="mt-2 text-muted-foreground">
</p>
<Link
href="/learn/other"
className="mt-2 inline-block text-sm text-primary underline underline-offset-4"
>
</Link>
</div>
<div className="overflow-x-auto rounded-lg border">
<table className="w-full text-sm">
<thead className="bg-muted/50">
<tr>
<th className="px-4 py-2 text-left font-medium"></th>
<th className="px-4 py-2 text-left font-medium"></th>
</tr>
</thead>
<tbody>
{guaMarks.map((mark) => (
<tr key={mark} className="border-t">
<td className="px-4 py-2 font-mono text-muted-foreground">
{getGuaNumber(mark)}
</td>
<td className="px-4 py-2">
<Link
href={`/learn/${encodeURIComponent(mark)}`}
className="text-primary underline-offset-4 hover:underline"
>
{getGuaName(mark)}
</Link>
</td>
</tr>
))}
</tbody>
</table>
</div>
</PageShell>
);
}