diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a58b9e747..75dc62b13 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,6 +40,13 @@ jobs: - name: Check content invariants run: npm run check:content + # Images are served exactly as committed: a static export has no image + # optimization server, so next/image cannot resize anything. Without this + # gate a phone-resolution headshot goes straight to production — which is + # how /team came to weigh 8.5 MB. See docs/PERFORMANCE.md. + - name: Check image budgets + run: npm run check:images + - name: Build static export run: npm run build diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 810f07ba9..e6b41f56b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -122,6 +122,46 @@ Confirm that every contributed asset: - has meaningful alternative text where it is rendered; - follows any crop, aspect-ratio, and naming rules documented in the backlog. +### Images must be resized before they are committed + +**An image is served exactly as committed.** `output: "export"` requires +`images: { unoptimized: true }`, because GitHub Pages has no image optimization +server. `next/image` therefore does no resizing, emits no `srcset`, and does no +WebP negotiation — a 2644x2644 photograph dropped into `public/` is downloaded +at 2644x2644 by every visitor, even to fill a 48px circle. That is how `/team` +came to weigh 8.5 MB. + +After adding or replacing any image, run: + +```powershell +npm run optimize:images +``` + +It resizes anything over budget in place, keeps the same filename and format so +no reference changes, and leaves compliant files untouched — so it is safe to +run repeatedly and will not re-compress an already-optimized file. It also +strips EXIF, which removes any GPS coordinates a camera recorded. + +| Location | Max width | Rendered at | +|---|---|---| +| `public/team/` | 384px | 48px in listings, up to 128px on a profile | +| `public/logos/` | 256px | 56-80px tiles | +| `public/repos/` | 600px | small 2:3 poster cards | +| `public/summit/` | 1200px | gallery grid | +| `public/backgrounds/` | 1920px | full-page ground at ~9% opacity | +| `public/` (root) | 1920px | full-bleed hero photographs | + +`npm run check:images` reports violations without changing anything, and runs in +CI — a pull request adding an oversized image fails before merge. + +Two directories are deliberately exempt. `public/publications/` holds book pages +whose size is owned by `scripts/render-report-pages.mjs` through its own +`--scale` and `--quality` options, and `public/newsletter/` is copied verbatim +from the newsletter repository, so a heavy issue is fixed there. + +Because re-encoding is lossy, keep an unmodified copy of anything you may need +to re-crop later. Previously published originals remain in git history. + Some assets are wired up by filename rather than by code. An organisation logo dropped into `public/logos/` as `.` is discovered automatically and rendered without any change to `src/`. That convenience cuts diff --git a/UPDATES-NEEDED.md b/UPDATES-NEEDED.md index e231597a5..c36d2da52 100644 --- a/UPDATES-NEEDED.md +++ b/UPDATES-NEEDED.md @@ -328,6 +328,91 @@ CoLab does not hold. **Acceptance:** A clear, approved statement distinguishes code, original content, research publications, and third-party assets. +### UPD-013 - Resize the published images to their display size + +**Priority:** High — **done.** + +Source images were committed at their original camera resolution and served +unchanged, because `output: "export"` requires `images: { unoptimized: true }`, +which disables all resizing and format negotiation in `next/image`. The +committed file is the served file. + +Measured on 2026-08-25: + +- `/team` weighed **8.5 MB**, of which 7.6 MB was images; +- 26 of 27 team headshots exceeded 256px while rendering in a 48px circle — + `team/alex.jpg` was 2644x2644 / 1,096 KB, `team/grace.jpg` 3201x3218; +- the `/` and `/team` heroes **preloaded** a 548 KB photo that is then covered by + two darkening overlays; +- page backgrounds up to 741 KB rendered at roughly 9% opacity in grayscale. + +Full evidence and method: [docs/PERFORMANCE.md](docs/PERFORMANCE.md). + +**Done:** every committed image was resized to its display size in one pass from +the originals — **95 images, 16.01 MB -> 4.57 MB (-71%)**, taking `/team` from +8.5 MB to 2.1 MB. Formats and filenames were left unchanged, because renaming an +asset means editing every reference in `src/content` and a missed one fails +silently to initials rather than to a broken image. Full-resolution originals +remain in git history at `32e1ab9f`. + +**Acceptance:** met — no committed image materially exceeds its display size, +`/team` is under 2.2 MB, and a before/after comparison of all 95 images confirms +no lost transparency and no changed aspect ratio. + +### UPD-014 - Stop oversized images being committed again + +**Priority:** Medium — **done.** + +The documented intake for headshots was to drop a file in and convert it, with +no resize step, which is the root cause of UPD-013. Fixing the files without +fixing intake would mean the problem returns with the next cohort. + +**Done:** `scripts/optimize-images.mjs` resizes in place against a +per-directory budget (with per-file overrides), `npm run check:images` reports +violations without writing, and CI runs that check on every pull request. The +budget table and the command are recorded in `CONTRIBUTING.md` and +`public/team/README.md`. `sharp` is now an explicit `devDependency` rather than +being borrowed from `next`. + +**Acceptance:** met — adding a 2644px headshot fails the check with the exact +command that fixes it, verified by test. + +### UPD-015 - Reduce the burst when a book is opened + +**Priority:** Low + +The book viewer is already well behaved on page load: it is behind a click and +`page-flip` is dynamically imported, so the 58 MB of rendered pages never +affects normal browsing. But `mountFlipbook` calls `loadFromImages(pages)`, +which requests every page at once - 46 pages / 8.7 MB for *What Is Ethical AI*. + +**Proposed update:** Re-render pages at a lower `--scale`/`--quality` (both are +existing CLI options; sampled pages halve from 258 KB to 128 KB), and consider +loading only the current spread plus one either side. The second part is an +upstream change in `read-as-book`. + +**Acceptance:** Opening a book requests materially less than the whole book, and +the page turn stays smooth. + +### UPD-016 - Decide whether the dev-only routes should be public + +**Priority:** Low + +`/font-lab/` returns HTTP 200 on the live site. It was added as a throwaway aid +for choosing the wordmark face and loads three Google font families (18 `woff2`, +163 KB) that nothing else uses. The 18 `/print//` routes are build-time +scaffolding for the book renderer rather than pages meant to be visited. + +Neither slows down other pages, so this is a question of public surface and +build hygiene, not speed. + +**Proposed update:** Confirm with the author whether the font lab is still in +use. When it is finished, remove it or exclude it from the export, and decide +whether `/print/` should be excluded or marked non-indexable. + +**Acceptance:** Only intended pages are publicly reachable, and no unused font +family ships. + ## B. Draft requests to document the maintenance workflow - review before sending Addressed to [@carolina-moron](https://github.com/carolina-moron), who authors diff --git a/docs/PERFORMANCE.md b/docs/PERFORMANCE.md new file mode 100644 index 000000000..f59a8cb0b --- /dev/null +++ b/docs/PERFORMANCE.md @@ -0,0 +1,315 @@ +# Performance analysis — image and asset weight + +Measured on 2026-08-25 against `main` at `32e1ab9f` and the live site at +. + +> **Status: Tier 1 and Tier 2 are done.** Every committed image was resized to +> its display size, and a budget check now runs in CI. Full-resolution originals +> remain in git history at `32e1ab9f` and earlier. The measurements below are +> kept as they were taken, so the "before" figures still describe the problem +> the change fixed; §8 records what actually shipped. + +**Headline:** the site was heavy because **source images were published at their +original camera resolution and never resized**. `/team` shipped **8.5 MB**, of +which **7.6 MB was images**. It is now **2.1 MB**. + +## 1. Why nothing is being optimized automatically + +[`next.config.ts`](../next.config.ts) sets: + +```ts +output: "export", +images: { unoptimized: true }, // GitHub Pages has no image optimization server +``` + +That second line is correct for a static export, but it has a consequence worth +stating plainly: **`next/image` does no work here.** It emits a plain `` +pointing at the original file, with no resizing, no `srcset`, and no WebP/AVIF +negotiation. A 2644×2644 JPEG referenced through `next/image` is downloaded at +2644×2644. + +Since the build will not resize anything, **the committed file *is* the served +file**. Sizing has to happen before commit. + +## 2. How images are surfaced — three different paths + +| Path | Used by | Lazy? | Resized? | +|---|---|---|---| +| `next/image` + `unoptimized` | page heroes, header logo | No — heroes use `priority` | No | +| Raw `` | [TeamAvatar](../src/components/TeamAvatar.tsx), `/media`, `OrgShowcase`, `SlideDeck`, `IssueFrame` | Yes | No | +| CSS `background-image` | [SiteBackground](../src/components/SiteBackground.tsx), `RepoShowcase`, `PortfolioExplorer`, `DemoRunner` | **No — cannot be** | No | + +The lazy `` usage is good practice and is doing real work. The problem is +that all three paths ultimately serve **unscaled originals**. + +The CSS `background-image` path deserves attention because a background image +cannot opt into `loading="lazy"`. Every poster and page background referenced by +a rendered element is fetched eagerly. + +## 3. Measured page weight + +Per-route totals from the built `out/` directory (HTML + JS/CSS + referenced images): + +| Route | Total | Images | Code | +|---|---|---|---| +| `/team` | **8.5 MB** | 7,633 KB | 927 KB | +| `/demos` | 4.6 MB | 3,697 KB | 932 KB | +| `/media` | 3.5 MB | 2,615 KB | 918 KB | +| `/team/alex-du` | 2.6 MB | 1,730 KB | 918 KB | +| `/team/grace-driscoll` | 2.5 MB | 1,595 KB | 918 KB | +| `/` | 2.3 MB | 1,376 KB | 934 KB | +| `/portfolio` | 1.9 MB | 950 KB | 918 KB | + +JavaScript (~920 KB uncompressed on disk) is **not** the problem. It is gzipped +in transit — a sampled chunk went 23,985 → 7,485 bytes (-69%), putting the real +JS cost near 300 KB. Images are the problem. + +## 4. Findings, worst first + +### 4.1 Team headshots are camera originals — the single biggest issue + +Avatars render in a **48px** circle on `/team` (128px for the founder, 120px on +a profile page). A 256px file covers every one of those at 2× density. + +| File | Actual | Weight | Rendered at | +|---|---|---|---| +| `team/alex.jpg` | 2644×2644 | **1,096 KB** | 48px | +| `team/grace.jpg` | 3201×3218 (9.8 MP) | **960 KB** | 48px | +| `team/mohagani.jpg` | 2701×2908 | **924 KB** | 48px | +| `team/amanda.jpg` | 1424×1441 | 467 KB | 48px | +| `team/alexa.jpeg` | 1883×1887 | 455 KB | 48px | + +**26 of 27 avatars exceed 256px; all 27 together weigh 5.26 MB.** Re-encoded to +256px WebP they total **0.18 MB — a 97% reduction.** `alex.jpg` alone goes from +1,096 KB to about 5 KB. + +This is a direct consequence of the documented intake process: headshots are +dropped in and converted, with no resize step +([`public/team/README.md`](../public/team/README.md)). + +### 4.2 Hero photos are preloaded at full size + +`/`, `/team`, and `/portfolio` each render a hero through `next/image` with +`priority`. Next turns that into a preload hint, verified in the built HTML: + +```html + +``` + +That instructs the browser to fetch **548 KB** at high priority before almost +anything else — and the image is then covered by two darkening overlays. The +same applies to `nyu-w4.jpg` (534 KB) on `/portfolio`. + +`priority` is the right instinct for an LCP element; the file behind it is the +problem. At 1920px WebP, `nyu-subway.jpg` is 113 KB (-79%). + +### 4.3 Page backgrounds are full-resolution photos shown at 9% opacity + +[`SiteBackground`](../src/components/SiteBackground.tsx) sets a per-section +photo as a CSS background. Per the design system it renders **grayscale ~0.74 at +~0.09 opacity** — a texture, not a picture. + +`washington-square-arch-isabella.jpg` is **741 KB** (2000×1500) for that effect, +and loads eagerly on the landing page. All eight backgrounds total 2.99 MB and +compress to 1.09 MB at 1920px (-64%) — and could go far below that, because +almost no detail survives a 9%-opacity grayscale wash. + +### 4.4 Project posters load eagerly as CSS backgrounds + +27 posters totalling 3.37 MB, mostly 1440×900, displayed in small 2:3 cards. +Because they are CSS backgrounds they cannot be lazy-loaded, which is why +`/demos` reaches 4.6 MB. At a 600px budget they total 0.57 MB (-83%). + +### 4.5 Opening a book downloads every page at once + +The book viewer is well built for load: it is behind a click, and `page-flip` is +dynamically imported, so **none of the 58 MB of book pages affects normal page +load**. That is worth preserving. + +However, `mountFlipbook` calls `flip.loadFromImages(pages)`, which creates an +`` for **every** page immediately. Opening *What Is Ethical AI* pulls **46 +pages / 8.7 MB** in one burst; *After the Corridor* pulls 4.6 MB. + +Pages are rendered at `--scale 2` / `--quality 82` (≈150 dpi, 1190×1684). The +viewer caps page height at 1100 CSS px, so the extra resolution is only useful +on high-density screens. Re-rendering at a lower scale/quality **halves** the +payload: sampled pages went 258 KB → 128 KB. + +### 4.6 A throwaway font lab is live in production + +`/font-lab/` returns **HTTP 200** on the public site. It was added as "a +throwaway font lab for choosing the wordmark face" and pulls **three extra +Google font families** (Sixtyfour, Pixelify Sans, Silkscreen) — 18 `woff2` +files, 163 KB — that nothing else on the site uses. + +It does not slow down other pages, but it is publicly reachable, publicly +indexable, and ships fonts the site does not use. The 18 `/print//` routes +(build-time scaffolding for the book renderer) are likewise public. + +### 4.7 GitHub Pages caching amplifies every byte + +Verified against production: + +``` +Cache-Control: max-age=600 # on *everything* +``` + +Ten minutes — even for content-hashed `_next/static/*` assets that are safe to +cache forever. GitHub Pages does not allow custom headers, so **this cannot be +fixed**; it can only be designed around. Practically, a returning visitor +re-downloads the whole page, so the 8.5 MB `/team` page is paid repeatedly +rather than once. + +This makes payload reduction unusually valuable here compared with a host where +`immutable` caching would absorb the cost. + +Good news: HTML, JS, and CSS **are** gzipped in transit (verified). Images are +not compressible further, which is precisely why their dimensions matter so +much. + +## 5. Projected result + +Re-encoding to display-appropriate sizes, per asset class: + +| Class | Files | Now | Optimized | Saved | +|---|---|---|---|---| +| Team avatars | 27 | 5.26 MB | 0.18 MB | **97%** | +| Project posters | 27 | 3.37 MB | 0.57 MB | **83%** | +| Page backgrounds | 8 | 2.99 MB | 1.09 MB | 64% | +| Summit photos | 13 | 2.07 MB | 0.89 MB | 57% | +| Root hero photos | 5 | 1.29 MB | 0.39 MB | 70% | +| Partner logos | 15 | 1.04 MB | 0.09 MB | 91% | +| **Total** | **95** | **16.01 MB** | **3.21 MB** | **80%** | + +Per route: + +| Route | Now | After | Cut | +|---|---|---|---| +| `/team` | 8.5 MB | **1.5 MB** | -94% images | +| `/demos` | 4.6 MB | 1.7 MB | -81% images | +| `/media` | 3.5 MB | 2.0 MB | -61% images | +| `/team/` | 2.6 MB | 1.0 MB | -95% images | +| `/` | 2.3 MB | 1.3 MB | -81% images | +| `/portfolio` | 1.9 MB | 1.4 MB | -58% images | + +These are measured re-encodes, not estimates. + +## 6. Recommendations, in priority order + +### Tier 1 — large win, no visible change, low risk + +1. **Resize the team headshots to 256px.** Recovers 5.1 MB and takes `/team` + from 8.5 MB to about 1.5 MB. Nothing on screen changes: the images are + currently downscaled by the browser anyway. +2. **Resize the hero and background photos to ≤1920px** and re-encode. The + backgrounds can go further still, since they render at 9% opacity in + grayscale — quality there is nearly free to trade away. +3. **Resize the project posters to ~600px wide.** They display in small 2:3 + cards, and they load eagerly because they are CSS backgrounds. +4. **Keep the originals somewhere outside the deployed tree** before any + re-encode. Re-encoding is lossy and irreversible; the backlog already records + an earlier lossy poster re-crop as a regret. + +### Tier 2 — process, so this does not recur + +5. **Add an image budget to the contribution guide and the team-photo README** + — e.g. avatars ≤256px, posters ≤600px, heroes/backgrounds ≤1920px, and a + per-file ceiling of ~250 KB. The current intake process explicitly has no + resize step, which is the root cause. +6. **Add a `scripts/optimize-images.mjs`** using `sharp` so contributors run one + command rather than resizing by hand. Note that `sharp` is currently only + available transitively through `next`; it should be an explicit + `devDependency` if a script depends on it. +7. **Add a CI check that fails on oversized committed images.** This is the only + durable fix — it catches the next 3201×3218 headshot automatically. It fits + naturally into the pull-request CI proposed in + [SITE-CONTROL-RECOMMENDATIONS.md](../SITE-CONTROL-RECOMMENDATIONS.md). + +### Tier 3 — worthwhile, slightly more involved + +8. **Serve responsive `srcset` for the few large heroes.** Because + `unoptimized` disables Next's generation, this means emitting 2–3 widths per + hero at build time and writing `srcset`/`sizes` by hand. Worth it only for + the handful of genuinely large images. +9. **Re-render book pages at a lower scale/quality** (`--scale`/`--quality` are + already CLI options). Halves a 4–9 MB burst when a reader opens a book. +10. **Lazy-load book pages** in `read-as-book` rather than + `loadFromImages(all)` — load the current spread plus one either side. This + is an upstream change in that package and the highest-effort item here. +11. **Remove `/font-lab` from the production build** once the wordmark is + chosen, or exclude it from export. Same question for the 18 `/print/` + routes, which exist to feed the book renderer rather than to be visited. + +### Explicitly not recommended + +- **Do not turn off `unoptimized`.** It is required for `output: "export"`; the + build would fail or demand a runtime image server. +- **Do not chase the JS bundle.** At ~920 KB uncompressed and gzipped in + transit, it is not what changed and not what is slow. +- **Do not try to fix caching with headers.** GitHub Pages does not permit it. + The only lever is sending fewer bytes. + +## 7. How these numbers were produced + +- Dimensions parsed from JPEG/PNG/WebP headers for all 491 raster files. +- Per-route weight computed by scanning each built `out/**/index.html` for + ``, `srcset`, and CSS `url(...)` references and summing the real files. +- Savings measured by actually re-encoding with `sharp` at the display budget + for each asset class — not estimated from a compression ratio. +- Production behaviour (status, `Content-Length`, `Cache-Control`, + `Content-Encoding`) confirmed with live requests against GitHub Pages. + +## 8. What shipped + +Tier 1 and Tier 2 were implemented together. + +**Result: 95 images, 16.01 MB -> 4.57 MB (-71%).** Measured page weight, which +includes ~920 KB of JavaScript per route that gzips to roughly 300 KB in +transit: + +| Route | Before | After | Change | +|---|---|---|---| +| `/team` | 8.5 MB | **2.1 MB** | -76% | +| `/demos` | 4.6 MB | 2.4 MB | -48% | +| `/media` | 3.5 MB | 2.4 MB | -32% | +| `/` | 2.3 MB | 1.3 MB | -46% | +| `/portfolio` | 1.9 MB | 1.4 MB | -29% | +| `/team/` | 2.6 MB | 1.1 MB | -60% | + +The saving is smaller than the 80% projected in §5 because that projection +assumed conversion to WebP. Formats and filenames were deliberately left +unchanged: renaming `team/alex.jpg` would mean editing every reference in +`src/content`, and a missed one fails *silently* — `TeamAvatar` falls back to +initials rather than showing a broken image. Nearly all of the win comes from +the resize, so the risk was not worth the remaining few percent. + +### How it is enforced + +[`scripts/optimize-images.mjs`](../scripts/optimize-images.mjs) resizes in place +against a per-directory budget, and `npm run check:images` runs in CI. Adding a +2644px headshot now fails the pull request with the command that fixes it. + +The script only touches files that break their budget, so it is idempotent and +never re-compresses a compliant file a second generation. It also applies EXIF +orientation before stripping metadata, which removes any GPS coordinates a +camera recorded. + +### Two things worth knowing + +**A bug this change nearly introduced.** sharp's `png({ effort })` and +`png({ palette })` options put libvips into a palette-quantizing path that +*silently drops the alpha channel* on some images. It dropped it on four logos. +`coinbase.png` is mostly transparent, so it would have arrived on the white logo +tile as a solid dark rectangle — while still passing the build, the byte budget, +and every automated check. The encoder now uses `compressionLevel` only, and +verifies after encoding that an image which had transparency still has it. + +**Budgets are per-directory, with per-file overrides.** The root of `public/` +mixes hero photographs that need 1600px with small marks that do not: +`etc-logo.png` renders at 32px in the header on every page and was 238px / 87 KB. +`logos/microsoft-garage.png` is exempt outright — it is 696px but only 12 KB, and +every re-encode comes out larger. + +Items 8-11 in §6 (responsive `srcset`, lower book render scale, lazy book pages, +removing `/font-lab`) were **not** done. They are tracked as UPD-015 and UPD-016 +in [UPDATES-NEEDED.md](../UPDATES-NEEDED.md). diff --git a/package-lock.json b/package-lock.json index b946cdc02..9306154b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,8 +24,12 @@ "eslint": "^9", "eslint-config-next": "16.2.9", "pdfjs-dist": "^6.1.200", + "sharp": "0.35.3", "tailwindcss": "^4", "typescript": "^5" + }, + "engines": { + "node": ">=24" } }, "node_modules/@alloc/quick-lru": { @@ -528,16 +532,16 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "devOptional": true, "license": "MIT", - "optional": true, "engines": { "node": ">=18" } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "version": "0.35.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha1-iydAiEvFixJ/wwIEeaASlPoQlcs=", "cpu": [ "arm64" ], @@ -547,19 +551,19 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "@img/sharp-libvips-darwin-arm64": "1.3.2" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "version": "0.35.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha1-kt+RMg+vV8xUszEYV3DVqT1RAEk=", "cpu": [ "x64" ], @@ -569,19 +573,38 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "@img/sharp-libvips-darwin-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha1-SAGME3mo9QfWgdbNjb5D2XldyAk=", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "version": "1.3.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha1-IntB/8bJlhK86rpW+ZShkz13lXM=", "cpu": [ "arm64" ], @@ -595,9 +618,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "version": "1.3.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha1-aUkD7EEMAJRc5bDCbayD1xhMi4Y=", "cpu": [ "x64" ], @@ -611,9 +634,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "version": "1.3.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha1-5g4IjIBr3hrCi+ZItgw0ZfQcGKc=", "cpu": [ "arm" ], @@ -630,9 +653,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "version": "1.3.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha1-Sd3xVnKGZqId7tBxbzu3K7NRF54=", "cpu": [ "arm64" ], @@ -649,9 +672,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "version": "1.3.2", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha1-vjFhqv1llBez4mN037vNLaK/tiw=", "cpu": [ "ppc64" ], @@ -668,9 +691,9 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "version": "1.3.2", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha1-vwCKZ3nZvitWpN9nt1OUH3Z8lX0=", "cpu": [ "riscv64" ], @@ -687,9 +710,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "version": "1.3.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha1-lYOBS421iInIW62eXgt4JSV/85Q=", "cpu": [ "s390x" ], @@ -706,9 +729,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "version": "1.3.2", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha1-q8mjEUSVtwW6ILfBVoue7NYq67s=", "cpu": [ "x64" ], @@ -725,9 +748,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "version": "1.3.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha1-5Y+xSnh58V2ecKmChw84TzmoMqI=", "cpu": [ "arm64" ], @@ -744,9 +767,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "version": "1.3.2", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha1-VhFID8t0Zd1TFgRNtTrXqEPtSvg=", "cpu": [ "x64" ], @@ -763,9 +786,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "version": "0.35.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha1-yNpQDEAWiTqJ1G6YMtCKBu73fyc=", "cpu": [ "arm" ], @@ -778,19 +801,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "@img/sharp-libvips-linux-arm": "1.3.2" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "version": "0.35.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha1-RLZYI+zJd9RYWzCAcP/zlHJW3gQ=", "cpu": [ "arm64" ], @@ -803,19 +826,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "@img/sharp-libvips-linux-arm64": "1.3.2" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "version": "0.35.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha1-6h19uBj1KvHh4FfoLVXyOy3jhkw=", "cpu": [ "ppc64" ], @@ -828,19 +851,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "@img/sharp-libvips-linux-ppc64": "1.3.2" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "version": "0.35.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha1-civXmUZYeRsC0QN+oJyly3gDDY0=", "cpu": [ "riscv64" ], @@ -853,19 +876,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "@img/sharp-libvips-linux-riscv64": "1.3.2" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "version": "0.35.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha1-6ClIF9faSVVBpKhw9W5rXQ+GQ80=", "cpu": [ "s390x" ], @@ -878,19 +901,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "@img/sharp-libvips-linux-s390x": "1.3.2" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "version": "0.35.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha1-mEPDLzmurEtg3WD540FlIKTk3kY=", "cpu": [ "x64" ], @@ -903,19 +926,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "@img/sharp-libvips-linux-x64": "1.3.2" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "version": "0.35.3", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha1-nP7k7MPUGrmidOAZ3+e0BihAUQw=", "cpu": [ "arm64" ], @@ -928,19 +951,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "version": "0.35.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha1-IE0GeMjDsVMA2aJuy5wNzaEcpd4=", "cpu": [ "x64" ], @@ -953,38 +976,54 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "1.3.2" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "version": "0.35.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha1-QviXm9vhpUGi6bmdO1vgfmtxx8A=", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha1-gjqqk9FNuEmZ1bXcln/1bPGWbZ8=", "cpu": [ "wasm32" ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.7.0" + "@img/sharp-wasm32": "0.35.3" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "version": "0.35.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha1-81VNRcviRTKgrepzbsV2WPdKKRE=", "cpu": [ "arm64" ], @@ -994,16 +1033,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "version": "0.35.3", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha1-CUKyZDvLV+SEGf5BGd6EB4rgrHQ=", "cpu": [ "ia32" ], @@ -1013,16 +1052,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "version": "0.35.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha1-iiXKzcdajCYHfAf7pR6AVqrkdPE=", "cpu": [ "x64" ], @@ -1032,7 +1071,7 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" @@ -5601,115 +5640,629 @@ "react-dom": ">=18.2.0 || ^19.0.0" } }, - "node_modules/next/node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } + "node_modules/next/node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha1-bgcy3K3hJrZnCveqFwYLkmg16oY=", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/node-exports-info": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.2.tgz", - "integrity": "sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==", - "dev": true, - "license": "MIT", - "dependencies": { - "array.prototype.flatmap": "^1.3.3", - "es-errors": "^1.3.0", - "object.entries": "^1.1.9", - "semver": "^6.3.1" - }, "engines": { - "node": ">= 0.4" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" } }, - "node_modules/node-releases": { - "version": "2.0.50", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.50.tgz", - "integrity": "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==", - "dev": true, - "license": "MIT", + "node_modules/next/node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha1-Gbwd1uum1alig0mLnJ9AEYDunHs=", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=18" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "node_modules/next/node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha1-KJTAy4fUInbDiJlC6OLbUXpJLEM=", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, + "node_modules/next/node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha1-5jaB9FOalK+c0XJG7YiBc0OG+Mw=", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/libvips" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" + "node_modules/next/node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha1-uSYN0evm+eO9vL3KydKsEl81hS0=", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha1-sbKIs2hks7zlRa2R+m2tzxpK0xg=", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha1-S4Ps8qgpBXIis4hIx7Ai57TQeqc=", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha1-iAtGeACeWiCArxkjMrALCq+KSN4=", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha1-dPNDyOEPrYIbOPdc7TBIiTncWew=", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha1-30GD6L2EEPfWG2aFmjXt6rClMc4=", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha1-yNa0ghHfZxN1QQB+6NG3sfjKjgY=", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha1-vhHHW+5bCAy+4xoVOod5RI+Rn3U=", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha1-X7DDaV3RJSLTnD/3pryBZGF4Cg0=", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha1-eqd2TvnAAfFeYQVG1C/OVpEXkMw=", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha1-nCE6gVIKIMr2aXjz1MB0Vv8uCBM=", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha1-zdKBgndOrb4E9iZ1oWqrvMuDP2A=", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha1-k+rGAbnzKbsnkX4OGQmMci1jDfc=", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha1-VavHzXVP/KUAK2wrcZq9/IRoGag=", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/next/node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha1-1lFe6XG7YvcwAaSCm52GWhG3cIY=", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/next/node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha1-2Xl4rsfFIS+ZlxTy9bc2RX4S7p8=", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/next/node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://ms-feed-17.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha1-LxWAOqYm+MWd18nQu8dm8atSz6A=", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha1-Nwbp46w1/d/ByH+U6Enxt1MHzgo=", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://ms-feed-2.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha1-C3EWZZmwSeAy8IX7kmPgL05HiN4=", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha1-qB/7AOaSZ80KHWJurtuKhDCysvg=", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/next/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/semver/-/semver-7.8.5.tgz", + "integrity": "sha1-ObZGA33VDBT7RR5+TKxY7YuGP2k=", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/next/node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha1-tvFI5LjGHxeXveEanRz+u64sV7A=", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/node-exports-info": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.2.tgz", + "integrity": "sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/node-releases": { + "version": "2.0.50", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.50.tgz", + "integrity": "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object.entries": { @@ -6325,56 +6878,61 @@ } }, "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, + "version": "0.35.3", + "resolved": "https://ms-feed-12.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha1-Qe0hpGQGvhY+rNC+ezZLQvzyiF8=", + "devOptional": true, "license": "Apache-2.0", - "optional": true, "dependencies": { - "@img/colour": "^1.0.0", + "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", - "semver": "^7.7.3" + "semver": "^7.8.5" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" + "@img/sharp-darwin-arm64": "0.35.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/sharp/node_modules/semver": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "devOptional": true, "license": "ISC", - "optional": true, "bin": { "semver": "bin/semver.js" }, diff --git a/package.json b/package.json index 11ddab259..c1ba697be 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "start": "next start", "lint": "eslint", "check:content": "node --disable-warning=MODULE_TYPELESS_PACKAGE_JSON scripts/check-content-invariants.mjs", + "check:images": "node --disable-warning=MODULE_TYPELESS_PACKAGE_JSON scripts/optimize-images.mjs --check", + "optimize:images": "node --disable-warning=MODULE_TYPELESS_PACKAGE_JSON scripts/optimize-images.mjs", "sync:static": "bash scripts/sync-static-site.sh", "sync:newsletter": "node scripts/sync-newsletter.mjs", "export:haste-paper": "node scripts/export-haste-paper.mjs", @@ -34,6 +36,7 @@ "eslint": "^9", "eslint-config-next": "16.2.9", "pdfjs-dist": "^6.1.200", + "sharp": "0.35.3", "tailwindcss": "^4", "typescript": "^5" } diff --git a/public/backgrounds/nyu-subway-people.jpg b/public/backgrounds/nyu-subway-people.jpg index 886e5c780..f0a06022b 100644 Binary files a/public/backgrounds/nyu-subway-people.jpg and b/public/backgrounds/nyu-subway-people.jpg differ diff --git a/public/backgrounds/nyu-subway.jpg b/public/backgrounds/nyu-subway.jpg index 67fb9744f..feefa3d7b 100644 Binary files a/public/backgrounds/nyu-subway.jpg and b/public/backgrounds/nyu-subway.jpg differ diff --git a/public/backgrounds/washington-square-aerial.jpg b/public/backgrounds/washington-square-aerial.jpg index 2af6a362d..0f8077085 100644 Binary files a/public/backgrounds/washington-square-aerial.jpg and b/public/backgrounds/washington-square-aerial.jpg differ diff --git a/public/backgrounds/washington-square-arch-isabella.jpg b/public/backgrounds/washington-square-arch-isabella.jpg index bc6361b7d..7cd719552 100644 Binary files a/public/backgrounds/washington-square-arch-isabella.jpg and b/public/backgrounds/washington-square-arch-isabella.jpg differ diff --git a/public/backgrounds/washington-square-arch-tall.jpg b/public/backgrounds/washington-square-arch-tall.jpg index e1ce336ac..368402b67 100644 Binary files a/public/backgrounds/washington-square-arch-tall.jpg and b/public/backgrounds/washington-square-arch-tall.jpg differ diff --git a/public/backgrounds/washington-square-arch.jpg b/public/backgrounds/washington-square-arch.jpg index f40f1f55e..22f9e7d42 100644 Binary files a/public/backgrounds/washington-square-arch.jpg and b/public/backgrounds/washington-square-arch.jpg differ diff --git a/public/backgrounds/washington-square-classic.jpg b/public/backgrounds/washington-square-classic.jpg index 202d13f4f..999e8a4fe 100644 Binary files a/public/backgrounds/washington-square-classic.jpg and b/public/backgrounds/washington-square-classic.jpg differ diff --git a/public/etc-logo-3d.jpg b/public/etc-logo-3d.jpg index 7c491571e..dcf8043b6 100644 Binary files a/public/etc-logo-3d.jpg and b/public/etc-logo-3d.jpg differ diff --git a/public/etc-logo.png b/public/etc-logo.png index 580b796a7..ec511ab3f 100644 Binary files a/public/etc-logo.png and b/public/etc-logo.png differ diff --git a/public/logos/100x.jpg b/public/logos/100x.jpg index 66fb4eb0a..1473e1e06 100644 Binary files a/public/logos/100x.jpg and b/public/logos/100x.jpg differ diff --git a/public/logos/apne-aap-women-worldwide.png b/public/logos/apne-aap-women-worldwide.png index b41c10bed..e42ad1ae3 100644 Binary files a/public/logos/apne-aap-women-worldwide.png and b/public/logos/apne-aap-women-worldwide.png differ diff --git a/public/logos/art-antiquities-blockchain-consortium.png b/public/logos/art-antiquities-blockchain-consortium.png index 5bd09cd81..a9609daee 100644 Binary files a/public/logos/art-antiquities-blockchain-consortium.png and b/public/logos/art-antiquities-blockchain-consortium.png differ diff --git a/public/logos/b3iq.png b/public/logos/b3iq.png index 2239c7026..749b3d2ce 100644 Binary files a/public/logos/b3iq.png and b/public/logos/b3iq.png differ diff --git a/public/logos/blockchain-for-social-impact.png b/public/logos/blockchain-for-social-impact.png index 6000c676e..454218ee4 100644 Binary files a/public/logos/blockchain-for-social-impact.png and b/public/logos/blockchain-for-social-impact.png differ diff --git a/public/logos/coinbase.png b/public/logos/coinbase.png index f91c871bc..1a1c42646 100644 Binary files a/public/logos/coinbase.png and b/public/logos/coinbase.png differ diff --git a/public/logos/gaia.png b/public/logos/gaia.png index 12c9bac13..89d820e4c 100644 Binary files a/public/logos/gaia.png and b/public/logos/gaia.png differ diff --git a/public/logos/nyu-sps.jpg b/public/logos/nyu-sps.jpg index 9bc773507..5ae46f77d 100644 Binary files a/public/logos/nyu-sps.jpg and b/public/logos/nyu-sps.jpg differ diff --git a/public/logos/sayari.jpg b/public/logos/sayari.jpg index 28de321bf..52e8b57f0 100644 Binary files a/public/logos/sayari.jpg and b/public/logos/sayari.jpg differ diff --git a/public/logos/x402-foundation.jpg b/public/logos/x402-foundation.jpg index 3d73d2937..5af7e145d 100644 Binary files a/public/logos/x402-foundation.jpg and b/public/logos/x402-foundation.jpg differ diff --git a/public/nyu-sps-cga-logo.jpg b/public/nyu-sps-cga-logo.jpg index a6e6113c2..a78f8e507 100644 Binary files a/public/nyu-sps-cga-logo.jpg and b/public/nyu-sps-cga-logo.jpg differ diff --git a/public/nyu-subway.jpg b/public/nyu-subway.jpg index 886e5c780..8a93fc44f 100644 Binary files a/public/nyu-subway.jpg and b/public/nyu-subway.jpg differ diff --git a/public/nyu-w4.jpg b/public/nyu-w4.jpg index d820617f4..c10f92b18 100644 Binary files a/public/nyu-w4.jpg and b/public/nyu-w4.jpg differ diff --git a/public/repos/AI-Models-Research.jpg b/public/repos/AI-Models-Research.jpg index a7841e8f4..6e3a3f102 100644 Binary files a/public/repos/AI-Models-Research.jpg and b/public/repos/AI-Models-Research.jpg differ diff --git a/public/repos/CERAI_AR.jpg b/public/repos/CERAI_AR.jpg index e79e7afdb..759a95dd0 100644 Binary files a/public/repos/CERAI_AR.jpg and b/public/repos/CERAI_AR.jpg differ diff --git a/public/repos/Evac-Sim-Melanie.jpg b/public/repos/Evac-Sim-Melanie.jpg index 388f583ea..d48a8e927 100644 Binary files a/public/repos/Evac-Sim-Melanie.jpg and b/public/repos/Evac-Sim-Melanie.jpg differ diff --git a/public/repos/Exodus.jpg b/public/repos/Exodus.jpg index ff4d303bf..d10bbc389 100644 Binary files a/public/repos/Exodus.jpg and b/public/repos/Exodus.jpg differ diff --git a/public/repos/India-EvacSimulation.jpg b/public/repos/India-EvacSimulation.jpg index 12d05790c..0cfdb7b45 100644 Binary files a/public/repos/India-EvacSimulation.jpg and b/public/repos/India-EvacSimulation.jpg differ diff --git a/public/repos/MVDC.jpg b/public/repos/MVDC.jpg index 49ec8b29b..587763f4c 100644 Binary files a/public/repos/MVDC.jpg and b/public/repos/MVDC.jpg differ diff --git a/public/repos/VANGO.jpg b/public/repos/VANGO.jpg index 441a47505..5c970b472 100644 Binary files a/public/repos/VANGO.jpg and b/public/repos/VANGO.jpg differ diff --git a/public/repos/War-Games.jpg b/public/repos/War-Games.jpg index 18400a484..c106073fd 100644 Binary files a/public/repos/War-Games.jpg and b/public/repos/War-Games.jpg differ diff --git a/public/repos/arts-provenance-agent.jpg b/public/repos/arts-provenance-agent.jpg index e2ab0f01d..5cfc49365 100644 Binary files a/public/repos/arts-provenance-agent.jpg and b/public/repos/arts-provenance-agent.jpg differ diff --git a/public/repos/avatar-storytelling.jpg b/public/repos/avatar-storytelling.jpg index 6469d2c2a..3a745d64b 100644 Binary files a/public/repos/avatar-storytelling.jpg and b/public/repos/avatar-storytelling.jpg differ diff --git a/public/repos/diplomatic-simulator.jpg b/public/repos/diplomatic-simulator.jpg index 2de3934d4..cde7dd48c 100644 Binary files a/public/repos/diplomatic-simulator.jpg and b/public/repos/diplomatic-simulator.jpg differ diff --git a/public/repos/ercf.jpg b/public/repos/ercf.jpg index c4cac47b5..fb3300acf 100644 Binary files a/public/repos/ercf.jpg and b/public/repos/ercf.jpg differ diff --git a/public/repos/evacuation-inform-index-carolina.jpg b/public/repos/evacuation-inform-index-carolina.jpg index 9a9ee7730..18094b166 100644 Binary files a/public/repos/evacuation-inform-index-carolina.jpg and b/public/repos/evacuation-inform-index-carolina.jpg differ diff --git a/public/repos/forced-labor-structural-risk-index.jpg b/public/repos/forced-labor-structural-risk-index.jpg index 701954192..f59a23028 100644 Binary files a/public/repos/forced-labor-structural-risk-index.jpg and b/public/repos/forced-labor-structural-risk-index.jpg differ diff --git a/public/repos/grant-valuation-tool.jpg b/public/repos/grant-valuation-tool.jpg index d8193e340..3ef63e1f4 100644 Binary files a/public/repos/grant-valuation-tool.jpg and b/public/repos/grant-valuation-tool.jpg differ diff --git a/public/repos/mariupol-evacuation-model.jpg b/public/repos/mariupol-evacuation-model.jpg index 37ee7c6c9..b1dbc00bd 100644 Binary files a/public/repos/mariupol-evacuation-model.jpg and b/public/repos/mariupol-evacuation-model.jpg differ diff --git a/public/summit/ai-researcher.jpg b/public/summit/ai-researcher.jpg index 443b11da6..2457e4e58 100644 Binary files a/public/summit/ai-researcher.jpg and b/public/summit/ai-researcher.jpg differ diff --git a/public/summit/avatar-storytelling-dashboard.jpg b/public/summit/avatar-storytelling-dashboard.jpg index f3b446b96..25ad5d305 100644 Binary files a/public/summit/avatar-storytelling-dashboard.jpg and b/public/summit/avatar-storytelling-dashboard.jpg differ diff --git a/public/summit/convening.jpg b/public/summit/convening.jpg index 18ce1a826..f1887052b 100644 Binary files a/public/summit/convening.jpg and b/public/summit/convening.jpg differ diff --git a/public/summit/hackathon.jpg b/public/summit/hackathon.jpg index 3c7162065..162a8b8ea 100644 Binary files a/public/summit/hackathon.jpg and b/public/summit/hackathon.jpg differ diff --git a/public/summit/moment-1.jpg b/public/summit/moment-1.jpg index 36717dbc1..d62cba1f5 100644 Binary files a/public/summit/moment-1.jpg and b/public/summit/moment-1.jpg differ diff --git a/public/summit/panel.jpg b/public/summit/panel.jpg index bf045892d..2313b2618 100644 Binary files a/public/summit/panel.jpg and b/public/summit/panel.jpg differ diff --git a/public/summit/student-demo.jpg b/public/summit/student-demo.jpg index 68894ca53..719e0f2c3 100644 Binary files a/public/summit/student-demo.jpg and b/public/summit/student-demo.jpg differ diff --git a/public/summit/summit-floor.jpg b/public/summit/summit-floor.jpg index 78ca9c66b..218772c46 100644 Binary files a/public/summit/summit-floor.jpg and b/public/summit/summit-floor.jpg differ diff --git a/public/summit/working-session.jpg b/public/summit/working-session.jpg index 971c18c98..6ec99b36c 100644 Binary files a/public/summit/working-session.jpg and b/public/summit/working-session.jpg differ diff --git a/public/team/README.md b/public/team/README.md index 8914457ee..bd4b130d6 100644 --- a/public/team/README.md +++ b/public/team/README.md @@ -3,6 +3,15 @@ Drop headshots here, then uncomment the matching `photo:` line in `src/content/site.ts`. Square images crop best (they're shown in a circle). +**Then run `npm run optimize:images`.** A headshot straight from a phone or a +camera is several thousand pixels wide, and this site serves it at exactly that +size — the static export has no image optimization server, so `next/image` +cannot resize anything. One 2644x2644 file was being downloaded to fill a 48px +circle. The command resizes to the 384px budget in place, keeps the filename, and +strips EXIF (including any GPS coordinates a camera recorded). CI rejects an +oversized image, so this is not optional. See +[CONTRIBUTING.md](../../CONTRIBUTING.md) for the full budget table. + Status (founder + Summer 2026 researchers): - `yorke.jpg` — Yorke E Rhodes III (founder) ✅ diff --git a/public/team/adeline.jpg b/public/team/adeline.jpg index f13891092..884b5ff88 100644 Binary files a/public/team/adeline.jpg and b/public/team/adeline.jpg differ diff --git a/public/team/alana.jpg b/public/team/alana.jpg index 311e7457a..75b48f754 100644 Binary files a/public/team/alana.jpg and b/public/team/alana.jpg differ diff --git a/public/team/alex.jpg b/public/team/alex.jpg index a4eed109c..0c20e9db8 100644 Binary files a/public/team/alex.jpg and b/public/team/alex.jpg differ diff --git a/public/team/alexa.jpeg b/public/team/alexa.jpeg index b11120c24..0e14a0061 100644 Binary files a/public/team/alexa.jpeg and b/public/team/alexa.jpeg differ diff --git a/public/team/amanda.jpg b/public/team/amanda.jpg index 095cf385b..110040ab9 100644 Binary files a/public/team/amanda.jpg and b/public/team/amanda.jpg differ diff --git a/public/team/carlos.jpg b/public/team/carlos.jpg index 32e342ca0..cbff57ccf 100644 Binary files a/public/team/carlos.jpg and b/public/team/carlos.jpg differ diff --git a/public/team/carolina.jpg b/public/team/carolina.jpg index efe7669c4..949a7ca78 100644 Binary files a/public/team/carolina.jpg and b/public/team/carolina.jpg differ diff --git a/public/team/christine.jpg b/public/team/christine.jpg index f19740ec6..ddafb2242 100644 Binary files a/public/team/christine.jpg and b/public/team/christine.jpg differ diff --git a/public/team/grace.jpg b/public/team/grace.jpg index df0da1c54..c0cc3768e 100644 Binary files a/public/team/grace.jpg and b/public/team/grace.jpg differ diff --git a/public/team/hannah.jpg b/public/team/hannah.jpg index d989b7b2e..3aa47444a 100644 Binary files a/public/team/hannah.jpg and b/public/team/hannah.jpg differ diff --git a/public/team/india.jpg b/public/team/india.jpg index f5263bf88..28209d5a2 100644 Binary files a/public/team/india.jpg and b/public/team/india.jpg differ diff --git a/public/team/kirsten.jpeg b/public/team/kirsten.jpeg index 07edbe4eb..ac8787f20 100644 Binary files a/public/team/kirsten.jpeg and b/public/team/kirsten.jpeg differ diff --git a/public/team/melanie.jpg b/public/team/melanie.jpg index 3a9a4978d..09fd2f2df 100644 Binary files a/public/team/melanie.jpg and b/public/team/melanie.jpg differ diff --git a/public/team/mohagani.jpg b/public/team/mohagani.jpg index 2f6bd7123..9394c35ca 100644 Binary files a/public/team/mohagani.jpg and b/public/team/mohagani.jpg differ diff --git a/public/team/susan.jpg b/public/team/susan.jpg index 642a10fe1..333327d81 100644 Binary files a/public/team/susan.jpg and b/public/team/susan.jpg differ diff --git a/public/team/sylvia.jpg b/public/team/sylvia.jpg index b63330543..cb69603c1 100644 Binary files a/public/team/sylvia.jpg and b/public/team/sylvia.jpg differ diff --git a/public/team/taylor.jpg b/public/team/taylor.jpg index 7b435eb70..8fedc682b 100644 Binary files a/public/team/taylor.jpg and b/public/team/taylor.jpg differ diff --git a/public/team/teresa.jpg b/public/team/teresa.jpg index 0c0fc56fd..cdc8dbc68 100644 Binary files a/public/team/teresa.jpg and b/public/team/teresa.jpg differ diff --git a/public/team/yago.jpg b/public/team/yago.jpg index bc1675b44..e2dcf6f06 100644 Binary files a/public/team/yago.jpg and b/public/team/yago.jpg differ diff --git a/public/team/yorke.jpg b/public/team/yorke.jpg index 1686b2d5d..7f5ac0f26 100644 Binary files a/public/team/yorke.jpg and b/public/team/yorke.jpg differ diff --git a/scripts/optimize-images.mjs b/scripts/optimize-images.mjs new file mode 100644 index 000000000..42336b716 --- /dev/null +++ b/scripts/optimize-images.mjs @@ -0,0 +1,311 @@ +// Resize committed images down to the size they are actually displayed at. +// +// Why this exists +// --------------- +// `next.config.ts` sets `images: { unoptimized: true }`, which is required by +// `output: "export"` — GitHub Pages has no image optimization server. The +// consequence is easy to miss: `next/image` does no resizing, no `srcset`, and +// no format negotiation. Whatever is committed is exactly what every visitor +// downloads. +// +// Without a resize step at intake, headshots arrive straight from a phone or a +// camera. One 2644x2644 JPEG was being served into a 48px circle, and /team +// weighed 8.5 MB. See docs/PERFORMANCE.md for the measurements. +// +// Usage +// npm run optimize:images rewrite anything over budget +// npm run optimize:images -- --check report only, exit 1 if over budget (CI) +// npm run optimize:images -- --dry-run report what would change, write nothing +// +// Idempotency +// ----------- +// A file is only rewritten when it breaks its budget. Once it fits, later runs +// skip it untouched. That matters for more than speed: JPEG and WebP are lossy, +// so re-encoding an already-compliant file on every run would quietly degrade +// it a generation at a time. It also means `--check` is stable in CI. + +import { readdir, readFile, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import sharp from "sharp"; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const publicDir = path.join(root, "public"); + +/** + * Budgets are set from how large each image actually renders, doubled to stay + * sharp on a 2x display, then rounded up. + * + * `maxKB` is a backstop for the case dimensions cannot catch: an image that + * fits its box but was saved at an absurd quality. It is deliberately loose — + * it should flag a mistake, not force a re-encode of every borderline file. + */ +const BUDGETS = [ + { + dir: "team", + width: 384, + maxKB: 90, + quality: 78, + // Avatars render at 48px in listings, 120px on a profile, 128px for the + // founder. 384 keeps the largest of those crisp on a 3x phone screen. + note: "headshots render at most 128px", + }, + { + dir: "logos", + width: 256, + maxKB: 110, + quality: 82, + // Logo tiles are 56px in the partners band and 80px in the detail card, so + // 256 is already over 3x. The ceiling is loose because + // blockchain-for-social-impact.png is a photographic screen grab kept in + // PNG for its transparency, which is an expensive combination — BACKLOG + // already asks its owner for a better source file. + note: "logo tiles are 56-80px", + }, + { + dir: "repos", + width: 600, + maxKB: 150, + quality: 78, + // Posters render in a 2:3 card. 600px matches the re-crop width already + // used for the posters that were redone by hand. + note: "posters render in a small 2:3 card", + }, + { + dir: "summit", + width: 1200, + maxKB: 220, + quality: 74, + note: "event photos in a gallery grid", + }, + { + dir: "backgrounds", + width: 1600, + maxKB: 200, + quality: 62, + // These sit behind the whole page at ~0.09 opacity and ~0.74 grayscale. + // Almost no detail survives that, so quality is unusually cheap to trade. + note: "decorative ground at ~9% opacity, grayscale", + }, + { + dir: ".", + width: 1600, + maxKB: 250, + quality: 72, + // Full-bleed hero photographs at the top of /, /team and /portfolio. Each + // one sits under two darkening gradients, so it is never seen at full + // contrast. Small marks that also live at the root are covered by + // FILE_BUDGETS below. + note: "full-bleed hero photographs", + }, +]; + +/** + * Overrides for individual files whose directory budget does not describe them. + * + * The repository root of `public/` holds two unrelated kinds of image: hero + * photographs that genuinely need to be wide, and small marks that do not. + * `etc-logo.png` renders at 32px in the site header on every single page, so + * the 1600px hero budget would have let it stay at 238px and 87 KB forever. + */ +const FILE_BUDGETS = { + "etc-logo.png": { width: 128, maxKB: 40, quality: 82, note: "header mark renders at 32px" }, + "etc-logo-3d.jpg": { width: 512, maxKB: 40, quality: 78, note: "intro splash renders at 420px" }, + "nyu-sps-cga-logo.jpg": { width: 256, maxKB: 110, quality: 82, note: "partner logo tile" }, + + // Exempt: 696px but only 12 KB, because a flat mark compresses to almost + // nothing. Every re-encode of it comes out *larger*, so holding it to the + // 256px logo budget would fail the check with no fix available. If another + // file ever lands in this position, the optimizer says so explicitly and it + // belongs here too. + "logos/microsoft-garage.png": { + width: 696, + maxKB: 40, + quality: 82, + note: "flat mark; re-encoding makes it larger", + }, +}; + +/** + * Directories deliberately left alone. + * + * `publications/` holds book pages generated by scripts/render-report-pages.mjs + * — that script owns their size through its own --scale/--quality options, and + * re-encoding its output here would compress twice. `newsletter/` is copied + * verbatim from the newsletter repository by scripts/sync-newsletter.mjs; the + * fix for a heavy issue belongs there, not here. + */ +const SKIP_DIRS = new Set(["publications", "newsletter"]); + +const RASTER = /\.(jpe?g|png|webp)$/i; + +/** Whether an image already meets its budget, and so needs no work. */ +function isCompliant(kb, width, budget) { + return width <= budget.width && kb <= budget.maxKB; +} + +/** The budget governing a path like "team/alex.jpg", or null if exempt. */ +function budgetFor(rel) { + if (FILE_BUDGETS[rel]) return FILE_BUDGETS[rel]; + const segments = rel.split("/"); + const dir = segments.length > 1 ? segments[0] : "."; + if (SKIP_DIRS.has(dir)) return null; + return BUDGETS.find((b) => b.dir === dir) ?? null; +} + +async function listImages(dir, base = "") { + const found = []; + for (const entry of await readdir(dir, { withFileTypes: true })) { + const abs = path.join(dir, entry.name); + const rel = base ? `${base}/${entry.name}` : entry.name; + if (entry.isDirectory()) { + if (SKIP_DIRS.has(rel)) continue; + found.push(...(await listImages(abs, rel))); + } else if (RASTER.test(entry.name)) { + found.push({ abs, rel }); + } + } + return found; +} + +/** + * Re-encode to the same format. Changing the extension would mean editing every + * reference in src/content, and a missed one fails silently — TeamAvatar falls + * back to initials rather than showing a broken image. The saving here comes + * overwhelmingly from the resize, not from the container. + */ +async function encode(pipeline, ext, quality) { + if (ext === ".png") { + // compressionLevel only. Do NOT add `effort` or `palette` here: both put + // libvips into its palette-quantizing path, which silently drops the alpha + // channel on some images. That took coinbase.png from a transparent mark to + // an opaque one — which would have rendered as a black box on the white + // logo tile. The transparency guard in main() is the backstop; the cheapest + // fix is not to ask for quantization in the first place. + return pipeline.png({ compressionLevel: 9 }).toBuffer(); + } + if (ext === ".webp") return pipeline.webp({ quality }).toBuffer(); + return pipeline.jpeg({ quality, mozjpeg: true }).toBuffer(); +} + +async function main() { + const args = new Set(process.argv.slice(2)); + const check = args.has("--check"); + const dryRun = args.has("--dry-run"); + + const images = await listImages(publicDir); + const over = []; + let scanned = 0; + let bytesBefore = 0; + let bytesAfter = 0; + + for (const { abs, rel } of images) { + const budget = budgetFor(rel); + if (!budget) continue; + scanned++; + + // Read the bytes up front and hand sharp a buffer rather than a path. + // Reading from the path keeps a handle open on Windows, and writing the + // result back to that same path then fails with EBUSY/UNKNOWN. + const input = await readFile(abs); + const size = input.byteLength; + const meta = await sharp(input).metadata(); + const kb = size / 1024; + const tooWide = meta.width > budget.width; + bytesBefore += size; + + if (isCompliant(kb, meta.width, budget)) { + bytesAfter += size; + continue; + } + + if (check) { + over.push({ rel, width: meta.width, kb: Math.round(kb), budget }); + bytesAfter += size; + continue; + } + + // .rotate() with no argument applies the EXIF orientation flag. sharp drops + // metadata on write, so without this a phone photo saved sideways would be + // written out sideways. It also strips EXIF, which removes any GPS + // coordinates a camera recorded — worth having on published headshots. + let pipeline = sharp(input).rotate(); + if (tooWide) { + pipeline = pipeline.resize({ width: budget.width, withoutEnlargement: true }); + } + + const ext = path.extname(rel).toLowerCase(); + const buffer = await encode(pipeline, ext, budget.quality); + + // Refuse to publish a result that lost the alpha channel. A logo that is + // mostly transparent composites against black when its alpha is dropped, + // so it would arrive on the white logo tile as a solid dark rectangle — + // and nothing else in the pipeline would notice. + if (meta.hasAlpha) { + const out = await sharp(buffer).metadata(); + if (!out.hasAlpha) { + console.error( + ` ! ${rel} skipped: encoding dropped its transparency. ` + + `Check the encoder options in encode().` + ); + bytesAfter += size; + continue; + } + } + + // Never write a result that is not actually smaller. If this happens the + // file cannot meet its budget by re-encoding, so say what to do about it + // rather than leaving --check failing with no available fix. + if (buffer.length >= size) { + bytesAfter += size; + console.error( + ` ! ${rel} cannot be shrunk (re-encode came out no smaller). ` + + `Add it to FILE_BUDGETS in this script with its current size, and say why.` + ); + continue; + } + + bytesAfter += buffer.length; + const saved = Math.round((1 - buffer.length / size) * 100); + const line = + ` ${dryRun ? "~" : "\u2713"} ${rel} ` + + `${meta.width}px/${Math.round(size / 1024)}KB -> ` + + `${tooWide ? budget.width : meta.width}px/${Math.round(buffer.length / 1024)}KB (-${saved}%)`; + console.log(line); + if (!dryRun) await writeFile(abs, buffer); + } + + if (check) { + if (over.length === 0) { + console.log(`Image budgets OK (${scanned} images checked).`); + return; + } + console.error("Images over budget:\n"); + for (const o of over) { + console.error( + ` - public/${o.rel}\n` + + ` ${o.width}px / ${o.kb}KB exceeds ${o.budget.width}px / ${o.budget.maxKB}KB ` + + `(${o.budget.note})` + ); + } + console.error( + "\nThese are served exactly as committed: next/image cannot resize them,\n" + + "because a static export has no image optimization server.\n" + + "Fix with: npm run optimize:images\n" + ); + process.exit(1); + } + + const savedMB = (bytesBefore - bytesAfter) / 1024 / 1024; + console.log( + `\n${scanned} images checked. ` + + `${(bytesBefore / 1024 / 1024).toFixed(2)} MB -> ${(bytesAfter / 1024 / 1024).toFixed(2)} MB` + + (savedMB > 0.005 ? ` (saved ${savedMB.toFixed(2)} MB)` : " (nothing to do)") + ); + if (dryRun) console.log("Dry run: nothing was written."); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/static-site/backgrounds/nyu-subway-people.jpg b/static-site/backgrounds/nyu-subway-people.jpg index 886e5c780..f0a06022b 100644 Binary files a/static-site/backgrounds/nyu-subway-people.jpg and b/static-site/backgrounds/nyu-subway-people.jpg differ diff --git a/static-site/backgrounds/nyu-subway.jpg b/static-site/backgrounds/nyu-subway.jpg index 67fb9744f..feefa3d7b 100644 Binary files a/static-site/backgrounds/nyu-subway.jpg and b/static-site/backgrounds/nyu-subway.jpg differ diff --git a/static-site/backgrounds/washington-square-aerial.jpg b/static-site/backgrounds/washington-square-aerial.jpg index 2af6a362d..0f8077085 100644 Binary files a/static-site/backgrounds/washington-square-aerial.jpg and b/static-site/backgrounds/washington-square-aerial.jpg differ diff --git a/static-site/backgrounds/washington-square-arch-isabella.jpg b/static-site/backgrounds/washington-square-arch-isabella.jpg index bc6361b7d..7cd719552 100644 Binary files a/static-site/backgrounds/washington-square-arch-isabella.jpg and b/static-site/backgrounds/washington-square-arch-isabella.jpg differ diff --git a/static-site/backgrounds/washington-square-arch-tall.jpg b/static-site/backgrounds/washington-square-arch-tall.jpg index e1ce336ac..368402b67 100644 Binary files a/static-site/backgrounds/washington-square-arch-tall.jpg and b/static-site/backgrounds/washington-square-arch-tall.jpg differ diff --git a/static-site/backgrounds/washington-square-arch.jpg b/static-site/backgrounds/washington-square-arch.jpg index f40f1f55e..22f9e7d42 100644 Binary files a/static-site/backgrounds/washington-square-arch.jpg and b/static-site/backgrounds/washington-square-arch.jpg differ diff --git a/static-site/backgrounds/washington-square-classic.jpg b/static-site/backgrounds/washington-square-classic.jpg index 202d13f4f..999e8a4fe 100644 Binary files a/static-site/backgrounds/washington-square-classic.jpg and b/static-site/backgrounds/washington-square-classic.jpg differ diff --git a/static-site/etc-logo-3d.jpg b/static-site/etc-logo-3d.jpg index 7c491571e..dcf8043b6 100644 Binary files a/static-site/etc-logo-3d.jpg and b/static-site/etc-logo-3d.jpg differ diff --git a/static-site/etc-logo.png b/static-site/etc-logo.png index 580b796a7..ec511ab3f 100644 Binary files a/static-site/etc-logo.png and b/static-site/etc-logo.png differ diff --git a/static-site/logos/100x.jpg b/static-site/logos/100x.jpg index 66fb4eb0a..1473e1e06 100644 Binary files a/static-site/logos/100x.jpg and b/static-site/logos/100x.jpg differ diff --git a/static-site/logos/apne-aap-women-worldwide.png b/static-site/logos/apne-aap-women-worldwide.png index b41c10bed..e42ad1ae3 100644 Binary files a/static-site/logos/apne-aap-women-worldwide.png and b/static-site/logos/apne-aap-women-worldwide.png differ diff --git a/static-site/logos/art-antiquities-blockchain-consortium.png b/static-site/logos/art-antiquities-blockchain-consortium.png index 5bd09cd81..a9609daee 100644 Binary files a/static-site/logos/art-antiquities-blockchain-consortium.png and b/static-site/logos/art-antiquities-blockchain-consortium.png differ diff --git a/static-site/logos/b3iq.png b/static-site/logos/b3iq.png index 2239c7026..749b3d2ce 100644 Binary files a/static-site/logos/b3iq.png and b/static-site/logos/b3iq.png differ diff --git a/static-site/logos/blockchain-for-social-impact.png b/static-site/logos/blockchain-for-social-impact.png index 6000c676e..454218ee4 100644 Binary files a/static-site/logos/blockchain-for-social-impact.png and b/static-site/logos/blockchain-for-social-impact.png differ diff --git a/static-site/logos/coinbase.png b/static-site/logos/coinbase.png index f91c871bc..1a1c42646 100644 Binary files a/static-site/logos/coinbase.png and b/static-site/logos/coinbase.png differ diff --git a/static-site/logos/gaia.png b/static-site/logos/gaia.png index 12c9bac13..89d820e4c 100644 Binary files a/static-site/logos/gaia.png and b/static-site/logos/gaia.png differ diff --git a/static-site/logos/nyu-sps.jpg b/static-site/logos/nyu-sps.jpg index 9bc773507..5ae46f77d 100644 Binary files a/static-site/logos/nyu-sps.jpg and b/static-site/logos/nyu-sps.jpg differ diff --git a/static-site/logos/sayari.jpg b/static-site/logos/sayari.jpg index 28de321bf..52e8b57f0 100644 Binary files a/static-site/logos/sayari.jpg and b/static-site/logos/sayari.jpg differ diff --git a/static-site/logos/x402-foundation.jpg b/static-site/logos/x402-foundation.jpg index 3d73d2937..5af7e145d 100644 Binary files a/static-site/logos/x402-foundation.jpg and b/static-site/logos/x402-foundation.jpg differ diff --git a/static-site/nyu-sps-cga-logo.jpg b/static-site/nyu-sps-cga-logo.jpg index a6e6113c2..a78f8e507 100644 Binary files a/static-site/nyu-sps-cga-logo.jpg and b/static-site/nyu-sps-cga-logo.jpg differ diff --git a/static-site/nyu-subway.jpg b/static-site/nyu-subway.jpg index 886e5c780..8a93fc44f 100644 Binary files a/static-site/nyu-subway.jpg and b/static-site/nyu-subway.jpg differ diff --git a/static-site/nyu-w4.jpg b/static-site/nyu-w4.jpg index d820617f4..c10f92b18 100644 Binary files a/static-site/nyu-w4.jpg and b/static-site/nyu-w4.jpg differ diff --git a/static-site/repos/AI-Models-Research.jpg b/static-site/repos/AI-Models-Research.jpg index a7841e8f4..6e3a3f102 100644 Binary files a/static-site/repos/AI-Models-Research.jpg and b/static-site/repos/AI-Models-Research.jpg differ diff --git a/static-site/repos/CERAI_AR.jpg b/static-site/repos/CERAI_AR.jpg index e79e7afdb..759a95dd0 100644 Binary files a/static-site/repos/CERAI_AR.jpg and b/static-site/repos/CERAI_AR.jpg differ diff --git a/static-site/repos/Evac-Sim-Melanie.jpg b/static-site/repos/Evac-Sim-Melanie.jpg index 388f583ea..d48a8e927 100644 Binary files a/static-site/repos/Evac-Sim-Melanie.jpg and b/static-site/repos/Evac-Sim-Melanie.jpg differ diff --git a/static-site/repos/Exodus.jpg b/static-site/repos/Exodus.jpg index ff4d303bf..d10bbc389 100644 Binary files a/static-site/repos/Exodus.jpg and b/static-site/repos/Exodus.jpg differ diff --git a/static-site/repos/India-EvacSimulation.jpg b/static-site/repos/India-EvacSimulation.jpg index 12d05790c..0cfdb7b45 100644 Binary files a/static-site/repos/India-EvacSimulation.jpg and b/static-site/repos/India-EvacSimulation.jpg differ diff --git a/static-site/repos/MVDC.jpg b/static-site/repos/MVDC.jpg index 49ec8b29b..587763f4c 100644 Binary files a/static-site/repos/MVDC.jpg and b/static-site/repos/MVDC.jpg differ diff --git a/static-site/repos/VANGO.jpg b/static-site/repos/VANGO.jpg index 441a47505..5c970b472 100644 Binary files a/static-site/repos/VANGO.jpg and b/static-site/repos/VANGO.jpg differ diff --git a/static-site/repos/War-Games.jpg b/static-site/repos/War-Games.jpg index 18400a484..c106073fd 100644 Binary files a/static-site/repos/War-Games.jpg and b/static-site/repos/War-Games.jpg differ diff --git a/static-site/repos/arts-provenance-agent.jpg b/static-site/repos/arts-provenance-agent.jpg index e2ab0f01d..5cfc49365 100644 Binary files a/static-site/repos/arts-provenance-agent.jpg and b/static-site/repos/arts-provenance-agent.jpg differ diff --git a/static-site/repos/avatar-storytelling.jpg b/static-site/repos/avatar-storytelling.jpg index 6469d2c2a..3a745d64b 100644 Binary files a/static-site/repos/avatar-storytelling.jpg and b/static-site/repos/avatar-storytelling.jpg differ diff --git a/static-site/repos/diplomatic-simulator.jpg b/static-site/repos/diplomatic-simulator.jpg index 2de3934d4..cde7dd48c 100644 Binary files a/static-site/repos/diplomatic-simulator.jpg and b/static-site/repos/diplomatic-simulator.jpg differ diff --git a/static-site/repos/ercf.jpg b/static-site/repos/ercf.jpg index c4cac47b5..fb3300acf 100644 Binary files a/static-site/repos/ercf.jpg and b/static-site/repos/ercf.jpg differ diff --git a/static-site/repos/evacuation-inform-index-carolina.jpg b/static-site/repos/evacuation-inform-index-carolina.jpg index 9a9ee7730..18094b166 100644 Binary files a/static-site/repos/evacuation-inform-index-carolina.jpg and b/static-site/repos/evacuation-inform-index-carolina.jpg differ diff --git a/static-site/repos/forced-labor-structural-risk-index.jpg b/static-site/repos/forced-labor-structural-risk-index.jpg index 701954192..f59a23028 100644 Binary files a/static-site/repos/forced-labor-structural-risk-index.jpg and b/static-site/repos/forced-labor-structural-risk-index.jpg differ diff --git a/static-site/repos/grant-valuation-tool.jpg b/static-site/repos/grant-valuation-tool.jpg index d8193e340..3ef63e1f4 100644 Binary files a/static-site/repos/grant-valuation-tool.jpg and b/static-site/repos/grant-valuation-tool.jpg differ diff --git a/static-site/repos/mariupol-evacuation-model.jpg b/static-site/repos/mariupol-evacuation-model.jpg index 37ee7c6c9..b1dbc00bd 100644 Binary files a/static-site/repos/mariupol-evacuation-model.jpg and b/static-site/repos/mariupol-evacuation-model.jpg differ diff --git a/static-site/summit/ai-researcher.jpg b/static-site/summit/ai-researcher.jpg index 443b11da6..2457e4e58 100644 Binary files a/static-site/summit/ai-researcher.jpg and b/static-site/summit/ai-researcher.jpg differ diff --git a/static-site/summit/avatar-storytelling-dashboard.jpg b/static-site/summit/avatar-storytelling-dashboard.jpg index f3b446b96..25ad5d305 100644 Binary files a/static-site/summit/avatar-storytelling-dashboard.jpg and b/static-site/summit/avatar-storytelling-dashboard.jpg differ diff --git a/static-site/summit/convening.jpg b/static-site/summit/convening.jpg index 18ce1a826..f1887052b 100644 Binary files a/static-site/summit/convening.jpg and b/static-site/summit/convening.jpg differ diff --git a/static-site/summit/hackathon.jpg b/static-site/summit/hackathon.jpg index 3c7162065..162a8b8ea 100644 Binary files a/static-site/summit/hackathon.jpg and b/static-site/summit/hackathon.jpg differ diff --git a/static-site/summit/moment-1.jpg b/static-site/summit/moment-1.jpg index 36717dbc1..d62cba1f5 100644 Binary files a/static-site/summit/moment-1.jpg and b/static-site/summit/moment-1.jpg differ diff --git a/static-site/summit/panel.jpg b/static-site/summit/panel.jpg index bf045892d..2313b2618 100644 Binary files a/static-site/summit/panel.jpg and b/static-site/summit/panel.jpg differ diff --git a/static-site/summit/student-demo.jpg b/static-site/summit/student-demo.jpg index 68894ca53..719e0f2c3 100644 Binary files a/static-site/summit/student-demo.jpg and b/static-site/summit/student-demo.jpg differ diff --git a/static-site/summit/summit-floor.jpg b/static-site/summit/summit-floor.jpg index 78ca9c66b..218772c46 100644 Binary files a/static-site/summit/summit-floor.jpg and b/static-site/summit/summit-floor.jpg differ diff --git a/static-site/summit/working-session.jpg b/static-site/summit/working-session.jpg index 971c18c98..6ec99b36c 100644 Binary files a/static-site/summit/working-session.jpg and b/static-site/summit/working-session.jpg differ diff --git a/static-site/team/README.md b/static-site/team/README.md index 8914457ee..bd4b130d6 100644 --- a/static-site/team/README.md +++ b/static-site/team/README.md @@ -3,6 +3,15 @@ Drop headshots here, then uncomment the matching `photo:` line in `src/content/site.ts`. Square images crop best (they're shown in a circle). +**Then run `npm run optimize:images`.** A headshot straight from a phone or a +camera is several thousand pixels wide, and this site serves it at exactly that +size — the static export has no image optimization server, so `next/image` +cannot resize anything. One 2644x2644 file was being downloaded to fill a 48px +circle. The command resizes to the 384px budget in place, keeps the filename, and +strips EXIF (including any GPS coordinates a camera recorded). CI rejects an +oversized image, so this is not optional. See +[CONTRIBUTING.md](../../CONTRIBUTING.md) for the full budget table. + Status (founder + Summer 2026 researchers): - `yorke.jpg` — Yorke E Rhodes III (founder) ✅ diff --git a/static-site/team/adeline.jpg b/static-site/team/adeline.jpg index f13891092..884b5ff88 100644 Binary files a/static-site/team/adeline.jpg and b/static-site/team/adeline.jpg differ diff --git a/static-site/team/alana.jpg b/static-site/team/alana.jpg index 311e7457a..75b48f754 100644 Binary files a/static-site/team/alana.jpg and b/static-site/team/alana.jpg differ diff --git a/static-site/team/alex.jpg b/static-site/team/alex.jpg index a4eed109c..0c20e9db8 100644 Binary files a/static-site/team/alex.jpg and b/static-site/team/alex.jpg differ diff --git a/static-site/team/alexa.jpeg b/static-site/team/alexa.jpeg index b11120c24..0e14a0061 100644 Binary files a/static-site/team/alexa.jpeg and b/static-site/team/alexa.jpeg differ diff --git a/static-site/team/amanda.jpg b/static-site/team/amanda.jpg index 095cf385b..110040ab9 100644 Binary files a/static-site/team/amanda.jpg and b/static-site/team/amanda.jpg differ diff --git a/static-site/team/carlos.jpg b/static-site/team/carlos.jpg index 32e342ca0..cbff57ccf 100644 Binary files a/static-site/team/carlos.jpg and b/static-site/team/carlos.jpg differ diff --git a/static-site/team/carolina.jpg b/static-site/team/carolina.jpg index efe7669c4..949a7ca78 100644 Binary files a/static-site/team/carolina.jpg and b/static-site/team/carolina.jpg differ diff --git a/static-site/team/christine.jpg b/static-site/team/christine.jpg index f19740ec6..ddafb2242 100644 Binary files a/static-site/team/christine.jpg and b/static-site/team/christine.jpg differ diff --git a/static-site/team/grace.jpg b/static-site/team/grace.jpg index df0da1c54..c0cc3768e 100644 Binary files a/static-site/team/grace.jpg and b/static-site/team/grace.jpg differ diff --git a/static-site/team/hannah.jpg b/static-site/team/hannah.jpg index d989b7b2e..3aa47444a 100644 Binary files a/static-site/team/hannah.jpg and b/static-site/team/hannah.jpg differ diff --git a/static-site/team/india.jpg b/static-site/team/india.jpg index f5263bf88..28209d5a2 100644 Binary files a/static-site/team/india.jpg and b/static-site/team/india.jpg differ diff --git a/static-site/team/kirsten.jpeg b/static-site/team/kirsten.jpeg index 07edbe4eb..ac8787f20 100644 Binary files a/static-site/team/kirsten.jpeg and b/static-site/team/kirsten.jpeg differ diff --git a/static-site/team/melanie.jpg b/static-site/team/melanie.jpg index 3a9a4978d..09fd2f2df 100644 Binary files a/static-site/team/melanie.jpg and b/static-site/team/melanie.jpg differ diff --git a/static-site/team/mohagani.jpg b/static-site/team/mohagani.jpg index 2f6bd7123..9394c35ca 100644 Binary files a/static-site/team/mohagani.jpg and b/static-site/team/mohagani.jpg differ diff --git a/static-site/team/susan.jpg b/static-site/team/susan.jpg index 642a10fe1..333327d81 100644 Binary files a/static-site/team/susan.jpg and b/static-site/team/susan.jpg differ diff --git a/static-site/team/sylvia.jpg b/static-site/team/sylvia.jpg index b63330543..cb69603c1 100644 Binary files a/static-site/team/sylvia.jpg and b/static-site/team/sylvia.jpg differ diff --git a/static-site/team/taylor.jpg b/static-site/team/taylor.jpg index 7b435eb70..8fedc682b 100644 Binary files a/static-site/team/taylor.jpg and b/static-site/team/taylor.jpg differ diff --git a/static-site/team/teresa.jpg b/static-site/team/teresa.jpg index 0c0fc56fd..cdc8dbc68 100644 Binary files a/static-site/team/teresa.jpg and b/static-site/team/teresa.jpg differ diff --git a/static-site/team/yago.jpg b/static-site/team/yago.jpg index bc1675b44..e2dcf6f06 100644 Binary files a/static-site/team/yago.jpg and b/static-site/team/yago.jpg differ diff --git a/static-site/team/yorke.jpg b/static-site/team/yorke.jpg index 1686b2d5d..7f5ac0f26 100644 Binary files a/static-site/team/yorke.jpg and b/static-site/team/yorke.jpg differ