From 1281eaf62e54ccacdd2589f696732453b0863620 Mon Sep 17 00:00:00 2001 From: Giovanni Ferri Date: Sun, 2 Aug 2026 18:00:00 +0100 Subject: [PATCH] Monochrome menu bar icon, click-to-open, and a real .app bundle Three real gaps found live testing bpm-tray: 1. The tray icon used the full-colour app logo via SetIcon - rendered as an illegible colour blob at actual menu bar size. Generated a proper macOS 'template' icon (assets/logo-template.png): flat black silhouette with real alpha, cropped and downsized from the same source logo via ImageMagick (fuzzy black->transparent, recolour remaining pixels solid black, trim, resize). Wired via SetTemplateIcon so macOS tints it correctly for light/dark menu bars. 2. Clicking the icon required navigating a dropdown menu just to open the web UI. fyne.io/systray's SetOnTapped fires directly on left-click instead of showing the menu (confirmed by reading systray_darwin.go: tappedLeft, if set, is called instead of show_menu()). Wired left-click to open the browser directly; right-click still shows the menu (status + Quit), since SetOnSecondaryTapped is left unset. 3. Running bpm-tray meant launching a bare Mach-O binary from a terminal - no Spotlight, no Launchpad, no /Applications entry. Added packaging/macos/Info.plist.template and a release.yml step that assembles a real BpmTray.app (LSUIElement so it never shows a Dock icon, proper AppIcon.icns built from the same logo via sips+iconutil) and zips it per-arch as an additional release asset, alongside the existing raw binary tarball. Verified locally end-to-end: built, zipped, unzipped fresh, launched via `open BpmTray.app` (the same path Finder/Spotlight take), confirmed the server responds and the process never appears as a foreground/Dock app. --- .github/workflows/release.yml | 26 +++++++++++++++++++++- cmd/bpm-tray/main.go | 19 ++++++++++++++-- internal/webui/assets.go | 10 +++++++++ internal/webui/assets/logo-template.png | Bin 0 -> 1827 bytes packaging/macos/Info.plist.template | 28 ++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 internal/webui/assets/logo-template.png create mode 100644 packaging/macos/Info.plist.template diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ce00e92..01011c9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,7 +53,31 @@ jobs: CGO_ENABLED=1 GOARCH=$arch go build -o "$dir/bpm-tray" ./cmd/bpm-tray tar -C "$dir" -czf "dist-tray/bpm-tray_${{ github.ref_name }}_darwin_${arch}.tar.gz" bpm-tray done + - name: Build AppIcon.icns from the embedded logo + run: | + mkdir -p /tmp/icon.iconset + for size in 16 32 128 256 512; do + sips -z $size $size internal/webui/assets/logo.png --out /tmp/icon.iconset/icon_${size}x${size}.png >/dev/null + double=$((size*2)) + sips -z $double $double internal/webui/assets/logo.png --out /tmp/icon.iconset/icon_${size}x${size}@2x.png >/dev/null + done + iconutil -c icns /tmp/icon.iconset -o /tmp/AppIcon.icns + - name: Assemble BpmTray.app for both architectures + run: | + version="${{ github.ref_name }}" + version="${version#v}" + for arch in arm64 amd64; do + app="dist-tray/darwin_${arch}/BpmTray.app" + mkdir -p "$app/Contents/MacOS" "$app/Contents/Resources" + sed "s/VERSION_PLACEHOLDER/${version}/" packaging/macos/Info.plist.template > "$app/Contents/Info.plist" + cp "dist-tray/darwin_${arch}/bpm-tray" "$app/Contents/MacOS/bpm-tray" + cp /tmp/AppIcon.icns "$app/Contents/Resources/AppIcon.icns" + plutil -lint "$app/Contents/Info.plist" + ditto -c -k --sequesterRsrc --keepParent "$app" "dist-tray/BpmTray_${{ github.ref_name }}_darwin_${arch}.zip" + done - name: Attach to the release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: gh release upload ${{ github.ref_name }} dist-tray/*.tar.gz --clobber + run: | + gh release upload ${{ github.ref_name }} dist-tray/*.tar.gz --clobber + gh release upload ${{ github.ref_name }} dist-tray/*.zip --clobber diff --git a/cmd/bpm-tray/main.go b/cmd/bpm-tray/main.go index 86f33ad..fa950e8 100644 --- a/cmd/bpm-tray/main.go +++ b/cmd/bpm-tray/main.go @@ -30,9 +30,20 @@ func main() { } func onReady() { - systray.SetIcon(webui.LogoPNG) + // Template icon: a flat black silhouette with real alpha, so macOS tints + // it correctly for the light/dark menu bar - the full-colour app icon + // (via plain SetIcon) rendered as an illegible colour blob at actual + // menu bar size. regularIconBytes (LogoPNG) is only a fallback for + // platforms without template-icon support; bpm-tray only ships for + // macOS, so it's never actually shown. + systray.SetTemplateIcon(webui.LogoTemplatePNG, webui.LogoPNG) systray.SetTooltip("Bambu Profile Manager") + // A single click opens the web UI directly - no menu to navigate first. + // Right-click (the systray default when SetOnTapped is set) still shows + // the menu below, so Quit stays reachable. + systray.SetOnTapped(openBrowser) + mOpen := systray.AddMenuItem("Open bpm", "Open the web UI in your browser") mStatus := systray.AddMenuItem("Starting…", "") mStatus.Disable() @@ -51,7 +62,7 @@ func onReady() { for { select { case <-mOpen.ClickedCh: - exec.Command("open", "http://localhost"+addr).Start() + openBrowser() case <-mQuit.ClickedCh: systray.Quit() return @@ -60,6 +71,10 @@ func onReady() { }() } +func openBrowser() { + exec.Command("open", "http://localhost"+addr).Start() +} + // startServer wires up the same server cmd/bpm's `serve` builds, but with // zero flags: db and backups live under ~/Library/Application Support/bpm, // and Bambu Studio's real directories are auto-discovered rather than diff --git a/internal/webui/assets.go b/internal/webui/assets.go index 5cc4340..ebf3177 100644 --- a/internal/webui/assets.go +++ b/internal/webui/assets.go @@ -14,6 +14,16 @@ import ( //go:embed assets/logo.png var LogoPNG []byte +// LogoTemplatePNG is a monochrome (black-on-transparent) silhouette derived +// from LogoPNG, cropped and downsized for use as a macOS menu bar "template" +// image — those get tinted/inverted automatically for the light/dark menu +// bar, but only if they're a flat black shape with real alpha, not a full +// colour icon (real gap found live: SetIcon with the full-colour app icon +// rendered as a tiny, illegible blob at menu bar size). +// +//go:embed assets/logo-template.png +var LogoTemplatePNG []byte + // handleLogo serves LogoPNG over HTTP. Aggressively cacheable: the file // only changes on a new build, and go:embed gives every build a fresh byte // slice anyway. diff --git a/internal/webui/assets/logo-template.png b/internal/webui/assets/logo-template.png new file mode 100644 index 0000000000000000000000000000000000000000..c8c0907c5cb87d9f3dde1f3e3e350a647624c965 GIT binary patch literal 1827 zcmZ{lc{JOJ7RP^01&!u0v^CVOrBO=+(WXo-iPn~BEGa=J#F7-1r>WM~%AnC|tH@Yd zyT)3k6}3c&T1t^oYwSAIQpA)NZ9Fyk$010G@Nw>5vC-4%7j*a#kC4BVzZ1KiGrMECWD(RK`E~HS zKtIixK(0R9qUV)ZZ#f_PI3P_czH{xxNa4OAjoam{;M}5K(~aO4Z5}r5^A@0p@%=e# z8VgzS3rkn2ulm8E+}EI33%|VRy;iwSY~IMC_e@y_RH!Yrs}bT+-qCfny?$uj+%db< zGN@`mZJ263W>p?+qJ3TesC0(`(oPm!B1+exTV>zNdsi@aypGfMSy39w%~py^Z?NjK z@OPI1D1X1{SS^g zrqjP7i;Qpf+SQ${F!3`PJ7NZ+^OiG?c;3Ov-=9K=-dnofdJ)a!k;Vj#?%;P_gb=qLAot{NRbsHxWCTTy(w z8uW{C8*2Pb)&GluQ9RlPC8FU?}DY~sB>ou;o)ce_l_`5nFw20eWc-l{pooDc>(45I5Brf;!B zLxP49GIA3hrLqRGTykA+`stp^hyrwTwdB21?k5C1r?jw?4f{kwnME)>- z8Lr|+cj)rImKn5lBmc@Akw=pJSY8@mqW3A0A`)qh-+B1UB#drV=X-l`G~!YEk!3)| zEhW{i!fck%VR7y5Y#E+4rO^mc#e^{i1OqR2&aLCJi*|qBSqXgejh(4Ypt3Ahn`Q8@ z8;P&aRLOfn+zLzezTge4+G>1p=4H;Tvs@)4aggbJ|X}7o#^XE5#X{;iP$2T<* z&J*&rwU&JwMeN$^TPXD-hBgI~duhC#uE2mIoz)cA@PTIvPZoM?GU$e_ghFWe(YV+w z{R;{rC0O+sGM8I)elAK&N8OCW@(!LLBzB52F2x2o75f2lsnHDK8r5XoiS?)m}6dRe&=7K+?QEK*z!TyF6R!M zz?YSoc2=&z`elOdFp+JMtU}FgLfGy!{9(1%@u9b!Y?NT{s-frmI2mWK=(2_V@i{(G z^J_<)f7Hn@8Pvf^WHC=tR&#E$ym1;bU5F{fMP!o_P-juvh5~(~3ICjHV@B|cjXLqF z5rdch)t1AAanZUsQYe0nF{id!@;0>2b+DvN{#K7%H26M$yGWP+iPH-4X;xMXHK%M} zo*fKf6@98sUH`46lJRE`7<8Ow(%r6Xl8ZPe#D9p2yc;ze-o*tKmF$*8JE4>LI!RIe z9=O$qsVUQS3EB^{h|gSqav@n|3C3%f<1kI#ArD1JEqUO>I+LvU`=Mb6X{NGJwUl3T zh4m6IBeBS;&8xC!yIjXuR!m)~Pxg4G^*ORfd1sZ*|DGdnDO3{l9=wzJm)<1Cd%+Cn z+Um2J7L2k*iptVov^tckpyQKGf{Z4cgO5d2V4u=u*KVAg!#BF$4DOTLNXs@nAV`Sj zjDYs|F7IUx5wqJgf3GH-cR^1sC4b9x3q(J!JYV3mPU~|aA-wCn_Lxz>-JdGNIeTck z1>DVpoqiNOM$BDM_O(yaH+dUornk-6f!42a1JG@S@c8}t{(AFK$L37^TcaX|HVix* zs%H^;Z|jW+w+WiT&62y7y2o{ABzWdU&d&!(8PV?!Y;mG``L|)0pEp0XKAxQ96_gq) zv{eck@WgDm&{Y~mKl)V%hty7}m7bGeVLdmuf8X2Ok8U9~s4UEEHu--${~CR;Go+@f zz12v#-}W6k(X%hRj11cqe~FVcj0er%mll8`g$IZQK)?}51Na#OIKs^si83=qnITWZ z;V3vf4+8c4FCa9`pAs1P|AD_QzgiFjr~Z>bql5-rr}>6b|CcdC!T%kiPTU}h8NeQc KMOWJpfBhR0^;mBJ literal 0 HcmV?d00001 diff --git a/packaging/macos/Info.plist.template b/packaging/macos/Info.plist.template new file mode 100644 index 0000000..99722bd --- /dev/null +++ b/packaging/macos/Info.plist.template @@ -0,0 +1,28 @@ + + + + + CFBundleName + Bpm Tray + CFBundleDisplayName + Bpm Tray + CFBundleIdentifier + uk.syscode-labs.bpm-tray + CFBundleExecutable + bpm-tray + CFBundleIconFile + AppIcon + CFBundlePackageType + APPL + CFBundleShortVersionString + VERSION_PLACEHOLDER + CFBundleVersion + VERSION_PLACEHOLDER + LSMinimumSystemVersion + 11.0 + LSUIElement + + NSHighResolutionCapable + + +