[v3] macOS Dock Service - #4451
Conversation
WalkthroughAdds a new cross-platform Dock service API (Go) with platform implementations, removes the legacy badge package, updates generated TypeScript frontend bindings/examples to use the dock API, adds a dock example app with build/packaging assets, and adds documentation and an unreleased changelog entry. Changes
Sequence Diagram(s)sequenceDiagram
participant App as Application
participant DockSvc as DockService (Go facade)
participant Impl as platformDock (darwinDock / windowsDock / linuxDock)
participant OS as Native OS/UI
Note over App,DockSvc: App invokes dock/badge API
App->>DockSvc: HideAppIcon()
DockSvc->>Impl: HideAppIcon()
alt macOS
Impl->>OS: AppKit: hide dock icon (main thread)
else Windows
Impl->>OS: Windows: update taskbar/hicon (badge logic)
else Linux
Impl->>OS: no-op / unsupported
end
Note over App,DockSvc: Badge operations
App->>DockSvc: SetCustomBadge(label, options)
DockSvc->>Impl: SetCustomBadge(label, options)
Impl->>OS: create/update badge (platform-specific)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (5)
docs/src/content/docs/learn/dock.mdx (2)
21-21: Fix comment typo.The comment mentions "badge service" but should refer to "dock service".
-// Create a new badge service +// Create a new dock service
73-76: Fix table formatting inconsistency.The API reference table has inconsistent column alignment and formatting compared to the Service Management table above.
### Dock Operations -| Method | Description | -|--------------------------------|-------------------------------------------------------------| +| Method | Description | +|--------------------------------|-------------------------------------------------| | `HideAppIcon()` | Hides the app icon from the macOS Dock | -| `ShowAppIcon()` | Shows the app icon in the macOS Dock. | +| `ShowAppIcon()` | Shows the app icon in the macOS Dock |v3/pkg/services/dock/dock_darwin.go (2)
24-24: Fix comment inconsistency.The comment mentions "Badge Service" but should refer to "Dock Service".
-// Creates a new Badge Service. +// Creates a new Dock Service.
31-37: Consider implementing proper lifecycle management.The Startup and Shutdown methods currently return nil without any implementation. While this works for the current dock functionality, consider if any initialization or cleanup is needed for future enhancements.
v3/pkg/services/dock/dock.go (1)
18-18: Fix comment inconsistency.The comment mentions "notifications service" but should refer to "dock service".
-// Service represents the notifications service +// DockService represents the dock service
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
docs/src/content/docs/learn/dock.mdx(1 hunks)v3/pkg/services/dock/dock.go(1 hunks)v3/pkg/services/dock/dock_darwin.go(1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: popaprozac
PR: wailsapp/wails#4098
File: v3/pkg/services/notifications/notifications_darwin.go:39-46
Timestamp: 2025-03-23T00:41:39.612Z
Learning: For the macOS notifications implementation in Wails, an early panic is used when the bundle identifier check fails rather than returning an error, because the Objective-C code would crash later anyway. The panic provides clear instructions to developers about bundling and signing requirements.
Learnt from: popaprozac
PR: wailsapp/wails#4256
File: v2/internal/frontend/desktop/linux/notifications.go:27-28
Timestamp: 2025-04-29T23:54:07.488Z
Learning: In Wails v2, unlike v3-alpha which has a `ServiceShutdown` method for services, there is no standardized teardown pattern for frontend implementations. When implementing features that require cleanup (like goroutines or resources), add explicit cleanup methods (e.g., `CleanupNotifications()`) that handle resource release, context cancellation, and connection closure.
Learnt from: popaprozac
PR: wailsapp/wails#4256
File: v2/internal/frontend/desktop/linux/notifications.go:27-28
Timestamp: 2025-04-29T23:54:07.488Z
Learning: In Wails v2, unlike v3-alpha which has a `ServiceShutdown` method for services, there is no standardized teardown pattern for frontend implementations. When implementing features that require cleanup (like goroutines or resources), add explicit cleanup methods (e.g., `CleanupNotifications()`) that handle resource release, context cancellation, and connection closure.
Learnt from: popaprozac
PR: wailsapp/wails#4098
File: v3/pkg/services/notifications/notifications.go:46-55
Timestamp: 2025-03-24T20:22:56.233Z
Learning: In the notifications package, initialization of the `Service` struct is handled through platform-specific `New()` functions in each implementation file (darwin, windows, linux) rather than a generic constructor in the main package file.
Learnt from: popaprozac
PR: wailsapp/wails#4098
File: v3/pkg/services/notifications/notifications.go:46-55
Timestamp: 2025-03-24T20:22:56.233Z
Learning: In the notifications package, initialization of the `Service` struct is handled through platform-specific `New()` functions in each implementation file (darwin, windows, linux) rather than a generic constructor in the main package file. Each platform implementation follows a singleton pattern using `notificationServiceOnce.Do()` and creates a global `NotificationService` variable that's accessed through a thread-safe `getNotificationService()` function.
docs/src/content/docs/learn/dock.mdx (1)
Learnt from: stavros-k
PR: #3917
File: docs/src/content/docs/index.mdx:13-18
Timestamp: 2024-12-02T22:14:41.158Z
Learning: The documentation page /learn/services exists in docs/src/content/docs/learn/services.md.
v3/pkg/services/dock/dock_darwin.go (2)
Learnt from: popaprozac
PR: #4098
File: v3/pkg/services/notifications/notifications.go:46-55
Timestamp: 2025-03-24T20:22:56.233Z
Learning: In the notifications package, initialization of the Service struct is handled through platform-specific New() functions in each implementation file (darwin, windows, linux) rather than a generic constructor in the main package file.
Learnt from: popaprozac
PR: #4098
File: v3/pkg/services/notifications/notifications.go:46-55
Timestamp: 2025-03-24T20:22:56.233Z
Learning: In the notifications package, initialization of the Service struct is handled through platform-specific New() functions in each implementation file (darwin, windows, linux) rather than a generic constructor in the main package file. Each platform implementation follows a singleton pattern using notificationServiceOnce.Do() and creates a global NotificationService variable that's accessed through a thread-safe getNotificationService() function.
v3/pkg/services/dock/dock.go (5)
Learnt from: popaprozac
PR: #4098
File: v3/pkg/services/notifications/notifications.go:46-55
Timestamp: 2025-03-24T20:22:56.233Z
Learning: In the notifications package, initialization of the Service struct is handled through platform-specific New() functions in each implementation file (darwin, windows, linux) rather than a generic constructor in the main package file.
Learnt from: popaprozac
PR: #4098
File: v3/pkg/services/notifications/notifications.go:46-55
Timestamp: 2025-03-24T20:22:56.233Z
Learning: In the notifications package, initialization of the Service struct is handled through platform-specific New() functions in each implementation file (darwin, windows, linux) rather than a generic constructor in the main package file. Each platform implementation follows a singleton pattern using notificationServiceOnce.Do() and creates a global NotificationService variable that's accessed through a thread-safe getNotificationService() function.
Learnt from: popaprozac
PR: #4256
File: v2/internal/frontend/desktop/linux/notifications.go:27-28
Timestamp: 2025-04-29T23:54:07.488Z
Learning: In Wails v2, unlike v3-alpha which has a ServiceShutdown method for services, there is no standardized teardown pattern for frontend implementations. When implementing features that require cleanup (like goroutines or resources), add explicit cleanup methods (e.g., CleanupNotifications()) that handle resource release, context cancellation, and connection closure.
Learnt from: popaprozac
PR: #4256
File: v2/internal/frontend/desktop/linux/notifications.go:27-28
Timestamp: 2025-04-29T23:54:07.488Z
Learning: In Wails v2, unlike v3-alpha which has a ServiceShutdown method for services, there is no standardized teardown pattern for frontend implementations. When implementing features that require cleanup (like goroutines or resources), add explicit cleanup methods (e.g., CleanupNotifications()) that handle resource release, context cancellation, and connection closure.
Learnt from: leaanthony
PR: #4128
File: v3/examples/gin-service/services/gin_service.go:67-85
Timestamp: 2025-04-18T05:57:34.711Z
Learning: In Wails services (like GinService), initializing the app field in ServiceStartup rather than in the constructor is an acceptable pattern, as ServiceStartup is guaranteed to be called before any HTTP requests are handled.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: Run Go Tests v3 (macos-latest, 1.24)
- GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
- GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
🔇 Additional comments (5)
docs/src/content/docs/learn/dock.mdx (1)
52-63: Excellent behavioral documentation.The best practices section provides valuable information about macOS-specific behaviors when the dock icon is hidden, including Command+Tab behavior, window management, and quitting behavior. This will help developers understand the implications of using this feature.
v3/pkg/services/dock/dock_darwin.go (1)
8-14: LGTM: Correct Objective-C implementation.The cgo implementation correctly uses NSApplicationActivationPolicy to control dock icon visibility:
NSApplicationActivationPolicyAccessoryhides the dock iconNSApplicationActivationPolicyRegularshows the dock iconThis is the standard approach for macOS dock icon control.
v3/pkg/services/dock/dock.go (3)
9-16: Well-designed platform abstraction interface.The platformDock interface cleanly separates platform-specific behavior with appropriate lifecycle methods and dock control operations. This follows established Wails service patterns.
38-46: LGTM: Proper method delegation.The public methods correctly delegate to the platform-specific implementation while maintaining clear documentation about macOS-specific behavior.
24-26: No changes needed to ServiceName
The return value"github.com/wailsapp/wails/v3/services/dock"matches the established pattern used by other v3 services (e.g. notifications, badge). Please ignore this suggestion.Likely an incorrect or invalid review comment.
|
I think we need to add stubs for windows and linux so that if the program is cross compiled it just ignores this instead of throwing build errors. |
|
Yes totally! I wanted to first look at Windows/Linux if there is related functionality and what other features could end up in here. I think it might work the same on Windows. App icon visibility can be toggled in taskbar. So this got me thinking, what other features. I think the badging we did recently and this should actually go in the same service, interacting with the dock/taskbar app icon. In the future click handlers, or attention behavior (bounce/flash), or? Thoughts? |
|
Yeah I was thinking the same TBH. I think wrapping it all up in a Dock service is best and we can remove Badge. 🙏 |
|
@popaprozac - I've found that hiding the icon also means the window disappears (when in a bundle). Did you find this? Also, the call needds to be run on the main thread or you'll get random crashes. Will push up some changes for your review 👍 |
|
@leaanthony thanks for the info! I was mitigating the main thread stuff while bundling this with badging, I had noticed the same. I hadn't noticed the windows hiding when bundled. We bundle the app in dev now don't we? Away from my computer for a few more days but back 🔜 |
|
@leaanthony back this week and should have time to get this done by EOW. Sorry for the delay! |
|
@leaanthony re-requesting your review. Docs updated, every call done on the main thread. I couldn't reproduce the window hiding anymore, maybe a quirk of not dispatching on main? All the examples are updated so you can give it a shot. I tested on macOS 26 and 12.x. I think it's good to consolidate this now and in a separate effort I am going to look at Windows/Linux |
There was a problem hiding this comment.
Actionable comments posted: 19
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/models.ts (1)
22-41: Align TS BadgeOptions defaults with documented Windows defaultsThe Go backend’s BadgeOptions struct (v3/pkg/services/dock/dock.go) does not normalize zero‐values or empty strings into platform defaults. As a result, the current TS constructor will send:
- TextColour = RGBA(0,0,0,0) → fully transparent text
- BackgroundColour = RGBA(0,0,0,0) → fully transparent badge
- FontName = "" → unknown font
- FontSize/SmallFontSize = 0 → invisible text
To ensure badges render correctly on Windows, update the defaults in the TS constructor to match the documented defaults:
- TextColour → new color$0.RGBA({ R: 255, G: 255, B: 255, A: 255 })
- BackgroundColour → new color$0.RGBA({ R: 255, G: 0, B: 0, A: 255 })
- FontName → "segoeuib.ttf"
- FontSize → 18
- SmallFontSize → 14
Example diff in v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/models.ts:
- if (!("TextColour" in $$source)) { - this["TextColour"] = new color$0.RGBA(); - } + if (!("TextColour" in $$source)) { + this["TextColour"] = new color$0.RGBA({ R: 255, G: 255, B: 255, A: 255 }); + } - if (!("BackgroundColour" in $$source)) { - this["BackgroundColour"] = new color$0.RGBA(); - } + if (!("BackgroundColour" in $$source)) { + this["BackgroundColour"] = new color$0.RGBA({ R: 255, G: 0, B: 0, A: 255 }); + } - if (!("FontName" in $$source)) { - this["FontName"] = ""; - } - if (!("FontSize" in $$source)) { - this["FontSize"] = 0; - } - if (!("SmallFontSize" in $$source)) { - this["SmallFontSize"] = 0; - } + if (!("FontName" in $$source)) { + this["FontName"] = "segoeuib.ttf"; + } + if (!("FontSize" in $$source)) { + this["FontSize"] = 18; + } + if (!("SmallFontSize" in $$source)) { + this["SmallFontSize"] = 14; + }Repeat analogous changes in:
- v3/examples/dock/frontend/bindings/.../models.ts
- v3/examples/badge-custom/frontend/bindings/.../models.ts
If these files are generated, please adjust the generator to emit the platform defaults instead of zero‐values.
v3/pkg/services/dock/dock_windows.go (1)
315-366: Consider refactoring duplicated badge icon creation logic.The
createBadgeIconWithTextfunction exists in two versions with nearly identical logic - one as a method and one as a standalone function. This violates the DRY principle.Consider refactoring to eliminate duplication by having the method version delegate to the standalone function:
func (w *windowsDock) createBadgeIconWithText(label string) (w32.HICON, error) { - fontPath := w.fontManager.FindFontOrDefault(w.badgeOptions.FontName) - if fontPath == "" { - return w.createBadgeIcon() - } - - fontBytes, err := os.ReadFile(fontPath) - if err != nil { - return w.createBadgeIcon() - } - - ttf, err := opentype.Parse(fontBytes) - if err != nil { - return w.createBadgeIcon() - } - - fontSize := float64(w.badgeOptions.FontSize) - if len(label) > 1 { - fontSize = float64(w.badgeOptions.SmallFontSize) - } - - // Get DPI of the current screen - screen := w32.GetDesktopWindow() - dpi := w32.GetDpiForWindow(screen) - - face, err := opentype.NewFace(ttf, &opentype.FaceOptions{ - Size: fontSize, - DPI: float64(dpi), - Hinting: font.HintingFull, - }) - if err != nil { - return w.createBadgeIcon() - } - defer face.Close() - - d := &font.Drawer{ - Dst: w.badgeImg, - Src: image.NewUniform(w.badgeOptions.TextColour), - Face: face, - } - - textWidth := d.MeasureString(label).Ceil() - d.Dot = fixed.P((w.badgeSize-textWidth)/2, int(float64(w.badgeSize)/2+fontSize/2)) - d.DrawString(label) - - var buf bytes.Buffer - if err := png.Encode(&buf, w.badgeImg); err != nil { - return 0, err - } - - return w32.CreateSmallHIconFromImage(buf.Bytes()) + return createBadgeIconWithText(w, label, w.badgeSize, w.badgeImg, w.badgeOptions) }
♻️ Duplicate comments (1)
v3/examples/badge-custom/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/models.ts (1)
22-41: Same defaulting concern as the other BadgeOptions bindingThe default zero/empty values carry the same risk of invisible badges or zero font sizes unless the backend normalizes. See my comment in v3/examples/badge/.../models.ts for options and a verification script.
🧹 Nitpick comments (55)
v3/examples/dock/README.md (4)
11-13: Add language identifiers to fenced code blocks (markdownlint MD040).Helps syntax highlighting and satisfies linters.
- ``` + ```bash wails3 dev ```- ``` + ```bash wails3 build ```- ``` + ```bash go run . ```Also applies to: 19-21, 33-35
29-29: Tighten wording: avoid redundant path phrasing.“Visit the
examplesdirectory in thev3/examplesdirectory” is repetitive. Simplify to a single path.-1. **Check out the examples**: The best way to learn is by example. Visit the `examples` directory in the `v3/examples` directory to see various sample applications. +1. **Check out the examples**: The best way to learn is by example. Visit `v3/examples` to see various sample applications.
4-5: Add a brief “About this example” section with platform notes.Since this is the Dock example, call out what it demonstrates and the current platform status. This reduces confusion for users on non-macOS platforms and aligns with the PR’s cross-platform plan.
-## Getting Started +## About this example + +This example demonstrates the new Dock service: +- Hide/show the app icon in the macOS Dock +- Badge management (where supported) + +Platform notes: +- macOS: Fully implemented; Dock operations must run on the main thread (handled by the framework). +- Windows/Linux: Stubs in place; functionality may be limited or no-op pending future platform work. + +## Getting Started
1-1: Optional consistency: use “Wails v3” in prose, keepwails3for commands.House style often uses “Wails v3” in text while CLI is
wails3. Not required, but improves clarity.-# Welcome to Your New Wails3 Project! +# Welcome to Your New Wails v3 Project!-Congratulations on generating your Wails3 application! This README will guide you through the next steps to get your project up and running. +Congratulations on generating your Wails v3 application! This README will guide you through the next steps to get your project up and running.-## Exploring Wails3 Features +## Exploring Wails v3 Features-3. **Explore the documentation**: Visit the [Wails3 documentation](https://v3.wails.io/) for in-depth guides and API references. +3. **Explore the documentation**: Visit the [Wails v3 documentation](https://v3.wails.io/) for in-depth guides and API references.-Happy coding with Wails3! If you encounter any issues or have questions, don't hesitate to consult the documentation or reach out to the Wails community. +Happy coding with Wails v3! If you encounter any issues or have questions, don't hesitate to consult the documentation or reach out to the Wails community.Also applies to: 3-3, 25-25, 39-39, 59-59
v3/examples/dock/build/linux/nfpm/scripts/preremove.sh (1)
1-2: Make preremove script a safe no-op and align shebang with other scriptsCurrently this script is empty and uses /bin/bash while postinstall uses /bin/sh. Recommend:
- Use /bin/sh for portability/consistency.
- Mirror cache updates (desktop/mime) on removal so menus/mime caches are refreshed after uninstall.
Apply this diff:
-#!/bin/bash +#!/bin/sh + +# Ensure menu/mime caches are refreshed after removal of .desktop and MIME entries +set -e + +if command -v update-desktop-database >/dev/null 2>&1; then + echo "Updating desktop database (preremove)..." + update-desktop-database -q /usr/share/applications +else + echo "Warning: update-desktop-database command not found. Desktop database may be stale." >&2 +fi + +if command -v update-mime-database >/dev/null 2>&1; then + echo "Updating MIME database (preremove)..." + update-mime-database -n /usr/share/mime +else + echo "Warning: update-mime-database command not found. MIME database may be stale." >&2 +fi + +exit 0v3/pkg/services/dock/font.go (2)
23-35: Harden Windows font directory discovery (robust env fallbacks)If WINDIR/LOCALAPPDATA aren’t set (service contexts, unusual shells), filepath.Join will yield relative paths (“Fonts”) which is incorrect. Add robust fallbacks for both variables.
Apply this diff:
func NewFontManager() *FontManager { - return &FontManager{ - fontCache: make(map[string]string), - fontDirs: []string{ - filepath.Join(os.Getenv("windir"), "Fonts"), - filepath.Join(os.Getenv("localappdata"), "Microsoft", "Windows", "Fonts"), - }, - registryPaths: []string{ - `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts`, - }, - } + // Resolve Windows directories with sensible fallbacks + winDir := os.Getenv("WINDIR") + if winDir == "" { + winDir = os.Getenv("SystemRoot") + } + if winDir == "" { + winDir = `C:\Windows` + } + + localAppData := os.Getenv("LOCALAPPDATA") + if localAppData == "" { + if home, err := os.UserHomeDir(); err == nil && home != "" { + localAppData = filepath.Join(home, "AppData", "Local") + } + } + + return &FontManager{ + fontCache: make(map[string]string), + fontDirs: []string{ + filepath.Join(winDir, "Fonts"), + filepath.Join(localAppData, "Microsoft", "Windows", "Fonts"), + }, + registryPaths: []string{ + `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts`, + }, + } }
121-130: Use filepath.IsAbs for registry values instead of backslash heuristicSome font registry values may use forward slashes or absolute paths. Checking for “\” can misclassify absolute paths. filepath.IsAbs handles Windows semantics.
Apply this diff:
- // Check if this registry entry corresponds to our font + // Check if this registry entry corresponds to our font valueLower := strings.ToLower(value) if strings.HasSuffix(valueLower, fontFileLower) { - // If it's a relative path, assume it's in the default font directory - if !strings.Contains(value, "\\") { + // If it's a relative path, assume it's in the default font directory + if !filepath.IsAbs(value) { value = filepath.Join(defaultDir, value) } if fileExists(value) { return value, true }v3/examples/dock/frontend/tsconfig.json (1)
1-20: Tighten TS settings for examples and align module resolution with modern bundlersOptional improvements for DX and type-safety:
- moduleResolution: "Bundler" plays nicer with Vite/ESM.
- Enable noImplicitAny and noUnusedParameters to catch mistakes early (even in examples).
- Include generated bindings if they live under frontend/bindings to ensure the editor/TS server sees them.
Apply this diff:
"compilerOptions": { "target": "ESNext", "useDefineForClassFields": true, "module": "ESNext", "lib": ["ESNext", "DOM"], - "moduleResolution": "Node", + "moduleResolution": "Bundler", "strict": true, "resolveJsonModule": true, "isolatedModules": true, "esModuleInterop": true, "noEmit": true, "noUnusedLocals": true, - "noUnusedParameters": false, - "noImplicitAny": false, + "noUnusedParameters": true, + "noImplicitAny": true, "noImplicitReturns": true, "skipLibCheck": true }, - "include": ["src"] + "include": ["src", "bindings"]If your bindings are under a different directory (e.g., wailsjs), swap “bindings” accordingly.
v3/examples/dock/build/windows/wails.exe.manifest (1)
12-12: Use canonical casing for dpiAwareness values.Windows docs commonly use "PerMonitorV2,PerMonitor". While likely case-insensitive, canonical casing avoids surprises with tooling that parses this field.
- <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness> <!-- falls back to per-monitor if per-monitor v2 is not supported --> + <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2,PerMonitor</dpiAwareness> <!-- falls back to per-monitor if per-monitor v2 is not supported -->v3/examples/dock/frontend/package.json (1)
12-18: Avoid "latest" and pin to a semver range; confirm Node >= 18 for Vite 5.
- Pinning prevents unexpected breakages when a new major of @wailsio/runtime is published.
- Vite 5 requires Node 18+. Ensure CI/dev envs meet this.
- "dependencies": { - "@wailsio/runtime": "latest" - }, + "dependencies": { + "@wailsio/runtime": "3.x" + },If your environment supports it, consider updating TypeScript to a TS 5.x release to align better with Vite 5.
v3/examples/dock/build/linux/appimage/build.sh (1)
13-13: Don’t use -r when copying a regular file.
-ris for directories and is unnecessary here. Use a simple copy (or-pto preserve mode).-cp -r "${APP_BINARY}" "${APP_DIR}/usr/bin/" +cp "${APP_BINARY}" "${APP_DIR}/usr/bin/"v3/examples/dock/frontend/bindings/image/color/models.ts (2)
42-45: Safer JSON parsing in createFrom to avoid runtime exceptionsGuard JSON.parse to prevent crashes on malformed input and fall back to defaults.
- static createFrom($$source: any = {}): RGBA { - let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; - return new RGBA($$parsedSource as Partial<RGBA>); - } + static createFrom($$source: any = {}): RGBA { + let $$parsedSource = $$source; + if (typeof $$source === 'string') { + try { + $$parsedSource = JSON.parse($$source); + } catch { + // Fallback to defaults on invalid JSON + $$parsedSource = {}; + } + } + return new RGBA($$parsedSource as Partial<RGBA>); + }If you want, I can update the generator to emit this pattern for all model factories.
21-37: Optional: clamp and coerce channel values to 0–255 (and numbers)Given RGBA semantics (each channel is 8-bit and alpha-premultiplied), normalizing input improves robustness if callers pass strings/floats/out-of-range values.
Outside the selected range, add and use a small normalizer:
// Add inside the class: private static $$clampByte(v: unknown): number { const n = typeof v === 'number' ? v : Number(v); if (!Number.isFinite(n)) return 0; return Math.max(0, Math.min(255, Math.round(n))); }Then, at the end of the constructor (after Object.assign), normalize:
this["R"] = RGBA.$$clampByte(this["R"]); this["G"] = RGBA.$$clampByte(this["G"]); this["B"] = RGBA.$$clampByte(this["B"]); this["A"] = RGBA.$$clampByte(this["A"]);Happy to generate a PR to the bindings generator if preferred.
v3/examples/dock/build/windows/msix/template.xml (1)
50-53: Reminder: signing is required for MSIXEmpty CertificatePath will block installation on end-user machines. Keep it empty for the example, but ensure CI/release pipelines set a valid certificate when producing artifacts.
v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/models.ts (1)
46-57: createFrom: consider defensive parsing for nested typesIf createFrom receives strings or partials, defensive parsing reduces surprises.
Minimal enhancement inside createFrom after parsing:
if ("TextColour" in $$parsedSource) { $$parsedSource["TextColour"] = $$createField0_0($$parsedSource["TextColour"]); } if ("BackgroundColour" in $$parsedSource) { $$parsedSource["BackgroundColour"] = $$createField1_0($$parsedSource["BackgroundColour"]); }This already exists (good). Ensure upstream uses this factory rather than manual object construction when deserializing.
docs/src/content/docs/learn/dock.mdx (4)
133-146: Call out macOS main-thread requirement to prevent crashesThe implementation needed main-thread dispatching for macOS API calls. Add an explicit note so users avoid calling these too early or from the wrong context.
<TabItem label="macOS" icon="fa-brands:apple"> On macOS: - The dock icon can be **hidden** and **shown** - Badges are displayed directly on the dock icon - Badge options are **not customizable** (any options passed to `NewWithOptions`/`SetCustomBadge` are ignored) - The standard macOS dock badge styling is used and automatically adapts to appearance - Label overflow is handled by the system - Providing an empty label displays a default badge of "●" + - Note: Dock operations are executed on the macOS main thread under the hood. Avoid invoking them before the application event loop is running.
89-96: Clarify platform behavior of “default badge” exampleEmpty label shows a default dot on macOS. On Windows, the overlay may render as an empty badge unless normalized by the backend. Clarify this to set expectations.
-// Set a default badge +// Set a default badge (macOS) dockService.SetBadge("")
178-185: Minor copy edit for readabilityAdd “the” to “Command+Tab switcher.”
- - The app won't appear in Command+Tab switcher + - The app won't appear in the Command+Tab switcher
202-219: Fix table markdown so it renders correctlyThe separator rows don’t match table syntax and may not render. Use standard Markdown table delimiters.
-### Service Management -| Method | Description | -|--------------------------------------------|-------------------------------------------------------| -| `New()` | Creates a new dock service | -| `NewWithOptions(options BadgeOptions)` | Creates a new dock service with custom badge options (Windows only; options are ignored on macOS and Linux) | +### Service Management +| Method | Description | +| -------------------------------------- | ----------------------------------------------------------------------------------------------- | +| `New()` | Creates a new dock service | +| `NewWithOptions(options BadgeOptions)` | Creates a new dock service with custom badge options (Windows only; ignored on macOS and Linux) | @@ -### Dock Operations -| Method | Description | -|--------------------------------|-------------------------------------------------------------| -| `HideAppIcon()` | Hides the app icon from the macOS Dock (macOS only) | -| `ShowAppIcon()` | Shows the app icon in the macOS Dock (macOS only) | +### Dock Operations +| Method | Description | +| ---------------- | ------------------------------------------------ | +| `HideAppIcon()` | Hides the app icon from the macOS Dock (macOS) | +| `ShowAppIcon()` | Shows the app icon in the macOS Dock (macOS) | @@ -### Badge Operations -| Method | Description | -|---------------------------------------------------|------------------------------------------------------------| -| `SetBadge(label string) error` | Sets a badge with the specified label | -| `SetCustomBadge(label string, options BadgeOptions) error` | Sets a badge with the specified label and custom styling options (Windows only) | -| `RemoveBadge() error` | Removes the badge from the application icon | +### Badge Operations +| Method | Description | +| ---------------------------------------------------- | ------------------------------------------------------------------------ | +| `SetBadge(label string) error` | Sets a badge with the specified label | +| `SetCustomBadge(label string, options BadgeOptions)` | Sets a badge with the label and custom styling options (Windows only) | +| `RemoveBadge() error` | Removes the badge from the application icon |v3/examples/dock/build/darwin/Taskfile.yml (1)
45-46: Consider preserving architecture-specific binaries for debugging.The universal build task removes the architecture-specific binaries immediately after creating the universal binary. Consider keeping them for debugging purposes or make their removal optional.
- - lipo -create -output "{{.BIN_DIR}}/{{.APP_NAME}}" "{{.BIN_DIR}}/{{.APP_NAME}}-amd64" "{{.BIN_DIR}}/{{.APP_NAME}}-arm64" - - rm "{{.BIN_DIR}}/{{.APP_NAME}}-amd64" "{{.BIN_DIR}}/{{.APP_NAME}}-arm64" + - lipo -create -output "{{.BIN_DIR}}/{{.APP_NAME}}" "{{.BIN_DIR}}/{{.APP_NAME}}-amd64" "{{.BIN_DIR}}/{{.APP_NAME}}-arm64" + # Optional: Remove architecture-specific binaries after creating universal binary + # - rm "{{.BIN_DIR}}/{{.APP_NAME}}-amd64" "{{.BIN_DIR}}/{{.APP_NAME}}-arm64"v3/pkg/services/dock/dock_darwin.go (1)
75-76: Inconsistent empty string handling for badge.When the label is empty, the function sets a default bullet character "●" instead of removing the badge. This behavior differs from the Windows implementation where an empty string shows an empty badge icon. Consider making this behavior consistent across platforms.
For consistency with other platforms, consider removing the badge when the label is empty:
- } else { - cLabel = C.CString("●") // Default badge character - } - C.setBadge(cLabel) + } + if label != "" { + C.setBadge(cLabel) + } else { + C.setBadge(nil) + }v3/pkg/services/dock/dock_windows.go (1)
74-77: Clarify platform limitations in comments.The comments mention "macOS Dock" in Windows-specific code, which could be confusing. Consider updating the comments to accurately reflect Windows taskbar limitations.
-// HideAppIcon hides the app icon in the macOS Dock. +// HideAppIcon is not implemented on Windows. +// Note: Windows taskbar icon visibility is controlled by the shell and cannot be hidden programmatically. func (w *windowsDock) HideAppIcon() { // No-op: researching Windows options } -// ShowAppIcon shows the app icon in the macOS Dock. +// ShowAppIcon is not implemented on Windows. +// Note: Windows taskbar icons are shown by default when a window is created. func (w *windowsDock) ShowAppIcon() { // No-op: researching Windows options }Also applies to: 79-82
v3/examples/dock/frontend/src/main.ts (1)
6-12: Consider adding error handling for Dock service calls.The
ShowAppIcon()andHideAppIcon()methods likely return promises that could reject. Consider handling potential errors to improve user experience.showButton.addEventListener('click', () => { - DockService.ShowAppIcon(); + DockService.ShowAppIcon().catch((error) => { + console.error('Failed to show app icon:', error); + }); }); hideButton.addEventListener('click', () => { - DockService.HideAppIcon(); + DockService.HideAppIcon().catch((error) => { + console.error('Failed to hide app icon:', error); + }); });v3/examples/badge/main.go (1)
78-78: Remove unnecessary empty line.There's an extra empty line after the closing brace that can be removed for consistency.
} }) -v3/examples/badge-custom/frontend/src/main.ts (2)
13-17: Remove redundant casts and handle empty labels gracefully.
- labelElement is already typed as HTMLInputElement, so further casts are redundant.
- UX improvement: When the input is empty or whitespace, call RemoveBadge instead of setting an empty badge.
Apply this diff:
setCustomButton.addEventListener('click', () => { console.log("click!") - let label = (labelElement as HTMLInputElement).value + const label = labelElement.value SetCustomBadge(label, { BackgroundColour: RGBA.createFrom({ R: 0, G: 255, B: 255, A: 255, }), FontName: "arialb.ttf", // System font FontSize: 16, SmallFontSize: 10, TextColour: RGBA.createFrom({ R: 0, G: 0, B: 0, A: 255, }), }); }) setButton.addEventListener('click', () => { - let label = (labelElement as HTMLInputElement).value - SetBadge(label); + const label = labelElement.value.trim(); + if (label === "") { + RemoveBadge(); + } else { + SetBadge(label); + } }); setButtonUsingGo.addEventListener('click', () => { - let label = (labelElement as HTMLInputElement).value + const label = labelElement.value void Events.Emit({ name: "set:badge", data: label, }) })Also applies to: 35-38, 44-50
56-58: Tighten the type of the time event payload.Minor polish: add a typed payload and destructure the data.
-Events.On('time', (time: {data: any}) => { - timeElement.innerText = time.data; -}); +type TimeEvent = { data: string }; +Events.On('time', ({ data }: TimeEvent) => { + timeElement.innerText = data; +});v3/examples/badge/frontend/src/main.ts (3)
11-15: Handle empty labels by removing the badge instead of setting an empty one.Small UX improvement for the example.
setButton.addEventListener('click', () => { - let label = (labelElement as HTMLInputElement).value - SetBadge(label); + const label = labelElement.value.trim(); + if (label === "") { + RemoveBadge(); + } else { + SetBadge(label); + } });
21-26: Remove redundant cast and keep emission minimal.labelElement is already typed; also using const is preferable.
setButtonUsingGo.addEventListener('click', () => { - let label = (labelElement as HTMLInputElement).value + const label = labelElement.value void Events.Emit({ name: "set:badge", data: label, }) })
32-34: Tighten the type of the time event payload.-Events.On('time', (time: {data: any}) => { - timeElement.innerText = time.data; -}); +type TimeEvent = { data: string }; +Events.On('time', ({ data }: TimeEvent) => { + timeElement.innerText = data; +});v3/examples/dock/frontend/index.html (2)
13-18: Add href/rel/target to anchors for accessibility and semantics.Anchors without href are not keyboard-focusable. Adding href preserves semantics and improves accessibility. rel prevents opener leaks for external URLs.
- <a data-wml-openURL="https://wails.io"> + <a data-wml-openURL="https://wails.io" href="https://wails.io" target="_blank" rel="noopener noreferrer"> <img src="/wails.png" class="logo" alt="Wails logo"/> </a> - <a data-wml-openURL="https://www.typescriptlang.org/"> + <a data-wml-openURL="https://www.typescriptlang.org/" href="https://www.typescriptlang.org/" target="_blank" rel="noopener noreferrer"> <img src="/typescript.svg" class="logo vanilla" alt="Typescript logo"/> </a>
23-25: Explicit button type for future-proofing.Not strictly necessary here, but setting type="button" avoids accidental form submissions if the markup evolves.
- <button class="btn" id="show">Show</button> - <button class="btn" id="hide">Hide</button> + <button class="btn" id="show" type="button">Show</button> + <button class="btn" id="hide" type="button">Hide</button>v3/examples/dock/Taskfile.yml (1)
30-34: Optional: add a precondition to check the wails3 CLI is installed.Prevents confusing failures when running dev locally.
dev: summary: Runs the application in development mode + preconditions: + - sh: command -v wails3 >/dev/null 2>&1 + msg: "wails3 CLI not found in PATH. Please install Wails v3 CLI." cmds: - wails3 dev -config ./build/config.yml -port {{.VITE_PORT}}v3/examples/dock/main.go (1)
20-23: Comment is misleading (mentions a goroutine that does not exist)Keep the example comments accurate.
Apply this diff:
-// main function serves as the application's entry point. It initializes the application, creates a window, -// and starts a goroutine that emits a time-based event every second. It subsequently runs the application and -// logs any error that might occur. +// main initializes the application, registers the Dock service, creates a window, +// then runs the application and logs any error that might occur.v3/examples/dock/build/linux/nfpm/nfpm.yaml (1)
43-45: Fix YAML lint issues: trailing spaces and missing newline at EOFClean up trailing whitespace and ensure a newline at EOF.
Apply this diff:
- - # Arch Linux packages (WebKit 4.1) + # Arch Linux packages (WebKit 4.1) archlinux: @@ -# - not-bar -# changelog: "changelog.yaml" \ No newline at end of file +# - not-bar +# changelog: "changelog.yaml" +Also applies to: 73-73
v3/examples/dock/build/config.yml (1)
63-63: Add a newline at EOFKeeps linters happy and avoids diff churn.
Apply this diff:
- - name: My Other Data \ No newline at end of file + - name: My Other Data +v3/pkg/services/dock/dock_linux.go (1)
39-70: Consider surfacing "unsupported" for clarity (optional)No-ops are fine, but you may want to optionally log at startup that Dock features are not supported on Linux to aid debugging, or return a sentinel error from badge methods if that’s acceptable for the API.
Example (if you choose to log):
func (l *linuxDock) Startup(ctx context.Context, options application.ServiceOptions) error { - // No-op: Linux doesn't have standardized dock/badge support + // No-op: Linux doesn't have standardized dock/badge support + // log.Println("[dock] Linux platform: Dock features are not supported") return nil }v3/examples/dock/frontend/public/style.css (4)
22-28: Fix @font-face weight/file mismatch for InterYou reference Inter-Medium.ttf but declare font-weight: 400 (Regular). This can cause incorrect font selection. Either switch to the correct weight (500) or use the Regular file.
Apply this diff:
@font-face { font-family: "Inter"; font-style: normal; - font-weight: 400; + font-weight: 500; src: local(""), url("./Inter-Medium.ttf") format("truetype"); }
1-20: Avoid disabling text selection globallySetting user-select: none at the root prevents copy/paste across the page, which is unfriendly and may affect input elements depending on the browser. Scope this behavior to specific non-interactive elements instead.
Apply this diff to remove the global user-select:
:root { @@ - user-select: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; }Optionally, if you still want to prevent selection in specific areas, add a class-based rule elsewhere:
.noselect { user-select: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; }
56-59: Remove duplicate.resultblock
.resultis defined twice (Lines 56-59 and 103-108). Consolidate to a single definition.Apply this diff:
-.result { - height: 20px; - line-height: 20px; -}
103-108: Consolidate.resultstyles hereMove the height and line-height properties from the removed block to this consolidated one.
Apply this diff:
.result { + height: 20px; + line-height: 20px; margin: 1.5rem auto; text-align: center; }v3/examples/badge-custom/main.go (3)
5-5: Remove redundant blank import of embed
_ "embed"is unnecessary and may trigger linters; the typed importembedis sufficient for go:embed and embed.FS usage.Apply this diff:
-import ( - "embed" - _ "embed" +import ( + "embed"
77-83: Guard event data type assertion to avoid panic
event.Data.(string)will panic if the frontend sends a non-string. Use a safe assertion.Apply this diff:
app.Event.On("set:badge", func(event *application.CustomEvent) { - text := event.Data.(string) - err := dockService.SetBadge(text) + text, ok := event.Data.(string) + if !ok { + log.Printf("set:badge event payload not a string: %#v", event.Data) + return + } + err := dockService.SetBadge(text) if err != nil { log.Fatal(err) } })
32-38: BadgeOptions are ignored on macOS and LinuxOn darwin/linux builds,
NewWithOptionssimply callsNew()and drops the passedBadgeOptions(seedock_darwin.go:46–48anddock_linux.go:25–27). Only Windows currently applies custom colours/fonts.• If you need badge styling on macOS/Linux, update
v3/pkg/services/dock/dock_darwin.goand
v3/pkg/services/dock/dock_linux.go
to store and respectBadgeOptionsin the underlying implementations.
• Otherwise, add a note inv3/examples/badge-custom/main.goclarifying that custom colours and fonts are only honoured on Windows.Happy to help draft the platform-specific changes if you’d like.
v3/examples/dock/build/windows/msix/app_manifest.xml (1)
8-19: Placeholders in Identity/PropertiesPublisher/DisplayName/Description are placeholders. If this manifest is used beyond a template, ensure they’re replaced or parameterized via your packaging process.
v3/examples/dock/build/windows/Taskfile.yml (2)
28-31: GOARCH default looks odd; set an explicit default
'{{.ARCH | default ARCH}}'likely resolves to the literal "ARCH" if unset. Provide a real default (e.g., amd64).Apply this diff:
env: GOOS: windows CGO_ENABLED: 0 - GOARCH: '{{.ARCH | default ARCH}}' + GOARCH: '{{.ARCH | default "amd64"}}' PRODUCTION: '{{.PRODUCTION | default "false"}}'
65-67: ARG_FLAG depends on ARCH; ensure it has a sane defaultWith the GOARCH default fixed to "amd64", this will resolve deterministically. If you want to be defensive, guard empty ARCH too.
Optionally:
- ARG_FLAG: '{{if eq .ARCH "amd64"}}AMD64{{else}}ARM64{{end}}' + ARG_FLAG: '{{if or (eq .ARCH "amd64") (eq .ARCH "")}}AMD64{{else}}ARM64{{end}}'v3/examples/dock/build/windows/nsis/project.nsi (1)
91-93: Create Start Menu shortcut in a subfolder to avoid clutter, and ensure consistent command casing.Placing the shortcut directly under $SMPROGRAMS can clutter the Start Menu. Create a dedicated folder for the product and put the shortcut inside. Also use the standard CreateShortCut command for consistency.
Apply this diff:
- CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}" - CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}" + CreateDirectory "$SMPROGRAMS\${INFO_PRODUCTNAME}" + CreateShortCut "$SMPROGRAMS\${INFO_PRODUCTNAME}\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}" + CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"v3/examples/dock/build/linux/Taskfile.yml (1)
104-116: Double-check working directory for generate:dotdesktop.dir: build may not point to the intended directory when this Taskfile is invoked directly vs via an included parent. Since your command uses absolute output paths already, consider dropping the dir override to reduce ambiguity.
If this task is sometimes executed from the project root, leaving dir unset (inheriting the caller’s working directory) should be fine here. Do you want me to flatten these paths to be resilient regardless of invocation point?
v3/pkg/services/dock/dock.go (1)
39-43: ServiceName string likely should include '/pkg' to reflect the package path.Returning "github.com/wailsapp/wails/v3/services/dock" omits "/pkg". The generator often uses ServiceName to derive call IDs; inconsistency can desync bindings.
If you haven’t intentionally overridden the name, align it with the package path and regenerate bindings:
- return "github.com/wailsapp/wails/v3/services/dock" + return "github.com/wailsapp/wails/v3/pkg/services/dock"If you intentionally chose the shorter name, please confirm the TS bindings were generated with this exact string so the method IDs match at runtime.
v3/examples/dock/build/windows/nsis/wails_tools.nsh (1)
145-179: Ensure the WebView2 bootstrapper is present alongside the script before invoking makensis.The macro includes 'File "MicrosoftEdgeWebview2Setup.exe"'. If you run makensis manually (as documented in project.nsi), this file must exist under the nsis working directory.
If you want to make manual builds smoother, add a brief note to the top-level README for this example on where to obtain/place MicrosoftEdgeWebview2Setup.exe (or instruct to run 'wails build --nsis' once to stage it).
v3/examples/dock/build/Taskfile.yml (5)
28-33: Avoid watching node_modules/dist; narrow frontend sources to reduce needless rebuildsUsing
"**/*"underdir: frontendwill includenode_modulesanddist, leading to slow or constantly-dirty task status. Narrow the sources to typical app inputs.Apply this diff:
- sources: - - "**/*" + sources: + - "src/**/*" + - "public/**/*" + - "index.html" + - "vite.config.*" + - "tsconfig*.json" + - "package.json"Note: If your Task version supports negated globs, you could alternatively keep
"**/*"and add!node_modules/**and!dist/**. The above is the safest cross-version approach.
18-22: Usenpm --versionfor the precondition check
npm versionreads package metadata and can behave unexpectedly.npm --versionis a simpler existence check.- preconditions: - - sh: npm version + preconditions: + - sh: npm --version msg: "Looks like npm isn't installed. Npm is part of the Node installer: https://nodejs.org/en/download/"
63-73: Add a CLI precondition forwails3Ensure the icons task fails fast with a helpful message if the CLI isn’t installed.
generates: - "darwin/icons.icns" - "windows/icon.ico" + preconditions: + - sh: command -v wails3 >/dev/null 2>&1 + msg: "wails3 CLI not found. Install via: go install github.com/wailsapp/wails/v3/cmd/wails3@latest" cmds: - wails3 generate icons -input appicon.png -macfilename darwin/icons.icns -windowsfilename windows/icon.ico
74-81: Provide a default port fordev:frontendMake
VITE_PORToptional by giving it a sane default.dev:frontend: summary: Runs the frontend in development mode dir: frontend deps: - task: install:frontend:deps + vars: + VITE_PORT: '{{.VITE_PORT | default "5173"}}' cmds: - npm run dev -- --port {{.VITE_PORT}} --strictPort
82-87: Separate app and binary names; set defaults and check CLIUsing the app name (with spaces) as the binary name is fragile across platforms. Provide a distinct default
BINARY_NAMEand ensurewails3exists.update:build-assets: summary: Updates the build assets dir: build + preconditions: + - sh: command -v wails3 >/dev/null 2>&1 + msg: "wails3 CLI not found. Install via: go install github.com/wailsapp/wails/v3/cmd/wails3@latest" + vars: + APP_NAME: '{{.APP_NAME | default "Dock Example"}}' + BINARY_NAME: '{{.BINARY_NAME | default "dock-example"}}' cmds: - - wails3 update build-assets -name "{{.APP_NAME}}" -binaryname "{{.APP_NAME}}" -config config.yml -dir . + - wails3 update build-assets -name "{{.APP_NAME}}" -binaryname "{{.BINARY_NAME}}" -config config.yml -dir .
| <key>NSHighResolutionCapable</key> | ||
| <string>true</string> |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Fix NSHighResolutionCapable type (must be boolean, not string).
Same issue as the release plist; use a boolean.
- <key>NSHighResolutionCapable</key>
- <string>true</string>
+ <key>NSHighResolutionCapable</key>
+ <true/>📝 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.
| <key>NSHighResolutionCapable</key> | |
| <string>true</string> | |
| <key>NSHighResolutionCapable</key> | |
| <true/> |
🤖 Prompt for AI Agents
In v3/examples/dock/build/darwin/Info.dev.plist around lines 22 to 23, the
NSHighResolutionCapable key is set with a string value ("true"); change it to a
boolean by replacing the <string>true</string> element with a <true/> boolean
element so the plist uses the correct type.
| <key>NSHighResolutionCapable</key> | ||
| <string>true</string> |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Fix NSHighResolutionCapable type (must be boolean, not string).
Using a string here may cause the key to be ignored by system parsers.
- <key>NSHighResolutionCapable</key>
- <string>true</string>
+ <key>NSHighResolutionCapable</key>
+ <true/>🤖 Prompt for AI Agents
In v3/examples/dock/build/darwin/Info.plist around lines 22 to 23, the
NSHighResolutionCapable value is declared as a string ("true") but must be a
boolean; replace the string element with the proper plist boolean element so the
key is encoded as a boolean true (use the plist boolean form rather than a
string) to ensure system parsers recognize it.
| GOOS: darwin | ||
| CGO_ENABLED: 1 | ||
| GOARCH: '{{.ARCH | default ARCH}}' | ||
| CGO_CFLAGS: "-mmacosx-version-min=10.15" | ||
| CGO_LDFLAGS: "-mmacosx-version-min=10.15" | ||
| MACOSX_DEPLOYMENT_TARGET: "10.15" | ||
| PRODUCTION: '{{.PRODUCTION | default "false"}}' |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify CGO configuration for Apple Silicon compatibility.
The CGO_CFLAGS and CGO_LDFLAGS are set to macOS 10.15, which is reasonable for compatibility. However, since the PR mentions testing on macOS 26 (likely a typo for macOS 12.x), ensure these flags are compatible with newer macOS versions and Apple Silicon Macs.
🌐 Web query:
What are the recommended CGO_CFLAGS and CGO_LDFLAGS settings for macOS universal binaries targeting both Intel and Apple Silicon with minimum deployment target of macOS 10.15?
💡 Result:
Short answer — the keys are: pass an explicit sysroot, pass the macOS minimum-version flag at compile+link time, and target both archs (usually by building per-arch and combining with lipo). Example recommended flags:
-
For the Intel (x86_64) slice
CGO_CFLAGS="-isysroot $(xcrun --sdk macosx --show-sdk-path) -mmacosx-version-min=10.15 -arch x86_64"
CGO_LDFLAGS="-isysroot $(xcrun --sdk macosx --show-sdk-path) -mmacosx-version-min=10.15 -arch x86_64" -
For the Apple Silicon (arm64) slice
CGO_CFLAGS="-isysroot $(xcrun --sdk macosx --show-sdk-path) -mmacosx-version-min=11.0 -arch arm64"
CGO_LDFLAGS="-isysroot $(xcrun --sdk macosx --show-sdk-path) -mmacosx-version-min=11.0 -arch arm64"
Why:
- Use xcrun --sdk macosx --show-sdk-path for the SDK sysroot so you compile against the proper macOS SDK. (stackoverflow.com)
- -mmacosx-version-min must be supplied at both compile and link time so the binary’s minimum-OS load command is written correctly. For Intel you can target 10.15; arm64 slices must target macOS 11.0 or later. (stackoverflow.com, thebreakfastpost.com)
- -arch x86_64 / -arch arm64 are how clang/ld emit the respective slices; many projects build each arch separately (with the arch-specific mmacosx-version-min) then combine them with lipo to produce a universal (universal2) binary. (Building a single fat Go binary in one go is uncommon — the usual flow is per-arch builds + lipo.) (thebreakfastpost.com, groups.google.com)
Practical build notes
-
Set CGO_ENABLED=1 and ensure CC points to clang from your Xcode toolchain. Use the per-arch CGO_* values above and run:
- Build amd64: GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 ... (with the x86_64 CGO_* flags)
- Build arm64: GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 ... (with the arm64 CGO_* flags)
- Combine: xcrun lipo -create -output . (golang.google.cn, groups.google.com)
-
If you use C++ code, add appropriate CXX flags (e.g. -stdlib=libc++) and include the same -isysroot / -mmacosx-version-min / -arch bits for CGO_CXXFLAGS / CGO_LDFLAGS.
-
If cgo rejects your flags, you may need to relax the Go cgo-flag allow regex (CGO_CFLAGS_ALLOW / CGO_LDFLAGS_ALLOW). (golang.google.cn)
If you want, I can:
- Give exact shell commands for a two-step build + lipo sequence, or
- Show a sample Makefile / CI snippet (GitHub Actions) that builds both slices and produces a universal executable. Which would you prefer?
Citations:
- 1: https://stackoverflow.com/questions/18741675/how-to-get-the-path-of-latest-sdk-available-on-mac?utm_source=chatgpt.com
- 2: https://stackoverflow.com/questions/2923502/what-does-macosx-version-min-imply?utm_source=chatgpt.com
- 3: https://thebreakfastpost.com/2021/01/05/on-macos-arm64-and-universal-binaries/?utm_source=chatgpt.com
- 4: https://thebreakfastpost.com/2021/01/05/on-macos-arm64-and-universal-binaries/?utm_source=chatgpt.com
- 5: https://groups.google.com/g/golang-codereviews/c/4nWAUOUCcXA?utm_source=chatgpt.com
- 6: https://golang.google.cn/cmd/go/internal/help/?utm_source=chatgpt.com
- 7: https://groups.google.com/g/golang-codereviews/c/4nWAUOUCcXA?utm_source=chatgpt.com
- 8: https://golang.google.cn/cmd/go/internal/help/?utm_source=chatgpt.com
Enforce explicit sysroot, arch and minimum‐OS flags for universal macOS builds
Update v3/examples/dock/build/darwin/Taskfile.yml (lines 25–31) to pass the SDK sysroot, per-arch flags and correct minimum‐version for both Intel and Apple Silicon:
• x86_64 slice
• CGO_CFLAGS:
-isysroot $(xcrun --sdk macosx --show-sdk-path) -mmacosx-version-min=10.15 -arch x86_64
• CGO_LDFLAGS:
-isysroot $(xcrun --sdk macosx --show-sdk-path) -mmacosx-version-min=10.15 -arch x86_64
• MACOSX_DEPLOYMENT_TARGET: 10.15
• arm64 slice
• CGO_CFLAGS:
-isysroot $(xcrun --sdk macosx --show-sdk-path) -mmacosx-version-min=11.0 -arch arm64
• CGO_LDFLAGS:
-isysroot $(xcrun --sdk macosx --show-sdk-path) -mmacosx-version-min=11.0 -arch arm64
• MACOSX_DEPLOYMENT_TARGET: 11.0
Then combine the two binaries with:
xcrun lipo -create <build-amd64>/<binary> <build-arm64>/<binary> -output <binary>-universal
This ensures both slices link against the proper SDK, embed the correct load commands, and run on macOS 10.15+ (Intel) and 11.0+ (Apple Silicon).
🤖 Prompt for AI Agents
In v3/examples/dock/build/darwin/Taskfile.yml around lines 25–31, replace the
single shared CGO_CFLAGS/CGO_LDFLAGS and MACOSX_DEPLOYMENT_TARGET with explicit
per-architecture settings that include the SDK sysroot, arch and correct
minimum-OS for each slice: for x86_64 set CGO_CFLAGS and CGO_LDFLAGS to use
-isysroot $(xcrun --sdk macosx --show-sdk-path) -mmacosx-version-min=10.15 -arch
x86_64 and set MACOSX_DEPLOYMENT_TARGET to 10.15; for arm64 set CGO_CFLAGS and
CGO_LDFLAGS to use -isysroot $(xcrun --sdk macosx --show-sdk-path)
-mmacosx-version-min=11.0 -arch arm64 and set MACOSX_DEPLOYMENT_TARGET to 11.0;
ensure the build tasks produce separate amd64 and arm64 binaries and add the
final step to combine them with xcrun lipo -create ... -output ... to produce a
universal binary.
| - cp build/darwin/icons.icns {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/Resources | ||
| - cp {{.BIN_DIR}}/{{.APP_NAME}} {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents/MacOS | ||
| - cp build/darwin/Info.plist {{.BIN_DIR}}/{{.APP_NAME}}.app/Contents | ||
| - codesign --force --deep --sign - {{.BIN_DIR}}/{{.APP_NAME}}.app |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify codesigning requirements for distribution.
Both the release and development bundles use ad-hoc signing (--sign -), which is sufficient for local testing but not for distribution. Ensure proper code signing with a Developer ID certificate is documented for production releases.
Also applies to: 80-80
🌐 Web query:
What are the macOS codesigning requirements for distributing apps outside the App Store with Developer ID certificates?
💡 Result:
Short answer — if you want to distribute macOS apps outside the Mac App Store and have Gatekeeper allow them without extra user steps, you must:
-
Be a member of the Apple Developer Program (Account Holder) and obtain the proper Developer ID certificates. Use a "Developer ID Application" certificate for apps/bundles and a "Developer ID Installer" certificate to sign installer packages. (developer.apple.com)
-
Sign every piece of executable code in your product with your Developer ID Application identity (main executable, helper apps, XPC services, command-line tools, frameworks, embedded plugins). For installer packages sign the package with Developer ID Installer. Do not leave third‑party executables unsigned. (developer.apple.com, forums.developer.apple.com)
-
Include a secure timestamp and (for main executables) enable the Hardened Runtime when signing. Practically this means adding --timestamp (or codesign --timestamp) and --options=runtime / -o runtime to your codesign invocations, and supplying entitlements when you need runtime exceptions. (developer.apple.com)
-
Notarize the final distributable you will ship (ZIP, DMG, or PKG) with Apple’s notary service. Notarization is required for Developer ID–signed software to pass Gatekeeper on modern macOS versions. After a successful notarization you should staple the notary ticket to the artifact (xcrun stapler). (developer.apple.com)
-
Use current tools and workflow: xcrun notarytool (altool is deprecated), xcrun stapler, and codesign (or Xcode’s export). Test signing and notarization on recent macOS and verify with codesign and spctl. (developer.apple.com)
Typical practical checklist (order of operations)
- Join Apple Developer Program and enable 2FA; create Developer ID certs in your account. (developer.apple.com)
- Build your app and enable Hardened Runtime for main executables (or add the proper entitlements). (developer.apple.com)
- Sign all nested code individually, then sign the top-level bundle using your Developer ID Application identity, including --options=runtime and --timestamp (and --entitlements if needed). Example pattern:
codesign --force --options=runtime --timestamp --entitlements ./entitlements.plist --sign "Developer ID Application: Your Name (TEAMID)" YourApp.app
(sign helpers/tools first, then the whole bundle). (developer.apple.com) - Package into the distribution container you’ll deliver (zip, dmg, or pkg). Sign the package with the Developer ID Installer identity if using pkg. (developer.apple.com)
- Submit to the notary service (xcrun notarytool submit ... --wait) and confirm success; then staple the ticket (xcrun stapler staple YourApp.app or your.dmg). (developer.apple.com)
- Verify with:
- codesign --verify --deep --strict --verbose=2 YourApp.app
- spctl --assess --type execute --verbose=2 YourApp.app
- xcrun stapler validate YourApp.app (if stapled). (developer.apple.com)
Other important notes
- Notarization will refuse binaries that are not signed with a valid Developer ID Application identity, that lack a secure timestamp, or whose main executables do not have the hardened runtime enabled. Check notarization logs for specific failures. (forums.developer.apple.com, developer.apple.com)
- If you use third‑party native code or JIT/unsigned-executable-memory, you may need specific hardened-runtime entitlements (but those make Gatekeeper checks stricter). Work with library vendors where possible. (developer.apple.com, forums.developer.apple.com)
- Certificate expiration/ revocation: apps signed with a Developer ID certificate that was valid at build time will generally continue to run after that cert expires; installer packages checked at run time require a valid Installer cert. Revoked certs break installs/runs. (developer.apple.com)
If you want, I can:
- Give you a ready-to-run example script (codesign → notarytool → stapler) for a .app → .dmg workflow, or
- Help diagnose a specific codesign / notarization error (paste your notarization log or the codesign / spctl output).
Citations:
- 1: https://developer.apple.com/support/developer-id/?utm_source=chatgpt.com
- 2: https://developer.apple.com/forums/thread/128166?utm_source=chatgpt.com
- 3: https://forums.developer.apple.com/forums/thread/683743?utm_source=chatgpt.com
- 4: https://developer.apple.com/forums/thread/128166?utm_source=chatgpt.com
- 5: https://developer.apple.com/developer-id/?utm_source=chatgpt.com
- 6: https://developer.apple.com/developer-id/?utm_source=chatgpt.com
- 7: https://developer.apple.com/support/developer-id/?utm_source=chatgpt.com
- 8: https://developer.apple.com/forums/thread/128166?utm_source=chatgpt.com
- 9: https://developer.apple.com/forums/thread/128166?utm_source=chatgpt.com
- 10: https://developer.apple.com/forums/thread/128166?utm_source=chatgpt.com
- 11: https://developer.apple.com/developer-id/?utm_source=chatgpt.com
- 12: https://developer.apple.com/forums/thread/128166?utm_source=chatgpt.com
- 13: https://forums.developer.apple.com/forums/thread/128772?utm_source=chatgpt.com
- 14: https://developer.apple.com/forums/thread/122410?utm_source=chatgpt.com
- 15: https://developer.apple.com/forums/thread/757540?utm_source=chatgpt.com
- 16: https://forums.developer.apple.com/forums/thread/683743?utm_source=chatgpt.com
- 17: https://developer.apple.com/support/developer-id/?utm_source=chatgpt.com
Require Developer ID Signing, Hardened Runtime, Timestamping, and Notarization for Production Builds
Ad-hoc signing (--sign -) only works for local testing. For any macOS app you intend to distribute outside the Mac App Store, you must:
• Sign all executables (helpers, XPC services, frameworks, CLI tools) with your Developer ID Application identity.
• Enable the Hardened Runtime (--options=runtime) and include a secure timestamp (--timestamp).
• Package into a ZIP/DMG/PKG, sign with a Developer ID Installer certificate (for PKGs), submit to Apple’s notary service, and staple the ticket.
Attention needed:
- File:
v3/examples/dock/build/darwin/Taskfile.yml, line 72 - You’ll also need tasks for nested code signing, packaging, notarization (notarytool) and stapling.
Recommended diff:
- - codesign --force --deep --sign - {{.BIN_DIR}}/{{.APP_NAME}}.app
+ - codesign --force --deep \
+ --options=runtime \
+ --timestamp \
+ --entitlements path/to/entitlements.plist \
+ --sign "Developer ID Application: Your Name (TEAMID)" \
+ {{.BIN_DIR}}/{{.APP_NAME}}.appAfter building:
- Sign any installer packages:
productsign --sign "Developer ID Installer: Your Name (TEAMID)" app.pkg signed-app.pkg - Notarize & staple:
xcrun notarytool submit signed-app.pkg --keychain-profile PROFILE --wait && xcrun stapler staple app.pkg
📝 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.
| - codesign --force --deep --sign - {{.BIN_DIR}}/{{.APP_NAME}}.app | |
| - codesign --force --deep \ | |
| --options=runtime \ | |
| --timestamp \ | |
| --entitlements path/to/entitlements.plist \ | |
| --sign "Developer ID Application: Your Name (TEAMID)" \ | |
| {{.BIN_DIR}}/{{.APP_NAME}}.app |
🤖 Prompt for AI Agents
v3/examples/dock/build/darwin/Taskfile.yml around line 72: the current codesign
step uses ad-hoc signing ("--sign -") which is only for local testing; replace
it with signing using your Developer ID Application identity, add the hardened
runtime option and a secure timestamp, and ensure you perform nested signing for
all helpers, XPC services, frameworks and CLI tools before signing the .app;
additionally add Taskfile targets to package the build (zip/dmg/pkg), sign
installer packages with Developer ID Installer, submit the package to Apple’s
notary service (notarytool) and staple the ticket; implement tasks that (1)
recursively sign nested binaries with the Developer ID Application cert plus
--options=runtime and --timestamp, (2) create installer archive (zip/dmg/pkg)
and sign installer with Developer ID Installer cert, and (3) run notarytool
submit --wait and xcrun stapler staple on the signed installer.
| fi | ||
|
|
||
| # Rename the generated AppImage | ||
| mv "${APP_NAME}*.AppImage" "${APP_NAME}.AppImage" |
There was a problem hiding this comment.
Glob is quoted, preventing expansion; mv will fail.
With quotes, "${APP_NAME}*.AppImage" is treated literally and won’t match the produced file.
-mv "${APP_NAME}*.AppImage" "${APP_NAME}.AppImage"
+# Allow glob expansion to match the generated file
+mv ${APP_NAME}*.AppImage "${APP_NAME}.AppImage"Optional: for robustness when multiple files may match, collect candidates and validate there’s exactly one before renaming.
🤖 Prompt for AI Agents
In v3/examples/dock/build/linux/appimage/build.sh around line 34, the quoted
glob ("${APP_NAME}*.AppImage") prevents shell expansion causing mv to fail;
remove the quotes around the glob so the shell can expand it (or explicitly
expand into an array of candidates, check there is exactly one match, and then
rename that single file to "${APP_NAME}.AppImage"), and ensure errors are
handled when zero or multiple matches are found.
| vars: | ||
| ARCH: '{{.ARCH | default ARCH}}' | ||
| CERT_PATH: '{{.CERT_PATH | default ""}}' | ||
| PUBLISHER: '{{.PUBLISHER | default ""}}' | ||
| USE_MSIX_TOOL: '{{.USE_MSIX_TOOL | default "false"}}' |
There was a problem hiding this comment.
Boolean templating bug always selects --use-msix-tool
{{if .USE_MSIX_TOOL}} treats any non-empty string (including "false") as truthy, so the msix tool path is always chosen.
Apply this diff:
--out "{{.ROOT_DIR}}/{{.BIN_DIR}}/{{.APP_NAME}}-{{.ARCH}}.msix" \
- {{if .CERT_PATH}}--cert "{{.CERT_PATH}}"{{end}} \
- {{if .PUBLISHER}}--publisher "{{.PUBLISHER}}"{{end}} \
- {{if .USE_MSIX_TOOL}}--use-msix-tool{{else}}--use-makeappx{{end}}
+ {{if .CERT_PATH}}--cert "{{.CERT_PATH}}"{{end}} \
+ {{if .PUBLISHER}}--publisher "{{.PUBLISHER}}"{{end}} \
+ {{if eq .USE_MSIX_TOOL "true"}}--use-msix-tool{{else}}--use-makeappx{{end}}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In v3/examples/dock/build/windows/Taskfile.yml around lines 85-89, the
templating uses {{if .USE_MSIX_TOOL}} which treats any non-empty string
(including "false") as true; change the template logic to test the string value
explicitly (for example, use an equality check like eq .USE_MSIX_TOOL "true" or
otherwise parse/normalize the value) so --use-msix-tool is only selected when
USE_MSIX_TOOL is actually set to "true". Ensure the variable default remains
safely false/empty and update any calling docs or env examples to set
USE_MSIX_TOOL="true" when intended.
| @font-face { | ||
| font-family: "Inter"; | ||
| font-style: normal; | ||
| font-weight: 400; | ||
| src: local(""), | ||
| url("./Inter-Medium.ttf") format("truetype"); | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify font asset inclusion and licensing
Ensure Inter-Medium.ttf is present in the served path and that its license is included with distributions.
Run this script to verify the font and a nearby license file exist:
🏁 Script executed:
#!/bin/bash
fd -a -H --exact-path 'v3/examples/dock/frontend/public/Inter-Medium.ttf'
fd -a -H -g 'LICENSE*' v3/examples/dock/frontend/public v3/examples/dock/frontendLength of output: 394
🏁 Script executed:
#!/bin/bash
# Search for the Inter-Medium.ttf asset in the public folder
fd --hidden -e ttf Inter-Medium.ttf v3/examples/dock/frontend/public
# Search for any LICENSE* files adjacent to or above the public directory
fd --hidden -g 'LICENSE*' v3/examples/dock/frontend/public v3/examples/dock/frontendLength of output: 207
Include Inter font license with distribution
The Inter-Medium.ttf asset is present at
v3/examples/dock/frontend/public/Inter-Medium.ttf
but no LICENSE* file was found in that directory or its parent. Please include the appropriate font license alongside the asset:
- Add the SIL Open Font License (OFL) or equivalent as
OFL.txtorLICENSEin
v3/examples/dock/frontend/public - Ensure this license file is packaged and distributed with your production build.
🤖 Prompt for AI Agents
In v3/examples/dock/frontend/public/style.css around lines 22 to 28, the
Inter-Medium.ttf font is referenced but its license is missing from the public
directory; add the SIL Open Font License (OFL) or equivalent as a file named
OFL.txt or LICENSE into v3/examples/dock/frontend/public, ensure the license
text matches the font's distribution terms, and update your build/package
configuration so that this license file is included in production artifacts
alongside Inter-Medium.ttf.
| const showButton = document.getElementById('show')! as HTMLButtonElement; | ||
| const hideButton = document.getElementById('hide')! as HTMLButtonElement; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Add null checks for DOM element queries.
While the non-null assertions (!) indicate that these elements are expected to exist, the application will crash if they're missing. Consider adding proper error handling.
-const showButton = document.getElementById('show')! as HTMLButtonElement;
-const hideButton = document.getElementById('hide')! as HTMLButtonElement;
+const showButton = document.getElementById('show') as HTMLButtonElement | null;
+const hideButton = document.getElementById('hide') as HTMLButtonElement | null;
+
+if (!showButton || !hideButton) {
+ console.error('Required DOM elements not found');
+ // Optionally, you could show an error to the user
+ return;
+}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In v3/examples/dock/frontend/src/main.ts around lines 3-4, the code uses
non-null assertions when querying DOM elements which will throw if the elements
are missing; replace the assertions with runtime null checks: call
document.getElementById for 'show' and 'hide', test each result for null, and if
either is null log a clear error and abort initialization (or throw a
descriptive Error) to avoid hard crashes; when present, narrow the type to
HTMLButtonElement before using them (e.g., by type-guard or local typed
variables) and disable or skip wiring event handlers if elements are absent.
| import ( | ||
| "embed" | ||
| _ "embed" | ||
| "log" | ||
|
|
||
| "github.com/wailsapp/wails/v3/pkg/application" | ||
| "github.com/wailsapp/wails/v3/pkg/services/dock" | ||
| ) |
There was a problem hiding this comment.
Duplicate import of embed causes a compile error
embed is imported twice: once normally and once as a blank import. Go disallows importing the same package twice in a single file. Remove the blank import.
Apply this diff:
import (
- "embed"
- _ "embed"
+ "embed"
"log"
"github.com/wailsapp/wails/v3/pkg/application"
"github.com/wailsapp/wails/v3/pkg/services/dock"
)📝 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.
| import ( | |
| "embed" | |
| _ "embed" | |
| "log" | |
| "github.com/wailsapp/wails/v3/pkg/application" | |
| "github.com/wailsapp/wails/v3/pkg/services/dock" | |
| ) | |
| import ( | |
| "embed" | |
| "log" | |
| "github.com/wailsapp/wails/v3/pkg/application" | |
| "github.com/wailsapp/wails/v3/pkg/services/dock" | |
| ) |
🤖 Prompt for AI Agents
In v3/examples/dock/main.go around lines 3 to 10, the package "embed" is
imported twice (once normally and once as a blank import), which causes a
compile error; remove the duplicate blank import (`_ "embed"`) and keep the
single direct import `embed`, then save/gofmt the file so the import block is
valid.
| void hideDockIcon() { | ||
| [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory]; | ||
| } | ||
|
|
||
| void showDockIcon() { | ||
| [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; | ||
| } |
There was a problem hiding this comment.
Ensure Dock icon operations run on the main thread.
The PR comments mention that macOS calls must run on the main thread to avoid crashes. The C functions hideDockIcon() and showDockIcon() directly call NSApp methods without main thread dispatch. This could cause crashes when called from Go routines.
void hideDockIcon() {
- [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
+ });
}
void showDockIcon() {
- [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
+ });
}You'll also need to import the dispatch header:
#cgo LDFLAGS: -framework Cocoa
#import <Cocoa/Cocoa.h>
+#import <dispatch/dispatch.h>🤖 Prompt for AI Agents
In v3/pkg/services/dock/dock_darwin.go around lines 10 to 16, the hideDockIcon
and showDockIcon functions call NSApp activationPolicy methods off the main
thread which can crash; update the file to import the dispatch header
(<dispatch/dispatch.h>) and wrap the NSApp calls in a dispatch_async or
dispatch_sync to dispatch_get_main_queue() so the activationPolicy changes
execute on the main thread, ensuring thread-safe macOS API usage.
There was a problem hiding this comment.
Actionable comments posted: 19
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
v3/pkg/services/dock/font.go (2)
23-35: Harden Windows font dir discovery to avoid relative paths when env vars are missingIf WINDIR/LOCALAPPDATA are unset (service environments, CI), the current code builds relative paths like "Fonts", which may cause false negatives. Build absolute paths with sensible fallbacks and only include user font dir when available.
Apply this refactor to NewFontManager:
-func NewFontManager() *FontManager { - return &FontManager{ - fontCache: make(map[string]string), - fontDirs: []string{ - filepath.Join(os.Getenv("windir"), "Fonts"), - filepath.Join(os.Getenv("localappdata"), "Microsoft", "Windows", "Fonts"), - }, - registryPaths: []string{ - `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts`, - }, - } -} +func NewFontManager() *FontManager { + // Resolve system fonts directory + windir := os.Getenv("WINDIR") + if windir == "" { + windir = os.Getenv("windir") + } + if windir == "" { + windir = `C:\Windows` + } + fontsDir := filepath.Join(windir, "Fonts") + + // Resolve per-user fonts directory (optional) + fontDirs := []string{fontsDir} + if lad := os.Getenv("LOCALAPPDATA"); lad != "" { + fontDirs = append(fontDirs, filepath.Join(lad, "Microsoft", "Windows", "Fonts")) + } + + return &FontManager{ + fontCache: make(map[string]string), + fontDirs: fontDirs, + registryPaths: []string{ + `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts`, + }, + } +}
79-89: Avoid defers in loops; close registry keys eagerly to prevent handle buildupUsing defer in a loop delays closing until function exit. Close the key immediately after use to reduce resource pressure and avoid leaks on early returns.
- for _, regPath := range fm.registryPaths { - k, err := registry.OpenKey(registry.LOCAL_MACHINE, regPath, registry.QUERY_VALUE) - if err == nil { - defer k.Close() - - // Look for the specific font in registry values - fontPath, found := fm.findFontInRegistry(k, fontFileLower, fm.fontDirs[0]) - if found { - return fontPath, nil - } - } - } + for _, regPath := range fm.registryPaths { + k, err := registry.OpenKey(registry.LOCAL_MACHINE, regPath, registry.QUERY_VALUE) + if err == nil { + // Look for the specific font in registry values + fontPath, found := fm.findFontInRegistry(k, fontFileLower, fm.fontDirs[0]) + k.Close() + if found { + return fontPath, nil + } + } + } - for _, regPath := range fm.registryPaths { - k, err := registry.OpenKey(registry.CURRENT_USER, regPath, registry.QUERY_VALUE) - if err == nil { - defer k.Close() - - // Look for the specific font in registry values - fontPath, found := fm.findFontInRegistry(k, fontFileLower, fm.fontDirs[1]) - if found { - return fontPath, nil - } - } - } + // Choose a sensible default for per-user fonts (may be absent) + defaultUserDir := fm.fontDirs[0] + if len(fm.fontDirs) > 1 { + defaultUserDir = fm.fontDirs[1] + } + for _, regPath := range fm.registryPaths { + k, err := registry.OpenKey(registry.CURRENT_USER, regPath, registry.QUERY_VALUE) + if err == nil { + // Look for the specific font in registry values + fontPath, found := fm.findFontInRegistry(k, fontFileLower, defaultUserDir) + k.Close() + if found { + return fontPath, nil + } + } + }Also applies to: 92-103
v3/pkg/services/dock/dock_darwin.go (1)
18-25: setBadge must run on main thread; also fix empty label behavior and potential leaks
- setBadge touches NSApp UI off the main thread.
- When label == "", a C string is allocated but never freed (memory leak), and a default "●" is set which is surprising vs. RemoveBadge semantics.
Recommend:
- Dispatch setBadge on the main queue.
- Treat empty label as “remove badge”.
- Always free C strings allocated in Go.
Apply this diff to the Obj‑C bridge and Go method:
static void setBadge(const char *label) { - NSString *nsLabel = nil; - if (label != NULL) { - nsLabel = [NSString stringWithUTF8String:label]; - } - [[NSApp dockTile] setBadgeLabel:nsLabel]; - [[NSApp dockTile] display]; + runOnMain(^{ + NSString *nsLabel = nil; + if (label != NULL) { + nsLabel = [NSString stringWithUTF8String:label]; + } + [[NSApp dockTile] setBadgeLabel:nsLabel]; + [[NSApp dockTile] display]; + }); }func (d *darwinDock) SetBadge(label string) error { - var cLabel *C.char - if label != "" { - cLabel = C.CString(label) - defer C.free(unsafe.Pointer(cLabel)) - } else { - cLabel = C.CString("●") // Default badge character - } - C.setBadge(cLabel) + if label == "" { + C.setBadge(nil) + return nil + } + cLabel := C.CString(label) + defer C.free(unsafe.Pointer(cLabel)) + C.setBadge(cLabel) return nil } func (d *darwinDock) RemoveBadge() error { C.setBadge(nil) return nil }This enforces main-thread UI calls, removes the leak, and aligns empty-label behavior with RemoveBadge.
Also applies to: 68-79, 86-90
v3/examples/badge/main.go (1)
3-6: Remove duplicate/blank import of embed (won’t compile).The embed package is imported twice; a blank import is unnecessary and invalid here.
import ( "embed" - _ "embed" "log" "time"v3/examples/badge-custom/main.go (1)
3-6: Duplicate import of the special package "embed" will fail the buildYou import "embed" twice (once as normal and once as a blank import). The blank import is unnecessary and causes a compile error.
Apply this diff to fix the imports:
import ( "embed" - _ "embed" "image/color" "log" "time"v3/pkg/services/dock/dock_windows.go (1)
262-313: Replace invaliddefer face.Close()calls with a safe type‐asserted closerThe grep check still finds two instances of
defer face.Close()inv3/pkg/services/dock/dock_windows.gothat will not compile:
- Line 295
- Line 348
Please update both occurrences as follows:
@@ -292,7 +292,10 @@ func (w *windowsDock) createBadgeIconWithText(label string) (w32.HICON, error) { face, err := opentype.NewFace(ttf, &opentype.FaceOptions{ Size: fontSize, DPI: float64(dpi), Hinting: font.HintingFull, }) if err != nil { return w.createBadgeIcon() } - defer face.Close() + if c, ok := face.(interface{ Close() error }); ok { + defer c.Close() + } @@ -344,7 +347,10 @@ func (w *windowsDock) createBadgeIconWithText(label string) (w32.HICON, error) { face, err := opentype.NewFace(ttf, &opentype.FaceOptions{ Size: fontSize, DPI: float64(dpi), Hinting: font.HintingFull, }) if err != nil { return createBadgeIcon(badgeSize, img, options) } - defer face.Close() + if c, ok := face.(interface{ Close() error }); ok { + defer c.Close() + }
♻️ Duplicate comments (1)
v3/examples/badge/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/index.ts (1)
4-7: LGTM — mirrors the rename to DockService
Matches the badge-custom binding and the new Dock API surface.See the verification script suggested in the badge-custom binding index.ts to catch any stale references.
Also applies to: 10-11
🧹 Nitpick comments (64)
v3/examples/dock/build/linux/nfpm/scripts/postinstall.sh (2)
12-19: Clarify comment: x-scheme-handler is handled via desktop database, not MIME databasex-scheme-handler associations are derived from the .desktop file and picked up by update-desktop-database. update-mime-database only updates caches for shared-mime-info XML definitions under /usr/share/mime. If you aren’t shipping MIME type XMLs, keeping this step is harmless but the comment is misleading.
Apply this tweak to the comments to avoid confusion:
-# Update MIME database for custom URL schemes (x-scheme-handler) -# This ensures the system knows how to handle your custom protocols. +# Update MIME database (only needed if shipping shared-mime-info XMLs). +# Note: x-scheme-handler registrations come from the .desktop file and are +# picked up by update-desktop-database, not this command.
11-11: Also refresh GTK icon cache for hicolor themeYou install an icon under /usr/share/icons/hicolor in the package; many desktops require gtk-update-icon-cache to rebuild the theme cache so the icon appears immediately.
Insert this block after Line 11:
+ +# Update icon cache for hicolor theme so the new icon is recognized +if command -v gtk-update-icon-cache >/dev/null 2>&1; then + echo "Updating GTK icon cache..." + gtk-update-icon-cache -f -t /usr/share/icons/hicolor +else + echo "Warning: gtk-update-icon-cache command not found. Icons may not be immediately recognized." >&2 +fi +v3/examples/dock/build/linux/nfpm/nfpm.yaml (6)
20-22: Install binaries under /usr/bin rather than /usr/local/binPackages should generally place managed executables under /usr/bin. /usr/local is intended for locally compiled/unmanaged software.
- - src: "./bin/dock" - dst: "/usr/local/bin/dock" + - src: "./bin/dock" + dst: "/usr/bin/dock"
22-25: Consider shipping additional icon sizes for better desktop integrationSupplying only 128x128 can look blurry in some contexts. Adding at least 16, 32, 48, 64, 256, and scalable (SVG) under the hicolor theme improves appearance across desktops.
I can draft the contents mappings for multiple sizes if you have the assets.
43-44: Fix YAML lint issues: trailing spacesYAMLlint flagged trailing spaces on these lines.
Remove trailing spaces; they’ll be resolved by the earlier dependency diffs, but if applied separately:
- - # Arch Linux packages (WebKit 4.1) + # Arch Linux packages (WebKit 4.1)
73-73: Add missing newline at end of fileThis is a minor formatting nit but flagged by YAMLlint and some tooling.
-# changelog: "changelog.yaml" \ No newline at end of file +# changelog: "changelog.yaml" +
6-16: Package naming and metadata placeholders
- name: "dock" may collide with existing packages; consider a more specific name (e.g., wails-dock-example).
- description/vendor are placeholders; update before distributing outside the repo.
Would you like me to propose finalized metadata values?
52-55: Verified both paths exist; consider using YAML-relative pathBoth the configured path (
v3/examples/dock/build/linux/nfpm/scripts/postinstall.sh) and the path relative to thenfpm.yamldirectory (./scripts/postinstall.sh) resolve to the same file. To make this less brittle if nfpm is executed with its own directory as CWD, you can optionally simplify:In
v3/examples/dock/build/linux/nfpm/nfpm.yaml(lines 52–55):scripts: - postinstall: "./build/linux/nfpm/scripts/postinstall.sh" + postinstall: "./scripts/postinstall.sh"v3/pkg/services/dock/font.go (3)
121-127: Use filepath.IsAbs for robust relative-path detectionChecking for a backslash is brittle and fails for forward-slash or UNC paths. filepath.IsAbs is platform-aware and correct on Windows.
- // If it's a relative path, assume it's in the default font directory - if !strings.Contains(value, "\\") { + // If it's a relative path, assume it's in the default font directory + if !filepath.IsAbs(value) { value = filepath.Join(defaultDir, value) }
138-147: Exported method lacks GoDoc; add a short comment for clarityPublic methods should have a brief comment to aid discovery and tooling.
+// FindFontOrDefault tries the given font filename first, then common bold fonts. +// Returns the full path to the first found font or the empty string if none found. func (fm *FontManager) FindFontOrDefault(name string) string {
37-62: Optional: add negative caching to avoid repeated registry scans for missing fontsCurrently only positive hits are cached. Negative results trigger repeated registry traversals. Consider caching misses with a short TTL or a sentinel in fontCache.
If desired, I can draft a small LRU/TTL layer to record misses and reduce repeated registry calls. Want me to put that together?
v3/examples/dock/README.md (2)
11-13: Specify languages for fenced code blocks (markdownlint MD040)Add language hints for better rendering and linting.
- ``` + ```bash wails3 dev ``` @@ - ``` + ```bash wails3 build ``` @@ - ``` + ```bash go run . ```Also applies to: 19-21, 33-35
29-35: Tighten phrasing of the examples sectionRemoves redundancy and clarifies the path.
-1. **Check out the examples**: The best way to learn is by example. Visit the `examples` directory in the `v3/examples` directory to see various sample applications. +1. **Check out the examples**: Explore the sample apps under `v3/examples/`.v3/examples/dock/frontend/tsconfig.json (2)
7-7: Prefer moduleResolution "Bundler" for modern frontend toolchainsVite/esbuild-based setups work best with "Bundler" in TS 5+, aligning import semantics with the bundler.
- "moduleResolution": "Node", + "moduleResolution": "Bundler",
11-18: Enable verbatimModuleSyntax to avoid import elision surprisesThis keeps import/export statements intact, improving interop with ESM bundlers.
"esModuleInterop": true, + "verbatimModuleSyntax": true, "noEmit": true,v3/examples/dock/build/windows/wails.exe.manifest (1)
11-13: Optional: Add longPathAware and (if needed) OS compatibility sectionTo avoid path length issues in user environments and improve UX on modern Windows, consider adding longPathAware. Optionally add a compatibility section if you see legacy compatibility prompts.
Apply this diff to enable long paths:
<asmv3:windowsSettings> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> <!-- fallback for Windows 7 and 8 --> <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness> <!-- falls back to per-monitor if per-monitor v2 is not supported --> + <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware> </asmv3:windowsSettings>If desired later, a block (with supportedOS GUIDs) can be added at the assembly root to declare explicit OS targets.
v3/examples/dock/build/linux/appimage/build.sh (2)
8-16: Validate required inputs early for clearer failuresAPP_NAME, APP_BINARY, ICON_PATH, and DESKTOP_FILE are assumed to exist. Fast-fail with clear messages and file checks.
# Define variables APP_DIR="${APP_NAME}.AppDir" +# Validate required inputs +: "${APP_NAME:?APP_NAME is required}" +: "${APP_BINARY:?APP_BINARY is required}" +: "${ICON_PATH:?ICON_PATH is required}" +: "${DESKTOP_FILE:?DESKTOP_FILE is required}" +[[ -f "${APP_BINARY}" ]] || { echo "APP_BINARY not found: ${APP_BINARY}" >&2; exit 1; } +[[ -f "${ICON_PATH}" ]] || { echo "ICON_PATH not found: ${ICON_PATH}" >&2; exit 1; } +[[ -f "${DESKTOP_FILE}" ]] || { echo "DESKTOP_FILE not found: ${DESKTOP_FILE}" >&2; exit 1; } + # Create AppDir structure mkdir -p "${APP_DIR}/usr/bin" -cp -r "${APP_BINARY}" "${APP_DIR}/usr/bin/" +cp "${APP_BINARY}" "${APP_DIR}/usr/bin/" cp "${ICON_PATH}" "${APP_DIR}/" cp "${DESKTOP_FILE}" "${APP_DIR}/"
13-13: Nit: Avoid -r when copying a single binaryThe binary is a file, not a directory. Using cp without -r is clearer.
-cp -r "${APP_BINARY}" "${APP_DIR}/usr/bin/" +cp "${APP_BINARY}" "${APP_DIR}/usr/bin/"v3/examples/dock/frontend/package.json (3)
12-14: Avoid "latest" for @wailsio/runtime to ensure reproducible buildsPin the dependency to a known version (or a tagged alpha) to prevent unexpected breakage when "latest" changes.
16-17: Vite 5 requires Node 18+ — declare engines to guide contributorsAdd an engines field to avoid local build confusion on older Node versions.
Apply this diff to add the engines constraint:
"devDependencies": { "typescript": "^4.9.3", "vite": "^5.0.0" - } + }, + "engines": { + "node": ">=18.0.0" + }
6-11: Add a typecheck script to CI-friendly workflowsA dedicated typecheck script helps catch TS issues without building/bundling.
Apply this diff to add a typecheck script:
"scripts": { "dev": "vite", "build:dev": "tsc && vite build --minify false --mode development", "build": "tsc && vite build --mode production", - "preview": "vite preview" + "preview": "vite preview", + "typecheck": "tsc --noEmit" },v3/examples/dock/frontend/public/style.css (4)
56-59: Duplicate .result rules — keep oneThere are two .result blocks. Remove the earlier one to avoid future divergence.
Apply this diff to drop the duplicate:
-.result { - height: 20px; - line-height: 20px; -}Also applies to: 103-108
22-28: Font-face should set font-display for better UXAdd font-display: swap to prevent FOIT on slow loads.
Apply this diff:
@font-face { font-family: "Inter"; font-style: normal; font-weight: 400; src: local(""), url("./Inter-Medium.ttf") format("truetype"); + font-display: swap; }
45-54: Add focus-visible styles to buttons for accessibilityKeyboard users need a visible focus indicator.
Apply this diff:
button { width: 60px; height: 30px; line-height: 30px; border-radius: 3px; border: none; margin: 0 0 0 20px; padding: 0 8px; cursor: pointer; } + +button:focus-visible { + outline: 2px solid #747bff; + outline-offset: 2px; +}
16-19: Global user-select: none hampers text copyingDisabling selection at :root can be frustrating. Consider scoping this to non-text UI elements if intentional.
v3/pkg/services/dock/dock_darwin.go (1)
44-48: Clarify options handling on macOSNewWithOptions ignores options (fine), but consider documenting this in the public docs or returning the default options on Darwin for transparency.
v3/examples/dock/build/windows/nsis/wails_tools.nsh (3)
114-128: Write uninstall registry entries under SHCTX to support user-level installsCurrent implementation hard-codes HKLM. If REQUEST_EXECUTION_LEVEL is "user", writes will fail or require elevation. Use SHCTX (context-aware HKLM/HKCU based on SetShellVarContext).
Apply this diff:
!macro wails.writeUninstaller WriteUninstaller "$INSTDIR\uninstall.exe" SetRegView 64 - WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}" - WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}" - WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${INFO_PRODUCTVERSION}" - WriteRegStr HKLM "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_EXECUTABLE}" - WriteRegStr HKLM "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" - WriteRegStr HKLM "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S" + WriteRegStr SHCTX "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}" + WriteRegStr SHCTX "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}" + WriteRegStr SHCTX "${UNINST_KEY}" "DisplayVersion" "${INFO_PRODUCTVERSION}" + WriteRegStr SHCTX "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_EXECUTABLE}" + WriteRegStr SHCTX "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" + WriteRegStr SHCTX "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S" ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 IntFmt $0 "0x%08X" $0 - WriteRegDWORD HKLM "${UNINST_KEY}" "EstimatedSize" "$0" + WriteRegDWORD SHCTX "${UNINST_KEY}" "EstimatedSize" "$0" !macroend !macro wails.deleteUninstaller Delete "$INSTDIR\uninstall.exe" SetRegView 64 - DeleteRegKey HKLM "${UNINST_KEY}" + DeleteRegKey SHCTX "${UNINST_KEY}" !macroendFollow-up: Ensure wails.setShellContext is invoked before wails.writeUninstaller in project.nsi so SHCTX maps correctly.
Also applies to: 130-136
152-179: WebView2 detection should check both 64-bit and 32-bit registry keysOnly checking WOW6432Node may miss 64-bit installations and trigger unnecessary installs. Check the 64-bit Clients key first, then the WOW6432Node fallback.
Apply this diff:
!macro wails.webview2runtime @@ - SetRegView 64 - # If the admin key exists and is not empty then webview2 is already installed - ReadRegStr $0 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" - ${If} $0 != "" - Goto ok - ${EndIf} + SetRegView 64 + # If the admin key exists and is not empty then webview2 is already installed + # Check native 64-bit first + ReadRegStr $0 HKLM "SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" + ${If} $0 != "" + Goto ok_webview + ${EndIf} + # Fallback: 32-bit client on 64-bit OS + ReadRegStr $0 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" + ${If} $0 != "" + Goto ok_webview + ${EndIf} @@ - ${If} ${REQUEST_EXECUTION_LEVEL} == "user" + ${If} ${REQUEST_EXECUTION_LEVEL} == "user" # If the installer is run in user level, check the user specific key exists and is not empty then webview2 is already installed - ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" + ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv" ${If} $0 != "" - Goto ok + Goto ok_webview ${EndIf} ${EndIf} @@ - SetDetailsPrint both - ok: + SetDetailsPrint both + ok_webview: !macroendOptional: capture ExecWait exit code (in $0/$1) and log on failure for easier diagnostics.
58-99: Avoid label collisions across macros (rename generic ‘ok:’ labels)Both macros define a label named ok:. If invoked within the same Section/Function, NSIS will error on duplicate labels. Rename labels to be macro-specific.
Apply this diff to wails.checkArchitecture (optional) and wails.webview2runtime (shown above already):
!macro wails.checkArchitecture @@ - Goto ok + Goto ok_arch @@ - Goto ok + Goto ok_arch @@ - ok: + ok_arch: !macroendNote: The complementary rename for wails.webview2runtime is included in the WebView2 diff as ok_webview.
Also applies to: 147-179
v3/examples/dock/frontend/src/main.ts (1)
6-12: Avoid unhandled promise rejections from DockService callsIf bindings return Promises (typical in Wails), calling without await/void can surface unhandled rejections in the console. Prefix with void or add minimal error handling.
Apply this diff:
showButton.addEventListener('click', () => { - DockService.ShowAppIcon(); + void DockService.ShowAppIcon(); }); hideButton.addEventListener('click', () => { - DockService.HideAppIcon(); + void DockService.HideAppIcon(); });v3/examples/dock/build/linux/desktop (1)
6-6: Use a portable Exec/try-exec pattern (avoid hard-coded path and stray %u)Hard-coding /usr/local/bin makes packaging brittle. Unless you’re registering a URL handler, %u is unnecessary. Prefer Exec=dock and add TryExec for DEs to validate presence.
Apply this diff:
-Exec=/usr/local/bin/dock %u +TryExec=dock +Exec=dockIf you intend to register as a URL handler, add an appropriate MimeType (e.g., x-scheme-handler/your-scheme) instead of hard-coding %u here.
v3/examples/dock/main.go (3)
20-23: Outdated/incorrect comment about a goroutine and eventsThe comment claims a time-based event goroutine is started, but no such code exists. Update it to reflect what main actually does.
-// main function serves as the application's entry point. It initializes the application, creates a window, -// and starts a goroutine that emits a time-based event every second. It subsequently runs the application and -// logs any error that might occur. +// main is the application entry point. It initializes the app, registers the Dock service, +// creates a window, runs the application, and logs any error that occurs.
29-30: Grammar nit: “an macOS” → “on macOS”-// 'Mac' options tailor the application when running an macOS. +// 'Mac' options tailor the application when running on macOS.
34-36: Align example description with the Dock example
Current description reads like a generic HTML/CSS demo.- Description: "A demo of using raw HTML & CSS", + Description: "Dock service demo (show/hide app icon, badges)",v3/examples/dock/build/windows/msix/app_manifest.xml (1)
14-19: Align product identifiers and display metadata across the repo.These placeholders ("My Product", "My Company") differ from other files (e.g., config.yml productIdentifier). Consistent identifiers ease packaging/signing and avoid confusion.
If you intend to align with the dock example naming, consider:
- <DisplayName>My Product</DisplayName> - <PublisherDisplayName>My Company</PublisherDisplayName> - <Description>My Product Description</Description> + <DisplayName>Wails Dock Example</DisplayName> + <PublisherDisplayName>Wails</PublisherDisplayName> + <Description>Wails v3 Dock service example</Description> @@ - DisplayName="My Product" - Description="My Product Description" + DisplayName="Wails Dock Example" + Description="Wails v3 Dock service example"And ensure productIdentifier matches across build/config.yml.
Also applies to: 31-37
v3/examples/dock/frontend/index.html (2)
17-18: Fix brand capitalization in alt text.“TypeScript” is the correct casing.
- <img src="/typescript.svg" class="logo vanilla" alt="Typescript logo"/> + <img src="/typescript.svg" class="logo vanilla" alt="TypeScript logo"/>
23-24: Explicitly set button type to avoid unintended form submission.If this markup is ever embedded in a form, default type="submit" can cause surprises.
- <button class="btn" id="show">Show</button> - <button class="btn" id="hide">Hide</button> + <button class="btn" id="show" type="button">Show</button> + <button class="btn" id="hide" type="button">Hide</button>v3/examples/badge/main.go (2)
71-77: Defend against non-string event payloads to avoid panics.event.Data.(string) will panic if the payload isn’t a string. Prefer a checked assertion.
- text := event.Data.(string) - err := dockService.SetBadge(text) + text, ok := event.Data.(string) + if !ok { + log.Printf("set:badge event payload is not a string: %#v", event.Data) + return + } + err := dockService.SetBadge(text)
34-36: Consider renaming the example app to reflect Dock.Minor naming nit: the example still uses Name: "badge".
- Name: "badge", - Description: "A demo of using raw HTML & CSS", + Name: "dock", + Description: "Dock service example (badge + show/hide)",v3/examples/dock/build/config.yml (2)
63-63: Add a newline at end of file.YAMLlint flagged missing newline at EOF.
- name: My Other Data +
8-15: Unify product identity with Windows MSIX manifest.productIdentifier here ("com.mycompany.myproduct") differs from app_manifest.xml ("com.wails.dock"). Aligning avoids packaging/signing mismatches.
- productIdentifier: "com.mycompany.myproduct" # The unique product identifier - productName: "My Product" # The name of the application - description: "A program that does X" # The application description + productIdentifier: "com.wails.dock" # The unique product identifier + productName: "Wails Dock Example" # The name of the application + description: "Wails v3 Dock service example" # The application descriptionv3/pkg/services/dock/dock_linux.go (2)
11-12: Add compile-time interface assertion to catch signature drift early.Helps ensure linuxDock continues to satisfy the internal platformDock interface as it evolves.
type linuxDock struct{} +// Ensure linuxDock implements platformDock. +var _ platformDock = (*linuxDock)(nil)
29-37: Optional: log a debug message on Startup/Shutdown.A no-op is fine; emitting a debug log can help developers understand why nothing happens on Linux without digging into source.
I can add conditional logging using a package-level logger if you have one available in the service context.
v3/examples/badge-custom/main.go (4)
30-31: Minor grammar: “an macOS” → “on macOS”Tiny docstring polish.
Apply this diff:
- // 'Mac' options tailor the application when running an macOS. + // 'Mac' options tailor the application when running on macOS.
32-38: Clarify platform behavior for options (macOS ignores BadgeOptions)On macOS, NewWithOptions currently ignores BadgeOptions (per dock_darwin.go). To avoid confusing readers, either call dock.New() on macOS or add a comment noting options are Windows-only.
Example tweak:
- dockService := dock.NewWithOptions(dock.BadgeOptions{ + // Note: BadgeOptions have effect on Windows; macOS currently ignores them. + dockService := dock.NewWithOptions(dock.BadgeOptions{
70-75: Don’t terminate the app on minor badge failuresUsing log.Fatal here exits the app if RemoveBadge fails. Consider logging and returning to keep the app running.
Apply this diff:
- err := dockService.RemoveBadge() - if err != nil { - log.Fatal(err) - } + if err := dockService.RemoveBadge(); err != nil { + log.Printf("remove:badge failed: %v", err) + return + }
77-83: Defensive cast of event payload to avoid panicsA bad payload from the frontend will panic on a direct string assertion.
Apply this diff:
- text := event.Data.(string) - err := dockService.SetBadge(text) - if err != nil { - log.Fatal(err) - } + text, ok := event.Data.(string) + if !ok { + log.Printf("set:badge event payload is not a string: %#v", event.Data) + return + } + if err := dockService.SetBadge(text); err != nil { + log.Printf("set:badge failed: %v", err) + return + }v3/examples/dock/build/windows/nsis/project.nsi (1)
91-93: Use consistent NSIS directive spelling for shortcut creationNSIS directives are case-insensitive, but consistency helps readability. You use both CreateShortcut and CreateShortCut. Prefer the canonical CreateShortCut.
Apply this diff:
- CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}" + CreateShortCut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}" CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"v3/examples/dock/build/darwin/Taskfile.yml (1)
33-47: Avoid redundant frontend/icon generation in universal buildsbuild:universal invokes build twice, and each build reruns frontend/icon tasks via deps. For large frontends this adds time. Consider extracting artifact generation to a separate prerequisite task and making build steps depend only on the compiled Go outputs.
A pattern to consider:
- Create task build:binary that only compiles Go (no deps).
- Keep common:build:frontend and common:generate:icons as a prerequisite once for both arch builds.
docs/src/content/docs/learn/dock.mdx (2)
178-185: Minor copyedits for clarity and punctuationTighten phrasing and punctuation.
Apply this diff:
-1. **When hiding the dock icon (macOS):** - - Ensure users can still access your app (e.g., via [system tray](https://v3alpha.wails.io/learn/systray/)) +1. **When hiding the dock icon (macOS):** + - Ensure users can still access your app (e.g., via the [system tray](https://v3alpha.wails.io/learn/systray/)) - Include a "Quit" option in your alternative UI - - The app won't appear in Command+Tab switcher + - The app won't appear in the Command+Tab switcher - Open windows remain visible and functional - Closing all windows may not quit the app (macOS behavior varies) - Users lose the standard way to quit via Dock right-click
190-198: Minor copyedit to improve readabilitySmall wording tweak.
Apply this diff:
-4. **For Windows badge customization:** +4. **For Windows badge customization:** - Ensure high contrast between text and background colors - - Test with different text lengths as font size decreases with length + - Test with different text lengths, as the font size decreases as text length increases - Use common system fonts to ensure availabilityv3/examples/dock/build/Taskfile.yml (2)
28-34: Exclude node_modules and dist from frontend build sources to avoid heavy scans and false rebuildsScanning the entire frontend dir, including node_modules and dist, is slow and may cause unnecessary rebuilds.
Apply this diff:
sources: - "**/*" + sources_exclude: + - node_modules/**/* + - dist/**/*
74-81: Provide a default for VITE_PORT
dev:frontendrelies on{{.VITE_PORT}}but no default is set here. Add a default to make the task self-contained.Apply this diff:
dev:frontend: summary: Runs the frontend in development mode dir: frontend deps: - task: install:frontend:deps cmds: - npm run dev -- --port {{.VITE_PORT}} --strictPort + vars: + VITE_PORT: '{{.VITE_PORT | default "5173"}}'v3/pkg/services/dock/dock.go (1)
25-37: Optional: Add compile-time interface assertions for clarityInterface assertions help ensure DockService continues to satisfy the application service interfaces during refactors.
Add this near the top of the file (outside the selected lines):
var ( _ application.ServiceName = (*DockService)(nil) _ application.ServiceStartup = (*DockService)(nil) _ application.ServiceShutdown = (*DockService)(nil) )v3/examples/dock/build/linux/Taskfile.yml (1)
40-58: Double-check dir and path composition in create:appimageWith
dir: build/linux/appimagerelative to build/linux, the resolved dir becomes build/linux/build/linux/appimage. Consider switching dir to the app root and referencing paths directly, or set dir: ../.. and use absolute-ish paths.If you want to keep current structure, set:
- dir: build/linux/appimage + dir: ../..and adjust commands to:
- - cp {{.APP_BINARY}} {{.APP_NAME}} - - cp ../../appicon.png appicon.png - - wails3 generate appimage -binary {{.APP_NAME}} -icon {{.ICON}} -desktopfile {{.DESKTOP_FILE}} -outputdir {{.OUTPUT_DIR}} -builddir {{.ROOT_DIR}}/build/linux/appimage/build + - cp bin/{{.APP_NAME}} build/linux/appimage/{{.APP_NAME}} + - cp build/appicon.png build/linux/appimage/appicon.png + - wails3 generate appimage -binary build/linux/appimage/{{.APP_NAME}} -icon build/appicon.png -desktopfile build/linux/{{.APP_NAME}}.desktop -outputdir bin -builddir build/linux/appimage/buildI can send a complete, consistent set of diffs across all Linux packaging tasks if you confirm preferred working dir.
v3/pkg/services/dock/dock_windows.go (9)
74-83: Incorrect platform in comments + improve no-op stubsThese are Windows stubs but the comments say “macOS Dock”. Also, consider documenting the no-op status for Windows and optionally logging a warning to aid users.
-// HideAppIcon hides the app icon in the macOS Dock. +// HideAppIcon hides the app icon in the Windows taskbar (no-op on Windows for now). func (w *windowsDock) HideAppIcon() { - // No-op: researching Windows options + // No-op: researching Windows options. + // TODO: Evaluate feasible approaches (e.g. WS_EX_TOOLWINDOW, owner window patterns) and implement. } -// ShowAppIcon shows the app icon in the macOS Dock. +// ShowAppIcon shows the app icon in the Windows taskbar (no-op on Windows for now). func (w *windowsDock) ShowAppIcon() { - // No-op: researching Windows options + // No-op: researching Windows options. }If you want, I can prototype the Windows approach (taskbar visibility toggling via extended styles) behind a best-effort guard and feature flag.
21-27: State mutation not guarded; consider thread-safety or making drawing purely localbadgeImg and badgeSize are mutated/read across methods. If calls can happen concurrently, this can race. Either:
- Keep image/dimensions local per operation (preferred), or
- Add a mutex and guard read/writes.
Minimal addition:
type windowsDock struct { taskbar *w32.ITaskbarList3 badgeImg *image.RGBA badgeSize int fontManager *FontManager badgeOptions BadgeOptions + // mu protects badgeImg and badgeSize when called from multiple goroutines. + mu sync.Mutex }Then guard in createBadge/createBadgeIcon/createBadgeIconWithText. Alternatively, refactor createBadge to return an image without storing it on the struct, and pass that through to the icon creators.
85-124: Consolidate HWND acquisition + clarify empty label semantics
- The window handle acquisition code is duplicated across SetBadge/SetCustomBadge/RemoveBadge.
- Empty label currently draws a “dot” badge; some APIs treat empty as “remove”. Ensure docs/specs clarify this behavior to avoid confusion.
Refactor handle acquisition:
@@ - app := application.Get() - if app == nil { - return nil - } - - window := app.Window.Current() - if window == nil { - return nil - } - - nativeWindow := window.NativeWindow() - if nativeWindow == nil { - return errors.New("window native handle unavailable") - } - hwnd := uintptr(nativeWindow) + hwnd, err := w.getActiveHWND() + if err != nil { + return err + }Add helper outside this hunk:
// getActiveHWND resolves the HWND of the current window. func (w *windowsDock) getActiveHWND() (uintptr, error) { app := application.Get() if app == nil { return 0, nil } window := app.Window.Current() if window == nil { return 0, nil } nativeWindow := window.NativeWindow() if nativeWindow == nil { return 0, errors.New("window native handle unavailable") } return uintptr(nativeWindow), nil }If “empty label = dot” is intentional, consider documenting that in the GoDoc for SetBadge.
127-183: Reduce duplication and unify badge sizing
- badgeSize is hardcoded here but stored on the struct elsewhere (w.badgeSize = 32 in createBadge). Define a single constant and reuse to avoid drift.
Inside this hunk:
- const badgeSize = 32 + const badgeSize = defaultBadgeSizeAnd elsewhere (outside this hunk), define once:
const defaultBadgeSize = 32Also consider reusing the same background drawing routine as createBadge to keep rendering identical between SetBadge and SetCustomBadge.
211-234: createBadgeIcon: OK; note on stateWorks as intended over the image created by createBadge(). If you keep badgeImg as struct state, consider guarding (see earlier note) or return a fresh image per call to avoid races.
236-259: Helper createBadgeIcon: OKMirrors the method receiver variant for custom options; duplication is acceptable but could be unified to a single implementation that accepts a struct of dependencies.
297-305: Centering text: consider using window DPI instead of desktop DPIUsing GetDpiForWindow(GetDesktopWindow()) can vary from the app window DPI in per-monitor DPI aware setups. If feasible, pass the current HWND DPI to createBadgeIconWithText to improve alignment across monitors.
Example change (conceptual; requires passing hwnd):
- screen := w32.GetDesktopWindow() - dpi := w32.GetDpiForWindow(screen) + dpi := w32.GetDpiForWindow(hwnd)If plumbing hwnd here is heavy, the current approach is acceptable for a first pass.
Also applies to: 350-358
37-39: Outdated comment: “Badge Service” → “Dock Service”Update the doc comment to reflect the new consolidated Dock service.
-// Creates a new Badge Service. +// New creates a new Dock service.
369-391: createBadge: unify with a single size constant and consider local image
- Hardcoded 32 matches SetCustomBadge; unify via a single defaultBadgeSize constant.
- To avoid shared mutable state, consider returning img instead of storing it on the struct, and pass it to the icon creators.
- w.badgeSize = 32 + w.badgeSize = defaultBadgeSizeOutside this hunk (once per file):
const defaultBadgeSize = 32
| <key>NSHighResolutionCapable</key> | ||
| <string>true</string> |
There was a problem hiding this comment.
NSHighResolutionCapable must be a boolean, not a string
Using a string may cause the key to be ignored by macOS.
- <key>NSHighResolutionCapable</key>
- <string>true</string>
+ <key>NSHighResolutionCapable</key>
+ <true/>📝 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.
| <key>NSHighResolutionCapable</key> | |
| <string>true</string> | |
| <key>NSHighResolutionCapable</key> | |
| <true/> |
🤖 Prompt for AI Agents
In v3/examples/dock/build/darwin/Info.plist around lines 22 to 23, the
NSHighResolutionCapable key is currently set as a string; replace the
<string>true</string> value with a proper boolean plist entry (<true/>) so the
key is a boolean rather than a string, ensuring macOS recognizes it.
| GOOS: darwin | ||
| CGO_ENABLED: 1 | ||
| GOARCH: '{{.ARCH | default ARCH}}' | ||
| CGO_CFLAGS: "-mmacosx-version-min=10.15" | ||
| CGO_LDFLAGS: "-mmacosx-version-min=10.15" | ||
| MACOSX_DEPLOYMENT_TARGET: "10.15" | ||
| PRODUCTION: '{{.PRODUCTION | default "false"}}' |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Confirm GOARCH fallback resolves as intended
GOARCH: '{{.ARCH | default ARCH}}' relies on a global ARCH variable being available in the template scope. If not, GOARCH could become the literal string ARCH, breaking builds.
Please verify Task’s template resolution with this pattern elsewhere in the repo. If unsure, consider this safer alternative that falls back to the Task built-in ARCH variable:
env:
GOARCH: '{{ if .ARCH }}{{ .ARCH }}{{ else }}{{ ARCH }}{{ end }}'To check other usages, run:
🏁 Script executed:
#!/bin/bash
rg -n -C2 --type=yaml "default ARCH|GOARCH:" Length of output: 20640
Replace {{.ARCH | default ARCH}} with an explicit conditional fallback
The {{.ARCH | default ARCH}} pipeline will emit the literal string "ARCH" whenever .ARCH isn’t set—causing GOARCH to break. Update all instances to:
env:
GOARCH: '{{ if .ARCH }}{{ .ARCH }}{{ else }}{{ ARCH }}{{ end }}'Affected locations (non‐exhaustive):
- v3/examples/dock/build/{darwin,linux,windows}/Taskfile.yml
- v3/examples/notifications/build/{darwin,linux}/Taskfile.yml
- v3/examples/file-association/build/{Taskfile.darwin.yml,Taskfile.linux.yml,Taskfile.windows.yml}
- v3/examples/badge*/build/*/Taskfile.yml
- v3/examples/custom-protocol-example/build/{darwin,linux,windows}/Taskfile.yml
- v3/internal/commands/build_assets/{darwin,linux,windows}/Taskfile.yml
Please scan the repo for
GOARCH: '{{.ARCH | default ARCH}}'
and replace it with the conditional snippet above to ensure GOARCH falls back to the built-in ARCH correctly.
🤖 Prompt for AI Agents
In v3/examples/dock/build/darwin/Taskfile.yml around lines 25 to 31, the GOARCH
assignment uses the template pipeline '{{.ARCH | default ARCH}}' which emits the
literal string "ARCH" when .ARCH is unset; replace that line with an explicit
conditional fallback that checks .ARCH and falls back to ARCH (i.e. use an
if/.ARCH else/ARCH end pattern) so GOARCH correctly resolves; scan the repo for
all occurrences of GOARCH: '{{.ARCH | default ARCH}}' (including the listed
example and internal paths) and update each to use the explicit conditional
template snippet.
| fi | ||
|
|
||
| # Rename the generated AppImage | ||
| mv "${APP_NAME}*.AppImage" "${APP_NAME}.AppImage" |
There was a problem hiding this comment.
Bug: Glob is quoted; mv won’t match the AppImage file
Quoting the glob prevents expansion, so the move will fail under set -e. Fix by moving the quotes to only wrap the variable.
-mv "${APP_NAME}*.AppImage" "${APP_NAME}.AppImage"
+mv "${APP_NAME}"*.AppImage "${APP_NAME}.AppImage"🤖 Prompt for AI Agents
In v3/examples/dock/build/linux/appimage/build.sh around line 34 the mv command
quotes the whole glob so the shell does not expand "${APP_NAME}*.AppImage" and
the move fails under set -e; change the quoting so only the variable is quoted
and the glob can expand (e.g. mv "${APP_NAME}"*.AppImage "${APP_NAME}.AppImage")
ensuring the source pattern is unquoted so the shell can match the actual
AppImage file.
| # Default dependencies for Debian 12/Ubuntu 22.04+ with WebKit 4.1 | ||
| depends: | ||
| - libgtk-3-dev | ||
| - libwebkit2gtk-4.1-dev | ||
| - build-essential | ||
| - pkg-config | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Use runtime dependencies, not -dev and build toolchains
-dev and build-essential/pkg-config are not required at runtime and will force unnecessary toolchains onto end users. Prefer runtime libraries.
Example for Debian/Ubuntu:
depends:
- - libgtk-3-dev
- - libwebkit2gtk-4.1-dev
- - build-essential
- - pkg-config
+ - libgtk-3-0
+ - libwebkit2gtk-4.1-0Note: Exact package names vary across distributions/releases; please verify against your target distros.
📝 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.
| # Default dependencies for Debian 12/Ubuntu 22.04+ with WebKit 4.1 | |
| depends: | |
| - libgtk-3-dev | |
| - libwebkit2gtk-4.1-dev | |
| - build-essential | |
| - pkg-config | |
| # Default dependencies for Debian 12/Ubuntu 22.04+ with WebKit 4.1 | |
| depends: | |
| - libgtk-3-0 | |
| - libwebkit2gtk-4.1-0 |
🤖 Prompt for AI Agents
In v3/examples/dock/build/linux/nfpm/nfpm.yaml around lines 27-33, the package
list uses -dev and build-time packages which are not required at runtime;
replace libgtk-3-dev with the runtime libgtk-3-0, replace libwebkit2gtk-4.1-dev
with libwebkit2gtk-4.1-0 (or the exact runtime name for your target
distro/release), and remove build-essential and pkg-config from depends so only
runtime libraries are listed; verify exact package names per target distro and
adjust accordingly.
| # RPM packages for RHEL/CentOS/AlmaLinux/Rocky Linux (WebKit 4.0) | ||
| rpm: | ||
| depends: | ||
| - gtk3-devel | ||
| - webkit2gtk3-devel | ||
| - gcc-c++ | ||
| - pkg-config | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Fix RPM dependencies to runtime packages and remove build toolchains
For RPM families, depend on runtime libs (gtk3, webkit2gtk3), not -devel or compilers.
rpm:
depends:
- - gtk3-devel
- - webkit2gtk3-devel
- - gcc-c++
- - pkg-config
+ - gtk3
+ - webkit2gtk3This also removes the trailing whitespace YAML lint noted on Line 43.
📝 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.
| # RPM packages for RHEL/CentOS/AlmaLinux/Rocky Linux (WebKit 4.0) | |
| rpm: | |
| depends: | |
| - gtk3-devel | |
| - webkit2gtk3-devel | |
| - gcc-c++ | |
| - pkg-config | |
| # RPM packages for RHEL/CentOS/AlmaLinux/Rocky Linux (WebKit 4.0) | |
| rpm: | |
| depends: | |
| - gtk3 | |
| - webkit2gtk3 |
🧰 Tools
🪛 YAMLlint (1.37.1)
[error] 43-43: trailing spaces
(trailing-spaces)
🤖 Prompt for AI Agents
In v3/examples/dock/build/linux/nfpm/nfpm.yaml around lines 36 to 43, the RPM
dependencies incorrectly list build-time packages (gtk3-devel,
webkit2gtk3-devel, gcc-c++, pkg-config) and include trailing whitespace; replace
the -devel entries with their runtime counterparts (gtk3, webkit2gtk3), remove
build toolchain packages (gcc-c++, pkg-config) from the rpm.depends list, and
remove the trailing whitespace on line 43 so the YAML lists only runtime
libraries.
| - |- | ||
| if [ "{{.FORMAT | default "nsis"}}" = "msix" ]; then | ||
| task: create:msix:package | ||
| else | ||
| task: create:nsis:installer | ||
| fi | ||
| vars: |
There was a problem hiding this comment.
Broken conditional in package task: ‘task:’ inside shell script does not invoke a Task
Within a shell heredoc, task: create:... is literal text, not a Task invocation. The branch will always fail. Use Task’s cmd objects to call tasks or invoke task CLI explicitly.
Apply one of these fixes:
Option A (invoke subtasks via cmd objects and template condition):
- cmds:
- - |-
- if [ "{{.FORMAT | default "nsis"}}" = "msix" ]; then
- task: create:msix:package
- else
- task: create:nsis:installer
- fi
+ cmds:
+ - cmd: task create:msix:package
+ silent: true
+ platforms: [windows, linux, darwin]
+ status:
+ - '[ "{{.FORMAT | default "nsis"}}" != "msix" ]'
+ - cmd: task create:nsis:installer
+ silent: true
+ platforms: [windows, linux, darwin]
+ status:
+ - '[ "{{.FORMAT | default "nsis"}}" = "msix" ]'Option B (simpler): Split into two explicit tasks and let users call the desired one. I can draft that if preferred.
📝 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.
| - |- | |
| if [ "{{.FORMAT | default "nsis"}}" = "msix" ]; then | |
| task: create:msix:package | |
| else | |
| task: create:nsis:installer | |
| fi | |
| vars: | |
| cmds: | |
| - cmd: task create:msix:package | |
| silent: true | |
| platforms: [windows, linux, darwin] | |
| status: | |
| - '[ "{{.FORMAT | default "nsis"}}" != "msix" ]' | |
| - cmd: task create:nsis:installer | |
| silent: true | |
| platforms: [windows, linux, darwin] | |
| status: | |
| - '[ "{{.FORMAT | default "nsis"}}" = "msix" ]' | |
| vars: |
🤖 Prompt for AI Agents
In v3/examples/dock/build/windows/Taskfile.yml around lines 36 to 42, the shell
heredoc contains literal lines like "task: create:msix:package" which do not
invoke Task and thus the conditional branch always fails; replace this heredoc
approach with a real Task invocation by either (A) using Task's cmd objects and
templating to conditionally run subtasks (e.g., use a template/vars to choose
which cmd to run and set cmd to "task create:msix:package" or "task
create:nsis:installer"), or (B) split into two explicit tasks (create:msix and
create:nsis or similar) and remove the conditional, so callers run the specific
task they need; update vars/templates accordingly and ensure the cmd uses the
Task CLI invocation (or direct subtasks via dependencies) rather than embedding
"task:" text inside a shell heredoc.
| generate:syso: | ||
| summary: Generates Windows `.syso` file | ||
| dir: build | ||
| cmds: | ||
| - wails3 generate syso -arch {{.ARCH}} -icon windows/icon.ico -manifest windows/wails.exe.manifest -info windows/info.json -out ../wails_windows_{{.ARCH}}.syso | ||
| vars: | ||
| ARCH: '{{.ARCH | default ARCH}}' | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
generate:syso dir and paths look off
With dir: build under build/windows, the CLI runs in build/windows/build which likely doesn’t exist; relative paths for manifest/info will be wrong.
Apply this diff:
- generate:syso:
- summary: Generates Windows `.syso` file
- dir: build
+ generate:syso:
+ summary: Generates Windows `.syso` file
+ dir: ..
cmds:
- - wails3 generate syso -arch {{.ARCH}} -icon windows/icon.ico -manifest windows/wails.exe.manifest -info windows/info.json -out ../wails_windows_{{.ARCH}}.syso
+ - wails3 generate syso -arch {{.ARCH}} -icon windows/icon.ico -manifest windows/wails.exe.manifest -info windows/info.json -out wails_windows_{{.ARCH}}.syso
vars:
- ARCH: '{{.ARCH | default ARCH}}'
+ ARCH: '{{.ARCH | default (env "GOARCH") | default "amd64"}}'This runs in build/windows, keeping windows/* paths valid and placing the .syso at build/windows/wails_windows_{{ARCH}}.syso.
📝 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.
| generate:syso: | |
| summary: Generates Windows `.syso` file | |
| dir: build | |
| cmds: | |
| - wails3 generate syso -arch {{.ARCH}} -icon windows/icon.ico -manifest windows/wails.exe.manifest -info windows/info.json -out ../wails_windows_{{.ARCH}}.syso | |
| vars: | |
| ARCH: '{{.ARCH | default ARCH}}' | |
| generate:syso: | |
| summary: Generates Windows `.syso` file | |
| dir: .. | |
| cmds: | |
| - wails3 generate syso -arch {{.ARCH}} -icon windows/icon.ico -manifest windows/wails.exe.manifest -info windows/info.json -out wails_windows_{{.ARCH}}.syso | |
| vars: | |
| ARCH: '{{.ARCH | default (env "GOARCH") | default "amd64"}}' |
🤖 Prompt for AI Agents
In v3/examples/dock/build/windows/Taskfile.yml around lines 45 to 52, the task
sets dir: build which causes the command to run in build/windows/build and
breaks relative windows/* paths and output location; change dir to . (or remove
dir) so the command runs in build/windows, update the output path to
./wails_windows_{{.ARCH}}.syso (or ../ if you prefer different placement)
ensuring the manifest/info/icon relative paths remain
windows/wails.exe.manifest, windows/info.json, windows/icon.ico and the
generated .syso is created in build/windows as wails_windows_{{.ARCH}}.syso.
| import ( | ||
| "embed" | ||
| _ "embed" | ||
| "log" | ||
|
|
||
| "github.com/wailsapp/wails/v3/pkg/application" | ||
| "github.com/wailsapp/wails/v3/pkg/services/dock" | ||
| ) |
There was a problem hiding this comment.
Remove duplicate/blank import of "embed" — this will not compile
The import block includes both a normal and a blank import of "embed". The blank import has no side effects and will trigger an “imported and not used” error.
Apply this diff to fix:
import (
- "embed"
- _ "embed"
+ "embed"
"log"
"github.com/wailsapp/wails/v3/pkg/application"
"github.com/wailsapp/wails/v3/pkg/services/dock"
)📝 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.
| import ( | |
| "embed" | |
| _ "embed" | |
| "log" | |
| "github.com/wailsapp/wails/v3/pkg/application" | |
| "github.com/wailsapp/wails/v3/pkg/services/dock" | |
| ) | |
| import ( | |
| "embed" | |
| "log" | |
| "github.com/wailsapp/wails/v3/pkg/application" | |
| "github.com/wailsapp/wails/v3/pkg/services/dock" | |
| ) |
🤖 Prompt for AI Agents
In v3/examples/dock/main.go around lines 3 to 10 the import block contains both
a normal import of "embed" and a blank import (_ "embed"), which causes an
"imported and not used" compile error; remove the duplicate/blank import so
"embed" is only imported once (or remove the unused one entirely) and ensure the
import block only lists required packages.
| void hideDockIcon() { | ||
| [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory]; | ||
| } | ||
|
|
||
| void showDockIcon() { | ||
| [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; | ||
| } |
There was a problem hiding this comment.
UI calls must run on the main thread (risk of crashes)
Cocoa APIs like setActivationPolicy must be executed on the main thread. Ensure the Obj‑C bridge dispatches to the main queue; do not rely on service method call sites being on the main thread.
Apply this diff to guarantee main-thread execution using GCD (synchronous to avoid lifetime issues and ensure immediate effect):
#import <Cocoa/Cocoa.h>
+ #import <dispatch/dispatch.h>
-void hideDockIcon() {
- [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
-}
+static inline void runOnMain(void (^block)(void)) {
+ if ([NSThread isMainThread]) {
+ block();
+ } else {
+ dispatch_sync(dispatch_get_main_queue(), block);
+ }
+}
+
+void hideDockIcon() {
+ runOnMain(^{
+ [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
+ });
+}
-void showDockIcon() {
- [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
-}
+void showDockIcon() {
+ runOnMain(^{
+ [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
+ });
+}Note: dispatch_sync is safe here due to the isMainThread guard, and it ensures the effect occurs before returning to Go.
Also applies to: 58-66
🤖 Prompt for AI Agents
In v3/pkg/services/dock/dock_darwin.go around lines 10-16 (and also update the
similar block at 58-66), the calls to [NSApp setActivationPolicy:...] must be
dispatched to the main thread; wrap each call so it executes synchronously on
the main queue when not already on the main thread. Implement an is-main-thread
guard (using [NSThread isMainThread] or NSThread.isMainThread) and if false call
dispatch_sync(dispatch_get_main_queue(), ^{ ...call setActivationPolicy... });
otherwise call setActivationPolicy directly; add the necessary imports for
dispatch if missing. Ensure both hideDockIcon and showDockIcon use this pattern
so the UI calls always run on the main thread and take effect before returning.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (3)
v3/pkg/services/dock/dock.go (1)
39-42: ServiceName path is now correct (matches /pkg).This resolves the earlier mismatch noted in past reviews. Good catch.
v3/pkg/services/dock/dock_darwin.go (2)
6-9: Missing dispatch header causes build failures when using GCD.dispatch_async is used but <dispatch/dispatch.h> is not imported.
#cgo CFLAGS: -x objective-c #cgo LDFLAGS: -framework Cocoa #import <Cocoa/Cocoa.h> +#import <dispatch/dispatch.h>
10-20: Run UI calls on the main thread synchronously to avoid crashes and ordering bugs.Using dispatch_async introduces race conditions (and interacts badly with Go-managed C strings later). Switch to a robust runOnMain helper with synchronous dispatch.
-void hideDockIcon() { - dispatch_async(dispatch_get_main_queue(), ^{ - [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory]; - }); -} +static inline void runOnMain(void (^block)(void)) { + if ([NSThread isMainThread]) { + block(); + } else { + dispatch_sync(dispatch_get_main_queue(), block); + } +} + +void hideDockIcon() { + runOnMain(^{ + [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory]; + }); +} -void showDockIcon() { - dispatch_async(dispatch_get_main_queue(), ^{ - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - }); -} +void showDockIcon() { + runOnMain(^{ + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + }); +} -static void setBadge(const char *label) { - dispatch_async(dispatch_get_main_queue(), ^{ +static void setBadge(const char *label) { + runOnMain(^{ NSString *nsLabel = nil; if (label != NULL) { nsLabel = [NSString stringWithUTF8String:label]; } [[NSApp dockTile] setBadgeLabel:nsLabel]; [[NSApp dockTile] display]; - }); + }); }Also applies to: 22-31
🧹 Nitpick comments (3)
v3/pkg/services/dock/dock.go (2)
25-26: Fix GoDoc comment to satisfy lint (“starts with the identifier”).Prefer comments that start with the type name.
-// Service represents the dock service +// DockService represents the dock service. type DockService struct {
10-23: Interface surface is coherent across platforms; consider compile-time assertions.Optional: add compile-time interface satisfaction checks to catch regressions during refactors.
import ( "context" "image/color" "github.com/wailsapp/wails/v3/pkg/application" ) +// Compile-time interface assertions for service lifecycle. +var ( + _ application.ServiceName = (*DockService)(nil) + _ application.ServiceStartup = (*DockService)(nil) + _ application.ServiceShutdown= (*DockService)(nil) +)v3/pkg/services/dock/dock_darwin.go (1)
43-54: Polish GoDoc for exported constructors.Minor style improvement for godoc readability.
-// Creates a new Dock Service. +// New creates a new Dock service. func New() *DockService { @@ -// NewWithOptions creates a new dock service with badge options. -// Currently, options are not available on macOS and are ignored. +// NewWithOptions creates a new Dock service with badge options. +// On macOS, options are currently ignored. func NewWithOptions(options BadgeOptions) *DockService {
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
v3/pkg/services/dock/dock.go(1 hunks)v3/pkg/services/dock/dock_darwin.go(1 hunks)v3/pkg/services/dock/dock_windows.go(11 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- v3/pkg/services/dock/dock_windows.go
🧰 Additional context used
🧬 Code Graph Analysis (2)
v3/pkg/services/dock/dock.go (3)
v3/pkg/application/services.go (4)
ServiceOptions(17-40)ServiceName(73-75)ServiceStartup(98-100)ServiceShutdown(118-120)v3/examples/dock/frontend/bindings/github.com/wailsapp/wails/v3/pkg/services/dock/models.ts (1)
BadgeOptions(15-58)v3/examples/dock/frontend/bindings/image/color/models.ts (1)
RGBA(15-46)
v3/pkg/services/dock/dock_darwin.go (3)
v3/pkg/services/dock/dock_windows.go (2)
New(38-44)NewWithOptions(47-53)v3/pkg/services/dock/dock_linux.go (2)
New(16-20)NewWithOptions(25-27)v3/pkg/services/dock/dock.go (2)
DockService(26-28)BadgeOptions(31-37)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
- GitHub Check: Run Go Tests v3 (macos-latest, 1.24)
- GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
- GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (5)
v3/pkg/services/dock/dock.go (1)
54-77: Thin façade looks good; forwards appropriately to platform impl.Methods are concise and defer behavior to the platformDock. No issues here.
v3/pkg/services/dock/dock_darwin.go (4)
92-96: RemoveBadge path looks correct; now executes synchronously after earlier fix.No additional changes needed.
56-62: Startup/Shutdown no-ops are acceptable for darwin.Clear and explicit; no side effects. Keep as-is.
87-90: SetCustomBadge fallback is fine; document in API docs that options are ignored on macOS.No functional issues.
Please confirm the public docs explicitly note that macOS ignores BadgeOptions so developers aren’t surprised.
74-96: Behavior when label is empty (“●” default) vs. removing the badge—confirm intended API semantics.Empty string maps to a dot badge, while RemoveBadge clears it. If this differs from v2 behavior or docs, it can cause confusion.
Would you like me to update the docs/examples to clarify:
- SetBadge("") shows “●”
- RemoveBadge() clears the badge
?
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (4)
v3/pkg/services/dock/dock_darwin.go (4)
64-72: Ensure Hide/Show semantics are synchronous (otherwise callers see stale state).If C remains async, these methods may return before the Dock state updates. With the runOnMain change above, the state change is guaranteed before return.
Would you like me to add a short doc comment to these methods clarifying that they are synchronous and thread-safe after the C change?
5-9: Import GCD header; current code uses dispatch_ without including <dispatch/dispatch.h>.*Without importing the dispatch header, the file may fail to compile or rely on transitive includes. Be explicit.
#cgo LDFLAGS: -framework Cocoa #import <Cocoa/Cocoa.h> + #import <dispatch/dispatch.h>
10-20: Run Cocoa calls synchronously on the main thread (fix races, crashes, and ordering).Using dispatch_async introduces two problems:
- UI APIs must run on the main thread, and async defers execution non-deterministically.
- setBadge currently captures a C string that may be freed before the async block runs (use-after-free).
Adopt a small helper that synchronously hops to the main queue when needed. This guarantees effects before returning to Go and eliminates the lifetime bug.
-void hideDockIcon() { - dispatch_async(dispatch_get_main_queue(), ^{ - [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory]; - }); -} - -void showDockIcon() { - dispatch_async(dispatch_get_main_queue(), ^{ - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - }); -} - -static void setBadge(const char *label) { - dispatch_async(dispatch_get_main_queue(), ^{ - NSString *nsLabel = nil; - if (label != NULL) { - nsLabel = [NSString stringWithUTF8String:label]; - } - [[NSApp dockTile] setBadgeLabel:nsLabel]; - [[NSApp dockTile] display]; - }); -} +static inline void runOnMain(void (^block)(void)) { + if ([NSThread isMainThread]) { + block(); + } else { + dispatch_sync(dispatch_get_main_queue(), block); + } +} + +void hideDockIcon() { + runOnMain(^{ + [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory]; + }); +} + +void showDockIcon() { + runOnMain(^{ + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + }); +} + +static void setBadge(const char *label) { + runOnMain(^{ + NSString *nsLabel = nil; + if (label != NULL) { + nsLabel = [NSString stringWithUTF8String:label]; + } + [[NSApp dockTile] setBadgeLabel:nsLabel]; + [[NSApp dockTile] display]; + }); +}Also applies to: 22-31
75-86: SetBadge: fixed “allocate once + free once” is good, but still unsafe without synchronous C.As written, deferring C.free is correct only if C.setBadge completes badge string consumption before the free. With dispatch_async, the block runs later and dereferences freed memory. The runOnMain change above resolves this by completing on the main thread before returning.
If you prefer to keep async semantics (not recommended), you must not free from Go and instead copy inside C before scheduling, but that introduces leaks unless you refcount carefully. The synchronous approach is simpler and safer.
🧹 Nitpick comments (1)
v3/pkg/services/dock/dock_darwin.go (1)
56-62: Startup/Shutdown as no-ops are acceptable.Given all work is per-call UI state changes, making lifecycle methods no-ops on Darwin is reasonable. Consider adding a brief comment stating that AppKit state is manipulated on demand.
func (d *darwinDock) Startup(ctx context.Context, options application.ServiceOptions) error { - return nil + // macOS: no persistent resources to initialise; UI updates occur per call. + return nil } func (d *darwinDock) Shutdown() error { - return nil + // macOS: nothing to tear down for this service. + return nil }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
v3/pkg/services/dock/dock_darwin.go(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
v3/pkg/services/dock/dock_darwin.go (4)
v3/pkg/services/dock/dock_linux.go (2)
New(16-20)NewWithOptions(25-27)v3/pkg/services/dock/dock_windows.go (2)
New(38-44)NewWithOptions(47-53)v3/pkg/services/dock/dock.go (2)
DockService(26-28)BadgeOptions(31-37)v3/pkg/application/services.go (1)
ServiceOptions(17-40)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
- GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
- GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (3)
v3/pkg/services/dock/dock_darwin.go (3)
43-54: Constructors align with cross-platform shape.New/NewWithOptions mirror Linux/Windows implementations and keep macOS behavior simple (ignoring options). Good consistency with the service façade.
93-97: RemoveBadge path looks correct.Passing NULL clears the dock tile badge; no allocations involved. LGTM.
1-98: Replace raw dispatch_async calls with therunOnMainhelper
- In
v3/pkg/services/dock/dock_darwin.go(around lines 11, 17, and 23), the Cgo block still invokesPlease route these through the Go‐leveldispatch_async(dispatch_get_main_queue(), ^{ … });runOnMainhelper (and import<dispatch/dispatch.h>only if you continue to usedispatch_asyncdirectly) to centralize main‐thread UI scheduling.- Confirm that the Linux and Windows variants each provide both
in their respective files (func New() *DockService func NewWithOptions(BadgeOptions) *DockServicedock_linux.go,dock_windows.go) so the constructor shape stays consistent across platforms.- Ensure
darwinDockimplements every method of theplatformDockinterface inv3/pkg/services/dock/dock.go—namelyStartup,Shutdown,HideAppIcon,ShowAppIcon,SetBadge,SetCustomBadge, andRemoveBadge.
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
v3/UNRELEASED_CHANGELOG.md (1)
20-20: Clarify macOS-only scope and capitalize “Dock”; consider noting API rename/removal
- Capitalize “Dock” when referring to the macOS component.
- Make scope explicit (macOS-only for now; Windows/Linux stubs) to set expectations.
- Given the PR replaces the Badge service with a Dock service, add bullets under Changed/Removed to reflect the public API evolution.
Apply this diff to improve the Added entry:
-- Add macOS Dock service to hide/show app icon in the dock @popaprozac in [PR](https://github.com/wailsapp/wails/pull/4451) +- Add macOS Dock service to hide/show the application icon in the Dock (macOS-only in this release; Windows/Linux stubs) — @popaprozac in [#4451](https://github.com/wailsapp/wails/pull/4451)And consider appending to the Changed/Removed sections:
@@ ## Changed <!-- Changes in existing functionality --> - macOS: Use `visibleFrame` instead of `frame` for window centering to exclude menu bar and dock areas + - Public API: Replace Badge service with Dock service; rename `Options` to `BadgeOptions`; update frontend bindings and examples @@ ## Removed <!-- Features removed in this release --> + - Legacy Badge service, superseded by the Dock service APIAlso applies to: 22-25, 36-38
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
v3/UNRELEASED_CHANGELOG.md(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Run Go Tests v3 (macos-latest, 1.24)
- GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
* dock service * add docs and update comments * remove unneeded doc imports * update comment * update comment * update comment * update changelog * consolidate to dock service * update examples + thread safety * fix linux * update docs * thread safety * Update v3/pkg/services/dock/dock_darwin.go Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: Lea Anthony <lea.anthony@gmail.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>


Description
macOS Dock service that can hide/show the app icon in the dock.
Currently investigating platform norms and capabilities on Windows/Linux.
Fixes #3700
Type of change
Please select the option that is relevant.
How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration using
wails doctor.If you checked Linux, please specify the distro and version.
Test Configuration
Please paste the output of
wails doctor. If you are unable to run this command, please describe your environment in as much detail as possible.Checklist:
website/src/pages/changelog.mdxwith details of this PRSummary by CodeRabbit
New Features
Documentation
Examples
Chores