From 29033b5757ca0d3e68183a0082adc249bbbcb925 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 10 Jun 2026 23:28:10 +0800 Subject: [PATCH] fix: align canvas loading and composer controls --- app/canvas/[id]/CanvasPageClientRouter.tsx | 70 ++++++++++++++++--- app/canvas/[id]/page.tsx | 34 +++++++-- app/canvas/loading.tsx | 19 ++--- components/canvas/CanvasChatPanel.tsx | 6 +- components/canvas/canvas-logo-loading.tsx | 59 ++++++++++++++++ .../home/lovart/lovart-hero-composer.tsx | 12 ++-- 6 files changed, 167 insertions(+), 33 deletions(-) create mode 100644 components/canvas/canvas-logo-loading.tsx diff --git a/app/canvas/[id]/CanvasPageClientRouter.tsx b/app/canvas/[id]/CanvasPageClientRouter.tsx index 575ce88af..6c4fef3fa 100644 --- a/app/canvas/[id]/CanvasPageClientRouter.tsx +++ b/app/canvas/[id]/CanvasPageClientRouter.tsx @@ -1,9 +1,9 @@ "use client"; -import { Loader2 } from "lucide-react"; import dynamic from "next/dynamic"; import { useParams, useSearchParams } from "next/navigation"; -import { useMemo } from "react"; +import { createContext, type ReactNode, useContext, useMemo } from "react"; +import { CanvasLogoLoadingView } from "@/components/canvas/canvas-logo-loading"; const CanvasEditorLegacyPage = dynamic(() => import("./CanvasEditorLegacyPageClient"), { loading: () => , @@ -21,10 +21,16 @@ const LiblibVideoCanvasReplica = dynamic( export function CanvasPageClientRouter({ initialId, initialMode = "", + initialSiteLogo = null, + initialSiteLogoDark = null, + initialSiteName = "AI Chatbot", initialWorkspaceType = "", }: { initialId: string; initialMode?: string; + initialSiteLogo?: string | null; + initialSiteLogoDark?: string | null; + initialSiteName?: string | null; initialWorkspaceType?: string; }) { const routeParams = useParams(); @@ -37,23 +43,69 @@ export function CanvasPageClientRouter({ const requestedAgentMode = mode === "agent"; const isVideoMode = !requestedAgentMode && (mode === "video" || requestedWorkspaceType === "video"); const legacyParams = useMemo(() => Promise.resolve({ id }), [id]); + const loadingBrand = useMemo( + () => ({ + siteLogo: initialSiteLogo, + siteLogoDark: initialSiteLogoDark, + siteName: initialSiteName, + }), + [initialSiteLogo, initialSiteLogoDark, initialSiteName] + ); if (!id) { - return ; + return ( + + + + ); } if (isVideoMode) { - return ; + return ( + + + + ); } - return ; + return ( + + + + ); +} + +type CanvasLoadingBrand = { + siteLogo?: string | null; + siteLogoDark?: string | null; + siteName?: string | null; +}; + +const CanvasLoadingBrandContext = createContext({}); + +function CanvasLoadingBrandProvider({ + children, + value, +}: { + children: ReactNode; + value: CanvasLoadingBrand; +}) { + return ( + + {children} + + ); } function CanvasRouteLoader({ label }: { label: string }) { + const brand = useContext(CanvasLoadingBrandContext); + return ( -
- - {label} -
+ ); } diff --git a/app/canvas/[id]/page.tsx b/app/canvas/[id]/page.tsx index 694f53b64..9487759c8 100644 --- a/app/canvas/[id]/page.tsx +++ b/app/canvas/[id]/page.tsx @@ -1,5 +1,6 @@ -import { Loader2 } from "lucide-react"; import { Suspense } from "react"; +import { CanvasLogoLoadingView } from "@/components/canvas/canvas-logo-loading"; +import { getSiteSettings } from "@/lib/db/queries"; import { CanvasPageClientRouter } from "./CanvasPageClientRouter"; type CanvasEditorPageProps = { @@ -20,12 +21,21 @@ export default async function CanvasEditorPage({ }: CanvasEditorPageProps) { const { id } = await params; const resolvedSearchParams = searchParams ? await searchParams : {}; + const siteSettings = await getSiteSettings().catch(() => null); + const loadingBrand = { + siteLogo: siteSettings?.siteLogo, + siteLogoDark: siteSettings?.siteLogoDark, + siteName: siteSettings?.siteName, + }; return ( - }> + }> - - 正在加载画布... - + ); } diff --git a/app/canvas/loading.tsx b/app/canvas/loading.tsx index b1c3afc82..f9137067d 100644 --- a/app/canvas/loading.tsx +++ b/app/canvas/loading.tsx @@ -1,12 +1,15 @@ -import { Loader2 } from "lucide-react"; +import { CanvasLogoLoadingView } from "@/components/canvas/canvas-logo-loading"; +import { getSiteSettings } from "@/lib/db/queries"; + +export default async function CanvasLoading() { + const settings = await getSiteSettings().catch(() => null); -export default function CanvasLoading() { return ( -
-
- -
正在进入工作台...
-
-
+ ); } diff --git a/components/canvas/CanvasChatPanel.tsx b/components/canvas/CanvasChatPanel.tsx index 2939389b2..c69f22945 100644 --- a/components/canvas/CanvasChatPanel.tsx +++ b/components/canvas/CanvasChatPanel.tsx @@ -10875,7 +10875,7 @@ export function CanvasChatPanel({ ? "bg-gradient-to-r from-violet-600 to-fuchsia-600 text-white shadow-md hover:shadow-lg hover:opacity-90 dark:from-violet-500 dark:to-fuchsia-500" : "bg-muted text-muted-foreground hover:bg-muted/80" : canSend - ? "bg-[#1f2328] text-white shadow-sm hover:bg-[#111418]" + ? "bg-[#eef0f3] text-[#20242a] shadow-sm ring-1 ring-inset ring-[#d5dae2] hover:bg-[#e4e7ec] dark:bg-white dark:text-zinc-950 dark:ring-white/15 dark:hover:bg-zinc-100" : "bg-[#f1f1f2] text-[#b6bac2] hover:bg-[#ececee] dark:bg-[#242529] dark:text-[#777d88] dark:hover:bg-[#2c2d33]" )} onClick={handleSend} @@ -10899,7 +10899,7 @@ export function CanvasChatPanel({ canSend && (isVideoWorkspace || isDetailGenerationMode ? "opacity-95" - : "opacity-95 invert dark:invert-0") + : "opacity-95") )} fallback={Sparkles} item={getToolbarItem("chatStartGeneration")} @@ -10919,7 +10919,7 @@ export function CanvasChatPanel({ canSend && (isVideoWorkspace || isDetailGenerationMode ? "opacity-95" - : "opacity-95 invert dark:invert-0") + : "opacity-95") )} fallback={ArrowUp} item={getToolbarItem( diff --git a/components/canvas/canvas-logo-loading.tsx b/components/canvas/canvas-logo-loading.tsx new file mode 100644 index 000000000..fb6f8c022 --- /dev/null +++ b/components/canvas/canvas-logo-loading.tsx @@ -0,0 +1,59 @@ +import { Sparkles } from "lucide-react"; +import { cn } from "@/lib/utils"; + +type CanvasLogoLoadingViewProps = { + label?: string; + siteName?: string | null; + siteLogo?: string | null; + siteLogoDark?: string | null; +}; + +export function CanvasLogoLoadingView({ + label = "正在加载画布...", + siteName = "AI Chatbot", + siteLogo, + siteLogoDark, +}: CanvasLogoLoadingViewProps) { + const resolvedSiteName = String(siteName || "AI Chatbot").trim(); + const lightLogo = String(siteLogo || siteLogoDark || "").trim(); + const darkLogo = String(siteLogoDark || siteLogo || "").trim(); + const hasSeparateDarkLogo = Boolean(darkLogo && darkLogo !== lightLogo); + + return ( +
+
+
+
+
+
+
+ {lightLogo ? ( + <> + {resolvedSiteName} + {hasSeparateDarkLogo ? ( + {resolvedSiteName} + ) : null} + + ) : ( + + )} +
+
+
+ {label} +
+
+
+ ); +} diff --git a/components/home/lovart/lovart-hero-composer.tsx b/components/home/lovart/lovart-hero-composer.tsx index 18741ffc2..e951778b5 100644 --- a/components/home/lovart/lovart-hero-composer.tsx +++ b/components/home/lovart/lovart-hero-composer.tsx @@ -150,24 +150,24 @@ export function LovartHeroComposer({
-
+