You've already forked Nano-Banana-AI-Image-Editor
新增 生成过程中可以中断;
新增 生成结果上传到OSS; 新增 历史记录使用上传后的图片;
This commit is contained in:
@@ -39,6 +39,18 @@ export const HistoryPanel: React.FC = () => {
|
||||
const generations = currentProject?.generations || [];
|
||||
const edits = currentProject?.edits || [];
|
||||
|
||||
// 获取上传后的图片链接
|
||||
const getUploadedImageUrl = (generationOrEdit: any, index: number) => {
|
||||
if (generationOrEdit.uploadResults && generationOrEdit.uploadResults[index]) {
|
||||
const uploadResult = generationOrEdit.uploadResults[index];
|
||||
if (uploadResult.success && uploadResult.url) {
|
||||
// 添加参数以降低图片质量
|
||||
return `${uploadResult.url}?x-oss-process=image/quality,q_50`;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// 获取当前图像尺寸
|
||||
const [imageDimensions, setImageDimensions] = React.useState<{ width: number; height: number } | null>(null);
|
||||
|
||||
@@ -184,6 +196,13 @@ export const HistoryPanel: React.FC = () => {
|
||||
>
|
||||
{generation.outputAssetsBlobUrls && generation.outputAssetsBlobUrls.length > 0 ? (
|
||||
(() => {
|
||||
// 首先尝试使用上传后的图片链接
|
||||
const uploadedUrl = getUploadedImageUrl(generation, 0);
|
||||
if (uploadedUrl) {
|
||||
return <img src={uploadedUrl} alt="生成的变体" className="w-full h-full object-cover" />;
|
||||
}
|
||||
|
||||
// 如果没有上传链接,则使用原来的Blob URL
|
||||
const blobUrl = generation.outputAssetsBlobUrls[0];
|
||||
const decodedUrl = decodedImages[blobUrl];
|
||||
if (decodedUrl) {
|
||||
@@ -239,6 +258,13 @@ export const HistoryPanel: React.FC = () => {
|
||||
>
|
||||
{edit.outputAssetsBlobUrls && edit.outputAssetsBlobUrls.length > 0 ? (
|
||||
(() => {
|
||||
// 首先尝试使用上传后的图片链接
|
||||
const uploadedUrl = getUploadedImageUrl(edit, 0);
|
||||
if (uploadedUrl) {
|
||||
return <img src={uploadedUrl} alt="编辑的变体" className="w-full h-full object-cover" />;
|
||||
}
|
||||
|
||||
// 如果没有上传链接,则使用原来的Blob URL
|
||||
const blobUrl = edit.outputAssetsBlobUrls[0];
|
||||
const decodedUrl = decodedImages[blobUrl];
|
||||
if (decodedUrl) {
|
||||
@@ -319,6 +345,35 @@ export const HistoryPanel: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 上传结果 */}
|
||||
{gen.uploadResults && gen.uploadResults.length > 0 && (
|
||||
<div>
|
||||
<h5 className="text-xs font-medium text-gray-500 mb-2">上传结果</h5>
|
||||
<div className="space-y-1">
|
||||
{gen.uploadResults.map((result, index) => (
|
||||
<div key={index} className="text-xs">
|
||||
<div className="flex justify-between">
|
||||
<span>图像 {index + 1}:</span>
|
||||
<span className={result.success ? 'text-green-600' : 'text-red-600'}>
|
||||
{result.success ? '成功' : '失败'}
|
||||
</span>
|
||||
</div>
|
||||
{result.success && result.url && (
|
||||
<div className="text-blue-600 truncate">
|
||||
{result.url.split('/').pop()}
|
||||
</div>
|
||||
)}
|
||||
{result.error && (
|
||||
<div className="text-red-600 truncate">
|
||||
{result.error}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 参考图像信息 */}
|
||||
{gen.sourceAssets.length > 0 && (
|
||||
<div>
|
||||
@@ -355,6 +410,35 @@ export const HistoryPanel: React.FC = () => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 上传结果 */}
|
||||
{selectedEdit.uploadResults && selectedEdit.uploadResults.length > 0 && (
|
||||
<div>
|
||||
<h5 className="text-xs font-medium text-gray-500 mb-2">上传结果</h5>
|
||||
<div className="space-y-1">
|
||||
{selectedEdit.uploadResults.map((result, index) => (
|
||||
<div key={index} className="text-xs">
|
||||
<div className="flex justify-between">
|
||||
<span>图像 {index + 1}:</span>
|
||||
<span className={result.success ? 'text-green-600' : 'text-red-600'}>
|
||||
{result.success ? '成功' : '失败'}
|
||||
</span>
|
||||
</div>
|
||||
{result.success && result.url && (
|
||||
<div className="text-blue-600 truncate">
|
||||
{result.url.split('/').pop()}
|
||||
</div>
|
||||
)}
|
||||
{result.error && (
|
||||
<div className="text-red-600 truncate">
|
||||
{result.error}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 原始生成参考 */}
|
||||
{parentGen && (
|
||||
<div>
|
||||
|
||||
@@ -34,8 +34,8 @@ export const PromptComposer: React.FC = () => {
|
||||
clearBrushStrokes,
|
||||
} = useAppStore();
|
||||
|
||||
const { generate } = useImageGeneration();
|
||||
const { edit } = useImageEditing();
|
||||
const { generate, cancelGeneration } = useImageGeneration();
|
||||
const { edit, cancelEdit } = useImageEditing();
|
||||
const [showAdvanced, setShowAdvanced] = useState(false);
|
||||
const [showClearConfirm, setShowClearConfirm] = useState(false);
|
||||
const [showHintsModal, setShowHintsModal] = useState(false);
|
||||
@@ -327,23 +327,26 @@ export const PromptComposer: React.FC = () => {
|
||||
|
||||
|
||||
{/* 生成按钮 */}
|
||||
<Button
|
||||
onClick={handleGenerate}
|
||||
disabled={isGenerating || !currentPrompt.trim()}
|
||||
className="w-full h-14 text-base font-medium"
|
||||
>
|
||||
{isGenerating ? (
|
||||
<>
|
||||
{isGenerating ? (
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
onClick={() => selectedTool === 'generate' ? cancelGeneration() : cancelEdit()}
|
||||
className="flex-1 h-14 text-base font-medium bg-red-500 hover:bg-red-600"
|
||||
>
|
||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-gray-900 mr-2" />
|
||||
生成中...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Wand2 className="h-4 w-4 mr-2" />
|
||||
{selectedTool === 'generate' ? '生成' : '应用编辑'}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
中断
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
onClick={handleGenerate}
|
||||
disabled={!currentPrompt.trim()}
|
||||
className="w-full h-14 text-base font-medium"
|
||||
>
|
||||
<Wand2 className="h-4 w-4 mr-2" />
|
||||
{selectedTool === 'generate' ? '生成' : '应用编辑'}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* 高级控制 */}
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user