Mount it:

// server/server.ts
import importIconsRoute from "./api/icons/import-from-screenshot";
app.use("/api/icons/import-from-screenshot", importIconsRoute);

2) Minimal UI — Icon Importer page (under Stock Library)

Upload screenshot

Set rows/cols

Adjust threshold/invert

Preview all returned SVGs

Select → Save to Library

src/pages/business-assets/stock/IconImporter.tsx

import React, { useState } from "react";

type SvgItem = { id: string; svg: string; name?: string; };

export default function IconImporter() {
  const [file, setFile] = useState<File | null>(null);
  const [rows, setRows] = useState(6);
  const [cols, setCols] = useState(6);
  const [margin, setMargin] = useState(0);
  const [cellPad, setCellPad] = useState(6);
  const [threshold, setThreshold] = useState(180);
  const [invert, setInvert] = useState(false);

  const [svgs, setSvgs] = useState<SvgItem[]>([]);
  const [selected, setSelected] = useState<Record<string, boolean>>({});

  function onPick(e: React.ChangeEvent<HTMLInputElement>) {
    const f = e.target.files?.[0];
    if (f) setFile(f);
  }

  async function process() {
    if (!file) { alert("Choose a screenshot first"); return; }
    const fd = new FormData();
    fd.append("file", file);
    fd.append("rows", String(rows));
    fd.append("cols", String(cols));
    fd.append("margin", String(margin));
    fd.append("cellPad", String(cellPad));
    fd.append("threshold", String(threshold));
    fd.append("invert", String(invert));

    const res = await fetch("/api/icons/import-from-screenshot", { method: "POST", body: fd });
    if (!res.ok) { alert("Vectorize failed"); return; }
    const json = await res.json();
    setSvgs(json.svgs);
    setSelected({});
  }

  async function saveSelected() {
    const picks = svgs.filter(s => selected[s.id]);
    if (!picks.length) { alert("Select at least one icon"); return; }

    // TODO: replace with your existing /api/icons/create (DB + storage)
    // Below we simply download a ZIP-less .json for demo, or call an existing save endpoint.
    const payload = picks.map((p, i) => ({
      name: p.name || `icon_${i+1}`,
      svg: p.svg,
      style: "flat", // or let user choose
      tags: [],
    }));

    // Example local download for now:
    const blob = new Blob([JSON.stringify(payload, null, 2)], { type: "application/json" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url; a.download = "icons.json"; a.click();
    URL.revokeObjectURL(url);

    // If you already have an API, do:
    // await fetch("/api/icons/bulk", { method: "POST", headers: {"Content-Type":"application/json"}, body: JSON.stringify(payload) });
  }

  return (
    <div className="p-6 space-y-6">
      <div className="flex items-center justify-between">
        <h1 className="text-2xl font-bold">Icon Importer (Screenshot → SVG)</h1>
        <div className="flex gap-2">
          <button onClick={process} className="px-4 py-2 rounded-lg bg-emerald-600 hover:bg-emerald-700 text-white">Process</button>
          <button onClick={saveSelected} className="px-4 py-2 rounded-lg border border-white/10 hover:bg-white/5">Save Selected</button>
        </div>
      </div>

      <div className="grid lg:grid-cols-[360px,1fr] gap-6">
        {/* Controls */}
        <aside className="rounded-xl border border-white/10 bg-slate-900/60 p-4 space-y-3">
          <div>
            <div className="text-sm text-slate-400 mb-1">Screenshot</div>
            <input type="file" accept="image/*" onChange={onPick} />
          </div>
          <div className="grid grid-cols-2 gap-2">
            <label className="text-xs">Rows
              <input type="number" min={1} value={rows} onChange={e=>setRows(parseInt(e.target.value||"1"))}
                className="mt-1 w-full rounded bg-slate-950/60 border border-white/10 p-2"/>
            </label>
            <label className="text-xs">Cols
              <input type="number" min={1} value={cols} onChange={e=>setCols(parseInt(e.target.value||"1"))}
                className="mt-1 w-full rounded bg-slate-950/60 border border-white/10 p-2"/>
            </label>
          </div>
          <div className="grid grid-cols-2 gap-2">
            <label className="text-xs">Outer Margin (px)
              <input type="number" min={0} value={margin} onChange={e=>setMargin(parseInt(e.target.value||"0"))}
                className="mt-1 w-full rounded bg-slate-950/60 border border-white/10 p-2"/>
            </label>
            <label className="text-xs">Cell Padding (px)
              <input type="number" min={0} value={cellPad} onChange={e=>setCellPad(parseInt(e.target.value||"0"))}
                className="mt-1 w-full rounded bg-slate-950/60 border border-white/10 p-2"/>
            </label>
          </div>
          <div className="grid grid-cols-2 gap-2">
            <label className="text-xs">Threshold
              <input type="range" min={0} max={255} value={threshold} onChange={e=>setThreshold(parseInt(e.target.value))}
                className="w-full mt-1"/>
            </label>
            <label className="text-xs flex items-center gap-2 mt-4">
              <input type="checkbox" checked={invert} onChange={e=>setInvert(e.target.checked)}/>
              Invert (light icons on dark bg)
            </label>
          </div>
          <p className="text-[11px] text-slate-400">Tip: Use a high-res screenshot with crisp icons on a plain background for best results.</p>
        </aside>

        {/* Results grid */}
        <section className="rounded-xl border border-white/10 bg-slate-900/60 p-4">
          {svgs.length === 0 ? (
            <div className="text-sm text-slate-400">Upload a screenshot and click <b>Process</b> to see vectorized icons.</div>
          ) : (
            <div className="grid sm:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6 gap-3">
              {svgs.map(item => (
                <div key={item.id} className={`rounded-lg border ${selected[item.id] ? "border-sky-500/50 bg-sky-500/5" : "border-white/10 bg-slate-950/60"} p-3`}>
                  <div className="flex items-center justify-between">
                    <label className="inline-flex items-center gap-2 text-xs">
                      <input type="checkbox" checked={!!selected[item.id]} onChange={()=>setSelected(s=>({...s,[item.id]:!s[item.id]}))} className="accent-sky-500"/>
                      Select
                    </label>
                  </div>
                  <div className="mt-2 aspect-square rounded bg-white flex items-center justify-center overflow-hidden">
                    {/* render raw SVG */}
                    <div dangerouslySetInnerHTML={{ __html: item.svg }} style={{ width: "70%", height: "70%" }} />
                  </div>
                </div>
              ))}
            </div>
          )}
        </section>
      </div>
    </div>
  );
}