Redesign UI with zen cards, split AI panel, and PWA install support.

Learn uses 64-gua card grid; liuyao/bazi/combined use two input cards plus sticky right AI panel; add manifest, service worker, and install prompt.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-10 23:24:55 +08:00
parent 206673fd90
commit 6265e56a7f
20 changed files with 682 additions and 423 deletions
+3 -3
View File
@@ -4,9 +4,9 @@ import BaziForm from "@/components/modes/bazi-form";
export default function BaziPage() {
return (
<PageShell>
<div className="px-4 pt-6 text-center">
<h1 className="text-xl font-bold"></h1>
<p className="mt-1 text-sm text-muted-foreground">
<div className="border-b border-border/40 px-4 py-6 text-center">
<h1 className="text-2xl font-bold tracking-wide"></h1>
<p className="mt-2 text-sm text-muted-foreground">
· · AI
</p>
</div>
+3 -3
View File
@@ -4,9 +4,9 @@ import CombinedForm from "@/components/modes/combined-form";
export default function CombinedPage() {
return (
<PageShell>
<div className="px-4 pt-6 text-center">
<h1 className="text-xl font-bold"></h1>
<p className="mt-1 text-sm text-muted-foreground">
<div className="border-b border-border/40 px-4 py-6 text-center">
<h1 className="text-2xl font-bold tracking-wide"></h1>
<p className="mt-2 text-sm text-muted-foreground">
· · ·
</p>
</div>
+17 -1
View File
@@ -72,7 +72,7 @@
}
body {
@apply gap flex flex-col bg-background text-foreground;
@apply gap flex flex-col bg-background text-foreground zen-bg;
}
body {
@@ -106,4 +106,20 @@
::-webkit-scrollbar-thumb {
@apply rounded-md bg-border;
}
.zen-bg {
background-image:
radial-gradient(ellipse 80% 50% at 50% -20%, hsl(40 20% 90% / 0.5), transparent),
radial-gradient(circle at 100% 100%, hsl(30 15% 92% / 0.4), transparent 40%);
}
.dark .zen-bg {
background-image:
radial-gradient(ellipse 80% 50% at 50% -20%, hsl(30 10% 20% / 0.5), transparent),
radial-gradient(circle at 100% 100%, hsl(25 8% 15% / 0.4), transparent 40%);
}
.zen-card-glow {
background: radial-gradient(circle, hsl(var(--primary) / 0.06) 0%, transparent 70%);
}
}
+5
View File
@@ -2,6 +2,7 @@ import "./globals.css";
import type { Metadata, Viewport } from "next";
import React from "react";
import Umami from "@/components/umami";
import PwaProvider from "@/components/pwa/pwa-provider";
import { ThemeProvider } from "next-themes";
export const metadata: Metadata = {
@@ -9,8 +10,11 @@ export const metadata: Metadata = {
description:
"知命阁 — 融合周易智慧与人工智能,提供易经学习、六爻算卦、生辰八字排盘、综合测算等服务",
appleWebApp: {
capable: true,
title: "知命阁",
statusBarStyle: "default",
},
manifest: "/manifest.webmanifest",
};
export const viewport: Viewport = {
@@ -45,6 +49,7 @@ export default function RootLayout({
disableTransitionOnChange
>
{children}
<PwaProvider />
</ThemeProvider>
<Umami />
</body>
+38 -37
View File
@@ -1,5 +1,7 @@
import Link from "next/link";
import PageShell from "@/components/page-shell";
import { GuaGrid } from "@/components/learn/gua-grid";
import { ZenCard } from "@/components/ui/zen-card";
import {
getGuaName,
guaNumFromMark,
@@ -8,48 +10,47 @@ import {
export default async function LearnOtherPage() {
const guaMarks = await listGuaMarks("simplified");
const items = guaMarks.map((mark) => ({
num: guaNumFromMark(mark),
name: getGuaName(mark),
href: `/learn/other/${guaNumFromMark(mark)}`,
}));
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">
<PageShell className="max-w-6xl px-4 py-8">
<div className="mb-8 text-center">
<h1 className="text-2xl font-bold tracking-wide"></h1>
<p className="mt-2 text-sm text-muted-foreground">
·
</p>
<Link
href="/learn"
className="mt-2 inline-block text-sm text-primary underline underline-offset-4"
>
</div>
<div className="mb-8 grid gap-4 sm:grid-cols-2">
<Link href="/learn" className="block">
<ZenCard
title="繁体精简版"
subtitle="原文精要"
className="h-full transition-colors hover:border-primary/30"
>
<p className="text-sm text-muted-foreground">
</p>
<span className="inline-block text-xs text-primary"> </span>
</ZenCard>
</Link>
<ZenCard
title="简体图文版"
subtitle="当前浏览"
className="ring-1 ring-primary/20"
>
<p className="text-sm text-muted-foreground">
</p>
<span className="inline-block text-xs text-primary"></span>
</ZenCard>
</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">
{guaNumFromMark(mark)}
</td>
<td className="px-4 py-2">
<Link
href={`/learn/other/${guaNumFromMark(mark)}`}
className="text-primary underline-offset-4 hover:underline"
>
{getGuaName(mark)}
</Link>
</td>
</tr>
))}
</tbody>
</table>
</div>
<GuaGrid items={items} />
</PageShell>
);
}
+37 -36
View File
@@ -1,5 +1,7 @@
import Link from "next/link";
import PageShell from "@/components/page-shell";
import { GuaGrid } from "@/components/learn/gua-grid";
import { ZenCard } from "@/components/ui/zen-card";
import {
getGuaName,
guaNumFromMark,
@@ -8,48 +10,47 @@ import {
export default async function LearnPage() {
const guaMarks = await listGuaMarks("traditional");
const items = guaMarks.map((mark) => ({
num: guaNumFromMark(mark),
name: getGuaName(mark),
href: `/learn/${guaNumFromMark(mark)}`,
}));
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">
<PageShell className="max-w-6xl px-4 py-8">
<div className="mb-8 text-center">
<h1 className="text-2xl font-bold tracking-wide"></h1>
<p className="mt-2 text-sm text-muted-foreground">
·
</p>
<Link
href="/learn/other"
className="mt-2 inline-block text-sm text-primary underline underline-offset-4"
</div>
<div className="mb-8 grid gap-4 sm:grid-cols-2">
<ZenCard
title="繁体精简版"
subtitle="当前浏览 · 原文精要"
className="ring-1 ring-primary/20"
>
<p className="text-sm text-muted-foreground">
AI
</p>
<span className="inline-block text-xs text-primary"></span>
</ZenCard>
<Link href="/learn/other" className="block">
<ZenCard
title="简体图文版"
subtitle="互卦 · 错卦 · 综卦"
className="h-full transition-colors hover:border-primary/30"
>
<p className="text-sm text-muted-foreground">
</p>
<span className="inline-block text-xs text-primary"> </span>
</ZenCard>
</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">
{guaNumFromMark(mark)}
</td>
<td className="px-4 py-2">
<Link
href={`/learn/${guaNumFromMark(mark)}`}
className="text-primary underline-offset-4 hover:underline"
>
{getGuaName(mark)}
</Link>
</td>
</tr>
))}
</tbody>
</table>
</div>
<GuaGrid items={items} />
</PageShell>
);
}
+3 -3
View File
@@ -4,9 +4,9 @@ import LiuyaoForm from "@/components/modes/liuyao-form";
export default function LiuyaoPage() {
return (
<PageShell>
<div className="px-4 pt-6 text-center">
<h1 className="text-xl font-bold"></h1>
<p className="mt-1 text-sm text-muted-foreground">
<div className="border-b border-border/40 px-4 py-6 text-center">
<h1 className="text-2xl font-bold tracking-wide"></h1>
<p className="mt-2 text-sm text-muted-foreground">
· · 线 / 线
</p>
</div>
+36
View File
@@ -0,0 +1,36 @@
import type { MetadataRoute } from "next";
export default function manifest(): MetadataRoute.Manifest {
return {
name: "知命阁",
short_name: "知命阁",
description:
"融合周易智慧与人工智能 — 易经学习、六爻算卦、生辰八字、综合测算",
start_url: "/",
display: "standalone",
orientation: "portrait-primary",
background_color: "#f5f5f4",
theme_color: "#525252",
lang: "zh-CN",
icons: [
{
src: "/apple-icon.png",
sizes: "180x180",
type: "image/png",
purpose: "any",
},
{
src: "/apple-icon.png",
sizes: "192x192",
type: "image/png",
purpose: "maskable",
},
{
src: "/apple-icon.png",
sizes: "512x512",
type: "image/png",
purpose: "any",
},
],
};
}
+29 -19
View File
@@ -1,55 +1,65 @@
import Link from "next/link";
import PageShell from "@/components/page-shell";
import { BookOpen, BrainCircuit, Compass, Sparkles } from "lucide-react";
import { ZenCard } from "@/components/ui/zen-card";
const MODULES = [
{
href: "/learn",
title: "易经学习",
description: "64 卦原文阅读,繁体精简与简体图文",
description: "64 卦卡片择读,繁体精简与简体图文",
icon: BookOpen,
accent: "from-amber-500/10 to-transparent",
},
{
href: "/liuyao",
title: "六爻算卦",
description: "三钱法起卦,结合卦辞 AI 智能解读",
description: "三钱法起卦,卦辞 AI 智能解读",
icon: Sparkles,
accent: "from-stone-500/10 to-transparent",
},
{
href: "/bazi",
title: "生辰八字",
description: "四柱排盘,十神大运流年AI 命理解读",
description: "四柱排盘,十神大运,AI 命理解读",
icon: BrainCircuit,
accent: "from-zinc-500/10 to-transparent",
},
{
href: "/combined",
title: "综合测算",
description: "天时地利人和融合分析,六爻可选",
description: "天时地利人和,六爻可选",
icon: Compass,
accent: "from-neutral-500/10 to-transparent",
},
];
export default function Home() {
return (
<PageShell className="max-w-2xl px-4 py-8">
<div className="mb-8 text-center">
<h1 className="text-2xl font-bold tracking-tight"></h1>
<p className="mt-2 text-muted-foreground">
<PageShell className="max-w-4xl px-4 py-10 sm:py-14">
<div className="mb-10 text-center">
<p className="text-4xl opacity-30"></p>
<h1 className="mt-2 text-3xl font-bold tracking-[0.2em]"></h1>
<p className="mt-3 text-sm tracking-wide text-muted-foreground">
</p>
</div>
<div className="grid gap-4 sm:grid-cols-2">
{MODULES.map(({ href, title, description, icon: Icon }) => (
<Link
key={href}
href={href}
className="group rounded-lg border bg-card p-5 shadow-sm transition-colors hover:border-primary/40 hover:bg-accent/30"
>
<div className="mb-3 flex items-center gap-2">
<Icon size={20} className="text-primary" />
<span className="font-medium">{title}</span>
</div>
<p className="text-sm text-muted-foreground">{description}</p>
{MODULES.map(({ href, title, description, icon: Icon, accent }) => (
<Link key={href} href={href} className="group block">
<ZenCard
className={`h-full bg-gradient-to-br ${accent} transition-all duration-300 group-hover:-translate-y-0.5 group-hover:shadow-md`}
>
<div className="mb-3 flex items-center gap-3">
<span className="flex h-10 w-10 items-center justify-center rounded-full border border-border/60 bg-background/80">
<Icon size={20} className="text-primary/80" />
</span>
<span className="text-lg font-medium tracking-wide">{title}</span>
</div>
<p className="text-sm leading-relaxed text-muted-foreground">
{description}
</p>
</ZenCard>
</Link>
))}
</div>