Skip to content

feat(v3): make DMG icon layout configurable - #5864

Open
leaanthony wants to merge 2 commits into
masterfrom
feature/dmg-icon-layout
Open

feat(v3): make DMG icon layout configurable#5864
leaanthony wants to merge 2 commits into
masterfrom
feature/dmg-icon-layout

Conversation

@leaanthony

@leaanthony leaanthony commented Aug 1, 2026

Copy link
Copy Markdown
Member

Summary

Makes Finder icon placement in Wails DMG packages configurable instead of hard-coded.

  • Adds --icon-layout auto|manual to wails3 tool package.
  • Adds --icon-positions 'name=x,y;name=x,y' for manual Finder-window pixel centres.
  • Adds DMG_ICON_LAYOUT and DMG_ICON_POSITIONS to generated Darwin Taskfiles.
  • Uses library auto layout by default; manual mode requires an exact position for every packaged item and rejects unknown or malformed entries.
  • Documents both modes and adds unit coverage for defaults, manual positioning, and invalid input.

Validation

  • go test ./internal/commands ./internal/flags ./internal/templates
  • Built the CLI and verified both new flags are exposed by wails3 tool package --help.

Summary by CodeRabbit

  • New Features

    • Added configurable DMG icon layouts, including automatic placement or manually specified icon coordinates.
    • Added support for positioning applications, folders, and additional files within DMG packages.
    • Added validation and clear error handling for invalid layouts, coordinates, missing positions, and unknown items.
  • Documentation

    • Documented DMG icon layout options, defaults, and coordinate-based positioning configuration.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d8288e92-96ec-41d0-be5e-ac82b6907d0e

📥 Commits

Reviewing files that changed from the base of the PR and between 29ba819 and b642b11.

📒 Files selected for processing (5)
  • docs/src/content/docs/guides/build/macos.mdx
  • v3/internal/commands/build_assets/darwin/Taskfile.yml
  • v3/internal/commands/tool_package.go
  • v3/internal/commands/tool_package_test.go
  • v3/internal/flags/package.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • v3/internal/flags/package.go
  • docs/src/content/docs/guides/build/macos.mdx
  • v3/internal/commands/build_assets/darwin/Taskfile.yml
  • v3/internal/commands/tool_package_test.go
  • v3/internal/commands/tool_package.go

Walkthrough

DMG packaging now supports automatic or manual icon layouts. Manual layouts accept named Finder-window coordinates. The implementation validates layout modes, coordinates, required DMG items, and unknown items. Task configuration, flags, tests, and macOS documentation expose the new settings.

Changes

DMG icon layout

Layer / File(s) Summary
Layout configuration and task wiring
v3/internal/flags/package.go, v3/internal/commands/build_assets/darwin/Taskfile.yml
Adds DMG layout and position settings to ToolPackage and passes them through the Darwin create:dmg task.
Layout selection and coordinate parsing
v3/internal/commands/tool_package.go
Supports auto and manual layouts. Manual coordinates use name=x,y entries. Invalid layouts, coordinates, missing positions, and unknown items return errors.
Validation coverage and usage documentation
v3/internal/commands/tool_package_test.go, docs/src/content/docs/guides/build/macos.mdx
Tests cover valid and invalid configurations, including names with spaces. Documentation describes the layout variables and coordinate syntax.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ToolPackage
  participant createDmgTask
  participant buildDMGOptions
  participant DMGTool
  ToolPackage->>createDmgTask: DMG layout and position settings
  createDmgTask->>buildDMGOptions: Layout arguments
  buildDMGOptions->>buildDMGOptions: Validate and parse configuration
  buildDMGOptions->>DMGTool: Generated icon options
Loading

Possibly related PRs

Poem

A rabbit places icons with care,
In automatic order or chosen pair.
Coordinates guide each file in view,
Invalid points are rejected too.
The DMG layout is ready to share.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the feature and validation, but it omits the linked issue, change type, platform checklist, test configuration, and required checklist. Add the linked issue, select the change type, provide platform and wails doctor test details, and complete the repository checklist.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: configurable DMG icon layout.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/dmg-icon-layout

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added Documentation Improvements or additions to documentation cli v3-alpha labels Aug 1, 2026
@leaanthony
leaanthony marked this pull request as ready for review August 1, 2026 03:43
Copilot AI review requested due to automatic review settings August 1, 2026 03:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes DMG Finder icon placement configurable for wails3 tool package, replacing the previously hard-coded layout with either library-driven auto layout or explicit, validated manual coordinates. It also propagates the new configuration into the Darwin Taskfile template and documentation, with test coverage for the new behavior.

Changes:

  • Added --icon-layout auto|manual and --icon-positions flags, with parsing/validation for manual positioning.
  • Updated the Darwin Taskfile template to pass DMG_ICON_LAYOUT/DMG_ICON_POSITIONS through to the packager.
  • Updated macOS DMG documentation and expanded unit tests around layout defaults and invalid input.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
v3/internal/flags/package.go Adds new CLI flags for DMG icon layout and manual icon positioning.
v3/internal/commands/tool_package.go Removes hard-coded icon positions and introduces layout selection + parsing/validation helpers.
v3/internal/commands/tool_package_test.go Adds/updates unit tests for auto default behavior, manual layout, and invalid inputs.
v3/internal/commands/build_assets/darwin/Taskfile.yml Wires new DMG layout/position variables into the generated DMG packaging task.
docs/src/content/docs/guides/build/macos.mdx Documents new DMG icon layout modes and related Taskfile variables.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +149 to +155
for name := range iconPositions {
if name != "Applications" {
if _, ok := opts.Files[name]; !ok {
return fmt.Errorf("DMG icon position references unknown item %q", name)
}
}
}
Comment on lines +188 to +200
xy := strings.Split(coordinates, ",")
if len(xy) != 2 {
return nil, fmt.Errorf("invalid DMG icon position %q: expected name=x,y", item)
}
x, err := strconv.Atoi(strings.TrimSpace(xy[0]))
if err != nil {
return nil, fmt.Errorf("invalid DMG icon X coordinate in %q: %w", item, err)
}
y, err := strconv.Atoi(strings.TrimSpace(xy[1]))
if err != nil {
return nil, fmt.Errorf("invalid DMG icon Y coordinate in %q: %w", item, err)
}
positions[strings.TrimSpace(name)] = dmg.IconPosition{X: x, Y: y}
Comment on lines +125 to +128
# auto lets the DMG library distribute every icon. For manual placement,
# use `manual` and specify every position as name=x,y;name=x,y.
DMG_ICON_LAYOUT: auto
DMG_ICON_POSITIONS: ""

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@v3/internal/commands/tool_package.go`:
- Around line 173-206: Update parseDMGIconPositions to detect when the trimmed
icon name already exists in positions before assigning it, and return an error
matching the existing duplicate-entry pattern instead of overwriting the prior
position. Preserve the current coordinate validation and successful parsing
behavior for unique names.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1d6d7924-acff-4151-828a-cd749c4aa985

📥 Commits

Reviewing files that changed from the base of the PR and between c7f7536 and 29ba819.

📒 Files selected for processing (5)
  • docs/src/content/docs/guides/build/macos.mdx
  • v3/internal/commands/build_assets/darwin/Taskfile.yml
  • v3/internal/commands/tool_package.go
  • v3/internal/commands/tool_package_test.go
  • v3/internal/flags/package.go

Comment on lines +173 to +206
func parseDMGIconPositions(value string) (map[string]dmg.IconPosition, error) {
if strings.TrimSpace(value) == "" {
return nil, fmt.Errorf("manual DMG icon layout requires icon positions")
}

positions := make(map[string]dmg.IconPosition)
for _, item := range strings.Split(value, ";") {
item = strings.TrimSpace(item)
if item == "" {
continue
}
name, coordinates, ok := strings.Cut(item, "=")
if !ok || strings.TrimSpace(name) == "" {
return nil, fmt.Errorf("invalid DMG icon position %q: expected name=x,y", item)
}
xy := strings.Split(coordinates, ",")
if len(xy) != 2 {
return nil, fmt.Errorf("invalid DMG icon position %q: expected name=x,y", item)
}
x, err := strconv.Atoi(strings.TrimSpace(xy[0]))
if err != nil {
return nil, fmt.Errorf("invalid DMG icon X coordinate in %q: %w", item, err)
}
y, err := strconv.Atoi(strings.TrimSpace(xy[1]))
if err != nil {
return nil, fmt.Errorf("invalid DMG icon Y coordinate in %q: %w", item, err)
}
positions[strings.TrimSpace(name)] = dmg.IconPosition{X: x, Y: y}
}
if len(positions) == 0 {
return nil, fmt.Errorf("manual DMG icon layout requires icon positions")
}
return positions, nil
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject duplicate names in manual icon positions.

parseDMGIconPositions silently overwrites an earlier entry when the same name appears twice in the ;-separated list. addDMGFiles, in the same file, explicitly rejects a duplicate name with "DMG file %q conflicts with an existing entry". Apply the same fail-loud pattern here so a typo does not silently discard a position.

🛠️ Proposed fix to reject duplicate position names
 		name, coordinates, ok := strings.Cut(item, "=")
 		if !ok || strings.TrimSpace(name) == "" {
 			return nil, fmt.Errorf("invalid DMG icon position %q: expected name=x,y", item)
 		}
+		name = strings.TrimSpace(name)
+		if _, exists := positions[name]; exists {
+			return nil, fmt.Errorf("duplicate DMG icon position for %q", name)
+		}
 		xy := strings.Split(coordinates, ",")
 		if len(xy) != 2 {
 			return nil, fmt.Errorf("invalid DMG icon position %q: expected name=x,y", item)
 		}
 		x, err := strconv.Atoi(strings.TrimSpace(xy[0]))
 		if err != nil {
 			return nil, fmt.Errorf("invalid DMG icon X coordinate in %q: %w", item, err)
 		}
 		y, err := strconv.Atoi(strings.TrimSpace(xy[1]))
 		if err != nil {
 			return nil, fmt.Errorf("invalid DMG icon Y coordinate in %q: %w", item, err)
 		}
-		positions[strings.TrimSpace(name)] = dmg.IconPosition{X: x, Y: y}
+		positions[name] = dmg.IconPosition{X: x, Y: y}
📝 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.

Suggested change
func parseDMGIconPositions(value string) (map[string]dmg.IconPosition, error) {
if strings.TrimSpace(value) == "" {
return nil, fmt.Errorf("manual DMG icon layout requires icon positions")
}
positions := make(map[string]dmg.IconPosition)
for _, item := range strings.Split(value, ";") {
item = strings.TrimSpace(item)
if item == "" {
continue
}
name, coordinates, ok := strings.Cut(item, "=")
if !ok || strings.TrimSpace(name) == "" {
return nil, fmt.Errorf("invalid DMG icon position %q: expected name=x,y", item)
}
xy := strings.Split(coordinates, ",")
if len(xy) != 2 {
return nil, fmt.Errorf("invalid DMG icon position %q: expected name=x,y", item)
}
x, err := strconv.Atoi(strings.TrimSpace(xy[0]))
if err != nil {
return nil, fmt.Errorf("invalid DMG icon X coordinate in %q: %w", item, err)
}
y, err := strconv.Atoi(strings.TrimSpace(xy[1]))
if err != nil {
return nil, fmt.Errorf("invalid DMG icon Y coordinate in %q: %w", item, err)
}
positions[strings.TrimSpace(name)] = dmg.IconPosition{X: x, Y: y}
}
if len(positions) == 0 {
return nil, fmt.Errorf("manual DMG icon layout requires icon positions")
}
return positions, nil
}
func parseDMGIconPositions(value string) (map[string]dmg.IconPosition, error) {
if strings.TrimSpace(value) == "" {
return nil, fmt.Errorf("manual DMG icon layout requires icon positions")
}
positions := make(map[string]dmg.IconPosition)
for _, item := range strings.Split(value, ";") {
item = strings.TrimSpace(item)
if item == "" {
continue
}
name, coordinates, ok := strings.Cut(item, "=")
if !ok || strings.TrimSpace(name) == "" {
return nil, fmt.Errorf("invalid DMG icon position %q: expected name=x,y", item)
}
name = strings.TrimSpace(name)
if _, exists := positions[name]; exists {
return nil, fmt.Errorf("duplicate DMG icon position for %q", name)
}
xy := strings.Split(coordinates, ",")
if len(xy) != 2 {
return nil, fmt.Errorf("invalid DMG icon position %q: expected name=x,y", item)
}
x, err := strconv.Atoi(strings.TrimSpace(xy[0]))
if err != nil {
return nil, fmt.Errorf("invalid DMG icon X coordinate in %q: %w", item, err)
}
y, err := strconv.Atoi(strings.TrimSpace(xy[1]))
if err != nil {
return nil, fmt.Errorf("invalid DMG icon Y coordinate in %q: %w", item, err)
}
positions[name] = dmg.IconPosition{X: x, Y: y}
}
if len(positions) == 0 {
return nil, fmt.Errorf("manual DMG icon layout requires icon positions")
}
return positions, nil
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/internal/commands/tool_package.go` around lines 173 - 206, Update
parseDMGIconPositions to detect when the trimmed icon name already exists in
positions before assigning it, and return an error matching the existing
duplicate-entry pattern instead of overwriting the prior position. Preserve the
current coordinate validation and successful parsing behavior for unique names.

@leaanthony
leaanthony force-pushed the feature/dmg-icon-layout branch from 29ba819 to b642b11 Compare August 1, 2026 03:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli Documentation Improvements or additions to documentation v3-alpha

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants