MOMO0302-02 7d4caf833c fix: a batch of small UI, tray, floating window and file handling bugs (#2129)
* fix: keep the Dock icon in sync when the tray is clicked rapidly

On macOS the Dock icon is toggled from two places: the window `show` event
calls showDockIcon(), and the window `close` handler calls hideDockIcon()
after awaiting the app config. Both delegate to Electron, where
`Browser::DockShow` defers TransformProcessType to the main queue and
`Browser::DockHide` returns immediately when it is called within one second
of the last DockShow - a workaround for macOS leaving duplicate Dock icons
behind. Toggling the window from the tray twice in quick succession therefore
lost the hide: the Dock icon stayed visible although the window was hidden
and "show Dock icon" was off, and only another slow toggle brought it back.

Serialize the two operations on a single chain, coalesce them on the desired
state so a stale request cannot win, and wait out Electron's suppression
window before hiding. The `isVisible()` guard has to go with it: after a
suppressed hide the activation policy is still regular, so the guard would
skip the retry that actually fixes the state.

The close handler additionally skips the hide when the window became visible
again while the config was being read.

Refs #1867

(cherry picked from commit 9dbc79da14abc7504d66b00a32d011776b6f8f06)

* fix: keep the tray status colour when the macOS traffic display is on

With "show speed in the status bar" enabled the tray icon is composed in the
renderer (logo + text on a canvas) and the main process only displays it.
updateTrayIcon()/updateTrayIconImmediate() therefore return early, and the
trayIconUpdate handler called setTemplateImage(true) unconditionally, so
macOS renders the alpha channel only: the system proxy / TUN colours were
dropped as soon as the traffic display was turned on. Turning it off brought
them straight back, which is exactly what the report describes.

The main process cannot tint an image it did not draw, so it now tells the
renderer which icon to use and what colour to write the text in
(getTrayTrafficStyle), and the renderer reports back whether the frame it
produced carries a colour. Only coloured frames stop being template images;
without a status colour, or with "disable tray icon colour" on, nothing
changes and the icon stays a template image as before.

The text colour follows the system appearance rather than nativeTheme,
because the menu bar keeps using the system appearance even when the user
forces a light or dark theme inside the app - a template image used to get
that inversion for free.

Refs #1143

(cherry picked from commit 88ed2997869e362715ae7c25fa1051da6b9740fc)

* fix: bring the tray icon back when the floating window is gone

Hiding the tray icon is only offered while the floating window is enabled,
because the floating window is then the remaining way to reach the app. That
guarantee is dropped at runtime: if the floating window fails to be created,
or its renderer crashes and the window is destroyed, `disableTray` stays on.
The app is then left with no tray icon and no floating window, so closing the
main window hides it for good and the process has to be killed (#2046).

Restore the tray icon (and clear `disableTray`) on both paths, the same way
`closeFloatingWindow` already does when the window is closed deliberately.

(cherry picked from commit e6d1c735f5047a1bdf0da5d7777606c79c39c0fe)

* fix: keep the floating window position when it sits at a screen edge (#415)

The floating window restores its position through electron-window-state,
whose validateState() only accepts a saved position when the window is
*fully* contained in one display; otherwise it calls resetStateToDefault()
which hardcodes x/y to 0. Users routinely park the floating window against
the right or bottom edge with part of it off screen, so every restart moved
it to the top-left corner.

Read the saved position directly and clamp it into the work area of the
nearest display instead. A position that is already fully on screen is
returned unchanged, so nothing moves for windows that were not affected.

Measured on Windows 11 with a 2560x1440 primary display: a saved position of
(2540, 400) makes electron-window-state return (0, 0), while clamping returns
(2425, 400).

(cherry picked from commit d12cb1dfc1fde5c7a32ea40d2e061fcb0f69dc80)

* fix: keep the proxy group search box usable while filtering

The per-group search box lives inside the group header, which GroupedVirtuoso
renders virtually. As soon as typing shrinks the group, the list gets shorter,
the header scrolls out of the render window and is unmounted: the box loses
focus, collapses back to width 0 and the still-applied filter becomes
invisible. Composition-based IMEs (WeChat, Sogou) are aborted mid-word by the
same unmount, which is why letters end up typed literally (#332, #1621).

- pin the group being filtered to the top of the list on every search change,
  so its header stays mounted
- keep the input expanded whenever it holds a search term instead of only
  while focused
- take focus back when the input is remounted with a search term and nothing
  else has claimed focus
- show the filtered node count in the group chip instead of the total

(cherry picked from commit 8efecd62d591f287458318bfd8977e2c24b87677)

* fix: keep TUN enabled when saving TUN settings

The save button on the TUN page sent

    { tun: { device, stack, auto-route, ..., mtu } }

with no `enable` field. patchControledMihomoConfig forwards that patch verbatim
to the running core as PATCH /configs, and in the core `tunSchema.Enable` is a
plain bool, not a pointer - an absent `enable` decodes to false, and
pointerOrDefaultTun then overwrites the live value, so ReCreateTun immediately
tears the virtual adapter down.

Today the follow-up mihomoHotReloadConfig() puts it back, so the visible effect
is that the adapter is destroyed and recreated twice per save. But if that hot
reload fails (bad profile, PUT /configs error) the user is left with TUN off in
the core while the switch, the tray icon and the config file all still say it
is on.

Send the current enable state along with the rest of the TUN settings so the
patch can never mean "turn TUN off".

Relates to #554

(cherry picked from commit 921bffbd2e4bf0a41ad5e67d81af584a0fa3ac98)

* fix: send WebDAV basic auth credentials as UTF-8

The webdav client builds its Basic auth header with the `base-64` package,
which only accepts Latin1 characters and throws `InvalidCharacterError` for
anything outside that range. A WebDAV account with a non-ASCII user name or
password therefore made every backup/restore call fail before a request was
even sent (#323).

Build the Authorization header locally with `Buffer.from(..., "utf-8")` as
RFC 7617 requires and disable the library-side auth so it does not overwrite
it. The header is byte-identical to the previous one for ASCII credentials.

(cherry picked from commit 5d6593e683b4da4f13d214e832d45a61d113da8d)

* fix: stop asking Windows for the SSID on every poll cycle

The SSID watcher polls `netsh wlan show interfaces` on a timer (30s at the time
of the report, 15s today). On Windows 11 the SSID counts as location data, so
every one of those calls makes "Network Command Shell" (netsh.exe) request the
user's location and flashes the location indicator - forever, for as long as
the app runs, without the user doing anything.

The SSID cannot change while the network interfaces are untouched, so take a
cheap fingerprint of os.networkInterfaces() (names plus addresses and MACs)
first and only shell out to netsh when it differs from the previous probe. Any
association change - joining, leaving or roaming to another network - changes
the wireless adapter's addresses, so real SSID changes are still picked up on
the next tick. When a probe returns no SSID at all (wireless not up yet, or no
wireless adapter) the fingerprint gate is bypassed every 5 minutes so the
watcher can still recover.

Measured on Windows 11: the fingerprint stayed byte-identical across 10
consecutive one-second samples, so a steady-state machine goes from one netsh
call every 15s to none.

Closes #479

(cherry picked from commit fb1a6646e189c78b2f8c92b58d8b0bd6362cc892)

* fix: retry resource copy when the target is replaced mid-copy

initFiles() rethrows every error other than EPERM/EBUSY/EACCES, and
geoip.dat / geosite.dat / country.mmdb are on the critical list, so a
single failure aborts the whole runtime file initialization.

The log attached to #1510 shows exactly that happening with an ENOENT:

  Failed to copy geoip.dat: Error: ENOENT: no such file or directory,
  unlink '...\mihomo-party\work\geoip.dat'

ENOENT here does not mean the source is missing - that is checked before
the copy starts. fs.cp() with force:true stats the destination and then
unlinks/chmods it, and anything else touching the work directory in
between (the kernel's own geo auto-update, antivirus, a second instance)
turns that into ENOENT. A tight loop that deletes the destination while
copying reproduces it on 2275 of 2321 attempts.

Retry the copy once instead of failing initialization outright.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 35ff76173f921dc6be2e2fd508b6365ea9ce8db1)

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 00:15:56 +08:00
2026-07-01 08:46:41 +08:00
2026-04-01 08:18:15 +08:00
2026-04-01 08:18:15 +08:00
2024-08-07 09:48:27 +08:00
2026-07-01 08:46:41 +08:00
2024-08-07 09:48:27 +08:00
2024-08-07 09:48:59 +08:00
2026-09-09 12:17:51 +08:00
2024-08-07 09:48:27 +08:00
2026-07-01 08:46:41 +08:00
2026-07-01 08:46:41 +08:00

Another Mihomo GUI

本项目认证稳定机场推荐:“狗狗加速

狗狗加速 —— 技术流机场 Doggygo VPN
  • 高性能海外机场,稳定首选,海外团队,无跑路风险
  • Clash Party专属8折优惠码party仅有500份
  • Party专属链接注册送 3 天,每天 1G 流量 免费试用
  • 优惠套餐每月仅需 15.8 元160G 流量,年付 8 折
  • 全球首家支持Hysteria1/2 协议集群负载均衡设计高速专线基于最新UDP quic技术极低延迟无视晚高峰4K 秒开配合Clash Party食用更省心
  • 解锁流媒体及 ChatGPT
  • 官网:https://狗狗加速.com

特性

  • 一键 Smart Core 规则覆写,基于 AI 模型自动选择最优节点 详细介绍请看 这里
  • 开箱即用,无需服务模式的 Tun
  • 多种配色主题可选UI 焕然一新
  • 支持大部分 Mihomo(Clash Meta) 常用配置修改
  • 内置 Smart内核 与 Mihomo(Clash Meta) 内核
  • 通过 WebDAV 一键备份和恢复配置
  • 强大的覆写功能,任意修订配置文件
  • 深度集成 Sub-Store轻松管理订阅

安装/使用指南见 官方文档

Description
No description provided
Readme GPL-3.0 41 MiB
Languages
TypeScript 89.5%
JavaScript 8.9%
Shell 1.2%
CSS 0.2%
NSIS 0.1%