From c78f4ac231b09ebc8cb5d744cdb16a84c07f06aa Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 5 Jul 2026 01:33:34 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=86=95=20Add=20macOS=20application=20pack?= =?UTF-8?q?aging=20support=20with=20custom=20Dock=20icon=20integration=20a?= =?UTF-8?q?nd=20build=20documentation.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 +++- Info.plist | 28 ++++++++++++++++++ README.md | 22 +++++++++++++++ build.sh | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++ icon.go | 16 +++++++++++ logo.jpeg | Bin 0 -> 3086 bytes main.go | 2 ++ 7 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 Info.plist create mode 100755 build.sh create mode 100644 icon.go create mode 100644 logo.jpeg diff --git a/.gitignore b/.gitignore index e85e0b7..dfce26f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ README.md.tinyenc secure-editor-go +secure-editor-go.app +logo.icns +logo.iconset # ---> macOS # General @@ -8,7 +11,7 @@ secure-editor-go .LSOverride # Icon must end with two \r -Icon +Icon # Thumbnails ._* diff --git a/Info.plist b/Info.plist new file mode 100644 index 0000000..1945d77 --- /dev/null +++ b/Info.plist @@ -0,0 +1,28 @@ + + + + + CFBundleName + Secure Editor + CFBundleDisplayName + Secure Editor + CFBundleIdentifier + ink.hatter.secure-editor-go + CFBundleVersion + 0.1.1 + CFBundleShortVersionString + 0.1.1 + CFBundlePackageType + APPL + CFBundleExecutable + secure-editor-go + CFBundleIconFile + logo.icns + CFBundleIconName + logo + LSMinimumSystemVersion + 10.13 + NSHighResolutionCapable + + + diff --git a/README.md b/README.md index fb8d326..4802331 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,28 @@ go mod tidy go build ``` +## 打包为 macOS 应用(让 Dock 显示 LOGO) + +在 macOS 上,独立的命令行二进制在 Dock 中只会显示通用图标,`app.SetIcon` / `window.SetIcon` 无法改变 Dock 图标。要让 Dock 显示 `logo.jpeg`,需要将程序打包成 `.app` 应用包并附带 `.icns` 图标: + +```shell +./build.sh # 编译并生成 secure-editor-go.app(含由 logo.jpeg 转换的 logo.icns) +./build.sh --binary-only # 仅编译裸二进制 +``` + +运行(仍以命令行参数方式调用包内可执行文件): + +```shell +./secure-editor-go.app/Contents/MacOS/secure-editor-go aes-256-gcm +``` + +`build.sh` 使用 macOS 自带的 `sips` 与 `iconutil` 把 `logo.jpeg` 生成多尺寸的 `logo.icns`,再按 `Info.plist` 组装 `.app` 包。重新打包后若 Dock 图标未刷新,可执行: + +```shell +/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -f secure-editor-go.app +killall Dock +``` + ## 功能特性 - **AES-256-GCM 加解密**:以 `aes-256-gcm` 算法解密文件内容并在编辑器中展示,保存时重新加密写回(保留原文件权限位)。 diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..a02f967 --- /dev/null +++ b/build.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +# Build secure-editor-go and package it as a macOS .app bundle so the +# Dock shows the bundled logo (iconutil/sips convert logo.jpeg -> logo.icns). +# +# Usage: +# ./build.sh # build binary + assemble .app bundle +# ./build.sh --binary-only # build only the plain binary, no bundle +# +# Run the app: +# ./secure-editor-go.app/Contents/MacOS/secure-editor-go aes-256-gcm + +set -euo pipefail + +APP_NAME="secure-editor-go" +APP_BUNDLE="${APP_NAME}.app" +THIS_DIR="$(cd "$(dirname "$0")" && pwd)" +cd "$THIS_DIR" + +BINARY_ONLY=0 +if [ "${1-}" = "--binary-only" ]; then + BINARY_ONLY=1 +fi + +# 1. Build the Go binary. +echo "==> building binary" +go build -o "$APP_NAME" . + +if [ "$BINARY_ONLY" -eq 1 ]; then + echo "==> done (binary only): $APP_NAME" + exit 0 +fi + +# 2. Generate logo.icns from logo.jpeg using macOS tooling. +if [ -f logo.jpeg ]; then + echo "==> generating logo.icns" + rm -rf logo.iconset logo.icns + mkdir -p logo.iconset + + gen() { + # $1 = pixel size, $2 = iconset file name (without .png) + sips -s format png -z "$1" "$1" logo.jpeg --out "logo.iconset/$2.png" >/dev/null + } + + gen 16 icon_16x16 + gen 32 icon_16x16@2x + gen 32 icon_32x32 + gen 64 icon_32x32@2x + gen 128 icon_128x128 + gen 256 icon_128x128@2x + gen 256 icon_256x256 + gen 512 icon_256x256@2x + gen 512 icon_512x512 + gen 1024 icon_512x512@2x + + iconutil -c icns logo.iconset -o logo.icns + rm -rf logo.iconset +else + echo "warning: logo.jpeg not found, bundle will have no icon" >&2 +fi + +# 3. Assemble the .app bundle. +echo "==> assembling $APP_BUNDLE" +rm -rf "$APP_BUNDLE" +mkdir -p "$APP_BUNDLE/Contents/MacOS" +mkdir -p "$APP_BUNDLE/Contents/Resources" +cp "$APP_NAME" "$APP_BUNDLE/Contents/MacOS/$APP_NAME" +[ -f logo.icns ] && cp logo.icns "$APP_BUNDLE/Contents/Resources/logo.icns" +cp Info.plist "$APP_BUNDLE/Contents/Info.plist" + +# Refresh LaunchServices' view of the bundle so the icon takes effect. +touch "$APP_BUNDLE" +touch "$APP_BUNDLE/Contents/Info.plist" + +# 4. (Optional) register with LaunchServices so the icon is picked up. +LSREGISTER="/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister" +if [ -x "$LSREGISTER" ]; then + "$LSREGISTER" "$APP_BUNDLE" >/dev/null 2>&1 || true +fi + +echo "==> done: $APP_BUNDLE" +echo " run: $APP_BUNDLE/Contents/MacOS/$APP_NAME aes-256-gcm " diff --git a/icon.go b/icon.go new file mode 100644 index 0000000..70e5ab7 --- /dev/null +++ b/icon.go @@ -0,0 +1,16 @@ +package main + +import ( + _ "embed" + + "fyne.io/fyne/v2" +) + +//go:embed logo.jpeg +var logoBytes []byte + +// appIcon returns the bundled logo as a Fyne resource, used as the +// application/window icon shown while the editor loads. +func appIcon() fyne.Resource { + return fyne.NewStaticResource("logo.jpeg", logoBytes) +} diff --git a/logo.jpeg b/logo.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..4eb6460afb17fc2daa1d757b58e2a01e8080a6b3 GIT binary patch literal 3086 zcmZ8i3m{W{8$a7fXf6}EgwpmhHfkF3Vgalt^>SrJ|4uT_k0gNRrHD?wU%h ztWqu^OueQg91{%#f*9L#W^3?wd7R%T9(?4cz zJs`Om_zHZ5fK&jmBnTo2;=TqHcmN?Fo&f&{2)s#9NC3in_@@K_27w@(Hj9XW1R!fK zfWQy|LCH-!_n6rogGs4ih#{9NWpwN>#U_&<9zKX9U;@lbtOeyw7tSX9pl&lH+a+}NBr`(EXF}q) zkX}(|sUu6z1Nuqm`Dz7Yzm#w1^heT*owNE~r^`<1%KM2@jqPVqEq%VSkng3DZ1u2W zLe+wam{5~pRk|TDIy4{$?2!4M&GDVYe{tQ;e&m+=?P}p-z2aR>d)J-qA%!ot248sE zfA((!8;g9Ad>x;KM{n7y#63sF32~`pQhxT8Y-Cv#!Kk(z|JZjXbZEQ)_2WVy;k=%+ z%@12v#gWV#_iXA`NaLobO*iuReHG9C91y?RFilC=@1trFg1$m?IAlwVus}ZU%cI{> z;QUOZ6^I(fh1p+esGdAZoUI%2OBXqpTa((=D*cNw@|y1^dTwtfBj86q>%9F?`GxRd z!_!sxha=r_G3u!=E^6m=w!nR8&!SJWSK5<0QuMC*Cfp=6p;n8I3dwFO#vh83Yj|VD zY0_D-&H=NA`&qf4ytCD<@IIyr{+E9_r8Tgaj#jO9fo^l5AU%QR!6zlSy>0b|_f9$XsN%&6DFK>umzh6NEPvx}Zf>toL3{bT zsNWkqklCPHim_F~x#>4+rbN*Qf{V)7c@OV!e#GCh-MWk(tQIVjNw%u0A=GFY^kNSC zJIi%kwZNp)rxs1y06>2Ec17@x3D^-@#)D_Vs}qsQ$%$?xv=(OEm)bK*3y#+xjdO4& zx`388oGu5Rjrb5(M7XOP`?+$qR5gpO_RK!bAS{P{;ds}U6V5#g(yWhcbL?}wcPpPH z^i2W7MU9gK6Ou`^>kYGG9^wQ_$~ zMoIOs-E!x5Qy&00u+K2DGSP!||LISe^FHYr;w?)3CWkhbi3J_<5NkDWHd-{^g2k4< zOPL2@Z_Zk@Eoi5tE@x?1e=IupE`B!1iIx@MHSknj-$+-F3xxHD20rdXj|7f;r%UuF zf#8>xTt;~5*p2k#F|UHer3hagq7oT-J~)W|SL>)$Mjjzva+)}oOo2l!*RR64x=jV~r>*<5iTf#Fhf3g3jyU7#1Iyq)+iWy2pkjT%On1han~f1?&a5zxB>TR+gvj{rhST%gurP`#s7 zALX+!Kp$l6LXoO2&m2lDJYY0bad*^w%st>8$O})l>v&q{mG0l4T)t;sJdrpN1=ADw z4O1iCgb$O3sX4=kxfzp}2Jg}U3xi*$q6Cu>I5Q`B5&LP7?@2$ZwIgDjHiz!z0yM$5 zVYLSa)xC;TM+$is&skFGypQ2bw_a+5#^_M`ad^zN zZ7#D^+=0THQx0s6+^gN+B1cU5W|=$p(C*ylDf+8eRBPBDbS4*2NOY`fF_b#ZLCL?L z#rcu`R>!hvNUFZBT9a4iha~gGs1TRVydQ;&ozwn{+H)FhrJI{2D5Svl`bejrL#MV+ z?mh4Jp|Kd76n>9^9zR-yFz2yBQQ_(v?=YFfI}s_h*p@ejHq zpPbSQNx;D?tT0wY$qfab007%jMddg%`^>M7T?FWEc5hFuCnFl$J*l7*p&{%@9(tSm znDKcSQ+n9eHY|vk((t78l0X`~kRO*v60tNUk5MrNyS$HUipx@aR}2&|zdMEZ^iT8q zol#dRn!bI%VUo8fy2+XxpU}L{toK9TUcEi$`s^iz$MN!&$d?)WLJSw2N-FYdDyLt} zPU?l=;AAFR*v|~5jcPjSCXC4eKrPQFBYGY(lVHV&rd#HLk+qF4w666W{~yCU8`r8L zRf8u;+#qsaKw-6ORaVyUC#vd=ln>3k&ug8{>%b|5g$B(pV~uRty8Aye-CuMi(^W36 zm3otv7X9#wYNNk3#q%V;u(HvBSp{fTUgcvZv~8>~@5cRf<>wdp-@^>g?H$zNvm(d~6aMDp92c;)_{8HHU*xoA!Q!#L6e==*zFOwFj6dPZQf*pU zVqsmoHKHSv_qPekub-Z_V*lO6`xZ-%_$VUB`?9O+mRm8ExfS1zjCRE%@aX;O$Qq*s zxtFG_1z-EdSL|e1i~1cSHRe7-ylN~*8#q?Zt|mF878n@d339k`AG{3sb#7rQuhAZq z5r7HuS{x5&sx+_dJQ|)owSzOna-(