fix: refine angle panel and agent workspace scroll

This commit is contained in:
Your Name 2026-05-08 22:16:33 +08:00
parent 1991e5c4cc
commit 12671a1e36
2 changed files with 120 additions and 63 deletions

View File

@ -5335,8 +5335,39 @@ export function AgentGenerationWorkspaceDarkV2({
currentPsdWorkflowContext?.fifthCategory || ""
).trim();
useEffect(() => {
if (typeof document === "undefined") {
return;
}
const html = document.documentElement;
const body = document.body;
const previousHtmlOverflow = html.style.overflow;
const previousBodyOverflow = body.style.overflow;
const previousHtmlOverscrollBehavior = html.style.overscrollBehavior;
const previousBodyOverscrollBehavior = body.style.overscrollBehavior;
const previousHtmlBackgroundColor = html.style.backgroundColor;
const previousBodyBackgroundColor = body.style.backgroundColor;
html.style.overflow = "hidden";
body.style.overflow = "hidden";
html.style.overscrollBehavior = "none";
body.style.overscrollBehavior = "none";
html.style.backgroundColor = "#050607";
body.style.backgroundColor = "#050607";
return () => {
html.style.overflow = previousHtmlOverflow;
body.style.overflow = previousBodyOverflow;
html.style.overscrollBehavior = previousHtmlOverscrollBehavior;
body.style.overscrollBehavior = previousBodyOverscrollBehavior;
html.style.backgroundColor = previousHtmlBackgroundColor;
body.style.backgroundColor = previousBodyBackgroundColor;
};
}, []);
return (
<div className="flex h-dvh flex-col overflow-hidden bg-black">
<div className="flex h-dvh min-h-0 flex-col overflow-hidden bg-black">
<AgentCanvasTopNavDark
siteName={siteName}
siteLogo={siteLogo}
@ -5348,7 +5379,7 @@ export function AgentGenerationWorkspaceDarkV2({
/>
{/* Main Content */}
<main className="flex-1 overflow-y-auto overscroll-contain pt-16">
<main className="min-h-0 flex-1 overflow-y-auto overscroll-none bg-black pt-16">
<div className="mx-auto flex w-full max-w-[1480px] flex-col gap-6 px-6 py-6 pb-24 lg:px-8 xl:px-10">
{showcaseConfig ? (
<motion.section

View File

@ -42,7 +42,7 @@ import {
} from "lucide-react";
import type React from "react";
import { useParams, useRouter } from "next/navigation";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { toast } from "sonner";
import useSWR from "swr";
@ -236,6 +236,7 @@ const BOX_CUTOUT_DEFAULT_PADDING_RATIO = 0.08;
const BOX_CUTOUT_MIN_EDGE_PX = 24;
const FLOATING_TOOLBAR_SCALE = 0.8;
const IMAGE_EDIT_PANEL_SCALE = 0.5;
const ANGLE_PANEL_SCALE = 0.65;
const ANGLE_PREVIEW_HORIZONTAL_DRAG_MULTIPLIER = 0.72;
const ANGLE_PREVIEW_VERTICAL_DRAG_MULTIPLIER = 0.46;
const ANGLE_PANEL_SOURCE_WIDTH = 520;
@ -243,12 +244,14 @@ const ANGLE_PANEL_SOURCE_HEIGHT = 700;
const ANGLE_PANEL_PREVIEW_SIZE = 392;
const ANGLE_PANEL_CAMERA_SCENE_SIZE = 384;
const ANGLE_PANEL_WIREFRAME_RADIUS = 132;
const ANGLE_PANEL_WIREFRAME_CENTER = 160;
const ANGLE_PREVIEW_ORBIT_ROTATIONS = [
0, 14, 28, 42, 56, 70, 84, 98, 112, 126, 140, 154, 168,
const ANGLE_PANEL_WIREFRAME_DIAMETER = ANGLE_PANEL_WIREFRAME_RADIUS * 2;
const ANGLE_PREVIEW_VERTICAL_RING_ROTATIONS = [
0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165,
];
const ANGLE_PREVIEW_LATITUDE_RATIOS = [1, 0.9, 0.78, 0.64, 0.5];
const ANGLE_PREVIEW_MERIDIAN_RATIOS = [1, 0.9, 0.78, 0.64];
const ANGLE_PREVIEW_HORIZONTAL_RING_ROTATIONS = [
-78, -56, -34, -14, 0, 14, 34, 56, 78,
];
const ANGLE_PREVIEW_DIAGONAL_RING_ROTATIONS = [-62, -40, -18, 8, 34, 58];
const DEFAULT_CROP_PRESET_ID = "1:1";
const IMAGE_ADJUST_NOISE_DATA_URI = `data:image/svg+xml;utf8,${encodeURIComponent(
"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 160 160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='1.18' numOctaves='2' stitchTiles='stitch'/></filter><rect width='160' height='160' filter='url(#n)' opacity='1'/></svg>"
@ -1642,6 +1645,7 @@ export function ImageNode({
const [angleZoom, setAngleZoom] = useState(50);
const [isApplyingAngleEdit, setIsApplyingAngleEdit] = useState(false);
const [isAnglePreviewDragging, setIsAnglePreviewDragging] = useState(false);
const [anglePanelOpening, setAnglePanelOpening] = useState(false);
const [sceneFusionDialogOpen, setSceneFusionDialogOpen] = useState(false);
const [resizeAnnotatorOpen, setResizeAnnotatorOpen] = useState(false);
const [promptDialogOpen, setPromptDialogOpen] = useState(false);
@ -1796,6 +1800,19 @@ export function ImageNode({
setAngleZoom(clampNumber(savedSettings.zoom, 0, 100));
}
}, [anglePanelOpen, data.metadata]);
useEffect(() => {
if (!anglePanelOpen || typeof window === "undefined") {
setAnglePanelOpening(false);
return;
}
setAnglePanelOpening(true);
const timer = window.setTimeout(() => {
setAnglePanelOpening(false);
}, 560);
return () => window.clearTimeout(timer);
}, [anglePanelOpen]);
useEffect(() => {
const nextDefaultTripoModelId =
getDefaultThreeDModelAliasId(publicModelAliasConfig.tripoModels) ||
@ -2140,13 +2157,18 @@ export function ImageNode({
}, [angleCameraOrbitPosition]);
const angleOrbitWireframeStyle = useMemo<React.CSSProperties>(
() => ({
transform: `rotateX(${-angleVerticalAngle * 0.22}deg) rotateY(${angleHorizontalAngle * 0.28}deg) rotateZ(${angleHorizontalAngle * 0.04}deg)`,
transform: `rotateX(${(-angleVerticalAngle * 0.24) + (anglePanelOpening ? 14 : 0)}deg) rotateY(${(angleHorizontalAngle * 0.34) + (anglePanelOpening ? -34 : 0)}deg) rotateZ(${(angleHorizontalAngle * 0.05) + (anglePanelOpening ? 7 : 0)}deg)`,
transformStyle: "preserve-3d",
transition: isAnglePreviewDragging
? "none"
: "transform 180ms cubic-bezier(0.22, 1, 0.36, 1)",
: `transform ${anglePanelOpening ? 560 : 180}ms cubic-bezier(0.22, 1, 0.36, 1)`,
}),
[angleHorizontalAngle, angleVerticalAngle, isAnglePreviewDragging]
[
angleHorizontalAngle,
anglePanelOpening,
angleVerticalAngle,
isAnglePreviewDragging,
]
);
const angleCameraSceneScale = useMemo(
() => Number((0.94 + (angleZoom / 100) * 0.22).toFixed(3)),
@ -3607,8 +3629,8 @@ export function ImageNode({
}
const rect = containerRef.current.getBoundingClientRect();
const panelWidth = ANGLE_PANEL_SOURCE_WIDTH * IMAGE_EDIT_PANEL_SCALE;
const panelHeight = ANGLE_PANEL_SOURCE_HEIGHT * IMAGE_EDIT_PANEL_SCALE;
const panelWidth = ANGLE_PANEL_SOURCE_WIDTH * ANGLE_PANEL_SCALE;
const panelHeight = ANGLE_PANEL_SOURCE_HEIGHT * ANGLE_PANEL_SCALE;
const spacing = 16;
const maxTop = Math.max(12, window.innerHeight - panelHeight);
@ -4678,6 +4700,7 @@ export function ImageNode({
setTextEditPanelOpen(false);
setNotePanelOpen(false);
resetBoxCutoutState();
setAnglePanelOpening(true);
setAnglePanelOpen(true);
return;
}
@ -8219,7 +8242,7 @@ export function ImageNode({
style={{
top: anglePanelPosition.top,
left: anglePanelPosition.left,
transform: `scale(${IMAGE_EDIT_PANEL_SCALE})`,
transform: `scale(${ANGLE_PANEL_SCALE})`,
transformOrigin: "top left",
}}
>
@ -8287,61 +8310,64 @@ export function ImageNode({
className="pointer-events-none absolute inset-0 flex items-center justify-center"
style={angleOrbitWireframeStyle}
>
<svg
className="shrink-0"
fill="none"
height={ANGLE_PANEL_PREVIEW_SIZE}
viewBox="0 0 320 320"
width={ANGLE_PANEL_PREVIEW_SIZE}
<div
className="relative shrink-0 [transform-style:preserve-3d]"
style={{
width: `${ANGLE_PANEL_WIREFRAME_DIAMETER}px`,
height: `${ANGLE_PANEL_WIREFRAME_DIAMETER}px`,
}}
>
{ANGLE_PREVIEW_ORBIT_ROTATIONS.map((rotation, index) => (
<ellipse
key={`orbit-rotation-${rotation}`}
cx={ANGLE_PANEL_WIREFRAME_CENTER}
cy={ANGLE_PANEL_WIREFRAME_CENTER}
rx={ANGLE_PANEL_WIREFRAME_RADIUS}
ry={ANGLE_PANEL_WIREFRAME_RADIUS}
stroke={index === 0 ? "#e5e8ef" : "#e8ebf1"}
strokeOpacity={index === 0 ? 0.92 : 0.84}
strokeWidth={index % 3 === 0 ? "1.12" : "1.02"}
transform={
rotation === 0
? undefined
: `rotate(${rotation} ${ANGLE_PANEL_WIREFRAME_CENTER} ${ANGLE_PANEL_WIREFRAME_CENTER})`
}
<div className="absolute inset-0 rounded-full border border-[#e4e8ef]/90" />
{ANGLE_PREVIEW_VERTICAL_RING_ROTATIONS.map((rotation, index) => (
<div
className="absolute inset-0 rounded-full border border-[#e7ebf2]"
key={`angle-vertical-ring-${rotation}`}
style={{
backfaceVisibility: "hidden",
opacity: 0.9 - index * 0.026,
transform: `rotateY(${rotation}deg)`,
transformStyle: "preserve-3d",
}}
/>
))}
{ANGLE_PREVIEW_LATITUDE_RATIOS.map((ratio, index) => (
<ellipse
key={`orbit-latitude-${ratio}`}
cx={ANGLE_PANEL_WIREFRAME_CENTER}
cy={ANGLE_PANEL_WIREFRAME_CENTER}
rx={ANGLE_PANEL_WIREFRAME_RADIUS}
ry={Number(
(ANGLE_PANEL_WIREFRAME_RADIUS * ratio).toFixed(2)
)}
stroke="#eceff4"
strokeOpacity={0.86 - index * 0.08}
strokeWidth={index === 0 ? "1.08" : "1.0"}
{ANGLE_PREVIEW_HORIZONTAL_RING_ROTATIONS.map((rotation, index) => (
<div
className="absolute inset-0 rounded-full border border-[#e6eaf1]"
key={`angle-horizontal-ring-${rotation}`}
style={{
backfaceVisibility: "hidden",
opacity: rotation === 0 ? 0.94 : 0.82 - index * 0.045,
transform: `rotateX(${rotation}deg)`,
transformStyle: "preserve-3d",
}}
/>
))}
{ANGLE_PREVIEW_MERIDIAN_RATIOS.map((ratio, index) => (
<ellipse
key={`orbit-meridian-${ratio}`}
cx={ANGLE_PANEL_WIREFRAME_CENTER}
cy={ANGLE_PANEL_WIREFRAME_CENTER}
rx={Number(
(ANGLE_PANEL_WIREFRAME_RADIUS * ratio).toFixed(2)
)}
ry={ANGLE_PANEL_WIREFRAME_RADIUS}
stroke="#e9edf3"
strokeOpacity={0.78 - index * 0.08}
strokeDasharray={index === 0 ? undefined : "4 6"}
strokeWidth="1.0"
transform={`rotate(90 ${ANGLE_PANEL_WIREFRAME_CENTER} ${ANGLE_PANEL_WIREFRAME_CENTER})`}
/>
{ANGLE_PREVIEW_DIAGONAL_RING_ROTATIONS.map((rotation, index) => (
<Fragment key={`angle-diagonal-ring-${rotation}`}>
<div
className="absolute inset-0 rounded-full border border-[#edf0f5]"
style={{
backfaceVisibility: "hidden",
opacity: 0.54 - index * 0.035,
transform: `rotateX(67deg) rotateY(${rotation}deg)`,
transformStyle: "preserve-3d",
}}
/>
<div
className="absolute inset-0 rounded-full border border-[#edf0f5]"
style={{
backfaceVisibility: "hidden",
opacity: 0.5 - index * 0.032,
transform: `rotateX(-67deg) rotateY(${rotation + 12}deg)`,
transformStyle: "preserve-3d",
}}
/>
</Fragment>
))}
</svg>
</div>
</div>
<button