Support ultrawide layout with equal columns and taiji branding.

Use 50/50 mode workspace columns, widen content to 1920px on fish screens, replace header logo with taiji icon, and add full-page taiji backdrop.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-10 23:50:19 +08:00
parent f22e3f4d16
commit 04c77dbf77
18 changed files with 120 additions and 31 deletions
+2 -1
View File
@@ -1,10 +1,11 @@
import PageShell from "@/components/page-shell"; import PageShell from "@/components/page-shell";
import BaziForm from "@/components/modes/bazi-form"; import BaziForm from "@/components/modes/bazi-form";
import { APP_CONTAINER } from "@/lib/layout";
export default function BaziPage() { export default function BaziPage() {
return ( return (
<PageShell> <PageShell>
<div className="border-b border-border/40 px-4 py-6 text-center"> <div className={`${APP_CONTAINER} border-b border-border/40 py-6 text-center`}>
<h1 className="text-2xl font-bold tracking-wide"></h1> <h1 className="text-2xl font-bold tracking-wide"></h1>
<p className="mt-2 text-sm text-muted-foreground"> <p className="mt-2 text-sm text-muted-foreground">
· · AI · · AI
+2 -1
View File
@@ -1,10 +1,11 @@
import PageShell from "@/components/page-shell"; import PageShell from "@/components/page-shell";
import CombinedForm from "@/components/modes/combined-form"; import CombinedForm from "@/components/modes/combined-form";
import { APP_CONTAINER } from "@/lib/layout";
export default function CombinedPage() { export default function CombinedPage() {
return ( return (
<PageShell> <PageShell>
<div className="border-b border-border/40 px-4 py-6 text-center"> <div className={`${APP_CONTAINER} border-b border-border/40 py-6 text-center`}>
<h1 className="text-2xl font-bold tracking-wide"></h1> <h1 className="text-2xl font-bold tracking-wide"></h1>
<p className="mt-2 text-sm text-muted-foreground"> <p className="mt-2 text-sm text-muted-foreground">
· · · · · ·
+5 -1
View File
@@ -72,7 +72,7 @@
} }
body { body {
@apply gap flex flex-col bg-background text-foreground zen-bg; @apply gap flex flex-col bg-background/95 text-foreground;
} }
body { body {
@@ -122,4 +122,8 @@
.zen-card-glow { .zen-card-glow {
background: radial-gradient(circle, hsl(var(--primary) / 0.06) 0%, transparent 70%); background: radial-gradient(circle, hsl(var(--primary) / 0.06) 0%, transparent 70%);
} }
.zen-card {
@apply bg-card/85 backdrop-blur-sm;
}
} }
+2 -1
View File
@@ -2,6 +2,7 @@ import Link from "next/link";
import PageShell from "@/components/page-shell"; import PageShell from "@/components/page-shell";
import { GuaGrid } from "@/components/learn/gua-grid"; import { GuaGrid } from "@/components/learn/gua-grid";
import { ZenCard } from "@/components/ui/zen-card"; import { ZenCard } from "@/components/ui/zen-card";
import { APP_CONTAINER } from "@/lib/layout";
import { import {
getGuaName, getGuaName,
guaNumFromMark, guaNumFromMark,
@@ -17,7 +18,7 @@ export default async function LearnOtherPage() {
})); }));
return ( return (
<PageShell className="max-w-6xl px-4 py-8"> <PageShell className={`${APP_CONTAINER} py-8`}>
<div className="mb-8 text-center"> <div className="mb-8 text-center">
<h1 className="text-2xl font-bold tracking-wide"></h1> <h1 className="text-2xl font-bold tracking-wide"></h1>
<p className="mt-2 text-sm text-muted-foreground"> <p className="mt-2 text-sm text-muted-foreground">
+2 -1
View File
@@ -2,6 +2,7 @@ import Link from "next/link";
import PageShell from "@/components/page-shell"; import PageShell from "@/components/page-shell";
import { GuaGrid } from "@/components/learn/gua-grid"; import { GuaGrid } from "@/components/learn/gua-grid";
import { ZenCard } from "@/components/ui/zen-card"; import { ZenCard } from "@/components/ui/zen-card";
import { APP_CONTAINER } from "@/lib/layout";
import { import {
getGuaName, getGuaName,
guaNumFromMark, guaNumFromMark,
@@ -17,7 +18,7 @@ export default async function LearnPage() {
})); }));
return ( return (
<PageShell className="max-w-6xl px-4 py-8"> <PageShell className={`${APP_CONTAINER} py-8`}>
<div className="mb-8 text-center"> <div className="mb-8 text-center">
<h1 className="text-2xl font-bold tracking-wide"></h1> <h1 className="text-2xl font-bold tracking-wide"></h1>
<p className="mt-2 text-sm text-muted-foreground"> <p className="mt-2 text-sm text-muted-foreground">
+2 -1
View File
@@ -1,10 +1,11 @@
import PageShell from "@/components/page-shell"; import PageShell from "@/components/page-shell";
import LiuyaoForm from "@/components/modes/liuyao-form"; import LiuyaoForm from "@/components/modes/liuyao-form";
import { APP_CONTAINER } from "@/lib/layout";
export default function LiuyaoPage() { export default function LiuyaoPage() {
return ( return (
<PageShell> <PageShell>
<div className="border-b border-border/40 px-4 py-6 text-center"> <div className={`${APP_CONTAINER} border-b border-border/40 py-6 text-center`}>
<h1 className="text-2xl font-bold tracking-wide"></h1> <h1 className="text-2xl font-bold tracking-wide"></h1>
<p className="mt-2 text-sm text-muted-foreground"> <p className="mt-2 text-sm text-muted-foreground">
· · 线 / 线 · · 线 / 线
+2 -1
View File
@@ -2,6 +2,7 @@ import Link from "next/link";
import PageShell from "@/components/page-shell"; import PageShell from "@/components/page-shell";
import { BookOpen, BrainCircuit, Compass, Sparkles } from "lucide-react"; import { BookOpen, BrainCircuit, Compass, Sparkles } from "lucide-react";
import { ZenCard } from "@/components/ui/zen-card"; import { ZenCard } from "@/components/ui/zen-card";
import { APP_CONTAINER } from "@/lib/layout";
const MODULES = [ const MODULES = [
{ {
@@ -36,7 +37,7 @@ const MODULES = [
export default function Home() { export default function Home() {
return ( return (
<PageShell className="max-w-4xl px-4 py-10 sm:py-14"> <PageShell className={`${APP_CONTAINER} py-10 sm:py-14`}>
<div className="mb-10 text-center"> <div className="mb-10 text-center">
<p className="text-4xl opacity-30"></p> <p className="text-4xl opacity-30"></p>
<h1 className="mt-2 text-3xl font-bold tracking-[0.2em]"></h1> <h1 className="mt-2 text-3xl font-bold tracking-[0.2em]"></h1>
+4 -1
View File
@@ -1,9 +1,12 @@
import React from "react"; import React from "react";
import { VERSION } from "@/lib/constant"; import { VERSION } from "@/lib/constant";
import { APP_CONTAINER } from "@/lib/layout";
function Footer() { function Footer() {
return ( return (
<footer className="mx-auto flex items-center gap-1 text-xs text-muted-foreground/80"> <footer
className={`${APP_CONTAINER} relative z-10 flex items-center justify-center gap-1 py-4 text-xs text-muted-foreground/80`}
>
<span className="italic"></span> <span className="italic"></span>
<span className="text-muted-foreground/60">·</span> <span className="text-muted-foreground/60">·</span>
<span> v{VERSION}</span> <span> v{VERSION}</span>
+6 -5
View File
@@ -1,8 +1,9 @@
"use client"; "use client";
import Link from "next/link"; import Link from "next/link";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import { ChatGPT } from "@/components/svg"; import { TaijiIcon } from "@/components/svg/taiji";
import { ModeToggle } from "@/components/mode-toggle"; import { ModeToggle } from "@/components/mode-toggle";
import { APP_CONTAINER } from "@/lib/layout";
const NAV_ITEMS = [ const NAV_ITEMS = [
{ href: "/learn", label: "易经学习" }, { href: "/learn", label: "易经学习" },
@@ -34,11 +35,11 @@ function NavLink({ href, label }: { href: string; label: string }) {
export default function Header() { export default function Header() {
return ( return (
<header className="border-b border-border/40 bg-card/60 py-3 shadow-sm backdrop-blur-md"> <header className="relative z-10 border-b border-border/40 bg-card/70 py-3 shadow-sm backdrop-blur-md">
<div className="mx-auto flex w-full max-w-6xl flex-col gap-2 px-4 sm:flex-row sm:items-center sm:justify-between"> <div className={`${APP_CONTAINER} flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between`}>
<Link href="/" className="flex items-center justify-center gap-2 sm:justify-start"> <Link href="/" className="flex items-center justify-center gap-2 sm:justify-start">
<ChatGPT /> <TaijiIcon />
<span className="font-medium"></span> <span className="font-medium tracking-wide"></span>
</Link> </Link>
<nav className="flex flex-wrap items-center justify-center gap-x-4 gap-y-1"> <nav className="flex flex-wrap items-center justify-center gap-x-4 gap-y-1">
{NAV_ITEMS.map((item) => ( {NAV_ITEMS.map((item) => (
+9 -6
View File
@@ -1,6 +1,7 @@
"use client"; "use client";
import { Sparkles } from "lucide-react"; import { Sparkles } from "lucide-react";
import { APP_CONTAINER } from "@/lib/layout";
export function ModeWorkspace({ export function ModeWorkspace({
children, children,
@@ -12,18 +13,20 @@ export function ModeWorkspace({
aiTitle?: string; aiTitle?: string;
}) { }) {
return ( return (
<div className="mx-auto w-full max-w-6xl px-4 py-6 lg:py-8"> <div className={`${APP_CONTAINER} py-6 lg:py-8`}>
<div className="grid gap-6 lg:grid-cols-[minmax(0,1fr)_minmax(280px,380px)] lg:items-start lg:gap-8"> <div className="grid gap-6 lg:grid-cols-2 lg:items-stretch lg:gap-8 xl:gap-10">
<div className="space-y-5">{children}</div> <div className="grid h-full min-h-0 auto-rows-fr gap-5 lg:min-h-[calc(100vh-9rem)]">
<aside className="lg:sticky lg:top-6"> {children}
<div className="zen-card flex min-h-[320px] flex-col rounded-2xl border border-border/60 bg-card/90 shadow-md backdrop-blur-sm lg:min-h-[calc(100vh-8rem)]"> </div>
<aside className="flex min-h-[320px] flex-col lg:min-h-[calc(100vh-9rem)]">
<div className="zen-card flex min-h-0 flex-1 flex-col rounded-2xl border border-border/60 bg-card/90 shadow-md backdrop-blur-sm">
<div className="flex items-center gap-2 border-b border-border/50 px-5 py-4"> <div className="flex items-center gap-2 border-b border-border/50 px-5 py-4">
<Sparkles size={18} className="text-primary/80" /> <Sparkles size={18} className="text-primary/80" />
<h2 className="text-sm font-medium tracking-widest text-muted-foreground"> <h2 className="text-sm font-medium tracking-widest text-muted-foreground">
{aiTitle} {aiTitle}
</h2> </h2>
</div> </div>
<div className="flex flex-1 flex-col p-4 sm:p-5">{aiPanel}</div> <div className="flex min-h-0 flex-1 flex-col p-4 sm:p-5">{aiPanel}</div>
</div> </div>
</aside> </aside>
</div> </div>
+15
View File
@@ -0,0 +1,15 @@
import { TaijiWatermark } from "@/components/svg/taiji";
/** 全页太极背景水印 */
export default function TaijiBackdrop() {
return (
<div
aria-hidden
className="pointer-events-none fixed inset-0 -z-10 overflow-hidden bg-background/80"
>
<TaijiWatermark className="absolute left-1/2 top-1/2 h-[min(95vmin,1200px)] w-[min(95vmin,1200px)] -translate-x-1/2 -translate-y-1/2 opacity-90" />
<TaijiWatermark className="absolute -right-[8%] top-[6%] h-[min(42vmin,520px)] w-[min(42vmin,520px)] opacity-45" />
<TaijiWatermark className="absolute -left-[10%] bottom-[4%] h-[min(38vmin,460px)] w-[min(38vmin,460px)] opacity-40" />
</div>
);
}
+2 -2
View File
@@ -109,7 +109,7 @@ export default function BaziForm() {
/> />
} }
> >
<ZenCard title="出生 · 命局" subtitle="四柱排盘所需信息"> <ZenCard title="出生 · 命局" subtitle="四柱排盘所需信息" className="flex h-full min-h-0 flex-col">
<DateTimePicker <DateTimePicker
label="出生日期 / 时间" label="出生日期 / 时间"
date={date} date={date}
@@ -148,7 +148,7 @@ export default function BaziForm() {
/> />
</ZenCard> </ZenCard>
<ZenCard title="问事 · 排盘" subtitle="所求与命盘结果"> <ZenCard title="问事 · 排盘" subtitle="所求与命盘结果" className="flex h-full min-h-0 flex-col">
<Textarea <Textarea
placeholder="事业、婚姻、健康等..." placeholder="事业、婚姻、健康等..."
value={question} value={question}
+2 -2
View File
@@ -156,7 +156,7 @@ export default function CombinedForm() {
/> />
} }
> >
<ZenCard title="人和 · 生辰" subtitle="出生时空与地域"> <ZenCard title="人和 · 生辰" subtitle="出生时空与地域" className="flex h-full min-h-0 flex-col">
<DateTimePicker <DateTimePicker
label="出生日期 / 时间" label="出生日期 / 时间"
date={birthDate} date={birthDate}
@@ -195,7 +195,7 @@ export default function CombinedForm() {
/> />
</ZenCard> </ZenCard>
<ZenCard title="天时地利 · 问事" subtitle="当前时空与所求"> <ZenCard title="天时地利 · 问事" subtitle="当前时空与所求" className="flex h-full min-h-0 flex-col">
<RegionSelect <RegionSelect
label="当前所在地域" label="当前所在地域"
provinceCode={currentProvince} provinceCode={currentProvince}
+2 -2
View File
@@ -117,7 +117,7 @@ export default function LiuyaoForm() {
/> />
} }
> >
<ZenCard title="问事 · 心意" subtitle="所求何事,心诚则灵"> <ZenCard title="问事 · 心意" subtitle="所求何事,心诚则灵" className="flex h-full min-h-0 flex-col">
<Textarea <Textarea
placeholder="您想算点什么?" placeholder="您想算点什么?"
value={question} value={question}
@@ -139,7 +139,7 @@ export default function LiuyaoForm() {
</div> </div>
</ZenCard> </ZenCard>
<ZenCard title="起卦 · 时空" subtitle="地域、时辰与六爻"> <ZenCard title="起卦 · 时空" subtitle="地域、时辰与六爻" className="flex h-full min-h-0 flex-col">
<RegionSelect <RegionSelect
label="起卦地域" label="起卦地域"
provinceCode={provinceCode} provinceCode={provinceCode}
+4 -4
View File
@@ -1,5 +1,6 @@
import Header from "@/components/header"; import Header from "@/components/header";
import Footer from "@/components/footer"; import Footer from "@/components/footer";
import TaijiBackdrop from "@/components/layout/taiji-backdrop";
export default function PageShell({ export default function PageShell({
children, children,
@@ -9,11 +10,10 @@ export default function PageShell({
className?: string; className?: string;
}) { }) {
return ( return (
<div className="flex min-h-screen flex-col"> <div className="relative flex min-h-screen flex-col">
<TaijiBackdrop />
<Header /> <Header />
<div className={`mx-auto flex w-full flex-1 flex-col ${className}`}> <div className={`flex w-full flex-1 flex-col ${className}`}>{children}</div>
{children}
</div>
<Footer /> <Footer />
</div> </div>
); );
+3 -2
View File
@@ -2,6 +2,7 @@ import React, { useEffect, useRef, useState } from "react";
import { RotateCw } from "lucide-react"; import { RotateCw } from "lucide-react";
import Markdown from "react-markdown"; import Markdown from "react-markdown";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { TaijiIcon } from "@/components/svg/taiji";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
function ResultAI({ function ResultAI({
@@ -85,8 +86,8 @@ function ResultAI({
<p className="text-sm text-muted-foreground"> AI ...</p> <p className="text-sm text-muted-foreground"> AI ...</p>
) : ( ) : (
<div className="flex h-full min-h-[200px] flex-col items-center justify-center text-center text-muted-foreground"> <div className="flex h-full min-h-[200px] flex-col items-center justify-center text-center text-muted-foreground">
<p className="text-4xl opacity-20"></p> <TaijiIcon className="h-16 w-16 opacity-25" />
<p className="mt-3 max-w-[200px] text-sm leading-relaxed"> <p className="mt-3 max-w-[220px] text-sm leading-relaxed">
{emptyHint} {emptyHint}
</p> </p>
</div> </div>
+53
View File
@@ -0,0 +1,53 @@
import React from "react";
import { cn } from "@/lib/utils";
const YANG = "fill-stone-100 dark:fill-stone-200";
const YIN = "fill-stone-800 dark:fill-stone-900";
/** 太极阴阳鱼图标(页眉 Logo) */
export const TaijiIcon = React.memo(function TaijiIcon({
className,
}: {
className?: string;
}) {
return (
<svg
viewBox="0 0 64 64"
xmlns="http://www.w3.org/2000/svg"
className={cn("h-7 w-7 shrink-0 drop-shadow-sm", className)}
aria-hidden
>
<circle cx="32" cy="32" r="30" className={YANG} />
<path
d="M32 2a30 30 0 0 1 0 60 15 15 0 0 1 0-30 15 15 0 0 0 0-30z"
className={YIN}
/>
<circle cx="32" cy="17" r="5" className={YANG} />
<circle cx="32" cy="47" r="5" className={YIN} />
</svg>
);
});
/** 背景用水印版太极 */
export function TaijiWatermark({
className,
}: {
className?: string;
}) {
return (
<svg
viewBox="0 0 200 200"
xmlns="http://www.w3.org/2000/svg"
className={className}
aria-hidden
>
<circle cx="100" cy="100" r="98" className={`${YANG} opacity-[0.35]`} />
<path
d="M100 2a98 98 0 0 1 0 196 49 49 0 0 1 0-98 49 49 0 0 0 0-98z"
className={`${YIN} opacity-[0.08] dark:opacity-[0.12]`}
/>
<circle cx="100" cy="51" r="10" className={`${YANG} opacity-50`} />
<circle cx="100" cy="149" r="10" className={`${YIN} opacity-20 dark:opacity-30`} />
</svg>
);
}
+3
View File
@@ -0,0 +1,3 @@
/** 全站内容区宽度:手机满宽,带鱼屏最大约 1920px 并留边距 */
export const APP_CONTAINER =
"mx-auto w-full max-w-[min(100%,1920px)] px-4 sm:px-6 lg:px-8 xl:px-12 2xl:px-16";