Skip to content

Commit 67e0e49

Browse files
feat: compare two molecules side by side
Add a 'Compare molecules' panel — two inputs (with the rich typeahead) and example pairs (vanillin vs ethylvanillin, R- vs S-carvone, limonene vs menthol). Compare renders the two branded flavor cards side by side (reuses /api/card), so taste, aroma, and structure line up at a glance — handy for a reformulation or a chirality pair. Each card opens the full read on click; responsive (stacks on mobile). Verified with Playwright: both cards render for vanillin vs ethylvanillin. Signed-off-by: Austin L. <86896075+rvnminers-A-and-N@users.noreply.github.com>
1 parent 8dc2398 commit 67e0e49

1 file changed

Lines changed: 46 additions & 1 deletion

File tree

training/workbench.html

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,17 @@
312312
.mix-btn{font-family:var(--ui);font-weight:600;font-size:14px;padding:9px 18px;border:none;border-radius:8px;background:var(--accent);color:#fff;cursor:pointer}
313313
.mix-btn:disabled{opacity:.5}
314314
#mixResults{margin-top:14px}
315+
/* compare molecules */
316+
.cmp-inputs{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin:0 0 12px}
317+
.cmp-in{flex:1;min-width:200px;font:inherit;font-size:13.5px;padding:9px 12px;border:1px solid var(--line);border-radius:8px;background:var(--panel);color:var(--ink)}
318+
.cmp-in:focus{outline:none;border-color:var(--brand-2)}
319+
.cmp-vs{font-family:'Cinzel Decorative',Georgia,serif;color:var(--cream);font-size:13px}
320+
.cmp-results{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:14px}
321+
@media(max-width:760px){.cmp-results{grid-template-columns:1fr}}
322+
.cmp-card{border:1px solid var(--line);border-radius:12px;overflow:hidden;background:#0F1319;min-height:120px;cursor:pointer;transition:border-color .14s}
323+
.cmp-card:hover{border-color:var(--brand-2)}
324+
.cmp-card img{display:block;width:100%;height:auto}
325+
.cmp-card .empty{padding:20px}
315326
.mix-ings{display:flex;flex-direction:column;gap:6px;margin-bottom:8px}
316327
.mix-ing{display:flex;gap:10px;align-items:center;padding:8px;border:1px solid var(--line);border-radius:8px;background:var(--surface)}
317328
.mix-ing:hover{background:var(--accent-soft)}
@@ -680,6 +691,24 @@ <h1>Flavormancer</h1>
680691
<div id="mixResults"></div>
681692
</div>
682693

694+
<label class="toggle"><input type="checkbox" id="cmpToggle"> <span id="cmpCaret"></span> Compare molecules — two reads side by side</label>
695+
<div id="cmpPanel" style="display:none">
696+
<p class="mix-explain">Enter two molecules to see their <b>flavor cards side by side</b> — compare
697+
taste, aroma, and structure at a glance (great for a reformulation or a chirality pair).</p>
698+
<div class="cmp-inputs">
699+
<input id="cmpA" class="cmp-in" autocomplete="off" placeholder="name or SMILES — e.g. vanillin">
700+
<span class="cmp-vs">vs</span>
701+
<input id="cmpB" class="cmp-in" autocomplete="off" placeholder="name or SMILES — e.g. ethylvanillin">
702+
<button class="mix-btn" id="cmpGo">Compare</button>
703+
</div>
704+
<div class="mix-examples"><span>Try:</span>
705+
<button type="button" class="mix-ex cmp-ex" data-a="vanillin" data-b="ethylvanillin">vanillin vs ethyl&nbsp;vanillin</button>
706+
<button type="button" class="mix-ex cmp-ex" data-a="(R)-carvone" data-b="(S)-carvone">R- vs S-carvone</button>
707+
<button type="button" class="mix-ex cmp-ex" data-a="limonene" data-b="menthol">limonene vs menthol</button>
708+
</div>
709+
<div id="cmpResults" class="cmp-results"></div>
710+
</div>
711+
683712
<div id="browse" class="browse">
684713
<div class="browse-head">Or browse the library — pick a category, then a molecule (each card has a 2D/3D toggle)</div>
685714
<div id="browseCats" class="browse-cats"></div>
@@ -1924,7 +1953,7 @@ <h4>Software &amp; type</h4>
19241953
});
19251954
if($('mixToggle').checked && !$('mixRows').children.length){ mixAddRow(); mixAddRow(); } // open by default
19261955
// click a bad-combo example -> load its ingredients + process and run the screen
1927-
document.querySelectorAll('.mix-ex').forEach(b=> b.addEventListener('click', ()=>{
1956+
document.querySelectorAll('.mix-ex:not(.cmp-ex)').forEach(b=> b.addEventListener('click', ()=>{
19281957
$('mixRows').innerHTML='';
19291958
b.dataset.ings.split('|').forEach(s=>mixAddRow(s));
19301959
document.querySelectorAll('.proc').forEach(c=>{ c.checked = (b.dataset.proc||'').split(',').includes(c.value); });
@@ -1934,6 +1963,22 @@ <h4>Software &amp; type</h4>
19341963
const card=e.target.closest('.mix-ing[data-smi]');
19351964
if(card && card.dataset.smi){ $('mixToggle').checked=false; $('mixToggle').dispatchEvent(new Event('change')); q.value=card.dataset.smi; window.scrollTo({top:0,behavior:'smooth'}); run(); }
19361965
});
1966+
1967+
// --- compare two molecules: side-by-side flavor cards (reuses /api/card) ---
1968+
$('cmpToggle').addEventListener('change', e=>{ $('cmpPanel').style.display=e.target.checked?'block':'none'; $('cmpCaret').textContent=e.target.checked?'▾':'▸'; });
1969+
attachRichSuggest($('cmpA')); attachRichSuggest($('cmpB'));
1970+
function cmpRun(){
1971+
const a=$('cmpA').value.trim(), b=$('cmpB').value.trim();
1972+
if(!a||!b){ $('cmpResults').innerHTML='<div class="empty">Enter two molecules to compare.</div>'; return; }
1973+
$('cmpResults').innerHTML=[a,b].map(m=>{ const e=m.replace(/"/g,'&quot;');
1974+
return `<div class="cmp-card" data-q="${e}" title="Open the full read"><img loading="lazy" src="/api/card?q=${encodeURIComponent(m)}" alt="flavor card for ${e}" onerror="this.parentElement.textContent='No read for that molecule'"></div>`;
1975+
}).join('');
1976+
}
1977+
$('cmpGo').addEventListener('click', cmpRun);
1978+
$('cmpA').addEventListener('keydown', e=>{ if(e.key==='Enter') cmpRun(); });
1979+
$('cmpB').addEventListener('keydown', e=>{ if(e.key==='Enter') cmpRun(); });
1980+
$('cmpResults').addEventListener('click', e=>{ const c=e.target.closest('.cmp-card[data-q]'); if(c){ q.value=c.dataset.q; window.scrollTo({top:0,behavior:'smooth'}); run(); } });
1981+
document.querySelectorAll('.cmp-ex').forEach(b=> b.addEventListener('click', ()=>{ $('cmpA').value=b.dataset.a; $('cmpB').value=b.dataset.b; cmpRun(); }));
19371982
$('mixCheck').addEventListener('click', async ()=>{
19381983
const ings=[...$('mixRows').querySelectorAll('input')].map(i=>i.value.trim()).filter(Boolean);
19391984
const procs=[...document.querySelectorAll('.proc:checked')].map(c=>c.value);

0 commit comments

Comments
 (0)