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 (
<>
-