Skip to content

Rotate and flip an image (BL-16741) - #8227

Open
hatton wants to merge 1 commit into
masterfrom
BL-16741-rotate-image
Open

hatton wants to merge 1 commit into
masterfrom
BL-16741-rotate-image

Conversation

@hatton

@hatton hatton commented Aug 23, 2026 •

Copy link
Copy Markdown
Member

Problem

A picture that arrives on its side stays on its side. Bloom could crop a picture and move it,
but it could not rotate it, mirror it, or set an item on a page at an angle. An author whose
camera saved a photograph sideways had to leave Bloom, rotate the file in another program, and
put it back.

Fix

  • Rotate right on the picture menu rotates the picture 90 degrees clockwise. An ordinary
    picture rotates as a box. A background picture fills its page area and cannot rotate as a box,
    so the picture inside it rotates and its area is reshaped to match. The result is upright, at
    the same framing, with any crop kept, and a picture set to fill the page still fills it.
    Neither path acts on an empty placeholder.
  • Flip horizontal and Flip vertical mirror the picture about its own axes, whatever it
    and its box are rotated by, so flipping and then rotating gives the same result as rotating
    and then flipping.
  • A rotation handle rotates a selected item to any angle: a knob on a short stem above its top
    edge, snapping to every 45 degrees within 14 degrees of one, with CTRL to turn snapping off.
    Past 135 degrees the knob moves to the item's other edge so it stays clear of the control
    panel. Items whose outline comicaljs draws (speech bubbles, captions, rectangles, ellipses) and
    videos get no handle. Navigation buttons and book link grids are never rotated: no handle, and no Rotate right.
  • Dragging, resizing and cropping work on a rotated item. Mouse movement is converted into
    the item's own coordinates, a click picks the right item, and a rotated item stays put across
    page loads and crop drags. The control frame rotates with the item: each handle shows the
    cursor for the direction it really moves, tooltips stay level, and the crop marks show the
    sides that are really hidden.
  • Undo puts back a Rotate right, a Flip, and a drag of the handle, one step each. Reset
    Image
    clears the crop and the picture's rotation and mirror; it does not straighten a rotated
    box. A new picture, chosen or pasted, arrives upright and uncropped, and a duplicate keeps
    its original's angle. The picture menu is regrouped, Escape now closes the
    canvas element menu, and a click in the Flip submenu closes it.
  • Publishing writes a rotated or flipped picture into the published image upright and
    cropped to what the page shows, for BloomPUB, ePUB, upload and spreadsheet export, so readers
    see what the author saw. The AI Image Editor gets the same upright view. An item rotated with
    the handle keeps its rotation in the published HTML.
  • Resize and crop handles follow the pointer at any edit zoom (they ran ahead of it when zoomed,
    rotated or not).

Every angle and mirror lives in the item's own inline transform, saved in the book HTML.

Ref: https://issues.bloomlibrary.org/youtrack/issue/BL-16741

Devin review


This change is Reviewable

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@hatton

hatton commented Aug 23, 2026

Copy link
Copy Markdown
Member Author

[Claude Opus 5 from Hatton's machine during preflight] Consulted Devin on 2026-08-23 22:56 UTC up to commit 379ee06. The review job reached completed, but its findings pass was still pending after 32 minutes, so no findings could be read: timed out. I re-triggered the review as I gave up; a later preflight run will fold in whatever it produces.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

Comment thread DistFiles/localization/en/BloomMediumPriority.xlf
@hatton

hatton commented Aug 24, 2026 •

Copy link
Copy Markdown
Member Author

[Claude Opus 5 from Hatton's machine during preflight] Consulted Devin on this pull request, up to aaeb2d8c86.

Devin raised these findings, plus two informational notes that need no action. Each is now a review thread above, with what came of it:

  • Flip used the wrong axis after the box had been turned — real, and fixed in 5b137574ff.
  • Two diagonal resize cursors were the wrong way round at 45 degrees — real, and fixed in 5b137574ff, with tests.
  • The offset of the point you grabbed, on a turned item — not a problem: the two measurements are taken in the same frame.
  • The new menu strings are marked translate="no" — that is this repository's convention for a new string, so that Crowdin does not see it before the strings are declared ready.
  • Reset Image records no undo step — deliberate, and now said so in a comment beside the code.
  • Hit testing on a turned item passes a fifth argument to getBubbleHit — that argument exists in the version of comicaljs this branch uses.

Devin has been re-triggered for 5b137574ff by the push.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

Comment thread src/BloomBrowserUI/bookEdit/js/ImageUndoManager.ts
@hatton

hatton commented Aug 24, 2026 •

Copy link
Copy Markdown
Member Author

[Claude Opus 5 from Hatton's machine during preflight] Consulted Devin again, up to 4138030b26.

On 4138030b26 Devin returned the same eight findings it gave for the previous commit, all of which are already fixed or answered in the threads above. It is repeating its earlier set rather than reading the current tree: it still reports the diagonal cursors at CanvasElementSelectionUi.ts:267, where that table no longer is. It is fixed in the pushed code.

One note in the last round is new and is a real question rather than a defect: a turn cannot be undone once the selection moves away. That thread is open, and the question is on the preflight report for Hatton.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@hatton

hatton commented Aug 24, 2026 •

Copy link
Copy Markdown
Member Author

[Claude Opus 5 from Hatton's machine during preflight] Consulted Devin on this PR up to 2e5cfad65c.

Devin re-read the whole PR after the change that makes Rotate right keep a crop. It raised no new
finding, and it now marks its earlier Bug about the wrong flip axis on a box that had been turned as fixed.
That thread is already replied to and resolved above.

Its five Investigate flags are the same five as before, each with its own thread above: four are
replied to and resolved, and the one on ImageUndoManager.ts is still open, because it asks a
question about what Undo should do that is Hatton's to answer.

CI is green. Greptile posted only a notice that its trial has ended, so it reviewed nothing.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@hatton

hatton commented Aug 24, 2026 •

Copy link
Copy Markdown
Member Author

[Claude Opus 5 from Hatton's machine during preflight]

Consulted Devin on this PR up to 639699d2745d929fe482b22e9c990c75d9129bc4.

Devin re-read the whole PR at this commit and raised nothing new. It now marks all three of its
Bugs as fixed, and it reports the same five Investigate flags as before, every one of them with a
documented outcome: four it marks fixed itself, and the fifth, on rotated hit testing, has its
own resolved thread above. Its other ten flags are informational.

Devin review page

@greptile-apps

greptile-apps Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Retrigger

The PR appears safe to merge because no blocking failure remains within the eligible follow-up review scope.

Summary

This PR adds rotation and mirroring for pictures and arbitrary-angle rotation for supported canvas elements, preserving these transformations through editing, undo, and publishing.

  • Adds picture rotation and flip commands plus an interactive rotation handle.
  • Makes selection, dragging, resizing, cropping, and control frames rotation-aware.
  • Bakes picture transforms into published, exported, and AI-editor images.
  • Adds unit and end-to-end coverage for transform geometry, undo, publishing, and UI behavior.

Reviews (2) · Last reviewed commit: "Rotate and flip pictures, and rotate can..."

@hatton

hatton commented Aug 24, 2026

Copy link
Copy Markdown
Member Author

[Claude Opus 5 from Hatton's machine during preflight]

Consulted Devin on this PR up to 31d5b3b8c02549ed88991f65c37e58abd87c5f6e, which is the commit
that keeps a turned item still and keeps its controls reachable, merged with master.

Devin re-read the whole PR at this commit and raised nothing new. It marks all three of its
Bugs as fixed, and it reports the same five Investigate flags as before, each already answered in
a resolved thread above:

  • The diagonal cursors on an item turned 45 degrees — real, fixed in 5b137574ff.
  • Where the pointer takes hold of a turned item — not a problem. comicaljs itself measures a
    hit the same way, and says so in its own code: the offsets are relative to the picture area,
    which is the frame the pointer position is already in.
  • Whether comicaljs really skips the items we ask it to skip — confirmed from the installed
    package: the fifth argument is named ignoreSelector, and the code drops every item that
    matches it.
  • The new menu words are marked as not for translation — that is this project's rule for a
    new entry, and Crowdin picks them up later.
  • Reset Image cannot be undone — deliberate, and Hatton has said to leave it. It is written
    next to the code and in the notes for the tester.

Nothing Devin says touches the newest work in this commit: the rotation knob moving to the other
edge only past 135 degrees, and a video's buttons working on a turned item.

@hatton

hatton commented Sep 23, 2026

Copy link
Copy Markdown
Member Author

[Claude Fable 5.1 following a prompt from Hatton]

Preflight needed before this lands. Today's UI-test run pushed product-code changes to this branch, not only tests:

  • f34360f — Escape closes a canvas element's context menu, and the Flip submenu closes on a click (CanvasElementContextControls.tsx, localizableMenuItem.tsx, new parentMenuOpen prop)
  • 4dd51b8 — data-testid pass-through on nested menu rows

Tests for both are in 26fcd02 and fb2ac42 (Notion Test Case ID 827). Run preflight on this branch so the product changes get the normal self-review before the PR leaves draft.

@hatton hatton left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@hatton reviewed 3 files and made 6 comments.
Reviewable status: 0 of 31 files reviewed, 15 unresolved discussions.


src/BloomBrowserUI/bookEdit/js/bloomEditing.ts line 155 at r3 (raw file):

        // Marks the turned canvas element the pointer is inside, so it means nothing once the
        // page is saved. See kPointerInsideClass in canvasElementRotation.ts.
        $(this).removeClass(kPointerInsideClass);

Long ago, we had a convention where all classes that were for UI only should just start with "ui-" And thus we wouldn't have to list each one for cleanup? is that gone?


src/BloomBrowserUI/bookEdit/js/canvasElementManager/CanvasElementPointerInteractions.ts line 98 at r3 (raw file):

        bloomCanvas.onmouseup = null;

        // While the pointer is inside the bloom-canvas, onMouseMove keeps the mark on the turned

this comment is hard for humans to read. improve it. Don't use "mark". Don't assume the reader is thinking in terms of rotating.


src/BloomBrowserUI/bookEdit/js/canvasElementManager/CanvasElementPointerInteractions.ts line 450 at r3 (raw file):

        }
        if (event.buttons === 0) {
            // Not a drag, so keep the mark on the turned element the pointer is inside. This has

"the mark" is Not helpful.


src/BloomBrowserUI/bookEdit/js/canvasElementManager/CanvasElementPointerInteractions.ts line 481 at r3 (raw file):

    // also keeps the work small: we walk the turned elements, not every element, and we do no
    // comicaljs hit test.
    private updateTurnedElementUnderPointer(event: MouseEvent) {

Don't use the term turned; use rotated.


src/BloomBrowserUI/bookEdit/js/canvasElementManager/CanvasElementPointerInteractions.ts line 498 at r3 (raw file):

    }

    // Put the mark on one turned element and take it off the others. Pass undefined to clear it

Don't use mark or turned.


src/BloomBrowserUI/bookEdit/js/canvasElementManager/CanvasElementPointerInteractions.ts line 609 at r3 (raw file):

    // another element covers is not clicked. And a button that is not displayed is not in the
    // list at all, so a hidden video cannot be started.
    private getVideoButtonInsideTurnedElement(

Rotated, not turned.

@hatton hatton left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@hatton+JHAI made 6 comments and resolved 6 discussions.
Reviewable status: 0 of 31 files reviewed, 9 unresolved discussions.


src/BloomBrowserUI/bookEdit/js/bloomEditing.ts line 155 at r3 (raw file):

Previously, hatton (John Hatton) wrote…

Long ago, we had a convention where all classes that were for UI only should just start with "ui-" And thus we wouldn't have to list each one for cleanup? is that gone?

[Claude Opus 5.5 from Hatton's machine during reviewable-replies] That line is gone along with the video support, so this spot no longer adds a class to the list.

On the convention: it still exists, but it works on elements, not on class-name prefixes. Cleanup() removes any element that has the class bloom-ui ($(".bloom-ui").remove()), and HtmlDom.cs strips the same elements on the C# side. Nothing removes classes whose names start with ui-. The ui-draggable, ui-resizable and ui-...Highlight names in that list come from jQuery UI, and each one is removed individually. A class that Bloom adds to a real page element only while editing still has to be listed there by hand, or removed by the code that added it.


src/BloomBrowserUI/bookEdit/js/canvasElementManager/CanvasElementPointerInteractions.ts line 98 at r3 (raw file):

Previously, hatton (John Hatton) wrote…

this comment is hard for humans to read. improve it. Don't use "mark". Don't assume the reader is thinking in terms of rotating.

[Claude Opus 5.5 from Hatton's machine during reviewable-replies] Gone. The code this comment described existed only so that a rotated video could show its play, pause and replay buttons. Rotating a video is no longer supported, so the comment and the code under it are deleted. That removes the mouseleave listener and every use of the class it cleared.


src/BloomBrowserUI/bookEdit/js/canvasElementManager/CanvasElementPointerInteractions.ts line 450 at r3 (raw file):

Previously, hatton (John Hatton) wrote…

"the mark" is Not helpful.

[Claude Opus 5.5 from Hatton's machine during reviewable-replies] Gone. The code this comment described existed only so that a rotated video could show its play, pause and replay buttons. Rotating a video is no longer supported, so the comment and the code under it are deleted.


src/BloomBrowserUI/bookEdit/js/canvasElementManager/CanvasElementPointerInteractions.ts line 481 at r3 (raw file):

Previously, hatton (John Hatton) wrote…

Don't use the term turned; use rotated.

[Claude Opus 5.5 from Hatton's machine during reviewable-replies] Gone. The code this comment described existed only so that a rotated video could show its play, pause and replay buttons. Rotating a video is no longer supported, so the comment and the code under it are deleted. Separately, a pass over the whole branch is replacing "turn" and "turned" with "rotate" and "rotated" in the comments, test names and identifiers.


src/BloomBrowserUI/bookEdit/js/canvasElementManager/CanvasElementPointerInteractions.ts line 498 at r3 (raw file):

Previously, hatton (John Hatton) wrote…

Don't use mark or turned.

[Claude Opus 5.5 from Hatton's machine during reviewable-replies] Gone. The code this comment described existed only so that a rotated video could show its play, pause and replay buttons. Rotating a video is no longer supported, so the comment and the code under it are deleted.


src/BloomBrowserUI/bookEdit/js/canvasElementManager/CanvasElementPointerInteractions.ts line 609 at r3 (raw file):

Previously, hatton (John Hatton) wrote…

Rotated, not turned.

[Claude Opus 5.5 from Hatton's machine during reviewable-replies] Gone. The code this comment described existed only so that a rotated video could show its play, pause and replay buttons. Rotating a video is no longer supported, so the comment and the code under it are deleted. This method passed clicks through to the buttons of a rotated video.

Comment thread src/BloomBrowserUI/bookEdit/js/imageContentTransform.ts Outdated
Comment thread src/BloomExe/ImageProcessing/ImageUtils.cs
@hatton

hatton commented Sep 24, 2026

Copy link
Copy Markdown
Member Author

[Claude Opus 5.5 from Hatton's machine during preflight] Consulted Devin on 2026-09-24 up to 6d38680b4e. It raised nothing new, and it now marks the BloomPUB bug fixed. Its one new flag was that it had read an out-of-date PR description; answered and resolved. No review thread is open.

Comment thread src/BloomBrowserUI/bookEdit/js/ImageUndoManager.ts
Comment thread src/BloomBrowserUI/bookEdit/js/ImageUndoManager.ts
@hatton

hatton commented Sep 24, 2026

Copy link
Copy Markdown
Member Author

[Devin] Bug: Editing shortcuts multiply after adding elements (src/BloomBrowserUI/bookEdit/js/bloomEditing.ts:300 — outside diff, not resolvable as a thread)

The document-level key handlers are bound again each time a canvas element is added, so one key press runs several copies.

[Claude Opus 5.5 from Hatton's machine during preflight] Not changed by this PR: bloomEditing.ts is now identical to master. The shortcuts there (clear formatting, the justify keys) give the same result however many copies run, so the extra copies do no harm. They only mattered while Ctrl+R rotated a picture, and that shortcut is gone.

@hatton

hatton commented Sep 24, 2026 •

Copy link
Copy Markdown
Member Author

[Claude Opus 5.5 from Hatton's machine during preflight]

Consulted Devin on this PR up to afcb4815df76c98bcca85c057ffc174d9830ae40.

Devin re-read the whole PR from scratch at c3e0a33c80 and again at this commit, and repeated its earlier findings, all already fixed or answered on their threads. Two were real and are fixed in afcb4815df: Undo could change a picture that is not selected, and pasting over a rotated picture kept the old picture's rotation. Each new finding has its own resolved thread above. CI's pr-automation check passed.

@hatton hatton left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@hatton partially reviewed 27 files and made 2 comments.
Reviewable status: 0 of 41 files reviewed, 30 unresolved discussions.


src/BloomBrowserUI/bookEdit/js/canvasElementManager/CanvasElementManager.ts line 1361 at r8 (raw file):

    // Rotate the active canvas element 90 degrees clockwise. Used by the Rotate Right
    // menu command, for everything except a background image; see rotateActiveImageRight.
    public rotateActiveElementRight(): void {

put the 90Degrees in the name (For all such functions). The idea is to differentiate this from the freeform rotation that we also support.


src/BloomBrowserUI/bookEdit/js/canvasElementManager/CanvasElementSelectionUi.ts line 30 at r8 (raw file):

// control frame is plain DOM that this file creates and removes by hand. The path is the one
// in @mui/icons-material/Refresh.
const kRefreshIconPath =

Okay, we reuse the refresh icon, but it's confusing to see that in the code. Just call it the rotate icon.

@hatton hatton left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@hatton partially reviewed 12 files.
Reviewable status: 0 of 41 files reviewed, 30 unresolved discussions.

@hatton hatton left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@hatton+JHAI made 2 comments and resolved 2 discussions.
Reviewable status: 0 of 41 files reviewed, 28 unresolved discussions.


src/BloomBrowserUI/bookEdit/js/canvasElementManager/CanvasElementManager.ts line 1361 at r8 (raw file):

Previously, hatton (John Hatton) wrote…

put the 90Degrees in the name (For all such functions). The idea is to differentiate this from the freeform rotation that we also support.

[Claude Opus 5.5 from Hatton's machine during reviewable-replies] Done in 9b599666d2. The four functions that rotate by a quarter turn are now rotateActiveElementRight90Degrees, rotateActiveImageRight90Degrees, rotateImageContentRight90Degrees, and the e2e helper rotateSelectedImageRight90Degrees.


src/BloomBrowserUI/bookEdit/js/canvasElementManager/CanvasElementSelectionUi.ts line 30 at r8 (raw file):

Previously, hatton (John Hatton) wrote…

Okay, we reuse the refresh icon, but it's confusing to see that in the code. Just call it the rotate icon.

[Claude Opus 5.5 from Hatton's machine during reviewable-replies] Done in 9b599666d2. The constant is now kRotateIconPath, and the comment calls it the rotate icon; it mentions Refresh only to say where the path was copied from.

Comment thread src/BloomBrowserUI/bookEdit/js/ImageUndoManager.ts
Comment thread src/BloomBrowserUI/bookEdit/js/imageContentTransform.ts
Comment thread src/BloomBrowserUI/bookEdit/js/imageContentTransform.ts
Comment thread src/BloomE2E/helpers/canvasElements.ts
Comment thread src/BloomBrowserUI/bookEdit/js/ImageUndoManager.ts
Comment thread src/BloomBrowserUI/bookEdit/js/ImageUndoManager.ts
Comment thread src/BloomBrowserUI/bookEdit/js/ImageUndoManager.ts
Comment thread src/BloomBrowserUI/bookEdit/js/ImageUndoManager.ts
Comment thread src/BloomBrowserUI/bookEdit/js/ImageUndoManager.ts
Comment thread src/BloomBrowserUI/bookEdit/js/ImageUndoManager.ts
Comment thread src/BloomBrowserUI/bookEdit/js/ImageUndoManager.ts
@hatton

hatton commented Sep 24, 2026

Copy link
Copy Markdown
Member Author

[Claude Opus 5.5 from Hatton's machine during preflight]

Consulted Devin on this PR up to 46a2be0c3a3ed5bca9ca77b980b946c2f39dbc38.

Since the last log, Devin re-read the whole PR at each push. The real findings are fixed, each on its own resolved thread: typing, formatting, links and line breaks after a rotation are undone before the rotation, a duplicate keeps its angle and lands on its source, and navigation buttons and book link grids are never rotated. Three predicted edge cases in the Undo comparison did not happen when Hatton tried them, and their threads say so. At this commit it raised nothing new.

@hatton hatton left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@hatton partially reviewed 22 files.
Reviewable status: 0 of 45 files reviewed, 42 unresolved discussions.

…741)

A picture that arrived on its side stayed on its side: Bloom could crop
and move a picture but not rotate or mirror it, or set an item at an
angle.

- Rotate right on the picture menu rotates a picture 90 degrees
  clockwise. An ordinary picture rotates as a box; a background picture
  rotates inside its area, which is reshaped to match, keeping the crop.
  It does nothing on an empty placeholder.
- Flip horizontal and Flip vertical mirror the picture about its own
  axes, whatever it is rotated by, so flipping then rotating gives the
  same result as rotating then flipping.
- A rotation knob rotates a selected item to any angle, snapping to
  45-degree steps unless Ctrl is held. Speech bubbles and similar
  shapes, videos, navigation buttons and book link grids get no knob;
  navigation buttons and book link grids are never rotated.
- Dragging, resizing, cropping and typing work on a rotated item at any
  edit zoom, and a rotated background keeps its framing when the page
  changes shape.
- Undo takes back each Rotate right, Flip and knob drag, only for the
  selected item, and after typing or formatting in a rotated text box
  it takes back the text edit first. Undo of a picture replacement is
  offered only while that picture is selected.
- Reset Image clears the crop and the picture's rotation and mirror; it
  does not straighten a rotated box. A new picture, chosen or pasted,
  arrives upright and uncropped, and a duplicate keeps its original's
  angle and mirror.
- Publishing writes a rotated or flipped picture into the published
  image upright and cropped to what the page shows, for BloomPUB, ePUB,
  upload and spreadsheet export; the AI Image Editor gets the same
  upright view.

Every angle and mirror lives in the item's inline transform, saved in
the book HTML. The rotate-and-flip e2e spec automates manual Test Case
827.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@hatton
hatton force-pushed the BL-16741-rotate-image branch from 46a2be0 to b520cf1 Compare September 24, 2026 22:13
@hatton
hatton marked this pull request as ready for review September 24, 2026 22:14
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