From 3032225475df4b98569037ef03a49b50d7d7a84e Mon Sep 17 00:00:00 2001 From: Teimur Gasanov Date: Fri, 17 Jul 2026 13:38:50 +0600 Subject: [PATCH 1/2] perf: replace pixelmatch with @blazediff/core Co-Authored-By: Claude Opus 4.8 --- .../js/__tests__/RootContainer.test.tsx | 2 +- .../assets/js/widgets/lib/cssbattle.ts | 6 +-- apps/codebattle/package.json | 2 +- apps/codebattle/pnpm-lock.yaml | 47 ++++++++----------- apps/runner/images/css/checker.js | 4 +- apps/runner/images/css/package-lock.json | 22 ++++----- apps/runner/images/css/package.json | 2 +- 7 files changed, 35 insertions(+), 50 deletions(-) diff --git a/apps/codebattle/assets/js/__tests__/RootContainer.test.tsx b/apps/codebattle/assets/js/__tests__/RootContainer.test.tsx index 24de95b83..11b90cb88 100644 --- a/apps/codebattle/assets/js/__tests__/RootContainer.test.tsx +++ b/apps/codebattle/assets/js/__tests__/RootContainer.test.tsx @@ -16,7 +16,7 @@ import task from '../widgets/machines/task'; import RootContainer from '../widgets/pages/RoomWidget'; import reducers from '../widgets/slices'; -vi.mock('pixelmatch', () => ({ default: () => {} })); +vi.mock('@blazediff/core', () => ({ default: () => {} })); vi.mock('monaco-editor', () => ({ editor: { diff --git a/apps/codebattle/assets/js/widgets/lib/cssbattle.ts b/apps/codebattle/assets/js/widgets/lib/cssbattle.ts index 13be60cc1..2572be2fe 100644 --- a/apps/codebattle/assets/js/widgets/lib/cssbattle.ts +++ b/apps/codebattle/assets/js/widgets/lib/cssbattle.ts @@ -1,4 +1,4 @@ -import pixelmatch from 'pixelmatch'; +import blazediff from '@blazediff/core'; const diffThreshold = 0.1; @@ -74,7 +74,7 @@ export const matchBattlePictures = ( const firstDiff = firstDiffContext.createImageData(width, height); const secondDiff = secondDiffContext.createImageData(width, height); - const firstStats = pixelmatch( + const firstStats = blazediff( firstDataImg.data, targetDataImg.data, firstDiff.data, @@ -82,7 +82,7 @@ export const matchBattlePictures = ( height, { threshold: diffThreshold }, ); - const secondStats = pixelmatch( + const secondStats = blazediff( secondDataImg.data, targetDataImg.data, secondDiff.data, diff --git a/apps/codebattle/package.json b/apps/codebattle/package.json index 1a1ba3133..bb9460c29 100644 --- a/apps/codebattle/package.json +++ b/apps/codebattle/package.json @@ -31,6 +31,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@blazediff/core": "^1.9.3", "@dnd-kit/core": "^6.3.1", "@ebay/nice-modal-react": "^1.2.13", "@emoji-mart/data": "^1.2.1", @@ -73,7 +74,6 @@ "phoenix": "^1.8.7", "phoenix_html": "^4.3.0", "phoenix_live_view": "^1.1.31", - "pixelmatch": "^7.2.0", "popper.js": "^1.16.1", "process": "^0.11.10", "prop-types": "^15.8.1", diff --git a/apps/codebattle/pnpm-lock.yaml b/apps/codebattle/pnpm-lock.yaml index 743440595..e744ea1a5 100644 --- a/apps/codebattle/pnpm-lock.yaml +++ b/apps/codebattle/pnpm-lock.yaml @@ -25,6 +25,9 @@ importers: .: dependencies: + '@blazediff/core': + specifier: ^1.9.3 + version: 1.9.3 '@dnd-kit/core': specifier: ^6.3.1 version: 6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -54,7 +57,7 @@ importers: version: 3.4.0(@fortawesome/fontawesome-svg-core@7.3.0)(react@19.2.7) '@inertiajs/react': specifier: ^2.3.27 - version: 2.3.27(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 2.3.27(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@5.5.0) '@inline-svg-unique-id/react': specifier: ^1.2.3 version: 1.2.3(react@19.2.7) @@ -151,9 +154,6 @@ importers: phoenix_live_view: specifier: ^1.1.31 version: 1.1.31 - pixelmatch: - specifier: ^7.2.0 - version: 7.2.0 popper.js: specifier: ^1.16.1 version: 1.16.1 @@ -450,6 +450,9 @@ packages: resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} + '@blazediff/core@1.9.3': + resolution: {integrity: sha512-4XGs39o9rkkBMgyr7QtI+IRIN78wZa2423C0EbSLvXK58A3WivQCwXICGbvEqBucpSdCK4re3ezQ9ko3IYQMHg==} + '@bramus/specificity@2.4.2': resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} hasBin: true @@ -3410,14 +3413,6 @@ packages: resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} - pixelmatch@7.2.0: - resolution: {integrity: sha512-xhcb4yHu9sM/G7foGzoLtXYcC0zHEaOXXjRKhGup0fw78Nf2Tkiapv4EQyMzrbcmQPsllAI7DbFY2UT7PlI9Pg==} - hasBin: true - - pngjs@7.0.0: - resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} - engines: {node: '>=14.19.0'} - polished@2.3.3: resolution: {integrity: sha512-59V4fDbdxtH4I1m9TWxFsoGJbC8nnOpUYo5uFmvMfKp9Qh+6suo4VMUle1TGIIUZIGxfkW+Rs485zPk0wcwR2Q==} deprecated: polished@2.X is no longer supported. Please upgrade to @latest for important bug and security fixes. @@ -4637,6 +4632,8 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 + '@blazediff/core@1.9.3': {} + '@bramus/specificity@2.4.2': dependencies: css-tree: 3.2.1 @@ -4954,10 +4951,10 @@ snapshots: react: 19.2.7 warning: 4.0.3 - '@inertiajs/core@2.3.27': + '@inertiajs/core@2.3.27(supports-color@5.5.0)': dependencies: '@types/lodash-es': 4.17.12 - axios: 1.18.1 + axios: 1.18.1(supports-color@5.5.0) laravel-precognition: 1.0.2 lodash-es: 4.18.1 qs: 6.15.3 @@ -4965,9 +4962,9 @@ snapshots: - debug - supports-color - '@inertiajs/react@2.3.27(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@inertiajs/react@2.3.27(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@5.5.0)': dependencies: - '@inertiajs/core': 2.3.27 + '@inertiajs/core': 2.3.27(supports-color@5.5.0) '@types/lodash-es': 4.17.12 laravel-precognition: 1.0.2 lodash-es: 4.18.1 @@ -5677,7 +5674,7 @@ snapshots: acorn@8.16.0: optional: true - agent-base@6.0.2: + agent-base@6.0.2(supports-color@5.5.0): dependencies: debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: @@ -5765,11 +5762,11 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - axios@1.18.1: + axios@1.18.1(supports-color@5.5.0): dependencies: follow-redirects: 1.16.0 form-data: 4.0.6 - https-proxy-agent: 5.0.1 + https-proxy-agent: 5.0.1(supports-color@5.5.0) proxy-from-env: 2.1.0 transitivePeerDependencies: - debug @@ -6658,9 +6655,9 @@ snapshots: html-url-attributes@3.0.1: {} - https-proxy-agent@5.0.1: + https-proxy-agent@5.0.1(supports-color@5.5.0): dependencies: - agent-base: 6.0.2 + agent-base: 6.0.2(supports-color@5.5.0) debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -6921,7 +6918,7 @@ snapshots: laravel-precognition@1.0.2: dependencies: - axios: 1.18.1 + axios: 1.18.1(supports-color@5.5.0) lodash-es: 4.18.1 transitivePeerDependencies: - debug @@ -7494,12 +7491,6 @@ snapshots: picomatch@4.0.5: {} - pixelmatch@7.2.0: - dependencies: - pngjs: 7.0.0 - - pngjs@7.0.0: {} - polished@2.3.3: dependencies: '@babel/runtime': 7.29.7 diff --git a/apps/runner/images/css/checker.js b/apps/runner/images/css/checker.js index 27261e5ce..a316b583b 100644 --- a/apps/runner/images/css/checker.js +++ b/apps/runner/images/css/checker.js @@ -1,5 +1,5 @@ import { readFileSync, writeFileSync } from 'fs'; -import pixelmatch from 'pixelmatch'; +import blazediff from '@blazediff/core'; import LZString from 'lz-string'; import nodeHtmlToImage from 'node-html-to-image'; import { PNG } from 'pngjs'; @@ -61,7 +61,7 @@ const run = function run(args = []) { const targetImgDataUri = `data:${mime};${encoding},${targetImageBuffer.toString(encoding)}`; try { - const stats = pixelmatch(solutionImg.data, targetImg.data, diff.data, width, height, { threshold: 0.1 }); + const stats = blazediff(solutionImg.data, targetImg.data, diff.data, width, height, { threshold: 0.1 }); const match = getMatchPoints(stats, width, height); const matchPercentage = getMatchPercentageText(match); const diffBuffer = PNG.sync.write(diff); diff --git a/apps/runner/images/css/package-lock.json b/apps/runner/images/css/package-lock.json index c75a7f525..1b778a5aa 100644 --- a/apps/runner/images/css/package-lock.json +++ b/apps/runner/images/css/package-lock.json @@ -8,10 +8,10 @@ "name": "css-checker", "version": "0.0.1", "dependencies": { + "@blazediff/core": "^1.9.3", "@types/node": "^20.11.25", "lz-string": "^1.5.0", "node-html-to-image": "^5.0.0", - "pixelmatch": "^6.0.0", "pngjs": "^7.0.0" } }, @@ -34,6 +34,12 @@ "node": ">=6.9.0" } }, + "node_modules/@blazediff/core": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@blazediff/core/-/core-1.9.3.tgz", + "integrity": "sha512-4XGs39o9rkkBMgyr7QtI+IRIN78wZa2423C0EbSLvXK58A3WivQCwXICGbvEqBucpSdCK4re3ezQ9ko3IYQMHg==", + "license": "MIT" + }, "node_modules/@puppeteer/browsers": { "version": "2.3.1", "license": "Apache-2.0", @@ -381,8 +387,7 @@ }, "node_modules/devtools-protocol": { "version": "0.0.1330662", - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -800,16 +805,6 @@ "version": "1.1.1", "license": "ISC" }, - "node_modules/pixelmatch": { - "version": "6.0.0", - "license": "ISC", - "dependencies": { - "pngjs": "^7.0.0" - }, - "bin": { - "pixelmatch": "bin/pixelmatch" - } - }, "node_modules/pngjs": { "version": "7.0.0", "license": "MIT", @@ -857,7 +852,6 @@ "version": "23.2.2", "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@puppeteer/browsers": "2.3.1", "chromium-bidi": "0.6.5", diff --git a/apps/runner/images/css/package.json b/apps/runner/images/css/package.json index e8516a40b..994e612b6 100644 --- a/apps/runner/images/css/package.json +++ b/apps/runner/images/css/package.json @@ -9,10 +9,10 @@ "js-yaml": "^4.1.1" }, "dependencies": { + "@blazediff/core": "^1.9.3", "@types/node": "^20.11.25", "lz-string": "^1.5.0", "node-html-to-image": "^5.0.0", - "pixelmatch": "^6.0.0", "pngjs": "^7.0.0" } } From 9871417786fba8cbfef08d50a6bafbfee35faeef Mon Sep 17 00:00:00 2001 From: Teimur Gasanov Date: Mon, 20 Jul 2026 11:59:22 +0600 Subject: [PATCH 2/2] chore: pin @blazediff/core to 1.9.3 Co-Authored-By: Claude Opus 4.8 --- apps/codebattle/package.json | 2 +- apps/codebattle/pnpm-lock.yaml | 2 +- apps/runner/images/css/package-lock.json | 2 +- apps/runner/images/css/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/codebattle/package.json b/apps/codebattle/package.json index bb9460c29..60ef451d3 100644 --- a/apps/codebattle/package.json +++ b/apps/codebattle/package.json @@ -31,7 +31,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@blazediff/core": "^1.9.3", + "@blazediff/core": "1.9.3", "@dnd-kit/core": "^6.3.1", "@ebay/nice-modal-react": "^1.2.13", "@emoji-mart/data": "^1.2.1", diff --git a/apps/codebattle/pnpm-lock.yaml b/apps/codebattle/pnpm-lock.yaml index e744ea1a5..92c879c5b 100644 --- a/apps/codebattle/pnpm-lock.yaml +++ b/apps/codebattle/pnpm-lock.yaml @@ -26,7 +26,7 @@ importers: .: dependencies: '@blazediff/core': - specifier: ^1.9.3 + specifier: 1.9.3 version: 1.9.3 '@dnd-kit/core': specifier: ^6.3.1 diff --git a/apps/runner/images/css/package-lock.json b/apps/runner/images/css/package-lock.json index 1b778a5aa..e5a862ba9 100644 --- a/apps/runner/images/css/package-lock.json +++ b/apps/runner/images/css/package-lock.json @@ -8,7 +8,7 @@ "name": "css-checker", "version": "0.0.1", "dependencies": { - "@blazediff/core": "^1.9.3", + "@blazediff/core": "1.9.3", "@types/node": "^20.11.25", "lz-string": "^1.5.0", "node-html-to-image": "^5.0.0", diff --git a/apps/runner/images/css/package.json b/apps/runner/images/css/package.json index 994e612b6..b2737e74a 100644 --- a/apps/runner/images/css/package.json +++ b/apps/runner/images/css/package.json @@ -9,7 +9,7 @@ "js-yaml": "^4.1.1" }, "dependencies": { - "@blazediff/core": "^1.9.3", + "@blazediff/core": "1.9.3", "@types/node": "^20.11.25", "lz-string": "^1.5.0", "node-html-to-image": "^5.0.0",