Skip to content

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

Merged
EtienneLescot merged 3 commits into
mainfrom
claude/windows-store-tile-icons-vmxibl
Aug 8, 2026
Merged

fix(appx): ship branded Store tiles instead of electron-builder's placeholders#294
EtienneLescot merged 3 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 Windows Store assets.
  • Documentation

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

    • Updated packaging resources for Windows Store builds.
    • 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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dc09941b-d03b-4c47-852b-3babfdb24042

📥 Commits

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

📒 Files selected for processing (2)
  • .github/workflows/build.yml
  • scripts/generate-appx-assets.mjs
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/build.yml

📝 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 validates the master-image capacity, recreates build/appx/, writes configured variants, and reports the output count. The workflow checks the exact asset list and compares packaged PNG hashes. 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
  participant AppxPackage as .appx package

  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->>AppxPackage: create .appx package
  Workflow->>AppxPackage: compare packaged PNG hashes
  AppxPackage-->>Workflow: report missing or differing assets
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes replacing electron-builder placeholders with branded Windows Store tiles.
Description check ✅ Passed The description covers the summary, issue context, change type, release impact, Windows packaging impact, visuals, and testing status.
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 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 thread .github/workflows/build.yml Outdated
…ld/appx

The verification read build/appx/ to decide which assets to look for, which makes
it blind in the one case it exists for: delete a PNG there and the name drops out
of the expected set too, so the loop passes while electron-builder swaps its blank
placeholder in under that exact name. An empty directory reported success with
zero assets checked.

The generator now owns the list — `--list` prints it, derived from ASSETS and
TARGET_SIZES with no filesystem involved — and CI compares that list to both the
committed directory (missing, or hand-added files) and the package. Refactoring
the names out of the write loop also folded the targetsize variants into the same
compose() path; at size == canvas with no shift it is the same pixels, and the
regenerated assets are byte-identical.

Reported by CodeRabbit on #294.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZV4hzYgcpTfL15wkxyYQz
@EtienneLescot
EtienneLescot merged commit aacdefb into main Aug 8, 2026
18 checks passed
@EtienneLescot
EtienneLescot deleted the claude/windows-store-tile-icons-vmxibl branch August 8, 2026 11:22
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