Skip to content

🌈 feat(notes): tint inline notes per agent author#294

Open
sdougbrown wants to merge 2 commits into
modem-dev:mainfrom
sdougbrown:pr/named-agent-color
Open

🌈 feat(notes): tint inline notes per agent author#294
sdougbrown wants to merge 2 commits into
modem-dev:mainfrom
sdougbrown:pr/named-agent-color

Conversation

@sdougbrown
Copy link
Copy Markdown

@sdougbrown sdougbrown commented May 11, 2026

Summary

Tint inline agent notes per author so reviews with multiple agents are visually distinct at a glance. The author name in the title bar (added in the linked PR) tells the reviewer which agent wrote a note; this PR makes that distinction readable without reading the label, by giving each agent a stable colour across the diff.

I personally want this because I'm doing crazy agent orchestration for laughs and I'd like to know which agent is saying what.

Implementation:

  • New per-theme noteAccentPalette on themes.ts (five accents per appearance) tuned for dark + light themes.
  • New src/ui/lib/agentColor.ts resolves an annotation's author to a stable palette slot via FNV-1a hash, then derives:
    • the border accent for the inline note frame (top/sides/bottom + the title row's right-edge bar)
    • the guide-cap accent for the glyph below a multi-row annotation range
    • the body and title backgrounds, computed in HSL with low saturation + theme-appropriate lightness so theme.text / theme.muted body copy stays legible.
  • The cap accent threads through the planned note-guide-cap row in reviewRenderPlan.ts and resolves in PierreDiffView.tsx. First-author-wins when two notes share a row+side.
  • Background tint falls back to theme.noteBackground / theme.noteTitleBackground whenever no author is set, palette resolution returns null, or accent parsing fails. Unauthored notes also keep theme.noteBorder.
  • Demo update: spread 3-agent-review-demo across all five accent slots (llama, grok, phi, gemini, sonnet) so one screenshot captures the full palette.

Tests include contrast-ratio assertions against theme.text for both palettes, so accidentally introducing an illegible accent fails CI.

Screenshot

Screenshot 2026-05-11 at 10 52 19 AM

Verified

  • bun run typecheck
  • bun run lint
  • bun test — same baseline failures as upstream/main, no new failures. +21 net passing tests from the new colour/render-plan/ui suites.
  • bun run test:integration (PTY)
  • bun run test:tty-smoke
  • Manual TTY smoke on examples/3-agent-review-demo/change.patch with --agent-context examples/3-agent-review-demo/agent-context.json — five distinct tints visible on the demo.

Related

Depends on #293. The colour tests in this PR assert title text like "sonnet · ▶ new 2-4", which is rendered by the title-bar change in #293. Please merge #293 first; this PR's diff will collapse to its own commit once the base lands on main.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 11, 2026

Confidence Score: 4/5

Safe to merge; color logic is well-tested with WCAG contrast assertions and all paths fall back to existing theme defaults.

The implementation is thorough and the fallback chain is correctly handled at every call site. The only noteworthy items are two style-level observations: duplicate title-formatting functions across AgentInlineNote and agentPopover that will need coordinated updates if the format ever changes, and the mutable string[] type on noteAccentPalette where readonly string[] would better reflect the intent. Neither affects runtime behaviour.

No files require special attention; src/ui/lib/agentColor.test.ts has a minor WCAG threshold inaccuracy but it does not affect any of the assertions for the current palette values.

Important Files Changed

Filename Overview
src/ui/lib/agentColor.ts New module implementing FNV-1a author→palette-slot hashing and HSL-based background derivation; logic is correct and edge cases (empty palette, whitespace-only author, malformed hex) are handled.
src/ui/lib/agentColor.test.ts Comprehensive test suite including WCAG contrast assertions; uses the obsolete IEC sRGB linearization threshold (0.03928 vs 0.04045) in the local contrastRatio helper, though the palette values tested are far from the affected range so no test is actually wrong.
src/ui/themes.ts Adds required noteAccentPalette field to AppTheme interface and populates it for all four themes; all themes covered, no missing entries.
src/ui/components/panes/AgentInlineNote.tsx Wires accent/background derivation into the note frame and title strip; fallback to theme defaults is correct; duplicate title-formatting function with agentPopover.ts is a minor DRY concern.
src/ui/diff/reviewRenderPlan.ts Guide-cap tracking refactored from Set to Map to carry the first-author-wins accent; logic is correct and the first-author-wins comment documents the intentional tie-breaking rule.
src/ui/lib/agentPopover.ts Adds author parameter to buildAgentPopoverContent and the local title formatter; logic is a direct mirror of AgentInlineNote.tsx's inlineNoteTitle, creating two copies of the same formatting rule.
src/ui/diff/PierreDiffView.tsx Resolves the cap accent from plannedRow.author and passes it to AgentInlineNoteGuideCap; null→undefined conversion is intentional to satisfy the optional prop type.
src/ui/components/panes/AgentCard.tsx Adds author-aware accent to the floating card; fallback to theme.accent preserves the previous behaviour for cards without an author.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["annotation.author (string | undefined)"] --> B["resolveAuthorAccent(author, theme.noteAccentPalette)"]
    B -- "null (no author / empty palette)" --> C["theme.noteBorder / theme.accent fallback"]
    B -- "accent hex string" --> D["AgentInlineNote border + sides"]
    B -- "accent hex string" --> E["deriveAuthorBackground(accent, appearance)"]
    B -- "accent hex string" --> F["deriveAuthorTitleBackground(accent, appearance)"]
    E -- "HSL: fix lightness, cap saturation" --> G["noteBackground tint"]
    F -- "HSL: fix lightness+1 step, cap saturation" --> H["noteTitleBackground tint"]
    B -- "accent hex string" --> I["reviewRenderPlan: Map(side → author)"]
    I --> J["PlannedRow note-guide-cap { author }"]
    J --> K["PierreDiffView → AgentInlineNoteGuideCap accent prop"]
Loading

Comments Outside Diff (1)

  1. src/ui/lib/agentPopover.ts, line 944-949 (link)

    P2 Duplicated title-formatting logic

    agentPopoverTitle in agentPopover.ts and inlineNoteTitle in AgentInlineNote.tsx are byte-for-byte identical in their author/count branching. If the format string ever changes (e.g., to add an emoji or separator), both sites need updating independently — with no compiler help to catch the second copy.

    Fix in Claude Code Fix in Codex

Fix All in Claude Code Fix All in Codex

Reviews (1): Last reviewed commit: "🌈 feat(notes): tint inline notes per ag..." | Re-trigger Greptile

Comment thread src/ui/lib/agentColor.test.ts Outdated
Comment thread src/ui/themes.ts Outdated
@sdougbrown sdougbrown force-pushed the pr/named-agent-color branch from 3f32dcf to cd11527 Compare May 11, 2026 21:51
The AgentAnnotation schema has carried an optional `author` field
end-to-end (sidecar JSON, session daemon, wire protocol) but the TUI
never surfaced it. Render it in the note title bar and the matching
agent popover so reviewers can tell which agent left which note when
multiple agents annotate the same diff. Falls back to "AI note" when
author is absent for backward compat.
When an annotation carries `author`, derive a stable accent color from
a new per-theme `noteAccentPalette` via FNV-1a hash and tint the entire
inline note card from that accent — top/side/bottom borders, the title
row's right-edge bar, the trailing `╵` guide cap below a multi-row
range, and the body/title backgrounds (derived in HSL with low
saturation and theme-appropriate lightness so `theme.text` /
`theme.muted` body copy stays legible). The cap accent is threaded
through the planned `note-guide-cap` row and resolved in
PierreDiffView, with first-author-wins when two notes share a row+side.
Background tint falls back to `theme.noteBackground` /
`theme.noteTitleBackground` when no author is set, palette resolution
returns null, or accent parsing fails; unauthored notes keep
`theme.noteBorder`. Contrast-ratio tests guard the dark and light theme
palettes against `theme.text`.

Also spread the `3-agent-review-demo` example across all five accent
slots (llama, grok, phi, gemini, sonnet) so one screenshot captures
the full palette.
@sdougbrown sdougbrown force-pushed the pr/named-agent-color branch from cd11527 to 9a2a460 Compare May 12, 2026 00:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant