|
1 | | - |
2 | 1 | <!doctype html> |
3 | 2 | <html lang="en"> |
4 | 3 | <head> |
|
16 | 15 | <line x1='10' y1='16' x2='22' y2='10' stroke='white' stroke-width='2'/> |
17 | 16 | <line x1='10' y1='16' x2='22' y2='22' stroke='white' stroke-width='2'/> |
18 | 17 | </svg>"> |
19 | | - |
| 18 | + |
20 | 19 | <style> |
21 | 20 | :root { |
22 | 21 | --bg: #f8fafc; |
|
221 | 220 | border-color: var(--accent); |
222 | 221 | } |
223 | 222 |
|
224 | | -.controls { |
225 | | - margin-top: 0.75rem; |
226 | | - display: flex; |
227 | | - gap: 0.5rem; |
228 | | - flex-wrap: wrap; |
229 | | -} |
| 223 | + .controls { |
| 224 | + margin-top: 0.75rem; |
| 225 | + display: grid; |
| 226 | + grid-template-columns: repeat(2, 1fr); |
| 227 | + gap: 0.5rem; |
| 228 | + } |
230 | 229 |
|
231 | | -button { |
232 | | - appearance: none; |
233 | | - border: 1px solid var(--border); |
234 | | - background: #fff; |
235 | | - color: var(--text); |
236 | | - padding: 0.4rem 0.7rem; |
237 | | - border-radius: 8px; |
238 | | - font-size: 0.85rem; |
239 | | - cursor: pointer; |
240 | | -} |
| 230 | + .controls button { |
| 231 | + display: inline-flex; |
| 232 | + align-items: center; |
| 233 | + gap: 4px; |
| 234 | + } |
| 235 | + |
| 236 | + .controls button svg { |
| 237 | + width: 14px; |
| 238 | + height: 14px; |
| 239 | + } |
| 240 | + |
| 241 | + button { |
| 242 | + appearance: none; |
| 243 | + border: 1px solid var(--border); |
| 244 | + background: #fff; |
| 245 | + color: var(--text); |
| 246 | + padding: 0.4rem 0.7rem; |
| 247 | + border-radius: 8px; |
| 248 | + font-size: 0.85rem; |
| 249 | + cursor: pointer; |
| 250 | + width: auto; |
| 251 | + } |
241 | 252 |
|
242 | 253 | button.primary { |
243 | 254 | background: var(--accent); |
|
265 | 276 | border-color: #22c55e; |
266 | 277 | } |
267 | 278 |
|
268 | | -.drop { |
269 | | - margin-top: 0.75rem; |
270 | | - border: 1px dashed var(--border); |
271 | | - border-radius: 8px; |
272 | | - padding: 0.6rem; |
273 | | - font-size: 0.8rem; |
274 | | - color: var(--muted); |
275 | | - text-align: center; |
276 | | -} |
| 279 | + .drop { |
| 280 | + margin-top: 0.75rem; |
| 281 | + border: 1px dashed var(--border); |
| 282 | + border-radius: 8px; |
| 283 | + padding: 0.6rem; |
| 284 | + font-size: 0.8rem; |
| 285 | + color: var(--muted); |
| 286 | + text-align: center; |
| 287 | + margin-bottom: 1rem; |
| 288 | + } |
| 289 | + |
| 290 | + .separator { |
| 291 | + margin-top: 0.75rem; |
| 292 | + margin-bottom: 0.75rem; |
| 293 | + text-align: center; |
| 294 | + font-size: 0.75rem; |
| 295 | + color: var(--muted); |
| 296 | + } |
277 | 297 |
|
278 | 298 | #error { |
279 | 299 | margin-top: 0.75rem; |
|
291 | 311 | .grid { grid-template-columns: 1fr; } |
292 | 312 | } |
293 | 313 |
|
294 | | -svg { |
295 | | - max-width: 100%; |
296 | | - border: 1px solid var(--border); |
297 | | - border-radius: 6px; |
298 | | - background: #fff; |
299 | | -} |
| 314 | + #svg svg { |
| 315 | + width: 100%; |
| 316 | + max-width: 100%; |
| 317 | + border: 1px solid var(--border); |
| 318 | + border-radius: 6px; |
| 319 | + background: #fff; |
| 320 | + display: block; |
| 321 | + } |
| 322 | + |
| 323 | + #svg { |
| 324 | + width: 100%; |
| 325 | + } |
300 | 326 |
|
301 | 327 | table { |
302 | 328 | width: 100%; |
|
341 | 367 | background: #f8fafc; |
342 | 368 | } |
343 | 369 |
|
344 | | - |
345 | 370 | .footer .content { |
346 | 371 | max-width: 1100px; |
347 | 372 | margin: 0 auto; |
@@ -408,38 +433,67 @@ <h1>Molecule Clipboard</h1> |
408 | 433 | <div class="panel"> |
409 | 434 | <textarea id="input" placeholder="Paste SMILES, MolBlock, or SDF here"></textarea> |
410 | 435 |
|
| 436 | + <div class="separator">-- or --</div> |
| 437 | + |
411 | 438 | <div class="drop" id="drop"> |
412 | 439 | Drag & drop MOL / SDF / TXT files here |
413 | 440 | </div> |
414 | 441 |
|
415 | | - <div class="controls"> |
416 | | - <button id="processBtn" class="primary" disabled>Loading RDKit…</button> |
417 | | - <button onclick="copyShareLink()">Copy link</button> |
418 | | - <button onclick="clearAll()">Clear</button> |
| 442 | + <div style="display: flex;"> |
| 443 | + <button onclick="clearAll()" style="color: #b91c1c; border-color: #b91c1c;">Clear</button> |
419 | 444 | </div> |
420 | 445 |
|
421 | 446 | <div id="error"></div> |
422 | 447 | </div> |
423 | 448 |
|
424 | 449 | <div id="output" style="display:none"> |
425 | 450 |
|
426 | | - <div class="panel grid"> |
427 | | - <div> |
428 | | - <strong>Structure</strong><br><br> |
429 | | - <div id="svg"></div><br> |
430 | | - <button onclick="downloadSVG()">Download SVG</button> |
431 | | - <button onclick="downloadPNG()">Download PNG</button> |
432 | | - <button onclick="copySVG()">Copy SVG</button> |
433 | | - </div> |
434 | | - |
435 | | - <div> |
436 | | - <strong>Properties</strong> |
437 | | - <table id="props"></table> |
438 | | - |
439 | | - <br> |
440 | | - <strong>Rules</strong> |
441 | | - <table id="rules"></table> |
442 | | - </div> |
| 451 | + <div class="panel grid"> |
| 452 | + <div> |
| 453 | + <strong>Structure</strong> |
| 454 | + <div id="svg" style="margin-top:0.75rem; margin-bottom:0.75rem;"></div> |
| 455 | + <div class="controls"> |
| 456 | + <button onclick="downloadSVG()"> |
| 457 | + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| 458 | + <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> |
| 459 | + <polyline points="7,10 12,15 17,10"/> |
| 460 | + <line x1="12" y1="15" x2="12" y2="3"/> |
| 461 | + </svg> |
| 462 | + Download SVG |
| 463 | + </button> |
| 464 | + <button onclick="downloadPNG()"> |
| 465 | + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| 466 | + <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> |
| 467 | + <polyline points="7,10 12,15 17,10"/> |
| 468 | + <line x1="12" y1="15" x2="12" y2="3"/> |
| 469 | + </svg> |
| 470 | + Download PNG |
| 471 | + </button> |
| 472 | + <button onclick="copySVG()"> |
| 473 | + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| 474 | + <rect x="9" y="9" width="13" height="13" rx="2" ry="2"/> |
| 475 | + <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/> |
| 476 | + </svg> |
| 477 | + Copy SVG |
| 478 | + </button> |
| 479 | + <button onclick="copyShareLink()"> |
| 480 | + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| 481 | + <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/> |
| 482 | + <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/> |
| 483 | + </svg> |
| 484 | + Share link |
| 485 | + </button> |
| 486 | + </div> |
| 487 | + </div> |
| 488 | + |
| 489 | + <div> |
| 490 | + <strong>Properties</strong> |
| 491 | + <table id="props"></table> |
| 492 | + |
| 493 | + <br> |
| 494 | + <strong>Rules</strong> |
| 495 | + <table id="rules"></table> |
| 496 | + </div> |
443 | 497 | </div> |
444 | 498 |
|
445 | 499 | <div class="panel"> |
@@ -479,19 +533,27 @@ <h1>Molecule Clipboard</h1> |
479 | 533 | <script> |
480 | 534 | let rdkitModule = null; |
481 | 535 |
|
482 | | - async function initRDKit() { |
483 | | - try { |
484 | | - rdkitModule = await initRDKitModule(); |
485 | | - const btn = document.getElementById("processBtn"); |
486 | | - btn.disabled = false; |
487 | | - btn.textContent = "Process"; |
488 | | - btn.onclick = process; |
489 | | - displayVersion(); |
490 | | - loadFromURL(); |
491 | | - } catch(e) { |
492 | | - document.getElementById("error").textContent = "Failed to load RDKit: " + e; |
493 | | - } |
494 | | - } |
| 536 | + async function initRDKit() { |
| 537 | + try { |
| 538 | + rdkitModule = await initRDKitModule(); |
| 539 | + displayVersion(); |
| 540 | + loadFromURL(); |
| 541 | + setupAutoProcess(); |
| 542 | + } catch(e) { |
| 543 | + document.getElementById("error").textContent = "Failed to load RDKit: " + e; |
| 544 | + } |
| 545 | + } |
| 546 | + |
| 547 | + let debounceTimer; |
| 548 | + function setupAutoProcess() { |
| 549 | + const input = document.getElementById("input"); |
| 550 | + input.addEventListener("input", () => { |
| 551 | + clearTimeout(debounceTimer); |
| 552 | + debounceTimer = setTimeout(() => { |
| 553 | + process(true); |
| 554 | + }, 500); |
| 555 | + }); |
| 556 | + } |
495 | 557 | </script> |
496 | 558 | <script src="https://cdn.jsdelivr.net/npm/@rdkit/rdkit/dist/RDKit_minimal.js"></script> |
497 | 559 | <script>initRDKit();</script> |
|
0 commit comments