Skip to content

Commit 53d5cb2

Browse files
feat(demo): much more descriptive Behavior & safety card (#68)
Surface everything predict() already returns but the UI hid: chemesthesis classes (cooling/pungent/astringent), stability watch-flags (oxidation/hydrolysis/photo), water-solubility (logS) estimate, TPSA / H-bond donors-acceptors / rotatable bonds, ionizable groups with typical pKa, and EU declarable-allergen labeling — organized into Properties / Chemesthesis / Stability / Regulatory / Safety sections, each value still confidence-tagged, with the full safety disclaimer. Signed-off-by: Austin L. <86896075+rvnminers-A-and-N@users.noreply.github.com>
1 parent 7bc5a10 commit 53d5cb2

1 file changed

Lines changed: 38 additions & 15 deletions

File tree

training/workbench.html

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
.kv{display:grid;grid-template-columns:auto 1fr;gap:6px 16px;margin-bottom:10px}
7878
.kv .k{font-weight:600;font-size:13px}
7979
.kv .v{font-family:var(--mono);font-size:13px;color:var(--muted)}
80+
.bsub{font-size:11px;font-weight:650;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);margin:14px 0 6px}
8081
.structure{text-align:center;margin:0 0 14px;background:#fff;border:1px solid var(--line);border-radius:8px;padding:6px}
8182
.structure svg{max-width:100%;height:auto}
8283
.structure:empty{display:none}
@@ -281,26 +282,48 @@ <h2>Aroma</h2>
281282
}
282283

283284
function renderBehavior(p){
284-
const pc = p.physchem||{}, sf = p.safety||{};
285-
const comp = pc.computed||{}, meas = pc.measured||{}, qual = pc.qualitative||{};
286-
const MEAS = ' <span class="tag measured">measured</span>';
287-
const rows = [];
288-
if(typeof comp.mol_weight==='number') rows.push(['MW', comp.mol_weight+' g/mol']);
289-
if(typeof comp.logP==='number') rows.push(['logP', comp.logP]);
285+
const pc=p.physchem||{}, sf=p.safety||{}, st=p.stability||{}, ch=p.chemesthesis||{}, lb=p.labeling||{};
286+
const comp=pc.computed||{}, est=pc.estimate||{}, meas=pc.measured||{}, qual=pc.qualitative||{};
287+
const MEAS=' <span class="tag measured">measured</span>', EST=' <span class="tag predicted">estimate</span>';
288+
const kv = rows => '<div class="kv">'+rows.map(r=>`<div class="k">${r[0]}</div><div class="v">${r[1]}</div>`).join('')+'</div>';
289+
const chips = (arr,style)=> '<div class="chips">'+arr.map(x=>`<span class="chip" ${style||''}>${x}</span>`).join('')+'</div>';
290+
const WARN='style="background:#FBEDE9;color:#8A3A22;border-color:#E7C4B8"';
291+
292+
const rows=[];
293+
if(typeof comp.mol_weight==='number') rows.push(['Molecular weight', comp.mol_weight+' g/mol']);
294+
if(typeof comp.logP==='number') rows.push(['logP (lipophilicity)', comp.logP]);
295+
if(typeof comp.tpsa==='number') rows.push(['Polar surface area', comp.tpsa+' Ų']);
296+
if(typeof comp.h_bond_donors==='number') rows.push(['H-bond donors / acceptors', comp.h_bond_donors+' / '+comp.h_bond_acceptors]);
297+
if(typeof comp.rotatable_bonds==='number') rows.push(['Rotatable bonds', comp.rotatable_bonds]);
298+
if(typeof est.water_solubility_logS==='number') rows.push(['Water solubility (logS)', est.water_solubility_logS+EST]);
290299
if(typeof meas.boiling_point_c==='number'){
291-
const atP = (typeof meas.boiling_point_pressure_mmhg==='number') ? ` (at ${meas.boiling_point_pressure_mmhg} mmHg)` : '';
300+
const atP=(typeof meas.boiling_point_pressure_mmhg==='number')?` (at ${meas.boiling_point_pressure_mmhg} mmHg)`:'';
292301
rows.push(['Boiling point', meas.boiling_point_c+' °C'+atP+MEAS]);
293302
}
294303
if(qual.aroma_volatility) rows.push(['Volatility', qual.aroma_volatility]);
295304
if(typeof meas.vapor_pressure_pa==='number') rows.push(['Vapor pressure', meas.vapor_pressure_pa+' Pa'+MEAS]);
296-
if(sf.gras_status) rows.push(['Food-use status', sf.gras_status]);
297-
if(sf.ttc_hint && sf.ttc_hint.preliminary_tier) rows.push(['TTC tier (prelim.)', sf.ttc_hint.preliminary_tier]);
298-
let html = '<div class="kv">'+rows.map(r=>`<div class="k">${r[0]}</div><div class="v">${r[1]}</div>`).join('')+'</div>';
299-
const alerts = sf.structural_alerts||[];
300-
if(alerts.length) html += '<div class="chips">'+alerts.map(a=>`<span class="chip" style="background:#FBEDE9;color:#8A3A22;border-color:#E7C4B8">⚠ ${a}</span>`).join('')+'</div>';
301-
const tox = sf.tox_screen||{};
302-
if((p.applicability||{}).in_domain!==false && tox.available && (tox.flags||[]).length) html += '<div class="chips">'+tox.flags.map(f=>`<span class="chip" style="background:#FBEDE9;color:#8A3A22;border-color:#E7C4B8">⚠ tox-assay: ${f}</span>`).join('')+'</div>';
303-
html += '<div class="empty" style="font-size:12px">Defensive screen — flags for review, never a clearance.</div>';
305+
const ions=(qual.ionizable_groups||[]).map(g=>`${g.group} (pKa≈${g.typical_pKa})`).join(', ');
306+
if(ions) rows.push(['Ionizable groups', ions]);
307+
let html = kv(rows);
308+
309+
const cc=ch.classes||[];
310+
if(cc.length) html += '<div class="bsub">Chemesthesis (trigeminal)</div>'+chips(cc,'style="background:var(--aroma);color:#fff;border-color:transparent"');
311+
312+
const sw=[...(st.oxidation_watch||[]).map(x=>'oxidation: '+x),...(st.hydrolysis_watch||[]).map(x=>'hydrolysis: '+x),...(st.photodegradation_watch||[]).map(x=>'photo: '+x)];
313+
if(sw.length) html += '<div class="bsub">Stability watch</div>'+chips(sw);
314+
315+
if(lb.eu_declarable_allergen) html += '<div class="bsub">Regulatory</div>'+chips(['EU declarable allergen'+(lb.allergen_name?': '+lb.allergen_name:'')],'style="background:#FFF6E6;border-color:#E8D38A;color:#7A5A12"');
316+
317+
html += '<div class="bsub">Safety (defensive, caution-only)</div>';
318+
const srows=[];
319+
if(sf.gras_status) srows.push(['Food-use status', sf.gras_status]);
320+
if(sf.ttc_hint && sf.ttc_hint.preliminary_tier) srows.push(['TTC tier (prelim.)', sf.ttc_hint.preliminary_tier]);
321+
if(srows.length) html += kv(srows);
322+
const alerts=sf.structural_alerts||[];
323+
if(alerts.length) html += chips(alerts.map(a=>'⚠ '+a), WARN);
324+
const tox=sf.tox_screen||{};
325+
if((p.applicability||{}).in_domain!==false && tox.available && (tox.flags||[]).length) html += chips(tox.flags.map(f=>'⚠ tox-assay: '+f), WARN);
326+
html += '<div class="empty" style="font-size:12px">'+(sf.disclaimer||'Defensive screen — flags for review, never a clearance.')+'</div>';
304327
$('behavior').innerHTML = html;
305328
}
306329

0 commit comments

Comments
 (0)