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: smaller publication circles, author avatar fallback, DID from documents

- reduce radius multiplier from 0.5 to 0.35, cap at 28px
- extract DID from document metadata when turso lookup misses
- batch-fetch author avatars from bsky public API at build time
- atlas.js: prefer coverImage, fall back to avatar URL

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

zzstoatzz 9626fd3f 872433e8

+48 -12
+35 -1
scripts/build-atlas
··· 466 466 coords = X_2d[indices] 467 467 centroid = coords.mean(axis=0) 468 468 info = pub_lookup.get(bp, {}) 469 + # get DID from turso lookup first, fall back to documents 470 + did = info.get("did", "") 471 + if not did: 472 + for idx in indices: 473 + d = metadata[idx].get("did", "") 474 + if d: 475 + did = d 476 + break 469 477 pub = { 470 478 "name": info.get("name") or bp, 471 479 "basePath": bp, 472 - "did": info.get("did", ""), 480 + "did": did, 473 481 "cx": round(float(centroid[0]), 4), 474 482 "cy": round(float(centroid[1]), 4), 475 483 "count": len(indices), ··· 485 493 486 494 publications.sort(key=lambda p: p["count"], reverse=True) 487 495 log(f" {len(publications)} publications with 2+ documents") 496 + 497 + # fetch author avatars for publications (fallback when no cover image) 498 + unique_dids = {p["did"] for p in publications if p["did"]} 499 + log(f" resolving avatars for {len(unique_dids)} unique authors...") 500 + did_avatars: dict[str, str] = {} 501 + batch_size = 25 502 + did_list = list(unique_dids) 503 + for i in range(0, len(did_list), batch_size): 504 + batch = did_list[i : i + batch_size] 505 + for did in batch: 506 + try: 507 + resp = client.get( 508 + f"https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor={did}", 509 + timeout=10, 510 + ) 511 + if resp.status_code == 200: 512 + avatar = resp.json().get("avatar", "") 513 + if avatar: 514 + did_avatars[did] = avatar 515 + except Exception: 516 + pass 517 + log(f" resolved {len(did_avatars)} avatars") 518 + 519 + # attach avatar URLs to publications 520 + for pub in publications: 521 + pub["avatar"] = did_avatars.get(pub["did"], "") 488 522 489 523 # --- step 6: build output --- 490 524 log("building output...")
+13 -11
site/atlas.js
··· 94 94 var PUB_MAX_CONCURRENT = 6; 95 95 var pubLoadCount = 0; 96 96 97 - function pubImageUrl(did, cid) { 98 - if (!did || !cid) return null; 99 - return 'https://cdn.bsky.app/img/feed_thumbnail/plain/' + did + '/' + cid + '@jpeg'; 97 + function pubImageUrl(pub) { 98 + // prefer cover image, fall back to author avatar 99 + if (pub.did && pub.coverImage) { 100 + return 'https://cdn.bsky.app/img/feed_thumbnail/plain/' + pub.did + '/' + pub.coverImage + '@jpeg'; 101 + } 102 + if (pub.avatar) return pub.avatar; 103 + return null; 100 104 } 101 105 102 106 function loadPubImage(pub) { 103 107 var key = pub.basePath; 104 108 if (pubImages[key] || pubFailed[key] || pubLoading[key]) return; 105 109 if (pubLoadCount >= PUB_MAX_CONCURRENT) return; 106 - var url = pubImageUrl(pub.did, pub.coverImage); 110 + var url = pubImageUrl(pub); 107 111 if (!url) { pubFailed[key] = true; return; } 108 112 pubLoading[key] = true; 109 113 pubLoadCount++; ··· 479 483 } 480 484 481 485 // --- publication circles --- 482 - // radius = sqrt(count) * zoom * 0.5, no floor — small pubs vanish at low zoom 483 - // at zoom 1: sqrt(50)≈7 → 3.5px (visible), sqrt(10)≈3 → 1.5px (culled) 484 - // at zoom 3: sqrt(10)≈3 → 4.5px (appears), sqrt(4)=2 → 3px (culled) 485 - // at zoom 6: sqrt(4)=2 → 6px (appears) 486 - // result: ~25 visible at overview, hundreds at deep zoom, all smooth 486 + // radius = sqrt(count) * zoom * 0.35, capped at 28px 487 + // at zoom 1: sqrt(236)≈15 → 5.4px (visible), sqrt(30)≈5.5 → 1.9px (culled) 488 + // smaller than before — publications accent the map, not dominate it 487 489 if (pubData && pubData.length > 0) { 488 490 var pubLabelZoom = 3; 489 491 var pubRendered = 0; 490 492 for (var pi2 = 0; pi2 < pubData.length; pi2++) { 491 493 var pub = pubData[pi2]; 492 - var pr = Math.min(40, Math.sqrt(pub.count) * zoom * 0.5); 494 + var pr = Math.min(28, Math.sqrt(pub.count) * zoom * 0.35); 493 495 if (pr < 4) continue; // natural culling — small pubs disappear 494 496 var psx = cx + pub.cx * scale, psy = cy + pub.cy * scale; 495 497 // cull off-screen (with padding for labels) ··· 786 788 var z = view.zoom; 787 789 for (var i = 0; i < pubData.length; i++) { 788 790 var pub = pubData[i]; 789 - var pr = Math.min(40, Math.sqrt(pub.count) * z * 0.5); 791 + var pr = Math.min(28, Math.sqrt(pub.count) * z * 0.35); 790 792 if (pr < 4) continue; 791 793 var psx = cx + pub.cx * scale, psy = cy + pub.cy * scale; 792 794 var dx = sx - psx, dy = sy - psy;