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.

feat: make dashboard bridgy-fed-aware with stacked timeline bars

Show bridgy fed as a distinct category in platform breakdown, add
amber stacked bars in the timeline chart with legend, add bridgy fed
count to top metrics, exclude bridgy fed from top publications.

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

+91 -18
+23 -8
backend/src/server/dashboard.zig
··· 7 7 8 8 // JSON output types 9 9 const TagJson = struct { tag: []const u8, count: i64 }; 10 - const TimelineJson = struct { date: []const u8, count: i64 }; 10 + const TimelineJson = struct { date: []const u8, count: i64, bridgyfed: i64 }; 11 11 const PubJson = struct { name: []const u8, basePath: []const u8, count: i64 }; 12 12 const PlatformJson = struct { platform: []const u8, count: i64 }; 13 13 ··· 18 18 publications: i64, 19 19 documents: i64, 20 20 embeddings: i64, 21 + bridgyfed_documents: i64, 21 22 relay_url: []const u8, 22 23 tags_json: []const u8, 23 24 timeline_json: []const u8, ··· 39 40 \\ (SELECT total_searches FROM stats WHERE id = 1) as searches, 40 41 \\ (SELECT total_errors FROM stats WHERE id = 1) as errors, 41 42 \\ (SELECT service_started_at FROM stats WHERE id = 1) as started_at, 42 - \\ (SELECT COUNT(*) FROM documents WHERE embedded_at IS NOT NULL) as embeddings 43 + \\ (SELECT COUNT(*) FROM documents WHERE embedded_at IS NOT NULL) as embeddings, 44 + \\ (SELECT COUNT(*) FROM documents WHERE COALESCE(is_bridgyfed, 0) = 1) as bridgyfed 43 45 ; 44 46 45 47 const PLATFORMS_SQL = 46 - \\SELECT platform, COUNT(*) as count 48 + \\SELECT CASE WHEN COALESCE(is_bridgyfed, 0) = 1 THEN 'bridgy fed' 49 + \\ ELSE platform END as platform, 50 + \\ COUNT(*) as count 47 51 \\FROM documents 48 - \\GROUP BY platform 52 + \\GROUP BY CASE WHEN COALESCE(is_bridgyfed, 0) = 1 THEN 'bridgy fed' 53 + \\ ELSE platform END 49 54 \\ORDER BY count DESC 50 55 ; 51 56 ··· 58 63 ; 59 64 60 65 const TIMELINE_SQL = 61 - \\SELECT DATE(indexed_at) as date, COUNT(*) as count 66 + \\SELECT DATE(indexed_at) as date, COUNT(*) as count, 67 + \\ SUM(CASE WHEN COALESCE(is_bridgyfed, 0) = 1 THEN 1 ELSE 0 END) as bridgyfed 62 68 \\FROM documents 63 69 \\WHERE indexed_at IS NOT NULL AND indexed_at != '' 64 70 \\AND DATE(indexed_at) <= DATE('now') ··· 71 77 \\SELECT p.name, p.base_path, COUNT(d.uri) as doc_count 72 78 \\FROM publications p 73 79 \\JOIN documents d ON d.publication_uri = p.uri 80 + \\WHERE COALESCE(d.is_bridgyfed, 0) = 0 74 81 \\GROUP BY p.uri 75 82 \\ORDER BY doc_count DESC 76 83 \\LIMIT 8 ··· 109 116 const publications = if (stats_row) |r| r.int(1) else 0; 110 117 const documents = if (stats_row) |r| r.int(0) else 0; 111 118 const embeddings = if (stats_row) |r| r.int(5) else 0; 119 + const bridgyfed_documents = if (stats_row) |r| r.int(6) else 0; 112 120 113 121 return .{ 114 122 .started_at = started_at, ··· 116 124 .publications = publications, 117 125 .documents = documents, 118 126 .embeddings = embeddings, 127 + .bridgyfed_documents = bridgyfed_documents, 119 128 .relay_url = getRelayUrl(), 120 129 .tags_json = try formatTagsJson(alloc, batch.get(2)), 121 130 .timeline_json = try formatTimelineJson(alloc, batch.get(3)), ··· 142 151 \\SELECT 143 152 \\ (SELECT COUNT(*) FROM documents) as docs, 144 153 \\ (SELECT COUNT(*) FROM publications) as pubs, 145 - \\ (SELECT COUNT(*) FROM documents WHERE embedded_at IS NOT NULL) as embeddings 154 + \\ (SELECT COUNT(*) FROM documents WHERE embedded_at IS NOT NULL) as embeddings, 155 + \\ (SELECT COUNT(*) FROM documents WHERE COALESCE(is_bridgyfed, 0) = 1) as bridgyfed 146 156 , .{}); 147 157 defer counts_rows.deinit(); 148 158 const counts_row = counts_rows.next() orelse return error.NoStats; 149 159 const documents = counts_row.int(0); 150 160 const publications = counts_row.int(1); 151 161 const embeddings = counts_row.int(2); 162 + const bridgyfed_documents = counts_row.int(3); 152 163 153 164 // platforms query 154 165 var platforms_rows = try local.query(PLATFORMS_SQL, .{}); ··· 176 187 .publications = publications, 177 188 .documents = documents, 178 189 .embeddings = embeddings, 190 + .bridgyfed_documents = bridgyfed_documents, 179 191 .relay_url = getRelayUrl(), 180 192 .tags_json = tags_json, 181 193 .timeline_json = timeline_json, ··· 204 216 var jw: json.Stringify = .{ .writer = &output.writer }; 205 217 try jw.beginArray(); 206 218 while (rows.next()) |row| { 207 - try jw.write(TimelineJson{ .date = row.text(0), .count = row.int(1) }); 219 + try jw.write(TimelineJson{ .date = row.text(0), .count = row.int(1), .bridgyfed = row.int(2) }); 208 220 } 209 221 try jw.endArray(); 210 222 return try output.toOwnedSlice(); ··· 249 261 errdefer output.deinit(); 250 262 var jw: json.Stringify = .{ .writer = &output.writer }; 251 263 try jw.beginArray(); 252 - for (rows) |row| try jw.write(TimelineJson{ .date = row.text(0), .count = row.int(1) }); 264 + for (rows) |row| try jw.write(TimelineJson{ .date = row.text(0), .count = row.int(1), .bridgyfed = row.int(2) }); 253 265 try jw.endArray(); 254 266 return try output.toOwnedSlice(); 255 267 } ··· 366 378 367 379 try jw.objectField("embeddings"); 368 380 try jw.write(data.embeddings); 381 + 382 + try jw.objectField("bridgyfedDocuments"); 383 + try jw.write(data.bridgyfed_documents); 369 384 370 385 try jw.objectField("relayUrl"); 371 386 try jw.write(data.relay_url);
+26 -3
site/dashboard.css
··· 105 105 height: 60px; 106 106 } 107 107 108 - .bar { 108 + .bar-stack { 109 109 flex: 1; 110 - background: #1B7340; 110 + display: flex; 111 + flex-direction: column; 111 112 min-height: 2px; 112 113 } 113 - .bar:hover { background: #2a9d5c; } 114 + .bar-stack:hover .bar-normal { background: #2a9d5c; } 115 + .bar-stack:hover .bar-bridgy { background: #e5a00d; } 116 + .bar-normal { background: #1B7340; flex-shrink: 0; } 117 + .bar-bridgy { background: #d97706; flex-shrink: 0; } 118 + 119 + .timeline-legend { 120 + display: flex; 121 + gap: 12px; 122 + font-size: 10px; 123 + color: var(--text-dim); 124 + margin-bottom: 6px; 125 + } 126 + .legend-swatch { 127 + display: inline-block; 128 + width: 8px; 129 + height: 8px; 130 + margin-right: 4px; 131 + vertical-align: middle; 132 + } 133 + 134 + .metric-bridgyfed { color: #d97706; } 135 + 136 + .doc-type.bridgy-fed { color: #d97706; } 114 137 115 138 .doc-row { 116 139 display: flex;
+4
site/dashboard.html
··· 37 37 <div class="metric-label">embeddings</div> 38 38 </div> 39 39 <div> 40 + <div class="metric-value metric-bridgyfed" id="bridgyfed">--</div> 41 + <div class="metric-label">bridgy fed</div> 42 + </div> 43 + <div> 40 44 <div class="metric-value" id="relay">--</div> 41 45 <div class="metric-label">relay</div> 42 46 </div>
+38 -7
site/dashboard.js
··· 29 29 const el = document.getElementById('timeline'); 30 30 if (!timeline || timeline.length === 0) return; 31 31 32 + // add legend if any bridgy fed data 33 + const hasBridgy = timeline.some(d => (d.bridgyfed || 0) > 0); 34 + if (hasBridgy) { 35 + const legend = document.createElement('div'); 36 + legend.className = 'timeline-legend'; 37 + legend.innerHTML = 38 + '<span><span class="legend-swatch" style="background:#1B7340"></span>content</span>' + 39 + '<span><span class="legend-swatch" style="background:#d97706"></span>bridgy fed</span>'; 40 + el.parentNode.insertBefore(legend, el); 41 + } 42 + 32 43 const max = Math.max(...timeline.map(d => d.count)); 33 44 [...timeline].reverse().forEach(d => { 34 - const h = max > 0 ? (d.count / max * 100) : 0; 35 - const bar = document.createElement('div'); 36 - bar.className = 'bar'; 37 - bar.style.height = Math.max(h, 3) + '%'; 38 - bar.title = d.date + ': ' + d.count; 39 - el.appendChild(bar); 45 + const totalH = max > 0 ? (d.count / max * 100) : 0; 46 + const bridgy = d.bridgyfed || 0; 47 + const normal = d.count - bridgy; 48 + 49 + const stack = document.createElement('div'); 50 + stack.className = 'bar-stack'; 51 + stack.style.height = Math.max(totalH, 3) + '%'; 52 + stack.title = d.date + ': ' + d.count + (bridgy ? ' (' + bridgy + ' bridgy fed)' : ''); 53 + 54 + if (bridgy > 0 && d.count > 0) { 55 + const bridgyDiv = document.createElement('div'); 56 + bridgyDiv.className = 'bar-bridgy'; 57 + bridgyDiv.style.height = (bridgy / d.count * 100) + '%'; 58 + stack.appendChild(bridgyDiv); 59 + } 60 + if (normal > 0 && d.count > 0) { 61 + const normalDiv = document.createElement('div'); 62 + normalDiv.className = 'bar-normal'; 63 + normalDiv.style.height = (normal / d.count * 100) + '%'; 64 + stack.appendChild(normalDiv); 65 + } 66 + 67 + el.appendChild(stack); 40 68 }); 41 69 } 42 70 ··· 72 100 platforms.forEach(p => { 73 101 const row = document.createElement('div'); 74 102 row.className = 'doc-row'; 75 - row.innerHTML = '<span class="doc-type">' + escapeHtml(p.platform) + '</span><span class="doc-count">' + p.count + '</span>'; 103 + const isBridgy = p.platform === 'bridgy fed'; 104 + const typeClass = 'doc-type' + (isBridgy ? ' bridgy-fed' : ''); 105 + row.innerHTML = '<span class="' + typeClass + '">' + escapeHtml(p.platform) + '</span><span class="doc-count">' + p.count + '</span>'; 76 106 el.appendChild(row); 77 107 }); 78 108 } ··· 383 413 document.getElementById('searches').textContent = data.searches; 384 414 document.getElementById('publications').textContent = data.publications; 385 415 document.getElementById('embeddings').textContent = data.embeddings ?? '--'; 416 + document.getElementById('bridgyfed').textContent = data.bridgyfedDocuments ?? '--'; 386 417 387 418 if (data.relayUrl) { 388 419 const relayEl = document.getElementById('relay');