@teamnovu/nuxt-image 0.5.5 → 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +229 -24
  2. package/dist/module.d.mts +70 -0
  3. package/dist/module.json +12 -0
  4. package/dist/module.mjs +67 -0
  5. package/dist/runtime/components/NovuBunnyImage.d.vue.ts +11 -0
  6. package/dist/runtime/components/NovuBunnyImage.vue +67 -0
  7. package/dist/runtime/components/NovuBunnyImage.vue.d.ts +11 -0
  8. package/dist/runtime/components/NovuCloudinaryImage.d.vue.ts +19 -0
  9. package/dist/runtime/components/NovuCloudinaryImage.vue +79 -0
  10. package/dist/runtime/components/NovuCloudinaryImage.vue.d.ts +19 -0
  11. package/dist/runtime/components/NovuImage.d.vue.ts +28 -0
  12. package/dist/runtime/components/NovuImage.vue +90 -0
  13. package/dist/runtime/components/NovuImage.vue.d.ts +28 -0
  14. package/dist/runtime/components/NovuImgproxyImage.d.vue.ts +12 -0
  15. package/dist/runtime/components/NovuImgproxyImage.vue +68 -0
  16. package/dist/runtime/components/NovuImgproxyImage.vue.d.ts +12 -0
  17. package/dist/runtime/components/NovuStatamicImage.d.vue.ts +17 -0
  18. package/dist/runtime/components/NovuStatamicImage.vue +87 -0
  19. package/dist/runtime/components/NovuStatamicImage.vue.d.ts +17 -0
  20. package/dist/runtime/composables/useResponsiveImage.d.ts +42 -0
  21. package/dist/runtime/composables/useResponsiveImage.js +115 -0
  22. package/dist/runtime/providers/imgproxy.d.ts +68 -0
  23. package/dist/runtime/providers/imgproxy.js +161 -0
  24. package/dist/runtime/types.d.ts +56 -0
  25. package/dist/runtime/types.js +0 -0
  26. package/dist/runtime/utils/focal.d.ts +11 -0
  27. package/dist/runtime/utils/focal.js +33 -0
  28. package/dist/runtime/utils/i18n.d.ts +1 -0
  29. package/dist/runtime/utils/i18n.js +10 -0
  30. package/dist/runtime/utils/numbers.d.ts +1 -0
  31. package/dist/runtime/utils/numbers.js +6 -0
  32. package/dist/runtime/utils/providerModifiers.d.ts +4 -0
  33. package/dist/runtime/utils/providerModifiers.js +36 -0
  34. package/dist/runtime/utils/screens.d.ts +5 -0
  35. package/dist/runtime/utils/screens.js +19 -0
  36. package/dist/runtime/utils/statamic.d.ts +3 -0
  37. package/dist/runtime/utils/statamic.js +143 -0
  38. package/dist/types.d.mts +3 -0
  39. package/package.json +65 -68
  40. package/CHANGELOG.md +0 -373
  41. package/LICENSE +0 -21
  42. package/dist/module.js +0 -482
  43. package/dist/runtime/components/image.mixin.d.ts +0 -46
  44. package/dist/runtime/components/image.mixin.js +0 -58
  45. package/dist/runtime/components/nuxt-img.vue +0 -49
  46. package/dist/runtime/components/nuxt-img.vue.d.ts +0 -12
  47. package/dist/runtime/components/nuxt-picture.vue +0 -86
  48. package/dist/runtime/components/nuxt-picture.vue.d.ts +0 -15
  49. package/dist/runtime/image.d.ts +0 -2
  50. package/dist/runtime/image.js +0 -194
  51. package/dist/runtime/index.d.ts +0 -2
  52. package/dist/runtime/index.js +0 -2
  53. package/dist/runtime/ipx.d.ts +0 -3
  54. package/dist/runtime/ipx.js +0 -3
  55. package/dist/runtime/plugin.d.ts +0 -1
  56. package/dist/runtime/plugin.js +0 -31
  57. package/dist/runtime/providers/cloudinary.d.ts +0 -2
  58. package/dist/runtime/providers/cloudinary.js +0 -96
  59. package/dist/runtime/providers/fastly.d.ts +0 -2
  60. package/dist/runtime/providers/fastly.js +0 -21
  61. package/dist/runtime/providers/glide.d.ts +0 -2
  62. package/dist/runtime/providers/glide.js +0 -49
  63. package/dist/runtime/providers/imagekit.d.ts +0 -2
  64. package/dist/runtime/providers/imagekit.js +0 -172
  65. package/dist/runtime/providers/imgix.d.ts +0 -3
  66. package/dist/runtime/providers/imgix.js +0 -153
  67. package/dist/runtime/providers/ipx.d.ts +0 -4
  68. package/dist/runtime/providers/ipx.js +0 -28
  69. package/dist/runtime/providers/netlify.d.ts +0 -3
  70. package/dist/runtime/providers/netlify.js +0 -40
  71. package/dist/runtime/providers/prismic.d.ts +0 -2
  72. package/dist/runtime/providers/prismic.js +0 -10
  73. package/dist/runtime/providers/sanity.d.ts +0 -2
  74. package/dist/runtime/providers/sanity.js +0 -87
  75. package/dist/runtime/providers/static.d.ts +0 -3
  76. package/dist/runtime/providers/static.js +0 -6
  77. package/dist/runtime/providers/storyblok.d.ts +0 -2
  78. package/dist/runtime/providers/storyblok.js +0 -27
  79. package/dist/runtime/providers/twicpics.d.ts +0 -2
  80. package/dist/runtime/providers/twicpics.js +0 -58
  81. package/dist/runtime/providers/vercel.d.ts +0 -3
  82. package/dist/runtime/providers/vercel.js +0 -28
  83. package/dist/runtime/utils/index.d.ts +0 -17
  84. package/dist/runtime/utils/index.js +0 -72
  85. package/dist/runtime/utils/meta.d.ts +0 -2
  86. package/dist/runtime/utils/meta.js +0 -67
  87. package/dist/runtime/utils/static-map.d.ts +0 -2
  88. package/dist/runtime/utils/static-map.js +0 -20
  89. package/dist/types.d.ts +0 -172
  90. package/vetur/attributes.json +0 -32
  91. package/vetur/tags.json +0 -32
package/README.md CHANGED
@@ -1,35 +1,240 @@
1
- [![@nuxt/image](./docs/static/social.svg "Nuxt Image")](https://image.nuxtjs.org)
1
+ # @teamnovu/nuxt-image
2
2
 
3
- [![npm version][npm-version-src]][npm-version-href]
4
- [![npm downloads][npm-downloads-src]][npm-downloads-href]
5
- [![Checks][checks-src]][checks-href]
6
- [![Codecov][codecov-src]][codecov-href]
3
+ Responsive images for Nuxt 4. Builds on [`@nuxt/image`](https://image.nuxt.com) for provider-agnostic URL generation, ships a JS-driven `sizes` strategy for accurate per-element widths, and includes a Statamic adapter so `<NovuStatamicImage :src="asset" />` handles the common case end-to-end.
7
4
 
8
- - [📖 &nbsp;Read Documentation](https://image.nuxtjs.org)
9
- - [▶️ &nbsp;Play online](https://githubbox.com/nuxt/image/tree/main/example)
5
+ ## Highlights
10
6
 
7
+ - Single-image element with provider-agnostic responsive `srcset`.
8
+ - `sizes` starts at `1px` (so SSR ships a tiny blurred placeholder candidate) and is upgraded client-side to the actual rendered element width in `px`, recomputed via `ResizeObserver`. For `object-fit: cover` images that are wider than their container, the value reflects the cropped image's true width (`containerHeight * imageAspect`) so the browser picks a candidate big enough to fill the visible area without upscaling.
9
+ - Five components, layered:
10
+ - `<NovuImage>` - provider-agnostic base
11
+ - `<NovuCloudinaryImage>` - Cloudinary-flavoured (`focal: 'face'`, `zoom`, `crop`, raw `transforms`)
12
+ - `<NovuBunnyImage>` - Bunny-flavoured (`faceCrop`, `cropGravity`, `sharpen`, `blur`)
13
+ - `<NovuImgproxyImage>` - imgproxy-flavoured (`preset`, `gravity`, `resizingType`, `extend`)
14
+ - `<NovuStatamicImage>` - Statamic adapter that auto-resolves to the matching wrapper (or `<NovuImage>` when no wrapper matches)
15
+ - Built-in imgproxy provider (vendored from [nuxt/image#2117](https://github.com/nuxt/image/pull/2117) until it merges) with HMAC URL signing.
16
+ - Performance defaults: `loading="lazy"`, `decoding="async"`, inline `aspect-ratio` CSS for CLS prevention. Opt-in `priority` flips to `loading="eager"` + `fetchpriority="high"`.
11
17
 
12
- ### Contributing
18
+ ## Install
13
19
 
14
- 1. Clone this repository
15
- 2. Install dependencies using `yarn install`
16
- 3. Start development server using `yarn dev`
20
+ ```bash
21
+ pnpm add @teamnovu/nuxt-image
22
+ ```
17
23
 
24
+ ```ts
25
+ export default defineNuxtConfig({
26
+ modules: ['@teamnovu/nuxt-image'],
18
27
 
19
- ## 📑 License
28
+ novuImage: {
29
+ provider: 'cloudinary',
30
+ },
20
31
 
21
- Copyright (c) Nuxt Team
32
+ image: {
33
+ cloudinary: {
34
+ baseURL: 'https://res.cloudinary.com/your-cloud/image/upload/',
35
+ },
36
+ // bunny: { baseURL: 'https://your-zone.b-cdn.net' },
37
+ // imgproxy: { baseURL: 'https://imgproxy.example.com', key: '...', salt: '...' },
38
+ },
39
+ })
40
+ ```
22
41
 
42
+ The module installs `@nuxt/image` for you and auto-registers the imgproxy provider. Configure `image.<provider>` exactly as documented in the [`@nuxt/image` docs](https://image.nuxt.com/get-started/configuration).
23
43
 
44
+ ## Module options (`novuImage`)
24
45
 
25
- <!-- Badges -->
26
- [npm-version-src]: https://flat.badgen.net/npm/v/@nuxt/image
27
- [npm-version-href]: https://npmjs.com/package/@nuxt/image
28
- [npm-downloads-src]: https://flat.badgen.net/npm/dm/@nuxt/image
29
- [npm-downloads-href]: https://npmjs.com/package/@nuxt/image
30
- [checks-src]: https://flat.badgen.net/github/checks/nuxt/image/master
31
- [checks-href]: https://github.com/nuxt/image/actions
32
- [codecov-src]: https://flat.badgen.net/codecov/c/github/nuxt/image
33
- [codecov-href]: https://codecov.io/gh/nuxt/image
34
- [license-src]: https://img.shields.io/npm/l/@nuxt/image.svg
35
- [license-href]: https://github.com/nuxt/image/blob/main/LICENSE
46
+ | Option | Type | Default | Notes |
47
+ | -------------------- | ------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------ |
48
+ | `screens` | `Record<string, number\|string>` | `{ xs: 320, sm: 640, md: 768, lg: 1024, xl: 1280, '2xl': 1600, '3xl': 2000 }` | Each entry generates one `srcset` candidate at that pixel width. |
49
+ | `fallbackWidth` | `number` | `2000` | Width used for the legacy `src` URL. |
50
+ | `usePlaceholder` | `boolean` | `true` | Inlines a low-quality candidate (descriptor `32w`) into `srcset`. |
51
+ | `placeholderQuality` | `number` | `30` | Quality of the inlined placeholder. |
52
+ | `placeholderWidth` | `number` | `300` | Width of the inlined placeholder. |
53
+ | `defaultQuality` | `number\|string?` | - | Optional global `quality` modifier. Leave unset to defer to the provider's own default (Cloudinary auto-applies `q_auto`, etc.). |
54
+ | `defaultFormat` | `string?` | - | Optional global `format` modifier. Leave unset to defer to the provider's own default (Cloudinary auto-applies `f_auto`, imgproxy uses `webp`, etc.). |
55
+ | `provider` | `string?` | - | Default `@nuxt/image` provider; also drives `<NovuStatamicImage>` wrapper resolution. |
56
+ | `statamicPlaceholderFields` | `string[]` | `['placeholder', 'lqip', 'thumbhash']` | Statamic asset field handles checked for `daun/statamic-placeholders` data. |
57
+
58
+ ## Components
59
+
60
+ ### `<NovuImage>` - provider-agnostic base
61
+
62
+ ```vue
63
+ <NovuImage
64
+ src="path/to/image.jpg"
65
+ alt="A cat"
66
+ :width="1600"
67
+ :height="900"
68
+ :focal="[1200, 660]"
69
+ provider="cloudinary"
70
+ />
71
+ ```
72
+
73
+ Common props (all optional unless noted):
74
+
75
+ | Prop | Type | Notes |
76
+ | ------------- | ----------------------------- | -------------------------------------------------------------------- |
77
+ | `src` | `string` (required) | Asset key/path passed to the configured provider. |
78
+ | `alt` | `string` | Alt text. |
79
+ | `width/height`| `number\|string` | Rendered image dimensions. Used for the `aspect-ratio` CSS hint and candidate height derivation. |
80
+ | `sourceWidth/sourceHeight` | `number\|string` | Original source dimensions. Used to map source-pixel focal points for providers such as imgproxy when rendered dimensions differ from the source. |
81
+ | `aspectRatio` | `number` | Explicit aspect ratio (overrides `width/height`). |
82
+ | `focal` | `'auto' \| [number, number]` | Source-pixel focal point, translated per provider. Providers with native auto focal support default to `'auto'`. |
83
+ | `provider` | `string` | Override the default provider for this element. |
84
+ | `quality` | `number\|string` | Forwarded to `useImage`. |
85
+ | `format` | `string` | Forwarded to `useImage`. |
86
+ | `fit` | `string` | Forwarded to `useImage`. |
87
+ | `modifiers` | `Record<string, unknown>` | Raw modifiers escape hatch. |
88
+ | `sizes` | `string` | Hard override for the dynamic `sizes` calculation. |
89
+ | `priority` | `boolean` | Sets `loading="eager"` + `fetchpriority="high"`. |
90
+ | `loading` | `'lazy' \| 'eager'` | Defaults to `'lazy'` (or `'eager'` when `priority`). |
91
+ | `decoding` | `'async' \| 'sync' \| 'auto'` | Defaults to `'async'`. |
92
+
93
+ `focal` tuples are source image pixel coordinates, for example `[1200, 660]` on a `4000x2667` source. When the rendered `width/height` differ from the source dimensions, pass `sourceWidth/sourceHeight` as well so providers that need normalized coordinates, such as imgproxy, can map the focal point correctly. `<NovuStatamicImage>` fills these source dimensions from the asset automatically. Cloudinary and imgproxy default to their native auto focal modes; Bunny does not, because its `face_crop` API is a separate explicit behavior.
94
+
95
+ ### `<NovuCloudinaryImage>`
96
+
97
+ Pins `provider="cloudinary"` and adds Cloudinary-only conveniences. Most importantly, `focal="face"` triggers Cloudinary's face-detection gravity.
98
+
99
+ | Prop | Type | Notes |
100
+ | ---------------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------- |
101
+ | `crop` | `string` | Cloudinary crop mode (URL parameter `c_`). Accepts both Cloudinary-native names (`'lfill'`, `'thumb'`) and `@nuxt/image`'s aliases (`'cover'`, `'thumbnail'`). |
102
+ | `focal` | `'auto' \| 'face' \| [x, y]` | Adds `'face'` on top of the agnostic `focal` values. |
103
+ | `zoom` | `number \| string` | Cloudinary zoom factor (`z_<value>`). |
104
+ | `transformation` | `string \| Record<string, any>` | Named Cloudinary transformation preset (`t_<name>`), or a raw modifiers object spread into `useImage`'s modifiers. |
105
+
106
+ ```vue
107
+ <NovuCloudinaryImage
108
+ src="profile.jpg"
109
+ alt="Profile photo"
110
+ :width="800"
111
+ :height="800"
112
+ focal="face"
113
+ crop="thumb"
114
+ zoom="0.7"
115
+ />
116
+ ```
117
+
118
+ ### `<NovuBunnyImage>`
119
+
120
+ Pins `provider="bunny"`. `faceCrop` maps to Bunny's face-detection `face_crop` parameter, while `cropGravity` maps directly to Bunny's documented `crop_gravity` anchors (`center`, `north`, `south`, `east`, `west`, `northeast`, `northwest`, `southeast`, `southwest`). Pixel-coordinate focal points are ignored because Bunny's manual focal crop is not wired yet.
121
+
122
+ ```vue
123
+ <NovuBunnyImage
124
+ src="hero.jpg"
125
+ :width="1600"
126
+ :height="900"
127
+ face-crop
128
+ :sharpen="true"
129
+ />
130
+ ```
131
+
132
+ ### `<NovuImgproxyImage>`
133
+
134
+ Pins `provider="imgproxy"`. Use `gravity` to pass an imgproxy-native gravity string (e.g. `'fp:0.3:0.25'`); when `gravity` is set the agnostic `focal` mapping is bypassed for that element.
135
+
136
+ ```vue
137
+ <NovuImgproxyImage
138
+ src="https://origin.example.com/photo.jpg"
139
+ :width="1600"
140
+ :height="900"
141
+ preset="hero"
142
+ resizing-type="fill"
143
+ />
144
+ ```
145
+
146
+ ### `<NovuStatamicImage>` - the 90% case
147
+
148
+ Renders any Statamic image asset. Auto-resolves to the matching provider wrapper (`provider="cloudinary"` -> `<NovuCloudinaryImage>`, etc.) and falls back to `<NovuImage>` when no wrapper matches.
149
+
150
+ ```vue
151
+ <NovuStatamicImage :src="entry.hero_image" />
152
+ <NovuStatamicImage :src="entry.hero_image" provider="bunny" />
153
+ <NovuStatamicImage :src="user.avatar" :fallback-alt="user.name" />
154
+ <NovuStatamicImage :src="entry.hero_image" placeholder-field="hero_lqip" />
155
+ ```
156
+
157
+ #### Alt-text resolution order
158
+
159
+ 1. `props.alt` (hard override).
160
+ 2. `asset.alt_${locale}` if a locale is detected from `useNuxtApp().$i18n.locale` or set via the `locale` prop.
161
+ 3. `asset.alt`.
162
+ 4. `props.fallbackAlt` (use this for things like `user.name`).
163
+ 5. `undefined`.
164
+
165
+ #### Focal-point resolution order
166
+
167
+ 1. `props.focal` (hard override).
168
+ 2. `asset.focus` (Statamic's `"x-y"` percent format).
169
+ 3. `asset.focus_css` (Statamic's `"x% y%"` format - some sites add this via augmentation).
170
+ 4. `undefined` (provider falls back to its own default - usually centre).
171
+
172
+ The dead-centre default (`focus: '50-50'`) is treated as "no focal point" so providers can pick their own smarter centre/strategy.
173
+
174
+ #### Statamic placeholders
175
+
176
+ `<NovuStatamicImage>` can consume placeholder data generated by [`daun/statamic-placeholders`](https://github.com/daun/statamic-placeholders). By default it checks the `placeholder`, `lqip`, and `thumbhash` asset fields; override the checked handles globally with `novuImage.statamicPlaceholderFields` or per component with `placeholderField`.
177
+
178
+ Query the ready-to-use data URI when payload size is not a concern:
179
+
180
+ ```graphql
181
+ ... on Asset_Assets {
182
+ alt
183
+ lqip {
184
+ uri
185
+ }
186
+ }
187
+ ```
188
+
189
+ Or query only the ThumbHash hash and let the Nuxt component decode it:
190
+
191
+ ```graphql
192
+ ... on Asset_Assets {
193
+ alt
194
+ lqip {
195
+ type
196
+ hash
197
+ }
198
+ }
199
+ ```
200
+
201
+ When both `uri` and `hash` are present, `uri` wins. Hash decoding is supported for ThumbHash placeholders only; BlurHash or average-color placeholders need to provide `uri`. If a Statamic site does not have the addon or the queried asset object has no placeholder field, rendering falls back to the existing generated low-quality `srcset` placeholder. Only include placeholder subfields in GraphQL queries for Statamic schemas where the addon field exists, because GraphQL itself rejects unknown fields before this component can handle the missing data.
202
+
203
+ #### Statamic blueprint requirements
204
+
205
+ The `<NovuStatamicImage>` component reads the following fields from a Statamic asset object:
206
+
207
+ - `permalink` (required) - absolute URL; the host is stripped so the configured provider's `baseURL` applies.
208
+ - `width` / `height` - required to convert `focus` percentages to pixel coordinates (and to compute `aspect-ratio`).
209
+ - `alt` - default alt text.
210
+ - `alt_<locale>` (e.g. `alt_de`, `alt_fr`) - localised alt text. Required only if you want per-locale alts.
211
+ - `focus` - Statamic's native `"x-y"` percent string. Optional.
212
+ - `placeholder` / `lqip` / `thumbhash` - optional placeholder fields from `daun/statamic-placeholders`, either as `{ uri }` or `{ type, hash }`.
213
+
214
+ ## Composable
215
+
216
+ `resolveStatamicAsset(src, props?, options?)` is a pure helper exposed for cases where you want to compute the resolved `{ src, placeholderSrc, alt, focal, width, height, aspectRatio }` outside of the component (for SEO heads, OG images, etc.).
217
+
218
+ ```ts
219
+ import { resolveStatamicAsset } from '#imports'
220
+
221
+ const resolved = resolveStatamicAsset(asset, { fallbackAlt: 'Article hero' }, { locale: 'de' })
222
+ ```
223
+
224
+ ## A note on the imgproxy provider
225
+
226
+ The imgproxy provider used here is vendored from [nuxt/image#2117](https://github.com/nuxt/image/pull/2117). When that PR merges into `@nuxt/image`, the vendored version (and the `@noble/hashes` runtime dependency) will be removed.
227
+
228
+ ## Development
229
+
230
+ ```bash
231
+ pnpm dev:prepare # build types + prepare playground
232
+ pnpm dev # run the playground
233
+ pnpm test # run vitest
234
+ pnpm lint # run eslint
235
+ pnpm test:types # vue-tsc on src/ and playground/
236
+ ```
237
+
238
+ ## License
239
+
240
+ MIT
@@ -0,0 +1,70 @@
1
+ import * as _nuxt_schema from '@nuxt/schema';
2
+
3
+ interface ModuleOptions {
4
+ /**
5
+ * Breakpoint widths used to build the `srcset`. Each entry generates one
6
+ * candidate at that pixel width. Plain numbers or strings with `px` suffix.
7
+ *
8
+ * @default { xs: 320, sm: 640, md: 768, lg: 1024, xl: 1280, '2xl': 1600, '3xl': 2000 }
9
+ */
10
+ screens: Record<string, number | string>;
11
+ /**
12
+ * Width used for the legacy `src` URL (browsers that ignore `srcset`/`sizes`).
13
+ * @default 2000
14
+ */
15
+ fallbackWidth: number;
16
+ /**
17
+ * Whether to inline a low-quality blurred placeholder into the `srcset`
18
+ * (descriptor `32w`) so the smallest candidate is shown until the real image loads.
19
+ * @default true
20
+ */
21
+ usePlaceholder: boolean;
22
+ /** Quality of the inlined placeholder. @default 30 */
23
+ placeholderQuality: number;
24
+ /** Width of the inlined placeholder in px. @default 300 */
25
+ placeholderWidth: number;
26
+ /**
27
+ * Optional global `quality` modifier forwarded to `useImage`. Leave unset
28
+ * to defer to the provider's own default (Cloudinary auto-applies `q_auto`,
29
+ * imgproxy uses its own configured quality, etc.).
30
+ */
31
+ defaultQuality?: number | string;
32
+ /**
33
+ * Optional global `format` modifier forwarded to `useImage`. Leave unset
34
+ * to defer to the provider's own default (Cloudinary auto-applies `f_auto`,
35
+ * imgproxy defaults to `webp`, etc.).
36
+ */
37
+ defaultFormat?: string;
38
+ /**
39
+ * Default provider name forwarded to `useImage`. Also used by
40
+ * `<NovuStatamicImage>` to auto-resolve the matching wrapper component.
41
+ */
42
+ provider?: string;
43
+ /**
44
+ * Statamic asset field handles checked for daun/statamic-placeholders data.
45
+ * Each field may be a data URI string or a GraphQL object with `uri`, `type`,
46
+ * and `hash` subfields.
47
+ *
48
+ * @default ['placeholder', 'lqip', 'thumbhash']
49
+ */
50
+ statamicPlaceholderFields: string[];
51
+ }
52
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
53
+
54
+ declare module '@nuxt/schema' {
55
+ interface PublicRuntimeConfig {
56
+ novuImage: ModuleOptions;
57
+ }
58
+ }
59
+ declare module '@nuxt/image' {
60
+ interface ImageProviders {
61
+ imgproxy: {
62
+ baseURL: string;
63
+ key: string;
64
+ salt: string;
65
+ };
66
+ }
67
+ }
68
+
69
+ export { _default as default };
70
+ export type { ModuleOptions };
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@teamnovu/nuxt-image",
3
+ "configKey": "novuImage",
4
+ "compatibility": {
5
+ "nuxt": ">=3.13.0"
6
+ },
7
+ "version": "1.0.0-beta.1",
8
+ "builder": {
9
+ "@nuxt/module-builder": "1.0.2",
10
+ "unbuild": "unknown"
11
+ }
12
+ }
@@ -0,0 +1,67 @@
1
+ import { defineNuxtModule, createResolver, installModule, addComponent, addImports, addImportsDir } from '@nuxt/kit';
2
+ import { defu } from 'defu';
3
+
4
+ const DEFAULT_SCREENS = {
5
+ "xs": 320,
6
+ "sm": 640,
7
+ "md": 768,
8
+ "lg": 1024,
9
+ "xl": 1280,
10
+ "2xl": 1600,
11
+ "3xl": 2e3
12
+ };
13
+ const DEFAULTS = {
14
+ screens: DEFAULT_SCREENS,
15
+ fallbackWidth: 2e3,
16
+ usePlaceholder: true,
17
+ placeholderQuality: 30,
18
+ placeholderWidth: 300,
19
+ statamicPlaceholderFields: ["placeholder", "lqip", "thumbhash"]
20
+ };
21
+ const module$1 = defineNuxtModule({
22
+ meta: {
23
+ name: "@teamnovu/nuxt-image",
24
+ configKey: "novuImage",
25
+ compatibility: {
26
+ nuxt: ">=3.13.0"
27
+ }
28
+ },
29
+ defaults: DEFAULTS,
30
+ async setup(options, nuxt) {
31
+ const resolver = createResolver(import.meta.url);
32
+ const imageOptions = nuxt.options.image && typeof nuxt.options.image === "object" ? nuxt.options.image : {};
33
+ nuxt.options.image = imageOptions;
34
+ const providers = imageOptions.providers ??= {};
35
+ if (!providers.imgproxy) {
36
+ const imgproxyOptions = imageOptions.imgproxy;
37
+ providers.imgproxy = {
38
+ provider: resolver.resolve("./runtime/providers/imgproxy"),
39
+ ...imgproxyOptions ? { options: imgproxyOptions } : {}
40
+ };
41
+ }
42
+ await installModule("@nuxt/image", imageOptions);
43
+ const publicConfig = nuxt.options.runtimeConfig.public;
44
+ publicConfig.novuImage = defu(publicConfig.novuImage, options);
45
+ for (const name of [
46
+ "NovuImage",
47
+ "NovuCloudinaryImage",
48
+ "NovuBunnyImage",
49
+ "NovuImgproxyImage",
50
+ "NovuStatamicImage"
51
+ ]) {
52
+ addComponent({
53
+ name,
54
+ filePath: resolver.resolve(`./runtime/components/${name}.vue`),
55
+ global: true
56
+ });
57
+ }
58
+ addImports({
59
+ name: "resolveStatamicAsset",
60
+ from: resolver.resolve("./runtime/utils/statamic")
61
+ });
62
+ addImportsDir(resolver.resolve("./runtime/composables"));
63
+ nuxt.options.alias["#novu-image"] = resolver.resolve("./runtime");
64
+ }
65
+ });
66
+
67
+ export { module$1 as default };
@@ -0,0 +1,11 @@
1
+ import type { NovuImageProps } from './NovuImage.vue.js';
2
+ export type BunnyCropGravity = 'center' | 'north' | 'south' | 'east' | 'west' | 'northeast' | 'northwest' | 'southeast' | 'southwest';
3
+ export interface NovuBunnyImageProps extends Omit<NovuImageProps, 'provider'> {
4
+ cropGravity?: BunnyCropGravity;
5
+ faceCrop?: boolean | string;
6
+ sharpen?: boolean | number;
7
+ blur?: number;
8
+ }
9
+ declare const __VLS_export: import("vue").DefineComponent<NovuBunnyImageProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<NovuBunnyImageProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
10
+ declare const _default: typeof __VLS_export;
11
+ export default _default;
@@ -0,0 +1,67 @@
1
+ <script setup>
2
+ import { computed } from "vue";
3
+ import NovuImage from "./NovuImage.vue";
4
+ const props = defineProps({
5
+ cropGravity: { type: String, required: false, default: void 0 },
6
+ faceCrop: { type: [Boolean, String], required: false, default: void 0 },
7
+ sharpen: { type: [Boolean, Number], required: false, default: void 0 },
8
+ blur: { type: Number, required: false, default: void 0 },
9
+ src: { type: String, required: true },
10
+ alt: { type: String, required: false },
11
+ width: { type: [Number, String], required: false },
12
+ height: { type: [Number, String], required: false },
13
+ sourceWidth: { type: [Number, String], required: false },
14
+ sourceHeight: { type: [Number, String], required: false },
15
+ aspectRatio: { type: Number, required: false },
16
+ quality: { type: [Number, String], required: false },
17
+ format: { type: String, required: false },
18
+ fit: { type: String, required: false },
19
+ focal: { type: [String, Array], required: false },
20
+ modifiers: { type: Object, required: false },
21
+ sizes: { type: String, required: false },
22
+ loading: { type: String, required: false },
23
+ decoding: { type: String, required: false },
24
+ priority: { type: Boolean, required: false },
25
+ fallbackWidth: { type: Number, required: false },
26
+ usePlaceholder: { type: Boolean, required: false },
27
+ placeholderQuality: { type: Number, required: false },
28
+ placeholderWidth: { type: Number, required: false },
29
+ placeholderSrc: { type: String, required: false }
30
+ });
31
+ const bunnyModifiers = computed(() => {
32
+ const out = { ...props.modifiers ?? {} };
33
+ if (props.cropGravity !== void 0) out.cropGravity = props.cropGravity;
34
+ if (props.faceCrop !== void 0 && props.faceCrop !== false) out.faceCrop = props.faceCrop;
35
+ if (props.sharpen !== void 0) out.sharpen = props.sharpen;
36
+ if (props.blur !== void 0) out.blur = props.blur;
37
+ return out;
38
+ });
39
+ </script>
40
+
41
+ <template>
42
+ <NovuImage
43
+ v-bind="$attrs"
44
+ :src="src"
45
+ :alt="alt"
46
+ :width="width"
47
+ :height="height"
48
+ :source-width="sourceWidth"
49
+ :source-height="sourceHeight"
50
+ :aspect-ratio="aspectRatio"
51
+ :quality="quality"
52
+ :format="format"
53
+ :fit="fit"
54
+ :focal="focal"
55
+ :modifiers="bunnyModifiers"
56
+ :sizes="sizes"
57
+ :loading="loading"
58
+ :decoding="decoding"
59
+ :priority="priority"
60
+ :fallback-width="fallbackWidth"
61
+ :use-placeholder="usePlaceholder"
62
+ :placeholder-quality="placeholderQuality"
63
+ :placeholder-width="placeholderWidth"
64
+ :placeholder-src="placeholderSrc"
65
+ provider="bunny"
66
+ />
67
+ </template>
@@ -0,0 +1,11 @@
1
+ import type { NovuImageProps } from './NovuImage.vue.js';
2
+ export type BunnyCropGravity = 'center' | 'north' | 'south' | 'east' | 'west' | 'northeast' | 'northwest' | 'southeast' | 'southwest';
3
+ export interface NovuBunnyImageProps extends Omit<NovuImageProps, 'provider'> {
4
+ cropGravity?: BunnyCropGravity;
5
+ faceCrop?: boolean | string;
6
+ sharpen?: boolean | number;
7
+ blur?: number;
8
+ }
9
+ declare const __VLS_export: import("vue").DefineComponent<NovuBunnyImageProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<NovuBunnyImageProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
10
+ declare const _default: typeof __VLS_export;
11
+ export default _default;
@@ -0,0 +1,19 @@
1
+ import type { NovuImageProps } from './NovuImage.vue.js';
2
+ import type { FocalPoint } from '../utils/focal.js';
3
+ export type CloudinaryFocal = 'auto' | 'face' | FocalPoint;
4
+ export interface NovuCloudinaryImageProps extends Omit<NovuImageProps, 'focal' | 'provider'> {
5
+ focal?: CloudinaryFocal;
6
+ zoom?: number | string;
7
+ /**
8
+ * Cloudinary crop mode (URL parameter `c_`). Accepts both Cloudinary-native
9
+ * values (`'lfill'`, `'thumb'`, `'fill'`, `'pad'`, ...) and `@nuxt/image`'s
10
+ * normalised aliases (`'cover'`, `'contain'`, `'thumbnail'`, ...). Maps to
11
+ * the `fit` modifier under the hood.
12
+ */
13
+ crop?: string;
14
+ /** Named Cloudinary transformation preset (URL parameter `t_<name>`), or a raw modifiers object spread into `useImage`'s modifiers. */
15
+ transformation?: string | Record<string, unknown>;
16
+ }
17
+ declare const __VLS_export: import("vue").DefineComponent<NovuCloudinaryImageProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<NovuCloudinaryImageProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
+ declare const _default: typeof __VLS_export;
19
+ export default _default;
@@ -0,0 +1,79 @@
1
+ <script setup>
2
+ import { computed } from "vue";
3
+ import NovuImage from "./NovuImage.vue";
4
+ const props = defineProps({
5
+ focal: { type: [String, Array], required: false, default: void 0 },
6
+ zoom: { type: [Number, String], required: false, default: void 0 },
7
+ crop: { type: String, required: false, default: void 0 },
8
+ transformation: { type: [String, Object], required: false, default: void 0 },
9
+ src: { type: String, required: true },
10
+ alt: { type: String, required: false },
11
+ width: { type: [Number, String], required: false },
12
+ height: { type: [Number, String], required: false },
13
+ sourceWidth: { type: [Number, String], required: false },
14
+ sourceHeight: { type: [Number, String], required: false },
15
+ aspectRatio: { type: Number, required: false },
16
+ quality: { type: [Number, String], required: false },
17
+ format: { type: String, required: false },
18
+ fit: { type: String, required: false },
19
+ modifiers: { type: Object, required: false },
20
+ sizes: { type: String, required: false },
21
+ loading: { type: String, required: false },
22
+ decoding: { type: String, required: false },
23
+ priority: { type: Boolean, required: false },
24
+ fallbackWidth: { type: Number, required: false },
25
+ usePlaceholder: { type: Boolean, required: false },
26
+ placeholderQuality: { type: Number, required: false },
27
+ placeholderWidth: { type: Number, required: false },
28
+ placeholderSrc: { type: String, required: false }
29
+ });
30
+ const cloudinaryFocal = computed(() => {
31
+ if (props.focal === "face") return void 0;
32
+ return props.focal;
33
+ });
34
+ const effectiveFit = computed(() => props.crop ?? props.fit);
35
+ const cloudinaryModifiers = computed(() => {
36
+ const out = { ...props.modifiers ?? {} };
37
+ if (props.zoom !== void 0) out.zoom = props.zoom;
38
+ if (props.focal === "face") {
39
+ out.gravity = "face";
40
+ if (props.crop === void 0 && out.fit === void 0) out.fit = "fill";
41
+ }
42
+ if (props.transformation) {
43
+ if (typeof props.transformation === "string") {
44
+ out.transformation = props.transformation;
45
+ } else {
46
+ Object.assign(out, props.transformation);
47
+ }
48
+ }
49
+ return out;
50
+ });
51
+ </script>
52
+
53
+ <template>
54
+ <NovuImage
55
+ v-bind="$attrs"
56
+ :src="src"
57
+ :alt="alt"
58
+ :width="width"
59
+ :height="height"
60
+ :source-width="sourceWidth"
61
+ :source-height="sourceHeight"
62
+ :aspect-ratio="aspectRatio"
63
+ :quality="quality"
64
+ :format="format"
65
+ :fit="effectiveFit"
66
+ :focal="cloudinaryFocal"
67
+ :modifiers="cloudinaryModifiers"
68
+ :sizes="sizes"
69
+ :loading="loading"
70
+ :decoding="decoding"
71
+ :priority="priority"
72
+ :fallback-width="fallbackWidth"
73
+ :use-placeholder="usePlaceholder"
74
+ :placeholder-quality="placeholderQuality"
75
+ :placeholder-width="placeholderWidth"
76
+ :placeholder-src="placeholderSrc"
77
+ provider="cloudinary"
78
+ />
79
+ </template>
@@ -0,0 +1,19 @@
1
+ import type { NovuImageProps } from './NovuImage.vue.js';
2
+ import type { FocalPoint } from '../utils/focal.js';
3
+ export type CloudinaryFocal = 'auto' | 'face' | FocalPoint;
4
+ export interface NovuCloudinaryImageProps extends Omit<NovuImageProps, 'focal' | 'provider'> {
5
+ focal?: CloudinaryFocal;
6
+ zoom?: number | string;
7
+ /**
8
+ * Cloudinary crop mode (URL parameter `c_`). Accepts both Cloudinary-native
9
+ * values (`'lfill'`, `'thumb'`, `'fill'`, `'pad'`, ...) and `@nuxt/image`'s
10
+ * normalised aliases (`'cover'`, `'contain'`, `'thumbnail'`, ...). Maps to
11
+ * the `fit` modifier under the hood.
12
+ */
13
+ crop?: string;
14
+ /** Named Cloudinary transformation preset (URL parameter `t_<name>`), or a raw modifiers object spread into `useImage`'s modifiers. */
15
+ transformation?: string | Record<string, unknown>;
16
+ }
17
+ declare const __VLS_export: import("vue").DefineComponent<NovuCloudinaryImageProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<NovuCloudinaryImageProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
18
+ declare const _default: typeof __VLS_export;
19
+ export default _default;
@@ -0,0 +1,28 @@
1
+ import type { FocalPoint } from '../utils/focal.js';
2
+ export interface NovuImageProps {
3
+ src: string;
4
+ alt?: string;
5
+ width?: number | string;
6
+ height?: number | string;
7
+ sourceWidth?: number | string;
8
+ sourceHeight?: number | string;
9
+ aspectRatio?: number;
10
+ quality?: number | string;
11
+ format?: string;
12
+ fit?: string;
13
+ focal?: FocalPoint;
14
+ modifiers?: Record<string, unknown>;
15
+ provider?: string;
16
+ sizes?: string;
17
+ loading?: 'lazy' | 'eager';
18
+ decoding?: 'async' | 'sync' | 'auto';
19
+ priority?: boolean;
20
+ fallbackWidth?: number;
21
+ usePlaceholder?: boolean;
22
+ placeholderQuality?: number;
23
+ placeholderWidth?: number;
24
+ placeholderSrc?: string;
25
+ }
26
+ declare const __VLS_export: import("vue").DefineComponent<NovuImageProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<NovuImageProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
27
+ declare const _default: typeof __VLS_export;
28
+ export default _default;