From 9608b75636510a6913dddefb19ad5e64e0c79720 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 26 May 2026 05:51:51 +0000 Subject: [PATCH] TrendingLexicons: chat.bsky out of trending, chat.* out of top, schema link, microcosm credit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three small refinements: 1. Hidden-prefix lists per mode: - Trending hides app.bsky.* AND chat.bsky.* (both Bluesky flavors). - Top hides chat.* (private DM traffic isn't a fair "what's hot" signal). Bluesky public namespace stays in Top — it's the elephant in the room and that's accurate to show. 2. Clicking a row now opens the lexicon's schema record on its publisher, not the explorer's collection view. Convention: NSID `..<...>` maps to publisher `.`, so `net.anisota.foo` -> `anisota.net`. Deep link goes to the explorer's record page at `/explore//com.atproto.lexicon.schema/` and reuses RecordPreview to render the schema doc. Falls through to the explorer's not-found UI when the publisher hasn't published one — acceptable fallback. 3. Small microcosm credit row at the bottom of the card: "Data from microcosm.blue". UFOs is theirs; surface the attribution. --- src/components/explore/TrendingLexicons.tsx | 91 ++++++++++++++++++--- 1 file changed, 80 insertions(+), 11 deletions(-) diff --git a/src/components/explore/TrendingLexicons.tsx b/src/components/explore/TrendingLexicons.tsx index 8351262..3edbe39 100644 --- a/src/components/explore/TrendingLexicons.tsx +++ b/src/components/explore/TrendingLexicons.tsx @@ -118,9 +118,7 @@ export default function TrendingLexicons() { className="explore-placeholder" style={{ padding: '1rem', margin: 0 }} > - {mode === 'trending' - ? 'No non-Bluesky lexicons matched in this window.' - : 'No lexicons matched in this window.'} + No lexicons matched in this window.

) : (
    @@ -146,11 +144,45 @@ export default function TrendingLexicons() {

    ) : null} + ); } +function Credit() { + return ( +
    + Data from + + microcosm.blue + +
    + ); +} + function Header({ mode, setMode, @@ -310,8 +342,8 @@ function Row({ }} > nsid.startsWith(p)); } /** First two segments — `app.bsky.feed.post` -> `app.bsky`. Single-segment @@ -487,12 +534,13 @@ function namespaceKey(nsid: string): string { function filterAndDedup( rows: T[], - dropBluesky: boolean, + mode: Mode, ): T[] { + const hidden = mode === 'trending' ? TRENDING_HIDDEN_PREFIXES : TOP_HIDDEN_PREFIXES; const seen = new Set(); const out: T[] = []; for (const r of rows) { - if (dropBluesky && isBluesky(r.nsid)) continue; + if (hasHiddenPrefix(r.nsid, hidden)) continue; const key = namespaceKey(r.nsid); if (seen.has(key)) continue; seen.add(key); @@ -501,6 +549,27 @@ function filterAndDedup( return out; } +/** + * Convention: an NSID like `..<...>` maps to `.` + * as the publisher's handle. `net.anisota.harvest.minigame` -> + * `anisota.net`, `app.bsky.feed.post` -> `bsky.app`, etc. Used to build + * the deep link into the explorer's lexicon-schema record for an NSID. + */ +function publisherForNsid(nsid: string): string { + const parts = nsid.split('.'); + if (parts.length < 2) return nsid; + return `${parts[1]}.${parts[0]}`; +} + +/** Lexicon-schema records live at this collection on the publisher's repo, + * keyed by the full NSID. The explorer's record page renders them nicely + * via RecordPreview, and falls through to a not-found message when the + * publisher hasn't published a schema. */ +function schemaPathFor(nsid: string): string { + const publisher = publisherForNsid(nsid); + return `/explore/${publisher}/com.atproto.lexicon.schema/${encodeURIComponent(nsid)}`; +} + // ─── Fetching ────────────────────────────────────────────────────────────── type RawCollection = { @@ -552,8 +621,8 @@ async function fetchRanking( orderForMetric(metric), ); - // 2) Filter (bsky dropped only in trending) + dedup by top-2-segment ns. - const filtered = filterAndDedup(candidates, mode === 'trending'); + // 2) Filter (mode-specific) + dedup by top-2-segment namespace. + const filtered = filterAndDedup(candidates, mode); if (filtered.length === 0) return []; // 3) Score by the chosen metric. /collections gives us creates + -- 2.51.2