Skip to content

fix: Subject location only worked on Safari, incorrect subject location upscaling#1566

Merged
fsbraun merged 10 commits intomasterfrom
fix/correct-fp-scaling
Jan 13, 2026
Merged

fix: Subject location only worked on Safari, incorrect subject location upscaling#1566
fsbraun merged 10 commits intomasterfrom
fix/correct-fp-scaling

Conversation

@fsbraun
Copy link
Member

@fsbraun fsbraun commented Jan 13, 2026

Description

Fixes an JS issue which miscalculated the subject location from the focal point selector widget:
image

Fix focal point widget positioning to correctly use top-left coordinates and respect container bounds when dragging or initializing the subject marker.

Bug Fixes:

  • Correct subject location calculation when dragging the focal point selector to avoid mis-scaled coordinates.
  • Align focal point circle styling and stored coordinates to use the same origin, preventing offset errors in subject positioning.

Related resources

  • #...
  • #...

Checklist

  • I have opened this pull request against master
  • I have added or modified the tests when changing logic
  • I have followed the conventional commits guidelines to add meaningful information into the changelog
  • I have read the contribution guidelines and I have joined #workgroup-pr-review on
    Slack to find a “pr review buddy” who is going to review my pull request.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jan 13, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Adjusts focal-point widget drag math and positioning to correctly store subject coordinates, and recompiles the affected JS bundles.

Sequence diagram for updated focal point dragging and coordinate storage

sequenceDiagram
    actor AdminUser
    participant Browser
    participant FocalPointInstance
    participant ImageContainer
    participant LocationInput

    AdminUser->>Browser: Drag focal point circle
    Browser->>FocalPointInstance: _onMouseDown(event)
    FocalPointInstance->>ImageContainer: getBoundingClientRect()
    FocalPointInstance->>FocalPointInstance: store dragStartX, dragStartY, circleStartX, circleStartY

    loop While dragging
        Browser->>FocalPointInstance: _onMouseMove(event)
        FocalPointInstance->>ImageContainer: getBoundingClientRect()
        FocalPointInstance->>FocalPointInstance: compute deltaX, deltaY
        FocalPointInstance->>FocalPointInstance: centerX = circleStartX + deltaX
        FocalPointInstance->>FocalPointInstance: centerY = circleStartY + deltaY
        FocalPointInstance->>FocalPointInstance: centerX = clamp(0, containerRect.width - 1, centerX)
        FocalPointInstance->>FocalPointInstance: centerY = clamp(0, containerRect.height - 1, centerY)
        FocalPointInstance->>ImageContainer: circle.style.left = centerX px
        FocalPointInstance->>ImageContainer: circle.style.top = centerY px
        FocalPointInstance->>LocationInput: _updateLocationValue(centerX, centerY)
    end

    Browser->>FocalPointInstance: _onMouseUp()
    FocalPointInstance->>FocalPointInstance: remove drag listeners
Loading

Updated class diagram for focal point manager and instance

classDiagram
    class FocalPointManager {
      +options
      +focalPointInstances
      +FocalPointManager(options)
      +initialize()
      +destroy()
      +_init(container)
    }

    class FocalPointInstance {
      +options
      +container
      +image
      +circle
      +location
      +ratio
      +isDragging
      +dragStartX
      +dragStartY
      +circleStartX
      +circleStartY
      +FocalPointInstance(container, options)
      +_updateLocationValue(x, y)
      +_getLocation()
      +_makeDraggable()
      +_onMouseDown(event)
      +_onMouseMove(event)
      +_onMouseUp()
      +_onImageLoaded()
      +destroy()
    }

    FocalPointManager "1" o--> "*" FocalPointInstance
Loading

File-Level Changes

Change Details Files
Fix focal-point drag logic to work with circle coordinates in container space instead of center-based math.
  • Simplified drag coordinate math to track the draggable element by its top-left corner instead of center plus radius.
  • Updated boundary constraints to clamp coordinates between 0 and container width/height minus one pixel.
  • Adjusted location value updates to store the unclamped top-left coordinates that match the visual circle position.
filer/static/filer/js/addons/focal-point.js
filer/static/filer/js/dist/filer-base.bundle.js
Align initial focal-point rendering with new coordinate convention and regenerate bundles.
  • Changed initial circle placement after image load to set left/top directly to the scaled location values rather than offsetting by half the circle size.
  • Regenerated the compiled filer-base and admin-file-widget bundles so they include the new focal-point behavior.
filer/static/filer/js/addons/focal-point.js
filer/static/filer/js/dist/filer-base.bundle.js
filer/static/filer/js/dist/admin-file-widget.bundle.js

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • In _onMouseDown, circleStartX/circleStartY still add r.width/r.height but _onMouseMove now uses these as the top-left values directly, which will cause the focal point to jump by one circle size on drag; consider basing circleStartX/Y on r.left - n.left and r.top - n.top instead.
  • After the change to position the circle using its top-left (style.left = centerX, style.top = centerY), the variable names and semantics (centerX, centerY, and _updateLocationValue(centerX, centerY)) no longer reflect centers; clarify whether the stored coordinates are intended to be circle centers or top-lefts and adjust the math/naming accordingly to avoid future confusion.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `_onMouseDown`, `circleStartX`/`circleStartY` still add `r.width`/`r.height` but `_onMouseMove` now uses these as the top-left values directly, which will cause the focal point to jump by one circle size on drag; consider basing `circleStartX/Y` on `r.left - n.left` and `r.top - n.top` instead.
- After the change to position the circle using its top-left (`style.left = centerX`, `style.top = centerY`), the variable names and semantics (`centerX`, `centerY`, and `_updateLocationValue(centerX, centerY)`) no longer reflect centers; clarify whether the stored coordinates are intended to be circle centers or top-lefts and adjust the math/naming accordingly to avoid future confusion.

## Individual Comments

### Comment 1
<location> `filer/static/filer/js/addons/focal-point.js:169-170` </location>
<code_context>
-            const halfCircle = circleRect.width / 2;
-
             // Constrain center to container bounds
-            centerX = Math.max(halfCircle, Math.min(containerRect.width + halfCircle, centerX));
-            centerY = Math.max(halfCircle, Math.min(containerRect.height + halfCircle, centerY));
+            centerX = Math.max(0, Math.min(containerRect.width - 1, centerX));
+            centerY = Math.max(0, Math.min(containerRect.height - 1, centerY));

             // Position circle by its top-left corner (center - radius)
-            this.circle.style.left = `${centerX - halfCircle}px`;
-            this.circle.style.top = `${centerY - halfCircle}px`;
+            this.circle.style.left = `${centerX}px`;
+            this.circle.style.top = `${centerY}px`;

             // Update location value with center coordinates
</code_context>

<issue_to_address>
**issue (bug_risk):** Circle bounds and stored coordinates no longer account for the circle size, which can push the circle partially outside the container and changes semantics of the stored focal point.

Previously `centerX`/`centerY` were true center coordinates: they were clamped with `halfCircle`, rendered via `center - halfCircle`, and stored as centers in `_updateLocationValue`. Now they’re clamped to `[0, width - 1]` / `[0, height - 1]` and used directly for `style.left/top`, making them effectively top-left coordinates while `_updateLocationValue(centerX, centerY)` still suggests center semantics.

To keep center-based focal points, clamp using the circle radius (`halfCircle` or `circleRect.width/height / 2`), render via `center - radius`, and continue storing center coordinates. If you intend to switch to top-left semantics instead, rename the variables, clamp using `containerRect.width - circleRect.width` / `height - circleRect.height`, and update `_updateLocationValue` and `_onImageLoaded` to use top-left consistently.
</issue_to_address>

### Comment 2
<location> `filer/static/filer/js/addons/focal-point.js:165-174` </location>
<code_context>
             let centerY = this.circleStartY + deltaY;

-            // Get circle dimensions
-            const circleRect = this.circle.getBoundingClientRect();
-            const halfCircle = circleRect.width / 2;
-
</code_context>

<issue_to_address>
**issue (bug_risk):** Initial placement on image load is no longer centered on the stored focal point, which may cause visual mismatch vs previously saved values.

`_onImageLoaded` previously treated `x`/`y` as the focal point center, offsetting by `circleRect.width/2` and `circleRect.height/2` before setting `style.left/top`. The new code uses `x`/`y` as the top-left, but `_updateLocationValue` still persists the raw drag coordinates assuming center semantics. This will shift existing saved focal points by half the circle size. If the top-left convention is intentional, either normalize/migrate existing values at load time, or keep `_onImageLoaded` using center-based coordinates until the stored data format is updated.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@fsbraun fsbraun changed the title fix: Correct subject location upscaling fix: Subject location only worked on Safari, incorrect subject location upscaling Jan 13, 2026
@codecov
Copy link

codecov bot commented Jan 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.95%. Comparing base (e121761) to head (08ca8d8).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1566   +/-   ##
=======================================
  Coverage   76.95%   76.95%           
=======================================
  Files          77       77           
  Lines        3666     3666           
  Branches      498      498           
=======================================
  Hits         2821     2821           
  Misses        675      675           
  Partials      170      170           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@fsbraun fsbraun requested a review from jrief January 13, 2026 13:30
@jrief
Copy link
Collaborator

jrief commented Jan 13, 2026

Thanks for the quick response!

@fsbraun fsbraun merged commit 83d47b7 into master Jan 13, 2026
89 checks passed
@fsbraun fsbraun deleted the fix/correct-fp-scaling branch January 13, 2026 15:22
PeterW-LWL added a commit to PeterW-LWL/django-filer that referenced this pull request Jan 15, 2026
fsbraun added a commit that referenced this pull request Jan 15, 2026
* chore: remove `console.log` statements from codebase

This was unintentional added in #1566

* Change django-polymorphic dependency version constraint

---------

Co-authored-by: Fabian Braun <fsbraun@gmx.de>
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