Texture fills show in the vector tab as soon as they load (#196)
game-maker / gate (push) Successful in 2m21s
game-maker / suite (push) Failing after 7m29s

After signing out and back in, a texture fill stayed in its base
colour until something else redrew the vector tab. The texture
library is a singleton that outlives the editor. Four widgets
subscribed to it and never unsubscribed. After a new sign-in the
first loaded texture called a destroyed canvas, which threw and
stopped the loop before the vector tab heard about the image.

- TextureCanvas, TileCanvas, ShaderPreview and WorldViewport now
  unsubscribe in destroy(). This is the pattern FontPreview and
  VectorArtCanvas already use. WorldViewport leaves both the
  texture and the font library.
- FileLibrary.notifyChange catches errors per listener. A broken
  listener no longer starves the others. The error is still
  logged as "[textures] a listener failed".
- e2e 47i no longer forces a redraw. It waits up to 3 s for the
  tab to show the texture on its own.

Findings:
- The trigger was e2e module 45 (sign out, sign in), not the
  texture fill. My note in #196 that the browser showed no
  exception was wrong. The counter-check shows it clearly.

Tests:
- ./check.sh: 137 files, 2051 tests, exit 0 (before 136 / 2049)
- 2 new unit tests in tests/viewport/file-library.test.ts
- GM_STEPS=45,47 after a fresh seed: ALL PASSED, 71 checks
- Counter-check with the old source and the new check: 1 FAIL
  (shape stays #0000ff) plus the TypeError. Old FileLibrary.ts
  fails the first unit test.
- No full e2e run locally. The check count is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5 committed 2026-09-21 17:48:10 +02:00
1 parent 66d6aafbb4
commit 6a2b06dd4b
7 files changed
+114 -14

No files matched your search

+10 -8
View File
@@ -843,15 +843,17 @@ export async function vectorArt(t) {
const halvesEntry = Boolean(halvesName) && filteredOffers.includes(halvesName);
await press(halvesEntry ? 'Enter' : 'Escape');
await settle();
// The picture loads asynchronously and the shape keeps its plain fill until it is there. In the
// full run the tab then waits for the next redraw instead of drawing when the picture lands
// (#196), so the check asks for that redraw itself. Remove the preview() with #196.
await sleep(300);
await evaluate('__gm.state.preview()');
await settle();
// The picture loads asynchronously and the shape keeps its plain fill until it is there; the tab
// redraws by itself when it lands. Nothing here asks for that redraw: after module 45 had signed
// out and in, a canvas of the old editor used to swallow the news (#196).
let leftHalf = await tabPixel(60, 100);
let rightHalf = await tabPixel(196, 100);
for (let waited = 0; waited < 3000 && colourDistance(rightHalf, HALVES_RIGHT) >= 20; waited += 100) {
await sleep(100);
leftHalf = await tabPixel(60, 100);
rightHalf = await tabPixel(196, 100);
}
const tiled = (await sky())?.paint;
const leftHalf = await tabPixel(60, 100);
const rightHalf = await tabPixel(196, 100);
check(
'Texture offers the uploaded pictures but no drawing, and fills the shape with the one picked',
halvesEntry &&