From 06478667bfd9376107f1e47f5d72a9792ee8624a Mon Sep 17 00:00:00 2001 From: Zeke Sikelianos Date: Thu, 29 May 2025 07:36:36 -0700 Subject: [PATCH 1/4] switch to kontext model --- README.md | 2 +- components/footer.js | 8 ++++---- pages/about.js | 28 ++++------------------------ pages/api/predictions/index.js | 3 +-- pages/index.js | 4 ++-- 5 files changed, 12 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 9615a60..7b1ec4c 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ This app is powered by: 🚀 [Replicate](https://replicate.com/?utm_source=project&utm_campaign=paintbytext), a platform for running machine learning models in the cloud. -🎨 [InstructPix2Pix](https://replicate.com/timothybrooks/instruct-pix2pix?utm_source=project&utm_campaign=paintbytext), an open-source machine learning model that generates images from text. +🎨 [Kontext](https://replicate.com/black-forest-labs/flux-kontext-pro?utm_source=project&utm_campaign=paintbytext), an open-source machine learning model that edits images using text. ▲ [Vercel](https://vercel.com/), a platform for running web apps. diff --git a/components/footer.js b/components/footer.js index 64fd308..5f3a8d2 100644 --- a/components/footer.js +++ b/components/footer.js @@ -37,7 +37,7 @@ export default function Footer({ events, startOver, handleImageDropped }) { )} @@ -59,12 +59,12 @@ export default function Footer({ events, startOver, handleImageDropped }) {
Powered by{" "} - - InstructPix2Pix + + Black Forest Labs ,{" "} Replicate diff --git a/pages/about.js b/pages/about.js index cc2b5db..fc1e571 100644 --- a/pages/about.js +++ b/pages/about.js @@ -21,37 +21,17 @@ export default function About() { images based on the prompt.

-

- The image generation is powered by{" "} - - InstructPix2Pix - - , an open-source machine learning model that combines the knowledge - from GPT-3 and{" "} - - Stable Diffusion - {" "} - to generate a large dataset of image editing examples. This model was - created at the University of California, Berkeley by{" "} - Tim Brooks,{" "} - Aleksander Holynski, and{" "} - - Alexei A. Efros - - . -

-

The model is hosted on{" "} - + Replicate , which exposes a cloud API for running predictions. This website is - built with Next.js and hosted on + built with Next.js and hosted on{" "} Vercel, and uses - Replicate's API to run the InstructPix2Pix model. The source code + Replicate's API to run the Kontext Pro model. The source code is publicly available on{" "} - + GitHub . Pull requests welcome! diff --git a/pages/api/predictions/index.js b/pages/api/predictions/index.js index 930b2d4..74c2fa3 100644 --- a/pages/api/predictions/index.js +++ b/pages/api/predictions/index.js @@ -34,8 +34,7 @@ export default async function handler(req, res) { ); } else { console.log("Not using deployment") - // https://replicate.com/timothybrooks/instruct-pix2pix/versions - const version = "30c1d0b916a6f8efce20493f5d61ee27491ab2a60437c13c588468b9810ec23f" + const version = "d8ac233ef32825fd74937c891a2d56536d2736bcbd44b54d37ca5c2652a0a051" prediction = await replicate.predictions.create({ version, input: req.body diff --git a/pages/index.js b/pages/index.js index 7e9fecf..422f2b5 100644 --- a/pages/index.js +++ b/pages/index.js @@ -53,7 +53,7 @@ export default function Home() { const body = { prompt, - image: lastImage, + input_image: lastImage, }; const response = await fetch("/api/predictions", { @@ -88,7 +88,7 @@ export default function Home() { if (prediction.status === "succeeded") { setEvents( myEvents.concat([ - { image: prediction.output?.[prediction.output.length - 1] }, + { image: prediction.output }, ]) ); } From 92504ffdc58403db2a270406dfb70f1bdbdb2ba2 Mon Sep 17 00:00:00 2001 From: Zeke Sikelianos Date: Thu, 29 May 2025 12:45:00 -0700 Subject: [PATCH 2/4] use the official model and the latest replicate client --- package-lock.json | 256 ++++++++++++++++++++++++++++++++- package.json | 2 +- pages/api/predictions/index.js | 24 +--- 3 files changed, 257 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index ea002f8..4d10fcf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "react-dom": "18.2.0", "react-dropzone": "^14.2.2", "react-spinners": "^0.13.8", - "replicate": "^0.20.1" + "replicate": "^1.0.1" }, "devDependencies": { "autoprefixer": "^10.4.8", @@ -466,6 +466,19 @@ "react": "^16.8||^17||^18" } }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "optional": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/acorn": { "version": "8.8.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", @@ -729,6 +742,27 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -788,6 +822,31 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -1575,6 +1634,26 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1954,6 +2033,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause", + "optional": true + }, "node_modules/ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -2906,6 +3006,16 @@ "node": ">= 0.8.0" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -3019,6 +3129,23 @@ "pify": "^2.3.0" } }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "optional": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -3067,14 +3194,18 @@ } }, "node_modules/replicate": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/replicate/-/replicate-0.20.1.tgz", - "integrity": "sha512-QVyI1rowGsSfNuDrRmumYPdCHa/fN/RkI3NHpcK0i5hSSiWK69URAyheAC/0MIAiS3oUs4kD56PB9zEI4oHENw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replicate/-/replicate-1.0.1.tgz", + "integrity": "sha512-EY+rK1YR5bKHcM9pd6WyaIbv6m2aRIvHfHDh51j/LahlHTLKemTYXF6ptif2sLa+YospupAsIoxw8Ndt5nI3vg==", + "license": "Apache-2.0", "engines": { "git": ">=2.11.0", "node": ">=18.0.0", "npm": ">=7.19.0", "yarn": ">=1.7.0" + }, + "optionalDependencies": { + "readable-stream": ">=4.0.0" } }, "node_modules/resolve": { @@ -3151,6 +3282,27 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -3228,6 +3380,16 @@ "node": ">=10.0.0" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "optional": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", @@ -3914,6 +4076,15 @@ "integrity": "sha512-PQrOI8BJ9qUiVJuQfnKiJd15eDjDJH9TBKsNeMrtelT4NAk7d9mBVz1CoZkvoFnHQ0OW7Xnqmr1F2nScfAnznQ==", "requires": {} }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "optional": true, + "requires": { + "event-target-shim": "^5.0.0" + } + }, "acorn": { "version": "8.8.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", @@ -4097,6 +4268,12 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "optional": true + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -4134,6 +4311,16 @@ "update-browserslist-db": "^1.0.5" } }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "optional": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -4724,6 +4911,18 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "optional": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "optional": true + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5004,6 +5203,12 @@ "has-symbols": "^1.0.2" } }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "optional": true + }, "ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -5647,6 +5852,12 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "optional": true + }, "prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5722,6 +5933,19 @@ "pify": "^2.3.0" } }, + "readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "optional": true, + "requires": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + } + }, "readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -5755,9 +5979,12 @@ "dev": true }, "replicate": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/replicate/-/replicate-0.20.1.tgz", - "integrity": "sha512-QVyI1rowGsSfNuDrRmumYPdCHa/fN/RkI3NHpcK0i5hSSiWK69URAyheAC/0MIAiS3oUs4kD56PB9zEI4oHENw==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replicate/-/replicate-1.0.1.tgz", + "integrity": "sha512-EY+rK1YR5bKHcM9pd6WyaIbv6m2aRIvHfHDh51j/LahlHTLKemTYXF6ptif2sLa+YospupAsIoxw8Ndt5nI3vg==", + "requires": { + "readable-stream": ">=4.0.0" + } }, "resolve": { "version": "1.22.1", @@ -5800,6 +6027,12 @@ "queue-microtask": "^1.2.2" } }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "optional": true + }, "scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -5856,6 +6089,15 @@ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "optional": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, "string.prototype.matchall": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", diff --git a/package.json b/package.json index 36bee4a..e6ccb9e 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "react-dom": "18.2.0", "react-dropzone": "^14.2.2", "react-spinners": "^0.13.8", - "replicate": "^0.20.1" + "replicate": "^1.0.1" }, "devDependencies": { "autoprefixer": "^10.4.8", diff --git a/pages/api/predictions/index.js b/pages/api/predictions/index.js index 74c2fa3..7f8447e 100644 --- a/pages/api/predictions/index.js +++ b/pages/api/predictions/index.js @@ -23,23 +23,13 @@ export default async function handler(req, res) { ); let prediction - if (process.env.USE_REPLICATE_DEPLOYMENT) { - console.log("Using deployment") - prediction = await replicate.deployments.predictions.create( - "replicate", - "paint-by-text", - { - input: req.body - } - ); - } else { - console.log("Not using deployment") - const version = "d8ac233ef32825fd74937c891a2d56536d2736bcbd44b54d37ca5c2652a0a051" - prediction = await replicate.predictions.create({ - version, - input: req.body - }); - } + + const model = "black-forest-labs/flux-kontext-pro" + prediction = await replicate.predictions.create({ + model, + input: req.body + }); + console.log({prediction}); From 5c3c74bf7dbe053b116543cfe47efc6430d13c6a Mon Sep 17 00:00:00 2001 From: Zeke Sikelianos Date: Thu, 29 May 2025 12:51:07 -0700 Subject: [PATCH 3/4] preserve aspect ratio of uploaded images --- lib/prepare-image-file-for-upload.js | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/lib/prepare-image-file-for-upload.js b/lib/prepare-image-file-for-upload.js index c603e10..3510eea 100644 --- a/lib/prepare-image-file-for-upload.js +++ b/lib/prepare-image-file-for-upload.js @@ -10,17 +10,12 @@ export default function prepareImageFileForUpload(file) { let width = img.width; let height = img.height; - if (width > height) { - if (width > MAX_WIDTH) { - width = MAX_WIDTH; - height = height * (MAX_WIDTH / width); - } - } else { - if (height > MAX_HEIGHT) { - width = width * (MAX_HEIGHT / height); - height = MAX_HEIGHT; - } - } + // Calculate the scaling factor to fit within the max dimensions while preserving aspect ratio + const widthRatio = MAX_WIDTH / width; + const heightRatio = MAX_HEIGHT / height; + const scale = Math.min(widthRatio, heightRatio, 1); // Don't upscale + width = Math.round(width * scale); + height = Math.round(height * scale); const canvas = document.createElement("canvas"); canvas.width = width; From 483a1355891d5a59eafafa9fe669e4ae2c8b4149 Mon Sep 17 00:00:00 2001 From: Zeke Sikelianos Date: Thu, 29 May 2025 12:52:29 -0700 Subject: [PATCH 4/4] tweaks --- components/footer.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/footer.js b/components/footer.js index 5f3a8d2..87d3a55 100644 --- a/components/footer.js +++ b/components/footer.js @@ -59,12 +59,12 @@ export default function Footer({ events, startOver, handleImageDropped }) {

Powered by{" "} - + Black Forest Labs ,{" "} Replicate @@ -73,7 +73,7 @@ export default function Footer({ events, startOver, handleImageDropped }) { Vercel , and{" "} - + GitHub