fix: Resolve JSX syntax error in PromptHero.tsx and restore mobile toolbar layout
This commit is contained in:
parent
d8cb0fb176
commit
e784d89873
1 changed files with 182 additions and 120 deletions
|
|
@ -538,12 +538,74 @@ export function PromptHero() {
|
|||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{/* Controls Area */}
|
||||
<div className="flex flex-col md:flex-row items-center justify-between gap-3 pt-1">
|
||||
|
||||
{/* Hidden file inputs for upload */ }
|
||||
{/* Left Controls: References */}
|
||||
{/* For Meta AI: Only Subject is enabled (for video generation), Scene/Style disabled */}
|
||||
<div className="flex items-center gap-2 w-full md:w-auto overflow-x-auto md:overflow-visible pb-2 md:pb-0 scrollbar-none">
|
||||
{((settings.provider === 'meta'
|
||||
? ['subject']
|
||||
: ['subject', 'scene', 'style']) as ReferenceCategory[]).map((cat) => {
|
||||
const refs = references[cat] || [];
|
||||
const hasRefs = refs.length > 0;
|
||||
const isUploading = uploadingRefs[cat];
|
||||
|
||||
return (
|
||||
<div key={cat} className="relative group flex-shrink-0">
|
||||
<button
|
||||
onClick={() => toggleReference(cat)}
|
||||
onDragOver={handleDragOver}
|
||||
onDrop={(e) => handleDrop(e, cat)}
|
||||
title={settings.provider === 'meta' && cat === 'subject'
|
||||
? "Upload image to animate into video"
|
||||
: undefined}
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 rounded-lg px-3 py-2 md:px-3 md:py-1.5 text-xs md:text-[10px] font-medium transition-all border relative overflow-hidden min-h-[44px] md:min-h-0",
|
||||
hasRefs
|
||||
? "bg-purple-500/10 text-purple-200 border-purple-500/30 hover:bg-purple-500/20"
|
||||
: "bg-white/5 text-white/40 border-white/5 hover:bg-white/10 hover:text-white/70 hover:border-white/10",
|
||||
isUploading && "animate-pulse cursor-wait"
|
||||
)}
|
||||
>
|
||||
{isUploading ? (
|
||||
<div className="h-3 w-3 animate-spin rounded-full border-2 border-current border-t-transparent" />
|
||||
) : hasRefs ? (
|
||||
<div className="flex -space-x-1.5">
|
||||
{refs.slice(0, 4).map((ref, idx) => (
|
||||
<img
|
||||
key={ref.id}
|
||||
src={ref.thumbnail}
|
||||
alt=""
|
||||
className="h-4 w-4 rounded-sm object-cover ring-1 ring-white/20"
|
||||
style={{ zIndex: 10 - idx }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<Upload className="h-3 w-3" />
|
||||
)}
|
||||
<span className="capitalize tracking-wide">{cat}</span>
|
||||
{refs.length > 0 && (
|
||||
<span className="text-[9px] bg-purple-500/30 text-purple-100 rounded-full px-1.5 h-3 flex items-center">{refs.length}</span>
|
||||
)}
|
||||
</button>
|
||||
{/* Clear all button */}
|
||||
{hasRefs && !isUploading && (
|
||||
<button
|
||||
className="absolute -top-1 -right-1 z-10 p-0.5 rounded-full bg-red-500/80 text-white opacity-100 md:opacity-0 group-hover:opacity-100 transition-opacity hover:bg-red-500"
|
||||
onClick={(e) => { e.stopPropagation(); clearReferences(cat); }}
|
||||
title={`Clear all ${cat} references`}
|
||||
>
|
||||
<X className="h-2 w-2" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Hidden file inputs for upload */}
|
||||
<input
|
||||
type="file"
|
||||
ref={fileInputRefs.subject}
|
||||
|
|
@ -571,133 +633,133 @@ export function PromptHero() {
|
|||
|
||||
|
||||
|
||||
{/* Right Controls: Settings & Generate */ }
|
||||
<div className="flex flex-col md:flex-row items-center gap-3 w-full md:w-auto md:ml-auto">
|
||||
{/* Right Controls: Settings & Generate */}
|
||||
<div className="flex flex-col md:flex-row items-center gap-3 w-full md:w-auto md:ml-auto">
|
||||
|
||||
{/* Settings Group */}
|
||||
<div className="flex items-center gap-2 w-full md:w-auto overflow-x-auto md:overflow-visible pb-2 md:pb-0 scrollbar-none snap-x">
|
||||
<div className="flex items-center gap-0.5 bg-[#0E0E10] p-1 rounded-lg border border-white/10 flex-shrink-0 snap-start">
|
||||
{/* Image Count */}
|
||||
<button
|
||||
onClick={settings.provider === 'meta' ? undefined : cycleImageCount}
|
||||
className={cn(
|
||||
"flex items-center gap-1 px-2 py-1 rounded-md text-[10px] font-medium transition-colors",
|
||||
settings.provider === 'meta'
|
||||
? "text-blue-200/50 cursor-not-allowed"
|
||||
: "text-white/60 hover:text-white hover:bg-white/5"
|
||||
)}
|
||||
title={settings.provider === 'meta' ? "Meta AI always generates 4 images" : "Number of images"}
|
||||
>
|
||||
<Hash className="h-3 w-3 opacity-70" />
|
||||
<span>{settings.provider === 'meta' ? 4 : settings.imageCount}</span>
|
||||
</button>
|
||||
{/* Settings Group */}
|
||||
<div className="flex items-center gap-2 w-full md:w-auto overflow-x-auto md:overflow-visible pb-2 md:pb-0 scrollbar-none snap-x">
|
||||
<div className="flex items-center gap-0.5 bg-[#0E0E10] p-1 rounded-lg border border-white/10 flex-shrink-0 snap-start">
|
||||
{/* Image Count */}
|
||||
<button
|
||||
onClick={settings.provider === 'meta' ? undefined : cycleImageCount}
|
||||
className={cn(
|
||||
"flex items-center gap-1 px-2 py-1 rounded-md text-[10px] font-medium transition-colors",
|
||||
settings.provider === 'meta'
|
||||
? "text-blue-200/50 cursor-not-allowed"
|
||||
: "text-white/60 hover:text-white hover:bg-white/5"
|
||||
)}
|
||||
title={settings.provider === 'meta' ? "Meta AI always generates 4 images" : "Number of images"}
|
||||
>
|
||||
<Hash className="h-3 w-3 opacity-70" />
|
||||
<span>{settings.provider === 'meta' ? 4 : settings.imageCount}</span>
|
||||
</button>
|
||||
|
||||
<div className="w-px h-3 bg-white/10 mx-1" />
|
||||
<div className="w-px h-3 bg-white/10 mx-1" />
|
||||
|
||||
{/* Aspect Ratio */}
|
||||
<button
|
||||
onClick={nextAspectRatio}
|
||||
className="px-2 py-1 rounded-md text-[10px] font-medium text-white/60 hover:text-white hover:bg-white/5 transition-colors"
|
||||
title="Aspect Ratio"
|
||||
>
|
||||
<span className="opacity-70">Ratio:</span>
|
||||
<span className="ml-1 text-white/80">{settings.aspectRatio}</span>
|
||||
</button>
|
||||
{/* Aspect Ratio */}
|
||||
<button
|
||||
onClick={nextAspectRatio}
|
||||
className="px-2 py-1 rounded-md text-[10px] font-medium text-white/60 hover:text-white hover:bg-white/5 transition-colors"
|
||||
title="Aspect Ratio"
|
||||
>
|
||||
<span className="opacity-70">Ratio:</span>
|
||||
<span className="ml-1 text-white/80">{settings.aspectRatio}</span>
|
||||
</button>
|
||||
|
||||
<div className="w-px h-3 bg-white/10 mx-1" />
|
||||
<div className="w-px h-3 bg-white/10 mx-1" />
|
||||
|
||||
{/* Precise Mode */}
|
||||
<button
|
||||
onClick={() => setSettings({ preciseMode: !settings.preciseMode })}
|
||||
className={cn(
|
||||
"px-2 py-1 rounded-md text-[10px] font-medium transition-all flex items-center gap-1",
|
||||
settings.preciseMode
|
||||
? "text-amber-300 bg-amber-500/10 ring-1 ring-amber-500/30"
|
||||
: "text-white/40 hover:text-white hover:bg-white/5"
|
||||
)}
|
||||
title="Precise Mode"
|
||||
>
|
||||
{/* <span>🍌</span> */}
|
||||
<span>Precise</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/* Precise Mode */}
|
||||
<button
|
||||
onClick={() => setSettings({ preciseMode: !settings.preciseMode })}
|
||||
className={cn(
|
||||
"px-2 py-1 rounded-md text-[10px] font-medium transition-all flex items-center gap-1",
|
||||
settings.preciseMode
|
||||
? "text-amber-300 bg-amber-500/10 ring-1 ring-amber-500/30"
|
||||
: "text-white/40 hover:text-white hover:bg-white/5"
|
||||
)}
|
||||
title="Precise Mode"
|
||||
>
|
||||
{/* <span>🍌</span> */}
|
||||
<span>Precise</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Generate Button */}
|
||||
<button
|
||||
onClick={handleGenerate}
|
||||
disabled={isGenerating || !prompt.trim()}
|
||||
className={cn(
|
||||
"relative overflow-hidden px-5 py-3 md:px-4 md:py-1.5 rounded-xl md:rounded-lg font-bold text-base md:text-sm text-white shadow-lg transition-all active:scale-95 group border border-white/10 w-full md:w-auto min-h-[48px] md:min-h-0 mt-2 md:mt-0 z-30",
|
||||
"bg-gradient-to-r from-purple-600 to-indigo-600 hover:from-purple-500 hover:to-indigo-500 hover:shadow-indigo-500/25"
|
||||
)}
|
||||
>
|
||||
<div className="relative z-10 flex items-center justify-center gap-1.5">
|
||||
{isGenerating ? (
|
||||
<>
|
||||
<div className="h-3 w-3 animate-spin rounded-full border-2 border-white border-t-transparent" />
|
||||
<span className="animate-pulse">Dreaming...</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Sparkles className="h-3 w-3 group-hover:rotate-12 transition-transform" />
|
||||
<span>Generate</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
{/* Generate Button */}
|
||||
<button
|
||||
onClick={handleGenerate}
|
||||
disabled={isGenerating || !prompt.trim()}
|
||||
className={cn(
|
||||
"relative overflow-hidden px-5 py-3 md:px-4 md:py-1.5 rounded-xl md:rounded-lg font-bold text-base md:text-sm text-white shadow-lg transition-all active:scale-95 group border border-white/10 w-full md:w-auto min-h-[48px] md:min-h-0 mt-2 md:mt-0 z-30",
|
||||
"bg-gradient-to-r from-purple-600 to-indigo-600 hover:from-purple-500 hover:to-indigo-500 hover:shadow-indigo-500/25"
|
||||
)}
|
||||
>
|
||||
<div className="relative z-10 flex items-center justify-center gap-1.5">
|
||||
{isGenerating ? (
|
||||
<>
|
||||
<div className="h-3 w-3 animate-spin rounded-full border-2 border-white border-t-transparent" />
|
||||
<span className="animate-pulse">Dreaming...</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Sparkles className="h-3 w-3 group-hover:rotate-12 transition-transform" />
|
||||
<span>Generate</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
|
||||
{/* Reference Preview Panel - shows when any references exist */ }
|
||||
{
|
||||
(references.subject?.length || references.scene?.length || references.style?.length) ? (
|
||||
<div className="mt-4 p-3 rounded-xl bg-white/5 border border-white/10">
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{(['subject', 'scene', 'style'] as ReferenceCategory[]).map((cat) => {
|
||||
const refs = references[cat] || [];
|
||||
if (refs.length === 0) return null;
|
||||
return (
|
||||
<div key={cat} className="flex-1 min-w-[120px]">
|
||||
<div className="text-[10px] uppercase tracking-wider text-white/40 mb-2 flex items-center justify-between">
|
||||
<span>{cat}</span>
|
||||
<span className="text-purple-300">{refs.length}</span>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{refs.map((ref) => (
|
||||
<div key={ref.id} className="relative group/thumb">
|
||||
<img
|
||||
src={ref.thumbnail}
|
||||
alt=""
|
||||
className="h-12 w-12 md:h-10 md:w-10 rounded object-cover ring-1 ring-white/10 group-hover/thumb:ring-purple-500/50 transition-all"
|
||||
/>
|
||||
<button
|
||||
onClick={() => removeReference(cat, ref.id)}
|
||||
className="absolute -top-1 -right-1 p-0.5 rounded-full bg-red-500 text-white opacity-0 group-hover/thumb:opacity-100 transition-opacity hover:bg-red-600"
|
||||
title="Remove this reference"
|
||||
>
|
||||
<X className="h-2.5 w-2.5" />
|
||||
</button>
|
||||
{/* Reference Preview Panel - shows when any references exist */}
|
||||
{
|
||||
(references.subject?.length || references.scene?.length || references.style?.length) ? (
|
||||
<div className="mt-4 p-3 rounded-xl bg-white/5 border border-white/10">
|
||||
<div className="flex flex-wrap gap-4">
|
||||
{(['subject', 'scene', 'style'] as ReferenceCategory[]).map((cat) => {
|
||||
const refs = references[cat] || [];
|
||||
if (refs.length === 0) return null;
|
||||
return (
|
||||
<div key={cat} className="flex-1 min-w-[120px]">
|
||||
<div className="text-[10px] uppercase tracking-wider text-white/40 mb-2 flex items-center justify-between">
|
||||
<span>{cat}</span>
|
||||
<span className="text-purple-300">{refs.length}</span>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{refs.map((ref) => (
|
||||
<div key={ref.id} className="relative group/thumb">
|
||||
<img
|
||||
src={ref.thumbnail}
|
||||
alt=""
|
||||
className="h-12 w-12 md:h-10 md:w-10 rounded object-cover ring-1 ring-white/10 group-hover/thumb:ring-purple-500/50 transition-all"
|
||||
/>
|
||||
<button
|
||||
onClick={() => removeReference(cat, ref.id)}
|
||||
className="absolute -top-1 -right-1 p-0.5 rounded-full bg-red-500 text-white opacity-0 group-hover/thumb:opacity-100 transition-opacity hover:bg-red-600"
|
||||
title="Remove this reference"
|
||||
>
|
||||
<X className="h-2.5 w-2.5" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
{/* Add more button */}
|
||||
<button
|
||||
onClick={() => openFilePicker(cat)}
|
||||
className="h-10 w-10 rounded border border-dashed border-white/20 flex items-center justify-center text-white/30 hover:text-white/60 hover:border-white/40 transition-colors"
|
||||
title={`Add more ${cat} references`}
|
||||
>
|
||||
<span className="text-lg">+</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{/* Add more button */}
|
||||
<button
|
||||
onClick={() => openFilePicker(cat)}
|
||||
className="h-10 w-10 rounded border border-dashed border-white/20 flex items-center justify-center text-white/30 hover:text-white/60 hover:border-white/40 transition-colors"
|
||||
title={`Add more ${cat} references`}
|
||||
>
|
||||
<span className="text-lg">+</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
</div >
|
||||
</div >
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue