1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js"; 2import { a as api } from "./index-w5lXls15.js"; 3import { bg as Music, ci as Database, bi as Sparkles, a_ as CircleCheck, g5 as HeartPulse, cM as RefreshCw, b7 as Check, ba as Info, aY as TriangleAlert, aW as X } from "./vendor-D0P4XgNj.js"; 4import "./vendor-i18n-BC7ZMXWQ.js"; 5const ScanAndTag = () => { 6 const [activeTab, setActiveTab] = reactExports.useState("review"); 7 const [stats, setStats] = reactExports.useState({ totalReferences: 0, totalUntaggedWithMelody: 0 }); 8 const [tagHealth, setTagHealth] = reactExports.useState([]); 9 const [matchCandidates, setMatchCandidates] = reactExports.useState([]); 10 const [loading, setLoading] = reactExports.useState(false); 11 const [scanning, setScanning] = reactExports.useState(false); 12 const [applying, setApplying] = reactExports.useState(false); 13 const [scanLimit, setScanLimit] = reactExports.useState(50); 14 const [scanThreshold, setScanThreshold] = reactExports.useState(0.7); 15 const [targetTagSlug, setTargetTagSlug] = reactExports.useState(""); 16 const [selectedMatches, setSelectedMatches] = reactExports.useState({}); 17 const [refreshTrigger, setRefreshTrigger] = reactExports.useState(0); 18 const fetchStatsAndHealth = reactExports.useCallback(async () => { 19 try { 20 setLoading(true); 21 const [statsData, healthData] = await Promise.all([ 22 api.getScanTagStats(), 23 api.getScanTagHealth() 24 ]); 25 setStats(statsData); 26 setTagHealth(healthData); 27 } catch (e) { 28 console.error("Failed to load scan-tag data:", e); 29 } finally { 30 setLoading(false); 31 } 32 }, []); 33 reactExports.useEffect(() => { 34 fetchStatsAndHealth(); 35 }, [fetchStatsAndHealth, refreshTrigger]); 36 const handleScan = async () => { 37 try { 38 setScanning(true); 39 const candidates = await api.runMelodyScan({ 40 limit: scanLimit, 41 threshold: scanThreshold, 42 targetTagSlug: targetTagSlug || void 0 43 }); 44 setMatchCandidates(candidates); 45 const autoSelected = {}; 46 candidates.forEach((c) => { 47 if (c.bestMatch.confidence >= 0.85 && !c.isMismatch) { 48 autoSelected[c.midiId] = c.bestMatch.targetTagSlug; 49 } 50 }); 51 setSelectedMatches(autoSelected); 52 } catch (e) { 53 console.error("Failed to execute melody scan:", e); 54 } finally { 55 setScanning(false); 56 } 57 }; 58 const handleToggleSelect = (midiId, tagSlug) => { 59 setSelectedMatches((prev) => { 60 const next = { ...prev }; 61 if (next[midiId]) { 62 delete next[midiId]; 63 } else { 64 next[midiId] = tagSlug; 65 } 66 return next; 67 }); 68 }; 69 const handleApplySelected = async () => { 70 const list = Object.entries(selectedMatches).map(([midiId, tagSlug]) => ({ 71 midiId, 72 tagSlug 73 })); 74 if (list.length === 0) return; 75 try { 76 setApplying(true); 77 await api.applyScanTagMatches(list); 78 setMatchCandidates((prev) => prev.filter((c) => !selectedMatches[c.midiId])); 79 setSelectedMatches({}); 80 setRefreshTrigger((p) => p + 1); 81 } catch (e) { 82 console.error("Failed to apply tags:", e); 83 } finally { 84 setApplying(false); 85 } 86 }; 87 const eligibleGroups = tagHealth.filter((t) => t.hasMelodyCount >= 3); 88 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-6 space-y-6 max-w-7xl mx-auto text-zinc-800 dark:text-zinc-100", children: [ 89 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-6", children: [
90 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative overflow-hidden p-6 rounded-2xl bg-white/50 dark:bg-zinc-900/40 backdrop-blur-xl border border-zinc-200 dark:border-zinc-800/80 shadow-xl group hover:shadow-2xl transition-all duration-300", children: [ 91 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-0 right-0 p-8 opacity-10 dark:opacity-5 text-indigo-500 group-hover:scale-110 transition-transform duration-300", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { size: 120 }) }), 92 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [ 93 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-3 bg-indigo-500/10 rounded-xl text-indigo-500", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { size: 24 }) }), 94 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 95 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-semibold tracking-wide text-zinc-500 uppercase", children: "Active Reference Groups" }),
96 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-3xl font-extrabold mt-1 text-zinc-900 dark:text-white tracking-tight", children: stats.totalReferences }) 97 ] }) 98 ] }) 99 ] }), 100 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative overflow-hidden p-6 rounded-2xl bg-white/50 dark:bg-zinc-900/40 backdrop-blur-xl border border-zinc-200 dark:border-zinc-800/80 shadow-xl group hover:shadow-2xl transition-all duration-300", children: [ 101 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-0 right-0 p-8 opacity-10 dark:opacity-5 text-emerald-500 group-hover:scale-110 transition-transform duration-300", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Database, { size: 120 }) }), 102 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [ 103 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-3 bg-emerald-500/10 rounded-xl text-emerald-500", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Database, { size: 24 }) }),
104 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 105 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-semibold tracking-wide text-zinc-500 uppercase", children: "Untagged tracks with melody" }), 106 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-3xl font-extrabold mt-1 text-zinc-900 dark:text-white tracking-tight", children: stats.totalUntaggedWithMelody }) 107 ] }) 108 ] }) 109 ] }), 110 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative overflow-hidden p-6 rounded-2xl bg-white/50 dark:bg-zinc-900/40 backdrop-blur-xl border border-zinc-200 dark:border-zinc-800/80 shadow-xl group hover:shadow-2xl transition-all duration-300", children: [ 111 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-0 right-0 p-8 opacity-10 dark:opacity-5 text-indigo-500 group-hover:scale-110 transition-transform duration-300", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 120 }) }), 112 /* @__P
112URE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [ 113 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-3 bg-indigo-500/10 rounded-xl text-indigo-500", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 24 }) }), 114 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 115 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-semibold tracking-wide text-zinc-500 uppercase", children: "Status & System info" }), 116 /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-lg font-bold mt-1 text-emerald-500 flex items-center gap-1.5", children: [ 117 /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheck, { size: 18 }), 118 " Self-Referencing Engine Active" 119 ] }) 120 ] }) 121 ] }) 122 ] }) 123 ] }), 124 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex border-b border-zinc-200 dark:border-zinc-800", children: [ 125 /* @__PURE__ */ jsxRuntimeExports.jsxs( 126 "button", 127 { 128 onClick: () => setActiveTab("review"), 129 className: `px-6 py-3 font-semibold text-sm transition-all duration-200 border-b-2 flex items-center gap-2 ${activeTab === "review" ? "border-indigo-500 text-indigo-600 dark:text-indigo-400" : "border-transparent text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-200"}`, 130 children: [
131 /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 16 }), 132 "Match Review Dashboard" 133 ] 134 } 135 ), 136 /* @__PURE__ */ jsxRuntimeExports.jsxs( 137 "button", 138 { 139 onClick: () => setActiveTab("references"), 140 className: `px-6 py-3 font-semibold text-sm transition-all duration-200 border-b-2 flex items-center gap-2 ${activeTab === "references" ? "border-indigo-500 text-indigo-600 dark:text-indigo-400" : "border-transparent text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-200"}`, 141 children: [ 142 /* @__PURE__ */ jsxRuntimeExports.jsx(HeartPulse, { size: 16 }), 143 "Tag Health Dashboard" 144 ] 145 } 146 ) 147 ] }), 148 activeTab === "review" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-6", children: [ 149 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-end justify-between gap-4 p-6 rounded-2xl bg-white/30 dark:bg-zinc-900/20 backdrop-blur-md border border-zinc-200/60 dark:border-zinc-800/40", children: [
150 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap gap-4 items-center", children: [ 151 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [ 152 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 uppercase tracking-wider mb-1", children: "Max Scan Limit" }), 153 /* @__PURE__ */ jsxRuntimeExports.jsxs( 154 "select", 155 { 156 value: scanLimit, 157 onChange: (e) => setScanLimit(Number(e.target.value)), 158 className: "bg-white dark:bg-zinc-950 border border-zinc-200 dark:border-zinc-800 text-zinc-800 dark:text-zinc-200 rounded-xl px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500", 159 children: [ 160 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", value: 10, children: "10 tracks" }), 161 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", value: 50, children: "50 tracks" }),
162 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", value: 100, children: "100 tracks" }), 163 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", value: 200, children: "200 tracks" }) 164 ] 165 } 166 ) 167 ] }), 168 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [ 169 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 uppercase tracking-wider mb-1", children: "Confidence Threshold" }), 170 /* @__PURE__ */ jsxRuntimeExports.jsxs( 171 "select", 172 { 173 value: scanThreshold, 174 onChange: (e) => setScanThreshold(Number(e.target.value)), 175 className: "bg-white dark:bg-zinc-950 border border-zinc-200 dark:border-zinc-800 text-zinc-800 dark:text-zinc-200 rounded-xl px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500", 176 children: [
177 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", value: 0.5, children: "50% (Very Loose)" }), 178 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", value: 0.6, children: "60% (Loose)" }), 179 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", value: 0.7, children: "70% (Balanced)" }), 180 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", value: 0.8, children: "80% (Strict)" }), 181 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", value: 0.9, children: "90% (Very Strict)" }), 182 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", value: 0.95, children: "95% (Exact Only)" }) 183 ] 184 } 185 ) 186 ] }),
187 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [ 188 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "text-xs font-bold text-zinc-500 uppercase tracking-wider mb-1", children: "Target Tag Filter" }), 189 /* @__PURE__ */ jsxRuntimeExports.jsxs( 190 "select", 191 { 192 value: targetTagSlug, 193 onChange: (e) => setTargetTagSlug(e.target.value), 194 className: "bg-white dark:bg-zinc-950 border border-zinc-200 dark:border-zinc-800 text-zinc-800 dark:text-zinc-200 rounded-xl px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 min-w-[200px]", 195 children: [ 196 /* @__PURE__ */ jsxRuntimeExports.jsxs("option", { className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", value: "", children: [ 197 "All reference tags (", 198 stats.totalReferences, 199 ")" 200 ] }), 201 eligibleGroups.map((tag) => /* @__PURE__ */ jsxRuntimeExports.jsxs("option", { className: "bg-white dark:bg-zinc-900 text-zinc-800 dark:text-zinc-200", value: tag.slug, children: [ 202 tag.name, 203 " (", 204 tag.hasMelodyCount, 205 " references)" 206 ] }, tag.slug)) 207 ] 208 } 209 ) 210 ] }) 211 ] }),
212 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-3", children: [ 213 /* @__PURE__ */ jsxRuntimeExports.jsxs( 214 "button", 215 { 216 onClick: handleScan, 217 disabled: scanning, 218 className: "flex items-center gap-2 px-5 py-2.5 rounded-xl bg-indigo-600 hover:bg-indigo-500 text-white font-medium text-sm transition-all disabled:opacity-50 disabled:cursor-not-allowed hover:shadow-lg hover:shadow-indigo-500/20 active:scale-95", 219 children: [ 220 /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 16, className: scanning ? "animate-spin" : "" }), 221 scanning ? "Scanning database..." : "Scan Now" 222 ] 223 } 224 ), 225 matchCandidates.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs( 226 "button", 227 { 228 onClick: handleApplySelected, 229 disabled: applying || Object.keys(selectedMatches).length === 0, 230 className: "flex items-center gap-2 px-5 py-2.5 rounded-xl bg-emerald-600 hover:bg-emerald-500 text-white font-medium text-sm transition-all disabled:opacity-50 disabled:cursor-not-allowed hover:shadow-lg hover:shadow-emerald-500/20 active:scale-95", 231 children: [
232 /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { size: 16 }), 233 applying ? "Applying tags..." : `Approve & Tag Selected (${Object.keys(selectedMatches).length})` 234 ] 235 } 236 ) 237 ] }) 238 ] }), 239 scanning ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center p-16 rounded-2xl bg-white/20 dark:bg-zinc-900/10 border border-dashed border-zinc-200 dark:border-zinc-800", children: [ 240 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-20 h-20", children: [ 241 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 rounded-full border-4 border-indigo-500/20 border-t-indigo-500 animate-spin" }), 242 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-4 rounded-full border-4 border-indigo-500/40 border-b-indigo-500 animate-spin-reverse" }) 243 ] }), 244 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-6 text-sm font-medium text-zinc-500 animate-pulse font-semibold", children: "Running LCS interval comparisons against reference sets..." }) 245 ] }) : matchCandidates.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center p-16 rounded-2xl bg-white/20 dark:bg-zinc-900/10 border border-dashed border-zinc-200 dark:border-zinc-800", children: [
246 /* @__PURE__ */ jsxRuntimeExports.jsx(Info, { size: 48, className: "text-zinc-400" }), 247 /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "mt-4 font-bold text-lg text-zinc-700 dark:text-zinc-300", children: "No scan results loaded" }), 248 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-1 text-sm text-zinc-500 max-w-sm text-center", children: 'Click "Scan Now" to query the database and search for matching melodies.' }) 249 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "overflow-hidden rounded-2xl bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 shadow-xl", children: [ 250 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-5 border-b border-zinc-200 dark:border-zinc-800 flex items-center justify-between", children: [ 251 /* @__PURE__ */ jsxRuntimeExports.jsxs("h4", { className: "font-bold text-zinc-900 dark:text-white flex items-center gap-2", children: [ 252 "Match Candidates Found", 253 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 py-0.5 text-xs bg-indigo-500/10 text-indigo-500 rounded-full", children: matchCandidates.length }) 254 ] }),
255 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [ 256 /* @__PURE__ */ jsxRuntimeExports.jsx( 257 "button", 258 { 259 onClick: () => { 260 const all = {}; 261 matchCandidates.forEach((c) => { 262 if (!c.isMismatch) { 263 all[c.midiId] = c.bestMatch.targetTagSlug; 264 } 265 }); 266 setSelectedMatches(all); 267 }, 268 className: "text-xs text-indigo-500 hover:text-indigo-600 font-medium", 269 children: "Select Clean Matches" 270 } 271 ), 272 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-300 dark:text-zinc-700", children: "|" }), 273 /* @__PURE__ */ jsxRuntimeExports.jsx( 274 "button", 275 { 276 onClick: () => setSelectedMatches({}), 277 className: "text-xs text-zinc-500 hover:text-zinc-600 font-medium", 278 children: "Clear All" 279 } 280 ) 281 ] }) 282 ] }),
283 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "w-full text-left border-collapse min-w-[800px]", children: [ 284 /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "bg-zinc-50 dark:bg-zinc-950 border-b border-zinc-200 dark:border-zinc-800 text-xs font-semibold text-zinc-500 tracking-wider", children: [ 285 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-4 w-12 text-center" }), 286 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-4", children: "Midi Track" }), 287 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-4", children: "Reference Match" }), 288 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-4", children: "Target Source Tag" }), 289 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-4", children: "Confidence" }), 290 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-4 text-right", children: "Actions" }) 291 ] }) }),
292 /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { className: "divide-y divide-zinc-200 dark:divide-zinc-800", children: matchCandidates.map((c) => { 293 const isSelected = !!selectedMatches[c.midiId]; 294 const conf = c.bestMatch.confidence; 295 let confBadgeColor = "bg-red-500/10 text-red-500 border border-red-500/20"; 296 if (conf >= 0.9) { 297 confBadgeColor = "bg-emerald-500/10 text-emerald-500 border border-emerald-500/20"; 298 } else if (conf >= 0.8) { 299 confBadgeColor = "bg-amber-500/10 text-amber-500 border border-amber-500/20"; 300 } 301 return /* @__PURE__ */ jsxRuntimeExports.jsxs( 302 "tr", 303 { 304 className: `hover:bg-zinc-50 dark:hover:bg-zinc-950 transition-colors ${isSelected ? "bg-indigo-500/[0.02] dark:bg-indigo-500/[0.01]" : ""} ${c.isMismatch ? "bg-red-500/[0.02] dark:bg-red-500/[0.01]" : ""}`, 305 children: [ 306 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-4 text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 307 "input", 308 { 309 type: "checkbox", 310 checked: isSelected, 311 onChange: () => handleToggleSelect(c.midiId, c.bestMatch.targetTagSlug), 312 className: "rounded border-zinc-300 dark:border-zinc-700 text-indigo-600 focus:ring-indigo-500 h-4.5 w-4.5" 313 } 314 ) }),
315 /* @__PURE__ */ jsxRuntimeExports.jsxs("td", { className: "p-4", children: [ 316 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "font-semibold text-zinc-900 dark:text-zinc-100 flex items-center gap-2", children: [ 317 c.title, 318 c.isMismatch && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 rounded text-[10px] font-bold bg-amber-500/10 text-amber-600 dark:text-amber-400 border border-amber-500/20", children: [ 319 /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { size: 10 }), 320 " Mismatch Warning" 321 ] }) 322 ] }), 323 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-zinc-400 mt-0.5 flex flex-wrap items-center gap-x-2", children: [ 324 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [ 325 c.artist, 326 " (ID: ", 327 c.midiId, 328 ")" 329 ] }), 330 c.isMismatch && c.existingTag && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
331 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-300 dark:text-zinc-700", children: "â¢" }), 332 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-red-500 font-semibold", children: [ 333 'Currently tagged: "', 334 c.existingTag.name, 335 '"' 336 ] }) 337 ] }) 338 ] }) 339 ] }), 340 /* @__PURE__ */ jsxRuntimeExports.jsxs("td", { className: "p-4", children: [ 341 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "font-medium text-zinc-800 dark:text-zinc-200", children: [ 342 '"', 343 c.bestMatch.show, 344 '"' 345 ] }), 346 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-zinc-500 mt-0.5", children: [ 347 "Matched against ", 348 c.bestMatch.referenceCount, 349 " reference tracks" 350 ] }) 351 ] }),
352 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 py-0.5 text-xs bg-zinc-100 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 font-mono rounded text-zinc-600 dark:text-zinc-400", children: c.bestMatch.targetTagSlug }) }), 353 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-4", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 354 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: `px-2 py-0.5 text-xs font-bold font-mono rounded ${confBadgeColor}`, children: [ 355 (conf * 100).toFixed(0), 356 "%" 357 ] }), 358 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-16 h-1.5 bg-zinc-200 dark:bg-zinc-800 rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 359 "div", 360 { 361 className: `h-full ${conf >= 0.9 ? "bg-emerald-500" : conf >= 0.8 ? "bg-amber-500" : "bg-red-500"}`, 362 style: { width: `${conf * 100}%` } 363 } 364 ) }) 365 ] }) }),
366 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-4 text-right", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-end gap-2", children: [ 367 /* @__PURE__ */ jsxRuntimeExports.jsx( 368 "button", 369 { 370 onClick: () => { 371 api.applyScanTagMatches([{ midiId: c.midiId, tagSlug: c.bestMatch.targetTagSlug }]).then(() => { 372 setMatchCandidates((prev) => prev.filter((item) => item.midiId !== c.midiId)); 373 setRefreshTrigger((p) => p + 1); 374 }); 375 }, 376 className: "p-2 bg-emerald-500/10 hover:bg-emerald-500/20 text-emerald-600 dark:text-emerald-400 rounded-lg transition-colors", 377 title: "Approve & Tag", 378 children: /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { size: 16 }) 379 } 380 ),
381 /* @__PURE__ */ jsxRuntimeExports.jsx( 382 "button", 383 { 384 onClick: () => { 385 setMatchCandidates((prev) => prev.filter((item) => item.midiId !== c.midiId)); 386 }, 387 className: "p-2 bg-zinc-100 hover:bg-zinc-200 dark:bg-zinc-800 dark:hover:bg-zinc-700 text-zinc-500 rounded-lg transition-colors", 388 title: "Ignore", 389 children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 16 }) 390 } 391 ) 392 ] }) }) 393 ] 394 }, 395 c.midiId 396 ); 397 }) }) 398 ] }) }) 399 ] }) 400 ] }), 401 activeTab === "references" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-6", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "overflow-hidden rounded-2xl bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 shadow-xl", children: [
402 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-5 border-b border-zinc-200 dark:border-zinc-800", children: [ 403 /* @__PURE__ */ jsxRuntimeExports.jsxs("h4", { className: "font-bold text-zinc-900 dark:text-white flex items-center gap-2", children: [ 404 "Tag Health & Reference Sizes", 405 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "px-2 py-0.5 text-xs bg-indigo-500/10 text-indigo-500 rounded-full", children: [ 406 tagHealth.length, 407 " SOURCE tags" 408 ] }) 409 ] }), 410 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 mt-1", children: "Lists all SOURCE-type tags. A tag must have at least 3 active tracks with processed melody data to serve as a Reference Group for auto-tagging." }) 411 ] }), 412 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "w-full text-left border-collapse", children: [ 413 /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "bg-zinc-50 dark:bg-zinc-950 border-b border-zinc-200 dark:border-zinc-800 text-xs font-semibold text-zinc-500 tracking-wider", children: [
414 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-4", children: "Show/Tag Name" }), 415 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-4", children: "Tag Slug" }), 416 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-4", children: "Reference Group Size" }), 417 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-4", children: "Total Tagged Tracks" }), 418 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-4", children: "Reference Status" }) 419 ] }) }), 420 /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { className: "divide-y divide-zinc-200 dark:divide-zinc-800", children: tagHealth.map((tag) => { 421 const size = tag.hasMelodyCount; 422 let statusLabel = "Insufficient"; 423 let statusColor = "bg-red-500/10 text-red-500 border border-red-500/20"; 424 if (size >= 5) { 425 statusLabel = "Healthy Reference"; 426 statusColor = "bg-emerald-500/10 text-emerald-500 border border-emerald-500/20"; 427 } else if (size >= 3) { 428 statusLabel = "Bootstrapping"; 429 statusColor = "bg-amber-500/10 text-amber-500 border border-amber-500/20"; 430 } 431 return /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "hover:bg-zinc-50 dark:hover:bg-zinc-950 transition-colors", children: [
432 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-4 font-semibold text-zinc-950 dark:text-zinc-50", children: tag.name }), 433 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "px-2 py-0.5 bg-zinc-100 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded text-xs font-mono font-medium", children: tag.slug }) }), 434 /* @__PURE__ */ jsxRuntimeExports.jsxs("td", { className: "p-4 font-mono font-bold text-zinc-900 dark:text-white", children: [ 435 size, 436 " tracks" 437 ] }), 438 /* @__PURE__ */ jsxRuntimeExports.jsxs("td", { className: "p-4 text-zinc-500", children: [ 439 tag.count, 440 " tracks" 441 ] }), 442 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-2 py-0.5 rounded-full text-xs font-bold ${statusColor}`, children: statusLabel }) }) 443 ] }, tag.slug); 444 }) }) 445 ] }) }) 446 ] }) }) 447 ] }); 448}; 449export { 450 ScanAndTag as default 451};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.