"use client"; import { useEffect, useState } from "react"; type Interaction = { partner_gene: string; combined_score: number; escore: number; dscore: number; tscore: number; ascore: number; }; export function StringDBViewer({ geneName }: { geneName: string }) { const [data, setData] = useState<{ interactions: Interaction[] } | null>(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [imgError, setImgError] = useState(false); const [copied, setCopied] = useState(false); const species = 9606; useEffect(() => { setLoading(true); setError(null); fetch(`/api/backend/api/interactions/${encodeURIComponent(geneName)}?limit=12`) .then(r => { if (!r.ok) return r.json().then(e => Promise.reject(new Error(e.detail || `Status ${r.status}`))); return r.json(); }) .then(setData) .catch(e => setError(e.message)) .finally(() => setLoading(false)); }, [geneName]); const scoreChannels = [ { key: "escore" as const, label: "Experimental", color: "#00F5D4" }, { key: "dscore" as const, label: "Database", color: "#7C3AED" }, { key: "ascore" as const, label: "Co-expression", color: "#F59E0B" }, { key: "tscore" as const, label: "Text mining", color: "#EF4444" }, ]; const stringDbUrl = `https://string-db.org/api/image/network?identifiers=${geneName}&species=${species}`; const allGenes = data?.interactions?.map(i => i.partner_gene).join("%0d") ?? ""; const exportPng = () => { const img = new window.Image(); img.crossOrigin = "anonymous"; img.onload = () => { const canvas = document.createElement("canvas"); canvas.width = img.width; canvas.height = img.height; const ctx = canvas.getContext("2d")!; ctx.fillStyle = "#04040A"; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.drawImage(img, 0, 0); const a = document.createElement("a"); a.download = `${geneName}_stringdb_network.png`; a.href = canvas.toDataURL("image/png"); a.click(); }; img.src = stringDbUrl; }; const exportSvgTable = () => { if (!data?.interactions) return; const rows = data.interactions.map(i => `${i.partner_gene}\t${i.combined_score.toFixed(3)}\t${i.escore.toFixed(3)}\t${i.dscore.toFixed(3)}\t${i.ascore.toFixed(3)}\t${i.tscore.toFixed(3)}` ).join("\n"); const tsv = `Partner\tCombined\tExperimental\tDatabase\tCo-expression\tText mining\n${rows}`; const a = document.createElement("a"); a.download = `${geneName}_stringdb_scores.tsv`; a.href = "data:text/tab-separated-values;charset=utf-8," + encodeURIComponent(tsv); a.click(); }; const copyGeneList = () => { if (!data?.interactions) return; const genes = data.interactions.map(i => i.partner_gene).join("\n"); navigator.clipboard.writeText(genes).then(() => { setCopied(true); setTimeout(() => setCopied(false), 2000); }); }; if (loading) return
Fetching STRING-DB interactions…
; if (error) return
{error}
; if (!data?.interactions?.length) return
No interactions found.
; const { interactions } = data; return (

STRING-DB Interactions — {geneName}

View on STRING-DB ↗
{!imgError ? ( {`STRING-DB setImgError(true)} /> ) : (
STRING-DB image unavailable. Open on STRING-DB ↗
)}
{scoreChannels.map(c => ( ))} {interactions.map((inter, i) => ( {scoreChannels.map(c => ( ))} ))}
Partner Combined{c.label}
{inter.partner_gene} {inter.combined_score.toFixed(3)}{inter[c.key].toFixed(3)}
); }