prepare app migration to db.lasereverything.net

- remove hardcoded makearmy.io references
- convert utilities to relative paths
- fix external link detection
- add svgnest middleware rule
- update background remover navigation
This commit is contained in:
makearmy 2026-03-04 21:10:23 -05:00
parent e08d4d81b3
commit 3614acd297
3 changed files with 208 additions and 73 deletions

View file

@ -78,9 +78,13 @@ export default function BackgroundRemoverPage() {
const [sourceUrl, setSourceUrl] = useState<string | null>(null);
const [natural, setNatural] = useState<{ w: number; h: number } | null>(null);
const [status, setStatus] = useState<Record<Canonical, Status>>(
() => Object.fromEntries(METHODS.map((m) => [m.key, "idle"])) as Record<Canonical, Status>
);
const [status, setStatus] = useState<Record<Canonical, Status>>(() => {
return Object.fromEntries(METHODS.map((m) => [m.key, "idle"])) as Record<
Canonical,
Status
>;
});
const [results, setResults] = useState<ResultMap>({});
const resultsRef = useRef<ResultMap>({});
useEffect(() => {
@ -182,7 +186,9 @@ export default function BackgroundRemoverPage() {
const runOne = async (key: Canonical) => {
// When GPU-safe is on, try progressively smaller long-edge previews.
const sizes = gpuSafe ? BATCH_SIZES : [Math.max(natural?.w || 0, natural?.h || 0) || 4096];
const sizes = gpuSafe
? BATCH_SIZES
: [Math.max(natural?.w || 0, natural?.h || 0) || 4096];
let lastErr: string | null = null;
const t0 = performance.now();
@ -195,10 +201,15 @@ export default function BackgroundRemoverPage() {
fd.append("method", key);
// CHANGED: call local proxy instead of a hardcoded service
const res = await fetch("/api/bgbye/process", { method: "POST", body: fd });
const res = await fetch("/api/bgbye/process", {
method: "POST",
body: fd,
});
if (!res.ok) {
const txt = await res.text().catch(() => "");
const retryable = /out of memory|onnxruntime|cuda|allocate|500/i.test(txt);
const retryable =
/out of memory|onnxruntime|cuda|allocate|500/i.test(txt);
if (gpuSafe && retryable) {
lastErr = txt || `HTTP ${res.status}`;
continue; // try next smaller size
@ -261,12 +272,16 @@ export default function BackgroundRemoverPage() {
const f = e.dataTransfer.files?.[0];
if (f) onPick(f);
};
const onSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
const f = e.target.files?.[0] ?? null;
onPick(f);
};
const aspect = useMemo(() => (!natural ? 16 / 9 : natural.w / natural.h || 16 / 9), [natural]);
const aspect = useMemo(
() => (!natural ? 16 / 9 : natural.w / natural.h || 16 / 9),
[natural]
);
const updateByClientX = useCallback((clientX: number) => {
const el = frameRef.current;
@ -275,6 +290,7 @@ export default function BackgroundRemoverPage() {
const pct = ((clientX - rect.left) / rect.width) * 100;
setReveal(Math.min(100, Math.max(0, pct)));
}, []);
const onMouseDown = (e: React.MouseEvent) => {
draggingRef.current = true;
updateByClientX(e.clientX);
@ -284,6 +300,7 @@ export default function BackgroundRemoverPage() {
updateByClientX(e.clientX);
};
const onMouseUp = () => (draggingRef.current = false);
const onTouchStart = (e: React.TouchEvent) => {
draggingRef.current = true;
updateByClientX(e.touches[0].clientX);
@ -321,24 +338,36 @@ export default function BackgroundRemoverPage() {
const fd = new FormData();
fd.append("file", file);
fd.append("method", active);
// CHANGED: call local proxy instead of a hardcoded service
const res = await fetch("/api/bgbye/process", { method: "POST", body: fd });
if (!res.ok) throw new Error(await res.text());
const outBlob = await res.blob();
const ms = performance.now() - t0;
const previewBlob = await makePreview(outBlob);
const previewUrl = URL.createObjectURL(previewBlob);
const prev = resultsRef.current[active];
if (prev) revoke(prev.previewUrl);
setResults((r) => ({ ...r, [active]: { fullBlob: outBlob, previewUrl, bytes: outBlob.size, ms } }));
setResults((r) => ({
...r,
[active]: { fullBlob: outBlob, previewUrl, bytes: outBlob.size, ms },
}));
setStatus((s) => ({ ...s, [active]: "ok" }));
} catch {
setStatus((s) => ({ ...s, [active]: "error" }));
}
}, [file, active]);
const doneCount = useMemo(() => METHODS.filter((m) => status[m.key] === "ok").length, [status]);
const pendingCount = useMemo(() => METHODS.filter((m) => status[m.key] === "pending").length, [status]);
const doneCount = useMemo(
() => METHODS.filter((m) => status[m.key] === "ok").length,
[status]
);
const pendingCount = useMemo(
() => METHODS.filter((m) => status[m.key] === "pending").length,
[status]
);
function StatusDot({ s }: { s: Status }) {
const cls =
@ -362,7 +391,7 @@ export default function BackgroundRemoverPage() {
<div className="mb-4 flex items-center justify-between gap-3">
<h1 className="text-2xl font-semibold">Background Remover</h1>
<a
href="https://makearmy.io"
href="/"
className="px-3 py-1 rounded-md border border-zinc-700 hover:bg-zinc-800/60 text-sm"
>
Back to main
@ -379,7 +408,12 @@ export default function BackgroundRemoverPage() {
<div
ref={frameRef}
className="app-frame checkerboard relative w-full rounded-2xl shadow-inner"
style={{ aspectRatio: `${aspect}`, maxWidth: "1200px", maxHeight: "80vh", marginInline: "auto" }}
style={{
aspectRatio: `${aspect}`,
maxWidth: "1200px",
maxHeight: "80vh",
marginInline: "auto",
}}
onDragOver={(e) => e.preventDefault()}
onDrop={onDrop}
onMouseDown={onMouseDown}
@ -437,7 +471,9 @@ export default function BackgroundRemoverPage() {
{/* Divider & Thumb */}
<div
className="slider-handle"
style={{ "--reveal": `${Math.min(100, Math.max(0, reveal))}` } as React.CSSProperties}
style={
{ "--reveal": `${Math.min(100, Math.max(0, reveal))}` } as React.CSSProperties
}
>
<div className="slider-thumb">
<div className="w-1.5 h-4 bg-white/80 rounded" />
@ -453,7 +489,9 @@ export default function BackgroundRemoverPage() {
<button
key={key}
className={`w-full justify-center px-3 py-2 rounded-md border flex items-center gap-2 ${
active === key ? "border-blue-400 bg-blue-500/20" : "border-zinc-700 hover:bg-zinc-800/60"
active === key
? "border-blue-400 bg-blue-500/20"
: "border-zinc-700 hover:bg-zinc-800/60"
}`}
onClick={() => setActive(key)}
disabled={!file}
@ -479,13 +517,20 @@ export default function BackgroundRemoverPage() {
{/* GPU-safe toggle */}
<label className="flex items-center gap-2 text-sm text-zinc-300 cursor-pointer select-none order-1">
<input type="checkbox" checked={gpuSafe} onChange={(e) => setGpuSafe(e.target.checked)} /> GPU-safe mode
<input
type="checkbox"
checked={gpuSafe}
onChange={(e) => setGpuSafe(e.target.checked)}
/>{" "}
GPU-safe mode
</label>
<div className="text-zinc-400 text-sm order-2">
{file ? (
pendingCount > 0 ? (
<span>Processing {doneCount}/{METHODS.length} finished</span>
<span>
Processing {doneCount}/{METHODS.length} finished
</span>
) : doneCount > 0 ? (
<span>Done: {doneCount} methods succeeded</span>
) : (
@ -516,7 +561,13 @@ export default function BackgroundRemoverPage() {
? "border-sky-600 bg-sky-600/20 hover:bg-sky-600/30"
: "border-zinc-700 text-zinc-400 cursor-not-allowed"
}`}
title={!file ? "Select a file first" : !active ? "Choose a method" : "Render selected method at full resolution"}
title={
!file
? "Select a file first"
: !active
? "Choose a method"
: "Render selected method at full resolution"
}
>
Full-res render
</button>