Skip to content

Commit 84dc21a

Browse files
polish UI: denser grid (?limit), gradient avatars, colorful follow-graph
1 parent 84f30fa commit 84dc21a

2 files changed

Lines changed: 12 additions & 9 deletions

File tree

public/index.html

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@
3939
.card{background:var(--surface);border:1px solid var(--line);border-radius:16px;padding:1.2rem;cursor:pointer;
4040
transition:transform .18s ease,box-shadow .18s ease,border-color .18s;box-shadow:0 1px 2px rgba(20,20,30,.04)}
4141
.card:hover{transform:translateY(-3px);box-shadow:0 12px 30px rgba(80,70,160,.13);border-color:#dcd8f5}
42-
.av{width:56px;height:56px;border-radius:50%;object-fit:cover;background:var(--accent-soft);display:grid;place-items:center;
42+
.av{position:relative;width:56px;height:56px;border-radius:50%;background:var(--accent-soft);display:grid;place-items:center;
4343
font-weight:700;color:var(--accent);font-size:1.3rem;overflow:hidden}
44+
.av img,.fav img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
4445
.card .name{font-family:var(--serif);font-size:1.25rem;font-weight:600;margin:.7rem 0 .1rem;line-height:1.2}
4546
.card .nip05{color:var(--accent);font-size:.82rem;font-weight:500}
4647
.card .about{color:var(--muted);font-size:.9rem;margin-top:.5rem;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
@@ -63,7 +64,8 @@
6364
.panel{background:var(--surface);border:1px solid var(--line);border-radius:16px;padding:1.2rem 1.3rem;box-shadow:0 1px 2px rgba(20,20,30,.04)}
6465
.panel h3{font-size:.74rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);margin-bottom:.9rem}
6566
.follows{display:flex;flex-wrap:wrap;gap:.5rem}
66-
.follows .fav{width:34px;height:34px;border-radius:50%;background:var(--accent-soft);object-fit:cover}
67+
.follows .fav{position:relative;width:32px;height:32px;border-radius:50%;background:var(--accent-soft);overflow:hidden;transition:transform .12s}
68+
.follows .fav:hover{transform:scale(1.2);z-index:1}
6769
pre{font-family:var(--mono);font-size:.74rem;line-height:1.55;color:#2a2a35;white-space:pre-wrap;word-break:break-word;max-height:460px;overflow:auto}
6870
.k{color:var(--accent)} .s{color:#1c8c6b}
6971
footer{text-align:center;color:var(--faint);font-size:.8rem;margin-top:3rem}
@@ -91,10 +93,11 @@ <h1>A directory of <em style="font-style:italic">did:nostr</em> identities</h1>
9193
const content = (p) => { try { return JSON.parse(p.content || '{}'); } catch { return {}; } };
9294
const initial = (c, pk) => (c.name || c.display_name || pk || '?').trim()[0]?.toUpperCase() || '?';
9395

96+
function hueFrom(s = '') { let h = 0; for (const ch of String(s)) h = (h * 31 + ch.charCodeAt(0)) % 360; return h; }
97+
function gradStyle(seed) { const a = hueFrom(seed), b = (a + 38) % 360; return `background:linear-gradient(135deg,hsl(${a} 68% 70%),hsl(${b} 72% 55%));color:#fff`; }
9498
function avatar(c, pk, cls = 'av') {
95-
const pic = c.picture;
96-
if (pic) return `<img class="${cls}" src="${esc(pic)}" alt="" onerror="this.replaceWith(Object.assign(document.createElement('div'),{className:'${cls}',textContent:'${esc(initial(c,pk))}'}))">`;
97-
return `<div class="${cls}">${esc(initial(c, pk))}</div>`;
99+
const img = c.picture ? `<img src="${esc(c.picture)}" alt="" onerror="this.style.display='none'">` : '';
100+
return `<div class="${cls}" style="${gradStyle(pk)}">${esc(initial(c, pk))}${img}</div>`;
98101
}
99102

100103
function highlight(obj) {
@@ -106,7 +109,7 @@ <h1>A directory of <em style="font-style:italic">did:nostr</em> identities</h1>
106109
async function home() {
107110
location.hash = '';
108111
view.innerHTML = '<div class="loading">loading recent identities…</div>';
109-
const profiles = await fetch('/api/profiles').then((r) => r.json()).catch(() => []);
112+
const profiles = await fetch('/api/profiles?limit=36').then((r) => r.json()).catch(() => []);
110113
$('#stat').textContent = profiles.length ? `${profiles.length} recently active identities` : '';
111114
if (!profiles.length) { view.innerHTML = '<div class="loading">No profiles indexed yet. Start the indexer and refresh.</div>'; return; }
112115
view.innerHTML = `<div class="grid">${profiles.map((p) => {
@@ -149,7 +152,7 @@ <h2>${esc(c.name || short(pk))}</h2>
149152
<div class="sections">
150153
<div class="panel"><h3>DID document</h3><pre>${highlight(doc)}</pre></div>
151154
<div class="panel"><h3>Following · ${follows.length}</h3>
152-
${follows.length ? `<div class="follows">${follows.slice(0, 60).map((d) => `<a class="fav" title="${esc(d)}" href="#${d.replace('did:nostr:', '')}"></a>`).join('')}</div>`
155+
${follows.length ? `<div class="follows">${follows.slice(0, 96).map((d) => { const fk = d.replace('did:nostr:', ''); return `<a class="fav" style="${gradStyle(fk)}" title="${esc(fk)}" href="#${fk}"></a>`; }).join('')}</div>`
153156
: '<div style="color:var(--faint);font-size:.9rem">No follow list indexed.</div>'}
154157
</div>
155158
</div>

src/server.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ export async function startServer(port = process.env.PORT || 3000) {
1313
const app = express();
1414
app.use(express.static(PUBLIC));
1515

16-
app.get('/api/profiles', async (_req, res, next) => {
17-
try { res.json(await recentProfiles(10)); } catch (e) { next(e); }
16+
app.get('/api/profiles', async (req, res, next) => {
17+
try { res.json(await recentProfiles(Math.min(Number(req.query.limit) || 30, 100))); } catch (e) { next(e); }
1818
});
1919
app.get('/api/profile/:pubkey', async (req, res, next) => {
2020
try { res.json(await getProfile(req.params.pubkey)); } catch (e) { next(e); }

0 commit comments

Comments
 (0)