feat: improve app ui and project metadata
This commit is contained in:
@@ -3,7 +3,8 @@
|
||||
@tailwind utilities;
|
||||
|
||||
/* 纸感设计 token(UX_SPEC §2.1) */
|
||||
:root {
|
||||
:root,
|
||||
[data-theme="paper"] {
|
||||
--color-bg: #f5f1e8;
|
||||
--color-panel: #fbf8f1;
|
||||
--color-ink: #2b2620;
|
||||
@@ -15,6 +16,32 @@
|
||||
--color-overdue: #c8893a;
|
||||
--color-pass: #5a6b4f;
|
||||
--color-info: #4a5a6b;
|
||||
--color-conflict-mark: #b5543a26;
|
||||
--color-conflict-mark-strong: #b5543a8c;
|
||||
--shadow-paper: #2b26200f;
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
[data-theme="night"] {
|
||||
--color-bg: #181614;
|
||||
--color-panel: #221f1b;
|
||||
--color-ink: #efe6d7;
|
||||
--color-ink-soft: #b9ab96;
|
||||
--color-line: #3c352c;
|
||||
--color-cinnabar: #e07866;
|
||||
--color-cinnabar-wash: #e0786620;
|
||||
--color-conflict: #ef8a72;
|
||||
--color-overdue: #d8a65d;
|
||||
--color-pass: #95b47d;
|
||||
--color-info: #8ca8ca;
|
||||
--color-conflict-mark: #ef8a7230;
|
||||
--color-conflict-mark-strong: #ef8a7290;
|
||||
--shadow-paper: #00000045;
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
html {
|
||||
background: var(--color-bg);
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -22,6 +49,11 @@ body {
|
||||
color: var(--color-ink);
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: var(--color-cinnabar-wash);
|
||||
color: var(--color-ink);
|
||||
}
|
||||
|
||||
/* 流式打字机光标:朱砂闪烁;尊重 prefers-reduced-motion(UX §10)。 */
|
||||
.typewriter-cursor {
|
||||
animation: typewriter-blink 1s step-end infinite;
|
||||
@@ -42,7 +74,7 @@ body {
|
||||
|
||||
/* 终稿正文:冲突原文持续高亮(朱砂淡底),点锚点定位时闪烁一下(UX §8.3)。 */
|
||||
.draft-mark {
|
||||
background-color: #b5543a26; /* --color-conflict @ ~15% */
|
||||
background-color: var(--color-conflict-mark);
|
||||
border-radius: 2px;
|
||||
}
|
||||
.draft-mark-flash {
|
||||
@@ -52,10 +84,10 @@ body {
|
||||
@keyframes draft-mark-flash {
|
||||
0%,
|
||||
100% {
|
||||
background-color: #b5543a26;
|
||||
background-color: var(--color-conflict-mark);
|
||||
}
|
||||
30% {
|
||||
background-color: #b5543a8c; /* --color-conflict @ ~55% */
|
||||
background-color: var(--color-conflict-mark-strong);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
|
||||
import "./globals.css";
|
||||
|
||||
import { CommandPaletteMount } from "@/components/command/CommandPaletteMount";
|
||||
import { ThemeScript } from "@/components/ThemeScript";
|
||||
import { ToastProvider } from "@/components/Toast";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@@ -19,6 +20,7 @@ export default function RootLayout({
|
||||
return (
|
||||
<html lang="zh-CN" suppressHydrationWarning>
|
||||
<body className="font-sans antialiased" suppressHydrationWarning>
|
||||
<ThemeScript />
|
||||
<ToastProvider>
|
||||
{children}
|
||||
<CommandPaletteMount />
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import Link from "next/link";
|
||||
import { BookOpen, Plus } from "lucide-react";
|
||||
|
||||
import { AppShell } from "@/components/AppShell";
|
||||
import { ProjectCard } from "@/components/ProjectCard";
|
||||
import { ProjectLibrary } from "@/components/projects/ProjectLibrary";
|
||||
import { EmptyState } from "@/components/ui/EmptyState";
|
||||
import { PageHeader } from "@/components/ui/PageHeader";
|
||||
import { fetchProjects } from "@/lib/api/server";
|
||||
import type { ProjectResponse } from "@/lib/api/types";
|
||||
import { buttonClass } from "@/lib/ui/variants";
|
||||
|
||||
// 作品库(Dashboard 入口,UX §6.1)。Server Component 读取。
|
||||
export default async function DashboardPage() {
|
||||
@@ -18,63 +22,44 @@ export default async function DashboardPage() {
|
||||
|
||||
return (
|
||||
<AppShell>
|
||||
<div className="mx-auto max-w-5xl px-8 py-10">
|
||||
<div className="mb-8 flex items-center justify-between">
|
||||
<h1 className="font-serif text-3xl text-ink">我的作品</h1>
|
||||
<Link
|
||||
href="/projects/new"
|
||||
className="rounded bg-cinnabar px-4 py-2 text-sm text-panel hover:opacity-90"
|
||||
>
|
||||
+ 新建作品
|
||||
</Link>
|
||||
</div>
|
||||
<div className="mx-auto max-w-5xl px-6 py-10 sm:px-8">
|
||||
<PageHeader
|
||||
title="我的作品"
|
||||
description="从一个灵感进入正文、设定、审稿与验收闭环。"
|
||||
actions={
|
||||
<Link
|
||||
href="/projects/new"
|
||||
className={buttonClass({ variant: "primary" })}
|
||||
>
|
||||
<Plus className="h-4 w-4" aria-hidden="true" />
|
||||
新建作品
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
|
||||
{loadError ? (
|
||||
<p className="rounded border border-conflict bg-panel p-6 text-conflict">
|
||||
无法连接后端服务,请确认 API 已启动后刷新。
|
||||
</p>
|
||||
) : projects.length === 0 ? (
|
||||
<EmptyState />
|
||||
<EmptyState
|
||||
icon={BookOpen}
|
||||
title="还没有作品"
|
||||
description="从一句灵感开始,后续的设定库、大纲、写章和审稿都会围绕这本书展开。"
|
||||
action={
|
||||
<Link
|
||||
href="/projects/new"
|
||||
className={buttonClass({ variant: "primary" })}
|
||||
>
|
||||
<Plus className="h-4 w-4" aria-hidden="true" />
|
||||
新建作品
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<ul className="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{projects.map((p) => (
|
||||
<li key={p.id}>
|
||||
<ProjectCard project={p} />
|
||||
</li>
|
||||
))}
|
||||
<li>
|
||||
<NewProjectCard />
|
||||
</li>
|
||||
</ul>
|
||||
<ProjectLibrary projects={projects} />
|
||||
)}
|
||||
</div>
|
||||
</AppShell>
|
||||
);
|
||||
}
|
||||
|
||||
function EmptyState() {
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-4 rounded border border-line bg-panel py-20 text-center">
|
||||
<p className="font-serif text-2xl text-ink">还没有作品</p>
|
||||
<p className="text-ink-soft">从一句灵感开始你的第一本书。</p>
|
||||
<Link
|
||||
href="/projects/new"
|
||||
className="rounded bg-cinnabar px-5 py-2 text-sm text-panel hover:opacity-90"
|
||||
>
|
||||
+ 新建作品
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function NewProjectCard() {
|
||||
return (
|
||||
<Link
|
||||
href="/projects/new"
|
||||
className="flex h-full min-h-[160px] flex-col items-center justify-center rounded border border-dashed border-line bg-panel text-center hover:border-cinnabar"
|
||||
>
|
||||
<span className="font-serif text-xl text-cinnabar">+ 新建</span>
|
||||
<span className="mt-2 text-sm text-ink-soft">从一句灵感开始一本书</span>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AppShell } from "@/components/AppShell";
|
||||
import { ProvidersSettings } from "@/components/settings/ProvidersSettings";
|
||||
import { PageHeader } from "@/components/ui/PageHeader";
|
||||
import { fetchKimiOauthStatus, fetchProviders } from "@/lib/api/server";
|
||||
import type { ProvidersResponse } from "@/lib/api/types";
|
||||
|
||||
@@ -27,7 +28,11 @@ export default async function ProvidersSettingsPage() {
|
||||
|
||||
return (
|
||||
<AppShell title="设置 › 模型与提供商">
|
||||
<div className="mx-auto max-w-3xl px-8 py-10">
|
||||
<div className="mx-auto max-w-6xl px-4 py-8 sm:px-8 sm:py-10">
|
||||
<PageHeader
|
||||
title="模型与提供商"
|
||||
description="配置写手、分析、轻量三类能力档位的路由,并管理 API Key 与 Kimi Code OAuth。"
|
||||
/>
|
||||
{loadError ? (
|
||||
<p className="rounded border border-conflict bg-panel p-6 text-conflict">
|
||||
无法连接后端服务,请确认 API 已启动后刷新。
|
||||
|
||||
Reference in New Issue
Block a user