From 98a229af5cad67734cd2c84279bdd1741790ced1 Mon Sep 17 00:00:00 2001
From: HouYunFei <1844025705@qq.com>
Date: Tue, 2 Jun 2026 16:22:40 +0800
Subject: [PATCH] feat(docs): add documentation link in top menu and user
status actions
---
docs/content/docs/progress/pending-test.mdx | 1 +
.../(user)/canvas/[id]/canvas-client-page.tsx | 4 +-
web/src/components/layout/app-top-nav.tsx | 41 +------------------
.../components/layout/user-status-actions.tsx | 18 ++++----
web/src/constant/env.ts | 2 +
5 files changed, 19 insertions(+), 47 deletions(-)
diff --git a/docs/content/docs/progress/pending-test.mdx b/docs/content/docs/progress/pending-test.mdx
index c8613e2..ac212c3 100644
--- a/docs/content/docs/progress/pending-test.mdx
+++ b/docs/content/docs/progress/pending-test.mdx
@@ -5,6 +5,7 @@ description: 当前版本已实现但仍需人工验证的变更项
# 待测试
+- 画布左上角菜单和右上角状态栏新增“文档”入口,会使用 `NEXT_PUBLIC_DOC_URL` 配置的地址并在新标签打开文档站;需要验证登录和未登录状态下顶部入口都可见。
- 文档站搜索改为中英文混合 tokenizer,中文正文、标题和短语会按中文词、单字、二元和三元片段建立索引;需要验证 `/api/search` 和搜索弹窗能命中文档中的中文关键词。
- 文档站改为 Next.js standalone server 输出,新增 `docs/Dockerfile`、`docs/docker-compose.yml` 和 `docs/docker-compose.local.yml` 独立运行入口;需要验证文档页、搜索接口和 LLM 文本接口在文档站容器中可访问。
- 画布连线支持右键打开删除菜单;拖拽连线到目标卡片内部、连接点附近或卡片边缘外扩范围内会自动吸附并连接,拖到已有但不可连接的节点附近不会再误弹创建节点菜单。
diff --git a/web/src/app/(user)/canvas/[id]/canvas-client-page.tsx b/web/src/app/(user)/canvas/[id]/canvas-client-page.tsx
index 1721a4e..776de60 100644
--- a/web/src/app/(user)/canvas/[id]/canvas-client-page.tsx
+++ b/web/src/app/(user)/canvas/[id]/canvas-client-page.tsx
@@ -3,11 +3,12 @@
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
import type { ChangeEvent as ReactChangeEvent, DragEvent as ReactDragEvent, MouseEvent as ReactMouseEvent, PointerEvent as ReactPointerEvent } from "react";
import { useParams, useRouter } from "next/navigation";
-import { Home, ImageIcon, Images, List, Menu, MessageSquare, Music2, Plus, Redo2, Settings2, Trash2, Undo2, Upload, Video } from "lucide-react";
+import { BookOpen, Home, ImageIcon, Images, List, Menu, MessageSquare, Music2, Plus, Redo2, Settings2, Trash2, Undo2, Upload, Video } from "lucide-react";
import { saveAs } from "file-saver";
import { requestEdit, requestGeneration, requestImageQuestion } from "@/services/api/image";
import { requestVideoGeneration, storeGeneratedVideo } from "@/services/api/video";
+import { DOCS_URL } from "@/constant/env";
import { defaultConfig, type AiConfig, useConfigStore, useEffectiveConfig } from "@/stores/use-config-store";
import { resolveImageUrl, uploadImage, type UploadedImage } from "@/services/image-storage";
import { resolveMediaUrl, uploadMediaFile, type UploadedFile } from "@/services/file-storage";
@@ -2441,6 +2442,7 @@ function CanvasTopBar({
menu={{
items: [
{ key: "home", icon: , label: "主页", onClick: onHome },
+ { key: "docs", icon: , label: "文档", onClick: () => window.open(DOCS_URL, "_blank", "noopener,noreferrer") },
{ key: "projects", icon: , label: "我的画布", onClick: onProjects },
{ type: "divider" },
{ key: "new", icon: , label: "新建画布", onClick: onCreateProject },
diff --git a/web/src/components/layout/app-top-nav.tsx b/web/src/components/layout/app-top-nav.tsx
index 0a368fc..0940afa 100644
--- a/web/src/components/layout/app-top-nav.tsx
+++ b/web/src/components/layout/app-top-nav.tsx
@@ -1,30 +1,19 @@
"use client";
-import { Menu, Settings2 } from "lucide-react";
+import { Menu } from "lucide-react";
import Link from "next/link";
import { usePathname } from "next/navigation";
-import { AnimatedThemeToggler } from "@/components/ui/animated-theme-toggler";
import { navigationTools, type NavigationToolSlug } from "@/constant/navigation-tools";
import { AppConfigModal } from "@/components/layout/app-config-modal";
-import { GitHubLink } from "@/components/layout/github-link";
import { MobileNavDrawer } from "@/components/layout/mobile-nav-drawer";
import { UserStatusActions } from "@/components/layout/user-status-actions";
-import { VersionReleaseModal } from "@/components/layout/version-release-modal";
-import { useConfigStore } from "@/stores/use-config-store";
-import { useThemeStore } from "@/stores/use-theme-store";
-import { useUserStore } from "@/stores/use-user-store";
import { cn } from "@/lib/utils";
import { useState } from "react";
export function AppTopNav() {
const pathname = usePathname();
const [mobileNavOpen, setMobileNavOpen] = useState(false);
- const openConfigDialog = useConfigStore((state) => state.openConfigDialog);
- const theme = useThemeStore((state) => state.theme);
- const setTheme = useThemeStore((state) => state.setTheme);
- const user = useUserStore((state) => state.user);
- const isReady = useUserStore((state) => state.isReady);
const hideHeader = /^\/canvas\/[^/]+/.test(pathname);
const slug = pathname.split("/").filter(Boolean)[0];
const activeToolSlug = navigationTools.some((tool) => tool.slug === slug) ? (slug as NavigationToolSlug) : undefined;
@@ -80,33 +69,7 @@ export function AppTopNav() {
- {isReady && user ? (
-
- ) : (
- <>
-
-
-
-
-
- 登录
-
- >
- )}
+
diff --git a/web/src/components/layout/user-status-actions.tsx b/web/src/components/layout/user-status-actions.tsx
index 9793bab..a035278 100644
--- a/web/src/components/layout/user-status-actions.tsx
+++ b/web/src/components/layout/user-status-actions.tsx
@@ -2,7 +2,7 @@
import type { CSSProperties, RefObject } from "react";
import { Avatar, Dropdown, Tooltip } from "antd";
-import { Keyboard, LogOut, Settings2, Shield } from "lucide-react";
+import { BookOpen, Keyboard, LogOut, Settings2, Shield } from "lucide-react";
import type { ItemType } from "antd/es/menu/interface";
import Link from "next/link";
@@ -10,6 +10,7 @@ import { AnimatedThemeToggler } from "@/components/ui/animated-theme-toggler";
import { GitHubLink } from "@/components/layout/github-link";
import { VersionReleaseModal } from "@/components/layout/version-release-modal";
import { CreditSymbol } from "@/constant/credits";
+import { DOCS_URL } from "@/constant/env";
import { cn } from "@/lib/utils";
import { canvasThemes } from "@/lib/canvas-theme";
import { useConfigStore } from "@/stores/use-config-store";
@@ -37,10 +38,10 @@ export function UserStatusActions({ showConfig = true, variant = "default", onOp
const credits = user?.credits ?? 0;
const avatarUrl = user?.avatarUrl?.trim();
const avatarText = (userName.trim()[0] || "U").toUpperCase();
- const naturalIconClass = "inline-flex size-8 shrink-0 items-center justify-center text-stone-600 transition hover:text-stone-950 dark:text-stone-300 dark:hover:text-white [&_svg]:size-4";
+ const naturalIconClass = "inline-flex size-7 shrink-0 items-center justify-center text-stone-600 transition hover:text-stone-950 dark:text-stone-300 dark:hover:text-white [&_svg]:size-4";
const iconStyle: CSSProperties | undefined = variant === "canvas" ? { color: canvasTheme.node.text } : undefined;
const versionStyle = iconStyle;
- const gitHubClassName = variant === "canvas" ? "size-11 text-base" : undefined;
+ const gitHubClassName = "size-7 text-base";
const gitHubStyle = iconStyle;
const avatarStyle: CSSProperties | undefined = variant === "canvas" ? { borderColor: canvasTheme.toolbar.border, color: canvasTheme.node.text, background: "transparent" } : undefined;
const menuItems: ItemType[] = [
@@ -52,7 +53,10 @@ export function UserStatusActions({ showConfig = true, variant = "default", onOp
];
return (
-
+
+
+
+
{showConfig ? (