Skip to content

Say unread on the tray icon itself, not only in its tooltip - #127

Merged
jlucaso1 merged 1 commit into
mainfrom
claude/issue-125-pr-hxm3gb
Sep 3, 2026
Merged

Say unread on the tray icon itself, not only in its tooltip#127
jlucaso1 merged 1 commit into
mainfrom
claude/issue-125-pr-hxm3gb

Conversation

@jlucaso1

@jlucaso1 jlucaso1 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Closes #125.

What was wrong

The unread count already reaches the tray — TrayState::unread is computed in the store, filtered through ChatSummary::counts_toward_unread, and watched by the tray task — and then stopped at the tooltip. icon_name asked one question, whether the connection was up, so the icon was user-available over a chat list with three unread messages in it and over one with none. The reporter had messages waiting and the icon showed nothing, which is exactly right: the only thing that knew was a tooltip you have to hover to read.

What this does

  • The icon changes. icon_name now answers three states rather than two: user-offline disconnected, user-available connected and caught up, and a themed mail-unread while something is waiting. Still a name from the icon-naming spec rather than shipped pixels, like the two it joins, so it follows the user's theme.
  • The item asks for attention. status goes to NeedsAttention while unread, which is the only thing StatusNotifierItem has that resembles a badge — it asks a host to emphasise the icon, and it keeps the item visible in a host that hides passive ones. attention_icon_name names the same icon as icon_name: a host honouring the status swaps to it, a host ignoring the status reads the other, and naming one icon in both is what keeps the two answers from showing different things.
  • The category becomes Communications. It was the default ApplicationStatus. Hosts group by it, and some only emphasise an item that claims to be a communications one — which is the state above.
  • The count stays in the tooltip, and also in its title. SNI carries no number, so the tooltip is where it can be said; the title gets oxidezap (3) for a host that renders only the first line, and the description is unchanged.
  • One place reads the count. Item::unread is what the icon, the status and the tooltip all go through, and it reads zero while the connection is down — a count nothing is refreshing is not worth asking to be looked at, and "Disconnected" is what the tooltip already said in that state. Written once so the icon and the tooltip cannot come to disagree about what is unread.
  • docs/gotchas.md gets the reasoning, next to the rest of the tray's.

Not in scope

A number drawn on the icon. That means icon_pixmap — rendering ARGB32 ourselves, with a font, at whatever sizes the host asks for, and giving up the themed name that follows the user's icon set and light/dark switch. The protocol offers no counter, and every tray that shows one paints it. Worth doing if the state above turns out not to be enough, but it is a different change and it costs the theming.

Nothing changes off Linux: the tray has one implementation and the others still return "no tray implementation for this platform yet" from spawn.

Tests

tray::linux::tests, new — the icon over a state, with the hub and the click left at their defaults since nothing asks them anything:

  • unread_reaches_the_icon_itself — the report itself: an icon with something to read does not look like one without, it is the unread name, the status is NeedsAttention, the attention icon matches it, and an idle icon is Active.
  • a_disconnected_icon_says_the_connection_and_not_a_stale_count — three unread and no connection is user-offline, Active, and "Disconnected".
  • the_tooltip_carries_the_count — the title and the description at zero, one and four, and the tooltip's icon following the one being shown.

Ran locally on 1.98: cargo fmt --all --check, cargo clippy -p oxidezap-daemon --all-targets --all-features (clean), and cargo test -p oxidezap-daemon --all-features — 196 + 8 passed, 0 failed. Leaving the full-workspace and web checks to CI.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NX17ZrYJmz5SXjLMRn1VXo


Generated by Claude Code


Summary by cubic

Fixes the tray icon to show unread messages on the icon itself, not just in its tooltip. Closes #125.

  • The icon now changes to mail-unread when there are unread messages, instead of always showing user-available while connected.
  • The item's status goes to NeedsAttention and category to Communications, which hosts use to emphasise the icon and keep it visible.
  • The unread count also appears in the tooltip title (e.g., oxidezap (3)).
  • Item::unread is the single source of truth for the count, reading zero while disconnected so a stale count is not shown.
  • No number is drawn on the icon; that would require custom pixmaps and break theming.

Written for commit d4a5e79. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Linux tray icons now indicate offline, connected-without-unread-items, and unread states.
    • Unread items trigger the tray’s attention state.
    • Tooltips display the current unread count and active icon.
    • Unread counts show as zero while disconnected, preventing stale information.
  • Documentation

    • Added guidance describing how unread status and counts are represented in the Linux tray.

The count reached the tray and stopped at the tooltip: the icon was
"user-available" whether or not anything was waiting, so a glance at a
full chat list saw an idle icon.

StatusNotifierItem has no badge, so the count is spoken as a state. The
icon takes a themed `mail-unread` while something is unread and the item
goes to `NeedsAttention`, which also keeps it visible in a host that
hides passive items; both `icon_name` and `attention_icon_name` name it,
because a host may honour one and not the other. The category becomes
`Communications`, which some hosts require before emphasising an item.

The number stays in the tooltip and now rides in its title too, for a
host that renders only the first line. `Item::unread` is the one place
that reads the count, and reads it as zero while disconnected: a stale
number nothing is refreshing is not worth asking to be looked at, and it
is what the tooltip already said.

Closes #125.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NX17ZrYJmz5SXjLMRn1VXo
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: 407a633f-d9aa-41a3-a259-1b35e1fc715d

📥 Commits

Reviewing files that changed from the base of the PR and between 415ba6b and d4a5e79.

📒 Files selected for processing (2)
  • crates/daemon/src/tray/linux.rs
  • docs/gotchas.md

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The Linux tray now derives unread state through a connection-aware helper. It uses themed icons, NeedsAttention, the Communications category, and tooltips that show the normalized unread count and active icon.

Changes

Linux tray unread state

Layer / File(s) Summary
Normalize and render unread state
crates/daemon/src/tray/linux.rs
Item::unread returns zero while disconnected. The tray selects offline, idle, and unread icons, reports NeedsAttention, uses the Communications category, and includes the count and icon in tooltips.
Validate and document tray behavior
crates/daemon/src/tray/linux.rs, docs/gotchas.md
Tests cover unread state, disconnected stale data, and tooltip output. The documentation describes the tray icon and status contract.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to d4a5e

Linux tray items now visibly distinguish offline, idle, and unread-message states while keeping connection-aware unread counts consistent in icons and tooltips. No current merge-readiness risk remains.

Sequence Diagram(s)

sequenceDiagram
  participant DaemonConnection
  participant Item
  participant StatusNotifierHost
  DaemonConnection->>Item: provide connection and unread state
  Item->>Item: normalize unread count
  Item->>StatusNotifierHost: provide icon, status, category, and tooltip
Loading

Poem

A rabbit reads each line,
The patch grows clear beneath the moon,
Small changes hop in place,
Tests guard the garden path,
Reviews bloom before the dawn.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 14 functions across 1 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly states the main change: unread messages are visible on the Linux tray icon instead of only in the tooltip.
Linked Issues check ✅ Passed The changes directly address issue #125 by showing unread messages through tray icon states and attention metadata. Tooltip and disconnected-state updates support consistent unread reporting.
Out of Scope Changes check ✅ Passed The Linux tray implementation, tests, and documentation changes are directly related to the linked issue and stated objectives. No unrelated code or non-Linux changes are identified.
Full details: Docstring Coverage

Explanation

Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 14 functions across 1 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@jlucaso1
jlucaso1 merged commit 39e8c9d into main Sep 3, 2026
10 checks passed
@jlucaso1
jlucaso1 deleted the claude/issue-125-pr-hxm3gb branch September 3, 2026 16:15
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.

Pending messages on the tray icon

2 participants