新增 连续生成功能;

添加了自动化测试套件;
This commit is contained in:
2025-10-02 18:13:44 +08:00
parent d7e355e9c6
commit d70e9e62b8
14 changed files with 985 additions and 47 deletions

View File

@@ -1,5 +1,6 @@
import React, { useRef, useEffect, useState, useCallback } from 'react';
import { Stage, Layer, Image as KonvaImage, Line } from 'react-konva';
import type { KonvaEventObject } from 'konva/lib/Node';
import { useAppStore } from '../store/useAppStore';
import { Button } from './ui/Button';
import { ZoomIn, ZoomOut, RotateCcw, Download } from 'lucide-react';
@@ -8,7 +9,7 @@ export const ImageCanvas: React.FC = () => {
const {
canvasImage,
canvasZoom,
canvasPan,
// canvasPan,
setCanvasZoom,
setCanvasPan,
brushStrokes,
@@ -16,6 +17,8 @@ export const ImageCanvas: React.FC = () => {
showMasks,
selectedTool,
isGenerating,
isContinuousGenerating,
retryCount,
brushSize,
showHistory,
showPromptPanel
@@ -296,14 +299,16 @@ export const ImageCanvas: React.FC = () => {
return () => container.removeEventListener('wheel', handleWheel);
}, [canvasZoom, handleZoom]);
const handleMouseDown = (e: Konva.KonvaEventObject<MouseEvent>) => {
const handleMouseDown = (e: KonvaEventObject<MouseEvent>) => {
if (selectedTool !== 'mask' || !image) return;
setIsDrawing(true);
const stage = e.target.getStage();
if (!stage) return;
// 使用 Konva 的 getRelativePointerPosition 获取准确坐标
const relativePos = stage.getRelativePointerPosition();
if (!relativePos) return;
// 计算图像在舞台上的边界
const imageX = (stageSize.width / canvasZoom - image.width) / 2;
@@ -319,13 +324,15 @@ export const ImageCanvas: React.FC = () => {
}
};
const handleMouseMove = (e: Konva.KonvaEventObject<MouseEvent>) => {
const handleMouseMove = (e: KonvaEventObject<MouseEvent>) => {
if (!isDrawing || selectedTool !== 'mask' || !image) return;
const stage = e.target.getStage();
if (!stage) return;
// 使用 Konva 的 getRelativePointerPosition 获取准确坐标
const relativePos = stage.getRelativePointerPosition();
if (!relativePos) return;
// 计算图像在舞台上的边界
const imageX = (stageSize.width / canvasZoom - image.width) / 2;
@@ -353,6 +360,7 @@ export const ImageCanvas: React.FC = () => {
id: `stroke-${Date.now()}`,
points: currentStroke,
brushSize,
color: '#A855F7',
});
setCurrentStroke([]);
};
@@ -424,12 +432,14 @@ export const ImageCanvas: React.FC = () => {
console.error('下载图像失败:', error);
// 如果fetch失败回退到直接使用a标签
const link = document.createElement('a');
link.href = uploadResult.url;
link.download = `nano-banana-${Date.now()}.png`;
link.target = '_blank';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
if (uploadResult.url) {
link.href = uploadResult.url;
link.download = `nano-banana-${Date.now()}.png`;
link.target = '_blank';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
});
// 立即返回
@@ -571,6 +581,12 @@ export const ImageCanvas: React.FC = () => {
<div className="text-center bg-white/90 rounded-xl p-6 card-lg backdrop-blur-sm animate-in scale-in duration-200">
<div className="animate-spin rounded-full h-10 w-10 border-2 border-yellow-400 border-t-transparent mx-auto mb-3" />
<p className="text-gray-700 text-sm font-medium">...</p>
{/* 显示重试次数 */}
{isContinuousGenerating && (
<p className="text-gray-500 text-xs mt-2">
: {retryCount}
</p>
)}
</div>
</div>
)}
@@ -592,8 +608,8 @@ export const ImageCanvas: React.FC = () => {
}
}}
onMouseDown={handleMouseDown}
onMousemove={handleMouseMove}
onMouseup={handleMouseUp}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
style={{
cursor: selectedTool === 'mask' ? 'crosshair' : 'default',
zIndex: 10