From 8779530806e6e16faf618baeba757aae2a5d79d7 Mon Sep 17 00:00:00 2001 From: Yaojia Wang Date: Sat, 20 Jun 2026 18:19:46 +0200 Subject: [PATCH] =?UTF-8?q?feat(ux):=20T4-a=20=E5=85=A8=E5=B1=80=20AI=20?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E6=9D=A1=20=E2=80=94=20=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E9=A1=B5=E9=A1=B6=E9=83=A8=E5=B8=B8=E9=A9=BB=E5=86=99=E6=9C=AC?= =?UTF-8?q?=E7=AB=A0/=E5=AE=A1=E7=A8=BF/=E5=A4=A7=E7=BA=B2/=E8=AE=BE?= =?UTF-8?q?=E5=AE=9A=E5=BA=93/=E5=B7=A5=E5=85=B7=E7=AE=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新 lib/nav/ai-tools.ts (aiToolItems) + components/AiToolbar.tsx 服务端组件;AppShell 顶栏下渲染(仅项目页),用 --chrome CSS 变量统一 chrome 高度,6 个固定高度页改用 calc(100vh-var(--chrome,4rem)) 适配。TDD: ai-tools 测。前端门禁绿: lint/tsc/vitest/build。 --- apps/web/components/AiToolbar.tsx | 48 +++++++++++++++++++ apps/web/components/AppShell.tsx | 13 ++++- apps/web/components/codex/CodexPage.tsx | 2 +- .../components/foreshadow/ForeshadowBoard.tsx | 2 +- apps/web/components/outline/OutlineEditor.tsx | 2 +- apps/web/components/review/ReviewReport.tsx | 2 +- apps/web/components/style/StylePage.tsx | 2 +- apps/web/components/workbench/Workbench.tsx | 2 +- apps/web/lib/nav/ai-tools.test.ts | 28 +++++++++++ apps/web/lib/nav/ai-tools.ts | 29 +++++++++++ 10 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 apps/web/components/AiToolbar.tsx create mode 100644 apps/web/lib/nav/ai-tools.test.ts create mode 100644 apps/web/lib/nav/ai-tools.ts diff --git a/apps/web/components/AiToolbar.tsx b/apps/web/components/AiToolbar.tsx new file mode 100644 index 0000000..7d86e82 --- /dev/null +++ b/apps/web/components/AiToolbar.tsx @@ -0,0 +1,48 @@ +import Link from "next/link"; + +import { aiToolItems } from "@/lib/nav/ai-tools"; +import type { ActiveNav } from "@/lib/nav/items"; + +interface AiToolbarProps { + projectId: string; + activeNav?: ActiveNav; +} + +// T4-a · 项目内页常驻 AI 工具条(顶栏下,UX §3/§5)。 +// 纯链接服务端组件:写本章=朱砂主动作,其余次级;激活项朱砂高亮。窄屏横向滚动。 +export function AiToolbar({ projectId, activeNav }: AiToolbarProps) { + return ( + + ); +} + +function toolClassName(isPrimary: boolean, isActive: boolean): string { + const base = + "flex items-center gap-1.5 rounded border px-3 py-1.5 text-sm transition-colors"; + if (isActive) { + return `${base} border-cinnabar text-cinnabar`; + } + if (isPrimary) { + return `${base} border-cinnabar bg-cinnabar text-panel hover:border-cinnabar`; + } + return `${base} border-line text-ink-soft hover:border-cinnabar hover:text-cinnabar`; +} diff --git a/apps/web/components/AppShell.tsx b/apps/web/components/AppShell.tsx index 634e137..372e7a5 100644 --- a/apps/web/components/AppShell.tsx +++ b/apps/web/components/AppShell.tsx @@ -2,6 +2,7 @@ import Link from "next/link"; import type { ReactNode } from "react"; import type { ActiveNav } from "@/lib/nav/items"; +import { AiToolbar } from "./AiToolbar"; import { LeftNav } from "./LeftNav"; import { NavDrawer } from "./NavDrawer"; @@ -25,7 +26,10 @@ export function AppShell({ activeNav, }: AppShellProps) { return ( -
+
+ {projectId ? ( + + ) : null}
-
{children}
+
+ {children} +
); diff --git a/apps/web/components/codex/CodexPage.tsx b/apps/web/components/codex/CodexPage.tsx index 803cb8c..ba12ffc 100644 --- a/apps/web/components/codex/CodexPage.tsx +++ b/apps/web/components/codex/CodexPage.tsx @@ -59,7 +59,7 @@ export function CodexPage({ projectId={project.id} activeNav="codex" > -
+
{TABS.map((t) => (