From 50649bb13453a87c9a539e157f786d75affc8b48 Mon Sep 17 00:00:00 2001 From: Peter Maquiran Date: Sat, 11 Apr 2026 02:39:13 +0100 Subject: [PATCH] fix --- app/panel/page.tsx | 188 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 3 +- pnpm-lock.yaml | 21 +++++ 3 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 app/panel/page.tsx diff --git a/app/panel/page.tsx b/app/panel/page.tsx new file mode 100644 index 0000000..0c397b4 --- /dev/null +++ b/app/panel/page.tsx @@ -0,0 +1,188 @@ +"use client"; +import React, { useState, useCallback } from "react"; +import Cropper from "react-easy-crop"; + +const RATIOS = [ + { label: "Hero Banner (Ultra Wide)", value: 21 / 9, text: "21/9" }, + { label: "News Feed (Widescreen)", value: 16 / 9, text: "16/9" }, + { label: "Profile / Post (Square)", value: 1 / 1, text: "1/1" }, +]; + +export default function FullPageEditor() { + const [image, setImage] = useState(null); + const [crops, setCrops] = useState>( + RATIOS.reduce((acc, r) => ({ ...acc, [r.text]: { x: 0, y: 0, zoom: 1 } }), {}) + ); + const [completedCrops, setCompletedCrops] = useState>({}); + const [isExporting, setIsExporting] = useState(false); + const [results, setResults] = useState>({}); + + const onSelectFile = (e: React.ChangeEvent) => { + if (e.target.files && e.target.files.length > 0) { + const reader = new FileReader(); + reader.onload = () => setImage(reader.result as string); + reader.readAsDataURL(e.target.files[0]); + } + }; + + const handleExport = async () => { + setIsExporting(true); + const bundle: Record = {}; + for (const ratio of RATIOS) { + const crop = completedCrops[ratio.text]; + if (crop) bundle[ratio.text] = await getCroppedImg(image!, crop); + } + setResults(bundle); + setIsExporting(false); + }; + + return ( +
+ {/* 1. Global Navigation */} + + + {/* 2. Workspace Area */} +
+ {!image ? ( +
+
📁
+

Editor is Empty

+

Upload a high-resolution image to begin the multi-aspect framing process.

+
+ ) : ( +
+ {RATIOS.map((ratio) => ( +
+ {/* Section Header */} +
+
+ Aspect Ratio +

{ratio.label}

+
+
+

Current Zoom

+

{Math.round((crops[ratio.text]?.zoom || 1) * 100)}%

+
+
+ + {/* BIG Editor Window */} +
+ setCrops(prev => ({ ...prev, [ratio.text]: { ...prev[ratio.text], ...c } }))} + onZoomChange={(z) => setCrops(prev => ({ ...prev, [ratio.text]: { ...prev[ratio.text], zoom: z } }))} + onCropComplete={(_, px) => setCompletedCrops(prev => ({ ...prev, [ratio.text]: px }))} + objectFit="cover" + showGrid={true} + /> +
+ + {/* Floating Zoom Control (Large & Accessible) */} +
+ Adjust Zoom + setCrops(prev => ({ ...prev, [ratio.text]: { ...prev[ratio.text], zoom: Number(e.target.value) } }))} + className="flex-1 accent-indigo-500 h-2 bg-zinc-800 rounded-lg appearance-none cursor-pointer" + /> + +
+
+ ))} +
+ )} +
+ + {/* 3. Global Results Modal/Area */} + {Object.keys(results).length > 0 && ( +
+
+

EXPORT BUNDLE

+ +
+
+ {Object.entries(results).map(([ratio, b64]) => ( +
+
+ {ratio} Result + +
+ Crop preview +