mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: clean up files section and add KB/MB unit selector to compress
- Remove duplicate "Files" heading (section header + inline count) - Space "Show more" and "Clear all" apart on the same row - Replace bare number input with number + KB/MB dropdown for target size, defaulting to KB; MB values are converted to KB for the API - Hide native number input spin buttons for a cleaner look
This commit is contained in:
@@ -5,6 +5,7 @@ import { useToolProcessor } from "@/hooks/use-tool-processor";
|
|||||||
import { useFileStore } from "@/stores/file-store";
|
import { useFileStore } from "@/stores/file-store";
|
||||||
|
|
||||||
type CompressMode = "quality" | "targetSize";
|
type CompressMode = "quality" | "targetSize";
|
||||||
|
type SizeUnit = "KB" | "MB";
|
||||||
|
|
||||||
export interface CompressControlsProps {
|
export interface CompressControlsProps {
|
||||||
settings?: Record<string, unknown>;
|
settings?: Record<string, unknown>;
|
||||||
@@ -14,7 +15,8 @@ export interface CompressControlsProps {
|
|||||||
export function CompressControls({ settings: initialSettings, onChange }: CompressControlsProps) {
|
export function CompressControls({ settings: initialSettings, onChange }: CompressControlsProps) {
|
||||||
const [mode, setMode] = useState<CompressMode>("targetSize");
|
const [mode, setMode] = useState<CompressMode>("targetSize");
|
||||||
const [quality, setQuality] = useState(75);
|
const [quality, setQuality] = useState(75);
|
||||||
const [targetSizeKb, setTargetSizeKb] = useState("");
|
const [targetSizeValue, setTargetSizeValue] = useState("");
|
||||||
|
const [sizeUnit, setSizeUnit] = useState<SizeUnit>("KB");
|
||||||
|
|
||||||
const initializedRef = useRef(false);
|
const initializedRef = useRef(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -22,7 +24,8 @@ export function CompressControls({ settings: initialSettings, onChange }: Compre
|
|||||||
initializedRef.current = true;
|
initializedRef.current = true;
|
||||||
if (initialSettings.mode != null) setMode(initialSettings.mode as CompressMode);
|
if (initialSettings.mode != null) setMode(initialSettings.mode as CompressMode);
|
||||||
if (initialSettings.quality != null) setQuality(Number(initialSettings.quality));
|
if (initialSettings.quality != null) setQuality(Number(initialSettings.quality));
|
||||||
if (initialSettings.targetSizeKb != null) setTargetSizeKb(String(initialSettings.targetSizeKb));
|
if (initialSettings.targetSizeKb != null)
|
||||||
|
setTargetSizeValue(String(initialSettings.targetSizeKb));
|
||||||
}, [initialSettings]);
|
}, [initialSettings]);
|
||||||
|
|
||||||
const onChangeRef = useRef(onChange);
|
const onChangeRef = useRef(onChange);
|
||||||
@@ -34,9 +37,11 @@ export function CompressControls({ settings: initialSettings, onChange }: Compre
|
|||||||
if (mode === "quality") {
|
if (mode === "quality") {
|
||||||
onChangeRef.current?.({ mode, quality });
|
onChangeRef.current?.({ mode, quality });
|
||||||
} else {
|
} else {
|
||||||
onChangeRef.current?.({ mode, targetSizeKb: Number(targetSizeKb) });
|
const valueNum = Number(targetSizeValue);
|
||||||
|
const targetSizeKb = sizeUnit === "MB" ? valueNum * 1024 : valueNum;
|
||||||
|
onChangeRef.current?.({ mode, targetSizeKb });
|
||||||
}
|
}
|
||||||
}, [mode, quality, targetSizeKb]);
|
}, [mode, quality, targetSizeValue, sizeUnit]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
@@ -64,17 +69,27 @@ export function CompressControls({ settings: initialSettings, onChange }: Compre
|
|||||||
{mode === "targetSize" ? (
|
{mode === "targetSize" ? (
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="compress-target-size" className="text-xs text-muted-foreground">
|
<label htmlFor="compress-target-size" className="text-xs text-muted-foreground">
|
||||||
Target Size (KB)
|
Target Size
|
||||||
</label>
|
</label>
|
||||||
<input
|
<div className="flex gap-1.5 mt-0.5">
|
||||||
id="compress-target-size"
|
<input
|
||||||
type="number"
|
id="compress-target-size"
|
||||||
value={targetSizeKb}
|
type="number"
|
||||||
onChange={(e) => setTargetSizeKb(e.target.value)}
|
value={targetSizeValue}
|
||||||
min={1}
|
onChange={(e) => setTargetSizeValue(e.target.value)}
|
||||||
placeholder="e.g. 200"
|
min={1}
|
||||||
className="w-full mt-0.5 px-2 py-1.5 rounded border border-border bg-background text-sm text-foreground"
|
placeholder={sizeUnit === "KB" ? "e.g. 200" : "e.g. 2"}
|
||||||
/>
|
className="flex-1 min-w-0 px-2 py-1.5 rounded border border-border bg-background text-sm text-foreground tabular-nums [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
|
||||||
|
/>
|
||||||
|
<select
|
||||||
|
value={sizeUnit}
|
||||||
|
onChange={(e) => setSizeUnit(e.target.value as SizeUnit)}
|
||||||
|
className="px-2 py-1.5 rounded border border-border bg-background text-sm text-foreground"
|
||||||
|
>
|
||||||
|
<option value="KB">KB</option>
|
||||||
|
<option value="MB">MB</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -123,23 +123,26 @@ function FileSelectionInfo({
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showToggle && (
|
<div className="flex items-center justify-between">
|
||||||
|
{showToggle ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setExpanded(!expanded)}
|
||||||
|
className="text-xs text-primary hover:text-primary/80"
|
||||||
|
>
|
||||||
|
{expanded ? "Show less" : `Show ${files.length - COLLAPSED_LIMIT} more`}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<span />
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setExpanded(!expanded)}
|
onClick={onClear}
|
||||||
className="text-xs text-primary hover:text-primary/80"
|
className="text-xs text-muted-foreground hover:text-foreground"
|
||||||
>
|
>
|
||||||
{expanded ? "Show less" : `Show ${files.length - COLLAPSED_LIMIT} more`}
|
Clear all
|
||||||
</button>
|
</button>
|
||||||
)}
|
</div>
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={onClear}
|
|
||||||
className="text-xs text-muted-foreground hover:text-foreground"
|
|
||||||
>
|
|
||||||
Clear all
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -643,7 +646,6 @@ export function ToolPage() {
|
|||||||
<>
|
<>
|
||||||
{!isNoDropzone && (
|
{!isNoDropzone && (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<h3 className="text-sm font-medium text-muted-foreground">Files</h3>
|
|
||||||
<FileSelectionInfo
|
<FileSelectionInfo
|
||||||
files={files}
|
files={files}
|
||||||
selectedIndex={selectedIndex}
|
selectedIndex={selectedIndex}
|
||||||
|
|||||||
Reference in New Issue
Block a user