refactor(canvas): derive toolbar offsets from panel geometry
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-03-04 00:07:29 +08:00
parent 93e5493993
commit f2b752dd7d

View File

@ -79,6 +79,10 @@ const SIZE_PRESETS = [
{ label: "9:16", value: "9:16", width: 1024, height: 1820 },
];
const FLOATING_PANEL_WIDTH = 460;
const FLOATING_PANEL_INSET = 16;
const FLOATING_UI_GAP = 16;
export default function CanvasEditorPage({ params }: PageProps) {
// 使用 useParams 确保客户端导航时能正确获取更新的 id
const routeParams = useParams();
@ -124,6 +128,7 @@ export default function CanvasEditorPage({ params }: PageProps) {
const [isPanelOpen, setIsPanelOpen] = useState(true);
// 面板模式: 'chat' 聊天面板, 'fileList' 文件列表
const [panelMode, setPanelMode] = useState<'chat' | 'fileList'>('chat');
const [viewportWidth, setViewportWidth] = useState(0);
const { data: siteSettingsData } = useSWR<{
siteName?: string;
@ -145,6 +150,13 @@ export default function CanvasEditorPage({ params }: PageProps) {
? nodes.find(n => n.id === selectedNodeIds[0] && n.type === 'image')
: null;
const resolvedPanelWidth = viewportWidth > 0
? Math.min(FLOATING_PANEL_WIDTH, Math.max(viewportWidth - FLOATING_PANEL_INSET, 0))
: FLOATING_PANEL_WIDTH;
const panelOcclusionRight = isPanelOpen ? resolvedPanelWidth + FLOATING_PANEL_INSET : 0;
const floatingToolbarOffset = isPanelOpen ? -(panelOcclusionRight / 2) : 0;
const compactControlsRight = isPanelOpen ? panelOcclusionRight + FLOATING_UI_GAP : FLOATING_UI_GAP;
// 加载画布数据
useEffect(() => {
console.log('[Canvas] Loading project:', id);
@ -179,6 +191,13 @@ export default function CanvasEditorPage({ params }: PageProps) {
loadProject();
}, [id]); // 只依赖 id,使用 getState() 直接调用 store actions
useEffect(() => {
const updateViewportWidth = () => setViewportWidth(window.innerWidth);
updateViewportWidth();
window.addEventListener("resize", updateViewportWidth);
return () => window.removeEventListener("resize", updateViewportWidth);
}, []);
const persistCanvasState = useCallback(
async (reason: "autosave" | "delete" | "beforeunload") => {
const canvasData = getCanvasState();
@ -561,7 +580,7 @@ export default function CanvasEditorPage({ params }: PageProps) {
{/* 1. Canvas Layer (Full Screen) - 使用 key 强制在 id 变化时重新挂载 */}
<div className="absolute inset-0 z-0" key={`workspace-${id}`}>
<CanvasWorkspace occlusionRight={isPanelOpen ? 476 : 0} />
<CanvasWorkspace occlusionRight={panelOcclusionRight} />
</div>
{/* Brand Entry (Top Left) */}
@ -641,7 +660,7 @@ export default function CanvasEditorPage({ params }: PageProps) {
{/* 3. Floating Actions (Top Right) */}
{!isPanelOpen && (
<div className="absolute top-4 right-4 z-20">
<div className="absolute z-20" style={{ top: FLOATING_PANEL_INSET, right: FLOATING_PANEL_INSET }}>
<Tooltip>
<TooltipTrigger asChild>
<Button
@ -660,9 +679,16 @@ export default function CanvasEditorPage({ params }: PageProps) {
{/* 4. Floating Sidebar (Right) */}
<div className={cn(
"absolute top-4 right-4 bottom-4 z-10 w-[460px] max-w-[calc(100vw-1rem)] transition-all duration-300 ease-in-out",
"absolute z-10 transition-all duration-300 ease-in-out",
isPanelOpen ? "translate-x-0 opacity-100" : "translate-x-[120%] opacity-0 pointer-events-none"
)}>
)}
style={{
top: FLOATING_PANEL_INSET,
right: FLOATING_PANEL_INSET,
bottom: FLOATING_PANEL_INSET,
width: FLOATING_PANEL_WIDTH,
maxWidth: `calc(100vw - ${FLOATING_PANEL_INSET}px)`,
}}>
<div className="h-full w-full rounded-2xl border bg-white dark:bg-zinc-950 shadow-2xl overflow-hidden flex flex-col">
{panelMode === 'chat' ? (
// AI 对话内容 - 使用 key 强制在 id 变化时重新挂载
@ -687,9 +713,7 @@ export default function CanvasEditorPage({ params }: PageProps) {
<div className="absolute bottom-5 left-1/2 z-20 -translate-x-1/2">
<div
className="flex items-center gap-1 rounded-[24px] border bg-background/88 px-2.5 py-1.5 shadow-2xl backdrop-blur-md"
style={{
marginLeft: isPanelOpen ? "-238px" : "0px",
}}
style={{ marginLeft: `${floatingToolbarOffset}px` }}
>
<Tooltip>
<TooltipTrigger asChild>
@ -797,7 +821,7 @@ export default function CanvasEditorPage({ params }: PageProps) {
{/* 6. Bottom Right Compact Controls */}
<div
className="absolute bottom-5 z-20"
style={{ right: isPanelOpen ? "492px" : "16px" }}
style={{ right: `${compactControlsRight}px` }}
>
<div className="flex items-center gap-1 rounded-2xl border bg-background/88 px-2 py-1.5 shadow-xl backdrop-blur-md">
<Tooltip>