feat(picsum): add Lorem Picsum placeholder image provider#2106
Conversation
Add support for Lorem Picsum (https://picsum.photos/) as a built-in provider for placeholder images during development. Features: - Support for random images with dimensions: /200/300 - Support for specific images by ID: /id/237/200/300 - Support for seeded images: /seed/picsum/200/300 - Modifiers: grayscale, blur (1-10) Usage: ```vue <NuxtImg provider="picsum" src="id/237" width="200" height="300" /> <NuxtImg provider="picsum" src="id/237" :modifiers="{ grayscale: true, blur: 2 }" width="200" height="300" /> ``` Closes nuxt#361
📝 WalkthroughWalkthroughThis pull request introduces a new Picsum image provider for the Nuxt Image module. The implementation adds a provider module (src/runtime/providers/picsum.ts) that builds Picsum URLs with optional src, width, height, and modifiers (grayscale, blur), registers the provider in BuiltInProviders (src/provider.ts), enables it in playground config (playground/nuxt.config.ts and playground/app/providers.ts), and adds tests, snapshots, and documentation pages for the new provider. Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@src/runtime/providers/picsum.ts`:
- Around line 41-47: The Picsum URL builder currently appends only a single
segment when height is provided but width is missing, causing Picsum to
interpret it as a square and ignore the user's intent; update the logic in the
function that builds the parts array (referencing parts, width, height in
src/runtime/providers/picsum.ts) so that if height is provided and width is not
you append two segments (e.g., use height for both width and height) or
otherwise supply a sensible default width before pushing the height segment;
ensure both path segments are always present when either dimension is supplied
so the generated Picsum URL is unambiguous.
- Around line 34-38: The code currently pushes `${type}/${id}` even when id is
undefined (e.g., src === "id"), producing "id/undefined"; update the conditional
around the split result to ensure id is present and non-empty before pushing
into parts — for example change the check that uses src/type/id to require id
(e.g., if (type && (type === 'id' || type === 'seed') && typeof id === 'string'
&& id.trim().length > 0) { parts.push(`${type}/${id}`) }) so you only append
valid id segments and avoid "undefined" in URLs.
In `@test/providers.ts`:
- Line 119: The picsum placeholder currently uses the URL
'https://picsum.photos/200' which Picsum treats as a square (200×200) when only
height is intended; update the picsum provider so that when only height is
specified it emits an explicit height/width path (e.g.
'https://picsum.photos/200/200') or adjust the URL generation logic in the
picsum provider to compose `${height}/${height}` when width is undefined; locate
the picsum entry (symbol: picsum) in test/providers.ts and change its url
generation to produce the explicit 'height/height' form or add a note
documenting the limitation if you prefer not to change the URL.
🧹 Nitpick comments (1)
test/nuxt/providers.test.ts (1)
466-474: Test coverage gap: no tests forsrc(id/seed) or picsum-specific modifiers (grayscale, blur).The current test only validates dimension-based URL generation with an empty
src. The provider's distinguishing features —id/{id},seed/{seed},grayscale, andblur— are untested. Consider adding dedicated test cases similar to how other providers have provider-specific tests (e.g.,cloudinary fetch,imageengine modifiers).Example cases to cover:
src = 'id/237'withwidth: 200, height: 300→https://picsum.photos/id/237/200/300src = 'seed/picsum'withwidth: 200→https://picsum.photos/seed/picsum/200- Modifiers:
{ grayscale: true, blur: 5 }→?grayscale&blur=5- Edge cases:
blur: 0,blur: 15(clamping),src: 'id'(missing ID)Do you want me to generate the additional test cases?
| if (src && src !== '/') { | ||
| const [type, id] = (src.startsWith('/') ? src.slice(1) : src).split('/') | ||
| if (type && (type === 'id' || type === 'seed')) { | ||
| parts.push(`${type}/${id}`) | ||
| } |
There was a problem hiding this comment.
Missing validation: id can be undefined when src is "id" or "seed".
If src is "id" (without a trailing ID value), split('/') yields ["id"], so id is undefined, and parts.push(\${type}/${id}`)produces"id/undefined"` in the URL.
Proposed fix
if (src && src !== '/') {
const [type, id] = (src.startsWith('/') ? src.slice(1) : src).split('/')
- if (type && (type === 'id' || type === 'seed')) {
+ if (type && id && (type === 'id' || type === 'seed')) {
parts.push(`${type}/${id}`)
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (src && src !== '/') { | |
| const [type, id] = (src.startsWith('/') ? src.slice(1) : src).split('/') | |
| if (type && (type === 'id' || type === 'seed')) { | |
| parts.push(`${type}/${id}`) | |
| } | |
| if (src && src !== '/') { | |
| const [type, id] = (src.startsWith('/') ? src.slice(1) : src).split('/') | |
| if (type && id && (type === 'id' || type === 'seed')) { | |
| parts.push(`${type}/${id}`) | |
| } | |
| } |
🤖 Prompt for AI Agents
In `@src/runtime/providers/picsum.ts` around lines 34 - 38, The code currently
pushes `${type}/${id}` even when id is undefined (e.g., src === "id"), producing
"id/undefined"; update the conditional around the split result to ensure id is
present and non-empty before pushing into parts — for example change the check
that uses src/type/id to require id (e.g., if (type && (type === 'id' || type
=== 'seed') && typeof id === 'string' && id.trim().length > 0) {
parts.push(`${type}/${id}`) }) so you only append valid id segments and avoid
"undefined" in URLs.
| // Add dimensions - these come after the ID/seed path | ||
| if (width) { | ||
| parts.push(String(width)) | ||
| } | ||
| if (height) { | ||
| parts.push(String(height)) | ||
| } |
There was a problem hiding this comment.
Height without width produces an ambiguous Picsum URL.
When only height is provided, only one path segment is appended (e.g., /200), which Picsum treats as a square image (width = height = 200). This silently ignores the user's intent of specifying only the height dimension.
Consider either: (a) documenting this as a known limitation, or (b) emitting both segments (e.g., using a default width or swapping to /{height}/{height}).
🤖 Prompt for AI Agents
In `@src/runtime/providers/picsum.ts` around lines 41 - 47, The Picsum URL builder
currently appends only a single segment when height is provided but width is
missing, causing Picsum to interpret it as a square and ignore the user's
intent; update the logic in the function that builds the parts array
(referencing parts, width, height in src/runtime/providers/picsum.ts) so that if
height is provided and width is not you append two segments (e.g., use height
for both width and height) or otherwise supply a sensible default width before
pushing the height segment; ensure both path segments are always present when
either dimension is supplied so the generated Picsum URL is unambiguous.
| hygraph: { url: 'https://eu-central-1-shared-euc1-02.graphassets.com/cltsj3mii0pvd07vwb5cyh1ig/resize=height:200/auto_image/cltsrex89477t08unlckqx9ue' }, | ||
| caisy: { url: 'https://assets.caisy.io/assets/b76210be-a043-4989-98df-ecaf6c6e68d8/056c27e2-81f5-4cd3-b728-cef181dfe7dc/d83ea6f0-f90a-462c-aebd-b8bc615fdce0pexelsmiguelapadrinan1591056.jpg?h=200' }, | ||
| bunny: { url: 'https://bunnyoptimizerdemo.b-cdn.net?height=200' }, | ||
| picsum: { url: 'https://picsum.photos/200' }, |
There was a problem hiding this comment.
Height-only requests produce a square image, not a height-constrained one.
When only height: 200 is specified (no width), the generated URL is https://picsum.photos/200, which Picsum interprets as a 200×200 square image — not a height-only constraint. This is a semantic mismatch with the user's intent. The same URL is produced for width: 200 (line 80).
For a placeholder image provider this is likely acceptable, but it may be worth documenting this limitation or considering emitting https://picsum.photos/200/200 explicitly for clarity.
🤖 Prompt for AI Agents
In `@test/providers.ts` at line 119, The picsum placeholder currently uses the URL
'https://picsum.photos/200' which Picsum treats as a square (200×200) when only
height is intended; update the picsum provider so that when only height is
specified it emits an explicit height/width path (e.g.
'https://picsum.photos/200/200') or adjust the URL generation logic in the
picsum provider to compose `${height}/${height}` when width is undefined; locate
the picsum entry (symbol: picsum) in test/providers.ts and change its url
generation to produce the explicit 'height/height' form or add a note
documenting the limitation if you prefer not to change the URL.
commit: |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2106 +/- ##
========================================
- Coverage 6.99% 6.94% -0.05%
========================================
Files 78 79 +1
Lines 3632 3671 +39
Branches 140 141 +1
========================================
+ Hits 254 255 +1
- Misses 3329 3367 +38
Partials 49 49 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Add support for Lorem Picsum (https://picsum.photos/) as a built-in provider for placeholder images during development.
Features:
Usage:
Closes #361
🔗 Linked issue
❓ Type of change
📚 Description