diff --git a/frontend/CLAUDE.md b/frontend/CLAUDE.md index a771f79..a90361c 100644 --- a/frontend/CLAUDE.md +++ b/frontend/CLAUDE.md @@ -14,13 +14,24 @@ npm run lint # ESLint (no test suite exists yet) ## Architecture -This is a single-page admin console for managing AI video assistants. It is a Next.js 16 app using the App Router, React 19, Tailwind CSS v4, and shadcn components backed by Radix UI primitives. +This is an admin console for managing AI video assistants. It is a Next.js 16 app using the App Router, React 19, Tailwind CSS v4, and shadcn components backed by Radix UI primitives. -**Navigation model** — the app has no Next.js routes beyond `/`. All "pages" are React components in `src/components/pages/` that are conditionally rendered by `AppShell` based on a `NavKey` state value. `AppShell` owns the active page and sidebar-collapsed state and threads them down as props. +**Navigation model** — each sidebar section is a real App Router route, so refresh/deep-link lands on the same page. Route files in `src/app/` are thin server components that render the page components from `src/components/pages/`. The route map: + +| Route | Page | +| --- | --- | +| `/` | `HomePage` | +| `/assistants` | `AssistantPage mode="list"` (助手列表) | +| `/assistants/new` | `AssistantPage mode="choose"` (引导:取名+选构建方式,确认即 POST 建库并跳转编辑页) | +| `/assistants/[id]` | `AssistantPage mode="edit"` (按 id 拉取并按类型回填编辑器) | +| `/components/{models,knowledge,tools}` | 组件库三页 | +| `/history`, `/dashboard`, `/test`, `/profile` | 其余侧栏页 | + +`AssistantPage` is one client component driven by a discriminated-union `mode` prop; all in-page transitions (创建/编辑/返回) navigate via `router.push` instead of local view state. There is no separate create form: the 引导 page creates the assistant immediately (blank fields + chosen name/type) and the editor always works against an existing id. 保存 stays on the editor page — the save button is enabled only when the form differs from the last-saved snapshot (`savedSnapshot` JSON diff), and the header back button returns to the list. **Component layers:** -- `src/app/` — Next.js entry: `layout.tsx` (fonts, theme-flash script, metadata) and `page.tsx` (renders ``) -- `src/components/layout/` — `AppShell` (page-switching shell), `Sidebar` (collapsible nav, 252 → 76px), `Topbar` (theme toggle, notifications), `ThemeToggle` +- `src/app/` — Next.js entry: `layout.tsx` (fonts, theme-flash script, metadata, wraps everything in `AppShell`) plus one thin `page.tsx` per route +- `src/components/layout/` — `AppShell` (sidebar+topbar shell around route children, owns sidebar-collapsed state), `Sidebar` (collapsible nav, 252 → 76px; `Link`-based, active state from `usePathname`), `Topbar` (theme toggle, notifications), `ThemeToggle` - `src/components/pages/` — one component per nav section; `PlaceholderPage` is a shared editorial header for unimplemented pages - `src/components/ui/` — shadcn primitives (button, card, badge, dialog, etc.) - `src/hooks/` — `use-mobile.ts` diff --git a/frontend/src/app/assistants/[id]/page.tsx b/frontend/src/app/assistants/[id]/page.tsx new file mode 100644 index 0000000..c27312b --- /dev/null +++ b/frontend/src/app/assistants/[id]/page.tsx @@ -0,0 +1,10 @@ +import { AssistantPage } from "@/components/pages/AssistantPage"; + +export default async function Page({ + params, +}: { + params: Promise<{ id: string }>; +}) { + const { id } = await params; + return ; +} diff --git a/frontend/src/app/assistants/new/page.tsx b/frontend/src/app/assistants/new/page.tsx new file mode 100644 index 0000000..5c03fe3 --- /dev/null +++ b/frontend/src/app/assistants/new/page.tsx @@ -0,0 +1,5 @@ +import { AssistantPage } from "@/components/pages/AssistantPage"; + +export default function Page() { + return ; +} diff --git a/frontend/src/app/assistants/page.tsx b/frontend/src/app/assistants/page.tsx new file mode 100644 index 0000000..b84c1b5 --- /dev/null +++ b/frontend/src/app/assistants/page.tsx @@ -0,0 +1,5 @@ +import { AssistantPage } from "@/components/pages/AssistantPage"; + +export default function Page() { + return ; +} diff --git a/frontend/src/app/components/knowledge/page.tsx b/frontend/src/app/components/knowledge/page.tsx new file mode 100644 index 0000000..b277f08 --- /dev/null +++ b/frontend/src/app/components/knowledge/page.tsx @@ -0,0 +1,5 @@ +import { ComponentsKnowledgePage } from "@/components/pages/ComponentsKnowledgePage"; + +export default function Page() { + return ; +} diff --git a/frontend/src/app/components/models/page.tsx b/frontend/src/app/components/models/page.tsx new file mode 100644 index 0000000..4e15a50 --- /dev/null +++ b/frontend/src/app/components/models/page.tsx @@ -0,0 +1,5 @@ +import { ComponentsModelsPage } from "@/components/pages/ComponentsModelsPage"; + +export default function Page() { + return ; +} diff --git a/frontend/src/app/components/tools/page.tsx b/frontend/src/app/components/tools/page.tsx new file mode 100644 index 0000000..1af9be8 --- /dev/null +++ b/frontend/src/app/components/tools/page.tsx @@ -0,0 +1,5 @@ +import { ComponentsToolsPage } from "@/components/pages/ComponentsToolsPage"; + +export default function Page() { + return ; +} diff --git a/frontend/src/app/dashboard/page.tsx b/frontend/src/app/dashboard/page.tsx new file mode 100644 index 0000000..90421bc --- /dev/null +++ b/frontend/src/app/dashboard/page.tsx @@ -0,0 +1,5 @@ +import { DashboardPage } from "@/components/pages/DashboardPage"; + +export default function Page() { + return ; +} diff --git a/frontend/src/app/history/page.tsx b/frontend/src/app/history/page.tsx new file mode 100644 index 0000000..c797c18 --- /dev/null +++ b/frontend/src/app/history/page.tsx @@ -0,0 +1,5 @@ +import { HistoryPage } from "@/components/pages/HistoryPage"; + +export default function Page() { + return ; +} diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 4211032..f61bfff 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -2,6 +2,7 @@ import type { Metadata } from "next"; import { Geist_Mono, Inter, Cormorant_Garamond } from "next/font/google"; import "./globals.css"; import { cn } from "@/lib/utils"; +import { AppShell } from "@/components/layout/AppShell"; const inter = Inter({ subsets: ["latin"], variable: "--font-sans" }); @@ -48,7 +49,9 @@ export default function RootLayout({