Skip to content

Refit a background image when its bloom-canvas is sized after page load - #8312

Draft
hatton wants to merge 3 commits into
masterfrom
canvas-background-refit
Draft

Refit a background image when its bloom-canvas is sized after page load#8312
hatton wants to merge 3 commits into
masterfrom
canvas-background-refit

Conversation

@hatton

@hatton hatton commented Sep 4, 2026

Copy link
Copy Markdown
Member

Problem

Bloom fits a background image to its bloom-canvas once, during the page-load pass. When a container is laid out with JavaScript after that pass, the bloom-canvas inside it still has no size at fitting time, and three things go wrong:

  1. The fit is computed from a box with no area. The numbers it writes are not neutral: they become the baseline that later resizes scale from, so the picture ends up somewhere arbitrary.
  2. Nothing can ask for a re-fit once the container has its real size.
  3. A bloom-canvas nested inside another bloom-canvas loses its flower placeholder, because the two rules that hide the placeholder on a background image use descendant combinators, so the outer canvas's elements satisfy the :has() of the inner one.

What the PR does

  • adjustBackgroundImageSizeToFit returns early when the bloom-canvas has a width or height of zero or less (a hidden canvas with a border reports a negative size), leaving the fitting to whoever knows the real size. A hidden or not-yet-laid-out canvas is in that state.
  • CanvasElementManager gains two public methods: adjustAfterContainerResize() re-fits every bloom-canvas on the page, and refitBackgroundImage(bloomCanvas) re-fits one and returns a promise that settles when the image is loaded and fitted. The general resize path preserves and scales each child's offsets, which is right for elements the user placed and wrong for a background image fitted before layout.
  • Every place that looks up a bloom-canvas's background image goes through one function, getBackgroundCanvasElement, which takes the first background image whose nearest bloom-canvas is the one asked about. A plain descendant search would hand an outer canvas the inner canvas's picture; a direct-child search would miss the Image Description tool's temporary wrapper.
  • The two placeholder rules in placeHolderImages.less use child combinators. A canvas element is always a direct child of its bloom-canvas, except while the Image Description tool wraps it in a bloom-describedImage, and that case hides the placeholder anyway.
  • A new test file covers the zero-area guard (zero and negative sizes, plus a sized canvas that is still fitted) and the scoped lookup (nested canvases with and without their own background image, and the Image Description wrapper).

Notes for the reviewer: this PR targets master on purpose, as groundwork that should land on master alone; the temporary Version6.5 rule in AGENTS.md does not apply to it. The two new methods have no caller in this PR. They are the seam that a later feature (custom JavaScript layouts, which size a container after the page-load pass) will call.

Devin review


This change is Reviewable

hatton and others added 3 commits September 4, 2026 14:36
Bloom fits a background image to its bloom-canvas during the page-load pass.
When something lays out a container with JavaScript after that pass, the
bloom-canvas inside it has no size yet at fitting time, and three things go
wrong.

- adjustBackgroundImageSizeToFit computed sizes from a box with no area. The
  numbers it wrote were not neutral: they become the baseline that later
  resizes scale from, so the picture ended up somewhere arbitrary. It now
  returns early for a zero-width or zero-height bloom-canvas, leaving the
  fitting to whoever knows the real size.

- There was no way for such a host to ask for a re-fit afterwards.
  CanvasElementManager gains adjustAfterContainerResize(), which re-fits every
  bloom-canvas on the page, and refitBackgroundImage(bloomCanvas) for one
  canvas. The general resize path (AdjustChildrenIfSizeChanged) preserves and
  scales each child's offsets, which is right for canvas elements the user
  placed and wrong for a background image fitted before layout.

- The two rules in placeHolderImages.less that hide the flower placeholder on
  a background image used descendant combinators, so a bloom-canvas nested
  inside another bloom-canvas satisfied the :has() from the outer canvas's
  elements and lost its own placeholder. They now use child combinators. A
  canvas element is always a direct child of its bloom-canvas, except while
  the Image Description tool has it wrapped in a bloom-describedImage, and
  that case hides the placeholder anyway.

Adds CanvasElementBackgroundImageManager.test.ts, covering the zero-area
guard, with a third test showing that a bloom-canvas which does have a size is
still fitted, so the first two cannot pass for the wrong reason.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The doc comment said the method re-fits background images. It runs the same
adjustments as an origami splitter drag: legacy background conversion, then a
rescale of every positioned canvas element. A background image that was never
fitted has no position and is skipped, so the comment now points a caller at
refitBackgroundImage for that case.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Four places found a bloom-canvas's background image with a descendant search
for bloom-backgroundImage. Once a bloom-canvas can be nested inside another,
that search finds the inner canvas's background image and treats it as the
outer one's. getBackgroundCanvasElement takes the first background image
whose nearest bloom-canvas is the one asked about, which also still works
while the Image Description tool has the image wrapped in bloom-describedImage.

The zero-area guard in adjustBackgroundImageSizeToFit now also catches a
negative size. A hidden bloom-canvas with a border reports one, because
getExactClientSize subtracts the border from a zero bounding rectangle.

refitBackgroundImage returns the fitting promise, so a caller can wait for the
image to load and be fitted before laying out anything that depends on it.

Tests cover the negative size and the scoped lookup: a nested canvas with and
without its own background image, and the Image Description wrapper.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@hatton

hatton commented Sep 4, 2026

Copy link
Copy Markdown
Member Author

[Claude Fable 5.1 from Hatton's machine during preflight] Consulted Devin on 2026-09-04 21:15 UTC up to commit be753fdb0d179b4dcf1f7f4060796661ad56637d. Two passes ran. The first raised two bugs (nested-canvas background lookup, negative-size guard) and two Investigate flags (promise not returned, test coverage of the new public methods); all four are mirrored as review threads above, each with its outcome, and Devin's re-review marks both bugs fixed. The re-review added one flag about the PR's base branch, answered on its thread: master is the developer's deliberate choice. No informational items. CI passed.

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.

1 participant