mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat: add quality tier selector to AI canvas expand UI
This commit is contained in:
@@ -4,6 +4,14 @@ import { ProgressCard } from "@/components/common/progress-card";
|
|||||||
import { useToolProcessor } from "@/hooks/use-tool-processor";
|
import { useToolProcessor } from "@/hooks/use-tool-processor";
|
||||||
import { useFileStore } from "@/stores/file-store";
|
import { useFileStore } from "@/stores/file-store";
|
||||||
|
|
||||||
|
type Tier = "fast" | "balanced" | "high";
|
||||||
|
|
||||||
|
const TIERS: { id: Tier; label: string; desc: string }[] = [
|
||||||
|
{ id: "fast", label: "Fast", desc: "Quick preview, fewer AI passes" },
|
||||||
|
{ id: "balanced", label: "Balanced", desc: "Good quality, moderate speed" },
|
||||||
|
{ id: "high", label: "High Quality", desc: "Best results, slower" },
|
||||||
|
];
|
||||||
|
|
||||||
const EXTEND_PRESETS = [
|
const EXTEND_PRESETS = [
|
||||||
{ label: "16:9", aspect: 16 / 9 },
|
{ label: "16:9", aspect: 16 / 9 },
|
||||||
{ label: "1:1", aspect: 1 },
|
{ label: "1:1", aspect: 1 },
|
||||||
@@ -13,15 +21,16 @@ const EXTEND_PRESETS = [
|
|||||||
{ label: "4:5", aspect: 4 / 5 },
|
{ label: "4:5", aspect: 4 / 5 },
|
||||||
];
|
];
|
||||||
|
|
||||||
export function ContentAwareCropSettings() {
|
export function AiCanvasExpandSettings() {
|
||||||
const { files } = useFileStore();
|
const { files } = useFileStore();
|
||||||
const { processFiles, processAllFiles, processing, error, downloadUrl, progress } =
|
const { processFiles, processAllFiles, processing, error, downloadUrl, progress } =
|
||||||
useToolProcessor("content-aware-crop");
|
useToolProcessor("ai-canvas-expand");
|
||||||
|
|
||||||
const [extendTop, setExtendTop] = useState(0);
|
const [extendTop, setExtendTop] = useState(0);
|
||||||
const [extendRight, setExtendRight] = useState(0);
|
const [extendRight, setExtendRight] = useState(0);
|
||||||
const [extendBottom, setExtendBottom] = useState(0);
|
const [extendBottom, setExtendBottom] = useState(0);
|
||||||
const [extendLeft, setExtendLeft] = useState(0);
|
const [extendLeft, setExtendLeft] = useState(0);
|
||||||
|
const [tier, setTier] = useState<Tier>("balanced");
|
||||||
const [imgDimensions, setImgDimensions] = useState<{ width: number; height: number } | null>(
|
const [imgDimensions, setImgDimensions] = useState<{ width: number; height: number } | null>(
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
@@ -71,7 +80,7 @@ export function ContentAwareCropSettings() {
|
|||||||
const hasExtension = extendTop > 0 || extendRight > 0 || extendBottom > 0 || extendLeft > 0;
|
const hasExtension = extendTop > 0 || extendRight > 0 || extendBottom > 0 || extendLeft > 0;
|
||||||
|
|
||||||
const handleProcess = () => {
|
const handleProcess = () => {
|
||||||
const settings = { extendTop, extendRight, extendBottom, extendLeft };
|
const settings = { extendTop, extendRight, extendBottom, extendLeft, tier };
|
||||||
if (files.length > 1) {
|
if (files.length > 1) {
|
||||||
processAllFiles(files, settings);
|
processAllFiles(files, settings);
|
||||||
} else {
|
} else {
|
||||||
@@ -86,6 +95,31 @@ export function ContentAwareCropSettings() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
|
{/* Quality tier */}
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-muted-foreground mb-1">Quality</p>
|
||||||
|
<div className="grid grid-cols-3 gap-1">
|
||||||
|
{TIERS.map((t) => (
|
||||||
|
<button
|
||||||
|
key={t.id}
|
||||||
|
type="button"
|
||||||
|
data-testid={`tier-${t.id}`}
|
||||||
|
onClick={() => setTier(t.id)}
|
||||||
|
className={`text-xs py-2 rounded transition-colors ${
|
||||||
|
tier === t.id
|
||||||
|
? "bg-primary text-primary-foreground"
|
||||||
|
: "bg-muted text-muted-foreground hover:bg-muted/80"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{t.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<p className="text-[10px] text-muted-foreground mt-1">
|
||||||
|
{TIERS.find((t) => t.id === tier)?.desc}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Aspect ratio presets */}
|
{/* Aspect ratio presets */}
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs text-muted-foreground mb-1">Extend to aspect ratio</p>
|
<p className="text-xs text-muted-foreground mb-1">Extend to aspect ratio</p>
|
||||||
@@ -185,7 +219,7 @@ export function ContentAwareCropSettings() {
|
|||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
data-testid="content-aware-crop-submit"
|
data-testid="ai-canvas-expand-submit"
|
||||||
disabled={!hasFile || !hasExtension || processing}
|
disabled={!hasFile || !hasExtension || processing}
|
||||||
className="w-full py-2.5 rounded-lg bg-primary text-primary-foreground font-medium disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
|
className="w-full py-2.5 rounded-lg bg-primary text-primary-foreground font-medium disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
|
||||||
>
|
>
|
||||||
@@ -197,7 +231,7 @@ export function ContentAwareCropSettings() {
|
|||||||
<a
|
<a
|
||||||
href={downloadUrl}
|
href={downloadUrl}
|
||||||
download
|
download
|
||||||
data-testid="content-aware-crop-download"
|
data-testid="ai-canvas-expand-download"
|
||||||
className="w-full py-2.5 rounded-lg border border-primary text-primary font-medium flex items-center justify-center gap-2 hover:bg-primary/5"
|
className="w-full py-2.5 rounded-lg border border-primary text-primary font-medium flex items-center justify-center gap-2 hover:bg-primary/5"
|
||||||
>
|
>
|
||||||
<Download className="h-4 w-4" />
|
<Download className="h-4 w-4" />
|
||||||
|
|||||||
Reference in New Issue
Block a user