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
+4 -1
View File
@@ -1,9 +1,12 @@
import React from "react";
import { VERSION } from "@/lib/constant";
import { APP_CONTAINER } from "@/lib/layout";
function Footer() {
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="text-muted-foreground/60">·</span>
<span> v{VERSION}</span>
+6 -5
View File
@@ -1,8 +1,9 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { ChatGPT } from "@/components/svg";
import { TaijiIcon } from "@/components/svg/taiji";
import { ModeToggle } from "@/components/mode-toggle";
import { APP_CONTAINER } from "@/lib/layout";
const NAV_ITEMS = [
{ href: "/learn", label: "易经学习" },
@@ -34,11 +35,11 @@ function NavLink({ href, label }: { href: string; label: string }) {
export default function Header() {
return (
<header className="border-b border-border/40 bg-card/60 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">
<header className="relative z-10 border-b border-border/40 bg-card/70 py-3 shadow-sm backdrop-blur-md">
<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">
<ChatGPT />
<span className="font-medium"></span>
<TaijiIcon />
<span className="font-medium tracking-wide"></span>
</Link>
<nav className="flex flex-wrap items-center justify-center gap-x-4 gap-y-1">
{NAV_ITEMS.map((item) => (
+9 -6
View File
@@ -1,6 +1,7 @@
"use client";
import { Sparkles } from "lucide-react";
import { APP_CONTAINER } from "@/lib/layout";
export function ModeWorkspace({
children,
@@ -12,18 +13,20 @@ export function ModeWorkspace({
aiTitle?: string;
}) {
return (
<div className="mx-auto w-full max-w-6xl px-4 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="space-y-5">{children}</div>
<aside className="lg:sticky lg:top-6">
<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 className={`${APP_CONTAINER} py-6 lg:py-8`}>
<div className="grid gap-6 lg:grid-cols-2 lg:items-stretch lg:gap-8 xl:gap-10">
<div className="grid h-full min-h-0 auto-rows-fr gap-5 lg:min-h-[calc(100vh-9rem)]">
{children}
</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">
<Sparkles size={18} className="text-primary/80" />
<h2 className="text-sm font-medium tracking-widest text-muted-foreground">
{aiTitle}
</h2>
</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>
</aside>
</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
label="出生日期 / 时间"
date={date}
@@ -148,7 +148,7 @@ export default function BaziForm() {
/>
</ZenCard>
<ZenCard title="问事 · 排盘" subtitle="所求与命盘结果">
<ZenCard title="问事 · 排盘" subtitle="所求与命盘结果" className="flex h-full min-h-0 flex-col">
<Textarea
placeholder="事业、婚姻、健康等..."
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
label="出生日期 / 时间"
date={birthDate}
@@ -195,7 +195,7 @@ export default function CombinedForm() {
/>
</ZenCard>
<ZenCard title="天时地利 · 问事" subtitle="当前时空与所求">
<ZenCard title="天时地利 · 问事" subtitle="当前时空与所求" className="flex h-full min-h-0 flex-col">
<RegionSelect
label="当前所在地域"
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
placeholder="您想算点什么?"
value={question}
@@ -139,7 +139,7 @@ export default function LiuyaoForm() {
</div>
</ZenCard>
<ZenCard title="起卦 · 时空" subtitle="地域、时辰与六爻">
<ZenCard title="起卦 · 时空" subtitle="地域、时辰与六爻" className="flex h-full min-h-0 flex-col">
<RegionSelect
label="起卦地域"
provinceCode={provinceCode}
+4 -4
View File
@@ -1,5 +1,6 @@
import Header from "@/components/header";
import Footer from "@/components/footer";
import TaijiBackdrop from "@/components/layout/taiji-backdrop";
export default function PageShell({
children,
@@ -9,11 +10,10 @@ export default function PageShell({
className?: string;
}) {
return (
<div className="flex min-h-screen flex-col">
<div className="relative flex min-h-screen flex-col">
<TaijiBackdrop />
<Header />
<div className={`mx-auto flex w-full flex-1 flex-col ${className}`}>
{children}
</div>
<div className={`flex w-full flex-1 flex-col ${className}`}>{children}</div>
<Footer />
</div>
);
+3 -2
View File
@@ -2,6 +2,7 @@ import React, { useEffect, useRef, useState } from "react";
import { RotateCw } from "lucide-react";
import Markdown from "react-markdown";
import { Button } from "@/components/ui/button";
import { TaijiIcon } from "@/components/svg/taiji";
import { cn } from "@/lib/utils";
function ResultAI({
@@ -85,8 +86,8 @@ function ResultAI({
<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">
<p className="text-4xl opacity-20"></p>
<p className="mt-3 max-w-[200px] text-sm leading-relaxed">
<TaijiIcon className="h-16 w-16 opacity-25" />
<p className="mt-3 max-w-[220px] text-sm leading-relaxed">
{emptyHint}
</p>
</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>
);
}