diff --git a/README.md b/README.md index b7f7495..08cec9e 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ - **引导式立项** — 一句灵感走向连载:选题材 / 基调 / 结局走向 / 叙事视角,AI 起草世界观、主角、金手指、总纲方案,一键建库。 - **设定库(Codex)** — 结构化的单一真相源: - - 角色卡(外貌 / 动机 / 目标 / 口癖 / 性格弧光 / 人物关系图) + - 角色卡(外貌 / 动机 / 性格 / 背景 / 口癖 / 性格弧光 / 人物关系图) - 世界观(力量体系 / 势力 / 地理 / 词条) - **大纲 / 细纲** — AI 排分卷分章骨架,逐层细化到场景清单,并提示伏笔回收窗口。 - **写章(SSE 流式)** — 注入设定 + 文风指纹,按 genre-aware 的网文写作教条(黄金三章、章末钩子、爽点密度)流式产出本章草稿。 @@ -19,6 +19,7 @@ - **创作工具箱(15 个生成器)** — 声明式 skill 框架,「加生成器 = 加一份声明」:脑洞 / 书名 / 简介 / 取名 / 金手指 / 词条 / 黄金开篇 / 细纲 / 续写 / 扩写 / 降 AI / 拆书,外加世界观 / 角色 / 大纲。 - **模板库** — 复用与沉淀常用创作模板。 - **多 Provider LLM 网关** — 路由 / 回退 / 熔断的自建薄网关;Agent 只声明能力 tier,网关按配置映射到 provider+model;支持 Kimi Code 订阅 OAuth(device flow)。 +- **沉浸式写作体验** — 纸感暖色 + 夜读双主题(`data-theme` 切换)、写作台专注模式(隐藏侧栏加宽正文)、`⌘K` 命令面板(跨页跳转 / 执行命令)、应用内帮助页 [`/help`](http://localhost:4000/help)(功能介绍 + 新手上手)。UI 对标编辑部式暖奶油设计语言,全程尊重 `prefers-reduced-motion` 与 WCAG AA。 --- diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index 60fd9c7..dc332cc 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -5,12 +5,26 @@ /* 纸感设计 token(UX_SPEC §2.1) */ :root, [data-theme="paper"] { + /* 面色阶:底 → 分隔带 → 内容卡(比底暗一步)→ 选中强调 → 浮起面板 */ --color-bg: #f5f1e8; + --color-surface-soft: #efe9dc; + --color-surface-card: #ece4d3; + --color-surface-strong: #e4d9c4; --color-panel: #fbf8f1; + /* 文字色阶(5 级):标题 → 强调段 → 正文 → 次级 → 说明/细则 */ --color-ink: #2b2620; + --color-body-strong: #3a342b; + --color-body: #4a4234; --color-ink-soft: #6b6356; + /* muted-soft 达 WCAG AA 小字(≥4.5:1)于 bg/card/panel/soft 各面(P4-1)。 */ + --color-muted-soft: #6f6556; + /* 边框:主边框 + 同带内更弱分隔 */ --color-line: #e5ddcd; + --color-line-soft: #ece4d5; + /* 朱砂 coral:主色 + 按压变深 + 奶油化禁用 + 淡底 */ --color-cinnabar: #a23b2e; + --color-cinnabar-active: #8a2f24; + --color-cinnabar-disabled: #d9cdbd; --color-cinnabar-wash: #a23b2e14; --color-conflict: #b5543a; --color-overdue: #c8893a; @@ -18,17 +32,42 @@ --color-info: #4a5a6b; --color-conflict-mark: #b5543a26; --color-conflict-mark-strong: #b5543a8c; + /* 暖深色 callout:仅用于英雄/CTA 时刻(对标 DESIGN.md cta-band-dark),非内容底。 */ + --color-callout: #201d18; + --color-on-callout: #f2ede2; + --color-on-callout-soft: #b8ae9c; --shadow-paper: #2b26200f; color-scheme: light; } +/* 动效 token(与主题无关,单处定义;夜读模式不覆盖) */ +:root { + --ease-standard: cubic-bezier(0.2, 0, 0, 1); + --dur-fast: 120ms; + --dur-base: 180ms; +} + [data-theme="night"] { + /* 面色阶:底 → 分隔带 → 浮起面板 → 内容卡(暗面向上抬)→ 选中强调 */ --color-bg: #181614; + --color-surface-soft: #1e1b17; + --color-surface-card: #2a2620; + --color-surface-strong: #332e26; --color-panel: #221f1b; + /* 文字色阶(5 级) */ --color-ink: #efe6d7; + --color-body-strong: #ddd2c0; + --color-body: #cdc0ab; --color-ink-soft: #b9ab96; + /* muted-soft 达 AA 小字(≥4.5:1)于夜读 bg/card/soft 各面(P4-1)。 */ + --color-muted-soft: #9a8e7c; + /* 边框 */ --color-line: #3c352c; + --color-line-soft: #2f2a23; + /* 朱砂 coral */ --color-cinnabar: #e07866; + --color-cinnabar-active: #c85f4d; + --color-cinnabar-disabled: #3c352c; --color-cinnabar-wash: #e0786620; --color-conflict: #ef8a72; --color-overdue: #d8a65d; @@ -36,6 +75,10 @@ --color-info: #8ca8ca; --color-conflict-mark: #ef8a7230; --color-conflict-mark-strong: #ef8a7290; + /* 夜读下 callout 为更深的凹陷暖井,仍与 bg 拉开层次。 */ + --color-callout: #100e0c; + --color-on-callout: #efe6d7; + --color-on-callout-soft: #a89d8a; --shadow-paper: #00000045; color-scheme: dark; } @@ -91,6 +134,21 @@ body { } } +/* Toast 入场:淡入 + 轻微上移(尊重 prefers-reduced-motion,见下)。 */ +.toast-enter { + animation: toast-in var(--dur-base) var(--ease-standard); +} +@keyframes toast-in { + from { + opacity: 0; + transform: translateY(6px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + @media (prefers-reduced-motion: reduce) { .typewriter-cursor { animation: none; @@ -102,4 +160,7 @@ body { animation: none; outline: 2px solid var(--color-conflict); } + .toast-enter { + animation: none; + } } diff --git a/apps/web/app/help/page.tsx b/apps/web/app/help/page.tsx new file mode 100644 index 0000000..1776bfa --- /dev/null +++ b/apps/web/app/help/page.tsx @@ -0,0 +1,298 @@ +import Link from "next/link"; +import type { LucideIcon } from "lucide-react"; +import { + BookMarked, + BookOpen, + Blocks, + CheckCircle2, + ClipboardCheck, + FileText, + Flag, + LayoutGrid, + ListTree, + Moon, + PanelLeftClose, + PenLine, + Route, + Search, + Settings, + ShieldCheck, + Sparkles, +} from "lucide-react"; + +import { AppShell } from "@/components/AppShell"; +import { Badge } from "@/components/ui/Badge"; +import { Card } from "@/components/ui/Card"; +import { Eyebrow } from "@/components/ui/Eyebrow"; +import { PageContainer } from "@/components/ui/PageContainer"; +import { buttonClass } from "@/lib/ui/variants"; + +// 帮助中心:功能介绍 + 新手上手(静态内容,Server Component)。 + +interface Step { + icon: LucideIcon; + title: string; + body: string; +} + +const STEPS: Step[] = [ + { + icon: PenLine, + title: "立项,或直接开始写", + body: "从一句灵感开始:「先立项」立好核心卖点与一句话故事,再到设定库、大纲把架构搭起来;或「直接开始写」落笔即成书,边写边补设定。", + }, + { + icon: Sparkles, + title: "让 AI 写这章", + body: "在写作台告诉 AI 这章想怎么写,它对着大纲给出草稿(流式出稿)。你可以续写接着写、润色选段、或整章重写——原文永远你说了算。", + }, + { + icon: ClipboardCheck, + title: "四审,然后验收", + body: "写完点「审稿」,AI 从一致性 / 伏笔 / 文风 / 节奏四个维度逐条检查、就地标注冲突。你裁决后验收,系统更新真相源与伏笔账本。", + }, +]; + +interface Principle { + title: string; + body: string; +} + +const PRINCIPLES: Principle[] = [ + { + title: "把写小说当软件工程", + body: "先立架构(世界观 + 人物 + 大纲),再对着规格逐章生成,每章写完即测,全程维护单一真相源。不让 AI 一口气瞎写十万字。", + }, + { + title: "单一真相源 · 记忆不衰减", + body: "设定、状态、伏笔集中记账;AI 写每一章都按需读取相关记忆(确定性选择,非一次性塞满上下文),对抗长篇遗忘。", + }, + { + title: "你始终掌控", + body: "AI 是灵感增幅器 + 质检员,不是全自动黑箱。四审只读、只给建议,任何写入都要你裁决采纳,不会静默覆盖你的原文。", + }, +]; + +interface Feature { + icon: LucideIcon; + name: string; + desc: string; +} + +interface FeatureGroup { + label: string; + features: Feature[]; +} + +const FEATURE_GROUPS: FeatureGroup[] = [ + { + label: "写作", + features: [ + { + icon: PenLine, + name: "写作台", + desc: "对着大纲逐章生成、流式出稿;续写接着写、润色选段、整章重写,随时进入专注模式沉浸写作。", + }, + ], + }, + { + label: "资料库", + features: [ + { icon: ListTree, name: "大纲", desc: "卷 → 章 → 节拍逐层拆解;可用「细纲生成器」把某章展开为场景,入库时并入该章节拍。" }, + { icon: BookMarked, name: "设定库", desc: "世界观圣经 + 人物卡(动机、人物弧光,像接口约束行为)+ 关系图谱。" }, + { icon: Flag, name: "伏笔", desc: "「埋设 → 回收」的显式账本,逾期未收进「已逾期」泳道并标记,不再漏坑。" }, + { icon: FileText, name: "文风", desc: "抽取你的文风指纹并注入生成,抵抗机翻腔与风格漂移。" }, + ], + }, + { + label: "审阅", + features: [ + { + icon: ClipboardCheck, + name: "审稿 · 四审", + desc: "每章写完即审:一致性 / 伏笔 / 文风 / 节奏,冲突就地标注、逐条裁决后验收。", + }, + { icon: Route, name: "多章链", desc: "从指定章起循环「写章 → 四审 → 验收」批量推进,遇未决冲突暂停等你裁决再续跑。" }, + ], + }, + { + label: "工具与全局", + features: [ + { icon: Blocks, name: "工具箱", desc: "十多个生成器:脑洞 / 书名 / 简介 / 金手指 / 词条 / 黄金开篇 / 细纲 / 拆书等;部分可一键入库,部分为预览产出。" }, + { icon: ShieldCheck, name: "规则", desc: "沉淀你的创作规则,分全局 / 题材 / 文风 / 本作四级,约束 AI 少犯同类错。" }, + { icon: LayoutGrid, name: "模板库", desc: "可复用的提示词模板,在工具箱生成器里一键填入需求 / 原文。" }, + { icon: Settings, name: "设置", desc: "写手 / 分析 / 轻量三档模型路由 + API Key / Kimi OAuth。" }, + ], + }, +]; + +interface Tip { + icon: LucideIcon; + title: string; + body: string; +} + +const TIPS: Tip[] = [ + { icon: Search, title: "命令面板 ⌘K", body: "任意页面按 ⌘K 快速跳转页面或执行命令。" }, + { icon: PanelLeftClose, title: "专注写作模式", body: "写作台底栏点「专注」,隐藏侧栏、加宽正文,沉浸写作。" }, + { icon: Moon, title: "夜读模式", body: "顶栏月亮图标切换暖色夜读,长时间写作更护眼。" }, + { icon: CheckCircle2, title: "原文永远你说了算", body: "AI 给的是草稿,采不采用由你定,绝不静默覆盖你的正文。" }, +]; + +interface Faq { + q: string; + a: string; +} + +const FAQS: Faq[] = [ + { + q: "AI 会自动改我的正文吗?", + a: "不会。四审是只读的、只给建议;任何写入都要你在验收环节裁决,冲突未解决会阻止验收。", + }, + { + q: "用的是哪个大模型?", + a: "后端是多提供商网关,写手 / 分析 / 轻量三档可各自路由(Claude / DeepSeek / Kimi / GPT / Gemini / 通义 / GLM 等,可回退),在「设置 → 模型与提供商」里配。", + }, + { + q: "写到几十万字会不会记忆衰减?", + a: "靠单一真相源 + 按需记忆注入:写每章只挑相关的设定、角色与最近若干章,而不是把全书塞进上下文。", + }, + { + q: "我的数据在哪里?", + a: "本地 Postgres,单用户原型;设定/正文/审稿结果都以数据库为唯一真相源,记忆追加保留历史、不就地覆盖。", + }, +]; + +function SectionTitle({ eyebrow, title }: { eyebrow: string; title: string }) { + return ( +
+ {eyebrow} +

{title}

+
+ ); +} + +function IconChip({ icon: Icon }: { icon: LucideIcon }) { + return ( + + + ); +} + +export default function HelpPage() { + return ( + + + {/* 英雄:暖深色编辑部条(对标 DESIGN.md cta-band-dark)。 */} +
+ 墨痕 · 使用指南 +

+ 把写小说,当软件工程来做 +

+

+ 立项 → 设定 → 大纲 → 写章 → 四审 → 验收。先立架构再对着规格生成,每章写完即测,全程维护单一真相源。下面三步带你上手,并逐一介绍每个功能。 +

+
+ +
+
+ + {/* 新手三步 */} +
+ +
    + {STEPS.map((step, i) => ( +
  1. + +
    + + + 第 {i + 1} 步 + +
    +

    {step.title}

    +

    {step.body}

    +
    +
  2. + ))} +
+
+ + {/* 核心理念 */} +
+ +
+ {PRINCIPLES.map((p) => ( + +

{p.title}

+

{p.body}

+
+ ))} +
+
+ + {/* 功能一览 */} +
+ +
+ {FEATURE_GROUPS.map((group) => ( +
+

+ {group.label} + {group.features.length} +

+
+ {group.features.map((f) => ( + + +
+

{f.name}

+

{f.desc}

+
+
+ ))} +
+
+ ))} +
+
+ + {/* 小技巧 */} +
+ +
+ {TIPS.map((t) => ( +
+ +
+

{t.title}

+

{t.body}

+
+
+ ))} +
+
+ + {/* 常见问题 */} +
+ +
+ {FAQS.map((f) => ( + +
{f.q}
+
{f.a}
+
+ ))} +
+
+
+
+ ); +} diff --git a/apps/web/app/loading.tsx b/apps/web/app/loading.tsx index 9e27ca5..ed16941 100644 --- a/apps/web/app/loading.tsx +++ b/apps/web/app/loading.tsx @@ -1,9 +1,79 @@ -import { ThinkingIndicator } from "@/components/ThinkingIndicator"; +import { Card } from "@/components/ui/Card"; +import { PageContainer } from "@/components/ui/PageContainer"; +import { Skeleton } from "@/components/ui/Skeleton"; +// 全站加载骨架:与作品库结构近似(标题条 + 工具条 + 卡片网格), +// motion-safe 脉冲,尊重 prefers-reduced-motion。 export default function Loading() { return ( -
- +
+ +
+ 加载中… + + + +
+
); } + +// 标题区:大标题条 + 说明条 + 右侧两枚按钮位。 +function HeaderSkeleton() { + return ( +
+
+ + +
+
+ + +
+
+ ); +} + +// 工具条:搜索框 + 三枚控件位。 +function ToolbarSkeleton() { + return ( + + + + + + + ); +} + +// 卡片网格:6 张与作品卡等高的占位卡。 +function GridSkeleton() { + return ( +
+ {Array.from({ length: 6 }).map((_, i) => ( + + ))} +
+ ); +} + +// 单张作品卡骨架:标题 + 徽章 + 三行简介 + 底部时间。 +function CardSkeleton() { + return ( + +
+
+ + +
+ +
+
+ + +
+ +
+ ); +} diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 441d137..8c2c3c0 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -4,7 +4,7 @@ import { BookOpen, PenLine, Plus } from "lucide-react"; import { AppShell } from "@/components/AppShell"; import { BackendDownNotice } from "@/components/BackendDownNotice"; import { ProjectLibrary } from "@/components/projects/ProjectLibrary"; -import { EmptyState } from "@/components/ui/EmptyState"; +import { PageContainer } from "@/components/ui/PageContainer"; import { PageHeader } from "@/components/ui/PageHeader"; import { fetchProjects } from "@/lib/api/server"; import type { ProjectResponse } from "@/lib/api/types"; @@ -23,7 +23,7 @@ export default async function DashboardPage() { return ( -
+ ) : projects.length === 0 ? ( - - -
- } - /> + // 首次进入的暖深色编辑部英雄(对标 DESIGN.md cta-band-dark;仅零作品时出现,不干扰工作库)。 +
+
) : ( )} - +
); } diff --git a/apps/web/components/AppShell.tsx b/apps/web/components/AppShell.tsx index c7e6f80..5c42157 100644 --- a/apps/web/components/AppShell.tsx +++ b/apps/web/components/AppShell.tsx @@ -1,6 +1,6 @@ import Link from "next/link"; import type { ReactNode } from "react"; -import { Settings } from "lucide-react"; +import { FileQuestion, Settings } from "lucide-react"; import type { ActiveNav } from "@/lib/nav/items"; import { buttonClass, focusRing } from "@/lib/ui/variants"; @@ -18,6 +18,9 @@ interface AppShellProps { projectId?: string; // 当前激活的项目级入口键(用于高亮)。 activeNav?: ActiveNav; + // 专注写作模式(P3-4):隐藏桌面左侧图标导航以加宽正文;顶栏汉堡/命令面板仍可达导航。 + // 缺省 false → 其余页面外观不变(加法式可选项)。 + hideNav?: boolean; } // 全局外壳:顶栏(64px) + 左导航 + 主区(UX §5.1)。 @@ -27,6 +30,7 @@ export function AppShell({ subtitle, projectId, activeNav, + hideNav = false, }: AppShellProps) { return (
+ +