Swap in the vector Delta branding, and fix Top Contributors - #184
Merged
Conversation
Replaces the raster logo.png wordmark with the SVG artwork, adds the triangle mark for the collapsed sidebar, and gives the app a favicon that is actually the product's own logo rather than The Triangle's blue "T". All three source files ship with the artwork sitting in a 377x347 canvas that the mark occupies a small band of, so each viewBox is tightened to the real bounding box. Left alone, object-contain would have scaled the wordmark to roughly a fifth of its intended size. Sizing changed with it. The new wordmark is 5.3:1 where the PNG was 2.8:1, so the old fixed w-30 h-30 square rendered it at half the visual height; it is now height-anchored. max-w-full replaces shrink-0 so it cannot spill past the 60px rail, which the PNG did. The collapsed mark is 40px rather than matching the wordmark's 28px because its two concentric rings merge into one muddy band below ~36px. Its rings are dropped entirely from the favicon, where 16px leaves no chance of resolving them. The favicon deliberately avoids CSS custom properties. A first pass used var(--mark) and rendering caught that librsvg drops it -- fill fell back to black and stroke: var() failed outright, silently deleting the crossbar. Browsers handle it, but favicons pass through enough non-browser pipelines that plain class rules are the safer choice. Its base color is brand blue because Safari ignores prefers-color-scheme in favicons; blue stays legible on both light and dark tab strips, and the media queries sharpen it to white or navy where they are honored. Verified the fallback path renders correctly. triangle.ico stays registered for browsers with no SVG favicon support. logo.png is left in place for now; it is no longer referenced. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The card mapped over `users`, a Set built from the event feed, so it listed everyone who had ever done anything in whatever order their name first showed up. In practice that meant an unsorted list with someone sitting at the bottom on a single event -- it read as broken, because it was. Each row also recomputed its own count with a full events.filter(), so the card was O(users * events) for a number the same pass could have produced once. topContributors now counts into a Map in one pass, sorts by count descending with a name tiebreak, and caps at five. It is the same shape as the actionEntries memo directly above it, which was already doing this correctly. Capped rather than thresholded on purpose. A minimum-count filter would blank the card on a quiet install; a cap drops the single-event case while still showing everyone when there are only a few contributors. `users` stays deliberately unsorted, and that is the trap here: it is what pins a person to an avatar color, and the feed picks its colors from the same array by index. Sorting it in place would have recolored the feed and, worse, given the same person two different colors across the two panels. Both now go through one avatarColor helper. Also adds the empty state the Actions Breakdown card already had. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two unrelated frontend cleanups that came up together.
Vector Delta branding
The sidebar wordmark was a raster
logo.png. It is now the SVG artwork, with the triangle mark shown when the sidebar is collapsed, and a real favicon — the app was previously serving The Triangle's blue "T", not Delta's own mark.The supplied SVGs sit in a 377x347 canvas that the artwork occupies a thin band of, so each
viewBoxis tightened to the actual bounding box; otherwiseobject-containshrinks the wordmark to about a fifth of its intended size. Sizing moved from a fixedw-30 h-30square to height-anchored, since the new mark is 5.3:1 where the PNG was 2.8:1.Sizes were picked by rendering rather than by guessing:
Two things in the favicon that are load-bearing and easy to undo by accident:
var(--mark); rendering caught that librsvg silently drops it, sofillfell back to black andstroke: var(--mark)failed outright, deleting the crossbar. Browsers are fine with it, but favicons pass through enough non-browser pipelines that plain class rules are safer.prefers-color-schemein favicons, so the base has to survive both light and dark tab strips. The media queries sharpen it to white or navy where they are honored. The fallback path was rendered and checked.triangle.icostays registered for browsers without SVG favicon support.logo.pngis now unreferenced but left in the tree.Top Contributors
The card mapped over
users— aSetbuilt from the event feed — so it listed every person who had ever done anything, ordered by whenever their name first appeared. Unsorted, uncapped, with someone sitting at the bottom on a single event. Each row also recomputed its count with a fullevents.filter(), making the card O(users × events).It now counts into a
Mapin one pass, sorts by count descending with a name tiebreak, and caps at five — the same shape as theactionEntriesmemo directly above it, which was already correct. Capped rather than thresholded on purpose: a minimum-count filter would blank the card on a quiet install.Reviewer note:
usersis still deliberately unsorted. It is what pins a person to an avatar color, and the feed indexes into the same array. Sorting it would recolor the feed and give the same person two different colors across the two panels. Both paths now go through a singleavatarColorhelper — that is the thing to preserve if this code moves.Verification
tsc --noEmitandeslintclean,vite buildsucceeds, favicon confirmed to land indist/. The ranking logic was run against the numbers from the actual broken screenshot and produces the expected order. Rendering checks were done withrsvg-convertat real display sizes on both background colors.Not visually verified in a running browser — authenticated CMS pages have no login-free path, so that needs a manual look after deploy.
🤖 Generated with Claude Code