Skip to content

feat: add high-DPI scale to chart image downloads#39285

Open
eschutho wants to merge 3 commits intoapache:masterfrom
eschutho:image-download-scale-pr
Open

feat: add high-DPI scale to chart image downloads#39285
eschutho wants to merge 3 commits intoapache:masterfrom
eschutho:image-download-scale-pr

Conversation

@eschutho
Copy link
Copy Markdown
Member

Summary

  • Adds IMAGE_DOWNLOAD_SCALE = window.devicePixelRatio || 2 constant in downloadAsImage.tsx
  • Passes scale to both domToImage.toJpeg() calls (regular and ag-grid paths)
  • Retina users get native display resolution; all users get a minimum 2× output

Why

The dom-to-image-more library defaults to 1× (CSS pixels only) when no scale is provided. Downloaded chart images were lower resolution than what's visible on screen, especially on retina/HiDPI displays.

Test plan

  • Download a chart image from a dashboard on a retina display — confirm image dimensions are 2× the chart's CSS pixel size
  • Download a chart image on a standard display — confirm same 2× improvement
  • Download an ag-grid table chart — confirm the same improvement applies

@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review bot commented Apr 10, 2026

Code Review Agent Run #9a4123

Actionable Suggestions - 0
Review Details
  • Files reviewed - 1 · Commit Range: a8f82f0..a8f82f0
    • superset-frontend/src/utils/downloadAsImage.tsx
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

import { addWarningToast } from 'src/components/MessageToasts/actions';

const IMAGE_DOWNLOAD_QUALITY = 0.95;
const IMAGE_DOWNLOAD_SCALE = window.devicePixelRatio || 2;
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.

🟠 Architect Review — HIGH

The IMAGE_DOWNLOAD_SCALE constant uses window.devicePixelRatio || 2, so on standard displays where devicePixelRatio is 1 (truthy) the scale remains 1× and does not satisfy the stated requirement that all users get at least 2× output resolution.

Suggestion: Change the scale computation to enforce a 2× lower bound, for example const IMAGE_DOWNLOAD_SCALE = Math.max(window.devicePixelRatio || 1, 2);, so both standard and HiDPI displays meet the intended minimum resolution.

import { addWarningToast } from 'src/components/MessageToasts/actions';

const IMAGE_DOWNLOAD_QUALITY = 0.95;
const IMAGE_DOWNLOAD_SCALE = window.devicePixelRatio || 2;
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.

Suggestion: The scale calculation does not enforce the intended minimum 2× output: when devicePixelRatio is 1 (standard displays), the current expression resolves to 1, so image downloads remain low-resolution. Use a max check so scale is never below 2. [logic error]

Severity Level: Major ⚠️
- ⚠️ Dashboard 'Download as image' exports low-resolution JPEGs on 1×.
- ⚠️ Per-chart image downloads lower resolution than promised 2×.
- ⚠️ PR promise of minimum 2× scale not met.
Suggested change
const IMAGE_DOWNLOAD_SCALE = window.devicePixelRatio || 2;
const IMAGE_DOWNLOAD_SCALE = Math.max(window.devicePixelRatio || 1, 2);
Steps of Reproduction ✅
1. Open any Superset dashboard in a desktop browser on a standard display where
`window.devicePixelRatio === 1` (e.g., 100% zoom on many non-HiDPI monitors).

2. Trigger a dashboard-wide image download from the main menu: this calls
`useDownloadMenuItems` in
`superset-frontend/src/dashboard/components/menu/DownloadMenuItems/index.tsx:43-48`, whose
`onDownloadImage` handler at lines 75-77 invokes
`downloadAsImage(SCREENSHOT_NODE_SELECTOR, dashboardTitle, true)(e)`.

3. Alternatively, trigger a per-chart image download from a slice header menu: this
executes the `MenuKeys.DownloadAsImage` case in
`superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx:49-71`, which
calls `downloadAsImage(getScreenshotNodeSelector(props.slice.slice_id),
props.slice.slice_name, true, theme)(domEvent)`.

4. In both flows, `downloadAsImage` is the default export from
`superset-frontend/src/utils/downloadAsImage.tsx:266-324`; inside this file
`IMAGE_DOWNLOAD_SCALE` is defined at line 28 as `const IMAGE_DOWNLOAD_SCALE =
window.devicePixelRatio || 2;` and passed to `domToImage.toJpeg` at lines 299-304. On a
standard display (`devicePixelRatio === 1`), this expression evaluates to `1` (since `1`
is truthy and the `|| 2` fallback is not used), so the exported JPEG uses a scale of 1×
instead of the PR's intended minimum 2×, producing lower-resolution images than specified.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/src/utils/downloadAsImage.tsx
**Line:** 28:28
**Comment:**
	*Logic Error: The scale calculation does not enforce the intended minimum 2× output: when `devicePixelRatio` is `1` (standard displays), the current expression resolves to `1`, so image downloads remain low-resolution. Use a max check so scale is never below 2.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

@bito-code-review
Copy link
Copy Markdown
Contributor

Yes, the suggestion is valid — it adjusts the fallback to 1 and applies Math.max to guarantee a minimum scale of 2, addressing the issue where standard displays (devicePixelRatio=1) would otherwise get 1x resolution.

Add IMAGE_DOWNLOAD_SCALE constant using window.devicePixelRatio (|| 2
fallback) and pass it as the `scale` option to both dom-to-image-more
toJpeg calls. This doubles image resolution for retina displays and
ensures a minimum 2x output for all users.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@netlify
Copy link
Copy Markdown

netlify bot commented Apr 10, 2026

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit fc24d4b
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/69d970e03d45de00084c63f0
😎 Deploy Preview https://deploy-preview-39285--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@eschutho eschutho force-pushed the image-download-scale-pr branch from a8f82f0 to 00ab3a3 Compare April 10, 2026 21:38
Change IMAGE_DOWNLOAD_SCALE from `window.devicePixelRatio || 2` to
`Math.max(window.devicePixelRatio || 1, 2)` so that standard displays
(devicePixelRatio=1) also get 2x output. The previous expression
evaluated to 1 on standard displays since 1 is truthy.

Add tests covering the standard display (dpr=1→2) and zero (dpr=0→2)
cases, and rename the prior fallback test accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@eschutho eschutho added the 🎪 ⚡ showtime-trigger-start Create new ephemeral environment for this PR label Apr 10, 2026
@github-actions github-actions bot added 🎪 fc24d4b 🚦 building Environment fc24d4b status: building 🎪 fc24d4b 📅 2026-04-10T21-52 Environment fc24d4b created at 2026-04-10T21-52 🎪 fc24d4b 🤡 eschutho Environment fc24d4b requested by eschutho 🎪 ⌛ 48h Environment expires after 48 hours (default) and removed 🎪 ⚡ showtime-trigger-start Create new ephemeral environment for this PR labels Apr 10, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🎪 Showtime is building environment on GHA for fc24d4b

@github-actions github-actions bot added 🎪 fc24d4b 🚦 deploying Environment fc24d4b status: deploying 🎪 fc24d4b 🚦 running Environment fc24d4b status: running 🎪 🎯 fc24d4b Active environment pointer - fc24d4b is receiving traffic 🎪 fc24d4b 🌐 35.89.121.178:8080 Environment fc24d4b URL: http://35.89.121.178:8080 (click to visit) and removed 🎪 fc24d4b 🚦 building Environment fc24d4b status: building 🎪 fc24d4b 🚦 deploying Environment fc24d4b status: deploying 🎪 fc24d4b 🚦 running Environment fc24d4b status: running 🎪 🎯 fc24d4b Active environment pointer - fc24d4b is receiving traffic labels Apr 10, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🎪 Showtime deployed environment on GHA for fc24d4b

Environment: http://35.89.121.178:8080 (admin/admin)
Lifetime: 48h auto-cleanup
Updates: New commits create fresh environments automatically

- Run prettier on downloadAsImage.tsx to fix pre-commit CI failure
- Replace isolateModulesAsync scale tests (jest.mock inside async
  callbacks is hoisted and doesn't capture scale) with simpler
  capturedScale pattern using mockImplementation, and a pure formula
  test verifying Math.max(dpr || 1, 2) behaviour

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions github-actions bot added 🎪 6be81d3 🚦 building Environment 6be81d3 status: building 🎪 6be81d3 📅 2026-04-10T22-32 Environment 6be81d3 created at 2026-04-10T22-32 labels Apr 10, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🎪 Showtime is building environment on GHA for 6be81d3

@github-actions github-actions bot added the 🎪 6be81d3 🤡 eschutho Environment 6be81d3 requested by eschutho label Apr 10, 2026
@dosubot dosubot bot added the viz:charts:export Related to exporting charts label Apr 10, 2026
@github-actions github-actions bot added 🎪 6be81d3 🚦 deploying Environment 6be81d3 status: deploying and removed 🎪 6be81d3 🚦 building Environment 6be81d3 status: building labels Apr 10, 2026
@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review bot commented Apr 10, 2026

Code Review Agent Run #4253c3

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 00ab3a3..6be81d3
    • superset-frontend/src/utils/downloadAsImage.test.ts
    • superset-frontend/src/utils/downloadAsImage.tsx
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 10, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 64.42%. Comparing base (de40b58) to head (6be81d3).

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #39285   +/-   ##
=======================================
  Coverage   64.42%   64.42%           
=======================================
  Files        2553     2553           
  Lines      132588   132589    +1     
  Branches    30758    30759    +1     
=======================================
+ Hits        85416    85417    +1     
  Misses      45686    45686           
  Partials     1486     1486           
Flag Coverage Δ
javascript 66.11% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.

@github-actions github-actions bot added 🎪 6be81d3 🚦 failed Environment 6be81d3 status: failed 🎪 6be81d3 🌐 16.146.97.212:8080 Environment 6be81d3 URL: http://16.146.97.212:8080 (click to visit) and removed 🎪 6be81d3 🚦 deploying Environment 6be81d3 status: deploying labels Apr 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🎪 fc24d4b 🤡 eschutho Environment fc24d4b requested by eschutho 🎪 fc24d4b 🚦 running Environment fc24d4b status: running 🎪 fc24d4b 🌐 35.89.121.178:8080 Environment fc24d4b URL: http://35.89.121.178:8080 (click to visit) 🎪 fc24d4b 📅 2026-04-10T21-52 Environment fc24d4b created at 2026-04-10T21-52 size/M viz:charts:export Related to exporting charts 🎪 6be81d3 🤡 eschutho Environment 6be81d3 requested by eschutho 🎪 6be81d3 🚦 failed Environment 6be81d3 status: failed 🎪 6be81d3 🌐 16.146.97.212:8080 Environment 6be81d3 URL: http://16.146.97.212:8080 (click to visit) 🎪 6be81d3 📅 2026-04-10T22-32 Environment 6be81d3 created at 2026-04-10T22-32 🎪 ⌛ 48h Environment expires after 48 hours (default)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant