From 90a903805fe9e66a81a2a3b1ea1b2dda7193d0fa Mon Sep 17 00:00:00 2001
From: ddCat <2695092045@qq.com>
Date: Fri, 31 Jul 2026 15:33:37 +0800
Subject: [PATCH] =?UTF-8?q?feat(*):=20=E7=94=A8=E6=88=B7=E5=B7=A5=E4=BD=9C?=
=?UTF-8?q?=E5=8F=B0=20-=20=E7=BB=9F=E4=B8=80=E9=BB=91=E7=99=BD=E7=81=B0?=
=?UTF-8?q?=E7=A9=BA=E9=97=B4=E7=95=8C=E9=9D=A2=E5=B9=B6=E6=95=B4=E7=90=86?=
=?UTF-8?q?=E7=94=BB=E5=B8=83=E5=85=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
web/src/components/layout/app-top-nav.tsx | 57 +--
.../layout/workspace-sidebar-footer.tsx | 15 +-
web/src/constant/navigation-tools.ts | 24 +-
web/src/layouts/user-layout.tsx | 7 +-
web/src/lib/canvas/canvas-aceternity-style.ts | 4 +-
web/src/lib/workspace-routes.ts | 5 +
web/src/pages/home/index.tsx | 79 ++++-
web/src/pages/projects/detail.tsx | 2 +-
web/src/pages/projects/detail/overview.tsx | 14 +-
web/src/pages/projects/index.tsx | 2 +-
web/src/styles/globals.css | 334 ++++++++++++++++++
11 files changed, 484 insertions(+), 59 deletions(-)
create mode 100644 web/src/lib/workspace-routes.ts
diff --git a/web/src/components/layout/app-top-nav.tsx b/web/src/components/layout/app-top-nav.tsx
index 5a8e6c2ce..3e9fc2690 100644
--- a/web/src/components/layout/app-top-nav.tsx
+++ b/web/src/components/layout/app-top-nav.tsx
@@ -1,5 +1,5 @@
import { Infinity as InfinityIcon, PanelLeftClose, PanelLeftOpen } from "lucide-react";
-import { useEffect, useState, type ReactNode } from "react";
+import { Fragment, useEffect, useState, type ReactNode } from "react";
import { Link, useLocation, useNavigate } from "react-router";
import { ModelSetupGuide } from "@/components/layout/model-setup-guide";
@@ -7,6 +7,7 @@ import { WORKSPACE_SIDEBAR_STORAGE_KEY } from "@/components/layout/workspace-sid
import { WorkspaceSidebarFooter } from "@/components/layout/workspace-sidebar-footer";
import { navigationTools, type NavigationToolSlug } from "@/constant/navigation-tools";
import { cn } from "@/lib/utils";
+import { isSpatialWorkbenchPath } from "@/lib/workspace-routes";
export function AppWorkspaceShell({ children }: { children: ReactNode }) {
const { pathname } = useLocation();
@@ -14,9 +15,11 @@ export function AppWorkspaceShell({ children }: { children: ReactNode }) {
const [mobileSidebarExpanded, setMobileSidebarExpanded] = useState(false);
const [sidebarCollapsed, setSidebarCollapsed] = useState(() => window.localStorage.getItem(WORKSPACE_SIDEBAR_STORAGE_KEY) === "1");
const hideChrome = pathname.startsWith("/admin") || /^\/canvas\/[^/]+/.test(pathname);
+ const spatialWorkbench = isSpatialWorkbenchPath(pathname);
const compactSidebar = sidebarCollapsed;
const slug = pathname.split("/").filter(Boolean)[0];
const activeToolSlug = navigationTools.some((tool) => tool.slug === slug) ? (slug as NavigationToolSlug) : undefined;
+ const visibleNavigationTools = spatialWorkbench ? navigationTools : navigationTools.filter((tool) => tool.section === "创作空间");
useEffect(() => {
window.localStorage.setItem(WORKSPACE_SIDEBAR_STORAGE_KEY, sidebarCollapsed ? "1" : "0");
@@ -35,9 +38,10 @@ export function AppWorkspaceShell({ children }: { children: ReactNode }) {
return (
<>
-
+
diff --git a/web/src/lib/canvas/canvas-aceternity-style.ts b/web/src/lib/canvas/canvas-aceternity-style.ts
index 316e1119b..5ff3e5c9b 100644
--- a/web/src/lib/canvas/canvas-aceternity-style.ts
+++ b/web/src/lib/canvas/canvas-aceternity-style.ts
@@ -10,8 +10,8 @@ export function canvasDockStyle(theme: CanvasTheme, color: string = theme.toolba
boxShadow: `0 18px 52px ${theme.spatial.shadow}, inset 0 1px 0 rgba(255,255,255,.14)`,
"--dock-command-bg": theme.spatial.surface,
"--dock-command-hover": theme.toolbar.itemHover,
- "--dock-command-active": theme.node.text,
- "--dock-command-active-text": theme.canvas.background,
+ "--dock-command-active": theme.toolbar.activeBg,
+ "--dock-command-active-text": theme.toolbar.activeText,
"--dock-command-danger": theme.accent.danger,
"--dock-tooltip-bg": theme.spatial.elevated,
"--dock-tooltip-border": theme.toolbar.border,
diff --git a/web/src/lib/workspace-routes.ts b/web/src/lib/workspace-routes.ts
new file mode 100644
index 000000000..301b1ec7f
--- /dev/null
+++ b/web/src/lib/workspace-routes.ts
@@ -0,0 +1,5 @@
+export function isSpatialWorkbenchPath(pathname: string) {
+ // 画布列表属于用户工作台,只有具体画布编辑器与运营后台保留各自现有视觉契约。
+ const canvasEditor = /^\/canvas\/[^/]+(?:\/|$)/.test(pathname);
+ return !canvasEditor && !pathname.startsWith("/admin");
+}
diff --git a/web/src/pages/home/index.tsx b/web/src/pages/home/index.tsx
index 8b7adbcf9..60d5ba93e 100644
--- a/web/src/pages/home/index.tsx
+++ b/web/src/pages/home/index.tsx
@@ -1,13 +1,13 @@
-import { useMemo, type ReactNode } from "react";
+import { useMemo, type CSSProperties, type ReactNode } from "react";
import { useQuery } from "@tanstack/react-query";
import { App, Button } from "antd";
-import { ArrowRight, Bot, CheckCircle2, CircleAlert, Clapperboard, Clock3, FolderKanban, Images, LayoutGrid, ListChecks, Plus, Sparkles } from "lucide-react";
+import { ArrowRight, BookOpenText, Bot, CheckCircle2, CircleAlert, Clapperboard, Clock3, FolderKanban, Images, LayoutGrid, ListChecks, Plus, Sparkles } from "lucide-react";
import { Link, useNavigate } from "react-router";
import { CanvasProjectCard } from "@/components/canvas/canvas-project-card";
import { WorkspaceErrorState, WorkspaceLoadingState } from "@/components/layout/workspace-state";
import { WorkspaceSignalIcon } from "@/components/ui/aceternity/workspace-signal-icon";
-import { projectContinueTarget, projectDetailStage, projectNextActions, projectSummaryCompletion } from "@/lib/project-workbench";
+import { projectAttentionCount, projectContinueTarget, projectDetailStage, projectNextActions, projectSummaryCompletion } from "@/lib/project-workbench";
import { getProject, listProjects, type ProjectSummary } from "@/services/api/projects";
import { createCanvasProjectWithRemoteSync } from "@/services/user-data-sync";
import { useCanvasStore } from "@/stores/canvas/use-canvas-store";
@@ -58,7 +58,7 @@ export default function IndexPage() {
const loadingUserWorkspace = !userHydrated || (Boolean(user) && domainProjectsQuery.isLoading);
return (
-
+
{loadingUserWorkspace ? (
) : user && domainProjectsQuery.isError ? (
@@ -99,9 +99,10 @@ function ReturningWorkspace({ summary, detail, detailLoading, detailError, recen
const continueTarget = detail ? projectContinueTarget(detail) : { href: `/projects/${summary.project.id}/overview`, title: summary.project.name, context: "打开项目概览", updatedAt: summary.project.updatedAt };
const nextActions = detail ? projectNextActions(detail, 3) : [];
const completion = projectSummaryCompletion(summary);
+ const attentionCount = detail ? projectAttentionCount(detail) : 0;
return (
<>
-