semantic bufo search find-bufo.com
bufo

improve ui polish and no-results display

- redesign filter toggle with icon and glassmorphic styling
- center and enlarge 'no bufos found' shrugging buffo image
- improve alpha slider precision to 0.01 steps
- add hover states and better visual hierarchy

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

+91 -20
+91 -20
static/index.html
··· 90 90 } 91 91 92 92 .options-toggle { 93 - background: none; 94 - border: none; 93 + background: rgba(102, 126, 234, 0.1); 94 + border: 1.5px solid rgba(102, 126, 234, 0.3); 95 + border-radius: 6px; 95 96 color: #667eea; 96 - font-size: 0.9em; 97 + font-size: 0.85em; 97 98 font-family: inherit; 99 + font-weight: 600; 98 100 cursor: pointer; 99 - padding: 5px 0; 100 - text-decoration: underline; 101 + padding: 8px 14px; 101 102 margin-top: 10px; 103 + transition: all 0.2s; 104 + display: inline-flex; 105 + align-items: center; 106 + gap: 6px; 102 107 } 103 108 104 109 .options-toggle:hover { 105 - color: #5568d3; 110 + background: rgba(102, 126, 234, 0.15); 111 + border-color: rgba(102, 126, 234, 0.5); 112 + transform: translateY(-1px); 113 + } 114 + 115 + .options-toggle svg { 116 + width: 14px; 117 + height: 14px; 106 118 } 107 119 108 120 .option-group { ··· 122 134 font-weight: 600; 123 135 } 124 136 137 + .option-name a { 138 + text-decoration: none; 139 + color: inherit; 140 + border-bottom: 1px dotted #999; 141 + transition: border-color 0.2s, color 0.2s; 142 + } 143 + 144 + .option-name a:hover { 145 + color: #667eea; 146 + border-bottom-color: #667eea; 147 + } 148 + 125 149 .option-value { 126 150 color: #667eea; 127 151 font-weight: 700; 152 + font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace; 128 153 } 129 154 130 155 .option-description { ··· 380 405 } 381 406 382 407 .no-results { 408 + grid-column: 1 / -1; 409 + display: flex; 410 + flex-direction: column; 411 + align-items: center; 412 + justify-content: center; 383 413 text-align: center; 414 + padding: 40px 20px; 415 + margin: 0 auto; 416 + } 417 + 418 + .no-results-text { 419 + font-size: 3em; 420 + margin-bottom: 50px; 421 + font-weight: 700; 384 422 color: white; 385 - font-size: 1.2em; 386 - padding: 40px; 423 + text-shadow: 2px 2px 4px rgba(0,0,0,0.2); 424 + } 425 + 426 + .no-results-bufo { 427 + max-width: 600px; 428 + width: 100%; 429 + height: auto; 430 + display: block; 431 + margin: 0 auto; 432 + } 433 + 434 + @media (max-width: 600px) { 435 + .no-results-text { 436 + font-size: 2em; 437 + } 438 + 439 + .no-results-bufo { 440 + max-width: 300px; 441 + } 387 442 } 388 443 389 444 @keyframes peek-in-out { ··· 461 516 <div class="container"> 462 517 <div class="header"> 463 518 <h1><a href="/">find bufo</a></h1> 464 - <p class="subtitle"><a href="https://tangled.org/@zzstoatzz.io/find-bufo/blob/main/src/search.rs" target="_blank">semantic search</a> for <a href="https://bufo.zone" target="_blank">bufo.zone</a></p> 519 + <p class="subtitle"><a href="https://tangled.org/@zzstoatzz.io/find-bufo/blob/main/src/search.rs#L1-L41" target="_blank">hybrid search</a> for <a href="https://bufo.zone" target="_blank">bufo.zone</a></p> 465 520 </div> 466 521 467 522 <div class="search-box"> ··· 475 530 <button id="searchButton">search</button> 476 531 </div> 477 532 478 - <button class="options-toggle" id="optionsToggle">search options</button> 533 + <button class="options-toggle" id="optionsToggle"> 534 + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 535 + <line x1="4" y1="6" x2="20" y2="6"></line> 536 + <line x1="4" y1="12" x2="20" y2="12"></line> 537 + <line x1="4" y1="18" x2="20" y2="18"></line> 538 + <circle cx="7" cy="6" r="2" fill="currentColor"></circle> 539 + <circle cx="14" cy="12" r="2" fill="currentColor"></circle> 540 + <circle cx="17" cy="18" r="2" fill="currentColor"></circle> 541 + </svg> 542 + <span id="optionsToggleText">search filters</span> 543 + </button> 479 544 480 545 <div class="search-options collapsed" id="searchOptions"> 481 546 <div class="option-group"> 482 547 <div class="option-label"> 483 - <span class="option-name">search mode (alpha)</span> 484 - <span class="option-value" id="alphaValue">0.7</span> 548 + <span class="option-name">search mode (<a href="https://tangled.org/@zzstoatzz.io/find-bufo/blob/main/src/search.rs#L22" target="_blank">α</a>)</span> 549 + <span class="option-value" id="alphaValue">0.70</span> 485 550 </div> 486 551 <div class="option-description"> 487 552 balance between semantic understanding and exact keyword matching ··· 491 556 id="alphaSlider" 492 557 min="0" 493 558 max="1" 494 - step="0.1" 559 + step="0.01" 495 560 value="0.7" 496 561 > 497 562 <div class="alpha-markers"> ··· 535 600 let hasSearched = false; 536 601 537 602 // toggle search options 603 + const optionsToggleText = document.getElementById('optionsToggleText'); 538 604 optionsToggle.addEventListener('click', () => { 539 605 searchOptions.classList.toggle('collapsed'); 540 - optionsToggle.textContent = searchOptions.classList.contains('collapsed') 541 - ? 'search options' 542 - : 'hide options'; 606 + optionsToggleText.textContent = searchOptions.classList.contains('collapsed') 607 + ? 'search filters' 608 + : 'hide filters'; 543 609 }); 544 610 545 611 // update alpha value display 546 612 alphaSlider.addEventListener('input', (e) => { 547 - alphaValue.textContent = e.target.value; 613 + alphaValue.textContent = parseFloat(e.target.value).toFixed(2); 548 614 }); 549 615 550 616 async function search(updateUrl = true) { ··· 605 671 606 672 function displayResults(results) { 607 673 if (results.length === 0) { 608 - resultsDiv.innerHTML = '<div class="no-results">no bufos found</div>'; 674 + resultsDiv.innerHTML = ` 675 + <div class="no-results"> 676 + <div class="no-results-text">no bufos found</div> 677 + <img src="https://all-the.bufo.zone/bufo-shrug.png" alt="bufo shrug" class="no-results-bufo"> 678 + </div> 679 + `; 609 680 return; 610 681 } 611 682 ··· 635 706 searchInput.value = e.state.query; 636 707 if (e.state.alpha !== undefined) { 637 708 alphaSlider.value = e.state.alpha; 638 - alphaValue.textContent = e.state.alpha; 709 + alphaValue.textContent = parseFloat(e.state.alpha).toFixed(2); 639 710 } 640 711 search(false); 641 712 } ··· 649 720 650 721 if (alpha) { 651 722 alphaSlider.value = alpha; 652 - alphaValue.textContent = alpha; 723 + alphaValue.textContent = parseFloat(alpha).toFixed(2); 653 724 } 654 725 655 726 if (query) {