Client-side media: Add JPEG XL (JXL) support via canonical plugin#76990
Client-side media: Add JPEG XL (JXL) support via canonical plugin#76990adamsilverstein wants to merge 10 commits intotrunkfrom
Conversation
Add client-side JPEG XL image processing using a canonical plugin (wp-vips-jxl) that ships the vips-jxl.wasm module on demand, avoiding a 3.1 MB increase to the Gutenberg bundle. The implementation follows the same pattern as FFmpeg WASM (#76964): - Plugin sets window.__vipsJxlConfig on editor pages - Auto-installs via REST API when user uploads JXL and has capabilities - Falls back to server-side processing when plugin unavailable Key changes: - vips: Add setJxlWasmUrl() for runtime JXL WASM URL injection with lazy re-initialization of the vips instance - upload-media: Add jxl-plugin.ts for plugin detection, installation, and config fetching - upload-media: Add JXL to supported MIME types and image formats - upload-media: Add JXL plugin availability check in prepareItem() with graceful degradation Closes #76981
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Use the store name string 'core' directly instead of importing coreStore from the core-data package. This avoids adding a circular dependency (upload-media -> core-data -> block-editor -> upload-media) in the tsconfig project references graph.
Cast dispatch() and resolveSelect() results to the expected shapes since using the store name string 'core' (instead of the typed store descriptor) returns unknown.
|
Size Change: +688 B (+0.01%) Total Size: 7.76 MB 📦 View Changed
ℹ️ View Unchanged
|
|
Flaky tests detected in 5a83fce. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/24861610571
|
|
Looks like something with the rebase went wrong.
Love it! Makes me wonder whether this warrants a more standardized extensibility mechanism somehow 💡 🤔 Maybe in the future. |
Treat the REST config endpoint and the window.__vipsJxlConfig inline-script input as untrusted boundary data. Reject responses that don't carry a non-empty wasmUrl string instead of blindly casting and loading a potentially malformed URL into the WASM locateFile resolver. Also log install/fetch failures via console.warn so the silent server fallback is debuggable when something goes wrong. Replaces the inline ad-hoc window/dispatch casts with named interfaces (CoreDataDispatch, CoreDataResolveSelect, JxlWindowGlobals) documented at the top of the file. The core-data string literal workaround stays, since adding a project reference would create a circular upload-media -> core-data -> block-editor -> upload-media chain.
Cover the full degradation chain that the JXL upload flow depends on: plugin already active, malformed window config, empty wasmUrl, user without install_plugins capability, successful install + REST fetch, install failure, REST returning non-ok status, REST returning malformed body, fetch throwing, session-level caching of both success and failure, and fallback REST root when wpApiSettings is absent.
Since 'image/jxl' is now in CLIENT_SIDE_SUPPORTED_MIME_TYPES, isJxl already implies isVipsSupported, so the '(isJxl || isVipsSupported)' disjunction was redundant. Also lift the imageOutputFormats destructure and outputMimeType lookup out of the JXL block so they can be reused by the transcode check below, removing a duplicated destructure.
Replace the three-line mutable array build with an inline spread so the dynamicLibraries value is visible at the call site.
Fixed the rebase. Yes, I think a dedicated pluggable system for adding media format support probably makes sense at this point, I was thinking the same might be nice for UltraHDR support and already have proposed the same for ffmpeg support. |
Interesting approach! The canonical plugin idea is a compelling, but it sounds like this will have a few drawbacks:
I.e. will this mean we're effectively splitting core behaviour for client-side media processing? What I mean is: if the promise of client-side media processing is that you can throw pretty much any file at WordPress and it'll gracefully convert it, what does it mean if we have two or more formats that only work nicely if you're an admin user and hosting in a certain way? |
Great point @andrewserong - I hadn't really considered the permissions complexity or multisite. Two possible approaches:
So if we really want everyone to get support for these features, we probably need to include them directly. Thats also the less complex route which is appealing. I'm going to work up / finish the PRs that take that approach for this and GIF support. This will let us see the actual weight these libraries add to the bundle so we can make a more informed decision. Curious what @swissspidy's thoughts are on this. |
|
Thanks for looking into it!
Yes, IMO our goal should be: how do we ensure the maximum number of users can benefit from these features. In terms of what it means for the overall build size of Gutenberg and WP, it'd be good to understand what the main concerns are re: the overall size of it. I.e. is it mostly Playground, or are hosts concerned about the size? I agree if WP is still smaller than Drupal in overall filesize, then it sounds like we (theoretically) might have some wiggle room 🤞
One more idea, and this mightn't be any good, but just throwing it out there: what if we treat these features as part of WP but (somehow) allow a build flag of some kind that can safely strip out the built files and if they're not present, then we gracefully fallback to server processing. I guess what I'm saying is: make it possible for folks who need a smaller size of WordPress to build one, without it being the overall standard way someone downloads WordPress. This might be a non-starter, but just an idea. |
Resolve conflict in packages/upload-media/src/store/private-actions.ts by adopting trunk's refactored prepareItem flow. Main-file format conversion moved server-side on trunk, so the PR's client-side transcoding (with JXL gating) is no longer needed. Preserve the JXL plugin init (ensureVipsJxlAvailable + initVipsJxl) since it is still required for decoding JXL input client-side. Simplify the gate so it only runs when the input is JXL, matching trunk's new server-driven output-format approach.
Summary
Add client-side JPEG XL image processing using a canonical plugin (
wp-vips-jxl) that ships thevips-jxl.wasmmodule on demand.This follows the same pattern established for FFmpeg WASM in #76964: the heavy WASM binary (~2.3 MB) lives in a separate WordPress plugin that is auto-installed when a user first uploads a JXL image, keeping the Gutenberg bundle size unchanged.
How it works
image/jxlMIME typewindow.__vipsJxlConfigexists (set by the plugin)install_pluginscapability: auto-installs and activates via REST APIGraceful degradation
crossOriginIsolatedcrossOriginIsolatedunavailableKey changes
@wordpress/vips: AddsetJxlWasmUrl()for runtime JXL WASM URL injection with lazy re-initialization of the vips instance. Add JXL tosupportsQuality(). Set effort=3 for JXL encoding (default 7 is too slow for interactive use).@wordpress/upload-media: Newjxl-plugin.tsfor plugin detection, auto-installation, and config fetching. Addimage/jxlto supported MIME types andjxlto valid image formats. Add JXL plugin availability check inprepareItem()with proper fallback when output format is JXL but plugin unavailable.The canonical plugin (
wp-vips-jxl)A minimal WordPress plugin (~70 lines PHP) that:
vips-jxl.wasm(~2.3 MB) fromwasm-vips@0.0.16window.__vipsJxlConfigon editor pages viawp_add_inline_script/wp-vips-jxl/v1/configfor mid-session URL discovery after auto-installationTest plan
.jxlfile withwp-vips-jxlplugin active — verify client-side processing (resize, compress, thumbnails).jxlfile without plugin as admin — verify auto-install flow.jxlfile as non-admin without plugin — verify server-side fallbackvips-jxl.wasmis NOT in the Gutenberg build outputCloses #76981