mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat: add expansion preview for content-aware crop mode
This commit is contained in:
@@ -36,6 +36,7 @@ export interface CropProps {
|
|||||||
onCropChange: (crop: Crop) => void;
|
onCropChange: (crop: Crop) => void;
|
||||||
onAspectChange: (aspect: number | undefined) => void;
|
onAspectChange: (aspect: number | undefined) => void;
|
||||||
onGridToggle: (show: boolean) => void;
|
onGridToggle: (show: boolean) => void;
|
||||||
|
onModeChange?: (mode: "standard" | "content-aware") => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EraserProps {
|
export interface EraserProps {
|
||||||
|
|||||||
@@ -229,6 +229,7 @@ export function ToolPage() {
|
|||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
const [cropMode, setCropMode] = useState<"standard" | "content-aware">("standard");
|
||||||
|
|
||||||
const cropState = useMemo(
|
const cropState = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@@ -266,6 +267,7 @@ export function ToolPage() {
|
|||||||
setCropAspect(undefined);
|
setCropAspect(undefined);
|
||||||
setCropShowGrid(true);
|
setCropShowGrid(true);
|
||||||
setCropImgDimensions(null);
|
setCropImgDimensions(null);
|
||||||
|
setCropMode("standard");
|
||||||
setEraserHasStrokes(false);
|
setEraserHasStrokes(false);
|
||||||
setEraserBrushSize(30);
|
setEraserBrushSize(30);
|
||||||
setEraserSliderInitPos(null);
|
setEraserSliderInitPos(null);
|
||||||
@@ -363,8 +365,11 @@ export function ToolPage() {
|
|||||||
? canBrowserPreview(processedUrl, currentEntry?.processedFilename)
|
? canBrowserPreview(processedUrl, currentEntry?.processedFilename)
|
||||||
: false;
|
: false;
|
||||||
// Use server-generated preview for non-previewable formats (HEIC, TIFF).
|
// Use server-generated preview for non-previewable formats (HEIC, TIFF).
|
||||||
// Always a string when hasProcessed is true (processedUrl is non-null).
|
// Falls back to the upload-decoded blobUrl so TIFF/DNG always have a renderable src.
|
||||||
const displayUrl = (processedPreviewUrl ?? processedUrl) as string;
|
const displayUrl = (processedPreviewUrl ??
|
||||||
|
(isProcessedPreviewable ? processedUrl : null) ??
|
||||||
|
originalBlobUrl ??
|
||||||
|
processedUrl) as string;
|
||||||
|
|
||||||
// Build settings props
|
// Build settings props
|
||||||
const settingsProps = {
|
const settingsProps = {
|
||||||
@@ -379,6 +384,7 @@ export function ToolPage() {
|
|||||||
onCropChange: setCropCrop,
|
onCropChange: setCropCrop,
|
||||||
onAspectChange: setCropAspect,
|
onAspectChange: setCropAspect,
|
||||||
onGridToggle: setCropShowGrid,
|
onGridToggle: setCropShowGrid,
|
||||||
|
onModeChange: setCropMode,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
eraserProps:
|
eraserProps:
|
||||||
@@ -438,6 +444,11 @@ export function ToolPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (displayMode === "interactive-crop" && hasFile && !hasProcessed && originalBlobUrl) {
|
if (displayMode === "interactive-crop" && hasFile && !hasProcessed && originalBlobUrl) {
|
||||||
|
if (cropMode === "content-aware") {
|
||||||
|
const fname = selectedFileName ?? files[0].name;
|
||||||
|
const fsize = selectedFileSize ?? files[0].size;
|
||||||
|
return <ImageViewer src={originalBlobUrl} filename={fname} fileSize={fsize} />;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<CropCanvas
|
<CropCanvas
|
||||||
imageSrc={originalBlobUrl}
|
imageSrc={originalBlobUrl}
|
||||||
@@ -488,8 +499,8 @@ export function ToolPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-previewable format with no server-generated preview - show success card
|
// Non-previewable format with no fallback at all - show success card
|
||||||
if (hasProcessed && !isProcessedPreviewable && !processedPreviewUrl) {
|
if (hasProcessed && !isProcessedPreviewable && !processedPreviewUrl && !originalBlobUrl) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center h-full gap-4 text-center p-8">
|
<div className="flex flex-col items-center justify-center h-full gap-4 text-center p-8">
|
||||||
<div className="w-16 h-16 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center">
|
<div className="w-16 h-16 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center">
|
||||||
@@ -705,7 +716,11 @@ export function ToolPage() {
|
|||||||
fileSize={processedSize}
|
fileSize={processedSize}
|
||||||
fileType={processedFileType}
|
fileType={processedFileType}
|
||||||
downloadUrl={processedUrl}
|
downloadUrl={processedUrl}
|
||||||
previewUrl={isProcessedPreviewable ? processedUrl : (processedPreviewUrl ?? undefined)}
|
previewUrl={
|
||||||
|
isProcessedPreviewable
|
||||||
|
? processedUrl
|
||||||
|
: (processedPreviewUrl ?? originalBlobUrl ?? undefined)
|
||||||
|
}
|
||||||
onUndo={handleUndo}
|
onUndo={handleUndo}
|
||||||
currentToolId={tool?.id ?? ""}
|
currentToolId={tool?.id ?? ""}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user