Fix canvas toolbar tone and placeholder scaling
This commit is contained in:
parent
1aad39b588
commit
41522cc481
@ -9251,6 +9251,7 @@ export function ImageNode({
|
||||
onImageUpload={handleImageUpload}
|
||||
onSelectFile={handleSelectFile}
|
||||
status={data.status}
|
||||
viewportScale={viewportZoom}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -9261,6 +9262,7 @@ export function ImageNode({
|
||||
<ImageNodeProcessingOverlay
|
||||
detailProgressLabel={detailProgressLabel || undefined}
|
||||
generationProgress={generationProgress}
|
||||
viewportScale={viewportZoom}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -9575,7 +9577,7 @@ export function ImageNode({
|
||||
typeof document !== "undefined" &&
|
||||
createPortal(
|
||||
<div
|
||||
className="fixed z-[9999] flex items-center gap-2 rounded-[24px] border border-slate-200 bg-white px-2 py-2 text-gray-800 shadow-[0_14px_38px_rgba(15,23,42,0.16)] dark:border-slate-700 dark:bg-slate-900 dark:text-gray-100"
|
||||
className="fixed z-[9999] flex items-center gap-2 rounded-[24px] border border-slate-200 bg-white px-2 py-2 text-gray-800 shadow-[0_14px_38px_rgba(15,23,42,0.16)] dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-100 dark:shadow-[0_16px_44px_rgba(0,0,0,0.45)]"
|
||||
data-local-edit-control="true"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
onMouseDown={(event) => event.stopPropagation()}
|
||||
@ -9641,7 +9643,7 @@ export function ImageNode({
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="mx-1 h-6 w-px bg-slate-200 dark:bg-slate-700" />
|
||||
<div className="mx-1 h-6 w-px bg-slate-200 dark:bg-zinc-800" />
|
||||
|
||||
<Button
|
||||
className="mr-1 inline-flex h-10 shrink-0 items-center gap-1.5 whitespace-nowrap rounded-xl bg-slate-950 px-3 font-semibold text-[13px] text-white hover:bg-slate-800 hover:text-white disabled:bg-slate-300 disabled:text-slate-500 dark:border dark:border-white/14 dark:bg-black dark:text-white dark:disabled:border-transparent dark:disabled:bg-slate-700 dark:disabled:text-slate-400 dark:hover:bg-zinc-900"
|
||||
@ -9935,7 +9937,7 @@ export function ImageNode({
|
||||
createPortal(
|
||||
<TooltipProvider delayDuration={200}>
|
||||
<div
|
||||
className="fixed z-[9999] flex items-center gap-0.5 rounded-[24px] border border-slate-200 bg-white px-2 py-1.5 text-gray-800 shadow-[0_14px_38px_rgba(15,23,42,0.16)] dark:border-slate-700 dark:bg-slate-900 dark:text-gray-100"
|
||||
className="fixed z-[9999] flex items-center gap-0.5 rounded-[24px] border border-slate-200 bg-white px-2 py-1.5 text-gray-800 shadow-[0_14px_38px_rgba(15,23,42,0.16)] dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-100 dark:shadow-[0_16px_44px_rgba(0,0,0,0.45)]"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onMouseDown={(e) => e.stopPropagation()}
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
@ -9979,7 +9981,7 @@ export function ImageNode({
|
||||
{imageAddToChatEnabled &&
|
||||
(hasSelectedImagePrimaryToolbarActions ||
|
||||
hasSelectedImageSecondaryToolbarActions) ? (
|
||||
<div className="mx-1 h-6 w-px bg-slate-200 dark:bg-slate-700" />
|
||||
<div className="mx-1 h-6 w-px bg-slate-200 dark:bg-zinc-800" />
|
||||
) : null}
|
||||
|
||||
{isSelectedImageActionInline("pickObject") ? (
|
||||
@ -10352,7 +10354,7 @@ export function ImageNode({
|
||||
|
||||
{hasSelectedImagePrimaryToolbarActions &&
|
||||
hasSelectedImageSecondaryToolbarActions ? (
|
||||
<div className="mx-1 h-6 w-px bg-slate-200 dark:bg-slate-700" />
|
||||
<div className="mx-1 h-6 w-px bg-slate-200 dark:bg-zinc-800" />
|
||||
) : null}
|
||||
|
||||
{hasSelectedImageMoreMenu ? (
|
||||
@ -10374,7 +10376,7 @@ export function ImageNode({
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
align="center"
|
||||
className="bg-white opacity-100 backdrop-blur-none dark:bg-slate-900 dark:text-slate-100"
|
||||
className="bg-white opacity-100 backdrop-blur-none dark:bg-zinc-950 dark:text-zinc-100"
|
||||
side="bottom"
|
||||
>
|
||||
{isSelectedImageActionInMore("boxCutout") ? (
|
||||
@ -10594,7 +10596,7 @@ export function ImageNode({
|
||||
typeof document !== "undefined" &&
|
||||
createPortal(
|
||||
<div
|
||||
className="fixed z-[9999] flex items-center rounded-[24px] border border-slate-200 bg-white px-2 py-2 text-gray-800 shadow-[0_14px_38px_rgba(15,23,42,0.16)] dark:border-slate-700 dark:bg-slate-900 dark:text-gray-100"
|
||||
className="fixed z-[9999] flex items-center rounded-[24px] border border-slate-200 bg-white px-2 py-2 text-gray-800 shadow-[0_14px_38px_rgba(15,23,42,0.16)] dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-100 dark:shadow-[0_16px_44px_rgba(0,0,0,0.45)]"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
onMouseDown={(event) => event.stopPropagation()}
|
||||
onPointerDown={(event) => event.stopPropagation()}
|
||||
@ -10622,7 +10624,7 @@ export function ImageNode({
|
||||
<span className="whitespace-nowrap">{boxCutoutHintText}</span>
|
||||
</div>
|
||||
|
||||
<div className="mx-1 h-6 w-px bg-slate-200 dark:bg-slate-700" />
|
||||
<div className="mx-1 h-6 w-px bg-slate-200 dark:bg-zinc-800" />
|
||||
|
||||
<Button
|
||||
className="mr-1 inline-flex h-10 shrink-0 items-center gap-1.5 whitespace-nowrap rounded-xl bg-slate-900 px-3 font-semibold text-[13px] text-white hover:bg-slate-800 hover:text-white disabled:bg-slate-300 disabled:text-slate-500 dark:bg-slate-100 dark:text-slate-900 dark:disabled:bg-slate-700 dark:disabled:text-slate-400 dark:hover:bg-slate-200"
|
||||
|
||||
@ -11,6 +11,7 @@ interface ImageNodeEmptyStateProps {
|
||||
onImageUpload: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
onSelectFile: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
status?: string;
|
||||
viewportScale?: number;
|
||||
}
|
||||
|
||||
export function ImageNodeDeferredLoadState() {
|
||||
@ -27,9 +28,11 @@ export function ImageNodeDeferredLoadState() {
|
||||
export function ImageNodeProcessingOverlay({
|
||||
detailProgressLabel,
|
||||
generationProgress,
|
||||
viewportScale,
|
||||
}: {
|
||||
detailProgressLabel?: string;
|
||||
generationProgress?: number | null;
|
||||
viewportScale?: number;
|
||||
}) {
|
||||
return (
|
||||
<div className="absolute inset-0 overflow-hidden rounded-[inherit] bg-white/96 backdrop-blur-[1px]">
|
||||
@ -39,6 +42,7 @@ export function ImageNodeProcessingOverlay({
|
||||
phase="processing"
|
||||
progress={generationProgress}
|
||||
title={detailProgressLabel || "正在处理图片"}
|
||||
viewportScale={viewportScale}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@ -51,6 +55,7 @@ export function ImageNodeEmptyState({
|
||||
onImageUpload,
|
||||
onSelectFile,
|
||||
status,
|
||||
viewportScale,
|
||||
}: ImageNodeEmptyStateProps) {
|
||||
if (status === "generating") {
|
||||
return (
|
||||
@ -60,6 +65,7 @@ export function ImageNodeEmptyState({
|
||||
phase="processing"
|
||||
progress={generationProgress}
|
||||
title={detailProgressLabel || undefined}
|
||||
viewportScale={viewportScale}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ interface GenerationDotPlaceholderProps {
|
||||
progress?: number | null;
|
||||
compact?: boolean;
|
||||
showProgress?: boolean;
|
||||
viewportScale?: number;
|
||||
}
|
||||
|
||||
const PHASE_TITLES: Record<GenerationPhase, string> = {
|
||||
@ -242,6 +243,17 @@ function clampProgress(value: number) {
|
||||
return Math.max(0, Math.min(100, Math.round(value)));
|
||||
}
|
||||
|
||||
function normalizeViewportScale(value: number | undefined) {
|
||||
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
||||
return 1;
|
||||
}
|
||||
return Math.max(0.1, Math.min(4, value));
|
||||
}
|
||||
|
||||
function getParticlePointSize(compact: boolean, viewportScale: number) {
|
||||
return (compact ? 76 : 94) * viewportScale;
|
||||
}
|
||||
|
||||
function estimateProgress(phase: GenerationPhase, elapsedMs: number) {
|
||||
if (phase === "completed") return 100;
|
||||
if (phase === "failed") return 100;
|
||||
@ -259,11 +271,25 @@ function estimateProgress(phase: GenerationPhase, elapsedMs: number) {
|
||||
function ScatterParticleLayer({
|
||||
compact,
|
||||
opacity,
|
||||
viewportScale,
|
||||
}: {
|
||||
compact: boolean;
|
||||
opacity: number;
|
||||
viewportScale: number;
|
||||
}) {
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const pointSizeUniformRef = useRef<{ value: number } | null>(null);
|
||||
const viewportScaleRef = useRef(viewportScale);
|
||||
|
||||
useEffect(() => {
|
||||
viewportScaleRef.current = viewportScale;
|
||||
if (pointSizeUniformRef.current) {
|
||||
pointSizeUniformRef.current.value = getParticlePointSize(
|
||||
compact,
|
||||
viewportScale
|
||||
);
|
||||
}
|
||||
}, [compact, viewportScale]);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
@ -286,12 +312,12 @@ function ScatterParticleLayer({
|
||||
antialias: true,
|
||||
powerPreference: "high-performance",
|
||||
});
|
||||
renderer.setClearColor(0xffffff, 0);
|
||||
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2.5));
|
||||
renderer.domElement.style.display = "block";
|
||||
renderer.domElement.style.height = "100%";
|
||||
renderer.domElement.style.width = "100%";
|
||||
container.appendChild(renderer.domElement);
|
||||
renderer.setClearColor(0xffffff, 0);
|
||||
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2.5));
|
||||
renderer.domElement.style.display = "block";
|
||||
renderer.domElement.style.height = "100%";
|
||||
renderer.domElement.style.width = "100%";
|
||||
container.appendChild(renderer.domElement);
|
||||
|
||||
const particleCount = compact ? 7200 : 12800;
|
||||
const positions = new Float32Array(particleCount * 3);
|
||||
@ -323,11 +349,16 @@ function ScatterParticleLayer({
|
||||
blending: THREE.NormalBlending,
|
||||
uniforms: {
|
||||
uTime: { value: 0 },
|
||||
uPointSize: { value: compact ? 76 : 94 },
|
||||
uPointSize: {
|
||||
value: getParticlePointSize(compact, viewportScaleRef.current),
|
||||
},
|
||||
uEffectIntensity: { value: 1 },
|
||||
uOpacity: { value: opacity },
|
||||
},
|
||||
});
|
||||
pointSizeUniformRef.current = material.uniforms.uPointSize as {
|
||||
value: number;
|
||||
};
|
||||
|
||||
const points = new THREE.Points(geometry, material);
|
||||
points.rotation.x = -0.06;
|
||||
@ -371,6 +402,7 @@ function ScatterParticleLayer({
|
||||
material.dispose();
|
||||
renderer.dispose();
|
||||
renderer.domElement.remove();
|
||||
pointSizeUniformRef.current = null;
|
||||
};
|
||||
}
|
||||
|
||||
@ -391,8 +423,10 @@ export function GenerationDotPlaceholder({
|
||||
progress,
|
||||
compact = false,
|
||||
showProgress = true,
|
||||
viewportScale,
|
||||
}: GenerationDotPlaceholderProps) {
|
||||
const [elapsedMs, setElapsedMs] = useState(0);
|
||||
const resolvedViewportScale = normalizeViewportScale(viewportScale);
|
||||
|
||||
useEffect(() => {
|
||||
const startedAt = Date.now();
|
||||
@ -489,7 +523,11 @@ export function GenerationDotPlaceholder({
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<ScatterParticleLayer compact={compact} opacity={dotOpacity} />
|
||||
<ScatterParticleLayer
|
||||
compact={compact}
|
||||
opacity={dotOpacity}
|
||||
viewportScale={resolvedViewportScale}
|
||||
/>
|
||||
<style jsx>{`
|
||||
@keyframes generationCursorBlink {
|
||||
0%,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user