Skip to content

fix(appx): ship branded Store tiles instead of electron-builder's placeholders - #294

Open
EtienneLescot wants to merge 2 commits into
mainfrom
claude/windows-store-tile-icons-vmxibl
Open

fix(appx): ship branded Store tiles instead of electron-builder's placeholders#294
EtienneLescot wants to merge 2 commits into
mainfrom
claude/windows-store-tile-icons-vmxibl

Conversation

@EtienneLescot

@EtienneLescot EtienneLescot commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

Store certification rejected the 1.9.0 submission under 10.1.1.11 "On Device Tiles""The available product tile icons include a default image."

electron-builder reads AppX tile assets from <buildResources>/appx/, i.e. build/appx/. That directory did not exist, so AppXTarget.computeUserAssets() silently mapped its own vendored placeholders into every package:

appxAssets/SampleAppx.50x50.png   → assets\StoreLogo.png
appxAssets/SampleAppx.150x150.png → assets\Square150x150Logo.png
appxAssets/SampleAppx.44x44.png   → assets\Square44x44Logo.png
appxAssets/SampleAppx.310x150.png → assets\Wide310x150Logo.png

Those four files (extracted from the winCodeSign-2.6.0 bundle to confirm) are blank white squares. win.icon does not cover this path — it feeds the NSIS installer and the executable, not the tiles — so every Store submission so far carried a default image, with no warning anywhere in the build.

This PR adds build/appx/ (41 PNGs, 960 KB) generated from the 1024 px master icon, plus the generator behind npm run assets:appx.

Design notes

  • The four names above are load-bearing — they are exactly what electron-builder substitutes when absent. SmallTile, LargeTile and SplashScreen are opt-in: their <uap:> manifest attributes only appear when a matching file is present.
  • Each logo ships .scale-125/150/200 (plus .scale-400 on the small assets), and the 44×44 app-list icon also ships .targetsize-* and .targetsize-*_altform-unplated for the taskbar and Start list. This switches electron-builder onto its makepri.exe code path — verified that makepri.exe ships in the same vendored bundle as makeappx.exe (windows-10/x64/), so no new tooling requirement.
  • The unqualified 100 % file of every asset is kept as the neutral MRT candidate, so an unresolved scale qualifier still finds art rather than nothing.
  • appx.backgroundColor is transparent, so Windows paints the tile in the user's accent colour: tiles are a padded logo on a transparent canvas rather than full-bleed art, and the two tiles carrying showNameOnTiles shift their logo up to clear the name band.
  • Assets are committed, not generated during packaging. A build-time step goes missing exactly when someone packages from a checkout that skipped it, and the failure mode is a silently generic tile — the bug being fixed here.
  • The generator does its own PNG decode/encode on node:zlib. The dependency tree has no image library, and pulling one with native prebuilds in to draw seven static logos is not a trade worth making. Resampling averages in premultiplied alpha, so the transparent black outside the icon's rounded corners cannot bleed into the visible edge at small sizes.

directories.buildResources is now spelled out in electron-builder.json5. It was already the default — behaviour is unchanged — but the whole failure hinged on an implicit path, so it is documented rather than assumed.

Related issue

None — reported through Partner Center certification feedback, not a tracked issue.

Type of change

  • Bug fix
  • Feature
  • Enhancement
  • Documentation
  • Refactor / maintenance
  • Performance
  • Security

Release impact

  • Patch
  • Minor
  • Major / breaking change
  • No release note needed

Desktop impact

  • Windows
  • macOS
  • Linux
  • Installer / packaging
  • Not platform-specific

Screenshots / video

Before: there is nothing to show — the shipped 150×150 tile was SampleAppx.150x150.png, a blank white square from electron-builder's vendored bundle. That blankness is what certification flagged.

After — the generated tiles (rendered here at their 100 % dimensions; the transparent canvas is what Windows fills with the accent colour):

Square150x150Logo.scale-200 Wide310x150Logo.scale-200
StoreLogo Square44x44Logo.scale-200 SmallTile SplashScreen

Testing

Verified locally:

  • npm run assets:appx — 41 files, byte-identical across runs (deterministic output, so re-running never produces a spurious diff).
  • Every generated PNG checked programmatically for correct pixel dimensions and valid chunk CRCs; each one also opened and inspected visually.
  • Confirmed the root cause end to end by downloading winCodeSign-2.6.0.7z and extracting appxAssets/SampleAppx.150x150.png — a blank white square.
  • Confirmed windows-10/x64/makepri.exe exists in that same bundle, which is what the scaled-asset code path invokes.
  • node scripts/check-docs.mjs — OK (22 files). Biome clean on the changed JS/JSON.

Not verified locally: the .appx itself cannot be built here (Windows-only). A CI run of build.yml is in flight on this branch to exercise the makepri.exe path for the first time: https://github.com/getopenscreen/openscreen/actions/runs/31124509799 — the Windows Store package job is the one that matters. The resulting .appx is a ZIP, so assets/Square150x150Logo.png inside it can be checked directly before the next submission.

Note for the next Store submission: this correction round still has to be uploaded manually. Automated submission via the msstore CLI requires the app to be published and live in the Store first, which it is not while 1.9.0 sits rejected.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JZV4hzYgcpTfL15wkxyYQz

Summary by CodeRabbit

  • New Features

    • Added generation of Microsoft Store AppX/MSIX tile assets in required sizes and scales.
    • Added an npm command to create the Windows Store assets.
  • Documentation

    • Documented Windows Store tile requirements, generation steps, scaling variants, and transparent tile behavior.
  • Build Improvements

    • Updated packaging configuration to use the designated build resources directory.
    • Added automated validation to ensure packaged Store assets match the generated tiles.

…ceholders

Store certification rejected 1.9.0 under 10.1.1.11 "On Device Tiles": "The
available product tile icons include a default image."

electron-builder reads AppX tile assets from `<buildResources>/appx/` and, when
that directory is missing, silently maps its own vendored placeholders into the
package — SampleAppx.50x50/150x150/44x44/310x150.png from the winCodeSign
bundle, which are blank white squares. `win.icon` does not cover this: it feeds
the NSIS installer and the executable, not the tiles. So every Store submission
so far carried a default image and nothing in the build warned about it.

Add build/appx/, generated from the 1024px master icon by the new
`npm run assets:appx`. Each of the seven logos ships its 100% file plus
.scale-125/150/200 (and .scale-400 on the small ones), and the 44x44 app-list
icon also ships .targetsize-* and .targetsize-*_altform-unplated for the taskbar
and Start list. The unqualified file of every asset is kept as the neutral MRT
candidate so an unresolved qualifier still finds art rather than falling back to
nothing.

The generator does its own PNG decode/encode on node:zlib — the dependency tree
has no image library, and pulling one with native prebuilds in to draw seven
static logos is not a trade worth making. Resampling averages in premultiplied
alpha so the transparent black outside the icon's rounded corners cannot bleed
into the visible edge at small sizes.

Assets are committed rather than generated during packaging: a build-time step
goes missing exactly when someone packages from a checkout that skipped it, and
the failure mode is a silently generic tile, which is the bug being fixed here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZV4hzYgcpTfL15wkxyYQz
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The project now generates Microsoft Store AppX/MSIX tile assets from a source PNG. Electron Builder uses build for resources. The Windows workflow verifies packaged assets against generated files. Documentation describes the process.

Changes

AppX asset generation

Layer / File(s) Summary
Asset generation wiring
electron-builder.json5, package.json, scripts/generate-appx-assets.mjs
Electron Builder uses build for resources. The assets:appx script runs the generator. The generator defines the source icon, output directory, tile variants, scales, and target sizes.
PNG processing and tile composition
scripts/generate-appx-assets.mjs
The generator validates and decodes RGBA PNG files, performs premultiplied-alpha resampling, composes transparent tiles, and encodes PNG output.
Asset output and packaging verification
scripts/generate-appx-assets.mjs, .github/workflows/build.yml, technical-documentation/engineering/build-and-packaging.md
The generator recreates build/appx/, writes configured variants, and reports the output count. The workflow compares packaged PNG hashes with generated assets. The documentation describes filenames, regeneration, packaging, and transparent tile layout.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Developer
  participant AssetsScript as assets:appx
  participant Generator as generate-appx-assets.mjs
  participant Builder as Electron Builder
  participant Workflow as Windows build workflow

  Developer->>AssetsScript: run asset generation
  AssetsScript->>Generator: invoke generator
  Generator->>Generator: decode, resample, and encode PNG variants
  Generator->>Builder: provide assets in build/appx
  Builder-->>Workflow: produce .appx package
  Workflow->>Workflow: compare packaged PNG hashes
Loading

Possibly related PRs

Suggested reviewers: siddharthvaddem

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: replacing Electron Builder's default AppX placeholders with branded Store tiles.
Description check ✅ Passed The description covers the change, issue context, classifications, platform impact, visual evidence, testing, and known Windows-only limitations.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/windows-store-tile-icons-vmxibl

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.

The tile substitution that failed certification is invisible from the outside:
electron-builder maps a placeholder in for any name missing from build/appx/ and
says nothing about it, so a renamed or dropped asset would silently reintroduce
the exact bug the previous commit fixes — and we would only learn about it from
Partner Center, days later, on a submission that has to be redone.

Open the built .appx (it is a ZIP) and compare every committed asset against the
copy actually inside it, by hash. Missing or substituted, either way the build
fails with the asset named.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZV4hzYgcpTfL15wkxyYQz

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 @.github/workflows/build.yml:
- Around line 103-117: Replace the `$expected = Get-ChildItem build/appx -Filter
*.png` source in the APPX validation block with an independent authoritative
asset manifest or clean generator output. Validate the complete expected
filename set against `$entries`, report missing or unexpected PNGs, and retain
hash comparisons for every expected asset so deleted source files or
placeholders cannot pass.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a1a1c0b0-26b3-432a-89a6-8b33d7907ac7

📥 Commits

Reviewing files that changed from the base of the PR and between b81914c and acb4096.

📒 Files selected for processing (1)
  • .github/workflows/build.yml

Comment on lines +103 to +117
$expected = Get-ChildItem build/appx -Filter *.png
foreach ($asset in $expected) {
$entry = $entries["assets/$($asset.Name)"]
if (-not $entry) { $problems += "missing from package: $($asset.Name)"; continue }
$stream = $entry.Open()
try { $packaged = [BitConverter]::ToString($sha.ComputeHash($stream)) }
finally { $stream.Dispose() }
$source = [BitConverter]::ToString($sha.ComputeHash([IO.File]::ReadAllBytes($asset.FullName)))
if ($packaged -ne $source) { $problems += "packaged copy differs from build/appx: $($asset.Name)" }
}
if ($problems) {
$problems | ForEach-Object { Write-Output "::error::$_" }
throw "$($problems.Count) tile asset problem(s) in $($appx.Name)"
}
Write-Output "$($expected.Count) tile assets present in $($appx.Name), byte-identical to build/appx/"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Use an independent expected asset list.

Line 103 derives $expected from the same build/appx directory that supplies the package. If a required PNG is deleted, its name is absent from $expected, so the loop can pass while the package contains a placeholder. An empty directory also reports success with zero assets.

Load expected names from an authoritative manifest or a clean generator output. Compare the complete filename set and hashes.

🤖 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 @.github/workflows/build.yml around lines 103 - 117, Replace the `$expected =
Get-ChildItem build/appx -Filter *.png` source in the APPX validation block with
an independent authoritative asset manifest or clean generator output. Validate
the complete expected filename set against `$entries`, report missing or
unexpected PNGs, and retain hash comparisons for every expected asset so deleted
source files or placeholders cannot pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants