You've already forked Nano-Banana-AI-Image-Editor
初始化提交
This commit is contained in:
@@ -34,7 +34,7 @@ export const HistoryPanel: React.FC = () => {
|
||||
const generations = currentProject?.generations || [];
|
||||
const edits = currentProject?.edits || [];
|
||||
|
||||
// Get current image dimensions
|
||||
// 获取当前图像尺寸
|
||||
const [imageDimensions, setImageDimensions] = React.useState<{ width: number; height: number } | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
@@ -51,11 +51,11 @@ export const HistoryPanel: React.FC = () => {
|
||||
|
||||
if (!showHistory) {
|
||||
return (
|
||||
<div className="w-8 bg-gray-950 border-l border-gray-800 flex flex-col items-center justify-center">
|
||||
<div className="w-8 bg-white border-l border-gray-200 flex flex-col items-center justify-center">
|
||||
<button
|
||||
onClick={() => setShowHistory(true)}
|
||||
className="w-6 h-16 bg-gray-800 hover:bg-gray-700 rounded-l-lg border border-r-0 border-gray-700 flex items-center justify-center transition-colors group"
|
||||
title="Show History Panel"
|
||||
className="w-6 h-16 bg-gray-100 hover:bg-gray-200 rounded-l-lg border border-r-0 border-gray-300 flex items-center justify-center transition-colors group"
|
||||
title="显示历史面板"
|
||||
>
|
||||
<div className="flex flex-col space-y-1">
|
||||
<div className="w-1 h-1 bg-gray-500 group-hover:bg-gray-400 rounded-full"></div>
|
||||
@@ -68,35 +68,35 @@ export const HistoryPanel: React.FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-80 bg-gray-950 border-l border-gray-800 p-6 flex flex-col h-full">
|
||||
{/* Header */}
|
||||
<div className="w-80 bg-white border-l border-gray-200 p-6 flex flex-col h-full">
|
||||
{/* 头部 */}
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div className="flex items-center space-x-2">
|
||||
<History className="h-5 w-5 text-gray-400" />
|
||||
<h3 className="text-sm font-medium text-gray-300">History & Variants</h3>
|
||||
<h3 className="text-sm font-medium text-gray-300">历史记录和变体</h3>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => setShowHistory(!showHistory)}
|
||||
className="h-6 w-6"
|
||||
title="Hide History Panel"
|
||||
title="隐藏历史面板"
|
||||
>
|
||||
×
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Variants Grid */}
|
||||
{/* 变体网格 */}
|
||||
<div className="mb-6 flex-shrink-0">
|
||||
<h4 className="text-xs font-medium text-gray-400 mb-3">Current Variants</h4>
|
||||
<h4 className="text-xs font-medium text-gray-400 mb-3">当前变体</h4>
|
||||
{generations.length === 0 && edits.length === 0 ? (
|
||||
<div className="text-center py-8">
|
||||
<div className="text-4xl mb-2">🖼️</div>
|
||||
<p className="text-sm text-gray-500">No generations yet</p>
|
||||
<p className="text-sm text-gray-500">暂无生成记录</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
{/* Show generations */}
|
||||
{/* 显示生成记录 */}
|
||||
{generations.slice(-2).map((generation, index) => (
|
||||
<div
|
||||
key={generation.id}
|
||||
@@ -117,7 +117,7 @@ export const HistoryPanel: React.FC = () => {
|
||||
<>
|
||||
<img
|
||||
src={generation.outputAssets[0].url}
|
||||
alt="Generated variant"
|
||||
alt="生成的变体"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</>
|
||||
@@ -127,14 +127,14 @@ export const HistoryPanel: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Variant Number */}
|
||||
{/* 变体编号 */}
|
||||
<div className="absolute top-2 left-2 bg-gray-900/80 text-xs px-2 py-1 rounded">
|
||||
#{index + 1}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Show edits */}
|
||||
{/* 显示编辑记录 */}
|
||||
{edits.slice(-2).map((edit, index) => (
|
||||
<div
|
||||
key={edit.id}
|
||||
@@ -155,7 +155,7 @@ export const HistoryPanel: React.FC = () => {
|
||||
{edit.outputAssets[0] ? (
|
||||
<img
|
||||
src={edit.outputAssets[0].url}
|
||||
alt="Edited variant"
|
||||
alt="编辑的变体"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
@@ -164,9 +164,9 @@ export const HistoryPanel: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Edit Label */}
|
||||
{/* 编辑标签 */}
|
||||
<div className="absolute top-2 left-2 bg-purple-900/80 text-xs px-2 py-1 rounded">
|
||||
Edit #{index + 1}
|
||||
编辑 #{index + 1}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -174,28 +174,28 @@ export const HistoryPanel: React.FC = () => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Current Image Info */}
|
||||
{/* 当前图像信息 */}
|
||||
{(canvasImage || imageDimensions) && (
|
||||
<div className="mb-4 p-3 bg-gray-900 rounded-lg border border-gray-700">
|
||||
<h4 className="text-xs font-medium text-gray-400 mb-2">Current Image</h4>
|
||||
<div className="mb-4 p-3 bg-gray-100 rounded-lg border border-gray-300">
|
||||
<h4 className="text-xs font-medium text-gray-400 mb-2">当前图像</h4>
|
||||
<div className="space-y-1 text-xs text-gray-500">
|
||||
{imageDimensions && (
|
||||
<div className="flex justify-between">
|
||||
<span>Dimensions:</span>
|
||||
<span>尺寸:</span>
|
||||
<span className="text-gray-300">{imageDimensions.width} × {imageDimensions.height}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-between">
|
||||
<span>Mode:</span>
|
||||
<span>模式:</span>
|
||||
<span className="text-gray-300 capitalize">{selectedTool}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Generation Details */}
|
||||
<div className="mb-6 p-4 bg-gray-900 rounded-lg border border-gray-700 flex-1 overflow-y-auto min-h-0">
|
||||
<h4 className="text-xs font-medium text-gray-400 mb-2">Generation Details</h4>
|
||||
{/* 生成详情 */}
|
||||
<div className="mb-6 p-4 bg-gray-100 rounded-lg border border-gray-300 flex-1 overflow-y-auto min-h-0">
|
||||
<h4 className="text-xs font-medium text-gray-400 mb-2">生成详情</h4>
|
||||
{(() => {
|
||||
const gen = generations.find(g => g.id === selectedGenerationId);
|
||||
const selectedEdit = edits.find(e => e.id === selectedEditId);
|
||||
@@ -205,25 +205,25 @@ export const HistoryPanel: React.FC = () => {
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-2 text-xs text-gray-500">
|
||||
<div>
|
||||
<span className="text-gray-400">Prompt:</span>
|
||||
<span className="text-gray-400">提示:</span>
|
||||
<p className="text-gray-300 mt-1">{gen.prompt}</p>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>Model:</span>
|
||||
<span>模型:</span>
|
||||
<span>{gen.modelVersion}</span>
|
||||
</div>
|
||||
{gen.parameters.seed && (
|
||||
<div className="flex justify-between">
|
||||
<span>Seed:</span>
|
||||
<span>种子:</span>
|
||||
<span>{gen.parameters.seed}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Reference Images */}
|
||||
{/* 参考图像 */}
|
||||
{gen.sourceAssets.length > 0 && (
|
||||
<div>
|
||||
<h5 className="text-xs font-medium text-gray-400 mb-2">Reference Images</h5>
|
||||
<h5 className="text-xs font-medium text-gray-400 mb-2">参考图像</h5>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{gen.sourceAssets.map((asset, index) => (
|
||||
<button
|
||||
@@ -231,21 +231,21 @@ export const HistoryPanel: React.FC = () => {
|
||||
onClick={() => setPreviewModal({
|
||||
open: true,
|
||||
imageUrl: asset.url,
|
||||
title: `Reference Image ${index + 1}`,
|
||||
description: 'This reference image was used to guide the generation'
|
||||
title: `参考图像 ${index + 1}`,
|
||||
description: '此参考图像用于指导生成'
|
||||
})}
|
||||
className="relative aspect-square rounded border border-gray-700 hover:border-gray-600 transition-colors overflow-hidden group"
|
||||
>
|
||||
<img
|
||||
src={asset.url}
|
||||
alt={`Reference ${index + 1}`}
|
||||
alt={`参考 ${index + 1}`}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/20 transition-colors flex items-center justify-center">
|
||||
<ImageIcon className="h-4 w-4 text-white opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</div>
|
||||
<div className="absolute bottom-1 left-1 bg-gray-900/80 text-xs px-1 py-0.5 rounded text-gray-300">
|
||||
Ref {index + 1}
|
||||
参考 {index + 1}
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
@@ -260,41 +260,41 @@ export const HistoryPanel: React.FC = () => {
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-2 text-xs text-gray-500">
|
||||
<div>
|
||||
<span className="text-gray-400">Edit Instruction:</span>
|
||||
<span className="text-gray-400">编辑指令:</span>
|
||||
<p className="text-gray-300 mt-1">{selectedEdit.instruction}</p>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>Type:</span>
|
||||
<span>Image Edit</span>
|
||||
<span>类型:</span>
|
||||
<span>图像编辑</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>Created:</span>
|
||||
<span>创建时间:</span>
|
||||
<span>{new Date(selectedEdit.timestamp).toLocaleTimeString()}</span>
|
||||
</div>
|
||||
{selectedEdit.maskAssetId && (
|
||||
<div className="flex justify-between">
|
||||
<span>Mask:</span>
|
||||
<span className="text-purple-400">Applied</span>
|
||||
<span>遮罩:</span>
|
||||
<span className="text-purple-400">已应用</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Parent Generation Reference */}
|
||||
{/* 原始生成参考 */}
|
||||
{parentGen && (
|
||||
<div>
|
||||
<h5 className="text-xs font-medium text-gray-400 mb-2">Original Image</h5>
|
||||
<h5 className="text-xs font-medium text-gray-400 mb-2">原始图像</h5>
|
||||
<button
|
||||
onClick={() => setPreviewModal({
|
||||
open: true,
|
||||
imageUrl: parentGen.outputAssets[0]?.url || '',
|
||||
title: 'Original Image',
|
||||
description: 'The base image that was edited'
|
||||
title: '原始图像',
|
||||
description: '被编辑的基础图像'
|
||||
})}
|
||||
className="relative aspect-square w-16 rounded border border-gray-700 hover:border-gray-600 transition-colors overflow-hidden group"
|
||||
>
|
||||
<img
|
||||
src={parentGen.outputAssets[0]?.url}
|
||||
alt="Original"
|
||||
alt="原始"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/20 transition-colors flex items-center justify-center">
|
||||
@@ -304,29 +304,29 @@ export const HistoryPanel: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Mask Visualization */}
|
||||
{/* 遮罩可视化 */}
|
||||
{selectedEdit.maskReferenceAsset && (
|
||||
<div>
|
||||
<h5 className="text-xs font-medium text-gray-400 mb-2">Masked Reference</h5>
|
||||
<h5 className="text-xs font-medium text-gray-400 mb-2">遮罩参考</h5>
|
||||
<button
|
||||
onClick={() => setPreviewModal({
|
||||
open: true,
|
||||
imageUrl: selectedEdit.maskReferenceAsset!.url,
|
||||
title: 'Masked Reference Image',
|
||||
description: 'This image with mask overlay was sent to the AI model to guide the edit'
|
||||
title: '遮罩参考图像',
|
||||
description: '带有遮罩叠加的图像被发送到AI模型以指导编辑'
|
||||
})}
|
||||
className="relative aspect-square w-16 rounded border border-gray-700 hover:border-gray-600 transition-colors overflow-hidden group"
|
||||
>
|
||||
<img
|
||||
src={selectedEdit.maskReferenceAsset.url}
|
||||
alt="Masked reference"
|
||||
alt="遮罩参考"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/20 transition-colors flex items-center justify-center">
|
||||
<ImageIcon className="h-3 w-3 text-white opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</div>
|
||||
<div className="absolute bottom-1 left-1 bg-purple-900/80 text-xs px-1 py-0.5 rounded text-purple-300">
|
||||
Mask
|
||||
遮罩
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
@@ -336,34 +336,34 @@ export const HistoryPanel: React.FC = () => {
|
||||
} else {
|
||||
return (
|
||||
<div className="space-y-2 text-xs text-gray-500">
|
||||
<p className="text-gray-400">Select a generation or edit to view details</p>
|
||||
<p className="text-gray-400">选择一个生成记录或编辑记录以查看详细信息</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})()}
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
{/* 操作 */}
|
||||
<div className="space-y-3 flex-shrink-0">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="w-full"
|
||||
onClick={() => {
|
||||
// Find the currently displayed image (either generation or edit)
|
||||
// 查找当前显示的图像(生成记录或编辑记录)
|
||||
let imageUrl: string | null = null;
|
||||
|
||||
if (selectedGenerationId) {
|
||||
const gen = generations.find(g => g.id === selectedGenerationId);
|
||||
imageUrl = gen?.outputAssets[0]?.url || null;
|
||||
} else {
|
||||
// If no generation selected, try to get the current canvas image
|
||||
// 如果没有选择生成记录,尝试获取当前画布图像
|
||||
const { canvasImage } = useAppStore.getState();
|
||||
imageUrl = canvasImage;
|
||||
}
|
||||
|
||||
if (imageUrl) {
|
||||
// Handle both data URLs and regular URLs
|
||||
// 处理数据URL和常规URL
|
||||
if (imageUrl.startsWith('data:')) {
|
||||
const link = document.createElement('a');
|
||||
link.href = imageUrl;
|
||||
@@ -372,7 +372,7 @@ export const HistoryPanel: React.FC = () => {
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
} else {
|
||||
// For external URLs, we need to fetch and convert to blob
|
||||
// 对于外部URL,我们需要获取并转换为blob
|
||||
fetch(imageUrl)
|
||||
.then(response => response.blob())
|
||||
.then(blob => {
|
||||
@@ -391,11 +391,11 @@ export const HistoryPanel: React.FC = () => {
|
||||
disabled={!selectedGenerationId && !useAppStore.getState().canvasImage}
|
||||
>
|
||||
<Download className="h-4 w-4 mr-2" />
|
||||
Download
|
||||
下载
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Image Preview Modal */}
|
||||
{/* 图像预览模态框 */}
|
||||
<ImagePreviewModal
|
||||
open={previewModal.open}
|
||||
onOpenChange={(open) => setPreviewModal(prev => ({ ...prev, open }))}
|
||||
|
||||
Reference in New Issue
Block a user