Files
writer-work-flow/apps/web/components/AppShell.tsx
Yaojia Wang 5674158707 feat(ui): 工作台精修 + 拆分(P3-3..7)
- Workbench.tsx 937→467 行,抽出 AiToolbar/WorkbenchToolbar/MobileContextBar
- P3-3 AI 工具条分组留白 + 眉题 + 主 CTA 凸显
- P3-4 专注写作模式(useFocusMode hook+vitest,隐藏侧栏加宽正文)+ 左双栏面色区分
- P3-5 右栏本章参考小标题去衬线(font-sans) + 空态引导(无标题保 h2→h3 大纲)
- P3-6 底栏信息/操作/状态三区 + TOC 当前章高亮;TOC 状态点因无每章状态数据跳过
- P3-7 展示层流式跟随滚动(不触碰 SSE 逻辑,尊重 reduced-motion)
- AppShell 加可选 hideNav(加法式,默认 false)
2026-07-11 08:04:56 +02:00

99 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Link from "next/link";
import type { ReactNode } from "react";
import { Settings } from "lucide-react";
import type { ActiveNav } from "@/lib/nav/items";
import { buttonClass, focusRing } from "@/lib/ui/variants";
import { CommandSearchButton } from "./command/CommandPaletteMount";
import { LeftNav } from "./LeftNav";
import { NavDrawer } from "./NavDrawer";
import { ThemeToggle } from "./ThemeToggle";
interface AppShellProps {
children: ReactNode;
// 顶栏右侧的上下文信息(如作品名 / 进度)。
title?: string;
subtitle?: string;
// 项目内页给出 projectId → 左导航展开项目级入口(大纲/伏笔/审稿UX §3.1)。
projectId?: string;
// 当前激活的项目级入口键(用于高亮)。
activeNav?: ActiveNav;
// 专注写作模式P3-4隐藏桌面左侧图标导航以加宽正文顶栏汉堡/命令面板仍可达导航。
// 缺省 false → 其余页面外观不变(加法式可选项)。
hideNav?: boolean;
}
// 全局外壳:顶栏(64px) + 左导航 + 主区UX §5.1)。
export function AppShell({
children,
title,
subtitle,
projectId,
activeNav,
hideNav = false,
}: AppShellProps) {
return (
<div
className="min-h-screen"
style={{ ["--chrome" as string]: "4rem" }}
>
<a
href="#main"
className="sr-only focus:not-sr-only focus:absolute focus:left-4 focus:top-4 focus:z-50 focus:rounded focus:border focus:border-line focus:bg-panel focus:px-3 focus:py-2 focus:text-sm focus:text-ink focus:shadow-paper"
>
</a>
<header className="sticky top-0 z-30 flex h-16 items-center gap-4 border-b border-line bg-panel px-4 sm:px-6">
<NavDrawer projectId={projectId} activeNav={activeNav} />
<Link
href="/"
className={`shrink-0 whitespace-nowrap rounded font-serif text-xl text-cinnabar ${focusRing}`}
aria-label="返回作品库"
>
</Link>
{title ? (
<span className="min-w-0 truncate font-serif text-lg text-ink">
{title}
</span>
) : null}
{subtitle ? (
<span className="hidden shrink-0 font-mono text-xs text-ink-soft sm:inline">
{subtitle}
</span>
) : null}
<nav
aria-label="快捷操作"
className="ml-auto flex shrink-0 items-center gap-2 text-sm"
>
<CommandSearchButton />
<ThemeToggle />
<Link
href="/settings/providers"
aria-label="设置"
title="设置"
className={buttonClass({
variant: "ghost",
size: "sm",
className: "border-transparent",
})}
>
<Settings className="h-4 w-4" aria-hidden="true" />
<span className="hidden sm:inline"></span>
</Link>
</nav>
</header>
<div className="flex">
{hideNav ? null : <LeftNav projectId={projectId} activeNav={activeNav} />}
<main
id="main"
tabIndex={-1}
className="min-h-[calc(100vh-var(--chrome,4rem))] min-w-0 flex-1 bg-bg"
>
{children}
</main>
</div>
</div>
);
}