You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#41989 [Regression]: tsconfig "extends" bare specifier isn't resolved via node_modules walk-up like tsc (fatal since 1.62)
#41998 [Regression]: directory-form tsconfig project references ("path": "../pkg") fail to resolve (fatal since 1.62)
#41985 Accessibility snapshot drops button name when text is nested inside spans with aria-hidden SVG
#42000 [Regression]: page.evaluate() arg of a branded primitive type (string & { brand }) no longer type-checks since 1.62
#42013 [BUG]Image-type actionable elements are not presented in the snapshot.
### v1.62.0## 🧱 New component testing model
Component testing moves to a stories and galleries model.
A story wraps your component in one specific scenario — hard-coded props, mock data, providers — and a gallery page that you serve renders stories on demand.
The new fixtures.mount() fixture navigates to the gallery, mounts a story by id, and returns a Locator scoped to the story's root element:
test('click should expand',async({ mount })=>{constcomponent=awaitmount('components/Expandable/Stateful');awaitcomponent.getByRole('button').click();awaitexpect(component.getByTestId('expanded')).toHaveValue('true');});
Pass a story type as a template argument to type-check its props, and use update(props) / unmount() on the returned locator to re-render or tear down within a test.
🛑 Cancel operations with AbortSignal
Most operations and web-first assertions now accept a signal option that takes an AbortSignal, letting you cancel long-running actions, navigations, waits, and assertions:
// Visual comparisons store the golden snapshot as lossless WebP.awaitexpect(page).toHaveScreenshot('homepage.webp');// Standalone screenshots can trade quality for size with lossy WebP.awaitpage.screenshot({path: 'homepage.webp',quality: 50});
page.screenshot() and [locator.screenshot() (https://playwright.dev/docs/api/class-locator#locator-screenshot) also accept webp as a type, where quality 100 (the default) is lossless and lower values use lossy compression.
🧩 Custom test filtering with Reporter.preprocess()
New reporter.preprocess() hook runs after the configuration is resolved and before reporter.onBegin(), letting a reporter mark individual tests as skipped, excluded, fixed, or failing through a TestRun object:
New testConfig.retryStrategy controls when failed tests are retried.
The default 'immediate' retries as soon as a worker is free; 'isolated' runs all retries at the end, one by one in a single worker, to minimize interference with the rest of the suite:
New option credentials includes the context's virtual WebAuthn Credentials (passkeys) in the storage state, so they can be persisted and re-seeded into later contexts.
Actions
New scroll option ("auto" | "none") on actions to opt out of Playwright's automatic scroll-into-view.
Network
New apiResponse.timing() returns resource timing information for an API response.
Evaluation
New locator.waitForFunction() waits until a function — called with the matching element — returns a truthy value.
page.evaluate() and related methods now accept functions as evaluate arguments.
This version was also tested against the following stable channels:
Google Chrome 151
Microsoft Edge 151
### v1.61.1### Bug Fixes
#41365 [Bug]: Expect.Extend matcher with same name as default matcher in same expect instance overrides default matchers implementation to custom matcher
#41351 [Bug]: Playwright UI mode: apiRequestContext._wrapApiCall reports unexpected number of bytes (same test passes in headed mode)
#41360 [Bug]: Trace viewer: message times in websockets are downscaled by 1000
#41311 [Bug]: [Regression]: Sync loader throws "context.conditions?.includes is not a function" on Node 22.15
#41371 [Regression]: Sync ESM loader (registerHooks) fails to resolve extensionless .ts subpath imports across pnpm workspace symlinks
Commit history:
15b1ae perf(mcp): skip aria snapshot capture when the response discards it (#41923)
e4e04a docs: release notes for v1.62 Python, Java, and .NET (#42043)
Add the Version 1.62 section to release-notes-python.md, release-notes-java.md, and release-notes-csharp.md, covering the cross-language 1.62 features:
WebP screenshots via [method: Page.screenshot] / [method: Locator.screenshot]
new scroll action option
[method: Locator.waitForFunction]
[method: APIResponse.timing]
Should be cherry-picked to release-1.62 alongside the language rolls.
0e057b docs(release-notes): mention the isolated headless clipboard in 1.62 (#42040)
headless browsers keep the clipboard to themselves instead of sharing the clipboard of the operating system, which WebKit on macOS started doing in build 2334
announce it in the 1.62 release notes so that anyone who relied on the clipboard of the machine being involved knows what changed
Sample colors from part of an image instead of the whole thing — the most-requested evergreen feature, open since 2021 (#176, with earlier attempts in #44 and #90). Thanks @runshotgun for the original ask.
New region option
Coordinates are fractions of the image size (0–1) from the top-left, so a region is resolution-independent — the same values work on a thumbnail and the full-size original.
// Colors from the bottom third — e.g. for a gradient overlapping the imageconstpalette=awaitgetPalette(img,{region: {x: 0,y: 0.66,width: 1,height: 0.34},});// Center cropconstcolor=awaitgetColor(img,{region: {x: 0.25,y: 0.25,width: 0.5,height: 0.5},});
Works everywhere an option object does: getColor, getPalette, getSwatches, getPaletteProgressive, the *Sync functions, observe(), and the CLI — in both browser and Node.
colorthief image.jpg --region 0,0.66,1,0.34
What changed
Cropping happens on the decoded pixel buffer right after loading, before sampling — so every entry point gets region support, including custom loaders and quantizers supplied via configure().
Regions are validated up front, so a malformed rect throws before the image is decoded rather than after.
A region running past the right or bottom edge is clamped to the image; out-of-range or zero-sized values throw.
Proportions are relative to the region, not the whole image.
⚠️ The worker option is now a no-op
worker: true is still accepted but ignored, and logs a one-time deprecation warning. The isWorkerSupported, extractInWorker, and terminateWorker exports from colorthief/internals are now no-op shims. Nothing breaks in v3 — all of it is removed in v4.
It cost more than it saved. Only quantization ran off-thread. Decoding, pixel sampling, and the structured clone of the pixel array all stayed on the main thread — and serializing Array<[r, g, b]> (one small array per sampled pixel) ran several times longer than the quantization it avoided:
Image
Quantize (what the worker saved)
Structured clone
0.3 MP, quality 10
0.7 ms
2.9 ms
2 MP, quality 10
2.5 ms
19.9 ms
12 MP, quality 10
12.9 ms
199 ms
The gap widens as images get larger — the opposite of how the feature was meant to scale.
It was also returning different colors. The worker carried a hand-inlined copy of MMCQ that quantized in RGB while the main path defaults to OKLCH, and it skipped the few-color short-circuit and filter relaxation. Two calls differing only by worker: true disagreed. Routing the flag through the normal pipeline makes them agree.
Getting extraction off the main thread
Run Color Thief inside your own worker and hand it an ImageBitmap. Bitmaps are transferable, so pixels move without being copied and the whole pipeline — decode, sampling, quantization — runs off-thread:
// main.jsconstbitmap=awaitcreateImageBitmap(await(awaitfetch(url)).blob());constworker=newWorker('./palette-worker.js',{type: 'module'});worker.postMessage({ bitmap },[bitmap]);// transferred, not clonedworker.onmessage=(e)=>render(e.data.palette);
// palette-worker.jsimport{getPalette}from'colorthief';self.onmessage=async({ data })=>{constpalette=awaitgetPalette(data.bitmap,{colorCount: 5});// Color objects don't survive structured clone — send plain dataself.postMessage({palette: palette.map((c)=>c.hex())});};
Smaller bundles
Deleting the inlined quantizer shrank every build:
Bundle
Before
After
dist/index.js
55.6 kB
46.2 kB
−17%
dist/internals.js
45.0 kB
39.4 kB
−13%
dist/umd/color-thief.global.js
30.0 kB
22.3 kB
−25%
Notes
Backward compatible — no breaking changes. The only behavior change is that worker: true now returns the same palette as the default path instead of a different one.
The deprecation warning fires once per page load, not once per call.
### v3.4.1Patch release fixing a bundler warning reported in #283.
Fixed
WasmQuantizer fell back to importing ../../dist/wasm/color_thief_wasm.js when constructed with no arguments, but that file has never been included in the published package. Bundlers resolving colorthief/internals — for example to use oklchToRgb — reported it as an unresolvable module. Applications built and ran correctly (the dead code was tree-shaken away), but every build printed a warning.
The wasm-bindgen glue module is now supplied by the caller:
init() with no module now throws a message pointing at the wasm-pack build step. Neither of the old code paths could succeed from an npm install, so nothing that previously worked has changed.
Thanks to @magic-akari for the report and diagnosis.
Color Thief can now read and report wide-gamut colors, fixing incorrect results on P3-tagged images where out-of-sRGB colors were collapsed down to sRGB (thanks @LeaVerou — #266).
New gamut option
Opt in per call (or via configure): 'srgb' (default), 'display-p3', or 'auto'.
constpalette=awaitgetPalette(img,{gamut: 'display-p3'});palette[0].css();// 'color(display-p3 …)'palette[0].gamut;// 'display-p3'// 'auto' upgrades to P3 only when the image actually uses out-of-sRGB colorsconstauto=awaitgetPalette(img,{gamut: 'auto'});
What changed
Browser loaders read the image through a P3 canvas (getContext('2d', { colorSpace: 'display-p3' })) with feature detection and automatic sRGB fallback.
Gamut-aware OKLCH quantization preserves the extra saturation instead of clamping it away.
Color objects now carry .gamut. .css() emits color(display-p3 …) and .oklch() reports the true wider chroma, while .rgb()/.array()/.hex() stay sRGB (gamut-mapped) so existing consumers keep working. Use .rgb('display-p3') for raw P3 components.
Threaded through the async, sync, worker, and progressive paths.
Notes
Backward compatible — default behavior is unchanged sRGB.
Node output is sRGB for now; P3 there is tracked as a follow-up.
Bumps brace-expansion 2.1.1 -> 2.1.4, clearing two high-severity DoS
advisories (GHSA-3jxr-9vmj-r5cp, GHSA-mh99-v99m-4gvg), and picks up a
mocha patch bump along the way. Lockfile only; no dependency ranges
change.
Three advisories are deliberately left in place:
esbuild is reachable only through tsup's ^0.27.0 range, while the
fix landed in 0.28.1. Forcing it would mean an overrides entry
pinning a 0.x minor ahead of what tsup was tested against, and the
advisory covers arbitrary file read via esbuild's dev server on
Windows — a server tsup never starts.
diff and serialize-javascript come in through mocha, and the only
offered fix is a downgrade to mocha 11.3.0.
sharp needs 0.35.0, which requires Node >=20.9.0. That drops Node 18,
so it is queued for v4 — see ROADMAP.md.
Add package contents check to CI
Guards the class of bug behind Bump playwright-core from 1.49.1 to 1.50.1 #283, where dist/ shipped an import of
dist/wasm/color_thief_wasm.js — a file the published tarball never
contained. Builds still succeeded, since the dead code was tree-shaken,
but every bundler resolving colorthief/internals printed an
unresolvable-import warning and nothing in CI noticed. The fix went out
in 3.4.1; this stops it recurring.
scripts/check-package.mjs makes three assertions against the file list
npm pack would actually produce:
Every path package.json points at (exports, main, module, types, bin)
is in the tarball.
Every relative specifier emitted into dist/ resolves to a file in the
tarball.
The browser bundles carry no reference to sharp, so the Node loader
cannot leak into a browser build.
A plain regex over dist/ is unusable for (2): it reports four false
positives on the current tree, because WasmQuantizer's "build this
yourself" error message quotes an import(...) call inside a string, and
the generated .d.ts doc comments show an illustrative import path.
Neither is a reference a bundler would try to resolve. So the scanner
blanks comments and replaces string literals with opaque placeholders
before matching, and resolves the .js specifiers that declaration files
use to the .d.ts siblings actually on disk.
Verified it fails on all four import forms — bare side-effect import,
dynamic import (the exact Bump playwright-core from 1.49.1 to 1.50.1 #283 shape), require, and re-export — plus a
sharp leak and a package.json entry pointing at an unpublished file,
while staying quiet on specifiers inside strings and comments.
Wired into CI alongside npm run typecheck, which the package has had
as a script but has never run in CI, and into prepublishOnly so a bad
tarball cannot go out even when publishing by hand.
Queue sharp and Node floor bump for v4
peerDependencies accepts sharp >=0.33.0, but every sharp below 0.35.0
inherits four libvips CVEs (GHSA-f88m-g3jw-g9cj), so anyone auditing a
project that installs the Node path can satisfy our range with a
vulnerable sharp.
The fix cannot ship in a 3.x minor: sharp 0.35 requires Node >=20.9.0,
so raising the sharp floor also drops Node 18. Records the full change
set for v4 — the peer range, an engines field the package has never
declared, the CI matrix and .nvmrc move off Node 18 (EOL April 2025),
and the matching devDependency bump.
Until then 3.x keeps the wider range: sharp is an optional peer, browser
users never install it, and Node users can choose 0.35+ themselves.
Release notes previously lived only on GitHub, so anyone installing from
npm had no in-package record of what changed between versions — which
matters most for 3.5.0, where an option people may be passing (worker)
silently became a no-op.
Covers 3.5.0 back to 2.3.2, seeded from the existing GitHub release
notes; earlier releases point at the GitHub releases page rather than
inventing history for them. Notes that 3.3.2 and 2.5.0 were tagged on
GitHub but never published to npm, and which release actually carried
their changes.
Adds CHANGELOG.md to the files array — npm does not include changelogs
automatically, and files was scoped to dist/ and src/, so it would not
otherwise ship. Cross-document links use absolute URLs since ROADMAP.md
is not part of the tarball.
Also links the changelog and roadmap from the README, and adds a
changelog step to the release checklist so it stays current.
Sample a sub-rectangle instead of the whole image via region: { x, y, width, height } in normalized 0-1 coordinates, so the region is
independent of pixel dimensions. The crop is applied to the decoded
pixel buffer right after loading, before sampling — so async, sync,
progressive, observe(), and the CLI all get it without changes, and so
do custom loaders and quantizers supplied via configure(). Regions are
validated up front, so a bad rect throws before the image is decoded.
Also adds --region x,y,width,height to the CLI.
Deprecate the Web Worker path
worker: true is now accepted but ignored, and warns once. The three
worker exports in colorthief/internals (isWorkerSupported,
extractInWorker, terminateWorker) become no-op shims. All of it is
removed in v4. Nothing breaks in v3.
Offloading cost more than it saved. Only quantization ran off-thread;
decode, pixel sampling, and the structured clone of the pixel array all
stayed on the main thread — and serializing Array<[r, g, b]>, one
small array per sampled pixel, ran several times longer than the
quantization it avoided. At 2 MP / quality 10 that was ~20ms of cloning
to skip ~2.5ms of quantizing, and the gap widens as images get larger.
It was also drifting. The worker carried a hand-inlined copy of MMCQ
that quantized in RGB while the main path defaults to OKLCH, and it
skipped the few-color short-circuit and filter relaxation, so worker: true returned different colors than the default. Routing the
flag through the normal pipeline makes both paths agree.
Callers who want extraction off the main thread should run Color Thief
inside their own worker with an ImageBitmap or OffscreenCanvas source:
the whole pipeline moves off-thread and the bitmap transfers without
copying. README has the recipe.
Deleting the inlined quantizer also shrank the bundles: index.js
55.6kB -> 46.2kB, internals.js 45.0kB -> 39.4kB, and the UMD global
30.0kB -> 22.3kB.
WasmQuantizer fell back to import('../../dist/wasm/color_thief_wasm.js')
when constructed with no arguments, but that file has never been included in
the published package. Bundlers resolving colorthief/internals reported it
as a missing module (Bump playwright-core from 1.49.1 to 1.50.1 #283). The other branch — fetching a .wasm URL and
calling WebAssembly.instantiate directly — was also non-functional, since
the wasm-bindgen quantize(&[u8]) -> Vec<u8> signature needs the generated
JS glue for marshalling.
The glue module is now supplied by the caller:
const q = new WasmQuantizer(await import('./pkg/color_thief_wasm.js'));
init() with no module throws a message pointing at the wasm-pack build
step instead of failing on a path that was never shipped. Nothing that
previously worked changes — neither code path could succeed from npm.
Adds regression coverage: WasmQuantizer init/error behavior, and an
assertion that no built bundle references the unshipped dist/wasm path.
Fixes Bump @types/node from 22.8.1 to 22.8.7 #266: extraction of P3-tagged / wide-gamut images previously
collapsed out-of-sRGB colors (e.g. P3 red read as sRGB red), losing the
extra saturation.
Adds an opt-in gamut option ('srgb' default, 'display-p3', 'auto'):
Browser loaders read through a P3 canvas (getContext colorSpace) with
feature detection + sRGB fallback; shared canvas logic factored into
loaders/canvas-utils.ts (removing browser/sync duplication).
Quantization runs in gamut-aware OKLCH; color-space.ts gains P3 matrices
(composed via XYZ, sRGB path kept byte-identical), isOutOfSrgbGamut,
p3ToSrgb/srgbToP3, and gamut-aware luminance.
Color objects carry .gamut: css() emits color(display-p3 ...) and
oklch() reports the true wider chroma, while rgb()/array()/hex() stay
sRGB (gamut-mapped) so existing consumers don't break. rgb('display-p3')
exposes raw P3 components.
'auto' upgrades to P3 only when a sampled pixel falls outside sRGB.
Threaded through pipeline, worker, sync, and progressive paths.
resolveOutputGamut: explicit 'srgb' request now always wins.
Node output stays sRGB for now (loader accepts the option); documented as
a follow-up.
Tests: +31 node tests (gamut math, pipeline integration via synthetic P3
buffers, Color accessors, prior gaps) and a new Cypress spec with a real
P3-tagged PNG covering img/canvas/ImageData/worker paths.
Verified locally: read the full diff, traced control flow, built clean, and all 82 node tests pass (ESM + CJS).
The short-circuit for few-color images now runs before the RGB→OKLCH→RGB conversion, so images with fewer distinct colors than requested return their exact original RGB instead of a round-trip approximation (e.g. pure red comes back as 255,0,0, not 254,0,1). The early break also bounds the unique-color scan so complex images pay ~nothing. Nice accuracy + perf win.
Two non-blocking notes for future reference:
The short-circuit now applies to any quantizer (WASM, custom via configure()), not just MMCQ — an improvement, but MmcqQuantizer called directly via colorthief/internals no longer self-short-circuits.
The worker path (extractInWorker) is separate and already bypassed both OKLCH and the short-circuit; unchanged here, but that main-thread/worker inconsistency remains worth a future issue.
Thanks @ksubileau!
51a47a docs: add feature roadmap items from monthly health report
36079c docs: replace PLAN.md and V3.md with ROADMAP.md
Everything in the v2 plan and v3 rewrite docs has shipped as of 3.3.1.
Consolidate the one remaining forward-looking item (productizing the
WASM quantizer) into ROADMAP.md and drop the superseded docs.
feat: add CLI tool for extracting colors from images
13dbd7 feat: add CLI tool for extracting colors from images
Adds colorthief CLI with color, palette, and swatches subcommands.
Supports --json, --css, and ANSI output formats, stdin piping, multi-file
input, and a friendly error message when sharp is not installed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
94dcd3 docs: update default color space in README and TypeScript interfaces to 'oklch'
012f96 fix: add browser-specific builds to eliminate sharp warnings in bundlers
Browser bundlers (webpack/Angular, Vite, etc.) were warning about the
unresolvable 'sharp' dependency even though it's only used in Node.js.
Adds conditional exports with a "browser" condition that points to builds
with no sharp or Node loader references.
df2b71 [office-js] [office-js-preview] (Access) Ensure that deprecation warn… (#75336)
8589d9 🤖 Merge PR #75286 [clearoutio__clearout] Remove, bundled with @clearoutio/clearout by @max-programming
5bd77b 🤖 Merge PR #75235 CoverageJSON Update: Rework how CoverageCollection,Coverage generics work. Add bounds property to axes by @murithigeo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Updated Packages