You've already forked Nano-Banana-AI-Image-Editor
优化 调整了整体的UI界面
This commit is contained in:
@@ -156,43 +156,46 @@ export const PromptComposer: React.FC = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-80 lg:w-72 xl:w-80 h-full bg-white border-r border-gray-200 p-6 flex flex-col space-y-6 overflow-y-auto">
|
||||
<div className="w-72 h-full bg-white border-r border-gray-200 p-4 flex flex-col space-y-5 overflow-y-auto">
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h3 className="text-sm font-medium text-gray-300">模式</h3>
|
||||
<div className="flex items-center space-x-1">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h3 className="text-xs font-medium text-gray-500 uppercase tracking-wide">模式</h3>
|
||||
<div className="flex items-center space-x-0.5">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => setShowHintsModal(true)}
|
||||
className="h-6 w-6"
|
||||
className="h-6 w-6 text-gray-400 hover:text-gray-600 hover:bg-gray-100"
|
||||
>
|
||||
<HelpCircle className="h-4 w-4" />
|
||||
<HelpCircle className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => setShowPromptPanel(false)}
|
||||
className="h-6 w-6"
|
||||
title="隐藏提示面板"
|
||||
className="h-6 w-6 text-gray-400 hover:text-gray-600 hover:bg-gray-100"
|
||||
title="隐藏面板"
|
||||
>
|
||||
×
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
<div className="grid grid-cols-3 gap-1.5">
|
||||
{tools.map((tool) => (
|
||||
<button
|
||||
key={tool.id}
|
||||
onClick={() => setSelectedTool(tool.id)}
|
||||
className={cn(
|
||||
'flex flex-col items-center p-3 rounded-lg border transition-all duration-200',
|
||||
'flex flex-col items-center p-2 rounded-lg border transition-all duration-200',
|
||||
selectedTool === tool.id
|
||||
? 'bg-yellow-400/10 border-yellow-400/50 text-yellow-400'
|
||||
: 'bg-white border-yellow-400/50 text-gray-400 hover:bg-yellow-400 hover:text-white'
|
||||
? 'bg-yellow-50 border-yellow-300 text-yellow-700 shadow-sm'
|
||||
: 'bg-gray-50 border-gray-200 text-gray-500 hover:bg-yellow-50 hover:border-yellow-300 hover:text-yellow-700'
|
||||
)}
|
||||
>
|
||||
<tool.icon className="h-5 w-5 mb-1" />
|
||||
<tool.icon className="h-4 w-4 mb-1" />
|
||||
<span className="text-xs font-medium">{tool.label}</span>
|
||||
</button>
|
||||
))}
|
||||
@@ -206,7 +209,7 @@ export const PromptComposer: React.FC = () => {
|
||||
onDragLeave={handleDragLeave}
|
||||
onDrop={handleDrop}
|
||||
className={cn(
|
||||
"border-2 border-dashed rounded-lg p-6 text-center transition-colors",
|
||||
"border border-dashed rounded-md p-4 text-center transition-colors",
|
||||
isDragOver
|
||||
? "border-yellow-400 bg-yellow-400/10"
|
||||
: "border-gray-300 hover:border-yellow-400"
|
||||
@@ -216,13 +219,13 @@ export const PromptComposer: React.FC = () => {
|
||||
{selectedTool === 'generate' ? '参考图像' : selectedTool === 'edit' ? '样式参考' : '上传图像'}
|
||||
</label>
|
||||
{selectedTool === 'mask' && (
|
||||
<p className="text-xs text-gray-500 mb-3">使用遮罩编辑图像</p>
|
||||
<p className="text-xs text-gray-500 mb-2">使用遮罩编辑图像</p>
|
||||
)}
|
||||
{selectedTool === 'generate' && (
|
||||
<p className="text-xs text-gray-500 mb-3">可选,最多2张图像</p>
|
||||
<p className="text-xs text-gray-500 mb-2">可选,最多2张图像</p>
|
||||
)}
|
||||
{selectedTool === 'edit' && (
|
||||
<p className="text-xs text-gray-500 mb-3">
|
||||
<p className="text-xs text-gray-500 mb-2">
|
||||
{canvasImage ? '可选样式参考,最多2张图像' : '上传要编辑的图像,最多2张图像'}
|
||||
</p>
|
||||
)}
|
||||
@@ -235,29 +238,30 @@ export const PromptComposer: React.FC = () => {
|
||||
className="hidden"
|
||||
/>
|
||||
|
||||
<div className="flex flex-col items-center justify-center space-y-3">
|
||||
<Upload className={cn("h-8 w-8", isDragOver ? "text-yellow-500" : "text-gray-400")} />
|
||||
<div className="flex flex-col items-center justify-center space-y-2">
|
||||
<Upload className={cn("h-6 w-6", isDragOver ? "text-yellow-500" : "text-gray-400")} />
|
||||
<div>
|
||||
<p className={cn(
|
||||
"text-sm font-medium",
|
||||
isDragOver ? "text-yellow-700" : "text-gray-600"
|
||||
)}>
|
||||
{isDragOver ? "释放文件以上传" : "拖拽图像到此处或点击上传"}
|
||||
{isDragOver ? "释放文件以上传" : "拖拽或点击上传"}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
支持 JPG, PNG, GIF 格式
|
||||
支持 JPG, PNG, GIF
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
className="mt-2"
|
||||
className="mt-1"
|
||||
disabled={
|
||||
(selectedTool === 'generate' && uploadedImages.length >= 2) ||
|
||||
(selectedTool === 'edit' && editReferenceImages.length >= 2)
|
||||
}
|
||||
>
|
||||
<Upload className="h-4 w-4 mr-2" />
|
||||
<Upload className="h-3 w-3 mr-1" />
|
||||
选择文件
|
||||
</Button>
|
||||
</div>
|
||||
@@ -266,13 +270,13 @@ export const PromptComposer: React.FC = () => {
|
||||
{/* Show uploaded images preview */}
|
||||
{((selectedTool === 'generate' && uploadedImages.length > 0) ||
|
||||
(selectedTool === 'edit' && editReferenceImages.length > 0)) && (
|
||||
<div className="mt-3 space-y-2">
|
||||
<div className="mt-2 space-y-2">
|
||||
{(selectedTool === 'generate' ? uploadedImages : editReferenceImages).map((image, index) => (
|
||||
<div key={index} className="relative">
|
||||
<img
|
||||
src={image}
|
||||
alt={`参考图像 ${index + 1}`}
|
||||
className="w-full h-20 object-cover rounded-lg border border-gray-300"
|
||||
className="w-full h-16 object-cover rounded border border-gray-300"
|
||||
/>
|
||||
<button
|
||||
onClick={() => selectedTool === 'generate' ? removeUploadedImage(index) : removeEditReferenceImage(index)}
|
||||
@@ -280,7 +284,7 @@ export const PromptComposer: React.FC = () => {
|
||||
>
|
||||
×
|
||||
</button>
|
||||
<div className="absolute bottom-1 left-1 bg-gray-100/80 text-xs px-2 py-1 rounded text-gray-700">
|
||||
<div className="absolute bottom-1 left-1 bg-gray-100/80 text-xs px-1 py-0.5 rounded text-gray-700">
|
||||
参考 {index + 1}
|
||||
</div>
|
||||
</div>
|
||||
@@ -291,36 +295,36 @@ export const PromptComposer: React.FC = () => {
|
||||
|
||||
{/* 提示输入 */}
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 mb-3 block">
|
||||
{selectedTool === 'generate' ? '描述您想要创建的内容' : '描述您的修改'}
|
||||
<label className="text-xs font-medium text-gray-500 mb-2 block uppercase tracking-wide">
|
||||
{selectedTool === 'generate' ? '提示词' : '编辑指令'}
|
||||
</label>
|
||||
<Textarea
|
||||
value={currentPrompt}
|
||||
onChange={(e) => setCurrentPrompt(e.target.value)}
|
||||
placeholder={
|
||||
selectedTool === 'generate'
|
||||
? '宁静的山景日落,湖面倒映着金色的天空...'
|
||||
: '让天空更加戏剧化,添加暴风云...'
|
||||
? '描述您想要创建的内容...'
|
||||
: '描述您想要的修改...'
|
||||
}
|
||||
className="min-h-[120px] resize-none"
|
||||
className="min-h-[100px] resize-none text-sm"
|
||||
/>
|
||||
|
||||
{/* 提示质量指示器 */}
|
||||
<button
|
||||
onClick={() => setShowHintsModal(true)}
|
||||
className="mt-2 flex items-center text-xs hover:text-gray-600 transition-colors group"
|
||||
className="mt-2 flex items-center text-xs hover:text-gray-700 transition-colors group"
|
||||
>
|
||||
{currentPrompt.length < 20 ? (
|
||||
<HelpCircle className="h-3 w-3 mr-2 text-red-500 group-hover:text-red-400" />
|
||||
<HelpCircle className="h-3 w-3 mr-2 text-red-400 group-hover:text-red-500" />
|
||||
) : (
|
||||
<div className={cn(
|
||||
'h-2 w-2 rounded-full mr-2',
|
||||
currentPrompt.length < 50 ? 'bg-yellow-500' : 'bg-green-500'
|
||||
currentPrompt.length < 50 ? 'bg-yellow-400' : 'bg-green-400'
|
||||
)} />
|
||||
)}
|
||||
<span className="text-gray-600 group-hover:text-gray-800">
|
||||
{currentPrompt.length < 20 ? '添加更多细节以获得更好的结果' :
|
||||
currentPrompt.length < 50 ? '细节水平良好' : '提示细节优秀'}
|
||||
<span className="text-gray-500 group-hover:text-gray-700">
|
||||
{currentPrompt.length < 20 ? '添加更多细节' :
|
||||
currentPrompt.length < 50 ? '细节良好' : '细节优秀'}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -331,9 +335,9 @@ export const PromptComposer: React.FC = () => {
|
||||
<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"
|
||||
className="flex-1 h-12 text-sm font-medium bg-red-500 hover:bg-red-600 rounded-lg"
|
||||
>
|
||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-gray-900 mr-2" />
|
||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2" />
|
||||
中断
|
||||
</Button>
|
||||
</div>
|
||||
@@ -341,63 +345,30 @@ export const PromptComposer: React.FC = () => {
|
||||
<Button
|
||||
onClick={handleGenerate}
|
||||
disabled={!currentPrompt.trim()}
|
||||
className="w-full h-14 text-base font-medium"
|
||||
className="w-full h-12 text-sm font-medium rounded-lg shadow-sm hover:shadow-md transition-shadow"
|
||||
>
|
||||
<Wand2 className="h-4 w-4 mr-2" />
|
||||
{selectedTool === 'generate' ? '生成' : '应用编辑'}
|
||||
{selectedTool === 'generate' ? '生成图像' : '应用编辑'}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* 高级控制 */}
|
||||
<div>
|
||||
<div className="pt-2 border-t border-gray-100">
|
||||
<button
|
||||
onClick={() => setShowAdvanced(!showAdvanced)}
|
||||
className="flex items-center text-sm text-gray-600 hover:text-gray-800 transition-colors duration-200"
|
||||
className="flex items-center text-xs text-gray-500 hover:text-gray-700 transition-colors duration-200"
|
||||
>
|
||||
{showAdvanced ? <ChevronDown className="h-4 w-4 mr-1" /> : <ChevronRight className="h-4 w-4 mr-1" />}
|
||||
{showAdvanced ? '隐藏' : '显示'} 高级控制
|
||||
{showAdvanced ? <ChevronDown className="h-3 w-3 mr-1" /> : <ChevronRight className="h-3 w-3 mr-1" />}
|
||||
高级选项
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => setShowClearConfirm(!showClearConfirm)}
|
||||
className="flex items-center text-sm text-gray-600 hover:text-red-500 transition-colors duration-200 mt-2"
|
||||
>
|
||||
<RotateCcw className="h-4 w-4 mr-2" />
|
||||
清除会话
|
||||
</button>
|
||||
|
||||
{showClearConfirm && (
|
||||
<div className="mt-3 p-3 bg-gray-100 rounded-lg border border-gray-300">
|
||||
<p className="text-xs text-gray-600 mb-3">
|
||||
您确定要清除此会话吗?这将删除所有上传、提示和画布内容。
|
||||
</p>
|
||||
<div className="flex space-x-2">
|
||||
<Button
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
onClick={handleClearSession}
|
||||
className="flex-1"
|
||||
>
|
||||
确认清除
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setShowClearConfirm(false)}
|
||||
className="flex-1"
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showAdvanced && (
|
||||
<div className="mt-4 space-y-4">
|
||||
<div className="mt-3 space-y-3">
|
||||
{/* 创造力 */}
|
||||
<div>
|
||||
<label className="text-xs text-gray-600 mb-2 block">
|
||||
创造力 ({temperature})
|
||||
<label className="text-xs text-gray-500 mb-1.5 block flex justify-between">
|
||||
<span>创造力</span>
|
||||
<span className="font-mono">{temperature}</span>
|
||||
</label>
|
||||
<input
|
||||
type="range"
|
||||
@@ -406,13 +377,13 @@ export const PromptComposer: React.FC = () => {
|
||||
step="0.1"
|
||||
value={temperature}
|
||||
onChange={(e) => setTemperature(parseFloat(e.target.value))}
|
||||
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer slider"
|
||||
className="w-full h-1.5 bg-gray-200 rounded-full appearance-none cursor-pointer slider"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 种子 */}
|
||||
<div>
|
||||
<label className="text-xs text-gray-600 mb-2 block">
|
||||
<label className="text-xs text-gray-500 mb-1.5 block">
|
||||
种子 (可选)
|
||||
</label>
|
||||
<input
|
||||
@@ -420,36 +391,70 @@ export const PromptComposer: React.FC = () => {
|
||||
value={seed || ''}
|
||||
onChange={(e) => setSeed(e.target.value ? parseInt(e.target.value) : null)}
|
||||
placeholder="随机"
|
||||
className="w-full h-8 px-2 bg-white border border-gray-300 rounded text-xs text-gray-900"
|
||||
className="w-full h-8 px-2.5 bg-gray-50 border border-gray-200 rounded-md text-xs text-gray-700 focus:outline-none focus:ring-1 focus:ring-yellow-400"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={() => setShowClearConfirm(!showClearConfirm)}
|
||||
className="flex items-center text-xs text-gray-500 hover:text-red-500 transition-colors duration-200 mt-3"
|
||||
>
|
||||
<RotateCcw className="h-3 w-3 mr-1.5" />
|
||||
清除会话
|
||||
</button>
|
||||
|
||||
{showClearConfirm && (
|
||||
<div className="mt-2 p-3 bg-red-50 rounded-lg border border-red-100">
|
||||
<p className="text-xs text-red-700 mb-3">
|
||||
确定要清除此会话吗?这将删除所有内容。
|
||||
</p>
|
||||
<div className="flex space-x-2">
|
||||
<Button
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
onClick={handleClearSession}
|
||||
className="flex-1 h-8 text-xs"
|
||||
>
|
||||
确认
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setShowClearConfirm(false)}
|
||||
className="flex-1 h-8 text-xs border-gray-200"
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 键盘快捷键 */}
|
||||
<div className="pt-4 border-t border-gray-200">
|
||||
<h4 className="text-xs font-medium text-gray-600 mb-2">快捷键</h4>
|
||||
<div className="space-y-1 text-xs text-gray-700">
|
||||
<div className="pt-3 border-t border-gray-100">
|
||||
<h4 className="text-xs font-medium text-gray-500 mb-2 uppercase tracking-wide">快捷键</h4>
|
||||
<div className="space-y-1.5 text-xs text-gray-600">
|
||||
<div className="flex justify-between">
|
||||
<span>生成</span>
|
||||
<span>⌘ + Enter</span>
|
||||
<span className="text-gray-500">生成</span>
|
||||
<span className="font-mono bg-gray-100 px-1.5 py-0.5 rounded text-gray-700">⌘ + Enter</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>重新生成</span>
|
||||
<span>⇧ + R</span>
|
||||
<span className="text-gray-500">重新生成</span>
|
||||
<span className="font-mono bg-gray-100 px-1.5 py-0.5 rounded text-gray-700">⇧ + R</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>编辑模式</span>
|
||||
<span>E</span>
|
||||
<span className="text-gray-500">编辑模式</span>
|
||||
<span className="font-mono bg-gray-100 px-1.5 py-0.5 rounded text-gray-700">E</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>历史记录</span>
|
||||
<span>H</span>
|
||||
<span className="text-gray-500">历史记录</span>
|
||||
<span className="font-mono bg-gray-100 px-1.5 py-0.5 rounded text-gray-700">H</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>切换面板</span>
|
||||
<span>P</span>
|
||||
<span className="text-gray-500">切换面板</span>
|
||||
<span className="font-mono bg-gray-100 px-1.5 py-0.5 rounded text-gray-700">P</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user