refactor: extract RemoveBgControls for DRY reuse in pipeline steps

Extract the settings controls (subject type, quality, background color)
from RemoveBgSettings into a shared RemoveBgControls component that
accepts settings + onChange props. Both the standalone tool page and the
pipeline step configurator now render the same component, so the UI is
identical and changes only need to be made in one place.
This commit is contained in:
Siddharth Kumar Sah
2026-03-28 15:40:39 +08:00
parent e5238f5661
commit 5a50aecd0b
2 changed files with 45 additions and 34 deletions
@@ -1,3 +1,5 @@
import { RemoveBgControls } from "./remove-bg-settings";
type FieldType = "number" | "select" | "boolean" | "text" | "color";
interface FieldDef {
@@ -479,27 +481,7 @@ const TOOL_FIELDS: Record<string, FieldDef[]> = {
{ key: "background", label: "Background", type: "color", defaultValue: "#FFFFFF" },
],
"remove-background": [
{
key: "model",
label: "AI Model",
type: "select",
defaultValue: "birefnet-general-lite",
options: [
{ value: "u2net", label: "Fast (u2net)" },
{ value: "birefnet-general-lite", label: "Balanced (general)" },
{ value: "birefnet-general", label: "Best (general)" },
{ value: "birefnet-portrait", label: "Portrait / Passport" },
{ value: "bria-rmbg", label: "Products (bria)" },
],
},
{
key: "backgroundColor",
label: "Background color",
type: "color",
defaultValue: "",
},
],
// remove-background uses its own shared controls component (DRY)
favicon: [],
"color-palette": [],
"barcode-read": [],
@@ -514,6 +496,11 @@ interface PipelineStepSettingsProps {
}
export function PipelineStepSettings({ toolId, settings, onChange }: PipelineStepSettingsProps) {
// Tools with their own shared controls component (DRY - same UI as standalone page)
if (toolId === "remove-background") {
return <RemoveBgControls settings={settings} onChange={onChange} />;
}
const fields = TOOL_FIELDS[toolId];
if (!fields || fields.length === 0) {
@@ -1,5 +1,5 @@
import { Download, ImageIcon, Package, User } from "lucide-react";
import { useState } from "react";
import { useEffect, useState } from "react";
import { ProgressCard } from "@/components/common/progress-card";
import { useToolProcessor } from "@/hooks/use-tool-processor";
import { useFileStore } from "@/stores/file-store";
@@ -41,25 +41,27 @@ const BG_PRESETS = [
{ color: "#0000FF", label: "Blue", preview: "#0000FF" },
];
export function RemoveBgSettings() {
const { files } = useFileStore();
const { processFiles, processing, error, downloadUrl, originalSize, processedSize, progress } =
useToolProcessor("remove-background");
// ── Shared controls (used by both standalone page and pipeline steps) ──
export interface RemoveBgControlsProps {
settings: Record<string, unknown>;
onChange: (settings: Record<string, unknown>) => void;
}
export function RemoveBgControls({ settings, onChange }: RemoveBgControlsProps) {
const [subject, setSubject] = useState<SubjectType>("people");
const [quality, setQuality] = useState<Quality>("balanced");
const [isPassport, setIsPassport] = useState(false);
const [bgColor, setBgColor] = useState("");
const [bgColor, setBgColor] = useState((settings.backgroundColor as string) || "");
const model = isPassport ? "birefnet-portrait" : MODEL_MAP[subject][quality];
const handleProcess = () => {
const settings: Record<string, unknown> = { model };
if (bgColor) settings.backgroundColor = bgColor;
processFiles(files, settings);
};
const hasFile = files.length > 0;
// Sync settings on every control change
useEffect(() => {
const next: Record<string, unknown> = { model };
if (bgColor) next.backgroundColor = bgColor;
onChange(next);
}, [model, bgColor, onChange]);
return (
<div className="space-y-4">
@@ -175,6 +177,28 @@ export function RemoveBgSettings() {
/>
</div>
</div>
</div>
);
}
// ── Standalone tool page wrapper ──────────────────────────────────────
export function RemoveBgSettings() {
const { files } = useFileStore();
const { processFiles, processing, error, downloadUrl, originalSize, processedSize, progress } =
useToolProcessor("remove-background");
const [settings, setSettings] = useState<Record<string, unknown>>({});
const handleProcess = () => {
processFiles(files, settings);
};
const hasFile = files.length > 0;
return (
<div className="space-y-4">
<RemoveBgControls settings={settings} onChange={setSettings} />
{/* Error */}
{error && <p className="text-xs text-red-500">{error}</p>}