search for standard sites pub-search.waow.tech
search zig blog atproto
11
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: prefer path slug over rkey for leaflet URLs, add "view on atlas" link

leaflet.pub posts with custom slugs (e.g. /run-claude-and-opencode-...)
were 404ing because we always built URLs with rkey. now prefer the path
field when available.

also adds: small scatter-dot icon on each search result that navigates
directly to that document on the atlas (by URI lookup, no API call),
touch-active feedback for mobile, and fixes atlas.js variable ordering
bug that crashed the page when ?q= was present.

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

zzstoatzz 6f0774e7 d44e49ef

+77 -16
+9 -3
backend/src/server/search.zig
··· 115 115 } 116 116 // skip non-document-serving hosts (blento is a card portal, not a document platform) 117 117 const usable_base = base_path.len > 0 and !std.mem.startsWith(u8, base_path, "blento.app"); 118 - // leaflet + basePath + rkey → https://{basePath}/{rkey} 119 - if (std.mem.eql(u8, platform, "leaflet") and usable_base and rkey.len > 0) { 120 - return std.fmt.allocPrint(alloc, "https://{s}/{s}", .{ base_path, rkey }) catch ""; 118 + // leaflet + basePath → prefer path slug when available, fall back to rkey 119 + if (std.mem.eql(u8, platform, "leaflet") and usable_base) { 120 + if (path.len > 0) { 121 + const sep: []const u8 = if (path[0] == '/') "" else "/"; 122 + return std.fmt.allocPrint(alloc, "https://{s}{s}{s}", .{ base_path, sep, path }) catch ""; 123 + } 124 + if (rkey.len > 0) { 125 + return std.fmt.allocPrint(alloc, "https://{s}/{s}", .{ base_path, rkey }) catch ""; 126 + } 121 127 } 122 128 // basePath + path → https://{basePath}[/]{path} 123 129 if (usable_base and path.len > 0) {
+42 -12
site/atlas.js
··· 717 717 if (platform === 'whitewind' || collection.startsWith('com.whtwnd.')) return 'https://whtwnd.com/' + did + '/' + rkey; 718 718 // skip non-document-serving hosts (blento is a card portal, not a document platform) 719 719 var usableBase = basePath && !basePath.startsWith('blento.app'); 720 - // leaflet uses rkey directly 720 + // leaflet: prefer path slug when available, fall back to rkey 721 + if (platform === 'leaflet' && usableBase && path) { 722 + var sep = path.charAt(0) === '/' ? '' : '/'; 723 + return 'https://' + basePath + sep + path; 724 + } 721 725 if (platform === 'leaflet' && usableBase) return 'https://' + basePath + '/' + rkey; 722 726 // leaflet without basePath 723 727 if (platform === 'leaflet') return 'https://leaflet.pub/p/' + did + '/' + rkey; ··· 803 807 d.clusters.fine.length + ' clusters'; 804 808 document.getElementById('loading').classList.add('hidden'); 805 809 view.dirty = true; 810 + // jump to specific document by URI (from "view on atlas" links) 811 + if (pendingUri) { 812 + var idx = uriToIndex.get(pendingUri); 813 + if (idx !== undefined) { 814 + searchMatches = new Set([idx]); 815 + searchCenter = { x: pointsX[idx], y: pointsY[idx] }; 816 + searchQuery = d.points[idx].title || ''; 817 + setSearchStatus('1 document'); 818 + animateTo(searchCenter.x, searchCenter.y, 12); 819 + // show tooltip after animation 820 + setTimeout(function() { 821 + cacheTransform(); 822 + var sx = cx + pointsX[idx] * scale; 823 + var sy = cy + pointsY[idx] * scale; 824 + hoveredIndex = idx; 825 + selectedIndex = idx; 826 + showTooltip(idx, sx, sy); 827 + }, ANIM_DURATION + 50); 828 + } 829 + pendingUri = null; 830 + } 806 831 // apply prefetched search results (fired in parallel with atlas.json) 807 - if (pendingSearchResults) { 832 + else if (pendingSearchResults) { 808 833 pendingSearchResults.then(function(resp) { 809 834 pendingSearchResults = null; 810 835 if (resp) applySearchResults(resp, searchInput.value); ··· 817 842 }); 818 843 } 819 844 845 + // --- search --- 846 + var API_URL = 'https://leaflet-search-backend.fly.dev'; 847 + var searchInput = document.getElementById('search-input'); 848 + var searchForm = document.getElementById('search-form'); 849 + var searchStatusEl = null; 850 + var pendingSearchResults = null; // promise for prefetched search results 851 + var pendingUri = null; // URI to jump to after data loads (from "view on atlas" links) 852 + 820 853 window.addEventListener('resize', resizeCanvas); 821 854 resizeCanvas(); 822 - // prefetch search results in parallel with atlas.json when ?q= is present 823 - var urlQ = new URLSearchParams(window.location.search).get('q'); 824 - if (urlQ) { 855 + // jump to specific document by URI, or prefetch search results 856 + var urlParams = new URLSearchParams(window.location.search); 857 + var urlUri = urlParams.get('uri'); 858 + var urlQ = urlParams.get('q'); 859 + if (urlUri) { 860 + pendingUri = urlUri; 861 + } else if (urlQ) { 825 862 searchInput.value = urlQ; 826 863 pendingSearchResults = fetch(API_URL + '/search?mode=semantic&limit=20&format=v2&q=' + encodeURIComponent(urlQ)) 827 864 .then(function(r) { return r.ok ? r.json() : null; }) ··· 829 866 } 830 867 loadData(); 831 868 loop(); 832 - 833 - // --- search --- 834 - var API_URL = 'https://leaflet-search-backend.fly.dev'; 835 - var searchInput = document.getElementById('search-input'); 836 - var searchForm = document.getElementById('search-form'); 837 - var searchStatusEl = null; 838 - var pendingSearchResults = null; // promise for prefetched search results 839 869 840 870 function setSearchStatus(msg) { 841 871 if (!searchStatusEl) {
+26 -1
site/index.html
··· 690 690 object-fit: cover; 691 691 } 692 692 693 + .atlas-link { 694 + color: var(--text-muted); 695 + text-decoration: none; 696 + margin-left: 6px; 697 + display: inline-flex; 698 + align-items: center; 699 + vertical-align: middle; 700 + opacity: 0.5; 701 + transition: opacity 0.15s; 702 + } 703 + 704 + .atlas-link:hover { 705 + opacity: 1; 706 + color: var(--text-secondary); 707 + } 708 + 709 + .atlas-link svg { display: block; } 710 + 711 + /* touch-active feedback */ 712 + @media (hover: none) and (pointer: coarse) { 713 + .result:active { 714 + background: var(--result-hover); 715 + } 716 + } 717 + 693 718 /* mobile improvements */ 694 719 @media (max-width: 600px) { 695 720 body { ··· 1024 1049 <div class="result-meta" ${doc.did ? `data-did="${escapeHtml(doc.did)}"` : ''}> 1025 1050 ${date ? `${date} | ` : ''}<span class="author-name"></span>${platformHome.url 1026 1051 ? `<a href="${platformHome.url}" target="_blank">${platformHome.label}</a>` 1027 - : platformHome.label} 1052 + : platformHome.label}<a href="/atlas.html?uri=${encodeURIComponent(doc.uri || '')}" class="atlas-link" title="view on atlas"><svg width="12" height="12" viewBox="0 0 12 12" fill="none"><circle cx="3" cy="4" r="1.2" fill="currentColor"/><circle cx="8" cy="3" r="1" fill="currentColor" opacity=".6"/><circle cx="6" cy="8" r="1.2" fill="currentColor"/><circle cx="10" cy="7" r=".8" fill="currentColor" opacity=".4"/></svg></a> 1028 1053 </div> 1029 1054 </div> 1030 1055 `;