Per-author fireflies + co-author support, commit-pane perf, gem palette#50
Merged
Conversation
Wire-format rename in preparation for Co-authored-by trailer parsing and per-author fireflies. Cache version bump to 11 silently invalidates old git-history caches. No behavior change yet — every commit still has exactly one author in authors[0]. Phase B will parse trailers.
Wire-format mirror of the api change. Most readers pull authors[0]; the top app-header breadcrumb chip surfaces co-author count via a '<primary> (+N)' suffix when N > 0. Phase B+ will parse trailers server-side and per-author fireflies will fan out client-side.
Uses git's native %(trailers:key=Co-authored-by,valueonly,separator) placeholder — no body parsing on our side. Trailers field placed before %s in the log format so the existing tabs-in-subject invariant holds. Deduped per commit on the name string (primary author + each unique co-author). Adds a multi-author fixture commit with a Signed-off-by trailer (ignored) and a duplicate primary-author trailer (deduped) to verify both edge cases.
Privacy fix — _build_authors_list previously emitted the full email when a trailer had no name (e.g. `<bot@host>`), contradicting the "emails stripped" guarantee on CommitEntry and CommitDetailResponse. Now keeps only the email local-part as the identity. Adds direct unit tests for the helper (no test coverage existed before — only integration coverage via the fixture). Also fixes a stale docstring on _serve_commit_detail that still referenced the pre-rename `author` field. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the ORBS_PER_TREE=1 constant with iteration over commit.authors. Each author gets a stable, per-(sha, author) seed so multi-author commits emit visibly distinct orbits with per-author colors. Co-authorship counts as full credit toward the per-author scale tally.
- Fix test comment claiming Alice was "solo" on a co-authored commit - Rename per-tree-orb-count test to clarify it now covers the single-author regression case (post-Phase-C the count isn't a general invariant) - Restore the dropped inline comment explaining the orbit-radius multiplier - Drop the stale ORBS_PER_TREE reference from renderLoop.ts's structural-key comment Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the single .commit-author block with a loop over commit.authors. Primary author stays first; each row carries the per-author color dot. Existing CSS for .commit-author renders the rows as a stack (block-level divs).
- appHeader.ts: update stale comment to match the new (+N) format - TODO.md: mark per-author fireflies + co-author support done
Before this fix, hover/select on a multi-author commit's tree showed only ONE orbit ring even though Phase C renders multiple per-author orbs. Root cause: orbitRings.ts keyed its placement lookup by commitIndex with a single FireflyPlacement value, so last-write-wins collapsed the N per-author orbs to one. The single ring built used only the last author's orbitRadius/orbitTilt. Fix: store FireflyPlacement[] per commitIndex, and let each slot (hover + selected) own N meshes — one per author orb on the active commit. Disposal walks the array. Adds 5 multi-author tests (hover N rings, selected N rings, hover+selected on different multi-author commits, clear-hover disposes all N, distinct geometries per author).
Hover orbit rings now render in each author's pastel hue (OKLCH at L=0.90, C=0.10 — pastel of the orb color) so a hovered multi-author tree shows one tinted ring per author. Selected orbit rings render the shared RAINBOW chase used by the tree-canopy outline (vertexColors on the TubeGeometry, hue-per-tubular-segment, advanced every frame). Drops the now-dead ORBIT_RING_HOVER_COLOR and ORBIT_RING_SELECTED_COLOR knobs from FirefliesConfig and the Controls pane. Bumps the schema version comment to v8. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Was OKLCH (L=0.90, C=0.10) — too pastel; the hovered ring read as generic-light instead of "author X's color, but lighter". Now L=0.84, C=0.18 (same chroma as the orb, only +0.06 lift on lightness) so the hue identity carries through.
…rs.ts Three previously-separate modules (hsl.ts, oklch.ts, colorInterp.ts) covered overlapping color-math territory. Merge into one colors.ts with sections for each space: - HSL string helpers (mirror hsl.glsl for shader parity) - OKLab Cartesian conversion (out-params for hot paths) - OKLCH polar wrapper (allocates; palette-generation use cases) - linear sRGB → hex encoding - interpolateOklch (perceptual color interpolation) Update the three importers (authorColor.ts, treeRenderer.ts, gem.ts — gem in next commit) and relocate colorInterp.test.ts next to its module. Delete the old hsl.ts and colorInterp.ts. The oklchToLinearRgb function previously inlined in authorColor.ts is gone — it now lives in the shared module and reuses oklabToLinear internally, removing the duplicate OKLab→linear-sRGB matrix.
Hand-picked pastel hex values gave perceptually-uneven transitions (yellow read brighter than blue at the same lightness). Generate the 8 defaults from OKLCH at L=0.78, C=0.18 across evenly-spaced hues instead — each face is the same perceptual lightness, so the rainbow transitions evenly around the gem. Matches the firefly orb palette parameters (also L=0.78, C=0.18) so the gem reads visually consistent with the per-author orbs. User overrides via Controls are unaffected — only the defaults change.
The four favicon triangles now use 4 of the 8 OKLCH-derived face colors (hues 135°/225°/315°/45° at L=0.78, C=0.18) so the icon reads as the same gem the user sees floating in the city. The previous hand-picked #bfff33/#26e5ff/#9940ff/#ff338c were perceptually uneven in the same way the old face palette was. gem-simple.svg stays untouched — it's the monochrome variant for inline use on colored backgrounds.
The previous quartet (135/225/315/45) put lime (NW) next to sky (NE) on the top edge — both cool-greens-to-blues, too perceptually close for a tiny icon. Replace with the other four of the 8-face palette (90/0/180/270): gold + pink on top (both warm, but clearly distinct hues), teal + periwinkle on bottom (the less-prominent diagonal). Still 4 of the 8 OKLCH face colors, so the icon still reads as "the gem palette" — just a different sample.
…vicon sizes Without strokes between the 4 triangles, anti-aliasing at 16-24px favicon scales blended adjacent fills into a muddy gradient — even with high-contrast colors the user couldn't tell faces apart. Add the same white stroke pattern gem-simple.svg already uses (stroke-width=2, stroke-linejoin=round). Matches the in-app gem's EDGE_COLOR (#ffffff) so the icon and the 3D gem render with the same "outlined faces" look.
…order Four primary-color faces with high lightness AND hue separation so they stay distinct at 16-24px favicon sizes without needing a stroke.
Picks 4 of the 8 OKLCH face hues — 0°/90°/180°/270° (pink/gold/teal/ periwinkle) — and arranges them clockwise around the diamond as warm/cool/warm/cool so every adjacent edge has a temperature jump. Same uniform lightness as the in-app gem, so the favicon literally samples the gem palette.
Keep the in-scene gem palette OKLCH-derived (config/components/gem.ts still generates faces via oklchToHex). Only the favicon goes back to its hand-picked quartet — those colors hold up better at 16-24px favicon sizes than any uniform-lightness 4-of-8 subset of the OKLCH palette did without strokes. Also picks up a stale import path in colorInterp.test.ts that was missed in the consolidation commit.
Faces are large solid areas, so a moderate chroma bump reads as noticeably more saturated without crossing into neon. The fireflies stay at their original L/C because they're tiny moving dots whose color already gets pushed by the additive bloom pass.
Render every piece of manifest-resident metadata (authors, sha, date, files, same-day, subject) synchronously on setCommit. Only the lazy-fetched body waits for /api/commit. The full-pane 'Loading commit…' blanking state goes away — clicking a commit tree now displays the skeleton instantly, and the body fades in below. Layout reorder so the lazy region sits at the BOTTOM of the pane: authors → meta (age · files) → same-day → no-remote → subject → body slot. When the body resolves, nothing above moves. The body slot has four states (loading / text / error / hidden) and only this slot mutates after the skeleton renders. Errors no longer blank the rest of the pane. Subject bumps to font-xl / text-primary / weight 600 — it's now the title of the message block at the bottom. Body's nested max-height scrollbar is gone; the pane-body owns scrolling.
- Move _bodySlotEl + BodyState declarations to precede _renderEmpty (was previously order-fragile — _renderEmpty wrote to _bodySlotEl before its declaration line) - Update CSS comments that still referenced the old "footer" layout (.commit-meta and friends now sit ABOVE the message, not below it) - Drop the empty .commit-message-body-slot and .commit-message-body-slot --error CSS rules — base class has no styles, error modifier is a JS-side semantic marker only Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reorder: commit title (subject) moves to the top of the pane so it's the first thing the eye lands on. Authors drop one type-step (xl→lg, 14px→12px) and the author dot shrinks from 10px to 8px so they read as secondary metadata below the title. New order: subject → authors → meta → same-day → no-remote → body slot.
- .commit-age now reads just "1 month ago" (was "committed 1 month ago") - Stacked author rows tighten from ~12px to 4px effective gap. The parent flex gap stays at 8px for between-section breathing room; consecutive .commit-author rows pull up with a small negative margin.
Visible text drops "that day" ("Busy day — 24 commits that day" →
"Busy day: 24 commits") and moves the date into the hover tooltip:
"24 commits on March 12, 2026". The tooltip uses toLocaleDateString
with a manual YMD parse so the date doesn't shift across timezones
the way `new Date('YYYY-MM-DD')` would.
Move formatRelativeAge from views/widgets/ to utils/ and add three sibling helpers used elsewhere: - formatRelativeAgeShort (was appFooter's private _relativeTime) - formatShortDate (was appFooter's private _formatDate, sans the null-handling — call sites already null-guard) - formatFullDate (was commitPane's private _formatFullDate) appFooter and commitPane drop their private copies; inputHandlers updates its import path. Test file moves alongside the source.
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.
Summary
Three feature areas across 28 commits.
Per-author fireflies + co-author support
Co-authored-by:trailers via git's native%(trailers)placeholder.CommitEntry.author: str→authors: list[str]; emails stripped (local-part fallback for email-only trailers).<primary> (+N)for multi-author commits.Commit-pane perf + layout
Busy day: 24 commits; full date moves to a hover tooltip.max-heightscrollbar removed — pane-body owns scrolling.Gem palette + color utilities
scene/utils/color/colors.ts. AddsoklchToHexfor palette generation.utils/dates.ts(formatRelativeAge,formatRelativeAgeShort,formatFullDate,formatShortDate). Drops private helpers inappFooterandcommitPane.Tests + docs
api/tests/fixtures/sample-repoexercises co-authored trailers including a duplicate-primary-author edge case and an email-only trailer._build_authors_listdirect unit tests + integration coverage on the multi-author commit.Test plan
just test— expect ~264 api tests + ~2010 app tests passing.Co-authored-by:commits (e.g. this repo). Click a multi-author tree:<primary> (+N).🤖 Generated with Claude Code