Repository navigation
Refuse images far past the screen before they reach the watch - #78
Merged
Merged
Conversation
A user flashed a face with a 1386x1386 AOD image to a CMF Watch 3 Pro. The firmware decodes every bitmap into RAM when it loads a face, ran out, and the watch now restarts in a loop that nothing can connect through. Two guards: - Before any install, editor or marketplace, flashFx runs assertFlashable, which refuses a file with an image side over 480 px. Official faces top out at 467. A file we can't parse is let through to the watch's own installer, as before. - Images added in the editor (new frames, replaced frames, ring art, JPEG backgrounds) are scaled down to fit the 466 px panel. The troubleshooting page no longer says a bad face is always harmless, and lists the recovery steps worth trying. None of them is confirmed yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nha4vjfwn4ueN8dzTVyDWr
freethinkel
added a commit
that referenced
this pull request
Oct 20, 2026
The 480 px cap from #78 refused a factory Watch 3 Pro face (Gravity_well, 576x530), so it was stricter than what the firmware accepts. Every other stock face, Pro 2 and Pro 3, stays at or under 470. The hard limit is now 580. Between 470 and 580, flashFx holds the upload and the new size dialog says what the risk is. The face goes to the watch only after "Install anyway". Accepting marks the pending payload, so a full watch's slot dialog retry doesn't ask again. Closing the dialog any other way drops the flash. On the marketplace this turns about 56 refusals into confirmations. Faces over 580 are still refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vSR9vwRaYBqnq7ioL9dQ1
freethinkel
added a commit
that referenced
this pull request
Oct 20, 2026
The 480 px cap from #78 refused a factory Watch 3 Pro face (Gravity_well, 576x530), so it was stricter than what the firmware accepts. Every other stock face, Pro 2 and Pro 3, stays at or under 470. The hard limit is now 580. Between 470 and 580, flashFx holds the upload and the new size dialog says what the risk is. The face goes to the watch only after "Install anyway". Accepting marks the pending payload, so a full watch's slot dialog retry doesn't ask again. Closing the dialog any other way drops the flash. On the marketplace this turns about 56 refusals into confirmations. Faces over 580 are still refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vSR9vwRaYBqnq7ioL9dQ1
freethinkel
added a commit
that referenced
this pull request
Oct 20, 2026
… dial go (#84) * Refuse an AOD screen with no thumbnail, count Pro 3 slots, let a gone dial go Three of the four open points in #49: - The FMC face that crash-loops a Watch 3 Pro on AOD (market 6vkrot44d1oxb19) has an AOD screen with no 0x28 preview node. Every stock AOD screen has one: 129/129 on the Pro 3, 94/94 on the Pro 2. The face reported as working (f4uj4uqrpefo4f2) has one too. assertFlashable now refuses such a file before any install, and the editor's export puts a blank preview back (withPreviews) and bakes it, so opening the face in the editor and flashing it from there fixes it. The tree lets the Preview layer be deleted, which is how a face loses it. - The slot count is per model: 7 on a Watch 3 Pro, as the reporter counted after a factory reset, 6 otherwise. Neither number is measured here. - A dial flashed from this browser stays in the watch list until FMC itself overwrites it. a055 never lists side-loaded dials, so the list only knows them from a local claim, and deleting one on the watch can't reach that claim. Those dials now get a remove button that drops the claim. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vSR9vwRaYBqnq7ioL9dQ1 * Install images up to 580 px, but only after the user accepts the risk The 480 px cap from #78 refused a factory Watch 3 Pro face (Gravity_well, 576x530), so it was stricter than what the firmware accepts. Every other stock face, Pro 2 and Pro 3, stays at or under 470. The hard limit is now 580. Between 470 and 580, flashFx holds the upload and the new size dialog says what the risk is. The face goes to the watch only after "Install anyway". Accepting marks the pending payload, so a full watch's slot dialog retry doesn't ask again. Closing the dialog any other way drops the flash. On the marketplace this turns about 56 refusals into confirmations. Faces over 580 are still refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018vSR9vwRaYBqnq7ioL9dQ1 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A user flashed a face with a 1386×1386 AOD image to a CMF Watch 3 Pro. The firmware decodes every bitmap into RAM when it loads a face, ran out of memory, and the watch now restarts in a loop (logo, black, restart) that neither Nothing X nor FMC can connect through.
Changes
assertFlashableincore/format/bin.tsrefuses a file with any image side overMAX_IMAGE_SIDE(480). It runs inflashFx, which both the editor and the marketplace install go through, so a face that is already published is covered too. Official faces top out at 467 (scanned all oftests/__fixtures__). A file we can't parse still goes to the watch's own installer, as it did before.resourceFromFilescales an added image down to the 466 px panel. It never scales up. A replaced JPEG frame gets re-encoded the same way.Known ceiling
480 is a cap on each image's side. It is not a measured memory budget. A manual resize or a turned full-screen background can still go past it, and the flash guard then refuses the file. This is deliberately strict until someone measures where the firmware actually falls over.
Tests
tests/flashable.test.ts: an official face passes, and a 1386×1386 resource is refused.tests/fit-screen.browser.test.ts: a dropped 1386×693 image becomes 466×233, and a small one keeps its size.pnpm test: 307 passed.🤖 Generated with Claude Code
https://claude.ai/code/session_01Nha4vjfwn4ueN8dzTVyDWr