Skip to content

Fix Windows notification color inversion#211

Open
rtibbles wants to merge 1 commit into
mainfrom
codex/fix-kolibri-tray-icon-color-inversion
Open

Fix Windows notification color inversion#211
rtibbles wants to merge 1 commit into
mainfrom
codex/fix-kolibri-tray-icon-color-inversion

Conversation

@rtibbles

@rtibbles rtibbles commented Dec 3, 2025

Copy link
Copy Markdown
Member

Summary

Fixes the Kolibri tray-notification icon rendering as a color negative on Windows light theme (#184).

Root cause: every entry in kolibri.ico was PNG-compressed, including the small 16–48px sizes. The Shell_NotifyIcon balloon path can't decode PNG-in-ICO and falls back to legacy XOR-mask drawing — XOR against a light background inverts the colors, XOR against a dark background leaves them intact. This matches the symptom exactly: inverted in light theme, correct in dark theme, correct in the tray icon itself.

Fix: re-encode the small sizes (16/24/32/48) as uncompressed BMP; keep 64/128/256 as PNG. Icon renders pixel-identically everywhere; no code change.

Testing

On Windows 10/11, Settings > Personalization > Colors, set mode to Light. Launch Kolibri and wait for the "Kolibri is starting…" / "Kolibri Ready" notification. The Kolibri logo should show its correct colors (yellow background, blue bird) — no longer inverted.

@rtibbles rtibbles force-pushed the codex/fix-kolibri-tray-icon-color-inversion branch from 492bae9 to 2520cbd Compare December 3, 2025 17:23
@rtibbles rtibbles changed the title Remove redundant wxPython import comment Fix Windows notification color inversion Dec 3, 2025
@rtibbles rtibbles marked this pull request as draft December 3, 2025 19:02
@rtibbles rtibbles requested a review from rtibblesbot March 4, 2026 03:45

@rtibblesbot rtibblesbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean fix for the Windows notification icon color inversion (#184). Replaces legacy ShowBalloon() with wx.adv.NotificationMessage, which handles icon rendering correctly across Windows themes. CI passing.

  • praise: Good defensive fallback chain and error handling throughout

@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?

Reviewed the pull request diff checking for:

  • Correctness: bugs, edge cases, undocumented behavior, resource leaks, hardcoded values
  • Design: unnecessary complexity, naming, readability, comment accuracy, redundant state
  • Architecture: duplicated concerns, minimal interfaces, composition over inheritance
  • Testing: behavior-based assertions, mocks only at hard boundaries, accurate coverage
  • Completeness: missing dependencies, unupdated usages, i18n, accessibility, security
  • Principles: DRY (same reason to change), SRP, Rule of Three (no premature abstraction)
  • Checked CI status and linked issue acceptance criteria
  • For UI changes: inspected screenshots for layout, visual completeness, and consistency

Comment thread src/kolibri_app/taskbar_icon.py Outdated
logging.error(
f"Failed to show notification with NotificationMessage: {notification_error}"
)
self._show_legacy_notification(title, message, timeout)

@rtibblesbot rtibblesbot Mar 4, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved — addressed in the current code.

praise: Well-structured fallback chain: NotificationMessageShowBalloonMessageBox. Each layer catches specific exceptions and logs before falling through, so notifications degrade gracefully on older Windows versions or unexpected runtime errors.

Comment thread src/kolibri_app/taskbar_icon.py Outdated
# Cached icon used for notifications
self._tray_icon = None
# Keep reference alive while shown
self._last_notification = None

@rtibblesbot rtibblesbot Mar 4, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved — addressed in the current code.

praise: Good call keeping _last_notification as an instance attribute — prevents the NotificationMessage object from being garbage-collected while the notification is still visible.

@rtibbles rtibbles marked this pull request as ready for review March 5, 2026 22:04
@rtibbles rtibbles force-pushed the codex/fix-kolibri-tray-icon-color-inversion branch from 2520cbd to 8519ef3 Compare June 23, 2026 17:41
@rtibbles rtibbles requested a review from rtibblesbot June 23, 2026 17:42
@pcenov

pcenov commented Jun 30, 2026

Copy link
Copy Markdown
Member

Hi @rtibbles - unfortunately the app is not starting in both Windows 11 and 10, seems that this is the relevant error in the logs:

Kolibri App Initializing
Started at: 2026-06-30 13:55:29.681412
Traceback (most recent call last):
  File "kolibri_app\application.py", line 96, in OnInit
  File "kolibri_app\application.py", line 165, in start_server
  File "kolibri_app\taskbar_icon.py", line 172, in notify_server_starting
  File "kolibri_app\taskbar_icon.py", line 131, in show_notification
  File "kolibri_app\taskbar_icon.py", line 149, in _notification_timeout
AttributeError: type object 'NotificationMessage' has no attribute 'Timeout_Default'
OnInit returned false, exiting...

Logs: logs.zip

The tray balloon rendered the Kolibri icon as a color negative in Windows
light theme (correct in dark theme, and correct in the tray itself). Every
entry in kolibri.ico was PNG-compressed, including the 16-48px sizes. The
Shell_NotifyIcon balloon path can't decode PNG-in-ICO, so it falls back to
XOR-mask drawing: XOR against a light background inverts the colors, XOR
against a dark one leaves them intact.

Re-encode the small sizes (16/24/32/48) as uncompressed BMP; keep 64/128/256
as PNG. Renders identically everywhere.

Fixes #184

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018nCc7x3MRihUCQFUUP46jc
@rtibbles rtibbles force-pushed the codex/fix-kolibri-tray-icon-color-inversion branch from 8519ef3 to 4303712 Compare July 4, 2026 01:33
@rtibblesbot

rtibblesbot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

🔵 Review posted

Last updated: 2026-07-04 01:42 UTC

@rtibblesbot rtibblesbot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 of 3 prior findings resolved — the prior approach (NotificationMessage) was abandoned entirely in favor of a binary icon re-encode, so the Timeout_Default crash and both praise comments on that removed code are moot.

Still open (not caused by this PR, but blocking a green merge):

  • important: CI's "All file linting" (uv-lock prek hook) is failing, but this predates this branch — confirmed the same job fails on main's tip (a9f1e13, this branch's merge-base) via the check-runs API. pyproject.toml's exclude-newer = "7 days" is a rolling window, so uv.lock needs periodic re-resolution independent of this diff. Nothing in this PR (kolibri.ico only) can fix it — needs a uv lock regen on main and a rebase.
  • suggestion: Since this PR no longer touches taskbar_icon.py, @pcenov's crash report was against the abandoned approach. Worth a short comment confirming they've re-tested against this narrower icon-only fix.

Verified the re-encoded kolibri.ico directly: 16/24/32/48px are now raw BMP (matching the Shell_NotifyIcon legacy balloon path that caused the original inversion), 64/128/256px remain PNG. Decoded all 7 sizes — consistent yellow/blue colors, no inversion.

Prior-finding status

RESOLVED — src/kolibri_app/taskbar_icon.py — NotificationMessage.Timeout_Default crash reported by pcenov (approach replaced with icon re-encode, not patched)
RESOLVED — src/kolibri_app/taskbar_icon.py:115 — praise: defensive NotificationMessage → ShowBalloon → MessageBox fallback chain (code removed, no longer applicable)
RESOLVED — src/kolibri_app/taskbar_icon.py — praise: _last_notification instance attribute (code removed, no longer applicable)


@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

How was this generated?

Compared the current PR state against findings from a prior review:

  • Retrieved prior bot reviews via the GitHub API
  • Classified each prior finding as RESOLVED, UNADDRESSED, ACKNOWLEDGED, or CONTESTED
  • Only raised NEW findings for newly introduced code
  • Ran the same phased review passes as a first review (core, frontend/backend lenses, manual QA when required)
  • Synthesized one review from the passes and chose the verdict from the findings, CI status, and QA evidence

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants