Skip to content

feat(linux): integrate GTK4 WebKitGTK6 stack - #3

Merged
OlympusLedgerOrg merged 9 commits into
gtk4-webkit6-tao-wryfrom
gtk4-webkit6-integration
Jul 7, 2026
Merged

feat(linux): integrate GTK4 WebKitGTK6 stack#3
OlympusLedgerOrg merged 9 commits into
gtk4-webkit6-tao-wryfrom
gtk4-webkit6-integration

Conversation

@OlympusLedgerOrg

@OlympusLedgerOrg OlympusLedgerOrg commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Second stacked PR for the GTK4/WebKitGTK6 port, kept below CodeRabbit's 300-file limit.\n\nChanges:\n- vendors the GTK4 muda port and KSNI tray-icon port\n- switches Tauri crates to gtk4 / webkit6 and regenerates Cargo.lock\n- updates Linux CI package installs from GTK3/WebKit2GTK/AppIndicator to GTK4/WebKitGTK6/D-Bus\n- updates CLI/bundler Linux package metadata and AppImage WebKitGTK helper paths\n- adds tests pinning deb/rpm runtime dependency names away from WebKit2GTK/GTK3/AppIndicator\n\nValidation:\n- cargo fmt --check\n- git diff --check\n- cargo fmt --check --manifest-path ports/muda/Cargo.toml\n- cargo fmt --check --manifest-path ports/tray-icon/Cargo.toml\n- cargo test -p tauri-cli linux_\n- cargo check -p tauri-runtime -p tauri-runtime-wry -p tauri --features wry,tray-icon\n- cargo check -p tauri-runtime-wry --all-features\n- cargo check -p tauri --all-features

Summary by CodeRabbit

  • New Features

    • Added broader menu and tray icon capabilities, including richer menu items, accelerators, icons, and “About” metadata support.
    • Added new examples and docs for tray icons and menus across desktop platforms.
  • Bug Fixes

    • Updated Linux and macOS support to use newer GTK/WebKit integrations for better compatibility.
    • Improved Android handling in CI and runtime setup.
  • Chores

    • Expanded CI coverage and updated build dependencies to newer versions.
    • Added release, lint, audit, and changelog-related project files for the new ports.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 66196514-9bd6-45c8-80de-946c4be61225

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch gtk4-webkit6-integration

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

@OlympusLedgerOrg
OlympusLedgerOrg force-pushed the gtk4-webkit6-integration branch 3 times, most recently from bc1795b to 0f451e7 Compare July 7, 2026 20:59
@OlympusLedgerOrg
OlympusLedgerOrg force-pushed the gtk4-webkit6-integration branch from 0f451e7 to 9dfa122 Compare July 7, 2026 21:03
@OlympusLedgerOrg
OlympusLedgerOrg force-pushed the gtk4-webkit6-tao-wry branch 2 times, most recently from 13357dc to ec59c7c Compare July 7, 2026 21:05
@OlympusLedgerOrg
OlympusLedgerOrg force-pushed the gtk4-webkit6-integration branch 2 times, most recently from e63ee02 to 778bdca Compare July 7, 2026 21:28
@OlympusLedgerOrg
OlympusLedgerOrg force-pushed the gtk4-webkit6-integration branch from 0496dcf to 81c038e Compare July 7, 2026 21:53

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 18

🧹 Nitpick comments (16)
ports/muda/.github/workflows/covector-version-or-publish.yml (1)

26-26: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

actions/checkout@v6 inconsistent with sibling workflows.

The other muda workflows (audit.yml, clippy-fmt.yml, test.yml) pin actions/checkout@v4, while this one uses @v6. v6 is a valid current major version, but the inconsistency is worth aligning for maintainability.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ports/muda/.github/workflows/covector-version-or-publish.yml` at line 26, The
workflow uses a different actions/checkout major version than the other muda
workflows, so align this file with the sibling workflow pins for consistency.
Update the checkout step in the covector-version-or-publish workflow to match
the version used in audit.yml, clippy-fmt.yml, and test.yml, keeping the change
localized to the actions/checkout reference.
ports/muda/renovate.json (1)

1-4: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Deprecated Renovate preset config:base.

Renovate's config validator flags config:base as requiring migration to config:recommended.

♻️ Proposed fix
 {
   "$schema": "https://docs.renovatebot.com/renovate-schema.json",
-  "extends": ["config:base", ":disableDependencyDashboard"]
+  "extends": ["config:recommended", ":disableDependencyDashboard"]
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ports/muda/renovate.json` around lines 1 - 4, The Renovate config in the root
JSON still extends the deprecated preset config:base, so update the extends list
to use config:recommended instead while keeping the existing
:disableDependencyDashboard preset. Make this change in the renovate.json config
object so the Renovate validator no longer flags the deprecated preset.
ports/muda/src/menu.rs (1)

391-408: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

gtk_menubar_for_gtk_window takes self by value, unlike every other method here.

All other Menu methods (init_for_gtk_window, remove_for_gtk_window, show_for_gtk_window, etc.) take &self; this one consumes self. Since Menu is cheap to clone (Rc-backed), this isn't unsafe, but it's an inconsistent public API surface that forces unnecessary clones at call sites.

Suggested fix
-    pub fn gtk_menubar_for_gtk_window<W>(self, window: &W) -> Option<gtk::PopoverMenuBar>
+    pub fn gtk_menubar_for_gtk_window<W>(&self, window: &W) -> Option<gtk::PopoverMenuBar>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ports/muda/src/menu.rs` around lines 391 - 408, The public Menu API is
inconsistent because gtk_menubar_for_gtk_window takes self by value while the
other methods on Menu (such as init_for_gtk_window, remove_for_gtk_window, and
show_for_gtk_window) take &self. Update gtk_menubar_for_gtk_window to borrow
self instead of consuming it, and keep the delegation to
self.inner.borrow().gtk_menubar_for_gtk_window(window) unchanged so call sites
do not need unnecessary clones.
ports/muda/src/platform_impl/gtk/icon.rs (1)

16-23: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Redundant/risky manual unsafe impl Send/Sync.

PlatformIcon only holds a Vec<u8>, which is already automatically Send + Sync; the struct would already be Send + Sync without any manual impl. Providing an explicit unsafe impl here is unneeded today, but it also removes the compiler's automatic re-check: if a future field (e.g. a cached gtk::gio::BytesIcon, Rc, or raw pointer) is added, this manual impl will silently keep the type Send/Sync even though it would then be unsound, instead of the compiler catching it.

♻️ Drop the redundant unsafe impls
-// PlatformIcon is Send + Sync because it only contains Vec<u8>
-// The BytesIcon is created lazily on the GTK main thread when needed
-unsafe impl Send for PlatformIcon {}
-unsafe impl Sync for PlatformIcon {}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ports/muda/src/platform_impl/gtk/icon.rs` around lines 16 - 23, Remove the
manual unsafe Send/Sync implementations from PlatformIcon in icon.rs, since the
struct currently only contains Vec<u8> and is already auto-derived as
thread-safe. Keep PlatformIcon relying on the compiler’s automatic Send/Sync
behavior so any future non-thread-safe field additions are caught by the type
system instead of being masked by explicit unsafe impls.
ports/muda/src/platform_impl/gtk/about_dialog.rs (1)

34-37: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Orphaned doc comment for a removed method.

This doc comment describes a from_compat-style constructor ("Creates a new AboutDialog from compat metadata...") but no such function follows it — it dangles directly before the show() doc comment. Looks like leftover from a refactor; remove it or restore the intended constructor.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ports/muda/src/platform_impl/gtk/about_dialog.rs` around lines 34 - 37,
Remove the orphaned `from_compat` constructor doc comment in `about_dialog.rs`
because it no longer precedes a matching method and now dangles before `show()`.
If the constructor was intentionally removed, delete the stale comment block; if
it should still exist, reintroduce the `from_compat`-style method in
`AboutDialog` and place the docs directly above it so the documentation matches
the actual API.
ports/tray-icon/.github/workflows/audit.yml (1)

35-37: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider rustsec/audit-check@v2.

The main repo's audit.yml uses rustsec/audit-check@v2, while this vendored copy pins @v1. Since this workflow lives under ports/tray-icon/.github/workflows/, GitHub Actions won't pick it up as part of the monorepo's own CI (only top-level .github/workflows/ is scanned), so this is low-impact unless the crate is later extracted to its own repo.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ports/tray-icon/.github/workflows/audit.yml` around lines 35 - 37, Update the
audit workflow to use the newer rustsec/audit-check action version by changing
the step in the audit.yml workflow from the current pinned major to the v2
release. Use the existing rustsec/audit-check@... entry in the workflow as the
target, and keep the token wiring unchanged.
ports/tray-icon/README.md (1)

25-25: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Fix heading level increments (h2 → h4 skips h3).

markdownlint flags these headings as skipping a level; same pattern repeats at line 59.

Also applies to: 47-47

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ports/tray-icon/README.md` at line 25, The README headings are skipping a
level, with the Arch Linux / Manjaro section using a h4 where the surrounding
structure expects a smoother increment. Update the affected markdown headings in
the tray-icon README so the hierarchy advances one level at a time, and apply
the same fix to the repeated heading near the other referenced section. Use the
nearby section titles in this README to keep the heading levels consistent.

Source: Linters/SAST tools

crates/tauri/src/window/mod.rs (1)

749-765: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Doc link still points to GTK3 despite gtk4-only signature.

The doc comment above transient_for_raw (and transient_for at line 734) still references https://docs.gtk.org/gtk3/method.Window.set_transient_for.html, but the parameter type is now gtk::glib::object::IsA<gtk::Window> (gtk4). crates/tauri-runtime/src/window.rs in this same PR already updated its equivalent doc link to the gtk4 reference page — worth doing the same here for consistency.

📝 Proposed doc fix
   /// Sets the window to be created transient for parent.
   ///
-  /// See <https://docs.gtk.org/gtk3/method.Window.set_transient_for.html>
+  /// See <https://docs.gtk.org/gtk4/method.Window.set_transient_for.html>
   ///
   /// **Note:** This is a low level API. See [`Self::parent`] and [`Self::transient_for`] for higher level wrappers for Tauri windows.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tauri/src/window/mod.rs` around lines 749 - 765, The doc comment for
transient_for_raw (and the matching transient_for wrapper) still links to the
GTK3 set_transient_for page even though the API now uses gtk4 types; update the
referenced documentation URL to the GTK4 version so it matches the current
signature and stays consistent with the equivalent window docs in this PR.
crates/tauri-runtime-wry/src/undecorated_resizing.rs (1)

509-522: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Undocumented magic sentinel 8 for the "unknown/no-edge" case.

SurfaceEdge::__Unknown(8) is a hardcoded value purely to route Client/NoWhere into the "ignore" branch below. A brief comment (or a named constant) explaining why 8 was picked would help future maintainers avoid confusing it with a real edge value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tauri-runtime-wry/src/undecorated_resizing.rs` around lines 509 - 522,
The to_gtk_edge match in HitTestResult uses a hardcoded
SurfaceEdge::__Unknown(8) for Client and NoWhere, which is a confusing sentinel.
Add a brief explanatory comment or replace the raw value with a named constant
near to_gtk_edge/HitTestResult so it’s clear this is intentionally used to fall
into the ignore path and not a real edge.
crates/tauri-runtime-wry/src/lib.rs (1)

3339-3341: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Two different upcast idioms used for the same conversion.

Line 3340 upcasts the gtk window via .clone().into(), while line 4577 uses .upcast_ref() (relying on the newly-imported Cast trait) for essentially the same "concrete window → gtk::Window" conversion. Both should work given gtk4-rs's generated ancestor conversions, but standardizing on one idiom (e.g., .upcast()/.upcast_ref() via Cast) would be more consistent and self-documenting.

Also applies to: 4577-4577

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tauri-runtime-wry/src/lib.rs` around lines 3339 - 3341, The same
concrete-to-gtk::Window conversion is written in two different styles in the
WindowMessage handling and the later GTK window path, so standardize both call
sites to one Cast-based idiom. Update the `WindowMessage::GtkWindow` send path
and the other `gtk_window` conversion site to use the same `upcast`/`upcast_ref`
approach from the `Cast` trait so the code is consistent and self-documenting.
.github/workflows/bench.yml (1)

53-59: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Apt-source cleanup snippet duplicated across every workflow.

The sudo find /etc/apt/sources.list.d ... packages.microsoft.com ... rm -f one-liner is repeated verbatim in this file and in check-generated-files.yml, covector-version-or-publish.yml (x2), docker.yml (x2), test-core.yml, and udeps.yml (x2). Extracting it into a reusable composite action (e.g. .github/actions/setup-linux-deps) would remove this duplication and make future changes (e.g. adjusting the MS-source workaround) a single-point edit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/bench.yml around lines 53 - 59, The apt-source cleanup
logic is duplicated across multiple workflows, including the install step in
bench.yml, so refactor it into a reusable composite action (such as a
setup-linux-deps action under .github/actions) and replace the repeated shell
snippet with that shared action. Keep the existing behavior for the GNU/Ubuntu
dependency setup in the workflow step, but centralize the packages.microsoft.com
source removal so future changes only need to be made in one place.
.github/workflows/test-android.yml (1)

70-75: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Bind ndk-path output via env instead of direct expression interpolation.

zizmor flags line 75 for potential template-injection: ${{ steps.setup-ndk.outputs.ndk-path }} is interpolated directly into the shell script. While this output isn't attacker-influenced in practice, the safer GH Actions pattern is to pass it through env to avoid any shell-expansion/quoting hazards.

🔒 Proposed fix
       - name: Restore Android Symlinks
         if: matrix.platform == 'ubuntu-latest' || matrix.platform == 'macos-latest'
+        env:
+          NDK_PATH: ${{ steps.setup-ndk.outputs.ndk-path }}
         run: |
           case "$RUNNER_OS" in
             Linux) prebuilt="linux-x86_64" ;;
             macOS) prebuilt="darwin-x86_64" ;;
           esac

-          directory="${{ steps.setup-ndk.outputs.ndk-path }}/toolchains/llvm/prebuilt/${prebuilt}/bin"
+          directory="$NDK_PATH/toolchains/llvm/prebuilt/${prebuilt}/bin"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/test-android.yml around lines 70 - 75, The shell snippet
in the Android test workflow interpolates the setup-ndk output directly into the
script, which should be moved to an environment variable instead. Update the
step that computes directory to read the ndk path from env, binding
steps.setup-ndk.outputs.ndk-path through env first and then using that variable
in the script, so the logic in the case/prebuilt block stays the same while
avoiding direct expression interpolation.

Source: Linters/SAST tools

ports/muda/examples/gtk.rs (1)

76-85: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer upcast_ref() over dynamic_cast_ref().unwrap().

ApplicationWindow statically implements IsA<Window>, so the cast can never fail. Using dynamic_cast_ref (a runtime type check) plus .unwrap() introduces an unnecessary panic path for a cast that's known-safe at compile time; upcast_ref() is the idiomatic glib API for this.

♻️ Suggested fix
-        file_menu.show_context_menu_for_gtk_window(w.dynamic_cast_ref().unwrap(), None);
+        file_menu.show_context_menu_for_gtk_window(w.upcast_ref(), None);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ports/muda/examples/gtk.rs` around lines 76 - 85, The gtk example is using a
runtime cast with a panic path where a static upcast is sufficient. In the
btn.connect_clicked closure in gtk.rs, replace the use of window.clone()
combined with w.dynamic_cast_ref().unwrap() with the idiomatic upcast_ref() on
the ApplicationWindow value before passing it to
file_menu.show_context_menu_for_gtk_window. This keeps the code aligned with the
IsA<Window> relationship and removes the unnecessary unwrap.
ports/tray-icon/src/platform_impl/windows/mod.rs (1)

96-172: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Boxed TrayUserData leaks if CreateWindowExW fails.

Box::into_raw(Box::new(traydata)) (Line 146) is passed as lpCreateParams. If CreateWindowExW returns null (Line 148), WM_NCCREATE/WM_DESTROY are never processed for this window, so the box is never reclaimed via Box::from_raw — a leak on this (rare) error path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ports/tray-icon/src/platform_impl/windows/mod.rs` around lines 96 - 172, The
TrayUserData allocation in TrayIcon::new is leaked when CreateWindowExW fails
because Box::into_raw is used before confirming window creation and the cleanup
path in tray_proc never runs. Fix this by keeping ownership of the boxed
TrayUserData until CreateWindowExW succeeds, then only transfer ownership to
lpCreateParams on success or explicitly reconstruct and drop the box immediately
when hwnd is null; use the TrayIcon::new and tray_proc flow to place the cleanup
where the failure is detected.
ports/muda/src/builders/check.rs (1)

86-97: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

build() silently discards accelerator-setting errors.

let _ = item.set_key_accelerator(...) drops any error from the platform implementation (e.g. GTK accelerator conversion failures), so callers get a built item with a silently-unset accelerator and no signal anything went wrong. This is inconsistent with SubmenuBuilder::build() in the same module, which returns crate::Result<Submenu> and propagates errors.

Consider changing build() to return crate::Result<CheckMenuItem> (mirroring SubmenuBuilder), or at minimum surface/log the discarded error.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ports/muda/src/builders/check.rs` around lines 86 - 97,
CheckMenuItemBuilder::build() currently ignores the result of
set_key_accelerator, so accelerator conversion/installation failures are hidden.
Update build() to propagate that error instead of discarding it, ideally by
changing its return type to crate::Result<CheckMenuItem> like
SubmenuBuilder::build(), and ensure the CheckMenuItem construction plus
key_accelerator setup both flow through the same error path.
ports/muda/src/items/submenu.rs (1)

147-157: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

remove_at reports success even if the underlying remove fails.

The Result from self.remove(item.as_ref()) is discarded, yet Some(item) is returned unconditionally — callers will believe the item was removed even when it wasn't, creating a potential desync between caller-tracked state and the actual submenu contents.

Proposed fix
     pub fn remove_at(&self, position: usize) -> Option<MenuItemKind> {
         let mut items = self.items();
         if items.len() > position {
             let item = items.remove(position);
-            let _ = self.remove(item.as_ref());
-            Some(item)
+            self.remove(item.as_ref()).ok().map(|_| item)
         } else {
             None
         }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ports/muda/src/items/submenu.rs` around lines 147 - 157, The submenu removal
logic in remove_at ignores the result of self.remove(item.as_ref()) and always
returns Some(item), so callers can see a successful removal even when the
underlying removal fails. Update remove_at to propagate or check the result from
self.remove before returning success, and only return Some(item) when the item
was actually removed; otherwise return None or an error-consistent outcome that
matches the behavior of remove and MenuItemKind.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/udeps.yml:
- Around line 150-154: The Linux udeps job is missing the libxdo-dev system
dependency, which can break builds when cargo udeps enables Tauri’s linux-libxdo
path. Update the “Install required packages” step in the udeps workflow to
include libxdo-dev, matching the Linux test setup, so tray-icon/libxdo and
muda/libxdo can compile successfully.

In `@crates/tauri-runtime-wry/src/monitor/linux.rs`:
- Around line 5-11: Document the Linux fallback behavior in the
`MonitorExt::work_area` implementation for `tao::monitor::MonitorHandle` and in
any related `prevent_overflow` docs, since `gdk_monitor().geometry()` returns
the full monitor rect rather than a taskbar/panel-aware work area. Add a clear
note that this Linux path intentionally falls back to full-geometry placement
and does not account for panels/taskbars, so callers understand the limitation
when using `work_area()` and `prevent_overflow`.

In `@ports/muda/src/icon.rs`:
- Around line 89-93: The `Error::source()` implementation for `BadIcon` is
self-referential and causes infinite error-chain recursion. Update
`BadIcon::source` to return no underlying source unless there is a distinct
wrapped error to expose, and keep the `impl Error for BadIcon` behavior
terminating correctly for chain-walking consumers like `sources()` and logging
printers.

In `@ports/muda/src/platform_impl/gtk/about_dialog.rs`:
- Around line 42-101: The GTK About dialog logic in show() is diverging from the
inline handler in gtk/mod.rs, causing inconsistent handling of version fields.
Update show() in about_dialog.rs to reuse metadata.full_version() the same way
PredefinedMenuItemType::About does, instead of manually checking version and
short_version and dropping one of them. Keep the other metadata fields wired the
same, and make the GTK tray/about path match the menu handler’s AboutDialog
behavior.

In `@ports/muda/src/platform_impl/gtk/mod.rs`:
- Around line 781-831: The submenu context menu is only built once in
show_context_menu_for_gtk_window, so it can show stale labels, icons, and
checked state on later opens. Update MenuChild::show_context_menu_for_gtk_window
to follow the same rebuild behavior as Menu::show_context_menu_for_gtk_window by
recreating the context-menu instance each time before popup, rather than reusing
the first entry from self.instances. Ensure the logic around self.ctx_menu_id,
self.instances, and add_menu_item_with_id still repopulates the menu from
current items on every call.
- Around line 167-187: The custom GTK menu row binding for icon items is only
created during initial setup, so later mutations leave icon items out of sync.
Update Menu::add_menu_item and MenuChild::add_menu_item to also add or rebind
the custom child for newly inserted icon items on existing instances, and update
set_text and set_icon to preserve/reapply the "custom" attribute when recreating
the gio::MenuItem and refresh the bound custom_menu_row. Use the existing
bind_custom_children and add_custom_child paths so Menu, MenuChild, and the
icon-item mutation methods stay consistent.
- Around line 296-309: The menu bar attachment logic in the GTK platform
implementation can silently do nothing when `container` is present but not a
`GtkBox`, `GtkFixed`, or `GtkStack`. Update the container handling in the menu
setup path to either attach the menu bar through a safe fallback or
return/surface an error for unsupported container types, using the existing
`container` type check and `window.set_child` branch as the main entry points.
- Around line 404-449: The context-menu rebuild in
show_context_menu_for_gtk_window is dropping the old GtkMenuBar::ContextMenu
without first detaching its previously parented PopoverMenu from the window.
Before calling self.instances.remove(&self.ctx_menu_id), fetch the existing
entry, get its context_menu(), and call unparent on it if it has a parent so the
old widget is fully detached before being discarded. Keep the later
context_menu.parent()/unparent() logic for the newly created menu if needed, but
ensure the cleanup happens on the prior instance, not the fresh one.

In `@ports/muda/src/platform_impl/macos/util.rs`:
- Around line 14-20: The strip_mnemonic function currently uses a literal
placeholder that can collide with real input text, so update strip_mnemonic in
util.rs to avoid multi-step string replacement. Replace the placeholder-based
approach with a single-pass scan over the input characters that preserves
escaped ampersands and removes mnemonic markers without ever introducing an
intermediate token, so existing text cannot be accidentally rewritten.

In `@ports/muda/src/platform_impl/windows/dark_menu_bar.rs`:
- Around line 334-350: Set HIGHCONTRASTA.cbSize before calling
SystemParametersInfoA with SPI_GETHIGHCONTRAST in is_high_contrast(). The
current zero-initialized cbSize causes the query to fail, so initialize cbSize
to the size of HIGHCONTRASTA before the unsafe call and keep the dwFlags check
as-is so dark_menu_bar logic respects high-contrast mode correctly.

In `@ports/tray-icon/LICENSE.spdx`:
- Around line 4-8: The SPDX tag:value metadata is malformed in the LICENSE.spdx
document: replace the invalid DataFormat field with the correct SPDX document
metadata and ensure PackageLicenseDeclared appears only once as a single SPDX
expression. Update the existing SPDX entries in this file so the package fields
use valid tag names and combine the dual license declaration into one expression
like Apache-2.0 OR MIT, keeping the rest of the package metadata consistent.

In `@ports/tray-icon/README.md`:
- Line 18: Fix the spelling mistakes in the tray-icon README entries: update the
`libxdo` description to say “predefined” instead of “predfined”, and correct the
`libayatna` reference to “libayatana” in the affected README lines. Make sure
both feature descriptions in the same section use the proper library names and
wording consistently.

In `@ports/tray-icon/src/lib.rs`:
- Around line 13-22: The rustdoc platform list in lib.rs is incomplete because
it only names Windows, macOS, and Linux, while the crate also supports BSD
variants through the gtk backend. Update the documentation under the tray icon
module to include the BSD targets referenced by platform_impl/mod.rs and
reflected in README.md, keeping the platform-specific notes accurate for
gtk-only support on FreeBSD/DragonFly/NetBSD/OpenBSD.
- Line 26: Fix the spelling mistakes in the tray icon documentation comments and
any matching README text by correcting the typo in the `lib.rs` module docs (the
`Copy`/`Cut`/`Paste`/`SelectAll` sentence with `predfined` and `libayatnat`) and
the later doc line containing `emmited`; update the relevant doc comment strings
so they read correctly while keeping the existing meaning, and make sure the
same corrections are applied consistently wherever the duplicated text appears.
- Around line 227-233: TrayIconBuilder currently derives Default, which
initializes id through TrayIconId::default() instead of the unique id used by
TrayIconBuilder::new(). Replace the derived default behavior with a manual
Default implementation (or equivalent shared initializer) so
TrayIconBuilder::default() also assigns TrayIconId::new_unique() and matches the
documented builder behavior. Keep the fix localized to TrayIconBuilder and its
constructor/default path so both entry points produce consistent, unique ids.

In `@ports/tray-icon/src/platform_impl/linux/mod.rs`:
- Around line 42-62: The tray update loop in the Linux `TrayIcon` implementation
is using the shared `muda::recv_menu_update()` receiver directly, so each wakeup
can be consumed by the wrong tray thread and leave others stale. Update the
`TrayIcon::new` spawn path to avoid per-tray blocking on the shared
`MenuEvent::receiver()` and instead route updates through a per-tray channel or
a single dispatcher that broadcasts to all `tray_handle` instances, keeping the
`shutdown` handling intact.

In `@ports/tray-icon/src/platform_impl/windows/mod.rs`:
- Around line 226-257: The tooltip handling in set_tooltip currently copies up
to 128 UTF-16 units into NOTIFYICONDATAW::szTip without guaranteeing a trailing
null when the text is at the limit. Update the copy logic to reserve space for
the terminator and ensure szTip is always null-terminated, and apply the same
fix in register_tray_icon where the tray icon tooltip is initialized.

---

Nitpick comments:
In @.github/workflows/bench.yml:
- Around line 53-59: The apt-source cleanup logic is duplicated across multiple
workflows, including the install step in bench.yml, so refactor it into a
reusable composite action (such as a setup-linux-deps action under
.github/actions) and replace the repeated shell snippet with that shared action.
Keep the existing behavior for the GNU/Ubuntu dependency setup in the workflow
step, but centralize the packages.microsoft.com source removal so future changes
only need to be made in one place.

In @.github/workflows/test-android.yml:
- Around line 70-75: The shell snippet in the Android test workflow interpolates
the setup-ndk output directly into the script, which should be moved to an
environment variable instead. Update the step that computes directory to read
the ndk path from env, binding steps.setup-ndk.outputs.ndk-path through env
first and then using that variable in the script, so the logic in the
case/prebuilt block stays the same while avoiding direct expression
interpolation.

In `@crates/tauri-runtime-wry/src/lib.rs`:
- Around line 3339-3341: The same concrete-to-gtk::Window conversion is written
in two different styles in the WindowMessage handling and the later GTK window
path, so standardize both call sites to one Cast-based idiom. Update the
`WindowMessage::GtkWindow` send path and the other `gtk_window` conversion site
to use the same `upcast`/`upcast_ref` approach from the `Cast` trait so the code
is consistent and self-documenting.

In `@crates/tauri-runtime-wry/src/undecorated_resizing.rs`:
- Around line 509-522: The to_gtk_edge match in HitTestResult uses a hardcoded
SurfaceEdge::__Unknown(8) for Client and NoWhere, which is a confusing sentinel.
Add a brief explanatory comment or replace the raw value with a named constant
near to_gtk_edge/HitTestResult so it’s clear this is intentionally used to fall
into the ignore path and not a real edge.

In `@crates/tauri/src/window/mod.rs`:
- Around line 749-765: The doc comment for transient_for_raw (and the matching
transient_for wrapper) still links to the GTK3 set_transient_for page even
though the API now uses gtk4 types; update the referenced documentation URL to
the GTK4 version so it matches the current signature and stays consistent with
the equivalent window docs in this PR.

In `@ports/muda/.github/workflows/covector-version-or-publish.yml`:
- Line 26: The workflow uses a different actions/checkout major version than the
other muda workflows, so align this file with the sibling workflow pins for
consistency. Update the checkout step in the covector-version-or-publish
workflow to match the version used in audit.yml, clippy-fmt.yml, and test.yml,
keeping the change localized to the actions/checkout reference.

In `@ports/muda/examples/gtk.rs`:
- Around line 76-85: The gtk example is using a runtime cast with a panic path
where a static upcast is sufficient. In the btn.connect_clicked closure in
gtk.rs, replace the use of window.clone() combined with
w.dynamic_cast_ref().unwrap() with the idiomatic upcast_ref() on the
ApplicationWindow value before passing it to
file_menu.show_context_menu_for_gtk_window. This keeps the code aligned with the
IsA<Window> relationship and removes the unnecessary unwrap.

In `@ports/muda/renovate.json`:
- Around line 1-4: The Renovate config in the root JSON still extends the
deprecated preset config:base, so update the extends list to use
config:recommended instead while keeping the existing
:disableDependencyDashboard preset. Make this change in the renovate.json config
object so the Renovate validator no longer flags the deprecated preset.

In `@ports/muda/src/builders/check.rs`:
- Around line 86-97: CheckMenuItemBuilder::build() currently ignores the result
of set_key_accelerator, so accelerator conversion/installation failures are
hidden. Update build() to propagate that error instead of discarding it, ideally
by changing its return type to crate::Result<CheckMenuItem> like
SubmenuBuilder::build(), and ensure the CheckMenuItem construction plus
key_accelerator setup both flow through the same error path.

In `@ports/muda/src/items/submenu.rs`:
- Around line 147-157: The submenu removal logic in remove_at ignores the result
of self.remove(item.as_ref()) and always returns Some(item), so callers can see
a successful removal even when the underlying removal fails. Update remove_at to
propagate or check the result from self.remove before returning success, and
only return Some(item) when the item was actually removed; otherwise return None
or an error-consistent outcome that matches the behavior of remove and
MenuItemKind.

In `@ports/muda/src/menu.rs`:
- Around line 391-408: The public Menu API is inconsistent because
gtk_menubar_for_gtk_window takes self by value while the other methods on Menu
(such as init_for_gtk_window, remove_for_gtk_window, and show_for_gtk_window)
take &self. Update gtk_menubar_for_gtk_window to borrow self instead of
consuming it, and keep the delegation to
self.inner.borrow().gtk_menubar_for_gtk_window(window) unchanged so call sites
do not need unnecessary clones.

In `@ports/muda/src/platform_impl/gtk/about_dialog.rs`:
- Around line 34-37: Remove the orphaned `from_compat` constructor doc comment
in `about_dialog.rs` because it no longer precedes a matching method and now
dangles before `show()`. If the constructor was intentionally removed, delete
the stale comment block; if it should still exist, reintroduce the
`from_compat`-style method in `AboutDialog` and place the docs directly above it
so the documentation matches the actual API.

In `@ports/muda/src/platform_impl/gtk/icon.rs`:
- Around line 16-23: Remove the manual unsafe Send/Sync implementations from
PlatformIcon in icon.rs, since the struct currently only contains Vec<u8> and is
already auto-derived as thread-safe. Keep PlatformIcon relying on the compiler’s
automatic Send/Sync behavior so any future non-thread-safe field additions are
caught by the type system instead of being masked by explicit unsafe impls.

In `@ports/tray-icon/.github/workflows/audit.yml`:
- Around line 35-37: Update the audit workflow to use the newer
rustsec/audit-check action version by changing the step in the audit.yml
workflow from the current pinned major to the v2 release. Use the existing
rustsec/audit-check@... entry in the workflow as the target, and keep the token
wiring unchanged.

In `@ports/tray-icon/README.md`:
- Line 25: The README headings are skipping a level, with the Arch Linux /
Manjaro section using a h4 where the surrounding structure expects a smoother
increment. Update the affected markdown headings in the tray-icon README so the
hierarchy advances one level at a time, and apply the same fix to the repeated
heading near the other referenced section. Use the nearby section titles in this
README to keep the heading levels consistent.

In `@ports/tray-icon/src/platform_impl/windows/mod.rs`:
- Around line 96-172: The TrayUserData allocation in TrayIcon::new is leaked
when CreateWindowExW fails because Box::into_raw is used before confirming
window creation and the cleanup path in tray_proc never runs. Fix this by
keeping ownership of the boxed TrayUserData until CreateWindowExW succeeds, then
only transfer ownership to lpCreateParams on success or explicitly reconstruct
and drop the box immediately when hwnd is null; use the TrayIcon::new and
tray_proc flow to place the cleanup where the failure is detected.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a3e194f8-8a5b-449a-ace1-f507d8b1fbe7

📥 Commits

Reviewing files that changed from the base of the PR and between 29b8eca and fbe1c70.

⛔ Files ignored due to path filters (5)
  • Cargo.lock is excluded by !**/*.lock
  • ports/muda/Cargo.lock is excluded by !**/*.lock
  • ports/muda/examples/icon.png is excluded by !**/*.png
  • ports/tray-icon/Cargo.lock is excluded by !**/*.lock
  • ports/tray-icon/examples/icon.png is excluded by !**/*.png
📒 Files selected for processing (141)
  • .github/workflows/bench.yml
  • .github/workflows/check-generated-files.yml
  • .github/workflows/covector-version-or-publish.yml
  • .github/workflows/docker.yml
  • .github/workflows/lint-rust.yml
  • .github/workflows/publish-cli-js.yml
  • .github/workflows/publish-cli-rs.yml
  • .github/workflows/test-android.yml
  • .github/workflows/test-cli-js.yml
  • .github/workflows/test-cli-rs.yml
  • .github/workflows/test-core.yml
  • .github/workflows/udeps.yml
  • Cargo.toml
  • crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.sh
  • crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy.rs
  • crates/tauri-cli/src/info/env_system.rs
  • crates/tauri-cli/src/init.rs
  • crates/tauri-cli/src/interface/rust.rs
  • crates/tauri-cli/src/interface/rust/manifest.rs
  • crates/tauri-cli/templates/app/src-tauri/Cargo.crate-manifest
  • crates/tauri-runtime-wry/Cargo.toml
  • crates/tauri-runtime-wry/src/lib.rs
  • crates/tauri-runtime-wry/src/monitor/linux.rs
  • crates/tauri-runtime-wry/src/undecorated_resizing.rs
  • crates/tauri-runtime-wry/src/webview.rs
  • crates/tauri-runtime/Cargo.toml
  • crates/tauri-runtime/src/webview.rs
  • crates/tauri-runtime/src/window.rs
  • crates/tauri/Cargo.toml
  • crates/tauri/src/app.rs
  • crates/tauri/src/ipc/channel.rs
  • crates/tauri/src/plugin.rs
  • crates/tauri/src/test/mock_runtime.rs
  • crates/tauri/src/webview/mod.rs
  • crates/tauri/src/webview/webview_window.rs
  • crates/tauri/src/window/mod.rs
  • ports/muda/.changes/config.json
  • ports/muda/.changes/gtk4.md
  • ports/muda/.changes/readme.md
  • ports/muda/.github/workflows/audit.yml
  • ports/muda/.github/workflows/clippy-fmt.yml
  • ports/muda/.github/workflows/covector-status.yml
  • ports/muda/.github/workflows/covector-version-or-publish.yml
  • ports/muda/.github/workflows/test.yml
  • ports/muda/.gitignore
  • ports/muda/CHANGELOG.md
  • ports/muda/Cargo.toml
  • ports/muda/LICENSE-APACHE
  • ports/muda/LICENSE-MIT
  • ports/muda/LICENSE.spdx
  • ports/muda/README.md
  • ports/muda/examples/gtk.rs
  • ports/muda/examples/tao.rs
  • ports/muda/examples/windows-common-controls-v6/.gitignore
  • ports/muda/examples/windows-common-controls-v6/Cargo.toml
  • ports/muda/examples/windows-common-controls-v6/app.exe.manifest
  • ports/muda/examples/windows-common-controls-v6/build.rs
  • ports/muda/examples/windows-common-controls-v6/manifest.rc
  • ports/muda/examples/windows-common-controls-v6/src/main.rs
  • ports/muda/examples/winit.rs
  • ports/muda/examples/wry.rs
  • ports/muda/renovate.json
  • ports/muda/rustfmt.toml
  • ports/muda/src/about_metadata.rs
  • ports/muda/src/accelerator.rs
  • ports/muda/src/builders/check.rs
  • ports/muda/src/builders/icon.rs
  • ports/muda/src/builders/mod.rs
  • ports/muda/src/builders/normal.rs
  • ports/muda/src/builders/submenu.rs
  • ports/muda/src/error.rs
  • ports/muda/src/icon.rs
  • ports/muda/src/items/check.rs
  • ports/muda/src/items/compat.rs
  • ports/muda/src/items/icon.rs
  • ports/muda/src/items/mod.rs
  • ports/muda/src/items/normal.rs
  • ports/muda/src/items/predefined.rs
  • ports/muda/src/items/submenu.rs
  • ports/muda/src/lib.rs
  • ports/muda/src/menu.rs
  • ports/muda/src/menu_id.rs
  • ports/muda/src/platform_impl/gtk/about_dialog.rs
  • ports/muda/src/platform_impl/gtk/accelerator.rs
  • ports/muda/src/platform_impl/gtk/icon.rs
  • ports/muda/src/platform_impl/gtk/mnemonic.rs
  • ports/muda/src/platform_impl/gtk/mod.rs
  • ports/muda/src/platform_impl/macos/accelerator.rs
  • ports/muda/src/platform_impl/macos/icon.rs
  • ports/muda/src/platform_impl/macos/mod.rs
  • ports/muda/src/platform_impl/macos/util.rs
  • ports/muda/src/platform_impl/mod.rs
  • ports/muda/src/platform_impl/windows/accelerator.rs
  • ports/muda/src/platform_impl/windows/dark_menu_bar.rs
  • ports/muda/src/platform_impl/windows/icon.rs
  • ports/muda/src/platform_impl/windows/mod.rs
  • ports/muda/src/platform_impl/windows/util.rs
  • ports/muda/src/util.rs
  • ports/tray-icon/.changes/config.json
  • ports/tray-icon/.changes/nis-hidden.md
  • ports/tray-icon/.changes/perf-macos-avoid-allocating.md
  • ports/tray-icon/.changes/readme.md
  • ports/tray-icon/.changes/set-cb-size.md
  • ports/tray-icon/.github/workflows/audit.yml
  • ports/tray-icon/.github/workflows/clippy-fmt.yml
  • ports/tray-icon/.github/workflows/covector-status.yml
  • ports/tray-icon/.github/workflows/covector-version-or-publish.yml
  • ports/tray-icon/.github/workflows/test.yml
  • ports/tray-icon/.gitignore
  • ports/tray-icon/CHANGELOG.md
  • ports/tray-icon/Cargo.toml
  • ports/tray-icon/LICENSE-APACHE
  • ports/tray-icon/LICENSE-MIT
  • ports/tray-icon/LICENSE.spdx
  • ports/tray-icon/README.md
  • ports/tray-icon/examples/egui.rs
  • ports/tray-icon/examples/tao.rs
  • ports/tray-icon/examples/winit.rs
  • ports/tray-icon/renovate.json
  • ports/tray-icon/rustfmt.toml
  • ports/tray-icon/src/counter.rs
  • ports/tray-icon/src/error.rs
  • ports/tray-icon/src/icon.rs
  • ports/tray-icon/src/lib.rs
  • ports/tray-icon/src/platform_impl/gtk/icon.rs
  • ports/tray-icon/src/platform_impl/gtk/mod.rs
  • ports/tray-icon/src/platform_impl/linux/icon.rs
  • ports/tray-icon/src/platform_impl/linux/menu.rs
  • ports/tray-icon/src/platform_impl/linux/mod.rs
  • ports/tray-icon/src/platform_impl/linux/tray.rs
  • ports/tray-icon/src/platform_impl/macos/icon.rs
  • ports/tray-icon/src/platform_impl/macos/mod.rs
  • ports/tray-icon/src/platform_impl/mod.rs
  • ports/tray-icon/src/platform_impl/windows/icon.rs
  • ports/tray-icon/src/platform_impl/windows/mod.rs
  • ports/tray-icon/src/platform_impl/windows/util.rs
  • ports/tray-icon/src/tray_icon_id.rs
  • ports/wry/Cargo.toml
  • ports/wry/src/android/main_pipe.rs
  • ports/wry/src/android/mod.rs
  • ports/wry/src/lib.rs
💤 Files with no reviewable changes (1)
  • ports/wry/src/android/main_pipe.rs

Comment thread .github/workflows/udeps.yml Outdated
Comment on lines +150 to +154
- name: Install required packages
run: |
sudo find /etc/apt/sources.list.d -maxdepth 1 -type f -exec grep -l "packages.microsoft.com" {} \; | xargs -r sudo rm -f
sudo apt-get update
sudo apt-get install -y webkit2gtk-4.1 libayatana-appindicator3-dev
sudo apt-get install -y libgtk-4-dev libwebkitgtk-6.0-dev libdbus-1-dev

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm libxdo feature is reachable via --all-features on any udeps matrix path
rg -n 'libxdo' -g '*.toml' ports/muda ports/tray-icon crates/tauri crates/tauri-runtime-wry
rg -n '\[features\]' -A 20 crates/tauri/Cargo.toml

Repository: OlympusLedgerOrg/tauri

Length of output: 2064


🌐 Web query:

"OlympusLedgerOrg/tauri .github/workflows/udeps.yml libxdo-dev test-core.yml muda tray-icon all-features"

💡 Result:

The terms in your query relate to the repository OlympusLedgerOrg/Olympus [1], a Rust-based project that uses Tauri for its desktop application [1][2]. The project's workflow infrastructure and dependency management reflect specific requirements for its Tauri-based desktop architecture [2][3][4]: 1. libxdo-dev: This is a required system dependency for Linux builds to support GUI interactions, specifically for predefined menu items (like Copy, Cut, Paste) in Tauri-based applications that utilize crates like muda (which Tauri uses for menus) [5][6]. It is commonly added to Linux CI workflows to ensure successful builds [7]. 2. udeps.yml: While not explicitly defined as a single file in the Olympus repository, projects often use cargo-udeps in GitHub Actions (e.g.,.github/workflows/udeps.yml) to detect and remove unused dependencies from Cargo.toml files. 3. test-core.yml: This typically refers to a CI workflow file dedicated to testing the core business logic or core crates (such as olympus-crypto) of the repository, often separated from UI or platform-specific tests [3]. 4. muda and tray-icon: These are crates used by Tauri applications to manage application menus (muda) [5][6] and system tray icons (tray-icon) [8]. 5. all-features: This is a common Cargo flag used in CI/CD pipelines (e.g., cargo test --all-features) to ensure that code is validated against all optional features enabled, maintaining test coverage across different configurations [4][9]. The Olympus repository specifically maintains a structured CI environment that splits unit tests, prover (ZK) tests, and platform-specific builds (e.g., macOS, Linux) to manage the complexity of its embedded Axum/PostgreSQL/Tauri stack [2][3][4][10].

Citations:


🌐 Web query:

site:github.com/OlympusLedgerOrg/tauri ".github/workflows/udeps.yml" "libxdo-dev" "test-core.yml" "muda" "tray-icon"

💡 Result:

I couldn’t find a matching result under OlympusLedgerOrg/tauri from that exact query. The closest match is the upstream tauri-apps/tauri repo, which has CI/workflow files like .github/workflows/test-core.yml and .github/workflows/udeps.yml, and the project also references tray-icon, muda, and Linux dependency notes such as libxdo-dev. [1][2][3][4]


🌐 Web query:

site:github.com/tauri-apps/tauri ".github/workflows/udeps.yml" "cargo udeps" "all-targets" "all-features" "libxdo-dev"

💡 Result:

The files and arguments you referenced are components of the continuous integration (CI) pipeline for the Tauri project, specifically used to identify unused dependencies in their Rust codebase [1][2][3][4]. 1..github/workflows/udeps.yml: This is a GitHub Actions workflow file that automates the execution of cargo-udeps to ensure no unnecessary dependencies are included in the project [1][2]. 2. cargo udeps: This is a cargo subcommand used to find unused dependencies in your Cargo.toml file. 3. --all-targets --all-features: These flags are passed to cargo-udeps to ensure it inspects the crate by checking every target (e.g., tests, examples, benchmarks) and every feature combination [1][2]. This is crucial for avoiding false positives where a dependency might appear unused because it is only required for a specific target or feature that was not analyzed by default. 4. libxdo-dev: This is a system-level development package (libxdo-dev, often required for xdotool) that is frequently installed in these CI environments via apt-get [5][6]. It is typically required as a prerequisite because some of the project's dependencies or build tasks (like UI testing or window manipulation) rely on native libraries that need these headers to compile or run successfully on Linux [5]. In summary, the workflow runs cargo-udeps with comprehensive flags to maintain a lean dependency tree, while the apt-get install commands ensure that necessary system dependencies like libxdo-dev are present in the environment before cargo-udeps attempts to analyze the workspace [1][2][5].

Citations:


🌐 Web query:

site:github.com/tauri-apps/tauri/blob/8718d081/.github/workflows/udeps.yml "libgtk-4-dev" "libwebkitgtk-6.0-dev" "libdbus-1-dev"

💡 Result:

The packages libgtk-4-dev, libwebkitgtk-6.0-dev, and libdbus-1-dev are development headers for critical Linux system libraries, frequently required when building cross-platform GUI applications or web-based desktop tools, such as those built with the Tauri framework [1]. libgtk-4-dev contains the development files for the GTK 4 toolkit, which is used for creating graphical user interfaces. libwebkitgtk-6.0-dev provides the development headers for the WebKitGTK engine, version 6.0, which allows applications to render web content and integrate web technologies into desktop interfaces. libdbus-1-dev includes the development files for D-Bus, a message bus system that provides a way for applications on a Linux system to communicate with one another. These packages are typically installed via a package manager like apt on Debian-based systems to satisfy build-time dependencies when compiling software from source [1]. Because these are development headers, they include the headers and libraries needed to link against these systems during the software compilation process.

Citations:


🌐 Web query:

site:github.com/tauri-apps/tauri "muda" "tray-icon" "Cargo.toml" "features" "libxdo"

💡 Result:

In the Tauri ecosystem, the linux-libxdo feature in Cargo.toml is a specific configuration used to enable support for programmatic clipboard and selection actions on Linux [1][2][3]. Specifically, this feature enables linking to libxdo (the library behind xdotool), which is required for features such as Cut, Copy, Paste, and Select All to function correctly in menu items on Linux systems [3][4]. Within the tauri crate's Cargo.toml, this feature is defined to propagate the requirement to its dependencies, muda (the menu library) and tray-icon (the tray icon library) [1][2][5]. The definition typically appears as follows in the features section: linux-libxdo = ["tray-icon/libxdo", "muda/libxdo"] By enabling this feature, you allow both muda and tray-icon to utilize the underlying libxdo functionality where necessary on Linux platforms [1][2][3]. You should enable this feature if your Tauri application relies on these menu-based clipboard actions on a Linux environment [3][4].

Citations:


Add libxdo-dev to the Linux udeps job
cargo udeps --manifest-path ... --all-targets --all-features can enable Tauri’s linux-libxdo path, which pulls in tray-icon/libxdo and muda/libxdo on Linux. Add libxdo-dev here, like test-core.yml, or this job can fail when that feature is built.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/udeps.yml around lines 150 - 154, The Linux udeps job is
missing the libxdo-dev system dependency, which can break builds when cargo
udeps enables Tauri’s linux-libxdo path. Update the “Install required packages”
step in the udeps workflow to include libxdo-dev, matching the Linux test setup,
so tray-icon/libxdo and muda/libxdo can compile successfully.

Comment on lines +5 to +11
use gtk::gdk::prelude::MonitorExt;
use tao::platform::unix::MonitorHandleExtUnix;
use tauri_runtime::dpi::{LogicalPosition, LogicalSize, PhysicalRect};

impl super::MonitorExt for tao::monitor::MonitorHandle {
fn work_area(&self) -> PhysicalRect<i32, u32> {
let rect = self.gdk_monitor().workarea();
let rect = self.gdk_monitor().geometry();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== monitor/linux.rs ==\n'
sed -n '1,120p' crates/tauri-runtime-wry/src/monitor/linux.rs

printf '\n== trait / callers ==\n'
rg -n "work_area\(|prevent_overflow|center\(" crates/tauri-runtime-wry crates/tauri/src -g '!**/target/**'

printf '\n== monitor docs / comments ==\n'
rg -n "taskbar|work area|work_area|monitor size" crates/tauri-runtime-wry crates/tauri -g '!**/target/**'

Repository: OlympusLedgerOrg/tauri

Length of output: 2035


🌐 Web query:

GDK4 Monitor geometry workarea taskbar panels docs.gtk.org gdk4 workarea removed

💡 Result:

In GDK4, the gdk_monitor_get_workarea() API and the associated work-area property were removed [1][2]. This change was made because the concept of a "workarea" (the screen area excluding panels, taskbars, and docks) is not portable across all platforms and display protocols that GTK4 supports [1]. Key points regarding this change: 1. Removal of Portable API: There is no longer a generic, cross-platform function in GDK4 to retrieve the workarea [1][3]. Applications that previously relied on this to position windows or popups must adapt, as the API will simply not be available [4][3]. 2. Backend-Specific Alternatives: For some platforms, individual backends may still provide the necessary information [4][1]. For example, on X11, developers may use gdk_x11_monitor_get_workarea() if they require that specific functionality [4][2]. Developers are advised to check which backend is currently in use before calling such platform-specific APIs [4][3]. 3. Wayland Considerations: In environments like Wayland, the concept of a "workarea" does not exist in the same way it does on traditional desktop environments [5]. Wayland surfaces operate in isolated coordinate spaces, and the compositor generally manages window placement, making the manual calculation of "available screen space" incompatible with the protocol's design [5]. 4. Recommended Approach: Applications should generally rely on the windowing system to handle window placement and maximization rather than attempting to calculate offsets based on panel/taskbar geometry [5]. For more information, consult the official GTK 4 migration guide, which explicitly addresses the removal of gdk_monitor_get_workarea() [4][3].

Citations:


Document the Linux fallback behavior

geometry() is the only generic GDK4 option here, but it returns the full monitor rect, so Linux no longer has taskbar/panel-aware placement. Call out this limitation in the work_area()/prevent_overflow docs if that behavior is intentional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tauri-runtime-wry/src/monitor/linux.rs` around lines 5 - 11, Document
the Linux fallback behavior in the `MonitorExt::work_area` implementation for
`tao::monitor::MonitorHandle` and in any related `prevent_overflow` docs, since
`gdk_monitor().geometry()` returns the full monitor rect rather than a
taskbar/panel-aware work area. Add a clear note that this Linux path
intentionally falls back to full-geometry placement and does not account for
panels/taskbars, so callers understand the limitation when using `work_area()`
and `prevent_overflow`.

Comment thread ports/muda/src/icon.rs
Comment on lines +89 to +93
impl Error for BadIcon {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(self)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Error::source() returns itself, causing infinite recursion.

Some(self) makes BadIcon its own source. Any chain-walking consumer (std::error::Error::sources(), anyhow/eyre chain printers, logging macros) will loop forever instead of terminating.

🐛 Proposed fix
 impl Error for BadIcon {
-    fn source(&self) -> Option<&(dyn Error + 'static)> {
-        Some(self)
-    }
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
+        match self {
+            BadIcon::OsError(e) => Some(e),
+            #[cfg(all(
+                any(
+                    target_os = "linux",
+                    target_os = "dragonfly",
+                    target_os = "freebsd",
+                    target_os = "netbsd",
+                    target_os = "openbsd"
+                ),
+                feature = "gtk"
+            ))]
+            BadIcon::PngEncodingError(e) => Some(e),
+            _ => None,
+        }
+    }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
impl Error for BadIcon {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(self)
}
}
impl Error for BadIcon {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
BadIcon::OsError(e) => Some(e),
#[cfg(all(
any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
),
feature = "gtk"
))]
BadIcon::PngEncodingError(e) => Some(e),
_ => None,
}
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ports/muda/src/icon.rs` around lines 89 - 93, The `Error::source()`
implementation for `BadIcon` is self-referential and causes infinite error-chain
recursion. Update `BadIcon::source` to return no underlying source unless there
is a distinct wrapped error to expose, and keep the `impl Error for BadIcon`
behavior terminating correctly for chain-walking consumers like `sources()` and
logging printers.

Comment on lines +42 to +101
pub fn show(&self) {
let metadata = self.metadata.clone();

gtk4::glib::MainContext::default().invoke(move || {
let dialog = gtk4::AboutDialog::new();

// Set dialog properties from metadata
if let Some(ref name) = metadata.name {
dialog.set_program_name(Some(name));
}

if let Some(ref version) = metadata.version {
dialog.set_version(Some(version));
}

if let Some(ref short_version) = metadata.short_version {
// GTK4 doesn't have a separate short version, but we can include it
// in the version string if both are set
if metadata.version.is_some() {
// Version is already set, we could append short_version but
// GTK4's AboutDialog handles this differently than macOS
let _ = short_version; // Acknowledge unused on GTK4
} else {
dialog.set_version(Some(short_version));
}
}

if let Some(ref copyright) = metadata.copyright {
dialog.set_copyright(Some(copyright));
}

if let Some(ref comments) = metadata.comments {
dialog.set_comments(Some(comments));
}

if let Some(ref license) = metadata.license {
dialog.set_license(Some(license));
}

if let Some(ref website) = metadata.website {
dialog.set_website(Some(website));
}

if let Some(ref website_label) = metadata.website_label {
dialog.set_website_label(website_label);
}

if let Some(ref authors) = metadata.authors {
let authors_strs: Vec<&str> = authors.iter().map(|s| s.as_str()).collect();
dialog.set_authors(&authors_strs);
}

// Note: credits in muda is Option<String>, not used directly in GTK4
// The credits field is primarily for macOS
let _ = &metadata.credits;

// Present the dialog
dialog.present();
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Duplicate/diverging About-dialog logic vs. gtk/mod.rs's inline handler.

gtk/mod.rs's PredefinedMenuItemType::About handler already builds an equivalent gtk::AboutDialog and uses metadata.full_version() to combine version/short_version into a single version string. This show() instead manually inspects version/short_version and, when both are set, silently drops short_version (line 61-63 is a no-op). The two About-dialog implementations will now produce different output depending on which path triggers them (menu vs. ksni tray), and short_version/credits are silently dropped here while the other path handles full_version() correctly.

Prefer reusing metadata.full_version() here instead of re-deriving version text, to keep both code paths consistent.

♻️ Use full_version() to match gtk/mod.rs behavior
-            if let Some(ref version) = metadata.version {
-                dialog.set_version(Some(version));
-            }
-
-            if let Some(ref short_version) = metadata.short_version {
-                // GTK4 doesn't have a separate short version, but we can include it
-                // in the version string if both are set
-                if metadata.version.is_some() {
-                    // Version is already set, we could append short_version but
-                    // GTK4's AboutDialog handles this differently than macOS
-                    let _ = short_version; // Acknowledge unused on GTK4
-                } else {
-                    dialog.set_version(Some(short_version));
-                }
-            }
+            if let Some(version) = metadata.full_version() {
+                dialog.set_version(Some(&version));
+            }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub fn show(&self) {
let metadata = self.metadata.clone();
gtk4::glib::MainContext::default().invoke(move || {
let dialog = gtk4::AboutDialog::new();
// Set dialog properties from metadata
if let Some(ref name) = metadata.name {
dialog.set_program_name(Some(name));
}
if let Some(ref version) = metadata.version {
dialog.set_version(Some(version));
}
if let Some(ref short_version) = metadata.short_version {
// GTK4 doesn't have a separate short version, but we can include it
// in the version string if both are set
if metadata.version.is_some() {
// Version is already set, we could append short_version but
// GTK4's AboutDialog handles this differently than macOS
let _ = short_version; // Acknowledge unused on GTK4
} else {
dialog.set_version(Some(short_version));
}
}
if let Some(ref copyright) = metadata.copyright {
dialog.set_copyright(Some(copyright));
}
if let Some(ref comments) = metadata.comments {
dialog.set_comments(Some(comments));
}
if let Some(ref license) = metadata.license {
dialog.set_license(Some(license));
}
if let Some(ref website) = metadata.website {
dialog.set_website(Some(website));
}
if let Some(ref website_label) = metadata.website_label {
dialog.set_website_label(website_label);
}
if let Some(ref authors) = metadata.authors {
let authors_strs: Vec<&str> = authors.iter().map(|s| s.as_str()).collect();
dialog.set_authors(&authors_strs);
}
// Note: credits in muda is Option<String>, not used directly in GTK4
// The credits field is primarily for macOS
let _ = &metadata.credits;
// Present the dialog
dialog.present();
});
}
pub fn show(&self) {
let metadata = self.metadata.clone();
gtk4::glib::MainContext::default().invoke(move || {
let dialog = gtk4::AboutDialog::new();
// Set dialog properties from metadata
if let Some(ref name) = metadata.name {
dialog.set_program_name(Some(name));
}
if let Some(version) = metadata.full_version() {
dialog.set_version(Some(&version));
}
if let Some(ref copyright) = metadata.copyright {
dialog.set_copyright(Some(copyright));
}
if let Some(ref comments) = metadata.comments {
dialog.set_comments(Some(comments));
}
if let Some(ref license) = metadata.license {
dialog.set_license(Some(license));
}
if let Some(ref website) = metadata.website {
dialog.set_website(Some(website));
}
if let Some(ref website_label) = metadata.website_label {
dialog.set_website_label(website_label);
}
if let Some(ref authors) = metadata.authors {
let authors_strs: Vec<&str> = authors.iter().map(|s| s.as_str()).collect();
dialog.set_authors(&authors_strs);
}
// Note: credits in muda is Option<String>, not used directly in GTK4
// The credits field is primarily for macOS
let _ = &metadata.credits;
// Present the dialog
dialog.present();
});
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ports/muda/src/platform_impl/gtk/about_dialog.rs` around lines 42 - 101, The
GTK About dialog logic in show() is diverging from the inline handler in
gtk/mod.rs, causing inconsistent handling of version fields. Update show() in
about_dialog.rs to reuse metadata.full_version() the same way
PredefinedMenuItemType::About does, instead of manually checking version and
short_version and dropping one of them. Keep the other metadata fields wired the
same, and make the GTK tray/about path match the menu handler’s AboutDialog
behavior.

Comment on lines +167 to +187
pub fn add_menu_item(&mut self, item: &dyn IsMenuItem, op: AddOp) -> crate::Result<()> {
match op {
AddOp::Append => self.children.push(item.child()),
AddOp::Insert(i) => self.children.insert(i, item.child()),
}

for (menu_id, menu_bar) in &self.instances {
let parent_menu = menu_bar.menu();
let gtk_item =
item.make_gtk_menu_item(menu_bar.application(), *menu_id, parent_menu)?;
match op {
AddOp::Append => parent_menu.append_item(&gtk_item),
AddOp::Insert(position) => parent_menu.insert_item(position as i32, &gtk_item),
}
}

#[cfg(all(feature = "linux-ksni", target_os = "linux"))]
crate::send_menu_update();

Ok(())
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

custom child bindings for icon menu items aren't kept in sync after initial construction.

GTK4's PopoverMenu/PopoverMenuBar ignore icons set directly on a gio::MenuItem, so icon items are rendered via a separate custom attribute + a bound gtk::Button row (custom_menu_row), wired up once by bind_custom_children (lines 200-213) when a window/context-menu instance is first built.

None of the later mutation paths keep this in sync:

  • Menu::add_menu_item (167-187) and MenuChild::add_menu_item (668-691) create the new gio::MenuItem (preserving the custom attribute for icon items) but never call add_custom_child/rebuild bind_custom_children for the newly-inserted item on already-initialized instances, so an icon item added after the menu bar is shown won't have a bound row widget.
  • set_text (910-963) and set_icon (1370-1423) remove/reinsert the gio::MenuItem, but the replacement (new_item, e.g. lines 938-941 / 1398-1401) never re-applies item.set_attribute_value("custom", ...), and no new/updated custom_menu_row widget is bound. The previously-bound row (fixed text/icon captured at bind time) is also never refreshed, so set_text/set_icon on an Icon-type item is effectively a no-op visually on Linux.

(Note: Menu::show_context_menu_for_gtk_window's full context-menu rebuild masks this for that one path, since it re-runs bind_custom_children from scratch each time.)

Also applies to: 200-213, 668-691, 910-963, 1370-1423

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ports/muda/src/platform_impl/gtk/mod.rs` around lines 167 - 187, The custom
GTK menu row binding for icon items is only created during initial setup, so
later mutations leave icon items out of sync. Update Menu::add_menu_item and
MenuChild::add_menu_item to also add or rebind the custom child for newly
inserted icon items on existing instances, and update set_text and set_icon to
preserve/reapply the "custom" attribute when recreating the gio::MenuItem and
refresh the bound custom_menu_row. Use the existing bind_custom_children and
add_custom_child paths so Menu, MenuChild, and the icon-item mutation methods
stay consistent.

Comment thread ports/tray-icon/src/lib.rs Outdated
//!
//! # Dependencies (Linux Only)
//!
//! On Linux, `gtk`, `libxdo` is used to make the predfined `Copy`, `Cut`, `Paste` and `SelectAll` menu items work and `libappindicator` or `libayatnat-appindicator` are used to create the tray icon, so make sure to install them on your system.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Duplicate/typo doc text: "predfined", "libayatnat", "emmited".

Same spelling issues as README.md (line 26) plus a new one — "emmited" should be "emitted" (line 554).

✏️ Proposed fix
-//! On Linux, `gtk`, `libxdo` is used to make the predfined `Copy`, `Cut`, `Paste` and `SelectAll` menu items work and `libappindicator` or `libayatnat-appindicator` are used to create the tray icon, so make sure to install them on your system.
+//! On Linux, `gtk`, `libxdo` is used to make the predefined `Copy`, `Cut`, `Paste` and `SelectAll` menu items work and `libappindicator` or `libayatana-appindicator` are used to create the tray icon, so make sure to install them on your system.
-/// - **Linux**: Unsupported. The event is not emmited even though the icon is shown
+/// - **Linux**: Unsupported. The event is not emitted even though the icon is shown

Also applies to: 554-555

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ports/tray-icon/src/lib.rs` at line 26, Fix the spelling mistakes in the tray
icon documentation comments and any matching README text by correcting the typo
in the `lib.rs` module docs (the `Copy`/`Cut`/`Paste`/`SelectAll` sentence with
`predfined` and `libayatnat`) and the later doc line containing `emmited`;
update the relevant doc comment strings so they read correctly while keeping the
existing meaning, and make sure the same corrections are applied consistently
wherever the duplicated text appears.

Comment on lines +227 to +233
/// [`TrayIcon`] builder struct and associated methods.
#[derive(Default)]
pub struct TrayIconBuilder {
id: TrayIconId,
attrs: TrayIconAttributes,
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

TrayIconBuilder::default() produces a non-unique id, unlike ::new().

TrayIconBuilder::new() explicitly assigns TrayIconId::new_unique(), but the derived Default impl builds id via TrayIconId::default() which is an empty string. Any external caller using TrayIconBuilder::default() will silently get id == "", diverging from the documented ::new() behavior and risking id collisions if multiple trays are built this way.

🐛 Proposed fix
 /// [`TrayIcon`] builder struct and associated methods.
-#[derive(Default)]
 pub struct TrayIconBuilder {
     id: TrayIconId,
     attrs: TrayIconAttributes,
 }
+
+impl Default for TrayIconBuilder {
+    fn default() -> Self {
+        Self::new()
+    }
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// [`TrayIcon`] builder struct and associated methods.
#[derive(Default)]
pub struct TrayIconBuilder {
id: TrayIconId,
attrs: TrayIconAttributes,
}
/// [`TrayIcon`] builder struct and associated methods.
pub struct TrayIconBuilder {
id: TrayIconId,
attrs: TrayIconAttributes,
}
impl Default for TrayIconBuilder {
fn default() -> Self {
Self::new()
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ports/tray-icon/src/lib.rs` around lines 227 - 233, TrayIconBuilder currently
derives Default, which initializes id through TrayIconId::default() instead of
the unique id used by TrayIconBuilder::new(). Replace the derived default
behavior with a manual Default implementation (or equivalent shared initializer)
so TrayIconBuilder::default() also assigns TrayIconId::new_unique() and matches
the documented builder behavior. Keep the fix localized to TrayIconBuilder and
its constructor/default path so both entry points produce consistent, unique
ids.

Comment on lines +21 to +27
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
Ok(Self {
rgba,
width: width as i32,
height: height as i32,
})
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Missing RGBA buffer validation, unlike the KSNI/Linux backend.

PlatformIcon::from_rgba never checks rgba.len() % 4 == 0 or that width * height == rgba.len() / 4, even though the public Icon::from_rgba contract documents that invalid buffers should return BadIcon. The sibling linux/icon.rs::PlatformIcon::from_rgba (Lines 19-24 there) does perform the divisible-by-4 check. On this GTK backend, a malformed buffer will silently construct an Ok icon and only fail later inside write_to_png (Line 33-38) with a less specific PNG-encoder error, and only when the icon is actually written.

🛡️ Proposed fix to align with the documented contract
 impl PlatformIcon {
     pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
+        if !rgba.len().is_multiple_of(4) {
+            return Err(BadIcon::ByteCountNotDivisibleBy4 {
+                byte_count: rgba.len(),
+            });
+        }
         Ok(Self {
             rgba,
             width: width as i32,
             height: height as i32,
         })
     }

Comment on lines +42 to +62
let shutdown = Arc::new(AtomicBool::new(false));
let tray_service = ksni::TrayService::new(Tray::new(id, icon, title, tooltip, menu));
let tray_handle = tray_service.handle();
tray_service.spawn();

let update_tray_handle = tray_handle.clone();
let update_shutdown = shutdown.clone();
thread::spawn(move || {
while muda::recv_menu_update().is_ok() {
if update_shutdown.load(Ordering::Relaxed) {
break;
}
update_tray_handle.update(|_| {});
}
});

Ok(Self {
tray_handle,
shutdown,
})
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n 'MENU_UPDATE_CHANNEL' -A3 -B3 ports/muda/src/lib.rs

Repository: OlympusLedgerOrg/tauri

Length of output: 2064


🌐 Web query:

"MENU_UPDATE_CHANNEL crossbeam_channel muda recv_menu_update send_menu_update"

💡 Result:

The terms mentioned appear to stem from the internal implementation of the 'muda' (Menu Utilities for Desktop Applications) crate in Rust [1][2]. There is no public API in the 'muda' crate named 'MENU_UPDATE_CHANNEL', 'recv_menu_update', or 'send_menu_update'. Instead, 'muda' uses a private static channel to handle menu events [3]. The following breakdown explains the related concepts found in the source code: 1. MENU_CHANNEL: In the 'muda' crate source code, there is a private static variable defined as: static MENU_CHANNEL: Lazy<(Sender, MenuEventReceiver)> = Lazy::new(unbounded); This uses 'crossbeam-channel' (specifically 'unbounded' and 'Sender'/'Receiver') to manage the communication of 'MenuEvent' objects [3]. 2. Handling Events: - Users typically interact with menu events via 'MenuEvent::receiver' [4][3], which returns a reference to the 'MenuEventReceiver' (a type alias for 'Receiver' from 'crossbeam-channel') [3]. - Events are consumed using standard channel methods like 'try_recv' [1][3][2]. - Users can alternatively register a handler using 'MenuEvent::set_event_handler' [4][3][2]. When a handler is set, the internal 'send' function calls the handler directly instead of sending the event through 'MENU_CHANNEL' [3]. The query likely refers to a misunderstanding of these internal names or perhaps custom code built on top of the 'muda' library. 'crossbeam-channel' is a popular, independent crate used by 'muda' to implement these event-passing capabilities [5][6][3].

Citations:


🌐 Web query:

"docs.rs muda MenuEvent receiver send crossbeam-channel single receiver multiple consumers"

💡 Result:

In the muda crate, MenuEvent::receiver provides access to a MenuEventReceiver [1][2], which is a type alias for a crossbeam_channel::Receiver [2]. Regarding your interest in a single-receiver/multiple-consumers pattern with crossbeam-channel: 1. Crossbeam channels are Multi-Producer, Multi-Consumer (MPMC) [3][4]. 2. You can clone a Receiver handle [3][4]. However, cloning the receiver does not create multiple independent streams of the same messages (i.e., it is not a broadcast/pub-sub mechanism) [3][5][6]. Instead, it creates a new handle to the same underlying channel [3][4]. 3. When multiple consumers hold cloned Receiver handles, any message sent into the channel will be delivered to exactly one of those receivers [3][6][7]. Each message is consumed once and removed from the channel [7]. If you need a broadcast pattern where multiple consumers receive a copy of every message, crossbeam-channel does not support this natively. You would need to implement a broadcast mechanism, such as creating multiple channels and distributing messages to all of them, or using a specialized broadcast crate. For muda specifically, the recommended way to handle menu events in applications using GUI event loops (like winit or tao) is to use MenuEvent::set_event_handler to forward events to the application's main event loop via an EventLoopProxy [8][9][10], rather than attempting to share the receiver across multiple consumers manually [8][11].

Citations:


Shared menu updates need broadcast semantics. MenuEvent::receiver() is a crossbeam_channel::Receiver, so each wakeup is delivered to only one consumer. With one blocking thread per TrayIcon, a Drop wakeup can land on a different tray’s thread, leaving the intended thread blocked and other icons stale. Route this through a per-tray channel or a single dispatcher that fans out updates instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ports/tray-icon/src/platform_impl/linux/mod.rs` around lines 42 - 62, The
tray update loop in the Linux `TrayIcon` implementation is using the shared
`muda::recv_menu_update()` receiver directly, so each wakeup can be consumed by
the wrong tray thread and leave others stale. Update the `TrayIcon::new` spawn
path to avoid per-tray blocking on the shared `MenuEvent::receiver()` and
instead route updates through a per-tray channel or a single dispatcher that
broadcasts to all `tray_handle` instances, keeping the `shutdown` handling
intact.

Comment on lines +226 to +257
pub fn set_tooltip<S: AsRef<str>>(&mut self, tooltip: Option<S>) -> crate::Result<()> {
unsafe {
let mut nid = NOTIFYICONDATAW {
uFlags: NIF_TIP,
hWnd: self.hwnd,
uID: self.internal_id,
cbSize: size_of::<NOTIFYICONDATAW>() as u32,
..std::mem::zeroed()
};
if let Some(tooltip) = &tooltip {
let tip = util::encode_wide(tooltip.as_ref());
#[allow(clippy::manual_memcpy)]
for i in 0..tip.len().min(128) {
nid.szTip[i] = tip[i];
}
}

if Shell_NotifyIconW(NIM_MODIFY, &nid) == 0 {
return Err(crate::Error::OsError(std::io::Error::last_os_error()));
}

// send the new tooltip to the subclass proc to store it in the tray data
SendMessageW(
self.hwnd,
WM_USER_UPDATE_TRAYTOOLTIP,
Box::into_raw(Box::new(tooltip.map(|t| t.as_ref().to_string()))) as _,
0,
);
}

Ok(())
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Tooltip may lack null terminator when text is ≥128 UTF-16 units.

tip.len().min(128) copies at most 128 elements into the fixed [u16; 128] szTip buffer. encode_wide appends a trailing 0, so tip.len() = text length + 1. If text length is ≥128, the loop copies 128 raw characters with no room left for the terminator, leaving szTip unterminated and potentially causing garbled tooltip rendering.

🩹 Proposed fix
-            for i in 0..tip.len().min(128) {
+            for i in 0..tip.len().min(127) {
                 nid.szTip[i] = tip[i];
             }
+            nid.szTip[127] = 0;

Apply the equivalent change in register_tray_icon (Lines 585-589).

Also applies to: 565-613

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ports/tray-icon/src/platform_impl/windows/mod.rs` around lines 226 - 257, The
tooltip handling in set_tooltip currently copies up to 128 UTF-16 units into
NOTIFYICONDATAW::szTip without guaranteeing a trailing null when the text is at
the limit. Update the copy logic to reserve space for the terminator and ensure
szTip is always null-terminated, and apply the same fix in register_tray_icon
where the tray icon tooltip is initialized.

@OlympusLedgerOrg
OlympusLedgerOrg merged commit 246f8f3 into gtk4-webkit6-tao-wry Jul 7, 2026
35 checks passed
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