diff --git a/README.md b/README.md index 4984e32..d1f7fab 100644 --- a/README.md +++ b/README.md @@ -256,7 +256,8 @@ https://evilfox.win/ - 3.8.2+3: poll без os.Stat ядер + кэш host; Darwin sysproxy batch; Connect без Config.Clone; - 3.8.2+4: единый GUI Windows/macOS на glaze + HTTP `/api` (вместо WebView2 Bind); автоинкремент номера сборки при compile; артефакты Windows с версией в имени (`Navis-x.y.z.b.exe`); - 3.8.2+5: delta-poll UI (`getState` по rev); пауза опроса при скрытом окне; cancel lifecycle dock/watchdog; invalidate/лимит hostCache; PollUI без lock на `Engine.Running`; singleflight corebin; PingAll worker-pool; -- 3.8.2+6: поиск по серверам и хоткеи; Windows close→tray (Открыть/Выход); фоновый прогрев активного core; SSE `/api/events` + редкий poll fallback. +- 3.8.2+6: поиск по серверам и хоткеи; Windows close→tray (Открыть/Выход); фоновый прогрев активного core; SSE `/api/events` + редкий poll fallback; +- 3.8.2+7: Windows — иконка в панели задач и трее цвета морской волны при подключении (как Dock на macOS). В 3.8.1: - меньше нагрузка в простое: лёгкий опрос интерфейса без полного клонирования конфига, кэш путей к ядрам, лог с лимитом размера; diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 8a36aa6..14e1638 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -13,8 +13,8 @@ android { minSdk = 26 targetSdk = 35 // versionCode = major*1_000_000 + minor*10_000 + patch*100 + build - versionCode = 3080206 - versionName = "3.8.2+6" + versionCode = 3080207 + versionName = "3.8.2+7" vectorDrawables.useSupportLibrary = true ndk { abiFilters += listOf("arm64-v8a", "armeabi-v7a", "x86_64") diff --git a/build-macos.bat b/build-macos.bat index 53b0a4b..f5c832a 100644 --- a/build-macos.bat +++ b/build-macos.bat @@ -42,11 +42,11 @@ if errorlevel 1 exit /b 1 go build -o "tools\packmac\packmac.exe" .\tools\packmac if errorlevel 1 exit /b 1 -tools\packmac\packmac.exe -bin "dist\navis-release\darwin-arm64\Navis" -out "dist\navis-release\darwin-arm64" -version 3.8.2 -build 3.8.2.6 -arch arm64 +tools\packmac\packmac.exe -bin "dist\navis-release\darwin-arm64\Navis" -out "dist\navis-release\darwin-arm64" -version 3.8.2 -build 3.8.2.7 -arch arm64 if errorlevel 1 exit /b 1 -tools\packmac\packmac.exe -bin "dist\navis-release\darwin-amd64\Navis" -out "dist\navis-release\darwin-amd64" -version 3.8.2 -build 3.8.2.6 -arch amd64 +tools\packmac\packmac.exe -bin "dist\navis-release\darwin-amd64\Navis" -out "dist\navis-release\darwin-amd64" -version 3.8.2 -build 3.8.2.7 -arch amd64 if errorlevel 1 exit /b 1 -tools\packmac\packmac.exe -bin "dist\navis-release\darwin-universal\Navis" -out "dist\navis-release\darwin-universal" -version 3.8.2 -build 3.8.2.6 -arch universal +tools\packmac\packmac.exe -bin "dist\navis-release\darwin-universal\Navis" -out "dist\navis-release\darwin-universal" -version 3.8.2 -build 3.8.2.7 -arch universal if errorlevel 1 exit /b 1 echo Built Mac GUI + CLI: diff --git a/cmd/vpnapp/gui_desktop.go b/cmd/vpnapp/gui_desktop.go index b6ffe15..c573742 100644 --- a/cmd/vpnapp/gui_desktop.go +++ b/cmd/vpnapp/gui_desktop.go @@ -15,6 +15,7 @@ import ( "vpnclient/internal/apphost" "vpnclient/internal/config" "vpnclient/internal/core" + "vpnclient/internal/dockicon" "vpnclient/internal/logbuf" "vpnclient/internal/trayhost" "vpnclient/internal/update" @@ -71,6 +72,7 @@ func main() { }() a.StartBackground() + dockicon.OnIconChange = trayhost.SetConnectedIcon trayhost.Start("Navis", trayhost.Hooks{ Connect: func() { _ = a.Connect() }, Disconnect: func() { _ = a.Disconnect() }, diff --git a/cmd/vpnapp/platform_windows.go b/cmd/vpnapp/platform_windows.go index d0ef030..bdd467f 100644 --- a/cmd/vpnapp/platform_windows.go +++ b/cmd/vpnapp/platform_windows.go @@ -12,6 +12,8 @@ import ( "github.com/crgimenes/glaze" "golang.org/x/sys/windows" + + "vpnclient/internal/dockicon" ) var ( @@ -68,6 +70,7 @@ func decorateWindow(w glaze.WebView) { } hwnd := w.Window() applyWindowIcon(hwnd) + dockicon.BindWindow(hwnd) installCloseToHide(hwnd) } diff --git a/dist/navis-release/Navis-3.8.2.7.exe b/dist/navis-release/Navis-3.8.2.7.exe new file mode 100644 index 0000000..0661746 Binary files /dev/null and b/dist/navis-release/Navis-3.8.2.7.exe differ diff --git a/dist/navis-release/Navis.exe b/dist/navis-release/Navis.exe index 245160b..0661746 100644 Binary files a/dist/navis-release/Navis.exe and b/dist/navis-release/Navis.exe differ diff --git a/dist/navis-release/update.json b/dist/navis-release/update.json index edf3b9d..18320cb 100644 --- a/dist/navis-release/update.json +++ b/dist/navis-release/update.json @@ -1,16 +1,16 @@ { "version": "3.8.2", - "notes": "Navis 3.8.2+6: поиск/фильтр серверов и хоткеи (/, Ctrl+F, Space/Enter, Esc); Windows: закрытие окна = скрытие в трей, «Открыть»/Выход из трея; фоновый EnsureCore активного протокола; SSE /api/events для мгновенного статуса + poll 12 с как safety net.", + "notes": "Navis 3.8.2+7: Windows — иконка в панели задач и трее цвета морской волны при подключении (как Dock на macOS); при отключении возвращается обычная.", "platform": "windows-amd64", "os": "windows", "arch": "amd64", "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "aab618d452f8eddd7cc4400770e53a6a9c456b2bc6093190dd21f1b3fbcd765e", + "sha256": "b7902251aeb671946dbfe7ab2f3470dea82cf29cc0cd68e42f6632653ea90605", "mandatory": false, "platforms": { "windows-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "aab618d452f8eddd7cc4400770e53a6a9c456b2bc6093190dd21f1b3fbcd765e", + "sha256": "b7902251aeb671946dbfe7ab2f3470dea82cf29cc0cd68e42f6632653ea90605", "os": "windows", "arch": "amd64" }, diff --git a/dist/navis-release/vpnclient-3.8.2.7.exe b/dist/navis-release/vpnclient-3.8.2.7.exe new file mode 100644 index 0000000..ac5b3b9 Binary files /dev/null and b/dist/navis-release/vpnclient-3.8.2.7.exe differ diff --git a/dist/navis-release/vpnclient.exe b/dist/navis-release/vpnclient.exe index 42f54d8..ac5b3b9 100644 Binary files a/dist/navis-release/vpnclient.exe and b/dist/navis-release/vpnclient.exe differ diff --git a/dist/update.json b/dist/update.json index edf3b9d..18320cb 100644 --- a/dist/update.json +++ b/dist/update.json @@ -1,16 +1,16 @@ { "version": "3.8.2", - "notes": "Navis 3.8.2+6: поиск/фильтр серверов и хоткеи (/, Ctrl+F, Space/Enter, Esc); Windows: закрытие окна = скрытие в трей, «Открыть»/Выход из трея; фоновый EnsureCore активного протокола; SSE /api/events для мгновенного статуса + poll 12 с как safety net.", + "notes": "Navis 3.8.2+7: Windows — иконка в панели задач и трее цвета морской волны при подключении (как Dock на macOS); при отключении возвращается обычная.", "platform": "windows-amd64", "os": "windows", "arch": "amd64", "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "aab618d452f8eddd7cc4400770e53a6a9c456b2bc6093190dd21f1b3fbcd765e", + "sha256": "b7902251aeb671946dbfe7ab2f3470dea82cf29cc0cd68e42f6632653ea90605", "mandatory": false, "platforms": { "windows-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "aab618d452f8eddd7cc4400770e53a6a9c456b2bc6093190dd21f1b3fbcd765e", + "sha256": "b7902251aeb671946dbfe7ab2f3470dea82cf29cc0cd68e42f6632653ea90605", "os": "windows", "arch": "amd64" }, diff --git a/internal/dockicon/dockicon_darwin.go b/internal/dockicon/dockicon_darwin.go index 0c45efd..a8d9a10 100644 --- a/internal/dockicon/dockicon_darwin.go +++ b/internal/dockicon/dockicon_darwin.go @@ -12,6 +12,9 @@ import ( "github.com/ebitengine/purego/objc" ) +// BindWindow is a no-op on macOS (Dock uses NSApplication icon image). +func BindWindow(h unsafe.Pointer) { _ = h } + //go:embed icon_idle.png var iconIdlePNG []byte @@ -103,4 +106,7 @@ func SetConnected(connected bool) { } // AppKit must run on the main thread (glaze owns it). app.Send(selPerf, selSet, img, false) + if OnIconChange != nil { + OnIconChange(connected) + } } diff --git a/internal/dockicon/dockicon_other.go b/internal/dockicon/dockicon_other.go index 304212c..239be59 100644 --- a/internal/dockicon/dockicon_other.go +++ b/internal/dockicon/dockicon_other.go @@ -1,6 +1,11 @@ -//go:build !darwin +//go:build !darwin && !windows package dockicon -// SetConnected is a no-op outside macOS (Dock icons are a Darwin feature). +import "unsafe" + +// SetConnected is a no-op on platforms without a Dock/taskbar icon swap. func SetConnected(connected bool) {} + +// BindWindow is a no-op outside Windows. +func BindWindow(h unsafe.Pointer) { _ = h } diff --git a/internal/dockicon/dockicon_windows.go b/internal/dockicon/dockicon_windows.go new file mode 100644 index 0000000..655786c --- /dev/null +++ b/internal/dockicon/dockicon_windows.go @@ -0,0 +1,295 @@ +//go:build windows + +package dockicon + +import ( + "bytes" + _ "embed" + "image" + "image/png" + "sync" + "unsafe" + + "golang.org/x/image/draw" + "golang.org/x/sys/windows" +) + +//go:embed icon_idle.png +var iconIdlePNG []byte + +//go:embed icon_connected.png +var iconConnectedPNG []byte + +var ( + user32 = windows.NewLazySystemDLL("user32.dll") + gdi32 = windows.NewLazySystemDLL("gdi32.dll") + + procSendMessage = user32.NewProc("SendMessageW") + procCreateIconFromResource = user32.NewProc("CreateIconFromResourceEx") + procCreateIconIndirect = user32.NewProc("CreateIconIndirect") + procDestroyIcon = user32.NewProc("DestroyIcon") + procCreateDIBSection = gdi32.NewProc("CreateDIBSection") + procCreateBitmap = gdi32.NewProc("CreateBitmap") + procDeleteObject = gdi32.NewProc("DeleteObject") + + mu sync.Mutex + + hwnd uintptr + last *bool + + idleSmall windows.Handle + idleBig windows.Handle + connSmall windows.Handle + connBig windows.Handle + iconsReady bool +) + +const ( + wmSetIcon = 0x0080 + iconSmall = 0 + iconBig = 1 + biRGB = 0 + dibRGBColors = 0 +) + +type iconInfo struct { + FIcon int32 + XHotspot uint32 + YHotspot uint32 + HbmMask windows.Handle + HbmColor windows.Handle +} + +type bitmapInfoHeader struct { + BiSize uint32 + BiWidth int32 + BiHeight int32 + BiPlanes uint16 + BiBitCount uint16 + BiCompression uint32 + BiSizeImage uint32 + BiXPelsPerMeter int32 + BiYPelsPerMeter int32 + BiClrUsed uint32 + BiClrImportant uint32 +} + +type bitmapInfo struct { + Header bitmapInfoHeader + Colors [1]uint32 +} + +// BindWindow attaches the main HWND so SetConnected can update the taskbar icon. +func BindWindow(h unsafe.Pointer) { + mu.Lock() + defer mu.Unlock() + hwnd = uintptr(h) + if err := ensureIconsLocked(); err != nil { + return + } + connected := last != nil && *last + applyLocked(connected) +} + +// SetConnected swaps the taskbar/window icon: sea-wave N when connected, original when idle. +func SetConnected(connected bool) { + mu.Lock() + if last != nil && *last == connected { + mu.Unlock() + return + } + v := connected + last = &v + if hwnd == 0 { + mu.Unlock() + if OnIconChange != nil { + OnIconChange(connected) + } + return + } + if err := ensureIconsLocked(); err != nil { + mu.Unlock() + return + } + applyLocked(connected) + mu.Unlock() + if OnIconChange != nil { + OnIconChange(connected) + } +} + +// StatusIcon returns a shared HICON for the current connection state (do not DestroyIcon). +func StatusIcon(connected bool) windows.Handle { + mu.Lock() + defer mu.Unlock() + if err := ensureIconsLocked(); err != nil { + return 0 + } + if connected { + return connBig + } + return idleBig +} + +func applyLocked(connected bool) { + if hwnd == 0 { + return + } + small, big := idleSmall, idleBig + if connected { + small, big = connSmall, connBig + } + if small != 0 { + procSendMessage.Call(hwnd, wmSetIcon, iconSmall, uintptr(small)) + } + if big != 0 { + procSendMessage.Call(hwnd, wmSetIcon, iconBig, uintptr(big)) + } +} + +func ensureIconsLocked() error { + if iconsReady { + return nil + } + var err error + idleSmall, err = hiconFromPNG(iconIdlePNG, 16) + if err != nil { + return err + } + idleBig, err = hiconFromPNG(iconIdlePNG, 32) + if err != nil { + return err + } + connSmall, err = hiconFromPNG(iconConnectedPNG, 16) + if err != nil { + return err + } + connBig, err = hiconFromPNG(iconConnectedPNG, 32) + if err != nil { + return err + } + iconsReady = true + return nil +} + +func hiconFromPNG(src []byte, size int) (windows.Handle, error) { + resized, err := resizePNG(src, size) + if err != nil { + return 0, err + } + if h := createIconFromPNGResource(resized, size); h != 0 { + return h, nil + } + return createIconFromImage(resized, size) +} + +func resizePNG(src []byte, size int) ([]byte, error) { + img, err := png.Decode(bytes.NewReader(src)) + if err != nil { + return nil, err + } + dst := image.NewRGBA(image.Rect(0, 0, size, size)) + draw.CatmullRom.Scale(dst, dst.Bounds(), img, img.Bounds(), draw.Over, nil) + var buf bytes.Buffer + if err := png.Encode(&buf, dst); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func createIconFromPNGResource(pngBytes []byte, size int) windows.Handle { + if len(pngBytes) == 0 { + return 0 + } + h, _, _ := procCreateIconFromResource.Call( + uintptr(unsafe.Pointer(&pngBytes[0])), + uintptr(len(pngBytes)), + 1, + 0x00030000, + uintptr(size), + uintptr(size), + 0, + ) + return windows.Handle(h) +} + +func createIconFromImage(pngBytes []byte, size int) (windows.Handle, error) { + img, err := png.Decode(bytes.NewReader(pngBytes)) + if err != nil { + return 0, err + } + rgba, ok := img.(*image.RGBA) + if !ok { + tmp := image.NewRGBA(img.Bounds()) + draw.Draw(tmp, tmp.Bounds(), img, img.Bounds().Min, draw.Src) + rgba = tmp + } + + hbmColor, err := dibFromRGBA(rgba) + if err != nil { + return 0, err + } + // Monochrome mask: all zeros => fully opaque where color has alpha handled by 32-bit DIB. + hbmMask, _, err2 := procCreateBitmap.Call(uintptr(size), uintptr(size), 1, 1, 0) + if hbmMask == 0 { + procDeleteObject.Call(uintptr(hbmColor)) + if err2 != nil { + return 0, err2 + } + return 0, windows.ERROR_INVALID_HANDLE + } + + ii := iconInfo{ + FIcon: 1, + XHotspot: 0, + YHotspot: 0, + HbmMask: windows.Handle(hbmMask), + HbmColor: hbmColor, + } + h, _, err3 := procCreateIconIndirect.Call(uintptr(unsafe.Pointer(&ii))) + procDeleteObject.Call(uintptr(hbmMask)) + procDeleteObject.Call(uintptr(hbmColor)) + if h == 0 { + if err3 != nil { + return 0, err3 + } + return 0, windows.ERROR_INVALID_HANDLE + } + return windows.Handle(h), nil +} + +func dibFromRGBA(src *image.RGBA) (windows.Handle, error) { + w := src.Bounds().Dx() + h := src.Bounds().Dy() + bi := bitmapInfo{ + Header: bitmapInfoHeader{ + BiSize: uint32(unsafe.Sizeof(bitmapInfoHeader{})), + BiWidth: int32(w), + BiHeight: -int32(h), // top-down + BiPlanes: 1, + BiBitCount: 32, + BiCompression: biRGB, + }, + } + var bits unsafe.Pointer + hbm, _, err := procCreateDIBSection.Call(0, uintptr(unsafe.Pointer(&bi)), dibRGBColors, uintptr(unsafe.Pointer(&bits)), 0, 0) + if hbm == 0 || bits == nil { + if err != nil { + return 0, err + } + return 0, windows.ERROR_INVALID_HANDLE + } + // Windows wants BGRA. + dst := unsafe.Slice((*byte)(bits), w*h*4) + for y := 0; y < h; y++ { + for x := 0; x < w; x++ { + i := src.PixOffset(x, y) + o := (y*w + x) * 4 + dst[o+0] = src.Pix[i+2] // B + dst[o+1] = src.Pix[i+1] // G + dst[o+2] = src.Pix[i+0] // R + dst[o+3] = src.Pix[i+3] // A + } + } + return windows.Handle(hbm), nil +} diff --git a/internal/dockicon/hooks.go b/internal/dockicon/hooks.go new file mode 100644 index 0000000..3c96389 --- /dev/null +++ b/internal/dockicon/hooks.go @@ -0,0 +1,5 @@ +package dockicon + +// OnIconChange is invoked after SetConnected updates the platform icon. +// Used on Windows to keep the tray glyph in sync; unused on other platforms. +var OnIconChange func(connected bool) diff --git a/internal/trayhost/tray_stub.go b/internal/trayhost/tray_stub.go index fefb0ef..3fe609a 100644 --- a/internal/trayhost/tray_stub.go +++ b/internal/trayhost/tray_stub.go @@ -8,3 +8,6 @@ func start(appName string, h Hooks) bool { _ = h return false } + +// SetConnectedIcon is a no-op when tray is unsupported. +func SetConnectedIcon(connected bool) { _ = connected } diff --git a/internal/trayhost/tray_windows.go b/internal/trayhost/tray_windows.go index 2301149..cb1e6e3 100644 --- a/internal/trayhost/tray_windows.go +++ b/internal/trayhost/tray_windows.go @@ -4,8 +4,11 @@ package trayhost import ( "runtime" + "sync" "unsafe" + "vpnclient/internal/dockicon" + "golang.org/x/sys/windows" ) @@ -90,6 +93,12 @@ type msg struct { Pt point } +var ( + trayMu sync.Mutex + trayNID *notifyIconData + trayLastUp *bool +) + func start(appName string, h Hooks) bool { if h.Quit == nil { return false @@ -102,6 +111,35 @@ func start(appName string, h Hooks) bool { return true } +// SetConnectedIcon updates the tray glyph (sea-wave N when connected). +func SetConnectedIcon(connected bool) { + trayMu.Lock() + nid := trayNID + if trayLastUp != nil && *trayLastUp == connected { + trayMu.Unlock() + return + } + v := connected + trayLastUp = &v + trayMu.Unlock() + if nid == nil { + return + } + icon := dockicon.StatusIcon(connected) + if icon == 0 { + return + } + trayMu.Lock() + if trayNID == nil { + trayMu.Unlock() + return + } + trayNID.HIcon = icon + trayNID.UFlags = nifMessage | nifIcon | nifTip + procShellNotify.Call(nimModify, uintptr(unsafe.Pointer(trayNID))) + trayMu.Unlock() +} + func runTray(appName string, h Hooks) { className, _ := windows.UTF16PtrFromString("NavisTrayClass") title, _ := windows.UTF16PtrFromString(appName) @@ -155,17 +193,27 @@ func runTray(appName string, h Hooks) { return } - icon, _, _ := procLoadIcon.Call(0, uintptr(idiApplication)) + icon := dockicon.StatusIcon(false) + if icon == 0 { + h, _, _ := procLoadIcon.Call(0, uintptr(idiApplication)) + icon = windows.Handle(h) + } var nid notifyIconData nid.CbSize = uint32(unsafe.Sizeof(nid)) nid.Hwnd = windows.Handle(hwnd) nid.UID = 1 nid.UFlags = nifMessage | nifIcon | nifTip nid.UCallbackMessage = wmTray - nid.HIcon = windows.Handle(icon) + nid.HIcon = icon tip, _ := windows.UTF16FromString(appName) copy(nid.SzTip[:], tip) procShellNotify.Call(nimAdd, uintptr(unsafe.Pointer(&nid))) + trayMu.Lock() + trayNID = &nid + trayMu.Unlock() + if h.IsUp != nil { + SetConnectedIcon(h.IsUp()) + } var m msg for { @@ -176,6 +224,9 @@ func runTray(appName string, h Hooks) { procTranslate.Call(uintptr(unsafe.Pointer(&m))) procDispatch.Call(uintptr(unsafe.Pointer(&m))) } + trayMu.Lock() + trayNID = nil + trayMu.Unlock() procShellNotify.Call(nimDelete, uintptr(unsafe.Pointer(&nid))) } diff --git a/internal/update/update.go b/internal/update/update.go index e1bc63b..a57c643 100644 --- a/internal/update/update.go +++ b/internal/update/update.go @@ -22,7 +22,7 @@ const CurrentVersion = "3.8.2" // BuildNumber is the monotonic build within CurrentVersion (Windows FileVersion 4th part, // macOS CFBundleVersion suffix, Android versionCode low digits). Bump on every release build. -const BuildNumber = 6 +const BuildNumber = 7 // DefaultManifestURL is the update feed (hosted in the project git repo). const DefaultManifestURL = "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/update.json" diff --git a/server/update.json b/server/update.json index edf3b9d..18320cb 100644 --- a/server/update.json +++ b/server/update.json @@ -1,16 +1,16 @@ { "version": "3.8.2", - "notes": "Navis 3.8.2+6: поиск/фильтр серверов и хоткеи (/, Ctrl+F, Space/Enter, Esc); Windows: закрытие окна = скрытие в трей, «Открыть»/Выход из трея; фоновый EnsureCore активного протокола; SSE /api/events для мгновенного статуса + poll 12 с как safety net.", + "notes": "Navis 3.8.2+7: Windows — иконка в панели задач и трее цвета морской волны при подключении (как Dock на macOS); при отключении возвращается обычная.", "platform": "windows-amd64", "os": "windows", "arch": "amd64", "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "aab618d452f8eddd7cc4400770e53a6a9c456b2bc6093190dd21f1b3fbcd765e", + "sha256": "b7902251aeb671946dbfe7ab2f3470dea82cf29cc0cd68e42f6632653ea90605", "mandatory": false, "platforms": { "windows-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "aab618d452f8eddd7cc4400770e53a6a9c456b2bc6093190dd21f1b3fbcd765e", + "sha256": "b7902251aeb671946dbfe7ab2f3470dea82cf29cc0cd68e42f6632653ea90605", "os": "windows", "arch": "amd64" }, diff --git a/versioninfo.json b/versioninfo.json index aa4a74a..784e8f8 100644 --- a/versioninfo.json +++ b/versioninfo.json @@ -4,13 +4,13 @@ "Major": 3, "Minor": 8, "Patch": 2, - "Build": 6 + "Build": 7 }, "ProductVersion": { "Major": 3, "Minor": 8, "Patch": 2, - "Build": 6 + "Build": 7 }, "FileFlagsMask": "3f", "FileFlags": "00", @@ -21,12 +21,12 @@ "StringFileInfo": { "CompanyName": "EvilFox", "FileDescription": "Navis — VPN client (Naive / Hy2 / AWG / VLESS / VMess / Trojan)", - "FileVersion": "3.8.2.6", + "FileVersion": "3.8.2.7", "InternalName": "Navis", "LegalCopyright": "Copyright (c) EvilFox", "OriginalFilename": "Navis.exe", "ProductName": "Navis", - "ProductVersion": "3.8.2.6", + "ProductVersion": "3.8.2.7", "Comments": "Open-source VPN/proxy client. https://evilfox.win/" }, "VarFileInfo": {