tone3000: IR downloads, model detail, arch filter, metadata
- NAM/IR tab toggle in Downloads overlay - Architecture filter (All/Nano/Feather/Standard) - filters via Supabase architecture_version column - Model detail overlay: tap a search result to see full description, gear info, author, pack, architecture, thumbnail before downloading - IR search + install with .meta.json metadata saving - Backend: search_models/get_trending_models accept arch param - Backend: tone_gear field added to model search response
This commit is contained in:
@@ -226,7 +226,18 @@ body{display:flex;flex-direction:column;max-height:100vh;user-select:none;-webki
|
||||
<!-- Downloads Overlay -->
|
||||
<div class="overlay" id="overlayDownloads">
|
||||
<div class="overlay-hdr"><span class="overlay-title">⬇ Downloads</span><button class="overlay-close" id="closeDownloads">✕</button></div>
|
||||
<div class="overlay-body">
|
||||
<!-- Tab + filter nav -->
|
||||
<div class="settings-nav">
|
||||
<button class="sn-btn active" id="dlTabNam" onclick="switchDlTab('nam')">🎛 NAM</button>
|
||||
<button class="sn-btn" id="dlTabIr" onclick="switchDlTab('ir')">📦 IR</button>
|
||||
</div>
|
||||
<div class="settings-nav" style="border-bottom:1px solid var(--border);padding-top:0">
|
||||
<button class="sn-btn active" data-arch="" onclick="setArch(this,'')">All</button>
|
||||
<button class="sn-btn" data-arch="nano" onclick="setArch(this,'nano')">Nano</button>
|
||||
<button class="sn-btn" data-arch="feather" onclick="setArch(this,'feather')">Feather</button>
|
||||
<button class="sn-btn" data-arch="standard" onclick="setArch(this,'standard')">Standard</button>
|
||||
</div>
|
||||
<div class="overlay-body" style="padding-top:4px">
|
||||
<div style="display:flex;gap:6px;margin-bottom:6px">
|
||||
<input id="dlSearch" placeholder="Search Tone3000..." style="flex:1;padding:6px 8px;border-radius:6px;border:1px solid var(--border);background:var(--surface);color:var(--text);font-family:var(--font);font-size:11px;outline:none;min-height:36px">
|
||||
<button class="sr-action primary" id="dlSearchBtn">🔍</button>
|
||||
@@ -235,6 +246,14 @@ body{display:flex;flex-direction:column;max-height:100vh;user-select:none;-webki
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Model Detail Overlay -->
|
||||
<div class="overlay" id="overlayDetail">
|
||||
<div class="overlay-hdr"><span class="overlay-title" id="dlDetailTitle">Model</span><button class="overlay-close">✕</button></div>
|
||||
<div class="overlay-body">
|
||||
<div id="dlDetailContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Overlay -->
|
||||
<div class="overlay" id="overlaySettings">
|
||||
<div class="overlay-hdr"><span class="overlay-title">⚙ Settings</span><button class="overlay-close" id="closeSettings">✕</button></div>
|
||||
@@ -1006,31 +1025,80 @@ function init(){
|
||||
try{await API.saveSnapshot(currentChannel,slot);toast('Saved to slot '+slot);loadSnapshots();}catch(e){toast(e.message);}
|
||||
};
|
||||
|
||||
// Downloads search
|
||||
document.getElementById('dlSearchBtn').onclick=async()=>{
|
||||
// Downloads - state
|
||||
let dlTab='nam', dlArch='';
|
||||
|
||||
function switchDlTab(tab){
|
||||
dlTab=tab;
|
||||
document.getElementById('dlTabNam').className='sn-btn'+(tab==='nam'?' active':'');
|
||||
document.getElementById('dlTabIr').className='sn-btn'+(tab==='ir'?' active':'');
|
||||
const archRow=document.querySelectorAll('#overlayDownloads > .settings-nav')[1];
|
||||
if(archRow)archRow.style.display=tab==='nam'?'flex':'none';
|
||||
doDlSearch();
|
||||
}
|
||||
function setArch(btn,arch){
|
||||
document.querySelectorAll('#overlayDownloads .settings-nav')[1].querySelectorAll('.sn-btn').forEach(b=>b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
dlArch=arch;
|
||||
doDlSearch();
|
||||
}
|
||||
async function doDlSearch(){
|
||||
const q=document.getElementById('dlSearch').value.trim();
|
||||
if(!q)return;
|
||||
const res=document.getElementById('dlResults');
|
||||
res.innerHTML='<div style="text-align:center;padding:16px"><span class="spinner"></span></div>';
|
||||
try{
|
||||
const data=await API._fetch('/api/models/tonedownload/search?q='+encodeURIComponent(q)+'&page=0');
|
||||
const endpoint=dlTab==='nam'?'/api/models/tonedownload/search':'/api/irs/tonedownload/search';
|
||||
const params='?q='+encodeURIComponent(q)+(dlArch?'&arch='+dlArch:'')+'&page=0';
|
||||
const data=await API._fetch(endpoint+params);
|
||||
const items=data.results||[];
|
||||
res.innerHTML='';
|
||||
if(items.length===0){res.innerHTML='<div style="text-align:center;padding:16px;color:var(--text-dim);font-size:11px">No results</div>';return;}
|
||||
if(items.length===0){res.innerHTML='<div style="text-align:center;padding:16px;color:var(--text-dim);font-size:11px">No results'+(q?'':' \u2014 tap Search or type a query')+'</div>';return;}
|
||||
items.forEach(item=>{
|
||||
const d=document.createElement('div');
|
||||
d.className='preset-card';
|
||||
d.style.cursor='default';
|
||||
d.innerHTML='<div class="pc-name">'+(item.name||'Unknown')+'</div><div class="pc-num">'+(item.author||'')+(item.pack_name?' · '+item.pack_name:'')+'</div><div style="font-size:8px;color:var(--text-dim);margin-top:2px">'+(item.size_display||'')+(item.architecture?' · '+item.architecture:'')+'</div><button class="sr-action primary" style="margin-top:4px;align-self:flex-end">Get</button>';
|
||||
d.querySelector('button').onclick=async()=>{
|
||||
d.querySelector('button').textContent='...';
|
||||
try{await API._fetch('/api/models/tonedownload/install',{method:'POST',body:JSON.stringify({id:item.id,download_url:item.download_url,name:item.name,filename:item.filename,size:item.size,author:item.author,author_avatar:item.author_avatar,thumbnail:item.thumbnail,tone_description:item.tone_description,pack_name:item.pack_name,architecture:item.architecture,created_at:item.created_at}),timeout:60000});toast('Downloaded!');d.querySelector('button').textContent='✓';}catch(e){toast(e.message);d.querySelector('button').textContent='Retry';}
|
||||
};
|
||||
d.style.cursor='pointer';
|
||||
const meta=[];
|
||||
if(item.size_display)meta.push(item.size_display);
|
||||
if(item.architecture)meta.push(item.architecture);
|
||||
if(item.tone_gear&&item.tone_gear!=='nam')meta.push(item.tone_gear);
|
||||
d.innerHTML='<div class="pc-name">'+(item.name||'Unknown')+'</div><div class="pc-num">'+(item.author||'')+(item.pack_name?' \u00b7 '+item.pack_name:'')+'</div><div style="font-size:8px;color:var(--text-dim);margin-top:2px">'+meta.join(' \u00b7 ')+'</div>';
|
||||
d.onclick=()=>showDlDetail(item);
|
||||
res.appendChild(d);
|
||||
});
|
||||
}catch(e){res.innerHTML='<div style="text-align:center;padding:16px;color:var(--text-dim)">Error</div>';}
|
||||
};
|
||||
document.getElementById('dlSearch').onkeydown=(e)=>{if(e.key==='Enter')document.getElementById('dlSearchBtn').click();};
|
||||
}
|
||||
function showDlDetail(item){
|
||||
const c=document.getElementById('dlDetailContent');
|
||||
const isIr=dlTab==='ir';
|
||||
let html='<div style="margin-bottom:8px">';
|
||||
if(item.thumbnail)html+='<img src="'+item.thumbnail+'" style="width:100%;border-radius:8px;margin-bottom:6px;max-height:160px;object-fit:cover">';
|
||||
html+='<div style="font-size:14px;font-weight:700;margin-bottom:4px">'+(item.name||'Unknown')+'</div>';
|
||||
html+='<div style="font-size:11px;color:var(--text-sec);margin-bottom:6px">by '+(item.author||'Unknown')+'</div>';
|
||||
if(item.pack_name)html+='<div style="font-size:10px;color:var(--text-dim);margin-bottom:4px">Pack: '+item.pack_name+'</div>';
|
||||
if(item.architecture)html+='<div style="font-size:10px;color:var(--text-dim);margin-bottom:4px">Arch: '+item.architecture+'</div>';
|
||||
if(item.size_display)html+='<div style="font-size:10px;color:var(--text-dim);margin-bottom:4px">Size: '+item.size_display+'</div>';
|
||||
if(item.tone_gear)html+='<div style="font-size:10px;color:var(--text-dim);margin-bottom:4px">Gear: '+item.tone_gear+'</div>';
|
||||
if(item.tone_description)html+='<div style="font-size:10px;color:var(--text2);margin-top:6px;padding:6px 8px;background:var(--surface);border-radius:6px;border:1px solid var(--border);line-height:1.4">'+(item.tone_description||'')+'</div>';
|
||||
html+='</div>';
|
||||
html+='<button class="sr-action primary" id="dlDetailBtn" style="width:100%;min-height:42px;font-size:13px">Download'+(isIr?' IR':'')+'</button>';
|
||||
c.innerHTML=html;
|
||||
document.getElementById('dlDetailTitle').textContent=(item.name||'Model');
|
||||
document.getElementById('overlayDetail').classList.add('open');
|
||||
document.getElementById('dlDetailBtn').onclick=async()=>{
|
||||
const btn=document.getElementById('dlDetailBtn');
|
||||
btn.textContent='...';
|
||||
try{
|
||||
const endpoint=isIr?'/api/irs/tonedownload/install':'/api/models/tonedownload/install';
|
||||
await API._fetch(endpoint,{method:'POST',body:JSON.stringify({id:item.id,download_url:item.download_url,name:item.name,filename:item.filename,size:item.size,author:item.author,author_avatar:item.author_avatar,thumbnail:item.thumbnail,tone_description:item.tone_description,pack_name:item.pack_name,architecture:item.architecture,created_at:item.created_at}),timeout:60000});
|
||||
toast('Downloaded!');
|
||||
btn.textContent='Downloaded';
|
||||
btn.className='sr-action';
|
||||
}catch(e){toast(e.message);btn.textContent='Retry';}
|
||||
};
|
||||
}
|
||||
// Wire downloads search
|
||||
document.getElementById('dlSearchBtn').onclick=doDlSearch;
|
||||
document.getElementById('dlSearch').onkeydown=(e)=>{if(e.key==='Enter')doDlSearch();};
|
||||
|
||||
// Start polling
|
||||
startPolling();
|
||||
|
||||
Reference in New Issue
Block a user