Team Ai
Apppublic

softwareDevelopment/molexplorer

sourceHugging Faceapache-2.0updated 7mo agoView on Hugging Face
0likes
index.html2236 linesDownload Raw Back to root
1<!DOCTYPE html>2<html lang="en">3<head>4<meta charset="UTF-8">5<meta name="viewport" content="width=device-width, initial-scale=1.0">6<title>Docking Pro - Complete Structural Biology Platform</title>7<link href="https://fonts.googleapis.com/css2?family=Space+Mono:ital,wght@0,400;0,700;1,400&family=Syne:wght@400;600;700;800&display=swap" rel="stylesheet">8<script src="https://3dmol.csb.pitt.edu/build/3Dmol-min.js"></script>9<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.0/chart.umd.min.js"></script>10 11<style>12*{margin:0;padding:0;box-sizing:border-box}13 14:root{15  --bg:#07090f;16  --bg2:#0d101a;17  --bg3:#141826;18  --bg4:#1c2235;19  --cyan:#00f0ff;20  --purple:#c084fc;21  --green:#34d399;22  --yellow:#fbbf24;23  --red:#f87171;24  --orange:#fb923c;25  --text:#e8edf8;26  --text2:#8892b0;27  --text3:#4a5568;28  --border:#1e2a40;29  --border2:#2a3a55;30}31 32body{33  font-family:'Syne',sans-serif;34  background:var(--bg);35  color:var(--text);36  min-height:100vh;37  background-image:38    radial-gradient(ellipse 60% 50% at 20% 10%, rgba(0,240,255,0.04) 0%, transparent 60%),39    radial-gradient(ellipse 50% 40% at 80% 90%, rgba(192,132,252,0.04) 0%, transparent 60%);40}41 42#app{max-width:1600px;margin:0 auto;padding:20px}43 44/* HEADER */45.header{46  display:flex;align-items:center;gap:20px;47  padding:18px 28px;48  background:linear-gradient(135deg,rgba(13,16,26,0.9),rgba(20,24,38,0.9));49  border:1px solid var(--border2);50  border-radius:20px;51  margin-bottom:24px;52  backdrop-filter:blur(20px);53  position:relative;overflow:hidden;54}55.header::before{56  content:'';position:absolute;top:0;left:0;right:0;height:1px;57  background:linear-gradient(90deg,transparent,var(--cyan),var(--purple),transparent);58}59.logo{60  font-size:22px;font-weight:800;letter-spacing:-0.5px;61  background:linear-gradient(135deg,var(--cyan),var(--purple));62  -webkit-background-clip:text;-webkit-text-fill-color:transparent;63}64.logo-sub{color:var(--text3);font-size:11px;font-family:'Space Mono',monospace;margin-top:2px;letter-spacing:1px}65.badges{margin-left:auto;display:flex;gap:10px;flex-wrap:wrap}66.badge{67  padding:5px 14px;border-radius:100px;font-size:11px;font-weight:700;68  font-family:'Space Mono',monospace;69  background:var(--bg3);border:1px solid var(--border);color:var(--text2);70  letter-spacing:0.5px;transition:all .2s;71}72.badge.lit{background:rgba(0,240,255,0.08);border-color:rgba(0,240,255,0.3);color:var(--cyan)}73 74/* TABS */75.tabs{76  display:flex;gap:6px;padding:6px;77  background:var(--bg2);border:1px solid var(--border);border-radius:16px;78  margin-bottom:24px;79}80.tab{81  flex:1;padding:10px 16px;background:transparent;border:none;82  color:var(--text3);font-weight:700;font-size:13px;83  font-family:'Syne',sans-serif;border-radius:10px;cursor:pointer;84  transition:all .25s;letter-spacing:0.3px;85}86.tab:hover{color:var(--text2);background:var(--bg3)}87.tab.active{88  background:linear-gradient(135deg,rgba(0,240,255,0.12),rgba(192,132,252,0.12));89  color:var(--cyan);border:1px solid rgba(0,240,255,0.25);90}91 92/* PANELS */93.panel{display:none}94.panel.active{display:block}95.grid2{display:grid;grid-template-columns:1fr 1fr;gap:20px}96@media(max-width:1100px){.grid2{grid-template-columns:1fr}}97 98/* CARD */99.card{100  background:var(--bg2);border:1px solid var(--border);101  border-radius:18px;padding:22px;margin-bottom:20px;102  transition:border-color .2s,transform .15s;103}104.card:hover{border-color:var(--border2);transform:translateY(-1px)}105.card-title{106  font-size:10px;font-weight:700;font-family:'Space Mono',monospace;107  text-transform:uppercase;letter-spacing:2px;color:var(--text3);108  margin-bottom:16px;display:flex;align-items:center;gap:8px;109}110.card-title::before{content:'';width:3px;height:12px;background:linear-gradient(var(--cyan),var(--purple));border-radius:2px}111 112/* INPUTS */113.row{display:flex;gap:10px;margin-bottom:14px}114input[type=text],input[type=password],input[type=number],select,textarea{115  width:100%;padding:10px 14px;116  background:var(--bg3);border:1px solid var(--border);border-radius:10px;117  color:var(--text);font-family:'Space Mono',monospace;font-size:13px;118  transition:border-color .2s;outline:none;119}120input:focus,select:focus,textarea:focus{border-color:rgba(0,240,255,0.4);background:var(--bg4)}121textarea{min-height:110px;resize:vertical;margin-bottom:14px}122label{font-size:11px;color:var(--text2);display:block;margin-bottom:6px;font-family:'Space Mono',monospace}123 124/* FILE UPLOAD */125.upload-zone{126  border:2px dashed var(--border2);border-radius:12px;127  padding:24px;text-align:center;cursor:pointer;128  transition:all .2s;background:var(--bg3);margin-bottom:14px;129}130.upload-zone:hover,.upload-zone.drag{border-color:var(--cyan);background:rgba(0,240,255,0.04)}131.upload-zone input{display:none}132.upload-zone .icon{font-size:28px;margin-bottom:8px}133.upload-zone p{color:var(--text2);font-size:12px;font-family:'Space Mono',monospace}134.upload-zone .hint{color:var(--text3);font-size:10px;margin-top:4px}135 136/* BUTTONS */137.btn{138  padding:10px 20px;border:1px solid var(--border);border-radius:10px;139  font-weight:700;font-size:13px;font-family:'Syne',sans-serif;cursor:pointer;140  transition:all .2s;background:var(--bg3);color:var(--text);141  white-space:nowrap;142}143.btn:hover{background:var(--bg4);border-color:var(--border2);transform:translateY(-1px)}144.btn:disabled{opacity:0.4;cursor:not-allowed;transform:none}145.btn-primary{146  background:linear-gradient(135deg,rgba(0,240,255,0.15),rgba(192,132,252,0.15));147  border-color:rgba(0,240,255,0.35);color:var(--cyan);148}149.btn-primary:hover{background:linear-gradient(135deg,rgba(0,240,255,0.25),rgba(192,132,252,0.25));box-shadow:0 4px 20px rgba(0,240,255,0.15)}150.btn-success{background:rgba(52,211,153,0.12);border-color:rgba(52,211,153,0.3);color:var(--green)}151.btn-success:hover{background:rgba(52,211,153,0.2);box-shadow:0 4px 20px rgba(52,211,153,0.1)}152.btn-full{width:100%}153.btn-sm{padding:6px 14px;font-size:11px}154 155/* VIEWERS */156.viewer-wrap{157  height:380px;background:var(--bg3);border:1px solid var(--border);158  border-radius:14px;overflow:hidden;position:relative;margin-bottom:14px;159}160.viewer-wrap .viewer{width:100%;height:100%}161.vplaceholder{162  position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);163  text-align:center;color:var(--text3);pointer-events:none;z-index:1;164}165.vplaceholder .ico{font-size:40px;margin-bottom:10px;opacity:0.3}166 167/* CHIPS */168.chips{display:flex;gap:8px;flex-wrap:wrap}169.chip{170  padding:5px 13px;background:var(--bg3);border:1px solid var(--border);171  border-radius:100px;font-size:11px;font-weight:600;cursor:pointer;172  transition:all .2s;color:var(--text2);font-family:'Space Mono',monospace;173}174.chip:hover,.chip.on{175  background:rgba(0,240,255,0.08);border-color:rgba(0,240,255,0.35);color:var(--cyan);176}177 178/* ALERTS */179.alert{padding:12px 16px;border-radius:10px;font-size:12px;margin-bottom:14px;font-family:'Space Mono',monospace;line-height:1.6}180.alert-info{background:rgba(59,130,246,0.08);border:1px solid rgba(59,130,246,0.2);color:#60a5fa}181.alert-success{background:rgba(52,211,153,0.08);border:1px solid rgba(52,211,153,0.2);color:var(--green)}182.alert-warning{background:rgba(251,191,36,0.08);border:1px solid rgba(251,191,36,0.2);color:var(--yellow)}183.alert-error{background:rgba(248,113,113,0.08);border:1px solid rgba(248,113,113,0.2);color:var(--red)}184 185/* PROGRESS */186.progress-bar{height:6px;background:var(--bg3);border-radius:6px;overflow:hidden;margin:12px 0}187.progress-fill{height:100%;background:linear-gradient(90deg,var(--cyan),var(--purple));border-radius:6px;transition:width .4s ease;width:0%}188.progress-label{text-align:center;font-size:11px;color:var(--text3);font-family:'Space Mono',monospace}189 190/* STEPS */191.steps{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:18px}192.step{193  padding:5px 12px;border-radius:100px;font-size:10px;font-weight:700;194  font-family:'Space Mono',monospace;background:var(--bg3);border:1px solid var(--border);color:var(--text3);195}196.step.running{background:rgba(0,240,255,0.08);border-color:var(--cyan);color:var(--cyan);animation:pulse 1.2s infinite}197.step.done{background:rgba(52,211,153,0.08);border-color:var(--green);color:var(--green)}198@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}}199 200/* PROPS */201.prop-row{202  display:flex;justify-content:space-between;align-items:center;203  padding:9px 0;border-bottom:1px solid var(--border);204}205.prop-key{color:var(--text2);font-size:12px;font-family:'Space Mono',monospace}206.prop-val{color:var(--text);font-weight:600;font-family:'Space Mono',monospace;font-size:12px}207 208/* BEST POSE CARD */209.best-pose-card{210  background:linear-gradient(135deg,rgba(52,211,153,0.06),rgba(0,240,255,0.06));211  border:1px solid rgba(52,211,153,0.25);border-radius:16px;padding:24px;212  text-align:center;margin-bottom:18px;213}214.best-energy{215  font-size:48px;font-weight:800;color:var(--green);216  line-height:1;margin-bottom:6px;217}218.best-unit{font-size:14px;color:var(--text2);font-family:'Space Mono',monospace}219.best-ki{220  margin-top:12px;padding:8px 16px;221  background:rgba(0,0,0,0.3);border-radius:8px;222  font-family:'Space Mono',monospace;font-size:12px;color:var(--text2);223  display:inline-block;224}225 226/* SMILES BOX */227.smiles-box{228  background:var(--bg3);border:1px solid var(--border);border-radius:10px;229  padding:14px;font-family:'Space Mono',monospace;font-size:11px;230  color:var(--yellow);word-break:break-all;margin:10px 0;231}232 233/* CHAT */234.chat-window{235  height:360px;overflow-y:auto;background:var(--bg3);236  border:1px solid var(--border);border-radius:14px;237  padding:16px;margin-bottom:14px;display:flex;flex-direction:column;gap:10px;238  scrollbar-width:thin;scrollbar-color:var(--border) transparent;239}240.msg{padding:10px 14px;border-radius:12px;max-width:82%;font-size:12px;line-height:1.7;font-family:'Space Mono',monospace}241.msg.user{background:rgba(0,240,255,0.08);border:1px solid rgba(0,240,255,0.2);color:var(--cyan);align-self:flex-end}242.msg.assistant{background:var(--bg2);border:1px solid var(--border);color:var(--text);align-self:flex-start}243.msg.sys{background:rgba(59,130,246,0.07);border:1px solid rgba(59,130,246,0.15);color:#60a5fa;align-self:center;text-align:center;max-width:100%;font-size:11px}244 245/* TOAST */246#toast{position:fixed;bottom:28px;right:28px;z-index:9999;display:none;max-width:380px}247.toast-inner{248  background:var(--bg2);border:1px solid var(--border2);border-radius:14px;249  padding:14px 18px;display:flex;align-items:center;gap:12px;250  box-shadow:0 20px 40px rgba(0,0,0,0.5);backdrop-filter:blur(16px);251}252.toast-msg{flex:1;font-size:12px;font-family:'Space Mono',monospace}253.toast-x{cursor:pointer;opacity:0.4}254.toast-x:hover{opacity:1}255 256/* SPINNER */257.spin{258  display:inline-block;width:14px;height:14px;259  border:2px solid var(--border2);border-top-color:var(--cyan);260  border-radius:50%;animation:sp 0.8s linear infinite;vertical-align:middle;margin-right:6px;261}262@keyframes sp{to{transform:rotate(360deg)}}263 264/* IFRAME for PubChem Sketcher */265.sketch-frame{266  width:100%;267  height:480px;268  border:1px solid var(--border);269  border-radius:12px;270  background:#ffffff;271  margin-bottom:14px;272}273 274/* MarVliew container */275.marvin-container{276  width:100%;277  height:480px;278  border:1px solid var(--border);279  border-radius:12px;280  background:#ffffff;281  margin-bottom:14px;282  position:relative;283}284.marvin-container iframe{285  width:100%;286  height:100%;287  border-radius:12px;288}289 290/* MolView link styling */291.molview-link{292  display:flex;293  align-items:center;294  justify-content:space-between;295  background:var(--bg3);296  border:1px solid var(--border);297  border-radius:12px;298  padding:16px;299  margin-bottom:14px;300}301 302.molview-icon{303  font-size:32px;304  opacity:0.8;305}306 307/* INTERACTION TABLE */308.interaction-table{width:100%;border-collapse:collapse;font-family:'Space Mono',monospace;font-size:11px}309.interaction-table th{padding:7px 12px;text-align:left;color:var(--text3);font-weight:700;border-bottom:1px solid var(--border);background:var(--bg3)}310.interaction-table td{padding:8px 12px;border-bottom:1px solid var(--border);color:var(--text)}311.int-tag{padding:2px 8px;border-radius:100px;font-size:10px;font-weight:700}312.int-hbond{background:rgba(59,130,246,0.12);color:#60a5fa;border:1px solid rgba(59,130,246,0.25)}313.int-hydro{background:rgba(251,191,36,0.1);color:var(--yellow);border:1px solid rgba(251,191,36,0.2)}314.int-pi{background:rgba(192,132,252,0.1);color:var(--purple);border:1px solid rgba(192,132,252,0.2)}315.int-vdw{background:rgba(52,211,153,0.1);color:var(--green);border:1px solid rgba(52,211,153,0.2)}316 317/* FOOTER */318.footer{319  margin-top:40px;padding:36px;background:var(--bg2);320  border:1px solid var(--border);border-radius:20px;text-align:center;321  position:relative;overflow:hidden;322}323.footer::before{324  content:'';position:absolute;bottom:0;left:0;right:0;height:1px;325  background:linear-gradient(90deg,transparent,var(--purple),var(--cyan),transparent);326}327.footer-email{328  display:inline-flex;align-items:center;gap:14px;329  padding:12px 28px;border-radius:100px;cursor:pointer;330  background:linear-gradient(135deg,rgba(0,240,255,0.08),rgba(192,132,252,0.08));331  border:1px solid rgba(0,240,255,0.2);margin-top:18px;332  transition:all .2s;font-family:'Space Mono',monospace;font-size:12px;333}334.footer-email:hover{transform:translateY(-2px);box-shadow:0 10px 30px rgba(0,240,255,0.1)}335 336/* Grid for docking params */337.param-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;margin:14px 0}338.param-grid .param-item label{font-size:10px;margin-bottom:4px}339.param-grid .param-item input{font-size:12px;padding:8px 12px}340 341/* Range slider */342input[type=range]{343  -webkit-appearance:none;width:100%;height:4px;344  background:var(--bg4);border-radius:4px;outline:none;margin:8px 0;345}346input[type=range]::-webkit-slider-thumb{347  -webkit-appearance:none;width:16px;height:16px;348  background:linear-gradient(135deg,var(--cyan),var(--purple));349  border-radius:50%;cursor:pointer;350}351 352/* Sequence analysis */353.aa-bar{height:8px;background:var(--bg3);border-radius:4px;overflow:hidden;margin:4px 0}354.aa-fill{height:100%;border-radius:4px;transition:width .5s ease}355 356</style>357</head>358<body>359<div id="app">360 361<!-- HEADER -->362<div class="header">363  <div>364    <div class="logo">⚛ Docking Pro</div>365    <div class="logo-sub">STRUCTURAL BIOLOGY PLATFORM v2.0</div>366  </div>367  <div class="badges">368    <span id="b-protein" class="badge">🧬 Protein: None</span>369    <span id="b-ligand" class="badge">💊 Ligand: None</span>370    <span id="b-api" class="badge">🔑 API: —</span>371  </div>372</div>373 374<!-- TABS -->375<div class="tabs">376  <button class="tab active" data-tab="protein">🧬 Protein</button>377  <button class="tab" data-tab="molecule">💊 Molecule</button>378  <button class="tab" data-tab="draw">✏️ Sketch</button>379  <button class="tab" data-tab="docking">🎯 Docking</button>380  <button class="tab" data-tab="sequence">🔬 Sequence</button>381  <button class="tab" data-tab="ai">🤖 AI</button>382</div>383 384<!-- ═══════════════ PROTEIN PANEL ═══════════════ -->385<div id="protein-panel" class="panel active">386  <div class="grid2">387    <div>388      <div class="card">389        <div class="card-title">Load from Database</div>390        <div class="row">391          <input type="text" id="pdb-input" placeholder="PDB ID (e.g. 6LU7)" oninput="this.value=this.value.toUpperCase()">392          <button class="btn btn-primary" onclick="loadPDB()">Load</button>393        </div>394        <div class="row">395          <input type="text" id="uniprot-input" placeholder="UniProt ID for AlphaFold">396          <button class="btn btn-success" onclick="loadAlphaFold()">AlphaFold</button>397        </div>398        <div class="chips">399          <span class="chip" onclick="setPDB('1HHO')">1HHO</span>400          <span class="chip" onclick="setPDB('6LU7')">6LU7</span>401          <span class="chip" onclick="setPDB('3HTB')">3HTB</span>402          <span class="chip" onclick="setPDB('1AKE')">1AKE</span>403          <span class="chip" onclick="setPDB('4HHB')">4HHB</span>404        </div>405      </div>406 407      <!-- FILE UPLOAD for Protein -->408      <div class="card">409        <div class="card-title">Upload Protein File</div>410        <div class="upload-zone" id="protein-upload-zone" onclick="document.getElementById('protein-file').click()" ondragover="handleDrag(event,'protein-upload-zone')" ondrop="handleProteinDrop(event)" ondragleave="leaveDrag('protein-upload-zone')">411          <input type="file" id="protein-file" accept=".pdb,.ent,.cif" onchange="loadProteinFile(event)">412          <div class="icon">📁</div>413          <p>Click or drag & drop protein file</p>414          <div class="hint">Accepts: .pdb, .ent, .cif</div>415        </div>416        <div id="protein-file-status"></div>417      </div>418 419      <div class="card">420        <div class="card-title">Properties</div>421        <div id="protein-props"><div class="alert alert-info">Load a protein to view properties</div></div>422      </div>423      <div id="protein-dl" style="display:none">424        <button class="btn btn-full" onclick="downloadPDB()">⬇ Download PDB</button>425      </div>426    </div>427 428    <div>429      <div class="card">430        <div class="card-title">3D Viewer</div>431        <div class="viewer-wrap">432          <div id="pv" class="viewer"></div>433          <div class="vplaceholder" id="pp"><div class="ico">🧬</div><div>Load a protein structure</div></div>434        </div>435        <div class="chips" id="protein-style-chips">436          <span class="chip on" onclick="setStyle('protein','cartoon',this)">Cartoon</span>437          <span class="chip" onclick="setStyle('protein','surface',this)">Surface</span>438          <span class="chip" onclick="setStyle('protein','stick',this)">Stick</span>439          <span class="chip" onclick="setStyle('protein','sphere',this)">Sphere</span>440        </div>441      </div>442      <div class="card">443        <div class="card-title">Ramachandran Plot</div>444        <canvas id="rama-canvas" style="width:100%;height:260px"></canvas>445        <button class="btn btn-full" style="margin-top:14px" onclick="genRama()">Generate</button>446      </div>447    </div>448  </div>449</div>450 451<!-- ═══════════════ MOLECULE PANEL ═══════════════ -->452<div id="molecule-panel" class="panel">453  <div class="grid2">454    <div>455      <div class="card">456        <div class="card-title">Search PubChem</div>457        <div class="row">458          <input type="text" id="pubchem-input" placeholder="Compound name (e.g. aspirin)">459          <button class="btn btn-primary" onclick="searchPubChem()">Search</button>460        </div>461      </div>462 463      <!-- FILE UPLOAD for Ligand -->464      <div class="card">465        <div class="card-title">Upload Ligand File</div>466        <div class="upload-zone" id="ligand-upload-zone" onclick="document.getElementById('ligand-file').click()" ondragover="handleDrag(event,'ligand-upload-zone')" ondrop="handleLigandDrop(event)" ondragleave="leaveDrag('ligand-upload-zone')">467          <input type="file" id="ligand-file" accept=".sdf,.mol,.mol2,.pdb" onchange="loadLigandFile(event)">468          <div class="icon">📁</div>469          <p>Click or drag & drop ligand file</p>470          <div class="hint">Accepts: .sdf, .mol, .mol2, .pdb</div>471        </div>472        <div id="ligand-file-status"></div>473      </div>474 475      <div class="card">476        <div class="card-title">IUPAC → 3D Structure</div>477        <div class="alert alert-info">Uses OpenMolecules OPSIN + CIR + PubChem cascade</div>478        <div class="row">479          <input type="text" id="iupac-input" placeholder="Enter IUPAC name">480          <button class="btn btn-success" onclick="convertIUPAC()">Convert</button>481        </div>482        <div id="iupac-sources" class="converter-source"></div>483        <div id="iupac-result"></div>484        <div class="chips" style="margin-top:12px">485          <span class="chip" onclick="ti('aspirin')">Aspirin</span>486          <span class="chip" onclick="ti('caffeine')">Caffeine</span>487          <span class="chip" onclick="ti('ibuprofen')">Ibuprofen</span>488          <span class="chip" onclick="ti('morphine')">Morphine</span>489          <span class="chip" onclick="ti('penicillin')">Penicillin</span>490        </div>491      </div>492 493      <div class="card">494        <div class="card-title">Molecular Properties</div>495        <div id="mol-props"><div class="alert alert-info">Load a molecule to view properties</div></div>496      </div>497 498      <div class="card">499        <div class="card-title">Lipinski Rule of Five</div>500        <div id="lipinski"><div class="alert alert-info">Load a molecule first</div></div>501      </div>502 503      <div id="mol-dl" style="display:none">504        <button class="btn btn-full" onclick="downloadSDF()">⬇ Download SDF</button>505      </div>506    </div>507 508    <div>509      <div class="card">510        <div class="card-title">3D Viewer — Ligand</div>511        <div class="viewer-wrap">512          <div id="lv" class="viewer"></div>513          <div class="vplaceholder" id="lp"><div class="ico">💊</div><div>Load a molecule</div></div>514        </div>515        <div class="chips" id="ligand-style-chips">516          <span class="chip on" onclick="setStyle('ligand','stick',this)">Stick</span>517          <span class="chip" onclick="setStyle('ligand','sphere',this)">Sphere</span>518          <span class="chip" onclick="setStyle('ligand','surface',this)">Surface</span>519          <span class="chip" onclick="setStyle('ligand','line',this)">Line</span>520        </div>521      </div>522    </div>523  </div>524</div>525 526<!-- ═══════════════ SKETCH PANEL ═══════════════ -->527<div id="draw-panel" class="panel">528  <div class="grid2">529    <div>530      <div class="card">531        <div class="card-title">PubChem Sketcher</div>532        <div class="alert alert-info">Draw molecules directly in PubChem's online sketcher</div>533        534        <!-- PubChem Sketcher Iframe -->535        <iframe id="pubchem-sketch" class="sketch-frame" src="https://pubchem.ncbi.nlm.nih.gov/edit2/index.html" allow="clipboard-read; clipboard-write"></iframe>536        537        <div style="display:flex;gap:8px;margin-top:12px;flex-wrap:wrap">538          <button class="btn btn-primary" style="flex:1" onclick="getFromPubChemSketch()">📋 Get SMILES from Sketcher</button>539          <button class="btn btn-success" style="flex:1" onclick="loadSketchAsLigand()">🔬 Load as Ligand</button>540          <button class="btn" onclick="clearSketch()">🗑 Clear</button>541        </div>542        <div id="sketch-smiles-out" class="smiles-box" style="margin-top:10px;display:none"></div>543      </div>544 545      <div class="card">546        <div class="card-title">Marvin JS (Alternative)</div>547        <div class="alert alert-info">Advanced chemical editor from ChemAxon</div>548        549        <!-- Marvin JS Container -->550        <div id="marvin-container" class="marvin-container">551          <div style="padding:40px;text-align:center;color:var(--text3)">552            Loading Marvin JS...<br>553            <span class="spin"></span>554          </div>555        </div>556        557        <div style="display:flex;gap:8px;margin-top:12px">558          <button class="btn btn-primary" style="flex:1" onclick="getFromMarvin()">📋 Get SMILES</button>559          <button class="btn btn-success" style="flex:1" onclick="loadMarvinAsLigand()">🔬 Load as Ligand</button>560        </div>561      </div>562 563      <div class="card">564        <div class="card-title">MolView (External)</div>565        <div class="molview-link">566          <div>567            <div style="font-weight:700;margin-bottom:4px">⚛ MolView</div>568            <div style="font-size:11px;color:var(--text2)">Open in new tab - draw, then export SMILES</div>569          </div>570          <div class="molview-icon">🔗</div>571        </div>572        <div class="row">573          <input type="text" id="molview-smiles" placeholder="Paste SMILES from MolView">574          <button class="btn btn-success" onclick="loadMolViewSMILES()">Load</button>575        </div>576        <div class="chips" style="margin-top:8px">577          <span class="chip" onclick="window.open('https://molview.org', '_blank')">Open MolView ↗</span>578          <span class="chip" onclick="document.getElementById('molview-smiles').value = 'c1ccccc1'">Benzene</span>579          <span class="chip" onclick="document.getElementById('molview-smiles').value = 'CC(=O)Oc1ccccc1C(=O)O'">Aspirin</span>580        </div>581      </div>582    </div>583 584    <div>585      <div class="card">586        <div class="card-title">3D Preview</div>587        <div class="viewer-wrap">588          <div id="dv" class="viewer"></div>589          <div class="vplaceholder" id="dp"><div class="ico">✏️</div><div>Draw and load structure</div></div>590        </div>591        <div class="chips">592          <span class="chip on" onclick="setStyle('draw','stick',this)">Stick</span>593          <span class="chip" onclick="setStyle('draw','sphere',this)">Sphere</span>594          <span class="chip" onclick="setStyle('draw','surface',this)">Surface</span>595        </div>596      </div>597      598      <div class="card">599        <div class="card-title">Recent Sketches</div>600        <div id="recent-sketches">601          <div class="alert alert-info">No recent sketches</div>602        </div>603      </div>604    </div>605  </div>606</div>607 608<!-- ═══════════════ DOCKING PANEL ═══════════════ -->609<div id="docking-panel" class="panel">610  <div class="grid2">611    <div>612      <div class="card">613        <div class="card-title">Status</div>614        <div id="dk-prot-status" class="alert alert-warning">⚠ No protein loaded</div>615        <div id="dk-lig-status" class="alert alert-warning">⚠ No ligand loaded</div>616      </div>617 618      <div class="card">619        <div class="card-title">AutoDock Vina Pipeline</div>620        <div class="steps" id="dock-steps">621          <span class="step" id="s1">1 · Protein</span>622          <span class="step" id="s2">2 · Ligand</span>623          <span class="step" id="s3">3 · Grid</span>624          <span class="step" id="s4">4 · Dock</span>625          <span class="step" id="s5">5 · Result</span>626        </div>627        <div class="progress-bar"><div class="progress-fill" id="dock-prog"></div></div>628        <div class="progress-label" id="dock-label">Ready</div>629 630        <div class="param-grid" style="margin-top:18px">631          <div class="param-item"><label>Center X</label><input type="number" id="bx" value="0" step="1"></div>632          <div class="param-item"><label>Center Y</label><input type="number" id="by" value="0" step="1"></div>633          <div class="param-item"><label>Center Z</label><input type="number" id="bz" value="0" step="1"></div>634          <div class="param-item"><label>Size X</label><input type="number" id="sx" value="20" min="10" max="60"></div>635          <div class="param-item"><label>Size Y</label><input type="number" id="sy" value="20" min="10" max="60"></div>636          <div class="param-item"><label>Size Z</label><input type="number" id="sz" value="20" min="10" max="60"></div>637        </div>638 639        <label>Exhaustiveness: <b id="ex-val">8</b></label>640        <input type="range" id="exhaustiveness" min="1" max="32" value="8" oninput="document.getElementById('ex-val').textContent=this.value">641 642        <button class="btn btn-primary btn-full" style="margin-top:18px" onclick="runDocking()" id="dock-btn">▶ Run Full Docking</button>643      </div>644    </div>645 646    <div>647      <!-- RESULTS: only best pose shown -->648      <div id="dock-results"></div>649 650      <div class="card">651        <div class="card-title">Complex Viewer — Best Pose</div>652        <div class="viewer-wrap">653          <div id="cv" class="viewer"></div>654          <div class="vplaceholder" id="cp"><div class="ico">🎯</div><div>Run docking to see complex</div></div>655        </div>656        <div class="chips" id="complex-style-chips">657          <span class="chip on" onclick="complexStyle('default',this)">Default</span>658          <span class="chip" onclick="complexStyle('surface',this)">Surface</span>659          <span class="chip" onclick="complexStyle('cartoon',this)">Cartoon</span>660        </div>661      </div>662    </div>663  </div>664</div>665 666<!-- ═══════════════ SEQUENCE PANEL ═══════════════ -->667<div id="sequence-panel" class="panel">668  <div class="grid2">669    <div>670      <div class="card">671        <div class="card-title">Protein Structure Prediction</div>672 673        <div class="alert alert-info" style="font-size:11px;margin-bottom:14px">674          <strong>ESMFold</strong> (Meta AI) — real all-atom prediction via public API, no key needed.<br>675          <strong>AlphaFold DB</strong> — fetches pre-computed structure if UniProt ID is known.<br>676          <strong>RaptorX / Robetta</strong> — redirect to web server (requires manual submission).677        </div>678 679        <textarea id="seq-input" placeholder=">protein_name&#10;MVLSPADKTNVKAAWGKVGAHAG...&#10;&#10;Plain sequence or FASTA format. Standard single-letter amino acids only."></textarea>680 681        <div id="seq-len-info" style="font-size:10px;color:var(--text3);font-family:'Space Mono',monospace;margin-bottom:10px;text-align:right"></div>682 683        <div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:14px">684          <button class="btn btn-primary" id="btn-esmfold" onclick="runESMFold()">🔬 ESMFold (Meta AI)</button>685          <button class="btn btn-success" id="btn-alphafold" onclick="runAlphaFoldDB()">🧬 AlphaFold DB</button>686          687        </div>688 689        <div id="seq-progress" style="display:none">690          <div class="alert alert-info">691            <span class="spin"></span>692            <span id="seq-progress-msg">Submitting to prediction server…</span>693          </div>694          <div class="progress-bar"><div class="progress-fill" id="seq-prog-fill"></div></div>695        </div>696 697        <div id="seq-result"></div>698 699        <div style="margin-top:14px">700          <div style="font-size:10px;font-family:'Space Mono',monospace;color:var(--text3);margin-bottom:8px;text-transform:uppercase;letter-spacing:1px">Example sequences</div>701          <div class="chips">702            <span class="chip" onclick="loadSeq('villin')">Villin HP35 (35aa)</span>703            <span class="chip" onclick="loadSeq('insulin')">Insulin B-chain (30aa)</span>704            <span class="chip" onclick="loadSeq('lysozyme')">Lysozyme (129aa)</span>705            <span class="chip" onclick="loadSeq('hemoglobin')">Hemoglobin α (141aa)</span>706            <span class="chip" onclick="loadSeq('trp-cage')">Trp-cage (20aa)</span>707          </div>708        </div>709      </div>710 711      <div class="card">712        <div class="card-title">Sequence Analysis</div>713        <div id="seq-analysis"><div class="alert alert-info">Enter a sequence to analyze</div></div>714      </div>715 716      <div class="card">717        <div class="card-title">Use Loaded Protein</div>718        <button class="btn btn-full" onclick="extractSeq()">↩ Extract Sequence from Loaded Protein</button>719        <button class="btn btn-full" style="margin-top:8px" id="btn-use-pred" onclick="usePredAsProtein()" disabled>⬆ Use Predicted Structure for Docking</button>720      </div>721    </div>722 723    <div>724      <div class="card">725        <div class="card-title">Predicted Structure Viewer</div>726        <div class="viewer-wrap" style="height:420px">727          <div id="predv" class="viewer"></div>728          <div class="vplaceholder" id="predp"><div class="ico">🧬</div><div>Run prediction to see structure</div></div>729        </div>730        <div class="chips" id="pred-style-chips">731          <span class="chip on" onclick="setStyle('pred','cartoon',this)">Cartoon</span>732          <span class="chip" onclick="setPredpLDDT(this)">pLDDT Colors</span>733          <span class="chip" onclick="setStyle('pred','surface',this)">Surface</span>734          <span class="chip" onclick="setStyle('pred','stick',this)">Stick</span>735        </div>736      </div>737 738      <div class="card">739        <div class="card-title">Structure Quality Metrics</div>740        <div id="struct-quality"><div class="alert alert-info">Run prediction to see metrics</div></div>741        <div id="plddt-legend" style="display:none;margin-top:12px">742          <div style="font-size:10px;font-family:'Space Mono',monospace;color:var(--text3);margin-bottom:6px">pLDDT CONFIDENCE SCALE</div>743          <div style="display:flex;gap:6px;flex-wrap:wrap">744            <span style="padding:3px 10px;border-radius:6px;font-size:10px;font-family:'Space Mono',monospace;background:rgba(0,83,214,0.2);border:1px solid #0053d6;color:#60a5fa">90-100 Very High</span>745            <span style="padding:3px 10px;border-radius:6px;font-size:10px;font-family:'Space Mono',monospace;background:rgba(101,203,243,0.15);border:1px solid #65cbf3;color:#93e8ff">70-90 Confident</span>746            <span style="padding:3px 10px;border-radius:6px;font-size:10px;font-family:'Space Mono',monospace;background:rgba(255,219,19,0.15);border:1px solid #ffdb13;color:#fbbf24">50-70 Low</span>747            <span style="padding:3px 10px;border-radius:6px;font-size:10px;font-family:'Space Mono',monospace;background:rgba(255,125,69,0.15);border:1px solid #ff7d45;color:#fb923c">below 50 Very Low</span>748          </div>749        </div>750      </div>751 752      <div id="pred-download-card" style="display:none">753        <button class="btn btn-full" onclick="downloadPredPDB()">⬇ Download Predicted PDB</button>754      </div>755    </div>756  </div>757</div>758 759<!-- ═══════════════ AI PANEL ═══════════════ -->760<div id="ai-panel" class="panel">761  <div class="grid2">762    <div>763      <div class="card">764        <div class="card-title">API Configuration (Groq)</div>765        <label>API Key</label>766        <input type="password" id="api-key" placeholder="gsk_..." style="margin-bottom:10px">767        <label>Model</label>768        <select id="api-model" style="margin-bottom:10px">769          <option value="llama-3.1-8b-instant">Llama 3.1 8B (Fast)</option>770          <option value="llama-3.3-70b-versatile">Llama 3.3 70B (Smart)</option>771          <option value="mixtral-8x7b-32768">Mixtral 8x7B</option>772          <option value="gemma2-9b-it">Gemma 2 9B</option>773        </select>774        <button class="btn btn-primary btn-full" onclick="testAPI()">Test Connection</button>775        <div id="api-status" class="alert alert-info" style="margin-top:12px">API not configured</div>776      </div>777 778      <div class="card">779        <div class="card-title">Knowledge Base</div>780        <button class="btn btn-full" onclick="loadExampleRAG()">Load Example Documents</button>781        <div id="rag-docs" style="margin-top:10px"></div>782      </div>783 784      <div class="card">785        <div class="card-title">Context-Aware Query</div>786        <textarea id="rag-query" placeholder="Ask about protein, ligand, docking results…" style="height:80px"></textarea>787        <button class="btn btn-success btn-full" onclick="queryRAG()">Ask with Context</button>788        <div id="rag-response" style="margin-top:12px"></div>789      </div>790    </div>791 792    <div>793      <div class="card" style="height:580px;display:flex;flex-direction:column">794        <div class="card-title">AI Chat</div>795        <div class="chat-window" id="chat-win">796          <div class="msg sys">Set your Groq API key to start chatting</div>797        </div>798        <div class="row">799          <input type="text" id="chat-input" placeholder="Type message…" onkeypress="if(event.key==='Enter')sendChat()">800          <button class="btn btn-primary" onclick="sendChat()">Send</button>801        </div>802        <div class="chips" style="margin-top:10px">803          <span class="chip" onclick="qp('Analyze binding affinity')">Binding</span>804          <span class="chip" onclick="qp('Explain docking results')">Docking</span>805          <span class="chip" onclick="qp('Lipinski analysis')">Drug-like</span>806          <span class="chip" onclick="qp('Suggest modifications')">Design</span>807        </div>808      </div>809    </div>810  </div>811</div>812 813<!-- FOOTER -->814<div class="footer">815  <div style="font-size:22px;font-weight:800;background:linear-gradient(135deg,var(--cyan),var(--purple));-webkit-background-clip:text;-webkit-text-fill-color:transparent">⚛ Docking Pro</div>816  <div style="color:var(--text3);margin-top:6px;font-family:'Space Mono',monospace;font-size:11px">COMPLETE STRUCTURAL BIOLOGY PLATFORM</div>817  <div class="footer-email" onclick="copyEmail()">818    <span>📧</span><span><a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="0263716a6f6b766a636f65636c6771636c42656f636b6e2c616d6f">[email&#160;protected]</a></span><span style="opacity:0.4;font-size:10px">(click to copy)</span>819  </div>820</div>821 822</div><!-- #app -->823 824<!-- TOAST -->825<div id="toast">826  <div class="toast-inner">827    <span class="toast-msg" id="toast-msg"></span>828    <span class="toast-x" onclick="document.getElementById('toast').style.display='none'">✕</span>829  </div>830</div>831 832<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script>833<script>834// ═══════════════════════ STATE ═══════════════════════835const S = {836  proteinPDB:null, proteinId:null, proteinSrc:null,837  ligandSDF:null, ligandProps:null, ligandCID:null, ligandSMILES:null,838  dockingResult:null,839  ragDocs:[], chatHistory:[],840  apiKey:'', apiModel:'llama-3.1-8b-instant',841  viewers:{protein:null,ligand:null,complex:null,draw:null,pred:null},842  ramaChart:null,843  recentSketches: [] // Store recent SMILES844};845 846// ═══════════════════════ UTIL ═══════════════════════847function toast(msg, type='info'){848  const colors={info:'#60a5fa',success:'#34d399',warning:'#fbbf24',error:'#f87171'};849  document.getElementById('toast-msg').textContent=msg;850  const t=document.getElementById('toast');851  t.style.display='block';852  t.querySelector('.toast-inner').style.borderLeft=`3px solid ${colors[type]}`;853  setTimeout(()=>{t.style.display='none'},4000);854}855const sleep=ms=>new Promise(r=>setTimeout(r,ms));856 857function updateBadges(){858  document.getElementById('b-protein').textContent=S.proteinId?`🧬 ${S.proteinSrc}: ${S.proteinId}`:'🧬 Protein: None';859  document.getElementById('b-ligand').textContent=S.ligandCID?`💊 CID: ${S.ligandCID}`:(S.ligandSDF?'💊 Ligand: Loaded':'💊 Ligand: None');860  document.getElementById('b-api').textContent=S.apiKey?'🔑 API: ✓':'🔑 API: —';861  document.getElementById('b-protein').className='badge'+(S.proteinId?' lit':'');862  document.getElementById('b-ligand').className='badge'+(S.ligandSDF?' lit':'');863  document.getElementById('b-api').className='badge'+(S.apiKey?' lit':'');864}865 866// ═══════════════════════ TABS ═══════════════════════867document.querySelectorAll('.tab').forEach(t=>{868  t.addEventListener('click',()=>{869    document.querySelectorAll('.tab').forEach(x=>x.classList.remove('active'));870    document.querySelectorAll('.panel').forEach(x=>x.classList.remove('active'));871    t.classList.add('active');872    document.getElementById(t.dataset.tab+'-panel').classList.add('active');873    874    // Initialize Marvin when sketch tab is opened875    if(t.dataset.tab === 'draw') {876      initMarvin();877    }878  });879});880 881// ═══════════════════════ VIEWERS ═══════════════════════882function getViewer(key){883  const ids={protein:'pv',ligand:'lv',complex:'cv',draw:'dv',pred:'predv'};884  if(!S.viewers[key]){885    const el=document.getElementById(ids[key]);886    if(el) S.viewers[key]=$3Dmol.createViewer(el,{backgroundColor:'#141826'});887  }888  return S.viewers[key];889}890 891function setStyle(viewerKey, style, chip){892  const parent={893    protein:'#protein-style-chips',894    ligand:'#ligand-style-chips',895    complex:'#complex-style-chips',896    draw:'.draw-panel .chips',897    pred:'#pred-style-chips'898  };899  // deactivate chips in same group900  if(chip){901    const p=chip.closest('.chips,#protein-style-chips,#ligand-style-chips,#complex-style-chips,#pred-style-chips');902    if(p) p.querySelectorAll('.chip').forEach(c=>c.classList.remove('on'));903    chip.classList.add('on');904  }905  const v=getViewer(viewerKey);906  if(!v) return;907  v.setStyle({},{});908  try{ v.removeAllSurfaces(); }catch(e){}909  if(style==='cartoon') v.setStyle({},{cartoon:{color:'spectrum'}});910  else if(style==='stick') v.setStyle({},{stick:{radius:.15},sphere:{scale:.25}});911  else if(style==='sphere') v.setStyle({},{sphere:{scale:.35}});912  else if(style==='surface'){913    v.setStyle({},{cartoon:{color:'spectrum',opacity:0.1}});914    v.addSurface($3Dmol.SurfaceType.VDW,{opacity:.7,colorscheme:'greenCarbon'});915  }916  else if(style==='line') v.setStyle({},{line:{}});917  v.render();918}919 920// ═══════════════════════ PROTEIN ═══════════════════════921function initProteinViewer(pdb,id,src){922  S.proteinPDB=pdb; S.proteinId=id; S.proteinSrc=src;923  const v=getViewer('protein');924  v.clear();925  v.addModel(pdb,'pdb');926  v.setStyle({},{cartoon:{color:'spectrum'}});927  v.zoomTo(); v.render();928  document.getElementById('pp').style.display='none';929 930  const atoms=(pdb.match(/^ATOM/gm)||[]).length;931  const res=(pdb.match(/^\s*CA\s/gm)||pdb.match(/ CA /g)||[]).length;932  document.getElementById('protein-props').innerHTML=`933    <div class="prop-row"><span class="prop-key">ID</span><span class="prop-val">${id}</span></div>934    <div class="prop-row"><span class="prop-key">Source</span><span class="prop-val">${src}</span></div>935    <div class="prop-row"><span class="prop-key">ATOM records</span><span class="prop-val">${atoms.toLocaleString()}</span></div>936    <div class="prop-row"><span class="prop-key">~Residues (Cα)</span><span class="prop-val">${res}</span></div>937  `;938  document.getElementById('protein-dl').style.display='block';939 940  // Auto box center from CA coords941  const caLines=pdb.split('\n').filter(l=>l.startsWith('ATOM')&&l.substring(13,15).trim()==='CA');942  if(caLines.length>0){943    let x=0,y=0,z=0;944    caLines.forEach(l=>{x+=parseFloat(l.substring(30,38));y+=parseFloat(l.substring(38,46));z+=parseFloat(l.substring(46,54));});945    document.getElementById('bx').value=Math.round(x/caLines.length);946    document.getElementById('by').value=Math.round(y/caLines.length);947    document.getElementById('bz').value=Math.round(z/caLines.length);948  }949  updateBadges(); updateDockStatus();950}951 952function setPDB(id){document.getElementById('pdb-input').value=id;loadPDB();}953async function loadPDB(){954  const id=document.getElementById('pdb-input').value.trim().toUpperCase();955  if(!id) return; toast(`Loading PDB ${id}…`);956  try{957    const r=await fetch(`https://files.rcsb.org/download/${id}.pdb`);958    if(!r.ok) throw new Error('PDB not found');959    initProteinViewer(await r.text(),id,'PDB');960    toast(`Loaded ${id}`,'success');961  }catch(e){toast(e.message,'error');}962}963async function loadAlphaFold(){964  const id=document.getElementById('uniprot-input').value.trim();965  if(!id) return; toast(`Fetching AlphaFold for ${id}…`);966  try{967    const r=await fetch(`https://alphafold.ebi.ac.uk/api/prediction/${id}`);968    if(!r.ok) throw new Error('UniProt ID not found');969    const d=await r.json();970    const url=d[0]?.pdbUrl; if(!url) throw new Error('No structure');971    const pdb=await (await fetch(url)).text();972    initProteinViewer(pdb,id,'AlphaFold');973    toast(`AlphaFold ${id} loaded`,'success');974  }catch(e){toast(e.message,'error');}975}976 977// FILE UPLOAD - Protein978function loadProteinFile(event){979  const file=event.target.files[0];980  if(!file) return;981  const reader=new FileReader();982  reader.onload=e=>{983    const pdb=e.target.result;984    // Determine format985    let fmt='pdb';986    if(file.name.endsWith('.cif')) fmt='cif';987    initProteinViewer(pdb,file.name.replace(/\.[^.]+$/,''),'Upload');988    document.getElementById('protein-file-status').innerHTML=`<div class="alert alert-success">✓ Loaded: ${file.name} (${(file.size/1024).toFixed(1)} KB)</div>`;989    toast(`Protein file loaded: ${file.name}`,'success');990  };991  reader.readAsText(file);992}993function handleDrag(e,id){e.preventDefault();document.getElementById(id).classList.add('drag');}994function leaveDrag(id){document.getElementById(id).classList.remove('drag');}995function handleProteinDrop(e){996  e.preventDefault();leaveDrag('protein-upload-zone');997  const file=e.dataTransfer.files[0];998  if(file){document.getElementById('protein-file').files=e.dataTransfer.files;loadProteinFile({target:{files:e.dataTransfer.files}});}999}1000function handleLigandDrop(e){1001  e.preventDefault();leaveDrag('ligand-upload-zone');1002  const file=e.dataTransfer.files[0];1003  if(file){document.getElementById('ligand-file').files=e.dataTransfer.files;loadLigandFile({target:{files:e.dataTransfer.files}});}1004}1005 1006// FILE UPLOAD - Ligand1007function loadLigandFile(event){1008  const file=event.target.files[0];1009  if(!file) return;1010  const reader=new FileReader();1011  reader.onload=e=>{1012    const content=e.target.result;1013    const name=file.name.replace(/\.[^.]+$/,'');1014    // Use content as SDF (works for .sdf, .mol)1015    const sdf=content;1016    const props={1017      MolecularFormula:'From File',MolecularWeight:'—',1018      XLogP:'—',HBondDonorCount:'—',HBondAcceptorCount:'—',1019      RotatableBondCount:'—',HeavyAtomCount:'—',TPSA:'—',CanonicalSMILES:'—'1020    };1021    // Try to extract MW from SDF M  MW field1022    const mwMatch=content.match(/> <MW>\s*([\d.]+)/);1023    if(mwMatch) props.MolecularWeight=mwMatch[1]+' Da';1024    const smilesMatch=content.match(/> <SMILES>\s*(\S+)/);1025    if(smilesMatch) props.CanonicalSMILES=smilesMatch[1];1026 1027    initLigandViewer(sdf,props,'Upload');1028    document.getElementById('ligand-file-status').innerHTML=`<div class="alert alert-success">✓ Loaded: ${file.name} (${(file.size/1024).toFixed(1)} KB)</div>`;1029    toast(`Ligand file loaded: ${file.name}`,'success');1030  };1031  reader.readAsText(file);1032}1033 1034function initLigandViewer(sdf,props,cid){1035  S.ligandSDF=sdf; S.ligandProps=props; S.ligandCID=cid; S.ligandSMILES=props?.CanonicalSMILES;1036 1037  const lv=getViewer('ligand');1038  lv.clear(); lv.addModel(sdf,'sdf');1039  lv.setStyle({},{stick:{radius:.15},sphere:{scale:.25}});1040  lv.zoomTo(); lv.render();1041  document.getElementById('lp').style.display='none';1042 1043  const dv=getViewer('draw');1044  dv.clear(); dv.addModel(sdf,'sdf');1045  dv.setStyle({},{stick:{radius:.15},sphere:{scale:.25}});1046  dv.zoomTo(); dv.render();1047  document.getElementById('dp').style.display='none';1048 1049  document.getElementById('mol-dl').style.display='block';1050 1051  if(props&&props.MolecularFormula!=='From File'){1052    const rows=[1053      ['Formula',props.MolecularFormula],['MW',props.MolecularWeight+' Da'],1054      ['LogP',props.XLogP],['HBD',props.HBondDonorCount],1055      ['HBA',props.HBondAcceptorCount],['Rotatable Bonds',props.RotatableBondCount],1056      ['Heavy Atoms',props.HeavyAtomCount],['TPSA',props.TPSA+' Ų']1057    ];1058    document.getElementById('mol-props').innerHTML=rows.map(([k,v])=>1059      v&&v!=='undefined'&&v!=='null'?`<div class="prop-row"><span class="prop-key">${k}</span><span class="prop-val">${v}</span></div>`:''1060    ).join('');1061 1062    const mw=parseFloat(props.MolecularWeight)||0;1063    const lp=parseFloat(props.XLogP)||0;1064    const hbd=parseInt(props.HBondDonorCount)||0;1065    const hba=parseInt(props.HBondAcceptorCount)||0;1066    const v=(mw>500?1:0)+(lp>5?1:0)+(hbd>5?1:0)+(hba>10?1:0);1067    document.getElementById('lipinski').innerHTML=`1068      <div class="alert ${v===0?'alert-success':v<3?'alert-warning':'alert-error'}">${v} violation(s) of Lipinski's Rule of Five</div>1069      ${[['MW ≤500',mw.toFixed(1),mw<=500],['LogP ≤5',lp.toFixed(2),lp<=5],['HBD ≤5',hbd,hbd<=5],['HBA ≤10',hba,hba<=10]]1070        .map(([k,vl,ok])=>`<div class="prop-row"><span class="prop-key">${k}</span><span class="prop-val">${vl} ${ok?'✓':'✗'}</span></div>`)1071        .join('')}1072    `;1073  } else {1074    document.getElementById('mol-props').innerHTML=`<div class="alert alert-success">✓ Ligand file loaded successfully</div>`;1075    document.getElementById('lipinski').innerHTML=`<div class="alert alert-info">Upload an SDF from PubChem for full property analysis</div>`;1076  }1077  updateBadges(); updateDockStatus();1078}1079 1080// ═══════════════════════ PUBCHEM ═══════════════════════1081async function searchPubChem(){1082  const q=document.getElementById('pubchem-input').value.trim();1083  if(!q) return; toast(`Searching PubChem for ${q}…`);1084  try{1085    const r=await fetch(`https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/name/${encodeURIComponent(q)}/cids/JSON`);1086    if(!r.ok) throw new Error('Compound not found');1087    const d=await r.json();1088    const cid=d.IdentifierList?.CID?.[0];1089    if(!cid) throw new Error('No CID found');1090    await loadCID(cid);1091  }catch(e){toast(e.message,'error');}1092}1093 1094async function loadCID(cid){1095  toast(`Loading CID ${cid}…`);1096  try{1097    const [pr,sr]=await Promise.all([1098      fetch(`https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/${cid}/property/MolecularFormula,MolecularWeight,CanonicalSMILES,XLogP,HBondDonorCount,HBondAcceptorCount,RotatableBondCount,HeavyAtomCount,TPSA/JSON`),1099      fetch(`https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/cid/${cid}/SDF?record_type=3d`)1100    ]);1101    const propd=await pr.json();1102    const props=propd.PropertyTable?.Properties[0];1103    const sdf=await sr.text();1104    initLigandViewer(sdf,props,cid);1105    toast(`Loaded CID ${cid}`,'success');1106  }catch(e){toast(`Error: ${e.message}`,'error');}1107}1108 1109// ═══════════════════════ SKETCHER INTEGRATIONS ═══════════════════════1110 1111// PubChem Sketcher1112function getFromPubChemSketch() {1113  const iframe = document.getElementById('pubchem-sketch');1114  // Post message to PubChem sketcher to get SMILES1115  iframe.contentWindow.postMessage({1116    type: 'getSMILES'1117  }, 'https://pubchem.ncbi.nlm.nih.gov');1118  1119  toast('Requesting SMILES from sketcher...', 'info');1120  1121  // Listen for response (one-time)1122  window.addEventListener('message', function onSketchMessage(event) {1123    if (event.origin !== 'https://pubchem.ncbi.nlm.nih.gov') return;1124    1125    if (event.data && event.data.smiles) {1126      const smiles = event.data.smiles;1127      document.getElementById('sketch-smiles-out').style.display = 'block';1128      document.getElementById('sketch-smiles-out').textContent = smiles;1129      S.recentSketches.push(smiles);1130      updateRecentSketches();1131      toast('SMILES captured from sketcher', 'success');1132    }1133    window.removeEventListener('message', onSketchMessage);1134  });1135}1136 1137function loadSketchAsLigand() {1138  const smiles = document.getElementById('sketch-smiles-out').textContent;1139  if (!smiles || smiles === '') {1140    toast('No SMILES available. Click "Get SMILES from Sketcher" first', 'warning');1141    return;1142  }1143  loadFromSMILES(smiles, 'PubChem Sketch');1144}1145 1146function clearSketch() {1147  document.getElementById('sketch-smiles-out').style.display = 'none';1148  document.getElementById('sketch-smiles-out').textContent = '';1149  // Reload iframe to clear sketcher1150  document.getElementById('pubchem-sketch').src = 'https://pubchem.ncbi.nlm.nih.gov/edit2/index.html';1151}1152 1153// Marvin JS1154let marvinInstance = null;1155 1156function initMarvin() {1157  const container = document.getElementById('marvin-container');1158  1159  // Check if already loaded1160  if (document.getElementById('marvin-iframe')) return;1161  1162  // Create iframe for Marvin1163  const iframe = document.createElement('iframe');1164  iframe.id = 'marvin-iframe';1165  iframe.src = 'https://marvinjs.chemicalize.com/editor.html';1166  iframe.style.width = '100%';1167  iframe.style.height = '100%';1168  iframe.style.border = 'none';1169  iframe.style.borderRadius = '12px';1170  1171  container.innerHTML = '';1172  container.appendChild(iframe);1173  1174  iframe.onload = () => {1175    toast('Marvin JS loaded', 'success');1176  };1177}1178 1179function getFromMarvin() {1180  const iframe = document.getElementById('marvin-iframe');1181  if (!iframe) {1182    toast('Marvin not loaded yet', 'warning');1183    return;1184  }1185  1186  // Post message to Marvin to get SMILES1187  iframe.contentWindow.postMessage({1188    action: 'getSmiles'1189  }, 'https://marvinjs.chemicalize.com');1190  1191  window.addEventListener('message', function onMarvinMessage(event) {1192    if (event.origin !== 'https://marvinjs.chemicalize.com') return;1193    1194    if (event.data && event.data.smiles) {1195      const smiles = event.data.smiles;1196      document.getElementById('sketch-smiles-out').style.display = 'block';1197      document.getElementById('sketch-smiles-out').textContent = smiles;1198      S.recentSketches.push(smiles);1199      updateRecentSketches();1200      toast('SMILES captured from Marvin', 'success');

Showing the first 1,200 of 2236 lines. Download the file for the rest.